* [AI] fix(agents): emit aborted tool results for skipped tool calls on mid-turn abort
When an abort fires mid-batch in executeToolCalls (after the assistant
message with tool_use is committed but before all tool_results are
written), the sequential and parallel dispatch loops break out and skip
the remaining tool calls. The committed assistant turn retains N tool_use
blocks but only M < N tool_results land in context.messages, leaving
orphaned tool_use that corrupts retries/continuation and triggers provider
400 errors on providers that do not synthesize missing results
(allowSyntheticToolResults=false, e.g. openai-completions/DeepSeek).
Emit aborted tool results (createErrorToolResult("Operation aborted")) for
the skipped tail in both executeToolCallsSequential and
executeToolCallsParallel so every tool_use keeps a paired tool_result.
This complements the existing write-side guard (which only covers
synthetic-enabled providers) and the persisted replay repair.
The aborted tail outcomes are routed through finalizeToolCallOutcome (via
a shared finalizeAbortedToolCall helper) so config.afterToolOutcome hooks
(audit, redaction, metadata, error-normalization) observe these skipped
calls just like every immediate or executed outcome, instead of bypassing
the outcome contract. Regression tests assert afterToolOutcome fires for
every skipped call in both dispatch modes.
Fixes#116379
Co-Authored-By: Maas <noreply@anthropic.com>
* [AI] fix(agents): emit tool_execution_start before aborted end for skipped calls
The abort-tail backfill added in #116379 emits tool_execution_end (and a
paired tool_result) for tool calls the dispatch loop never reached, but it
skipped the matching tool_execution_start. Channel/client subscribers that
pair start→end events received an end for an unknown tool-call id during
abort recovery.
Emit tool_execution_start for each skipped call before its aborted end/result,
mirroring the start event every dispatched (including immediate non-executed)
call already emits. Covers both sequential and parallel dispatch, with
regression assertions that every skipped call has a start before its end and
that start/end counts stay paired.
Co-Authored-By: Maas <noreply@anthropic.com>
* fix(agents): complete aborted tool tails safely
Fixes#116379
---------
Co-authored-by: Maas <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* test(process): reproduce Windows taskkill process-tree leak
* fix(process): stop leaked Windows child process trees
Escalate only when Windows taskkill reports that graceful process-tree termination failed. Preserve awaited taskkill completion, grace-period fallback, one-shot signaling, and PID-reuse protection.
Closes#110789
Supersedes #112202
Co-authored-by: Mohammed Alkindi <alkndymhmd692@gmail.com>
---------
Co-authored-by: Mohammed Alkindi <alkndymhmd692@gmail.com>
* feat(anthropic): add Claude Opus 5 model support
Wire claude-opus-5 through the Claude 5 contract seams: adaptive-by-default
thinking with the full low..max effort range, default-sampling and prefill
stripping, streaming refusal contract, model-bound thinking replay, and 1M/128k
catalog metadata across anthropic, claude-cli, Vertex, Bedrock, and Mantle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(anthropic-vertex): normalize explicit Vertex Opus 5 model rows
Extend normalizeAnthropicVertexResolvedModel so user-configured Vertex Opus 5
rows regain reasoning, image input, 1M/128k limits, and the native thinking
map; update the live-model priority expectation and regenerate docs_map.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(infra): harden network client errors, session short ids, and extension load caching
* fix(infra): preserve injected dispatcher seams and drop the uncached extension loader
* fix(kill-tree): verify process group leader before group kill to prevent gateway SIGTERM (#76259)
- Add isProcessGroupLeader() to killProcessTree/signalProcessTree: ps -p <pid> -o pgid= primary check with /proc/<pid>/stat fallback on Linux. Group kill only when the PID is its own process group leader; non-leaders fall back to single-pid kill, preventing accidental gateway SIGTERM when a non-detached child shares the gateway's process group.
- Propagate detached: true to all detached-spawn cleanup callers (exec-termination, agent-bundle LSP, mcp-stdio, bash, supervisor pty, agent-core nodejs) so detached group cleanup survives leader exit.
- Gateway/daemon cleanup paths (schtasks, restart-health) keep the leader-checked default (detached omitted).
Closes#76259
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(process): tighten process-group ownership checks
* refactor(daemon): split restart diagnostics
* refactor(daemon): isolate restart health types
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(tooling): extend strict-ratchet lane to all remaining leaf packages
* fix(packages): burn down indexed-access debt in ai and agent-core
All 196 noUncheckedIndexedAccess errors fixed behavior-identically
(iteration, .at()/slices, DataView byte math, guarded queue peeks) plus
cleanup finds: a sparse-tool diagnostic bug, deduplicated Responses
call/item ID parsing with an honest string|undefined splitter, and a
clearer local failure for redacted-thinking blocks missing signatures.
Removes all 17 remaining non-null assertions across ai, gateway-client,
and llm-core so the assertion ban aligns 1:1 with the ratchet lane.
speech-core joins memory-host-sdk as structurally deferred (imports
src/** via plugin-sdk path mappings).
* fix(agent-core): keep file info basename on Windows paths
* fix(agent-core): use platform path basenames
* chore: sync PR branch before changelog
* docs(changelog): credit Windows file metadata fix
* fix(agent-core): use platform path basenames
* test(agent-core): isolate Windows shell discovery
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
* fix(agent-core): handle stdout/stderr stream errors in harness exec
Register error handlers on stdout and stderr streams in
NodeExecutionEnv.exec() to prevent uncaught exceptions when
a pipe breaks (e.g. EPIPE after child process exit).
Without these listeners, Node.js throws an uncaught exception
that crashes the entire agent-core process.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agent-core): address review — hoist-safe mock and curly lint
- Use vi.hoisted pattern for spawnMock to fix ReferenceError in CI
- Wrap one-line if guard in braces per repo curly rule
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: keep release notes in PR context
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
The active user turn was decorated at request-build time with an inbound
metadata block (Conversation info / reply-target / sender / forwarded /
chat-history), but the session store persists the bare text and the LLM boundary
strips that block from historical replay. So a user message's serialized bytes
changed retroactively — decorated as the active turn, bare as history — on every
turn, invalidating any prefix/exact-match provider prompt cache from that point
onward, every turn. (#90811 fixed the timestamp half of this same asymmetry.)
Rework of the earlier "strip the active turn" approach (which lost model-visible
context — ClawSweeper's P1) into one that keeps the context AND recovers the
cache cost:
- Route current-turn inbound metadata out of the user prompt and into the hidden
runtime-context custom message (the mechanism from openclaw#89428), and
relocate that carrier to the ABSOLUTE TAIL of the wire request (after the
active user turn and any tool-call scaffolding). The user turn stays bare and
byte-identical in both the active and historical positions; the volatile
carrier vanishes next turn exactly where the assistant reply begins anyway, so
the request is an append-only prefix-extension through the active user turn.
- A durable marker (UserMessage.runtimeContextCarrier, set by convertToLlm from
the carrier's details) lets the Anthropic SDK transport, the managed Anthropic
transport (anthropic-transport-stream / anthropic-payload-policy), and
OpenAI-completions skip the carrier when selecting the deepest cache_control
breakpoint, keeping the anchor on the last stable user turn.
Storage stays BARE (invariant preserved); runtime-only turns (room events) keep
their existing inline behavior, which is byte-stable because room-event/system
context is not strip-eligible; legacy bare-stored sessions are unchanged.
Reviewed with adversarial gpt-5.5/codex sweeps (correctness, byte-identity,
compatibility, test adequacy): found + fixed the managed Anthropic transport
cache-anchor path and added the missing OpenAI-completions/managed-transport
breakpoint tests; a theoretical runtime-only mutation was verified unreachable
and pinned with a test. Final sweep clean.
Co-authored-by: openclaw#89428 (runtime-context handoff approach)
Compaction failure and timeout recovery can no longer leave a session transcript assistant-last and wedged after restoring pre-compaction state.
Transcript-continuation failures now carry a typed agent-core error code and no longer demote the active model through model fallback scoring.
Surface: embedded agent runner compaction recovery, model fallback classification, packages/agent-core.
Refs #100312. Includes regression coverage for #99943.
Centralized managed worktrees under <state-dir>/worktrees/<repo-fingerprint>/<name>
with branch-per-task (openclaw/<name>), .worktreeinclude provisioning, an optional
.openclaw/worktree-setup.sh repo hook, and a SQLite registry in the shared state DB.
Removal always snapshots the tree (untracked included, gitignored excluded) to
refs/openclaw/snapshots/<id>; restore rebuilds the branch at the original commit with
the snapshot content as uncommitted state. Lossless run-end cleanup, 7-day idle GC for
run-owned worktrees (manual exempt), orphan reconciliation, 30-day snapshot retention.
Surfaces: worktrees.* gateway RPC (operator.admin mutations), openclaw worktrees CLI,
Control UI page, plugin-SDK facade + Workboard kind:"worktree" materialization.
E2E-verified on Testbox: full create->work->remove->restore->gc lifecycle.
* fix(tui): show last tool-error summary on aborted runs
When a run ends while looping on tool-call validation errors the TUI showed
only "run aborted". Carry the last tool failure on the terminal lifecycle
metadata and render a sanitized one-line summary on the abort line, e.g.
"run aborted: edit tool validation failed: edits: must have required properties edits".
Argument dumps are stripped; with no summary it stays "run aborted".
Refs #90982
* fix(tui): secure abort validation diagnostics
* docs(changelog): move TUI fix to unreleased
* docs(changelog): aggregate TUI fixes
* fix(tui): scope abort validation diagnostics
* docs(changelog): restore unreleased spacing
* fix(tui): scope abort validation diagnostics
* fix(tui): scope abort validation diagnostics
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* refactor: extract reusable AI runtime package
* refactor: complete AI provider relocation
* refactor: keep llm core internal
* refactor(ai): make @openclaw/ai self-contained with host policy ports
Move pure transport helpers (tool projections, strict-schema normalization,
prompt-cache boundary, stream guards, anthropic/openai compat, request
activity) from src into packages/ai; move utf16-slice into
normalization-core. Inject host policy (guarded fetch, redaction,
strict-tool defaults, diagnostics logging) through AiTransportHost with
inert library defaults installed by src/llm/stream.ts. Narrow the public
barrel to instance-scoped createApiRegistry/createLlmRuntime; the
process-default runtime moves behind internal/ and
registerBuiltInApiProviders takes an explicit registry. Delete the
src/llm/api-registry re-export facade.
* fix(ai): teach node, jiti, and vite resolvers the @openclaw/ai and utf16-slice subpaths
The workspace alias tables in root-alias.cjs, plugin-sdk-native-resolver,
sdk-alias, the shared vitest config, and the Control UI vite config only
knew @openclaw/llm-core; Node-side plugin loading resolved @openclaw/ai
through the pnpm symlink to the unbuilt dist (checks-node-compact CI
failures), and the Control UI build broke on the new
normalization-core/utf16-slice subpath.
* chore(ui): drop leftover service-worker debug logging
* build(release): ship @openclaw/ai with its own shrinkwrap and honest dependency set
packages/ai declares only its six real runtime deps (kysely, chalk, json5,
tslog, zod, fs-safe, and proxyline were never imported); orphaned root deps
removed. generate-npm-shrinkwrap now treats publishable packages/* like
publishable plugins so the AI tarball pins its transitive tree even though
workspace deps are omitted from the root shrinkwrap. knip learns the
package entry points; the tsdown dts neverBundle option moves to its
documented deps.dts home; the README documents the no-semver internal/*
contract and host ports.
* docs(ai): add minimal external-consumer example app
examples/ai-chat consumes only the public @openclaw/ai surface (built dist
via the workspace link): isolated runtime, built-in provider registration,
one streamed completion. Supports Anthropic/OpenAI via env keys and a
keyless local Ollama target; live-verified against Ollama.
* docs(ai): document the @openclaw/ai package and workspace shrinkwrap boundary
* chore(check): include examples/ in duplicate-scan targets
* fix: emit normalization package subpaths
* fix: complete AI package boundary artifacts
* fix: align AI package boundary contracts
* fix(ci): stabilize package release contracts
* test: align documentation contract checks
* test: keep cron docs guard aligned
* test: align restored docs contract guards
* test: follow upstream docs contracts
* docs: drop superseded talk wording
truncateLine could cut a surrogate pair when the maxChars boundary
fell between a high surrogate and its paired low surrogate, producing
a broken unpaired surrogate in grep tool output.