- **Coding agents** (`dh hooks install <agent>`, `dh hook <agent>`): Decision Horizon on every tool call of Claude Code,
Cursor, GitHub Copilot (CLI, cloud agent, VS Code), OpenAI Codex CLI, Gemini CLI, Windsurf, Cline, Kiro, Factory,
Continue CLI, Augment, OpenCode and Amp, through each agent's own hook system: horizon, policy, Ostiary and a signed
intent before the tool runs (answered as allow / deny / ask in the agent's format), a signed outcome after. One chain
per agent session; parallel hook processes serialise on a lock; a chain changed behind the session is refused, not
forked. Deleting the records, reading the key or editing the agent's own hook configuration is denied. A call the
user declines is `unreported` at close; `dh hooks close-idle` for agents without a session-end event. `[hooks]
on_error` (default deny). Insertion point `agent_hook`, with its own coverage statement.
- **Claude Agent SDK** (`decision_horizon.claude_agent.DecisionHorizon().hooks()`): the same engine as in-process
callbacks; records identical to the Node SDK's `DecisionHorizonHooks`.
- `dh hooks install` creates `~/.dh/hooks.toml` and a signing key on first use. Shared factories moved to
`decision_horizon.factory` (no MCP import), so a hook call takes about 0.15 s.
sdk-ts 0.7.1 — 2026-10-05
- `DecisionHorizonHooks`: `~`, `$HOME` and `${HOME}` in a shell command expand to the home directory, and a shell
command that removes, moves or truncates protects the record store's ancestors, as in the Python hooks.
sdk-ts 0.7.0 — 2026-10-04
- **Claude Agent SDK, in process** (`DecisionHorizonHooks`): `options.hooks` for `anthropic-ai/claude-agent-sdk`'s
`query()` (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`). Each Bash, Write, Edit, MCP and other consequential
tool call gets horizon, policy, Ostiary and a signed intent before it runs and a signed outcome after; reads are
allowed and fold into the horizon unless they target a protected path (store, key, Ostiary baselines, `configFile`).
A deny reaches the model with the rule or Ostiary's reasons and the record id; there is no prompt, so a paused call
goes to the `approver` or is denied. `close()` signs each session's terminal record with `unreported` calls. One
chain per agent session, `claude-agent-sdk-<id>`, insertion point `agent_hook:claude-agent-sdk`. A failure to record
denies the call (`onError: "allow"` lets it through). The session lives in memory: no state files. The records equal
the Python `decision_horizon.claude_agent` ones field for field (parity test), and both verifiers accept both chains.
The Agent SDK is a devDependency used to type-check the callbacks; it is never imported at runtime.
- `Interceptor.intent(tool, args, who)` / `outcome(state, result, status, error?, { extra, clearWindow, observeResult })`:
the two halves of `call()` under separate lock acquisitions, for insertion points where the agent runs the tool in
between; `call()` composes them and its records are unchanged. Also `insertionPoint`, `extraAbstain`, `extraAction`,
`knownTools: "*"`, `observe(..., { resolvable: false })`, `closeSession(extra)`, public `isProtected` / `isSensitive`.
- Coverage: insertion point `agent_hook`, with the Python wording, and `tool_surface.basis`.
- Helpers shared with the Python hooks: `toolKind`, `TOOL_KINDS`, `CANON`, `rel`, `normaliseArgs`, `shellPaths`,
`shlexSplit`, `agentTextOf`, `sessionName`, `denyReason`.
- `pyRealpath` moved to its own module (still exported), so the browser build of Ostiary no longer bundles the
Interceptor's source; the Console's inlined copy is rebuilt with `npm run build:console` (same scorer).