59 Commits

Author SHA1 Message Date
Claude
08cff7b26a
Merge branch 'claude/phase2-coach' into claude/phase3-media
# Conflicts:
#	apps/mobile/lib/api.test.ts
#	apps/web/src/locales/de/messages.po
#	apps/web/src/locales/en/messages.po
#	apps/web/src/locales/es/messages.po
#	apps/web/src/locales/hi/messages.po
#	apps/web/src/locales/ko/messages.po
#	apps/web/src/locales/pt-BR/messages.po
#	apps/web/src/locales/ru/messages.po
#	apps/web/src/locales/tr/messages.po
#	apps/web/src/locales/zh-CN/messages.po
#	apps/web/src/pages/SettingsOverlay.tsx
#	apps/web/src/pages/shell/message-cards.tsx
#	packages/adapter-kit/src/index.ts
#	packages/adapters/src/builtin-tools.ts
#	packages/adapters/src/executor-tools.ts
#	packages/adapters/src/index.ts
2026-10-04 08:10:33 +00:00
Claude
a62e09f36b
fix(web): word the main conversation preview in the user's language during onboarding
While onboarding wrote the main conversation, its sidebar row, project rows and Heute showed the
server preview: nothing for a pending card (a card has no text of its own, so the row fell back
to "Ready") and the English fallback of the closing note, even for a German user.

Groups now carry `previewBlock` next to `preview`: the latest message when it is an onboarding
card or note, validated with the same block schemas the messages use (now named exports). The
web words it with the helpers that render the card and the note, so no copy is duplicated: the
question while a card waits, the answer once given, and the closing note in the user's language
and clock. A dismissed card keeps the plain preview, as the conversation no longer shows it.
The field is left out for every other message, and mobile keeps using the plain `preview`.

The chosen-answer lookup moves from the card into onboarding-copy so the card and the preview
share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-04 04:17:32 +00:00
Claude
681dec2f3f
Merge main into the media integration branch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 23:25:48 +00:00
Claude
77e822b9a7
Merge main into the Phase 2 integration branch
Main now gives every project its own main agent. Coaching stays with the
user's personal agent, the main bot of the home space: only it gets the
goal, habit, check-in, brief and nudge tools, the coach summary and the
open-onboarding note, and the tools refuse a project's main bot too. The
onboarding questions start with the personal agent only. A project the
scan creates gets its main agent like any other, and the inbox lists asks
from another space's main conversation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 23:24:21 +00:00
cynarAI
8449dec6c2
feat: give every project its own main agent (#125)
Each project (space) now has a main agent and a main conversation of its
own. The user talks to it, and it delegates to the project's specialists.
Creating a project sets both up and opens the conversation, and deleting
an otherwise empty project removes them with it.


Claude-Session: https://claude.ai/code/session_01R8hSsKTB3W3e61wD6kvzur

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-04 00:38:39 +02:00
Claude
93919ad3c2
Merge the updated legacy cleanup (#109, with main) into the media integration branch
The artifact pane from main loads and previews files through the streamed
artifact route like the file card, so its hook takes only the artifact id.
attach_file keeps media types and gains present, which marks image and
file blocks for the pane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 20:46:57 +00:00
Claude
e1cd9be8a6
Merge the updated legacy cleanup (#109, with main) into the Phase 2 integration branch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 20:40:03 +00:00
cynarAI
e28c3acf02
feat: let the pane beside a conversation follow the bot's work (#123)
The live screen opens wide beside the chat while the bot works on it or asks
for a takeover, a file the bot presents as its result opens next to the
conversation, and a question that scrolled away gets a "Needs you" pill back
to it. Shared core rules derive the focus from signals clients already have
(pointer events, takeover requests, a new `present` flag on attach_file,
pending asks), move the pane only on focus edges, never replace a pane the
user opened, and learn per bot whether the screen opens on its own. Pane
widths persist per device; mobile shows a pill that opens the computer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7VSRHHzyt2KwoCkUkZ2Gc
2026-10-03 22:23:46 +02:00
Claude
d143054966
Merge the updated legacy cleanup (#109, with main) into the media integration branch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 18:31:09 +00:00
Claude
09ed6c2136
Merge the updated legacy cleanup (#109, with main) into the Phase 2 integration branch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 18:23:22 +00:00
cynarAI
49431def83
feat: show the bot's live cursor and make the in-app browser work (#113)
Bots now announce every click and typing step before it runs, and the
web app draws a cursor in the bot's colour that glides to that point
over the live computer screen, ripples on clicks and shows typing.

- contracts: ComputerPointerPayload on the existing thread.computer event
- executor: computer_act and browser_act announce pointer steps
  (best effort); the page-browser helper reports element screen boxes,
  which are stripped before results reach the model
- web: agentPointerFeed + AgentCursor over the side-panel preview and
  the full-window screen, mapped through the noVNC letterbox
- in-app browser: native WebContentsView on desktop (own session, http(s)
  only, clamped bounds, back/forward, reload, DevTools, load errors) and
  a sandboxed iframe on the web; replaces the dead panel, the fake
  50%/40% cursors and the unimplemented devServer bridge


Claude-Session: https://claude.ai/code/session_01WiGooXtsKrZQmdszepRrNi

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-03 19:08:48 +02:00
Claude
d3689b88fc
feat(onboarding): show the onboarding cards and check-in names in the user's language
The onboarding cards, the closing line and the coach routine names were English server text
for every user. The server keeps the state and an English fallback; the web now words them
through Lingui, so there is one translation source.

- Choice cards: the web says the question, the option labels and the placeholder from the
  step and option ids, and keeps the server text for anything it does not know (quiet-hour
  ranges are already written in the user's locale). Another bot's focus card is recognized
  by its option ids. Mobile and agent history keep the server text.
- The "Got it. … are a good place to start." line is gone: the app cards speak for
  themselves, and with no cards nothing is said.
- The closing line becomes an intake_note block (note "done", nextCheckInAt, English text).
  The web words it with the first check-in in the user's clock; mobile blockText and
  blocksToAgentHistoryText read its text through their generic fallback.
- Routines carry coachKey. The routine list and editor show a check-in under its label in
  the UI language until the user renames it; saving an untouched label keeps the stored
  name. The English names move to contracts (COACH_ROUTINE_NAMES); prompts stay English.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:51:59 +00:00
Claude
05196fd20e
Merge media cards that work from any of the user's spaces
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:46:20 +00:00
Claude
14e1af6649
fix(media): act on media cards in their own space, not the selected one
The main conversation lives in the home space, but the user can have another
space selected while it is on screen, and the card's actions sent that space:
media.jobs.start resolved the conversation in the selected space and failed,
and media.jobs.get/cancel only found jobs of the selected space.

The server now takes the space from the card itself. start finds the card's
message in one of the actor's own threads (the conversation named by botId or
groupId), requires the actor to still be a member of that thread's space, and
resolves the conversation and the source artifacts there. get and cancel find
the actor's job by id and require membership of the job's space.
media.capabilities takes an optional job id and answers for that job's space;
the card asks through its job and shares the answer per conversation. Every
other case is a 404, as before.

A Postgres test runs a card in the home-space main conversation while another
space is selected (start, get, capabilities, cancel) and checks that another
user and a user who left the card's space get 404 on every procedure.

This also completes the media settings section committed earlier as work in
progress (01eea83); its code was final, only that commit message said
otherwise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:40:50 +00:00
Claude
6a450bb694
fix(media): use snake_case keys in every media tool input
The model saw edit_video and export_video with artifact_id next to
generate_image, generate_video, edit_image and transcribe with artifactId,
sourceArtifactId and negativePrompt. Mixed conventions in one tool list invite
wrong calls, so all media inputs now use snake_case like continue_after and the
other builtin tools: artifact_id, source_artifact_id, negative_prompt,
duration_seconds. Handlers, artifact checks, the media card's transcribe
action and the tests follow. Nothing has shipped, so stored job inputs need no
migration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:39:36 +00:00
Claude
46349acbd1
Merge transcription, subtitles, the Studio specialist and the media card
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:29:36 +00:00
Claude
e72b169ca4
Merge the conversational onboarding intake
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:26:11 +00:00
Claude
8d6ed7952f
Merge the coach: goals, habits, check-ins, rhythm and the Heute page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:25:28 +00:00
Claude
f9ffa5a961
feat(media): title transcription cards with what they transcribe
A card that only says "Transcript" does not tell two transcriptions apart,
and it is English whatever the user's language.

- transcribe takes an optional title like the video operations, so a bot
  names the card in the user's language; "Transcript" stays the default.
- Transcribe on a media card passes the source file name as the title.
- The E2E seeds the card through bootstrap as well as threads/get, since the
  bot thread's first snapshot arrives with bootstrap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:18:56 +00:00
Claude
4fbd087ec8
Merge branch 'claude/phase2-coach' into p2/a1-onboarding
Brings in profile editing, the cross-space inbox and consent-first discovery. The wizard
keeps both changes: no preferences step, and the new scan step without the main bot lookup.
The harness lists both new onboarding Postgres suites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:13:16 +00:00
Claude
245efc88b1
Merge web push, quiet hours and nudges
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:12:33 +00:00
Claude
31f4c2531d
fix(media): let bots title image edits in the user's language
Edit cards showed English words such as "crop · flip · extend" to every user.
edit_image now takes an optional title written by the bot in the user's
language, used for the card and the file name like edit_video. Without one,
the card falls back to the repaint prompt or to language-neutral tokens only:
sizes, ratios, degrees, arrows and format names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:11:27 +00:00
Claude
a02cbb5374
Merge claude/phase3-media into p3/b5-transcribe-studio
Brings in ffmpeg (probing, extractAudio) and the video operations. The
transcribe registration now gets the ffmpeg audio tools, so video is
transcribed through extractAudio with the demuxer forced from the MIME type,
and long recordings are converted and uploaded in ranges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:09:45 +00:00
Claude
e66ce42d73
feat(web): start exports, transcriptions and cancels from the media card
The card's follow-ups now reach the media job RPCs, and it only offers what
the space can run.

- media.capabilities answers which operations the actor's space can run
  (booleans only). The card asks once per minute per space, and only when it
  shows finished video or audio, so other cards cost nothing.
- Export and Transcribe call media.jobs.start with the card's message, so the
  same bot posts the new card; Cancel calls media.jobs.cancel. A refused
  start shows the server's message under the card.
- New files use top-level import type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:00:34 +00:00
Claude
469e6ec5d7
Merge consent-first discovery of projects and connected apps
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:00:25 +00:00
Claude
2c91a80f69
feat(onboarding): ask before discovering projects and let the user choose
The onboarding scan started on its own, discarded what it found, claimed
to read email and calendars, simulated progress for everyone who could
not scan, and its project creation always failed (wrong Space fields and
a space lookup by title across tenants).

Discovery now asks first and creates nothing without a choice:

- The scan step appears only for the deployment owner when
  ONBOARDING_SCAN_HOME is set; everyone else skips it. The simulated
  scan is gone.
- It shows the folder and asks. Only directory listings are read: git
  projects up to four levels deep, their languages and a few tools
  (Docker, GitHub Actions, Terraform). Symlinks are never followed and
  each chosen folder is resolved again before use, so nothing outside
  the folder is reached. Results stay with the account that scanned.
- Found projects are a checklist (none preselected); found languages and
  tools are facts (preselected). "Add" creates one project space per
  chosen project with createSpaceForMember and wireProjectWorkspace in
  the user's own organization, reusing the user's own space already
  linked to that folder, and saves the chosen facts as onboarding facts
  under the profile lock and fact limit.
- When a connection becomes connected (direct connect, authorization
  round trip, or catalog reconciliation), "Uses <App>" is remembered as
  a tools fact from the connector. Revoking keeps it.

The E2E harness now points ONBOARDING_SCAN_HOME at a fixture folder and
starts without a deployment owner, so owner-only onboarding steps appear
only in specs that take the seat through /__e2e/deployment-owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:58:39 +00:00
Claude
4e545c20f4
Merge the media integration branch into video editing again
Picks up ComfyUI generation, media tool dispatch and the streaming runner.
The video tools now share the image tools' schema helper, and both sets of
operations join the runtime's operation list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:57:18 +00:00
Claude
114103579f
Merge claude/phase3-media into p3/b5-transcribe-studio
Brings in the media job RPCs, generic media tool dispatch and the image
operations; transcribe joins the operation list and the tool catalog next to
them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:54:34 +00:00
Claude
faab1256c9
Merge media job RPCs, tool dispatch and the streaming runner
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:50:39 +00:00
Claude
2796965256
feat(media): media tools, media RPCs and owner-only media settings API
Bots and users need to start and follow media jobs, and the deployment owner
needs to point the deployment at their GPU and transcription servers.

- Media tools are named like their operations. A run sees one only when the
  space can run that operation (MediaRuntime.capabilities); the per-bot
  allowlist still applies. applyTool hands every media tool to one
  dispatcher (media-tools.ts) that splits off continue_after, starts the job
  under the tool call's effect key (a replay returns the same job) and returns
  at once; refusals (invalid input, unknown artifact, unavailable) go back to
  the model as tool errors.
- media.jobs.start starts an export, transcription or image edit from a card:
  the conversation is resolved like every thread procedure, the card's bot
  (or the group's lead) posts the new card, and the runtime refuses source
  artifacts outside the actor's space. media.jobs.get and media.jobs.cancel
  only see the actor's own jobs.
- media.settings.get/update are deployment-owner only, never return keys and
  answer invalid URLs with a 400 that says what is wrong.
- createApp accepts media operations as a test seam.
- Tests: a Postgres suite over the RPCs (owner-only settings, encryption,
  key retention per server, URL policy, start/get/cancel isolation between
  users, card jobs end to end) and unit tests for tool exposure and dispatch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:47:44 +00:00
Claude
44702e3770
feat(media): register video operations with the media runtime
edit_video and export_video now run as media jobs: both registrations join
the shared operation list the API and the worker build their runtime from,
name every artifact they read (source, appended clips, subtitle files) so
starting a job refuses artifacts the user cannot read, and their tool
schemas use the runtime's continue_after helper.

Container deployments get ffmpeg, ffprobe and the subtitle font in the
API/worker image. Tests cover the handlers against a fake job context:
cuts, blurred 9:16 reframes with subtitles, appended silent clips, audio
extraction, single frames, batch exports with posters and progress,
rotated phone video, clear input errors and cancellation. They skip where
ffmpeg is not installed; the planner and parser tests always run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:45:50 +00:00
Claude
dab9398c25
Merge memory append, profile editing and the cross-space inbox
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:43:12 +00:00
Claude
4217e4b4af
feat(media): generate images and video on ComfyUI and edit images
Bots can now make images and short clips on the user's own GPU server and
rework images locally, through the media job runtime.

- generate_image: prompt, aspect preset or exact size, up to 4 images, optional
  source image for image-to-image. Sizes target what the default models are
  tuned for, and sources are fitted to that size before upload, so large
  photos cannot exhaust GPU memory.
- generate_video: prompt, aspect, duration up to 10 s, optional start frame.
- edit_image: crop, rotate, flip, resize to a size or format (crop, pad or
  blurred background), extend, convert, in that order, with sharp; an optional
  prompt repaints the result on ComfyUI. Edits keep the source as parent.
- Registrations declare the input artifacts so jobs refuse ones the user
  cannot read, and generation declares a fixed GPU memory floor (6 GB images,
  12 GB video) that a custom workflow cannot lower.
- Tool definitions reuse the operation input schemas plus continue_after.
- Saving media settings rejects custom workflows the provider could not fill.
- Tests: sharp edits on generated fixtures, handlers against the ComfyUI
  emulator, and a PostgreSQL suite running the operations through the media
  runtime (card outputs, artifact versions, foreign artifacts, GPU wait).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:42:35 +00:00
Claude
fad25678ac
Merge the media integration branch into the ComfyUI workstream
Brings in artifact streaming and the media job runtime, settings and tool
helpers that the ComfyUI operations register with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:35:47 +00:00
Claude
d8c324a7af
Merge the media integration branch into video editing
Brings in artifact streaming and the media job runtime that the video
operations register with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:35:46 +00:00
Claude
1722ac1a0f
Merge claude/phase3-media into p3/b5-transcribe-studio
Brings in streamed artifacts with artifactUrl (replacing the local stand-in)
and the media runtime, settings and operation registry the transcribe
operation plugs into.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:35:36 +00:00
Claude
485090c391
feat(media): edit and export video with ffmpeg
Bots can now cut, reframe and export videos for every common format without
a hosted service: ffmpeg and ffprobe from PATH do the work on the server.

- ffmpeg wrapper: PATH detection, ffprobe JSON probing (rotation, pixel
  aspect, frame rate, audio, packet scan when the container has no
  duration), runs with -progress parsing, abort and stall kills, an output
  size cap, partial-output cleanup and short errors without temp paths.
  Inputs go through the file protocol with the demuxer forced from the MIME
  type, so a crafted upload cannot be probed as a playlist that reads other
  files or URLs. extractAudio (16 kHz mono WAV) and renderPoster for
  transcription and media cards.
- A pure planner turns edit steps (trim, keep, concat, reframe, speed,
  volume, mute, subtitles, extract_audio, poster) into one filter_complex
  pass: leading cuts seek the input, appended clips are normalized to the
  frame, fps and 48 kHz stereo (silence where a clip has none), reframes
  crop, pad or blur to the presets, speed chains atempo, subtitles burn in
  from generated file names with a readable style that stays clear of the
  controls on 9:16 frames. H.264/AAC MP4 with yuv420p and faststart;
  location metadata is stripped.
- edit_video and export_video operations and tool definitions; the inputs
  are shared contracts so the media card can start exports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:35:34 +00:00
Claude
5e0b09d9c1
feat(media): transcription input, chunked transcripts and the Studio trigger
Groundwork for the transcribe operation and for creating Studio when it is
first needed.

- Contracts: TranscribeInputSchema (artifact id, optional language code).
- transcribeChunks uploads audio chunks in order and stitches them into one
  transcript on the original timeline; transcriptionChunkSeconds sizes
  16 kHz mono WAV chunks below the server's upload limit; transcriptSummary
  hands the bot the transcript, cut at a word.
- ensureStudioWhenMediaConfigured creates Studio once a media server
  (generation or transcription) is configured. Local edits alone do not
  count, because ffmpeg and image tools are present on most installs and a
  specialist nobody asked for would appear for everyone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:35:21 +00:00
Claude
c60c693d13
feat(media): media job runtime, media queue and media settings
Media operations (generation, edits, transcription) need a runner that works
outside the agent turn, keeps a card in the conversation current and never
lets two processes put work on one GPU. Operations land in later commits; this
is the runtime they register with.

- Background job `media.run { jobId }` on a named `media` queue. Jobs carry an
  optional queue: the Graphile publisher passes it as queueName and the
  in-memory driver runs each queue one job at a time, in order.
- `createMediaRuntime` implements MediaRuntime (capabilities, start, get,
  cancel) over a list of `MediaOperationRegistration`s and runs jobs:
  start validates the input, creates the job and posts the bot's media card in
  one transaction (idempotent per tool call through an operation key), then
  enqueues it. The runner claims a fenced lease (an advisory lock keeps one
  live lease at a time, so the GPU stays single even when the queue is
  bypassed), renews it while polling for cancellation, writes progress and
  outputs into the card, defers with backoff when the GPU is short of memory
  or the operation asks to wait, bounds deferrals, crash restarts and run
  time, always cleans the job's temp directory and always ends the card in a
  terminal state. With continue_after the starting bot is woken in the same
  transaction that ends the job (new run trigger `media`).
- The reconciler re-enqueues jobs whose enqueue was lost, whose runner died
  or whose wait is over; a job stuck behind a dead worker's queue lock runs
  outside the queue.
- Media backends are deployment-wide settings stored encrypted as the
  `media` IntegrationProviderConfig row: `loadMediaSettings` and
  `saveMediaSettings` with the same address policy as OpenAI-compatible
  servers (no metadata hosts, public hosts only when allowed, HTTPS when a key
  is sent), a saved key never follows the settings to another server, and the
  view never returns keys. Contract in contracts/media-settings.ts.
- `createMediaOperations` is the one list both the API and the worker build
  their runtime from (empty until the operations land), and media-tools.ts has
  the shared `continue_after` schema helper for the tool definitions.
- Migration: lease, deferral, operation key and continueAfter columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:31:53 +00:00
Claude
3db0f0cfbd
feat(onboarding): save goal, check-in rhythm, quiet hours and push from the cards
With the coach and notification modules merged, the onboarding answers now take effect:

- the goal card creates a goal (source onboarding) through createGoal;
- the rhythm card sets the main bot's check-in routines through setCoachRhythm, and the
  closing line names the first check-in from the routines that were saved;
- the quiet-hours card updates the notification settings;
- the choice card tells the server whether this browser can still turn push on, so the
  notifications card is offered only there, and "Turn on" subscribes before answering.

The thread lock becomes NO KEY UPDATE so a rhythm routine, which references the thread, can
be written while the answer holds the lock. The goal answer is capped at the coach title
length. A Postgres suite walks the questions end to end: answers, skips, a dismissed card, a
stale and a doubled answer, another bot's focus card, and another user's access.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:28:59 +00:00
Claude
1a3829031f
feat(coach): coach RPCs for goals, habits, check-ins, today and rhythm
The web app needs to show and change the user's coach data, and the
check-in times need a way to change after onboarding.

- coach.today, coach.goals.*, coach.habits.*, coach.checkIns.* and
  coach.rhythm.get/set, owner-only and not space-scoped; another user's
  ids read as not found, invalid input as a bad request.
- coach-rhythm reuses the shared clock-time helpers.
- Tests: an API suite covering ownership and validation for every
  procedure, the coach tools driven through the main bot (and refused
  for a specialist), and a morning check-in routine that fires in the
  main conversation and writes today's brief; the unauthenticated
  authorization sweep lists the new procedures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:27:50 +00:00
Claude
e13dbe5ffa
feat(inbox): list and answer items from all of the user's spaces
The inbox in the main conversation only showed asks, approvals, takeovers and
unread threads of the current space. The main conversation lives in the home
space, so anything a bot needed in a project space never reached it.

- inbox.list now covers every space the user belongs to (except spaces being
  deleted), still only the user's own bots, runs and threads. Items from
  another space carry that space's id and name.
- The web inbox shows the space name next to the bot for those items, answers
  them with that space's header so the existing thread procedures check
  membership and ownership for that space, and opens their thread in it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:27:39 +00:00
Claude
fa0986bd2d
feat(media): add the ComfyUI provider, default workflows and emulator
Image and video generation run on the user's own GPU server. ComfyUI is the
common self-hosted backend, so this adds a provider for its HTTP API that the
generation operations can call.

- Core: API-format workflow templates with a small placeholder format
  ({{prompt}}, {{width}}, {{seed}}, ...) that is validated and filled without
  evaluating anything. Defaults: text-to-image and image-to-image on core nodes
  for any SD-family checkpoint, and text/image-to-video on ComfyUI's core
  LTX-Video nodes; a README lists the placeholders and model files.
- Adapters: createComfyUiProvider submits a workflow, polls its history,
  uploads input images and downloads outputs through the address-checked fetch
  used for other self-hosted model servers, with timeouts, capped reads and
  readable errors. A custom workflow from settings replaces the default it
  matches. Cancelling dequeues or interrupts only its own prompt. GPU status
  counts memory ComfyUI can free for itself as available.
- Contracts: input schemas for generate_image, generate_video and edit_image.
- Tests: an in-process ComfyUI emulator, a provider-neutral generation
  conformance suite and provider tests, all offline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:26:59 +00:00
Claude
ebcbf4c05f
Merge the Phase 2 integration branch for the shared clock-time contract
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:19:22 +00:00
Claude
a094c23b47
feat(notifications): fan push out to every device, honour quiet hours, add notify_user
Run notifications reached at most one phone and nothing could hold them back at night.

- PushNotificationProvider sends each notification to all of the user's registered devices
  (web push and Expo), skips everything during quiet hours in the profile time zone, drops
  devices the push service reports gone (404/410, DeviceNotRegistered), records lastUsedAt and
  links each push to its conversation.
- Expo tokens move from DATA_DIR/push-tokens files into push_subscriptions: the API imports and
  deletes the legacy files at start; notifications.registerPush/unregisterPush keep their
  contract for the mobile app (tokens must look like Expo tokens).
- NotificationMessage gains the nudge kind, and the main bot gets notify_user: a short push to
  the main conversation that respects the nudge switch, quiet hours and a cap of three per day.
  It is withheld from shared messaging channels and from runs answering another owner's agent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:19:12 +00:00
Claude
2c88d09eea
Merge branch 'claude/phase2-coach' into p2/a1-onboarding
Brings in the coach and notification interfaces the onboarding goal, rhythm, quiet-hours
and notification steps write to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:17:20 +00:00
Claude
f4e02eb248
feat(onboarding): ask the onboarding questions in the main conversation
The wizard asked for the account name again and a "Tell us about yourself" form, and the
focus answer in the main conversation was never saved. The main bot now gets to know the
user in the main conversation instead, one card after each answer.

- UserOnboarding keeps the pending step, the focus and when onboarding finished.
- onboarding-intake.ts declares the steps (focus, goal, rhythm, quiet hours, notifications)
  as cards plus what their answers change; onboarding.ts commits an answer, the focus app
  suggestions and the next card or the closing line in one transaction.
- The choice block gains a step, an optional text input and the typed answer; choose takes
  the step, the text and whether the device can still turn push on.
- The focus answer becomes a goals fact; the goal, rhythm and quiet-hours effects and the
  push seam are placeholders until the coach and notification modules are merged.
- The wizard drops the preferences step and fills time zone, locale and language from the
  browser; the main bot's prompt notes open onboarding cards; agent history shows a card's
  question and answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:17:06 +00:00
Claude
74503ad663
refactor(contracts): share one clock-time schema
The coach rhythm and the quiet hours both parse "HH:MM" wall-clock times
and each defined its own ClockTimeSchema, which the contracts index cannot
export twice. Both now import it from one module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:16:27 +00:00
Claude
6ad9be6c1e
Merge notification interfaces into the Phase 2 integration branch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:14:07 +00:00
Claude
a163447461
feat(notifications): add push subscriptions, notification settings and web push interfaces
Push could only reach one mobile device per user (a token file), quiet hours did not exist,
and the web app had no service worker, so a closed tab never heard about finished work.

This lands the shared interfaces first so onboarding can build on them:
- push_subscriptions (web push endpoints and Expo tokens, one row per device) and
  user_notification_settings (quiet hours as minutes in the profile time zone, nudges, a
  per-day nudge counter), with CHECK constraints.
- NotificationSettingsSchema and web push contracts, notifications.settings.get/update and
  notifications.webPush.config/subscribe/unsubscribe (owner-scoped, https endpoints only).
- getNotificationSettings/updateNotificationSettings plus the policy and nudge-cap helpers.
- VAPID keys from VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY or generated once and stored encrypted,
  and an SSRF-guarded web push sender built on the web-push package.
- The web app links its manifest, registers a push-only service worker where supported (secure
  context, not the desktop app) and exposes webPushSupported/webPushEnabled/enableWebPush/
  disableWebPush.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:13:22 +00:00