diff --git a/docs/capability-seams.md b/docs/capability-seams.md
index 29cf15944e..392bde5eda 100644
--- a/docs/capability-seams.md
+++ b/docs/capability-seams.md
@@ -24,6 +24,9 @@ flowchart LR
svc_sessionPersistence["ctx.sessionPersistence
Durable session persistence seam"]
pkg_session_persistence_jsonl["session-persistence-jsonl"]
pkg_session_persistence_sqlite["session-persistence-sqlite"]
+ pkg_tool_bash["tool-bash"]
+ pkg_hooks_claude["hooks-claude"]
+ pkg_hooks_codex["hooks-codex"]
pkg_acp["acp"]
svc_sessionQuery["ctx.sessionQuery
Exact session-history reads"]
pkg_system_prompt["system-prompt"]
@@ -33,7 +36,6 @@ flowchart LR
pkg_tool_web["tool-web"]
svc_tools["ctx.tools
Tool registry and guarded execution pipeline"]
pkg_tool_ask_user["tool-ask-user"]
- pkg_tool_bash["tool-bash"]
pkg_tool_cordis["tool-cordis"]
pkg_tool_skill["tool-skill"]
pkg_tool_subagent["tool-subagent"]
@@ -51,8 +53,6 @@ flowchart LR
svc_bash["ctx.bash
Bash executor seam"]
pkg_bash_local["bash-local"]
pkg_bash_sandbox["bash-sandbox"]
- pkg_hooks_claude["hooks-claude"]
- pkg_hooks_codex["hooks-codex"]
svc_bashEnv["ctx.bashEnv
Managed bash environment registry"]
pkg_sandbox["sandbox"]
svc_sandbox["ctx.sandbox
Process-sandbox seam"]
@@ -151,7 +151,10 @@ flowchart LR
svc_sandbox --> pkg_bash_sandbox
svc_sessionPersistence --> pkg_acp
svc_sessionPersistence --> pkg_agent_loop
+ svc_sessionPersistence --> pkg_hooks_claude
+ svc_sessionPersistence --> pkg_hooks_codex
svc_sessionPersistence --> pkg_session_query
+ svc_sessionPersistence --> pkg_tool_bash
svc_sessions --> pkg_agent
svc_sessions --> pkg_agent_loop
svc_sessions --> pkg_invariants
@@ -186,7 +189,7 @@ flowchart LR
| --- | --- | --- | --- | --- | --- | --- |
| `ctx.llm` | `seam` | [`llm`](../packages/llm/llm) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-replay`](../packages/support/llm-replay) | [`agent-loop`](../packages/core/agent-loop), [`compact-basic`](../packages/compact/compact-basic) | - | Adapters register provider implementations; the loop and compaction call the provider-neutral stream service. |
| `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-query`](../packages/session-query/session-query), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`invariants`](../packages/support/invariants) | - | Owns append-only Session instances and emits the durable session event feed. |
-| `ctx.sessionPersistence` | `seam` | [`session-persistence`](../packages/session-persistence/session-persistence) | [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/ui/acp), [`session-query`](../packages/session-query/session-query) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. |
+| `ctx.sessionPersistence` | `seam` | [`session-persistence`](../packages/session-persistence/session-persistence) | [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | [`agent-loop`](../packages/core/agent-loop), [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`acp`](../packages/ui/acp), [`session-query`](../packages/session-query/session-query) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. |
| `ctx.sessionQuery` | `seam` | [`session-query`](../packages/session-query/session-query) | - | - | - | Resolves live and optional persisted logs into one logical corpus for exact reads. |
| `ctx.systemPrompt` | `core` | [`system-prompt`](../packages/core/system-prompt) | - | [`agent-loop`](../packages/core/agent-loop), [`tools`](../packages/core/tools), [`tool-fs`](../packages/fs/tool-fs), [`tool-web`](../packages/web/tool-web) | - | Collects prompt sections and model-facing tool schemas for each step. |
| `ctx.tools` | `core` | [`tools`](../packages/core/tools) | - | [`agent-loop`](../packages/core/agent-loop), [`tool-ask-user`](../packages/ui/tool-ask-user), [`tool-bash`](../packages/bash/tool-bash), [`tool-cordis`](../packages/cordis/tool-cordis), [`tool-fs`](../packages/fs/tool-fs), [`tool-skill`](../packages/skill/tool-skill), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-todo`](../packages/todo/tool-todo), [`tool-web`](../packages/web/tool-web), [`acp`](../packages/ui/acp) | - | Registers capabilities, owns Code Mode transport, and routes calls through pre-policy, monotonic guards, around dispatch, post-policy, and final-result observation. |
diff --git a/docs/core-data-structures/bash.md b/docs/core-data-structures/bash.md
index 72ca27e8cb..80697356f9 100644
--- a/docs/core-data-structures/bash.md
+++ b/docs/core-data-structures/bash.md
@@ -4,6 +4,18 @@ The bash execution seam — the canonical [capability seam](../rfc/implemented/a
Source: [`packages/bash/bash/src/types.ts`](../../packages/bash/bash/src/types.ts)
+## Managed environment vocabulary
+
+The exported `DSH_ENV_PREFIX` constant is `'DSH_'`, the namespace reserved for harness-owned child-process facts. `DshEnvironmentKey` restricts managed keys to that namespace, and `DshEnvironment` is the immutable per-execution snapshot carried separately from ordinary environment overrides.
+
+```ts type-equiv
+type DshEnvironmentKey = `${typeof DSH_ENV_PREFIX}${string}`
+```
+
+```ts type-equiv
+type DshEnvironment = Readonly>
+```
+
## Request vs. spec: the `resolve()` split
The seam separates the **model-/plugin-facing request** (optional `workdir`/`timeoutMs`, filled from config) from the **fully-resolved spec** the executor acts on (those fields required). The tool layer calls `ctx.bash.resolve(request)` between them — this is the repo's "explicit > implicit at package seams" rule made concrete: the reader of a `BashExecSpec` never wonders where the working directory came from.
diff --git a/packages/bash/tool-bash/README.md b/packages/bash/tool-bash/README.md
index 73d0abafc9..fa6a5285ed 100644
--- a/packages/bash/tool-bash/README.md
+++ b/packages/bash/tool-bash/README.md
@@ -32,6 +32,8 @@ Every foreground and background model bash call receives a newly collected trust
import type { Context } from 'cordis'
import type {} from '@deepseek-ai/dsh-tool-bash'
+export const inject = ['bashEnv']
+
export function apply(ctx: Context): void {
ctx.bashEnv.register({
name: 'deployment-region',
diff --git a/packages/bash/tool-bash/src/index.ts b/packages/bash/tool-bash/src/index.ts
index e171033c9d..33712c1d1b 100644
--- a/packages/bash/tool-bash/src/index.ts
+++ b/packages/bash/tool-bash/src/index.ts
@@ -185,6 +185,8 @@ export class BashEnvRegistry extends Service {
return Object.freeze(Object.fromEntries(Object.entries(values).sort(([left], [right]) => left.localeCompare(right))))
}
+ // TODO(bash-env-list-builtins): Include registry-owned built-ins before diagnostics,
+ // prompt, or UI code treats list() as an exhaustive environment catalog.
/**
* Enumerate plugin-contributed variables without executing their resolvers.
* @returns declarations sorted by environment variable name.
diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts
index 446e726255..5c635d01ae 100644
--- a/scripts/gen-doc-graphs.ts
+++ b/scripts/gen-doc-graphs.ts
@@ -98,7 +98,7 @@ const SERVICE_ROLES: ServiceRole[] = [
title: 'Durable session persistence seam',
mode: 'seam',
implementations: ['session-persistence-jsonl', 'session-persistence-sqlite'],
- consumers: ['agent-loop', 'acp', 'session-query'],
+ consumers: ['agent-loop', 'tool-bash', 'hooks-claude', 'hooks-codex', 'acp', 'session-query'],
note: 'Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time.',
},
{
diff --git a/scripts/type-equiv.manifest.json b/scripts/type-equiv.manifest.json
index a8eba66fc5..a4ebf4ce9e 100644
--- a/scripts/type-equiv.manifest.json
+++ b/scripts/type-equiv.manifest.json
@@ -84,6 +84,8 @@
{ "doc": "docs/core-data-structures/approval.md", "symbol": "ApprovalPolicy", "source": "packages/ui/user-approval/src/index.ts" },
{ "doc": "docs/core-data-structures/approval.md", "symbol": "ApprovalRequest", "source": "packages/ui/user-approval/src/index.ts" },
+ { "doc": "docs/core-data-structures/bash.md", "symbol": "DshEnvironmentKey", "source": "packages/bash/bash/src/types.ts" },
+ { "doc": "docs/core-data-structures/bash.md", "symbol": "DshEnvironment", "source": "packages/bash/bash/src/types.ts" },
{ "doc": "docs/core-data-structures/bash.md", "symbol": "BashExecRequest", "source": "packages/bash/bash/src/types.ts" },
{ "doc": "docs/core-data-structures/bash.md", "symbol": "BashExecSpec", "source": "packages/bash/bash/src/types.ts" },
{ "doc": "docs/core-data-structures/bash.md", "symbol": "BashRunResult", "source": "packages/bash/bash/src/types.ts" },