* Fix red main: typecheck, lint, i18n and stale unit tests
Direct pushes to main left typecheck, lint and 16 unit tests red.
- Typecheck: AskCard test uses the current ask block shape, the sandbox
fault test calls the two-argument desktop writeFile, removing a queued
message notifies with the thread's latest event seq, and the replay
fixture only reads ref from steps that carry one.
- Lint: format and import order.
- i18n: transcript status labels were German source strings; they are
English now with German translations, catalogs re-extracted, and the
mobile "Failed to remove message" string is translated.
- Tests follow intended behavior changes: the time instruction now
closes the system prompt, short chat turns start at low reasoning,
owner-owned read-only files (git objects) pass the home check, and the
desktop computer may read and write host files outside its home. The
old desktop write-containment suite is replaced by a host file test.
- The composer task pill test mocks the Lingui macros like other tests.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnT7eVHuaCzZKY5CnYzpr1
* Bump Expo patch releases so expo install --check passes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnT7eVHuaCzZKY5CnYzpr1
* Bring web E2E back in line with the new side panel
The side panel lost its computer tab, which left the full computer window
(take control, teach, release) unreachable from the web app. The browser
view now offers an "Open in full window" button, and opening the panel no
longer boots a stopped computer.
Also: the slash picker scrolls instead of growing under the header, user
skills come first in it, the agent overview hides an empty routines block,
the hover time hides again with collapsed details, and the light destructive
token meets contrast on the sidebar. E2E specs follow the new panel layout,
and the cursor and hover tests no longer race the UI.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnT7eVHuaCzZKY5CnYzpr1
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Fix red main: mobile group kind, i18n catalogs, project picker check
Main CI failed on typecheck, unit tests and web E2E after the last two
direct pushes. MobileGroup now carries the optional chat kind the mobile
screens read, the Lingui catalogs are re-extracted with German strings
for the new voice texts, the project overview plus icon meets the UI
scale guard, and the sidebar E2E expects the renamed project lead.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R8L4XgqjAemFR2remB5hVY
* Format files merged from main
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
* Align E2E with the canvas close button and per-chat drafts
The in-app browser now closes from the side panel header, and each chat
keeps its own unsent draft. The group chat test waits for the settings
save to land before switching chats, so the switch no longer races the
panel closing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
---------
Co-authored-by: Claude <noreply@anthropic.com>
A supervisor that runs as a standalone container kept the image it was
started with, so broker fixes never reached it. The deploy now rebuilds
that image when its sources change and recreates the container with the
same network, restart policy, mounts and environment, restoring the old
one if the new one stays unhealthy. The broker socket moves into the data
directory and is shared from containerized supervisors too.
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
Co-authored-by: Claude <noreply@anthropic.com>
Agents kept configuring git with RAKAZO_GITHUB_BROKER_RUN_TOKEN as a
password because the system prompt listed it among "managed credentials"
to use in shell commands. GitHub rejects it, and the agent then asks the
user for a personal access token.
- List only the user's own agent secrets in that instruction; the broker
variables stay in the environment for the git shim.
- Make /usr/bin/git the shim as well, so git called by absolute path
still goes through the broker; the real binary moves out of PATH.
- Prompt no longer names the broker variable.
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
Co-authored-by: Claude <noreply@anthropic.com>
The computer's git and gh shims passed a literal "--" into the JSON argv
(python -c keeps it in sys.argv), so every brokered clone arrived as
"git -- clone" and the broker refused it. Agents then fell back to real
git with the broker run token as a password, which GitHub rejects.
- Drop the stray "--" from both shim payloads.
- Route git through the broker even behind -c/-C/--no-pager options.
- Share one broker client that falls back to the default gateway for
computers created before host.docker.internal was mapped, and says
plainly when the broker is unreachable or refuses a request.
- Broker normalizes GitHub SSH remotes and pasted credentials to plain
HTTPS and points a rejected stored token at reconnecting GitHub.
- Prompt says plain git clone works and the run token is not a GitHub token.
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
Co-authored-by: Claude <noreply@anthropic.com>
* Let computer git reach GitHub through the broker with working credentials
The broker URL handed to computers pointed at the supervisor's loopback
address, which a container cannot reach, and host git ignored the
integration token for HTTPS remotes. Computers now get the host through
host.docker.internal, host git receives the token via a one-off
credential helper, and clone paths and the working directory map from
the computer onto the host workspace.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Serve the GitHub broker to computers on a Linux host-run supervisor
A loopback-bound supervisor is unreachable from containers on Linux, so
it also serves the broker routes alone on the default bridge gateway,
which only containers on the same host can reach.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Find connected-app tools from queries written in words
Catalog search matched the query only as one substring, so "search
repositories" or "get file" found nothing for search_repositories and
get_file_contents and the agent concluded GitHub had no tools. Underscores,
hyphens and spaces now compare equal, a query matches when every word starts
a word of the tool, and a miss returns the source's tool names.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Bump Expo SDK 57 packages to the patch versions expo install expects
expo install --check now expects @expo/ui 57.0.25, expo 57.0.30 and
expo-router 57.0.28, which turns the mobile typecheck red on every branch.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
* media: check ffmpeg filter support before subtitle operations
* sandbox: handle synchronous spawn errors safely in desktop sandbox
* Voice-first streaming, non-blocking follow-ups, quiet routines and chat UX
Why: questions blocked the chat, follow-up messages had no acknowledgement, message actions sat beside the bubble, and voice turn-taking added latency.
What: waiting runs accept steering messages and the bot marks each claimed follow-up as seen; unclaimed follow-ups show as queued. Reaction, copy and reply controls sit under each message. Streaming voice (VAD endpointing, barge-in that stops the run, clause-based TTS), an optimistic input queue, procedural earcons and delta-based quiet routines were added. Onboarding can be skipped straight to the main conversation. Dual-surface layout and project pickers are included. Status colors use semantic tokens.
Tested: unit suites for core, adapters, api and web pass, typecheck and Biome are clean. Browser end-to-end tests were not run locally.
* Update translation catalogs
Re-extracted catalogs for the new and moved strings; the catalog sync test passes.
* Add project plan and test readiness notes
Notes from the milestone work: feature inventory, test infrastructure and test readiness. No code changes.
* fix(ci): align scale guards, space deletion, computer toggle, and mobile expo deps
* fix(ci): restore space deletion guard, browser toggle, and run-steps contracts
* fix(ci): restore model resolution errors, group stage, and onboarding flow
* fix(ci): restore in-app-browser close button, disable stop button while sending, and sync lingui catalogs
* fix(ci): address web e2e failures for bot-crud, choice cards, message quote, and voice onboarding
* test(web): wait for avatar and rig visibility in oo-avatar motion spec
* test(web): wait for persisted message IDs in message-hover-actions pagination spec
* feat: recruit library agents into project conversations and scan project folders
* test(web): expect repo description in GithubRepoPicker onChange
* Onboarding: always drop the local voice when the voice step is skipped
Skipping waited only for the latest voice load and disconnected only
when the client saw the connect succeed. An earlier or client-failed
connect could still persist the local credential, so a user who
skipped voice was left with one. Wait for every load and always
disconnect.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
* Shell: hide the optimistic copy once the transcript shows the send
A message sent while the bot works streamed into the transcript before
its send request resolved, so the same text showed twice until the
optimistic bubble was removed. Drop an optimistic message as soon as a
persisted user message with the same text appears.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
---------
Co-authored-by: Claude <noreply@anthropic.com>
* media: check ffmpeg filter support before subtitle operations
* sandbox: handle synchronous spawn errors safely in desktop sandbox
* Voice-first streaming, non-blocking follow-ups, quiet routines and chat UX
Why: questions blocked the chat, follow-up messages had no acknowledgement, message actions sat beside the bubble, and voice turn-taking added latency.
What: waiting runs accept steering messages and the bot marks each claimed follow-up as seen; unclaimed follow-ups show as queued. Reaction, copy and reply controls sit under each message. Streaming voice (VAD endpointing, barge-in that stops the run, clause-based TTS), an optimistic input queue, procedural earcons and delta-based quiet routines were added. Onboarding can be skipped straight to the main conversation. Dual-surface layout and project pickers are included. Status colors use semantic tokens.
Tested: unit suites for core, adapters, api and web pass, typecheck and Biome are clean. Browser end-to-end tests were not run locally.
* Update translation catalogs
Re-extracted catalogs for the new and moved strings; the catalog sync test passes.
* Add project plan and test readiness notes
Notes from the milestone work: feature inventory, test infrastructure and test readiness. No code changes.
* fix(ci): align scale guards, space deletion, computer toggle, and mobile expo deps
* fix(ci): restore space deletion guard, browser toggle, and run-steps contracts
* fix(ci): restore model resolution errors, group stage, and onboarding flow
* fix(ci): restore in-app-browser close button, disable stop button while sending, and sync lingui catalogs
* fix(ci): address web e2e failures for bot-crud, choice cards, message quote, and voice onboarding
* test(web): wait for avatar and rig visibility in oo-avatar motion spec
* test(web): wait for persisted message IDs in message-hover-actions pagination spec
* Onboarding: always drop the local voice when the voice step is skipped
Skipping waited only for the latest voice load and disconnected only
when the client saw the connect succeed. An earlier or client-failed
connect could still persist the local credential, so a user who
skipped voice was left with one. Wait for every load and always
disconnect.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
* Shell: hide the optimistic copy once the transcript shows the send
A message sent while the bot works streamed into the transcript before
its send request resolved, so the same text showed twice until the
optimistic bubble was removed. Drop an optimistic message as soon as a
persisted user message with the same text appears.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Broker GitHub integration token for computer git/gh
Use the Integrations GitHub MCP secret for supervisor broker sessions
so home and project computers can clone private repos without PAT prompts.
Add rakazo git/gh shims, push policy, deploy image rebuild, and tests.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* ci: retrigger product checks after runner timeout
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* ci: run product change detection on GitHub-hosted runners
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* style: format deployment-github-token
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* ci: self-hosted change detection with timeout and fail-open gate
Keep Detect product changes on evoo-ci with a 10-minute cap, continue-on-error
filtering, and a fail-open product flag when detection fails. Downstream jobs
and Product CI treat a failed or timed-out changes job as full product impact.
Add job timeouts on remaining self-hosted ci jobs.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* fix: continueRun tests and broker home path guard
- Skip broker workspace resolution when computer homeKey is absent
- Add shared continue-run Prisma/home/secretStore test stubs
- Update computer PATH expectation and evo-deploy command sequence
- Remove unused executor imports flagged by Biome
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* style: Biome format and import order for broker CI
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* fix: compose YAML, typecheck for broker stubs and GitHub hint
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* ci: retrigger Product CI after compose and typecheck fixes
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* ci: retry Product CI queue
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* ci: give self-hosted Web E2E a 45m job timeout
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* ci: extend Web E2E job timeout to 60m on self-hosted runners
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* fix: keep github-broker-token off the core browser barrel
Export broker HMAC helpers only from @rakazo/core/node/github-broker-token so
Vite does not pull node:crypto into the web client and break Playwright signup.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Catalog execute calls now merge parameters flattened next to an empty
arguments object and unwrap parameters nested under keys like parameters
or args, unless the tool itself takes that key. A failed validation lists
the required and received keys so the agent can correct its call, and the
prompt shows the expected call shape and says connected apps need no token.
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
Co-authored-by: Claude <noreply@anthropic.com>
Catalog execute calls now accept arguments sent as a JSON string or flattened
next to the id, the computer image ships git and curl, and the evo deploy
rebuilds that image when its sources change.
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
Co-authored-by: Claude <noreply@anthropic.com>
A live check showed the agent answering a business-idea question by creating a schedule that would run the analysis later. The schedule_create description now says never to use it for work the user wants now, however long.
A run of the real prompt against the local model showed reasoning written as chat text, internal tool names in a capabilities answer, and only a data fetch when asked to build a tool. The working principles now say that everything written is shown, to describe abilities in plain words, and that a request for a tool means building one.
The chart-links spec now waits for the chart or its error and reports browser errors, so the recurring 90-second timeout names its cause. The ZIP upload spec matches the file chip exactly, since the agent reply can quote the name.
Agents denied a connected GitHub and did not know they could install
skills: the prompt said "No plugins are connected yet." whenever only
installed tool sources existed, and the skill catalog never mentioned
skill_install. Connections and skills were also bound to the space they
were set up in, so a project never saw them.
- Name every connected app from the tools the run actually received,
including lazy catalogs and how to reach them.
- Tool sources and skills follow the user into every space; a skill in
the current space wins a name clash.
- Add shared working principles (do the work, verify before claiming,
keep it simple, build a missing tool as a tested script plus skill).
- Rewrite the skill catalog in plain words, mention skill_install, clip
long descriptions and count skills past the limit.
- Routine runs learn that nobody is waiting and history is not shown;
schedule prompts must be self-contained.
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
Co-authored-by: Claude <noreply@anthropic.com>
A new skill_install agent tool reads every SKILL.md behind a public github.com
repository, folder or file link and saves each as one of the user's own,
editable skills. Only GitHub's API and raw hosts are contacted; invalid files
and name clashes are skipped and reported.
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
Co-authored-by: Claude <noreply@anthropic.com>
* Add Second opinion and Founder builtin skills
Second opinion has a fresh reviewer approve the plan and inspect the build,
so whoever writes never approves their own work. Founder tests a business
idea with simulated roles and a crowd of 100 simulated buyers and ends in a
business plan card with a verdict.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Add Ship builtin skill for phased software work
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Connect GitHub from a token alone, without a vendor account
Integrations only reached GitHub through Composio or Pipedream, which a
self-hosted instance does not have. A built-in GitHub row now opens
GitHub's prefilled token form, and the pasted token connects GitHub's
own MCP server through the existing installed-connector path. Web and
mobile.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Sync web catalogs for the GitHub connect strings
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Allow ZIP uploads up to 5 GiB and stream them onto Docker computers
Repository archives could not be picked at all: application/zip was not an
allowed attachment type. ZIPs are now allowed with their own 5 GiB cap,
upload as a stream, and reach the bot's Docker computer through a new
streamed supervisor write that commits only after the body ended cleanly.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Use a disallowed type for the invalid-attachment check now that ZIP is allowed
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
Runs used GitHub's artifact storage and cache service, which ran out of
the monthly included quota and blocked publishing. Caches and job
handoffs now stay on the self-hosted runner host, the desktop release
hands builds over through its draft release, and Windows waits for a
self-hosted runner instead of a GitHub-hosted one. A test enforces it.
Claude-Session: https://claude.ai/code/session_01R8L4XgqjAemFR2remB5hVY
Co-authored-by: Claude <noreply@anthropic.com>
Each project is now a single sidebar row with its own icon and color that
opens the project's coordinator conversation. The whole row drags to
reorder. The row menu gains Customize for name, icon and color. Inside
the coordinator conversation, an overview lists the project's agent
threads that need the user, are running, or recently finished.
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
Co-authored-by: Claude <noreply@anthropic.com>
Creating a project whose name a member already uses (from the dialog, the
agent's create_space tool or the onboarding scan) now returns that project,
so the sidebar no longer lists two projects with one name. The conversation
picker lists the main agent first.
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
Co-authored-by: Claude <noreply@anthropic.com>
* Fold a run's step messages behind its latest message
Long tool runs posted every step as its own bubble, so the answer drowned in
narration and a summary written before saving a file appeared twice. Web and
mobile now fold a run's text-only step messages behind one "N steps" line
above the run's latest message. The thread snapshot also stops replaying live
narration that was already posted, which showed steps again after a refresh.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Keep a run's last step visible above a card
A card such as the peer-message receipt has no words of its own, so folding
the narration into it hid everything the bot said. Steps now fold only into
a written answer or the live row; before a card the last step stays visible.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Name a run's chat once when its group carries the bot's name
A project's main conversation is named after its lead bot, so Today
showed "Chief · Chief". Web and mobile now share one title helper that
drops the repeated name.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Drop the "1:1 with" line under a conversation's title
The stage header repeated what the chat already shows, such as
"1:1 with Chief" under a project's name. The title and the phase pill
remain.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Let the stage's translated thinking lines speak while a bot works
A running bot raised a "working" awareness event whose English line,
such as "Chief is working on your request.", replaced the translated
thinking lines and added an "In progress" strip next to the phase pill.
The strip now only shows collaboration.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Ask bots to describe their steps without naming tools
Long runs showed lines such as "I'm using browser_navigate" in the chat.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Announce the stage phase as status and keep following after a tap
The working strip used to be the only status region while a bot worked; the
stage's phase pill now carries that role. A tap at the latest message no
longer stops the transcript from following new messages.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Resume a waiting run after takeover even when its execution lease is gone
When resuming a run failed while it waited for the user, the run stayed
waiting_takeover but lost its execution lease. Taking over then bound no
run, so handing control back resumed nothing and the chat hung at the
computer. Takeover now binds the newest leaseless waiting run.
The stage also counted waiting runs as work and showed "working" while
the bot waited on the user; it now listens instead.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Tell every bot it is an agent in evOo, not the model behind it
Local models introduced themselves as the assistant product they were
trained as and even asked for that product's login. The identity block
now names evOo, and the fallback prompts say evOo instead of Rakazo.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Show runs waiting on the user in Today from every space
Today and its badge listed only runs of the selected space, while the
sidebar shows chats from all spaces. A chat waiting on the user in the
home space therefore showed "needs you" in the sidebar and "All quiet"
in Today. The activity list now spans every space the user belongs to,
carries each run's space, and opening a run switches to that space on
web and mobile.
The runs-list Postgres suite was not part of the integration run; it is
now, with a case for a waiting run in another space.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Keep a project's main conversation to its own asks
Every main conversation showed the inbox of all the user's spaces, so a
takeover waiting in the home chat sat above the composer of every
project. Only the home space's main conversation collects other spaces
now; a project shows its own items.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Also resume a waiting run that sits behind an older run's inactive lease
Inactive execution leases stay as fencing tombstones, so a run that lost
its lease can wait behind an expired lease of an earlier run. Takeover
now looks for the bot's waiting run whenever the lease is neither busy
nor bound to a waiting run. Journey 4f covers both the released and the
stale lease.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Translate the voice awareness labels and the computer card's state
The voice strip showed hardcoded English labels such as "In progress",
and the computer card showed its stored "Needs you" state untranslated
in German chats.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
Kokoro-FastAPI ships English, Spanish, French, Hindi, Italian, Japanese,
Portuguese and Chinese voices only. The German presets pointed at voice
ids no Kokoro server has, so German users either got English voices or
picked a voice that failed to speak.
The local voice now also lists the German voices whose Piper model is
installed (PIPER_BASE_URL) and speaks them through Piper's /synthesize
as WAV. Thorsten, the clearest model, is the German default. Mobile
plays WAV as well as MP3, and the E2E voice stub stands in for Piper.
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
Co-authored-by: Claude <noreply@anthropic.com>
* feat(web): Oo computer browser panel with cowork and local preview
Wire the in-app browser panel to the active Oo's live Chromium via the
computer screen stream, page-browser navigation, and interactive cowork
input with AgentCursor. User browser actions become system meta lines in
thread context; localhost preview remains as Lokale Vorschau.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* fix(i18n): sync Lingui catalogs for Oo browser panel
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Fix the Oo browser's lint and E2E failures
The shared-browser overlay is a focusable input surface, so it gets the
application role and a translated label. The local preview focuses the
address field again when no page is open, which the split out of
InAppBrowser had dropped. The Oo browser spec opens the panel by its exact
name, because the signed-up user's name also contains "Browser", and reads
the user action from the RPC body's json envelope.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
* Production readiness phase 1: journeys doc, onboarding persistence, teardown, chat UX
- Add docs/PRODUCTION-READINESS.md with journey matrix and defect backlog
- Persist onboarding draft in session storage across reload
- Localize onboarding discovery main-bot role; German Chief of Staff string
- Destroy team sandboxes and home folders when deleting an account
- Keep composer/transcript visible during queued runs; extend send watchdog
- Add Playwright specs: reload persistence, DE intake, queued progress, console guard
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Fix CI: sync Lingui catalogs, onboarding name draft, E2E stability
- Localize Chief of Staff in discovery shell; sync all locale PO files
- Propagate Oo name to onboarding draft while typing
- Adjust DE intake, queued-run, and console-error E2E specs
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Format chat-queued-progress E2E spec
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* fix(api): scope account-delete sandbox teardown to owned spaces
Only destroy team sandboxes and homes for spaces the deleting user
owns with no remaining members or bots. Guard home removal under
dataDir/homes. Add unit and postgres journey coverage.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* chore: apply biome formatting
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Align Oo avatar eye spacing with evoo-logo overlap ratio
Derive small-eye placement from evoo-logo.svg center spacing (~0.94 of
r_big + r_small) for the shared rig, EvooMascot brand lock, and face crops.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Format brand-lock spacing test for Biome
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Align mobile Expo packages for install --check in CI
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* ci: retrigger after flaky chart-links E2E
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Increase header and sidebar icon scale on web and mobile
Raise shared --icon-sm/md/lg tokens (~25%) for web shell chrome, widen
chat header action controls, and align Expo home/thread header glyphs
with 44pt tap targets.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* ci: retrigger checks after flaky evo-deploy test
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Pull updates from evOo instead of the upstream Rakazo repository
The in-app server updater, the image-based compose files and the desktop
auto-updater still pointed at elie222/rakazo, so updating an evOo install
would have pulled Rakazo. They now follow cynarAI/evOo-local, where the
publish workflow already pushes the images. The disabled Rakazo production
deploy job is gone from CI; evo deploys itself.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Call the product evOo everywhere people see its name
The desktop setup window and menu, account emails, the iOS app name and
permission prompts, sign-in headings, messages bots post into connected chat
apps, the web fetch user agent, issue templates and the marketing site still
said Rakazo, or EvOo. They now say evOo. The marketing site no longer calls
evOo an alternative to itself.
Internal identifiers (package scopes, env vars, bundle ids, the desktop
productName that keys its data folder and keychain entry) stay as they are.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Open conversation actions by holding a row on a phone
Pin, move to folder, mark unread, duplicate, archive and delete only opened
on right-click, which iOS Safari never fires. Holding a conversation or a
folder header now dispatches the same contextmenu event, so the existing menu
opens under the finger; Android's own long-press event is not doubled and the
lift does not also open the conversation.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Give small chrome icons a 44px touch area on phones
The browser and computer toggles, the activity bell, the create button and the
section plus buttons were 18 to 36 px targets. On touch screens each now gets
an invisible 44px hit area without changing how it looks; the create button
uses the same plus icon as the rest of the sidebar instead of a text glyph,
and the computer toggle gets an accessible name.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Add German to the mobile app
German speakers saw the phone app in English because the mobile UI only
knew English, Simplified Chinese, and Russian. The app now ships a German
catalog covering every mobile chrome string, follows a German device
language (de, de-DE, de-AT, de-CH, ...), and offers Deutsch in the Account
language picker.
Strings that also exist in the web German catalog reuse its wording so
web and phone say the same thing; the rest follow the same terms and the
informal "du".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Use the evOo sign-in strings in the German phone catalog
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Make account deletion work and give settings one section style
Deleting an account posted to the auth endpoint without a JSON content
type, so the server refused it and the button did nothing. The privacy
page now deletes through the auth client and returns to sign-in. It also
drops the claims it could not verify, the export button that only showed
an alert, and its own padding, and shows where the data and the computer
live. Notifications and media settings use the same SettingsSection.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Use status tokens, the shared switch and one German glossary
Voice stage dots, the onboarding progress bar and the Voice First button
used raw emerald, violet, cyan and rose; they now use foreground, success
and destructive like the rest of the app. The routine editor's hand-built
switch is the shared Switch. German says Unterhaltung for threads and
Space consistently on web and phone.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Expect the evOo setup copy in the desktop e2e specs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Translate the voice preview label in the German phone catalog
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
* Wait for the main thread URL without waiting for every resource
openMainBotThread already navigated with domcontentloaded; its URL check
then waited for the full load event, which can exceed 20 s on a busy
runner. The composer check that follows covers readiness.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Recommend Claude Opus 5.5 by bumping the Pi model catalog
The pinned pi-ai 0.85.1 had no Claude Opus 5.5, so the connect flow fell
back to Opus 5. pi-ai and pi-agent-core 0.87.1 add it, and the keyless
recommendation now preselects it.
The new catalog names router aliases "Qwen Max Latest (Qwen3.8 Max)";
those now read "(auto-updates)" like the other alias labels, so no
picker entry claims to be "latest".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
* Name HTTPS as the cause when dictation is unavailable, and translate it
Over plain HTTP on a LAN address, Safari hides the microphone and speech
recognition, so voice mode said the browser had no dictation, in English
inside a German UI. Dictation now checks for a secure context first and
says to open the address with https://. Its other errors go through
Lingui too, and Safari's blocked recognizer gets a clear message instead
of "Dictation failed: not-allowed".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
* Serve a fake Kokoro voice server in the E2E harness
Onboarding's voice step connects to the local Kokoro server on its
default port, so the shell E2E failed on any machine where nothing (or a
misbehaving server) listened there: the failed request shows up as a
browser console error. The harness now starts an offline stand-in and
points KOKORO_BASE_URL at it, so the suite no longer depends on the
runner host's own voice server.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
* Show only the recommended sign-in until a model is connected
With no model connected, "Connect model" opened the full model settings:
provider list, tiers and a model picker, even though the recommendation
was already preselected. Until something is connected, the Models panel
now shows just the preselected model with its sign-in and API key field;
"Other providers" reveals the full settings.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
* Give the second chart-links test the same cold-load allowance as the first
Each Playwright test runs in a fresh browser context, so the second test's
first navigation re-fetches the lazily imported Plot chunk just like the
first test's warm-up does. On a loaded CI runner that exceeded the default
20 s expect timeout.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
* Use the recommended model as the Anthropic deployment default
A deployment that sets only ANTHROPIC_API_KEY and PI_DEFAULT_PROVIDER=anthropic
now runs Claude Opus 5.5, the same model the connect flow preselects, instead
of an older default.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
* Run every space on the deployment's local models without a connect step
A deployment with PI_DEFAULT_PROVIDER=local and RAKAZO_LOCAL_MODELS set still
asked every user to connect a model, because only OpenRouter and Anthropic
counted as a deployment model. Configured local models now do, keylessly.
RAKAZO_LOCAL_TIERS assigns local models to the tiers the agent delegates to,
for spaces that have not mapped their own, and a RAKAZO_LOCAL_MODELS entry can
name its own server (id=url) when the models run behind different endpoints.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
* Hide model choices when the deployment routes models itself
When the server provides its own models, the system picks and routes
them per task, so Settings no longer offers Models and bot settings no
longer offer Model and Thinking, on web and mobile.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
* Wait for the workspace pane's width transition before measuring it
The pane animates its width over 300 ms, so a single boundingBox read
right after it opens can land mid-transition on a slow runner.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
* Allow chart re-renders inside the chart-links loops to take longer
Each navigation in the loops renders Observable Plot again. On a loaded
CI runner that exceeded the default expect timeout even after the
warm-up, so the per-iteration visibility checks get 30 s.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
---------
Co-authored-by: Claude <noreply@anthropic.com>
Move restart-mode prerequisites (systemd unit default, tmux, compose file,
script hook, EVO_SYSTEMD_SCOPE) ahead of git fetch/install/build/migrate.
Explicit systemd with no EVO_SYSTEMD_UNIT falls back to EVO_SYSTEMD_AUTO_UNIT.
die() after checkout triggers rollback; rollback marks in-progress to avoid loops.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* Export videos for named platforms and check each file against its limits
Exports only knew aspect ratios, so a cut could still be rejected or
degraded on upload: too long for the platform, over its size limit, or
far off its loudness. export_video now takes platforms (TikTok,
Instagram Reels and Feed, YouTube Shorts and YouTube, Facebook Reels,
LinkedIn, X, Pinterest, Snapchat Spotlight), up to six targets with the
presets. Each platform is rendered to its frame with the export's fit,
its frame rate cap, a bitrate cap that also keeps the file under the
size limit, and its loudness target in one loudnorm pass.
Every render is probed and measured afterwards. A file over the size
limit is encoded once more at a bitrate scaled from what it averaged,
and audio more than 2 LU off the target gets one linear loudness pass.
The result is stored on the output as a pass, warn or fail check with
language-neutral issue codes. A platform the video is too long or too
short for is not rendered: the card lists it with the reason, the
other targets still render, and the bot is told to trim first.
The limits come from the earlier profile research, with its sources,
review date and unverified flag; content, disclosure and caption rules
are left out because they need inference or a review flow. Preset
definitions move to their own module so the platform schemas can use
their aspect ratios without an import cycle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* Add remove_silence step to edit_video
Talking-head clips are mostly cut by hand to drop pauses, and the bot had
no way to find them: it could only keep segments it guessed. The new
remove_silence step finds every pause quieter than threshold_db (default
-35 dB) and at least min_silence long (default 0.6 s), keeps padding
(default 0.12 s) on each side so word onsets survive, and cuts the rest
through the existing keep-segments graph, so picture and sound stay in
sync.
Steps run in order, so silence is detected on the timeline the steps
before it leave: the planner gets an analysis mode that sends that audio
through silencedetect into a null output, and ametadata prints the marks
to a file in the job directory. The detected speech is handed back to the
planner per step. Nothing silent makes the step a no-op; nothing left
fails with a clear error. At most 200 pieces are kept, bridging the
shortest pauses first, so the filter graph stays a sensible size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* Offer platforms in the media card's export menu and show their checks
The card's Export menu now lists the platforms first, by brand name and
untranslated, then the aspect ratios after a separator; the platform an
output was made for is left out, like its own shape. A platform export
shows the platform's name under the player, or, when its check did not
pass, one line for the first issue instead ("Too long for TikTok (max
10:00)"), in the warning or destructive color. Skipped platforms get
the same kind of line, and they stand in for the error of a job that
skipped every platform, so the card does not say the same thing twice
in two languages.
The E2E spec seeds a platform export with a failed check and a skipped
platform, and captures the card and the open menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* Add analyze_video to find cut points for the bot
To cut a social video well the bot has to know where the cuts fit, and it
could not look at a video at all. analyze_video is a read-only media job:
one ffmpeg pass prints scene changes (scene score above 0.3, one per cut),
pauses (silencedetect with the remove_silence defaults) and short-term
loudness (ebur128) to files in the job directory, and a transcript made
from the video by transcribe (an SRT whose parent is the video) adds when
each line is spoken. The job stores nothing; its card just says done.
The result reaches the bot through the existing media job path: the job
always wakes the bot that started it (the tool has no continue_after) and
the summary carries a short legend plus compact JSON. The runtime's
summary limit grows to 12,000 characters and the summary is fitted into
it: at most 200 entries per list, then every list shrinks evenly, keeping
the strongest cuts, the longest pauses and the start of the transcript,
with omitted counts. The wake prompt no longer claims the card shows the
result when a job made no files.
Jobs read derived artifacts through a new fetchDerivedArtifact, scoped to
the job's user like fetchArtifact. A migration admits the operation in the
media_jobs check and adds the tool to existing Studio specialists that
still edit video. The web card gets an icon; no new copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* fix(media): keep one skipped entry per platform on the card
A job that recovers after a crash runs again and reports the same
skipped platforms; the newest reason per platform now replaces the
earlier one instead of listing it twice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* chore(db): add the widened media job operation check as NOT VALID and validate it separately
Follows the repository's migration convention: the constraint commits
without scanning media_jobs, and a second migration validates it with a
lock that lets reads and writes continue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* Brand videos with a logo overlay and an intro before the cut
edit_video gains an overlay step that lays a PNG, JPEG or WebP image over the
video at a corner or the center, sized to a share of the frame width, with
optional opacity and time range; tall frames keep the 9:16 safe areas clear.
concat takes at: "start" so an intro can go before the video. The image is
authorized like other inputs, read with its own demuxer and refused above
8192 pixels on a side. New Studio specialists are told to brand with the
user's logo and intro.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Pick the voice from a carousel in the app language, local voice only
Voice settings now list only the voices that speak the app language
(German shows German voices, English shows English ones) as a horizontal
carousel: one card per voice, a play button next to the name, tap to
make it the active voice. Changing the app language moves the saved
voice to that language. The hosted voice providers (ElevenLabs, OpenAI,
Cartesia, Fish Audio) are removed; Kokoro on the server is the only
voice. E2E runs against a stub Kokoro server so the carousel is covered.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxoSHAyzMXvdtwcDJZcn57
* test(e2e): measure the workspace panel width after its transition settles
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxoSHAyzMXvdtwcDJZcn57
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Export videos for named platforms and check each file against its limits
Exports only knew aspect ratios, so a cut could still be rejected or
degraded on upload: too long for the platform, over its size limit, or
far off its loudness. export_video now takes platforms (TikTok,
Instagram Reels and Feed, YouTube Shorts and YouTube, Facebook Reels,
LinkedIn, X, Pinterest, Snapchat Spotlight), up to six targets with the
presets. Each platform is rendered to its frame with the export's fit,
its frame rate cap, a bitrate cap that also keeps the file under the
size limit, and its loudness target in one loudnorm pass.
Every render is probed and measured afterwards. A file over the size
limit is encoded once more at a bitrate scaled from what it averaged,
and audio more than 2 LU off the target gets one linear loudness pass.
The result is stored on the output as a pass, warn or fail check with
language-neutral issue codes. A platform the video is too long or too
short for is not rendered: the card lists it with the reason, the
other targets still render, and the bot is told to trim first.
The limits come from the earlier profile research, with its sources,
review date and unverified flag; content, disclosure and caption rules
are left out because they need inference or a review flow. Preset
definitions move to their own module so the platform schemas can use
their aspect ratios without an import cycle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* Add remove_silence step to edit_video
Talking-head clips are mostly cut by hand to drop pauses, and the bot had
no way to find them: it could only keep segments it guessed. The new
remove_silence step finds every pause quieter than threshold_db (default
-35 dB) and at least min_silence long (default 0.6 s), keeps padding
(default 0.12 s) on each side so word onsets survive, and cuts the rest
through the existing keep-segments graph, so picture and sound stay in
sync.
Steps run in order, so silence is detected on the timeline the steps
before it leave: the planner gets an analysis mode that sends that audio
through silencedetect into a null output, and ametadata prints the marks
to a file in the job directory. The detected speech is handed back to the
planner per step. Nothing silent makes the step a no-op; nothing left
fails with a clear error. At most 200 pieces are kept, bridging the
shortest pauses first, so the filter graph stays a sensible size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* Offer platforms in the media card's export menu and show their checks
The card's Export menu now lists the platforms first, by brand name and
untranslated, then the aspect ratios after a separator; the platform an
output was made for is left out, like its own shape. A platform export
shows the platform's name under the player, or, when its check did not
pass, one line for the first issue instead ("Too long for TikTok (max
10:00)"), in the warning or destructive color. Skipped platforms get
the same kind of line, and they stand in for the error of a job that
skipped every platform, so the card does not say the same thing twice
in two languages.
The E2E spec seeds a platform export with a failed check and a skipped
platform, and captures the card and the open menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* Add analyze_video to find cut points for the bot
To cut a social video well the bot has to know where the cuts fit, and it
could not look at a video at all. analyze_video is a read-only media job:
one ffmpeg pass prints scene changes (scene score above 0.3, one per cut),
pauses (silencedetect with the remove_silence defaults) and short-term
loudness (ebur128) to files in the job directory, and a transcript made
from the video by transcribe (an SRT whose parent is the video) adds when
each line is spoken. The job stores nothing; its card just says done.
The result reaches the bot through the existing media job path: the job
always wakes the bot that started it (the tool has no continue_after) and
the summary carries a short legend plus compact JSON. The runtime's
summary limit grows to 12,000 characters and the summary is fitted into
it: at most 200 entries per list, then every list shrinks evenly, keeping
the strongest cuts, the longest pauses and the start of the transcript,
with omitted counts. The wake prompt no longer claims the card shows the
result when a job made no files.
Jobs read derived artifacts through a new fetchDerivedArtifact, scoped to
the job's user like fetchArtifact. A migration admits the operation in the
media_jobs check and adds the tool to existing Studio specialists that
still edit video. The web card gets an icon; no new copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* fix(media): keep one skipped entry per platform on the card
A job that recovers after a crash runs again and reports the same
skipped platforms; the newest reason per platform now replaces the
earlier one instead of listing it twice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* chore(db): add the widened media job operation check as NOT VALID and validate it separately
Follows the repository's migration convention: the constraint commits
without scanning media_jobs, and a second migration validates it with a
lock that lets reads and writes continue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Export videos for named platforms and check each file against its limits
Exports only knew aspect ratios, so a cut could still be rejected or
degraded on upload: too long for the platform, over its size limit, or
far off its loudness. export_video now takes platforms (TikTok,
Instagram Reels and Feed, YouTube Shorts and YouTube, Facebook Reels,
LinkedIn, X, Pinterest, Snapchat Spotlight), up to six targets with the
presets. Each platform is rendered to its frame with the export's fit,
its frame rate cap, a bitrate cap that also keeps the file under the
size limit, and its loudness target in one loudnorm pass.
Every render is probed and measured afterwards. A file over the size
limit is encoded once more at a bitrate scaled from what it averaged,
and audio more than 2 LU off the target gets one linear loudness pass.
The result is stored on the output as a pass, warn or fail check with
language-neutral issue codes. A platform the video is too long or too
short for is not rendered: the card lists it with the reason, the
other targets still render, and the bot is told to trim first.
The limits come from the earlier profile research, with its sources,
review date and unverified flag; content, disclosure and caption rules
are left out because they need inference or a review flow. Preset
definitions move to their own module so the platform schemas can use
their aspect ratios without an import cycle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* Offer platforms in the media card's export menu and show their checks
The card's Export menu now lists the platforms first, by brand name and
untranslated, then the aspect ratios after a separator; the platform an
output was made for is left out, like its own shape. A platform export
shows the platform's name under the player, or, when its check did not
pass, one line for the first issue instead ("Too long for TikTok (max
10:00)"), in the warning or destructive color. Skipped platforms get
the same kind of line, and they stand in for the error of a job that
skipped every platform, so the card does not say the same thing twice
in two languages.
The E2E spec seeds a platform export with a failed check and a skipped
platform, and captures the card and the open menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
* fix(media): keep one skipped entry per platform on the card
A job that recovers after a crash runs again and reports the same
skipped platforms; the newest reason per platform now replaces the
earlier one instead of listing it twice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
---------
Co-authored-by: Claude <noreply@anthropic.com>
Text sizes now follow a --font-scale variable that is 1 on wider screens and
8/7 below 768px, so the 14px base text reads at 16px on phones (Safari stops
zooming into fields at that size) and every other size grows by the same
ratio. One-off pixel sizes in the shell move onto the shared text tokens so
they scale too. The stage subtitle now truncates inside the screen instead
of spilling past both edges.
Claude-Session: https://claude.ai/code/session_01PFEDgbyJm12ExL3Kn4Dk8B
Co-authored-by: Claude <noreply@anthropic.com>
The deployment owner adds folders of photos and videos in the media settings. A per-folder
background job indexes them (relative path, kind, size, capture date, dimensions, duration),
re-indexes every six hours, and drops files that are gone. The owner's own runs get two tools:
search_media (words in folder and file names, kind, capture date range, newest first) and
import_media (copies one file into the conversation as an artifact). Files are never changed;
hidden entries and symlinks are skipped, and an import must resolve inside its folder.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
Without a model key the settings opened on the keyless deployment default
(OpenRouter). The connect flow now opens on Claude Opus 5, so a new user
only adds a key or signs in. A connected credential, an owner-chosen
default, or a deployment key still decides as before.
Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD
Co-authored-by: Claude <noreply@anthropic.com>
The autopilot's start line counts its steps and folds open to the latest
checkpoints, and its result reads as a card. The composer's plus menu
starts an autopilot with the draft as the order and a choice of until
done, tomorrow morning or a time. Today lists the autopilots that run,
wait or ended within a day, and their step runs no longer show one by
one. Autopilot runs send no completion or failure push; only a pause
notifies.
Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1
Co-authored-by: Claude <noreply@anthropic.com>