Merge branch 'codex/invariant-package-registration-gate' into codex/package-invariant-checks
# Conflicts: # .agents/notes/implemented/architecture/2026-07-19-package-invariant-runtime-contracts.i18n.yaml # .agents/notes/implemented/architecture/2026-07-19-package-invariant-runtime-contracts.md # .agents/notes/implemented/architecture/2026-07-19-package-invariant-runtime-contracts.zh.md # .agents/notes/implemented/architecture/2026-07-19-package-owned-invariant-service.i18n.yaml # docs/rfc/INDEX.md # packages/AGENTS.md
This commit is contained in:
@@ -57,58 +57,98 @@ The tool owns its `presentCall`/`presentResult` render intent. A foreground call
|
||||
|
||||
## The tool builds its request from named args only
|
||||
|
||||
The `BashExecRequest` seam carries optional `stdoutMaxBytes`, `stdin`, ordinary `env`, and managed `dshEnv`, used by trusted in-process plugins and this tool's environment registry. The model-facing tool exposes none of `stdoutMaxBytes`, `stdin`, or `env`: it builds requests from named command/workdir/timeout/signal/sandbox fields plus the registry-collected `dshEnv`. Extra model keys are ignored and cannot replace managed values. Shell syntax provides equivalent command-level behavior, while the local executor scrubs ambient credentials and stale `DSH_*` values. See the [stdin/env RFC](../../../docs/rfc/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md).
|
||||
The `BashExecRequest` seam carries optional `stdoutMaxBytes`, `stdin`, ordinary `env`, and managed `dshEnv`, used by trusted in-process plugins and this tool's environment registry. The model-facing tool exposes none of `stdoutMaxBytes`, `stdin`, or `env`: it builds requests from named command/workdir/timeout/signal/sandbox fields plus the registry-collected `dshEnv`. Extra model keys are ignored and cannot replace managed values. Shell syntax provides equivalent command-level behavior, while the local executor scrubs ambient credentials and stale `DSH_*` values. See the [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md).
|
||||
|
||||
## Permissions and escalation
|
||||
|
||||
Commands run with the executor's full authority unless a sandboxing executor ([`dsh-bash-sandbox`](../bash-sandbox/)) confines them — the deny-only sandbox reports denials as result facts, rendered here as the denial marker; per-call allow/deny/ask policy is the `tools/pre-execute` waterfall (see docs/architecture.md).
|
||||
|
||||
Escalating bash calls resolve `ctx.approval` before execution. `allowed-once` applies the requested mode only to that call; rejection, cancellation, unavailability, or missing approval context executes nothing and returns a distinct error. On a real denial, the model may retry the same command once in the same turn with the narrowest sufficient mode and justification; the approval prompt itself is the consent step. Escalation is never speculative, and a disabled or rejected approval is final. The [sandbox RFC](../../../docs/rfc/implemented/feature/2026-07-06-sandbox.md) owns the rationale.
|
||||
Escalating bash calls resolve `ctx.approval` before execution. `allowed-once` applies the requested mode only to that call; rejection, cancellation, unavailability, or missing approval context executes nothing and returns a distinct error. On a real denial, the model may retry the same command once in the same turn with the narrowest sufficient mode and justification; the approval prompt itself is the consent step. Escalation is never speculative, and a disabled or rejected approval is final. The [sandbox Agent Note](../../../.agents/notes/implemented/feature/2026-07-06-sandbox.md) owns the rationale.
|
||||
|
||||
## Per-session mode switching
|
||||
|
||||
For sandboxing executors, each call resolves mode as one-shot escalation, then session override, then executor default. Non-sandboxing and agent-less calls carry no session override. Neither the prompt nor a switch notice announces the standing mode; denial results report the effective mode when the boundary matters. See the [`dsh-bash` fold](../bash/README.md) and [sandbox switching contract](../../../docs/rfc/implemented/feature/2026-07-06-sandbox.md).
|
||||
For sandboxing executors, each call resolves mode as one-shot escalation, then session override, then executor default. Non-sandboxing and agent-less calls carry no session override. Neither the prompt nor a switch notice announces the standing mode; denial results report the effective mode when the boundary matters. See the [`dsh-bash` fold](../bash/README.md) and [sandbox switching contract](../../../.agents/notes/implemented/feature/2026-07-06-sandbox.md).
|
||||
|
||||
## Model Experience
|
||||
|
||||
### System prompt
|
||||
|
||||
**What the model sees**: Every request in this plugin's registration scope contains the bash guidance below. A sandboxing executor adds no mode statement or switch notice. Scoped tool restrictions can hide the schemas without removing this independently registered section.
|
||||
#### What the model sees
|
||||
|
||||
**Token effect**: Small fixed input cost per request while the plugin is active, unchanged by sandbox mode or mode switches.
|
||||
Every request in this plugin's registration scope contains the bash guidance below. A sandboxing executor adds no mode statement or switch notice. Scoped tool restrictions can hide the schemas without removing this independently registered section.
|
||||
|
||||
#### Bash guidance
|
||||
##### Bash guidance
|
||||
|
||||
```markdown
|
||||
Check the [exit code: N] marker on every bash result; investigate failures before moving on.
|
||||
```
|
||||
|
||||
#### Token effect
|
||||
|
||||
Small fixed input cost per request while the plugin is active, unchanged by sandbox mode or mode switches.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
Prefix-stable while the registration scope and prompt text are unchanged. Plugin activation or disposal may invalidate reuse from this prompt section; sandbox mode switches do not.
|
||||
|
||||
### Tool schemas
|
||||
|
||||
**What the model sees**: The model sees the generated [`bash` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-bash). `run_in_background` appears only when this producer enables it; `sandbox_permissions` and `justification` appear only when the mounted executor advertises sandboxing. Agent-scoped tool restrictions can remove the definition for that agent.
|
||||
#### What the model sees
|
||||
|
||||
**Token effect**: Fixed schema cost on every request where the tools are visible; sandbox support adds the escalation fields and its conditional description paragraph.
|
||||
The model sees the generated [`bash` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-bash). `run_in_background` appears only when this producer enables it; `sandbox_permissions` and `justification` appear only when the mounted executor advertises sandboxing. Agent-scoped tool restrictions can remove the definition for that agent.
|
||||
|
||||
#### Token effect
|
||||
|
||||
Fixed schema cost on every request where the tools are visible; sandbox support adds the escalation fields and its conditional description paragraph.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
Prefix-stable while visibility, background support, and executor sandbox capabilities are unchanged. A restriction, config change, or executor change may invalidate reuse from the first changed tool definition.
|
||||
|
||||
### Foreground result
|
||||
|
||||
**What the model sees**: The renderer emits the data-dependent stdout tail, then optional `[stderr]` and the stderr tail. With no output it emits exactly `(no output)`. Conditional lines are exactly `[output truncated; full output: <path-or-(unavailable)>]`, `[sandbox: file access denied under <mode> mode]`, `[timed out after <timeoutMs>ms]`, `[killed by signal: <signal>]`, and `[exit code: <exitCode>]`; the sandbox escalation and runner-failure lines are quoted in [`dsh-bash-sandbox`](../bash-sandbox/README.md).
|
||||
#### What the model sees
|
||||
|
||||
**Token effect**: Zero result tokens before a call. Output is bounded per stream, while each emitted line remains in history until compaction.
|
||||
The renderer emits the data-dependent stdout tail, then optional `[stderr]` and the stderr tail. With no output it emits exactly `(no output)`. Conditional lines are exactly `[output truncated; full output: <path-or-(unavailable)>]`, `[sandbox: file access denied under <mode> mode]`, `[timed out after <timeoutMs>ms]`, `[killed by signal: <signal>]`, and `[exit code: <exitCode>]`; the sandbox escalation and runner-failure lines are quoted in [`dsh-bash-sandbox`](../bash-sandbox/README.md).
|
||||
|
||||
#### Token effect
|
||||
|
||||
Zero result tokens before a call. Output is bounded per stream, while each emitted line remains in history until compaction.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
|
||||
|
||||
### Background task context and results
|
||||
|
||||
**What the model sees**: Start returns exactly `started background task <taskId>`. This producer supplies incremental process output, optional `[some output was dropped from memory; full output: <paths-or-(unavailable)>]`, sandbox facts, and terminal detail such as `exit code: <exitCode>` or `signal: <signal>` to the generic task runtime. [`dsh-tool-tasks`](../../tasks/tool-tasks/README.md) owns the visible status line, completion notice, listing, and cancellation response.
|
||||
#### What the model sees
|
||||
|
||||
**Token effect**: The start acknowledgement is small and retained; collected output is data-dependent and bounded by the executor's stream buffers. Consuming reads do not repeat prior output.
|
||||
Start returns exactly `started background task <taskId>`. This producer supplies incremental process output, optional `[some output was dropped from memory; full output: <paths-or-(unavailable)>]`, sandbox facts, and terminal detail such as `exit code: <exitCode>` or `signal: <signal>` to the generic task runtime. [`dsh-tool-tasks`](../../tasks/tool-tasks/README.md) owns the visible status line, completion notice, listing, and cancellation response.
|
||||
|
||||
#### Token effect
|
||||
|
||||
The start acknowledgement is small and retained; collected output is data-dependent and bounded by the executor's stream buffers. Consuming reads do not repeat prior output.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
|
||||
|
||||
### Tool errors
|
||||
|
||||
**What the model sees**: Validation and policy failures are normalized as `Error: <message>`. This package's stable messages are `invalid command: expected a non-empty string`, `invalid description: expected a non-empty string`, `invalid timeoutMs: expected a positive number, got <value>`, `invalid escalation: sandbox_permissions requires a justification`, `invalid escalation: justification is only valid together with sandbox_permissions`, `invalid justification: expected a non-empty sentence`, `background execution is disabled for this bash tool`, `background tasks unavailable: load @deepseek-ai/dsh-tasks and @deepseek-ai/dsh-tool-tasks`, `sandbox_permissions is not available in this composition (no sandboxing executor to escalate)`, `sandbox escalation to "<mode>" is not strictly wider than this call's current "<mode>" mode`, the approval-availability/rejection/cancellation variants, and `command aborted`.
|
||||
#### What the model sees
|
||||
|
||||
**Token effect**: Only the failing call adds these retained tokens; a rejected escalation does not add command output because the command does not run.
|
||||
Validation and policy failures are normalized as `Error: <message>`. This package's stable messages are `invalid command: expected a non-empty string`, `invalid description: expected a non-empty string`, `invalid timeoutMs: expected a positive number, got <value>`, `invalid escalation: sandbox_permissions requires a justification`, `invalid escalation: justification is only valid together with sandbox_permissions`, `invalid justification: expected a non-empty sentence`, `background execution is disabled for this bash tool`, `background tasks unavailable: load @deepseek-ai/dsh-tasks and @deepseek-ai/dsh-tool-tasks`, `sandbox_permissions is not available in this composition (no sandboxing executor to escalate)`, `sandbox escalation to "<mode>" is not strictly wider than this call's current "<mode>" mode`, the approval-availability/rejection/cancellation variants, and `command aborted`.
|
||||
|
||||
#### Token effect
|
||||
|
||||
Only the failing call adds these retained tokens; a rejected escalation does not add command output because the command does not run.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Replay exit pills parse from result text** — output whose final line happens to be exactly `[exit code: N]` / `[killed by signal: …]` shows a wrong pill on session replay; a display-only known residual.
|
||||
- **The `bash` tool opts out of `timeout-policy` budgets** — it keeps the executor-owned `BASH_TIMEOUT` path, per [the tool-call timeout-policy RFC](../../../docs/rfc/implemented/architecture/2026-07-07-tool-call-timeout-policy.md).
|
||||
- **The `bash` tool opts out of `timeout-policy` budgets** — it keeps the executor-owned `BASH_TIMEOUT` path, per [the tool-call timeout-policy Agent Note](../../../.agents/notes/implemented/architecture/2026-07-07-tool-call-timeout-policy.md).
|
||||
- **Background processes have no executor timeout** — callers must use `task_kill`, or rely on owner/service disposal, when work no longer matters.
|
||||
|
||||
@@ -287,7 +287,7 @@ describe('bash tool', () => {
|
||||
})
|
||||
|
||||
// Type and required-key violations are rejected by the harness
|
||||
// (defineTool validates against the SchemaSpec — the arg-validation RFC) before execute.
|
||||
// (defineTool validates against the SchemaSpec — the arg-validation Agent Note) before execute.
|
||||
it.each([
|
||||
[{}, /missing required property "command"/],
|
||||
[{ command: 42, description: 'd' }, /"command" must be a string/],
|
||||
@@ -946,7 +946,7 @@ describe('the model-facing bash tool builds its request from named args only (no
|
||||
* model input into the post-scrub `env` merge or per-run capture budget — NOT
|
||||
* to defend a trust boundary
|
||||
* (the credential scrub in dsh-bash-local is the security control; see the
|
||||
* bash-stdin-env RFC). Foreground `run()` returns a canned result; `start()`
|
||||
* bash-stdin-env Agent Note). Foreground `run()` returns a canned result; `start()`
|
||||
* hands back an already-settled fake handle so the task registration completes.
|
||||
*/
|
||||
class RecordingBashExecutor extends BashExecutor {
|
||||
|
||||
Reference in New Issue
Block a user