# @graphorin/agent

## 0.15.1

### Patch Changes

- Updated dependencies []:
  - @graphorin/core@0.15.1
  - @graphorin/memory@0.15.1
  - @graphorin/security@0.15.1
  - @graphorin/skills@0.15.1
  - @graphorin/tools@0.15.1

## 0.15.0

### Patch Changes

- Updated dependencies []:
  - @graphorin/core@0.15.0
  - @graphorin/memory@0.15.0
  - @graphorin/security@0.15.0
  - @graphorin/skills@0.15.0
  - @graphorin/tools@0.15.0

## 0.14.0

### Minor Changes

- [#247](https://github.com/o-stepper/graphorin/pull/247) [`8ff1d25`](https://github.com/o-stepper/graphorin/commit/8ff1d257a489e5da686318d8c617576fbf2b0002) Thanks [@o-stepper](https://github.com/o-stepper)! - Error-hierarchy corrections backing the new error-contract documentation: `ToolRateLimitError` now extends `GraphorinToolsError` (kind `'rate-limited'`) and `TimerDriverStoreUnsupportedError` now extends `WorkflowError` (new `'timer-driver-store-unsupported'` code in the union) - both were direct `Error` subclasses, invisible to catch sites filtering on the package bases. `AgentRuntimeError` and `SessionError` constructors accept a trailing `{ cause }` option so wrapped failures thread their root cause like every other package base.

### Patch Changes

- Updated dependencies [[`8ff1d25`](https://github.com/o-stepper/graphorin/commit/8ff1d257a489e5da686318d8c617576fbf2b0002), [`8ff1d25`](https://github.com/o-stepper/graphorin/commit/8ff1d257a489e5da686318d8c617576fbf2b0002)]:
  - @graphorin/tools@0.14.0
  - @graphorin/security@0.14.0
  - @graphorin/memory@0.14.0
  - @graphorin/skills@0.14.0
  - @graphorin/core@0.14.0

## 0.13.13

### Patch Changes

- [#244](https://github.com/o-stepper/graphorin/pull/244) [`0271df9`](https://github.com/o-stepper/graphorin/commit/0271df93b163af6fe6bdcba3462c13ef488a2aab) Thanks [@o-stepper](https://github.com/o-stepper)! - Fourteenth deep retest P3: every package sitting between an application and a zod-peer package (`core`/`tools`/`memory`/`mcp`) now re-declares the `zod` peer as **optional** (`peerDependenciesMeta`), so strict Yarn PnP installs stop emitting `YN0086` "does not provide zod" warnings - the application root's zod instance flows through the intermediaries. npm/pnpm behaviour is unchanged (optional peers are not auto-installed; the underlying required peers still resolve exactly as before).

- Updated dependencies [[`0271df9`](https://github.com/o-stepper/graphorin/commit/0271df93b163af6fe6bdcba3462c13ef488a2aab), [`0271df9`](https://github.com/o-stepper/graphorin/commit/0271df93b163af6fe6bdcba3462c13ef488a2aab)]:
  - @graphorin/security@0.13.13
  - @graphorin/skills@0.13.13
  - @graphorin/memory@0.13.13
  - @graphorin/tools@0.13.13
  - @graphorin/core@0.13.13

## 0.13.12

### Patch Changes

- Updated dependencies [[`8a85b51`](https://github.com/o-stepper/graphorin/commit/8a85b51f91fefab5f5432d49a69c5a54642126d8)]:
  - @graphorin/security@0.13.12
  - @graphorin/memory@0.13.12
  - @graphorin/skills@0.13.12
  - @graphorin/tools@0.13.12
  - @graphorin/core@0.13.12

## 0.13.11

### Patch Changes

- Updated dependencies [[`c9e1465`](https://github.com/o-stepper/graphorin/commit/c9e14652cb0674e121bfdaa3b96a75907360ee34)]:
  - @graphorin/security@0.13.11
  - @graphorin/memory@0.13.11
  - @graphorin/skills@0.13.11
  - @graphorin/tools@0.13.11
  - @graphorin/core@0.13.11

## 0.13.10

### Patch Changes

- Updated dependencies [[`7d47994`](https://github.com/o-stepper/graphorin/commit/7d4799415263d72e4c6744362504b290b55fade4)]:
  - @graphorin/security@0.13.10
  - @graphorin/memory@0.13.10
  - @graphorin/skills@0.13.10
  - @graphorin/tools@0.13.10
  - @graphorin/core@0.13.10

## 0.13.9

### Patch Changes

- Updated dependencies []:
  - @graphorin/core@0.13.9
  - @graphorin/memory@0.13.9
  - @graphorin/security@0.13.9
  - @graphorin/skills@0.13.9
  - @graphorin/tools@0.13.9

## 0.13.8

### Patch Changes

- Updated dependencies []:
  - @graphorin/core@0.13.8
  - @graphorin/memory@0.13.8
  - @graphorin/security@0.13.8
  - @graphorin/skills@0.13.8
  - @graphorin/tools@0.13.8

## 0.13.7

### Patch Changes

- Updated dependencies []:
  - @graphorin/core@0.13.7
  - @graphorin/memory@0.13.7
  - @graphorin/security@0.13.7
  - @graphorin/skills@0.13.7
  - @graphorin/tools@0.13.7

## 0.13.6

### Patch Changes

- Updated dependencies [[`6715ad4`](https://github.com/o-stepper/graphorin/commit/6715ad451e5617c882d282c7f2b2ce67ebd1e4ac)]:
  - @graphorin/security@0.13.6
  - @graphorin/memory@0.13.6
  - @graphorin/tools@0.13.6
  - @graphorin/skills@0.13.6
  - @graphorin/core@0.13.6

## 0.13.5

### Patch Changes

- [#224](https://github.com/o-stepper/graphorin/pull/224) [`e80d6af`](https://github.com/o-stepper/graphorin/commit/e80d6af60cbad7352b2ebaf35bcef45b91989ce6) Thanks [@o-stepper](https://github.com/o-stepper)! - Every type referenced by a public API is now exported from a documented barrel - about 130 previously unreachable types (memory tool input/output shapes, executor and truncation hooks, audit listener signatures, protocol frame schemas, sandbox peer-module views, new core agent-event variants, and more) became importable, clearing all 115 TypeDoc referenced-but-not-included warnings. Three previously file-local names were renamed while being made public: the tools audit listener is `ToolAuditListener`, the memory-guard listener is `MemoryGuardAuditListener`, the secret-value listener is `SecretValueAuditListener`, and the built-in tool-search match row is `ToolSearchToolMatch` (the registry-level `ToolSearchMatch` is unchanged). None of these were importable before, so no consumer code breaks.

- Updated dependencies [[`e80d6af`](https://github.com/o-stepper/graphorin/commit/e80d6af60cbad7352b2ebaf35bcef45b91989ce6)]:
  - @graphorin/core@0.13.5
  - @graphorin/memory@0.13.5
  - @graphorin/security@0.13.5
  - @graphorin/tools@0.13.5
  - @graphorin/skills@0.13.5

## 0.13.4

### Patch Changes

- Updated dependencies [[`a17f82d`](https://github.com/o-stepper/graphorin/commit/a17f82dc46be7727afbf7ceea22bfe788b8d1171)]:
  - @graphorin/security@0.13.4
  - @graphorin/memory@0.13.4
  - @graphorin/tools@0.13.4
  - @graphorin/skills@0.13.4
  - @graphorin/core@0.13.4

## 0.13.3

### Patch Changes

- Updated dependencies [[`1cdec71`](https://github.com/o-stepper/graphorin/commit/1cdec71b5a8bd4ed9dbbd283eb7bf578886088f8)]:
  - @graphorin/security@0.13.3
  - @graphorin/memory@0.13.3
  - @graphorin/tools@0.13.3
  - @graphorin/skills@0.13.3
  - @graphorin/core@0.13.3

## 0.13.2

### Patch Changes

- [#213](https://github.com/o-stepper/graphorin/pull/213) [`bba9048`](https://github.com/o-stepper/graphorin/commit/bba9048043fe97ce623079700f0e93b4b7705425) Thanks [@o-stepper](https://github.com/o-stepper)! - A provider stream that finishes while a tool call is still streaming its argument JSON (typically `finishReason: 'length'` at the output-token ceiling) no longer completes the run with the call silently dropped - the runtime now fails the run with `error.code: 'incomplete-tool-call'` so a never-executed side effect can never read as success. The loop threads the provider `finishReason` through the step chain, emits a new terminal `tool.call.incomplete` event per cut call (`toolCallId`, `toolName`, `finishReason`, accumulated `argsPrefix`; additive `AgentEvent` variant, forwarded under the `'lifecycle'` sub-agent policy), records the truncated call's token usage on the failed state, and deliberately skips fallback/retry (a fallback provider would hit the same ceiling, and re-running a side-effecting step needs the caller's idempotency decision). A `'length'` finish with no pending tool call still completes (the text is simply truncated) and is now observable as the new optional `RunStep.finishReason` field.

- Updated dependencies [[`bba9048`](https://github.com/o-stepper/graphorin/commit/bba9048043fe97ce623079700f0e93b4b7705425)]:
  - @graphorin/core@0.13.2
  - @graphorin/memory@0.13.2
  - @graphorin/security@0.13.2
  - @graphorin/skills@0.13.2
  - @graphorin/tools@0.13.2

## 0.13.1

### Patch Changes

- [#209](https://github.com/o-stepper/graphorin/pull/209) [`7991726`](https://github.com/o-stepper/graphorin/commit/7991726fbd06426bdfb1e9c1a5c1e4bd5466d0f4) Thanks [@o-stepper](https://github.com/o-stepper)! - Public TSDoc no longer carries internal audit/work-item ticket ids ("deep retest P1-3", "W-135", wave letters, finding families): roughly 1100 docblock sites across 28 packages were rewritten to describe the behaviour itself, with every technical statement preserved (decision-record references like ADR-x/DEC-x stay). Two runtime strings also dropped their ticket ids: the tools executor's approval-rewrite refusal message and the server's secret-resolution hint. A new `check-api-wording` gate scans the generated API reference and fails CI if ticket vocabulary ever leaks back in.

- Updated dependencies [[`7991726`](https://github.com/o-stepper/graphorin/commit/7991726fbd06426bdfb1e9c1a5c1e4bd5466d0f4)]:
  - @graphorin/core@0.13.1
  - @graphorin/memory@0.13.1
  - @graphorin/security@0.13.1
  - @graphorin/skills@0.13.1
  - @graphorin/tools@0.13.1

## 0.13.0

### Minor Changes

- [#206](https://github.com/o-stepper/graphorin/pull/206) [`8da43b7`](https://github.com/o-stepper/graphorin/commit/8da43b775eb5e53ef00e2ed3933aeef00d033034) Thanks [@o-stepper](https://github.com/o-stepper)! - BREAKING (opt-out available): `RunBudget.maxCostUsd` is now fail-closed when the usage carries no USD cost data. Previously an unpriced cost ceiling logged one WARN and ran unmetered - a caller who set a cost cap kept spending with no enforcement. The new `RunBudget.onUnpriced` defaults to `'fail'`: the run stops at the first between-step check (`'stop'` fails the run with `error.code: 'budget-unpriced'`; `'throw'` rejects with the new `AgentBudgetUnpricedError`). Restore the pre-0.13 warn-once behaviour with `RunBudget.onUnpriced: 'warn'`, or wire `withCostTracking` (`@graphorin/provider`) with a `@graphorin/pricing` snapshot so the ceiling can observe real spend. Found by the 2026-07-19 deep retest (P1-3): current cloud models missing from the bundled snapshot left `maxCostUsd` silently unenforced.

### Patch Changes

- Updated dependencies [[`8da43b7`](https://github.com/o-stepper/graphorin/commit/8da43b775eb5e53ef00e2ed3933aeef00d033034)]:
  - @graphorin/security@0.13.0
  - @graphorin/memory@0.13.0
  - @graphorin/skills@0.13.0
  - @graphorin/tools@0.13.0
  - @graphorin/core@0.13.0

## 0.12.1

### Patch Changes

- Updated dependencies []:
  - @graphorin/core@0.12.1
  - @graphorin/memory@0.12.1
  - @graphorin/security@0.12.1
  - @graphorin/skills@0.12.1
  - @graphorin/tools@0.12.1

## 0.12.0

### Minor Changes

- [#195](https://github.com/o-stepper/graphorin/pull/195) [`9bc93fe`](https://github.com/o-stepper/graphorin/commit/9bc93fe6135fdda421219fb5558bf8eb486437f7) Thanks [@o-stepper](https://github.com/o-stepper)! - Durable suspended agent runs (migration 038): a run parked on durable HITL (`awaiting_approval`) now survives a server restart. The `RunStateTracker` mirrors every park into the new `store.suspendedRuns` sidecar (`suspended_runs` table, session-scoped for the erasure cascade), boot hydration re-registers persisted parks, and `POST /runs/:runId/resume` rehydrates them through the owning agent's new `serializeState` / `deserializeState` codec (version-stamped, binary-safe, secret-redacted - the `Agent` interface gains both methods). Rows are dropped when the run settles (resume completes/fails, or an explicit `POST /runs/:runId/abort`); the graceful-shutdown force-abort deliberately keeps them. Custom `ServerAgentLike` fixtures without the codec keep today's in-memory behaviour and the resume endpoint answers an actionable `409 run-state-unavailable`; an unreadable durable payload answers `500 run-state-invalid`.

### Patch Changes

- Updated dependencies []:
  - @graphorin/memory@0.12.0
  - @graphorin/core@0.12.0
  - @graphorin/security@0.12.0
  - @graphorin/skills@0.12.0
  - @graphorin/tools@0.12.0

## 0.11.0

### Patch Changes

- Updated dependencies [[`47b6034`](https://github.com/o-stepper/graphorin/commit/47b60342dc8a345d1299a22a2fc4fe2aac50bb31)]:
  - @graphorin/core@0.11.0
  - @graphorin/memory@0.11.0
  - @graphorin/security@0.11.0
  - @graphorin/skills@0.11.0
  - @graphorin/tools@0.11.0

## 0.10.2

### Patch Changes

- [#190](https://github.com/o-stepper/graphorin/pull/190) [`fd159e0`](https://github.com/o-stepper/graphorin/commit/fd159e08448580d94daa3f0ac5ef1e23e4f7a553) Thanks [@o-stepper](https://github.com/o-stepper)! - docs: reconcile the last P3 doc drift from the 2026-07 e2e campaign

  - ORPHAN-SU-02: the reconnect backoff docstring matches the implementation
    (`baseMs * 2^(attempt-1)`, exponent clamped at 30, `attempt` 1-indexed).
  - LATERAL-L-03: the `ProtocolGuardConfig` docstring no longer claims a
    non-existent `Agent.protocolGuard` knob; it is passed to `guardOutboundContent`
    / `resolvePolicy` when wiring server boundaries.
  - SCAFFOLD-D1: the channels guide clarifies that only the `<<<commentary>>>`
    envelope is stripped outbound; `<<<untrusted_content ...>>>` markers are an
    inbound-only wrapper and are not removed on echo.
  - Guide pages catch up with the MEMORY-C-03 behavior that shipped in 0.10.1:
    a bare `createMemory()` documents compaction as off-and-silent until a
    `providerContextWindow` is supplied (memory-system table + agent-runtime
    context-management section), and the channels guide shows the real
    untrusted-content envelope attributes (`trust=` / `tool=` / `origin=`).

- [#190](https://github.com/o-stepper/graphorin/pull/190) [`fd159e0`](https://github.com/o-stepper/graphorin/commit/fd159e08448580d94daa3f0ac5ef1e23e4f7a553) Thanks [@o-stepper](https://github.com/o-stepper)! - Correct the handoff sensitivity-filter documentation to match the shipped contract (e2e 2026-07-16, AGENT-FIL-01 / AGENT-FIL-02 / AGENT-FIL-03, doc-drift). The API docs for `bySensitivity` and `stripSensitiveOutputs` described a per-part `MessageContent` sensitivity annotation (`inboundTrust` / `secret`) that does not exist on the public surface, implying a real tier gate that the filters do not perform. The docstrings (and the guide's Filter library table) now describe the actual behavior: `bySensitivity` is a coarse token heuristic that drops a message only when its content contains the literal `[REDACTED:secret]` token and `maxTier` is below `secret` (so untagged plaintext secrets are kept - use a custom predicate for a real per-part gate), and `stripSensitiveOutputs` strips `tool` messages carrying any `[REDACTED:...]` token. The table also notes that `lastN` / `lastUser` always retain system prompts in addition to the kept turns. Behavior is unchanged; a future release may add a real part-level annotation.

- Updated dependencies [[`42cff94`](https://github.com/o-stepper/graphorin/commit/42cff94a6a3636e3ebe80d22b2b83a428afc727f)]:
  - @graphorin/tools@0.10.2
  - @graphorin/memory@0.10.2
  - @graphorin/core@0.10.2
  - @graphorin/security@0.10.2
  - @graphorin/skills@0.10.2

## 0.10.1

### Patch Changes

- [#186](https://github.com/o-stepper/graphorin/pull/186) [`79ef389`](https://github.com/o-stepper/graphorin/commit/79ef3894c409c0a6b9d31fac9b6c888d4068d4e7) Thanks [@o-stepper](https://github.com/o-stepper)! - P3 documentation-drift sweep from the 2026-07 e2e campaign - docstring corrections only, no behavior changes: binary-json's `URL` corruption claim (a URL stringifies to its `href`, not `{}`; CORE-PRO-02); `isAgentFallbackEligible`'s bypass list now names the real `ProviderErrorKind` values (MODEL-FAL-02); `bySensitivity` / `stripSensitiveOutputs` document their actual weak redaction-token contract with an explicit warning instead of a nonexistent part-level sensitivity annotation (AGENT-FIL-01/02); `ProtocolGuardConfig` no longer advertises a nonexistent `Agent.protocolGuard` key (LATERAL-L-03); the token-counter serializer documents the real `[file:<mimeType>]` placeholder and the counter dispatch table the real per-family OpenAI encodings (PROVIDER-CT-02/03); `RegressionOptions` tolerances document their strictly-exceeds semantics (EVALS-REP-01); the reconnect backoff formula matches the implementation (`2^(attempt-1)`; ORPHAN-SU-02); the memory guard states its five tiers (CLI-05); the proactive cron-task docs speak about E1 deny-by-name in the present tense.

- [#184](https://github.com/o-stepper/graphorin/pull/184) [`96138c2`](https://github.com/o-stepper/graphorin/commit/96138c2969e79c06a77d02b83bc33606508dea9a) Thanks [@o-stepper](https://github.com/o-stepper)! - Fix the default lateral-leak denial catalogue never firing (e2e 2026-07-16, LATERAL-L-01, major). When a tool call failed, the run loop recorded only `tool.error:<toolName>` on the CausalityMonitor chain, but `DEFAULT_DENIAL_PATTERNS` match the error IDENTITY (`SecretAccessDenied`, `ToolApprovalDenied`, `SandboxViolation`, ...), so no runtime-recorded entry could ever match and an out-of-the-box monitor (`causalityMonitor: { strictness }` with no custom patterns) was inert - a headline injection defense that silently did nothing. The chain entry now includes the error kind and a bounded error message alongside the tool name, so the default catalogue matches a real denial failure. Regression test drives a tool that fails with a `SecretAccessDenied` identity through a monitor configured with only the defaults and asserts the leak is detected.

- [#184](https://github.com/o-stepper/graphorin/pull/184) [`96138c2`](https://github.com/o-stepper/graphorin/commit/96138c2969e79c06a77d02b83bc33606508dea9a) Thanks [@o-stepper](https://github.com/o-stepper)! - Fix the reasoning + local-adapter defects (e2e 2026-07-16, T3). REASONING-02 (major, privacy): the agent runtime resolved `reasoningContract: 'optional'` to `'pass-through-all'` instead of the documented conservative default `'strip'` (`REASONING_RETENTION_DEFAULTS.optional`), so chain-of-thought was persisted in the transcript and round-tripped to the next provider call by default; it now defaults to `'strip'` unless a caller opts in via an agent- or provider-level override. REASONING-01 (major): with `reasoningRetention` set to keep reasoning, the local-adapter message converters flattened reasoning content parts away with a misleading "multimodal" warning; they now round-trip onto the wire's reasoning slot (Ollama native `thinking`, OpenAI-compatible `reasoning_content`). OLLAMA-AD-01 (major): the native Ollama adapter reported `finishReason: 'stop'` on tool-call turns because Ollama's `/api/chat` sends `done_reason: 'stop'` alongside `tool_calls`; it now reports `'tool-calls'` (matching the OpenAI-compatible path) on both the stream and generate paths. OLLAMA-AD-02 (major): an aborted stream reported `'stop'` instead of `'aborted'` on both local adapters because the ndjson/SSE iterators end cleanly on abort and bypassed the in-loop check; a post-loop re-check now surfaces the honest `'aborted'` reason. (REASONING-03 was already fixed by the external-audit remediation.) Regression tests added for each.

- [#184](https://github.com/o-stepper/graphorin/pull/184) [`96138c2`](https://github.com/o-stepper/graphorin/commit/96138c2969e79c06a77d02b83bc33606508dea9a) Thanks [@o-stepper](https://github.com/o-stepper)! - Fix denied durable-HITL approvals leaving no audit trace (e2e 2026-07-16, TOOL-AUDI-01, major). The agent pre-screens `needsApproval` and suspends the run before the tool reaches the executor, so the executor's approval phase - which emits the `tool:approval:requested` / `granted` / `denied` audit rows - only ran retroactively when a granted call was dispatched, and a denied decision (which never reaches the executor) produced no audit row at all. The agent now emits `tool:approval:requested` at the suspend and `tool:approval:granted` / `tool:approval:denied` when the resume directive resolves, so the full approval lifecycle is on the audit chain regardless of the decision. To avoid a duplicate row, the executor skips its own approval phase on a pre-approved replay (the agent already owns the audited grant). Regression test pins that a denied resume emits exactly one `tool:approval:denied` audit event.

- Updated dependencies [[`15e65b2`](https://github.com/o-stepper/graphorin/commit/15e65b224ebe1170d6f840ea8af393609514e051), [`79ef389`](https://github.com/o-stepper/graphorin/commit/79ef3894c409c0a6b9d31fac9b6c888d4068d4e7), [`96138c2`](https://github.com/o-stepper/graphorin/commit/96138c2969e79c06a77d02b83bc33606508dea9a), [`15e65b2`](https://github.com/o-stepper/graphorin/commit/15e65b224ebe1170d6f840ea8af393609514e051), [`79ef389`](https://github.com/o-stepper/graphorin/commit/79ef3894c409c0a6b9d31fac9b6c888d4068d4e7), [`96138c2`](https://github.com/o-stepper/graphorin/commit/96138c2969e79c06a77d02b83bc33606508dea9a), [`96138c2`](https://github.com/o-stepper/graphorin/commit/96138c2969e79c06a77d02b83bc33606508dea9a), [`96138c2`](https://github.com/o-stepper/graphorin/commit/96138c2969e79c06a77d02b83bc33606508dea9a), [`15e65b2`](https://github.com/o-stepper/graphorin/commit/15e65b224ebe1170d6f840ea8af393609514e051), [`15e65b2`](https://github.com/o-stepper/graphorin/commit/15e65b224ebe1170d6f840ea8af393609514e051)]:
  - @graphorin/memory@0.10.1
  - @graphorin/core@0.10.1
  - @graphorin/security@0.10.1
  - @graphorin/tools@0.10.1
  - @graphorin/skills@0.10.1

## 0.10.0

### Patch Changes

- Updated dependencies []:
  - @graphorin/memory@0.10.0
  - @graphorin/core@0.10.0
  - @graphorin/security@0.10.0
  - @graphorin/skills@0.10.0
  - @graphorin/tools@0.10.0

## 0.9.0

### Minor Changes

- [#171](https://github.com/o-stepper/graphorin/pull/171) [`08cf387`](https://github.com/o-stepper/graphorin/commit/08cf387a4dc5f4cc9b62462a384efe990309e041) Thanks [@o-stepper](https://github.com/o-stepper)! - Channel-inbound trust boundary (bot-adoption wave B, B1.5). New `'channel-inbound'` member of the core `ToolTrustClass` union, registered in the single `UNTRUSTED_TRUST_CLASSES` source so the taint engine and the Rule-of-Two untrusted-input leg agree by construction; `defaultInboundSanitization` maps it to `detect-and-strip-and-wrap`. The `TaintLedger` gains an optional `recordInboundMessage` entry (same widening + verbatim-span semantics as `recordOutput`) - a first-class input for message-borne untrusted content, which the Rule-of-Two deliberately does not derive from ordinary user messages. Agent side: `DataFlowGuardWithLedgers.recordInboundMessage` plus the new `AgentCallOptions.inboundTaint` seed, stamped in run init before the first step (after the AG-19 resume seed; widen-only), so a channel gateway arms the data-flow policy for every run it starts.

- [#171](https://github.com/o-stepper/graphorin/pull/171) [`08cf387`](https://github.com/o-stepper/graphorin/commit/08cf387a4dc5f4cc9b62462a384efe990309e041) Thanks [@o-stepper](https://github.com/o-stepper)! - Memory writes strictly after guardrails (bot-adoption wave B, B3 / item 15). The run loop's commit gates stamp a per-turn verdict sidecar - `RunState.verdicts`, a plain JSON-safe object keyed `'<step>:<offset>'` with `RunTurnVerdict { guardrail?, lateralLeak?, dataflowFlags? }` - covering input-guardrail block/rewrite, lateral-leak blocks and assistant-output dataflow findings; widen-only merge, serialized through `SerializedRunState` with a defensive rebuild, wiped by compaction for the turns its splice summarized away, and surfaced directly as `AgentResult.verdicts`. Verdicts persist next to the message: `SessionMessagePushOptions.verdict` threads through core `SessionMemoryStore.push` (additive third argument), the memory session tier, `Session.push` and the sqlite store (`verdict_json` column, migration 035; malformed rows degrade to no verdict), and `SessionMessageRecord.verdict` exposes it on the consolidator read path. `createMemory({ ingestGate })` then filters the extraction batch deterministically on BOTH consolidator paths before noise filtering - the canonical `verdictIngestGate` excludes blocked and lateral-leak-withheld turns while rewritten turns pass with their rewritten text; the idempotency cursor still advances through excluded messages (a blocked turn can never wedge consolidation) and a throwing gate fails closed. This gate is the required precondition for the auto-promotion and proactive act-grant features of later waves.

- [#171](https://github.com/o-stepper/graphorin/pull/171) [`08cf387`](https://github.com/o-stepper/graphorin/commit/08cf387a4dc5f4cc9b62462a384efe990309e041) Thanks [@o-stepper](https://github.com/o-stepper)! - Assistant output as a data-flow sink + pluggable injection classifier (bot-adoption wave B, B4 / item 14). `DataFlowEvaluation` gains `sinkKind` (`'tool' | 'assistant-output'`; `isSink` honors it) and the agent guard gains `inspectAssistantOutput`: the run's outgoing text is evaluated as a sink with the stable id `'assistant-output'` in the commit path - enforce-mode blocks replace the durable message with a fixed notice and withhold the run's final output (unified with the lateral-leak path), shadow flags, and `declassifySinks: ['assistant-output']` re-opens the reply surface deliberately; findings land in the B3 verdict sidecar. New `@graphorin/security/inspect` subpath ships the `InjectionClassifier` seam (D-12): the resilient `runInjectionClassifier` (engine errors always degrade to the regex verdict) plus the `injectionClassifierOutputGuardrail` adapter, wired at all three regex layers - inbound sanitisation (`applyInboundSanitizationWithClassifier` in `@graphorin/tools/inbound`, exposed on the channel gateway as `injectionClassifier`), SDF-4 output guardrails, and the memory write-time quarantine gate (`createMemory({ injectionClassifier })`, widen-only). Offline default off everywhere; the framework ships no engine. W-103 stays warn + opt-in per D-13, with `treatPiiAsSensitive: true` recommended in the documented gateway preset.

- [#172](https://github.com/o-stepper/graphorin/pull/172) [`da7952b`](https://github.com/o-stepper/graphorin/commit/da7952b6b543958838aee8bfab249d24d1061a69) Thanks [@o-stepper](https://github.com/o-stepper)! - New package `@graphorin/proactive` (the 29th): `createHeartbeat` - a checklist-driven periodic agent beat with empty-checklist skip (zero model calls), busy-deferral (retry cadence + give-up cap), active-hours windows (IANA tz, midnight-crossing), sentinel suppression (`HEARTBEAT_OK`) with a minimum-length noise floor, cheap isolated per-beat profile (fresh session, per-beat run budget, fail-closed model pin) and typed `notify` outcomes from the core escalation ladder. Agent surface additions: public `Agent.isBusy()` (the busy signal behind the deferral) and `AgentCallOptions.pinnedProvider` (per-run fail-closed model pin - wins over `prepareStep` and the preference ladder, never consults the fallback chain; `PreferredModelResolution.source` gains `'pinned'`).

- [#172](https://github.com/o-stepper/graphorin/pull/172) [`da7952b`](https://github.com/o-stepper/graphorin/commit/da7952b6b543958838aee8bfab249d24d1061a69) Thanks [@o-stepper](https://github.com/o-stepper)! - Run-level budget (W-084 residual, decision D-8): `agent.run(input, { budget: { maxCostUsd?, maxTokens?, onExceed? } })` enforces a per-run spend ceiling as a between-step precheck against the accumulated usage, sub-agents included. `'stop'` (default) resolves the run as `failed` with `error.code: 'budget-exceeded'` (stop-condition-cut precedent, partial state stays resumable); `'throw'` rejects with the new `AgentBudgetExceededError`. A cost ceiling without USD-priced usage WARNs once per run and stays inert; `maxTokens` is provider-independent. Also fixes run-level cost aggregation: `addUsage`/`accumulateUsage`/`addModelUsage` now fold reported `Usage.cost` instead of dropping it, so `AgentResult.usage.cost` reflects the whole run when pricing middleware is wired.

- [#172](https://github.com/o-stepper/graphorin/pull/172) [`da7952b`](https://github.com/o-stepper/graphorin/commit/da7952b6b543958838aee8bfab249d24d1061a69) Thanks [@o-stepper](https://github.com/o-stepper)! - Scaffold preset (decision D-10): `createAgent({ scaffold: 'minimal' | 'full' })`, default `'full'` (pre-C6 behaviour). `'minimal'` is the cheap-run posture: instructions-only system prompt, defer-loading by default via the new registry-level `deferLoadingByDefault` option (`createToolRegistry` / `normaliseTool`; per-tool `defer_loading: false` still wins and `built-in` registrations stay eager), no plan tool / recitation - contradictory explicit flags are fail-fast config errors, and security layers are untouched. New "Minimal profile" guide page covers the preset, the lean install path and the README-on-demand skill pattern over lazy `Skill.resources`.

- [#176](https://github.com/o-stepper/graphorin/pull/176) [`7ac0470`](https://github.com/o-stepper/graphorin/commit/7ac0470bdfc579ee864c2ee54e119c94d24ad160) Thanks [@o-stepper](https://github.com/o-stepper)! - Pre-compaction flush, recall ledger, PromotionPolicy and auto-induction (wave-D D4, plan item 3). The context engine gains a `preCompactionHooks` seam (fired before the summarizer with the full buffer; side-effect only, failures fold into `hookFailures`) and the built-in `memoryFlushHook({ provider })` - one budgeted LLM call salvages durable facts from content about to be summarized away, passes candidates through the B3 ingest gate when configured, and writes them QUARANTINED; `SessionMemory.flushImportant` is deprecated in its favour. Migration 036 adds the recall ledger (`fact_recall_queries`: DISTINCT queries per fact by normalized-query hash, fed from the semantic search path, erased with its fact). The deterministic `PromotionPolicy` (`consolidator.promotion: { minSalience?, minRecalls?, minUniqueQueries?, minAgeMs?, allowedProvenance?, maxPerRun? }`) promotes quarantined facts whose recall evidence clears every threshold through the audited `validate` path (injection-flagged refused, pending W-019 supersedes completed; `PhaseOutcome.factsPromoted`). Fail-closed gates: `promotion` or `autoPromoteExtraction: true` without `ingestGate` now throws `IngestGateRequiredError`, and the W-083 guard forces autoPromote update/conflict decisions against QUARANTINED or USER-provenance targets onto the pending-supersede path. On the agent, `createAgent({ procedureInduction: { auto, minSteps?, minToolCalls?, minCostUsd? } })` distils COMPLETED runs above the thresholds via `memory.procedural.induceFromRun` (result stays quarantined; failures WARN once and never fail the run). SpanType gains `memory.consolidate.promotion`.

- [#177](https://github.com/o-stepper/graphorin/pull/177) [`9b389be`](https://github.com/o-stepper/graphorin/commit/9b389be2ac436f66d62b3ede9c64cd70808cfe9f) Thanks [@o-stepper](https://github.com/o-stepper)! - Code-mode runtime pass-through (wave-E E3, plan item 13 step 1). The code-mode runtime is now a named provider contract: `CodeModeRunner` (`@graphorin/security/sandbox`) is `(options: BridgedSourceOptions) => Promise<BridgedSourceResult>`, with `runBridgedSource` as the built-in `worker_threads` implementation. `AgentConfig.codeMode: { run?, limits? }` threads a caller-chosen runtime (subprocess provider, remote runner) and script limits (`timeoutMs` / `maxMemoryMb` / `maxToolCalls`) through `registerCodeMode` into `createCodeExecuteTool` - the previously undeliverable `run`/`limits` seam. Fixed invariant: a runner receives only the script source, the allowed tool names, the host `dispatch` bridge, the signal and the limits - credentials, `RunState` and policy stay on the harness side, since every in-script tool call routes back through the executor's governance. Defaults are byte-identical (in-process worker runner).

- [#177](https://github.com/o-stepper/graphorin/pull/177) [`9b389be`](https://github.com/o-stepper/graphorin/commit/9b389be2ac436f66d62b3ede9c64cd70808cfe9f) Thanks [@o-stepper](https://github.com/o-stepper)! - Four-value permissionDecision (wave-E E1, plan item 11). The tool-argument policy vocabulary widens to `allow | deny | ask | defer` with priority `deny > defer > ask > allow` (`'forbid'` stays accepted as the alias of `'deny'`): `evaluatePermissionDecision` is the four-value engine, `evaluateToolArgumentPolicy` its fail-closed binary projection, and `isToolDeniedByName` the advertise-time check over predicate-free deny rules. The executor gains the E1 `permissionHook` phase - one caller decision point evaluated on the validated input after schema validation and BEFORE approval; an allowed `updatedInput` rewrite is re-validated and substituted into both the validated input and the effective args (W-118), a throwing hook fails closed, and on `preApproved` resume replays a rewrite of the granted args is refused (tools-02). `ask`/`defer` verdicts ride the agent pre-screen's durable suspend (`ToolApproval.mode`, mirrored on `tool.approval.requested`); the bare executor fails them closed (`approval_denied`), and a granted resume satisfies them (`deny` still outranks the grant). Deny-by-name filters all three surfaces: the advertised per-step catalogue (post-promotions), `tool_search` results/promotion (`excludeTool`), and execution (executor mirror before validation + the run loop's inline handoff/sub-agent check). The defer parking half: `requestApproval(name, payload, { timeoutAt, timeoutDecision? })` stamps a durable deadline on the approval pause (enumerated by the existing timer daemon); a due `tick` resolves it with the timeout decision - `DEFAULT_APPROVAL_TIMEOUT_DECISION` (auto-deny) unless overridden. Sub-agent asks project through the W-001 composite key unchanged.

### Patch Changes

- Updated dependencies [[`24241a3`](https://github.com/o-stepper/graphorin/commit/24241a3cdb9c684338f02d4d66510c248eb47d7e), [`24241a3`](https://github.com/o-stepper/graphorin/commit/24241a3cdb9c684338f02d4d66510c248eb47d7e), [`24241a3`](https://github.com/o-stepper/graphorin/commit/24241a3cdb9c684338f02d4d66510c248eb47d7e), [`08cf387`](https://github.com/o-stepper/graphorin/commit/08cf387a4dc5f4cc9b62462a384efe990309e041), [`08cf387`](https://github.com/o-stepper/graphorin/commit/08cf387a4dc5f4cc9b62462a384efe990309e041), [`08cf387`](https://github.com/o-stepper/graphorin/commit/08cf387a4dc5f4cc9b62462a384efe990309e041), [`08cf387`](https://github.com/o-stepper/graphorin/commit/08cf387a4dc5f4cc9b62462a384efe990309e041), [`08cf387`](https://github.com/o-stepper/graphorin/commit/08cf387a4dc5f4cc9b62462a384efe990309e041), [`da7952b`](https://github.com/o-stepper/graphorin/commit/da7952b6b543958838aee8bfab249d24d1061a69), [`da7952b`](https://github.com/o-stepper/graphorin/commit/da7952b6b543958838aee8bfab249d24d1061a69), [`7ac0470`](https://github.com/o-stepper/graphorin/commit/7ac0470bdfc579ee864c2ee54e119c94d24ad160), [`7ac0470`](https://github.com/o-stepper/graphorin/commit/7ac0470bdfc579ee864c2ee54e119c94d24ad160), [`7ac0470`](https://github.com/o-stepper/graphorin/commit/7ac0470bdfc579ee864c2ee54e119c94d24ad160), [`7ac0470`](https://github.com/o-stepper/graphorin/commit/7ac0470bdfc579ee864c2ee54e119c94d24ad160), [`7ac0470`](https://github.com/o-stepper/graphorin/commit/7ac0470bdfc579ee864c2ee54e119c94d24ad160), [`9b389be`](https://github.com/o-stepper/graphorin/commit/9b389be2ac436f66d62b3ede9c64cd70808cfe9f), [`9b389be`](https://github.com/o-stepper/graphorin/commit/9b389be2ac436f66d62b3ede9c64cd70808cfe9f)]:
  - @graphorin/memory@0.9.0
  - @graphorin/core@0.9.0
  - @graphorin/security@0.9.0
  - @graphorin/tools@0.9.0
  - @graphorin/skills@0.9.0

## 0.8.0

### Patch Changes

- Updated dependencies [[`d6a0414`](https://github.com/o-stepper/graphorin/commit/d6a041402fa33d7695379c7536ed2311a7c0fd5b), [`d6a0414`](https://github.com/o-stepper/graphorin/commit/d6a041402fa33d7695379c7536ed2311a7c0fd5b), [`d6a0414`](https://github.com/o-stepper/graphorin/commit/d6a041402fa33d7695379c7536ed2311a7c0fd5b)]:
  - @graphorin/memory@0.8.0
  - @graphorin/security@0.8.0
  - @graphorin/skills@0.8.0
  - @graphorin/tools@0.8.0
  - @graphorin/core@0.8.0

## 0.7.0

### Minor Changes

- [#154](https://github.com/o-stepper/graphorin/pull/154) [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04) Thanks [@o-stepper](https://github.com/o-stepper)! - W-100: `AnyTool` existential type; `createAgent({ tools })` accepts concretely-typed tools without casts.

  `Tool` is invariant in `TInput` (the `needsApproval`/`idempotencyKey` predicate properties are contravariant), so a typed `Tool<{q: string}, number, D>` was never assignable to `Tool<unknown, unknown, D>` - forcing `as unknown as Tool<...>` at every collection seam. `@graphorin/core` now exports `AnyTool<TDeps> = Tool<any, any, TDeps>` (existential input/output, following the `HandoffEntry` precedent), and both `AgentConfig.tools` and `PrepareStepOverrides.tools` in `@graphorin/agent` accept `ReadonlyArray<AnyTool<TDeps>>`. Widening only - existing code keeps compiling. `ToolRegistry.register` was already per-call generic and needed no change.

- [#154](https://github.com/o-stepper/graphorin/pull/154) [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04) Thanks [@o-stepper](https://github.com/o-stepper)! - W-005: HITL/workflow checkpoints are linked to their session and erased by the session hard-delete cascade.

  HITL suspends persist the FULL serialized conversation (`RunState`) into `workflow_checkpoints`; previously nothing connected those rows to a session, so `DELETE /v1/sessions/:id` left the entire transcript recoverable forever. Now: `CheckpointMetadata` gains an optional `sessionId` (additive); migration 029 adds a `session_id` column + index to `workflow_checkpoints` and backfills historical `namespace='agent'` rows from the state blob; the agent runtime stamps `sessionId` on all three suspend write sites (step suspend, resume write-ahead intent, post-dispatch journal); and `deleteSession`/`pruneSessions` collect thread ids from BOTH `session_workflow_runs` and the new column, erasing `workflow_checkpoints` + `workflow_pending_writes` before dropping the mapping. Deleting a session now removes its suspended-run snapshots - time-travel/forensics for a deleted session is intentionally gone (that is what hard-delete means).

- [#153](https://github.com/o-stepper/graphorin/pull/153) [`832f22e`](https://github.com/o-stepper/graphorin/commit/832f22e570b8c3175c1adeb4c150070cbd131534) Thanks [@o-stepper](https://github.com/o-stepper)! - Reachable per-thread checkpoint erasure (W-005) - `CheckpointStore.deleteThread` finally has supported callers:

  - `@graphorin/workflow`: the workflow handle gains `deleteThread(threadId)` next to `listCheckpoints` (idempotent; deletes every checkpoint + pending write of the thread).
  - `@graphorin/server`: new route `DELETE /v1/workflows/:id/threads/:threadId` under scope `workflows:delete:<id>` (204 on success, 404 for an unknown workflow, 400 when the registered entry does not expose `deleteThread`); `ServerWorkflowLike` gains the optional `deleteThread?` member.
  - `@graphorin/agent`: opt-in `AgentConfig.checkpointPolicy: 'keep' | 'delete-on-terminal'` (default `'keep'` - byte-identical to today). With `'delete-on-terminal'` the run's checkpoint thread is best-effort deleted after `completed`/`failed` runs, mirroring the TL-10 spill lifecycle; `awaiting_approval` and `aborted` runs always keep theirs (the thread is the resume state).

  Full erasure cascades stay the job of the session purge path; these are the operator levers for hygiene and targeted per-thread GDPR requests.

- [#154](https://github.com/o-stepper/graphorin/pull/154) [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04) Thanks [@o-stepper](https://github.com/o-stepper)! - W-025/W-100: export `HandoffEntry` from the package root - it is the public type of `AgentConfig.handoffs` and was previously only reachable via type inference, forcing `as unknown as Agent<TDeps, unknown>` casts in consumers that wanted a typed handoff list.

- [#154](https://github.com/o-stepper/graphorin/pull/154) [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04) Thanks [@o-stepper](https://github.com/o-stepper)! - W-072: every export map's `import` condition becomes `default`, and the Node floor rises to `>=22.12.0`.

  CJS consumers previously hit a bewildering `ERR_PACKAGE_PATH_NOT_EXPORTED` instead of a clear ESM-only signal. With the `default` condition, plain `require('@graphorin/core')` works via Node's stable `require(esm)` - which shipped in 22.12, hence the engines bump across every workspace manifest (packages, examples, benchmarks, docs; enforced by the widened mvp-readiness sweep). No dual-instance hazard: there is no CJS build, `require()` returns the same ESM module instance. ESM consumers are unaffected (`default` serves both paths; `types` stays first). The pack gate now runs attw under the full `node16` profile (was `esm-only`) and adds a runtime `require(esm)` smoke against the packed tarballs. Installs on Node 22.0-22.11 with `engine-strict` will refuse - upgrade Node (see the migration guide).

- [#154](https://github.com/o-stepper/graphorin/pull/154) [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04) Thanks [@o-stepper](https://github.com/o-stepper)! - W-024: thinking-block signatures now actually round-trip - the whole retention pipeline was dead because nothing captured them.

  `ProviderEvent` gains a `{type: 'reasoning-end', meta?: ReasoningContentMeta}` terminator (per-block, matching both AI SDK generations). The vercel adapter maps v4 `reasoning-signature`/`redacted-reasoning` chunks and v7 `reasoning-end` (`providerMetadata.anthropic.signature`/`.redactedData`) onto it; `reasoning-start` stays a no-op. The agent runtime flushes buffered deltas into per-block `ReasoningContent` parts carrying the meta (redacted blocks become meta-only parts), and the step assembles those parts instead of one meta-less collapse - adapters without block structure keep the collapsed fallback. Downstream, the already-shipped chain finally engages: `applyReasoningPolicy('pass-through-claude')` retains the signed parts and `toAssistantPart` emits `providerOptions.anthropic.signature`, so multi-step tool use with Anthropic extended thinking replays each block byte-equal (pinned end-to-end: the step-2 request carries both signatures of a two-block step-1). Known scope limit: the one-shot `generate()` path still returns no reasoning (`ProviderResponse` has no field for it). MIGRATION: external exhaustive switches over `ProviderEvent` need a case for `'reasoning-end'`; transcripts may now carry several reasoning parts per step instead of one.

- [#154](https://github.com/o-stepper/graphorin/pull/154) [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04) Thanks [@o-stepper](https://github.com/o-stepper)! - W-101: the Rule-of-Two `untrustedInput` leg is actually enforced. It previously fed only `heldLegs`/`holdsFullTrifecta` bookkeeping - a profile giving up the leg still had every web-search/MCP tool callable while both remaining legs were live, exactly the configuration the preset promises to prevent. `buildRuleOfTwoPolicy` now compiles `untrustedInput: false` into a forbid rule over untrusted-SOURCE tools, decided by the new exported `isUntrustedTrustClass` (`@graphorin/security/dataflow`) so the preset and the taint engine share one definition of "untrusted". `ToolCallFacts` gains `untrustedSource?`; the tools executor passes the tool's `trustClass` into `ToolArgumentPolicyGuard.evaluate` (type-level breaking for custom structural guard implementations); the agent adapter derives the fact from it. Untrusted content in user messages is explicitly out of this rule's scope (documented).

- [#158](https://github.com/o-stepper/graphorin/pull/158) [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab) Thanks [@o-stepper](https://github.com/o-stepper)! - Durable HITL composes across the sub-agent boundary (W-001). A handoff target or `toTool` child that suspends on an approval-gated tool now PARKS on the parent (`RunState.pendingSubRuns`, new core type `PendingSubRun`) instead of surfacing a terminal `execution_failed`/thrown error: the child's pending approvals mirror onto the parent's `pendingApprovals` with the new `ToolApproval.subRunToolCallId` routing field, and the parent suspends once per step. Operators echo (`toolCallId`, `subRunToolCallId`) back in `ApprovalDecision` - decisions match on the composite key, so colliding child-local ids never cross-apply; nested parks route recursively via a `/`-separated path. On grant the child's side effect executes exactly once, its shaped output becomes the parent's tool message, and its usage folds into the parent. `toTool` tools are executed INLINE by the tool-call walk (marked with the well-known `SUBAGENT_TOOL` symbol) rather than through the executor; foreign harnesses mounting them outside the graphorin loop keep the plain throw. `serializeRunState`/`deserializeRunState` recurse into parked children (each nested snapshot is version-stamped and secret-redacted; the core wire codec projects them recursively). Consumers relying on the old terminal-error behavior for suspending children will observe a suspension instead; failed/aborted children still surface as tool errors.

- [#158](https://github.com/o-stepper/graphorin/pull/158) [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab) Thanks [@o-stepper](https://github.com/o-stepper)! - Child-run usage now folds into the parent run's accounting (W-033): handoff and `toTool` children fold their per-model usage into the parent's `RunState.usage`, `usageByModel` and usage accumulator on every outcome (completed or failed - tokens were spent either way), `FanOutResult` gains an additive `usage` field summing usage-reporting children, and run-level `gen_ai.usage.*` trace attributes now include delegated tokens. Operators relying on the previously near-zero parent usage should expect budget hooks and pricing to start seeing the real (larger) numbers. The handoff child seed is additionally sanitized to a well-formed transcript (the in-flight dangling tool call is stripped, orphan tool results dropped) - real providers reject those shapes with invalid-request.

- [#158](https://github.com/o-stepper/graphorin/pull/158) [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab) Thanks [@o-stepper](https://github.com/o-stepper)! - `RunState.currentAgentId` is restored to the parent when a handoff child returns (W-034): the transfer is now scoped to the child observation window (restored in `finally`, on the failed branch too), so post-handoff `RunStep.agentId`, resume-step attribution and session JSONL exports identify the agent whose model actually drove the step. Consumers that relied on the id sticking to the handoff target will observe the corrected attribution; the child's identity remains durably recorded in `RunState.handoffs` and the `handoff` event.

- [#158](https://github.com/o-stepper/graphorin/pull/158) [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab) Thanks [@o-stepper](https://github.com/o-stepper)! - `RunStep.stepNumber` stays strictly monotonic and unique across suspend/resume cycles (W-035): the loop counter seeds from the journal's max instead of restarting at 0, and the resume-dispatch step (plus its intent/dispatched checkpoints and `step.start` events) takes max + 1 instead of a hard-coded 0. Dashboards or snapshots that expected resume steps to be numbered 0, or post-resume numbering to restart at 1, will observe the corrected continuing sequence. Exactly-once resume mechanics are unaffected (they key on toolCallId, never on step numbers).

- [#158](https://github.com/o-stepper/graphorin/pull/158) [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab) Thanks [@o-stepper](https://github.com/o-stepper)! - Sub-agent transparency (W-036). New additive `AgentEvent` member `subagent.event` wraps a child's event (with the parent-side toolCallId and the child agent name) and forwards it into the parent stream per the `forwardEvents` policy on handoff entries and `AgentToToolOptions`: `'lifecycle'` (default) forwards tool execution/approval, guardrail, lateral-leak, compaction and error events - never text deltas; `'all'` forwards everything; `'none'` keeps the child a black box. Multi-agent runs now form ONE trace tree: `AgentCallOptions.parentSpan` (not persisted in RunState) parents the run span, and the runtime supplies it automatically for handoffs and `toTool` children from the live step span. The wire codec projects the wrapped event recursively. TypeScript consumers with exhaustive switches over `AgentEvent` must add the new case.

- [#158](https://github.com/o-stepper/graphorin/pull/158) [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab) Thanks [@o-stepper](https://github.com/o-stepper)! - The `onPendingApprovals` abort policy is now reachable and safe (W-038). An abort that races the durable-HITL suspend applies the policy to the just-collected approvals instead of parking them behind a stale `awaiting_approval` checkpoint; the last persisted checkpoint always reflects the final, policy-consistent state. `'deny'` now commits a tool message per drained approval so the transcript keeps no dangling `tool_use`; `'hold'` survives on the aborted state (bare `run(state)` stays parked, an explicit directive resumes); `'fail'` fails the run ONLY when approvals are actually pending - aborting with an empty queue ends `'aborted'`, never `'failed'` (this matches the documented contract; consumers relying on the old unconditional `failed` will observe `aborted`). The guide now attributes the 50 ms grace to the tools executor's `cancellationGraceMs`, where it lives.

- [#158](https://github.com/o-stepper/graphorin/pull/158) [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab) Thanks [@o-stepper](https://github.com/o-stepper)! - The ToolReturn envelope gets a symbol brand (W-115). New core exports: `TOOL_RETURN_BRAND` (`Symbol.for`, duplicate-copy safe), the `toolReturn()` factory, and the ONE shared guard `isToolReturnEnvelope` consumed by both the executor's unwrap and the registry's example-normalizer (the duplicated sniff is gone). The structural fallback for unbranded objects is deliberately narrow - own keys within `{output, contentParts, taint}` - so a tool legitimately returning `{output, exitCode, stderr}` now reaches the model whole instead of being silently stripped to `.output`; canonical unbranded literals keep unwrapping and increment `tool.result.envelope.unbranded-toolreturn.total` toward the sniff's future deprecation. First-party producers (MCP adaptCallResult, memory recall tools, toTool taint envelopes) now brand via `toolReturn()`. Downstream consumers relying on extra fields being dropped will now see them; plain data of exactly `{output: X}` remains ambiguous by contract - brand it or rename the field.

- [#154](https://github.com/o-stepper/graphorin/pull/154) [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04) Thanks [@o-stepper](https://github.com/o-stepper)! - W-004: JSON-safe binary codec for message content and a `WireRunState` projection; run-state schema 1.2.

  `@graphorin/core` gains `EncodedBytes`/`EncodedUrl` envelopes, `WireMessage`/`WireMessageContent`/`WireRunState` wire twins and pure `toJsonSafeMessage`/`fromJsonSafeMessage`, `toJsonSafeContentParts`/`fromJsonSafeContentParts`, `toJsonSafeRunState`/`fromJsonSafeRunState` codecs (plus `bytesToBase64`/`base64ToBytes`, Buffer-free). `serializeRunState` in `@graphorin/agent` now projects binary payloads (`Uint8Array | URL` in `messages` and tool-outcome `contentParts`) through the codec before its detach stringify, so a run with an image checkpointed at `awaiting_approval` and resumed no longer hands the provider corrupted bytes. `RUN_STATE_SCHEMA_VERSION` is now `graphorin-run-state/1.2`; 1.0/1.1 payloads remain readable and their stringify-corrupted numeric-key byte objects are repaired best-effort on rehydration. `SerializedRunState.messages`/`.steps` are now typed as `WireMessage[]`/`WireRunStep[]` - the on-disk truth.

### Patch Changes

- [#154](https://github.com/o-stepper/graphorin/pull/154) [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04) Thanks [@o-stepper](https://github.com/o-stepper)! - W-047: `RunContext.state` is now typed as the new `ReadonlyRunState` projection.

  Tools and hooks observe the run; they do not mutate its bookkeeping - assignments to `status`/`finishedAt` and `push`/`splice` on `steps`/`messages`/`pendingApprovals` through `ctx.state` are now compile errors. `ReadonlyRunState` is a hand-written structural mirror of `RunState` (keyof parity pinned by type tests); `RunState` remains assignable to it, so runtime call sites needed no changes. This is a compile-time contract only (no runtime freeze). BREAKING at the type level for tools that wrote to `ctx.runContext.state` - that was never supported. Companion cleanup in `@graphorin/agent`: `finishRunBase`/`finalize` take `MutableRunState & RunState`, removing the last `as unknown as RunState` cast in the runtime.

- [#160](https://github.com/o-stepper/graphorin/pull/160) [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156) Thanks [@o-stepper](https://github.com/o-stepper)! - W-049: `tool.execute.start` / `tool.execute.end` / `tool.execute.error` events now carry an optional `toolName` (the agent runtime always fills it, on every emit path: batch dispatch, handoff, inline sub-agent, approval pre-screen and resumed dispatch). Direct stream subscribers can render the tool name from any lifecycle event without a stateful join back to `tool.call.start`. The union TSDoc now states the correlation policy explicitly: cross-run attribution belongs to the server envelope (`subject`), in-lifecycle correlation is by `toolCallId`, and `runId` is deliberately NOT retrofitted onto every variant. Additive and wire-compatible (the wire projection spreads unknown fields through); old consumers keep working.

- [#162](https://github.com/o-stepper/graphorin/pull/162) [`73b19ca`](https://github.com/o-stepper/graphorin/commit/73b19caeda388bda628a48138cb7d70b1db839a3) Thanks [@o-stepper](https://github.com/o-stepper)! - Remove phantom workspace dependencies that no source file imports: agent no longer depends on provider and observability, mcp/workflow/server no longer depend on observability, sessions no longer depends on security (and its memory edge moves to devDependencies where the single test import lives), skills no longer depends on tools. Dead tsdown `external` entries for the removed edges are gone too, so a future import can no longer build as external without a declared dependency. Consumer install graphs shrink accordingly; a new repo-wide `check-phantom-deps` CI gate keeps the manifest graph honest from here on.

- [#160](https://github.com/o-stepper/graphorin/pull/160) [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156) Thanks [@o-stepper](https://github.com/o-stepper)! - W-054: the SDF-1 memory-guard region reader now calls the statically-typed `Memory.working.compile(scope, agentId)` directly instead of reaching working memory through an `as unknown as` double cast with optional-chaining. A future signature change on `WorkingMemory.compile` breaks the agent BUILD instead of silently degrading the DEC-153 guard to an empty region read. Runtime semantics are unchanged: the reader stays best-effort (a throwing compile still degrades to an empty string and never fails the run). The remaining variance cast on the plan-tool registration is registration-time-only and out of this finding's scope.

- [#160](https://github.com/o-stepper/graphorin/pull/160) [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156) Thanks [@o-stepper](https://github.com/o-stepper)! - W-056: the compaction-summary wrapper marker has a single canonical definition. `@graphorin/memory` now exports `COMPACTION_SUMMARY_TAG` / `_OPEN` / `_CLOSE` / `_MARKER` from the module that renders the summary template, and the agent runtime imports (and re-exports) `COMPACTION_SUMMARY_MARKER` instead of defining its own literal. The VALUE is unchanged and frozen - persisted summaries in existing session stores carry it - with pin tests in both packages asserting the raw literal so a change breaks CI rather than migrating silently.

- [#160](https://github.com/o-stepper/graphorin/pull/160) [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156) Thanks [@o-stepper](https://github.com/o-stepper)! - W-103 / W-104: enabling `dataFlowPolicy` without any way to arm the lethal-trifecta leg is now visible. The agent prints one construction-time warning when the policy is on, `guardTrifecta` is not disabled, `treatPiiAsSensitive` is unset and no registered tool declares a sensitivity within the effective `sensitiveTiers` (default `['secret']` - and no built-in tool ships with that tag): in that configuration the only active default signal is the best-effort verbatim probe, which paraphrasing bypasses. The security and agent-runtime guides now state this explicitly and document the recommended adoption ladder (shadow -> tag private-data tools -> widen tiers/PII -> derivedTaint strict -> enforce). Runtime policy behaviour is unchanged.

- [#164](https://github.com/o-stepper/graphorin/pull/164) [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00) Thanks [@o-stepper](https://github.com/o-stepper)! - Documentation sync on the operator-facing HITL and usage surfaces (W-125): the agent-runtime guide now describes the real `tool.approval.requested` event shape (`{ type, toolCallId, reason? }`, with tool name and args read from `RunState.pendingApprovals`), the README names the real `RunState.usageByModel` field (was `RunState.usage.byModel`), and the `ResponseVerifier` TSDoc states what actually happens when a verifier throws (treated as passed; the `verifier.result` event still fires with `ok: true` - nothing is logged, since `AgentConfig` has no logger).

- [#164](https://github.com/o-stepper/graphorin/pull/164) [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00) Thanks [@o-stepper](https://github.com/o-stepper)! - TSDoc `{@link}` hygiene sweep (W-130): all 55 broken links found by TypeDoc's now-enabled `validation.invalidLink` are fixed - two resolved to their real targets (`GraphorinMCPError` was misnamed `MCPError`), the rest (cross-package, `import()`-form, unexported-constant, and DOM-type references that have never rendered as hrefs) converted to plain inline code. The docs build now fails on any new broken `{@link}` via a scoped gate.

- [#164](https://github.com/o-stepper/graphorin/pull/164) [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00) Thanks [@o-stepper](https://github.com/o-stepper)! - Tarballs now ship `src/` so the published `dist/**/*.d.ts.map` files actually work (W-136): the maps referenced `../src/*.ts` that the `files` whitelist excluded, so go-to-definition fell back into `.d.ts` and the shipped maps were dead weight. The pack gate gains a `map-integrity` leg: every source referenced by a shipped map must resolve inside the tarball (or be embedded via `sourcesContent`), with an anti-vacuous guard - a package whose tsdown config emits declaration maps must contain a non-zero number of `.d.ts.map` files, so a cache-restored dist that silently dropped maps fails the gate instead of passing vacuously. `mvp-readiness` now requires `src` in every publishable `files` array.

- [#164](https://github.com/o-stepper/graphorin/pull/164) [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00) Thanks [@o-stepper](https://github.com/o-stepper)! - Every published package now declares its tree-shaking contract via `sideEffects` (W-137): 18 packages audited to a pure module scope get `false`, the CLI declares its bin entry (`["./dist/bin/*"]`), and `@graphorin/security` gets an explicit `true` - its secrets subsystem registers built-in resolvers and the SecretValue caller-context provider at import time, so marking it pure would let bundlers drop those registrations. `mvp-readiness` now fails any publishable manifest without a declared `sideEffects`, closing the drift for future packages.

- Updated dependencies [[`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`832f22e`](https://github.com/o-stepper/graphorin/commit/832f22e570b8c3175c1adeb4c150070cbd131534), [`c1af9c7`](https://github.com/o-stepper/graphorin/commit/c1af9c790757fbe82da6dd2b6c1fdc497b5c605e), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`832f22e`](https://github.com/o-stepper/graphorin/commit/832f22e570b8c3175c1adeb4c150070cbd131534), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`832f22e`](https://github.com/o-stepper/graphorin/commit/832f22e570b8c3175c1adeb4c150070cbd131534), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`832f22e`](https://github.com/o-stepper/graphorin/commit/832f22e570b8c3175c1adeb4c150070cbd131534), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`832f22e`](https://github.com/o-stepper/graphorin/commit/832f22e570b8c3175c1adeb4c150070cbd131534), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`832f22e`](https://github.com/o-stepper/graphorin/commit/832f22e570b8c3175c1adeb4c150070cbd131534), [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab), [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab), [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`73b19ca`](https://github.com/o-stepper/graphorin/commit/73b19caeda388bda628a48138cb7d70b1db839a3), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab), [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`32bbd03`](https://github.com/o-stepper/graphorin/commit/32bbd03b588136a355e4b5ad6ac5e19b36b4d8ab), [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00), [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00), [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00), [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00), [`4ee256e`](https://github.com/o-stepper/graphorin/commit/4ee256e30fe9190cef6c48dc6785464757707156), [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00), [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00), [`764239b`](https://github.com/o-stepper/graphorin/commit/764239b97e0e0202442e91272583f13adeb12d00), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04), [`fe98522`](https://github.com/o-stepper/graphorin/commit/fe98522ce2477c9a7dc09029f9dcfdb1f7c9aa04)]:
  - @graphorin/core@0.7.0
  - @graphorin/security@0.7.0
  - @graphorin/memory@0.7.0
  - @graphorin/tools@0.7.0
  - @graphorin/skills@0.7.0

## 0.6.1

### Patch Changes

- [#142](https://github.com/o-stepper/graphorin/pull/142) [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430) Thanks [@o-stepper](https://github.com/o-stepper)! - Version constants and version-bearing strings now derive from each package's manifest at build time (`VERSION = pkg.version`; writer ids, client/server info, OTLP framework attributes, build-info metrics interpolate it). No behavioral change at the current version: the rendered strings are byte-identical. A release bump no longer edits source; the new `check-version-consistency` gate fails any reintroduced hardcoded framework version.

- Updated dependencies [[`826ee59`](https://github.com/o-stepper/graphorin/commit/826ee5933ad38693b0dd2f20a110abfecba7d23d), [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430)]:
  - @graphorin/observability@0.6.1
  - @graphorin/core@0.6.1
  - @graphorin/memory@0.6.1
  - @graphorin/provider@0.6.1
  - @graphorin/security@0.6.1
  - @graphorin/skills@0.6.1
  - @graphorin/tools@0.6.1

## 0.6.0

### Minor Changes

- [#136](https://github.com/o-stepper/graphorin/pull/136) [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84) Thanks [@o-stepper](https://github.com/o-stepper)! - Prompt-cache economics end-to-end (audit 2026-07-04 Wave C, cluster C1).

  - `Usage` gains `cachedReadTokens` / `cacheWriteTokens` (subsets of `promptTokens`), mapped by the vercel adapter (v7 `inputTokenDetails`; reasoning split kept exclusive of `completionTokens`) and the OpenAI-compatible adapter (`prompt_tokens_details.cached_tokens`); the fields flow through step/run aggregation, `usageByModel`, run-state (de)serialization and `withCostTracking` (new `cachedReadPerMtok`/`cacheWritePerMtok` lookup rates, full-input-rate fallback).
  - New opt-in `ProviderRequest.cachePolicy` / `AgentConfig.cachePolicy` (`{ breakpoints: 'auto', ttl? }`): the vercel adapter anchors Anthropic `cache_control` on the first and last conversation messages so the stable prefix is written once and read at the discounted rate every later step.
  - `ModelPrice` gains `cacheWriteUsdPerToken`; `calculateCost` bills cache writes; the bundled pricing snapshot is regenerated (2026-07-04) with current Anthropic 4.x / OpenAI gpt-5 + gpt-4.1 + o3/o4-mini / Gemini 2.5 families (legacy ids retained), `lookupPrice` resolves dated ids via a date-suffix fallback, and a new snapshot-coverage release gate cross-checks the model-tier classifier against the snapshot with an explicit known-unpriced allowlist (post-cutoff models report null cost + WARN instead of invented numbers).
  - Cache-friendly catalogue: handoff tools serialize BEFORE the growing promoted section (byte-stable prefix under `tool_search` promotions), and new `toolPromotion: 'run-boundary'` freezes the advertised catalogue for a whole run while still persisting discoveries.

- [#136](https://github.com/o-stepper/graphorin/pull/136) [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84) Thanks [@o-stepper](https://github.com/o-stepper)! - Agent harness quality (audit 2026-07-04 Wave C, cluster C3).

  - Recoverable error envelope: `ToolError` gains `recoverable` + `recoveryHint` (retry_later / check_input / try_alternative / report_to_user), stamped from the kind at the executor's completion funnel and rendered to the model as a bracketed recovery line under the familiar `Error: <message>`.
  - Transparent bounded tool retry: `rate_limited` outcomes from pure/read-only tools (or tools with an `idempotencyKey`) silently re-execute with exponential backoff up to 3 total attempts (`ToolRateLimitError.retryAfterMs` wins); tune via executor `retry` / `AgentConfig.toolRetry`.
  - Verifier seam: `AgentConfig.verifiers` run deterministic checks on every terminal response, emit `verifier.result` events, feed failures back as a user message and continue up to `maxVerifierRounds` (default 1); throwing verifiers count as passed. Deliberately no evidence-free self-reflection step.
  - ACI: empty successful tool output renders as an explicit `(tool ran successfully with no output)` marker.
  - Deterministic replay: opt-in `recordProviderResponses` journals each step's raw model response onto `RunState.steps[].providerResponse`; new `createReplayProvider(state)` re-drives a run offline and fails loudly on divergence.

- [#136](https://github.com/o-stepper/graphorin/pull/136) [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84) Thanks [@o-stepper](https://github.com/o-stepper)! - Deterministic security adoptions (audit 2026-07-04 Wave C, cluster C6).

  - Derived-taint propagation: opt-in `dataFlowPolicy.derivedTaint: 'strict'` fires the paraphrase-robust `derived-untrusted-to-sink` flow for every model-driven sink call once untrusted content entered the run (CaMeL-style control-flow integrity); the agent also records each tainted step's assistant text as `llm-derived` spans so model-echoed phrasing trips the verbatim probe.
  - Taint into memory (cross-session MINJA leg): `ToolReturn` gains a widen-only `taint` override honoured through the executor record path; `fact_search` / `deep_recall` / `recall_episodes` attach it when any returned item is quarantined or foreign-provenance, re-arming the ledger at recall. `RunState.taintSummary` additionally carries one-way FNV-1a span-tile hashes (no plaintext), so a resumed run re-detects pre-suspend verbatim copies.
  - MCP pinning completed: `toTools({ pinStore })` records fingerprints on first use and REJECTS drift by default when a store is present (rug-pull defense; `onPinMismatch: 'warn'` downgrades); tool-description injection hits at registration are counted (`mcp.tool-description.injection-flagged.total`).
  - Signal-only heuristics + Unicode pre-pass: shared `normalizeForMatching` (NFKC + zero-width strip) applied in the guardrails injection catalogue and the memory quarantine heuristics; security.md repositions all pattern catalogues as best-effort signal, never a sole gate. `TaintLabel.sourceKind` widened to `string` for the new descriptive kinds.

- [#136](https://github.com/o-stepper/graphorin/pull/136) [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84) Thanks [@o-stepper](https://github.com/o-stepper)! - Trace-tree observability (audit 2026-07-04 Wave C, cluster C7; pairs periphery-04).

  The agent loop now emits the previously-declared `agent.run` span per run and `agent.step` spans per step (parented under the run); `tool.execute` parents under the current step via the new optional `RunContext.span`; a `withTracing`-wrapped provider parents under the step via the new `ProviderRequest.parentSpan` (a live handle like `signal`). Attributes align to the OTel GenAI semantic conventions (`gen_ai.operation.name`, `gen_ai.agent.id/name`, `gen_ai.tool.name`, `gen_ai.request.model`, `gen_ai.usage.input_tokens/output_tokens`), parent-based sampling finally has parents to follow, and observability.md documents the real tree plus the memory-tier-spans-not-yet-parented limitation.

- [#137](https://github.com/o-stepper/graphorin/pull/137) [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627) Thanks [@o-stepper](https://github.com/o-stepper)! - Sub-agent isolation & orchestration primitives (audit 2026-07-04 Wave D, cluster D2).

  - Run-level `'read-only'` capability (single-writer constraint): `AgentConfig.capability` / `AgentCallOptions.capability` never advertise writer tools or handoffs and the executor deterministically blocks fabricated writer calls with the new `capability_blocked` `ToolErrorKind` (threaded through `executeBatch`/`executeOne`, HITL resume, and the code-mode bridge).
  - `toTool({ contextFold })` returns a compact distilled child-run outcome instead of raw output; `toTool({ propagateTaint })` (default on) carries the child's coarse taint flags across the fold as a widen-only `ToolReturn.taint` override (`sourceKind: 'sub-agent'`) that re-arms the parent's data-flow ledger.

- [#137](https://github.com/o-stepper/graphorin/pull/137) [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627) Thanks [@o-stepper](https://github.com/o-stepper)! - Security hardening (audit 2026-07-04 Wave D, cluster D4) - architectural, deterministic layers.

  - RFC-6962 Merkle transparency over the audit log (`@graphorin/security/audit`): tree head, inclusion + consistency proofs, and Ed25519-signed checkpoints, so anchoring a signed head out-of-band makes the trail tamper-resistant (a rewrite of a checkpointed prefix fails the consistency proof).
  - Operator trust root for skills: `verifySkillSignature`/`installSkillFrom{Npm,Git}` gain `trustRoot` - a valid signature from a key absent from the root returns `valid: false` reason `'untrusted-key'`.
  - Progent-style tool-argument policies + Rule-of-Two capability profiles (`@graphorin/security/policy`): `AgentConfig.toolPolicy` (forbid-before-allow, default-deny sensitive) + `ruleOfTwo` (drops a lethal-trifecta leg, forcing a read-only capability floor) enforced by a new `ExecutorOptions.argumentPolicy` hook (`capability_blocked`).
  - Code-mode sandbox blocklist parity (`bridged-source.ts`) blocks the process-escape modules (child_process/vm/worker_threads/cluster/inspector) via the ESM resolve hook + a CJS `Module._load` patch.

- [#137](https://github.com/o-stepper/graphorin/pull/137) [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627) Thanks [@o-stepper](https://github.com/o-stepper)! - Context engineering trial (audit 2026-07-04 Wave D, cluster D6) - opt-in; tool surface unchanged by default.

  - Structured plan tool (`update_plan`, TodoWrite-style full-replace checklist) journaled in the new `RunState.todos` (core `TodoItem`), surviving suspend/resume via the strict run-state (de)serializer.
  - Attention recitation: the current plan is rendered into a compact `<plan>` block appended to each step's request copy - request-only and cache-layout-aware (rides the last prompt-cache anchor, never touches the shared buffer or persisted state).
  - `AgentConfig.plan` opt-in. Progress files, resume-recap injection, and the degraded-mode ladder are documented as trial follow-ups.

### Patch Changes

- [#134](https://github.com/o-stepper/graphorin/pull/134) [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6) Thanks [@o-stepper](https://github.com/o-stepper)! - Fix three context-assembly defects (context-engine-02/03/04). The default post-compaction hooks (persona block, project rules, pinned facts) now apply the same D2 privacy decision as `assemble()`, so `sensitivity:'secret'` content the assembly withholds can no longer leak to the provider via the post-compaction splice. `renderMessageText` renders assistant tool-call arguments and `adaptTokenCounter` preserves the native `count(messages)` path, so tool-call args (file writes, code_execute scripts) finally contribute to trigger/before/after token arithmetic. And the trigger, SOTA-4 reclaim floor, and anti-thrash guard now share one full-buffer basis: `shouldCompact` takes `compactableFromIndex` (the pinned prefix is no longer counted as reclaimable) and `compactNow` takes `prefixMessages` so the guard arms against prefix + body + essentials instead of the sliced body - previously any real system prompt defeated the guard and a summarizer LLM call re-fired every step at the context edge.

- [#134](https://github.com/o-stepper/graphorin/pull/134) [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6) Thanks [@o-stepper](https://github.com/o-stepper)! - Make durable HITL exactly-once and gate approvals on validated input (agent-02, tools-02, agent-07). With a `checkpointStore` wired, the resume now persists a write-ahead intent checkpoint before dispatching approved calls and the journaled post-dispatch state after - so a re-delivered resume from the latest checkpoint (or from `result.state` in the manual JSON flow) cannot double-fire a side effect; re-resuming a stale pre-execution snapshot stays bounded at one re-execution. The executor validates (and repairs) args BEFORE the approval flow, evaluates `needsApproval` on the validated input, and the approval record carries the post-repair args a human actually vets; the agent pre-screen mirrors this (schema-invalid gated calls fail fast as `invalid_input`, never reaching a human) and the resumed dispatch runs with repair disabled so nothing can rewrite an approved payload behind the grant. Partial approval directives now execute the granted calls and re-suspend with the remainder instead of silently discarding grants. The FAQ and agent-runtime guide are rewritten to state the real exactly-once contract.

- [#134](https://github.com/o-stepper/graphorin/pull/134) [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6) Thanks [@o-stepper](https://github.com/o-stepper)! - Enforce transcript well-formedness so no path emits provider-rejected message sequences (agent-01, tools-07, context-engine-01). The durable-HITL pre-screen now collects EVERY approval-gated call in a step (not just the first) and executes the non-gated remainder before suspending, so the persisted transcript never carries dangling `tool_use` ids; `executeBatch` synthesizes an `execution_failed` outcome instead of silently dropping a slot whose `executeOne` rejected (e.g. a throwing tracer); summarize-compaction snaps its boundary backward so the preserved window never starts with an orphan `tool` message. The agent mock-provider harness now asserts transcript well-formedness on every request by default.

- [#134](https://github.com/o-stepper/graphorin/pull/134) [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6) Thanks [@o-stepper](https://github.com/o-stepper)! - Fix critical tool-schema wire bug (tools-01): plain Zod schemas were never converted to JSON Schema, so OpenAI-shaped/Ollama/vercel providers received `{"_def":...}` internals as tool `parameters` and MCP tools serialized to `{}`. Adds a shared structural Zod v3/v4 to JSON Schema converter (`@graphorin/tools/schema`, no new dependencies) used by the agent's `toolToDefinition`, the code-mode signature projection, and `ToolSearchMatch`; MCP's `buildJsonSchemaValidator` now retains the source JSON Schema and exposes it via `toJSON()`. Unprojectable schemas degrade loudly (WARN + permissive `{}`) instead of shipping serialized validator internals.

- [#135](https://github.com/o-stepper/graphorin/pull/135) [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3) Thanks [@o-stepper](https://github.com/o-stepper)! - Spill-handle producer taint now survives executor and process boundaries (audit 2026-07-04 Wave B, cluster B2; tools-03 / security-02 / agent-08).

  - The default spill writer persists a `<file>.meta.json` sidecar (mode 0600) recording `{producerTrustClass, source, sensitivity}` next to every artifact; `createFileResultReader` recovers it and reports the producer class (plus source/sensitivity) on the read outcome. An untrusted spill produced in one executor (code-mode's quiet executor) or a prior process can no longer launder to trusted through the `read_result` built-in: the executor re-applies the producer's sanitization policy and records dataflow provenance under the producer's class.
  - Producer taint is resolved BEFORE truncation, so a re-spill of a handle read persists the original producer's taint into the new artifact's sidecar, and a secret-produced body keeps its secret tier through re-spill (stays off disk).
  - The agent snapshots the coarse taint summary and the promoted-tool set on EVERY exit through the run finalizer, not just the approval suspend: aborted runs (resumable) and completed runs (re-entered as follow-ups) now rehydrate the enforce-mode sink gate and the discovered-tool catalogue.

- [#135](https://github.com/o-stepper/graphorin/pull/135) [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3) Thanks [@o-stepper](https://github.com/o-stepper)! - Compaction-seam hardening (audit 2026-07-04 Wave B, cluster B4).

  - context-engine-05: the agent's pinned-prefix scan stops at the `<graphorin_compaction_summary>` marker, so a compaction summary resumed from a suspended run stays compactable instead of being absorbed into the uncompactable prefix (one extra pinned summary per compact-then-resume cycle).
  - context-engine-06: hard context overflow gets a last-resort tier - on a `context-length` provider error the agent forces ONE aggressive compaction (`preserveRecentTurns: 2`) and retries the same provider before walking the fallback chain or failing. Thrown `ProviderHttpError`s now classify by their canonical `errorKind`, so a thrown 429 / overflow is treated like the structured-event equivalent.
  - context-engine-07: the summarizer prompt's older-messages dump is capped (default 96k chars, `summarizerInputCharBudget` strategy option; oldest lines elided with a marker) so pointing `summarizerModel` at a smaller model no longer overflows its window and silently disables compaction.
  - context-engine-09: `<<<older_messages>>>` / `<<</older_messages>>>` sequences inside message text are neutralized before entering the summarizer prompt's data-only envelope - a tool result carrying the closing marker can no longer inject instructions into the summarizer.
  - context-engine-10: new integration test drives the REAL context engine through the agent loop with tool loops crossing the threshold; the mock provider's transcript invariant validates every post-compaction request.

- [#136](https://github.com/o-stepper/graphorin/pull/136) [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84) Thanks [@o-stepper](https://github.com/o-stepper)! - Eval honesty (audit 2026-07-04 Wave C, cluster C8; pairs evals-01..07/09).

  The LongMemEval runner drops the harness-side keyword fan-out booster (numbers now measure the real search path), gains `--retrieval default|multi-query|hyde|iterative|graph` + `--embedder none|fake` A/B switches that wire the real library config, stamps a `benchConfig` block into every report, grades with a dedicated `--judge-provider` (self-judged real runs WARN and refuse to seed `--json` baselines without `--allow-self-judge`), reports pass rate as mean +/- stddev under `--iterations N` plus an abstention-rate aggregate, caches the in-flight ingest PROMISE (no concurrent double-ingest), and ships a committed deterministic stub+fixture baseline exercised by a new offline plumbing-gate step in the dispatch workflow. New adaptive injected-task scenarios (verbatim / unicode-obfuscated / split / paraphrase) gate the dataflow-policy defense claims both ways in the agent test suite.

- [#138](https://github.com/o-stepper/graphorin/pull/138) [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a) Thanks [@o-stepper](https://github.com/o-stepper)! - OTel GenAI alignment + honest eval statistics (audit 2026-07-04 Wave E, cluster E8).

  - Span names follow the GenAI semconv `{operation} {target}` shape when attributes carry the target (`chat <model>`, `execute_tool <tool>`, `invoke_agent <agent>`); the operation-mapping table and `GenAIOperationName` gain `invoke_agent` to match what the runtime actually emits; `agent.step` spans carry `gen_ai.agent.name`; pricing's `listMissingModels` reads the current `gen_ai.provider.name` attribute first (deprecated `gen_ai.system` kept as fallback).
  - New `@graphorin/evals` stats module: `mean`/`sampleStddev`, Wilson 95% interval, `passHatK` over `-iter-N` outcomes, and McNemar paired significance. `runEvals` summaries always carry `passRateCi` and (under `iterations > 1`) `passHatK`; `detectRegressions` annotates pass-rate-drop findings with the paired regressed/improved counts + p-value, and opt-in `requireSignificance` vetoes drops the paired test cannot distinguish from noise - the fixed tolerance alone was sample-size blind.

- Updated dependencies [[`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a), [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a), [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a)]:
  - @graphorin/memory@0.6.0
  - @graphorin/tools@0.6.0
  - @graphorin/core@0.6.0
  - @graphorin/skills@0.6.0
  - @graphorin/provider@0.6.0
  - @graphorin/security@0.6.0
  - @graphorin/observability@0.6.0

## 0.5.0

First version published to the npm registry (with Sigstore build
provenance). The 0.2.0, 0.3.0, and 0.4.0 versions were internal lockstep
milestones and were never published. All `@graphorin/*` packages release
lockstep at the same version; the full release notes for 0.2.0-0.5.0 live
in the repository-level
[CHANGELOG](https://github.com/o-stepper/graphorin/blob/main/CHANGELOG.md).

## 0.1.0

Initial release. See the workspace root `CHANGELOG.md` for the full
release notes; the per-package changelog is generated by Changesets
and tracks subsequent updates.
