The orcad/remote-install transport now exposes exec as a neutral
RemoteCommandChannel (a Duplex with stdin, stderr, close and exit/close
events) instead of ssh2's ClientChannel, and drops sftp(): the only
consumer was a fallback for doubles without writeFile/uploadDirectory,
which the transport type already requires. SshConnection still satisfies
the type structurally, so the SSH adapter keeps returning the connection
itself and identity-keyed caches are unchanged.
The stdio bridge provider is split: the loopback listener (admission,
queueing, socket piping) now takes a channel factory and knows nothing of
SSH; the SSH provider resolves and checks the bridge over the host
transport and hands the listener a factory. No behavior or wire change.
* Skip unnecessary compatibility-boundary parsing and benchmark it in CI
* Use pinned preflight Bun for compatibility checks and localization extraction
* Measure two concurrent Bun projects in full preflight and update its scope test
* fix(chat): jump to messages without scrolling animation
* Jump to chat messages instantly instead of animating the scroll
Smooth scrolling let rows measured mid-animation shift the target, so jumps could land short or cancel near the end of the transcript. Jumps now write the scroll offset directly, and the align hook keeps its target until the row settles. Tests assert no smooth scroll is requested and that a loaded target lands within one frame.
* fix(bench): make the idle-CPU benchmark run and report CPU on Windows
Three independent Windows defects in the idle-CPU benchmark (its report is
what Windows performance work would cite):
- `buildAppIfNeeded` spawned `npx`, which is `npx.cmd` on Windows, so
execFileSync failed with ENOENT before any sampling. Build through
`config/scripts/run-electron-vite-build.mjs` instead — the repo's own
Windows-safe wrapper, which also runs the boot-graph verification.
- `readWindowsProcesses` returned `cpuTimeSeconds: null`, so the sampler's
per-process CPU delta (idle-cpu-process-sampling.mjs:198) fell back to
the hardcoded `percentCpu: 0` and every CPU KPI in the report was zero.
Read `UserModeTime`/`KernelModeTime` from `Win32_Process` (100 ns units)
and keep `null` when a host does not return them.
Measured on Windows, same options before and after the CPU fix:
| class | mean CPU before | mean CPU after |
| --- | ---: | ---: |
| renderer | 0 | 2.20 |
| gpu | 0 | 2.08 |
| utility | 0 | 0.11 |
| agent-or-node | 0 | 0.06 |
| total | 0 | 4.51 (p95 8.40) |
RSS is unchanged (934 MiB -> 937 MiB), so only the CPU reporting changed.
Refs #25235
* fix(bench): reject a missing Win32_Process time instead of reading it as zero
Review follow-up (CodeRabbit). `Number(null)` is `0`, which is also a real
counter value, so an absent `UserModeTime`/`KernelModeTime` passed the
finite check and the sampler would report a kernel-only delta instead of
taking the fallback. Require an actual number from the CIM payload.
Verified on Windows that the real payload still reports CPU once the check
is tight: total mean 4.40%, p95 14.48%, renderer 3.51%.
* test(bench): cover the Windows CIM CPU time mapping
The mapping was only reachable through Get-CimInstance, so no test ran it.
Lift it into cimCpuSeconds and assert the 100ns-to-seconds sum plus the
missing / string / non-finite cases that must stay null.
* fix(bench): read the Windows process table as UTF-8 so JSON.parse survives
PowerShell writes to a redirected stdout in the OEM code page (437 here),
which best-fits characters such as U+2022 and U+2192 in any running
process's command line to raw 0x07 / 0x1A bytes. ConvertTo-Json had left
them unescaped because they are not control characters in UTF-16, so the
sampler's JSON.parse failed with "Bad control character in string literal"
whenever such a process happened to be alive -- hence intermittent.
Set [Console]::OutputEncoding to UTF-8, as the app's own CIM scan does,
and cover it with a Windows-only test that fails without the fix.
Fixes#25236
* fix(bench): pass the local runtime to fetchRepos in the idle-CPU fixture
fetchRepos now requires a runtimeEnvironmentId, so the fixture's bare call
threw "Cannot read properties of undefined (reading 'runtimeEnvironmentId')"
before sampling on every platform. null selects this app's own catalog, as
the e2e helpers already do.
* ci(bench): run the Windows process-table test in the Windows lane
The UTF-8 regression test self-skips off win32, so it must be registered in
both Windows lists or it runs on no machine.
---------
Co-authored-by: Paolo <paovalletta@hotmail.it>
Co-authored-by: Orca Worker <orca-worker@localhost>
* fix(bench): resolve dynamically imported sources as file URLs
`await import(path.join(ROOT, '...ts'))` hands Node a bare Windows path
(`C:\...`), which the ESM loader rejects as an unsupported URL scheme
("Received protocol 'c:'"). The two benchmarks that load production
modules this way crashed on Windows before running a single scenario,
while working on macOS and Linux where an absolute path is a valid
specifier.
Wrap each computed specifier in `pathToFileURL(...).href`, the form the
other benchmark scripts already use for the same purpose.
Verified on Windows: bench:session-write-hot-path and
bench:agent-inspection-cadence now run and print their measurement
tables. The fish mode-2031 trace capture resolves its import too, but its
harness targets macOS/Linux shells and cannot complete on Windows.
Fixes#25235
* style(bench): format the session-write benchmark's file-URL import
---------
Co-authored-by: Paolo <paovalletta@hotmail.it>
Co-authored-by: Orca Worker <orca-worker@localhost>
* feat(serve): supervised mode for orca serve --supervise
Restart the server with jittered backoff instead of exiting when it dies,
with a crash-loop brake whose exhaustion is durable per profile and build,
an install hold while the executable is missing or ShipIt replaces the
bundle, and standby while another Orca serves the profile. Only an
explicit --supervise turns it on; Windows refuses it for now.
* fix(serve): let a supervised orcad serve wait for a live peer instead of counting it
* fix(serve): gate update replacements on the install hold, honor stop during the bundle wait, keep a give-up while the executable is missing
* fix(serve): hand a removed executable back to the service manager and key the brake on the app archive
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(wsl): stop booting stopped distros from background work
Any `wsl.exe -d` (or \\wsl.localhost access) boots a stopped distro: 1-2s of
VM boot, and on WSL < 3.0.1 its later idle-stop wipes the VM-wide WSLInterop
binfmt entry for every other running distro (microsoft/WSL#13885).
Add one gate (wsl-background-distro-gate.ts): background work checks
`wsl --list --running` (never boots), and stopped-distro work can be deferred
until the distro is next seen running (a confirmed running probe or a local
WSL terminal spawn).
- Startup CLI registration reconciliation: only running candidates; stopped
ones are deferred per distro and stay registry candidates for later starts.
- Fish history GC: keeps the tombstone (and its meta.json) while the distro is
stopped and finishes the cleanup once it runs; no admission slot is held.
- Codex/Claude/Kimi quota polls for WSL targets: skip while stopped (Codex
keeps its last value). User-driven resets still boot.
- Codex WSL auth drain before an update restart: skip stopped distros (no
live legacy pane there; the next launch prep drains anyway).
* fix(rate-limits): capture the Codex fetch generation before home resolution
The Codex home resolver is now async (it checks whether the WSL distro is
running), so a reset that lands during that await bumped the generation the
full-refresh cycle then read as its own, letting a stale result overwrite it.
Co-authored-by: Syed Zohaib Hussain <26768511+RoxasZohbi@users.noreply.github.com>
* fix(wsl): read an unconfirmed running-distro probe as stopped in the background gate
* test(wsl): pin the empty running list wsl.exe reports when no distro runs
---------
Co-authored-by: Syed Zohaib Hussain <26768511+RoxasZohbi@users.noreply.github.com>
* fix(native-chat): put chat text on one type scale that follows Text size
Sizes were picked per component, so the same kind of row came out 12, 13 or
14px and the Text size setting reached only a few of them.
* fix(native-chat): scale chat spacing and icons with Text size
Icons and gaps stayed fixed while text grew, so at larger sizes they looked
undersized and sat off-centre.
* fix(native-chat): implement PR #26685 review fixes
- Scale row gap with text size so controls don't overlap
- Clamp 'Match terminal interface' to 12-20 for labels/controls
- Use existing --status-warning token for warning color
- Rewrite stale test for scaled typography tokens
- Use app colors for in-progress tasks in composer dock
* fix(native-chat): leave the task list's dock colour as it was
* fix(native-chat): message text follows the terminal size; controls and spacing stay in 12-20
* test(native-chat): labels and spacing follow the bounded chat size; main's new notice test uses the chat colour
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* refactor(startup): one agent-hook runtime binding set for every host
* fix(startup): read the hooks-enabled gate from its shared module
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
The relay runtime ladder (rungs A/B/C/legacy/D, its self-test, refusal
cache, per-host decision store and its telemetry) lost its only caller
when the relay was deleted. Two pieces were still live: picking the Node
runtime a host can run by glibc (managed orcad deploy, WSL guest
runtime) and the runtime-store pins orcad version GC keeps. Those move
to pinned-runtime-host-target.ts; pick/durationBucket move into
ssh-host-server-telemetry.ts, their only remaining user.
Co-authored-by: m4air <m4air@Mac.localdomain>
* perf(wsl): run ripgrep on the Windows host for drive-path projects
A project stored on a Windows drive (C:\...) with the WSL runtime selected
ran quick open, file-path search, content search and Markdown listing
through wsl.exe and the Linux rg reading /mnt/c over drvfs. rg only reads
files, so it has no git index whose stat cache two writers could thrash;
the host binary reads NTFS directly.
Measured on a 21.6k-file clone on C: (Windows 11, Ubuntu-24.04):
content search WSL rg 3.5 s -> host rg.exe 0.19 s
file listing WSL rg 0.14 s + ~65 ms wsl.exe spawn -> host 0.03 s
Git routing is intentionally unchanged: alternating Windows git and WSL git
on the same index forced a full ~250 s rehash on the next WSL git status.
WSL UNC roots and non-drive roots keep their existing routing.
* fix(search): keep host rg results whole when files only WSL can open exist
Windows rg cannot open a 'nul' file, Win32-trimmed names, or WSL-created
symlinks; treat those per-file errors as skipped rather than failing or
truncating the search, and keep symlink-following scans in WSL.
* feat(relay): declare Asia spare cell c35 as migration-only
Adds a seventh asia-east2 cell at the C34 shape (cap 3000, 6000 request
units, pool 16, e2-standard-4) in asia-east2-a, pinned to image F. It gets
its own topology and registration wave but no promotion wave, so the
admission script and workflow refuse to promote it; it stays a
migration-only spare and out of the fleet pool list. Mirrors #25336.
* test(relay): accept the c35 spare under the same-cap capacity allowlist
The switchers and the shared gh/glab preflight resolve the focused workspace's
owner row (getActiveWorkspaceOwnerTarget, which has no focus fallback) and fall
back to the default scope host only when nothing proves an owner. The task page
and Automations ignore a preflight whose context key names a paired server.
* refactor(daemon): share the cold-restore payload builder
Lets a reader outside the adapter build the same saved-screen payload from
history without spawning.
* feat(daemon): hold restored terminals a newer build's daemon may still run (STA-9257)
After a downgrade, an older build cannot adopt a newer-protocol daemon, so it
cold-restored that daemon's terminals under the same ids and resumed their
agents beside the originals. Startup now classifies newer-protocol pid records
in this profile's runtime dir as live, unverifiable or exited, and pty:spawn
refuses a restore no adopted daemon owns while one is live or unverifiable.
Reopen stops the newer daemon by verified signal only and reports success only
once its pid is confirmed gone.
* feat(terminal): show held restored panes read-only with a Reopen banner (STA-9257)
A pane refused for a newer daemon shows its saved screen read-only under a
pane-top warning. Reopen terminal stops the newer daemon and releases every held
pane through the existing reattach path, then toasts what actually reopened and
resumed; an unconfirmed stop reopens nothing and offers Try again.
* refactor(daemon): hold newer-daemon restores at the router, one classifier (STA-9257)
The hold moves from the pty:spawn IPC handler into DaemonPtyRouter.spawn so
every spawn source (IPC, runtime controller, mobile refresh, CLI) refuses a
cold restore of an id no adopted daemon owns while a newer daemon is held.
Discovery and stop share one classifier; an unidentifiable owner settles by
whether its endpoint accepts a connection, so a dead leftover never holds a tab
forever. Main announces a confirmed stop to the renderer.
* fix(terminal): per-pane failed state, one release path, no repeated screen (STA-9257)
A pane held after a failed Reopen shows the held state. Held panes also release
on main's stop announcement, with one toast only from the clicking window. A
pane whose saved buffer the layout restore already painted skips the history
screen and is not cleared on release.
* refactor(terminal): drop the release event, await the router hold, one phase per pane (STA-9257)
The release announcement only reached the window that clicked, which already
releases on its reply, so it is removed. The router hold check is always
awaited; three router tests released the adapter spawn before the router had
reached it and now wait for it instead. Each held pane carries its own phase.
* refactor(terminal): trim the newer-daemon hold, keep paired panes out of it (STA-9257)
A paired host's refusal is now an ordinary error: holding it locally looped,
since this window cannot stop that host's daemon. The history-screen read is
gone (the layout restore already paints saved output), discovery folds into the
hold module, the hold store uses zustand, the reattach returns its outcome so a
fresh-session fallback counts as reopened, and the toast uses plural keys.
* feat(terminal): show a held local pane's saved screen read-only (STA-9257)
Local daemon-backed panes keep no layout buffer, so a held pane was blank. Main
reads the session's saved screen from history without spawning and the pane
paints it read-only, unless the layout restore already painted the pane; it is
cleared before the normal reconnect.
* fix(daemon): re-probe a timed-out newer-daemon endpoint before holding (STA-9257)
* feat(terminal): count held panes in the reopen label, add Dismiss (STA-9257)
* fix(wsl): detect and restore a wiped WSLInterop entry in running distros
On WSL < 3.0.1 a graceful stop of any systemd distro deletes every
binfmt_misc entry VM-wide, so Windows .exe launches (code ., explorer.exe,
the orca WSL CLI bridge) fail with Exec format error in every other distro.
Check, never boot: 5 s after a WSL terminal env build (gated on the distro
being in the confirmed running list, at most once a minute per distro), and
whenever a confirmed running-distro probe shows another distro stopped.
Repair is automatic and idempotent as root, skipped when the distro's
wsl.conf sets [interop] enabled=false, single-flight per distro, with a
10 minute backoff after a failed repair.
* fix(wsl): repair interop with a single register write matching WSL's flags
Busybox printf writes a trailing newline separately; the kernel registers the
entry, then rejects the lone newline, so the repair reported failure after
succeeding. Write without the newline and let the re-check decide. Use WSL's
own P flags rather than PF, and treat an entry restored between probe and
repair as intact. Add a spawn-budget test.
* fix(runtime-environments): tolerate unreadable server records without dropping them
One corrupt or newer-format record in orca-environments.json or the
environment sidecar made every server unlistable. Each file now parses
per record: readable records load, unreadable ones are rewritten
verbatim on every write. A server whose sidecar entry cannot be read is
hidden and refused for edits, so it never connects or is rewritten
without its SSH access or deployment link.
* test(runtime-environments): fuzz interleaved writers against unreadable records
The orcad deploy, activation, serving and lifecycle code took the concrete
SshConnection class. It now takes OrcadHostTransport: exactly the connection
members that code and the shared install/lock/GC/host-probe helpers it calls
use (exec, usesSystemSshTransport, getConnectGeneration, sftp, writeFile,
uploadDirectory, getTarget). The shared helpers narrow to the same surface
(RemoteInstallTransport). SshConnection implements it as is; the SSH adapter
returns the connection itself so identity-keyed caches and ownership checks
are unchanged.
The managed tunnel (port forward, stdio bridge provider, connection manager
ownership) stays on SshConnection: it is the SSH reachability layer.
Type-only; no runtime, wire or persisted-format change.
* fix(editor): open WSL worktrees in Cursor as WSL remote windows
Cursor received the raw WSL UNC path and opened it as a local Windows folder. Its CLI accepts --remote wsl+<distro> like VS Code, so route WSL UNC paths through the same remote args while keeping --new-window.
* fix(wsl): ignore Windows-mounted CLIs in guest hook agent detection
WSL appends the Windows PATH, so the guest relay's agent lookup reported a Windows-only npm shim (e.g. /mnt/c/.../npm/codex) as installed in the distro and installed hooks for it. Reuse the Windows-mount skip that host-side WSL detection already applies; fish shells now hand their login PATH to the same POSIX walk.
* fix(skills): judge official skill updates by ownership, not snapshot matching
Orca decided whether an installed official skill was pristine by matching its
bytes against a shipped list of past snapshots, falling back to the skills CLI's
lock hash. Copies installed between releases, CRLF copies, and installs whose lock
recorded a sha256 hash all read "doesn't match the official version" and could not
be updated (#26841), and older content could be labelled newer and never offered.
Now an official skill is Orca's when the skills.sh global lock says it came from
stablyai/orca, and it is current only when its listed files equal the bundled
manifest (line endings and sidecars ignored); anything else is outdated. Install
and update are one pinned `skills add .../tree/v<appVersion>` command, used by the
in-app runner, `orca skills update`, and the Settings rails. The update dialog
preselects Orca-owned rows and lists same-named skills from elsewhere unchecked.
Removes the snapshot registry, release mapping, git-tree identity, lock trust and
convergence logic, and the release-cut provenance step.
* ci(skills): run the pinned-add round trip on a skills CLI that can fetch a commit
* fix(skills): let orca skills update target the shared folder on a bare host
Update became the same pinned add as install, so it inherited install's
refusal when no agent is detected. That broke the bare-host update the old
command guaranteed, and the built-CLI check (update --dry-run, no --agent) on
CI runners. The in-app runner already fell back to universal; both now share
one detectSkillsCliUpdateAgentKeys rule.
Also brings the CLI skills docs off `npx skills update`.
* refactor(skills): derive the update selection from the grouped rows
The selection hook took the live and the retained inventory, re-derived Orca
ownership from installations beside the grouping's own orcaOwned, and kept a
separate display count. Groups already carry both eligibility and ownership,
so the hook reads them once; the button's existing live-scan gate is the only
authorization check. The unselected line is inlined into the headline.
* refactor(skills): drop helpers the ownership design made redundant
- isOwnerManagedSkillScope only negated into the participation check once
plugin caches stopped participating; fold it in.
- Eligibility re-checked resolvedPath/physicalIdentity, which observation
already turns into 'inaccessible'.
- The manifest verifier probed access() before a readFile that throws anyway.
- orchestration-install-command.ts only forwarded one constant from the lib
module its three importers already import.
* i18n(skills): stop naming npx skills update in four skipped-copy strings
The es/ja/ko/zh cantUpdateReason and tipDuplicate strings still named
`npx skills update`, which Orca no longer runs for its skills. Say 'the update
command' instead.
* refactor(skills): inline the single-use pinned source URL helper
orcaSkillsSourceUrl was exported but only called by the builder beside it.
* fix(skills): let an update choice lapse when the install source changes
The selection froze every checked name, Orca's preselection included, the
moment the user ticked anything. A re-check that then attributed a name to
another source kept it checked, replacing it without consent. Store only the
user's explicit choices, keyed by name and lock source, and default to
ownership otherwise.
* fix(skills): label the empty update action 'Update skills', not 'Update 0 skills'
The button stays whenever a row is updatable, so unticking every row (or a
dialog with only non-Orca copies outdated) doesn't reflow the footer. At zero
selected it stays disabled and reads 'Update skills' instead of a zero count.
The label lives beside the dialog's other count copy.
* fix(skills): let keyboard focus open the Not from Orca tooltip
The source of a non-Orca copy is only in the tooltip, and its trigger was a
plain span. Make it focusable like Orca's other tooltip-on-text triggers
(tabIndex 0, cursor-help, the row primitive's focus ring), as in the agreed
mock.
* test(skills): hold the round-trip add equal to the shared install builder
The CI round trip hardcoded its skills add argv, so it could drift from what
Orca runs. A .mjs script can't import the TS builder (extensionless imports),
so the argv moves into an exported roundTripAddArgs and a unit test asserts it
is the builder's unattended add with the tag swapped for the PR commit. The
script runs under the same main guard as the manifest generator.
* i18n(skills): translate the skill row's location count and duplicate reason
es, ko and zh still carried English for SkillUpdateRow.oneLocation and
manyLocations, and es and ko for SkillFreshnessRow.skippedReasonDuplicate.
* fix(skills): pin official skill installs to the build commit and skills CLI
`skills add .../tree/v<version>` only works for stable and RC builds: hourly,
daily and adhoc builds publish elsewhere, so stablyai/orca has no tag for their
version and both install and update failed there. Every build is made from a
commit that exists in stablyai/orca, so the build now stamps `git rev-parse HEAD`
and the one shared builder pins to `tree/<commit>`, which equals the bundled
skills by construction. Main, renderer, web and vitest get it as
ORCA_BUILD_COMMIT (replacing ORCA_APP_VERSION); the tsc-built CLI reads it from
the compiled out/package.json boundary. A missing or malformed commit fails the
build or the command with a clear error instead of building a bad URL.
The builder also pins the skills CLI (`npx skills@1.7.2`): 1.5.x cannot fetch a
commit, and a bare `npx skills` can reuse a stale cache or resolve to another
binary (#18687). The CI round-trip script reuses the same constant.
* ci(skills): run the round trip on the shared pinned skills CLI version
* docs(skills): document the commit-pinned skills add
* refactor(skills): keep the pinned skills CLI version in the install builder
skills-cli-version.ts existed only so the plain-Node round-trip script could
strip-load a .ts file, which needed --disable-warning in CI. The version now
lives beside the builder that uses it; the script keeps a literal, and the
existing round-trip test already asserts its argv, skills@<version> included,
equals the builder's.
* refactor(skills): validate the build commit once, where it is resolved
resolveBuildSourceCommit already refuses anything but a full hash, and both the
define and the CLI's stamped package.json come from it. The builder's second
check could only fire as a module-load throw in the renderer's command
constants. Also drops the resolver's unused cwd parameter.
* test: stamp a fixed build commit instead of running git in the vitest config
Tests only interpolate ORCA_BUILD_COMMIT, so they need not read HEAD (or fail
outside a git checkout) on every run.
* refactor(settings): match only skills add in the Windows npx preflight
Every official skill command is now the builder's skills@<version> add; no
rail emits skills update any more.
* fix(skills): fail an update that leaves a broken skill link behind
Eligibility already skips names with a broken link before the run, so one
afterwards is the run's own damage; report it instead of claiming success.
* ci(release): refuse to tag when the skill manifest is stale
The removed --release step implicitly checked the committed manifest against
skills/. Keep that guarantee with a verify-only run; it writes nothing.
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
* feat(native-chat): add a chat-owned command receipt store
Adds the agent_session_command_receipts table and an insert-if-absent
receipt write that runs inside a command's own journal transaction. A
receipt records the command's identity, canonical hash, status and a
pointer to the row holding its result; it dies with its chat record.
Nothing uses it yet: the existing operation ledger stays authoritative.
* fix(native-chat): key command receipts like today's ledger ids
Rows are keyed by caller and operation id; a send looks across callers and
other methods look up only their caller's row, matching the existing ledger.
Bad rows and newer table shapes read as unreadable instead of throwing.
* fix(native-chat): order receipt lookups by insertion and key by operation id
Global lookups now take rows in insertion order, so a read and an insert
agree on which receipt answers an id regardless of the host clock. The
primary key leads with the operation id, so its own index serves both
lookups.
* fix(native-chat): accept sends through their own receipt
Send (direct, queued, steer) and /compact no longer commit a separate
ledger reservation before the message is saved. Their command receipt is
inserted-if-absent in the same transaction as the submission or queued
draft; a repeated id replays from it, a changed request conflicts.
Operation ids are checked for syntax only, so age, clock skew, pruning
and the reservation's own storage can no longer refuse a send.
The ledger success row they already co-committed stays as a temporary
insert-if-absent write so families not yet switched keep seeing these
ids; it is never read on this path.
* fix(native-chat): answer a send's receipt one way, share /compact's id space with Send, wake delivery once
- After its commit, a send whose publication fails answers through the same receipt replay a retry takes, so one id gets one answer; the in-memory copy and journal reload are gone.
- /compact ids are global like Send's: a retry under a new connection replays instead of needing a second acknowledgment write.
- The receipt route calls its runner directly; the runner wakes delivery when a new message row commits.
- The compatibility ledger row expires from host time; each temporary site is labelled.
- Receipt read errors and post-commit failures are logged.
* test(native-chat): expect receipt answers where tests assumed the send ledger
Restart-continuation tests now model the case Send cannot vouch for (saved but
not loaded into memory) and keep it unconfirmed; a later throw after the message
is in the chat answers continued, as a resend would. Drop /compact's unreachable
legacy replay branch and fix stale comments.
* test: run the queued tests that open the journal database in the Node runtime project
* fix(native-chat): queued-message actions and prompt answers accept through their own receipt
Send Now, Delete, Resume and prompt answers move onto the command-receipt route
Send uses: the receipt is inserted with the command's own write. A no-op answers
only once its receipt commits (otherwise outcome unknown), and a prompt answer's
replay reads the exact revision it was accepted at.
* fix(native-chat): replay a queued action from what its receipt recorded
Send Now's acknowledgement points at the submission it handed off; a question
answer's receipt keeps the accepted resolution; a no-op that cannot save its
receipt answers outcome unknown and holds nothing in memory; classified refusals
pass through.
* docs(native-chat): receipt comments say what a replay answers from
* fix(native-chat): refuse a prompt answer on a newer Orca's journal with the update words
A prompt answer now runs through the same newer-Orca refusal the queued-message actions use, so a read-only journal answers agent_session_journal_unreadable instead of throwing journal_read_only.
* test(e2e): record mixed-version, two-client and phone layout baselines on main (P3)
* test(e2e): P3 review fixes: reconnect and I/O proof, restart order bounds, Linux-only baselines
* test(e2e): re-record Linux phone and serve-restart baselines
* test(e2e): P3: echo drawn in the desktop pane; bound the group selection after restart
* test(e2e): re-record serve-restart group selection
* test(e2e): name P3 baseline helpers for what they record
Settings had two identical provider-scope types, a host-choice type that
copied RuntimeClientTarget, a scope-to-environment-id getter that copied
runtimeTargetEnvironmentId, and the remote-server/local label written
three times. Keep one of each. getProviderAccountScope now reads the
default host through defaultScopeHost, the sanctioned reader; output is
unchanged (trimmed id, blank means this computer).
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(providers): delete the SSH git and filesystem providers and relay-only wiring
* refactor(ssh): drop relay-protocol exports only the deleted stream readers used
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix: preserve module URLs in the headless server bundle
* test: exercise structured chat on the packaged Node server
* test(orcad): run the packaged chat check from the desktop's server template
The chat check loaded the server bundle directly, so a desktop build missing its
server template still passed. Materialize the slot from the built template the way
the desktop does, and replace the fixed streaming pause with a test-owned
acknowledgement so the partial-reply assertion can't race.
* Connect the packaged chat test through a paired connection
Main now sends remote runtime requests over a paired connection rather than a
pairing offer, so the test converts the server's offer once when it is ready.
Staging proved both on image F (2026-10-11): the sidecar keeps the director's
readiness through a Cloud SQL outage, and the event-loop stall alert reads
eventLoopDelayMsMax. Production adopts the shape on its serving image first.
* fix: preserve structured chat work during automatic server stops
* fix: require stop-request support before automatic cleanup
* fix: retain unverified session owners until exit is proven
* fix: preserve unreadable work and retry expired prompt stops
* fix: give unheld reservations and queued sends retirement exits
* fix: clear empty recovery leases and preserve unverified owners in tests
* Keep managed servers alive while chat providers are loaded
* Recheck returning clients before managed idle shutdown
* test: run the managed idle exit chat provider test in the Node runtime
It builds on the native chat rest test rig, which opens a real SQLite database.
* test(pty): pin pane-reservation launch races on both spawn lanes
* test(pty): pin failure-cleanup, commit-admission and spawn-guard launch races on both lanes
* test(pty): name launch-race fixture types so declarations stay portable
* test(pty): pin the runtime lane's hidden-mark rollback on a post-spawn throw
* test(pty): reshape launch race pins as per-lane tables and pin the review's missing drifts
* test(pty): pin the window lane's wait for a host create in progress
* test(pty): restore joiner-setup and early hidden-rollback pins; reword size and reattach notes
* test(pty): pare launch race pins to the cases later spawn-lane PRs rely on
Merge the four IPC race files into one, reuse the shared daemon provider
double, fold the host createTerminal guards and the window's create-wait
into existing specs, and drop pins of duplicated, unobservable or
already-decided behaviour.
* test(pty): drop the host early-hold pin that the claim-arbitration test already protects
Breaking the runtime lane's early pane reservation also fails
'reuses runtime materialization when renderer focuses the same pane during spawn'.
* test(pty): pin which lane overwrites a remembered size while a failed start builds
* test(launch): pin the window planner's quoting platform per workspace
* test(launch): pin the runtime a window pane sends on spawn
* test(launch): pin the host's quoting platform for repos and folders
* test(launch): pin the runtime assembler's shell per workspace
* test(launch): pin that a queued tab startup does not survive a restart
* test(launch): pin the quick composer's startup handed to workspace creation
* test(launch): pin the window-lane spawn assembler's shell, env and telemetry per launcher
* test(launch): pin what host-lane launches hand the terminal spawn
* test(launch): pin the spawn request desktop automations send, local and paired
* test(launch): pin typed-prompt, transcript-continue, OpenCode and paired new-tab launches
* test(launch): pin work-item quoting, create payloads, prompted paired tabs and the WSL cwd shell rule
* test(launch): type launch fixtures instead of casting them
* test(launch): drive both launch lanes to the provider from shared per-producer tables
* test(launch): pin rows 4, 5r, 7 and 8 through real producers and shared tables; fold duplicates
* test(launch): type the parity tables and harnesses
* test(launch): pin the pane identity keys main stamps on both lanes
* test(launch): drop a redundant cast and name the automation case fields
* test(launch): build every parity row as a real producer does; pin kitty, pane size, env values, hidden and paired-host spawns
Round-2 review fixes: local Windows ConPTY panes withhold the kitty flag, the pane's
measured grid reaches the provider, agent/caller env values and the hidden mark are
provider facts, rows 5r/7 run through the host's RPC handlers, SSH folder rows quote an
apostrophe, Windows clients use their real '' default shell, titled automations only,
the quick composer drives its real selection hook, and inputs no producer builds are gone.
* test(launch): drop the automation missing-folder row, which cannot fail
* test(launch): feed the composer hook its real eligible repos; type the paired RPC request
* test(launch): quick commands auto-submit, mobile agent.launch for row 10, real desktop caller, older-host seeding, local Windows folder composer, gone automation root
* test(launch): give each AI-button row the button that reaches its workspace kind
* test(launch): re-pin to main's execution-host platform: hosts report their OS, wsl$ and SSH folders quote for Linux, older paired hosts get the host's quoting
* test(launch): automations and window tabs share the execution-host quoting rule
* test(launch): give the test-only parity modules test suffixes
* test(launch): cut the parity tables to the rows a later plan PR checks against
Drop SSH rows (the SSH PTY provider is gone from main), rows whose behaviour is
already decided to change, rows existing tests already pin, and the hostAdmittedMembership
assertion main removed. Workspace-creation and paired new-tab producer tests move to C4.
Each remaining suite names the later PR that relies on it.
* test(launch): case runs pass full settings to the shared renderer fixture
The focus-read ratchet counts getDefaultSettings in a new test fixture; the suites
build the settings instead. Type the row 8 telemetry literal.
* feat(editor): run Nim and Typst on the shared TextMate layer with per-line limits
The editor's Nim and Typst highlighting moves from vscode-textmate onto the
Shiki core + Oniguruma layer chat uses, with the same per-line length cap and
time limit. A skipped line leaves the rest of the file plain instead of
freezing the window. Chat code block labels now name catalogue languages
(vue -> Vue, ts -> TypeScript).
* test(native-chat): expect the catalogue label for a ```ts fence
* ci: the layout oracle gates merges
An oracle-only job joins verify's needs. It runs only the two layout oracle
specs through the existing e2e workflow, routed by layout writer paths; a PR
outside the route skips it and verify treats the skip as a pass. The route is
widened to the terminal store, terminal pane, graph sync, frozen mirror and
compatibility folders while the layout-holder stack is open. The oracle specs
leave the advisory e2e job so a PR does not run them twice.
* ci: the layout oracle tests the merge commit and routes its harness and stack writers
Review fixes: run on the merge commit so a stale-base PR cannot pass on an old
main; route the oracle's own e2e harness and runtime client (a change there broke
main's oracle without running it), and the window-side layout writers the stack
edits; move the route into its own module.
* ci: route the remaining layout writers the stack edits to the layout oracle
* fix(native-chat): preserve pending diagrams and retire reveal history
* fix(native-chat): keep diagrams through theme changes and key reveal history by message id
- MermaidBlock: a result goes stale only when its source changes, so a theme switch keeps
the old diagram until the new one renders instead of flashing back to source.
- Reply reveals keep only loaded message ids (conversation rows plus subagent rows), since
only message rows reveal; drops the separate row inventory and restores nativeChatSlotKey
to the slots module.
- CommentMarkdownDocumentPre: drop the unreachable renderMermaid half of the fence check.
- Tests: Mermaid stays idle while streaming (waits past the async load), theme change keeps
the diagram, held renders are settled after each test, a reopened subagent section does
not replay its streaming reply through the real list, and retirement is tested by outcome.
* refactor(ipc): filesystem and repo handlers are local or runtime only
* test(ipc): drop two casts in the rewritten listing and import tests
* fix(providers): keep the default error name so IPC callers see the old text
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Orca-launched Codex on a headless runtime gets SSH_CONNECTION so Codex sends
OSC 52 to the viewing terminal instead of only the host's clipboard.
Fixes#23530
Co-authored-by: m4air <m4air@Mac.localdomain>
The layout oracle checks the saved profile's round trip at every settled step and the live layout's model rules on every published frame, which covers what this 1 s settle logger did.
* feat(web): the browser signs in with a non-extractable device key, or pairs for this session only
- On a secure page with WebCrypto Ed25519, the browser registers a non-extractable key
(stored in IndexedDB, create-exclusive across tabs) over a v2 token socket, then
reconnects with key auth and replaces its stored pairing code with a placeholder.
Requests on a keyed socket carry no token.
- On a plain-http page on another machine, or without Ed25519, existing stored pairings
keep working unchanged (with a warning); new pairings stay in memory and, when the host
supports it, register a session-only key.
- Only a sealed device_revoked naming this browser's key deletes it; a downgraded host,
a bad proof or host storage trouble keep it.
- The shared handshake core gains an async device-key credential (v2 only).
* fix(web): mark web sockets, bind keys to their pairing, survive a broken key store
- Every web socket carries ?orca-client=web so hosts gate only the web bundle.
- A key record belongs to one pairing revision: a re-pair uses its fresh code.
- A code that never migrated connects without reading IndexedDB; a migrated
pairing whose key store fails retries instead of going dead.
- A tab that finds the key already active forgets its code, and saving a stale
copy of the same pairing never puts the code back over a placeholder.
* fix(web): a stale tab never deletes or overwrites a newer pairing
A key record from a newer pairing revision is left alone (the tab stays on its
code), and saving an older revision never replaces a newer stored pairing.
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
* fix(native-chat): offer restart resume for chats whose only work was background
The quit started the chat teardown and then stopped the hook server, which
emptied the status store holding each chat's subagents and background
commands. The teardown reads that store to decide which chats were still
working, so a chat whose turn had finished but whose background command or
subagent was still running was never offered to resume after a restart or
update. The quit now stops hook ingress without clearing the store.
* test: preserve background-only chat resume across shutdown