better-dsh 0.2.3-e → 0.2.3-g
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/docs/50_test-reports/2026-09-13-preact-ui-shell/345/256/236/346/265/213/346/212/245/345/221/212.md +1 -1
- package/docs/50_test-reports/2026-09-14-4999-skill/346/270/205/345/215/225/344/270/216lsp-gate/345/256/236/346/265/213/346/212/245/345/221/212.md +54 -0
- package/docs/specs/agent/spec.md +54 -0
- package/docs/specs/ast/spec.md +34 -0
- package/docs/specs/compaction-recall/spec.md +46 -0
- package/docs/specs/ctx/spec.md +107 -0
- package/docs/specs/dsh/spec.md +47 -0
- package/docs/specs/dvc/spec.md +87 -0
- package/docs/specs/escalation-guidance/spec.md +44 -0
- package/docs/specs/fs-scheme-resolution/spec.md +37 -0
- package/docs/specs/hash-edit/spec.md +41 -0
- package/docs/specs/http-read/spec.md +73 -0
- package/docs/specs/kernel-provisioning/spec.md +53 -0
- package/docs/specs/lsp/spec.md +121 -0
- package/docs/specs/mobile-layout/spec.md +108 -0
- package/docs/specs/model-failover/spec.md +20 -0
- package/docs/specs/preact-ui-shell/spec.md +22 -0
- package/docs/specs/repl-dispatch-resilience/spec.md +21 -0
- package/docs/specs/skill/spec.md +58 -0
- package/docs/specs/tool-surface/spec.md +222 -0
- package/docs/specs/url-schema/spec.md +148 -0
- package/docs/specs/web-trust-fence/spec.md +43 -0
- package/dsh-docs/AGENTS.md +75 -0
- package/dsh-docs/agent-lifecycle.md +84 -0
- package/dsh-docs/agent-lifecycle.zh.md +86 -0
- package/dsh-docs/api-gateway.md +164 -0
- package/dsh-docs/api-gateway.zh.md +164 -0
- package/dsh-docs/architecture.md +150 -0
- package/dsh-docs/architecture.zh.md +154 -0
- package/dsh-docs/capability-seams.md +543 -0
- package/dsh-docs/capability-seams.zh.md +545 -0
- package/dsh-docs/config-catalog.md +3473 -0
- package/dsh-docs/config-catalog.zh.md +3474 -0
- package/dsh-docs/cookbook/adding-a-package.md +117 -0
- package/dsh-docs/cookbook/adding-a-package.zh.md +119 -0
- package/dsh-docs/cookbook/adding-a-remote-api.md +197 -0
- package/dsh-docs/cookbook/adding-a-remote-api.zh.md +197 -0
- package/dsh-docs/cookbook/adding-a-settings-card.md +102 -0
- package/dsh-docs/cookbook/adding-a-settings-card.zh.md +102 -0
- package/dsh-docs/cookbook/adding-a-tool.md +101 -0
- package/dsh-docs/cookbook/adding-a-tool.zh.md +103 -0
- package/dsh-docs/cookbook/adding-a-vendored-package.md +59 -0
- package/dsh-docs/cookbook/adding-a-vendored-package.zh.md +59 -0
- package/dsh-docs/cookbook/adding-an-llm-adapter.md +43 -0
- package/dsh-docs/cookbook/adding-an-llm-adapter.zh.md +43 -0
- package/dsh-docs/cookbook/extension-cookbook.md +132 -0
- package/dsh-docs/cookbook/extension-cookbook.zh.md +136 -0
- package/dsh-docs/cookbook/maintaining-dsh-code-review.md +64 -0
- package/dsh-docs/cookbook/maintaining-dsh-code-review.zh.md +64 -0
- package/dsh-docs/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
- package/dsh-docs/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
- package/dsh-docs/cordis-api/context.md +364 -0
- package/dsh-docs/cordis-api/context.zh.md +366 -0
- package/dsh-docs/cordis-api/events.md +207 -0
- package/dsh-docs/cordis-api/events.zh.md +209 -0
- package/dsh-docs/cordis-api/fiber.md +375 -0
- package/dsh-docs/cordis-api/fiber.zh.md +377 -0
- package/dsh-docs/cordis-api/inherited.md +39 -0
- package/dsh-docs/cordis-api/registry.md +152 -0
- package/dsh-docs/cordis-api/registry.zh.md +154 -0
- package/dsh-docs/cordis-api/service.md +102 -0
- package/dsh-docs/cordis-api/service.zh.md +104 -0
- package/dsh-docs/cordis-primer.md +45 -0
- package/dsh-docs/cordis-primer.zh.md +51 -0
- package/dsh-docs/cordis-tutorial/01-first-plugin.md +95 -0
- package/dsh-docs/cordis-tutorial/01-first-plugin.zh.md +95 -0
- package/dsh-docs/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
- package/dsh-docs/cordis-tutorial/02-lifecycle-and-effects.zh.md +98 -0
- package/dsh-docs/cordis-tutorial/03-services.md +98 -0
- package/dsh-docs/cordis-tutorial/03-services.zh.md +98 -0
- package/dsh-docs/cordis-tutorial/04-events.md +144 -0
- package/dsh-docs/cordis-tutorial/04-events.zh.md +144 -0
- package/dsh-docs/cordis-tutorial/05-config.md +84 -0
- package/dsh-docs/cordis-tutorial/05-config.zh.md +84 -0
- package/dsh-docs/cordis-tutorial/06-composition-and-hmr.md +113 -0
- package/dsh-docs/cordis-tutorial/06-composition-and-hmr.zh.md +113 -0
- package/dsh-docs/cordis-tutorial/07-into-the-harness.md +108 -0
- package/dsh-docs/cordis-tutorial/07-into-the-harness.zh.md +108 -0
- package/dsh-docs/cordis-tutorial/index.md +60 -0
- package/dsh-docs/cordis-tutorial/index.zh.md +62 -0
- package/dsh-docs/deepseek-llm-api-wire-extensions.md +163 -0
- package/dsh-docs/deepseek-llm-api-wire-extensions.zh.md +163 -0
- package/dsh-docs/defensive-patterns.md +33 -0
- package/dsh-docs/defensive-patterns.zh.md +35 -0
- package/dsh-docs/development.md +167 -0
- package/dsh-docs/development.zh.md +173 -0
- package/dsh-docs/event-producer-consumer.md +86 -0
- package/dsh-docs/event-producer-consumer.zh.md +88 -0
- package/dsh-docs/glossary.md +45 -0
- package/dsh-docs/glossary.zh.md +45 -0
- package/dsh-docs/graph-atlas.md +22 -0
- package/dsh-docs/graph-atlas.zh.md +24 -0
- package/dsh-docs/i18n/README.md +60 -0
- package/dsh-docs/i18n/README.zh.md +62 -0
- package/dsh-docs/i18n/style-samples.md +87 -0
- package/dsh-docs/i18n/terminology.md +214 -0
- package/dsh-docs/i18n/translation-prompt.md +263 -0
- package/dsh-docs/i18n/translation-rules.md +69 -0
- package/dsh-docs/i18n/translation-rules.zh.md +69 -0
- package/dsh-docs/module-graph.md +1411 -0
- package/dsh-docs/module-graph.zh.md +1413 -0
- package/dsh-docs/persistence-catalog.md +1075 -0
- package/dsh-docs/persistence-catalog.zh.md +1077 -0
- package/dsh-docs/postmortem/0001-acp-default-export-drops-inject.md +113 -0
- package/dsh-docs/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
- package/dsh-docs/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
- package/dsh-docs/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
- package/dsh-docs/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
- package/dsh-docs/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
- package/dsh-docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
- package/dsh-docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
- package/dsh-docs/postmortem/README.md +18 -0
- package/dsh-docs/postmortem/README.zh.md +18 -0
- package/dsh-docs/rescope.md +53 -0
- package/dsh-docs/rescope.zh.md +53 -0
- package/dsh-docs/subsystems/README.md +61 -0
- package/dsh-docs/subsystems/README.zh.md +61 -0
- package/dsh-docs/subsystems/agent-team.md +207 -0
- package/dsh-docs/subsystems/agent-team.zh.md +207 -0
- package/dsh-docs/subsystems/approval.md +170 -0
- package/dsh-docs/subsystems/approval.zh.md +170 -0
- package/dsh-docs/subsystems/attachment.md +351 -0
- package/dsh-docs/subsystems/attachment.zh.md +351 -0
- package/dsh-docs/subsystems/client-modules.md +168 -0
- package/dsh-docs/subsystems/client-modules.zh.md +168 -0
- package/dsh-docs/subsystems/code-runtime.md +195 -0
- package/dsh-docs/subsystems/code-runtime.zh.md +195 -0
- package/dsh-docs/subsystems/commands.md +219 -0
- package/dsh-docs/subsystems/commands.zh.md +219 -0
- package/dsh-docs/subsystems/compaction.md +238 -0
- package/dsh-docs/subsystems/compaction.zh.md +238 -0
- package/dsh-docs/subsystems/conversation.md +258 -0
- package/dsh-docs/subsystems/conversation.zh.md +258 -0
- package/dsh-docs/subsystems/core.md +1209 -0
- package/dsh-docs/subsystems/core.zh.md +1219 -0
- package/dsh-docs/subsystems/credentials.md +329 -0
- package/dsh-docs/subsystems/credentials.zh.md +329 -0
- package/dsh-docs/subsystems/extensions.md +382 -0
- package/dsh-docs/subsystems/extensions.zh.md +382 -0
- package/dsh-docs/subsystems/feedback.md +266 -0
- package/dsh-docs/subsystems/feedback.zh.md +266 -0
- package/dsh-docs/subsystems/filesystem.md +505 -0
- package/dsh-docs/subsystems/filesystem.zh.md +505 -0
- package/dsh-docs/subsystems/goal.md +277 -0
- package/dsh-docs/subsystems/goal.zh.md +277 -0
- package/dsh-docs/subsystems/invariants.md +88 -0
- package/dsh-docs/subsystems/invariants.zh.md +88 -0
- package/dsh-docs/subsystems/jobs.md +290 -0
- package/dsh-docs/subsystems/jobs.zh.md +290 -0
- package/dsh-docs/subsystems/llm-streaming.md +1080 -0
- package/dsh-docs/subsystems/llm-streaming.zh.md +1086 -0
- package/dsh-docs/subsystems/lsp.md +202 -0
- package/dsh-docs/subsystems/lsp.zh.md +202 -0
- package/dsh-docs/subsystems/permission-presets.md +131 -0
- package/dsh-docs/subsystems/permission-presets.zh.md +131 -0
- package/dsh-docs/subsystems/persistence.md +395 -0
- package/dsh-docs/subsystems/persistence.zh.md +395 -0
- package/dsh-docs/subsystems/plan.md +87 -0
- package/dsh-docs/subsystems/plan.zh.md +87 -0
- package/dsh-docs/subsystems/sandbox.md +220 -0
- package/dsh-docs/subsystems/sandbox.zh.md +220 -0
- package/dsh-docs/subsystems/schedule.md +192 -0
- package/dsh-docs/subsystems/schedule.zh.md +192 -0
- package/dsh-docs/subsystems/scope.md +59 -0
- package/dsh-docs/subsystems/scope.zh.md +59 -0
- package/dsh-docs/subsystems/session-projection.md +354 -0
- package/dsh-docs/subsystems/session-projection.zh.md +354 -0
- package/dsh-docs/subsystems/session-query.md +509 -0
- package/dsh-docs/subsystems/session-query.zh.md +509 -0
- package/dsh-docs/subsystems/session-reference.md +219 -0
- package/dsh-docs/subsystems/session-reference.zh.md +219 -0
- package/dsh-docs/subsystems/session-telemetry.md +194 -0
- package/dsh-docs/subsystems/session-telemetry.zh.md +194 -0
- package/dsh-docs/subsystems/session-title.md +204 -0
- package/dsh-docs/subsystems/session-title.zh.md +204 -0
- package/dsh-docs/subsystems/session.md +1155 -0
- package/dsh-docs/subsystems/session.zh.md +1159 -0
- package/dsh-docs/subsystems/settings.md +405 -0
- package/dsh-docs/subsystems/settings.zh.md +405 -0
- package/dsh-docs/subsystems/shell.md +303 -0
- package/dsh-docs/subsystems/shell.zh.md +303 -0
- package/dsh-docs/subsystems/skills.md +354 -0
- package/dsh-docs/subsystems/skills.zh.md +354 -0
- package/dsh-docs/subsystems/slots.md +175 -0
- package/dsh-docs/subsystems/slots.zh.md +175 -0
- package/dsh-docs/subsystems/spill.md +117 -0
- package/dsh-docs/subsystems/spill.zh.md +117 -0
- package/dsh-docs/subsystems/storage.md +260 -0
- package/dsh-docs/subsystems/storage.zh.md +260 -0
- package/dsh-docs/subsystems/subagent.md +766 -0
- package/dsh-docs/subsystems/subagent.zh.md +770 -0
- package/dsh-docs/subsystems/subprocess.md +324 -0
- package/dsh-docs/subsystems/subprocess.zh.md +324 -0
- package/dsh-docs/subsystems/system-prompt.md +220 -0
- package/dsh-docs/subsystems/system-prompt.zh.md +220 -0
- package/dsh-docs/subsystems/terminal.md +184 -0
- package/dsh-docs/subsystems/terminal.zh.md +184 -0
- package/dsh-docs/subsystems/todo.md +32 -0
- package/dsh-docs/subsystems/todo.zh.md +32 -0
- package/dsh-docs/subsystems/token-meter.md +105 -0
- package/dsh-docs/subsystems/token-meter.zh.md +105 -0
- package/dsh-docs/subsystems/tools.md +720 -0
- package/dsh-docs/subsystems/tools.zh.md +720 -0
- package/dsh-docs/subsystems/typert.md +343 -0
- package/dsh-docs/subsystems/typert.zh.md +343 -0
- package/dsh-docs/subsystems/user-questions.md +178 -0
- package/dsh-docs/subsystems/user-questions.zh.md +178 -0
- package/dsh-docs/subsystems/web-client.md +95 -0
- package/dsh-docs/subsystems/web-client.zh.md +95 -0
- package/dsh-docs/subsystems/web-server.md +154 -0
- package/dsh-docs/subsystems/web-server.zh.md +154 -0
- package/dsh-docs/subsystems/web.md +206 -0
- package/dsh-docs/subsystems/web.zh.md +206 -0
- package/dsh-docs/subsystems/webhook.md +70 -0
- package/dsh-docs/subsystems/webhook.zh.md +70 -0
- package/dsh-docs/subsystems/workflow.md +278 -0
- package/dsh-docs/subsystems/workflow.zh.md +278 -0
- package/dsh-docs/subsystems/workspace.md +321 -0
- package/dsh-docs/subsystems/workspace.zh.md +321 -0
- package/dsh-docs/testing.md +54 -0
- package/dsh-docs/testing.zh.md +54 -0
- package/dsh-docs/tool-catalog.md +2225 -0
- package/dsh-docs/tool-catalog.zh.md +2233 -0
- package/dsh-docs/tool-execution-pipeline.md +62 -0
- package/dsh-docs/tool-execution-pipeline.zh.md +64 -0
- package/dsh-docs/user/develop/basic/config.md +106 -0
- package/dsh-docs/user/develop/basic/config.zh.md +106 -0
- package/dsh-docs/user/develop/basic/index.md +144 -0
- package/dsh-docs/user/develop/basic/index.zh.md +144 -0
- package/dsh-docs/user/develop/basic/publish.md +183 -0
- package/dsh-docs/user/develop/basic/publish.zh.md +183 -0
- package/dsh-docs/user/develop/basic/tool.md +52 -0
- package/dsh-docs/user/develop/basic/tool.zh.md +52 -0
- package/dsh-docs/user/develop/framework/events.md +143 -0
- package/dsh-docs/user/develop/framework/events.zh.md +143 -0
- package/dsh-docs/user/develop/framework/index.md +137 -0
- package/dsh-docs/user/develop/framework/index.zh.md +137 -0
- package/dsh-docs/user/develop/framework/service.md +148 -0
- package/dsh-docs/user/develop/framework/service.zh.md +150 -0
- package/dsh-docs/user/develop/practice/dynamic-cordis.md +15 -0
- package/dsh-docs/user/develop/practice/dynamic-cordis.zh.md +15 -0
- package/dsh-docs/user/develop/practice/index.md +155 -0
- package/dsh-docs/user/develop/practice/index.zh.md +155 -0
- package/dsh-docs/user/develop/practice/llm-adapter.md +189 -0
- package/dsh-docs/user/develop/practice/llm-adapter.zh.md +189 -0
- package/dsh-docs/user/guide/github-review.md +102 -0
- package/dsh-docs/user/guide/github-review.zh.md +102 -0
- package/dsh-docs/user/guide/index.md +30 -0
- package/dsh-docs/user/guide/index.zh.md +30 -0
- package/dsh-docs/user/guide/mcp-memory.md +101 -0
- package/dsh-docs/user/guide/mcp-memory.zh.md +101 -0
- package/dsh-docs/user/guide/network-proxy.md +85 -0
- package/dsh-docs/user/guide/network-proxy.zh.md +85 -0
- package/dsh-docs/user/guide/providers.md +190 -0
- package/dsh-docs/user/guide/providers.zh.md +190 -0
- package/dsh-docs/user/guide/python-sdk.md +150 -0
- package/dsh-docs/user/guide/python-sdk.zh.md +150 -0
- package/dsh-docs/user/guide/schedule.md +21 -0
- package/dsh-docs/user/guide/schedule.zh.md +21 -0
- package/dsh-docs/user/index.md +11 -0
- package/dsh-docs/user/index.zh.md +11 -0
- package/dsh-docs/web-styling.md +29 -0
- package/dsh-docs/web-styling.zh.md +29 -0
- package/lib/client/index.js +268 -38
- package/lib/fs-aware/sandbox-plugin.js +1 -1
- package/lib/index.js +1112 -1261
- package/lib/lsp-server-registry-B8DNonhS.js +3 -0
- package/lib/lsp-server-registry-BexQagaK.js +943 -0
- package/lib/{wrap-DC8O3SYz.js → wrap-JFjcWwZf.js} +42 -16
- package/package.json +2 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# 运行时不变式
|
|
2
|
+
|
|
3
|
+
[English](invariants.md) | 中文
|
|
4
|
+
|
|
5
|
+
[dsh-invariants](../../packages/runtime-diagnostics/invariants) 是面向包自有运行时不变式检查的可配置注册表服务(`ctx.invariants`)。它是一个 support 组的包,不是三包能力 seam,也不属于 agent loop(智能体循环)主干:注册表拥有选择逻辑、名称保留、子 fiber 生命周期和归因到包的失败,而每个工作区包发布一个 `./invariant` 配套插件,以自己确切的 npm 包名注册检查。检查可以断言什么(权威事件流或可变数据,绝不是服务或方法是否存在)是 [AGENTS.md](../../AGENTS.md#conventions) 中的运行时不变式约定;注册表设计由[不变式服务 Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-package-owned-invariant-service.zh.md)规定。
|
|
6
|
+
|
|
7
|
+
源码:[`packages/runtime-diagnostics/invariants/src/index.ts`](../../packages/runtime-diagnostics/invariants/src/index.ts)
|
|
8
|
+
|
|
9
|
+
## 选择
|
|
10
|
+
|
|
11
|
+
```ts type-equiv
|
|
12
|
+
/** Runtime invariant selection configured on the service plugin. */
|
|
13
|
+
interface Config {
|
|
14
|
+
/** Global switch; defaults to `true`. */
|
|
15
|
+
readonly enabled?: boolean
|
|
16
|
+
/** Case-sensitive JavaScript regex sources that admit package names; empty admits all. */
|
|
17
|
+
readonly package_allowlist?: string[]
|
|
18
|
+
/** Case-sensitive JavaScript regex sources that exclude package names after allowlist matching. */
|
|
19
|
+
readonly package_blocklist?: string[]
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
一个包被选中的条件是:服务已启用,允许列表为空或至少一个模式匹配其完整 npm 名称,且没有任何阻止列表模式匹配;阻止列表匹配优先于允许列表匹配。条目用 `new RegExp(source)` 编译:除非模式自带 `^` 和 `$`,匹配不锚定;`/pattern/flags` 语法不被解析。校验在服务启动时明确报错:空白、首尾带空白、重复或无效的条目会抛出异常,而不是被跳过。有效模式可以不匹配任何当前已加载的包,因此后续加载与 HMR(热模块替换)保持确定性;过滤器在服务生命周期内固定不变([README](../../packages/runtime-diagnostics/invariants/README.zh.md))。
|
|
24
|
+
|
|
25
|
+
## 安装器
|
|
26
|
+
|
|
27
|
+
```ts type-equiv
|
|
28
|
+
/**
|
|
29
|
+
* Throw a package-attributed invariant failure.
|
|
30
|
+
* @param message - violated package contract without the standard prefix.
|
|
31
|
+
* @returns never because reporting a violation throws.
|
|
32
|
+
*/
|
|
33
|
+
type InvariantFailure = (message: string) => never
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```ts type-equiv
|
|
37
|
+
/** Install one package's checks into the registration's child context. */
|
|
38
|
+
interface InvariantInstaller {
|
|
39
|
+
/**
|
|
40
|
+
* Install the package contribution.
|
|
41
|
+
* @param ctx - child context owned by this invariant registration.
|
|
42
|
+
* @param fail - reporter bound to the registering package name.
|
|
43
|
+
* @returns nothing, or a promise settling after asynchronous checks finish.
|
|
44
|
+
*/
|
|
45
|
+
(ctx: Context, fail: InvariantFailure): void | Promise<void>
|
|
46
|
+
/** Services the child installer fiber may access. */
|
|
47
|
+
readonly inject?: Inject
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
被启用的安装器在专属的子 Cordis fiber 中运行;`installer.inject` 声明该 fiber 可以访问的服务,注册成功之前会先等待安装器同步或异步地执行完毕。`fail(message)` 抛出 `InvariantError`(`extends Error`,带稳定的 `code: 'INVARIANT'`、所属 `packageName`,以及前缀为 `invariant violated by "<package>": …` 的消息),因此违规可归因,而注册表无需导入任何产品包。
|
|
52
|
+
|
|
53
|
+
## 服务
|
|
54
|
+
|
|
55
|
+
`ctx.invariants.register(packageName, installer)` 为完整 npm 包名保留唯一一个活跃注册,并返回其绑定到 effect 的 disposer。即使过滤器使安装器保持不活跃,保留依然成立,因此两个插件绝不可能静默地认领同一个包名;重复、空白或含空白字符的名称会抛出异常。安装器失败会原子地 dispose(资源释放)子 fiber 并释放保留。服务拥有每个注册 fiber,而返回的 disposer 同时属于配套插件的 fiber:卸载任一侧都会移除监听器、trace 状态和保留项,因此配套插件可以重载并再次注册同一名称,不留残余状态。
|
|
56
|
+
|
|
57
|
+
## 配套插件约定
|
|
58
|
+
|
|
59
|
+
每个工作区包都拥有一个 `./invariant` 配套插件([包约定](../../packages/AGENTS.md));发布与注册是穷尽式的,但刻意不合成断言。只有当包拥有某个可观察事件或某种可变数据关系时,配套插件才安装检查;否则它导出一个空安装器,其起始注释以 `No runtime invariant:` 开头,针对该包具体解释为什么没有可检查项。`pnpm run verify-package-invariants` 机械地拒绝「生成文件」标记、无解释的空安装器、遗漏或忽略报告器的非空安装器、错误的注册名称,以及不完整的导出、发布、依赖或打包接线([机械规则 Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-package-invariant-runtime-contracts.zh.md))。可执行配套插件的目录与标准组合方式见[包 README](../../packages/runtime-diagnostics/invariants/README.zh.md)。
|
|
60
|
+
|
|
61
|
+
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
62
|
+
|
|
63
|
+
<a id="cordis-surface"></a>
|
|
64
|
+
|
|
65
|
+
## Cordis API
|
|
66
|
+
|
|
67
|
+
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`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.zh.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
|
|
68
|
+
|
|
69
|
+
<a id="ctxinvariants--invariantregistry"></a>
|
|
70
|
+
|
|
71
|
+
### `ctx.invariants` — `InvariantRegistry`
|
|
72
|
+
|
|
73
|
+
Package-owned invariant registry with global and regex-based selection.
|
|
74
|
+
|
|
75
|
+
```ts cordis-catalog
|
|
76
|
+
/**
|
|
77
|
+
* Register one package's invariant installer. The package name is reserved
|
|
78
|
+
* even when filtering disables its checks. Enabled installers run in a child
|
|
79
|
+
* fiber; failure disposes that fiber and releases the reservation.
|
|
80
|
+
* @param packageName - full npm package name that owns the contribution.
|
|
81
|
+
* @param installer - listener or startup-check installer for the child context.
|
|
82
|
+
* @returns an effect-scoped disposer for the registration.
|
|
83
|
+
*/
|
|
84
|
+
register(packageName: string, installer: InvariantInstaller): () => void
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Source: [`packages/runtime-diagnostics/invariants/src/index.ts`](../../packages/runtime-diagnostics/invariants/src/index.ts)
|
|
88
|
+
<!-- END GENERATED cordis-surface -->
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Background Task Runtime
|
|
2
|
+
|
|
3
|
+
English | [中文](jobs.zh.md)
|
|
4
|
+
|
|
5
|
+
Types shared by long-running producers, `ctx.jobs`, and job controls. The [runtime Agent Note](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md) owns the design; this page records the exact fields and variants from [`packages/jobs/jobs/src/types.ts`](../../packages/jobs/jobs/src/types.ts).
|
|
6
|
+
|
|
7
|
+
## Ids and status
|
|
8
|
+
|
|
9
|
+
`JobId` is a [branded id](core.md#branded-ids) generated as `<kind>-N`. Access control relies on owner authorization, not id secrecy. `JobKind` derives from a merge-extensible map; the registry treats kinds as opaque id namespaces.
|
|
10
|
+
|
|
11
|
+
```ts type-equiv
|
|
12
|
+
/**
|
|
13
|
+
* Producer-defined job kinds. Plugins extend this map by declaration merging;
|
|
14
|
+
* the registry treats every value as an opaque id namespace.
|
|
15
|
+
*/
|
|
16
|
+
interface JobKindMap {
|
|
17
|
+
bash: 'bash'
|
|
18
|
+
subagent: 'subagent'
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`JobStatus` is `'running' | 'stopping' | 'completed' | 'killed' | 'failed'`; producer-specific facts belong in `JobSnapshot.detail`.
|
|
23
|
+
|
|
24
|
+
## Producer contract
|
|
25
|
+
|
|
26
|
+
`JobStart` declares identity and a starter. The runtime finishes preflight before calling `run()` and commits without a later failable step. Producers own execution resources; the runtime owns identity, access, and lifecycle state.
|
|
27
|
+
|
|
28
|
+
```ts type-equiv
|
|
29
|
+
/**
|
|
30
|
+
* Producer declaration passed to {@link JobRegistry.start}. The runtime
|
|
31
|
+
* preflights access and cleanup before invoking {@link run}; the producer owns
|
|
32
|
+
* execution resources while the runtime owns identity and lifecycle state.
|
|
33
|
+
*/
|
|
34
|
+
interface JobStart {
|
|
35
|
+
/** Producer kind — also the id prefix (`bash`, `subagent`, …). */
|
|
36
|
+
kind: JobKind
|
|
37
|
+
/** One-line model-facing label (the command; the delegation description). */
|
|
38
|
+
label: string
|
|
39
|
+
/**
|
|
40
|
+
* Optional UTF-8 byte cap for each complete model-facing completion notice or
|
|
41
|
+
* output read, including controller status metadata.
|
|
42
|
+
*/
|
|
43
|
+
outputLimitBytes?: number
|
|
44
|
+
/**
|
|
45
|
+
* Owning live agent. Access is fenced by its session id, and agent disposal
|
|
46
|
+
* cancels and awaits the job. The instance must be the one currently
|
|
47
|
+
* registered under its agent id. Omitting the owner creates an unowned job,
|
|
48
|
+
* open to any caller until service disposal.
|
|
49
|
+
*/
|
|
50
|
+
owner?: Agent
|
|
51
|
+
/**
|
|
52
|
+
* Start the work after preflight and synchronously return its hooks. Called
|
|
53
|
+
* once; a throw leaves nothing registered, and the producer must clean up any
|
|
54
|
+
* partially started resources.
|
|
55
|
+
*/
|
|
56
|
+
run(): JobHooks
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`JobHooks.done` resolves after the producer releases its resources, not merely when work finishes. Optional `readOutput` distinguishes consuming stream jobs from final-output-only jobs.
|
|
61
|
+
|
|
62
|
+
```ts type-equiv
|
|
63
|
+
/** Hooks through which the runtime controls and observes producer work. */
|
|
64
|
+
interface JobHooks {
|
|
65
|
+
/**
|
|
66
|
+
* Request termination. Must be synchronous, idempotent, and eventually settle
|
|
67
|
+
* {@link done}; throws propagate. The optional reason is forwarded verbatim.
|
|
68
|
+
*/
|
|
69
|
+
cancel(reason?: string): void
|
|
70
|
+
/**
|
|
71
|
+
* Resolves after the producer releases its resources, not merely when work
|
|
72
|
+
* finishes. Must not reject; the runtime converts a rejection to `failed`.
|
|
73
|
+
* If teardown cancellation throws, the runtime may force-fail only the
|
|
74
|
+
* registry record without claiming that the work stopped.
|
|
75
|
+
*/
|
|
76
|
+
done: Promise<JobOutcome>
|
|
77
|
+
/**
|
|
78
|
+
* Consume output produced since the previous call. The producer formats
|
|
79
|
+
* truncation and spill notices. Absence marks a final-output-only job; each
|
|
80
|
+
* job has one consuming cursor.
|
|
81
|
+
*/
|
|
82
|
+
readOutput?(): string
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```ts type-equiv
|
|
87
|
+
/** Terminal result supplied by a producer through {@link JobHooks.done}. */
|
|
88
|
+
interface JobOutcome {
|
|
89
|
+
/** How the job ended: finished (`completed`), cancelled (`killed`), or broke (`failed`). */
|
|
90
|
+
status: 'completed' | 'killed' | 'failed'
|
|
91
|
+
/** Kind-specific detail rendered into status lines ('exit code: 3', 'max-tokens'). */
|
|
92
|
+
detail?: string
|
|
93
|
+
/** Final output for jobs without `readOutput`; stream jobs leave it unset. */
|
|
94
|
+
output?: string
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Consumer views
|
|
99
|
+
|
|
100
|
+
Snapshots are fresh read-only projections. `ownerSession` carries the shared `SessionId` used for authorization; completion listeners separately receive the exact owner object used for lifecycle cleanup. `reported` suppresses a completion notice after another reporter has delivered or committed to deliver the terminal state, including the teardown cancel that drains an owner or the service.
|
|
101
|
+
|
|
102
|
+
```ts type-equiv
|
|
103
|
+
/**
|
|
104
|
+
* A read-only projection of one job, safe to hand to listeners and tools —
|
|
105
|
+
* a fresh object per call, never live registry state.
|
|
106
|
+
*/
|
|
107
|
+
interface JobSnapshot {
|
|
108
|
+
/** The registry-issued id (`<kind>-N`). */
|
|
109
|
+
id: JobId
|
|
110
|
+
/** The producer kind the job was registered with. */
|
|
111
|
+
kind: JobKind
|
|
112
|
+
/** The producer-supplied one-line label. */
|
|
113
|
+
label: string
|
|
114
|
+
/** Producer-owned cap for complete model-facing notices and output reads. */
|
|
115
|
+
outputLimitBytes?: number
|
|
116
|
+
/**
|
|
117
|
+
* Owner session id used for authorization and correlation; absent for
|
|
118
|
+
* unowned jobs. Completion listeners receive the exact {@link Agent}
|
|
119
|
+
* separately through {@link JobDoneListener}.
|
|
120
|
+
*/
|
|
121
|
+
ownerSession?: SessionId
|
|
122
|
+
/** Current lifecycle state. */
|
|
123
|
+
status: JobStatus
|
|
124
|
+
/** Kind-specific status detail, present once the producer supplied one (usually terminal). */
|
|
125
|
+
detail?: string
|
|
126
|
+
/** Epoch ms when the job was registered. */
|
|
127
|
+
startedAt: number
|
|
128
|
+
/** Epoch ms when the job settled; absent while `running`/`stopping`. */
|
|
129
|
+
finishedAt?: number
|
|
130
|
+
/**
|
|
131
|
+
* True when a kill, read, wait, or teardown cancel has reported or committed
|
|
132
|
+
* to report the terminal state. Completion reporters suppress redundant
|
|
133
|
+
* notices when set. Teardown claims it because the owner or service being
|
|
134
|
+
* destroyed leaves no reader: a reporter that opens a turn on notice would
|
|
135
|
+
* otherwise spend a model request per teardown layer.
|
|
136
|
+
*/
|
|
137
|
+
reported: boolean
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```ts type-equiv
|
|
142
|
+
/** Output and post-read state returned by {@link JobRegistry.read}. */
|
|
143
|
+
interface JobRead {
|
|
144
|
+
/**
|
|
145
|
+
* Stream kinds: the consuming delta since the previous read. Final-output
|
|
146
|
+
* kinds: empty while live, the terminal {@link JobOutcome.output} (or
|
|
147
|
+
* empty) once settled — idempotent, never consumed.
|
|
148
|
+
*/
|
|
149
|
+
text: string
|
|
150
|
+
/** The job's state at read time. */
|
|
151
|
+
snapshot: JobSnapshot
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Service behavior
|
|
156
|
+
|
|
157
|
+
The abstract [`JobRegistry`](../../packages/jobs/jobs/src/index.ts) Service Definition specifies atomic `start`, caller-scoped `get` and `list`, `read`, `kill`, bounded `wait`, failure-isolated `onJobDone` and `onJobsChanged` listeners, and `attachController`; [`LocalJobRegistry`](../../packages/jobs/jobs-local/src/index.ts) is the process-local Service Provider. Authorization compares owner sessions; owner cleanup and admission use the exact registered `Agent` instance. The local provider's positive-safe-integer `maxConcurrentJobsPerOwner` config defaults to `10` and counts `running` plus `stopping` records per exact owner, with one shared bucket for unowned jobs; terminal producer settlement releases capacity. See [`dsh-jobs`](../../packages/jobs/jobs/README.md) for the Service Definition contract, [`dsh-jobs-local`](../../packages/jobs/jobs-local/README.md) for the registry lifecycle and admission policy, and [`dsh-tool-jobs`](../../packages/jobs/tool-jobs/README.md) for the model-facing Consumer.
|
|
158
|
+
|
|
159
|
+
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
160
|
+
|
|
161
|
+
<a id="cordis-surface"></a>
|
|
162
|
+
|
|
163
|
+
## Cordis API
|
|
164
|
+
|
|
165
|
+
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`) — the language sides differ only in locale-specific paired document paths. 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).
|
|
166
|
+
|
|
167
|
+
<a id="ctxjobs--jobregistry-abstract-seam"></a>
|
|
168
|
+
|
|
169
|
+
### `ctx.jobs` — `JobRegistry` (abstract seam)
|
|
170
|
+
|
|
171
|
+
Abstract background job registry. Subclass, implement the abstract methods, and load the subclass as a plugin — it registers as `ctx.jobs` (one implementation per context; loading a second throws, which is cordis' standard duplicate-service behavior).
|
|
172
|
+
|
|
173
|
+
Implementations must honor these semantics:
|
|
174
|
+
|
|
175
|
+
- Registrations outlive producer and controller fibers. Owner and service disposal cancel live work and await compliant producers; a throwing teardown cancel force-fails only the record. Teardown cancellation also marks the record reported, because a record its owner is being destroyed for has no reader left.
|
|
176
|
+
- Owned-job access is fenced by the owner's session id. Ids are predictable, so authorization — not secrecy — is the boundary.
|
|
177
|
+
- Settlement is first-wins: one terminal record, released waiters, and one round of contained listener notification, even against a late producer outcome. Completion is announced last, after the record is committed and every other observer of the settlement has seen it, because a reporter may open a model turn synchronously.
|
|
178
|
+
- start refuses work while no attached job controller serves the spec's owner, so a producer cannot start work that owner cannot collect or stop. One registry serves every composition in the process, so this question — and completion-listener delivery — is owner-relative rather than process-wide: registrations made from an unscoped context serve every owner, and registrations made under an agent composition's scope serve exactly the agents composed under it.
|
|
179
|
+
|
|
180
|
+
```ts cordis-catalog
|
|
181
|
+
/**
|
|
182
|
+
* Preflight access, validation, owner cleanup, and implementation-owned
|
|
183
|
+
* admission before starting and atomically registering work. Any preflight
|
|
184
|
+
* rejection leaves no job id or execution resource. A throwing starter
|
|
185
|
+
* leaves nothing registered; after it returns, registration cannot fail.
|
|
186
|
+
* Settlement records the outcome, notifies listeners, and releases waiters.
|
|
187
|
+
* @param spec - job identity, owner, and synchronous starter.
|
|
188
|
+
* @returns the registry-issued `<kind>-N` id.
|
|
189
|
+
*/
|
|
190
|
+
abstract start(spec: JobStart): JobId
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* List caller-owned and unowned jobs in registration order without exposing
|
|
194
|
+
* another session's labels.
|
|
195
|
+
* @param caller - reading agent; a non-agent caller sees only unowned jobs.
|
|
196
|
+
* @returns fresh snapshots.
|
|
197
|
+
*/
|
|
198
|
+
abstract list(caller?: Agent): JobSnapshot[]
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Return a non-consuming snapshot without changing its read cursor or notice
|
|
202
|
+
* state. Throws for an unknown or foreign job.
|
|
203
|
+
* @param id - job to look up.
|
|
204
|
+
* @param caller - reading agent checked against the owner.
|
|
205
|
+
* @returns a fresh snapshot.
|
|
206
|
+
*/
|
|
207
|
+
abstract get(id: JobId, caller?: Agent): JobSnapshot
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Read the next stream delta, or the idempotent final output after settlement.
|
|
211
|
+
* A terminal read marks the job reported. Throws for an unknown or foreign
|
|
212
|
+
* job.
|
|
213
|
+
* @param id - job to read.
|
|
214
|
+
* @param caller - reading agent checked against the owner.
|
|
215
|
+
* @returns output text and the post-read snapshot.
|
|
216
|
+
*/
|
|
217
|
+
abstract read(id: JobId, caller?: Agent): JobRead
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Request cancellation, then mark the job stopping and reported. A producer
|
|
221
|
+
* throw propagates without changing job state. Throws for an unknown or
|
|
222
|
+
* foreign job.
|
|
223
|
+
* @param id - job to cancel.
|
|
224
|
+
* @param caller - killing agent checked against the owner.
|
|
225
|
+
* @param reason - logged reason forwarded to the producer.
|
|
226
|
+
* @returns `requested` for live work, otherwise `already-finished`.
|
|
227
|
+
*/
|
|
228
|
+
abstract kill(id: JobId, caller?: Agent, reason?: string): 'requested' | 'already-finished'
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Wait for settlement or timeout without cancelling the job. Caller abort
|
|
232
|
+
* rejects only while the job is live; after settlement the terminal
|
|
233
|
+
* snapshot wins so a notice suppressed for this waiter is still delivered.
|
|
234
|
+
* Throws for invalid, unknown, or foreign input.
|
|
235
|
+
* @param id - job to wait for.
|
|
236
|
+
* @param timeoutMs - positive finite wait bound in milliseconds.
|
|
237
|
+
* @param caller - waiting agent checked against the owner.
|
|
238
|
+
* @param signal - optional cancellation of the wait itself.
|
|
239
|
+
* @returns snapshot at settlement or timeout.
|
|
240
|
+
*/
|
|
241
|
+
abstract wait(id: JobId, timeoutMs: number, caller?: Agent, signal?: AbortSignal): Promise<JobSnapshot>
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Register an effect-scoped completion listener. It receives the settlements
|
|
245
|
+
* of the owners its registering context's scope covers; each listener is
|
|
246
|
+
* contained; returned promises are observed but not awaited. No listener runs
|
|
247
|
+
* after service disposal.
|
|
248
|
+
* @param listener - receives each terminal snapshot and its exact owner.
|
|
249
|
+
* @returns disposer that unregisters the listener.
|
|
250
|
+
*/
|
|
251
|
+
abstract onJobDone(listener: JobDoneListener): () => void
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
/**
|
|
255
|
+
* Register an effect-scoped observer of visible-set changes. It fires after
|
|
256
|
+
* every commit that changes what {@link list} returns for that owner —
|
|
257
|
+
* registration, every stopping transition (including the one teardown
|
|
258
|
+
* performs before it awaits a slow producer), settlement, owner-disposal
|
|
259
|
+
* removal, and the emptying that service disposal commits — so an observer
|
|
260
|
+
* re-reads rather than accumulating deltas.
|
|
261
|
+
*
|
|
262
|
+
* Delivery is owner-relative on the same terms as {@link onJobDone}: an
|
|
263
|
+
* observer registered from an unscoped context — a host composition's own
|
|
264
|
+
* carrier — sees every owner, while one registered under an agent
|
|
265
|
+
* composition's scope sees exactly the agents composed under it.
|
|
266
|
+
*
|
|
267
|
+
* This is not a superset of {@link onJobDone}: that one delivers the terminal
|
|
268
|
+
* record under first-wins semantics a job controller couples to notice
|
|
269
|
+
* delivery, while this one carries no delivery meaning and marks nothing
|
|
270
|
+
* reported. Listeners are contained and never awaited.
|
|
271
|
+
* @param listener - receives the owner whose visible set changed, or
|
|
272
|
+
* `undefined` when an unowned job changed and every caller's set did.
|
|
273
|
+
* @returns disposer that unregisters the listener.
|
|
274
|
+
*/
|
|
275
|
+
abstract onJobsChanged(listener: JobsChangedListener): () => void
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Attach an effect-scoped controller that can read and stop jobs. It serves the
|
|
279
|
+
* owners its registering context's scope covers, and {@link start} refuses an
|
|
280
|
+
* owner no attached controller serves.
|
|
281
|
+
* @param name - diagnostic label; duplicate names remain independent.
|
|
282
|
+
* @returns disposer that detaches this controller.
|
|
283
|
+
*/
|
|
284
|
+
abstract attachController(name: string): () => void
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Types: [Agent](core.md)
|
|
288
|
+
|
|
289
|
+
Source: [`packages/jobs/jobs/src/index.ts`](../../packages/jobs/jobs/src/index.ts)
|
|
290
|
+
<!-- END GENERATED cordis-surface -->
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# 后台任务运行时
|
|
2
|
+
|
|
3
|
+
[English](jobs.md) | 中文
|
|
4
|
+
|
|
5
|
+
长时间运行的生产方、`ctx.jobs` 与任务控制命令共用的类型。[运行时 Agent Note](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.zh.md) 负责设计;本页记录 [`packages/jobs/jobs/src/types.ts`](../../packages/jobs/jobs/src/types.ts) 中的确切字段和变体。
|
|
6
|
+
|
|
7
|
+
## ID 与状态
|
|
8
|
+
|
|
9
|
+
`JobId` 是按 `<kind>-N` 生成的[品牌化 id](core.zh.md#branded-ids)。访问控制依赖拥有者授权,而非 id 的保密性。`JobKind` 派生自可合并扩展的 map;注册表将各个 kind 视为不透明的 id 命名空间。
|
|
10
|
+
|
|
11
|
+
```ts type-equiv
|
|
12
|
+
/**
|
|
13
|
+
* Producer-defined job kinds. Plugins extend this map by declaration merging;
|
|
14
|
+
* the registry treats every value as an opaque id namespace.
|
|
15
|
+
*/
|
|
16
|
+
interface JobKindMap {
|
|
17
|
+
bash: 'bash'
|
|
18
|
+
subagent: 'subagent'
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`JobStatus` 为 `'running' | 'stopping' | 'completed' | 'killed' | 'failed'`;生产方特有的事实归入 `JobSnapshot.detail`。
|
|
23
|
+
|
|
24
|
+
## 生产方约定
|
|
25
|
+
|
|
26
|
+
`JobStart` 声明身份和启动器。运行时会在调用 `run()` 前完成预检,随后提交注册,不再执行可能失败的步骤。生产方拥有执行资源;运行时拥有身份、访问权限和生命周期状态。
|
|
27
|
+
|
|
28
|
+
```ts type-equiv
|
|
29
|
+
/**
|
|
30
|
+
* Producer declaration passed to {@link JobRegistry.start}. The runtime
|
|
31
|
+
* preflights access and cleanup before invoking {@link run}; the producer owns
|
|
32
|
+
* execution resources while the runtime owns identity and lifecycle state.
|
|
33
|
+
*/
|
|
34
|
+
interface JobStart {
|
|
35
|
+
/** Producer kind — also the id prefix (`bash`, `subagent`, …). */
|
|
36
|
+
kind: JobKind
|
|
37
|
+
/** One-line model-facing label (the command; the delegation description). */
|
|
38
|
+
label: string
|
|
39
|
+
/**
|
|
40
|
+
* Optional UTF-8 byte cap for each complete model-facing completion notice or
|
|
41
|
+
* output read, including controller status metadata.
|
|
42
|
+
*/
|
|
43
|
+
outputLimitBytes?: number
|
|
44
|
+
/**
|
|
45
|
+
* Owning live agent. Access is fenced by its session id, and agent disposal
|
|
46
|
+
* cancels and awaits the job. The instance must be the one currently
|
|
47
|
+
* registered under its agent id. Omitting the owner creates an unowned job,
|
|
48
|
+
* open to any caller until service disposal.
|
|
49
|
+
*/
|
|
50
|
+
owner?: Agent
|
|
51
|
+
/**
|
|
52
|
+
* Start the work after preflight and synchronously return its hooks. Called
|
|
53
|
+
* once; a throw leaves nothing registered, and the producer must clean up any
|
|
54
|
+
* partially started resources.
|
|
55
|
+
*/
|
|
56
|
+
run(): JobHooks
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`JobHooks.done` 会在生产方释放其资源后 resolve,而不是仅在工作完成时 resolve。可选的 `readOutput` 用来区分会消费输出的流式任务和仅有最终输出的任务。
|
|
61
|
+
|
|
62
|
+
```ts type-equiv
|
|
63
|
+
/** Hooks through which the runtime controls and observes producer work. */
|
|
64
|
+
interface JobHooks {
|
|
65
|
+
/**
|
|
66
|
+
* Request termination. Must be synchronous, idempotent, and eventually settle
|
|
67
|
+
* {@link done}; throws propagate. The optional reason is forwarded verbatim.
|
|
68
|
+
*/
|
|
69
|
+
cancel(reason?: string): void
|
|
70
|
+
/**
|
|
71
|
+
* Resolves after the producer releases its resources, not merely when work
|
|
72
|
+
* finishes. Must not reject; the runtime converts a rejection to `failed`.
|
|
73
|
+
* If teardown cancellation throws, the runtime may force-fail only the
|
|
74
|
+
* registry record without claiming that the work stopped.
|
|
75
|
+
*/
|
|
76
|
+
done: Promise<JobOutcome>
|
|
77
|
+
/**
|
|
78
|
+
* Consume output produced since the previous call. The producer formats
|
|
79
|
+
* truncation and spill notices. Absence marks a final-output-only job; each
|
|
80
|
+
* job has one consuming cursor.
|
|
81
|
+
*/
|
|
82
|
+
readOutput?(): string
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```ts type-equiv
|
|
87
|
+
/** Terminal result supplied by a producer through {@link JobHooks.done}. */
|
|
88
|
+
interface JobOutcome {
|
|
89
|
+
/** How the job ended: finished (`completed`), cancelled (`killed`), or broke (`failed`). */
|
|
90
|
+
status: 'completed' | 'killed' | 'failed'
|
|
91
|
+
/** Kind-specific detail rendered into status lines ('exit code: 3', 'max-tokens'). */
|
|
92
|
+
detail?: string
|
|
93
|
+
/** Final output for jobs without `readOutput`; stream jobs leave it unset. */
|
|
94
|
+
output?: string
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 消费方视图
|
|
99
|
+
|
|
100
|
+
快照是每次新建的只读投影。`ownerSession` 携带用于授权的共享 `SessionId`;完成监听器则会另行收到用于生命周期清理的确切拥有者对象。另一个接口已经交付终止状态或承诺交付时,`reported` 会抑制完成通知;排空 owner 或服务的 teardown 取消同样计入。
|
|
101
|
+
|
|
102
|
+
```ts type-equiv
|
|
103
|
+
/**
|
|
104
|
+
* A read-only projection of one job, safe to hand to listeners and tools —
|
|
105
|
+
* a fresh object per call, never live registry state.
|
|
106
|
+
*/
|
|
107
|
+
interface JobSnapshot {
|
|
108
|
+
/** The registry-issued id (`<kind>-N`). */
|
|
109
|
+
id: JobId
|
|
110
|
+
/** The producer kind the job was registered with. */
|
|
111
|
+
kind: JobKind
|
|
112
|
+
/** The producer-supplied one-line label. */
|
|
113
|
+
label: string
|
|
114
|
+
/** Producer-owned cap for complete model-facing notices and output reads. */
|
|
115
|
+
outputLimitBytes?: number
|
|
116
|
+
/**
|
|
117
|
+
* Owner session id used for authorization and correlation; absent for
|
|
118
|
+
* unowned jobs. Completion listeners receive the exact {@link Agent}
|
|
119
|
+
* separately through {@link JobDoneListener}.
|
|
120
|
+
*/
|
|
121
|
+
ownerSession?: SessionId
|
|
122
|
+
/** Current lifecycle state. */
|
|
123
|
+
status: JobStatus
|
|
124
|
+
/** Kind-specific status detail, present once the producer supplied one (usually terminal). */
|
|
125
|
+
detail?: string
|
|
126
|
+
/** Epoch ms when the job was registered. */
|
|
127
|
+
startedAt: number
|
|
128
|
+
/** Epoch ms when the job settled; absent while `running`/`stopping`. */
|
|
129
|
+
finishedAt?: number
|
|
130
|
+
/**
|
|
131
|
+
* True when a kill, read, wait, or teardown cancel has reported or committed
|
|
132
|
+
* to report the terminal state. Completion reporters suppress redundant
|
|
133
|
+
* notices when set. Teardown claims it because the owner or service being
|
|
134
|
+
* destroyed leaves no reader: a reporter that opens a turn on notice would
|
|
135
|
+
* otherwise spend a model request per teardown layer.
|
|
136
|
+
*/
|
|
137
|
+
reported: boolean
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```ts type-equiv
|
|
142
|
+
/** Output and post-read state returned by {@link JobRegistry.read}. */
|
|
143
|
+
interface JobRead {
|
|
144
|
+
/**
|
|
145
|
+
* Stream kinds: the consuming delta since the previous read. Final-output
|
|
146
|
+
* kinds: empty while live, the terminal {@link JobOutcome.output} (or
|
|
147
|
+
* empty) once settled — idempotent, never consumed.
|
|
148
|
+
*/
|
|
149
|
+
text: string
|
|
150
|
+
/** The job's state at read time. */
|
|
151
|
+
snapshot: JobSnapshot
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## 服务行为
|
|
156
|
+
|
|
157
|
+
抽象的 [`JobRegistry`](../../packages/jobs/jobs/src/index.ts) Service Definition 规定原子 `start`、限定调用方作用域的 `get` 和 `list`、`read`、`kill`、有界 `wait`、故障隔离的 `onJobDone` 与 `onJobsChanged` 监听器,以及 `attachController`;[`LocalJobRegistry`](../../packages/jobs/jobs-local/src/index.ts) 是其进程局部 Service Provider。授权会比较拥有者会话;拥有者清理与准入会使用确切的已注册 `Agent` 实例。本地 Service Provider 的 `maxConcurrentJobsPerOwner` 配置必须是正的安全整数,默认值为 `10`;它按确切 owner 统计 `running` 与 `stopping` 记录,所有无 owner 任务共享一个服务级桶,并在生产方终止结算后释放容量。Service Definition 约定见 [`dsh-jobs`](../../packages/jobs/jobs/README.zh.md),注册表生命周期与准入策略见 [`dsh-jobs-local`](../../packages/jobs/jobs-local/README.zh.md),面向模型的 Consumer 见 [`dsh-tool-jobs`](../../packages/jobs/tool-jobs/README.zh.md)。
|
|
158
|
+
|
|
159
|
+
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
160
|
+
|
|
161
|
+
<a id="cordis-surface"></a>
|
|
162
|
+
|
|
163
|
+
## Cordis API
|
|
164
|
+
|
|
165
|
+
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`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.zh.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
|
|
166
|
+
|
|
167
|
+
<a id="ctxjobs--jobregistry-abstract-seam"></a>
|
|
168
|
+
|
|
169
|
+
### `ctx.jobs` — `JobRegistry` (abstract seam)
|
|
170
|
+
|
|
171
|
+
Abstract background job registry. Subclass, implement the abstract methods, and load the subclass as a plugin — it registers as `ctx.jobs` (one implementation per context; loading a second throws, which is cordis' standard duplicate-service behavior).
|
|
172
|
+
|
|
173
|
+
Implementations must honor these semantics:
|
|
174
|
+
|
|
175
|
+
- Registrations outlive producer and controller fibers. Owner and service disposal cancel live work and await compliant producers; a throwing teardown cancel force-fails only the record. Teardown cancellation also marks the record reported, because a record its owner is being destroyed for has no reader left.
|
|
176
|
+
- Owned-job access is fenced by the owner's session id. Ids are predictable, so authorization — not secrecy — is the boundary.
|
|
177
|
+
- Settlement is first-wins: one terminal record, released waiters, and one round of contained listener notification, even against a late producer outcome. Completion is announced last, after the record is committed and every other observer of the settlement has seen it, because a reporter may open a model turn synchronously.
|
|
178
|
+
- start refuses work while no attached job controller serves the spec's owner, so a producer cannot start work that owner cannot collect or stop. One registry serves every composition in the process, so this question — and completion-listener delivery — is owner-relative rather than process-wide: registrations made from an unscoped context serve every owner, and registrations made under an agent composition's scope serve exactly the agents composed under it.
|
|
179
|
+
|
|
180
|
+
```ts cordis-catalog
|
|
181
|
+
/**
|
|
182
|
+
* Preflight access, validation, owner cleanup, and implementation-owned
|
|
183
|
+
* admission before starting and atomically registering work. Any preflight
|
|
184
|
+
* rejection leaves no job id or execution resource. A throwing starter
|
|
185
|
+
* leaves nothing registered; after it returns, registration cannot fail.
|
|
186
|
+
* Settlement records the outcome, notifies listeners, and releases waiters.
|
|
187
|
+
* @param spec - job identity, owner, and synchronous starter.
|
|
188
|
+
* @returns the registry-issued `<kind>-N` id.
|
|
189
|
+
*/
|
|
190
|
+
abstract start(spec: JobStart): JobId
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* List caller-owned and unowned jobs in registration order without exposing
|
|
194
|
+
* another session's labels.
|
|
195
|
+
* @param caller - reading agent; a non-agent caller sees only unowned jobs.
|
|
196
|
+
* @returns fresh snapshots.
|
|
197
|
+
*/
|
|
198
|
+
abstract list(caller?: Agent): JobSnapshot[]
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Return a non-consuming snapshot without changing its read cursor or notice
|
|
202
|
+
* state. Throws for an unknown or foreign job.
|
|
203
|
+
* @param id - job to look up.
|
|
204
|
+
* @param caller - reading agent checked against the owner.
|
|
205
|
+
* @returns a fresh snapshot.
|
|
206
|
+
*/
|
|
207
|
+
abstract get(id: JobId, caller?: Agent): JobSnapshot
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Read the next stream delta, or the idempotent final output after settlement.
|
|
211
|
+
* A terminal read marks the job reported. Throws for an unknown or foreign
|
|
212
|
+
* job.
|
|
213
|
+
* @param id - job to read.
|
|
214
|
+
* @param caller - reading agent checked against the owner.
|
|
215
|
+
* @returns output text and the post-read snapshot.
|
|
216
|
+
*/
|
|
217
|
+
abstract read(id: JobId, caller?: Agent): JobRead
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Request cancellation, then mark the job stopping and reported. A producer
|
|
221
|
+
* throw propagates without changing job state. Throws for an unknown or
|
|
222
|
+
* foreign job.
|
|
223
|
+
* @param id - job to cancel.
|
|
224
|
+
* @param caller - killing agent checked against the owner.
|
|
225
|
+
* @param reason - logged reason forwarded to the producer.
|
|
226
|
+
* @returns `requested` for live work, otherwise `already-finished`.
|
|
227
|
+
*/
|
|
228
|
+
abstract kill(id: JobId, caller?: Agent, reason?: string): 'requested' | 'already-finished'
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Wait for settlement or timeout without cancelling the job. Caller abort
|
|
232
|
+
* rejects only while the job is live; after settlement the terminal
|
|
233
|
+
* snapshot wins so a notice suppressed for this waiter is still delivered.
|
|
234
|
+
* Throws for invalid, unknown, or foreign input.
|
|
235
|
+
* @param id - job to wait for.
|
|
236
|
+
* @param timeoutMs - positive finite wait bound in milliseconds.
|
|
237
|
+
* @param caller - waiting agent checked against the owner.
|
|
238
|
+
* @param signal - optional cancellation of the wait itself.
|
|
239
|
+
* @returns snapshot at settlement or timeout.
|
|
240
|
+
*/
|
|
241
|
+
abstract wait(id: JobId, timeoutMs: number, caller?: Agent, signal?: AbortSignal): Promise<JobSnapshot>
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Register an effect-scoped completion listener. It receives the settlements
|
|
245
|
+
* of the owners its registering context's scope covers; each listener is
|
|
246
|
+
* contained; returned promises are observed but not awaited. No listener runs
|
|
247
|
+
* after service disposal.
|
|
248
|
+
* @param listener - receives each terminal snapshot and its exact owner.
|
|
249
|
+
* @returns disposer that unregisters the listener.
|
|
250
|
+
*/
|
|
251
|
+
abstract onJobDone(listener: JobDoneListener): () => void
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
/**
|
|
255
|
+
* Register an effect-scoped observer of visible-set changes. It fires after
|
|
256
|
+
* every commit that changes what {@link list} returns for that owner —
|
|
257
|
+
* registration, every stopping transition (including the one teardown
|
|
258
|
+
* performs before it awaits a slow producer), settlement, owner-disposal
|
|
259
|
+
* removal, and the emptying that service disposal commits — so an observer
|
|
260
|
+
* re-reads rather than accumulating deltas.
|
|
261
|
+
*
|
|
262
|
+
* Delivery is owner-relative on the same terms as {@link onJobDone}: an
|
|
263
|
+
* observer registered from an unscoped context — a host composition's own
|
|
264
|
+
* carrier — sees every owner, while one registered under an agent
|
|
265
|
+
* composition's scope sees exactly the agents composed under it.
|
|
266
|
+
*
|
|
267
|
+
* This is not a superset of {@link onJobDone}: that one delivers the terminal
|
|
268
|
+
* record under first-wins semantics a job controller couples to notice
|
|
269
|
+
* delivery, while this one carries no delivery meaning and marks nothing
|
|
270
|
+
* reported. Listeners are contained and never awaited.
|
|
271
|
+
* @param listener - receives the owner whose visible set changed, or
|
|
272
|
+
* `undefined` when an unowned job changed and every caller's set did.
|
|
273
|
+
* @returns disposer that unregisters the listener.
|
|
274
|
+
*/
|
|
275
|
+
abstract onJobsChanged(listener: JobsChangedListener): () => void
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Attach an effect-scoped controller that can read and stop jobs. It serves the
|
|
279
|
+
* owners its registering context's scope covers, and {@link start} refuses an
|
|
280
|
+
* owner no attached controller serves.
|
|
281
|
+
* @param name - diagnostic label; duplicate names remain independent.
|
|
282
|
+
* @returns disposer that detaches this controller.
|
|
283
|
+
*/
|
|
284
|
+
abstract attachController(name: string): () => void
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Types: [Agent](core.zh.md)
|
|
288
|
+
|
|
289
|
+
Source: [`packages/jobs/jobs/src/index.ts`](../../packages/jobs/jobs/src/index.ts)
|
|
290
|
+
<!-- END GENERATED cordis-surface -->
|