Commit graph

189 commits

Author SHA1 Message Date
7079759c24 Starter oppgave 13.3 2026-03-18 00:21:52 +00:00
263f63bec8 Trait-aware frontend: samlingssider med dynamiske trait-paneler (oppgave 13.2)
Samlingsnoder med `metadata.traits` rendres nå som egne sider på
/collection/[id]. Hvert trait-navn mappes til en dedikert Svelte-komponent
som viser relevant UI. Traits uten egen komponent vises med et generisk panel.

Komponenter for 9 traits: editor, chat, kanban, podcast, publishing,
rss, calendar, recording, transcription. Mottak-siden viser traits som
pills og lenker til samlingssiden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:20:35 +00:00
a404751fe4 Starter oppgave 13.2 2026-03-18 00:15:34 +00:00
37cd133e1d Trait-validering for samlingsnoder (oppgave 13.1)
Maskinrommet validerer nå metadata.traits ved create_node og update_node
for collection-noder. Ukjente trait-navn avvises med 400 Bad Request.

Lukket katalog med 48 gyldige traits fra docs/primitiver/traits.md.
Konfigurasjon per trait er fri JSONB — kun nøkkelnavnene valideres.
Noder som ikke er collections valideres ikke (traits ignoreres).

Inkluderer 6 unit-tester for valideringsfunksjonen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:14:21 +00:00
a21da84122 Starter oppgave 13.1 2026-03-18 00:10:25 +00:00
e5c7791dfc Podcast-RSS: samlings-node med publiserings-edges → generert RSS-feed (oppgave 11.4)
Nytt endepunkt GET /pub/{slug}/feed.xml som genererer RSS 2.0 eller
Atom 1.0 feed for samlinger med rss-trait. Feeden er offentlig (ingen auth).

- Slår opp samling via publishing.slug i metadata.traits
- Henter belongs_to-edges (publiserte noder), sortert på publish_at
- Podcast-samlinger (med podcast-trait) inkluderer <enclosure>-tags
  med CAS-URL, MIME-type og filstørrelse fra has_media-edges
- Støtter RSS 2.0 (default) og Atom 1.0 via rss.format config
- iTunes-namespace for podcast-feeds
- Stabile GUID-er basert på node UUID
- 5 min cache (Cache-Control: public, max-age=300)

Manuell XML-generering uten ekstra avhengigheter — enklere enn å
introdusere en RSS-crate for dette omfanget.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:09:10 +00:00
da988234dd Starter oppgave 11.4 2026-03-18 00:03:43 +00:00
6d916d9860 Pruning-logikk: TTL per modalitet, signaler, disk-nødventil (oppgave 11.3)
Implementerer automatisk opprydding av CAS-filer basert på dokumentert
spec i docs/retninger/maskinrommet.md:

- TTL per modalitet: lyd 30d, bilde 30d, video 14d, tekst aldri
- Signaler som forlenger levetid: publishing-edge, siste tilgang
  (last_accessed_at), utranskribert lyd beholdes
- Tre-trinns disk-nødventil:
  - >85%: slett generert innhold (TTS osv, kan regenereres)
  - >90%: aggressiv pruning med kraftig redusert TTL
  - >95%: kritisk — alt uten publishing-edge slettes
- Periodisk bakgrunnsloop: hvert 6. time, oftere ved høy disk
- Tilgangslogging: serving oppdaterer last_accessed_at (fire-and-forget)
- Pruning-hendelser logges til resource_usage_log

Ny modul: maskinrommet/src/pruning.rs
Ny migrasjon: 010_pruning.sql (last_accessed_at kolonne + indeks)
CasStore utvidet med delete(), disk_usage_bytes(), disk_usage_percent()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:02:27 +00:00
70fb5eb141 Starter oppgave 11.3 2026-03-17 23:55:59 +00:00
445f32de69 Sanntidslyd: kommunikasjonsnode → LiveKit-rom (oppgave 11.2)
Kobler kommunikasjonsnoder til LiveKit for sanntidslyd.
Bruker sender join_communication-intensjon, maskinrommet validerer
tilgang og returnerer signert LiveKit JWT-token + rom-URL.

Nye komponenter:
- maskinrommet/src/livekit.rs: JWT token-generering (HS256-signert
  med LIVEKIT_API_SECRET, 1-times TTL, publisher/subscriber-roller)
- POST /intentions/join_communication: validerer deltaker-edge,
  genererer token, oppretter rom i STDB, oppdaterer node-metadata
- POST /intentions/leave_communication: fjerner deltaker fra STDB
- POST /intentions/close_communication: stenger rom (krever owner)
- SpacetimeDB: live_room + room_participant tabeller for sanntids
  deltakerliste (frontend abonnerer via WebSocket)

SpacetimeDB-modul publisert som synops-v2 (ny identitet etter
at den opprinnelige ikke lenger var tilgjengelig). .env oppdatert.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:54:40 +00:00
5392560807 Starter oppgave 11.2 2026-03-17 23:42:13 +00:00
e95b7d6663 TTS-pipeline: tekst → lyd via ElevenLabs (oppgave 10.4)
Ny jobbtype `tts_generate` som kaller ElevenLabs text-to-speech API,
lagrer MP3-lyd i CAS, og oppretter media-node med has_media-edge.

Voice-preferanse løses i tre lag: eksplisitt i payload → nodens
metadata.voice_preference → ELEVENLABS_DEFAULT_VOICE env.
Dette er "mottaker-preferanse i metadata" — en node kan sette
voice_preference i sin metadata for å styre hvilken stemme som brukes.

Ny migrasjon 009: resource_usage_log-tabell for sporing av
ressursforbruk (TTS, AI, Whisper, CAS). Ref: docs/features/ressursforbruk.md

Endringer:
- maskinrommet/src/tts.rs: TTS-handler med ElevenLabs-integrasjon
- maskinrommet/src/intentions.rs: POST /intentions/generate_tts
- maskinrommet/src/jobs.rs: Dispatcher for tts_generate
- migrations/009_resource_usage_and_tts.sql: resource_usage_log
- scripts/maskinrommet-env.sh: ELEVENLABS_* env-variabler

Krever: ELEVENLABS_API_KEY i /srv/synops/.env (placeholder lagt til)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:40:46 +00:00
1803619622 Starter oppgave 10.4 2026-03-17 23:32:29 +00:00
3c1d85026b AI-oppsummering av kommunikasjonsnoder (oppgave 10.3)
Ny jobbtype `summarize_communication` som henter alle meldinger fra
en kommunikasjonsnode, sender dem til LiteLLM for oppsummering, og
oppretter en content-node med sammendraget. Sammendraget knyttes til
kommunikasjonsnoden med `belongs_to`-edge (del av samtalen) og
`summary`-edge (lett å finne sammendrag for en gitt samtale).

API-endepunkt: POST /intentions/summarize { communication_id }
Verifiserer at brukeren er deltaker i samtalen. Jobbprioritiet 3
(bakgrunn). Modell konfigurerbar via AI_SUMMARY_MODEL env-variabel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:31:16 +00:00
b0c037ef71 Starter oppgave 10.3 2026-03-17 23:25:54 +00:00
63f022d739 AI-foreslåtte edges: LLM-analyse av innhold → topics og mentions (oppgave 10.2)
Ny jobbtype `suggest_edges` som automatisk trigges ved opprettelse av
content-noder med tilstrekkelig tekst (≥20 tegn). Sender innholdet til
LiteLLM (sidelinja/rutine) via AI Gateway, parser JSON-respons med
topics og mentions, og oppretter topic-noder + mentions-edges i grafen.

Flyten:
1. create_node oppdager content-node med nok tekst → enqueue suggest_edges
2. Worker henter node-innhold og eksisterende topics fra PG
3. LLM analyserer tekst og returnerer foreslåtte topics/mentions
4. Nye topic-noder opprettes (med ai_generated-flagg i metadata)
5. mentions-edges opprettes fra innholdsnode til topic/entitet-noder
6. Deduplisering: gjenbruker eksisterende topics ved case-insensitivt match

Filer:
- maskinrommet/src/ai_edges.rs: Ny modul med LLM-kall og edge-opprettelse
- maskinrommet/src/jobs.rs: suggest_edges registrert i dispatcher
- maskinrommet/src/intentions.rs: Trigger i create_node
- docs/: Oppdatert jobbkø og AI gateway-docs med ny jobbtype

NB: Krever gyldig API-nøkkel i LiteLLM (OpenRouter/Gemini/Anthropic).
Jobben feiler gracefully med retry+backoff ved manglende nøkkel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:24:29 +00:00
e08a4fd22f Fiks remote-navn: forgejo → origin i task-runner 2026-03-17 23:16:56 +00:00
b498700399 Starter oppgave 10.2 2026-03-17 23:14:11 +00:00
01ad35557f LiteLLM AI Gateway: Docker, DB-ruting, config-generering (oppgave 10.1)
Setter opp AI Gateway med LiteLLM som sentralisert proxy for alle
AI-kall. PG eier all modellkonfigurasjon — LiteLLM er stateløs.

- Migrasjon 008: ai_model_aliases, ai_model_providers, ai_job_routing
  med seed-data for sidelinja/rutine og sidelinja/resonering
- Config-generering fra PG: scripts/generate-litellm-config.sh
  filtrerer bort providers med tomme API-nøkler
- Docker-container kjører på sidelinja-net (intern, ingen eksponert port)
- Maskinrommet har AI_GATEWAY_URL via maskinrommet-env.sh
- API-nøkkel-placeholders i .env (GEMINI, ANTHROPIC, XAI)
- Oppdatert docs/infra/ai_gateway.md med faktisk config

Verifisert: container healthy, modellaliaser eksponert, maskinrommet
har korrekt gateway-URL. Reelle API-kall krever at Vegard fyller
inn leverandør-nøkler i /srv/synops/.env.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:12:46 +00:00
9d65e91244 Starter oppgave 10.1 2026-03-17 23:03:14 +00:00
7f5d23e0c6 Kunnskapsgraf: topic-noder, mentions-edges, visuell graf-visning (oppgave 9.4)
Backend:
- query_graph endpoint med rekursiv CTE-traversering (fokus + dybde)
- Filtrering på node_kind og edge_type, RLS-beskyttet
- Maks 200 noder, 1-3 hops dybde

Frontend:
- D3 force-directed graf på /graph med fargekodede noder
- Opprett topic-noder (node_kind='topic') direkte fra graf-visningen
- Opprett mentions-edges mellom vilkårlige noder
- Klikk for detaljer, dobbeltklikk for fokus, dra for å flytte
- Filter-legende for nodetyper og kanttyper
- Zoom, auto-fit, sidepanel med koblingsinfo
- Graf-knapp lagt til i mottaksflaten

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:01:57 +00:00
cce1c73b9e Starter oppgave 9.4 2026-03-17 22:56:09 +00:00
517311ca0f Frigjør stale oppgaver 9.4 og 10.1 etter API-feil 2026-03-17 22:55:47 +00:00
02fc38eb3d LiveKit oppsett: Docker-container for WebRTC (oppgave 11.1)
Legger til LiveKit som Docker-tjeneste for WebRTC-støtte.
Konfigurasjonen bruker livekit/livekit-server med signaling
proxyet gjennom Caddy på /livekit/*, og UDP 50000-50100 eksponert
direkte for WebRTC media-strømmer.

Endringer:
- docker-compose.yml: livekit-service (på /srv/synops/)
- livekit.yaml: server-konfig (på /srv/synops/config/livekit/)
- Caddy: /livekit/* route aktivert
- UFW: åpnet UDP 50000-50100 + TCP 7881
- maskinrommet-env.sh: LIVEKIT_URL/KEY/SECRET for Rust-API
- produksjon.md: oppdatert med LiveKit-detaljer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:54:50 +00:00
82b733b865 Starter oppgave 11.1 2026-03-17 22:50:11 +00:00
f303f8a9b4 Starter oppgave 10.1 2026-03-17 22:43:57 +00:00
5d25024719 Starter oppgave 9.4 2026-03-17 22:35:38 +00:00
2ba830c7b4 Dagbok-visning: privat journal sortert på tid (oppgave 9.3)
Ny /diary-route som viser brukerens private noder — de som kun har
owner-edge og ingen delte edges til andre. Gruppert etter dato,
nyeste først, med inline oppretting av nye innlegg.

Dagbok-knapp med tellebadge lagt til i mottak-siden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:34:13 +00:00
c505c9ebfc Starter oppgave 9.3 2026-03-17 22:28:49 +00:00
ea0671933d Kalender-visning: månedsrutenett med scheduled-edges (oppgave 9.2)
Ny rute /calendar som viser alle noder med scheduled-edge i et
månedsbasert kalenderrutenett. Bruker edge-metadata { at: ISO8601 }
for tidspunkt, med T12:00:00-konvensjon for heldagshendelser.

Funksjoner:
- Månedsnavigering med «I dag»-snarvei
- Drag-and-drop for å flytte hendelser mellom datoer (updateEdge)
- Inline-oppretting med tittel og valgfritt klokkeslett
- Fargekoding etter node_kind
- Hendelsesliste under rutenett for gjeldende måned
- Kalender-lenke med hendelsesteller på mottak-siden
- Sanntid via SpacetimeDB (edgeStore.byType('scheduled'))

Arkitekturvalg: Bruker scheduled-edges direkte fra SpacetimeDB
i stedet for legacy calendar_events-tabellen. En node blir en
kalenderoppføring ved å ha en scheduled-edge — konsistent med
«hva edges gjør med noder»-prinsippet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:27:28 +00:00
895f517d9d Fiks run-tasks.sh: unngå set -e krasj ved exitkode-håndtering
((completed++)) returnerer 1 når completed=0 under set -e.
Bytter til $((x + 1)) og fanger exitkode med || exit_code=$?.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:21:11 +00:00
f0a590303a Starter oppgave 9.2 2026-03-17 22:20:54 +00:00
d8c0cceb89 Kanban-visning: board med drag-and-drop statusendring (oppgave 9.1)
Implementerer kanban som noder+edges uten separate tabeller:
- Board = collection-node med metadata.board og metadata.columns
- Kort = content-noder med belongs_to-edge til board
- Status via status-edge (kort→board) med metadata.value
- Posisjon via belongs_to-edge metadata.position

Backend:
- POST /intentions/update_edge — oppdater edge-type/metadata
- GET /query/board?board_id= — hent kort med status og posisjon

Frontend:
- /board/[id] route med kolonner, drag-and-drop, kortoppretting
- Sanntid via SpacetimeDB edge-subscriptions
- Board-oppretting og navigasjon fra mottak-siden

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:13:22 +00:00
26e730e758 Starter oppgave 9.1 2026-03-17 22:05:19 +00:00
73999f2f45 Fiks task-runner: fase 13-15, auto-retry ved API-feil
- Legg til fase 13 (traits), 14 (publisering), 15 (adminpanel) i
  avhengighetskartet og blocked_phases-scan
- run-tasks.sh prøver automatisk igjen ved API-feil (500/529) med
  lineær backoff (60s, 120s, ...) opptil 10 forsøk
- Skiller mellom bevisst blokkering ([?]/[!]) og krasj — stopper
  bare ved blokkering, retries ved krasj
- systemd-service (synops-tasks) for auto-restart ved feil
- Oppdater prompt til å reflektere direkte serverkjøring

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:04:29 +00:00
244da69110 Legg til retry med backoff for Claude API-feil (500/529)
Maskinrommet prøver nå opptil 3 ganger med eksponentiell backoff
(2, 4, 8 sek) ved 500/529-feil fra Anthropic. Etter alle forsøk
vises en vennlig melding i chatten i stedet for rå feilmeldinger.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 21:56:54 +00:00
d4715831bf Flytt Caddy fra Docker til native + fiks frontend-kjeden
- Caddy installert via apt, kjører som systemd-service
- Docker-tjenester eksponerer porter på localhost (Authentik:9000,
  Forgejo:3000, SpacetimeDB:9080)
- Caddy-blokk fjernet fra docker-compose.yml
- iptables-hack fjernet (ingen Docker→host-proxy lenger)
- Caddy proxyer /api/* til maskinrommet (unngår CORS)
- SpacetimeDB URL fikset (trailing slash for korrekt URL-resolving)
- Arvet tilgang i nodeVisibility (belongs_to → foreldrenode)
- Signin-side bruker <a> i stedet for client-side signIn()
- /claude redirect-rute for testing
- @auth/core oppgradert 0.34.3→0.41.1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 21:26:06 +00:00
ab91e5396d Oppdater driftsmodell: hybrid native + Docker
Dokumenterer gjeldende modell der egenutviklet kode (maskinrommet,
SvelteKit) kjører native via systemd, mens tredjepartstjenester
(PG, STDB, Authentik, Caddy, Whisper, LiteLLM) kjører i Docker.

- CLAUDE.md: ny driftsmodell-tabell, Claude-agent-seksjon
- docs/arkitektur.md: teknologivalg med kjøremodus-kolonne
- docs/setup/produksjon.md: maskinrommet native instruksjoner

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 19:27:29 +00:00
1dd48317af Oppdater docs og config for native maskinrommet + Claude-agent
- CLAUDE.md: ny driftsmodell-seksjon, maskinrommet native, Claude-agent
- docs/infra/claude_agent.md: arkitektur, sikkerhet, drift, oppsett
- config/caddy/Caddyfile: synk fra server (host.docker.internal)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 19:24:23 +00:00
33a1b44946 Implementer Claude som chat-deltaker (Fase A: MVP)
Claude er nå en agent-node i grafen som kan delta i samtaler.
Når en bruker sender melding i en kommunikasjonsnode der Claude
er deltaker, enqueues en agent_respond-jobb som kaller claude CLI
direkte og skriver svaret tilbake til chatten.

Nye filer:
- migrations/007_agent_system.sql: agent_identities, agent_permissions, ai_usage_log
- maskinrommet/src/agent.rs: agent_respond job handler
- scripts/maskinrommet.service: systemd-tjeneste for native kjøring
- scripts/maskinrommet-env.sh: genererer env med Docker container-IPs

Endringer:
- intentions.rs: trigger agent_respond ved melding i agent-chat
- jobs.rs: dispatch agent_respond til agent-handler
- frontend chat: bot-badge (🤖) og amber-farge på agent-meldinger
- LiteLLM config: resonering-modellalias via OpenRouter

Maskinrommet kjører nå direkte på hosten (ikke i Docker) for å
ha tilgang til claude CLI. Caddy peker til host.docker.internal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 19:20:17 +00:00
91ccf4b270 Oppdater docs til server-only utviklingsmodell
Fjerner alle referanser til lokal WSL2-instans og to-instans-modellen.
All utvikling skjer nå direkte på produksjonsserveren via Claude Code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:29:45 +00:00
f81c8a96e0 Fullfør oppgave 8.2: Kontekstbasert identitet med alias
Når en bruker oppretter en node i en kommunikasjonskontekst der
brukerens alias er deltaker (owner/member_of/host_of), settes
created_by til alias-noden i stedet for brukerens hovednode.

Endringer:
- resolve_context_identity(): slår opp brukerens alias i konteksten
- create_node(): bruker alias som created_by når context_id er satt
- user_can_modify_node/edge(): gjenkjenner alias-eierskap ved endring/sletting
- 006_alias_aware_rls.sql: RLS-policies inkluderer alias-opprettede noder
- current_node_alias_ids(): PG-funksjon for alias-oppslag i RLS

Verifisert med integrasjonstest på server:
- Node i alias-kontekst → created_by = alias ✓
- Node uten kontekst → created_by = bruker ✓
- Update/delete av alias-node fungerer for hovedbruker ✓

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 19:19:36 +01:00
e7bb6d1f8a Starter oppgave 8.2 2026-03-17 19:10:43 +01:00
fab41419ec Fullfør oppgave 8.1: Alias-noder med system-edge
Implementert:
- POST /intentions/create_alias: oppretter person-node og alias-edge
  (system=true) fra brukerens hovednode til aliasnoden
- GET /query/aliases: returnerer brukerens alias-noder via alias-edges
- Alias-edgen er usynlig for traversering via eksisterende RLS-policy
  som filtrerer system-edges (edges.system = true)

Verifisert med integrasjonstest på server: opprettelse, PG-persistering,
spørring, og at system-edge er korrekt flagget.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 19:07:24 +01:00
71ad21e4d5 Legg til GET /query/aliases for å hente brukerens aliaser
Del av oppgave 8.1. Endepunktet returnerer alle alias-noder
koblet til brukerens hovednode via alias-edges (system=true).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 19:02:33 +01:00
ad7f430534 Dokumenter Claude Code server-oppsett
Node.js, Rust og Claude Code installert på produksjonsserveren.
Repo klonet til /home/vegard/synops/, tmux-alias konfigurert.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 19:00:26 +01:00
89abd5eee4 Legg til create_alias-endepunkt og alias-spørring
Oppgave 8.1: Alias-noder med system-edge. Nytt endepunkt
POST /intentions/create_alias oppretter en person-node og
en alias-edge (system=true) fra brukerens hovednode.
GET /query/aliases returnerer brukerens alias-noder.

Alias-edgen er usynlig for traversering via eksisterende
RLS-policy som filtrerer system-edges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 19:00:08 +01:00
b687c717c6 Starter oppgave 8.1 2026-03-17 18:54:51 +01:00
35701aeb2a Fullfør oppgave 7.8: SRT-eksport fra transkripsjons-segmenter
Nytt GET /query/segments/srt-endepunkt som genererer nedlastbar SRT-fil
fra transcription_segments-tabellen. Bruker RLS-verifisert tilgang.
Frontend har nedlastingsknapp i TranscriptionView med autentisert fetch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 18:47:50 +01:00
f9cc1328cd Starter oppgave 7.8 2026-03-17 18:43:49 +01:00