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.
- package/LICENSE +21 -0
- package/README.md +56 -0
- package/cordis.patch.yml +12 -0
- package/kb/INDEX.md +210 -0
- package/kb/README.md +69 -0
- package/kb/extra/AGENTS.md +75 -0
- package/kb/extra/api-gateway.md +164 -0
- package/kb/extra/api-gateway.zh.md +164 -0
- package/kb/extra/cookbook/adding-a-vendored-package.md +59 -0
- package/kb/extra/cookbook/adding-a-vendored-package.zh.md +59 -0
- package/kb/extra/cookbook/maintaining-dsh-code-review.md +64 -0
- package/kb/extra/cookbook/maintaining-dsh-code-review.zh.md +64 -0
- package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
- package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
- package/kb/extra/defensive-patterns.md +33 -0
- package/kb/extra/defensive-patterns.zh.md +33 -0
- package/kb/extra/development.md +171 -0
- package/kb/extra/development.zh.md +171 -0
- package/kb/extra/event-producer-consumer.md +76 -0
- package/kb/extra/event-producer-consumer.zh.md +78 -0
- package/kb/extra/glossary.md +45 -0
- package/kb/extra/glossary.zh.md +45 -0
- package/kb/extra/graph-atlas.md +24 -0
- package/kb/extra/graph-atlas.zh.md +26 -0
- package/kb/extra/i18n/README.md +60 -0
- package/kb/extra/i18n/README.zh.md +60 -0
- package/kb/extra/i18n/style-samples.md +87 -0
- package/kb/extra/i18n/terminology.md +214 -0
- package/kb/extra/i18n/translation-prompt.md +263 -0
- package/kb/extra/i18n/translation-rules.md +69 -0
- package/kb/extra/i18n/translation-rules.zh.md +69 -0
- package/kb/extra/module-graph.md +1641 -0
- package/kb/extra/module-graph.zh.md +1643 -0
- package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +113 -0
- package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
- package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
- package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
- package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
- package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
- package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
- package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
- package/kb/extra/postmortem/README.md +18 -0
- package/kb/extra/postmortem/README.zh.md +18 -0
- package/kb/extra/rescope.md +53 -0
- package/kb/extra/rescope.zh.md +53 -0
- package/kb/extra/subsystems/attachment.md +125 -0
- package/kb/extra/subsystems/attachment.zh.md +125 -0
- package/kb/extra/subsystems/extensions.md +364 -0
- package/kb/extra/subsystems/extensions.zh.md +364 -0
- package/kb/extra/subsystems/feedback.md +266 -0
- package/kb/extra/subsystems/feedback.zh.md +266 -0
- package/kb/extra/testing.md +49 -0
- package/kb/extra/testing.zh.md +49 -0
- package/kb/extra/web-styling.md +25 -0
- package/kb/extra/web-styling.zh.md +25 -0
- package/kb/meta/search-index.json +1328 -0
- package/kb/meta/site-pages.txt +168 -0
- package/kb/meta/source.json +13 -0
- package/kb/meta/topics.md +75 -0
- package/kb/site/develop/basic/config.md +108 -0
- package/kb/site/develop/basic/index.md +146 -0
- package/kb/site/develop/basic/publish.md +185 -0
- package/kb/site/develop/basic/tool.md +54 -0
- package/kb/site/develop/cordis-tutorial/01-first-plugin.md +95 -0
- package/kb/site/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
- package/kb/site/develop/cordis-tutorial/03-services.md +98 -0
- package/kb/site/develop/cordis-tutorial/04-events.md +144 -0
- package/kb/site/develop/cordis-tutorial/05-config.md +84 -0
- package/kb/site/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
- package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +107 -0
- package/kb/site/develop/cordis-tutorial/index.md +62 -0
- package/kb/site/develop/framework/events.md +145 -0
- package/kb/site/develop/framework/index.md +139 -0
- package/kb/site/develop/framework/service.md +152 -0
- package/kb/site/develop/practice/index.md +157 -0
- package/kb/site/develop/practice/llm-adapter.md +190 -0
- package/kb/site/en/develop/basic/config.md +108 -0
- package/kb/site/en/develop/basic/index.md +146 -0
- package/kb/site/en/develop/basic/publish.md +185 -0
- package/kb/site/en/develop/basic/tool.md +54 -0
- package/kb/site/en/develop/cordis-tutorial/01-first-plugin.md +95 -0
- package/kb/site/en/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
- package/kb/site/en/develop/cordis-tutorial/03-services.md +98 -0
- package/kb/site/en/develop/cordis-tutorial/04-events.md +144 -0
- package/kb/site/en/develop/cordis-tutorial/05-config.md +84 -0
- package/kb/site/en/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
- package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +107 -0
- package/kb/site/en/develop/cordis-tutorial/index.md +60 -0
- package/kb/site/en/develop/framework/events.md +145 -0
- package/kb/site/en/develop/framework/index.md +139 -0
- package/kb/site/en/develop/framework/service.md +150 -0
- package/kb/site/en/develop/practice/index.md +157 -0
- package/kb/site/en/develop/practice/llm-adapter.md +190 -0
- package/kb/site/en/guide/providers-custom-form.png +0 -0
- package/kb/site/en/guide/providers-models-page.png +0 -0
- package/kb/site/en/guide/providers.md +100 -0
- package/kb/site/en/guide/python-sdk.md +106 -0
- package/kb/site/en/guide/quickstart.md +32 -0
- package/kb/site/en/index.md +8 -0
- package/kb/site/en/reference/agent-lifecycle.md +86 -0
- package/kb/site/en/reference/capability-seams.md +475 -0
- package/kb/site/en/reference/config-catalog.md +3155 -0
- package/kb/site/en/reference/cookbook/adding-a-conversation-node.md +235 -0
- package/kb/site/en/reference/cookbook/adding-a-package.md +120 -0
- package/kb/site/en/reference/cookbook/adding-a-settings-card.md +102 -0
- package/kb/site/en/reference/cookbook/adding-a-tool.md +96 -0
- package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +45 -0
- package/kb/site/en/reference/cookbook/extension-cookbook.md +131 -0
- package/kb/site/en/reference/cordis-api/context.md +368 -0
- package/kb/site/en/reference/cordis-api/events.md +211 -0
- package/kb/site/en/reference/cordis-api/fiber.md +379 -0
- package/kb/site/en/reference/cordis-api/inherited.md +43 -0
- package/kb/site/en/reference/cordis-api/registry.md +156 -0
- package/kb/site/en/reference/cordis-api/service.md +106 -0
- package/kb/site/en/reference/cordis-primer.md +46 -0
- package/kb/site/en/reference/index.md +131 -0
- package/kb/site/en/reference/persistence-catalog.md +949 -0
- package/kb/site/en/reference/subsystems/approval.md +173 -0
- package/kb/site/en/reference/subsystems/client-modules.md +121 -0
- package/kb/site/en/reference/subsystems/code-runtime.md +194 -0
- package/kb/site/en/reference/subsystems/commands.md +190 -0
- package/kb/site/en/reference/subsystems/compaction.md +241 -0
- package/kb/site/en/reference/subsystems/core.md +1073 -0
- package/kb/site/en/reference/subsystems/credentials.md +136 -0
- package/kb/site/en/reference/subsystems/filesystem.md +498 -0
- package/kb/site/en/reference/subsystems/goal.md +280 -0
- package/kb/site/en/reference/subsystems/index.md +58 -0
- package/kb/site/en/reference/subsystems/invariants.md +91 -0
- package/kb/site/en/reference/subsystems/jobs.md +293 -0
- package/kb/site/en/reference/subsystems/llm-streaming.md +920 -0
- package/kb/site/en/reference/subsystems/lsp.md +205 -0
- package/kb/site/en/reference/subsystems/permission-presets.md +134 -0
- package/kb/site/en/reference/subsystems/persistence.md +388 -0
- package/kb/site/en/reference/subsystems/plan.md +90 -0
- package/kb/site/en/reference/subsystems/sandbox.md +221 -0
- package/kb/site/en/reference/subsystems/schedule.md +189 -0
- package/kb/site/en/reference/subsystems/scope.md +62 -0
- package/kb/site/en/reference/subsystems/session-projection.md +265 -0
- package/kb/site/en/reference/subsystems/session-query.md +498 -0
- package/kb/site/en/reference/subsystems/session-reference.md +111 -0
- package/kb/site/en/reference/subsystems/session-telemetry.md +197 -0
- package/kb/site/en/reference/subsystems/session-title.md +207 -0
- package/kb/site/en/reference/subsystems/session.md +852 -0
- package/kb/site/en/reference/subsystems/settings.md +313 -0
- package/kb/site/en/reference/subsystems/shell.md +306 -0
- package/kb/site/en/reference/subsystems/skills.md +334 -0
- package/kb/site/en/reference/subsystems/spill.md +120 -0
- package/kb/site/en/reference/subsystems/storage.md +232 -0
- package/kb/site/en/reference/subsystems/subagent.md +737 -0
- package/kb/site/en/reference/subsystems/subprocess.md +327 -0
- package/kb/site/en/reference/subsystems/system-prompt.md +210 -0
- package/kb/site/en/reference/subsystems/terminal.md +187 -0
- package/kb/site/en/reference/subsystems/token-meter.md +93 -0
- package/kb/site/en/reference/subsystems/tools.md +723 -0
- package/kb/site/en/reference/subsystems/typert.md +339 -0
- package/kb/site/en/reference/subsystems/user-questions.md +181 -0
- package/kb/site/en/reference/subsystems/web-server.md +111 -0
- package/kb/site/en/reference/subsystems/web.md +202 -0
- package/kb/site/en/reference/subsystems/workflow.md +281 -0
- package/kb/site/en/reference/subsystems/workspace.md +231 -0
- package/kb/site/en/reference/tool-catalog.md +1877 -0
- package/kb/site/en/reference/tool-execution-pipeline.md +66 -0
- package/kb/site/guide/providers-custom-form.zh.png +0 -0
- package/kb/site/guide/providers-models-page.zh.png +0 -0
- package/kb/site/guide/providers.md +100 -0
- package/kb/site/guide/python-sdk.md +106 -0
- package/kb/site/guide/quickstart.md +32 -0
- package/kb/site/index.md +8 -0
- package/kb/site/reference/agent-lifecycle.md +86 -0
- package/kb/site/reference/capability-seams.md +475 -0
- package/kb/site/reference/config-catalog.md +3154 -0
- package/kb/site/reference/cookbook/adding-a-conversation-node.md +235 -0
- package/kb/site/reference/cookbook/adding-a-package.md +120 -0
- package/kb/site/reference/cookbook/adding-a-settings-card.md +102 -0
- package/kb/site/reference/cookbook/adding-a-tool.md +98 -0
- package/kb/site/reference/cookbook/adding-an-llm-adapter.md +45 -0
- package/kb/site/reference/cookbook/extension-cookbook.md +133 -0
- package/kb/site/reference/cordis-api/context.md +368 -0
- package/kb/site/reference/cordis-api/events.md +211 -0
- package/kb/site/reference/cordis-api/fiber.md +379 -0
- package/kb/site/reference/cordis-api/inherited.md +43 -0
- package/kb/site/reference/cordis-api/registry.md +156 -0
- package/kb/site/reference/cordis-api/service.md +106 -0
- package/kb/site/reference/cordis-primer.md +52 -0
- package/kb/site/reference/index.md +135 -0
- package/kb/site/reference/persistence-catalog.md +949 -0
- package/kb/site/reference/subsystems/approval.md +173 -0
- package/kb/site/reference/subsystems/client-modules.md +121 -0
- package/kb/site/reference/subsystems/code-runtime.md +194 -0
- package/kb/site/reference/subsystems/commands.md +190 -0
- package/kb/site/reference/subsystems/compaction.md +241 -0
- package/kb/site/reference/subsystems/core.md +1081 -0
- package/kb/site/reference/subsystems/credentials.md +136 -0
- package/kb/site/reference/subsystems/filesystem.md +498 -0
- package/kb/site/reference/subsystems/goal.md +280 -0
- package/kb/site/reference/subsystems/index.md +58 -0
- package/kb/site/reference/subsystems/invariants.md +91 -0
- package/kb/site/reference/subsystems/jobs.md +293 -0
- package/kb/site/reference/subsystems/llm-streaming.md +926 -0
- package/kb/site/reference/subsystems/lsp.md +205 -0
- package/kb/site/reference/subsystems/permission-presets.md +134 -0
- package/kb/site/reference/subsystems/persistence.md +388 -0
- package/kb/site/reference/subsystems/plan.md +90 -0
- package/kb/site/reference/subsystems/sandbox.md +221 -0
- package/kb/site/reference/subsystems/schedule.md +189 -0
- package/kb/site/reference/subsystems/scope.md +62 -0
- package/kb/site/reference/subsystems/session-projection.md +265 -0
- package/kb/site/reference/subsystems/session-query.md +498 -0
- package/kb/site/reference/subsystems/session-reference.md +111 -0
- package/kb/site/reference/subsystems/session-telemetry.md +197 -0
- package/kb/site/reference/subsystems/session-title.md +207 -0
- package/kb/site/reference/subsystems/session.md +854 -0
- package/kb/site/reference/subsystems/settings.md +313 -0
- package/kb/site/reference/subsystems/shell.md +306 -0
- package/kb/site/reference/subsystems/skills.md +334 -0
- package/kb/site/reference/subsystems/spill.md +120 -0
- package/kb/site/reference/subsystems/storage.md +232 -0
- package/kb/site/reference/subsystems/subagent.md +739 -0
- package/kb/site/reference/subsystems/subprocess.md +327 -0
- package/kb/site/reference/subsystems/system-prompt.md +210 -0
- package/kb/site/reference/subsystems/terminal.md +187 -0
- package/kb/site/reference/subsystems/token-meter.md +93 -0
- package/kb/site/reference/subsystems/tools.md +723 -0
- package/kb/site/reference/subsystems/typert.md +339 -0
- package/kb/site/reference/subsystems/user-questions.md +181 -0
- package/kb/site/reference/subsystems/web-server.md +111 -0
- package/kb/site/reference/subsystems/web.md +202 -0
- package/kb/site/reference/subsystems/workflow.md +281 -0
- package/kb/site/reference/subsystems/workspace.md +231 -0
- package/kb/site/reference/tool-catalog.md +1880 -0
- package/kb/site/reference/tool-execution-pipeline.md +66 -0
- package/package.json +40 -0
- package/scripts/rebuild-index.mjs +88 -0
- package/skills/dsh-plugin-dev-kb.md +66 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
---
|
|
2
|
+
editSource: "docs/subsystems/settings.zh.md"
|
|
3
|
+
outline: [2,3]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 用户设置
|
|
7
|
+
|
|
8
|
+
[dsh-settings](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/settings/settings) 的用户设置 seam 持有一份按 namespace 分节的用户文档,并把每个已注册 namespace 解析为:schema 默认值,然后注册方的组合 `base`,最后用户分节。[dsh-settings-file](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/settings/settings-file) 这类提供方存储原始文档并推送外部编辑;消费方插件注册 schema 后读取或观察解析值。组合配置仍留在 `cordis.yml`——namespace 只承载用户可编辑子集。
|
|
9
|
+
|
|
10
|
+
来源:[`packages/settings/settings/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/settings/settings/src/index.ts)
|
|
11
|
+
|
|
12
|
+
## 标识
|
|
13
|
+
|
|
14
|
+
namespace 命名用户文档中一个归插件所有的分节。brand 防止调用方将设置 namespace 与在包或进程之间传递的其他 id 混用;构造时校验小写 kebab-case 语法。
|
|
15
|
+
|
|
16
|
+
```ts type-equiv
|
|
17
|
+
/** Nominal id of one registered settings namespace. */
|
|
18
|
+
type SettingsNamespace = Branded<'SettingsNamespace'>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 注册
|
|
22
|
+
|
|
23
|
+
注册把 schemastery schema 绑定到调用方插件 fiber 上的 namespace——dispose(资源释放)该 fiber 即移除 namespace 及其观察者。options 携带组合层、owner 的生效时机,以及一个可选的、用于校验 schema 表达不了的约束的钩子。
|
|
24
|
+
|
|
25
|
+
```ts type-equiv
|
|
26
|
+
/** Registration options beyond the namespace schema. */
|
|
27
|
+
interface SettingsRegisterOptions<T> {
|
|
28
|
+
/** Composition-layer values resolved below the user layer (entry-config subset). */
|
|
29
|
+
base?: Partial<T>
|
|
30
|
+
/** Owner's effect timing, surfaced to configuration UIs; defaults to `live`. */
|
|
31
|
+
applies?: SettingsApplies
|
|
32
|
+
/**
|
|
33
|
+
* Reject a resolved section the owner could not act on, for constraints its
|
|
34
|
+
* schema cannot express — a cross-field requirement, or one field's validity
|
|
35
|
+
* depending on another's. Throwing here refuses the *write* that produced the
|
|
36
|
+
* value, so a caller learns at `update`/`replace`/`mutate` instead of storing
|
|
37
|
+
* something that would silently disable the owner.
|
|
38
|
+
*
|
|
39
|
+
* Kept separate from the schema because the schema is also what a
|
|
40
|
+
* configuration surface renders and what an absent section resolves through;
|
|
41
|
+
* folding a cross-field check into it would change both.
|
|
42
|
+
*
|
|
43
|
+
* Once the owner is registered, a stored section that fails this keeps the
|
|
44
|
+
* namespace's last good value and warns, exactly as a schema failure does,
|
|
45
|
+
* so an externally edited document cannot strand a running owner. At
|
|
46
|
+
* registration there is no last good value yet, so a stored section that
|
|
47
|
+
* already fails rejects the registration itself — again exactly as a schema
|
|
48
|
+
* failure does.
|
|
49
|
+
* @param value - the resolved section, schema-valid by construction.
|
|
50
|
+
*/
|
|
51
|
+
validate?: (value: T) => void
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`validate` 在 schema 接纳该值之后运行,因此它看到的默认值和组合 base 与 owner 实际看到的完全一致。`dsh-llm-pi-ai` 用它在写入处拒绝自己无法服务的提供方 profile,而不是先存下来、再让该 namespace 下每条路由失效。
|
|
56
|
+
|
|
57
|
+
`applies` 是 UI 提示而非机制:`restart` 的 owner 只是从不 watch,其值在构造期读取一次,配置界面可为待生效变更加标。
|
|
58
|
+
|
|
59
|
+
```ts type-equiv
|
|
60
|
+
/** When a namespace's changes take effect for its owner. */
|
|
61
|
+
type SettingsApplies = 'live' | 'restart'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Owner scope
|
|
65
|
+
|
|
66
|
+
scope 是面向 owner 的句柄。`update` 把稀疏 patch 只合并进用户分节(绝不进 `base`);`replace` 整体替换分节,是删除/重置路径——替换中缺席的键重新继承 `base` 与 schema 默认值。同一 namespace 的写入按调用顺序串行,解析值是深冻结快照。
|
|
67
|
+
|
|
68
|
+
```ts type-equiv
|
|
69
|
+
/** Owner-facing handle for one registered namespace. */
|
|
70
|
+
interface SettingsScope<T> {
|
|
71
|
+
/** Current resolved value: schema defaults, then `base`, then the user layer. */
|
|
72
|
+
get(): T
|
|
73
|
+
/**
|
|
74
|
+
* Observe committed changes to this namespace's resolved value. Invocations
|
|
75
|
+
* of one callback run asynchronously, one at a time, in commit order; a
|
|
76
|
+
* rejection is contained and logged like a sync throw. After the disposer
|
|
77
|
+
* returns, no further invocation starts — one already queued is skipped;
|
|
78
|
+
* one already started still settles, and service disposal waits for it.
|
|
79
|
+
* @param callback - invoked after each commit with the next and previous values.
|
|
80
|
+
* @returns the disposer removing this observer.
|
|
81
|
+
*/
|
|
82
|
+
watch(callback: (next: T, prev: T) => void | Promise<void>): () => void
|
|
83
|
+
/**
|
|
84
|
+
* Merge a partial patch into this namespace's user layer and persist it.
|
|
85
|
+
* @param patch - plain-object patch over the user section; JSON-compatible data
|
|
86
|
+
* only (non-JSON values reject with their path before anything persists).
|
|
87
|
+
*/
|
|
88
|
+
update(patch: object): Promise<void>
|
|
89
|
+
/**
|
|
90
|
+
* Replace this namespace's user section wholesale; absent keys re-inherit
|
|
91
|
+
* the composition `base` and schema defaults (`replace({})` resets all).
|
|
92
|
+
* @param section - the complete next user section; JSON-compatible data only,
|
|
93
|
+
* as for {@link update}.
|
|
94
|
+
*/
|
|
95
|
+
replace(section: object): Promise<void>
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 描述符
|
|
100
|
+
|
|
101
|
+
`describe()` 为配置界面序列化每个已注册 namespace:schemastery 的 `toJSON()` 封装结构驱动 schema 渲染的表单,解析值填充表单,分离出的 `base`/`user` 层让表单按字段是否出现在 user 层标注「用户已覆盖」。`describe({ redactSecrets: true })`——每个对外传输接口都必须传入——从三层剥离 `role('secret')` 字段并枚举其 `{path, set}` slot,页面因此能渲染只写输入框而永远收不到机密值。
|
|
102
|
+
|
|
103
|
+
```ts type-equiv
|
|
104
|
+
/** One registered namespace as surfaced to configuration UIs. */
|
|
105
|
+
interface SettingsDescriptor {
|
|
106
|
+
/** The registered namespace. */
|
|
107
|
+
ns: SettingsNamespace
|
|
108
|
+
/** Serialized schemastery schema (`schema.toJSON()`). */
|
|
109
|
+
schema: unknown
|
|
110
|
+
/** Current resolved value. */
|
|
111
|
+
value: unknown
|
|
112
|
+
/**
|
|
113
|
+
* Monotonic revision of the raw user section this descriptor was read at.
|
|
114
|
+
* Send it back as `expectedRevision` on a write to refuse a stale one.
|
|
115
|
+
*/
|
|
116
|
+
revision: number
|
|
117
|
+
/** Registrant's composition `base` layer (detached), when one was declared. */
|
|
118
|
+
base?: unknown
|
|
119
|
+
/**
|
|
120
|
+
* Raw user section from the stored document (detached), when one exists and
|
|
121
|
+
* is well-formed; a field's presence here is what marks it user-overridden.
|
|
122
|
+
*/
|
|
123
|
+
user?: unknown
|
|
124
|
+
/** Owner's declared effect timing. */
|
|
125
|
+
applies: SettingsApplies
|
|
126
|
+
/** Schema-declared secret positions; present only under `redactSecrets`. */
|
|
127
|
+
secrets?: RedactedSecret[]
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
只持有脱敏 descriptor 的调用方无法安全地重建分节,因此删除改以路径 op 传递。每个 descriptor 还携带针对原始分节的 `revision`;写入可以把它作为 `expectedRevision` 送回,不再匹配的写入会被拒绝,而不会覆盖先落地的写入。
|
|
132
|
+
```ts type-equiv
|
|
133
|
+
/**
|
|
134
|
+
* One path-addressed edit to a namespace's user section. Path mutation exists
|
|
135
|
+
* for a caller holding an INCOMPLETE view of the section — a configuration UI
|
|
136
|
+
* reads the redacted descriptor, which by construction never received the
|
|
137
|
+
* `role('secret')` fields. Such a caller can name the field it means without
|
|
138
|
+
* restating the section: a wholesale `replace` rebuilt from a redacted
|
|
139
|
+
* document silently deletes every secret the wire never returned.
|
|
140
|
+
*/
|
|
141
|
+
type SettingsPathOp =
|
|
142
|
+
| { op: 'set'; path: readonly string[]; value: unknown }
|
|
143
|
+
| { op: 'unset'; path: readonly string[] }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```ts type-equiv
|
|
147
|
+
/** Options for {@link SettingsProvider.describe}. */
|
|
148
|
+
interface SettingsDescribeOptions {
|
|
149
|
+
/**
|
|
150
|
+
* Strip `role('secret')` fields from `value`/`base`/`user` and enumerate
|
|
151
|
+
* them in each descriptor's `secrets`. Every wire surface MUST pass this;
|
|
152
|
+
* the verbatim default exists for same-process configuration UIs only.
|
|
153
|
+
*/
|
|
154
|
+
redactSecrets?: boolean
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 变更提交
|
|
159
|
+
|
|
160
|
+
每次提交的变更——进程内写入或提供方观察到的外部编辑——在新值成为权威值之后发出 `settings/updated (ns, next, prev, source)`,解析值深相等时绝不发出。source 标记区分两条入口路径。
|
|
161
|
+
|
|
162
|
+
```ts type-equiv
|
|
163
|
+
/** Origin of one committed settings change. */
|
|
164
|
+
type SettingsUpdateSource = 'update' | 'provider'
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
168
|
+
|
|
169
|
+
<a id="cordis-surface"></a>
|
|
170
|
+
|
|
171
|
+
## Cordis API
|
|
172
|
+
|
|
173
|
+
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).
|
|
174
|
+
|
|
175
|
+
<a id="ctxsettings--settingsprovider-abstract-seam"></a>
|
|
176
|
+
|
|
177
|
+
### `ctx.settings` — `SettingsProvider` (abstract seam)
|
|
178
|
+
|
|
179
|
+
Abstract settings service. Providers implement raw-document storage (`load`/`persist`) and push external changes through Settings.publish; the base class owns namespace registration, resolution, validation, change detection, and the `settings/updated` commit event.
|
|
180
|
+
|
|
181
|
+
```ts cordis-catalog
|
|
182
|
+
/**
|
|
183
|
+
* Prepare the provider's user-editable document for a native editor. File
|
|
184
|
+
* providers may materialize an absent document before returning its path;
|
|
185
|
+
* non-file providers return undefined.
|
|
186
|
+
* @returns the absolute local document path, or undefined for non-file storage.
|
|
187
|
+
*/
|
|
188
|
+
prepareDocument(): Promise<string | undefined>
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Register a namespace schema and receive its owner scope. The registration
|
|
192
|
+
* is an effect on the calling plugin's fiber: disposing that fiber removes
|
|
193
|
+
* the namespace and its observers. An invalid stored section fails the
|
|
194
|
+
* registration itself — the earliest point where the schema can judge it.
|
|
195
|
+
* @param ns - unique namespace; duplicate registration fails loud.
|
|
196
|
+
* @param schema - schemastery schema resolving this namespace's value.
|
|
197
|
+
* @param options - composition `base` layer and effect timing.
|
|
198
|
+
* @returns the owner scope for reads, observation, and updates.
|
|
199
|
+
*/
|
|
200
|
+
register<T>(ns: SettingsNamespace, schema: z<T>, options?: SettingsRegisterOptions<T>): SettingsScope<T>
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Describe every registered namespace for configuration surfaces, including
|
|
204
|
+
* the composition `base` and raw user layers so a form can mark which fields
|
|
205
|
+
* the user overrode (presence in `user`) and what a reset returns to.
|
|
206
|
+
* @param options - redaction switch; wire surfaces must redact.
|
|
207
|
+
* @returns one descriptor per registered namespace, in registration order.
|
|
208
|
+
*/
|
|
209
|
+
describe(options?: SettingsDescribeOptions): SettingsDescriptor[]
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Read one registered namespace's resolved value.
|
|
213
|
+
* @param ns - the namespace to read.
|
|
214
|
+
* @returns the resolved value, or `undefined` while unregistered.
|
|
215
|
+
*/
|
|
216
|
+
get(ns: SettingsNamespace): unknown
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Merge a patch into one registered namespace's user layer, validate the
|
|
220
|
+
* resolved candidate, persist through the provider, then commit and emit.
|
|
221
|
+
* A validation failure rejects before anything is persisted. Writes to one
|
|
222
|
+
* namespace are serialized: concurrent updates apply in call order, each
|
|
223
|
+
* merging over the previous write's committed section.
|
|
224
|
+
* @param ns - the registered namespace to update.
|
|
225
|
+
* @param patch - plain-object patch over the user section.
|
|
226
|
+
* @param expectedRevision - the descriptor `revision` the caller read; a
|
|
227
|
+
* namespace that moved past it rejects with {@link SettingsConflictError}.
|
|
228
|
+
*/
|
|
229
|
+
async update(ns: SettingsNamespace, patch: object, expectedRevision?: number): Promise<void>
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Replace one registered namespace's user section wholesale, validate,
|
|
233
|
+
* persist, then commit and emit. Keys absent from `section` fall back to the
|
|
234
|
+
* composition `base` and schema defaults — this is the removal/reset path a
|
|
235
|
+
* merge-only patch cannot express (`replace({})` re-inherits everything).
|
|
236
|
+
* @param ns - the registered namespace to replace.
|
|
237
|
+
* @param section - the complete next user section.
|
|
238
|
+
* @param expectedRevision - the descriptor `revision` the caller read; a
|
|
239
|
+
* namespace that moved past it rejects with {@link SettingsConflictError}.
|
|
240
|
+
*/
|
|
241
|
+
async replace(ns: SettingsNamespace, section: object, expectedRevision?: number): Promise<void>
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Apply path-addressed edits to one registered namespace's user section,
|
|
245
|
+
* validate, persist, then commit and emit. The ops are applied to the
|
|
246
|
+
* section as it stands when the write reaches the front of the queue, so a
|
|
247
|
+
* caller never has to restate fields it did not touch — and, crucially,
|
|
248
|
+
* cannot delete fields it never saw. This is the write path for any caller
|
|
249
|
+
* holding a redacted view; `replace` remains the wholesale reset.
|
|
250
|
+
* @param ns - the registered namespace to edit.
|
|
251
|
+
* @param ops - ordered path edits; later ops observe earlier ones.
|
|
252
|
+
* @param expectedRevision - the descriptor `revision` the caller read; a
|
|
253
|
+
* namespace that moved past it rejects with {@link SettingsConflictError}.
|
|
254
|
+
*/
|
|
255
|
+
async mutate(ns: SettingsNamespace, ops: readonly SettingsPathOp[], expectedRevision?: number): Promise<void>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Source: [`packages/settings/settings/src/index.ts:350`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/settings/settings/src/index.ts)
|
|
259
|
+
|
|
260
|
+
<a id="settings-events"></a>
|
|
261
|
+
|
|
262
|
+
### `settings/*` events
|
|
263
|
+
|
|
264
|
+
<a id="settingsdocument-updated--emit"></a>
|
|
265
|
+
|
|
266
|
+
#### `settings/document-updated` — emit
|
|
267
|
+
|
|
268
|
+
One registered namespace's RAW user section changed, whether or not the resolved value did. `settings/updated` is the consumer-facing event and stays deep-equal-gated; this one exists for configuration surfaces, which must learn that a field went from inherited to overridden (same resolved value, different meaning) and that their held revision is stale. Listener containment matches `settings/updated`.
|
|
269
|
+
|
|
270
|
+
```ts cordis-catalog
|
|
271
|
+
/**
|
|
272
|
+
* One registered namespace's RAW user section changed, whether or not the
|
|
273
|
+
* resolved value did. `settings/updated` is the consumer-facing event and
|
|
274
|
+
* stays deep-equal-gated; this one exists for configuration surfaces,
|
|
275
|
+
* which must learn that a field went from inherited to overridden (same
|
|
276
|
+
* resolved value, different meaning) and that their held revision is
|
|
277
|
+
* stale. Listener containment matches `settings/updated`.
|
|
278
|
+
* @param ns - the namespace whose stored section changed.
|
|
279
|
+
* @param revision - the namespace's new revision.
|
|
280
|
+
* @mode emit
|
|
281
|
+
*/
|
|
282
|
+
'settings/document-updated'(ns: SettingsNamespace, revision: number): void
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Source: [`packages/settings/settings/src/types.ts:48`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/settings/settings/src/types.ts)
|
|
286
|
+
|
|
287
|
+
<a id="settingsupdated--emit"></a>
|
|
288
|
+
|
|
289
|
+
#### `settings/updated` — emit
|
|
290
|
+
|
|
291
|
+
Committed change to one registered namespace's resolved value. Emitted after the provider persisted (for `update`) or published (`provider`) the change; never emitted when the resolved value is deep-equal. Listener failures are contained and logged — a sync throw and an async rejection alike — 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.
|
|
292
|
+
|
|
293
|
+
```ts cordis-catalog
|
|
294
|
+
/**
|
|
295
|
+
* Committed change to one registered namespace's resolved value. Emitted
|
|
296
|
+
* after the provider persisted (for `update`) or published (`provider`)
|
|
297
|
+
* the change; never emitted when the resolved value is deep-equal.
|
|
298
|
+
* Listener failures are contained and logged — a sync throw and an async
|
|
299
|
+
* rejection alike — except `INVARIANT`-coded failures, which rethrow
|
|
300
|
+
* after every listener ran; that rethrow reaches the emitter only from
|
|
301
|
+
* synchronous listeners, so invariant checks on this event must not be
|
|
302
|
+
* async functions.
|
|
303
|
+
* @param ns - the namespace whose resolved value changed.
|
|
304
|
+
* @param next - the new resolved value.
|
|
305
|
+
* @param prev - the previous resolved value.
|
|
306
|
+
* @param source - whether the change entered through `update()` or the provider.
|
|
307
|
+
* @mode emit
|
|
308
|
+
*/
|
|
309
|
+
'settings/updated'(ns: SettingsNamespace, next: unknown, prev: unknown, source: SettingsUpdateSource): void
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Source: [`packages/settings/settings/src/types.ts:35`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/settings/settings/src/types.ts)
|
|
313
|
+
<!-- END GENERATED cordis-surface -->
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
---
|
|
2
|
+
editSource: "docs/subsystems/shell.zh.md"
|
|
3
|
+
outline: [2,3]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Bash 执行器
|
|
7
|
+
|
|
8
|
+
bash 执行 seam 分为 Service Definition([dsh-shell](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/shell),`ctx.shell`)、Service Provider([dsh-bash-local](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/bash-local) 与 [dsh-bash-sandbox](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/bash-sandbox))和 Consumer([dsh-tool-bash](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/shell/tool-bash),即 `bash` schema)。通用后台任务的 job id、所有权与控制位于 [jobs.md](./jobs.md);本 seam 返回一个不含任务概念的进程句柄。原始进程组机制封装在[子进程 seam](./subprocess.md)之后。
|
|
9
|
+
|
|
10
|
+
源码:[`packages/shell/shell/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/shell/shell/src/types.ts)
|
|
11
|
+
|
|
12
|
+
## 受管 shell 环境命名空间
|
|
13
|
+
|
|
14
|
+
`DSH_*` 变量是归 Harness 所有的子进程事实。面向模型的 bash 工具通过 `ctx.shellEnv` 收集它们,再经由 `ShellExecRequest.dshEnv` 传递;子进程服务在合并当前快照之前会移除继承而来的 `DSH_*` 名称。`DshEnvironmentKey`/`DshEnvironment` 词汇归[子进程 seam](./subprocess.md)所有,由 `dsh-shell` 重导出。
|
|
15
|
+
|
|
16
|
+
## 请求与规格:`resolve()` 拆分
|
|
17
|
+
|
|
18
|
+
该 seam 将**面向模型/插件的请求**(`workdir`/`timeoutMs`/`stdoutMaxBytes` 可选,由配置或请求策略补全)与执行器实际使用的**完全解析后的 spec**(这些字段均为必填)分开。工具层在二者之间调用 `ctx.shell.resolve(request)`(仓库的「包边界处显式优于隐式」规则);`ShellExecSpec` 携带的是已解析的值。
|
|
19
|
+
|
|
20
|
+
```ts type-equiv
|
|
21
|
+
/**
|
|
22
|
+
* A caller's execution REQUEST: `workdir` and `timeoutMs` are optional and
|
|
23
|
+
* filled by {@link ShellExecutor.resolve} from the implementation's config.
|
|
24
|
+
* This is the model-/plugin-facing shape; pass it to `resolve()` to obtain a
|
|
25
|
+
* fully-resolved {@link ShellExecSpec}.
|
|
26
|
+
*/
|
|
27
|
+
interface ShellExecRequest {
|
|
28
|
+
command: string
|
|
29
|
+
/** Working directory override (default: implementation-configured). */
|
|
30
|
+
workdir?: string | undefined
|
|
31
|
+
/** Timeout override in milliseconds (implementations cap it). */
|
|
32
|
+
timeoutMs?: number | undefined
|
|
33
|
+
/**
|
|
34
|
+
* Foreground stdout capture budget in bytes. Absent uses the executor's
|
|
35
|
+
* default output cap. Trusted in-process consumers use this when they must
|
|
36
|
+
* parse complete stdout up to their own bounded limit; the model-facing bash
|
|
37
|
+
* tool does not expose it as a parameter.
|
|
38
|
+
*/
|
|
39
|
+
stdoutMaxBytes?: number | undefined
|
|
40
|
+
/** Abort signal — implementations kill the command when it fires. */
|
|
41
|
+
signal?: AbortSignal | undefined
|
|
42
|
+
/**
|
|
43
|
+
* Bytes to write to the command's stdin, then close it. Absent leaves stdin
|
|
44
|
+
* closed/empty (the default for model-driven tool calls). Set by in-process
|
|
45
|
+
* plugins (e.g. the hooks bridges, which write a hook command's JSON payload
|
|
46
|
+
* to its stdin); the model-facing bash tool does not expose it as a parameter
|
|
47
|
+
* (a model that needs stdin uses shell syntax like a heredoc or a pipe).
|
|
48
|
+
*/
|
|
49
|
+
stdin?: string | undefined
|
|
50
|
+
/**
|
|
51
|
+
* Ordinary environment entries for the command, merged after the credential
|
|
52
|
+
* scrub. Managed facts belong in {@link dshEnv}, which merges after this
|
|
53
|
+
* map, so an entry here can never displace one. Set by in-process plugins
|
|
54
|
+
* (the hooks bridges set `CLAUDE_PROJECT_DIR`, `CLAUDE_PLUGIN_ROOT`, …); the
|
|
55
|
+
* model-facing bash tool does not expose it as a parameter.
|
|
56
|
+
*/
|
|
57
|
+
env?: Record<string, string> | undefined
|
|
58
|
+
/**
|
|
59
|
+
* Harness-owned `DSH_*` variables for this execution (typed to managed
|
|
60
|
+
* keys). Executors discard ambient `DSH_*` entries before merging this
|
|
61
|
+
* snapshot last, so an unavailable current fact cannot inherit a stale
|
|
62
|
+
* value from the harness process and a caller {@link env} entry cannot
|
|
63
|
+
* displace a managed one.
|
|
64
|
+
*/
|
|
65
|
+
dshEnv?: DshEnvironment | undefined
|
|
66
|
+
/** Fully resolved per-call sandbox policy; sandboxing executors default it. */
|
|
67
|
+
sandboxPolicy?: SandboxExecutionPolicy | undefined
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```ts type-equiv
|
|
72
|
+
/**
|
|
73
|
+
* A resolved execution spec. {@link ShellExecutor.resolve} fills and caps the
|
|
74
|
+
* required fields; {@link ShellExecutor.start} ignores `timeoutMs` because
|
|
75
|
+
* background processes have no executor timeout.
|
|
76
|
+
*/
|
|
77
|
+
interface ShellExecSpec {
|
|
78
|
+
command: string
|
|
79
|
+
workdir: string
|
|
80
|
+
timeoutMs: number
|
|
81
|
+
/**
|
|
82
|
+
* Resolved foreground stdout capture budget in bytes. `run()` uses it for
|
|
83
|
+
* stdout; background jobs and stderr keep the executor's own output cap.
|
|
84
|
+
*/
|
|
85
|
+
stdoutMaxBytes: number
|
|
86
|
+
/** Abort signal — implementations kill the command when it fires. */
|
|
87
|
+
signal?: AbortSignal | undefined
|
|
88
|
+
/** Bytes to write to stdin before closing it; absent means no stdin. */
|
|
89
|
+
stdin?: string | undefined
|
|
90
|
+
/**
|
|
91
|
+
* Ordinary environment entries carried through from
|
|
92
|
+
* {@link ShellExecRequest.env}; {@link dshEnv} still merges after them.
|
|
93
|
+
* OPTIONAL on the spec for the same reason as `stdin`: absent means no
|
|
94
|
+
* ordinary extra environment.
|
|
95
|
+
*/
|
|
96
|
+
env?: Record<string, string> | undefined
|
|
97
|
+
/** Managed `DSH_*` snapshot (typed to managed keys); merges after {@link env}. */
|
|
98
|
+
dshEnv?: DshEnvironment | undefined
|
|
99
|
+
/** Resolved sandbox policy; ignored by executors that do not confine. */
|
|
100
|
+
sandboxPolicy: SandboxExecutionPolicy | undefined
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`stdin` 和 `env` 是受信任的进程内插件输入,不由 `dsh-tool-bash` 暴露。本地执行器会先清除环境中的凭据,再合并调用方显式提供的 env。见 [bash-stdin-env Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-api.md)。
|
|
105
|
+
|
|
106
|
+
`stdoutMaxBytes` 同样仅供受信任插件使用。它让前台消费方能在有界解析预算内请求完整 stdout,而不会改变 stderr、后台任务或面向模型的 bash 工具的常规输出上限。
|
|
107
|
+
|
|
108
|
+
## 前台运行:`ShellRunResult`
|
|
109
|
+
|
|
110
|
+
一次已完成(或被终止)的前台运行的结果。正交的结果**独立报告**:一个进程可以同时超时并以退出码 0 退出(因为它捕获了信号),因此 `timedOut`、`aborted`、`signal` 和 `exitCode` 各自独立为一个字段;调用方永远不会把一次被提前中断的运行误读为正常成功。
|
|
111
|
+
|
|
112
|
+
```ts type-equiv
|
|
113
|
+
/** The outcome of one completed (or killed) foreground run. */
|
|
114
|
+
interface ShellRunResult {
|
|
115
|
+
/** Exit code; null when the process died from a signal. */
|
|
116
|
+
exitCode: number | null
|
|
117
|
+
/** Terminating signal (e.g. 'SIGTERM'); null on normal exit. */
|
|
118
|
+
signal: NodeJS.Signals | null
|
|
119
|
+
/**
|
|
120
|
+
* True when the executor's own timeout was the FIRST cause to cut the command
|
|
121
|
+
* short. Mutually exclusive with {@link aborted}: one fused deadline drives
|
|
122
|
+
* both the timeout and the caller's cancellation, so a timeout and an abort
|
|
123
|
+
* racing before process close report the single first-abort cause, not both
|
|
124
|
+
* (see the [timeout-library Agent Note](../../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md)).
|
|
125
|
+
*/
|
|
126
|
+
timedOut: boolean
|
|
127
|
+
/**
|
|
128
|
+
* True when the caller's `AbortSignal` was the FIRST cause to kill the command
|
|
129
|
+
* (and it was not the executor's own timeout). Mutually exclusive with
|
|
130
|
+
* {@link timedOut} — see there for the first-cause classification.
|
|
131
|
+
*/
|
|
132
|
+
aborted: boolean
|
|
133
|
+
/** The effective timeout applied to this run (after defaulting/capping). */
|
|
134
|
+
timeoutMs: number
|
|
135
|
+
stdout: CollectedOutput
|
|
136
|
+
stderr: CollectedOutput
|
|
137
|
+
/** Sandbox execution facts, absent for an unsandboxed executor. */
|
|
138
|
+
sandbox?: ShellSandboxInfo
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
每个流是一个 `CollectedOutput`:(可能被截断的)文本加恢复信息;截断时,`text` 是**尾部**,完整流溢出到一个私有文件。这些字段归[子进程 seam](./subprocess.md)所有,由 `dsh-shell` 重导出。
|
|
143
|
+
|
|
144
|
+
## 文件沙箱:`ShellSandboxInfo`
|
|
145
|
+
|
|
146
|
+
使用沙箱的执行器通过 `ShellExecutor.sandboxMode` 暴露其已配置的模式回退值。工具层请求 [`@deepseek-ai/dsh-sandbox-policy`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/sandbox/sandbox-policy/README.md),把每个调用会话的持久 `sandbox/mode` 覆盖值与不可变 cwd 解析为 `ShellExecRequest.sandboxPolicy`;经用户批准、严格更宽松的调用只替换模式。模式/root/enforcement 词汇归 [`@deepseek-ai/dsh-sandbox` 沙箱 seam](./sandbox.md) 所有;模式仅管辖文件效果。
|
|
147
|
+
|
|
148
|
+
沙箱化运行会报告其模式、保守的拒绝分类与强制执行完整度。`runnerFailed` 标记命令运行前沙箱 runner 已失败;前台执行会抛出 `SANDBOX_UNAVAILABLE`,而已结束的后台进程只能通过其事实通道报告。
|
|
149
|
+
|
|
150
|
+
```ts type-equiv
|
|
151
|
+
/**
|
|
152
|
+
* Sandbox facts for one run, present iff a sandboxing executor handled it.
|
|
153
|
+
* Facts are reported independently of process exit status so callers can
|
|
154
|
+
* distinguish command failures from policy denials and runner failures.
|
|
155
|
+
*/
|
|
156
|
+
interface ShellSandboxInfo {
|
|
157
|
+
/** The mode the command actually ran under. */
|
|
158
|
+
mode: SandboxMode
|
|
159
|
+
/** Whether the sandbox denied a file operation. */
|
|
160
|
+
denied: boolean
|
|
161
|
+
/** How completely the selected runner enforced the requested mode. */
|
|
162
|
+
enforcement?: SandboxEnforcement
|
|
163
|
+
/** Whether the sandbox runner failed before the command could run. */
|
|
164
|
+
runnerFailed?: boolean
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
当受限模式没有可用后端时,`ctx.sandbox` 提供方会抛出、执行器会传播由[沙箱 seam](./sandbox.md)所有的 `SANDBOX_UNAVAILABLE` 错误码。选定的 runner 拒绝其 profile 时会触达同一个故障关闭的前台错误;已结束的后台任务则记录 `runnerFailed`。模型会在结果中收到拒绝/runner 事实,仅当拒绝标记指出生效模式时才得知该模式,并可通过 `sandbox_permissions` 加 `justification` 请求一次性、严格更宽松的重试;执行任何操作前,`ctx.approval` 必须批准该次确切调用。完整的策略与切换设计见[沙箱 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-07-06-sandbox.md)。
|
|
169
|
+
|
|
170
|
+
## 后台进程:`ShellProcess`
|
|
171
|
+
|
|
172
|
+
`start()` 返回不含 id 或所有者的句柄。`dsh-tool-bash` 将它适配为 `ctx.jobs.start()` 钩子;随后由通用运行时拥有任务标识与生命周期。`done` 在进程关闭时完成且绝不被拒绝;进程结束后仍可读取,并且沙箱事实会在 `done` 完成前写入。
|
|
173
|
+
|
|
174
|
+
```ts type-equiv
|
|
175
|
+
/**
|
|
176
|
+
* A background process handle returned by {@link ShellExecutor.start}. It is the
|
|
177
|
+
* only access path; buffered output remains readable after exit. Composition
|
|
178
|
+
* teardown (the subprocess service's disposal) kills running processes and
|
|
179
|
+
* awaits {@link done}; an executor-only reload leaves them running.
|
|
180
|
+
*/
|
|
181
|
+
interface ShellProcess {
|
|
182
|
+
/** Process lifecycle state (settled exactly once). */
|
|
183
|
+
status: ShellProcessStatus
|
|
184
|
+
/** Exit code once finished (null = killed by signal / still running). */
|
|
185
|
+
exitCode: number | null
|
|
186
|
+
/** Terminating signal name, when signal-killed. */
|
|
187
|
+
signal: NodeJS.Signals | null
|
|
188
|
+
/** Resolves when the underlying process closes (never rejects — a spawn failure settles as `killed` with the error on stderr). */
|
|
189
|
+
readonly done: Promise<void>
|
|
190
|
+
/** Sandbox facts, stamped once a confined process settles. */
|
|
191
|
+
sandbox?: ShellSandboxInfo
|
|
192
|
+
/**
|
|
193
|
+
* Read output produced since the previous read (consuming — consecutive
|
|
194
|
+
* reads never re-deliver). Reads that lost data flag `lossy` and point at
|
|
195
|
+
* full-stream spill files when available.
|
|
196
|
+
*/
|
|
197
|
+
readOutput(): ShellProcessRead
|
|
198
|
+
/**
|
|
199
|
+
* Kill the process group. Returns false when it had already finished
|
|
200
|
+
* (no-op); idempotent.
|
|
201
|
+
*/
|
|
202
|
+
kill(): boolean
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`readOutput()` 返回增量内容与 spill 恢复信息:
|
|
207
|
+
|
|
208
|
+
```ts type-equiv
|
|
209
|
+
/** One incremental {@link ShellProcess.readOutput} read. */
|
|
210
|
+
interface ShellProcessRead {
|
|
211
|
+
/** Output produced since the previous read (stderr in a marked section). */
|
|
212
|
+
delta: string
|
|
213
|
+
/** True when truncation dropped unread bytes the delta cannot include. */
|
|
214
|
+
lossy: boolean
|
|
215
|
+
/** Full stdout spill file, when stdout truncation occurred and a safe path is available. */
|
|
216
|
+
stdoutSpillPath?: string
|
|
217
|
+
/** Full stderr spill file, when stderr truncation occurred and a safe path is available. */
|
|
218
|
+
stderrSpillPath?: string
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## 服务
|
|
223
|
+
|
|
224
|
+
`ShellExecutor` 拥有 `resolve`、前台 `run`、后台进程 `start` 以及 `sandboxMode` 能力事实。`dsh-bash-local` 拥有命令默认值补全、超时/中止分类、终端环境以及后台读取合并;进程组、有界收集器、spill 文件、凭据清除与 dispose(资源释放)后完全停稳归[子进程服务](./subprocess.md)所有。`dsh-tool-bash` 拥有面向模型的渲染,并将后台句柄适配到[通用任务运行时](./jobs.md)。`dsh-shell` 拥有 shell 工具共享的退出状态约定:导出的 `parseExitStatus`/`ParsedExitStatus` 是 `dsh-tool-bash` 的 `renderResult` 与 `dsh-tool-pwsh` 的 `renderPwshResult` 所追加的 `[exit code: N]` / `[killed by signal: X]` 标记的逆解析,两个工具的 `presentResult` 都用它把渲染文本拆分为 terminal 卡的输出正文与退出状态 pill。
|
|
225
|
+
|
|
226
|
+
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
227
|
+
|
|
228
|
+
<a id="cordis-surface"></a>
|
|
229
|
+
|
|
230
|
+
## Cordis API
|
|
231
|
+
|
|
232
|
+
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).
|
|
233
|
+
|
|
234
|
+
<a id="ctxshell--shellexecutor-abstract-seam"></a>
|
|
235
|
+
|
|
236
|
+
### `ctx.shell` — `ShellExecutor` (abstract seam)
|
|
237
|
+
|
|
238
|
+
Abstract bash execution service. Subclass, implement the abstract methods, and load the subclass as a plugin — it registers as `ctx.shell` (one implementation per context; loading a second throws, which is cordis' standard duplicate-service behavior).
|
|
239
|
+
|
|
240
|
+
Implementations must honor these semantics:
|
|
241
|
+
|
|
242
|
+
- run rejects only for infrastructure failures. Nonzero exits, timeout kills, and abort kills resolve with a ShellRunResult.
|
|
243
|
+
- start returns immediately; no timeout applies to background processes. `done` settles at process close and never rejects; spawn failures settle as `killed` with the error on stderr.
|
|
244
|
+
- ShellProcess.readOutput is incremental: consecutive reads never repeat output. Lossy reads report truncation and available spill files.
|
|
245
|
+
- A still-running background process is stopped and awaited when its owning composition tears down. With the subprocess seam that boundary is `ctx.subprocess` disposal, so a background process survives an executor-only reload.
|
|
246
|
+
|
|
247
|
+
```ts cordis-catalog
|
|
248
|
+
/**
|
|
249
|
+
* Apply implementation-owned defaults and caps to a request before execution.
|
|
250
|
+
* @param request - the caller's request; omitted fields get this
|
|
251
|
+
* implementation's defaults, capped fields are clamped.
|
|
252
|
+
* @returns the fully-specified spec to hand to {@link run}/{@link start}.
|
|
253
|
+
*/
|
|
254
|
+
abstract resolve(request: ShellExecRequest): ShellExecSpec
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Run a command in the foreground; resolves when it finishes.
|
|
258
|
+
* @param spec - a resolved spec from {@link resolve}, never a raw request.
|
|
259
|
+
* @returns the outcome; nonzero exits, timeout kills, and abort kills
|
|
260
|
+
* resolve with a descriptive result rather than reject.
|
|
261
|
+
*/
|
|
262
|
+
abstract run(spec: ShellExecSpec): Promise<ShellRunResult>
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Start a background process and return its handle immediately.
|
|
266
|
+
* @param spec - a resolved spec from {@link resolve}, never a raw request.
|
|
267
|
+
* @returns the live process handle (reads, kill, quiescence promise).
|
|
268
|
+
*/
|
|
269
|
+
abstract start(spec: ShellExecSpec): ShellProcess
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Source: [`packages/shell/shell/src/index.ts:65`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/shell/shell/src/index.ts)
|
|
273
|
+
|
|
274
|
+
<a id="ctxshellenv--shellenvregistry"></a>
|
|
275
|
+
|
|
276
|
+
### `ctx.shellEnv` — `ShellEnvRegistry`
|
|
277
|
+
|
|
278
|
+
Registry (`ctx.shellEnv`) for trusted, per-execution `DSH_*` variables. The namespace is rebuilt for every model shell call: ambient `DSH_*` values are discarded by the executor, then the registry's current snapshot is injected. Built-in shell facts remain owned by the registry itself while plugins can register additional, enumerable facts with effect-scoped disposal.
|
|
279
|
+
|
|
280
|
+
```ts cordis-catalog
|
|
281
|
+
/**
|
|
282
|
+
* Register one environment contributor. Names and keys are unique; built-in
|
|
283
|
+
* keys are reserved. Registration is disposed with the calling plugin fiber.
|
|
284
|
+
* @param contributor - declared key ownership and per-execution resolver.
|
|
285
|
+
* @returns the disposer that unregisters the contribution.
|
|
286
|
+
*/
|
|
287
|
+
register(contributor: BashEnvContributor): () => void
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Build the trusted `DSH_*` snapshot for one shell tool execution.
|
|
291
|
+
* @param execution - the current tool execution.
|
|
292
|
+
* @returns an immutable environment overlay containing built-ins and current contributions.
|
|
293
|
+
*/
|
|
294
|
+
collect(execution: ToolExecution): DshEnvironment
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Enumerate plugin-contributed variables without executing their resolvers.
|
|
298
|
+
* @returns declarations sorted by environment variable name.
|
|
299
|
+
*/
|
|
300
|
+
list(): BashEnvVariableInfo[]
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Types: [DshEnvironment](./subprocess.md) · [ToolExecution](./tools.md)
|
|
304
|
+
|
|
305
|
+
Source: [`packages/shell/shell-env/src/index.ts:89`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/shell/shell-env/src/index.ts)
|
|
306
|
+
<!-- END GENERATED cordis-surface -->
|