From 43ac97b554845929707f075cc29ef001fee3a173 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Tue, 25 Aug 2026 11:06:01 +0800 Subject: [PATCH] fix(system-prompt): centralize sparse section orders --- ...system-prompt-section-order-ties.i18n.yaml | 4 +- ...-08-24-system-prompt-section-order-ties.md | 1 + ...-24-system-prompt-section-order-ties.zh.md | 1 + .agents/notes/archived/manifest.json | 3 + ...bles-and-tool-guidance-ownership.i18n.yaml | 4 +- ...t-variables-and-tool-guidance-ownership.md | 2 +- ...ariables-and-tool-guidance-ownership.zh.md | 2 +- ...irst-party-prompt-section-orders.i18n.yaml | 6 ++ ...parse-first-party-prompt-section-orders.md | 57 +++++++++++++++ ...se-first-party-prompt-section-orders.zh.md | 57 +++++++++++++++ ...8-07-code-mode-executor-collapse.i18n.yaml | 4 +- .../2026-08-07-code-mode-executor-collapse.md | 2 +- ...26-08-07-code-mode-executor-collapse.zh.md | 2 +- ...tinuable-child-report-obligation.i18n.yaml | 4 +- ...-06-continuable-child-report-obligation.md | 2 +- ...-continuable-child-report-obligation.zh.md | 2 +- docs/config-catalog.i18n.yaml | 4 +- docs/config-catalog.md | 10 +-- docs/config-catalog.zh.md | 10 +-- docs/subsystems/system-prompt.i18n.yaml | 4 +- docs/subsystems/system-prompt.md | 8 +-- docs/subsystems/system-prompt.zh.md | 8 +-- packages/boot/app-boot/src/index.ts | 9 ++- packages/boot/app-boot/tests/app-boot.spec.ts | 4 +- packages/bundle/web-app/src/index.ts | 4 +- .../client/ui-deliverables/README.i18n.yaml | 4 +- packages/client/ui-deliverables/README.md | 2 +- packages/client/ui-deliverables/README.zh.md | 2 +- packages/client/ui-deliverables/src/index.ts | 4 +- .../context/file-reference-local/src/index.ts | 4 +- packages/core/system-prompt/README.i18n.yaml | 4 +- packages/core/system-prompt/README.md | 10 +-- packages/core/system-prompt/README.zh.md | 10 +-- packages/core/system-prompt/src/index.ts | 71 ++++++++++++++++--- .../system-prompt/tests/system-prompt.spec.ts | 27 +++++-- packages/core/tools/README.i18n.yaml | 4 +- packages/core/tools/README.md | 2 +- packages/core/tools/README.zh.md | 2 +- packages/core/tools/src/code-mode.ts | 5 +- packages/core/tools/src/index.ts | 12 ++-- packages/core/tools/tests/code-mode.spec.ts | 24 +++++-- .../experimental/tool-agent-team/src/index.ts | 3 +- packages/extensions/tool-cordis/src/index.ts | 8 ++- packages/fs/tool-fs-search/src/glob.ts | 4 +- packages/fs/tool-fs-search/src/grep.ts | 4 +- packages/fs/tool-fs/src/edit.ts | 4 +- packages/fs/tool-fs/src/read.ts | 4 +- packages/fs/tool-fs/src/write.ts | 4 +- packages/goal/tool-goal/src/index.ts | 4 +- packages/jobs/tool-jobs/src/index.ts | 6 +- packages/lsp/tool-lsp/src/index.ts | 8 ++- packages/plan/plan-mode/src/index.ts | 4 +- .../tool-session-query/src/index.ts | 4 +- packages/shell/tool-bash/src/index.ts | 4 +- packages/shell/tool-bash/tests/tools.spec.ts | 14 +++- packages/shell/tool-pwsh/src/index.ts | 4 +- .../README.i18n.yaml | 4 +- .../subagent-in-process-driver/README.md | 2 +- .../subagent-in-process-driver/README.zh.md | 2 +- .../src/structured.ts | 7 +- .../tests/structured.spec.ts | 9 ++- packages/subagent/subagent/package.json | 2 + packages/subagent/subagent/src/child-agent.ts | 3 +- packages/subagent/subagent/tsconfig.json | 3 + .../tool-subagent-report/src/index.ts | 4 +- packages/subagent/tool-subagent/src/index.ts | 4 +- packages/terminal/tool-terminal/src/index.ts | 3 +- packages/web/tool-web/src/fetch.ts | 4 +- packages/web/tool-web/src/search.ts | 4 +- packages/workflow/tool-ralph/src/index.ts | 5 +- packages/workflow/tool-workflow/src/index.ts | 5 +- pnpm-lock.yaml | 3 + .../system-prompt.1.expected.md | 4 +- .../system-prompt.1.expected.md | 4 +- .../system-prompt.1.expected.md | 4 +- .../sdk/text-turn/system-prompt.expected.md | 4 +- .../system-prompt.expected.md | 4 +- .../both-mode-turn/system-prompt.expected.md | 4 +- .../system-prompt.expected.md | 4 +- .../code-mode-turn/system-prompt.expected.md | 4 +- .../system-prompt.expected.md | 4 +- .../system-prompt.expected.md | 4 +- .../lsp-definition/system-prompt.expected.md | 4 +- .../system-prompt.expected.md | 4 +- .../system-prompt.expected.md | 4 +- .../ralph-loop/system-prompt.1.expected.md | 4 +- .../ralph-loop/system-prompt.2.expected.md | 4 +- .../read-image/system-prompt.expected.md | 4 +- .../system-prompt.expected.md | 4 +- .../text-turn/system-prompt.expected.md | 4 +- .../web-fetch/system-prompt.expected.md | 4 +- 91 files changed, 429 insertions(+), 191 deletions(-) rename .agents/notes/{implemented => archived}/bug-fix/2026-08-24-system-prompt-section-order-ties.i18n.yaml (67%) rename .agents/notes/{implemented => archived}/bug-fix/2026-08-24-system-prompt-section-order-ties.md (98%) rename .agents/notes/{implemented => archived}/bug-fix/2026-08-24-system-prompt-section-order-ties.zh.md (98%) create mode 100644 .agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.i18n.yaml create mode 100644 .agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.md create mode 100644 .agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.zh.md diff --git a/.agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.i18n.yaml b/.agents/notes/archived/bug-fix/2026-08-24-system-prompt-section-order-ties.i18n.yaml similarity index 67% rename from .agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.i18n.yaml rename to .agents/notes/archived/bug-fix/2026-08-24-system-prompt-section-order-ties.i18n.yaml index 17d2684a6c..b01341e7d2 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.i18n.yaml +++ b/.agents/notes/archived/bug-fix/2026-08-24-system-prompt-section-order-ties.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.md -2026-08-24-system-prompt-section-order-ties.md: 673c3b3cd668115ead9f9b5478c2bc432b78f930 -2026-08-24-system-prompt-section-order-ties.zh.md: 96a6843a0db48e52a2132ad9f8caf6243dfbbcd2 +2026-08-24-system-prompt-section-order-ties.md: d92756e751e893b1d03b8892ef71ff9faac9d2c6 +2026-08-24-system-prompt-section-order-ties.zh.md: 4a822b7925a38feb254dbc534fc6153c76a93e19 diff --git a/.agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.md b/.agents/notes/archived/bug-fix/2026-08-24-system-prompt-section-order-ties.md similarity index 98% rename from .agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.md rename to .agents/notes/archived/bug-fix/2026-08-24-system-prompt-section-order-ties.md index 673c3b3cd6..d92756e751 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.md +++ b/.agents/notes/archived/bug-fix/2026-08-24-system-prompt-section-order-ties.md @@ -1,6 +1,7 @@ # Agent Note: Equal-order system-prompt sections render in activation order Status: implemented +Archived: 2026-08-25 English | [中文](2026-08-24-system-prompt-section-order-ties.zh.md) diff --git a/.agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.zh.md b/.agents/notes/archived/bug-fix/2026-08-24-system-prompt-section-order-ties.zh.md similarity index 98% rename from .agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.zh.md rename to .agents/notes/archived/bug-fix/2026-08-24-system-prompt-section-order-ties.zh.md index 96a6843a0d..4a822b7925 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-24-system-prompt-section-order-ties.zh.md +++ b/.agents/notes/archived/bug-fix/2026-08-24-system-prompt-section-order-ties.zh.md @@ -1,6 +1,7 @@ # Agent Note: 等序系统提示词分段按激活顺序渲染 Status: implemented +Archived: 2026-08-25 [English](2026-08-24-system-prompt-section-order-ties.md) | 中文 diff --git a/.agents/notes/archived/manifest.json b/.agents/notes/archived/manifest.json index aa76eab57c..bb58c4fe5a 100644 --- a/.agents/notes/archived/manifest.json +++ b/.agents/notes/archived/manifest.json @@ -112,6 +112,9 @@ "bug-fix/2026-08-12-collapsed-sidebar-shared-entry-motion.i18n.yaml": "sha256:3ce4f6e39e173fc304bf64deca9c95bcddc1dbb492e065ca8c267a7a40788588", "bug-fix/2026-08-12-collapsed-sidebar-shared-entry-motion.md": "sha256:7b169aa4543edfc965de5a8b7b9e60aa9d9d5218693cd0b57908e2d482280723", "bug-fix/2026-08-12-collapsed-sidebar-shared-entry-motion.zh.md": "sha256:88db36c698800bf55c3c7531d6f92665576d978c29c15ff7d74215fb93376cb1", + "bug-fix/2026-08-24-system-prompt-section-order-ties.i18n.yaml": "sha256:f7a20bddd4544738ec0dbbfc52ea931f42317defa1674beb9a3c0daebd52fc2d", + "bug-fix/2026-08-24-system-prompt-section-order-ties.md": "sha256:108a97346eb7a62f1ab01f48dbb9fdd965e8991f53e382b0f501b916af0e9e23", + "bug-fix/2026-08-24-system-prompt-section-order-ties.zh.md": "sha256:3deaddfcf9736b3ff8d61b51093d7e46fdcc86103705033e4aa4c9d043794b16", "feature/2026-06-14-acp-agent-client-protocol.i18n.yaml": "sha256:006795baa43ae962a8d125cc0f1e9f134bc2ee9fb758b6e7669e3fa0126e1918", "feature/2026-06-14-acp-agent-client-protocol.md": "sha256:6828c0af74bb3fb96206ca6b21c0e56a000b50e4744aad4bc2c05092f3a5a31b", "feature/2026-06-14-acp-agent-client-protocol.zh.md": "sha256:ba104e841a1fb84edbd3b6c8119d50445b7785255a7a8d13bb9ac8a2cb4d2e69", diff --git a/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.i18n.yaml b/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.i18n.yaml index 13871d1cff..fcdffd69cd 100644 --- a/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md -2026-07-05-prompt-variables-and-tool-guidance-ownership.md: 9a53619d9510e3f4fa561f8420b2da3bedbbf4bb -2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md: dc1164198df0f92b843c75b618f140d8aef86e4f +2026-07-05-prompt-variables-and-tool-guidance-ownership.md: 361184fa7dbdaccd49ac19235c016daf5eb5ca53 +2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md: 6b462572883fb69ca64f2babf28974ae59e7bd74 diff --git a/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md b/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md index 9a53619d95..361184fa7d 100644 --- a/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md +++ b/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md @@ -32,7 +32,7 @@ Plugins register `{{name}}` values through `ctx.systemPrompt.variable(name, prov ### Persona as the order-0 section -`dsh-system-prompt` owns `harness:identity` at order `-100` and the configured `deployment:persona` at order 0, so both survive a replacement loop. Prompt rendering has one path, `renderPrompt(assembly)`, and the routed request header therefore records the exact prompt later replayed by `ctx.tokenMeter` for compaction pressure. An agent-scoped `deployment:persona` shadows the global default and lets subagent providers install a persona before publication. The conventional order bands are identity `-100`, persona `0`, and tool guidance `100–199`. +`dsh-system-prompt` owns `harness:identity` at first-party order `-1000` and the configured `deployment:persona` at order 0, so both survive a replacement loop. Prompt rendering has one path, `renderPrompt(assembly)`, and the routed request header therefore records the exact prompt later replayed by `ctx.tokenMeter` for compaction pressure. An agent-scoped `deployment:persona` shadows the global default and lets subagent providers install a persona before publication. The [first-party order allocation](2026-08-25-sparse-first-party-prompt-section-orders.md) owns the sparse named placements for identity, policy, tool guidance, generated protocol, and final-output obligations. ### Tool guidance ownership diff --git a/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md b/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md index dc1164198d..6b46257288 100644 --- a/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md +++ b/.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md @@ -32,7 +32,7 @@ Status: implemented ### Persona 作为 order-0 section -`dsh-system-prompt` 拥有 order 为 `-100` 的 `harness:identity` 和 order 为 0 的配置 `deployment:persona`,因此两者在循环被替换时仍然存活。提示词渲染只有一条路径 `renderPrompt(assembly)`,已路由请求 header 因此会记录准确的提示词,稍后由 `ctx.tokenMeter` 为压缩(compaction)压力回放。agent 作用域的 `deployment:persona` 遮蔽全局默认值,允许 subagent 提供方在发布前安装 persona。约定的 order 区间为:identity `-100`、persona `0`、工具指导 `100–199`。 +`dsh-system-prompt` 拥有 first-party order 为 `-1000` 的 `harness:identity` 和 order 为 0 的配置 `deployment:persona`,因此两者在循环被替换时仍然存活。提示词渲染只有一条路径 `renderPrompt(assembly)`,已路由请求 header 因此会记录准确的提示词,稍后由 `ctx.tokenMeter` 为压缩(compaction)压力回放。agent 作用域的 `deployment:persona` 遮蔽全局默认值,允许 subagent 提供方在发布前安装 persona。[first-party 顺序分配](2026-08-25-sparse-first-party-prompt-section-orders.zh.md)规定身份、策略、工具指导、生成协议和最终输出义务的稀疏具名位置。 ### 工具指导归属 diff --git a/.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.i18n.yaml new file mode 100644 index 0000000000..c7d62f839f --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.md +2026-08-25-sparse-first-party-prompt-section-orders.md: 2bf2e7441b449a6cfbd5b845f1d7e97b3fab09ae +2026-08-25-sparse-first-party-prompt-section-orders.zh.md: 624ed51f4d40848c72091097900ef05ec35fdd2e diff --git a/.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.md b/.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.md new file mode 100644 index 0000000000..2bf2e7441b --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.md @@ -0,0 +1,57 @@ +# Agent Note: Centralize sparse first-party prompt-section orders + +Status: implemented + +English | [中文](2026-08-25-sparse-first-party-prompt-section-orders.zh.md) + +## Problem + +Repository-owned system-prompt sections declared unrelated numeric literals across more than twenty packages. The main tool sequence occupied consecutive values from 100 through 117 and then used half-step values for insertions. A later change could therefore collide with an existing section without seeing the complete allocation. + +Equal orders used stable JavaScript sort behavior, which made plugin activation order the effective tie-breaker. The [Cordis/workflow prompt-order fix](../../archived/bug-fix/2026-08-24-system-prompt-section-order-ties.md) showed that clean compositions can activate the same plugins in different orders and produce different request headers and snapshot results. Fixing one collision locally did not prevent another package from reusing that value. + +The shell guidance also followed filesystem guidance even though shell commands have the broadest execution and failure semantics. A model should read the shell result obligation before the narrower instructions that route file work to dedicated tools. + +## Decision + +`@deepseek-ai/dsh-system-prompt` exports `FIRST_PARTY_SECTION_ORDER` as the single allocation for repository-owned sections. Every first-party contributor imports its named placement instead of declaring a numeric literal. Values are unique integers, and adjacent allocated values differ by at least ten. + +The allocation preserves the established first-party sequence except for two deliberate changes: Bash, or PowerShell in the Windows composition, leads per-tool guidance; and sections that shared an order receive an explicit sequence. The groups are: + +| Group | Entries | +|---|---| +| Product opening | `harness:identity` −1000, `harness:source` −900, `app:web-surface` −800, `deployment:persona` 0 | +| Work modes | `plan:policy` 500, `team:policy` 600 | +| Invocation prelude | `tools:code-only` 800, `context:file-reference` 900 | +| Local tools | `tool:bash` 1000, `tool:pwsh` 1010, `tool:read` 1100, `tool:write` 1200, `tool:edit` 1300, `tool:glob` 1400, `tool:grep` 1500, `tool:jobs` 1600, `tool:pty` 1700 | +| Higher-level tools | `tool:web_search` 2000, `tool:web_fetch` 2100, `tool:lsp` 2200, `tool:session-query` 2300, `tool:goal` 2400, `tool:cordis` 2500, `tool:workflow` 2600, `tool:ralph` 2700, continuable-subagent guidance 2800, `tool:report` 2900 | +| Generated protocol | `tools:sdk` 5000 | +| Final-output obligations | deliverable file references 9000, `tool:structured_output` 9900 | + +`SystemPrompt.assemble()` sorts equal-order sections by code-unit section name after comparing `order`. This makes third-party collisions deterministic without locale-sensitive comparison. First-party contributors still receive distinct ranks so their intended sequence remains explicit rather than depending on the fallback. + +Dynamic `PromptContext` order and tool-schema `toolOrder` are separate sequences and remain unchanged. A scoped `deployment:persona` continues to shadow the global section by name before section sorting, so it shares `PERSONA_ORDER` rather than consuming another placement. + +## Verification + +The system-prompt unit suite verifies that every exported first-party value is an integer, every value is unique, adjacent values differ by at least ten, and opposite registration permutations produce the same code-unit name order for a tie. Real-composition snapshots pin the model-visible ordering change, including Bash before filesystem guidance and the explicit Cordis, workflow, Ralph, subagent, and report sequence. + +## Alternatives considered + +**Keep package-local numeric literals and review collisions manually.** Rejected because a contributor cannot see the complete allocation locally, and the collision that motivated the earlier fix recurred after that fix merged. + +**Continue inserting fractional values.** Rejected because fractions provide no durable spacing rule, obscure the semantic groups, and still permit unrelated packages to choose the same value. + +**Normalize only snapshot comparisons.** Rejected because the runtime request header and model prompt would remain activation-order dependent while the test hid the difference. + +**Preserve activation order for equal ranks.** Rejected because activation order is not a prompt-order decision and varies across valid compositions. Name order is deterministic for external collisions; explicit named placements carry first-party intent. + +**Renumber dynamic contexts and tool schemas in the same allocation.** Rejected because they are independently assembled sequences. Combining them would imply cross-sequence ordering that the runtime does not perform. + +## Consequences + +Numeric ranks are not rendered, so the renumbering alone does not change model text. Bash or PowerShell moves before other per-tool guidance, and previously tied sections acquire deterministic order; those model-visible changes update request-header snapshots and may invalidate provider prefix reuse from the first moved paragraph. + +An external plugin that chose a raw number specifically to sit between old first-party values may move relative to repository sections. This repository is pre-release and provides no compatibility shim for the old allocation; extensions can select positions from the exported current allocation. Equal external ranks remain supported and deterministic by name. + +The system-prompt package now knows the names and relative placement of repository features. That centralized coupling is deliberate: the registry already owns the ordering semantics, while distributed numeric literals made the same relationship implicit and uncheckable. diff --git a/.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.zh.md b/.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.zh.md new file mode 100644 index 0000000000..624ed51f4d --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.zh.md @@ -0,0 +1,57 @@ +# Agent Note: 集中管理稀疏的 first-party 提示词段顺序 + +Status: implemented + +[English](2026-08-25-sparse-first-party-prompt-section-orders.md) | 中文 + +## 问题 + +仓库自带的系统提示词段分散在二十多个包中,各自声明互不关联的数字字面量。主要工具序列连续占用 100 到 117,后续插入还使用半步数值。因此,后续更改可能在无法看到完整分配表的情况下与已有段发生冲突。 + +相同 order 依赖 JavaScript 稳定排序,使插件激活顺序成为实际的平局规则。[Cordis/workflow 提示词顺序修复](../../archived/bug-fix/2026-08-24-system-prompt-section-order-ties.md)表明,完整且有效的组合可能按不同顺序激活同一组插件,进而产生不同的请求 header 和快照结果。局部修复一次冲突,无法阻止另一个包再次使用同一数值。 + +此外,shell 指导位于文件系统指导之后,但 shell 命令具有最广泛的执行和失败语义。模型应先读到 shell 结果义务,再阅读将文件操作分流到专用工具的更窄指令。 + +## 决策 + +`@deepseek-ai/dsh-system-prompt` 导出 `FIRST_PARTY_SECTION_ORDER`,作为仓库自带提示词段的唯一分配表。每个 first-party 贡献方都导入具名位置,不再声明数字字面量。所有值都是互不相同的整数,相邻已分配值之差至少为十。 + +除两项有意调整外,该分配保留既有 first-party 顺序:Bash,或 Windows 组合中的 PowerShell,位于逐工具指导的首位;原先共享 order 的段获得明确顺序。分组如下: + +| 分组 | 条目 | +|---|---| +| 产品开场 | `harness:identity` −1000、`harness:source` −900、`app:web-surface` −800、`deployment:persona` 0 | +| 工作模式 | `plan:policy` 500、`team:policy` 600 | +| 调用前置说明 | `tools:code-only` 800、`context:file-reference` 900 | +| 本地工具 | `tool:bash` 1000、`tool:pwsh` 1010、`tool:read` 1100、`tool:write` 1200、`tool:edit` 1300、`tool:glob` 1400、`tool:grep` 1500、`tool:jobs` 1600、`tool:pty` 1700 | +| 高层工具 | `tool:web_search` 2000、`tool:web_fetch` 2100、`tool:lsp` 2200、`tool:session-query` 2300、`tool:goal` 2400、`tool:cordis` 2500、`tool:workflow` 2600、`tool:ralph` 2700、可继续运行的 subagent 指导 2800、`tool:report` 2900 | +| 生成协议 | `tools:sdk` 5000 | +| 最终输出义务 | 可交付文件引用 9000、`tool:structured_output` 9900 | + +`SystemPrompt.assemble()` 比较 `order` 后,按提示词段名称的代码单元顺序排列同号项。这样无需使用受区域设置影响的比较,也能让第三方冲突产生确定结果。first-party 贡献方仍使用不同 rank,其预期顺序由分配表明确表达,而不依赖兜底规则。 + +动态 `PromptContext` 顺序和工具 schema 的 `toolOrder` 是独立序列,保持不变。带作用域的 `deployment:persona` 仍会在段排序之前按名称遮蔽全局段,因此共享 `PERSONA_ORDER`,而不占用另一个位置。 + +## 验证 + +系统提示词单元测试验证:导出的每个 first-party 值都是整数、所有值互不重复、相邻值之差至少为十,并且顺序相反的两种注册排列会对同号项产生相同的代码单元名称顺序。真实组合快照固定面向模型的顺序变化,包括 Bash 位于文件系统指导之前,以及 Cordis、workflow、Ralph、subagent 和 report 的明确序列。 + +## 考虑过的替代方案 + +**保留包内数字字面量并通过评审人工检查冲突。**未采用,因为贡献方无法在局部看到完整分配表,而且早期修复合入后,触发该修复的同类冲突再次出现。 + +**继续插入小数值。**未采用,因为小数没有持久的间距规则,难以表达语义分组,也无法阻止无关包选择同一数值。 + +**只规范化快照比较。**未采用,因为运行时请求 header 和模型提示词仍依赖激活顺序,测试只会隐藏差异。 + +**同 rank 时保留激活顺序。**未采用,因为激活顺序不是提示词顺序决策,并且会在有效组合之间变化。名称顺序为外部冲突提供确定结果;具名位置负责表达 first-party 意图。 + +**在同一分配表中重新编号动态上下文和工具 schema。**未采用,因为运行时独立组装这些序列。合并分配会暗示运行时并不执行的跨序列顺序。 + +## 后果 + +数字 rank 不会被渲染,因此单纯重新编号不会改变模型文本。Bash 或 PowerShell 会移到其他逐工具指导之前,原先同号的段会获得确定顺序;这些面向模型的变化会更新请求 header 快照,并可能从第一个移动的段落起使提供方前缀复用失效。 + +如果外部插件专门选择一个原始数字以插入旧 first-party 数值之间,它相对仓库段的位置可能改变。本仓库处于预发布阶段,不为旧分配提供兼容层;扩展可以根据当前导出的分配表选择位置。外部段仍可使用相同 rank,并会按名称获得确定顺序。 + +系统提示词包现在了解仓库功能的名称和相对位置。这种集中耦合是有意的:注册表本就拥有排序语义,而分散的数字字面量只是让同一关系变得隐式且无法检查。 diff --git a/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.i18n.yaml b/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.i18n.yaml index 967656184e..9db96229ec 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.i18n.yaml +++ b/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.md -2026-08-07-code-mode-executor-collapse.md: abfba369a2f6482f72d7224f6762f3ac75c8234e -2026-08-07-code-mode-executor-collapse.zh.md: 8bfdc8be6189d12b32fc260f2ab9637b8e2ba8c9 +2026-08-07-code-mode-executor-collapse.md: b19d0842d8b58b170a0f6839dcb32b9390211ca0 +2026-08-07-code-mode-executor-collapse.zh.md: 38bb319c8ce8fd01ab42ad29a6b3748c5d0f7925 diff --git a/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.md b/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.md index abfba369a2..b19d0842d8 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.md +++ b/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.md @@ -42,5 +42,5 @@ No provider guarantees interception of unadvertised names; the reported session - `both` and `native` behavior is unchanged; SDK sub-dispatches are unchanged (the `parent` token is the discriminator). - A collapsed call is rejected at `prepare`, BEFORE the extensible policy pipeline: pre-execute listeners, approval `ask`, and guards never observe it. `executionMode` also fails closed (`exclusive`), so scheduling has no observable difference. - Native-tool guidance sections (`tool:read`, `tool:write`, `tool:bash`, etc.) remain in the system prompt because they describe capabilities available through the generated SDK as well as native function calls, and several carry cross-tool routing policy (`read` over `bash cat`, `read` before `write` for the default fs-observation-policy, `subagent` over `workflow`) that no single tool description can hold. The executor collapse, not prompt filtering, prevents model-direct native calls. -- The prompt STATES the collapse, in the `tools:code-only` section ordered ahead of the 100-199 guidance band. Those sections name their tool without qualifying how it is reached, so a model that read only them emitted a native call, received `UNKNOWN_TOOL` for a tool the same prompt declared, and concluded the deployment was inconsistent rather than correcting itself. The denial carries the route for the same reason. `both` renders the rule empty: its native calls do execute, so stating it there would be false — which is why `both-mode-turn` no longer shares `code-mode-turn`'s expected prompt. +- The prompt STATES the collapse, in the `tools:code-only` section ordered ahead of first-party per-tool guidance. Those sections name their tool without qualifying how it is reached, so a model that read only them emitted a native call, received `UNKNOWN_TOOL` for a tool the same prompt declared, and concluded the deployment was inconsistent rather than correcting itself. The denial carries the route for the same reason. `both` renders the rule empty: its native calls do execute, so stating it there would be false — which is why `both-mode-turn` no longer shares `code-mode-turn`'s expected prompt. - Any future composite transport that sets a `parent` token opts its sub-dispatches into the full table, matching the nested-call semantics the token already documents. diff --git a/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.zh.md b/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.zh.md index 8bfdc8be61..38bb319c8c 100644 --- a/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.zh.md +++ b/.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.zh.md @@ -42,5 +42,5 @@ guard 是可选的插件扩展;安全不变量不能依赖部署恰好组装 - `both` 与 `native` 行为不变;SDK 子调用不变(判别信号是 `parent` token)。 - 被塌缩的调用在 `prepare` 阶段即被拒绝——在可扩展策略流水线之前:pre-execute 监听器、approval `ask` 与 guard 永远不会观察到它。`executionMode` 同样 fail-closed(`exclusive`),调度无可观察差异。 - 原生工具指引段(`tool:read`、`tool:write`、`tool:bash` 等)保留在系统提示词中,因为它们同时描述了通过生成 SDK 及原生函数调用可用的能力,其中若干段还承载着任何单个工具描述都装不下的跨工具路由策略(`read` 优先于 `bash cat`、默认 fs-observation-policy 要求先 `read` 再 `write`、一两个委派用 `subagent` 而非 `workflow`)。防止模型直呼原生工具的是执行器塌缩,而非提示词过滤。 -- 提示词会**声明**这条塌缩,位于排在 100–199 指导段之前的 `tools:code-only` 段。那些段只写出工具名而不限定其可达方式,因此只读到它们的模型会发出原生调用,为一个同一份提示词刚刚声明过的工具收到 `UNKNOWN_TOOL`,进而判定部署不一致,而不是自行纠正。拒绝信息给出正确路径也是同一原因。`both` 下该规则渲染为空:它的原生调用确实会执行,在那里声明就是假话——这也是 `both-mode-turn` 不再与 `code-mode-turn` 共用期望提示词的原因。 +- 提示词会**声明**这条塌缩,位于 first-party 逐工具指导之前的 `tools:code-only` 段。那些段只写出工具名而不限定其可达方式,因此只读到它们的模型会发出原生调用,为一个同一份提示词刚刚声明过的工具收到 `UNKNOWN_TOOL`,进而判定部署不一致,而不是自行纠正。拒绝信息给出正确路径也是同一原因。`both` 下该规则渲染为空:它的原生调用确实会执行,在那里声明就是假话——这也是 `both-mode-turn` 不再与 `code-mode-turn` 共用期望提示词的原因。 - 未来任何设置 `parent` token 的组合传输,其子调用自动走全表,与该 token 已有的嵌套调用语义一致。 diff --git a/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.i18n.yaml b/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.i18n.yaml index 5a895f08e9..a3fe8365a0 100644 --- a/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.md -2026-08-06-continuable-child-report-obligation.md: e771e81831147dd02a6c32a543c8d8944c2ec2f4 -2026-08-06-continuable-child-report-obligation.zh.md: e1cbb38644889459d8b8785382e1923a80c81957 +2026-08-06-continuable-child-report-obligation.md: 422d3ab74389e084becb75a145a82139dda38ac7 +2026-08-06-continuable-child-report-obligation.zh.md: ec7e19885204ddc99b2640407c605288f1e9c045 diff --git a/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.md b/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.md index e771e81831..422d3ab743 100644 --- a/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.md +++ b/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.md @@ -15,7 +15,7 @@ Each of those choices is defensible alone. Together they made the return channel The return channel is an instruction the child receives, not a capability it may discover. The report package installs two scope-local registrations into every continuable in-process child, and one disposer revokes both: - the `report` tool, whose description now states that the child calls it once before finishing with a self-contained final result, and earlier for progress that changes what the parent should do next; -- a `tool:report` system-prompt section at order 117 carrying the same obligation in the child's own voice, so a child that never reads tool descriptions closely still receives it. +- a `tool:report` system-prompt section at first-party order 2900 carrying the same obligation in the child's own voice, so a child that never reads tool descriptions closely still receives it. `reportDelivery` defaults to `next-step`. An accepted report wakes a parked parent driver or joins a running parent's nearest step boundary, matching the instruction to report findings that change the parent's next action. `quiet` remains available for deployments that prefer unread reports over model-work amplification. The [report/settlement ordering decision](../bug-fix/2026-08-17-subagent-report-settlement-ordering.md) owns the scheduling rationale. diff --git a/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.zh.md b/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.zh.md index e1cbb38644..ec7e198852 100644 --- a/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.zh.md +++ b/.agents/notes/implemented/feature/2026-08-06-continuable-child-report-obligation.zh.md @@ -15,7 +15,7 @@ Status: implemented 返回通道是 child 收到的一条指令,而不是它需要自行发现的能力。report 包会向每个可继续进程内 child 安装两项作用域局部注册,并由同一个 disposer 撤销两者: - `report` 工具,其描述现在说明 child 要在结束前调用一次并给出自足的最终结果,并在部分进展会改变 parent 下一步动作时提前调用; -- 一个 order 为 117 的 `tool:report` 系统提示词 section,用 child 自己的语气承载同一条义务,使从不细读工具描述的 child 仍能收到它。 +- 一个 first-party order 为 2900 的 `tool:report` 系统提示词 section,用 child 自己的语气承载同一条义务,使从不细读工具描述的 child 仍能收到它。 `reportDelivery` 的默认值为 `next-step`。一条被接受的报告会唤醒停驻的 parent driver,或加入运行中 parent 最近的 step 边界,与发现会改变 parent 下一步动作时上报的指令一致。对于宁可让报告无人阅读也要避免模型工作量放大的部署,`quiet` 依旧可用。[报告与结算顺序决策](../bug-fix/2026-08-17-subagent-report-settlement-ordering.zh.md)负责调度理由。 diff --git a/docs/config-catalog.i18n.yaml b/docs/config-catalog.i18n.yaml index cec89ac365..d1463fc88f 100644 --- a/docs/config-catalog.i18n.yaml +++ b/docs/config-catalog.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/config-catalog.md -config-catalog.md: 19f292ee40861ffa6f8e7fdef4d8717189a2149c -config-catalog.zh.md: 96f9721cae20a1a0ed172349c0189bc47576c75d +config-catalog.md: 6a486f8cbdac72eb1d2aedf0f728b742dd81612d +config-catalog.zh.md: e6b55350d931474e275e18b60db632021e41f63b diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 19f292ee40..6a486f8cbd 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -612,7 +612,7 @@ export interface Config { } ``` -Source: [`packages/experimental/tool-agent-team/src/index.ts:17`](../packages/experimental/tool-agent-team/src/index.ts) +Source: [`packages/experimental/tool-agent-team/src/index.ts:18`](../packages/experimental/tool-agent-team/src/index.ts) @@ -2468,7 +2468,7 @@ export interface Config { } ``` -Source: [`packages/core/system-prompt/src/index.ts:186`](../packages/core/system-prompt/src/index.ts) +Source: [`packages/core/system-prompt/src/index.ts:237`](../packages/core/system-prompt/src/index.ts) @@ -2788,7 +2788,7 @@ export interface Config { } ``` -Source: [`packages/workflow/tool-ralph/src/index.ts:23`](../packages/workflow/tool-ralph/src/index.ts) +Source: [`packages/workflow/tool-ralph/src/index.ts:22`](../packages/workflow/tool-ralph/src/index.ts) @@ -2953,7 +2953,7 @@ export interface Config { } ``` -Source: [`packages/terminal/tool-terminal/src/index.ts:35`](../packages/terminal/tool-terminal/src/index.ts) +Source: [`packages/terminal/tool-terminal/src/index.ts:36`](../packages/terminal/tool-terminal/src/index.ts) @@ -3021,7 +3021,7 @@ export interface Config { } ``` -Source: [`packages/workflow/tool-workflow/src/index.ts:33`](../packages/workflow/tool-workflow/src/index.ts) +Source: [`packages/workflow/tool-workflow/src/index.ts:32`](../packages/workflow/tool-workflow/src/index.ts) diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index 96f9721cae..e6b55350d9 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -614,7 +614,7 @@ export interface Config { } ``` -来源:[`packages/experimental/tool-agent-team/src/index.ts:17`](../packages/experimental/tool-agent-team/src/index.ts) +来源:[`packages/experimental/tool-agent-team/src/index.ts:18`](../packages/experimental/tool-agent-team/src/index.ts) @@ -2470,7 +2470,7 @@ export interface Config { } ``` -来源:[`packages/core/system-prompt/src/index.ts:186`](../packages/core/system-prompt/src/index.ts) +来源:[`packages/core/system-prompt/src/index.ts:237`](../packages/core/system-prompt/src/index.ts) @@ -2790,7 +2790,7 @@ export interface Config { } ``` -来源:[`packages/workflow/tool-ralph/src/index.ts:23`](../packages/workflow/tool-ralph/src/index.ts) +来源:[`packages/workflow/tool-ralph/src/index.ts:22`](../packages/workflow/tool-ralph/src/index.ts) @@ -2955,7 +2955,7 @@ export interface Config { } ``` -来源:[`packages/terminal/tool-terminal/src/index.ts:35`](../packages/terminal/tool-terminal/src/index.ts) +来源:[`packages/terminal/tool-terminal/src/index.ts:36`](../packages/terminal/tool-terminal/src/index.ts) @@ -3023,7 +3023,7 @@ export interface Config { } ``` -来源:[`packages/workflow/tool-workflow/src/index.ts:33`](../packages/workflow/tool-workflow/src/index.ts) +来源:[`packages/workflow/tool-workflow/src/index.ts:32`](../packages/workflow/tool-workflow/src/index.ts) diff --git a/docs/subsystems/system-prompt.i18n.yaml b/docs/subsystems/system-prompt.i18n.yaml index 1d1e9e3528..bf08fc3825 100644 --- a/docs/subsystems/system-prompt.i18n.yaml +++ b/docs/subsystems/system-prompt.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/system-prompt.md -system-prompt.md: f4cdf40703ac4f4a6f87d525efb6010e45f4480b -system-prompt.zh.md: d3eddc3c5e0df72546f80d953e7123fa0f795816 +system-prompt.md: 68eab4f5c93f14675d548c0fa635c5f3f3ce70a7 +system-prompt.zh.md: 086555593e507da03aea3e83a55017e0aa839fcb diff --git a/docs/subsystems/system-prompt.md b/docs/subsystems/system-prompt.md index f4cdf40703..68eab4f5c9 100644 --- a/docs/subsystems/system-prompt.md +++ b/docs/subsystems/system-prompt.md @@ -39,7 +39,7 @@ interface ToolProviderResult { ## Prompt sections -`PromptSection` is a readonly same-process registration contract. Its text may be static or resolved from the current assembly context. One effective `complete` section becomes the sole prompt section after cooperative assembly. +`PromptSection` is a readonly same-process registration contract. Its text may be static or resolved from the current assembly context. Sections sort by ascending order and then code-unit name; `FIRST_PARTY_SECTION_ORDER` publishes the sparse named allocation for repository-owned contributions. One effective `complete` section becomes the sole prompt section after cooperative assembly. ```ts type-equiv /** One contributed section of the system prompt (registry input). */ @@ -47,9 +47,9 @@ interface PromptSection { /** Unique name — a duplicate registration throws (see {@link SystemPrompt.section}). */ readonly name: string /** - * Sections are concatenated in ascending order. Convention: `-100` is the - * harness identity, `0` the deployment persona, tool guidance uses 100–199; - * other negative orders also render before the persona. + * Sections are concatenated in ascending order. Equal orders use code-unit + * name order. Repository-owned placements use + * {@link FIRST_PARTY_SECTION_ORDER}. */ readonly order: number /** diff --git a/docs/subsystems/system-prompt.zh.md b/docs/subsystems/system-prompt.zh.md index d3eddc3c5e..086555593e 100644 --- a/docs/subsystems/system-prompt.zh.md +++ b/docs/subsystems/system-prompt.zh.md @@ -39,7 +39,7 @@ interface ToolProviderResult { ## 提示词段落 -`PromptSection` 是一份只读的同进程注册约定。其文本可以是静态的,也可以从当前组装上下文动态解析。协作式组装完成后,一个有效的 `complete` 段会成为唯一的提示词段落。 +`PromptSection` 是一份只读的同进程注册约定。其文本可以是静态的,也可以从当前组装上下文动态解析。各段先按 order 升序排列,再按名称的代码单元顺序排列;`FIRST_PARTY_SECTION_ORDER` 公开仓库自带贡献的稀疏具名分配表。协作式组装完成后,一个有效的 `complete` 段会成为唯一的提示词段落。 ```ts type-equiv /** One contributed section of the system prompt (registry input). */ @@ -47,9 +47,9 @@ interface PromptSection { /** Unique name — a duplicate registration throws (see {@link SystemPrompt.section}). */ readonly name: string /** - * Sections are concatenated in ascending order. Convention: `-100` is the - * harness identity, `0` the deployment persona, tool guidance uses 100–199; - * other negative orders also render before the persona. + * Sections are concatenated in ascending order. Equal orders use code-unit + * name order. Repository-owned placements use + * {@link FIRST_PARTY_SECTION_ORDER}. */ readonly order: number /** diff --git a/packages/boot/app-boot/src/index.ts b/packages/boot/app-boot/src/index.ts index 9a1d05ffbf..e834ae3934 100644 --- a/packages/boot/app-boot/src/index.ts +++ b/packages/boot/app-boot/src/index.ts @@ -18,8 +18,7 @@ import Group from '@deepseek-ai/cordis-plugin-group' import { dshHomePath, resolveDshHome } from '@deepseek-ai/dsh-home-paths' import { createLaunchEnvironmentSnapshot, type LaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment' import type {} from '@deepseek-ai/cordis-plugin-hmr' -// Side-effect type import: resolves `ctx.get('systemPrompt')` to the service. -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' declare module '@deepseek-ai/cordis' { interface Context { @@ -826,8 +825,8 @@ export const HARNESS_SOURCE_SECTION = 'harness:source' * explicitly distinguishing it from the task workspace and current working * directory. The self-referential `dsh-tool-cordis` toolset reads and edits this * checkout. Call once on the settled boot context ({@link boot}); the section - * orders just after the harness identity opener (`-100`) and before the deployment - * persona (`0`). A booted tree with no `systemPrompt` service has no prompt to + * uses the shared first-party placement just after the harness identity opener + * and before the deployment persona. A booted tree with no `systemPrompt` service has no prompt to * augment, so this is then a no-op that returns `undefined`. The section is * registered against the `systemPrompt` service's fiber, so a dev HMR reload of * that plugin drops it until the next boot. @@ -840,7 +839,7 @@ export function addHarnessSourceSection(ctx: Context, sourceRoot: string): (() = if (systemPrompt === undefined) return undefined return systemPrompt.section({ name: HARNESS_SOURCE_SECTION, - order: -99, + order: FIRST_PARTY_SECTION_ORDER.HARNESS_SOURCE, text: `The DeepSeek Harness implementation checkout is at ${sourceRoot}. The checkout location and current working directory are separate values and may differ; never infer the working directory from this path. Use pwd to determine the current working directory. Use this checkout only to inspect or extend DSH itself.`, }) } diff --git a/packages/boot/app-boot/tests/app-boot.spec.ts b/packages/boot/app-boot/tests/app-boot.spec.ts index 8ae1a21cd2..e5fc24aaf3 100644 --- a/packages/boot/app-boot/tests/app-boot.spec.ts +++ b/packages/boot/app-boot/tests/app-boot.spec.ts @@ -817,8 +817,8 @@ describe('addHarnessSourceSection', () => { const systemPrompt = ctx.get('systemPrompt')! const rendered = renderPrompt(await systemPrompt.assemble()) expect(rendered).toContain(EXPECTED) - // Harness-owned opener (-100) → source (-99) → persona (0). The >= 0 guards - // keep a drifted opener/persona string from a false pass through `-1 < n`. + // The >= 0 guards keep a drifted opener/persona string from a false pass + // through `-1 < n`. const identityAt = rendered.indexOf('You are an AI agent powered by DeepSeek Harness.') const sourceAt = rendered.indexOf(EXPECTED) const personaAt = rendered.indexOf('You are a coding agent.') diff --git a/packages/bundle/web-app/src/index.ts b/packages/bundle/web-app/src/index.ts index 79d1e94862..713f6d10a4 100644 --- a/packages/bundle/web-app/src/index.ts +++ b/packages/bundle/web-app/src/index.ts @@ -24,7 +24,7 @@ import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment' import { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess' import type {} from '@deepseek-ai/cordis-plugin-loader' import type {} from '@deepseek-ai/dsh-host-webserver' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import type {} from '@deepseek-ai/dsh-shell-env' /** Stable Cordis plugin name. */ @@ -243,7 +243,7 @@ export function apply(ctx: Context, config: Config): void { addHarnessSourceSection(promptCtx, SOURCE_ROOT) promptCtx.systemPrompt.section({ name: 'app:web-surface', - order: -98, + order: FIRST_PARTY_SECTION_ORDER.WEB_SURFACE, text: () => webSurfacePrompt(localWebUrl(promptCtx)), }) }) diff --git a/packages/client/ui-deliverables/README.i18n.yaml b/packages/client/ui-deliverables/README.i18n.yaml index 77799f1afb..a9c6ad050a 100644 --- a/packages/client/ui-deliverables/README.i18n.yaml +++ b/packages/client/ui-deliverables/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-deliverables/README.md -README.md: ace08fae3a001080918973c23aa362080cd69066 -README.zh.md: 9df1664bc42c39012c2eebf397cb368b3b5c6260 +README.md: 58d68746c9d34f05c103430f330a042626213509 +README.zh.md: a62421eef8d524e0f5b65aa6d11d6db0c42c02da diff --git a/packages/client/ui-deliverables/README.md b/packages/client/ui-deliverables/README.md index ace08fae3a..58d68746c9 100644 --- a/packages/client/ui-deliverables/README.md +++ b/packages/client/ui-deliverables/README.md @@ -26,7 +26,7 @@ One fixed prompt paragraph whenever this package is loaded; no tool schema, tool #### KV Cache effect -The section is static at order 190 for the lifetime of the package mount, so it remains in the reusable prompt prefix and does not change across Turns. +The section is static at first-party order 9000 for the lifetime of the package mount, so it remains in the reusable prompt prefix and does not change across Turns. ## Known Limitations and Deferred Work diff --git a/packages/client/ui-deliverables/README.zh.md b/packages/client/ui-deliverables/README.zh.md index 9df1664bc4..a62421eef8 100644 --- a/packages/client/ui-deliverables/README.zh.md +++ b/packages/client/ui-deliverables/README.zh.md @@ -26,7 +26,7 @@ Node 侧注册静态系统提示词段落 `ui:deliverable-file-references`。它 #### KV Cache 影响 -该段落在本包加载期间始终以顺序 190 保持静态,因此留在可复用的提示词前缀中,不会随 Turn 改变。 +该段落在本包加载期间始终以 first-party 顺序 9000 保持静态,因此留在可复用的提示词前缀中,不会随 Turn 改变。 ## 已知限制与暂缓事项 diff --git a/packages/client/ui-deliverables/src/index.ts b/packages/client/ui-deliverables/src/index.ts index 6e6c387803..4f86a5f6a1 100644 --- a/packages/client/ui-deliverables/src/index.ts +++ b/packages/client/ui-deliverables/src/index.ts @@ -6,7 +6,7 @@ */ import type { Context } from '@deepseek-ai/cordis' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' /** Services required for the model guidance paired with the browser renderer. */ export const inject = ['systemPrompt'] @@ -22,7 +22,7 @@ const FILE_REFERENCE_PROMPT = 'When you successfully create or modify files, men export function apply(ctx: Context): void { ctx.systemPrompt.section({ name: 'ui:deliverable-file-references', - order: 190, + order: FIRST_PARTY_SECTION_ORDER.DELIVERABLE_FILE_REFERENCES, text: FILE_REFERENCE_PROMPT, }) } diff --git a/packages/context/file-reference-local/src/index.ts b/packages/context/file-reference-local/src/index.ts index 95d1cfec2d..4006e01e41 100644 --- a/packages/context/file-reference-local/src/index.ts +++ b/packages/context/file-reference-local/src/index.ts @@ -11,7 +11,7 @@ import FileReferenceService, { FILE_REFERENCE_PROMPT, type FileReferenceCandidate, } from '@deepseek-ai/dsh-file-reference' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import type {} from '@deepseek-ai/dsh-tools' import { DEFAULT_FILE_SEARCH_EXCLUDED_DIRECTORIES, @@ -69,7 +69,7 @@ export class LocalFileReferenceService extends FileReferenceService { const fiber = agent.ctx.inject(['systemPrompt', 'tools'], (scope) => { scope.systemPrompt.section({ name: 'context:file-reference', - order: 99, + order: FIRST_PARTY_SECTION_ORDER.FILE_REFERENCE, text: () => agent.ctx.tools.get('read', agent) === undefined ? '' : FILE_REFERENCE_PROMPT, }) }) diff --git a/packages/core/system-prompt/README.i18n.yaml b/packages/core/system-prompt/README.i18n.yaml index c34ff9547a..863c5ad548 100644 --- a/packages/core/system-prompt/README.i18n.yaml +++ b/packages/core/system-prompt/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/core/system-prompt/README.md -README.md: a52aa3e4c2782993fed5a525cc827aba4e3eaeb0 -README.zh.md: cf0ba43aa2f2f47ea61ef13c74fbf182fbd9f2ee +README.md: a76191c9bc68c73bb4edff07034837e90fc9f35a +README.zh.md: 34bf770a2f0a4458c0749bc25eea96d2857eda47 diff --git a/packages/core/system-prompt/README.md b/packages/core/system-prompt/README.md index a52aa3e4c2..a76191c9bc 100644 --- a/packages/core/system-prompt/README.md +++ b/packages/core/system-prompt/README.md @@ -8,7 +8,7 @@ System prompt assembly registry. Plugins contribute ordered sections, tool schem | Key | Default | Meaning | |---|---|---| -| `includeHarnessIdentity` | `true` | Include the fixed `You are an AI agent powered by DeepSeek Harness.` order-−100 opener. Set false only when a compatibility deployment owns the complete system prompt. | +| `includeHarnessIdentity` | `true` | Include the fixed `You are an AI agent powered by DeepSeek Harness.` first-party opener at order −1000. Set false only when a compatibility deployment owns the complete system prompt. | | `includeRuntimeContext` | `true` | Include ordered dynamic contexts in assembly. When false, context providers are not evaluated and contexts added by `system-prompt/assemble` listeners are discarded after the waterfall; other services and their enforcement remain active. | | `persona` | `''` | The global deployment-persona default: the ONE config-authored prompt fragment, rendered as the order-0 `deployment:persona` section unless an agent-scoped contribution shadows it. A template — complete `{{…}}` groups are interpreted strictly against the registered variables (the shipped loop registers `{{model}}`/`{{cwd}}`), with no escape syntax for literal braces yet. Empty ⇒ the section is dropped at render. | | `personaComplete` | `false` | Treat `persona` as the complete system prompt after assembly. Other sections remain registered but are omitted from model requests; tool schemas and variables remain available. | @@ -18,7 +18,7 @@ System prompt assembly registry. Plugins contribute ordered sections, tool schem ### Public API -- `ctx.systemPrompt.section(section: PromptSection): () => void` Contribute a section. The layer is the calling context's scope: `agent.ctx` contributes to that agent alone, shadowing a same-named global section there. A `complete: true` section becomes the exact complete prompt after the assembly waterfall; more than one effective complete section rejects assembly. Duplicate names within one layer and non-finite orders throw. Disposed with the calling fiber. +- `ctx.systemPrompt.section(section: PromptSection): () => void` Contribute a section. The layer is the calling context's scope: `agent.ctx` contributes to that agent alone, shadowing a same-named global section there. Sections sort by ascending order, then code-unit name for equal orders. A `complete: true` section becomes the exact complete prompt after the assembly waterfall; more than one effective complete section rejects assembly. Duplicate names within one layer and non-finite orders throw. Disposed with the calling fiber. - `ctx.systemPrompt.context(context: PromptContext): () => void` Contribute ordered dynamic context for the calling scope. Providers are evaluated for each eligible assembly and become a sourced runtime-context snapshot in model history under the shipped loop. - `ctx.systemPrompt.suppressRuntimeContext(): () => void` Suppress every dynamic-context contribution for the calling scope. Multiple registrations compose independently; disposing the returned effect restores context when no suppressor remains. - `ctx.systemPrompt.tools(provider: (context: AssembleContext) => ToolProviderResult): () => void` Contribute tool schemas, evaluated at each assembly with that assembly's context. `ToolProviderResult` = `{ schemas, knownNames? }`: `schemas` is the post-restriction visible set; `knownNames` is the pre-restriction universe used by `toolOrder`. A provider must not return a schema named `TOOL_ORDER_REST`. Scoped providers are consulted only for their scope's assemblies. Disposed with the calling fiber. @@ -32,7 +32,8 @@ System prompt assembly registry. Plugins contribute ordered sections, tool schem ### Key types - `AssembleContext` — what one `assemble()` call is FOR. Merge-extensible; declares `scope?: ScopeKey` (the layer selector) and `signal?: AbortSignal` (the explicit request control capability) here, while `dsh-agent` declares `agent?: Agent` (the typed DX field — never set without `scope`; use `assembleContextFor(agent, signal)`). Providers must tolerate absent fields because a bare `assemble()` carries an empty, scope-less, signal-less context. `signal` is a request value, not part of the ambient Agent execution frame. -- `PromptSection` — `{ name, order, text, complete? }`. Sections are concatenated in ascending `order`. Order bands: `-100` is the harness identity, `0` the deployment persona, tool guidance uses `100–199`. One effective `complete` section suppresses all other sections after cooperative assembly. +- `FIRST_PARTY_SECTION_ORDER` — the sparse named allocation for repository-owned sections. Values are unique integers whose adjacent allocated positions differ by at least ten; external sections may use any finite order. +- `PromptSection` — `{ name, order, text, complete? }`. Sections are concatenated in ascending `order`, with equal values ordered by code-unit `name`. One effective `complete` section suppresses all other sections after cooperative assembly. - `PromptAssembly` — `{ sections: AssembledSection[], tools: ToolSchema[], variables: Record }`. Section texts arrive resolved but not yet interpolated; `variables` holds every registered variable resolved against the context. Tool schemas are part of the assembly by design: "what the model is told it can do" is one coherent thing, even though adapters transmit schemas as a separate wire field. - `renderPrompt(assembly)` — interpolates `{{variable}}` references in each section, drops empty sections, joins with blank lines. STRICT: an unknown reference (`Object.hasOwn` lookup — prototype names like `{{constructor}}` are unknown), a registered-but-valueless reference, a malformed complete `{{…}}` group, or a `{{` that opens no complete group while a `}}` still follows (`{{{model}}}`) throws — fail loud beats shipping a malformed prompt. A lone `{{` with no `}}` anywhere after it passes through verbatim; substituted values are never re-scanned. @@ -45,7 +46,7 @@ Merge-extensible: plugins can declare extra fields on `PromptAssembly` and `Asse - Tool schema providers: `ToolRuntime` registers itself as a tool provider automatically. - The [`system-prompt/assemble` waterfall](#live-events): cooperatively mutate or replace the assembly per caller before any complete-section constraint is enforced. -Design rationale: [the prompt-variables Agent Note](../../../.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md). +Design rationale: [the prompt-variables Agent Note](../../../.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md) and [the first-party order allocation](../../../.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.md). ## Model Experience @@ -88,4 +89,3 @@ Prefix-stable while the visible schema set, rendering, and order are unchanged. - **Deployment-authored prompt text is config/composition only** — this plugin owns the global persona default, creator plugins may register agent-scoped shadows, and other sections come from the plugin that owns the fact; there is no end-user prompt-editing API. - **No escape syntax for literal `{{…}}` braces** — every complete group is interpolated against registered variables; an escape is deferred until a real prompt needs one. - **`toolOrder` misconfiguration surfaces at prompt assembly (the first turn), not at boot** — only shape violations throw at config load. -- **Sections sharing an `order` value tie-break by registration order** — a plugin-load artifact; determinism relies on the distinct-order band convention, unlike the canonicalized tool order. diff --git a/packages/core/system-prompt/README.zh.md b/packages/core/system-prompt/README.zh.md index cf0ba43aa2..34bf770a2f 100644 --- a/packages/core/system-prompt/README.zh.md +++ b/packages/core/system-prompt/README.zh.md @@ -8,7 +8,7 @@ | 键 | 默认值 | 含义 | |---|---|---| -| `includeHarnessIdentity` | `true` | 是否包含顺序为 −100 的固定开场白 `You are an AI agent powered by DeepSeek Harness.`。仅当兼容性部署拥有完整系统提示词时设为 false。 | +| `includeHarnessIdentity` | `true` | 是否包含顺序为 −1000 的 first-party 固定开场白 `You are an AI agent powered by DeepSeek Harness.`。仅当兼容性部署拥有完整系统提示词时设为 false。 | | `includeRuntimeContext` | `true` | 是否在组装中包含有序动态上下文。设为 false 时不会求值上下文提供方,并会在 waterfall 后丢弃 `system-prompt/assemble` 监听器添加的上下文;其他服务及其强制机制仍然生效。 | | `persona` | `''` | 全局部署 persona 默认值:唯一由配置提供的提示词片段,渲染为顺序为 0 的 `deployment:persona` 段,除非 agent 作用域的贡献将其遮蔽。它是模板,完整的 `{{…}}` 组会严格按已注册变量解释(随附循环注册 `{{model}}`/`{{cwd}}`),目前没有表达字面量花括号的转义语法。为空 ⇒ 渲染时删除该段。 | | `personaComplete` | `false` | 在组装后将 `persona` 作为完整系统提示词。其他段仍保持注册,但不会进入模型请求;工具 schema 与变量仍然可用。 | @@ -18,7 +18,7 @@ ### 公开 API -- `ctx.systemPrompt.section(section: PromptSection): () => void`:贡献一个段。层由调用上下文的作用域决定:`agent.ctx` 只为该 agent 贡献,并在该处遮蔽同名全局段。一个 `complete: true` 段会在组装 waterfall 之后成为精确的完整提示词;有效 complete 段超过一个时,组装会被拒绝。同一层中的重复名称和非有限顺序会抛出。随调用 fiber 一并 dispose(资源释放)。 +- `ctx.systemPrompt.section(section: PromptSection): () => void`:贡献一个段。层由调用上下文的作用域决定:`agent.ctx` 只为该 agent 贡献,并在该处遮蔽同名全局段。各段先按 order 升序排列,同号时再按名称的代码单元顺序排列。一个 `complete: true` 段会在组装 waterfall 之后成为精确的完整提示词;有效 complete 段超过一个时,组装会被拒绝。同一层中的重复名称和非有限顺序会抛出。随调用 fiber 一并 dispose(资源释放)。 - `ctx.systemPrompt.context(context: PromptContext): () => void`:为调用作用域贡献有序动态上下文。每次符合条件的组装都会求值提供方,并在随附循环下成为模型历史中带来源的 runtime-context 快照。 - `ctx.systemPrompt.suppressRuntimeContext(): () => void`:抑制调用作用域的所有动态上下文贡献。多个注册会独立组合;只有当不再存在抑制器时,dispose 返回的 effect 才会恢复上下文。 - `ctx.systemPrompt.tools(provider: (context: AssembleContext) => ToolProviderResult): () => void`:贡献工具 schema;每次组装时使用该次组装的上下文求值。`ToolProviderResult` = `{ schemas, knownNames? }`:`schemas` 是限制后的可见集合;`knownNames` 是限制前由 `toolOrder` 使用的全集。提供方不得返回名为 `TOOL_ORDER_REST` 的 schema。带作用域提供方只在其作用域的组装中查询。随调用 fiber 一并 dispose。 @@ -34,7 +34,8 @@ ### 关键类型 - `AssembleContext`:说明一次 `assemble()` 调用的用途。它可通过合并扩展;此处声明 `scope?: ScopeKey`(层选择器)与 `signal?: AbortSignal`(显式请求控制能力),而 `dsh-agent` 声明 `agent?: Agent`(类型化 DX 字段;绝不能在没有 `scope` 时设置,应使用 `assembleContextFor(agent, signal)`)。提供方必须容忍字段缺席,因为裸 `assemble()` 携带的是无作用域、无信号的空上下文。`signal` 是请求值,不是环境 Agent 执行 frame 的一部分。 -- `PromptSection`:`{ name, order, text, complete? }`。各段按 `order` 升序拼接。顺序区间:`-100` 是 harness 身份,`0` 是部署 persona,工具引导使用 `100–199`。协作式组装完成后,一个有效的 `complete` 段会抑制其他所有段。 +- `FIRST_PARTY_SECTION_ORDER`:仓库自带提示词段的稀疏具名分配表。所有值都是互不相同的整数,相邻已分配位置之差至少为十;外部段可以使用任意有限 order。 +- `PromptSection`:`{ name, order, text, complete? }`。各段按 `order` 升序拼接,同号时按代码单元 `name` 排列。协作式组装完成后,一个有效的 `complete` 段会抑制其他所有段。 - `PromptAssembly`:`{ sections: AssembledSection[], tools: ToolSchema[], variables: Record }`。各段文本到达时已求值,但尚未插值;`variables` 保存所有已注册变量在当前上下文中求得的值。工具 schema 按设计属于组装结果:「模型获知自己能做什么」是一个连贯整体,尽管适配器把 schema 作为独立 wire 字段传输。 - `renderPrompt(assembly)`:插值每个段中的 `{{variable}}` 引用,删除空段,并用空行连接。严格规则:未知引用(使用 `Object.hasOwn` 查找,因此 `{{constructor}}` 等原型名称未知)、已注册但无值的引用、格式错误的完整 `{{…}}` 组,或出现 `{{` 却没有形成完整组、而后文仍有 `}}`(`{{{model}}}`),都会抛出异常;明确失败胜过交付格式错误的提示词。孤立的 `{{` 如果后面任何位置都没有 `}}`,会按字面量通过;替换值绝不再次扫描。 @@ -47,7 +48,7 @@ - 工具 schema 提供方:`ToolRuntime` 自动将自身注册为工具提供方。 - [`system-prompt/assemble` waterfall](#live-events):按调用方协作式修改或替换组装结果,之后再实施 complete 段约束。 -设计原理:[提示词变量 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md)。 +设计原理:[提示词变量 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md)与 [first-party 顺序分配](../../../.agents/notes/implemented/architecture/2026-08-25-sparse-first-party-prompt-section-orders.zh.md)。 ## 模型体验 @@ -90,4 +91,3 @@ schema token 在每次请求中重复。限制工具会为该 agent 移除其全 - **部署方编写的提示词文本只来自配置/组合**:此插件拥有全局 persona 默认值;创建方插件可以注册 agent 作用域的遮蔽项;其他段来自拥有相应事实的插件。不存在终端用户提示词编辑 API。 - **没有表示字面量 `{{…}}` 花括号的转义语法**:每个完整组都会按已注册变量插值;只有实际提示词需要转义时才会实现。 - **`toolOrder` 配置错误在提示词组装(首轮)时出现,而不是启动时**:只有形状违规会在配置加载时抛出。 -- **共享同一 `order` 值的段按注册顺序打破平局**:这是插件加载产物;确定性依赖在顺序分段内使用不同值的约定,与已规范化的工具顺序不同。 diff --git a/packages/core/system-prompt/src/index.ts b/packages/core/system-prompt/src/index.ts index ec36b32432..4ac0010df9 100644 --- a/packages/core/system-prompt/src/index.ts +++ b/packages/core/system-prompt/src/index.ts @@ -54,9 +54,9 @@ export interface PromptSection { /** Unique name — a duplicate registration throws (see {@link SystemPrompt.section}). */ readonly name: string /** - * Sections are concatenated in ascending order. Convention: `-100` is the - * harness identity, `0` the deployment persona, tool guidance uses 100–199; - * other negative orders also render before the persona. + * Sections are concatenated in ascending order. Equal orders use code-unit + * name order. Repository-owned placements use + * {@link FIRST_PARTY_SECTION_ORDER}. */ readonly order: number /** @@ -119,6 +119,47 @@ export interface PromptAssembly { variables: Record } +/** + * Sparse integer placements for repository-owned prompt sections. + * + * Adjacent values differ by at least ten so a new first-party section can be + * inserted without renumbering the surrounding sequence. + * External plugins may use any finite order; equal orders are deterministic by + * section name. + */ +export const FIRST_PARTY_SECTION_ORDER = { + HARNESS_IDENTITY: -1000, + HARNESS_SOURCE: -900, + WEB_SURFACE: -800, + DEPLOYMENT_PERSONA: 0, + PLAN_POLICY: 500, + TEAM_POLICY: 600, + CODE_ONLY: 800, + FILE_REFERENCE: 900, + TOOL_BASH: 1000, + TOOL_PWSH: 1010, + TOOL_READ: 1100, + TOOL_WRITE: 1200, + TOOL_EDIT: 1300, + TOOL_GLOB: 1400, + TOOL_GREP: 1500, + TOOL_JOBS: 1600, + TOOL_PTY: 1700, + TOOL_WEB_SEARCH: 2000, + TOOL_WEB_FETCH: 2100, + TOOL_LSP: 2200, + TOOL_SESSION_QUERY: 2300, + TOOL_GOAL: 2400, + TOOL_CORDIS: 2500, + TOOL_WORKFLOW: 2600, + TOOL_RALPH: 2700, + TOOL_SUBAGENT: 2800, + TOOL_REPORT: 2900, + TOOLS_SDK: 5000, + DELIVERABLE_FILE_REFERENCES: 9000, + STRUCTURED_OUTPUT: 9900, +} as const + /** * The deployment persona's section name and order. Exported because a * composition can replace this slot — an agent preset shadows the @@ -127,8 +168,8 @@ export interface PromptAssembly { */ export const PERSONA_SECTION = 'deployment:persona' -/** Prompt order of the persona slot; the first section a model reads. */ -export const PERSONA_ORDER = 0 +/** Prompt order of the persona slot. */ +export const PERSONA_ORDER = FIRST_PARTY_SECTION_ORDER.DEPLOYMENT_PERSONA /** Valid variable names: how they are written between the braces. */ const VARIABLE_NAME = /^[a-z][a-z0-9_]*$/ @@ -177,9 +218,19 @@ function orderTools(tools: ToolSchema[], toolOrder: string[] | undefined, knownN name === TOOL_ORDER_REST ? rest : tools.filter(tool => tool.name === name)) } -/** Lexicographic (code-unit) name comparison — locale-independent, so the order is identical on every machine. */ +/** Code-unit name comparison — locale-independent, so the order is identical on every machine. */ +function compareNames(a: string, b: string): number { + return a < b ? -1 : a > b ? 1 : 0 +} + +/** Order prompt sections by their explicit placement, then deterministically by name. */ +function comparePromptSections(a: PromptSection, b: PromptSection): number { + return a.order - b.order || compareNames(a.name, b.name) +} + +/** Order tool schemas lexicographically by name. */ function compareToolNames(a: ToolSchema, b: ToolSchema): number { - return a.name < b.name ? -1 : a.name > b.name ? 1 : 0 + return compareNames(a.name, b.name) } /** Plugin config: the deployment-authored fragment of the system prompt (see {@link Config.persona} for its contract). */ @@ -360,7 +411,7 @@ export class SystemPrompt extends Service { if (config.includeHarnessIdentity ?? true) { this.section({ name: 'harness:identity', - order: -100, + order: FIRST_PARTY_SECTION_ORDER.HARNESS_IDENTITY, text: 'You are an AI agent powered by DeepSeek Harness.', }) } @@ -484,7 +535,7 @@ export class SystemPrompt extends Service { variables[name] = provider(context) } } - // Scoped sections shadow globals before the stable order sort. + // Scoped sections shadow globals before the deterministic order sort. const sectionByName = this.layers.merge(scope, layer => layer.sections) const contextByName = this.layers.merge(scope, layer => layer.contexts) // Validate order against pre-restriction names while collecting visible schemas. @@ -505,7 +556,7 @@ export class SystemPrompt extends Service { collected.push(...schemas) for (const name of acceptedKnownNames) knownNames.add(name) } - const sectionDefinitions = [...sectionByName.values()].sort((a, b) => a.order - b.order) + const sectionDefinitions = [...sectionByName.values()].sort(comparePromptSections) const completeSections = sectionDefinitions.filter(section => section.complete === true) if (completeSections.length > 1) { throw new Error(`multiple complete prompt sections are active: ${completeSections.map(section => JSON.stringify(section.name)).join(', ')}`) diff --git a/packages/core/system-prompt/tests/system-prompt.spec.ts b/packages/core/system-prompt/tests/system-prompt.spec.ts index c4018103d3..d903435718 100644 --- a/packages/core/system-prompt/tests/system-prompt.spec.ts +++ b/packages/core/system-prompt/tests/system-prompt.spec.ts @@ -1,10 +1,12 @@ import { describe, expect, it } from 'vitest' import { Context } from '@deepseek-ai/cordis' -import SystemPrompt, { AssembleContext, PromptAssembly, renderContextSnapshot, renderPrompt } from '@deepseek-ai/dsh-system-prompt' +import SystemPrompt, { + AssembleContext, FIRST_PARTY_SECTION_ORDER, PromptAssembly, renderContextSnapshot, renderPrompt, +} from '@deepseek-ai/dsh-system-prompt' /** * Every assembly carries the plugin's own built-ins — `harness:identity` - * (order −100) and `deployment:persona` (order 0, from config). Tests about + * and `deployment:persona` (from config). Tests about * registry MECHANICS strip them with {@link contributed} to stay focused on * their own sections; the built-ins' behavior is pinned by its own describe. */ @@ -15,6 +17,14 @@ function contributed(assembly: PromptAssembly): PromptAssembly['sections'] { } describe('SystemPrompt', () => { + it('keeps first-party section placements unique, integral, and at least ten apart', () => { + const orders = Object.values(FIRST_PARTY_SECTION_ORDER) + expect(orders.every(Number.isInteger)).toBe(true) + expect(new Set(orders).size).toBe(orders.length) + const sorted = [...orders].sort((a, b) => a - b) + expect(sorted.slice(1).every((order, index) => order - sorted[index]! >= 10)).toBe(true) + }) + describe('built-in sections', () => { it('registers the harness identity and the configured deployment persona', async () => { const ctx = new Context() @@ -115,6 +125,15 @@ describe('SystemPrompt', () => { expect(renderContextSnapshot(assembly)).toBe('Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\ncontext 1\n\ncontext 2') }) + it('breaks equal section orders by code-unit name regardless of registration order', async () => { + for (const names of [['äther', 'zeta'], ['zeta', 'äther']] as const) { + const ctx = new Context() + await ctx.plugin(SystemPrompt) + for (const name of names) ctx.systemPrompt.section({ name, order: 10, text: name }) + expect(contributed(await ctx.systemPrompt.assemble()).map(section => section.name)).toEqual(['zeta', 'äther']) + } + }) + it('resolves section text providers against the assemble context, at each assemble call', async () => { // The context is HOW per-agent sections work (the loop passes { agent }); // this spec stays agent-agnostic and smuggles a marker through a plain field. @@ -262,7 +281,7 @@ describe('SystemPrompt', () => { it('composes multiple system-prompt/assemble waterfall listeners in order, with the context', async () => { const ctx = new Context() await ctx.plugin(SystemPrompt) - ctx.systemPrompt.section({ name: 'base', order: 0, text: 'base' }) + ctx.systemPrompt.section({ name: 'base', order: 10, text: 'base' }) // Listener A appends a section, then delegates. const contexts: AssembleContext[] = [] @@ -329,7 +348,7 @@ describe('SystemPrompt', () => { it('assembles snapshots so one-step mutations do not leak into future assemblies', async () => { const ctx = new Context() await ctx.plugin(SystemPrompt) - ctx.systemPrompt.section({ name: 'base', order: 0, text: 'base' }) + ctx.systemPrompt.section({ name: 'base', order: 10, text: 'base' }) ctx.systemPrompt.tools(() => ({ schemas: [{ name: 't', description: 'tool', parameters: { type: 'object', properties: {} } }] })) const first = await ctx.systemPrompt.assemble() diff --git a/packages/core/tools/README.i18n.yaml b/packages/core/tools/README.i18n.yaml index 571012276f..f94a3ebeab 100644 --- a/packages/core/tools/README.i18n.yaml +++ b/packages/core/tools/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/core/tools/README.md -README.md: a140255a04187d4f2206df0f90c950d7608ee8ac -README.zh.md: f519ba0a364f05ab63967c07ffd6681a5f62a70a +README.md: 98f5d260e023e6b301590bbb4e4eda3beb7ee9ff +README.zh.md: 98045426b4c18317cd7685a907ce7042ca322269 diff --git a/packages/core/tools/README.md b/packages/core/tools/README.md index a140255a04..98f5d260e0 100644 --- a/packages/core/tools/README.md +++ b/packages/core/tools/README.md @@ -119,7 +119,7 @@ Under `code` or `both`, the registry exposes the reserved `run_code` transport a Under `code` — not `both` — the transport is also the only entry the model may use: a model-direct call naming any other visible tool resolves to `UNKNOWN_TOOL` at execution creation, before `tools/pre-execute`, approval `ask`, and guards, so nothing observes or approves a call that can only fail. The denial names the route back (`only \`run_code\` is callable directly — call \`\` from inside a \`run_code\` program instead`), because the same prompt declares that tool and a bare `unknown tool` reads as a broken deployment. SDK sub-dispatches carry the outer execution's `parent` token and are exempt, so programs keep every binding the SDK declared. See the [executor-collapse note](../../../.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.md), the [Code Mode foundation](../../../.agents/notes/implemented/feature/2026-06-15-code-mode.md), [typed-return contract](../../../.agents/notes/implemented/feature/2026-07-20-code-mode-typed-tool-returns.md), and [code-runtime seam](../../code-runtime/README.md). Try `pnpm run demo:code-mode`. -- **The SDK section** (`tools:sdk`, order 150): a lazy prompt section regenerating the language-appropriate SDK text at each assembly. In the TypeScript flavor it emits `JsonValue`, exact `ToolArgsMap` / `ToolOutputMap`, `ToolName`, the `ToolCallError` declaration, and a mapped `tools` namespace for the calling scope's visible end capabilities (exotic names via quoted keys), plus fixed usage instructions; the Python flavor (`ctx.codeRuntime.language === 'python'`) emits the equivalent named `TypedDict`s and a `tools` object with matching usage instructions. Deterministic — lexicographic tool order, byte-identical text for an unchanged tool set (prefix-cache-friendly). Both codegens are exported and never throw during prompt assembly: `jsonSchemaToTs` handles every unified schema construct and degrades unsupported raw constructs to `unknown`; `jsonSchemaToPy` does the same, degrading to `Any` (and a whole object to `dict[str, Any]` when a field name is not a legal `TypedDict` attribute, or whenever it is called outside the SDK render, which supplies the naming context a `TypedDict` declaration needs). +- **The SDK section** (`tools:sdk`, first-party order 5000): a lazy prompt section regenerating the language-appropriate SDK text at each assembly. In the TypeScript flavor it emits `JsonValue`, exact `ToolArgsMap` / `ToolOutputMap`, `ToolName`, the `ToolCallError` declaration, and a mapped `tools` namespace for the calling scope's visible end capabilities (exotic names via quoted keys), plus fixed usage instructions; the Python flavor (`ctx.codeRuntime.language === 'python'`) emits the equivalent named `TypedDict`s and a `tools` object with matching usage instructions. Deterministic — lexicographic tool order, byte-identical text for an unchanged tool set (prefix-cache-friendly). Both codegens are exported and never throw during prompt assembly: `jsonSchemaToTs` handles every unified schema construct and degrades unsupported raw constructs to `unknown`; `jsonSchemaToPy` does the same, degrading to `Any` (and a whole object to `dict[str, Any]` when a field name is not a legal `TypedDict` attribute, or whenever it is called outside the SDK render, which supplies the naming context a `TypedDict` declaration needs). - **The dispatch bridge** (`run_code`'s execute): every binding call is snapshotted as lossless JSON before dispatch, scheduled through a per-run pool that reuses the native concurrency contract — calls start strictly in submission order, consecutive `isConcurrencySafe` calls overlap up to the validated `maxParallelSubCalls` config (default 10; `1` restores serial dispatch), and an exclusive-classified call drains the pool, runs alone, and bars later calls — given the outer execution's opaque token as `parent`, and run through the complete pre-execute → guards → execute → post-execute → result pipeline. A success returns the final canonical value after policy; a failure reaches the worker as one message and becomes `ToolCallError(toolName, message)`. Each started sub-call logs a `tool/code-dispatch-start` event (deterministic id `:code:`, numbered by submission) at pipeline entry and settles with one `tool/code-dispatch` event carrying the complete model-facing `content`/`isError` outcome (the `tool/result` vocabulary, so UIs render sub-calls through the native path — the pair's `time` fields carry per-sub-call timing); a queued call abandoned by run settlement logs neither. `deriveMessages()` surfaces neither event nor persists the canonical value. Token correlation lets commit-style observers defer an inner success until the final `run_code` result without exposing the live outer execution; ordinary tool side effects are not rolled back. Every sub-call `additionalContexts` entry and every successful final content sequence containing an image is deferred through the outer `ToolRunContext` in dispatch order; the loop appends those contexts only after the parent `run_code` result, preserving adjacency and source attribution even when the program later fails. - **Settlement discipline**: the bridge owns a run-scoped abort that follows the outer signal in and fires when the run settles for any reason, so a budget expiry aborts an in-flight sub-tool instead of orphaning it; the bridge then drains its queue BEFORE returning, so every `tool/code-dispatch` lands inside the open turn. A failed run throws `CodeRunFailedError` (`code: 'CODE_RUN_FAILED'`, message = the failure kind + captured logs), which the pipeline converts to a structured `isError` the model self-corrects from. - **Result size**: intermediate binding values cross the worker process whole and have no per-binding byte cap. `run_code` returns canonical `{ logs: string[], result?: JsonValue }`; strings render raw, every other present JSON root renders through a stack-safe pretty JSON traversal whose total indentation is capped at ten characters (deeper subtrees stay compact), `null` remains explicit, and absent `result` means the program returned `undefined`. The worker's configurable `maxOutputBytes` (default 64 MiB) applies only to the combined serialized outer log-array, completion-value, or failure-message payloads; fixed result-envelope syntax and presentation whitespace are outside that limit. Invalid and over-limit completions fail explicitly, and only this outer result is eligible for ordinary spill. diff --git a/packages/core/tools/README.zh.md b/packages/core/tools/README.zh.md index f519ba0a36..98045426b4 100644 --- a/packages/core/tools/README.zh.md +++ b/packages/core/tools/README.zh.md @@ -121,7 +121,7 @@ ctx.tools.register(defineTool({ 在 `code`(而非 `both`)下,该传输同时也是模型唯一可用的入口:模型直呼其他任何可见工具名,都会在创建执行时、早于 `tools/pre-execute`、审批 `ask` 和 guards 解析为 `UNKNOWN_TOOL`,因此没有任何一方会观察或批准一个注定失败的调用。拒绝信息会给出正确路径(`only \`run_code\` is callable directly — call \`\` from inside a \`run_code\` program instead`),因为同一份提示词刚刚声明过那个工具,只说 `unknown tool` 会被读成部署损坏。SDK 子分发携带外层执行的 `parent` token,不受此限制,因此程序保留 SDK 声明的全部绑定。参见[执行器塌缩 note](../../../.agents/notes/implemented/bug-fix/2026-08-07-code-mode-executor-collapse.md)、[Code Mode 基础](../../../.agents/notes/implemented/feature/2026-06-15-code-mode.md)、[类型化返回约定](../../../.agents/notes/implemented/feature/2026-07-20-code-mode-typed-tool-returns.md)和[代码运行时 seam](../../code-runtime/README.md)。可以运行 `pnpm run demo:code-mode` 试用。 -- **SDK 段**(`tools:sdk`,顺序 150):一个在组装时求值的提示词段,每次组装都会重新生成与所加载运行时语言相符的 SDK 文本。TypeScript 形态会生成 `JsonValue`、精确的 `ToolArgsMap` / `ToolOutputMap`、`ToolName`、`ToolCallError` 声明,以及映射调用作用域最终可见工具的 `tools` 命名空间(特殊名称使用带引号的键),并附带固定的使用说明;Python 形态(`ctx.codeRuntime.language === 'python'`)发出等价的具名 `TypedDict` 与一个带相同用法说明的 `tools` 对象。其输出具有确定性:工具按字典序排列;工具集合不变时,文本逐字节相同(有利于前缀 cache)。两个代码生成器都已导出,且绝不会在提示词组装期间抛出:`jsonSchemaToTs` 处理统一 schema 的每种构造并将不受支持的原始构造降级为 `unknown`;`jsonSchemaToPy` 同理,降级为 `Any`(当某字段名不是合法的 `TypedDict` 属性时,或在 SDK 渲染之外被调用时——`TypedDict` 声明所需的命名上下文由该渲染提供——整个对象降级为 `dict[str, Any]`)。 +- **SDK 段**(`tools:sdk`,first-party 顺序 5000):一个在组装时求值的提示词段,每次组装都会重新生成与所加载运行时语言相符的 SDK 文本。TypeScript 形态会生成 `JsonValue`、精确的 `ToolArgsMap` / `ToolOutputMap`、`ToolName`、`ToolCallError` 声明,以及映射调用作用域最终可见工具的 `tools` 命名空间(特殊名称使用带引号的键),并附带固定的使用说明;Python 形态(`ctx.codeRuntime.language === 'python'`)发出等价的具名 `TypedDict` 与一个带相同用法说明的 `tools` 对象。其输出具有确定性:工具按字典序排列;工具集合不变时,文本逐字节相同(有利于前缀 cache)。两个代码生成器都已导出,且绝不会在提示词组装期间抛出:`jsonSchemaToTs` 处理统一 schema 的每种构造并将不受支持的原始构造降级为 `unknown`;`jsonSchemaToPy` 同理,降级为 `Any`(当某字段名不是合法的 `TypedDict` 属性时,或在 SDK 渲染之外被调用时——`TypedDict` 声明所需的命名上下文由该渲染提供——整个对象降级为 `dict[str, Any]`)。 - **分发桥接层**(`run_code` 的 execute):每个绑定调用都会在分发前快照为无损 JSON,经由每次运行独有、复用原生并发约定的池调度——调用严格按提交顺序启动,连续的 `isConcurrencySafe` 调用最多可重叠经校验的 `maxParallelSubCalls` 配置个(默认 10;设为 `1` 即恢复串行分发),被分类为独占的调用先排空池、单独运行并阻挡其后的调用——以外层执行的不透明 token 作为 `parent`,并经过完整的 pre-execute → guards → execute → post-execute → result 流水线。成功会返回策略处理后的最终规范值;失败以一条消息到达 worker,并成为 `ToolCallError(toolName, message)`。每个已启动的子调用在进入流水线时记录一条 `tool/code-dispatch-start` 事件(确定性 id `:code:`,按提交顺序编号),并以一条携带完整模型可见 `content`/`isError` 结果的 `tool/code-dispatch` 事件完结(采用 `tool/result` 词汇,因此 UI 会沿原生路径呈现子调用——这对事件的 `time` 字段承载每个子调用的计时);因 run 结算而被放弃的排队调用两者都不记录。`deriveMessages()` 既不公开这两个事件,也不持久化规范值。token 关联使按提交语义工作的观察器可以延后提交内部调用的成功结果,直到最终 `run_code` 结果确定,而无需暴露进行中的外层执行;普通工具副作用不会回滚。每个子调用的 `additionalContexts` 条目以及每份成功且含图片的最终内容序列都会按分发顺序通过外层 `ToolRunContext` 延迟;循环只在父级 `run_code` 结果之后追加这些上下文,从而保持相邻关系和来源归属,即使程序后来失败也不例外。 - **结算纪律**:桥接层拥有一个运行作用域的中止机制;该中止会跟随传入的外层信号,并在运行因任何原因结算时触发,因此预算耗尽会中止正在运行的子工具,而不会将其遗留。桥接层随后会在返回之前排空队列,使每个 `tool/code-dispatch` 都落在仍打开的轮次内。失败的运行会抛出 `CodeRunFailedError`(`code: 'CODE_RUN_FAILED'`,message = 失败类型 + 已捕获日志),流水线会将其转换为模型可据以自我修正的结构化 `isError`。 - **结果大小**:中间绑定值会完整传入 worker 进程,且没有逐绑定字节上限。`run_code` 返回规范的 `{ logs: string[], result?: JsonValue }`;字符串原样呈现,其他所有存在的 JSON 根都通过栈安全的美化 JSON 遍历呈现,总缩进最多为 10 个字符(更深的子树保持紧凑),`null` 保持显式,而缺少 `result` 表示程序返回 `undefined`。worker 可配置的 `maxOutputBytes`(默认 64 MiB)只应用于组合序列化后的外层日志数组、完成值或失败消息载荷;固定的结果封装语法和呈现空白不计入该上限。无效和超限的完成会明确失败,只有这个外层结果可以按常规 spill 机制处理。 diff --git a/packages/core/tools/src/code-mode.ts b/packages/core/tools/src/code-mode.ts index ad0e59b77e..729163b95c 100644 --- a/packages/core/tools/src/code-mode.ts +++ b/packages/core/tools/src/code-mode.ts @@ -11,6 +11,7 @@ import type { ContentBlock } from '@deepseek-ai/dsh-llm' import type { CodeBindingFunction, CodeRunResult, CodeRuntime } from '@deepseek-ai/dsh-code-runtime' import { snapshotJsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue } from '@deepseek-ai/dsh-session' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { defineTool, parameterSchemaSpecToJsonSchema } from './schema.ts' import { TOOL_RUNTIME_SCHEDULER } from './index.ts' import type { CodeDispatchLog, ToolDefinition, ToolExecutionResult, ToolRuntime, ToolRunContext } from './index.ts' @@ -19,8 +20,8 @@ import type {} from './types.ts' /** The model-facing name of the Code Mode tool. */ export const RUN_CODE_NAME = 'run_code' -/** The `tools:sdk` section order: inside the 100–199 tool-guidance band, after per-tool guidance sections. */ -export const SDK_SECTION_ORDER = 150 +/** The `tools:sdk` section order, after per-tool guidance sections. */ +export const SDK_SECTION_ORDER = FIRST_PARTY_SECTION_ORDER.TOOLS_SDK /** * The language-specific `run_code` schema text: the tool `description` and its diff --git a/packages/core/tools/src/index.ts b/packages/core/tools/src/index.ts index 3901480cb0..b51579e8ee 100644 --- a/packages/core/tools/src/index.ts +++ b/packages/core/tools/src/index.ts @@ -13,7 +13,7 @@ import { assertNever, deepFreeze, HarnessError } from '@deepseek-ai/dsh-llm' import type { Agent } from '@deepseek-ai/dsh-agent' import { snapshotJsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue, UserMessage } from '@deepseek-ai/dsh-session' -import type { ToolProviderResult } from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER, type ToolProviderResult } from '@deepseek-ai/dsh-system-prompt' import type { CodeRuntime } from '@deepseek-ai/dsh-code-runtime' // Type-only: makes `ctx.get('approval')` resolve to the ApprovalService // augmentation. The seam stays optional at runtime — see `serviceAsk`. @@ -45,10 +45,10 @@ import { renderToolsSdkPy } from './py-types.ts' */ /** * Prompt order of the `code` collapse statement: after the persona and before - * the 100-199 per-tool guidance band, so the model reads which tools it may - * call before it reads what each one is for. + * per-tool guidance, so the model reads which tools it may call before it + * reads what each one is for. */ -const COLLAPSE_SECTION_ORDER = 99 +const COLLAPSE_SECTION_ORDER = FIRST_PARTY_SECTION_ORDER.CODE_ONLY /** * The model-facing statement of the `code` collapse. Names the consequence @@ -842,8 +842,8 @@ export class ToolRuntime extends Service { * {@link sdkSection} is and rendering empty outside an effective `code`. * * Every tool contributes its own guidance section naming its tool, none of - * them qualify how that tool is reached, and they all render before the SDK - * (orders 100-199 against {@link SDK_SECTION_ORDER}). Without this the model + * them qualify how that tool is reached, and they all render before the SDK. + * Without this the model * reads a catalog of tools it is told to use and no statement that only * `run_code` may be called, so it emits a native call, receives * `UNKNOWN_TOOL` for a tool the prompt just declared, and concludes the diff --git a/packages/core/tools/tests/code-mode.spec.ts b/packages/core/tools/tests/code-mode.spec.ts index 98d849f097..563dffbf9f 100644 --- a/packages/core/tools/tests/code-mode.spec.ts +++ b/packages/core/tools/tests/code-mode.spec.ts @@ -3,7 +3,7 @@ import { Context } from '@deepseek-ai/cordis' import { createUserMessage, CallId } from '@deepseek-ai/dsh-llm' import { createScope } from '@deepseek-ai/dsh-scope' import type { Scope } from '@deepseek-ai/dsh-scope' -import SystemPrompt from '@deepseek-ai/dsh-system-prompt' +import SystemPrompt, { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { CodeRuntime } from '@deepseek-ai/dsh-code-runtime' import type { CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime' import ToolRuntime, { CodeRunFailedError, RUN_CODE_NAME, TOOL_ABORTED_BEFORE_DISPATCH, defineContentToolFixture, defineTool } from '@deepseek-ai/dsh-tools' @@ -138,9 +138,13 @@ describe('mode-aware wire contribution', () => { it("mode 'code' states the run_code-only rule BEFORE the per-tool guidance that names each tool", async () => { const { ctx, systemPrompt } = await setup({ mode: 'code' }) registerEcho(ctx) - // Stand in for a real tool's guidance section, which sits in the 100-199 - // band and names its tool without saying how it is reached. - ctx.systemPrompt.section({ name: 'tool:echo', order: 100, text: 'Use the echo tool.' }) + // Stand in for a real tool's guidance section, which names its tool without + // saying how it is reached. + ctx.systemPrompt.section({ + name: 'tool:echo', + order: FIRST_PARTY_SECTION_ORDER.TOOL_READ, + text: 'Use the echo tool.', + }) const assembly = await systemPrompt.assemble() const names = assembly.sections.map(section => section.name) @@ -206,7 +210,11 @@ describe('mode-aware wire contribution', () => { const { ctx, systemPrompt } = await setup({ mode }) registerEcho(ctx) const { scope, agent } = await mintAgentScope(ctx) - scope.ctx.systemPrompt.section({ name: 'tools:sdk', order: 150, text: 'SCOPED SDK' }) + scope.ctx.systemPrompt.section({ + name: 'tools:sdk', + order: FIRST_PARTY_SECTION_ORDER.TOOLS_SDK, + text: 'SCOPED SDK', + }) const scoped = await systemPrompt.assemble({ scope: agent }) const global = await systemPrompt.assemble() @@ -290,7 +298,11 @@ describe('mode-aware wire contribution', () => { expect(() => ctx.tools.register(impostor)).toThrow(/reserved for the Code Mode presentation transport/) expect(() => scope.ctx.tools.restrict({ allow: [RUN_CODE_NAME] })).toThrow(/cannot name reserved Code Mode presentation transport/) expect(() => scope.ctx.tools.restrict({ deny: [RUN_CODE_NAME] })).toThrow(/cannot name reserved Code Mode presentation transport/) - scope.ctx.systemPrompt.section({ name: 'scoped-note', order: 149, text: 'safe note' }) + scope.ctx.systemPrompt.section({ + name: 'scoped-note', + order: FIRST_PARTY_SECTION_ORDER.TOOLS_SDK - 10, + text: 'safe note', + }) scope.ctx.tools.register(defineContentToolFixture({ name: 'scoped_safe', description: 'Safe scoped tool.', diff --git a/packages/experimental/tool-agent-team/src/index.ts b/packages/experimental/tool-agent-team/src/index.ts index fa2802828a..56341a6206 100644 --- a/packages/experimental/tool-agent-team/src/index.ts +++ b/packages/experimental/tool-agent-team/src/index.ts @@ -5,6 +5,7 @@ import z from '@deepseek-ai/schemastery' import type { Agent } from '@deepseek-ai/dsh-agent' import { TeamTaskId } from '@deepseek-ai/dsh-experimental-agent-team' import type { TeamMemberView } from '@deepseek-ai/dsh-experimental-agent-team' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { defineTool } from '@deepseek-ai/dsh-tools' import type { InferValue, ValueSchemaSpec } from '@deepseek-ai/dsh-tools' @@ -163,7 +164,7 @@ function install(agent: Agent, ctx: Context, config: Required): () => vo try { register(scoped.systemPrompt.section({ name: 'team:policy', - order: 60, + order: FIRST_PARTY_SECTION_ORDER.TEAM_POLICY, text: () => { const membership = ctx.agentTeams.membership(agent) return `${POLICY}\n\nYour Team role is ${membership.role}; your Team name is ${membership.name}; Team id is ${membership.id}.` diff --git a/packages/extensions/tool-cordis/src/index.ts b/packages/extensions/tool-cordis/src/index.ts index d760797a5d..e090eb993d 100644 --- a/packages/extensions/tool-cordis/src/index.ts +++ b/packages/extensions/tool-cordis/src/index.ts @@ -14,7 +14,7 @@ import type { JsonValue } from '@deepseek-ai/dsh-session' import type { UserMessage } from '@deepseek-ai/dsh-session' import { defineTool } from '@deepseek-ai/dsh-tools' import type { ToolExecution } from '@deepseek-ai/dsh-tools' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { missingServices, providedServices } from './inspect.ts' import { presentDefineCall, presentInspectListCall, presentInspectQueryCall, presentInspectSelfCall, presentRunCall, @@ -33,7 +33,11 @@ function requireAgent(exec: ToolExecution): Agent { /** Register the Cordis tools and explicit `@pluginId` context injection. */ export function apply(ctx: Context): void { - ctx.systemPrompt.section({ name: 'tool:cordis', order: 115.5, text: CORDIS_SYSTEM_PROMPT }) + ctx.systemPrompt.section({ + name: 'tool:cordis', + order: FIRST_PARTY_SECTION_ORDER.TOOL_CORDIS, + text: CORDIS_SYSTEM_PROMPT, + }) for (const provider of hostInspectProviders(ctx)) { ctx.effect(() => ctx.cordisInspect.register(provider), `tool-cordis: inspect ${provider.manifest.id}`) } diff --git a/packages/fs/tool-fs-search/src/glob.ts b/packages/fs/tool-fs-search/src/glob.ts index 871dc16ae8..39a5fabd1d 100644 --- a/packages/fs/tool-fs-search/src/glob.ts +++ b/packages/fs/tool-fs-search/src/glob.ts @@ -14,7 +14,7 @@ import { sep } from 'node:path' import { defineTool } from '@deepseek-ai/dsh-tools' import type { GenericCallView, SearchResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type { SpillRef } from '@deepseek-ai/dsh-spill' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { runRipgrep, toWorkdirRelative, trySaveFormattedResult } from './search-core.ts' import { globSearchMeta, searchViewFromMeta } from './presentation.ts' import { acceptedDirectCallValue } from './direct-call.ts' @@ -300,7 +300,7 @@ export function applyGlobTool(ctx: Context, caps: GlobToolCaps): void { : 'while a larger one keeps the modification-time-ordered head.' ctx.systemPrompt.section({ name: 'tool:glob', - order: 103, + order: FIRST_PARTY_SECTION_ORDER.TOOL_GLOB, text: 'Use the glob tool — not shell find — to discover files by path pattern. A pattern with no "/" matches basenames at any depth, so "*" matches every file in the tree rather than its top level. ' + `Results are files only, never directories, and include hidden and ignored files: a result that fits comes back in modification-time order, ${overCapGuidance}`, }) diff --git a/packages/fs/tool-fs-search/src/grep.ts b/packages/fs/tool-fs-search/src/grep.ts index 38f54aa2a5..0d56c0ae63 100644 --- a/packages/fs/tool-fs-search/src/grep.ts +++ b/packages/fs/tool-fs-search/src/grep.ts @@ -16,7 +16,7 @@ import { defineTool } from '@deepseek-ai/dsh-tools' import type { GenericCallView, SearchResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type { RetainedItems } from '@deepseek-ai/dsh-output-retention' import type { SpillRef } from '@deepseek-ai/dsh-spill' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import type { GrepMatch } from './search-core.ts' import { SearchError, previewLine, retainGrepMatches, runRipgrep, toWorkdirRelative, trySaveFormattedResult } from './search-core.ts' import { grepSearchMeta, searchViewFromMeta } from './presentation.ts' @@ -275,7 +275,7 @@ export function presentGrepResult( export function applyGrepTool(ctx: Context, caps: GrepToolCaps): void { ctx.systemPrompt.section({ name: 'tool:grep', - order: 104, + order: FIRST_PARTY_SECTION_ORDER.TOOL_GREP, text: 'Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context.', }) diff --git a/packages/fs/tool-fs/src/edit.ts b/packages/fs/tool-fs/src/edit.ts index b9fc89d4ed..60f3913866 100644 --- a/packages/fs/tool-fs/src/edit.ts +++ b/packages/fs/tool-fs/src/edit.ts @@ -9,7 +9,7 @@ import type { Context } from '@deepseek-ai/cordis' import { defineTool } from '@deepseek-ai/dsh-tools' import type { DiffCallView, DiffResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type {} from '@deepseek-ai/dsh-fs' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { computeHunkDiffs, diffsFromMeta } from './diff.ts' import { remediateFsError } from './error.ts' import { sessionResolveOptions } from './session-cwd.ts' @@ -76,7 +76,7 @@ export function formatEditOutput(displayPath: string, replaceAll: boolean): stri export function applyEditTool(ctx: Context, sandbox: FsSandboxController): void { ctx.systemPrompt.section({ name: 'tool:edit', - order: 102, + order: FIRST_PARTY_SECTION_ORDER.TOOL_EDIT, text: 'Use the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-observation-policy requires it), unless you just created or edited it in this session.', }) diff --git a/packages/fs/tool-fs/src/read.ts b/packages/fs/tool-fs/src/read.ts index cc9bd4e937..0ec11074f8 100644 --- a/packages/fs/tool-fs/src/read.ts +++ b/packages/fs/tool-fs/src/read.ts @@ -8,7 +8,7 @@ import type { Context } from '@deepseek-ai/cordis' import { defineTool } from '@deepseek-ai/dsh-tools' import type { GenericCallView, ReadResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type {} from '@deepseek-ai/dsh-fs' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { buildWindow, formatReadOutput, langFromPath, readMetaFromMeta } from './read-render.ts' import { resolveRegularReadTarget } from './read-target.ts' @@ -69,7 +69,7 @@ export function parseReadArgs(args: { file_path: string; offset?: number; limit? export function applyReadTool(ctx: Context, caps: ReadToolCaps): void { ctx.systemPrompt.section({ name: 'tool:read', - order: 100, + order: FIRST_PARTY_SECTION_ORDER.TOOL_READ, text: 'Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.', }) diff --git a/packages/fs/tool-fs/src/write.ts b/packages/fs/tool-fs/src/write.ts index c7a7e555d2..20bdb2671f 100644 --- a/packages/fs/tool-fs/src/write.ts +++ b/packages/fs/tool-fs/src/write.ts @@ -10,7 +10,7 @@ import { defineTool } from '@deepseek-ai/dsh-tools' import type { DiffCallView, DiffResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type { FsWriteOutcome } from '@deepseek-ai/dsh-fs' import type {} from '@deepseek-ai/dsh-fs' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { computeHunkDiffs, diffsFromMeta } from './diff.ts' import { remediateFsError } from './error.ts' import { sessionResolveOptions } from './session-cwd.ts' @@ -62,7 +62,7 @@ interface WriteToolArgs { export function applyWriteTool(ctx: Context, sandbox: FsSandboxController): void { ctx.systemPrompt.section({ name: 'tool:write', - order: 101, + order: FIRST_PARTY_SECTION_ORDER.TOOL_WRITE, text: 'Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes.', }) diff --git a/packages/goal/tool-goal/src/index.ts b/packages/goal/tool-goal/src/index.ts index 903190de4d..ac13ea9213 100644 --- a/packages/goal/tool-goal/src/index.ts +++ b/packages/goal/tool-goal/src/index.ts @@ -11,7 +11,7 @@ import type { GoalRef, GoalView } from '@deepseek-ai/dsh-goal' import { boundContextSummary, createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm' import { defineTool } from '@deepseek-ai/dsh-tools' import type { GenericCallView } from '@deepseek-ai/dsh-tools' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { completionAuthority, goalToolExecution, @@ -188,7 +188,7 @@ export function apply(ctx: Context, config: Config): void { const resolved = resolveConfig(config) ctx.systemPrompt.section({ name: 'tool:goal', - order: 114, + order: FIRST_PARTY_SECTION_ORDER.TOOL_GOAL, text: guidance(resolved.blockedAfterConsecutiveRounds), }) diff --git a/packages/jobs/tool-jobs/src/index.ts b/packages/jobs/tool-jobs/src/index.ts index a04847b09a..3424abc3ef 100644 --- a/packages/jobs/tool-jobs/src/index.ts +++ b/packages/jobs/tool-jobs/src/index.ts @@ -15,7 +15,7 @@ import { defineTool } from '@deepseek-ai/dsh-tools' import type { GenericCallView, ToolDefinition, ToolExecution } from '@deepseek-ai/dsh-tools' import { JobId } from '@deepseek-ai/dsh-jobs' import type { JobSnapshot } from '@deepseek-ai/dsh-jobs' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import type { Agent } from '@deepseek-ai/dsh-agent' export const name = 'tool-jobs' @@ -259,10 +259,10 @@ export function apply(ctx: Context, config: Config): void { // Producers may start work only while a controller is attached. ctx.jobs.attachController('tool-jobs') - // Cross-call guidance follows the bash section and precedes product sections. + // Cross-call guidance follows the filesystem sections and precedes product sections. ctx.systemPrompt.section({ name: 'tool:jobs', - order: 106, + order: FIRST_PARTY_SECTION_ORDER.TOOL_JOBS, text: 'Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job\'s work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering.', }) diff --git a/packages/lsp/tool-lsp/src/index.ts b/packages/lsp/tool-lsp/src/index.ts index f2461a7539..b958688d5a 100644 --- a/packages/lsp/tool-lsp/src/index.ts +++ b/packages/lsp/tool-lsp/src/index.ts @@ -16,7 +16,7 @@ import { defineTool } from '@deepseek-ai/dsh-tools' import { assertNever } from '@deepseek-ai/dsh-llm' import { LspError } from '@deepseek-ai/dsh-lsp' import type {} from '@deepseek-ai/dsh-lsp' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout' import { DEFAULT_MAX_LOCATIONS, @@ -101,7 +101,11 @@ export function apply(ctx: Context, config: Config): void { assertPositiveInteger('maxResultChars', resolved.maxResultChars) assertTimer('timeoutMs', resolved.timeoutMs) - ctx.systemPrompt.section({ name: 'tool:lsp', order: 112, text: LSP_PROMPT_TEXT }) + ctx.systemPrompt.section({ + name: 'tool:lsp', + order: FIRST_PARTY_SECTION_ORDER.TOOL_LSP, + text: LSP_PROMPT_TEXT, + }) ctx.tools.register(defineTool({ name: 'lsp', diff --git a/packages/plan/plan-mode/src/index.ts b/packages/plan/plan-mode/src/index.ts index d81865932c..1664ccdaf4 100644 --- a/packages/plan/plan-mode/src/index.ts +++ b/packages/plan/plan-mode/src/index.ts @@ -30,7 +30,7 @@ import type { Agent, PreStepDecision } from '@deepseek-ai/dsh-agent' import { createUserMessage } from '@deepseek-ai/dsh-llm' import type { Session, SessionEvent, UserMessage } from '@deepseek-ai/dsh-session' import { defineTool } from '@deepseek-ai/dsh-tools' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { UserQuestionError } from '@deepseek-ai/dsh-user-questions' // Type-only edge: resolves `ctx.commands` for the optional command child. import type { CommandId } from '@deepseek-ai/dsh-commands' @@ -242,7 +242,7 @@ export class PlanModeController extends Service { ctx.systemPrompt.section({ name: 'plan:policy', - order: 50, + order: FIRST_PARTY_SECTION_ORDER.PLAN_POLICY, text: (context) => { if (context.agent === undefined) return '' const pending = this.pendingIntents.get(context.agent.session) diff --git a/packages/session-query/tool-session-query/src/index.ts b/packages/session-query/tool-session-query/src/index.ts index d204184cfe..efba41849c 100644 --- a/packages/session-query/tool-session-query/src/index.ts +++ b/packages/session-query/tool-session-query/src/index.ts @@ -8,7 +8,7 @@ import type { Context } from '@deepseek-ai/cordis' import z from '@deepseek-ai/schemastery' import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout' import { defineTool } from '@deepseek-ai/dsh-tools' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { toolInput } from './input.ts' import { operations } from './operations.ts' import { presentation } from './presentation.ts' @@ -59,7 +59,7 @@ export function apply(ctx: Context, config: Config): void { const resolved = resolveConfig(config) ctx.systemPrompt.section({ name: 'tool:session-query', - order: 113, + order: FIRST_PARTY_SECTION_ORDER.TOOL_SESSION_QUERY, text: PROMPT_TEXT, }) diff --git a/packages/shell/tool-bash/src/index.ts b/packages/shell/tool-bash/src/index.ts index 37c4a762e3..4c3069a10c 100644 --- a/packages/shell/tool-bash/src/index.ts +++ b/packages/shell/tool-bash/src/index.ts @@ -15,7 +15,7 @@ import { defineTool, TOOL_ABORTED } from '@deepseek-ai/dsh-tools' import type { GenericCallView, TerminalCallView, ToolExecution, ToolResult, ToolResultView } from '@deepseek-ai/dsh-tools' import { HarnessError } from '@deepseek-ai/dsh-llm' import type { Agent } from '@deepseek-ai/dsh-agent' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import type {} from '@deepseek-ai/dsh-jobs' import type {} from '@deepseek-ai/dsh-user-approval' import type {} from '@deepseek-ai/dsh-shell-env' @@ -235,7 +235,7 @@ export function apply(ctx: Context, config: Config = {}): void { // Cross-call guidance belongs in the prompt rather than one-call schema prose. ctx.systemPrompt.section({ name: 'tool:bash', - order: 105, + order: FIRST_PARTY_SECTION_ORDER.TOOL_BASH, text: 'Check the [exit code: N] marker on every bash result; investigate failures before moving on.', }) diff --git a/packages/shell/tool-bash/tests/tools.spec.ts b/packages/shell/tool-bash/tests/tools.spec.ts index 0f2e4d5a20..40314ef5fb 100644 --- a/packages/shell/tool-bash/tests/tools.spec.ts +++ b/packages/shell/tool-bash/tests/tools.spec.ts @@ -6,7 +6,7 @@ import { Context } from '@deepseek-ai/cordis' import { CallId } from '@deepseek-ai/dsh-llm' import { ShellExecutor } from '@deepseek-ai/dsh-shell' import type { ShellExecRequest, ShellExecSpec, ShellProcess, ShellProcessRead, ShellRunResult } from '@deepseek-ai/dsh-shell' -import SystemPrompt from '@deepseek-ai/dsh-system-prompt' +import SystemPrompt, { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import ToolRuntime, { TOOL_ABORTED, TOOL_ABORTED_BEFORE_DISPATCH } from '@deepseek-ai/dsh-tools' import AgentRegistry from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent' @@ -377,8 +377,16 @@ describe('bash tool', () => { it('contributes the exit-code habit as its prompt section (guidance the descriptions cannot carry)', async () => { const ctx = await setup() - ctx.systemPrompt.section({ name: 'test:before-bash', order: 104, text: 'before' }) - ctx.systemPrompt.section({ name: 'test:after-bash', order: 106, text: 'after' }) + ctx.systemPrompt.section({ + name: 'test:before-bash', + order: FIRST_PARTY_SECTION_ORDER.TOOL_BASH - 10, + text: 'before', + }) + ctx.systemPrompt.section({ + name: 'test:after-bash', + order: FIRST_PARTY_SECTION_ORDER.TOOL_BASH + 10, + text: 'after', + }) const assembly = await ctx.systemPrompt.assemble() const section = assembly.sections.find(s => s.name === 'tool:bash') expect(assembly.sections.map(s => s.name)).toEqual([ diff --git a/packages/shell/tool-pwsh/src/index.ts b/packages/shell/tool-pwsh/src/index.ts index 603322e381..10d7d40ed5 100644 --- a/packages/shell/tool-pwsh/src/index.ts +++ b/packages/shell/tool-pwsh/src/index.ts @@ -26,7 +26,7 @@ import { defineTool, TOOL_ABORTED } from '@deepseek-ai/dsh-tools' import type { GenericCallView, TerminalCallView, ToolExecution, ToolResult, ToolResultView } from '@deepseek-ai/dsh-tools' import { HarnessError } from '@deepseek-ai/dsh-llm' import type { Agent } from '@deepseek-ai/dsh-agent' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import type {} from '@deepseek-ai/dsh-jobs' import type {} from '@deepseek-ai/dsh-shell-env' import type {} from '@deepseek-ai/dsh-user-approval' @@ -243,7 +243,7 @@ export function apply(ctx: Context, config: Config = {}): void { ctx.systemPrompt.section({ name: 'tool:pwsh', - order: 105, + order: FIRST_PARTY_SECTION_ORDER.TOOL_PWSH, text: 'Non-zero exits are reported as `[exit code: N]` markers; investigate failures before moving on. ' + 'On Windows a killed process settles as `[exit code: 1]` without a signal marker; treat a bare exit 1 after an interruption as a termination, not a command failure.', }) diff --git a/packages/subagent/subagent-in-process-driver/README.i18n.yaml b/packages/subagent/subagent-in-process-driver/README.i18n.yaml index 16d07d8211..2ba6cce30b 100644 --- a/packages/subagent/subagent-in-process-driver/README.i18n.yaml +++ b/packages/subagent/subagent-in-process-driver/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/subagent-in-process-driver/README.md -README.md: ed2568fcff3fe1f0f3968d1cef43ebd914a8911b -README.zh.md: f9958e5c2b819d51bfdf8fc1e14d1f8c7c19be91 +README.md: 5a4ae0a38c34de5c01a5c8d80f2965bb5cb34150 +README.zh.md: 8cc5b37511b565d9899ba572b1c7322ea9fdf598 diff --git a/packages/subagent/subagent-in-process-driver/README.md b/packages/subagent/subagent-in-process-driver/README.md index ed2568fcff..5a4ae0a38c 100644 --- a/packages/subagent/subagent-in-process-driver/README.md +++ b/packages/subagent/subagent-in-process-driver/README.md @@ -39,7 +39,7 @@ Depth enforcement is internal to `startInProcessRun`: it reads the parent depth `attachStructuredRuntime(childCtx, schema)` installs the whole contract in the child's scope: - A `structured_output` tool registered with the requested schema validates and stages the model's value. -- An order-190 system-prompt section tells the child that the tool call is the terminal answer. +- A trailing first-party order-9900 system-prompt section tells the child that the tool call is the terminal answer. - Both contributions are ordinary child-scoped registrations. An expert `system-prompt/assemble` listener may replace them and therefore owns preserving the structured-output protocol for that child. - A `tools/result` observer commits a staged value only after that execution's authoritative final tool result succeeds, including the enclosing `run_code` result for Code Mode sub-dispatch. - A monotonic tool guard blocks later calls after capture, and the structured-output execution's `concludeTurn()` marker ends the turn after the result commits. diff --git a/packages/subagent/subagent-in-process-driver/README.zh.md b/packages/subagent/subagent-in-process-driver/README.zh.md index f9958e5c2b..8cc5b37511 100644 --- a/packages/subagent/subagent-in-process-driver/README.zh.md +++ b/packages/subagent/subagent-in-process-driver/README.zh.md @@ -39,7 +39,7 @@ `attachStructuredRuntime(childCtx, schema)` 会在子 agent 作用域中安装完整约定: - 使用请求 schema 注册的 `structured_output` 工具会校验并暂存模型值。 -- 一个顺序为 190 的系统提示词段会告诉子 agent,该工具调用就是终态答案。 +- 一个位于末尾、first-party 顺序为 9900 的系统提示词段会告诉子 agent,该工具调用就是终态答案。 - 两项贡献都是普通的子 agent 作用域注册。专家级 `system-prompt/assemble` 监听器可以替换它们,因此负责为该子 agent 保留结构化输出协议。 - `tools/result` 观察器只会在该次执行的权威最终工具结果成功后提交暂存值;Code Mode 子分派外层的 `run_code` 结果也包括在内。 - 单调工具防护会在捕获值后阻止后续调用,结构化输出执行的 `concludeTurn()` 标记则在结果提交后结束轮次。 diff --git a/packages/subagent/subagent-in-process-driver/src/structured.ts b/packages/subagent/subagent-in-process-driver/src/structured.ts index e5d8c67a79..1b764d7a4c 100644 --- a/packages/subagent/subagent-in-process-driver/src/structured.ts +++ b/packages/subagent/subagent-in-process-driver/src/structured.ts @@ -12,6 +12,7 @@ import type { Context } from '@deepseek-ai/cordis' import type { ToolSchema } from '@deepseek-ai/dsh-llm' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import type { ToolExecution, ToolRunContext } from '@deepseek-ai/dsh-tools' import { ToolArgsError, validateJsonSchemaValue, type ObjectJsonSchema } from '@deepseek-ai/dsh-tools' @@ -19,8 +20,8 @@ import { ToolArgsError, validateJsonSchemaValue, type ObjectJsonSchema } from '@ export const STRUCTURED_OUTPUT_TOOL = 'structured_output' /** - * The instruction registered as the child's trailing (order-190, the end of - * the tool-guidance band) scoped prompt section: the demand travels with the + * The instruction registered as the child's trailing scoped prompt section: + * the demand travels with the * tool, as ordinary prompt state of exactly one agent. */ export const STRUCTURED_OUTPUT_INSTRUCTION @@ -98,7 +99,7 @@ export function attachStructuredRuntime(childCtx: Context, schema: ObjectJsonSch childCtx.systemPrompt.section({ name: `tool:${STRUCTURED_OUTPUT_TOOL}`, - order: 190, + order: FIRST_PARTY_SECTION_ORDER.STRUCTURED_OUTPUT, text: STRUCTURED_OUTPUT_INSTRUCTION, }) diff --git a/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts b/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts index 6bb18f5f28..78a4068d95 100644 --- a/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts +++ b/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts @@ -5,6 +5,7 @@ import { SessionId } from '@deepseek-ai/dsh-session' import AgentLoop from '@deepseek-ai/dsh-agent-loop' import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit' import InvariantRegistry from '@deepseek-ai/dsh-invariants' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant' import * as AgentInvariant from '@deepseek-ai/dsh-agent/invariant' import * as AgentLoopInvariant from '@deepseek-ai/dsh-agent-loop/invariant' @@ -552,14 +553,18 @@ describe('in-process structured output', () => { toolCallResponse('c1', STRUCTURED_OUTPUT_TOOL, { answer: 7 }), ]) // A global tool sorts lexicographically after structured_output, while a - // global section above the 190 band follows the capture instruction. + // global section after the final-output slot follows the capture instruction. ctx.tools.register(defineContentToolFixture({ name: 'zz_probe', description: 'probe', parameters: {}, execute: () => Promise.resolve([{ type: 'text', text: 'x' }]), })) - ctx.systemPrompt.section({ name: 'after-band', order: 200, text: 'AFTER-BAND' }) + ctx.systemPrompt.section({ + name: 'after-band', + order: FIRST_PARTY_SECTION_ORDER.STRUCTURED_OUTPUT + 10, + text: 'AFTER-BAND', + }) const run = await ctx.subagents.start('spawn', structuredRequest(parent)) await run.result const request = adapter.requests[0]! diff --git a/packages/subagent/subagent/package.json b/packages/subagent/subagent/package.json index 293089b84d..02a2abaf02 100644 --- a/packages/subagent/subagent/package.json +++ b/packages/subagent/subagent/package.json @@ -48,6 +48,7 @@ "@deepseek-ai/dsh-sandbox": "workspace:^", "@deepseek-ai/dsh-sandbox-policy": "workspace:^", "@deepseek-ai/dsh-scope": "workspace:^", + "@deepseek-ai/dsh-system-prompt": "workspace:^", "@deepseek-ai/dsh-session": "workspace:^", "@deepseek-ai/dsh-session-persistence": "workspace:^", "@deepseek-ai/dsh-session-projection": "workspace:^", @@ -96,6 +97,7 @@ "@deepseek-ai/dsh-sandbox": "workspace:^", "@deepseek-ai/dsh-sandbox-policy": "workspace:^", "@deepseek-ai/dsh-scope": "workspace:^", + "@deepseek-ai/dsh-system-prompt": "workspace:^", "@deepseek-ai/dsh-session": "workspace:^", "@deepseek-ai/dsh-session-persistence": "workspace:^", "@deepseek-ai/dsh-session-projection": "workspace:^", diff --git a/packages/subagent/subagent/src/child-agent.ts b/packages/subagent/subagent/src/child-agent.ts index 22c9e77bf5..58709dee12 100644 --- a/packages/subagent/subagent/src/child-agent.ts +++ b/packages/subagent/subagent/src/child-agent.ts @@ -12,6 +12,7 @@ import type { Context } from '@deepseek-ai/cordis' import type { Agent, AgentOptions, CreateAgentOptions } from '@deepseek-ai/dsh-agent' import type { SandboxMode } from '@deepseek-ai/dsh-sandbox' import type { Session, SessionId } from '@deepseek-ai/dsh-session' +import { PERSONA_ORDER } from '@deepseek-ai/dsh-system-prompt' import type { ToolRestriction } from '@deepseek-ai/dsh-tools' // Type-only: make `ctx.get('sandboxPolicy')` / `ctx.get('approval')` resolve // to the policy services when composed — delegation consumes both @@ -204,7 +205,7 @@ export function applyChildComposition( // Order 120: after the sandbox:policy (110) and approval:policy (115) sentences. childCtx.systemPrompt.context({ name: 'subagent:delegation', order: 120, text: SUBAGENT_DELEGATION_CONTEXT }) if (composition.persona !== undefined) { - childCtx.systemPrompt.section({ name: 'deployment:persona', order: 0, text: composition.persona }) + childCtx.systemPrompt.section({ name: 'deployment:persona', order: PERSONA_ORDER, text: composition.persona }) } if (composition.toolFilter !== undefined) childCtx.tools.restrict(composition.toolFilter) } diff --git a/packages/subagent/subagent/tsconfig.json b/packages/subagent/subagent/tsconfig.json index b64e58a6cd..ae39b9a78b 100644 --- a/packages/subagent/subagent/tsconfig.json +++ b/packages/subagent/subagent/tsconfig.json @@ -26,6 +26,9 @@ { "path": "../../core/scope" }, + { + "path": "../../core/system-prompt" + }, { "path": "../../interaction/user-approval" }, diff --git a/packages/subagent/tool-subagent-report/src/index.ts b/packages/subagent/tool-subagent-report/src/index.ts index 85b139e641..fb484e8a2d 100644 --- a/packages/subagent/tool-subagent-report/src/index.ts +++ b/packages/subagent/tool-subagent-report/src/index.ts @@ -11,7 +11,7 @@ import z from '@deepseek-ai/schemastery' import type { Agent } from '@deepseek-ai/dsh-agent' import type { ContentBlock } from '@deepseek-ai/dsh-llm' import type { SubagentReportDelivery } from '@deepseek-ai/dsh-subagent' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { defineTool } from '@deepseek-ai/dsh-tools' export const name = 'tool-subagent-report' @@ -21,7 +21,7 @@ export const name = 'tool-subagent-report' export const inject = ['subagents', 'tools', 'systemPrompt'] /** Guidance order after every per-tool section a continuable child can carry. */ -const REPORT_SECTION_ORDER = 117 +const REPORT_SECTION_ORDER = FIRST_PARTY_SECTION_ORDER.TOOL_REPORT /** Config: how accepted reports are scheduled on the parent. */ export interface Config { diff --git a/packages/subagent/tool-subagent/src/index.ts b/packages/subagent/tool-subagent/src/index.ts index ceb04cced8..f71bdad511 100644 --- a/packages/subagent/tool-subagent/src/index.ts +++ b/packages/subagent/tool-subagent/src/index.ts @@ -23,7 +23,7 @@ import { } from '@deepseek-ai/dsh-subagent' import type { SubagentProvider, SubagentResult, SubagentRun } from '@deepseek-ai/dsh-subagent' import type { JobOutcome } from '@deepseek-ai/dsh-jobs' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { hasConfiguredLlmSelection, hasDelegationModelRequest, @@ -42,7 +42,7 @@ export const name = 'tool-subagent' export const inject = ['tools', 'subagents', 'systemPrompt'] /** Prompt order after bounded delegation policy and before child reporting. */ -const SUBAGENT_SECTION_ORDER = 116.5 +const SUBAGENT_SECTION_ORDER = FIRST_PARTY_SECTION_ORDER.TOOL_SUBAGENT /** Config: which registered provider this tool delegates to, plus child defaults. */ export interface Config { diff --git a/packages/terminal/tool-terminal/src/index.ts b/packages/terminal/tool-terminal/src/index.ts index b08938d5e9..8d063f288e 100644 --- a/packages/terminal/tool-terminal/src/index.ts +++ b/packages/terminal/tool-terminal/src/index.ts @@ -11,6 +11,7 @@ import type { ContentBlock } from '@deepseek-ai/dsh-llm' import { TerminalSessionId } from '@deepseek-ai/dsh-terminal' import type { TerminalSendResult, TerminalSessionId as TerminalSessionIdType, TerminalSignal } from '@deepseek-ai/dsh-terminal' import type {} from '@deepseek-ai/dsh-jobs' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import { defineTool } from '@deepseek-ai/dsh-tools' import type { ToolDefinition } from '@deepseek-ai/dsh-tools' import { boundTerminalText, renderList, renderRead, renderSend, renderSendRead, renderSpawn } from './render.ts' @@ -155,7 +156,7 @@ export function apply(ctx: Context, config: Config = {}): void { } ctx.systemPrompt.section({ name: 'tool:pty', - order: 106, + order: FIRST_PARTY_SECTION_ORDER.TOOL_PTY, text: 'Use a terminal session only when work needs persistent terminal state or interactive stdin; prefer shell/read/write/edit for bounded one-shot operations. Track every terminal session id and close sessions that no longer matter. An inferred_idle or timeout result does not prove the foreground command exited.', }) diff --git a/packages/web/tool-web/src/fetch.ts b/packages/web/tool-web/src/fetch.ts index 05637ea19f..d5274479c7 100644 --- a/packages/web/tool-web/src/fetch.ts +++ b/packages/web/tool-web/src/fetch.ts @@ -12,7 +12,7 @@ import { defineTool } from '@deepseek-ai/dsh-tools' import type { GenericCallView, JsonValue, ToolResult, WebFetchResultView } from '@deepseek-ai/dsh-tools' import type { WebFetchBody, WebFetchResult } from '@deepseek-ai/dsh-web' import { assertNever } from '@deepseek-ai/dsh-llm' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' /** * The shared HTML→markdown converter: turndown over its bundled domino DOM, @@ -429,7 +429,7 @@ export function presentFetchResult(args: { url: string }, result: ToolResult): W export function applyWebFetchTool(ctx: Context, timeoutMs: number, maxOutputChars: number): void { ctx.systemPrompt.section({ name: 'tool:web_fetch', - order: 111, + order: FIRST_PARTY_SECTION_ORDER.TOOL_WEB_FETCH, text: 'Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for example a result from web_search). It returns the page content decoded to text. Cite the URL as a markdown link when you use its content.', }) diff --git a/packages/web/tool-web/src/search.ts b/packages/web/tool-web/src/search.ts index f382582172..a17fcb6b6b 100644 --- a/packages/web/tool-web/src/search.ts +++ b/packages/web/tool-web/src/search.ts @@ -9,7 +9,7 @@ import type { Context } from '@deepseek-ai/cordis' import { defineTool } from '@deepseek-ai/dsh-tools' import type { GenericCallView, JsonValue, ToolResult, WebSearchResultView, WebSource } from '@deepseek-ai/dsh-tools' import type { WebSearchResult, WebSearchSource } from '@deepseek-ai/dsh-web' -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' /** * Default upper bound on returned sources (the `searchMaxResults` config). @@ -313,7 +313,7 @@ export function applyWebSearchTool( ): void { ctx.systemPrompt.section({ name: 'tool:web_search', - order: 110, + order: FIRST_PARTY_SECTION_ORDER.TOOL_WEB_SEARCH, text: fetchEnabled ? `Use the web_search tool to discover current information on the web. The required queries array accepts 1–${maxQueries} non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Follow up with web_fetch when you need the full content of a specific result, and cite the relevant URLs as markdown links.` : `Use the web_search tool to discover current information on the web. The required queries array accepts 1–${maxQueries} non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links.`, diff --git a/packages/workflow/tool-ralph/src/index.ts b/packages/workflow/tool-ralph/src/index.ts index 3e3e1b5943..cdce829ef6 100644 --- a/packages/workflow/tool-ralph/src/index.ts +++ b/packages/workflow/tool-ralph/src/index.ts @@ -13,8 +13,7 @@ import type { SubagentProvider } from '@deepseek-ai/dsh-subagent' import { defineTool } from '@deepseek-ai/dsh-tools' import type { ToolCallView, ToolResultView } from '@deepseek-ai/dsh-tools' import type { WorkflowResult, WorkflowRun } from '@deepseek-ai/dsh-workflow' -// Declaration merge only: makes ctx.systemPrompt visible for section registration. -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' export const name = 'tool-ralph' export const inject = ['tools', 'workflowEngine', 'subagents', 'systemPrompt'] @@ -406,7 +405,7 @@ export function apply(ctx: Context, config: Config): void { const resolved = resolveConfig(config) ctx.systemPrompt.section({ name: 'tool:ralph', - order: 116, + order: FIRST_PARTY_SECTION_ORDER.TOOL_RALPH, text: 'Use the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.', }) ctx.tools.register(defineTool({ diff --git a/packages/workflow/tool-workflow/src/index.ts b/packages/workflow/tool-workflow/src/index.ts index 7128fe4a95..1bdf8103f1 100644 --- a/packages/workflow/tool-workflow/src/index.ts +++ b/packages/workflow/tool-workflow/src/index.ts @@ -23,8 +23,7 @@ import type { ToolWorkflowAgentEndData, ToolWorkflowAgentStartData, ToolWorkflowRunEndData, ToolWorkflowRunStartData, } from './types.ts' -// Declaration merge only: makes ctx.systemPrompt visible for the section registration. -import type {} from '@deepseek-ai/dsh-system-prompt' +import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' export const name = 'tool-workflow' export const inject = ['tools', 'workflowEngine', 'systemPrompt'] @@ -211,7 +210,7 @@ export function apply(ctx: Context, config: Config): void { // lives in tool plugins as prompt sections, not in the deployment persona). ctx.systemPrompt.section({ name: `tool:${toolName}`, - order: 115.5, + order: FIRST_PARTY_SECTION_ORDER.TOOL_WORKFLOW, text: `Use the ${toolName} tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.`, }) ctx.tools.register(defineTool({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e75d16a06..85576e30a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8067,6 +8067,9 @@ importers: '@deepseek-ai/dsh-storage-domain': specifier: workspace:^ version: link:../../storage/storage-domain + '@deepseek-ai/dsh-system-prompt': + specifier: workspace:^ + version: link:../../core/system-prompt '@deepseek-ai/dsh-tools': specifier: workspace:^ version: link:../../core/tools diff --git a/snapshots/sdk/subagent-continuable-inheritance/system-prompt.1.expected.md b/snapshots/sdk/subagent-continuable-inheritance/system-prompt.1.expected.md index b198b48a12..c4dc7b6a5e 100644 --- a/snapshots/sdk/subagent-continuable-inheritance/system-prompt.1.expected.md +++ b/snapshots/sdk/subagent-continuable-inheritance/system-prompt.1.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/sdk/subagent-list-agents/system-prompt.1.expected.md b/snapshots/sdk/subagent-list-agents/system-prompt.1.expected.md index b198b48a12..c4dc7b6a5e 100644 --- a/snapshots/sdk/subagent-list-agents/system-prompt.1.expected.md +++ b/snapshots/sdk/subagent-list-agents/system-prompt.1.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/sdk/subagent-report/system-prompt.1.expected.md b/snapshots/sdk/subagent-report/system-prompt.1.expected.md index b198b48a12..c4dc7b6a5e 100644 --- a/snapshots/sdk/subagent-report/system-prompt.1.expected.md +++ b/snapshots/sdk/subagent-report/system-prompt.1.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/sdk/text-turn/system-prompt.expected.md b/snapshots/sdk/text-turn/system-prompt.expected.md index b70fd4112d..55f6c829b6 100644 --- a/snapshots/sdk/text-turn/system-prompt.expected.md +++ b/snapshots/sdk/text-turn/system-prompt.expected.md @@ -2,6 +2,8 @@ You are an AI agent powered by DeepSeek Harness. You are a coding agent powered by the deepseek-v4-flash model. Your working directory is {{cwd}}. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -12,8 +14,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/agent-instructions/system-prompt.expected.md b/snapshots/session/agent-instructions/system-prompt.expected.md index 7150bf2e6b..676b6532e3 100644 --- a/snapshots/session/agent-instructions/system-prompt.expected.md +++ b/snapshots/session/agent-instructions/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/both-mode-turn/system-prompt.expected.md b/snapshots/session/both-mode-turn/system-prompt.expected.md index b667c8dd6b..14d8892876 100644 --- a/snapshots/session/both-mode-turn/system-prompt.expected.md +++ b/snapshots/session/both-mode-turn/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/code-mode-read-image/system-prompt.expected.md b/snapshots/session/code-mode-read-image/system-prompt.expected.md index c9bad7d1fa..9593322100 100644 --- a/snapshots/session/code-mode-read-image/system-prompt.expected.md +++ b/snapshots/session/code-mode-read-image/system-prompt.expected.md @@ -7,6 +7,8 @@ Verify your work by running the code or tests. Keep answers brief and factual. `run_code` is the only tool you can call directly — a tool call naming any other tool fails. Reach every tool the SDK declares below from inside the program. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -17,8 +19,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/code-mode-turn/system-prompt.expected.md b/snapshots/session/code-mode-turn/system-prompt.expected.md index 7506ad8373..65908698b1 100644 --- a/snapshots/session/code-mode-turn/system-prompt.expected.md +++ b/snapshots/session/code-mode-turn/system-prompt.expected.md @@ -7,6 +7,8 @@ Verify your work by running the code or tests. Keep answers brief and factual. `run_code` is the only tool you can call directly — a tool call naming any other tool fails. Reach every tool the SDK declares below from inside the program. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -17,8 +19,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/cordis-inspect-jsdoc/system-prompt.expected.md b/snapshots/session/cordis-inspect-jsdoc/system-prompt.expected.md index b880f67453..db0ad128a8 100644 --- a/snapshots/session/cordis-inspect-jsdoc/system-prompt.expected.md +++ b/snapshots/session/cordis-inspect-jsdoc/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/fs-glob-sampling/system-prompt.expected.md b/snapshots/session/fs-glob-sampling/system-prompt.expected.md index 9b4698844c..8968f8848a 100644 --- a/snapshots/session/fs-glob-sampling/system-prompt.expected.md +++ b/snapshots/session/fs-glob-sampling/system-prompt.expected.md @@ -2,6 +2,8 @@ You are an AI agent powered by DeepSeek Harness. You are a concise snapshot agent working in {{cwd}}. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -12,8 +14,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. Use the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls. diff --git a/snapshots/session/lsp-definition/system-prompt.expected.md b/snapshots/session/lsp-definition/system-prompt.expected.md index b906b6f3c8..9a0ab0454b 100644 --- a/snapshots/session/lsp-definition/system-prompt.expected.md +++ b/snapshots/session/lsp-definition/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-pro model. Your working di Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/product-subagent-codex/system-prompt.expected.md b/snapshots/session/product-subagent-codex/system-prompt.expected.md index 545e903230..b11fb21674 100644 --- a/snapshots/session/product-subagent-codex/system-prompt.expected.md +++ b/snapshots/session/product-subagent-codex/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-pro model. Your working di Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/pty-tools-sandbox-backend/system-prompt.expected.md b/snapshots/session/pty-tools-sandbox-backend/system-prompt.expected.md index 06b614520c..bf79266c91 100644 --- a/snapshots/session/pty-tools-sandbox-backend/system-prompt.expected.md +++ b/snapshots/session/pty-tools-sandbox-backend/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-pro model. Your working di Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use a terminal session only when work needs persistent terminal state or interactive stdin; prefer shell/read/write/edit for bounded one-shot operations. Track every terminal session id and close sessions that no longer matter. An inferred_idle or timeout result does not prove the foreground command exited. diff --git a/snapshots/session/ralph-loop/system-prompt.1.expected.md b/snapshots/session/ralph-loop/system-prompt.1.expected.md index f9eb9268c2..61b1e078d3 100644 --- a/snapshots/session/ralph-loop/system-prompt.1.expected.md +++ b/snapshots/session/ralph-loop/system-prompt.1.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/ralph-loop/system-prompt.2.expected.md b/snapshots/session/ralph-loop/system-prompt.2.expected.md index f9eb9268c2..61b1e078d3 100644 --- a/snapshots/session/ralph-loop/system-prompt.2.expected.md +++ b/snapshots/session/ralph-loop/system-prompt.2.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/read-image/system-prompt.expected.md b/snapshots/session/read-image/system-prompt.expected.md index a0d3386eaa..039caca80e 100644 --- a/snapshots/session/read-image/system-prompt.expected.md +++ b/snapshots/session/read-image/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash-vision-exp model. Yo Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/session-query-spill/system-prompt.expected.md b/snapshots/session/session-query-spill/system-prompt.expected.md index 800356dccc..75f25bd445 100644 --- a/snapshots/session/session-query-spill/system-prompt.expected.md +++ b/snapshots/session/session-query-spill/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/text-turn/system-prompt.expected.md b/snapshots/session/text-turn/system-prompt.expected.md index 975b5a7baf..cc567a5291 100644 --- a/snapshots/session/text-turn/system-prompt.expected.md +++ b/snapshots/session/text-turn/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_search tool to discover current information on the web. The required queries array accepts 1–4 non-empty search queries; use a one-item array for a single search. It returns an optional answer plus a list of source URLs. Use the returned source snippets when available, and cite the relevant URLs as markdown links. diff --git a/snapshots/session/web-fetch/system-prompt.expected.md b/snapshots/session/web-fetch/system-prompt.expected.md index b70cc036d4..14009ee35f 100644 --- a/snapshots/session/web-fetch/system-prompt.expected.md +++ b/snapshots/session/web-fetch/system-prompt.expected.md @@ -5,6 +5,8 @@ You are a coding assistant powered by the deepseek-v4-pro model. Your working di Verify your work by running the code or tests. Keep answers brief and factual. +Check the [exit code: N] marker on every bash result; investigate failures before moving on. + Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files. Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes. @@ -15,8 +17,6 @@ Use the glob tool — not shell find — to discover files by path pattern. A pa Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context. -Check the [exit code: N] marker on every bash result; investigate failures before moving on. - Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job's work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering. Use the web_fetch tool to retrieve the content of a specific HTTP(S) URL (for example a result from web_search). It returns the page content decoded to text. Cite the URL as a markdown link when you use its content.