dsh-plugin-dev-kb 1.0.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 (234) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/cordis.patch.yml +12 -0
  4. package/kb/INDEX.md +210 -0
  5. package/kb/README.md +69 -0
  6. package/kb/extra/AGENTS.md +75 -0
  7. package/kb/extra/api-gateway.md +164 -0
  8. package/kb/extra/api-gateway.zh.md +164 -0
  9. package/kb/extra/cookbook/adding-a-vendored-package.md +59 -0
  10. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +59 -0
  11. package/kb/extra/cookbook/maintaining-dsh-code-review.md +64 -0
  12. package/kb/extra/cookbook/maintaining-dsh-code-review.zh.md +64 -0
  13. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
  14. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
  15. package/kb/extra/defensive-patterns.md +33 -0
  16. package/kb/extra/defensive-patterns.zh.md +33 -0
  17. package/kb/extra/development.md +171 -0
  18. package/kb/extra/development.zh.md +171 -0
  19. package/kb/extra/event-producer-consumer.md +76 -0
  20. package/kb/extra/event-producer-consumer.zh.md +78 -0
  21. package/kb/extra/glossary.md +45 -0
  22. package/kb/extra/glossary.zh.md +45 -0
  23. package/kb/extra/graph-atlas.md +24 -0
  24. package/kb/extra/graph-atlas.zh.md +26 -0
  25. package/kb/extra/i18n/README.md +60 -0
  26. package/kb/extra/i18n/README.zh.md +60 -0
  27. package/kb/extra/i18n/style-samples.md +87 -0
  28. package/kb/extra/i18n/terminology.md +214 -0
  29. package/kb/extra/i18n/translation-prompt.md +263 -0
  30. package/kb/extra/i18n/translation-rules.md +69 -0
  31. package/kb/extra/i18n/translation-rules.zh.md +69 -0
  32. package/kb/extra/module-graph.md +1641 -0
  33. package/kb/extra/module-graph.zh.md +1643 -0
  34. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +113 -0
  35. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
  36. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
  37. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
  38. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
  39. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
  40. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
  41. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
  42. package/kb/extra/postmortem/README.md +18 -0
  43. package/kb/extra/postmortem/README.zh.md +18 -0
  44. package/kb/extra/rescope.md +53 -0
  45. package/kb/extra/rescope.zh.md +53 -0
  46. package/kb/extra/subsystems/attachment.md +125 -0
  47. package/kb/extra/subsystems/attachment.zh.md +125 -0
  48. package/kb/extra/subsystems/extensions.md +364 -0
  49. package/kb/extra/subsystems/extensions.zh.md +364 -0
  50. package/kb/extra/subsystems/feedback.md +266 -0
  51. package/kb/extra/subsystems/feedback.zh.md +266 -0
  52. package/kb/extra/testing.md +49 -0
  53. package/kb/extra/testing.zh.md +49 -0
  54. package/kb/extra/web-styling.md +25 -0
  55. package/kb/extra/web-styling.zh.md +25 -0
  56. package/kb/meta/search-index.json +1328 -0
  57. package/kb/meta/site-pages.txt +168 -0
  58. package/kb/meta/source.json +13 -0
  59. package/kb/meta/topics.md +75 -0
  60. package/kb/site/develop/basic/config.md +108 -0
  61. package/kb/site/develop/basic/index.md +146 -0
  62. package/kb/site/develop/basic/publish.md +185 -0
  63. package/kb/site/develop/basic/tool.md +54 -0
  64. package/kb/site/develop/cordis-tutorial/01-first-plugin.md +95 -0
  65. package/kb/site/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  66. package/kb/site/develop/cordis-tutorial/03-services.md +98 -0
  67. package/kb/site/develop/cordis-tutorial/04-events.md +144 -0
  68. package/kb/site/develop/cordis-tutorial/05-config.md +84 -0
  69. package/kb/site/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
  70. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +107 -0
  71. package/kb/site/develop/cordis-tutorial/index.md +62 -0
  72. package/kb/site/develop/framework/events.md +145 -0
  73. package/kb/site/develop/framework/index.md +139 -0
  74. package/kb/site/develop/framework/service.md +152 -0
  75. package/kb/site/develop/practice/index.md +157 -0
  76. package/kb/site/develop/practice/llm-adapter.md +190 -0
  77. package/kb/site/en/develop/basic/config.md +108 -0
  78. package/kb/site/en/develop/basic/index.md +146 -0
  79. package/kb/site/en/develop/basic/publish.md +185 -0
  80. package/kb/site/en/develop/basic/tool.md +54 -0
  81. package/kb/site/en/develop/cordis-tutorial/01-first-plugin.md +95 -0
  82. package/kb/site/en/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  83. package/kb/site/en/develop/cordis-tutorial/03-services.md +98 -0
  84. package/kb/site/en/develop/cordis-tutorial/04-events.md +144 -0
  85. package/kb/site/en/develop/cordis-tutorial/05-config.md +84 -0
  86. package/kb/site/en/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
  87. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +107 -0
  88. package/kb/site/en/develop/cordis-tutorial/index.md +60 -0
  89. package/kb/site/en/develop/framework/events.md +145 -0
  90. package/kb/site/en/develop/framework/index.md +139 -0
  91. package/kb/site/en/develop/framework/service.md +150 -0
  92. package/kb/site/en/develop/practice/index.md +157 -0
  93. package/kb/site/en/develop/practice/llm-adapter.md +190 -0
  94. package/kb/site/en/guide/providers-custom-form.png +0 -0
  95. package/kb/site/en/guide/providers-models-page.png +0 -0
  96. package/kb/site/en/guide/providers.md +100 -0
  97. package/kb/site/en/guide/python-sdk.md +106 -0
  98. package/kb/site/en/guide/quickstart.md +32 -0
  99. package/kb/site/en/index.md +8 -0
  100. package/kb/site/en/reference/agent-lifecycle.md +86 -0
  101. package/kb/site/en/reference/capability-seams.md +475 -0
  102. package/kb/site/en/reference/config-catalog.md +3155 -0
  103. package/kb/site/en/reference/cookbook/adding-a-conversation-node.md +235 -0
  104. package/kb/site/en/reference/cookbook/adding-a-package.md +120 -0
  105. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +102 -0
  106. package/kb/site/en/reference/cookbook/adding-a-tool.md +96 -0
  107. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +45 -0
  108. package/kb/site/en/reference/cookbook/extension-cookbook.md +131 -0
  109. package/kb/site/en/reference/cordis-api/context.md +368 -0
  110. package/kb/site/en/reference/cordis-api/events.md +211 -0
  111. package/kb/site/en/reference/cordis-api/fiber.md +379 -0
  112. package/kb/site/en/reference/cordis-api/inherited.md +43 -0
  113. package/kb/site/en/reference/cordis-api/registry.md +156 -0
  114. package/kb/site/en/reference/cordis-api/service.md +106 -0
  115. package/kb/site/en/reference/cordis-primer.md +46 -0
  116. package/kb/site/en/reference/index.md +131 -0
  117. package/kb/site/en/reference/persistence-catalog.md +949 -0
  118. package/kb/site/en/reference/subsystems/approval.md +173 -0
  119. package/kb/site/en/reference/subsystems/client-modules.md +121 -0
  120. package/kb/site/en/reference/subsystems/code-runtime.md +194 -0
  121. package/kb/site/en/reference/subsystems/commands.md +190 -0
  122. package/kb/site/en/reference/subsystems/compaction.md +241 -0
  123. package/kb/site/en/reference/subsystems/core.md +1073 -0
  124. package/kb/site/en/reference/subsystems/credentials.md +136 -0
  125. package/kb/site/en/reference/subsystems/filesystem.md +498 -0
  126. package/kb/site/en/reference/subsystems/goal.md +280 -0
  127. package/kb/site/en/reference/subsystems/index.md +58 -0
  128. package/kb/site/en/reference/subsystems/invariants.md +91 -0
  129. package/kb/site/en/reference/subsystems/jobs.md +293 -0
  130. package/kb/site/en/reference/subsystems/llm-streaming.md +920 -0
  131. package/kb/site/en/reference/subsystems/lsp.md +205 -0
  132. package/kb/site/en/reference/subsystems/permission-presets.md +134 -0
  133. package/kb/site/en/reference/subsystems/persistence.md +388 -0
  134. package/kb/site/en/reference/subsystems/plan.md +90 -0
  135. package/kb/site/en/reference/subsystems/sandbox.md +221 -0
  136. package/kb/site/en/reference/subsystems/schedule.md +189 -0
  137. package/kb/site/en/reference/subsystems/scope.md +62 -0
  138. package/kb/site/en/reference/subsystems/session-projection.md +265 -0
  139. package/kb/site/en/reference/subsystems/session-query.md +498 -0
  140. package/kb/site/en/reference/subsystems/session-reference.md +111 -0
  141. package/kb/site/en/reference/subsystems/session-telemetry.md +197 -0
  142. package/kb/site/en/reference/subsystems/session-title.md +207 -0
  143. package/kb/site/en/reference/subsystems/session.md +852 -0
  144. package/kb/site/en/reference/subsystems/settings.md +313 -0
  145. package/kb/site/en/reference/subsystems/shell.md +306 -0
  146. package/kb/site/en/reference/subsystems/skills.md +334 -0
  147. package/kb/site/en/reference/subsystems/spill.md +120 -0
  148. package/kb/site/en/reference/subsystems/storage.md +232 -0
  149. package/kb/site/en/reference/subsystems/subagent.md +737 -0
  150. package/kb/site/en/reference/subsystems/subprocess.md +327 -0
  151. package/kb/site/en/reference/subsystems/system-prompt.md +210 -0
  152. package/kb/site/en/reference/subsystems/terminal.md +187 -0
  153. package/kb/site/en/reference/subsystems/token-meter.md +93 -0
  154. package/kb/site/en/reference/subsystems/tools.md +723 -0
  155. package/kb/site/en/reference/subsystems/typert.md +339 -0
  156. package/kb/site/en/reference/subsystems/user-questions.md +181 -0
  157. package/kb/site/en/reference/subsystems/web-server.md +111 -0
  158. package/kb/site/en/reference/subsystems/web.md +202 -0
  159. package/kb/site/en/reference/subsystems/workflow.md +281 -0
  160. package/kb/site/en/reference/subsystems/workspace.md +231 -0
  161. package/kb/site/en/reference/tool-catalog.md +1877 -0
  162. package/kb/site/en/reference/tool-execution-pipeline.md +66 -0
  163. package/kb/site/guide/providers-custom-form.zh.png +0 -0
  164. package/kb/site/guide/providers-models-page.zh.png +0 -0
  165. package/kb/site/guide/providers.md +100 -0
  166. package/kb/site/guide/python-sdk.md +106 -0
  167. package/kb/site/guide/quickstart.md +32 -0
  168. package/kb/site/index.md +8 -0
  169. package/kb/site/reference/agent-lifecycle.md +86 -0
  170. package/kb/site/reference/capability-seams.md +475 -0
  171. package/kb/site/reference/config-catalog.md +3154 -0
  172. package/kb/site/reference/cookbook/adding-a-conversation-node.md +235 -0
  173. package/kb/site/reference/cookbook/adding-a-package.md +120 -0
  174. package/kb/site/reference/cookbook/adding-a-settings-card.md +102 -0
  175. package/kb/site/reference/cookbook/adding-a-tool.md +98 -0
  176. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +45 -0
  177. package/kb/site/reference/cookbook/extension-cookbook.md +133 -0
  178. package/kb/site/reference/cordis-api/context.md +368 -0
  179. package/kb/site/reference/cordis-api/events.md +211 -0
  180. package/kb/site/reference/cordis-api/fiber.md +379 -0
  181. package/kb/site/reference/cordis-api/inherited.md +43 -0
  182. package/kb/site/reference/cordis-api/registry.md +156 -0
  183. package/kb/site/reference/cordis-api/service.md +106 -0
  184. package/kb/site/reference/cordis-primer.md +52 -0
  185. package/kb/site/reference/index.md +135 -0
  186. package/kb/site/reference/persistence-catalog.md +949 -0
  187. package/kb/site/reference/subsystems/approval.md +173 -0
  188. package/kb/site/reference/subsystems/client-modules.md +121 -0
  189. package/kb/site/reference/subsystems/code-runtime.md +194 -0
  190. package/kb/site/reference/subsystems/commands.md +190 -0
  191. package/kb/site/reference/subsystems/compaction.md +241 -0
  192. package/kb/site/reference/subsystems/core.md +1081 -0
  193. package/kb/site/reference/subsystems/credentials.md +136 -0
  194. package/kb/site/reference/subsystems/filesystem.md +498 -0
  195. package/kb/site/reference/subsystems/goal.md +280 -0
  196. package/kb/site/reference/subsystems/index.md +58 -0
  197. package/kb/site/reference/subsystems/invariants.md +91 -0
  198. package/kb/site/reference/subsystems/jobs.md +293 -0
  199. package/kb/site/reference/subsystems/llm-streaming.md +926 -0
  200. package/kb/site/reference/subsystems/lsp.md +205 -0
  201. package/kb/site/reference/subsystems/permission-presets.md +134 -0
  202. package/kb/site/reference/subsystems/persistence.md +388 -0
  203. package/kb/site/reference/subsystems/plan.md +90 -0
  204. package/kb/site/reference/subsystems/sandbox.md +221 -0
  205. package/kb/site/reference/subsystems/schedule.md +189 -0
  206. package/kb/site/reference/subsystems/scope.md +62 -0
  207. package/kb/site/reference/subsystems/session-projection.md +265 -0
  208. package/kb/site/reference/subsystems/session-query.md +498 -0
  209. package/kb/site/reference/subsystems/session-reference.md +111 -0
  210. package/kb/site/reference/subsystems/session-telemetry.md +197 -0
  211. package/kb/site/reference/subsystems/session-title.md +207 -0
  212. package/kb/site/reference/subsystems/session.md +854 -0
  213. package/kb/site/reference/subsystems/settings.md +313 -0
  214. package/kb/site/reference/subsystems/shell.md +306 -0
  215. package/kb/site/reference/subsystems/skills.md +334 -0
  216. package/kb/site/reference/subsystems/spill.md +120 -0
  217. package/kb/site/reference/subsystems/storage.md +232 -0
  218. package/kb/site/reference/subsystems/subagent.md +739 -0
  219. package/kb/site/reference/subsystems/subprocess.md +327 -0
  220. package/kb/site/reference/subsystems/system-prompt.md +210 -0
  221. package/kb/site/reference/subsystems/terminal.md +187 -0
  222. package/kb/site/reference/subsystems/token-meter.md +93 -0
  223. package/kb/site/reference/subsystems/tools.md +723 -0
  224. package/kb/site/reference/subsystems/typert.md +339 -0
  225. package/kb/site/reference/subsystems/user-questions.md +181 -0
  226. package/kb/site/reference/subsystems/web-server.md +111 -0
  227. package/kb/site/reference/subsystems/web.md +202 -0
  228. package/kb/site/reference/subsystems/workflow.md +281 -0
  229. package/kb/site/reference/subsystems/workspace.md +231 -0
  230. package/kb/site/reference/tool-catalog.md +1880 -0
  231. package/kb/site/reference/tool-execution-pipeline.md +66 -0
  232. package/package.json +40 -0
  233. package/scripts/rebuild-index.mjs +88 -0
  234. package/skills/dsh-plugin-dev-kb.md +66 -0
@@ -0,0 +1,136 @@
1
+ ---
2
+ editSource: "docs/subsystems/credentials.zh.md"
3
+ outline: [2,3]
4
+ ---
5
+
6
+ # 用户凭据
7
+
8
+ [dsh-credentials](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/credentials/credentials) 的凭据 seam 把机密挡在配置之外:settings 分节与 `cordis.yml` 条目携带的是*引用*(环境变量名),值归 [dsh-credentials-local](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/credentials/credentials-local) 这类提供方所有,消费方每个操作解析一次引用——LLM(大语言模型)适配器每次模型请求解析一次,因此轮换后的凭据无需任何重启即可作用于紧随其后的下一次请求。一条 seam 级规则约束每个提供方:空的存储值在任何地方都视为不存在。
9
+
10
+ 来源:[`packages/credentials/credentials/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/credentials/credentials/src/index.ts)
11
+
12
+ ## 标识
13
+
14
+ 引用以 POSIX 风格环境变量名命名一条凭据。brand 防止调用方将凭据引用与在包或进程之间传递的其他字符串混用;构造时校验 shell 标识符语法。
15
+
16
+ ```ts type-equiv
17
+ /** Nominal reference to one credential: a POSIX-style environment-variable name. */
18
+ type CredentialRef = Branded<'CredentialRef'>
19
+ ```
20
+
21
+ ## 解析
22
+
23
+ `resolve(ref)` 返回值及提供该值的来源层(由提供方定义);未配置期间返回 `undefined`。消费方在每个操作中重新解析,绝不跨操作缓存——这种按操作进行的读取正是热更新机制。
24
+
25
+ ```ts type-equiv
26
+ /** One resolved credential value and the source layer that supplied it. */
27
+ interface ResolvedCredential {
28
+ /** The non-empty secret value. */
29
+ value: string
30
+ /** Provider-defined source layer id (the local provider uses `env`, `file`, `project-env`, and `user-env`). */
31
+ source: string
32
+ }
33
+ ```
34
+
35
+ ## 描述
36
+
37
+ `describe(ref)` 在绝不暴露值的前提下回应配置界面:引用当前是否可解析、来自哪一层、`set` 当前能否成功。本地提供方把由当前进程环境供值的引用报告为 `writable: false`——那样的写入会表面成功而解析持续返回遮蔽值,因此 seam 直接拒绝,界面也得以提前把该引用渲染为只读。
38
+
39
+ ```ts type-equiv
40
+ /** Source and writability facts for one reference, safe for configuration UIs — never the value. */
41
+ interface CredentialInfo {
42
+ /** Whether {@link CredentialProvider.resolve} would currently return a value. */
43
+ configured: boolean
44
+ /** Source layer currently supplying the value; absent while unconfigured. */
45
+ source?: string
46
+ /** Whether {@link CredentialProvider.set} would currently succeed for this reference. */
47
+ writable: boolean
48
+ }
49
+ ```
50
+
51
+ ## 已提交的变更
52
+
53
+ `credentials/updated (ref)` 在提供方管理的来源发生已提交变更后发出——`set`、`unset` 或在存储中观察到的外部编辑。进程环境自身的变化不可观测,永不发出事件。消费方不需要该事件(它们按操作重新解析);它服务于配置界面刷新「已配置」徽标。
54
+
55
+ <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
56
+
57
+ <a id="cordis-surface"></a>
58
+
59
+ ## Cordis API
60
+
61
+ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
62
+
63
+ <a id="ctxcredentials--credentialprovider-abstract-seam"></a>
64
+
65
+ ### `ctx.credentials` — `CredentialProvider` (abstract seam)
66
+
67
+ Abstract credential service. Providers implement the four operations over their source layers; one seam-wide rule binds them all: an empty stored value is absent everywhere — `resolve` skips it, `describe` reports it unconfigured — so a blank never masquerades as a configured secret.
68
+
69
+ ```ts cordis-catalog
70
+ /**
71
+ * Resolve one reference to its current value. Resolution is per call:
72
+ * consumers re-resolve at each operation and must not cache across
73
+ * operations — that per-operation read is what makes a changed credential
74
+ * reach the next operation without a restart.
75
+ * @param ref - the reference to resolve.
76
+ * @returns the value and its source, or `undefined` while unconfigured.
77
+ */
78
+ abstract resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined>
79
+
80
+ /**
81
+ * Describe one reference for configuration surfaces without exposing the
82
+ * value.
83
+ * @param ref - the reference to describe.
84
+ * @returns configured state, supplying source, and writability.
85
+ */
86
+ abstract describe(ref: CredentialRef): Promise<CredentialInfo>
87
+
88
+ /**
89
+ * Durably store one value in the provider-managed writable source. Rejects
90
+ * while a read-only source shadows the reference — the write would appear
91
+ * to succeed while resolution keeps returning the shadowing value — and
92
+ * rejects an empty value (use {@link unset}).
93
+ * @param ref - the reference to store.
94
+ * @param value - the non-empty secret value.
95
+ */
96
+ abstract set(ref: CredentialRef, value: string): Promise<void>
97
+
98
+ /**
99
+ * Remove one reference from the provider-managed writable source; removing
100
+ * an absent reference is a no-op. Rejects while a read-only source shadows
101
+ * the reference, like {@link set}.
102
+ * @param ref - the reference to remove.
103
+ */
104
+ abstract unset(ref: CredentialRef): Promise<void>
105
+ ```
106
+
107
+ Source: [`packages/credentials/credentials/src/index.ts:60`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/credentials/credentials/src/index.ts)
108
+
109
+ <a id="credentials-events"></a>
110
+
111
+ ### `credentials/*` events
112
+
113
+ <a id="credentialsupdated--emit"></a>
114
+
115
+ #### `credentials/updated` — emit
116
+
117
+ Committed change to a provider-managed credential source: a `set`, an `unset`, or an external edit observed in storage. Ambient process-environment changes are not observable and never emit. Listener failures are contained and logged — a sync throw and an async rejection alike — without changing the committed operation's outcome, except `INVARIANT`-coded failures, which rethrow after every listener ran; that rethrow reaches the emitter only from synchronous listeners, so invariant checks on this event must not be async functions.
118
+
119
+ ```ts cordis-catalog
120
+ /**
121
+ * Committed change to a provider-managed credential source: a `set`, an
122
+ * `unset`, or an external edit observed in storage. Ambient
123
+ * process-environment changes are not observable and never emit. Listener
124
+ * failures are contained and logged — a sync throw and an async rejection
125
+ * alike — without changing the committed operation's outcome, except
126
+ * `INVARIANT`-coded failures, which rethrow after every listener ran;
127
+ * that rethrow reaches the emitter only from synchronous listeners, so
128
+ * invariant checks on this event must not be async functions.
129
+ * @param ref - the reference whose stored value changed.
130
+ * @mode emit
131
+ */
132
+ 'credentials/updated'(ref: CredentialRef): void
133
+ ```
134
+
135
+ Source: [`packages/credentials/credentials/src/types.ts:29`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/credentials/credentials/src/types.ts)
136
+ <!-- END GENERATED cordis-surface -->
@@ -0,0 +1,498 @@
1
+ ---
2
+ editSource: "docs/subsystems/filesystem.zh.md"
3
+ outline: [2,3]
4
+ ---
5
+
6
+ # 文件系统
7
+
8
+ 可选的文件系统能力由四个部分组成:[dsh-fs](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/fs/fs) 拥有 `ctx.fs` 以及带可选守卫的原子文本操作;[dsh-fs-local](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/fs/fs-local) 实现本地磁盘后端;[dsh-fs-observation-policy](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/fs/fs-observation-policy) 记录观测到的存在或缺失状态,并通过事件(而非服务)添加新鲜度规则;[dsh-tool-fs](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/fs/tool-fs) 直接执行面向模型的 read/write/edit 调用并渲染窗口。它位于 agent loop(智能体循环)主干之外;替换后端不会改变策略或工具 schema。
9
+
10
+ `dsh-fs-observation-policy` 是可选插件。没有该插件时,`FileSystem` 服务定义、一个提供方和 `dsh-tool-fs` 消费方组成完整且不受约束的文件系统 seam:`write` 无条件创建或覆盖,`edit` 无条件替换字面文本。策略插件通过裁决 `fs/*` waterfall(瀑布式事件)来改变这些操作。移除该插件不会破坏工具,因为工具调用 `ctx.fs` 并分发事件,而不调用策略方法。加载了 `dsh-tool-fs` 的部署也应加载 `dsh-fs-observation-policy`,使默认行为为「先读后写/编辑」。
11
+
12
+ 提供方源码:[`packages/fs/fs/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs/src/types.ts) 与 [`packages/fs/fs/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs/src/index.ts)。策略源码:[`packages/fs/fs-observation-policy/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs-observation-policy/src/types.ts)。读取渲染源码:[`packages/fs/tool-fs/src/read-render.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/tool-fs/src/read-render.ts)。
13
+
14
+ ## 目标标识与元数据(提供方约定)
15
+
16
+ 每个操作首先将用户提供的路径解析为不透明的后端目标。消费方可以显示 `displayPath`,但禁止解析 `targetKey`(一个品牌化的不透明 id),也不得假设它是本地绝对路径。
17
+
18
+ 与文件系统共享执行世界的消费方通过提供方获取跨能力坐标,而不是解释该身份:`processPath(target)` 返回子进程可以打开的规范化绝对路径,`fileUrl(target)` 返回采用提供方平台语法的 `file:` URI,`contains(parent, child)` 则检查规范化身份相等或后代包含关系。
19
+
20
+ ```ts type-equiv
21
+ /**
22
+ * A path resolved by a backend into a stable identity. `resolve()` produces
23
+ * this; every other operation takes it.
24
+ */
25
+ interface FsTarget {
26
+ /** Opaque key for stale guards and target lookup. */
27
+ targetKey: FsTargetKey
28
+ /**
29
+ * Path for model/UI-facing output. May be a local absolute path,
30
+ * workspace-relative path, or remote URI depending on the backend.
31
+ */
32
+ displayPath: string
33
+ }
34
+ ```
35
+
36
+ 后端拥有文件版本 token,即 write/edit 所守卫的新鲜度 token。策略插件存储它们以进行陈旧检查;消费方不解释其内容。两个 id 都是品牌化的不透明字符串。
37
+
38
+ ```ts type-equiv
39
+ /**
40
+ * Opaque key for stale guards and target lookup. The local backend uses a
41
+ * realpath-like string; a remote backend might use a workspace URI or file id.
42
+ * Consumers MUST NOT parse it or assume it is a local absolute path.
43
+ */
44
+ type FsTargetKey = Branded<'FsTargetKey'>
45
+ ```
46
+
47
+ ```ts type-equiv
48
+ /**
49
+ * Opaque file-version token — the freshness token a write/edit guards against.
50
+ * The local backend derives it from high-resolution stat identity and freshness
51
+ * fields; a remote backend might use a revision id. The policy layer records it
52
+ * for stale checks; consumers may display related metadata but MUST NOT
53
+ * interpret this token.
54
+ */
55
+ type FsVersion = Branded<'FsVersion'>
56
+ ```
57
+
58
+ `stat` 返回元数据(从不返回内容),目标不存在时返回 `undefined`。`type` 让消费方在读取前拒绝目录和特殊文件;`size` 让文本消费方无需通过失败探测即可选择 `readText` 还是 `streamText`。文本消费方在消费 `streamText` 时执行自己的保留量上限。原始字节消费方调用 `readBytes(target, signal, maxBytes)`;其必填的完整内容上限会使已知或读取中发现的超限以 `FS_TOO_LARGE` 失败,不会截断结果或无界缓冲。
59
+
60
+ ```ts type-equiv
61
+ /**
62
+ * Metadata about a target — what {@link FileSystem.stat} returns. Lets the
63
+ * policy layer reject directories/special files before reading and choose
64
+ * `readText` vs `streamText` from `size` without probing by failure. `version`
65
+ * is the freshness token. `undefined` from `stat` means the target is absent.
66
+ */
67
+ interface FsInfo {
68
+ /** Opaque freshness token of the target right now. */
69
+ version: FsVersion
70
+ /** Whether the target is a regular file, a directory, or something else. */
71
+ type: 'file' | 'directory' | 'other'
72
+ /** Byte size of a regular file, when the backend can report it. */
73
+ size?: number
74
+ }
75
+ ```
76
+
77
+ `lstat` 是路径级、不跟随链接的元数据原语。它接收路径而不是 `FsTarget`,因为 `resolve` 会有意跟随 symlink 以产生稳定标识;需要检查信任边界的消费方可以先调用 `lstat`,在解析前拒绝 `symlink`。
78
+
79
+ ```ts type-equiv
80
+ /**
81
+ * Metadata about a path without following the final path component when it is a
82
+ * symbolic link. Unlike {@link FsInfo}, this path-level probe can report
83
+ * `symlink` so consumers with trust-boundary rules can reject repository-owned
84
+ * links before resolving a target.
85
+ */
86
+ interface FsPathInfo {
87
+ /** Opaque freshness token of the path entry right now. */
88
+ version: FsVersion
89
+ /** Whether the path entry is a regular file, directory, symlink, or other. */
90
+ type: 'file' | 'directory' | 'symlink' | 'other'
91
+ /** Byte size of the path entry, when the backend can report it. */
92
+ size?: number
93
+ }
94
+ ```
95
+
96
+ `listDir` 按稳定的名称顺序返回直接子条目。每个条目携带子项的 basename、类型、已解析目标,以及后端能报告时的廉价元数据。它禁止读取文件内容,因此 `size` 仅用于普通文件,`version` 来自元数据。已损坏或已消失的子项可以作为 `other` 返回且不带元数据;列出或解析子项元数据时的权限或后端 I/O 失败会以 `FS_PERMISSION_DENIED` 或 `FS_IO_ERROR` 使整个列表操作失败。
97
+
98
+ ```ts type-equiv
99
+ /**
100
+ * One direct child returned by {@link FileSystem.listDir}. Listing returns
101
+ * metadata and resolved targets only; it must not read file contents.
102
+ */
103
+ interface FsDirEntry {
104
+ /** Basename of the child inside the listed directory. */
105
+ name: string
106
+ /** Whether the child is a regular file, a directory, or something else. */
107
+ type: 'file' | 'directory' | 'other'
108
+ /** Resolved child target for follow-up operations. */
109
+ target: FsTarget
110
+ /** Opaque freshness token when the backend can report metadata cheaply. */
111
+ version?: FsVersion
112
+ /** Byte size of a regular file, when the backend can report it. */
113
+ size?: number
114
+ }
115
+ ```
116
+
117
+ ## 写入与编辑守卫(提供方约定)
118
+
119
+ `writeText` 和 `editText` 的版本守卫都是可选的:省略守卫时执行无条件的裸提供方变更,提供守卫时则执行相应的条件检查。`writeText` 的守卫是 `FsWriteIntent`:`createIfAbsent` 在目标缺失时创建,目标已存在时以 `FS_NOT_OBSERVED` 拒绝;即使目标在提供方初始探测后才出现,也必须拒绝,因为发布操作本身不得替换。`replaceIfVersion` 仅在目标存在且版本匹配时替换,否则报 `FS_STALE_VERSION`。省略 `expected` 则无条件创建或覆盖。联合类型本身只包含两种有守卫的意图;「无守卫」通过省略表达,因此 write 和 edit 都使用同一个可选的 `expected` 字段。
120
+
121
+ ```ts type-equiv
122
+ /**
123
+ * Guarded write intent. `createIfAbsent` rejects an existing target with
124
+ * `FS_NOT_OBSERVED`; `replaceIfVersion` rejects absence or mismatch with
125
+ * `FS_STALE_VERSION`. Omitting the intent from `writeText` means unconditional
126
+ * create-or-overwrite, not a third union arm.
127
+ */
128
+ type FsWriteIntent =
129
+ | { kind: 'createIfAbsent' }
130
+ | { kind: 'replaceIfVersion'; version: FsVersion }
131
+ ```
132
+
133
+ ```ts type-equiv
134
+ /** Outcome of a full-file write. */
135
+ interface FsWriteOutcome {
136
+ /** Whether the write created a new file or replaced an existing one. */
137
+ operation: 'create' | 'update'
138
+ /** Opaque version of the file after the write. */
139
+ version: FsVersion
140
+ /**
141
+ * The file's content BEFORE the write, or `null` when the file did not exist
142
+ * (a create) or the backend declined a contextual basis (for example, a
143
+ * binary/non-UTF-8 prior file or either overwrite side reaching its exclusive limit).
144
+ * LF-normalized storage text (the diff basis), never a diff — a consumer
145
+ * computes the result-time contextual diff from `before`/`after` when
146
+ * `before` is present, else falls back to a whole-file diff.
147
+ */
148
+ before: string | null
149
+ /** The file's content AFTER the write, LF-normalized to share `before`'s diff basis. */
150
+ after: string
151
+ }
152
+ ```
153
+
154
+ `editText` 是提供方级别的变更操作,而非在别处组合的 `read` 加 `write`。带守卫时,它在字面匹配之前先验证预期版本(因此对陈旧内容的编辑报 `FS_STALE_VERSION`,而非对更新内容的匹配失败);不带守卫时,它编辑当前内容。无论哪种路径,它都应用替换并原子写入——将匹配、行尾处理、陈旧检查和原子替换保持在一个变更临界区内——目标缺失时两条路径都报 `FS_STALE_VERSION`。
155
+
156
+ ```ts type-equiv
157
+ /** A literal-replacement edit request. */
158
+ interface FsEditRequest {
159
+ /** Literal non-empty text to replace. Must match exactly (after line-ending normalization). */
160
+ oldString: string
161
+ /** Literal replacement text. An empty string deletes the matched text. */
162
+ newString: string
163
+ /** Replace every match instead of requiring exactly one. */
164
+ replaceAll: boolean
165
+ }
166
+ ```
167
+
168
+ ```ts type-equiv
169
+ /** Outcome of a literal edit. */
170
+ interface FsEditOutcome {
171
+ /** Opaque version of the file after the edit. */
172
+ version: FsVersion
173
+ /**
174
+ * The file's content BEFORE the edit. Raw storage text (LF-normalized by the
175
+ * backend), never a diff — a consumer computes the result-time contextual diff
176
+ * (the applied hunk with context) from `before`/`after`.
177
+ */
178
+ before: string
179
+ /** The file's content AFTER the edit. */
180
+ after: string
181
+ }
182
+ ```
183
+
184
+ ## fs 策略事件(提供方约定词汇)
185
+
186
+ `dsh-fs` 拥有三个事件,由工具分发、策略插件监听,使事件发出方(`dsh-tool-fs`)与监听方(`dsh-fs-observation-policy`)共享词汇,而事件发出方无需依赖策略插件。它们只携带 `dsh-fs` 词汇加一个不透明的 `object` actor,不含面向模型的概念,也不含 agent/会话所有者结构。
187
+
188
+ `fs/write-intent` 与 `fs/edit-intent` 是**单槽决策 waterfall**:工具分发时附带一个默认 thunk(返回 `undefined`,即裸提供方),监听方完全决策而不调用 `next()`。该 slot 按注册顺序先到先得——由策略插件占据是部署约定,而非强制不变式。`fs/observed` 是一个即发即弃的记录事件,携带 `FsObservation`:存在于某个版本,或确认缺失。该事件通过普通 `ctx.emit` 分发;其监听方必须是同步的、仅产生副作用,因为工具不会捕获该 emit 抛出的异常——抛出异常的监听方可能取代读取操作原本待返回的错误,或使工具在变更已经成功后返回 `isError` 结果。下方生成的 [cordis surface](#cordis-surface) 展示确切签名。
189
+
190
+ ```ts type-equiv
191
+ /**
192
+ * One authoritative observation of a target. A present observation carries the
193
+ * version used by guarded replacement; an absent observation authorizes only a
194
+ * guarded create, never an edit.
195
+ */
196
+ type FsObservation =
197
+ | { readonly kind: 'present'; readonly version: FsVersion }
198
+ | { readonly kind: 'absent' }
199
+ ```
200
+
201
+ ## 执行上下文(策略插件)
202
+
203
+ 策略插件只需要足够的执行上下文,通过收窄 `fs/*` 事件携带的不透明 `object` actor 来推导观测状态的所有者。`ToolExecution` 包含必需的字段,因此 `dsh-tool-fs` 将其执行对象作为 actor 直接传递,而无需让 `dsh-fs-observation-policy` 导入工具、agent 或会话包。
204
+
205
+ ```ts type-equiv
206
+ /**
207
+ * Minimal structural view of a tool execution the policy plugin needs to derive
208
+ * an observed-state owner. `@deepseek-ai/dsh-tools`' `ToolExecution` contains
209
+ * these fields, so the tool passes its `exec` straight through as the opaque
210
+ * `object` actor on the `fs/*` events; this plugin narrows that actor to
211
+ * `FsObservationActor` without importing `dsh-tools`, `dsh-agent`, or `dsh-session`.
212
+ *
213
+ * The owner is `agent.session` when present. It is treated as an opaque object
214
+ * identity (a `WeakMap` key); this package never reads any of its fields.
215
+ */
216
+ interface FsObservationActor {
217
+ /** The agent on whose behalf the call runs, when there is one. */
218
+ agent?: {
219
+ /** The session that owns observed-file state, used as an opaque key. */
220
+ session?: object
221
+ }
222
+ }
223
+ ```
224
+
225
+ ## 读取结果(消费方 / 读取渲染)
226
+
227
+ 文本读取受行窗口、字节上限和后端限制约束。达到字节上限后,扫描仍会继续,但不再保留更多行,因此 `totalLines` 仍为精确值。面向模型的 `read` 工具渲染的结果纯粹是展示性的;不存在 `full`/`partial` 视图区分——授权基于新鲜度(工具发出表示目标存在的 `fs/observed` 事件,并直接携带 stat 的版本),因此任何窗口化读取在文件未变时都能授权后续的 write/edit。元数据未命中时,工具会在返回 `FS_NOT_FOUND` 前 emit 缺失观测,使后续带守卫的写入可以重新创建外部删除的目标,但不会授权 edit。拥有读取操作的执行器 `dsh-tool-fs` 实现读取窗口化并构造该结果;策略插件不执行这些操作。
228
+
229
+ ```ts type-equiv
230
+ /** Outcome of a bounded text read — what {@link formatReadOutput} renders. */
231
+ interface FileReadOutcome {
232
+ /** 1-based first line requested. */
233
+ offset: number
234
+ /** Returned lines, already numbered. */
235
+ lines: FileTextLine[]
236
+ /** Exact total line count in the file. */
237
+ totalLines: number
238
+ /** Whether selected output hit the byte cap. */
239
+ truncatedByBytes?: true
240
+ }
241
+ ```
242
+
243
+ ## 已观测文件状态(策略插件)
244
+
245
+ 已观测状态是 `dsh-fs-observation-policy` 插件内部持有的 `WeakMap<owner, Map<targetKey, FsObservation>>`。映射中没有条目表示未见;`{ kind: 'absent' }` 表示 `read` 的元数据未命中,或 `str_replace_editor` 的 `view`、`str_replace`、`insert` 命令发生元数据未命中,从而确认缺失;`{ kind: 'present', version }` 表示 read、write 或 edit 观测到该版本。写入决策把未见和缺失映射到 `createIfAbsent`,把存在映射到 `replaceIfVersion`;编辑决策把未见映射到 `FS_NOT_OBSERVED`,把缺失映射到 `FS_NOT_FOUND`,把存在映射到其版本守卫。所有者从事件 actor 推导(通常是 `exec.agent.session`),被视为不透明且从不读取。dispose(资源释放)时丢弃全部数据(HMR(热模块替换)安全),策略不执行任何文件系统 I/O。
246
+
247
+ ## 错误分类体系(提供方约定)
248
+
249
+ 文件系统故障使用稳定的 `FsErrorCode` 字符串,由 `FsError`(`HarnessError`)携带。工具注册表在错误结果上保留 `{ name, code }`,使重试、权限和 UI 层可以按 code 分支而无需解析文本。
250
+
251
+ ```ts type-equiv
252
+ /**
253
+ * Stable, machine-routable codes for filesystem failures. Carried on
254
+ * {@link FsError}; the tool registry exposes `{ name, code }` on `isError`
255
+ * results so retry/permission/UI layers can branch without parsing messages.
256
+ */
257
+ type FsErrorCode =
258
+ | 'FS_NOT_FOUND'
259
+ | 'FS_NOT_DIRECTORY'
260
+ | 'FS_NOT_TEXT'
261
+ | 'FS_NOT_REGULAR_FILE'
262
+ | 'FS_TOO_LARGE'
263
+ | 'FS_PERMISSION_DENIED'
264
+ | 'FS_SANDBOX_DENIED'
265
+ | 'FS_IO_ERROR'
266
+ | 'FS_STALE_VERSION'
267
+ | 'FS_NOT_OBSERVED'
268
+ | 'FS_AMBIGUOUS_EDIT'
269
+ | 'FS_EDIT_NOT_FOUND'
270
+ | 'FS_ABORTED'
271
+ ```
272
+
273
+ 目录列表使用 `FS_NOT_DIRECTORY`、`FS_PERMISSION_DENIED` 与 `FS_IO_ERROR` 区分已存在但并非目录的目标、被拒绝的列表操作和意外的后端 I/O 失败。`FS_SANDBOX_DENIED` 是强制执行沙箱的后端(`dsh-fs-sandbox`)所作的策略拒绝——模式边界拒绝了写入/编辑——与 `FS_PERMISSION_DENIED`(宿主内核拒绝)不同。`FS_NOT_OBSERVED` 表示策略插件没有此所有者的先前观测记录(或 `createIfAbsent` 遇到了现有文件)。`FS_NOT_FOUND` 也表示策略因确认缺失而拒绝 edit。`FS_STALE_VERSION` 表示后端版本不再与观测到的版本匹配(或提供方本身收到针对缺失目标的 edit)。新鲜度授权没有部分/完整之分,因此不存在 `FS_PARTIAL_OBSERVATION`。
274
+
275
+ ## 文件 IO 不设超时
276
+
277
+ `read`/`write`/`edit` **不**接受 `timeoutMs`,提供方约定也不设置截止时间——不同于 bash 与 web(它们消费 [`@deepseek-ai/dsh-timeout`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/util/timeout/README.md))以及 subprocess 支撑的 `glob`/`grep`(其声明的 `timeoutMs` 由 `@deepseek-ai/dsh-tool-call-timeout-policy` 强制执行):那些是进程支撑的,截止时间可以真正终止工作。本地系统调用至多是尽力中止——超时无法迫使进行中的 `fsync`/`rename` 停下,因此这里的 `timeoutMs` 会成为 seam 无法强制执行的截止时间,而且恰好落在「显式优于隐式」禁止隐式默认值的位置。取消仍通过工具执行 signal 传播,在系统调用边界尽力中止。
278
+
279
+ ## 服务与插件
280
+
281
+ `FileSystem`(`ctx.fs`,abstract)拥有提供方原语:`resolve`、`processPath`、`fileUrl`、`contains`、`stat`、`lstat`、`readText`、`streamText`、`readBytes`、`listDir`、`writeText` 与 `editText`。`dsh-fs-observation-policy` **不注册服务**——它是一个通过 `fs/*` 事件门禁添加策略的插件:根据未见/缺失/存在状态对写入与编辑意图 waterfall 作出决策,并记录 `FsObservation` 值。执行器是 `dsh-tool-fs`:它通过 `ctx.fs` 读取/写入/编辑,分发 waterfall,并 emit 记录事件。下方生成的 [`ctx.fs` 小节](#ctxfs--filesystem-abstract-seam) 展示确切的 `ctx.fs` 签名。
282
+
283
+ <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
284
+
285
+ <a id="cordis-surface"></a>
286
+
287
+ ## Cordis API
288
+
289
+ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
290
+
291
+ <a id="ctxfs--filesystem-abstract-seam"></a>
292
+
293
+ ### `ctx.fs` — `FileSystem` (abstract seam)
294
+
295
+ Abstract filesystem provider. Targets must preserve identity across aliases; reads expose regular UTF-8 text or typed errors, listings are stable and content-free, and mutations are atomic. Optional guards add stale protection without changing the unguarded provider contract.
296
+
297
+ ```ts cordis-catalog
298
+ /**
299
+ * Resolve a model/plugin-supplied path into a stable {@link FsTarget}. May perform I/O (a
300
+ * remote/sandboxed backend may need a round-trip to map a path to a stable identity), hence
301
+ * async even though the local backend only normalizes + realpaths.
302
+ *
303
+ * @param path - the path to resolve; relative paths resolve against `opts.cwd`.
304
+ * @param opts - optional cwd override and cancellation signal.
305
+ * @returns the stable target; the same file yields the same `targetKey`.
306
+ */
307
+ abstract resolve(path: string, opts?: { cwd?: string; signal?: AbortSignal }): Promise<FsTarget>
308
+
309
+ /**
310
+ * Return the canonical absolute path a subprocess in this filesystem's
311
+ * execution world can open. The path is deliberately separate from
312
+ * {@link FsTarget.targetKey}: consumers may pass this value to another OS
313
+ * capability, but must continue treating the target key as opaque.
314
+ * @param target - the resolved target whose process path is required.
315
+ * @returns an absolute path in the backend's execution world.
316
+ */
317
+ abstract processPath(target: FsTarget): string
318
+
319
+ /**
320
+ * Return the canonical `file:` URI for a target in this filesystem's
321
+ * execution world. Backends own URI encoding because the host platform may
322
+ * differ from the execution platform.
323
+ * @param target - the resolved target to encode.
324
+ * @returns the target's canonical file URI.
325
+ */
326
+ abstract fileUrl(target: FsTarget): string
327
+
328
+ /**
329
+ * Test canonical containment without exposing or parsing backend target
330
+ * keys. Both targets must come from this provider.
331
+ * @param parent - canonical directory target.
332
+ * @param child - canonical candidate target.
333
+ * @returns true when `child` is `parent` or a descendant of it.
334
+ */
335
+ abstract contains(parent: FsTarget, child: FsTarget): boolean
336
+
337
+ /**
338
+ * Return target metadata, or `undefined` when the target does not exist.
339
+ * @param target - the resolved target to stat.
340
+ * @param signal - aborts the metadata round-trip.
341
+ * @returns metadata only, never content; undefined for an absent target.
342
+ */
343
+ abstract stat(target: FsTarget, signal?: AbortSignal): Promise<FsInfo | undefined>
344
+
345
+ /**
346
+ * Return path metadata without following the final path component when it is a
347
+ * symbolic link. This is intentionally path-shaped, not target-shaped:
348
+ * {@link resolve} follows symlinks to produce the stable identity used by
349
+ * normal reads/writes, while `lstat` lets a consumer reject the path itself
350
+ * before that follow happens.
351
+ *
352
+ * `opts.cwd` follows {@link resolve}'s cwd rules. `undefined` means the path is
353
+ * absent.
354
+ * @param path - the path to inspect; relative paths resolve against `opts.cwd`.
355
+ * @param opts - `cwd` overrides the backend's default base for relative paths.
356
+ * @param signal - aborts the metadata round-trip.
357
+ * @returns metadata only, never content; undefined for an absent path.
358
+ */
359
+ abstract lstat(path: string, opts?: { cwd?: string }, signal?: AbortSignal): Promise<FsPathInfo | undefined>
360
+
361
+ /**
362
+ * Read the whole regular text file as a single decoded string.
363
+ * @param target - the resolved target to read.
364
+ * @param signal - aborts the read.
365
+ * @returns the full decoded UTF-8 content.
366
+ */
367
+ abstract readText(target: FsTarget, signal?: AbortSignal): Promise<string>
368
+
369
+ /**
370
+ * Stream the whole regular text file as decoded text chunks (same text
371
+ * semantics as {@link readText}, for large files). The backend owns
372
+ * cross-chunk UTF-8 decoding and binary rejection so the policy layer never
373
+ * touches raw bytes.
374
+ * @param target - the resolved target to read.
375
+ * @param signal - aborts the stream, including between chunks.
376
+ * @returns the chunk iterable, decoded and validated like {@link readText}.
377
+ */
378
+ abstract streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>
379
+
380
+ /**
381
+ * Read the whole regular file as raw bytes with no decoding or binary
382
+ * rejection. The bound lives at this seam so a backend can never buffer an
383
+ * unbounded file: a target known or discovered to exceed `maxBytes` fails
384
+ * with `FS_TOO_LARGE` instead of returning a truncated result.
385
+ * @param target - the resolved target to read.
386
+ * @param signal - aborts the read.
387
+ * @param maxBytes - inclusive byte cap on the complete content.
388
+ * @returns the full raw content, at most `maxBytes` long.
389
+ */
390
+ abstract readBytes(target: FsTarget, signal: AbortSignal | undefined, maxBytes: number): Promise<Uint8Array>
391
+
392
+ /**
393
+ * List direct children of a directory in stable name order. Returns resolved
394
+ * child targets plus cheap metadata only; never reads file contents.
395
+ * @param target - the resolved directory target.
396
+ * @param signal - aborts the listing.
397
+ * @returns one entry per direct child, in stable name order.
398
+ */
399
+ abstract listDir(target: FsTarget, signal?: AbortSignal): Promise<FsDirEntry[]>
400
+
401
+ /**
402
+ * Atomically create or replace UTF-8 text. `expected` guards intent and
403
+ * staleness; omission allows unconditional overwrite.
404
+ * @param target - the resolved target to write.
405
+ * @param content - the full new file content.
406
+ * @param expected - the write intent guarding the write; omit for unconditional.
407
+ * @param signal - aborts before atomic publication takes effect.
408
+ * @param sandboxPolicy - the per-call mode and workspace root this write
409
+ * runs under; a sandboxing backend fences the write by it, the bare backend
410
+ * ignores it. Omit to leave the backend its own default.
411
+ * @returns the outcome, including the version the write produced.
412
+ */
413
+ abstract writeText( target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal, sandboxPolicy?: SandboxExecutionPolicy, ): Promise<FsWriteOutcome>
414
+
415
+ /**
416
+ * Atomically edit literal text. When supplied, the version guard is checked
417
+ * before matching so stale content reports `FS_STALE_VERSION`; omission edits
418
+ * the current content without a freshness precondition.
419
+ * @param target - the resolved target to edit.
420
+ * @param edit - the literal search/replace request.
421
+ * @param expected - the version guard; omit for an unconditional edit.
422
+ * @param signal - aborts before atomic publication takes effect.
423
+ * @param sandboxPolicy - the per-call mode and workspace root this edit runs
424
+ * under; a sandboxing backend fences the edit by it, the bare backend
425
+ * ignores it. Omit to leave the backend its own default.
426
+ * @returns the outcome, including the version the edit produced.
427
+ */
428
+ abstract editText( target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal, sandboxPolicy?: SandboxExecutionPolicy, ): Promise<FsEditOutcome>
429
+ ```
430
+
431
+ Types: [SandboxExecutionPolicy](./sandbox.md)
432
+
433
+ Source: [`packages/fs/fs/src/index.ts:86`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs/src/index.ts)
434
+
435
+ <a id="fs-events"></a>
436
+
437
+ ### `fs/*` events
438
+
439
+ <a id="fsedit-intent--waterfall"></a>
440
+
441
+ #### `fs/edit-intent` — waterfall
442
+
443
+ Single-slot decision for the next FileSystem.editText. Calling `next()` yields an unconditional edit; the first returned guard wins.
444
+
445
+ ```ts cordis-catalog
446
+ /**
447
+ * Single-slot decision for the next {@link FileSystem.editText}. Calling
448
+ * `next()` yields an unconditional edit; the first returned guard wins.
449
+ * @param target - the resolved target about to be edited.
450
+ * @param actor - the opaque tool-execution context the decider keys off.
451
+ * @mode waterfall
452
+ */
453
+ 'fs/edit-intent'(target: FsTarget, actor: object | undefined, next: () => { version: FsVersion } | undefined | Promise<{ version: FsVersion } | undefined>): Promise<{ version: FsVersion } | undefined>
454
+ ```
455
+
456
+ Source: [`packages/fs/fs/src/index.ts:66`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs/src/index.ts)
457
+
458
+ <a id="fsobserved--emit"></a>
459
+
460
+ #### `fs/observed` — emit
461
+
462
+ Record an authoritative positive or negative observation. Listeners must be synchronous recorders: throws fail the tool call and returned promises are not awaited.
463
+
464
+ ```ts cordis-catalog
465
+ /**
466
+ * Record an authoritative positive or negative observation. Listeners must
467
+ * be synchronous recorders: throws fail the tool call and returned promises
468
+ * are not awaited.
469
+ * @param target - the target whose presence or absence was observed.
470
+ * @param observation - present with its version, or confirmed absent.
471
+ * @param actor - the observing tool-execution context; undefined records nothing useful.
472
+ * @mode emit
473
+ */
474
+ 'fs/observed'(target: FsTarget, observation: FsObservation, actor: object | undefined): void
475
+ ```
476
+
477
+ Source: [`packages/fs/fs/src/index.ts:76`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs/src/index.ts)
478
+
479
+ <a id="fswrite-intent--waterfall"></a>
480
+
481
+ #### `fs/write-intent` — waterfall
482
+
483
+ Single-slot decision for the next FileSystem.writeText. Calling `next()` yields the bare provider's unconditional write; the first listener that returns an intent owns the decision rather than composing with peers.
484
+
485
+ ```ts cordis-catalog
486
+ /**
487
+ * Single-slot decision for the next {@link FileSystem.writeText}. Calling
488
+ * `next()` yields the bare provider's unconditional write; the first listener
489
+ * that returns an intent owns the decision rather than composing with peers.
490
+ * @param target - the resolved target about to be written.
491
+ * @param actor - the opaque tool-execution context the decider keys off.
492
+ * @mode waterfall
493
+ */
494
+ 'fs/write-intent'(target: FsTarget, actor: object | undefined, next: () => FsWriteIntent | undefined | Promise<FsWriteIntent | undefined>): Promise<FsWriteIntent | undefined>
495
+ ```
496
+
497
+ Source: [`packages/fs/fs/src/index.ts:58`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs/src/index.ts)
498
+ <!-- END GENERATED cordis-surface -->