Commit Graph

81255 Commits

Author SHA1 Message Date
Peter Steinberger 7a3f40c4ac feat(tts): add structured reply speech fields (#124913)
* feat(tts): add structured reply speech fields

* fix(tts): keep transcript directive parser acyclic

* fix(tts): drop unused directive fact export

* fix(outbound): preserve reply-payload metadata through response prefixes

The response-prefix rebuild spread the payload without its metadata carrier,
dropping structured speech facts (and any other payload metadata) whenever a
channel responsePrefix applied. Rebuild via copyReplyPayloadMetadata.

* fix(tts): keep voice-only sends visible when the speech runtime is cold

A structured voiceText-only send with no visible text returned unchanged from
the cold-runtime guard, so delivery normalized the empty payload to null and
the send silently vanished. The cold exit now applies the same visible
fallback as synthesis failure: explicit spoken text materializes as payload
text when no other content exists.
2026-08-16 18:43:43 -07:00
Peter Steinberger 78beccf053 feat: add cloud worker profiles and machine selection (#124864)
* feat(workers): add per-dispatch machine classes

* feat(ui): add cloud worker machine picker
2026-08-16 18:37:00 -07:00
Peter Steinberger 759e9cafec fix(ci): derive plugin SDK boundary cache inputs (#124897) 2026-08-16 18:35:58 -07:00
Peter Steinberger b65879e4b6 fix: deliver requested image artifacts across agent runtimes (#124910)
* fix: distinguish image inspection from delivery

Clarify that image-reading tools only load private model context, prefer native Codex image inspection when available, and preserve explicit outbound tool media across Claude CLI loopback runs.

Co-authored-by: pash <pash@openai.com>

* fix: propagate CLI vision capability

* chore: refresh CI after main fix

---------

Co-authored-by: pash <pash@openai.com>
2026-08-16 18:32:49 -07:00
ClawSweeper 327974fa2d improve(ui): simplify widget preview chrome (#124883)
Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
2026-08-16 18:30:55 -07:00
Peter Steinberger ab5611f0be fix(security): prevent approved scripts from changing before execution (#124858)
* fix(security): bind exec approvals to mutable operands

* test: align approval fixtures with operand binding

* fix(security): close durable operand binding gaps

* fix(security): preserve independent exec policy grants
2026-08-16 18:26:42 -07:00
Peter Steinberger 6575733bc0 fix(config): reject scalar config roots instead of loading defaults (#124945)
A config file whose JSON5 root parsed to a scalar (null, a number, a
bare string — the classic truncated/clobbered file) hit a special case
in loadConfigFromContext that returned {} with a snapshot marked
valid: true. Three failures in one: the process silently ran with
defaults while the operator's real config (channels, auth, allowlists)
disappeared; the valid snapshot could promote the corrupt file's
fingerprint as lastKnownGood, poisoning the clobber-recovery machinery
that exists to catch exactly this; and the load path contradicted the
snapshot path, which correctly reported the same bytes invalid.

Root cause: an early-return that collapsed invalid input into the
empty-config success shape. Deleted; a scalar root now flows into
validateConfigObjectWithPlugins, fails schema validation, records a
valid:false snapshot, and throws INVALID_CONFIG — same as an array
root, same as the snapshot path, handled by doctor's invalid-config
recovery.

Regression: new io.scalar-root.test.ts (null/number/string roots all
throw INVALID_CONFIG; snapshot agrees invalid) — all 3 fail pre-fix.
Updated the compat warning-fingerprint test that relied on null-root
loading as defaults.
2026-08-16 18:19:11 -07:00
Josh Avant c1d3b33aa7 fix: honor provider timeouts during stuck-session recovery (#123877)
* fix(diagnostics): respect active model request timeouts

* chore(diagnostics): keep recovery activity internal

* test(diagnostics): await stream wrapper results

* fix(diagnostics): harden provider timeout recovery

* fix(diagnostics): track compaction model calls

* test: prove provider timeout recovery boundary
2026-08-16 18:18:29 -07:00
Peter Steinberger a0698d27c2 test(ui): split chat follow-up E2E suite (#124938) 2026-08-16 18:04:07 -07:00
Peter Steinberger 9b5950874f refactor(sdk): source wire models from gateway-protocol schemas (#124943) 2026-08-16 17:49:42 -07:00
Peter Steinberger d3447dfc2b fix(cli): show actionable guidance for mistyped commands (#124892)
* fix(cli): render unknown commands as guidance

* fix(cli): keep parse JSON terminal-neutral
2026-08-16 17:47:27 -07:00
Peter Steinberger df123a1979 test(mistral): wait on delivered transcripts after reconnect (#124935)
* test(mistral): wait on delivered transcripts after reconnect

* test(nvidia): assert the remaining catalog fetch budget as a bound
2026-08-16 17:45:17 -07:00
Peter Steinberger f061fc24f9 fix(plugins): keep migration metadata authoritative (#124924)
Restrict exact config identity trust to prepared runtime generations and force startup convergence to verify against a fresh authoritative plugin snapshot.
2026-08-16 17:43:15 -07:00
Peter Steinberger 2d0b470885 test(ui): assert steer order by rendered position (#124905) 2026-08-16 17:42:34 -07:00
Peter Steinberger 907f5bacaa fix(agents): refuse add when prompts cannot run (#124940) 2026-08-16 17:41:00 -07:00
Vyctor H. Brzezowski c299c0da31 fix(ui): remove dashboard card text underlines (#124939) 2026-08-16 21:38:45 -03:00
Peter Steinberger 6205cf3bb3 fix(cron): stop advertising inactive JSON defaults (#124903)
* fix(cron): make --json help match behavior

* fix(cron): preserve scratch write JSON output

* test(cli): classify cron JSON result commands
2026-08-16 17:32:49 -07:00
Peter Steinberger 6405a59c45 fix(doctor): stop reporting phantom shared auth migrations (#124929) 2026-08-16 17:23:49 -07:00
Peter Steinberger e95300fa3e refactor(webui): read slash commands through the chat metadata store (#124931) 2026-08-16 17:20:59 -07:00
Peter Steinberger 562ac194e0 fix(doctor): preserve migrated session owner (#124928)
Amp-Thread-ID: https://ampcode.com/threads/T-01a00a6a-b64e-74a5-8b15-2d3b966a468d

Co-authored-by: Amp <amp@ampcode.com>
2026-08-16 17:19:52 -07:00
Peter Steinberger f7b372a49f fix(plugins): restore caller-owned workspace plugin-root projection
#124844 made resolvePluginMetadataSnapshot synthesize workspacePluginRootPresent
itself with a process-memoized fs.existsSync probe whenever a caller did not
supply it. That fact is owned by the prepared-model-runtime lease, which resolves
it only for real agent runs (resolveWorkspacePluginRootPresence) and passes it
explicitly, so the probe silently turned an opt-in fast path into the default for
unrelated control-plane callers.

Gateway startup config validation is one of them. It never asserts the fact, so it
began projecting the published lifecycle graph instead of loading a fresh one, and
the projection derives configFingerprint from that graph rather than a real load.
Startup convergence rewrites the persisted plugin index between the two reads that
form the migration checkpoint identity, so the pre-convergence read projected while
the post-convergence read loaded. The two pluginMigrationFingerprint values differed
and the gateway refused readiness with "OpenClaw plugin migration inputs changed
during startup convergence", failing every config-patch restart.

Deleting the probe restores one owner for the fact and a net-negative production
diff. The agent-side hunks of #124844 are untouched: they still pass the fact
explicitly from the lease.

Root cause: admission fact manufactured outside its lifecycle owner.
Owner boundary: src/agents/prepared-model-runtime-lease.ts owns workspace
plugin-root presence; src/plugins/plugin-metadata-snapshot.ts only consumes it.
Production LOC: -18.

Fixes red main ci-gate: QA Smoke memory-dreaming-sweep, matrix-restart-resume,
matrix-post-restart-room-continue.
2026-08-16 17:18:50 -07:00
Peter Steinberger 3d152540e3 fix(android): remove completed plan strikethrough (#124916) 2026-08-16 17:18:12 -07:00
Peter Steinberger 01e6bef816 test(gateway): make archive lifecycle waits deterministic (#124923) 2026-08-16 17:13:10 -07:00
Peter Steinberger 54e50bad5a refactor(ui): simplify chat history pagination (#124921) 2026-08-16 17:06:18 -07:00
Peter Steinberger e2cefa8fe4 fix(gateway): terminalize stale worker dispatches (#124920) 2026-08-16 17:04:39 -07:00
Peter Steinberger bda2d54dcf docs(codex): clarify loop relay behavior (#124918) 2026-08-16 16:57:43 -07:00
felirami 1e7874ef14 fix(plugins): adopt root context engines on caller-owned handles (#122457)
Keep caller-owned agent runtime registries in discovery mode so full-only
plugins cannot replace process-global sandbox backends. Copy runtime
context-engine factories from the composition-root registry instead.

Co-authored-by: Josh Lehman <550978+jalehman@users.noreply.github.com>
2026-08-16 16:53:35 -07:00
Peter Steinberger c520e666f8 fix(test): create sessions in default Control UI mock (#124912)
Amp-Thread-ID: https://ampcode.com/threads/T-01a00b6b-e4e9-74af-bb31-30363fae6c89

Co-authored-by: Amp <amp@ampcode.com>
2026-08-16 16:53:18 -07:00
Peter Steinberger de217e4990 fix(backup): give backup create actionable output errors (#124894)
* fix(backup): give backup create actionable output errors

* fix(backup): preserve output errors for debug
2026-08-16 16:45:27 -07:00
Peter Steinberger fa642a305e fix(ui): drop duplicate plan card summary (#124895) 2026-08-16 16:45:23 -07:00
Peter Steinberger 9cc742eb23 fix(ui): add spacing between suggested task actions (#124900)
* fix(ui): separate suggested task card actions

* test(ui): keep task suggestion spacing check lint-safe
2026-08-16 16:42:58 -07:00
Peter Steinberger 2740d9cc95 fix(codex): prevent app-server leaks after desktop fallback (#124885)
* fix(codex): reuse app server after managed fallback

* test(codex): opt relay fixtures into loop detection
2026-08-16 16:36:40 -07:00
Peter Steinberger 1d2d44c8ff perf(ci): price agents-core-models from its measured hybrid wall
`agentic-agents-core-models` carried the 36s scaled estimate while measuring
56.3s (n=6, p90 58.6s) across 260 compact jobs on 2026-08-16. Packed beside
`agentic-agents-core-runtime-hosted-1` (60.4s) it built the only bin running
>=1.25x its prediction: compact-large-19 ran 122s of work priced at 88s, which
made it the most frequent slowest job in the workflow.

Add the measured value to the hybrid hint map, which already exists for exactly
these Blacksmith-specific observations. The packer then separates the two, and
the tallest actual compact bin drops 122s -> 109s with no bin above 1.25x and
no change to row counts (48 push / 56 pull-request), so this costs no extra
runner registrations.

Two things deliberately left alone. The `agentic-gateway-core-3` 140s pin looks
like a 2x over-prediction against its 68.5s median, but run 31938297538 really
did take 138.0s (109.03s + 28.98s across its two configs) on a run whose fleet
slowdown factor was 0.98 -- a genuine tail on a healthy run, so unpinning it
would rebuild a >200s bin whenever the tail lands. Every other bin already sits
within 1.24x of its prediction.

The replaced guard pinned one bin arrangement (`runtime-hosted-1` not sharing a
job with `agents-core-tools`) with no stated failure mode, so any honest refit
broke it. It now asserts the property that comment was reaching for -- both
weight sources survive rebalancing under the body ceiling -- plus the specific
regression this fixes. Mutation-checked: dropping the hint fails the new guard.

Measurement note for the next refit: sum a shard's per-config Duration lines
before taking a median. Pooling them reads as a large over-prediction that is
not there.
2026-08-16 16:34:12 -07:00
Peter Steinberger fecb377da6 fix(doctor): partition retired session stores (#124846)
Recover owner-qualified rows from the retired top-level session store into each configured agent database without guessing ambiguous ownership. Relocate stale transcript paths, tolerate historical Unicode IDs when an explicit transcript exists, and avoid double-counting physical SQLite databases in migration totals.


Amp-Thread-ID: https://ampcode.com/threads/T-01a00a6a-b64e-74a5-8b15-2d3b966a468d

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Josh Lehman <josh@martian.engineering>
2026-08-16 16:33:28 -07:00
Peter Steinberger 22ab8e3c2c fix(gateway): stop a plugin dangerous flag from revoking desktop computer.act (#124863)
* fix(gateway): stop a plugin dangerous flag from revoking desktop computer.act

`resolveNodeCommandAllowlistInternal` subtracted every plugin-declared
dangerous command from the composed allowlist, including core's own
`PLATFORM_DEFAULTS` entries and including the pairing allowlist, where core's
dangerous defaults are exempted. `computer.act` is both a desktop platform
default (grant = node-local enablement + pairing approval) and a command that
`registerComputerUseProvider` marks dangerous. Since `cua-computer` became
enabled-by-default on darwin, every macOS-hosted Gateway stripped `computer.act`
from both allowlists, so `normalizeDeclaredNodeCommands` dropped it from the
node's declaration with no pairing upgrade, no prompt, and no record. The
`computer` capability survived because caps were never allowlist-filtered, and
the `computerUse` descriptor was then dropped for the missing command.

Scope the plugin-dangerous subtraction to commands outside the platform-default
base: the flag still keeps a plugin's own surface behind an explicit allow and
still forces a registered invoke policy, but it no longer revokes a command core
declares itself.

Also make cap-without-command unrepresentable. `retainFulfilledNodeCapabilities`
drops a capability when policy withheld commands from its family and admitted
none, and the reconciler records the withheld commands so a refused declaration
is never silent.

* test(gateway): type the computer-use fixture against its contract
2026-08-16 16:27:45 -07:00
Peter Steinberger 4f92cab8b4 fix(ui): keep message presentation stable when expanded (#124898) 2026-08-16 16:27:40 -07:00
Peter Steinberger 0e280f2d33 feat(agents): record agent creation provenance and add roster tree listing (#124828)
Adds an additive agent_provenance table (shared state DB, schema v8) owned
by src/state/agent-provenance.ts. createAgent() records operator/agent
provenance after commit, the system-agent create-agent operation passes its
own id as creator, and Claw installs record created-via claw at their roster
commit point. Agent deletion removes the agent's own row inside the deletion
journal transaction; children keep dangling creator ids as historical fact.
openclaw agents list gains --tree (provenance hierarchy) and JSON provenance
fields.
2026-08-16 16:25:44 -07:00
Peter Steinberger edd9234e54 fix(sessions): report self-archive before stopping the turn (#124896) 2026-08-16 16:25:27 -07:00
Peter Steinberger ed339f9ba3 fix: remove delivery markers from historical sessions (#124888)
* fix(sessions): migrate historical transcript directives

* fix(infra): complete directive migration on pre-archives-schema databases

session_transcript_archives is a lazy additive v17 surface; valid databases
may omit it. The archives phase now treats the missing table as empty and
completes the cursor instead of warning-skipping the whole database forever.
Verified against a real long-lived agent DB: previously stuck cursor
completes with zero warnings in ~100ms.

* chore(ci): allow directive migration SQLite access
2026-08-16 16:24:46 -07:00
Peter Steinberger 5bf9fbe387 fix(tui): report the first last-session write failure (#124886)
* fix(tui): report the first last-session write failure

rememberCurrentSessionKey swallowed writeTuiLastSessionKey failures
with catch(() => undefined). A corrupt or locked state DB silently
disabled session restore: the operator only discovers it on next
launch, landing in the wrong session with no clue why.

Root cause: fire-and-forget with no recorded failure. The write wrapper
moved to its owner (tui-last-session.ts) as
createRememberSessionKeyWriter, which surfaces the first failure via
the chat log ('session memory write failed: ...') and stays silent on
repeats — one line per TUI run, not one per session switch. Empty and
'unknown' keys still skip without touching the writer.

Regression: colocated tests prove first-failure-once reporting and the
skip path — fail pre-fix (factory did not exist; failure was
swallowed at the call site).

* chore(tui): make the last-session write dependency explicit

deadcode:exports rejects writeTuiLastSessionKey as unused when only the
factory default referenced it; tui.ts now passes it explicitly.
2026-08-16 16:23:01 -07:00
Peter Steinberger 5dc4cf602b fix(sandbox): surface list probe failures instead of empty results (#124881)
sandboxListCommand and fetchAndFilterContainers swallowed backend and
registry probe failures with catch(() => []). A broken Docker daemon or
corrupt registry rendered as 'No sandbox runtimes found.' — and in
recreate, filtered an entire fleet down to nothing — indistinguishable
from a clean empty state. --json emitted a success envelope for a
failed probe.

Root cause: failure collapsed into the empty success shape at the
consumer. The listers now propagate; the sandbox CLI runner already
owns the error path (message + exit 1), and the new --json failure
contract (6c66f48a7c) emits the JSON error envelope.

Regression: replaced the test that enshrined the masking ('handle
errors gracefully' asserting the empty-state message) with the
propagation contract — fails pre-fix.
2026-08-16 16:18:18 -07:00
Peter Steinberger b8a95e082b fix(ui): make terminal open failures retryable (#124890) 2026-08-16 16:15:24 -07:00
Peter Steinberger 00990506c0 fix(tui): close selection overlays and report rejected handlers (#124876)
openSelector's onSelect fired the async handler with void and only
closed the overlay after a successful await. A rejecting handler (e.g.
setAgent -> setSession against a failing gateway) left the selector
stranded open with an unhandled rejection: the TUI froze on the picker
with no visible cause — a silent dead-end.

Root cause: failure path missing from the overlay lifecycle. The
handler now catches, surfaces the cause via chatLog, and always closes
the overlay.

Regression: /agent selection with a rejecting setSession asserts the
overlay closes and the cause reaches the chat log — fails pre-fix.
2026-08-16 16:14:28 -07:00
ClawSweeper a4efa7c22b fix(ui): replace the broken terminal tab underline (#124875)
* fix(ui): redesign dock panel tabs

* fix(ui): remove stale terminal tab overrides

---------

Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
2026-08-16 16:11:30 -07:00
Peter Steinberger 572e9f907a fix(skills): expand explicit references on agent turns (#124784)
* fix(skills): expand explicit references on agent turns

Route generic Gateway, CLI, webhook, and local agent turns through the same explicit skill-reference renderer as channel auto-replies. Keep original transcript text, preserve unknown slash behavior, and fail visibly for allowlist-hidden skills.

Maintainer review: scoped Option 1 — generic agent turns expand both $skill-name and leading /skill-name args through shared skill rendering; they do not run the channel command dispatcher, and all other slash commands retain their existing behavior.

* fix(skills): bound explicit reference prompts

* fix(skills): prefer allowed reference collisions

* fix(skills): preserve command invocation boundaries

* fix(skills): reject hidden channel slash commands

* perf(skills): skip literal dollar discovery
2026-08-16 16:09:21 -07:00
Peter Steinberger 9cbf998c72 fix(cli): keep internal error causes out of operator output (#124887) 2026-08-16 16:08:56 -07:00
Peter Steinberger 06fa48099e fix(ui): reveal clipped session history (#124866)
* fix(ui): reveal clipped session history

Show a persistent earlier-history action for paginated sessions and reveal loaded content without chaining observer fetches.

Related: #110771

* refactor(ui): split chat history support
2026-08-16 16:04:30 -07:00
ClawSweeper 6bddfed530 fix: prevent clipped session selections and Code Mode timer crashes (#124879)
* fix(agents): support timers in code mode

* fix(ui): keep selected sessions clear of scrollbars

* fix(agents): propagate timer cancellation after resume

---------

Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
2026-08-16 16:03:54 -07:00
Josh Avant ffc94d0ff0 fix: stop forked commands after Gateway timeouts (#124081)
* fix: stop service child trees before timeout completion

* fix: preserve service child root results during cleanup

* test: isolate relay selection from linux spawn wrapping

* refactor: keep service anchor payload internal

* test: validate lifecycle probe pid output

* fix: close service child adversarial races

* fix: preserve service startup failures

* fix(process): flush service output before root result

* fix(process): bound cleanup on held output

* chore(process): satisfy promise executor lint

* fix(process): finish cleanup after lineage loss

* fix(process): retain relay output before subscription

* fix(ci): register service child runtime entries

* fix(ci): expose relay safety invariants

* fix(process): preserve service cancellation grace

* test(process): prove service grace on macOS
2026-08-16 16:02:34 -07:00
Peter Steinberger 1f1aa88f14 perf(ci): split Windows CI two ways instead of one serial lane
`checks-windows-node-test` ran the whole 222-266s Windows body in a single
Blacksmith lane, so every run that scheduled it finished at 287-334s against a
~180s plateau. Measured across 45 canonical main runs it was the wall in all
seven runs that triggered it (324-519s vs a 210s median elsewhere).

Blacksmith's Windows class admits exactly 2 concurrent jobs (run 31865243804),
which is why the earlier 3-part split failed: its tail queued 211s behind a
finished part. Pin the split width to that measured capacity instead. The two
parts are balanced by measured per-project wall time -- part 1 keeps the old
list 1 plus list 2's non-infra projects (~108s), part 2 takes list 2's infra
project plus the old list 3 (~112s) -- so both land near 113s and the lane
stops setting the wall.

One canonical partition now serves every backend; the github-mode/dispatch
3-part variant and its separate file inventory are gone, along with the
single-lane `task: test` case and the per-row `matrix.runner` the collapsed
lane needed. The complete 65-file Windows inventory is preserved exactly, and
the guards pin the width on both sides (mutation-checked: restoring 3 parts
fails the matrix guard).
2026-08-16 16:02:24 -07:00