Commit Graph

44091 Commits

Author SHA1 Message Date
Peter Steinberger 907f5bacaa fix(agents): refuse add when prompts cannot run (#124940) 2026-08-16 17:41:00 -07: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 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 01e6bef816 test(gateway): make archive lifecycle waits deterministic (#124923) 2026-08-16 17:13:10 -07:00
Peter Steinberger e2cefa8fe4 fix(gateway): terminalize stale worker dispatches (#124920) 2026-08-16 17:04:39 -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 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 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 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 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 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
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
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 57b1a69167 fix(state): report v9 registry migration row decisions (#124862)
* fix(state): report v9 registry migration row decisions in doctor and logs

* fix(state): consume registry migration summary type

* fix(state): avoid registry path observer import cycle
2026-08-16 15:52:42 -07:00
Peter Steinberger 72ddf22b93 fix(qa): recover repeated gateway restarts through channel ingress (#124746)
* fix(qa): repair repeated gateway restart recovery

* test(qa): satisfy restart recovery checks

* fix(audit): rekey repeated recovery identity

* fix(plugin-sdk): expose authored context cap

* test(android): synchronize process tree readiness
2026-08-16 15:47:31 -07:00
Peter Steinberger 358c06ec95 fix(gateway): persist agent turns before acknowledgement (#124857)
* fix(gateway): persist agent turns before acknowledgement

* fix(gateway): revalidate exec approval handoffs

* docs(gateway): clarify durable session ownership

* fix(gateway): reject stale transcript admission targets
2026-08-16 15:42:49 -07:00
Peter Steinberger add30d455d refactor(security): consolidate path containment onto canonical fs-safe guard (#124870)
* refactor(security): expose canonical path containment modes

* refactor(agents): use canonical path containment guard

* refactor(security): consolidate core path containment sites

* refactor(plugins): consolidate path containment sites

* test(security): cover canonical path containment behavior
2026-08-16 15:37:02 -07:00
Peter Steinberger 9b468ccaa2 test: replace assertion chains with typed fixture builders — wave 2 (#124865) 2026-08-16 15:36:45 -07:00
Peter Steinberger 2d3612da6b fix(gateway): preserve prepared plugin metadata under load (#124844)
* fix(gateway): preserve prepared plugin metadata under load

* fix(gateway): project metadata before catalog preparation

* test(agents): assert projected startup metadata

* test(gateway): bind load probes to each finalization wave
2026-08-16 15:14:39 -07:00
Peter Steinberger 6c66f48a7c fix(cli): emit one JSON failure contract for --json invocations (#124849)
* fix(cli): unify JSON failure output

* test(cli): update skills verify failure envelope
2026-08-16 15:09:58 -07:00
Peter Steinberger efdfb69adc fix(agents): keep queued followups from missing post-compaction context (#124850)
* fix(auto-reply): order post-compaction context before followups

Amp-Thread-ID: https://ampcode.com/threads/T-01a00b6b-e4e9-74af-bb31-30363fae6c89

* test(auto-reply): clean compaction workspace fixture

Amp-Thread-ID: https://ampcode.com/threads/T-01a00b6b-e4e9-74af-bb31-30363fae6c89

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-16 15:06:39 -07:00
Peter Steinberger 080887df92 refactor(types): discriminated unions for internal contracts — pilot lane A (#124845) 2026-08-16 14:49:28 -07:00
Peter Steinberger fb841993b1 perf(test): remove Gmail watcher shutdown wait (#124851) 2026-08-16 14:41:28 -07:00
Peter Steinberger 06101b1037 refactor(validation): consolidate boundary guards into schemas (#124820)
* refactor(validation): consolidate boundary guards into schemas — pilot lane B

* fix(meetings): reject non-object browser status
2026-08-16 14:41:14 -07:00
Peter Steinberger 33f3b72a19 refactor(plugin-sdk): extract stream and SecretRef primitives (#124835) 2026-08-16 14:30:11 -07:00
Peter Steinberger 86cfcd3833 fix(delivery): unify terminal settlement ownership (#124825)
* fix(delivery): unify terminal settlement ownership

Treat identityless adapter returns as potentially visible across channel, queue, and cron paths. Let recovery own terminal completion so ambiguity persists as notice debt instead of being double-settled or silently suppressed.

Co-authored-by: ruel225 <ruel225@users.noreply.github.com>

* refactor(delivery): narrow terminal internals

Remove now-unused internal exports after terminal-settlement ownership was consolidated.

* test(tts): preserve message runtime exports

Import and spread the actual message runtime so the focused mock retains every runtime binding while overriding only the durable send core.

---------

Co-authored-by: ruel225 <ruel225@users.noreply.github.com>
2026-08-16 14:28:26 -07:00
Peter Steinberger 8635f9cf03 fix(agent): preserve implicit roster in configless exec (#124805)
Amp-Thread-ID: https://ampcode.com/threads/T-01a00b7c-b9f0-73d5-8fb7-e619e8e458e1

Co-authored-by: Amp <amp@ampcode.com>
2026-08-16 14:27:29 -07:00
Peter Steinberger 2f98eeabfd feat(auto-reply): record message-tool mute outcomes (#124830)
* feat(auto-reply): record message-tool mute outcomes

* fix(auto-reply): require source delivery evidence

* test(infra): preserve historical agent schema fixtures
2026-08-16 14:26:20 -07:00
Peter Steinberger 313fcf1dba fix(channels): route direct DM replies to sender (#124837)
Default direct-message reply context to the authenticated sender address so targetless Reef and Nostr replies do not resolve to the local recipient.
2026-08-16 14:24:30 -07:00
Peter Steinberger 4aafed1f4a fix(media): bound image-optimize fetch headroom to the image cap (#124838)
When a caller passed an explicit maxBytes with optimizeImages on (the
default), the source-read bound was inflated to max(maxBytes, 100MB
document cap). A channel with a 5MB attachment cap therefore let a
hostile or oversized URL buffer up to 100MB before the final size check
— 20x resource amplification, and the inflation applied before content
classification, so it was not limited to images.

The inflation exists so oversized-but-compressible originals can be
fetched and compressed under the delivery cap. Size that headroom off
the thing that justifies it: 4x the 6MB image cap (24MB), enough for
large phone photos, instead of the document cap.

Accepted tradeoff (named at the constant): originals above 24MB that
would have compressed under the cap now fail; the fetch error names the
bound. Callers without an explicit cap keep per-kind defaults.

Live proof on a real localhost HTTP server (no mocked fetch): an 8.1MB
PNG under a 5MB cap compresses to 3.75MB and delivers; a 30MB-declared
image is rejected at the Content-Length precheck in 7ms with 'content
length 31457280 exceeds maxBytes 25165824'.

Regression: headroom-bound test fails pre-fix (30MB passes the old
100MB bound); companion test proves compression headroom still works.
2026-08-16 14:23:25 -07:00
Peter Steinberger 65922f9507 fix(gateway): share generated session titles with worktree names and start title work at dispatch (#124787)
Worktree sessions now derive their worktree/branch name from the same
concise generated title the sidebar shows (bounded 8s wait, raw-prompt
slug then crustacean fallback), persisted once as displayName so the
chat-send pass never duplicates the model call. Dashboard title
scheduling moves from the dispatch chain's finally (after the whole
first turn) back to dispatch start, so long or interrupted first turns
no longer leave sessions silently untitled (regression from #122471).
slugifyWorktreeTitle truncates at word boundaries; dead
generatedDisplayName plumbing removed.
2026-08-16 14:07:54 -07:00
Peter Steinberger 046a9ffdf4 fix(mcp): make Codex approval dead ends actionable (#124766)
* fix(mcp): make Codex approval dead ends actionable

* fix(mcp): preserve native approval fallback

* fix(mcp): configure saved approval modes

* fix(mcp): preserve saved Codex metadata

* style(mcp): simplify saved metadata spread
2026-08-16 14:00:08 -07:00
Peter Steinberger 5a27ebee67 fix(gateway): surface failed node event delivery (#124798)
* fix(gateway): surface failed node event delivery

* fix(plugin-sdk): preserve authored context cap
2026-08-16 13:57:19 -07:00
Peter Steinberger eb8005d7ce fix(gateway): stop closed clients from restoring session subscriptions (#124771)
* fix(gateway): reject closed session subscribers

* test(gateway): type subscription race params

* test(gateway): use public subscription assertion

* fix(ci): invalidate SDK declarations on attempt types

* chore(ci): align boundary cache comment with main
2026-08-16 13:54:16 -07:00
Peter Steinberger 0c6040eb14 fix(runners): align reclaim results with runtime (#124791)
* refactor(runners): prepare placement move targets

* fix(runners): preserve normalized dispatch targets

* style(protocol): format reclaim result type
2026-08-16 13:46:00 -07:00
Peter Steinberger 707d3f4adf refactor: split max-lines pilot files and retire suppressions (#124813) 2026-08-16 13:45:27 -07:00
Peter Steinberger 32f056f011 refactor(agents): persist directive facts at the assistant write boundary (#124793)
* refactor(agents): persist directive facts at the assistant write boundary

Assistant final text is now stripped of inline delivery directives once, at
the SessionManager append boundary, with the parsed facts persisted as a
typed openclawDelivery field on the assistant message (openclawDeliveryMirror
precedent, inside event_json — no schema change). Live session state is
replaced with the persisted canonical bytes so same-run continuations and
next-turn store rebuilds never diverge (prompt-cache invariant).

Delivery and restart recovery consume the stored facts instead of re-parsing
persisted text; pre-upgrade in-flight messages without facts intentionally get
no text-parse fallback (commented tradeoff at the read site). Directive
parsing is now code-region aware, so markers quoted in inline code or fences
neither parse nor strip — fixes the empty-code-pill display bug.

The undocumented reaction marker DSL ([[react:]], [[react_to_current:]]) is
deleted; structured message-tool reactions remain the canonical path. Stale
assertion-safety baseline entry removed with the parser.

Production net -10, tests net +38. Focused suites: 9,068 passed; pre-fix
regressions fail for the intended reasons. Codex autoreview clean.

* fix(agents): satisfy directive persistence gates

* fix(config): drop unused directive export

* fix(agents): preserve indented directive examples
2026-08-16 13:44:11 -07:00
Peter Steinberger a7433d92df fix(cli): restart disabled installed services (#124786) 2026-08-16 13:39:36 -07:00
Peter Steinberger 541f373bc0 refactor(validation): type producers and drop internal re-validation — pilot lane C (#124808) 2026-08-16 13:38:17 -07:00
Peter Steinberger 486b272e8c fix(ui): stop stale clients from acting connected (#124772)
* fix(ui): stop stale clients from acting connected

Carry reload metadata on terminal Control UI build rejections, render an explicit refresh-required recovery state, fence reconnect-only actions, and surface disconnected approval failures with accessible modal controls.

* fix(ui): preserve reconnecting session drafts

* fix(ui): preserve offline preference intent

* fix(ui): return owned fallback digest bytes

* test(ui): follow passive approval presentation

* fix(ui): distinguish stale builds from protocol mismatches

* style(ui): format gateway recovery phase
2026-08-16 13:38:09 -07:00
ClawSweeper 8277cb24a1 feat(tools): prompt proactive widget use (#124810) 2026-08-16 13:33:51 -07:00