@x1a0f3n9/dsh-tool-workflow 0.1.5-rc.5 → 0.1.7-rc.2

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/README.i18n.yaml CHANGED
@@ -1,6 +1,103 @@
1
- # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
- # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
- # after editing either side, bring the other along and re-record with:
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/workflow/tool-workflow/README.md
5
- README.md: 8c93fa2658703f2252b3300a4c55cec4ae5fa0cf
6
- README.zh.md: 354502a49f0157af2fb3e6c2109e303184400a44
5
+ /:
6
+ en: de1de2c83432e095
7
+ zh: facea56204dc9c76
8
+ /x1a0f3n9-dsh-tool-workflow:
9
+ en: cc1f12ead29e9889
10
+ zh: 78196882c9bcafa5
11
+ /x1a0f3n9-dsh-tool-workflow/summary:
12
+ en: c18878dbaecc42bb
13
+ zh: 360e2a13717bdb04
14
+ /x1a0f3n9-dsh-tool-workflow/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 09388d293f9be9cb
17
+ /x1a0f3n9-dsh-tool-workflow/use-this-package:
18
+ en: a0a9d655187f285d
19
+ zh: 230b4556fff9f08b
20
+ /x1a0f3n9-dsh-tool-workflow/use-this-package/calling-the-tool:
21
+ en: aeebba9469f0558d
22
+ zh: e2e74d4272e94201
23
+ /x1a0f3n9-dsh-tool-workflow/use-this-package/what-to-expect-during-a-run:
24
+ en: 19ec4f830edb8e1a
25
+ zh: 8579ae5274aca6de
26
+ /x1a0f3n9-dsh-tool-workflow/use-this-package/background-runs:
27
+ en: 3083f3f2657a46e3
28
+ zh: bca5cb741ea76798
29
+ /x1a0f3n9-dsh-tool-workflow/use-this-package/config:
30
+ en: 18d1067a480e072e
31
+ zh: cf27e51f0dec2f4d
32
+ /x1a0f3n9-dsh-tool-workflow/understand-the-implementation:
33
+ en: 18684e6da513cdb5
34
+ zh: bd4cdcabfe4722ec
35
+ /x1a0f3n9-dsh-tool-workflow/understand-the-implementation/design-concept:
36
+ en: 6d09119d1393c254
37
+ zh: a7cc03daa59650ec
38
+ /x1a0f3n9-dsh-tool-workflow/understand-the-implementation/run-lifecycle:
39
+ en: 8ea9ce564251249a
40
+ zh: a59b88cc14a25a1f
41
+ /x1a0f3n9-dsh-tool-workflow/understand-the-implementation/background-lifecycle:
42
+ en: 35950b2015800ec5
43
+ zh: 5ae6f5a5e53dc585
44
+ /x1a0f3n9-dsh-tool-workflow/understand-the-implementation/durable-session-records:
45
+ en: b491e7ab9590e625
46
+ zh: c0a2fb797eb1cca0
47
+ /x1a0f3n9-dsh-tool-workflow/understand-the-implementation/render-intent:
48
+ en: 389b3740ec1f5ac2
49
+ zh: 78430014f5e270e2
50
+ /x1a0f3n9-dsh-tool-workflow/understand-the-implementation/source-map:
51
+ en: 8a426c65b06a2320
52
+ zh: e2bb8f12252fbd4d
53
+ /x1a0f3n9-dsh-tool-workflow/further-exploration:
54
+ en: cf5f97e745586c05
55
+ zh: 32fa36224cde5d83
56
+ /x1a0f3n9-dsh-tool-workflow/model-experience:
57
+ en: 215e7ba838619b7b
58
+ zh: a311da3843709f90
59
+ /x1a0f3n9-dsh-tool-workflow/model-experience/system-prompt:
60
+ en: cf78b9dd5596495f
61
+ zh: bd44560f8c2435e8
62
+ /x1a0f3n9-dsh-tool-workflow/model-experience/system-prompt/what-the-model-sees:
63
+ en: 13411e342534174d
64
+ zh: 0991c63ac26ada3f
65
+ /x1a0f3n9-dsh-tool-workflow/model-experience/system-prompt/what-the-model-sees/workflow-guidance:
66
+ en: 214b61fe5bdb1097
67
+ zh: 2358fc753d21f12c
68
+ /x1a0f3n9-dsh-tool-workflow/model-experience/system-prompt/token-effect:
69
+ en: dab67480bfde6224
70
+ zh: 85bbf18da3767aa7
71
+ /x1a0f3n9-dsh-tool-workflow/model-experience/system-prompt/kv-cache-effect:
72
+ en: 13e7c6007bf6b5dc
73
+ zh: 1690c361c6f07ba4
74
+ /x1a0f3n9-dsh-tool-workflow/model-experience/tool-schema:
75
+ en: e4c7376d4f29dbd5
76
+ zh: 6b7a9eaf69c9ea53
77
+ /x1a0f3n9-dsh-tool-workflow/model-experience/tool-schema/what-the-model-sees:
78
+ en: 5af871e5bc1126c7
79
+ zh: 07b5e2b37c766a7e
80
+ /x1a0f3n9-dsh-tool-workflow/model-experience/tool-schema/token-effect:
81
+ en: 2038bdeb03f99195
82
+ zh: 90959fa2708b910f
83
+ /x1a0f3n9-dsh-tool-workflow/model-experience/tool-schema/kv-cache-effect:
84
+ en: e01353abf883d9ba
85
+ zh: 3c9e5d6bd7f4e831
86
+ /x1a0f3n9-dsh-tool-workflow/model-experience/tool-call-history-and-result:
87
+ en: 9717b983b6a857e3
88
+ zh: 611a2dadf7c0c706
89
+ /x1a0f3n9-dsh-tool-workflow/model-experience/tool-call-history-and-result/what-the-model-sees:
90
+ en: acd5e87161ced588
91
+ zh: 5ffe85926ee1b88d
92
+ /x1a0f3n9-dsh-tool-workflow/model-experience/tool-call-history-and-result/token-effect:
93
+ en: 88cfc89c7dc19dbb
94
+ zh: 75e674844f311937
95
+ /x1a0f3n9-dsh-tool-workflow/model-experience/tool-call-history-and-result/kv-cache-effect:
96
+ en: f5d5f97a153f7c70
97
+ zh: 3d6bc05b9eadfbc2
98
+ /x1a0f3n9-dsh-tool-workflow/known-limitations-and-deferred-work:
99
+ en: 28e362db0d4d929f
100
+ zh: d2b1a4da09f55a42
101
+ /x1a0f3n9-dsh-tool-workflow/known-limitations-and-deferred-work/dev-note:
102
+ en: 9e9abf9ef01e719a
103
+ zh: df286968b39ec955
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-tool-workflow` lets a model run a JavaScript orchestration script that delegates work to many subagents and returns the script's final JSON value. Use it only when the user explicitly requests a workflow or large multi-agent orchestration; use plain subagent calls for one or two delegations. The parent turn waits until every delegated task settles, and cancellation or abnormal completion returns an error rather than partial success. Deployments can rename the tool and cap rendered result text through `toolName` and `maxResultChars`.
12
+ `dsh-tool-workflow` lets a model run JavaScript orchestration that delegates to many subagents and returns a final JSON value. Use it only when the user explicitly requests a workflow or large multi-agent orchestration; prefer plain subagent calls for one or two delegations. Foreground execution waits for all work; cancellation or abnormal completion returns an error rather than partial success. `run_in_background: true` returns an owned job id immediately and exposes live output. Deployments can rename the tool with `toolName` and cap rendered results with `maxResultChars`.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -29,20 +29,25 @@ The `workflow` tool runs a model-authored orchestration script that fans work ou
29
29
 
30
30
  ### Calling the tool
31
31
 
32
- The model submits three parameters: `meta` (required identity data: `name`, `description`, and optional `whenToUse` and `phases`), `script` (required plain JavaScript body — no `export const meta` statement; the tool description carries the complete authoring contract), and `args` (optional JSON object exposed to the script as the `args` global; wrap a bare list in a field so the wire schema stays honest).
32
+ The model submits three parameters plus one flag: `meta` (required identity data: `name`, `description`, and optional `whenToUse` and `phases`), `script` (required plain JavaScript body — no `export const meta` statement; its parameter description carries the body rules and the tool description carries the hook contract), `args` (optional JSON object exposed to the script as the `args` global; wrap a bare list in a field so the wire schema stays honest), and `run_in_background` (optional; present only while `enableRunInBackground` holds).
33
33
 
34
- Success returns the canonical envelope `{ runId, agentsStarted, result }`, rendered to the model as `workflow "<name>" completed (<count> agent<optional-s>).` followed by `Return value:` and the pretty-printed JSON. A workflow that cannot start — a script parse or meta validation failure — returns an error the model can correct from. Cancellation and execution failures return `Error: workflow run was cancelled` or `Error: workflow run failed: <error>`; partial output is never reported as success.
34
+ A foreground success returns the envelope `{ kind: 'foreground', runId, agentsStarted, result }`, rendered to the model as `workflow "<name>" completed (<count> agent<optional-s>).` followed by `Return value:` and the pretty-printed JSON. A workflow that cannot start — a script parse or meta validation failure — returns an error the model can correct from. Cancellation and execution failures return `Error: workflow run was cancelled` or `Error: workflow run failed: <error>`; partial output is never reported as success.
35
35
 
36
36
  ### What to expect during a run
37
37
 
38
38
  While the script runs, the parent turn waits: the tool starts the run, awaits its result, and always disposes it, so the script and its children reach quiescence on every path — including cancellation, which is bridged from the parent step's abort signal. The model sees one final outcome, never intermediate child messages; the children's own work stays out of the parent conversation.
39
39
 
40
+ ### Background runs
41
+
42
+ `run_in_background: true` returns `{ kind: 'background', jobId, runId }` immediately: the run is registered on `ctx.jobs` as an owned `workflow` job, so the session-header job list streams its `phase()`, `log()`, and member lifecycle lines live from the job's output ring, and the row's progress line tracks the current phase. No tool-step signal reaches the run — `job_kill`, the list's stop control, and owner teardown are what cancel it. Settlement is the job's settlement: a completed run carries the same rendered return value as the job's result (the completion notice announces it, the model's first `job_output` after settlement carries it once), a cancelled run settles `killed` with the kill reason, and a failed run settles `failed` with the script's failure message. Without a live job registry and a controller serving the caller the call fails, naming the missing composition pieces.
43
+
40
44
  ### Config
41
45
 
42
46
  | Field | Default | Meaning |
43
47
  |---|---|---|
44
48
  | `toolName` | `workflow` | The model-facing tool name to register. |
45
49
  | `maxResultChars` | `50000` | Rendered-result ceiling; longer JSON is truncated with a notice. |
50
+ | `enableRunInBackground` | `true` | Expose `run_in_background`; disabled calls are also rejected. |
46
51
 
47
52
  The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tool-workflow) is the exhaustive source for every accepted field.
48
53
 
@@ -58,16 +63,22 @@ This section explains how the consumer is split from the engine and how the run
58
63
 
59
64
  ### Design concept
60
65
 
61
- The consumer owns the model-facing schema, the `tool:<toolName>` system-prompt guidance, and the result envelope; script parsing, execution, caps, and cancellation live behind `ctx.workflowEngine`, so a hardened engine swaps in without changing what the model sees. Usage guidance ships with the tool plugin as a prompt section, never in the deployment persona.
66
+ The consumer owns the model-facing schema, the `tool:<toolName>` system-prompt guidance, and the result envelope; script parsing, execution, caps, and cancellation live behind `ctx.workflowEngine`, while the PTC engine shares Node process confinement with `run_code`. Usage guidance ships with the tool plugin as a prompt section, never in the deployment persona.
62
67
 
63
68
  ### Run lifecycle
64
69
 
65
- `execute` starts the run and awaits `run.result` inside a `try/finally` that always disposes the run. `exec.signal` is bridged to `run.cancel()`, including the already-aborted-before-start case. A non-`completed` stop reason maps to an `isError` result reporting the reason; completion renders `{ runId, agentsStarted, result }`, with the Native renderer truncating only that projection at `maxResultChars`.
70
+ `execute` starts the run and awaits `run.result` inside a `try/finally` that always disposes the run. `exec.signal` is bridged to `run.cancel()`, including the already-aborted-before-start case. A non-`completed` stop reason maps to an `isError` result reporting the reason; completion renders `{ kind, runId, agentsStarted, result }`, with the Native renderer truncating only that projection at `maxResultChars`.
71
+
72
+ ### Background lifecycle
73
+
74
+ A background call registers the run through `jobs.start` inside the job starter, so a synchronous engine rejection registers nothing and admission preflight runs before the engine spawns. The job's `done` chains from `run.result`: dispose (a disposal failure is warned, never rejected into the registry), stop the mirrors, then map the stop reason onto the job outcome. The ring mirror (`src/record.ts`) subscribes `workflow/phase`, `workflow/log`, and member events once per plugin and routes them into the tracked runs' `JobHandle` faces (`append` for lines, `updateProgress` for the phase); a straggling event after settlement finds no tracked run, and an append against a settled job drops inside the registry.
66
75
 
67
76
  ### Durable session records
68
77
 
69
78
  For a root transport execution (`exec.parent` absent), the tool projects the run into the calling Agent's Session with four log-only events: run-start after `start()` returns, member starts and endings filtered by `run.id`, then run-end only after the result is available and disposal reaches quiescence. Nested transport calls execute normally but write no record. The first failed Session append disables later recording for that run with one warning, leaving either no record or a legal continuous prefix without changing the tool result or cleanup. The package invariant rejects duplicate starts, unpaired members, terminal events with open members, and updates after run-end on both cold load and live append, while accepting missing terminal suffixes.
70
79
 
80
+ The engine's `workflow/phase` and `workflow/log` events have no per-line durable surface from this tool: the session log deliberately records run and member lifecycle only, and the Web transcript derives from those records. A background run's lines reach a human through the job observation record instead, which is transient by design.
81
+
71
82
  ### Render intent
72
83
 
73
84
  Decided up front per the [render-intent Agent Note](../../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md): a `generic` card titled `workflow: <meta.name>`, read directly from `args.meta.name` — presentation is a pure function of args — with the script text carried as `rawInput`. The result keeps the generic card.
@@ -76,7 +87,8 @@ Decided up front per the [render-intent Agent Note](../../../.agents/notes/imple
76
87
 
77
88
  | File | Role |
78
89
  |---|---|
79
- | [`src/index.ts`](src/index.ts) | Plugin entry: tool registration, run lifecycle, recorder wiring |
90
+ | [`src/index.ts`](src/index.ts) | Plugin entry: tool registration, run lifecycle, background job registration, recorder wiring |
91
+ | [`src/record.ts`](src/record.ts) | Background runs' live-progress mirror into the job's output ring |
80
92
  | [`src/types.ts`](src/types.ts) | The four log-only record event payloads and their `SessionEventMap` declaration |
81
93
  | [`src/invariant.ts`](src/invariant.ts) | Invariant companion: durable workflow-record protocol validation |
82
94
 
@@ -91,7 +103,7 @@ Read these pages when the tool-level contract is not enough. They move from the
91
103
 
92
104
  - [Workflow subsystem](../../../docs/subsystems/workflow.md) — the seam contract, start request, and event payloads.
93
105
  - [Workflow seam](../workflow/README.md) — the run and result vocabulary behind the tool.
94
- - [Worker-thread engine](../workflow-worker-thread/README.md) — the engine that executes the scripts.
106
+ - [PTC workflow engine](../workflow-ptc/README.md) — the engine that executes the scripts.
95
107
  - [subagent tool](../../subagent/tool-subagent/README.md) — the plain-delegation alternative for one or two children.
96
108
  - [Group map](../README.md) — the workflow capability family and its packages.
97
109
  - [Dynamic workflows Agent Note](../../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md) — the seam design and its decisions.
@@ -139,7 +151,7 @@ Prefix-stable while `toolName`, definition, and visibility are unchanged. Renami
139
151
 
140
152
  #### What the model sees
141
153
 
142
- The full model-written script, metadata, and args remain in the assistant tool call. Success is exactly `workflow "<name>" completed (<count> agent<optional-s>).`, newline, `Return value:`, newline, and pretty-printed data-dependent JSON; a cap adds `… [truncated: <omitted> more characters]` on a new line. Failures are exactly `Error: workflow run was cancelled`, optionally suffixed ` (<error>)`, `Error: workflow run failed: <error-or-unknown error>`, or defensively `Error: workflow run ended abnormally (<reason>)`; a call without an owning agent becomes `Error: workflow tool requires a calling agent (exec.agent was undefined)`. Intermediate child messages are omitted.
154
+ The full model-written script, metadata, and args remain in the assistant tool call. A foreground success is exactly `workflow "<name>" completed (<count> agent<optional-s>).`, newline, `Return value:`, newline, and pretty-printed data-dependent JSON; a cap adds `… [truncated: <omitted> more characters]` on a new line. A background acceptance is exactly `workflow "<name>" started in the background as job <jobId>. Its return value arrives with the completion notice; check on it with job_output, stop it with job_kill.`, and the same rendered value later reaches the model through the job's completion notice and `job_output`. Failures are exactly `Error: workflow run was cancelled`, optionally suffixed ` (<error>)`, `Error: workflow run failed: <error-or-unknown error>`, or defensively `Error: workflow run ended abnormally (<reason>)`; a call without an owning agent becomes `Error: workflow tool requires a calling agent (exec.agent was undefined)`. Intermediate child messages are omitted.
143
155
 
144
156
  #### Token effect
145
157
 
@@ -156,10 +168,11 @@ Append-only; newly visible content follows the reusable request prefix and does
156
168
 
157
169
  These limits define what the tool does not yet support. They are current constraints, not a task backlog.
158
170
 
159
- - **The parent turn blocks until the whole workflow settles** — there is no background start/poll API, and cancellation discards partial output as an error.
171
+ - **A background run reports no intermediate value to the model** — `job_output` before settlement returns status only; the return value arrives whole at completion, and cancellation still discards partial output.
160
172
  - **`args` must be an object and Native result text is bounded** — callers wrap top-level arrays and scalars in a field; the canonical workflow result stays complete, while JSON beyond `maxResultChars` is truncated in the model-facing projection rather than stored behind a retrieval handle.
161
173
  - **Workflow policy is fixed per tool registration** — provider selection, caps, and tool name are deployment config, not model-call arguments.
162
174
  - **Durable records are top-level and observational** — nested PTC mode dispatches are not recorded, and a recording failure intentionally degrades to an incomplete prefix rather than changing execution.
175
+ - **No recorded-session scenario replays a background run yet** — unit and real-engine composition suites cover the path; the snapshot tree pins only the schema and prompt text.
163
176
 
164
177
  <a id="dev-note"></a>
165
178
  ### Dev Note
@@ -169,6 +182,6 @@ These limits define what the tool does not yet support. They are current constra
169
182
 
170
183
  This Dev Note is working context for maintainers: open directions that are not decided. It is explicitly non-authoritative — shipped behavior, limits, and accepted rationale live in the sections above, the package code, and the linked Agent Notes.
171
184
 
172
- Open directions: a background start/poll route so the parent turn does not block; storing truncated JSON behind a retrieval handle instead of clipping the projection; recording nested dispatches beyond the top level.
185
+ Open directions: storing truncated JSON behind a retrieval handle instead of clipping the projection; recording nested dispatches beyond the top level; a recorded-session scenario for the background path.
173
186
 
174
187
  </details>
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-tool-workflow` 让模型运行 JavaScript 编排脚本,把工作委派给多个 subagent,并返回脚本的最终 JSON 值。仅当用户明确要求工作流或大型多 agent(智能体)编排时使用;一两项委派应使用普通 subagent 调用。父级轮次会等待所有委派任务结束;取消或异常完成会返回错误,而不是部分成功。部署方可以通过 `toolName` 重命名工具,并通过 `maxResultChars` 限制渲染结果文本。
12
+ `dsh-tool-workflow` 让模型运行 JavaScript 编排,将工作委派给多个 subagent,并返回最终 JSON 值。仅当用户明确要求工作流或大型多 agent(智能体)编排时使用;一两项委派应优先使用普通 subagent 调用。前台执行等待所有工作结束;取消或异常完成返回错误,而不是部分成功。`run_in_background: true` 立即返回自有任务 id,并提供实时输出。部署方可以用 `toolName` 重命名工具,用 `maxResultChars` 限制渲染结果。
13
13
 
14
14
  ## 目录
15
15
 
@@ -29,20 +29,25 @@ kind: "package-reference"
29
29
 
30
30
  ### 调用工具
31
31
 
32
- 模型提交三个参数:`meta`(必需的身份数据:`name`、`description`,以及可选的 `whenToUse` 与 `phases`)、`script`(必需的纯 JavaScript 脚本体——不含 `export const meta` 语句;工具描述携带完整的编写约定)与 `args`(可选 JSON 对象,作为全局变量 `args` 向脚本公开;裸列表应包装到字段中,使协议 schema 如实表达形态)。
32
+ 模型提交三个参数外加一个开关:`meta`(必需的身份数据:`name`、`description`,以及可选的 `whenToUse` 与 `phases`)、`script`(必需的纯 JavaScript 脚本体——不含 `export const meta` 语句;其参数描述携带脚本体规则,工具描述携带钩子约定)、`args`(可选 JSON 对象,作为全局变量 `args` 向脚本公开;裸列表应包装到字段中,使协议 schema 如实表达形态),以及 `run_in_background`(可选;仅在 `enableRunInBackground` 生效时存在)。
33
33
 
34
- 成功返回规范包络 `{ runId, agentsStarted, result }`,向模型渲染为 `workflow "<name>" completed (<count> agent<optional-s>).`,后接 `Return value:` 与美化打印的 JSON。无法启动的工作流——脚本解析或 meta 校验失败——返回模型可以修正的错误。取消与执行失败返回 `Error: workflow run was cancelled` 或 `Error: workflow run failed: <error>`;部分输出绝不会被报告为成功。
34
+ 前台成功返回包络 `{ kind: 'foreground', runId, agentsStarted, result }`,向模型渲染为 `workflow "<name>" completed (<count> agent<optional-s>).`,后接 `Return value:` 与美化打印的 JSON。无法启动的工作流——脚本解析或 meta 校验失败——返回模型可以修正的错误。取消与执行失败返回 `Error: workflow run was cancelled` 或 `Error: workflow run failed: <error>`;部分输出绝不会被报告为成功。
35
35
 
36
36
  ### 运行期间的预期
37
37
 
38
38
  脚本运行期间,父级轮次会等待:工具启动运行、等待其结果,并始终对该运行执行 dispose(资源释放),因此脚本及其子 agent 在每条路径上完全停稳——包括从父级步骤中止信号桥接而来的取消。模型只看到最终结果,永远不会看到中间子 agent 消息;子 agent 自己的工作不会进入父级对话。
39
39
 
40
+ ### 后台运行
41
+
42
+ `run_in_background: true` 会立刻返回 `{ kind: 'background', jobId, runId }`:运行以自有 `workflow` 任务身份注册到 `ctx.jobs`,会话头部任务列表因此从该任务的输出环实时流式显示它的 `phase()`、`log()` 与成员生命周期行,行的进度行跟随当前阶段。没有任何工具步骤信号到达该运行——`job_kill`、列表里的停止控件与 owner 拆除才是取消它的途径。结算即任务的结算:完成的运行把同一份渲染后的返回值作为任务的 result 交出(完成通知宣布它,模型结算后的第一次 `job_output` 携带它一次),被取消的运行以 kill 原因结算为 `killed`,失败的运行以脚本的失败信息结算为 `failed`。没有活体任务注册表与服务于调用方的控制器时调用失败,并点名缺失的组合部件。
43
+
40
44
  ### 配置
41
45
 
42
46
  | 字段 | 默认值 | 含义 |
43
47
  |---|---|---|
44
48
  | `toolName` | `workflow` | 要注册的面向模型工具名称。 |
45
49
  | `maxResultChars` | `50000` | 渲染结果上限;更长的 JSON 会被截断并附上提示。 |
50
+ | `enableRunInBackground` | `true` | 公开 `run_in_background`;关闭后调用同样会被拒绝。 |
46
51
 
47
52
  生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-tool-workflow)是每个受支持字段的穷尽式真源。
48
53
 
@@ -58,16 +63,22 @@ kind: "package-reference"
58
63
 
59
64
  ### 设计理念
60
65
 
61
- 消费方拥有模型侧 schema、`tool:<toolName>` 系统提示词指导与结果包络;脚本解析、执行、上限与取消位于 `ctx.workflowEngine` 之后,因此更坚固的引擎可以无缝替换,而不改变模型看到的内容。使用指导以提示词段的形式随工具插件交付,绝不放入部署 persona。
66
+ 消费方拥有模型侧 schema、`tool:<toolName>` 系统提示词指导与结果包络;脚本解析、执行、上限与取消位于 `ctx.workflowEngine` 之后,PTC 引擎与 `run_code` 共享 Node 进程约束。使用指导以提示词段的形式随工具插件交付,绝不放入部署 persona。
62
67
 
63
68
  ### 运行生命周期
64
69
 
65
- `execute` 启动运行,并在 `try/finally` 内等待 `run.result`;该结构总会对运行执行 dispose。`exec.signal` 会桥接到 `run.cancel()`,包括启动前已经中止的情况。非 `completed` 结束原因会映射为报告原因的 `isError` 结果;完成时渲染 `{ runId, agentsStarted, result }`,Native 渲染器只会在 `maxResultChars` 处截断该投影。
70
+ `execute` 启动运行,并在 `try/finally` 内等待 `run.result`;该结构总会对运行执行 dispose。`exec.signal` 会桥接到 `run.cancel()`,包括启动前已经中止的情况。非 `completed` 结束原因会映射为报告原因的 `isError` 结果;完成时渲染 `{ kind, runId, agentsStarted, result }`,Native 渲染器只会在 `maxResultChars` 处截断该投影。
71
+
72
+ ### 后台生命周期
73
+
74
+ 后台调用在任务 starter 内经 `jobs.start` 注册运行,因此引擎的同步拒绝什么都不会注册,准入预检也先于引擎生成执行。任务的 `done` 链接自 `run.result`:先 dispose(释放失败只告警,绝不 reject 进注册表),再停止镜像,然后把停止原因映射到任务结果。环镜像(`src/record.ts`)按插件订阅一次 `workflow/phase`、`workflow/log` 与成员事件,并把它们路由进被跟踪运行的 `JobHandle` 面(`append` 写行,`updateProgress` 写阶段);结算后的零星事件找不到被跟踪的运行,对已结算任务的 append 则在注册表内丢弃。
66
75
 
67
76
  ### 持久会话记录
68
77
 
69
78
  对于根 transport 执行(`exec.parent` 缺省),工具会用四个 log-only 事件把运行投影到调用方 agent 的会话:`start()` 返回后写 run-start,只记录 `run.id` 匹配的成员开始与结束,并且只在结果可用且 dispose 完全停稳后写 run-end。嵌套 transport 调用照常执行,但不写任何记录。会话追加操作首次失败后,本运行会停止后续记录并只告警一次,留下空记录或合法连续前缀,同时不改变工具结果和清理。包 invariant 会在冷加载与实时追加时拒绝重复 start、未配对成员、仍有开放成员的终点与 run-end 后更新,同时允许缺失终态后缀的连续前缀。
70
79
 
80
+ 引擎的 `workflow/phase` 与 `workflow/log` 事件在本工具没有逐行的持久面:会话日志刻意只记录 run 与成员生命周期,Web transcript 由这些记录派生。后台运行的这些行改经任务观察 record 抵达人类,而 record 的瞬态是设计使然。
81
+
71
82
  ### 渲染意图
72
83
 
73
84
  按[渲染意图 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.zh.md)预先确定:使用 `generic` 卡片,标题为 `workflow: <meta.name>`,直接从 `args.meta.name` 读取——呈现是参数的纯函数——脚本文本作为 `rawInput` 携带。结果继续使用 generic 卡片。
@@ -76,7 +87,8 @@ kind: "package-reference"
76
87
 
77
88
  | 文件 | 职责 |
78
89
  |---|---|
79
- | [`src/index.ts`](src/index.ts) | 插件入口:工具注册、运行生命周期、记录器接线 |
90
+ | [`src/index.ts`](src/index.ts) | 插件入口:工具注册、运行生命周期、后台任务注册、记录器接线 |
91
+ | [`src/record.ts`](src/record.ts) | 后台运行进任务输出环的实时进度镜像 |
80
92
  | [`src/types.ts`](src/types.ts) | 四个 log-only 记录事件 payload 及其 `SessionEventMap` 声明 |
81
93
  | [`src/invariant.ts`](src/invariant.ts) | 不变式配套入口:持久工作流记录协议校验 |
82
94
 
@@ -91,7 +103,7 @@ kind: "package-reference"
91
103
 
92
104
  - [工作流子系统](../../../docs/subsystems/workflow.zh.md)——seam 约定、启动请求与事件载荷。
93
105
  - [工作流 seam](../workflow/README.zh.md)——工具背后的运行与结果词汇。
94
- - [worker-thread 引擎](../workflow-worker-thread/README.zh.md)——执行脚本的引擎。
106
+ - [PTC 工作流引擎](../workflow-ptc/README.zh.md)——执行脚本的引擎。
95
107
  - [subagent 工具](../../subagent/tool-subagent/README.zh.md)——一两项委派时的普通委派替代方案。
96
108
  - [组地图](../README.zh.md)——工作流能力家族及其包。
97
109
  - [动态工作流 Agent Note](../../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.zh.md)——seam 设计及其决策。
@@ -139,7 +151,7 @@ Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for
139
151
 
140
152
  #### 模型看到什么
141
153
 
142
- 由模型编写的完整脚本、元数据与 args 会保留在 assistant 工具调用中。成功结果精确为 `workflow "<name>" completed (<count> agent<optional-s>).`、换行、`Return value:`、换行,以及美化打印且依赖数据的 JSON;达到上限时,会在新行添加 `… [truncated: <omitted> more characters]`。失败结果精确为 `Error: workflow run was cancelled`(可以追加后缀 ` (<error>)`)、`Error: workflow run failed: <error-or-unknown error>` 或防御性的 `Error: workflow run ended abnormally (<reason>)`;没有所属 agent 的调用变为 `Error: workflow tool requires a calling agent (exec.agent was undefined)`。中间子 agent 消息会被省略。
154
+ 由模型编写的完整脚本、元数据与 args 会保留在 assistant 工具调用中。前台成功结果精确为 `workflow "<name>" completed (<count> agent<optional-s>).`、换行、`Return value:`、换行,以及美化打印且依赖数据的 JSON;达到上限时,会在新行添加 `… [truncated: <omitted> more characters]`。后台受理结果精确为 `workflow "<name>" started in the background as job <jobId>. Its return value arrives with the completion notice; check on it with job_output, stop it with job_kill.`,同样渲染的值稍后经任务完成播报与 `job_output` 抵达模型。失败结果精确为 `Error: workflow run was cancelled`(可以追加后缀 ` (<error>)`)、`Error: workflow run failed: <error-or-unknown error>` 或防御性的 `Error: workflow run ended abnormally (<reason>)`;没有所属 agent 的调用变为 `Error: workflow tool requires a calling agent (exec.agent was undefined)`。中间子 agent 消息会被省略。
143
155
 
144
156
  #### Token 影响
145
157
 
@@ -156,10 +168,11 @@ Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for
156
168
 
157
169
  这些限制说明该工具尚未支持什么。它们是当前约束,不是任务积压。
158
170
 
159
- - **父级轮次会阻塞到整个工作流结算**——没有后台启动/轮询接口,取消会丢弃局部输出并返回错误。
171
+ - **后台运行不向模型报告中间值**——结算前的 `job_output` 只返回状态;返回值在完成时整体送达,取消仍会丢弃局部输出。
160
172
  - **`args` 必须是对象,Native 结果文本有界**——调用方把顶层数组/标量包装到字段中;规范工作流结果保持完整,超过 `maxResultChars` 的 JSON 会在面向模型的投影中截断,而不是存储在检索句柄背后。
161
173
  - **每次工具注册的工作流策略固定**——提供方选择、上限与工具名称属于部署配置,不是模型调用参数。
162
174
  - **持久记录只覆盖顶层且只供观察**——嵌套 PTC mode dispatch 不记录;记录故障会刻意退化为不完整前缀,而不改变执行。
175
+ - **尚无回放后台运行的 recorded-session 场景**——单元与真实引擎组合套件覆盖该路径;快照树只钉住 schema 与提示词文本。
163
176
 
164
177
  <a id="dev-note"></a>
165
178
  ### 开发备注
@@ -169,6 +182,6 @@ Use the <toolName> tool ONLY when the user explicitly asks for a workflow or for
169
182
 
170
183
  本开发备注是维护者的工作上下文:尚未决定的开放方向。它明确不具权威性——已交付的行为、限制与既定理由以上文、包代码与相关 Agent Note 为准。
171
184
 
172
- 开放方向:让父级轮次不再阻塞的后台启动/轮询路径;把截断的 JSON 存储在检索句柄背后,而不是剪裁投影;记录超出顶层的嵌套 dispatch。
185
+ 开放方向:把截断的 JSON 存储在检索句柄背后,而不是剪裁投影;记录超出顶层的嵌套 dispatch;为后台路径补一个 recorded-session 场景。
173
186
 
174
187
  </details>
package/lib/index.js CHANGED
@@ -1,13 +1,58 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
2
  import { defineTool } from "@x1a0f3n9/dsh-tools";
3
+ //#region lib/types/record.js
4
+ /**
5
+ * Live-progress mirror for background workflow runs: streams the engine's
6
+ * `workflow/phase`, `workflow/log`, and member lifecycle events into the
7
+ * owning job's output ring as `log` chunks — observer-only narration the
8
+ * model's `job_output` never renders — and keeps the job's live progress
9
+ * line on the current phase. Appends against a settled job log and drop
10
+ * inside the registry, so a straggling event after settlement is harmless.
11
+ * @module @x1a0f3n9/dsh-tool-workflow/record
12
+ */
13
+ /**
14
+ * Create the run-to-ring mirror and subscribe the engine's live progress
15
+ * events for the runs it tracks.
16
+ * @param ctx - plugin context whose event bus carries the `workflow/*` events.
17
+ * @returns the mirror taps the tool wires around each background run.
18
+ */
19
+ function createWorkflowRecordMirror(ctx) {
20
+ const active = /* @__PURE__ */ new Map();
21
+ ctx.on("workflow/phase", (info, title) => {
22
+ const job = active.get(info.id);
23
+ if (job === void 0) return;
24
+ job.updateProgress(title);
25
+ job.append(`▸ ${title}\n`, { channel: "log" });
26
+ });
27
+ ctx.on("workflow/log", (info, message) => {
28
+ active.get(info.id)?.append(`${message}\n`, { channel: "log" });
29
+ });
30
+ ctx.on("workflow/agent-start", (info, agent) => {
31
+ active.get(info.id)?.append(`agent #${agent.seq} ${agent.label} started\n`, { channel: "log" });
32
+ });
33
+ ctx.on("workflow/agent-end", (info, agent) => {
34
+ active.get(info.id)?.append(`agent #${agent.seq} ${agent.outcome}\n`, { channel: "log" });
35
+ });
36
+ return {
37
+ start(runId, job) {
38
+ active.set(runId, job);
39
+ },
40
+ stop(runId) {
41
+ active.delete(runId);
42
+ }
43
+ };
44
+ }
45
+ //#endregion
3
46
  //#region lib/types/index.js
4
47
  /**
5
48
  * The model-facing `workflow` tool: run a JavaScript orchestration script that fans out
6
49
  * subagents, and return the script's final value. It owns the model-facing schema and run lifecycle; script
7
50
  * parsing, execution, caps, and cancellation live behind `ctx.workflowEngine`
8
51
  * (`@x1a0f3n9/dsh-workflow`), so a hardened engine swaps in without touching what the model
9
- * sees. Execution awaits `run.result` and always disposes the run; non-completed reasons become tool
10
- * errors, and background collection remains deferred. Presentation is an args-only generic card
52
+ * sees. Foreground execution awaits `run.result` and always disposes the run; non-completed reasons
53
+ * become tool errors. `run_in_background: true` instead registers the run as an owned `ctx.jobs` job
54
+ * and returns its id immediately — the job's output ring streams live progress, and the run's value
55
+ * arrives with the job's completion notice. Presentation is an args-only generic card
11
56
  * titled from `meta.name`. Explicit-ask usage guidance is registered as the tool's own prompt
12
57
  * section rather than deployment persona prose.
13
58
  * @module @x1a0f3n9/dsh-tool-workflow
@@ -20,7 +65,8 @@ const inject = [
20
65
  ];
21
66
  const Config = z.object({
22
67
  toolName: z.string().default("workflow"),
23
- maxResultChars: z.natural().min(1).default(5e4)
68
+ maxResultChars: z.natural().min(1).default(5e4),
69
+ enableRunInBackground: z.boolean().default(true)
24
70
  });
25
71
  /** Render a contained recording failure without trusting the thrown value. */
26
72
  function renderRecordingError(error) {
@@ -87,23 +133,19 @@ function createWorkflowRecorder(ctx) {
87
133
  };
88
134
  }
89
135
  /**
90
- * The script-authoring contract, embedded in the tool description. This IS the
91
- * model-facing spec: the meta block, the hooks and their exact semantics, and
92
- * the supported schema subset.
136
+ * The script-authoring contract, embedded in the tool description: the hooks,
137
+ * their exact semantics, and the supported schema subset. Parameter-level
138
+ * rules live in the parameter descriptions.
93
139
  */
94
140
  const DESCRIPTION = `Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.
95
141
 
96
- The workflow's identity rides the \`meta\` parameter as JSON: required \`name\` (short kebab-case) and \`description\` strings, optional \`whenToUse\` string and \`phases\` array (\`{title, detail?, provider?, model?}\`). The \`script\` parameter is the plain JavaScript body ONLY (NOT TypeScript, and NO \`export const meta\` statement — meta is a parameter, not code), running with top-level await; end with \`return <value>\` — the value must be JSON-serializable and is this tool's result.
97
-
98
142
  Script-body hooks:
99
- - \`agent(prompt, opts?): Promise<any>\` — run one subagent to completion. Without \`opts.schema\` it resolves to the child's final text; with \`opts.schema\` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf — no pattern/format/numeric bounds) it resolves to the validated object. Resolves \`null\` when the child fails (filter with \`.filter(Boolean)\`). Other opts: \`label\` (display), \`phase\` (progress group), and independent \`provider\`/\`model\` LLM target overrides (either may be provided alone). Anything else (\`effort\`/\`isolation\`/\`agentType\`) is rejected loudly.
100
- - \`pipeline(items, ...stages): Promise<any[]>\` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives \`(prev, item, index)\`. An ordinary stage throw drops that ITEM to \`null\` and skips its remaining stages.
143
+ - \`agent(prompt, opts?): Promise<any>\` — run one subagent to completion. Without \`opts.schema\` it resolves to the child's final text; with \`opts.schema\` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf) it resolves to the validated object. Resolves \`null\` when the child fails (filter with \`.filter(Boolean)\`). Other opts: \`label\` (display), \`phase\` (progress group), and independent \`provider\`/\`model\` LLM target overrides.
144
+ - \`pipeline(items, ...stages): Promise<any[]>\` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives \`(prev, item, index)\`. A stage throw drops that ITEM to \`null\` and skips its remaining stages.
101
145
  - \`parallel(thunks): Promise<any[]>\` — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to \`null\`.
102
146
  - \`phase(title)\` — start a progress phase; \`log(message)\` — narrate progress; \`args\` — the tool call's \`args\` input, verbatim.
103
147
 
104
- Misused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) throw errors that ALWAYS kill the script — they never dissolve into a per-item \`null\`.
105
-
106
- Constraints: concurrency and total-agent caps apply; no filesystem, network, timers, or Node.js APIs are provided — the agents do the work, the script only coordinates them. The run executes in the foreground: this call returns when the whole script finishes.`;
148
+ Misused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) end the whole script instead of producing \`null\`. The script has no filesystem, network, timer, or Node.js APIs; the agents do the work.`;
107
149
  /** The pending-state card: a generic card titled by the workflow's meta name. */
108
150
  function presentWorkflowCall(args) {
109
151
  return {
@@ -126,15 +168,98 @@ function stopReasonError(result) {
126
168
  default: return `workflow run ended abnormally (${String(result.stopReason)})`;
127
169
  }
128
170
  }
171
+ /**
172
+ * Map a settled background run onto the job outcome vocabulary. A completed
173
+ * run carries the rendered return value as the job's result; a
174
+ * cancelled run leaves the detail to the registry's kill-reason merge (the
175
+ * cancel reason it forwarded is the same string); an errored run fails with
176
+ * the script's failure message.
177
+ */
178
+ function jobOutcomeOf(result, name, maxChars) {
179
+ switch (result.stopReason) {
180
+ case "completed": return {
181
+ status: "completed",
182
+ detail: `${result.agentsStarted} agent${result.agentsStarted === 1 ? "" : "s"}`,
183
+ result: renderResult(name, result.agentsStarted, result.value, maxChars)
184
+ };
185
+ case "cancelled": return { status: "killed" };
186
+ case "error": return {
187
+ status: "failed",
188
+ detail: result.error ?? "unknown error"
189
+ };
190
+ /* v8 ignore start -- defensive: WorkflowStopReason is a closed union, exhaustive by construction; a future variant fails here loudly */
191
+ default: return {
192
+ status: "failed",
193
+ detail: `workflow run ended abnormally (${String(result.stopReason)})`
194
+ };
195
+ }
196
+ }
129
197
  /** Render the run's outcome text: the meta name, agent count, and the JSON value (capped). */
130
198
  function renderResult(name, agentsStarted, value, maxChars) {
131
199
  const rendered = JSON.stringify(value, null, 2);
132
200
  const clipped = rendered.length > maxChars ? `${rendered.slice(0, maxChars)}\n… [truncated: ${rendered.length - maxChars} more characters]` : rendered;
133
201
  return `workflow "${name}" completed (${agentsStarted} agent${agentsStarted === 1 ? "" : "s"}).\nReturn value:\n${clipped}`;
134
202
  }
203
+ /**
204
+ * Register a background run as an owned job. The engine run is started
205
+ * inside the job starter with no tool-step signal — the run belongs to the
206
+ * job, so a registry kill or owner teardown is what cancels it — and its
207
+ * settlement is the job's settlement: dispose, stop the mirrors, then map the
208
+ * stop reason onto the job outcome (a completed run's rendered return value
209
+ * rides `result` to the model's first read after settlement).
210
+ * @param ctx - plugin context (engine, optional jobs registry, logger).
211
+ * @param args - the validated tool call.
212
+ * @param parent - the calling agent; owns the job.
213
+ * @param recordsRun - whether this top-level call records durable run events.
214
+ * @param deps - the tool's recorder/mirror taps and the render cap.
215
+ * @returns the background result for the tool's output schema.
216
+ */
217
+ function startBackgroundRun(ctx, args, parent, recordsRun, deps) {
218
+ const jobs = ctx.get("jobs");
219
+ if (jobs === void 0) throw new Error("background jobs unavailable: load @x1a0f3n9/dsh-jobs and @x1a0f3n9/dsh-tool-jobs");
220
+ let run;
221
+ return {
222
+ kind: "background",
223
+ jobId: jobs.start({
224
+ kind: "workflow",
225
+ label: args.meta.name,
226
+ owner: parent.id,
227
+ run: (job) => {
228
+ run = ctx.workflowEngine.start({
229
+ script: args.script,
230
+ meta: args.meta,
231
+ ...args.args !== void 0 ? { args: args.args } : {},
232
+ parent
233
+ });
234
+ deps.mirror.start(run.id, job);
235
+ if (recordsRun) deps.recorder.start(parent.session, run);
236
+ return {
237
+ cancel: (reason) => {
238
+ run.cancel(reason ?? "background workflow job killed");
239
+ },
240
+ done: run.result.then(async (result) => {
241
+ try {
242
+ await run.dispose();
243
+ } catch (error) {
244
+ ctx.logger.warn(`background workflow run ${run.id} dispose failed: ${String(error)}`);
245
+ }
246
+ deps.mirror.stop(run.id);
247
+ if (recordsRun) {
248
+ deps.recorder.finish(run.id, result.stopReason);
249
+ deps.recorder.abandon(run.id);
250
+ }
251
+ return jobOutcomeOf(result, args.meta.name, deps.maxResultChars);
252
+ })
253
+ };
254
+ }
255
+ }),
256
+ runId: run.id
257
+ };
258
+ }
135
259
  function apply(ctx, config) {
136
- const { toolName, maxResultChars } = config;
260
+ const { toolName, maxResultChars, enableRunInBackground } = config;
137
261
  const recorder = createWorkflowRecorder(ctx);
262
+ const mirror = createWorkflowRecordMirror(ctx);
138
263
  ctx.systemPrompt.section({
139
264
  name: `tool:${toolName}`,
140
265
  order: ctx.systemPrompt.getSectionOrder("TOOL_WORKFLOW"),
@@ -147,13 +272,13 @@ function apply(ctx, config) {
147
272
  script: {
148
273
  type: "string",
149
274
  required: true,
150
- description: "The plain-JS workflow script body (top-level await allowed; NO `export const meta` statement; end with `return <json-value>`)."
275
+ description: "The plain JavaScript body, not TypeScript and without an `export const meta` statement; top-level await is allowed. End with `return <value>`; the JSON-serializable value is this tool's result."
151
276
  },
152
277
  meta: {
153
278
  type: "object",
154
279
  additionalProperties: true,
155
280
  required: true,
156
- description: "The workflow identity block (plain JSON — never code).",
281
+ description: "The workflow identity as plain JSON, not code.",
157
282
  properties: {
158
283
  name: {
159
284
  type: "string",
@@ -202,13 +327,40 @@ function apply(ctx, config) {
202
327
  type: "object",
203
328
  additionalProperties: true,
204
329
  description: "Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {\"files\": [...]})."
205
- }
330
+ },
331
+ ...enableRunInBackground ? { run_in_background: {
332
+ type: "boolean",
333
+ description: "Run as a background job: return a job id immediately instead of waiting; the return value arrives with the completion notice."
334
+ } } : {}
206
335
  },
207
336
  output: {
208
- schema: {
337
+ schema: { oneOf: [{
209
338
  type: "object",
210
339
  additionalProperties: false,
211
340
  properties: {
341
+ kind: {
342
+ type: "string",
343
+ required: true,
344
+ const: "background"
345
+ },
346
+ jobId: {
347
+ type: "string",
348
+ required: true
349
+ },
350
+ runId: {
351
+ type: "string",
352
+ required: true
353
+ }
354
+ }
355
+ }, {
356
+ type: "object",
357
+ additionalProperties: false,
358
+ properties: {
359
+ kind: {
360
+ type: "string",
361
+ required: true,
362
+ const: "foreground"
363
+ },
212
364
  runId: {
213
365
  type: "string",
214
366
  required: true
@@ -222,15 +374,23 @@ function apply(ctx, config) {
222
374
  required: true
223
375
  }
224
376
  }
225
- },
377
+ }] },
226
378
  render: (args, value) => [{
227
379
  type: "text",
228
- text: renderResult(args.meta.name, value.agentsStarted, value.result, maxResultChars)
380
+ text: value.kind === "background" ? `workflow "${args.meta.name}" started in the background as job ${value.jobId}. Its return value arrives with the completion notice; check on it with job_output, stop it with job_kill.` : renderResult(args.meta.name, value.agentsStarted, value.result, maxResultChars)
229
381
  }]
230
382
  },
231
383
  async execute(args, exec) {
232
384
  const parent = exec.agent;
233
385
  if (!parent) throw new Error("workflow tool requires a calling agent (exec.agent was undefined)");
386
+ if (args.run_in_background === true) {
387
+ if (!enableRunInBackground) throw new Error("run_in_background is disabled for this tool");
388
+ return startBackgroundRun(ctx, args, parent, exec.parent === void 0, {
389
+ recorder,
390
+ mirror,
391
+ maxResultChars
392
+ });
393
+ }
234
394
  const run = ctx.workflowEngine.start({
235
395
  script: args.script,
236
396
  meta: args.meta,
@@ -250,6 +410,7 @@ function apply(ctx, config) {
250
410
  const error = stopReasonError(result);
251
411
  if (error !== void 0) throw new Error(error);
252
412
  return {
413
+ kind: "foreground",
253
414
  runId: run.id,
254
415
  agentsStarted: result.agentsStarted,
255
416
  result: result.value
@@ -3,14 +3,21 @@
3
3
  * subagents, and return the script's final value. It owns the model-facing schema and run lifecycle; script
4
4
  * parsing, execution, caps, and cancellation live behind `ctx.workflowEngine`
5
5
  * (`@x1a0f3n9/dsh-workflow`), so a hardened engine swaps in without touching what the model
6
- * sees. Execution awaits `run.result` and always disposes the run; non-completed reasons become tool
7
- * errors, and background collection remains deferred. Presentation is an args-only generic card
6
+ * sees. Foreground execution awaits `run.result` and always disposes the run; non-completed reasons
7
+ * become tool errors. `run_in_background: true` instead registers the run as an owned `ctx.jobs` job
8
+ * and returns its id immediately — the job's output ring streams live progress, and the run's value
9
+ * arrives with the job's completion notice. Presentation is an args-only generic card
8
10
  * titled from `meta.name`. Explicit-ask usage guidance is registered as the tool's own prompt
9
11
  * section rather than deployment persona prose.
10
12
  * @module @x1a0f3n9/dsh-tool-workflow
11
13
  */
12
14
  import type { Context } from '@deepseek-ai/cordis';
13
15
  import z from '@deepseek-ai/schemastery';
16
+ declare module '@x1a0f3n9/dsh-jobs' {
17
+ interface JobKindMap {
18
+ workflow: 'workflow';
19
+ }
20
+ }
14
21
  export declare const name = "tool-workflow";
15
22
  export declare const inject: string[];
16
23
  /** Config: the model-facing tool name plus result rendering caps. */
@@ -19,6 +26,14 @@ export interface Config {
19
26
  toolName?: string;
20
27
  /** Rendered-result ceiling, in characters: a longer JSON value is truncated with a notice (default 50000). */
21
28
  maxResultChars?: number;
29
+ /**
30
+ * Expose `run_in_background` (default true); disabled calls are also
31
+ * rejected. A background run needs a live `ctx.jobs` registry with a
32
+ * controller serving the caller (`dsh-jobs-local` plus `dsh-tool-jobs` in
33
+ * the shipped composition); without one the call fails with the missing
34
+ * piece named.
35
+ */
36
+ enableRunInBackground?: boolean;
22
37
  }
23
38
  export declare const Config: z<Config>;
24
39
  export declare function apply(ctx: Context, config: Config): void;
@@ -3,19 +3,23 @@
3
3
  * subagents, and return the script's final value. It owns the model-facing schema and run lifecycle; script
4
4
  * parsing, execution, caps, and cancellation live behind `ctx.workflowEngine`
5
5
  * (`@x1a0f3n9/dsh-workflow`), so a hardened engine swaps in without touching what the model
6
- * sees. Execution awaits `run.result` and always disposes the run; non-completed reasons become tool
7
- * errors, and background collection remains deferred. Presentation is an args-only generic card
6
+ * sees. Foreground execution awaits `run.result` and always disposes the run; non-completed reasons
7
+ * become tool errors. `run_in_background: true` instead registers the run as an owned `ctx.jobs` job
8
+ * and returns its id immediately — the job's output ring streams live progress, and the run's value
9
+ * arrives with the job's completion notice. Presentation is an args-only generic card
8
10
  * titled from `meta.name`. Explicit-ask usage guidance is registered as the tool's own prompt
9
11
  * section rather than deployment persona prose.
10
12
  * @module @x1a0f3n9/dsh-tool-workflow
11
13
  */
12
14
  import z from '@deepseek-ai/schemastery';
13
15
  import { defineTool } from '@x1a0f3n9/dsh-tools';
16
+ import { createWorkflowRecordMirror } from "./record.js";
14
17
  export const name = 'tool-workflow';
15
18
  export const inject = ['tools', 'workflowEngine', 'systemPrompt'];
16
19
  export const Config = z.object({
17
20
  toolName: z.string().default('workflow'),
18
21
  maxResultChars: z.natural().min(1).default(50_000),
22
+ enableRunInBackground: z.boolean().default(true),
19
23
  });
20
24
  /** Render a contained recording failure without trusting the thrown value. */
21
25
  function renderRecordingError(error) {
@@ -87,23 +91,19 @@ function createWorkflowRecorder(ctx) {
87
91
  };
88
92
  }
89
93
  /**
90
- * The script-authoring contract, embedded in the tool description. This IS the
91
- * model-facing spec: the meta block, the hooks and their exact semantics, and
92
- * the supported schema subset.
94
+ * The script-authoring contract, embedded in the tool description: the hooks,
95
+ * their exact semantics, and the supported schema subset. Parameter-level
96
+ * rules live in the parameter descriptions.
93
97
  */
94
98
  const DESCRIPTION = `Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.
95
99
 
96
- The workflow's identity rides the \`meta\` parameter as JSON: required \`name\` (short kebab-case) and \`description\` strings, optional \`whenToUse\` string and \`phases\` array (\`{title, detail?, provider?, model?}\`). The \`script\` parameter is the plain JavaScript body ONLY (NOT TypeScript, and NO \`export const meta\` statement — meta is a parameter, not code), running with top-level await; end with \`return <value>\` — the value must be JSON-serializable and is this tool's result.
97
-
98
100
  Script-body hooks:
99
- - \`agent(prompt, opts?): Promise<any>\` — run one subagent to completion. Without \`opts.schema\` it resolves to the child's final text; with \`opts.schema\` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf — no pattern/format/numeric bounds) it resolves to the validated object. Resolves \`null\` when the child fails (filter with \`.filter(Boolean)\`). Other opts: \`label\` (display), \`phase\` (progress group), and independent \`provider\`/\`model\` LLM target overrides (either may be provided alone). Anything else (\`effort\`/\`isolation\`/\`agentType\`) is rejected loudly.
100
- - \`pipeline(items, ...stages): Promise<any[]>\` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives \`(prev, item, index)\`. An ordinary stage throw drops that ITEM to \`null\` and skips its remaining stages.
101
+ - \`agent(prompt, opts?): Promise<any>\` — run one subagent to completion. Without \`opts.schema\` it resolves to the child's final text; with \`opts.schema\` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf) it resolves to the validated object. Resolves \`null\` when the child fails (filter with \`.filter(Boolean)\`). Other opts: \`label\` (display), \`phase\` (progress group), and independent \`provider\`/\`model\` LLM target overrides.
102
+ - \`pipeline(items, ...stages): Promise<any[]>\` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives \`(prev, item, index)\`. A stage throw drops that ITEM to \`null\` and skips its remaining stages.
101
103
  - \`parallel(thunks): Promise<any[]>\` — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to \`null\`.
102
104
  - \`phase(title)\` — start a progress phase; \`log(message)\` — narrate progress; \`args\` — the tool call's \`args\` input, verbatim.
103
105
 
104
- Misused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) throw errors that ALWAYS kill the script — they never dissolve into a per-item \`null\`.
105
-
106
- Constraints: concurrency and total-agent caps apply; no filesystem, network, timers, or Node.js APIs are provided — the agents do the work, the script only coordinates them. The run executes in the foreground: this call returns when the whole script finishes.`;
106
+ Misused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) end the whole script instead of producing \`null\`. The script has no filesystem, network, timer, or Node.js APIs; the agents do the work.`;
107
107
  /** The pending-state card: a generic card titled by the workflow's meta name. */
108
108
  function presentWorkflowCall(args) {
109
109
  return {
@@ -133,6 +133,31 @@ function stopReasonError(result) {
133
133
  /* v8 ignore stop */
134
134
  }
135
135
  }
136
+ /**
137
+ * Map a settled background run onto the job outcome vocabulary. A completed
138
+ * run carries the rendered return value as the job's result; a
139
+ * cancelled run leaves the detail to the registry's kill-reason merge (the
140
+ * cancel reason it forwarded is the same string); an errored run fails with
141
+ * the script's failure message.
142
+ */
143
+ function jobOutcomeOf(result, name, maxChars) {
144
+ switch (result.stopReason) {
145
+ case 'completed':
146
+ return {
147
+ status: 'completed',
148
+ detail: `${result.agentsStarted} agent${result.agentsStarted === 1 ? '' : 's'}`,
149
+ result: renderResult(name, result.agentsStarted, result.value, maxChars),
150
+ };
151
+ case 'cancelled':
152
+ return { status: 'killed' };
153
+ case 'error':
154
+ return { status: 'failed', detail: result.error ?? 'unknown error' };
155
+ /* v8 ignore start -- defensive: WorkflowStopReason is a closed union, exhaustive by construction; a future variant fails here loudly */
156
+ default:
157
+ return { status: 'failed', detail: `workflow run ended abnormally (${String(result.stopReason)})` };
158
+ /* v8 ignore stop */
159
+ }
160
+ }
136
161
  /** Render the run's outcome text: the meta name, agent count, and the JSON value (capped). */
137
162
  function renderResult(name, agentsStarted, value, maxChars) {
138
163
  // The engine returns JSON data (null for a valueless script), so stringify never yields undefined.
@@ -142,11 +167,74 @@ function renderResult(name, agentsStarted, value, maxChars) {
142
167
  : rendered;
143
168
  return `workflow "${name}" completed (${agentsStarted} agent${agentsStarted === 1 ? '' : 's'}).\nReturn value:\n${clipped}`;
144
169
  }
170
+ /**
171
+ * Register a background run as an owned job. The engine run is started
172
+ * inside the job starter with no tool-step signal — the run belongs to the
173
+ * job, so a registry kill or owner teardown is what cancels it — and its
174
+ * settlement is the job's settlement: dispose, stop the mirrors, then map the
175
+ * stop reason onto the job outcome (a completed run's rendered return value
176
+ * rides `result` to the model's first read after settlement).
177
+ * @param ctx - plugin context (engine, optional jobs registry, logger).
178
+ * @param args - the validated tool call.
179
+ * @param parent - the calling agent; owns the job.
180
+ * @param recordsRun - whether this top-level call records durable run events.
181
+ * @param deps - the tool's recorder/mirror taps and the render cap.
182
+ * @returns the background result for the tool's output schema.
183
+ */
184
+ function startBackgroundRun(ctx, args, parent, recordsRun, deps) {
185
+ const jobs = ctx.get('jobs');
186
+ if (jobs === undefined) {
187
+ throw new Error('background jobs unavailable: load @x1a0f3n9/dsh-jobs and @x1a0f3n9/dsh-tool-jobs');
188
+ }
189
+ let run;
190
+ const jobId = jobs.start({
191
+ kind: 'workflow',
192
+ label: args.meta.name,
193
+ owner: parent.id,
194
+ run: (job) => {
195
+ // A synchronous engine rejection (META_INVALID/SCRIPT_PARSE) propagates
196
+ // out of the starter, so the registry registers nothing and the model
197
+ // sees the violation list as an ordinary tool error.
198
+ run = ctx.workflowEngine.start({
199
+ script: args.script,
200
+ meta: args.meta,
201
+ ...args.args !== undefined ? { args: args.args } : {},
202
+ parent,
203
+ });
204
+ deps.mirror.start(run.id, job);
205
+ if (recordsRun)
206
+ deps.recorder.start(parent.session, run);
207
+ const done = run.result.then(async (result) => {
208
+ try {
209
+ // Keep member listeners alive through disposal: an engine may
210
+ // synthesize cancelled member endings while reaching quiescence.
211
+ await run.dispose();
212
+ }
213
+ catch (error) {
214
+ // done must not reject; a failed disposal still has a settled result to report.
215
+ ctx.logger.warn(`background workflow run ${run.id} dispose failed: ${String(error)}`);
216
+ }
217
+ deps.mirror.stop(run.id);
218
+ if (recordsRun) {
219
+ deps.recorder.finish(run.id, result.stopReason);
220
+ deps.recorder.abandon(run.id);
221
+ }
222
+ return jobOutcomeOf(result, args.meta.name, deps.maxResultChars);
223
+ });
224
+ return {
225
+ cancel: (reason) => { run.cancel(reason ?? 'background workflow job killed'); },
226
+ done,
227
+ };
228
+ },
229
+ });
230
+ return { kind: 'background', jobId, runId: run.id };
231
+ }
145
232
  export function apply(ctx, config) {
146
233
  // schemastery (the exported Config schema) has already filled the defaulted
147
234
  // fields; the assertion records that resolution, not a hidden fallback.
148
- const { toolName, maxResultChars } = config;
235
+ const { toolName, maxResultChars, enableRunInBackground } = config;
149
236
  const recorder = createWorkflowRecorder(ctx);
237
+ const mirror = createWorkflowRecordMirror(ctx);
150
238
  // Usage policy ships with the tool (the master convention: tool guidance
151
239
  // lives in tool plugins as prompt sections, not in the deployment persona).
152
240
  ctx.systemPrompt.section({
@@ -161,13 +249,14 @@ export function apply(ctx, config) {
161
249
  script: {
162
250
  type: 'string',
163
251
  required: true,
164
- description: 'The plain-JS workflow script body (top-level await allowed; NO `export const meta` statement; end with `return <json-value>`).',
252
+ description: 'The plain JavaScript body, not TypeScript and without an `export const meta` statement; top-level await is allowed. '
253
+ + 'End with `return <value>`; the JSON-serializable value is this tool\'s result.',
165
254
  },
166
255
  meta: {
167
256
  type: 'object',
168
257
  additionalProperties: true,
169
258
  required: true,
170
- description: 'The workflow identity block (plain JSON — never code).',
259
+ description: 'The workflow identity as plain JSON, not code.',
171
260
  properties: {
172
261
  name: { type: 'string', required: true, description: 'Short kebab-case workflow name.' },
173
262
  description: { type: 'string', required: true, description: 'One-line description of what the workflow does.' },
@@ -193,20 +282,42 @@ export function apply(ctx, config) {
193
282
  additionalProperties: true,
194
283
  description: 'Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {"files": [...]}).',
195
284
  },
285
+ ...enableRunInBackground ? {
286
+ run_in_background: {
287
+ type: 'boolean',
288
+ description: 'Run as a background job: return a job id immediately instead of waiting; the return value arrives with the completion notice.',
289
+ },
290
+ } : {},
196
291
  },
197
292
  output: {
198
293
  schema: {
199
- type: 'object',
200
- additionalProperties: false,
201
- properties: {
202
- runId: { type: 'string', required: true },
203
- agentsStarted: { type: 'integer', required: true },
204
- result: { type: 'json', required: true },
205
- },
294
+ oneOf: [
295
+ {
296
+ type: 'object',
297
+ additionalProperties: false,
298
+ properties: {
299
+ kind: { type: 'string', required: true, const: 'background' },
300
+ jobId: { type: 'string', required: true },
301
+ runId: { type: 'string', required: true },
302
+ },
303
+ },
304
+ {
305
+ type: 'object',
306
+ additionalProperties: false,
307
+ properties: {
308
+ kind: { type: 'string', required: true, const: 'foreground' },
309
+ runId: { type: 'string', required: true },
310
+ agentsStarted: { type: 'integer', required: true },
311
+ result: { type: 'json', required: true },
312
+ },
313
+ },
314
+ ],
206
315
  },
207
316
  render: (args, value) => [{
208
317
  type: 'text',
209
- text: renderResult(args.meta.name, value.agentsStarted, value.result, maxResultChars),
318
+ text: value.kind === 'background'
319
+ ? `workflow "${args.meta.name}" started in the background as job ${value.jobId}. Its return value arrives with the completion notice; check on it with job_output, stop it with job_kill.`
320
+ : renderResult(args.meta.name, value.agentsStarted, value.result, maxResultChars),
210
321
  }],
211
322
  },
212
323
  async execute(args, exec) {
@@ -217,6 +328,21 @@ export function apply(ctx, config) {
217
328
  // parent to attribute the children to. Fail loud rather than guess.
218
329
  throw new Error('workflow tool requires a calling agent (exec.agent was undefined)');
219
330
  }
331
+ if (args.run_in_background === true) {
332
+ if (!enableRunInBackground) {
333
+ throw new Error('run_in_background is disabled for this tool');
334
+ }
335
+ // No pre-abort check here, unlike bash/pwsh: ToolRuntime re-reads the
336
+ // caller signal right before execute(), and this branch reaches
337
+ // jobs.start synchronously from there. The shell tools await a
338
+ // sandbox escalation approval before registering, which is the window
339
+ // their check covers.
340
+ return startBackgroundRun(ctx, args, parent, exec.parent === undefined, {
341
+ recorder,
342
+ mirror,
343
+ maxResultChars,
344
+ });
345
+ }
220
346
  // Meta/body validation failures (META_INVALID/SCRIPT_PARSE) throw
221
347
  // synchronously here and become isError results via the registry — the
222
348
  // model sees the violation list and can correct the call.
@@ -228,8 +354,7 @@ export function apply(ctx, config) {
228
354
  signal: exec.signal,
229
355
  });
230
356
  const recordsRun = exec.parent === undefined;
231
- // The shipped worker-thread engine publishes member events from later
232
- // worker messages, after start() returns and this run record is active.
357
+ // The engine publishes member events after start() returns and this run record is active.
233
358
  if (recordsRun)
234
359
  recorder.start(parent.session, run);
235
360
  // Bridge the tool's abort signal to the run: if the parent step is aborted while the
@@ -247,6 +372,7 @@ export function apply(ctx, config) {
247
372
  throw new Error(error);
248
373
  }
249
374
  return {
375
+ kind: 'foreground',
250
376
  runId: run.id,
251
377
  agentsStarted: result.agentsStarted,
252
378
  result: result.value,
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Live-progress mirror for background workflow runs: streams the engine's
3
+ * `workflow/phase`, `workflow/log`, and member lifecycle events into the
4
+ * owning job's output ring as `log` chunks — observer-only narration the
5
+ * model's `job_output` never renders — and keeps the job's live progress
6
+ * line on the current phase. Appends against a settled job log and drop
7
+ * inside the registry, so a straggling event after settlement is harmless.
8
+ * @module @x1a0f3n9/dsh-tool-workflow/record
9
+ */
10
+ import type { Context } from '@deepseek-ai/cordis';
11
+ import type { JobHandle } from '@x1a0f3n9/dsh-jobs';
12
+ import type { WorkflowRunId } from '@x1a0f3n9/dsh-workflow';
13
+ /** Job-ring taps for the background runs the tool tracks. */
14
+ export interface WorkflowRecordMirror {
15
+ /**
16
+ * Route a run's progress events into a job's ring. Call once per background
17
+ * run, from the job starter, before the worker publishes its first event.
18
+ * @param runId - the started run.
19
+ * @param job - the owning job's producer face.
20
+ */
21
+ start(runId: WorkflowRunId, job: JobHandle): void;
22
+ /**
23
+ * Stop routing a settled or abandoned run. Idempotent.
24
+ * @param runId - the run to drop.
25
+ */
26
+ stop(runId: WorkflowRunId): void;
27
+ }
28
+ /**
29
+ * Create the run-to-ring mirror and subscribe the engine's live progress
30
+ * events for the runs it tracks.
31
+ * @param ctx - plugin context whose event bus carries the `workflow/*` events.
32
+ * @returns the mirror taps the tool wires around each background run.
33
+ */
34
+ export declare function createWorkflowRecordMirror(ctx: Context): WorkflowRecordMirror;
35
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Live-progress mirror for background workflow runs: streams the engine's
3
+ * `workflow/phase`, `workflow/log`, and member lifecycle events into the
4
+ * owning job's output ring as `log` chunks — observer-only narration the
5
+ * model's `job_output` never renders — and keeps the job's live progress
6
+ * line on the current phase. Appends against a settled job log and drop
7
+ * inside the registry, so a straggling event after settlement is harmless.
8
+ * @module @x1a0f3n9/dsh-tool-workflow/record
9
+ */
10
+ /**
11
+ * Create the run-to-ring mirror and subscribe the engine's live progress
12
+ * events for the runs it tracks.
13
+ * @param ctx - plugin context whose event bus carries the `workflow/*` events.
14
+ * @returns the mirror taps the tool wires around each background run.
15
+ */
16
+ export function createWorkflowRecordMirror(ctx) {
17
+ const active = new Map();
18
+ ctx.on('workflow/phase', (info, title) => {
19
+ const job = active.get(info.id);
20
+ if (job === undefined)
21
+ return;
22
+ job.updateProgress(title);
23
+ job.append(`▸ ${title}\n`, { channel: 'log' });
24
+ });
25
+ ctx.on('workflow/log', (info, message) => {
26
+ active.get(info.id)?.append(`${message}\n`, { channel: 'log' });
27
+ });
28
+ ctx.on('workflow/agent-start', (info, agent) => {
29
+ active.get(info.id)?.append(`agent #${agent.seq} ${agent.label} started\n`, { channel: 'log' });
30
+ });
31
+ ctx.on('workflow/agent-end', (info, agent) => {
32
+ active.get(info.id)?.append(`agent #${agent.seq} ${agent.outcome}\n`, { channel: 'log' });
33
+ });
34
+ return {
35
+ start(runId, job) {
36
+ active.set(runId, job);
37
+ },
38
+ stop(runId) {
39
+ active.delete(runId);
40
+ },
41
+ };
42
+ }
43
+ //# sourceMappingURL=record.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@x1a0f3n9/dsh-tool-workflow",
3
3
  "description": "Model-facing workflow tool: run a JavaScript orchestration script over ctx.workflowEngine",
4
- "version": "0.1.5-rc.5",
4
+ "version": "0.1.7-rc.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -37,29 +37,34 @@
37
37
  ],
38
38
  "license": "MIT",
39
39
  "peerDependencies": {
40
- "@x1a0f3n9/dsh-agent": "^0.1.5-rc.5",
41
- "@x1a0f3n9/dsh-invariants": "^0.1.5-rc.5",
42
- "@x1a0f3n9/dsh-llm": "^0.1.5-rc.5",
43
- "@x1a0f3n9/dsh-session": "^0.1.5-rc.5",
44
- "@x1a0f3n9/dsh-system-prompt": "^0.1.5-rc.5",
45
- "@x1a0f3n9/dsh-tools": "^0.1.5-rc.5",
46
- "@x1a0f3n9/dsh-workflow": "^0.1.5-rc.5",
47
- "@deepseek-ai/cordis": "^4.0.2"
40
+ "@deepseek-ai/cordis": "~4.0.4",
41
+ "@x1a0f3n9/dsh-agent": "0.1.7-rc.2",
42
+ "@x1a0f3n9/dsh-invariants": "0.1.7-rc.2",
43
+ "@x1a0f3n9/dsh-jobs": "0.1.7-rc.2",
44
+ "@x1a0f3n9/dsh-llm": "0.1.7-rc.2",
45
+ "@x1a0f3n9/dsh-session": "0.1.7-rc.2",
46
+ "@x1a0f3n9/dsh-system-prompt": "0.1.7-rc.2",
47
+ "@x1a0f3n9/dsh-tools": "0.1.7-rc.2",
48
+ "@x1a0f3n9/dsh-workflow": "0.1.7-rc.2"
48
49
  },
49
50
  "dependencies": {
50
- "@deepseek-ai/schemastery": "^3.18.2"
51
+ "@deepseek-ai/schemastery": "~3.18.4"
51
52
  },
52
53
  "devDependencies": {
53
- "@x1a0f3n9/dsh-agent": "^0.1.5-rc.5",
54
- "@x1a0f3n9/dsh-invariants": "^0.1.5-rc.5",
55
- "@x1a0f3n9/dsh-llm": "^0.1.5-rc.5",
56
- "@x1a0f3n9/dsh-session": "^0.1.5-rc.5",
57
- "@x1a0f3n9/dsh-subagent": "^0.1.5-rc.5",
58
- "@x1a0f3n9/dsh-system-prompt": "^0.1.5-rc.5",
59
- "@x1a0f3n9/dsh-tools": "^0.1.5-rc.5",
60
- "@x1a0f3n9/dsh-workflow": "^0.1.5-rc.5",
61
- "@x1a0f3n9/dsh-workflow-worker-thread": "^0.1.5-rc.5",
62
- "@deepseek-ai/cordis": "^4.0.2",
63
- "@x1a0f3n9/dsh-session-projection": "^0.1.5-rc.5"
54
+ "@deepseek-ai/cordis": "~4.0.4",
55
+ "@x1a0f3n9/dsh-agent": "0.1.7-rc.2",
56
+ "@x1a0f3n9/dsh-agent-loop-testkit": "0.1.7-rc.2",
57
+ "@x1a0f3n9/dsh-invariants": "0.1.7-rc.2",
58
+ "@x1a0f3n9/dsh-jobs": "0.1.7-rc.2",
59
+ "@x1a0f3n9/dsh-jobs-local": "0.1.7-rc.2",
60
+ "@x1a0f3n9/dsh-llm": "0.1.7-rc.2",
61
+ "@x1a0f3n9/dsh-session-projection": "0.1.7-rc.2",
62
+ "@x1a0f3n9/dsh-subagent": "0.1.7-rc.2",
63
+ "@x1a0f3n9/dsh-system-prompt": "0.1.7-rc.2",
64
+ "@x1a0f3n9/dsh-tool-jobs": "0.1.7-rc.2",
65
+ "@x1a0f3n9/dsh-tools": "0.1.7-rc.2",
66
+ "@x1a0f3n9/dsh-workflow": "0.1.7-rc.2",
67
+ "@x1a0f3n9/dsh-workflow-ptc": "0.1.7-rc.2",
68
+ "@x1a0f3n9/dsh-session": "0.1.7-rc.2"
64
69
  }
65
70
  }