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.
This commit is contained in:
@@ -24,6 +24,9 @@ flowchart LR
|
|||||||
svc_sessionPersistence["ctx.sessionPersistence<br/>Durable session persistence seam"]
|
svc_sessionPersistence["ctx.sessionPersistence<br/>Durable session persistence seam"]
|
||||||
pkg_session_persistence_jsonl["session-persistence-jsonl"]
|
pkg_session_persistence_jsonl["session-persistence-jsonl"]
|
||||||
pkg_session_persistence_sqlite["session-persistence-sqlite"]
|
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"]
|
pkg_acp["acp"]
|
||||||
svc_sessionQuery["ctx.sessionQuery<br/>Exact session-history reads"]
|
svc_sessionQuery["ctx.sessionQuery<br/>Exact session-history reads"]
|
||||||
pkg_system_prompt["system-prompt"]
|
pkg_system_prompt["system-prompt"]
|
||||||
@@ -33,7 +36,6 @@ flowchart LR
|
|||||||
pkg_tool_web["tool-web"]
|
pkg_tool_web["tool-web"]
|
||||||
svc_tools["ctx.tools<br/>Tool registry and guarded execution pipeline"]
|
svc_tools["ctx.tools<br/>Tool registry and guarded execution pipeline"]
|
||||||
pkg_tool_ask_user["tool-ask-user"]
|
pkg_tool_ask_user["tool-ask-user"]
|
||||||
pkg_tool_bash["tool-bash"]
|
|
||||||
pkg_tool_cordis["tool-cordis"]
|
pkg_tool_cordis["tool-cordis"]
|
||||||
pkg_tool_skill["tool-skill"]
|
pkg_tool_skill["tool-skill"]
|
||||||
pkg_tool_subagent["tool-subagent"]
|
pkg_tool_subagent["tool-subagent"]
|
||||||
@@ -51,8 +53,6 @@ flowchart LR
|
|||||||
svc_bash["ctx.bash<br/>Bash executor seam"]
|
svc_bash["ctx.bash<br/>Bash executor seam"]
|
||||||
pkg_bash_local["bash-local"]
|
pkg_bash_local["bash-local"]
|
||||||
pkg_bash_sandbox["bash-sandbox"]
|
pkg_bash_sandbox["bash-sandbox"]
|
||||||
pkg_hooks_claude["hooks-claude"]
|
|
||||||
pkg_hooks_codex["hooks-codex"]
|
|
||||||
svc_bashEnv["ctx.bashEnv<br/>Managed bash environment registry"]
|
svc_bashEnv["ctx.bashEnv<br/>Managed bash environment registry"]
|
||||||
pkg_sandbox["sandbox"]
|
pkg_sandbox["sandbox"]
|
||||||
svc_sandbox["ctx.sandbox<br/>Process-sandbox seam"]
|
svc_sandbox["ctx.sandbox<br/>Process-sandbox seam"]
|
||||||
@@ -151,7 +151,10 @@ flowchart LR
|
|||||||
svc_sandbox --> pkg_bash_sandbox
|
svc_sandbox --> pkg_bash_sandbox
|
||||||
svc_sessionPersistence --> pkg_acp
|
svc_sessionPersistence --> pkg_acp
|
||||||
svc_sessionPersistence --> pkg_agent_loop
|
svc_sessionPersistence --> pkg_agent_loop
|
||||||
|
svc_sessionPersistence --> pkg_hooks_claude
|
||||||
|
svc_sessionPersistence --> pkg_hooks_codex
|
||||||
svc_sessionPersistence --> pkg_session_query
|
svc_sessionPersistence --> pkg_session_query
|
||||||
|
svc_sessionPersistence --> pkg_tool_bash
|
||||||
svc_sessions --> pkg_agent
|
svc_sessions --> pkg_agent
|
||||||
svc_sessions --> pkg_agent_loop
|
svc_sessions --> pkg_agent_loop
|
||||||
svc_sessions --> pkg_invariants
|
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.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.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.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.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. |
|
| `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. |
|
||||||
|
|||||||
@@ -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)
|
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<Record<DshEnvironmentKey, string>>
|
||||||
|
```
|
||||||
|
|
||||||
## Request vs. spec: the `resolve()` split
|
## 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.
|
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.
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ Every foreground and background model bash call receives a newly collected trust
|
|||||||
import type { Context } from 'cordis'
|
import type { Context } from 'cordis'
|
||||||
import type {} from '@deepseek-ai/dsh-tool-bash'
|
import type {} from '@deepseek-ai/dsh-tool-bash'
|
||||||
|
|
||||||
|
export const inject = ['bashEnv']
|
||||||
|
|
||||||
export function apply(ctx: Context): void {
|
export function apply(ctx: Context): void {
|
||||||
ctx.bashEnv.register({
|
ctx.bashEnv.register({
|
||||||
name: 'deployment-region',
|
name: 'deployment-region',
|
||||||
|
|||||||
@@ -185,6 +185,8 @@ export class BashEnvRegistry extends Service {
|
|||||||
return Object.freeze(Object.fromEntries(Object.entries(values).sort(([left], [right]) => left.localeCompare(right))))
|
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.
|
* Enumerate plugin-contributed variables without executing their resolvers.
|
||||||
* @returns declarations sorted by environment variable name.
|
* @returns declarations sorted by environment variable name.
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ const SERVICE_ROLES: ServiceRole[] = [
|
|||||||
title: 'Durable session persistence seam',
|
title: 'Durable session persistence seam',
|
||||||
mode: 'seam',
|
mode: 'seam',
|
||||||
implementations: ['session-persistence-jsonl', 'session-persistence-sqlite'],
|
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.',
|
note: 'Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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": "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/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": "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": "BashExecSpec", "source": "packages/bash/bash/src/types.ts" },
|
||||||
{ "doc": "docs/core-data-structures/bash.md", "symbol": "BashRunResult", "source": "packages/bash/bash/src/types.ts" },
|
{ "doc": "docs/core-data-structures/bash.md", "symbol": "BashRunResult", "source": "packages/bash/bash/src/types.ts" },
|
||||||
|
|||||||
Reference in New Issue
Block a user