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.md"
3
+ outline: [2,3]
4
+ ---
5
+
6
+ # User Credentials
7
+
8
+ The credential seam of [dsh-credentials](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/credentials/credentials) keeps secrets out of configuration: settings sections and `cordis.yml` entries carry *references* (environment-variable names), providers such as [dsh-credentials-local](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/credentials/credentials-local) own the values, and consumers resolve a reference once per operation — the LLM adapters resolve once per model request, so a rotated credential reaches the very next request without any restart. One seam-wide rule binds every provider: an empty stored value is absent everywhere.
9
+
10
+ Source: [`packages/credentials/credentials/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/credentials/credentials/src/index.ts)
11
+
12
+ ## Identity
13
+
14
+ A reference names one credential as a POSIX-style environment-variable name. The brand prevents callers from mixing credential references with other strings passed between packages or processes; construction validates the shell-identifier syntax.
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
+ ## Resolution
22
+
23
+ `resolve(ref)` returns the value with the provider-defined source layer that supplied it, or `undefined` while unconfigured. Consumers re-resolve at each operation and never cache across operations — that per-operation read is the hot-update mechanism.
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
+ ## Description
36
+
37
+ `describe(ref)` answers configuration surfaces without ever exposing a value: whether the reference resolves, from which layer, and whether `set` would currently succeed. The local provider reports a reference supplied by the live process environment as `writable: false` — a write would appear to succeed while resolution kept returning the shadowing value, so the seam rejects it and the UI can render the reference read-only up front.
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
+ ## Change commits
52
+
53
+ `credentials/updated (ref)` fires after a committed change to a provider-managed source — a `set`, an `unset`, or an external edit observed in storage. Ambient process-environment changes are not observable and never emit. Consumers do not need the event (they re-resolve per operation); it exists for configuration surfaces refreshing a "configured" badge.
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.md"
3
+ outline: [2,3]
4
+ ---
5
+
6
+ # Filesystem
7
+
8
+ The optional filesystem capability has four parts: [dsh-fs](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/fs/fs) owns `ctx.fs` and atomic text operations with optional guards, [dsh-fs-local](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/fs/fs-local) implements local disk, [dsh-fs-observation-policy](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/fs/fs-observation-policy) records observed presence or absence and adds freshness rules through events rather than a service, and [dsh-tool-fs](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/fs/tool-fs) directly executes model-facing read/write/edit calls and renders windows. It is outside the agent-loop spine; alternate backends do not change policy or tool schemas.
9
+
10
+ `dsh-fs-observation-policy` is optional. Without it, the `FileSystem` Service Definition, a provider, and the `dsh-tool-fs` Consumer form the complete, unconstrained filesystem seam: `write` unconditionally creates or overwrites, and `edit` unconditionally replaces literal text. The policy plugin changes these operations by deciding the `fs/*` waterfalls. Removing it does not break the tool because the tool calls `ctx.fs` and dispatches events; it does not call policy methods. A deployment that loads `dsh-tool-fs` is expected to also load `dsh-fs-observation-policy` so the default behavior is read-before-write/edit.
11
+
12
+ Provider source: [`packages/fs/fs/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs/src/types.ts) and [`packages/fs/fs/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/fs/fs/src/index.ts). Policy source: [`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). Read-rendering source: [`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
+ ## Target identity and metadata (provider contract)
15
+
16
+ Every operation resolves a user-supplied path to an opaque backend target first. Consumers may display `displayPath`, but must not parse `targetKey` (a branded opaque id) or assume it is a local absolute path.
17
+
18
+ Consumers that share the filesystem's execution world obtain cross-capability coordinates through the provider instead of interpreting that identity: `processPath(target)` returns the canonical absolute path a subprocess can open, `fileUrl(target)` returns its provider-platform `file:` URI, and `contains(parent, child)` tests canonical identity or descendant containment.
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
+ The backend owns file-version tokens — the freshness token a write/edit guards against. The policy plugin stores them for stale checks; consumers do not interpret them. Both ids are branded opaque strings.
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` returns metadata (never content), or `undefined` when the target is absent. `type` lets consumers reject directories and special files before reading, and `size` lets text consumers choose `readText` vs `streamText` without probing by failure. A text consumer applies its own retention ceiling while consuming `streamText`. Raw-byte consumers use `readBytes(target, signal, maxBytes)`; its required complete-content cap makes a known or discovered overflow fail with `FS_TOO_LARGE` instead of truncating or buffering without a bound.
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` is the path-level no-follow metadata primitive. It takes a path instead of an `FsTarget` because `resolve` intentionally follows symlinks to produce stable identity; consumers that need trust-boundary checks can call `lstat` first and reject `symlink` before resolving.
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` returns direct child entries in stable name order. Each entry carries the child basename, type, resolved target, and cheap metadata when the backend can report it. It must not read file contents, so `size` is only for regular files and `version` is metadata-derived. Broken or disappeared children may be returned as `other` without metadata; permission or backend I/O failures while listing or resolving child metadata fail the whole listing with `FS_PERMISSION_DENIED` or `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
+ ## Write and edit guards (provider contract)
118
+
119
+ Both `writeText` and `editText` take their version guard OPTIONALLY: omit it for an unconditional (bare-provider) mutation, supply it to guard. `writeText`'s guard is an `FsWriteIntent` — `createIfAbsent` creates a missing target and rejects an existing one with `FS_NOT_OBSERVED`, including a target that appears after the provider's initial probe because publication itself must be no-replace; `replaceIfVersion` replaces only when the target exists at the observed version, else `FS_STALE_VERSION`. Omitting `expected` unconditionally creates-or-overwrites. The union itself carries only the two guarded intents; "no guard" is expressed by omission, so write and edit both use the same optional `expected` field.
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` is a provider-level mutation, not a `read` plus `write` composed elsewhere. When guarded it verifies the expected version BEFORE literal matching (so a stale edit reports `FS_STALE_VERSION`, not a match failure against newer content); unguarded it edits the current content. Either way it applies the replacement and writes atomically — keeping matching, line-ending handling, the stale check, and atomic replacement inside one mutation critical section — and a missing target reports `FS_STALE_VERSION` on both paths.
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
+ ## The fs policy events (provider contract vocabulary)
185
+
186
+ `dsh-fs` owns three events the tool dispatches and the policy plugin listens for, so the emitter (`dsh-tool-fs`) and the listener (`dsh-fs-observation-policy`) share a vocabulary without the emitter depending on the policy plugin. They carry only `dsh-fs` vocabulary plus an opaque `object` actor — no model-facing concepts and no agent/session owner structure.
187
+
188
+ `fs/write-intent` and `fs/edit-intent` are **single-slot decision waterfalls**: the tool dispatches each with a default thunk returning `undefined` (the bare provider), and a listener fully decides without calling `next()`. The slot is first-wins by registration order — the policy plugin owning it is a deployment convention, not an enforced invariant. `fs/observed` is a fire-and-forget recording event carrying an `FsObservation`: present at a version or confirmed absent. It is dispatched with a plain `ctx.emit`; its listener MUST be synchronous and side-effect-only, because the tool does NOT guard the emit — a throwing listener can replace a read error or surface as the tool's `isError` result after a mutation already succeeded. The generated [cordis surface](#cordis-surface) below shows the exact signatures.
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
+ ## Execution context (policy plugin)
202
+
203
+ The policy plugin needs just enough execution context to derive the observed-state owner by narrowing the opaque `object` actor the `fs/*` events carry. `ToolExecution` has the required fields, so `dsh-tool-fs` passes its execution object through as the actor without making `dsh-fs-observation-policy` import the tool, agent, or session packages.
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
+ ## Read outcome (consumer / read rendering)
226
+
227
+ A text read is bounded by line window, byte cap, and backend limits. After the byte cap is reached, scanning continues without retaining more lines so `totalLines` remains exact. The result the model-facing `read` tool renders is purely presentational; there is no `full`/`partial` view — authorization is freshness-based (the tool emits a present `fs/observed` directly with the stat's version), so any windowed read can authorize a later write/edit when the file is unchanged. A metadata miss emits an absent observation before the tool returns `FS_NOT_FOUND`, allowing a later guarded write to recreate an externally deleted target without authorizing edit. `dsh-tool-fs`, the executor that owns the read, implements read windowing and constructs this result; the policy plugin does not.
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
+ ## Observed-file state (policy plugin)
244
+
245
+ Observed state is a `WeakMap<owner, Map<targetKey, FsObservation>>` held inside the `dsh-fs-observation-policy` plugin. Missing map entry means unseen; `{ kind: 'absent' }` means a `read` or `str_replace_editor` `view`, `str_replace`, or `insert` metadata miss confirmed absence; `{ kind: 'present', version }` means a read, write, or edit observed that version. The write decision maps unseen and absent to `createIfAbsent`, while present maps to `replaceIfVersion`; the edit decision maps unseen to `FS_NOT_OBSERVED`, absent to `FS_NOT_FOUND`, and present to its version guard. The owner is derived from the event actor (normally `exec.agent.session`), treated as opaque and never read. Disposal drops everything (HMR safety), and the policy performs no filesystem I/O.
246
+
247
+ ## Error taxonomy (provider contract)
248
+
249
+ Filesystem failures use stable `FsErrorCode` strings carried by `FsError` (`HarnessError`). The tool registry preserves `{ name, code }` on error results, so retry, permission, and UI layers can branch without parsing text.
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`, and `FS_IO_ERROR` are used by directory listing to distinguish an existing non-directory target, a denied listing, and an unexpected backend I/O failure. `FS_SANDBOX_DENIED` is a POLICY refusal from a sandbox-enforcing backend (`dsh-fs-sandbox`) — the mode fence denied a write/edit — distinct from `FS_PERMISSION_DENIED` (the host kernel refusing). `FS_NOT_OBSERVED` means the policy plugin has no prior-observation record for this owner (or a `createIfAbsent` hit an existing file). `FS_NOT_FOUND` also represents an edit rejected from confirmed absence. `FS_STALE_VERSION` means the backend version no longer matches the observed one (or the provider itself receives an edit for a missing target). Freshness authorization has no partial/full distinction, so there is no `FS_PARTIAL_OBSERVATION`.
274
+
275
+ ## No timeouts on file IO
276
+
277
+ `read`/`write`/`edit` take **no** `timeoutMs`, and the provider contract arms no deadline — unlike bash and web (which consume [`@deepseek-ai/dsh-timeout`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/util/timeout/README.md)) and the subprocess-backed `glob`/`grep` (whose declared `timeoutMs` is enforced by `@deepseek-ai/dsh-tool-call-timeout-policy`): those are process-backed, where a deadline can really kill the work. A local syscall is best-effort-abortable at most — a timeout could not force an in-progress `fsync`/`rename` to stop, so a `timeoutMs` here would be a deadline the seam cannot enforce, and an implicit default in the exact place explicit-over-implicit forbids. Cancellation still propagates through the tool-execution signal for best-effort abort at syscall boundaries.
278
+
279
+ ## The service and the plugin
280
+
281
+ `FileSystem` (`ctx.fs`, abstract) owns the provider primitives: `resolve`, `processPath`, `fileUrl`, `contains`, `stat`, `lstat`, `readText`, `streamText`, `readBytes`, `listDir`, `writeText`, and `editText`. `dsh-fs-observation-policy` registers **no service** — it is a plugin that adds policy through the `fs/*` event gate: it decides the write/edit intent waterfalls from unseen/absent/present state and records `FsObservation` values. The executor is `dsh-tool-fs`: it reads/writes/edits through `ctx.fs`, dispatches the waterfalls, and emits the recording event. The generated [`ctx.fs` section](#ctxfs--filesystem-abstract-seam) below shows the exact signatures.
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 -->