* fix(agents): session quota suspension resolves the real owning agent
suspendSession derived the agent id from path.basename(agentDir), which is
always the literal 'agent' in the default <state>/agents/<id>/agent layout —
the quota-suspension marker landed in a nonexistent agent's store and the
suspension silently no-opped. Callers now pass the explicit agentId through
runWithModelFallback/throwFallbackFailureSummary, with the agent-dir registry
as fallback for dir-only callers.
* fix(agents): embedded failure suspensions carry the run's agent id
…$ref anchors
resolveLocalRef decoded plain-anchor fragments with a bare
decodeURIComponent while the pointer branch already tolerated bad
escapes via decodePointerSegment's try/catch. A schema carrying
$ref: "#%" (a single typo'd hand-written anchor) made
findJsonSchemaShapeError throw a raw URIError that escaped
validateJsonSchemaValue, crashing plugin/channel config validation,
doctor, and gateway startup with a message that pointed nowhere near
the offending schema.
Decode anchor fragments with the same tolerance: a malformed escape
now resolves to { found: false } and surfaces as the intended
"<path>.$ref: unresolved ref" diagnostic.
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
* refactor(test): exercise real session rewind policy
* refactor(test): use real Telegram media policy
* refactor(test): exercise real HTTP cancellation lifecycle
* test(gateway): prove mutations clear queued session work
* feat(status): render /status as native rich tables on channels that support them
* feat(status): tighten rich /status layout into a titled native table card
* feat(status): trim rich /status tail to one clock-and-uptime context line
* feat(status): context meter, hot-window warning, and default-noise trim in rich card
* test(status): satisfy SessionEntry sessionId in meter fixture
* fix(telegram): gate rich table islands off legacy HTML sends and cover payload sends
* test(telegram): split outbound-adapter presentation tests to satisfy max-lines
* fix(outbound): make presentation capability resolution formatting-aware
* fix(telegram): defer presentation canonicalization on rich accounts until send
* feat(status): one fact per line in the plain status body
* feat(status): group the plain status body into blank-line sections
* feat(status): lead the rich status card with the version title
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(agents): make subagent hard-deny list non-overridable and deny message tool
The always-deny list for subagent sessions (gateway, cron, message, sessions_send,
conversations_*) could be overridden by ordinary allow/alsoAllow config entries,
letting a configured subagent profile re-enable direct user delivery outside the
announce chain. The hard-deny layer now applies unconditionally; message joins the
list so resumed/visible subagent sessions cannot send directly either (hidden
launches already disabled it at spawn time).
* chore: re-fire CI
* chore: re-fire CI against fixed main baseline
* test(agents): workspace authority reflects non-overridable subagent deny list
The delegating-worker rejection case relied on alsoAllow bypassing the
subagent hard-deny list; with the bypass closed the policy owner blocks
sessions_spawn and the worker stays confined, so the guard has nothing to
reject.
* fix(gateway): redact credentials from gateway URLs in status and logs diagnostics
Gateway URLs can carry basic-auth credentials or token query params. Status
output, status-all JSON, and logs-cli error diagnostics now project connection
details through a single diagnostics projection (projectGatewayConnectionDetailsForDiagnostics /
projectGatewayUrlForDiagnostics) so no diagnostic surface prints a raw URL;
the probe itself keeps using the unredacted URL.
* chore: re-fire CI
* fix(gateway): close remaining credential-leak paths in status diagnostics
ClawSweeper P1s: probe failure text (close reasons/transport errors) now
redacts URL-like credentials before status renderers print it; the status-all
remote-missing fallback line projects the environment gateway URL; gateway
transport-error JSON redacts the remote-controlled close reason and derived
message.
* fix(cron): retry on-exit watcher spawn/wait failures with backoff
A transient supervisor failure while arming or waiting on an on-exit watch
previously dropped the watch with only a log line: the job would never fire
and nothing recorded why. Failures now persist lastError/consecutiveErrors on
job state via the gateway cron service and re-arm with bounded backoff
(1s/5s/30s/5m); cancel clears any pending retry timer.
* chore: re-fire CI
* fix(cron): guard watcher-state writes against replaced on-exit jobs
ClawSweeper P1: an old watcher's failure write could land on a job that was
edited or converted to a different schedule, pushing the replacement into
failure backoff or auto-disable. Watcher-state persistence now uses the same
identity precondition as persistCompletion (enabled, on-exit, updatedAtMs
match) and treats a mismatch as a no-op.
* feat(plugins): support the Agent Plugins bundle format
* docs(plugins): document the Agent Plugins bundle format
* test(agents): preserve agent bundle runtime discovery
* fix(plugins): isolate Agent Plugins data-dir failures and align MCP support reporting
* docs(plugins): list Agent Plugins in the canonical plugin-format guides
* fix(plugins): gate Agent Plugins detection on schema, pure inspection, root-relative cwd
* fix(plugins): record Agent Plugins data-dir ownership explicitly
* docs(plugins): cover Agent Plugins in the CLI install detection guide
* fix(plugins): carry Agent Plugins data-dir and transport contracts through external MCP projections