From baf64e8b2117ff40db66f3c8d9c8677bcd8f07e7 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Wed, 15 Jul 2026 00:04:00 +0800 Subject: [PATCH] fix(bash): close managed environment review gaps The managed DSH_* runtime path was correct, but its public extension and documentation contracts were incomplete. A contributor following the README could access ctx.bashEnv without declaring an injection, the new environment types had no drift-checked catalog entries, and the capability graph omitted three packages that now query sessionPersistence. Declare the README injection, catalog DshEnvironmentKey and DshEnvironment, and add tool-bash plus both hook bridges to the generated persistence consumer graph. Keep BashEnvRegistry.list() contributor-only for now because no production caller treats it as exhaustive, but record the built-in enumeration gap before diagnostics, prompt, or UI code depends on it. Validated on the exact resulting tree with typecheck, lint, coverage, snapshot, documentation, module-graph, build, hygiene, demo-smoke, and built-artifact checks. --- docs/capability-seams.md | 11 +++++++---- docs/core-data-structures/bash.md | 12 ++++++++++++ packages/bash/tool-bash/README.md | 2 ++ packages/bash/tool-bash/src/index.ts | 2 ++ scripts/gen-doc-graphs.ts | 2 +- scripts/type-equiv.manifest.json | 2 ++ 6 files changed, 26 insertions(+), 5 deletions(-) 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" },