diff --git a/docs/cookbook/adding-a-package.md b/docs/cookbook/adding-a-package.md index e2f4a2cafe..7a99060869 100644 --- a/docs/cookbook/adding-a-package.md +++ b/docs/cookbook/adding-a-package.md @@ -68,7 +68,7 @@ Long stable prompt or schema description, copied exactly from source. Fill Model Experience from the implementation. Direct, multi-surface, conditional, capped, or lifetime effects use one H3 block per context surface; each block has the exact bold-led `What the model sees` and `Token effect` fields shown above. Quote short stable model-visible source literals verbatim in inline code, using named placeholders such as `` only for interpolated values. Put a long stable prompt or schema description under the optional exact `### Verbatim model-visible text` heading: give each literal an H4 title and its own `markdown` fence, then link it from the relevant context surface. Summarize only data-dependent payloads, provider-owned text, or schemas too large to reproduce, and identify their exact shape and renderer. Do not infer prompt visibility from tool-schema visibility because independently registered guidance can remain after a scoped tool restriction. -An audited package with no context effect or one simple consumer-owned path joins [`SENTENCE_MODEL_EXPERIENCE`](../../scripts/verify-package-readme-model-experience.ts) and replaces the structured blocks with one line beginning `None, as ` or `Indirectly, through `. Every package outside that allowlist keeps one or more context-surface blocks; the verifier gates their H3 headings, field labels, spacing, and the optional verbatim appendix's H4-plus-`markdown`-fence shape. A package with genuinely no limitations joins the separate allowlist in [`verify-readme-limitations.ts`](../../scripts/verify-readme-limitations.ts) and ends after Model Experience. The [Model Experience RFC](../rfc/implemented/process/2026-07-12-package-model-experience-contract.md) records the rationale. +An audited package with no context effect or one simple consumer-owned path belongs in [`SENTENCE_MODEL_EXPERIENCE`](../../scripts/verify-package-readme-model-experience.ts) and uses one line beginning `None, as ` or `Indirectly, through `. A provider backend whose single context path is formatted and inserted entirely by a named consumer uses the indirect sentence even when it caps or filters data before returning it; do not give it a structured block describing the consumer's work. Packages that register model input themselves, invoke a model, or own multiple context paths keep context-surface blocks; the verifier gates their H3 headings, field labels, spacing, and the optional verbatim appendix's H4-plus-`markdown`-fence shape. A package with genuinely no limitations joins the separate allowlist in [`verify-readme-limitations.ts`](../../scripts/verify-readme-limitations.ts) and ends after Model Experience. The [Model Experience RFC](../rfc/implemented/process/2026-07-12-package-model-experience-contract.md) records the rationale. ## 5. Verify diff --git a/docs/rfc/implemented/process/2026-07-12-package-model-experience-contract.md b/docs/rfc/implemented/process/2026-07-12-package-model-experience-contract.md index 15b26e791f..cbe0874331 100644 --- a/docs/rfc/implemented/process/2026-07-12-package-model-experience-contract.md +++ b/docs/rfc/implemented/process/2026-07-12-package-model-experience-contract.md @@ -10,7 +10,7 @@ A package README can explain APIs and runtime mechanics without answering the qu Every workspace package README ends with the canonical [Model Experience section](../../../cookbook/adding-a-package.md#4-write-the-package-readme), immediately before `## Known Limitations and Deferred Work`; a package on the no-limitations allowlist ends with Model Experience itself. Packages with direct, multi-surface, conditional, capped, or lifetime effects use one H3 block per context surface. Each block says what the relevant model literally receives and when under `**What the model sees**:`, then classifies the token effect under `**Token effect**:`. Short stable source literals are quoted verbatim in the first field, with named placeholders only for interpolated values. Long stable prompts or schema descriptions live in a `### Verbatim model-visible text` appendix after the context surfaces, one titled `markdown` fence per literal, and the relevant surface links to them. Summaries are reserved for data-dependent payloads, provider-owned text, or schemas too large to reproduce. The default subject is the conversation model; a package that invokes an auxiliary model, such as a summarizer or search provider, names that request separately. Agent-scoped visibility is stated where it changes which agent receives a contribution. Prompt text and tool schemas are described separately whenever configuration or scoping can hide one without the other. -Every package participates. A package with no model-context effect, or one simple effect rendered entirely by another package, can join the verifier's audited sentence allowlist. It then uses exactly one sentence beginning `None, as ` or `Indirectly, through ` instead of expanding a negative fact into a structured block. Implementations that shape results, caps, history, lifetimes, or more than one request surface keep the context-surface blocks even when they add zero direct prompt tokens. +Every package participates. A package with no model-context effect, or one simple effect rendered entirely by another package, uses the verifier's audited sentence allowlist. It carries exactly one sentence beginning `None, as ` or `Indirectly, through ` instead of expanding a negative fact into a structured block. A provider backend whose single context path is formatted and inserted entirely by a named consumer uses the indirect sentence even when it caps or filters data before returning it. Packages that register model input themselves, invoke a model, or own multiple context paths keep the context-surface blocks even when they add zero direct prompt tokens. `verify-package-readme-model-experience` discovers packages from `packages/*/*/package.json`, requires one sibling README and the canonical final-section order, and validates one of two package-classified bodies. An allowlisted package carries exactly one sentence with its assigned prefix; every other package carries at least one H3 context surface with the two exact, non-empty fields and one blank line between each element. Optional long literals follow the structured blocks under the exact appendix heading, with each H4 title paired to one non-empty `markdown` fence and linked from a model-view field. It runs in `doc-sync` and the parallel gate runner. The check owns package classification, structural presence, block and appendix shape, and order; implementation review owns coverage and the truth of the prose. diff --git a/packages/bash/bash-local/README.md b/packages/bash/bash-local/README.md index 1de3a7ef6b..e3692f2558 100644 --- a/packages/bash/bash-local/README.md +++ b/packages/bash/bash-local/README.md @@ -27,11 +27,7 @@ Design surveyed against the bash tools of Claude Code, OpenCode, Codex, and pi; ## Model Experience -### Bash tool results, indirectly - -**What the model sees**: Through `dsh-tool-bash`, the conversation model sees the data-dependent stdout and stderr tail inside that consumer's exact result wrappers, exit and timeout markers, background-task state, and a spill-file path when full output is available. This backend adds no prompt or schema itself. An unknown task becomes exactly `Error: unknown bash task ""`; a pre-spawn cancellation becomes `Error: aborted before spawn: `. - -**Token effect**: Zero tokens until a bash tool runs. Foreground output is bounded per stream by `maxOutputBytes`; background reads return only new output, so polling does not repeat already-delivered text. Results remain in history until compaction. +Indirectly, through `dsh-tool-bash`, which renders this executor's bounded stdout/stderr tails, background-task deltas and state, spill-file path, exact `Error: unknown bash task ""` and `Error: aborted before spawn: ` failures, and retains each resulting tool message until compaction. ## Known Limitations and Deferred Work diff --git a/packages/code-runtime/code-runtime-worker/README.md b/packages/code-runtime/code-runtime-worker/README.md index dcbd633236..87284d4e36 100644 --- a/packages/code-runtime/code-runtime-worker/README.md +++ b/packages/code-runtime/code-runtime-worker/README.md @@ -33,11 +33,7 @@ Every field is validated (positive numbers) and defaulted; there are no other tu ## Model Experience -### `run_code` result, indirectly - -**What the model sees**: Through Code Mode in `dsh-tools`, the conversation model sees only what the program prints or returns. A log cap emits exactly `[dsh-code-runtime-worker] log capture truncated at bytes`; an oversized or non-transferable return rendered as text ends exactly `… [truncated]`. Worker failures become `Error: code run failed (): `; this backend's stable messages include `compute budget exhausted (ms busy)`, `wall-clock ceiling reached (ms)`, `worker error: `, and `worker exited with code before completing`. Binding-call traffic and worker internals stay outside context. - -**Token effect**: Zero tokens until Code Mode executes a program. `maxLogBytes` and `maxValueBytes` cap the model-visible result, which then remains in tool history until compaction. +Indirectly, through Code Mode in `dsh-tools`, which renders this worker's capped printed or returned data, exact `[dsh-code-runtime-worker] log capture truncated at bytes` and `… [truncated]` markers, and `Error: code run failed (): ` failures into a retained `run_code` result while keeping binding traffic and worker internals outside context. ## Known Limitations and Deferred Work diff --git a/packages/fs/fs-local/README.md b/packages/fs/fs-local/README.md index 45f179c8c5..697a1ddb71 100644 --- a/packages/fs/fs-local/README.md +++ b/packages/fs/fs-local/README.md @@ -23,11 +23,7 @@ The raw I/O lives in `src/fsio.ts` (Cordis-free, independently unit-tested); `sr ## Model Experience -### Filesystem tool results, indirectly - -**What the model sees**: Through `dsh-tool-fs`, the model sees line-windowed UTF-8 file content or mutation acknowledgements. This backend's stable failures are normalized as `Error: `; messages include `cannot read "": binary file`, `cannot "": invalid UTF-8 text`, `cannot write "": not a regular file`, `cannot write "": file no longer exists`, `cannot "": file changed since it was read`, `cannot overwrite existing "" without reading it first`, `cannot edit "": binary file`, `old_string must be a non-empty string`, `old_string was not found in ""`, and `old_string matched times in ""; provide a more specific old_string or set replace_all to true`. Real paths, versions, atomic-write mechanics, and directory metadata remain internal unless a consumer renders them. - -**Token effect**: Zero direct tokens. Read tokens are bounded by the tool's line, line-length, and byte caps; mutation results or errors remain in history until compaction. +Indirectly, through `dsh-tool-fs`, which renders this provider's line-windowed UTF-8 content, mutation acknowledgements, and exact provider messages under `Error: ` into capped retained tool results while versions, atomic-write mechanics, and directory metadata remain internal. ## Known Limitations and Deferred Work diff --git a/packages/sandbox/sandbox-local/README.md b/packages/sandbox/sandbox-local/README.md index 4a08964ba8..578dc04bb1 100644 --- a/packages/sandbox/sandbox-local/README.md +++ b/packages/sandbox/sandbox-local/README.md @@ -19,11 +19,7 @@ Consumers: [`@deepseek-ai/dsh-bash-sandbox`](../../bash/bash-sandbox/); see [`ex ## Model Experience -### Sandbox result facts, indirectly - -**What the model sees**: This provider adds no prompt or tool. It supplies the selected enforcement and denial dialect to `dsh-bash-sandbox`, which can become that consumer's exact `[sandbox: file access denied under mode]` marker. If no local runner can confine the command, the model instead receives the exact `SANDBOX_UNAVAILABLE` text quoted in [`dsh-sandbox`](../sandbox/README.md). Runner selection and profiles are not shown. - -**Token effect**: Zero direct tokens; only the conditional marker or error reaches context through the bash consumer. +Indirectly, through `dsh-bash-sandbox` and `dsh-tool-bash`, which render this provider's enforcement dialect as the exact `[sandbox: file access denied under mode]` marker or the [`dsh-sandbox`](../sandbox/README.md) `SANDBOX_UNAVAILABLE` text while keeping runner selection and profiles outside context. ## Known Limitations and Deferred Work diff --git a/packages/sandbox/sandbox/README.md b/packages/sandbox/sandbox/README.md index b0eb7f4050..0e77b52b29 100644 --- a/packages/sandbox/sandbox/README.md +++ b/packages/sandbox/sandbox/README.md @@ -12,11 +12,7 @@ Implementations: [`@deepseek-ai/dsh-sandbox-local`](../sandbox-local/) (Linux: ` ## Model Experience -### Sandbox result, indirectly - -**What the model sees**: Through `dsh-bash-sandbox` and `dsh-tool-bash`, enforcement facts may become that consumer's exact denial marker. `SandboxUnavailableError` becomes exactly `Error: sandbox mode "" is requested but no sandbox backend is usable on this host; refusing to run the command unconfined. Install bubblewrap or run a Landlock-enforcing kernel (Linux), ensure sandbox-exec is usable (macOS) — Windows has no confinement backend yet — or switch the consumer to danger-full-access.`, optionally followed by ` Runner failure: `. - -**Token effect**: This package adds no prompt or schema. Only a denial or failed confinement adds retained result tokens. +Indirectly, through [`dsh-bash-sandbox`](../../bash/bash-sandbox/README.md) and `dsh-tool-bash`, which render this seam's enforcement facts as the exact denial or `SandboxUnavailableError` text documented by the consumer, with retained tokens added only for a denial or failed confinement. ## Known Limitations and Deferred Work diff --git a/packages/skill/skill-local/README.md b/packages/skill/skill-local/README.md index 83ac2ce770..50584c95ef 100644 --- a/packages/skill/skill-local/README.md +++ b/packages/skill/skill-local/README.md @@ -38,11 +38,7 @@ Skills can be single-level directory bundles (`/SKILL.md`) or flat Markdow ## Model Experience -### Skill catalog and loaded body, indirectly - -**What the model sees**: When `dsh-tool-skill` is visible, discovered model-invocable skill names and descriptions enter its session-prefix catalog; a `skill` call returns the selected instructions and resource-base guidance. Paths, provider ranks, and disabled skills stay out of the catalog. - -**Token effect**: Zero direct tokens from this provider. Catalog cost scales with discovered entries under the consumer's per-description cap; a full body is added only after selection and remains in tool history. +Indirectly, through `dsh-tool-skill`, which renders this provider's invocable names and capped descriptions into the session-prefix catalog and a selected instruction body plus resource-base guidance into retained tool history while paths, provider ranks, and disabled skills remain hidden. ## Known Limitations and Deferred Work diff --git a/packages/subagent/subagent/README.md b/packages/subagent/subagent/README.md index 5f8204842b..261effb450 100644 --- a/packages/subagent/subagent/README.md +++ b/packages/subagent/subagent/README.md @@ -62,11 +62,7 @@ The current model-facing tool collects synchronously: it awaits the child result ## Model Experience -### Delegation result, indirectly - -**What the model sees**: Through `dsh-tool-subagent`, registered providers create child contexts and return data-dependent final output. A missing provider or unsupported requested capability becomes exactly `Error: no subagent provider registered for ""` or `Error: subagent provider "" does not support the "" capability`. Provider-specific start errors receive the same `Error: ` wrapper. - -**Token effect**: This seam adds no parent schema itself. The parent retains only the final output or start error; child working tokens remain in the child. +Indirectly, through `dsh-tool-subagent`, which retains only a provider's data-dependent final output or exact `Error: no subagent provider registered for ""`, `Error: subagent provider "" does not support the "" capability`, and `Error: ` start failures in the parent while child working tokens remain child-only. ## Known Limitations and Deferred Work diff --git a/packages/ui/user-interaction/README.md b/packages/ui/user-interaction/README.md index e69786cdc8..a6677b3c65 100644 --- a/packages/ui/user-interaction/README.md +++ b/packages/ui/user-interaction/README.md @@ -25,11 +25,7 @@ This is the interface package. Model-facing consumers such as `@deepseek-ai/dsh- ## Model Experience -### Human-answer result, indirectly - -**What the model sees**: Through `dsh-tool-ask-user`, a successful provider answer becomes that consumer's exact compact JSON result. Seam-level failures become exactly `Error: ask_user_question was aborted before the user answered`, `Error: ask_user_question requires at least one question`, or `Error: no user-interaction provider is registered`; provider-owned failures receive the same `Error: ` wrapper. - -**Token effect**: This seam adds no prompt or schema. Only the consumer's completed or failed tool call adds retained tokens; waiting for the human adds none. +Indirectly, through `dsh-tool-ask-user`, which retains a successful provider answer as compact JSON or the exact `Error: ask_user_question was aborted before the user answered`, `Error: ask_user_question requires at least one question`, `Error: no user-interaction provider is registered`, and `Error: ` failures while waiting for the human adds no tokens. ## Known Limitations and Deferred Work diff --git a/packages/web/web-fetch-local/README.md b/packages/web/web-fetch-local/README.md index a351cabcbd..fc4dd9995e 100644 --- a/packages/web/web-fetch-local/README.md +++ b/packages/web/web-fetch-local/README.md @@ -35,11 +35,7 @@ The numeric limits are validated at plugin construction: every cap except `maxRe ## Model Experience -### Web fetch result, indirectly - -**What the model sees**: Through `dsh-tool-web`, the conversation model sees that consumer's exact fetch header plus the data-dependent decoded text or markdown-shaped HTML. This provider's stable failures become `Error: `; messages include `web fetch aborted`, `web fetch timed out`, `web fetch failed: `, `exceeded the maximum of redirects`, `redirect response (HTTP ) without a Location header`, `invalid redirect Location ""`, `cross-origin redirect to is not followed automatically; retry against that URL directly`, `unsupported content type ""`, `response exceeds the maximum of bytes`, `URL exceeds the maximum length of `, `invalid URL: `, `unsupported URL scheme "" (only http and https are allowed)`, `credentials in URLs are not allowed`, and `unsupported charset ""`. Redirects, headers, and transport mechanics are otherwise absent. - -**Token effect**: Zero direct tokens. `maxBodyChars` bounds decoded result length before the tool records it; the retained result or error is resent until compaction. +Indirectly, through `dsh-tool-web`, which renders this provider's `maxBodyChars`-bounded decoded text or markdown-shaped HTML under the exact fetch header and its stable failures under `Error: ` into retained tool history while redirects, headers, and transport mechanics remain hidden. ## Known Limitations and Deferred Work diff --git a/packages/web/web-search-exa/README.md b/packages/web/web-search-exa/README.md index fa728c8668..7562dcf8ff 100644 --- a/packages/web/web-search-exa/README.md +++ b/packages/web/web-search-exa/README.md @@ -27,11 +27,7 @@ Exa returns a flat `results[]` and no generated answer, so `content` is omitted. ## Model Experience -### Web search result, indirectly - -**What the model sees**: Through `dsh-tool-web`, the conversation model sees Exa result URLs, titles, first highlight snippets, and publication dates. No generated answer or provider-private response fields enter the tool result. Failures become `Error: Exa search aborted`, `Error: Exa search request failed: `, or `Error: Exa returned an unprocessable response body: `; HTTP failures pass through their provider message after `Error:`. - -**Token effect**: Zero direct harness-model tokens. Result size scales with the bounded source list and snippets; the seam enforces `maxResults`, and retained results or errors remain until compaction. +Indirectly, through `dsh-tool-web`, which retains this provider's `maxResults`-bounded URLs, titles, first highlights, and publication dates or exact `Error: Exa search aborted`, `Error: Exa search request failed: `, and `Error: Exa returned an unprocessable response body: ` failures while generated answers and provider-private fields remain outside context. ## Known Limitations and Deferred Work diff --git a/packages/web/web/README.md b/packages/web/web/README.md index 2f1006fd22..01677ed344 100644 --- a/packages/web/web/README.md +++ b/packages/web/web/README.md @@ -45,11 +45,7 @@ The failure branches throw `WebError`, whose structured code (plus message detai ## Model Experience -### Search or fetch result, indirectly - -**What the model sees**: Through `dsh-tool-web`, the model sees bounded normalized provider data. Selection failures become exactly `Error: configured web provider "" is not registered`, `Error: configured web provider "" is registered but unavailable`, `Error: no usable web provider is registered`, or `Error: multiple usable web providers are registered (); configure one explicitly`; provider failures use the same `Error: ` wrapper. - -**Token effect**: This seam adds no prompt or schema. Only a call result or error adds retained tokens; source caps bound search output. +Indirectly, through `dsh-tool-web`, which retains bounded normalized provider data or the exact configured-provider, unavailable-provider, no-provider, multiple-provider, and `Error: ` failures while this registry contributes no prompt or schema itself. ## Known Limitations and Deferred Work diff --git a/scripts/verify-package-readme-model-experience.ts b/scripts/verify-package-readme-model-experience.ts index a16f833f9f..baa6189d94 100644 --- a/scripts/verify-package-readme-model-experience.ts +++ b/scripts/verify-package-readme-model-experience.ts @@ -33,16 +33,27 @@ interface SentenceContract { */ const SENTENCE_MODEL_EXPERIENCE: Readonly> = { 'packages/bash/bash': { kind: 'indirect', reason: 'The service interface delegates all model rendering to dsh-tool-bash.' }, + 'packages/bash/bash-local': { kind: 'indirect', reason: 'The executor backend delegates model rendering to dsh-tool-bash.' }, 'packages/code-runtime/code-runtime': { kind: 'indirect', reason: 'The service interface delegates model rendering to Code Mode in dsh-tools.' }, + 'packages/code-runtime/code-runtime-worker': { kind: 'indirect', reason: 'The worker backend delegates model rendering to Code Mode in dsh-tools.' }, 'packages/fs/fs': { kind: 'indirect', reason: 'The service interface delegates model rendering to dsh-tool-fs.' }, + 'packages/fs/fs-local': { kind: 'indirect', reason: 'The provider backend delegates model rendering to dsh-tool-fs.' }, 'packages/hooks/hook-protocol': { kind: 'indirect', reason: 'Only the hook bridge plugins render decoded hook output to a model.' }, + 'packages/sandbox/sandbox': { kind: 'indirect', reason: 'Sandbox consumers render enforcement and availability facts.' }, + 'packages/sandbox/sandbox-local': { kind: 'indirect', reason: 'The provider backend delegates model rendering to dsh-bash-sandbox and dsh-tool-bash.' }, 'packages/skill/skill': { kind: 'indirect', reason: 'The provider registry delegates model rendering to dsh-tool-skill.' }, + 'packages/skill/skill-local': { kind: 'indirect', reason: 'The provider backend delegates model rendering to dsh-tool-skill.' }, + 'packages/subagent/subagent': { kind: 'indirect', reason: 'The provider registry delegates parent-model rendering to dsh-tool-subagent.' }, 'packages/subagent/subagent-subprocess': { kind: 'indirect', reason: 'Only process-based subagent backends compose a child model request.' }, 'packages/support/acp-snapshot': { kind: 'none', reason: 'The test harness observes and normalizes transcripts without changing live requests.' }, 'packages/support/invariants': { kind: 'none', reason: 'The observer validates requests but never rewrites their context.' }, 'packages/ui/app-boot': { kind: 'indirect', reason: 'Only the loaded plugin tree contributes model context.' }, + 'packages/ui/user-interaction': { kind: 'indirect', reason: 'Model-facing consumers render provider answers and seam errors.' }, 'packages/util/brand': { kind: 'none', reason: 'The type-only primitive is erased at compile time.' }, 'packages/util/timeout': { kind: 'indirect', reason: 'Only timeout consumers render timeout outcomes.' }, + 'packages/web/web': { kind: 'indirect', reason: 'The provider registry delegates model rendering to dsh-tool-web.' }, + 'packages/web/web-fetch-local': { kind: 'indirect', reason: 'The provider backend delegates model rendering to dsh-tool-web.' }, + 'packages/web/web-search-exa': { kind: 'indirect', reason: 'The provider backend delegates model rendering to dsh-tool-web.' }, 'packages/workflow/workflow': { kind: 'indirect', reason: 'The service delegates parent and child model rendering to its consumer and engine.' }, }