dsh-plugin-dev-kb 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.en.md +6 -6
  3. package/README.md +6 -6
  4. package/kb/INDEX.md +21 -5
  5. package/kb/README.md +11 -10
  6. package/kb/extra/AGENTS.md +4 -4
  7. package/kb/extra/cookbook/adding-a-remote-api.md +197 -0
  8. package/kb/extra/cookbook/adding-a-remote-api.zh.md +197 -0
  9. package/kb/extra/cookbook/adding-a-vendored-package.md +2 -2
  10. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +2 -2
  11. package/kb/extra/deepseek-llm-api-wire-extensions.md +163 -0
  12. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +163 -0
  13. package/kb/extra/development.md +8 -14
  14. package/kb/extra/development.zh.md +8 -14
  15. package/kb/extra/event-producer-consumer.md +55 -48
  16. package/kb/extra/event-producer-consumer.zh.md +58 -51
  17. package/kb/extra/glossary.md +1 -1
  18. package/kb/extra/glossary.zh.md +1 -1
  19. package/kb/extra/graph-atlas.md +0 -2
  20. package/kb/extra/graph-atlas.zh.md +0 -2
  21. package/kb/extra/i18n/README.md +4 -4
  22. package/kb/extra/i18n/README.zh.md +4 -4
  23. package/kb/extra/i18n/style-samples.md +2 -2
  24. package/kb/extra/module-graph.md +646 -926
  25. package/kb/extra/module-graph.zh.md +648 -928
  26. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +2 -2
  27. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +2 -2
  28. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +2 -2
  29. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +2 -2
  30. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +2 -2
  31. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +2 -2
  32. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +1 -1
  33. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +1 -1
  34. package/kb/extra/rescope.md +2 -2
  35. package/kb/extra/rescope.zh.md +2 -2
  36. package/kb/extra/subsystems/agent-team.md +28 -4
  37. package/kb/extra/subsystems/agent-team.zh.md +28 -4
  38. package/kb/extra/subsystems/attachment.md +168 -7
  39. package/kb/extra/subsystems/attachment.zh.md +168 -7
  40. package/kb/extra/subsystems/extensions.md +18 -0
  41. package/kb/extra/subsystems/extensions.zh.md +18 -0
  42. package/kb/extra/subsystems/feedback.md +4 -4
  43. package/kb/extra/subsystems/feedback.zh.md +4 -4
  44. package/kb/extra/subsystems/todo.md +32 -0
  45. package/kb/extra/subsystems/todo.zh.md +32 -0
  46. package/kb/extra/subsystems/webhook.md +70 -0
  47. package/kb/extra/subsystems/webhook.zh.md +70 -0
  48. package/kb/extra/testing.md +15 -10
  49. package/kb/extra/testing.zh.md +13 -8
  50. package/kb/extra/web-styling.md +4 -0
  51. package/kb/extra/web-styling.zh.md +4 -0
  52. package/kb/meta/search-index.json +309 -177
  53. package/kb/meta/site-pages.txt +183 -167
  54. package/kb/meta/source.json +5 -5
  55. package/kb/meta/topics.md +14 -6
  56. package/kb/site/develop/basic/publish.md +2 -2
  57. package/kb/site/develop/basic/tool.md +1 -1
  58. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +5 -4
  59. package/kb/site/develop/framework/events.md +1 -1
  60. package/kb/site/develop/practice/dynamic-cordis.md +17 -0
  61. package/kb/site/develop/practice/llm-adapter.md +4 -3
  62. package/kb/site/en/develop/basic/publish.md +2 -2
  63. package/kb/site/en/develop/basic/tool.md +1 -1
  64. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +5 -4
  65. package/kb/site/en/develop/framework/events.md +1 -1
  66. package/kb/site/en/develop/practice/dynamic-cordis.md +17 -0
  67. package/kb/site/en/develop/practice/llm-adapter.md +4 -3
  68. package/kb/site/en/guide/github-review.md +104 -0
  69. package/kb/site/en/guide/mcp-memory.md +103 -0
  70. package/kb/site/en/guide/network-proxy.md +87 -0
  71. package/kb/site/en/guide/providers.md +70 -17
  72. package/kb/site/en/guide/python-sdk.md +80 -34
  73. package/kb/site/en/guide/schedule.md +23 -0
  74. package/kb/site/en/reference/agent-lifecycle.md +6 -4
  75. package/kb/{extra → site/en/reference}/api-gateway.md +12 -10
  76. package/kb/site/en/reference/capability-seams.md +128 -73
  77. package/kb/site/en/reference/config-catalog.md +481 -360
  78. package/kb/site/en/reference/cookbook/adding-a-package.md +3 -4
  79. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +12 -10
  80. package/kb/site/en/reference/cookbook/adding-a-tool.md +11 -4
  81. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +1 -1
  82. package/kb/site/en/reference/cookbook/extension-cookbook.md +20 -17
  83. package/kb/site/en/reference/cordis-api/inherited.md +1 -1
  84. package/kb/site/en/reference/cordis-primer.md +2 -1
  85. package/kb/site/en/reference/index.md +30 -11
  86. package/kb/site/en/reference/persistence-catalog.md +148 -80
  87. package/kb/site/en/reference/subsystems/approval.md +10 -10
  88. package/kb/site/en/reference/subsystems/client-modules.md +58 -16
  89. package/kb/site/en/reference/subsystems/code-runtime.md +10 -6
  90. package/kb/site/en/reference/subsystems/commands.md +25 -16
  91. package/kb/site/en/reference/subsystems/compaction.md +11 -11
  92. package/kb/site/en/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +50 -24
  93. package/kb/site/en/reference/subsystems/core.md +156 -17
  94. package/kb/site/en/reference/subsystems/credentials.md +44 -3
  95. package/kb/site/en/reference/subsystems/filesystem.md +12 -2
  96. package/kb/site/en/reference/subsystems/goal.md +1 -1
  97. package/kb/site/en/reference/subsystems/index.md +7 -2
  98. package/kb/site/en/reference/subsystems/jobs.md +1 -1
  99. package/kb/site/en/reference/subsystems/llm-streaming.md +154 -12
  100. package/kb/site/en/reference/subsystems/permission-presets.md +6 -6
  101. package/kb/site/en/reference/subsystems/persistence.md +185 -175
  102. package/kb/site/en/reference/subsystems/plan.md +2 -2
  103. package/kb/site/en/reference/subsystems/sandbox.md +2 -0
  104. package/kb/site/en/reference/subsystems/schedule.md +9 -3
  105. package/kb/site/en/reference/subsystems/session-projection.md +115 -48
  106. package/kb/site/en/reference/subsystems/session-query.md +28 -14
  107. package/kb/site/en/reference/subsystems/session-reference.md +53 -8
  108. package/kb/site/en/reference/subsystems/session-telemetry.md +8 -8
  109. package/kb/site/en/reference/subsystems/session-title.md +6 -6
  110. package/kb/site/en/reference/subsystems/session.md +401 -99
  111. package/kb/site/en/reference/subsystems/settings.md +101 -6
  112. package/kb/site/en/reference/subsystems/skills.md +23 -0
  113. package/kb/site/en/reference/subsystems/slots.md +178 -0
  114. package/kb/site/en/reference/subsystems/spill.md +2 -2
  115. package/kb/site/en/reference/subsystems/storage.md +34 -3
  116. package/kb/site/en/reference/subsystems/subagent.md +122 -109
  117. package/kb/site/en/reference/subsystems/system-prompt.md +17 -4
  118. package/kb/site/en/reference/subsystems/token-meter.md +27 -12
  119. package/kb/site/en/reference/subsystems/tools.md +39 -39
  120. package/kb/site/en/reference/subsystems/typert.md +62 -55
  121. package/kb/site/en/reference/subsystems/user-questions.md +33 -33
  122. package/kb/site/en/reference/subsystems/web-client.md +98 -0
  123. package/kb/site/en/reference/subsystems/web-server.md +11 -5
  124. package/kb/site/en/reference/subsystems/web.md +7 -1
  125. package/kb/site/en/reference/subsystems/workspace.md +102 -9
  126. package/kb/site/en/reference/tool-catalog.md +86 -82
  127. package/kb/site/en/reference/tool-execution-pipeline.md +1 -1
  128. package/kb/site/guide/github-review.md +104 -0
  129. package/kb/site/guide/mcp-memory.md +103 -0
  130. package/kb/site/guide/network-proxy.md +87 -0
  131. package/kb/site/guide/providers.md +70 -17
  132. package/kb/site/guide/python-sdk.md +87 -41
  133. package/kb/site/guide/schedule.md +23 -0
  134. package/kb/site/reference/agent-lifecycle.md +6 -4
  135. package/kb/{extra/api-gateway.zh.md → site/reference/api-gateway.md} +12 -10
  136. package/kb/site/reference/capability-seams.md +128 -73
  137. package/kb/site/reference/config-catalog.md +481 -360
  138. package/kb/site/reference/cookbook/adding-a-package.md +3 -4
  139. package/kb/site/reference/cookbook/adding-a-settings-card.md +12 -10
  140. package/kb/site/reference/cookbook/adding-a-tool.md +11 -4
  141. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +1 -1
  142. package/kb/site/reference/cookbook/extension-cookbook.md +20 -17
  143. package/kb/site/reference/cordis-api/inherited.md +1 -1
  144. package/kb/site/reference/cordis-primer.md +2 -1
  145. package/kb/site/reference/index.md +30 -11
  146. package/kb/site/reference/persistence-catalog.md +148 -80
  147. package/kb/site/reference/subsystems/approval.md +10 -10
  148. package/kb/site/reference/subsystems/client-modules.md +58 -16
  149. package/kb/site/reference/subsystems/code-runtime.md +10 -6
  150. package/kb/site/reference/subsystems/commands.md +25 -16
  151. package/kb/site/reference/subsystems/compaction.md +11 -11
  152. package/kb/site/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +50 -24
  153. package/kb/site/reference/subsystems/core.md +156 -17
  154. package/kb/site/reference/subsystems/credentials.md +44 -3
  155. package/kb/site/reference/subsystems/filesystem.md +12 -2
  156. package/kb/site/reference/subsystems/goal.md +1 -1
  157. package/kb/site/reference/subsystems/index.md +7 -2
  158. package/kb/site/reference/subsystems/jobs.md +1 -1
  159. package/kb/site/reference/subsystems/llm-streaming.md +154 -12
  160. package/kb/site/reference/subsystems/permission-presets.md +5 -5
  161. package/kb/site/reference/subsystems/persistence.md +184 -174
  162. package/kb/site/reference/subsystems/plan.md +2 -2
  163. package/kb/site/reference/subsystems/schedule.md +9 -3
  164. package/kb/site/reference/subsystems/session-projection.md +115 -48
  165. package/kb/site/reference/subsystems/session-query.md +28 -14
  166. package/kb/site/reference/subsystems/session-reference.md +53 -8
  167. package/kb/site/reference/subsystems/session-telemetry.md +8 -8
  168. package/kb/site/reference/subsystems/session-title.md +6 -6
  169. package/kb/site/reference/subsystems/session.md +401 -99
  170. package/kb/site/reference/subsystems/settings.md +101 -6
  171. package/kb/site/reference/subsystems/skills.md +23 -0
  172. package/kb/site/reference/subsystems/slots.md +178 -0
  173. package/kb/site/reference/subsystems/spill.md +2 -2
  174. package/kb/site/reference/subsystems/storage.md +34 -3
  175. package/kb/site/reference/subsystems/subagent.md +122 -109
  176. package/kb/site/reference/subsystems/system-prompt.md +17 -4
  177. package/kb/site/reference/subsystems/token-meter.md +27 -12
  178. package/kb/site/reference/subsystems/tools.md +39 -39
  179. package/kb/site/reference/subsystems/typert.md +62 -55
  180. package/kb/site/reference/subsystems/user-questions.md +33 -33
  181. package/kb/site/reference/subsystems/web-client.md +98 -0
  182. package/kb/site/reference/subsystems/web-server.md +11 -5
  183. package/kb/site/reference/subsystems/web.md +7 -1
  184. package/kb/site/reference/subsystems/workspace.md +102 -9
  185. package/kb/site/reference/tool-catalog.md +85 -81
  186. package/kb/site/reference/tool-execution-pipeline.md +1 -1
  187. package/package.json +2 -2
  188. package/skills/dsh-plugin-dev-kb.md +8 -6
@@ -10,7 +10,7 @@ Two integration mistakes broke ACP despite full unit coverage: a default export
10
10
 
11
11
  ## Summary
12
12
 
13
- The ACP server (`examples/acp-agent`, `@deepseek-ai/dsh-acp`) crashed the instant a real editor (Zed) connected: the first `session/new` request returned `Internal error: cannot get property "agents" without inject`, and `session/load` returned the same for `sessionPersistence`. The bridge was completely non-functional in production despite 178 green unit tests and 100% line coverage. Two independent bugs were hiding behind the same error string, and the test suite missed both for the same reason: every test mounted the plugin through a path that did not exercise how it actually loads or how its services actually resolve.
13
+ The ACP server (`dsh --profile acp`, `@deepseek-ai/dsh-acp`) crashed the instant a real editor (Zed) connected: the first `session/new` request returned `Internal error: cannot get property "agents" without inject`, and `session/load` returned the same for `sessionPersistence`. The bridge was completely non-functional in production despite 178 green unit tests and 100% line coverage. Two independent bugs were hiding behind the same error string, and the test suite missed both for the same reason: every test mounted the plugin through a path that did not exercise how it actually loads or how its services actually resolve.
14
14
 
15
15
  ## Impact
16
16
 
@@ -101,7 +101,7 @@ Both bugs share one root process gap: **no test exercised the plugin through its
101
101
 
102
102
  - **Removed `export default apply`** (`packages/acp/acp/src/index.ts`) — the Bug #1 fix.
103
103
  - **`AgentLoop.resume` reads `this.ctx.get('sessionPersistence')`** (`packages/core/agent-loop/src/index.ts`) — the Bug #2 fix, with a comment explaining the shadow-walk trap.
104
- - **No-key `session/new` e2e over real stdio** (`examples/acp-agent/tests/acp.e2e.ts`): boots the example as a subprocess through the real Loader and asserts `session/new` resolves. This fails loudly on Bug #1 with no API key. Verified it fails when `export default apply` is restored.
104
+ - **No-key `session/new` e2e over real stdio** (`apps/cli/tests/profiles/acp/tests/acp.e2e.ts`): boots the profile as a subprocess through the real Loader and asserts `session/new` resolves. This fails loudly on Bug #1 with no API key. Verified it fails when `export default apply` is restored.
105
105
  - **`TSX_TSCONFIG_PATH` in the e2e spawn**: the subprocess runs from a temp cwd, where tsx cannot find the repo-root tsconfig `paths` map by searching upward — so dsh-* imports silently fell back to built `lib/`. Pointing tsx at the repo tsconfig makes resolution cwd-independent and ensures the test runs *source*, not a possibly-stale build.
106
106
  - **[docs/testing.md](../testing.md) rule**: "test the real entry path", line coverage is not behavior coverage — codifies the lesson for every future plugin.
107
107
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  ## 概述
12
12
 
13
- ACP 服务器(`examples/acp-agent`、`@deepseek-ai/dsh-acp`)在真实编辑器(Zed)连接的瞬间崩溃:第一个 `session/new` 请求返回 `Internal error: cannot get property "agents" without inject`,`session/load` 对 `sessionPersistence` 返回同样的错误。尽管有 178 个绿色单元测试和 100% 行覆盖率,bridge 在生产环境中完全无法工作。两个独立的 bug 隐藏在同一个错误字符串背后,测试套件之所以两个都没捕获,原因也相同:所有测试都通过一条不会触及插件真实加载方式和服务真实解析方式的路径来挂载插件。
13
+ ACP 服务器(`dsh --profile acp`、`@deepseek-ai/dsh-acp`)在真实编辑器(Zed)连接的瞬间崩溃:第一个 `session/new` 请求返回 `Internal error: cannot get property "agents" without inject`,`session/load` 对 `sessionPersistence` 返回同样的错误。尽管有 178 个绿色单元测试和 100% 行覆盖率,bridge 在生产环境中完全无法工作。两个独立的 bug 隐藏在同一个错误字符串背后,测试套件之所以两个都没捕获,原因也相同:所有测试都通过一条不会触及插件真实加载方式和服务真实解析方式的路径来挂载插件。
14
14
 
15
15
  ## 影响
16
16
 
@@ -101,7 +101,7 @@ if (!ctx.fiber.runtime) return ctx.reflect.get(prop, false) // ← direct glob
101
101
 
102
102
  - **删除 `export default apply`**(`packages/acp/acp/src/index.ts`)——Bug #1 的修复。
103
103
  - **`AgentLoop.resume` 使用 `this.ctx.get('sessionPersistence')`**(`packages/core/agent-loop/src/index.ts`)——Bug #2 的修复,附注释说明 shadow 遍历陷阱。
104
- - **无需 key 的 `session/new` e2e,通过真实 stdio 运行**(`examples/acp-agent/tests/acp.e2e.ts`):以子进程方式通过真实 Loader 启动示例,并断言 `session/new` 正常返回。无需 API key 即可明确暴露 Bug #1。已验证恢复 `export default apply` 时测试失败。
104
+ - **无需 key 的 `session/new` e2e,通过真实 stdio 运行**(`apps/cli/tests/profiles/acp/tests/acp.e2e.ts`):以子进程方式通过真实 Loader 启动 profile,并断言 `session/new` 正常返回。无需 API key 即可明确暴露 Bug #1。已验证恢复 `export default apply` 时测试失败。
105
105
  - **e2e spawn 中设置 `TSX_TSCONFIG_PATH`**:子进程从临时 cwd 运行,tsx 无法通过向上搜索找到仓库根的 tsconfig `paths` 映射——因此 dsh-* 的 import 静默回退到已构建的 `lib/`。将 tsx 指向仓库 tsconfig 使解析不依赖 cwd,确保测试运行的是*源码*而非可能陈旧的构建产物。
106
106
  - **[docs/testing.md](../testing.zh.md) 规则**:「测试真实入口路径」,行覆盖率不等于行为覆盖率——将这一教训编纂为所有未来插件的规则。
107
107
 
@@ -36,9 +36,9 @@ The snapshot framework treated any deterministic transcript as valid behavior. H
36
36
  ## Guardrails added
37
37
 
38
38
  - Filesystem scenarios boot `fs.cordis.yml`, an explicit fixed full-access overlay with a paired replay config and its own request-header class.
39
- - [`AGENTS.md`](../../AGENTS.md) and the [Cordis primer](../cordis-primer.md#loader-configuration) state that `!!js` is valid only under plugin `config` and conditional composition uses overlays.
39
+ - [`AGENTS.md`](../../AGENTS.md) and the [Cordis primer](../cordis-primer.md#loader-configuration) state that `!!js` is valid under plugin `config` and entry `disabled`; other entry metadata stays literal, so conditional composition uses overlays.
40
40
  - `verify-cordis-config` parses repository Cordis YAML and rejects expression nodes in Loader entry metadata, including include patches and inserted entries.
41
- - `dsh-acp-snapshot` rejects structured `UNKNOWN_TOOL` results in fresh runs and committed session fixtures before they can be committed as expected outputs.
41
+ - `dsh-session-snapshot` rejects structured `UNKNOWN_TOOL` results in fresh runs and committed session fixtures before they can be committed as expected outputs.
42
42
 
43
43
  ## Lessons
44
44
 
@@ -36,9 +36,9 @@ Cordis Include 将每个 `!!js` 标量解析为一个表达式对象。Loader
36
36
  ## 已添加的防护措施
37
37
 
38
38
  - 文件系统场景启动 `fs.cordis.yml`:一个显式的固定全权限 overlay,配有对应的回放配置和独立的 request-header 类。
39
- - [`AGENTS.md`](../../AGENTS.md) 与 [Cordis 入门](../cordis-primer.zh.md#loader-configuration)明确说明 `!!js` 仅在插件 `config` 内有效,条件式组合应使用 overlay。
39
+ - [`AGENTS.md`](../../AGENTS.md) 与 [Cordis 入门](../cordis-primer.zh.md#loader-configuration)明确说明 `!!js` 在插件 `config` 与配置项 `disabled` 内有效;其他配置项元数据保持字面量,因此条件式组合使用 overlay。
40
40
  - `verify-cordis-config` 解析仓库中的 Cordis YAML,拒绝 Loader 配置项元数据中的表达式节点(包括 include patch 和插入的配置项)。
41
- - `dsh-acp-snapshot` 在全新运行和已提交的会话 fixture 中拒绝结构化的 `UNKNOWN_TOOL` 结果,防止其被提交为预期输出。
41
+ - `dsh-session-snapshot` 在全新运行和已提交的会话 fixture 中拒绝结构化的 `UNKNOWN_TOOL` 结果,防止其被提交为预期输出。
42
42
 
43
43
  ## 教训
44
44
 
@@ -39,8 +39,8 @@ Background process semantics were also bypassed with shell `&`, so job identity,
39
39
 
40
40
  ## Guardrails added
41
41
 
42
- - The Web launcher publishes the canonical loopback URL and actual production/development mode in the logged `app:web-surface` prompt section and managed `$DSH_WEB_URL`/`$DSH_WEB_MODE` environment.
43
- - Production guidance requires rebuilding artifacts and verifying the existing URL after refresh. Development guidance explains that `dsh web --dev` mounts only the HMR receiver; `pnpm run dev:web` in the same checkout must also rebuild client-plugin bundles, while shell and plain-package changes still require refresh.
42
+ - The Web launcher publishes the canonical loopback URL in the logged `app:web-surface` prompt section and the managed `$DSH_WEB_URL` environment.
43
+ - Production guidance requires rebuilding artifacts and verifying the existing URL after refresh. Development guidance explains that the HMR receiver is always on; `pnpm run dev:web` in the same checkout rebuilds client-plugin bundles for refresh-free reload, while shell and plain-package changes still require refresh.
44
44
  - `apps/web` standalone Vite serve mode rejects during configuration. Its subprocess test proves natural exit and instruments `Server.listen()` so a transient bind cannot pass unnoticed.
45
45
  - Layered real-path tests cover the CLI request, exact production/development prompts, shell runtime facts, same-port static replacement, source watcher rebuild, host stat polling, and browser HMR under an unchanged page identity.
46
46
  - PR evidence preserves screenshots from the original 3081 session and a real-model before/after GUI run; external browser, HTTP, process, and session-log observations carry acceptance.
@@ -39,8 +39,8 @@ agent 还通过 shell `&` 绕过了后台进程语义,因此任务身份、完
39
39
 
40
40
  ## 已添加的防护措施
41
41
 
42
- - Web 启动器在记录到日志的 `app:web-surface` 提示词区段,以及受管的 `$DSH_WEB_URL`/`$DSH_WEB_MODE` 环境变量中,发布规范环回 URL 和实际的生产/开发模式。
43
- - 生产模式指南要求重新构建产物,并在刷新后验证既有 URL。开发模式指南说明,`dsh web --dev` 只挂载 HMR 接收端;同一源码检出目录中的 `pnpm run dev:web` 还必须重新构建客户端插件 bundle,而 Web shell 和普通包的改动仍然需要刷新页面。
42
+ - Web 启动器在记录到日志的 `app:web-surface` 提示词区段和受管的 `$DSH_WEB_URL` 环境变量中发布规范环回 URL
43
+ - 生产指南要求重新构建产物,并在刷新后验证既有 URL。开发指南说明 HMR 接收端始终开启;同一源码检出目录中的 `pnpm run dev:web` 会重新构建客户端插件 bundle,实现免刷新的重载,而 Web shell 和普通包的改动仍然需要刷新页面。
44
44
  - `apps/web` 的独立 Vite 服务模式会在配置阶段拒绝启动。其子进程测试验证进程自然退出,并插桩 `Server.listen()`,确保短暂绑定端口也不会漏检。
45
45
  - 分层的真实路径测试覆盖 CLI(命令行界面)请求、精确的生产/开发模式提示词、shell 运行时事实、同端口静态产物替换、源码 watcher 重建、宿主 stat 轮询,以及页面 identity 不变的浏览器 HMR。
46
46
  - PR(Pull Request)证据保留了原始 3081 会话的截图,以及真实模型驱动的 GUI 修改前后对比;验收以外部浏览器、HTTP、进程和会话日志的观测结果为准。
@@ -45,7 +45,7 @@ Stderr remains an in-band attribution channel. A confined child can deliberately
45
45
  - [`dsh-bash-sandbox`](../../packages/shell/bash-sandbox/) directly spawns the provider argv, so a pre-start rejection uses the spawn-error channel instead of localized shell diagnostics. Settled foreground and background execution share one evidence-returning classifier; fatal evidence outranks denial, and foreground errors report the matched fatal line without changing captured stderr.
46
46
  - [`dsh-tool-fs-search`](../../packages/fs/tool-fs-search/) uses packaged ripgrep through `ctx.subprocess` and remains outside the sandboxed bash seam.
47
47
  - The native-boundary regression cases live in [`partial-landlock.spec.ts`](../../packages/shell/bash-sandbox/tests/partial-landlock.spec.ts), including informational notices, fatal evidence, and foreground/background classification.
48
- - The assembled product path is pinned by the [`partial-landlock` snapshot composition](../../examples/acp-agent/partial-landlock.cordis.snapshot.yml), independently of filesystem-search implementation choices.
48
+ - The assembled product path is pinned by the [`partial-landlock` snapshot composition](../../snapshots/session/partial-landlock-child-failure/cordis.snapshot.yml), independently of filesystem-search implementation choices.
49
49
 
50
50
  ## Lessons
51
51
 
@@ -45,7 +45,7 @@ stderr 仍是带内归因通道。受限子进程可以故意复现 runner 的
45
45
  - [`dsh-bash-sandbox`](../../packages/shell/bash-sandbox/) 直接 spawn 提供方 argv,因此启动前遭拒时使用 spawn 错误通道,而非本地化的 shell 诊断。已结算的前台与后台执行共用一个返回证据的分类器;致命证据优先于拒绝,前台错误会报告匹配到的致命行,同时保持捕获的 stderr 不变。
46
46
  - [`dsh-tool-fs-search`](../../packages/fs/tool-fs-search/) 通过 `ctx.subprocess` 运行打包的 ripgrep,并继续位于沙箱化 bash seam 之外。
47
47
  - 原生边界回归用例位于 [`partial-landlock.spec.ts`](../../packages/shell/bash-sandbox/tests/partial-landlock.spec.ts),包括信息性通知、致命证据和前台/后台分类。
48
- - 组装后的产品路径由 [`partial-landlock` 快照组合](../../examples/acp-agent/partial-landlock.cordis.snapshot.yml)固定,独立于文件系统搜索的实现选择。
48
+ - 组装后的产品路径由 [`partial-landlock` 快照组合](../../snapshots/session/partial-landlock-child-failure/cordis.snapshot.yml)固定,独立于文件系统搜索的实现选择。
49
49
 
50
50
  ## 教训
51
51
 
@@ -6,7 +6,7 @@ The Cordis framework and its foundation libraries are vendored under [`vendor/`]
6
6
 
7
7
  ## Name mapping
8
8
 
9
- | Directory | Upstream name | Published name | Version | Role |
9
+ | Directory | Upstream name | Published name | Upstream version | Role |
10
10
  |---|---|---|---|---|
11
11
  | `vendor/cordis/` | `cordis` | `@deepseek-ai/cordis` | 4.0.0-rc.7 | Framework core: `Context`, `Service`, `Fiber`, events |
12
12
  | `vendor/cosmokit/` | `cosmokit` | `@deepseek-ai/cosmokit` | 1.8.1 | Shared utilities the framework and Schemastery build on |
@@ -22,7 +22,7 @@ Subpath exports keep their path: `@cordisjs/plugin-loader/repository` becomes `@
22
22
 
23
23
  ## What the rename does not touch
24
24
 
25
- - **Directory names and versions.** `vendor/hmr/` stays `vendor/hmr/`, and every package keeps the upstream version its manifest table row records, so the vendored tree still reads as an upstream snapshot.
25
+ - **Directory names and upstream source versions.** `vendor/hmr/` stays `vendor/hmr/`, and the table records the upstream version of the pinned source snapshot, so the manifest reads as an upstream snapshot; the vendored `package.json`'s own `version` field is the harness's released manifest version, which `pnpm run release:vendor` bumps and a re-sync restores to the upstream version.
26
26
  - **Dependency ranges.** A dependency entry changes its key, never its range: `"cordis": "^4.0.0-rc.7"` becomes `"@deepseek-ai/cordis": "^4.0.0-rc.7"`. `linkWorkspacePackages` resolves those preserved ranges to the pinned workspaces.
27
27
  - **The Loader's `cordis:` builtin prefix.** `cordis:include` and `cordis:group` are a protocol prefix, not a package name.
28
28
  - **The `cordis.yml` configuration family**, including `*.cordis.yml`, `*.cordis.snapshot.yml`, and `cordis.patch.yml`.
@@ -6,7 +6,7 @@ Cordis 框架及其基础库以源码形式 vendored 在 [`vendor/`](../vendor/R
6
6
 
7
7
  ## 名字映射
8
8
 
9
- | 目录 | 上游名 | 发布名 | 版本 | 角色 |
9
+ | 目录 | 上游名 | 发布名 | 上游版本 | 角色 |
10
10
  |---|---|---|---|---|
11
11
  | `vendor/cordis/` | `cordis` | `@deepseek-ai/cordis` | 4.0.0-rc.7 | 框架核心:`Context`、`Service`、`Fiber`、事件 |
12
12
  | `vendor/cosmokit/` | `cosmokit` | `@deepseek-ai/cosmokit` | 1.8.1 | 框架与 Schemastery 共用的基础工具 |
@@ -22,7 +22,7 @@ Cordis 框架及其基础库以源码形式 vendored 在 [`vendor/`](../vendor/R
22
22
 
23
23
  ## 改名不碰什么
24
24
 
25
- - **目录名与版本号。** `vendor/hmr/` 仍是 `vendor/hmr/`,每个包保留清单表那行记录的上游版本,所以 vendored 树依旧读作一份上游快照。
25
+ - **目录名与上游源码版本。** `vendor/hmr/` 仍是 `vendor/hmr/`,清单表记录的是所钉住源码快照的上游版本,因此清单读作一份上游快照;而每个 vendored 包 `package.json` 自身的 `version` 字段是 harness 发布的清单版本,`pnpm run release:vendor` 会提升它,重新 sync 时会恢复成上游版本。
26
26
  - **依赖 range。** 依赖条目只换键、不换范围:`"cordis": "^4.0.0-rc.7"` 变成 `"@deepseek-ai/cordis": "^4.0.0-rc.7"`;`linkWorkspacePackages` 靠这些保留下来的范围把它们解析到固定的 workspace。
27
27
  - **Loader 的 `cordis:` 内建前缀。** `cordis:include`、`cordis:group` 是协议前缀,不是包名。
28
28
  - **`cordis.yml` 配置文件家族**,包括 `*.cordis.yml`、`*.cordis.snapshot.yml`、`cordis.patch.yml`。
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [中文](agent-team.zh.md)
4
4
 
5
- Types shared by the experimental implicit-root Team domain, model tools, and host adapters. The [Agent Teams Agent Note](../../.agents/notes/implemented/feature/2026-08-05-agent-teams.md) owns identity, mailbox, task, and shared-checkout decisions; this page records the literal durable forms from [`packages/experimental/agent-team/src/types.ts`](../../packages/experimental/agent-team/src/types.ts).
5
+ Types shared by the experimental implicit-root Team domain, model tools, and host adapters. The [Agent Teams Agent Note](../../.agents/notes/implemented/feature/2026-08-05-agent-teams.md) owns identity, mailbox, task, and shared-checkout decisions; the [Team Steer messaging Agent Note](../../.agents/notes/implemented/simplification/2026-08-30-team-send-message-steer.md) owns message scheduling; this page records the literal durable forms from [`packages/experimental/agent-team/src/types.ts`](../../packages/experimental/agent-team/src/types.ts).
6
6
 
7
7
  ## Identity and roster
8
8
 
@@ -34,11 +34,12 @@ interface TeamMessageSnapshot {
34
34
  readonly senderId: SessionId
35
35
  readonly senderName: string
36
36
  readonly targetId: SessionId
37
- readonly delivery: 'quiet' | 'wakeup'
38
37
  readonly content: ContentBlock[]
39
38
  }
40
39
  ```
41
40
 
41
+ Every message attempts Steer delivery. A running target receives it at the nearest step boundary, an idle target starts a turn, and an inactive teammate cold-resumes. Scheduling is not stored in the durable record because callers cannot select another mode.
42
+
42
43
  The target Session keeps message identity and sender attribution on both the pending inbox item and the eventual user message. Folding that source across inbox and history is the target-side de-duplication key; the model-visible framing repeats the id and sender.
43
44
 
44
45
  ```ts type-equiv
@@ -74,7 +75,7 @@ interface TeamTaskSnapshot {
74
75
 
75
76
  ## Replay
76
77
 
77
- `foldTeam()` replays one root Session into the roster, task board, and queued-minus-delivered mailbox that every Team operation reads. It selects records by `TeamId`, so events inherited by an ordinary fork retain the ancestor id and never enter the new root's state. Session event `seq` and `time` remain the ordering and timing record; Team snapshots do not duplicate them. Roster and task reads reach callers as views that add owner name, readiness, and write-scope warnings, while pending mail stays internal to delivery and recovery. The package [README](../../packages/experimental/agent-team/README.md) owns operation, authorization, recovery, and limit behavior.
78
+ `foldTeam()` replays one root Session into the roster, task board, and queued-minus-delivered mailbox that every Team operation reads. It selects records by `TeamId`, so events inherited by an ordinary fork retain the ancestor id and never enter the new root's state. Session event `seq` and `time` remain the ordering and timing record; Team snapshots do not duplicate them. Roster and task reads reach callers as views; pending mail stays internal to delivery and recovery. The package [README](../../packages/experimental/agent-team/README.md) owns operation, authorization, recovery, and limit behavior.
78
79
 
79
80
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
80
81
 
@@ -116,7 +117,7 @@ async spawnTeammate(caller: Agent, request: SpawnTeammateRequest): Promise<Spawn
116
117
  /**
117
118
  * Queue one durable peer message, then attempt immediate delivery.
118
119
  * @param caller - exact live sending Team member.
119
- * @param request - target name, content, scheduling mode, and pre-queue cancellation.
120
+ * @param request - target name, content, and pre-queue cancellation.
120
121
  * @returns durable message identity and immediate-delivery observation.
121
122
  */
122
123
  async sendMessage(caller: Agent, request: SendTeamMessageRequest): Promise<SendTeamMessageResult>
@@ -175,6 +176,29 @@ interrupt(caller: Agent, targetName: string): { previousStatus: 'running' | 'idl
175
176
  * @returns Team membership, or undefined for non-Team subagents and stale identities.
176
177
  */
177
178
  tryMembership(agent: Agent): TeamMembership | undefined
179
+
180
+ /**
181
+ * Read the current roster and non-deleted task board through the generated Remote API.
182
+ * @param agent - exact live Team member used as the authority credential.
183
+ * @returns detached current roster and task views.
184
+ */
185
+ @Remote('view') remoteView(agent: Agent): TeamView
186
+
187
+ /**
188
+ * Create one shared task through the generated Remote API.
189
+ * @param agent - exact live Team member creating the task.
190
+ * @param request - task text, blockers, and advisory write scopes.
191
+ * @returns the revision-one task or a typed Team rejection.
192
+ */
193
+ @Remote('createTask') remoteCreateTask(agent: Agent, request: CreateTeamTaskRequest): Promise<TeamTaskMutationResult>
194
+
195
+ /**
196
+ * Apply one task mutation and preserve Team rejections as business results.
197
+ * @param agent - exact live Team member authorizing the mutation.
198
+ * @param request - task identity, expected revision, action, and action fields.
199
+ * @returns the committed task or a typed Team rejection.
200
+ */
201
+ @Remote('updateTask') remoteUpdateTask(agent: Agent, request: UpdateTeamTaskRequest): Promise<TeamTaskMutationResult>
178
202
  ```
179
203
 
180
204
  Types: [Agent](core.md)
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](agent-team.md) | 中文
4
4
 
5
- 实验性隐式 Root Team 领域、模型工具与宿主适配器共享的类型。[Agent Teams Agent Note](../../.agents/notes/implemented/feature/2026-08-05-agent-teams.zh.md)负责身份、mailbox、task 与共享 checkout 决策;本页记录 [`packages/experimental/agent-team/src/types.ts`](../../packages/experimental/agent-team/src/types.ts) 中的字面持久形式。
5
+ 实验性隐式 Root Team 领域、模型工具与宿主适配器共享的类型。[Agent Teams Agent Note](../../.agents/notes/implemented/feature/2026-08-05-agent-teams.zh.md)负责身份、mailbox、task 与共享 checkout 决策;[Team Steer 消息 Agent Note](../../.agents/notes/implemented/simplification/2026-08-30-team-send-message-steer.zh.md)负责消息调度;本页记录 [`packages/experimental/agent-team/src/types.ts`](../../packages/experimental/agent-team/src/types.ts) 中的字面持久形式。
6
6
 
7
7
  ## 身份与 roster
8
8
 
@@ -34,11 +34,12 @@ interface TeamMessageSnapshot {
34
34
  readonly senderId: SessionId
35
35
  readonly senderName: string
36
36
  readonly targetId: SessionId
37
- readonly delivery: 'quiet' | 'wakeup'
38
37
  readonly content: ContentBlock[]
39
38
  }
40
39
  ```
41
40
 
41
+ 每条消息都会尝试 Steer 投递。running target 在最近的步骤边界收到消息,idle target 启动一个轮次,inactive teammate 则冷恢复。调用方不能选择其他模式,因此持久记录不存储调度方式。
42
+
42
43
  target Session 会在 pending inbox 条目和最终用户消息上保留消息身份与发送者归因。跨 inbox 与历史折叠该 source 构成 target 侧去重键;模型可见的 framing 会重复 id 和发送者。
43
44
 
44
45
  ```ts type-equiv
@@ -74,7 +75,7 @@ interface TeamTaskSnapshot {
74
75
 
75
76
  ## 回放
76
77
 
77
- `foldTeam()` 把一个 Root Session 回放成每个 Team 操作所读取的 roster、任务板与 queued-minus-delivered mailbox。它按 `TeamId` 选取记录,因此普通 fork 继承的 event 保留 ancestor id,绝不会进入新 Root 的状态。Session event 的 `seq` 与 `time` 继续负责顺序和时间记录,Team snapshot 不再重复保存它们。roster 与 task 读取以 view 形式到达调用方,附带 owner name、readiness 与 write-scope 警告,而 pending 邮件仅供投递与恢复内部使用。包 [README](../../packages/experimental/agent-team/README.zh.md)负责 operation、authorization、recovery 和限制行为。
78
+ `foldTeam()` 把一个 Root Session 回放成每个 Team 操作所读取的 roster、任务板与 queued-minus-delivered mailbox。它按 `TeamId` 选取记录,因此普通 fork 继承的 event 保留 ancestor id,绝不会进入新 Root 的状态。Session event 的 `seq` 与 `time` 继续负责顺序和时间记录,Team snapshot 不再重复保存它们。roster 与 task 读取以 view 形式到达调用方,而 pending 邮件仅供投递与恢复内部使用。包 [README](../../packages/experimental/agent-team/README.zh.md)负责 operation、authorization、recovery 和限制行为。
78
79
 
79
80
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
80
81
 
@@ -116,7 +117,7 @@ async spawnTeammate(caller: Agent, request: SpawnTeammateRequest): Promise<Spawn
116
117
  /**
117
118
  * Queue one durable peer message, then attempt immediate delivery.
118
119
  * @param caller - exact live sending Team member.
119
- * @param request - target name, content, scheduling mode, and pre-queue cancellation.
120
+ * @param request - target name, content, and pre-queue cancellation.
120
121
  * @returns durable message identity and immediate-delivery observation.
121
122
  */
122
123
  async sendMessage(caller: Agent, request: SendTeamMessageRequest): Promise<SendTeamMessageResult>
@@ -175,6 +176,29 @@ interrupt(caller: Agent, targetName: string): { previousStatus: 'running' | 'idl
175
176
  * @returns Team membership, or undefined for non-Team subagents and stale identities.
176
177
  */
177
178
  tryMembership(agent: Agent): TeamMembership | undefined
179
+
180
+ /**
181
+ * Read the current roster and non-deleted task board through the generated Remote API.
182
+ * @param agent - exact live Team member used as the authority credential.
183
+ * @returns detached current roster and task views.
184
+ */
185
+ @Remote('view') remoteView(agent: Agent): TeamView
186
+
187
+ /**
188
+ * Create one shared task through the generated Remote API.
189
+ * @param agent - exact live Team member creating the task.
190
+ * @param request - task text, blockers, and advisory write scopes.
191
+ * @returns the revision-one task or a typed Team rejection.
192
+ */
193
+ @Remote('createTask') remoteCreateTask(agent: Agent, request: CreateTeamTaskRequest): Promise<TeamTaskMutationResult>
194
+
195
+ /**
196
+ * Apply one task mutation and preserve Team rejections as business results.
197
+ * @param agent - exact live Team member authorizing the mutation.
198
+ * @param request - task identity, expected revision, action, and action fields.
199
+ * @returns the committed task or a typed Team rejection.
200
+ */
201
+ @Remote('updateTask') remoteUpdateTask(agent: Agent, request: UpdateTeamTaskRequest): Promise<TeamTaskMutationResult>
178
202
  ```
179
203
 
180
204
  Types: [Agent](core.zh.md)
@@ -1,16 +1,16 @@
1
- # Durable Image Attachments
1
+ # Durable Attachments
2
2
 
3
3
  English | [中文](attachment.zh.md)
4
4
 
5
- The attachment seam separates binary image ownership from the session log. A producer gives validated encoded bytes to [`ctx.attachments`](#ctxattachments--attachmentstore-abstract-seam); the service publishes an immutable content-addressed reference only after the object is durable. Session events and model-visible `ImageBlock`s contain that reference and metadata, never a browser object URL, host temporary path, provider URL, or base64 payload.
5
+ The attachment seam separates binary image and generic-file ownership from the session log. A producer gives bytes to [`ctx.attachments`](#ctxattachments--attachmentstore-abstract-seam); the service publishes an immutable content-addressed reference only after the object is durable. Session events and model-visible attachment blocks contain that reference and metadata, never a browser object URL, host temporary path, provider URL, or base64 payload. The independent [`ctx.fileUploads`](#ctxfileuploads--fileuploads) service binds browser file transfers and staged receipts to the receiving Agent.
6
6
 
7
- Unsent browser drafts may stay in memory and native clients may stage them in operating-system temporary storage. Once the host accepts a user message, its images move below `<DSH_HOME>/attachments/v1` before the user event is appended. Structured model image output follows the same persist-before-event rule.
7
+ Unsent browser drafts may stay in memory and native clients may stage them in operating-system temporary storage. Browser generic files become durable before they receive a staged prompt receipt. Once the host accepts a user message, its images move below `<DSH_HOME>/attachments/v1` before the user event is appended. Structured model image output follows the same persist-before-event rule.
8
8
 
9
9
  Source: [`packages/attachment/attachment/src/types.ts`](../../packages/attachment/attachment/src/types.ts)
10
10
 
11
11
  ## Identity and verified metadata
12
12
 
13
- `AttachmentId` is a branded opaque string. The local backend currently emits `sha256:<digest>`, but consumers must neither parse that representation nor derive a filesystem path from it.
13
+ `AttachmentId` is a branded opaque string. The local backend currently emits `sha256:<digest>`, but consumers must neither parse that representation nor derive a filesystem path from it. A consumer may ask the attachment provider for its object location through `imageHostPath()`, then must use the current execution filesystem to decide whether model tools can read that host path.
14
14
 
15
15
  ```ts type-equiv
16
16
  /** Raster image formats accepted by the version-one attachment path. */
@@ -62,6 +62,38 @@ The reference records intrinsic dimensions and encoded length so clients can lay
62
62
 
63
63
  ## Commit and verified-read payloads
64
64
 
65
+ ```ts type-equiv
66
+ /**
67
+ * Browser-submitted prompt content accepted by Host prompt endpoints; the
68
+ * accepting Host promotes image parts to durable references through
69
+ * `ctx.attachments.admitPromptContent()` before any message is created, so a wire caller can
70
+ * never cite an attachment it did not upload.
71
+ */
72
+ type PromptContentPart =
73
+ | { readonly type: 'text'; readonly text: string }
74
+ | {
75
+ readonly type: 'image'
76
+ readonly mediaType: ImageMediaType
77
+ readonly data: string
78
+ readonly name?: string
79
+ }
80
+ ```
81
+
82
+ ```ts type-equiv
83
+ /** Host prompt content whose file receipts are resolved and whose image bytes await admission. */
84
+ type AttachmentAdmissionPart =
85
+ | PromptContentPart
86
+ | { readonly type: 'file'; readonly attachment: FileAttachmentRef }
87
+ ```
88
+
89
+ ```ts type-equiv
90
+ /** Host-admitted prompt content with every attachment represented by its durable reference. */
91
+ type AdmittedPromptContentPart =
92
+ | { readonly type: 'text'; readonly text: string }
93
+ | { readonly type: 'image'; readonly attachment: ImageAttachmentRef }
94
+ | { readonly type: 'file'; readonly attachment: FileAttachmentRef }
95
+ ```
96
+
65
97
  ```ts type-equiv
66
98
  /** Base64-encoded image upload accompanying one wire request. */
67
99
  interface EncodedImageAttachment {
@@ -98,7 +130,7 @@ interface StoredImageAttachment {
98
130
  interface ImageRequestPolicy {
99
131
  /** Maximum width multiplied by height after aspect-preserving projection. */
100
132
  maxPixels: number
101
- /** Encoded-byte cap before base64 expansion or Files API upload. */
133
+ /** Encoded-byte target before base64 expansion or Files API upload; the smallest quality-ladder output is kept when no quality fits. */
102
134
  maxBytes: number
103
135
  }
104
136
  ```
@@ -125,7 +157,7 @@ interface RequestImageAttachment {
125
157
  }
126
158
  ```
127
159
 
128
- `saveImage()` prepares and atomically commits a provider-independent normalized attachment before returning its `ImageAttachmentRef`. `saveImages()` prepares every validated attachment once before publishing the batch, so validation rejection leaves no partial objects and publication does not repeat decoding or quality selection. `admitEncodedImages()` is the wire entry for base64 uploads and delegates count, aggregate-byte, and ordered batch admission to `saveImages()`. `readImage()` verifies a normalized attachment from an authorized session path. `readImageRequest()` derives and caches one request version under an exact route pixel and byte budget; new entries are fully decoded before publication, while cache hits use a bounded metadata probe. Callers use `Promise.all` over the singular method when they need an ordered batch. The local implementation lazily encodes preferred candidates, singleflights equal request identities, lets each waiter cancel independently, stops shared work when no waiter remains, and bounds all transforms with its instance-level limiter, which defaults to two simultaneous transformations. The service is retention-neutral: resumed and forked sessions may share objects, so reference-aware garbage collection is deferred rather than tied to one session's deletion.
160
+ `saveImage()` prepares and atomically commits a provider-independent normalized attachment before returning its `ImageAttachmentRef`. `saveImages()` prepares every validated attachment once before publishing the batch, so validation rejection leaves no partial objects and publication does not repeat decoding or quality selection. `admitPromptContent()` accepts the complete ordered Host prompt after file receipt resolution, replaces base64 image uploads with durable references, and passes durable file references unchanged. `admitEncodedImages()` supports other wire entries and delegates count, aggregate-byte, and ordered batch admission to `saveImages()`. `admitEncodedFile()` gives encoded protocol adapters the same service-owned canonical-base64 admission, and `isAttachmentError()` lets those adapters recognize stable attachment failures without importing implementation helpers. `readImage()` verifies a normalized attachment from an authorized session path. `imageHostPath()` exposes only the provider-owned host object location; it does not decide whether the current tool execution world can read it. `readImageRequest()` derives and caches one deterministic request version under an exact route pixel and byte budget. That version contains encoded bytes and metadata but no execution-world path. New entries are fully decoded before publication, while cache hits use a bounded metadata probe. Callers use `Promise.all` over the singular method when they need an ordered batch. The local implementation lazily encodes preferred candidates, singleflights equal request identities, lets each waiter cancel independently, stops shared work when no waiter remains, and bounds all transforms with its instance-level limiter, which defaults to two simultaneous transformations. The service is retention-neutral: resumed and forked sessions may share objects, so reference-aware garbage collection is deferred rather than tied to one session's deletion.
129
161
 
130
162
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
131
163
 
@@ -157,6 +189,30 @@ abstract validateImage(input: SaveImageAttachment): Promise<void>
157
189
  */
158
190
  async saveImages(inputs: readonly SaveImageAttachment[]): Promise<readonly ImageAttachmentRef[]>
159
191
 
192
+ /**
193
+ * Admit one Host prompt and replace each uploaded image with its durable reference.
194
+ * Text and durable file references pass through unchanged. A prompt without image parts performs no storage operation.
195
+ * @param content - prompt parts in message order after file receipt resolution.
196
+ * @returns admitted prompt parts in the same order as `content`.
197
+ * @throws AttachmentError when the image batch is refused.
198
+ */
199
+ async admitPromptContent( content: readonly AttachmentAdmissionPart[], ): Promise<AdmittedPromptContentPart[]>
200
+
201
+ /**
202
+ * Decode and durably commit one canonical base64 file upload.
203
+ * @param input - canonical base64 bytes and optional display name.
204
+ * @returns the durable content-addressed file reference.
205
+ * @throws AttachmentError when the encoding or storage operation is refused.
206
+ */
207
+ admitEncodedFile(input: EncodedFileAttachment): Promise<FileAttachmentRef>
208
+
209
+ /**
210
+ * Identify a failure emitted by this attachment capability by its stable code.
211
+ * @param error - value caught from an attachment operation.
212
+ * @returns whether the value is an attachment failure.
213
+ */
214
+ isAttachmentError(error: unknown): error is AttachmentError
215
+
160
216
  /**
161
217
  * Validate and durably commit one image before its owning session event is appended.
162
218
  * The returned reference describes the persisted normalized image. When
@@ -176,10 +232,55 @@ abstract saveImage(input: SaveImageAttachment): Promise<ImageAttachmentRef>
176
232
  */
177
233
  abstract readImage(ref: ImageAttachmentRef, signal?: AbortSignal): Promise<StoredImageAttachment>
178
234
 
235
+ /**
236
+ * Locate the provider-owned normalized object in the harness host filesystem.
237
+ * @param ref - durable normalized attachment reference.
238
+ * @returns an absolute host path, or undefined when this backend is not host-file-backed.
239
+ * @throws an AttachmentError when the durable reference is invalid.
240
+ */
241
+ imageHostPath(ref: ImageAttachmentRef): string | undefined
242
+
243
+ /**
244
+ * Durably commit one file byte-for-byte before its owning session event is
245
+ * appended. Files carry no admission limits: any byte content and length is
246
+ * accepted, and the stored object is the exact submitted bytes. Backends
247
+ * without verbatim file storage keep this default rejection.
248
+ * @param input - exact bytes and optional display name.
249
+ * @returns the durable content-addressed file reference.
250
+ */
251
+ saveFile(input: SaveFileAttachment): Promise<FileAttachmentRef>
252
+
253
+ /**
254
+ * Durably commit one file byte-for-byte from bounded chunks. Providers must
255
+ * apply backpressure and must not collect the complete file in memory.
256
+ * Backends without streamed verbatim storage keep this default rejection.
257
+ * @param input - ordered exact bytes, optional cancellation, and display name.
258
+ * @returns the durable content-addressed file reference.
259
+ */
260
+ saveFileStream(input: SaveFileStreamAttachment): Promise<FileAttachmentRef>
261
+
262
+ /**
263
+ * Read and verify one verbatim stored file as bounded chunks. Providers must
264
+ * not collect the complete file in memory. Backends without verbatim file
265
+ * reads keep this default rejection.
266
+ * @param ref - durable reference from the session log.
267
+ * @param signal - optional cancellation for backend reads and verification work.
268
+ * @returns exact file bytes in order; integrity failures reject the iteration.
269
+ */
270
+ async *readFileStream( ref: FileAttachmentRef, signal?: AbortSignal, ): AsyncIterable<Uint8Array>
271
+
272
+ /**
273
+ * Locate the verbatim stored file object in the harness host filesystem.
274
+ * @param ref - durable file reference.
275
+ * @returns an absolute host path, or undefined when this backend is not host-file-backed.
276
+ * @throws an AttachmentError when the durable reference is invalid.
277
+ */
278
+ fileHostPath(ref: FileAttachmentRef): string | undefined
279
+
179
280
  /**
180
281
  * Generate or read one deterministic model-request version from the stored normalized image.
181
282
  * @param ref - durable provider-independent normalized attachment reference.
182
- * @param policy - exact route pixel and encoded-byte budget.
283
+ * @param policy - exact route pixel budget and encoded-byte target; a target no ladder quality meets yields the smallest ladder output.
183
284
  * @param signal - optional cancellation.
184
285
  * @returns request bytes and the cache/upload identity covering every transform input.
185
286
  */
@@ -187,4 +288,64 @@ readImageRequest( ref: ImageAttachmentRef, policy: ImageRequestPolicy, signal?:
187
288
  ```
188
289
 
189
290
  Source: [`packages/attachment/attachment/src/index.ts`](../../packages/attachment/attachment/src/index.ts)
291
+
292
+ <a id="ctxfileuploads--fileuploads"></a>
293
+
294
+ ### `ctx.fileUploads` — `FileUploads`
295
+
296
+ Host service owning upload storage and Agent-scoped staged receipts.
297
+
298
+ ```ts cordis-catalog
299
+ /**
300
+ * Register the ordinary-Session resolver used when a raw upload addresses a cold Session.
301
+ * @param resolve - resolver that returns the exact live Agent or throws a Remote error.
302
+ * @returns disposer removing this resolver.
303
+ */
304
+ registerAgentResolver(resolve: AgentResolver): () => void
305
+
306
+ /**
307
+ * Persist one encoded upload and stage it under the Agent receiver selected by Typert.
308
+ * @param agent - receiving Agent resolved from the Remote Agent scope.
309
+ * @param request - canonical base64 bytes and optional display name.
310
+ * @param signal - caller cancellation before storage begins.
311
+ * @returns the staged receipt and durable file reference.
312
+ */
313
+ @Remote('upload') upload(agent: Agent, request: EncodedFileUploadRequest, signal: AbortSignal): Promise<FileUploadValue>
314
+
315
+ /**
316
+ * Persist raw chunks for one Session without aggregating the upload.
317
+ * @param request - Session identity, ordered bytes, cancellation, and optional display name.
318
+ * @returns the staged receipt and durable file reference.
319
+ */
320
+ async uploadStream(request: { readonly sessionId: SessionId readonly data: AsyncIterable<Uint8Array> readonly signal?: AbortSignal readonly name?: string }): Promise<FileUploadValue>
321
+
322
+ /**
323
+ * Resolve one staged receipt inside its receiving Agent scope.
324
+ * @param agent - receiving Agent.
325
+ * @param receiptId - opaque receipt minted for one completed upload.
326
+ * @returns durable file reference, or `undefined` for an unknown or foreign receipt.
327
+ */
328
+ resolve(agent: Agent, receiptId: FileUploadReceiptId): FileAttachmentRef | undefined
329
+
330
+ /**
331
+ * Bind receipts while one prompt enters an Agent inbox.
332
+ * Disposal restores every prior binding unless the caller commits successful delivery.
333
+ * @param agent - receiving Agent.
334
+ * @param receiptIds - distinct staged receipts referenced by the prompt.
335
+ * @param requestId - prompt identity later observed in queue or history.
336
+ * @returns binding kept after commit until queue or history observation retires its receipts.
337
+ */
338
+ bindPrompt( agent: Agent, receiptIds: readonly FileUploadReceiptId[], requestId: string, ): PromptFileBinding
339
+
340
+ /**
341
+ * Retire every receipt accepted by one removed queue occurrence.
342
+ * @param agent - receiving Agent.
343
+ * @param requestId - prompt identity carried by the queue occurrence.
344
+ */
345
+ retirePrompt(agent: Agent, requestId: string): void
346
+ ```
347
+
348
+ Types: [Agent](core.md) · [SessionId](core.md)
349
+
350
+ Source: [`packages/client/file-upload/src/index.ts`](../../packages/client/file-upload/src/index.ts)
190
351
  <!-- END GENERATED cordis-surface -->