* feat(agents): add first-class DeepSeek Harness (dsh) support
Register DSH as a supervised Orca agent: catalog entry and detection for its
dsh-tui profile, status/question hooks through DeepSeek's own Claude-Code hook
bridge, composer-ready prompt delivery, session resume, headless Source Control
AI, and title identity that no longer collides with Gemini's.
* fix(dsh): reach Orca through DSH's credential scrub and stop reading its title as Gemini
DSH runs command hooks through its own shell executor, which drops every env var whose
name contains KEY, TOKEN, SECRET or PASSWORD — taking ORCA_PANE_KEY and
ORCA_AGENT_LAUNCH_TOKEN with it, so every hook exited without posting. Mirror both onto
scrub-safe aliases at spawn and restore them at the top of the DSH hook script.
Its title collided too: DSH rests on the same glyph Gemini works on, so a resting DSH
pane was relabelled Gemini CLI and reported working forever. Defer both the Gemini
classifier and the title status detector on DSH's whale, in the base module both copies
of that classifier read.
* test(mobile): repin the session-route closure for the DSH agent icon
* fix(dsh): address review — never splice user rows, cover remote panes, keep the diff off argv
- findManagedDshPatchRegion paired an orphan start marker with a later block's end, so a
truncated write made install/remove delete the user's own rows. Pair each end with the
nearest preceding start; regression test fails without the fix.
- The relay PTY env builder never applied the scrub-safe aliases, so remote DSH status
silently never appeared even with the remote hook installed.
- Source Control AI sent the whole diff on argv; send it over stdin with DSH's '-' marker.
- dsh-tui/dst already chose the interactive profile, so a workspace folder named 'web' or
'plugin' no longer marks a live agent pane non-interactive.
- Isolate USERPROFILE as well as HOME so a Windows run cannot edit the real home.
- Drop the duplicate README badge and revert an incidental doc reformat.
* refactor(dsh): share the managed-hooks reader and tighten the new modules
Reuse before reimplementing: readManagedDshHookEvents was a near-verbatim copy of Muse's,
with byte-identical private helpers. Both now call one readManagedHookEventsFromJson.
Also: one readTextOrAbsent instead of two spellings of the same read (dropping an
existsSync TOCTOU), one status() builder instead of four inline literals, rmSync(force)
instead of exists-then-unlink, and a redundant empty-string guard before JSON.parse.
The patch-file transforms lose their index juggling for a predicate plus a filter.
* fix(dsh): refuse a flow-style patch file, keep its mode, and stop the relay inheriting a pane
- applyManagedDshPatch matched only an exact `[]`, so `[] # keep empty` or a non-empty
flow sequence got a block entry appended after it — invalid YAML that would leave DSH
unable to load the user's own patch layer either. It now strips the token from an empty
sequence (keeping a trailing comment) and returns null for a non-empty one; install
reports that and changes nothing.
- The patch rewrite dropped an owner-only file to the umask default (CWE-732); pass
preserveMode.
- The relay PTY env never dropped inherited pane identity the way the local and daemon
builders do, so a spawn that specified none could inherit the relay's own and every
agent's hook would report against that pane.
* fix(dsh): keep the flow-style refusal in every status read, and scope the mode test to POSIX
A refused patch file carries no managed region, so getStatus() fell through to a bare
not_installed with detail null — the actionable 'rewrite it as a block sequence' message
only ever reached the one-shot install() return. Export the predicate and check it first,
behind one shared message constant.
The owner-only mode assertion cannot hold on Windows, where chmod only toggles the
read-only attribute and mode & 0o777 reads 0o666 for any writable file.
* docs(readme): restore the DeepSeek Harness badge lost in the rebase
* test(mobile): repin the session-route closure to the measured 4221
Measured, not derived: 4220 without the DSH icon entry, 4221 with it. Two of the three
modules above main's 4218 pin are not this change's — they arrived with the mobile work
after #22570 and were never repinned; the changelog records that split explicitly.
* fix(dsh): settle tui-idle on the agent's own hook, so supervised workers see it ready
Reported by a tester on the adhoc build: `terminal wait --for tui-idle` ran to its 90s
timeout against an already-ready DSH composer, so a supervised worker never sees the agent
as ready.
Every existing tier reads the title, and DSH deliberately carries no title status: its rest
prefix is Gemini's working glyph, so the detector reports none. A fresh first-party `done`
is better evidence than any title anyway — it is the agent's own account of its own turn,
and normalizeDshEvent drops subagent events, so it is the lead's. Scoped to DSH: for agents
whose hooks report child turns, a mid-turn `done` is the #6011 class this file prevents.
* test(daemon): record the DSH transcript's true-colour I2 divergences
Adding the dsh-tui capture to __fixtures__ enrolled it in the serialize replay sweep, where
it reports 10 I2 divergences and failed the unlisted-transcript default of 0.
Every one is the same shape — visible-grid row=0, a 24-bit background the round trip does
not restore to default — which is DSH's whale intro painting whole rows of true colour.
Verified as an upstream limitation rather than a regression by replaying against the
previous build (build-serialize-addon-at-ref.mjs --ref origin/main): I1 and I3 both hold.
* fix(dsh): return the new tui-idle verdict from the first-party done lane
Main refactored isTuiIdleSatisfied into evaluateTuiIdle, which returns a verdict rather
than a boolean. The DSH lane still returned `true`; it is tier-1 positive evidence, so it
returns READY_STRONG like the title/body lane above it. Re-verified the regression test
still fails without the lane.
* test(relay): pin the scrub-safe pane-identity aliases on the relay spawn path
The relay builds a remote pane's env itself, so the alias mirroring there had no
test: removing the call left every suite green while remote DSH status silently
vanished. Both cases fail without it.
* docs(dsh): point the hook service at the integration reference
The reference doc had no inbound link from anywhere in the repo.
* feat(agents): add first-class ZCode harness
Add ZCode (Z.ai's `zcode` CLI) as a supervised Orca agent: managed lifecycle
hooks on local, SSH and Windows hosts; status, question and approval reporting;
synthetic status titles; session resume; orchestration worker launch options;
and desktop + mobile agent-picker registration.
Written against the newly open-sourced `zai-org/ZCode` (agent CLI 0.16.9), not
against a remembered screen:
- ZCode's hook runner writes a Claude-compatible stdin alias set, so it routes
through the existing Claude-compatible vendor path while keeping its own
identity in the sidebar.
- `PermissionRequest` fires only once the approval card is on screen and racing
the user's answer, so it is proof the pane is blocked, not an auto-approval.
- ZCode's clarification tool is literally `AskUserQuestion` with Claude's
questions/options shape, so Orca's question card renders it unchanged.
- ZCode's `hooks.enabled` defaults to false, which is why configured hooks were
reported as never firing; the installer sets it.
- ZCode renames its own process to `zcode-cli`, so the expected foreground
process cannot be the launch command or dispatch refuses the pane.
- ZCode emits no OSC title in any state and repaints its ASCII banner forever,
so readiness comes from Orca's synthetic hook title and launch drafts wait on
the composer box rather than on a quiet render window.
Three files crossed their max-lines limit, so each is split along a real seam:
command-line entrypoint parsing out of agent process recognition, skill
classification out of skill root discovery, and registry coverage out of the
remote hook installer tests.
Refs #10564
* fix(zcode): drop the session-option catalog and pin the orchestration contract
ZCode's CLI exposes no `--model` flag at all, and the session-option launch path
refuses to apply any option until a model id is chosen. A catalog therefore could
not deliver `--mode` per worker, and would have accepted `--model` only to drop
it silently. Take opencode's position instead: no catalog, so `worker-start
--model` is refused with a clear message and ZCode launches with the model from
its own config. `--mode` stays reachable through agent args, which is also how
the yolo default is applied.
Add a contract test covering the parts that make ZCode a usable worker:
dispatchable foreground process, stdin prompt delivery, the prompt staying out
of the launch command, and the composer-gated draft paste.
* refactor(zcode): reuse shared helpers and cut the harness down
No behaviour change; every ZCode test still passes.
- Use installer-utils' own `hookDefinitionHasManagedCommand` instead of
re-walking a hook definition by hand, which also drops a local string reader.
- Share one `readZCodeEventMap` instead of keeping the same narrowing in both
hook-settings and hook-config-json.
- Collapse five identical error returns into one `zcodeHookError` builder, and
return early from the status branches instead of assigning through `let`.
- Split the event-to-status decision out of `normalizeZCodeEvent` into a pure
`readZCodeTurn`, so the normalizer reads as decide-then-build and stops
computing the tool name for events that never look at it.
- Take a script file name in `readManagedZCodeHookEvents` like its siblings,
which removes a `Parameters<typeof …>` indirection at the call site.
- Drop the unused `ZCodeHookEvent` export and inline a single-use path helper.
- Correct a stale comment: ZCode's loader is a strict `JSON.parse`, so the
in-place edit preserves key order and indentation, not comments.
* fix(zcode): address review — keep unmanaged event keys, correct comment, de-dupe README
- `removeZCodeManagedHooks` deleted any event key whose list ended up empty, so an
unrelated `"Notification": []` the user wrote was removed as collateral whenever a
managed hook elsewhere made the write happen. Only touch an event Orca actually
owned something in; covered by a new regression test.
- The `isNewTurnEvent` comment claimed UserPromptSubmit was ZCode's only turn
boundary while the expression below it also returned true for SessionStart. Say
what the code does: SessionStart lands the idle boundary, UserPromptSubmit is the
turn boundary (the Codex/Claude shape).
- ZCode appeared twice in the README's single agent-badge block; keep the
local-icon entry the link checker validates and drop the favicon duplicate.
* docs(zcode): call out that the desktop bundle's CLI cannot open a session
From live testing on #22464: pointing `zcode` at the desktop app's bundled
`glm/zcode.cjs` installs Orca's hooks fine but then fails with
`Cannot find package '@zcode/tui'`, so the pane never opens a session. The
symptom reads as a broken harness when the CLI simply has no TUI. Say which
build to use and how to check before reporting a problem.
Reported-by: JWu527
This reverts commit 0677271709.
#18790 was merged as one squash commit that carried two unrelated changes:
a process-incarnation fallback for reaping leaked orchestration worker
terminals, and an unannounced "Freebuff" third-party agent (catalog entry,
icon, locale strings, README rows). The Freebuff agent was never meant to
ship, so the whole PR is reverted; the reap fix should be re-submitted on
its own.
Until that re-land, a worker whose durable terminal handle goes stale is
again reported missing on release/stop instead of being re-found through
its process incarnation, so its terminal can leak on Remote Server.
The mobile session page closure pin moves 4218 -> 4219: the revert drops
the freebuff icon #22119 pinned (-1), and #22452 had already added two
src/shared modules without re-pinning (+2).
* feat(agents): add first-class Muse Code harness
Add Muse as a supervised Orca agent across desktop, mobile, session history, source control, local hooks, SSH, WSL, and native Windows. Preserve user settings, support Muse 1.3 hook environment allowlists, and recognize versioned foreground processes. Include question, waiting, completion, resume, and readiness coverage.
Co-authored-by: homesh-dev <300847526+homesh-dev@users.noreply.github.com>
Co-authored-by: jeffhuen <32542276+jeffhuen@users.noreply.github.com>
Co-authored-by: John Cusack <johncusackccm@gmail.com>
Co-authored-by: Adrien De oliveira <75085839+adriendeoliveira@users.noreply.github.com>
* test(agents): cover Muse remote hook registration
* test(agents): cover Muse hook and source-control contracts
* test(agents): exclude Muse hook metadata from script mode check
* test(agents): keep Muse skill picker coverage stable
* test(ai-vault): include Muse in every-agent fixture
* test(mobile): repin Muse agent icon closure
* fix(muse): detect questions and approvals from structured Muse signals
Muse 1.3 fires no hook for request_user_input, so a pending question left
the pane "working". Its internal reminder subagents also post hooks with
their own session ids (even after Stop), which surfaced "tool failed" rows
and flipped finished panes back to working.
- Read pending questions from Muse's session log
(user_input_prompt_requested/settled) via the existing transcript poll,
now generalized from Codex subagents to Muse on main and relay.
- Drop child-session hooks (SubagentStart ids, or turn_id === session_id).
- Treat Notification permission_prompt as the approval wait; PermissionRequest
also fires for auto-approved calls, so it only caches the approval card.
- Ignore Notification copy as the prompt; poll replays are not new prompts
or turn boundaries.
- Allowlist USERPROFILE so Windows cmd AutoRun doesn't fail every hook.
* perf(muse): parse only question events from the session log
Most Muse session-log lines are large model/tool records. Filter raw lines
by the user_input_prompt_ marker before JSON.parse via an optional
readJsonlCursor line filter.
* fix(muse): unwrap batched log records and scope questions to the live turn
Review follow-ups: question events inside retained_frame batches were
skipped, and a question left open by a crash or interrupt stayed pending
for the pane's life. Share the history scanner's retained_frame unwrapper,
and only report a pending question whose run_id matches the hook turn_id.
* refactor(muse): drop type assertion in retained_frame unwrap
* fix(agent-hooks): satisfy exhaustive-switch lint in transcript poll policy
---------
Co-authored-by: Adrien De oliveira <75085839+adriendeoliveira@users.noreply.github.com>
* fix(orchestration): remint live handle from process incarnation on worker release
When a durable terminal handle goes stale (rendererGraphEpoch fence),
inspectWorkerTerminal re-mints a live handle via
resolveTerminalHandleByProcessIncarnation + matchesProcessIncarnation so
release/stop/read act on the still-running PTY instead of reporting
missing and leaking the agent process tree.
- keep main shared host-scope re-exports; add matchesProcessIncarnation
- wire observation.terminalHandle through control/stop/release
- rebuild release-completion on main structured paths
- on missing/unattached + provably exited: settleDead fence first, then
same-incarnation settleWorker fall back (archive may block settleDead
mid-request); settle before recovery defer
* fix(orchestration): derive SSH host scope from the reminted handle; reuse fresh-request recovery guidance for structured workers
Addresses two open CodeRabbit review comments on PR #18790.
inspectWorkerTerminal read the dispatch authority with the stale durable
terminalHandle, so after a remint the lookup resolved nowhere and
currentHostScope was always undefined — an SSH worker with no liveness
verdict and no persisted host_scope got classified from terminal.connected
instead of unverifiable. It now reads the same effectiveHandle every other
observation in the function uses.
stopStructuredWorkerForRelease told the caller to repeat the release with
the same --retry-request, which only replays the stale release_unknown
receipt and made a structured-worker close failure permanently unretryable.
It now sources releaseUnknownRecovery from worker-release-completion so the
fresh-request-ID guidance lives in one place.
Pre-commit lint-staged (oxlint + oxfmt) run manually: clean.
* test(orchestration): exercise incarnation recovery through runtime paths
* test(orchestration): pin the incarnation read scenario to the reminted terminal
The read scenario only asserted that the call resolved, so it documented
nothing about which handle the read reached. Assert that the handle
readTerminal received resolves to the registered pane and incarnation, so
the scenario proves the read went through the reminted terminal instead of
passing on the incarnation fence's throw.
* refactor(orchestration): drop redundant incarnation prefix check; require liveTerminalHandle
* feat: add freebuff as a first-class TUI agent (#42)
<!-- orca-pr-loc -->
<!-- Programmatic LoC summary. Do not edit by hand; rewritten on every
commit. -->
| | Files | Added | Deleted | Net |
| :--- | ---: | ---: | ---: | ---: |
| Test | 0 | 0 | 0 | 0 |
| Prod | 28 | $\color{#1a7f37}{\Huge{\mathbf{+}}}$37 | 0 |
$\color{#1a7f37}{\Huge{\mathbf{+}}}$37 |
<!-- /orca-pr-loc -->
## ELI5
Add Freebuff (`freebuff`) as a recognized first-class TUI coding agent
in Orca alongside Codebuff and other supported agents.
## What Changed
- Registered `freebuff` across shared TUI agent definitions,
configuration catalogs, display names, and telemetry schemas.
- Added agent icons, favicons, status mappings, and mobile asset
references for Freebuff.
- Added localization strings across supported language packs (`en`,
`es`, `fr`, `ja`, `ko`, `zh`) and updated locale translation policy.
- Documented Freebuff CLI in README agent table (`npm i -g freebuff`).
## Why
Freebuff is a CLI coding agent twin of Codebuff (`npm i -g freebuff`).
Adding it to the catalog enables users to launch worktrees, run
automated sessions, and pick Freebuff directly within Orca.
## Linked Issue
N/A
## Visual Proof
`N/A` - Catalog registration and metadata definition for CLI agent
launch; UI rendering uses existing TUI agent picker and status
components.
## Testing
- Verified TypeScript contracts, schemas, and catalog configurations.
- Tested CLI detection / agent picker integration locally on Linux
(`worktree create --agent freebuff`).
## AI Disclosure
Assisted by AI coding tooling.
## Checklist
- [x] This PR is small and focused
- [x] I explained what changed and why (including ELI5)
- [x] Before/after screenshots or videos attached for UI changes, or
`N/A` with reason
- [x] Self-reviewed for correctness, security, and performance
- [x] Cross-platform, SSH/remote, and path/shortcut impact considered
(or N/A)
---------
Co-authored-by: Lesley Murfin <lesley@revivebusiness.ca>
* test(orchestration): erase method overloads in worker reap fixtures
* test: document worker fixture type boundaries
* test: simplify worker fixture typing
---------
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: svc-orca[bot] <313947298+svc-orca[bot]@users.noreply.github.com>
Co-authored-by: m4air <m4air@Mac.localdomain>
* chore: remove duplicate and unused documentation media
* chore: guard README local links and refresh tile-01 vendor metadata
- Add config/scripts/check-readme-local-links.mjs: every local src/srcset/href
in README.md and docs/readme/*.md must resolve to a tracked file. Runs in the
ungated root_directory_guard job so docs-only diffs (which skip static_analysis)
still catch a deleted docs-site or feature-wall asset the README embeds.
- Refresh tile-01.recorded-at.json to what vendor-feature-wall-assets.mjs now
emits for the tab-split source path.
- Drop the pr-19217 evidence prose that cited the removed screenshots.
* fix: accept single-quoted attributes in README local link check
The parser only matched double-quoted src/srcset/href, so <img src='missing.gif'>
was skipped and the guard passed a README that GitHub renders with a broken image.
Regression test fails without the parser change.
* Update mobile 0.0.48 Android download links
* Update the mobile docs page APK link to 0.0.48
The docs page the READMEs link to still pointed at 0.0.46, two releases stale.
---------
Co-authored-by: Merge Sim <sim@local>
Phase 6 of the relay split: the relay's deploy/operate surface moves under cloud/ with 24 cloud-* workflows gated on ORCA_CLOUD_OPERATIONS_ENABLED, the Cloud SQL rollout lease action, the relay Terraform root (dual-accept identities for both repositories), scripts, docs, CODEOWNERS, and a terraform validate job in Cloud Verify.
* Add automation runs dashboard with pagination and filtering
Adds a new Runs view in the Automations page that lets users browse all runs across automations with status/host filtering, search, and pagination support. Includes virtualized table rendering for efficient handling of large run histories and summary cards showing 24h/7d success/failure counts.
* Fix missing dependencies in useCallback hooks and imports
Missing dependencies in useCallback can cause stale closure bugs. This
adds missing state setters to dependency arrays and consolidates type
imports for consistency.
* Use keyset pagination for stable automation runs pages
Pagination now uses createdAt:id boundaries instead of offsets, so new
runs arriving between pages don't shift the window. Maintains backwards
compatibility with legacy offset cursors.
Move pagination to shared module, fix outcome counting for future-dated
runs, and improve hook state tracking on authority re-pairing or target
changes.
* Extract automation run details to top-level page view
Moves run display from detail pane to dedicated page, establishing
three-level navigation (Automations → Runs → Run Details) and simplifying
the detail pane component.
* Fix pagination stability when automation runs share createdAt
- Define a stable total order with createdAt and id tiebreaker to prevent runs tied on createdAt from being dropped when the boundary run is pruned between page requests
- Retain cursor on failed pagination so pages remain retryable
- Update ownerNotice type to AutomationActionNotice
* Extract automations list panel and worktree map logic
Split AutomationsPageSurface into smaller, focused modules for better maintainability and reusability. Move list panel UI rendering to AutomationsPageListPanel component and worktree map selection logic to a standalone utility function.
* Add i18n strings for automation runs dashboard
Adds localized strings for the automation runs dashboard view, including search, filtering by host and status, run counts for 24h/7d windows, and empty state messaging across all supported languages.
* fix missing translation
* fix missing translation
* style: format codebase
* style: format codebase
* refactor: extract skill install dialog footer and content
Extract footer and content sections from SkillInstallDialog and
SkillInstallManagementDialog into separate components for improved
maintainability and clarity of component responsibilities.
Provides alternative community group with QR code when primary group
reaches capacity. Updates documentation in both English and Chinese.
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
badgen.net rate-limits against GitHub and paints "429" into the badge.
Switch stars to shields.io (repo stargazers_count), use a static MIT
license badge, and drop /stargazers links which now 404 for the public
after GitHub's July 2026 stargazer access restrictions.
Updated README to guide Windows users to the latest RC release,
which includes critical Windows-specific bug fixes not yet in the
stable build. Added prominent notices at the top of the download
section and in the direct-download links.
- Replace outdated dual-QR (primary + backup) layout with one QR code, since the first group filled up and members should join the second group instead
- Applies to both README.md and the zh-CN translation
- Remove the now-unused backup/png QR image assets
- Refresh star-history chart and backup WeChat QR image assets
- Add spacing between the primary and backup WeChat QR codes in both README.md and README.zh-CN.md