@prettier-ai/dsh-workflow 0.1.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +143 -0
- package/README.zh.md +143 -0
- package/lib/index.js +92 -0
- package/lib/invariant.js +100 -0
- package/lib/types/index.d.ts +128 -0
- package/lib/types/index.js +80 -0
- package/lib/types/invariant.d.ts +13 -0
- package/lib/types/invariant.js +118 -0
- package/lib/types/runtime-types.d.ts +45 -0
- package/lib/types/runtime-types.js +9 -0
- package/lib/types/types.d.ts +118 -0
- package/lib/types/types.js +16 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeepSeek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
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:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/workflow/workflow/README.md
|
|
5
|
+
README.md: 80b9f1a912f9f58432d2fff1fc76615c04cd2751
|
|
6
|
+
README.zh.md: e692081d99021b7ed6059af61527e551517c9eea
|
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "The workflow orchestration capability: run a model-written script that fans out subagents, for users and maintainers choosing or building on ctx.workflowEngine."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-workflow
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`dsh-workflow` runs a plain-JavaScript orchestration script and gives the caller a live run whose result resolves with the script's final JSON value. The script can fan out subagents with `agent()`, combine independent work with `parallel()` and `pipeline()`, and narrate progress with `phase()` and `log()`; agents normally drive this through the `workflow` tool from `dsh-tool-workflow`. A run is holder-owned: its result never rejects, cancellation and disposal are bounded, and every child is attributed to the invoking agent. The package ships no execution engine — `dsh-workflow-worker-thread` is the current one — so a different isolation strategy can replace it without changing what callers or the model see.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Run a workflow when a task decomposes into many independent pieces that one script should coordinate — an audit across many files, a migration, multi-angle research — and the model explicitly asks for workflow-style orchestration. For one or two delegations, prefer a plain subagent call.
|
|
29
|
+
|
|
30
|
+
### The model-facing path
|
|
31
|
+
|
|
32
|
+
The model reaches the capability through the `workflow` tool from `dsh-tool-workflow`, which owns the call schema and result envelope; the engine supplies the execution underneath. A tool call submits `meta`, `script`, and optional `args` and returns `{ runId, agentsStarted, result }` when the run completes. The tool blocks the parent turn until the whole workflow settles, so the model sees one final outcome, never intermediate child messages.
|
|
33
|
+
|
|
34
|
+
### Running a workflow script
|
|
35
|
+
|
|
36
|
+
An orchestration script is a plain JavaScript body (not TypeScript) that runs with top-level `await` and ends with `return <json-value>`. The `meta` identity block and any `args` arrive as plain JSON data — never evaluated code. During execution the script calls the provided hooks: `agent(prompt, opts)` starts one subagent and resolves with its final text or, with a schema, a validated structured value; `parallel()` and `pipeline()` combine independent work; `phase()` and `log()` narrate progress for observers.
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
// Script body — runs with top-level await, ends with a JSON return value:
|
|
40
|
+
const reviews = await parallel([
|
|
41
|
+
() => agent('Review src/a.ts for correctness'),
|
|
42
|
+
() => agent('Review src/b.ts for correctness'),
|
|
43
|
+
])
|
|
44
|
+
return { reviewed: reviews.length }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
When the script settles, the run's result resolves with the returned value, the stop reason, and the number of children started. A script that returns nothing yields `null`.
|
|
48
|
+
|
|
49
|
+
### Programmatic runs
|
|
50
|
+
|
|
51
|
+
Plugin consumers can start a run directly: `ctx.workflowEngine.start({ script, meta, args?, parent, signal? })`. `parent` attributes every child to the invoking agent; `signal` cancels the run when aborted. `start()` validates the meta block and parses the script before a run exists, so a malformed request fails immediately with a violation list.
|
|
52
|
+
|
|
53
|
+
A returned run exposes `id`, `meta`, `result`, `cancel(reason?)`, and `dispose()`. The result never rejects: a script failure resolves with `stopReason: 'error'`, cancellation with `'cancelled'`. The caller owns the run — call `dispose()` on every path; it cancels remaining work and waits for script and children to settle within a bounded grace.
|
|
54
|
+
|
|
55
|
+
### Failures and recovery
|
|
56
|
+
|
|
57
|
+
A script that does not parse, a malformed meta block, an unavailable provider route, or an unsupported per-run limit is rejected synchronously before a run exists; the `workflow` tool reports these as errors the model can correct from. During execution, hook misuse — bad arguments, unknown options, unsupported schemas, tripped caps — kills the script loudly rather than dissolving into a per-item `null`. An ordinary child failure is not an infrastructure error: `agent()` resolves `null` and the script decides how to handle it.
|
|
58
|
+
|
|
59
|
+
-----
|
|
60
|
+
|
|
61
|
+
<a id="understand-the-implementation"></a>
|
|
62
|
+
## Understand the implementation
|
|
63
|
+
|
|
64
|
+
<details>
|
|
65
|
+
<summary>Implementation internals — click to expand</summary>
|
|
66
|
+
|
|
67
|
+
This section explains how the capability is split and where the contracts live; observable behavior is fully covered in [Use this package](#use-this-package).
|
|
68
|
+
|
|
69
|
+
### Design concept
|
|
70
|
+
|
|
71
|
+
The package separates the script, run, result, and event contracts from execution: any engine can implement `ctx.workflowEngine` behind the same vocabulary, and one engine serves a context at a time — loading a second engine fails loud, so swapping engines means changing which engine plugin the composition loads. The `workflow/*` events are observe-only: payloads carry run identity snapshots, never the live run, so listeners cannot acquire cancellation or disposal authority.
|
|
72
|
+
|
|
73
|
+
### Source map
|
|
74
|
+
|
|
75
|
+
| File | Role |
|
|
76
|
+
|---|---|
|
|
77
|
+
| [`src/index.ts`](src/index.ts) | Service definition, `workflow/*` event declarations, `WorkflowError` and its fatal flag |
|
|
78
|
+
| [`src/types.ts`](src/types.ts) | Browser-safe vocabulary: `WorkflowMeta`, `WorkflowResult`, run and agent event info |
|
|
79
|
+
| [`src/runtime-types.ts`](src/runtime-types.ts) | Host-only `WorkflowStartRequest` and `WorkflowRun` handles |
|
|
80
|
+
| [`src/invariant.ts`](src/invariant.ts) | Invariant companion: event pairing and identity checks |
|
|
81
|
+
|
|
82
|
+
### Lifecycle and ownership
|
|
83
|
+
|
|
84
|
+
A run is holder-owned: engine-plugin unload prevents new starts but does not revoke accepted runs, and the caller must dispose every run it starts. `dispose()` cancels if needed and awaits script and child quiescence within the engine's documented bound, so a consumer awaiting `result` is never wedged past a cancellation.
|
|
85
|
+
|
|
86
|
+
`workflow/start` and `workflow/end` pair the run; `workflow/phase` and `workflow/log` carry script narration; `workflow/agent-start` and `workflow/agent-end` pair each child call by `seq`. Every listener is independently contained: a throwing listener is logged without starving peers or changing execution, and each receives its own payload clone.
|
|
87
|
+
|
|
88
|
+
### Failure discipline
|
|
89
|
+
|
|
90
|
+
`WorkflowError` carries a machine-routable code and a `fatal` flag; every code is fatal, and `parallel()` and `pipeline()` re-throw fatal errors instead of mapping the item to `null` — a typo'd option must kill the script loudly. Codes cover start failures, contract violations, exceeded caps, provider and result faults, unserializable values, and cancellation; the exact set and meanings live in [`src/index.ts`](src/index.ts).
|
|
91
|
+
|
|
92
|
+
The per-item `null` is reserved for child-run failures and ordinary in-stage script errors, so a child that resolves normally with a non-completed stop reason is not an infrastructure exception: `agent()` returns `null`, letting the script handle an ordinary child failure.
|
|
93
|
+
|
|
94
|
+
</details>
|
|
95
|
+
|
|
96
|
+
-----
|
|
97
|
+
|
|
98
|
+
<a id="further-exploration"></a>
|
|
99
|
+
## Further Exploration
|
|
100
|
+
|
|
101
|
+
Read these pages when the package-level contract is not enough. They move from the shared workflow model to the current engine and the model-facing consumers.
|
|
102
|
+
|
|
103
|
+
- [Workflow subsystem](../../../docs/subsystems/workflow.md) — the full type vocabulary, start request, and event payloads.
|
|
104
|
+
- [Group map](../README.md) — the workflow capability family and its packages.
|
|
105
|
+
- [workflow tool](../tool-workflow/README.md) — the model-facing consumer that owns the call schema and result envelope.
|
|
106
|
+
- [Worker-thread engine](../workflow-worker-thread/README.md) — the current execution engine and its isolation boundary.
|
|
107
|
+
- [Dynamic workflows Agent Note](../../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md) — the seam design and its decisions.
|
|
108
|
+
|
|
109
|
+
-----
|
|
110
|
+
|
|
111
|
+
<a id="model-experience"></a>
|
|
112
|
+
## Model Experience
|
|
113
|
+
|
|
114
|
+
Indirectly, through its consumer `dsh-tool-workflow` and a workflow engine, which render the parent tool result and the child-agent requests.
|
|
115
|
+
|
|
116
|
+
#### KV Cache effect
|
|
117
|
+
|
|
118
|
+
No direct invalidation; the named consumer and engine own any request-prefix changes.
|
|
119
|
+
|
|
120
|
+
## Known Limitations and Deferred Work
|
|
121
|
+
|
|
122
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
These limits define what the capability does not yet support. They are current constraints, not a task backlog.
|
|
126
|
+
|
|
127
|
+
- **Foreground collection only** — the caller owns one live run and awaits it; background start/poll, spill handles, and detached collection are deferred.
|
|
128
|
+
- **No journaling or resume** — scripts, child progress, and intermediate values are not checkpointed, so a process restart cannot continue a run.
|
|
129
|
+
- **No saved or nested workflows** — the capability starts caller-supplied scripts only, and a workflow script receives no `workflow()` hook for recursive orchestration.
|
|
130
|
+
- **No token-budget vocabulary** — engines cap concurrency, items, and children, but neither the request nor the result accounts for model tokens across children.
|
|
131
|
+
- **Runs are holder-owned, not service-tracked** — unloading the engine does not discover independent live handles; every consumer must dispose the run it started.
|
|
132
|
+
|
|
133
|
+
<a id="dev-note"></a>
|
|
134
|
+
### Dev Note
|
|
135
|
+
|
|
136
|
+
<details>
|
|
137
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
138
|
+
|
|
139
|
+
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.
|
|
140
|
+
|
|
141
|
+
Deferred directions: a background start/poll API with spill handles and detached collection; saved and nested workflows; a token-budget vocabulary across children; and the seam's promise that a future process or sandbox engine can replace the worker-thread engine without changing the model-facing surface.
|
|
142
|
+
|
|
143
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "工作流编排能力:运行由模型编写的、扇出 subagent 的脚本,供选择或构建在 ctx.workflowEngine 之上的用户与维护者阅读。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-workflow
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`dsh-workflow` 运行一段纯 JavaScript 编排脚本,并交给调用方一个活动运行,其 result 在脚本结算时以脚本的最终 JSON 值兑现。脚本可以用 `agent()` 扇出 subagent,用 `parallel()` 和 `pipeline()` 组合独立工作,用 `phase()` 和 `log()` 叙述进度;agent 通常通过 `dsh-tool-workflow` 的 `workflow` 工具驱动这一切。运行由持有方负责:其 result 绝不拒绝,取消与 dispose(资源释放)有界,每个子 agent 都归属于调用它的 agent。本包不附带执行引擎——当前引擎是 `dsh-workflow-worker-thread`——因此可以用不同的隔离策略替换它,而不改变调用方或模型看到的内容。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
当任务分解为许多独立部分、适合用一段脚本统一协调——例如跨多个文件的审计、一次迁移、多角度研究——且模型明确要求工作流式编排时,运行工作流。一两项委派时,优先使用普通 subagent 调用。
|
|
29
|
+
|
|
30
|
+
### 模型侧路径
|
|
31
|
+
|
|
32
|
+
模型通过 `dsh-tool-workflow` 的 `workflow` 工具触达该能力;该工具拥有调用 schema 与结果包络,引擎提供其下的执行。一次工具调用提交 `meta`、`script` 与可选 `args`,运行完成时返回 `{ runId, agentsStarted, result }`。工具会阻塞父级轮次直到整个工作流结算,因此模型只看到最终结果,永远不会看到中间子 agent 消息。
|
|
33
|
+
|
|
34
|
+
### 运行工作流脚本
|
|
35
|
+
|
|
36
|
+
编排脚本是纯 JavaScript 脚本体(不是 TypeScript),以顶层 `await` 运行并以 `return <json-value>` 结尾。`meta` 身份块与任何 `args` 都以普通 JSON 数据到达——绝不作为代码求值。执行期间脚本调用提供的钩子:`agent(prompt, opts)` 启动一个 subagent,并以其最终文本、或在提供 schema 时以经过校验的结构化值兑现;`parallel()` 与 `pipeline()` 组合独立工作;`phase()` 与 `log()` 为观察者叙述进度。
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
// Script body — runs with top-level await, ends with a JSON return value:
|
|
40
|
+
const reviews = await parallel([
|
|
41
|
+
() => agent('Review src/a.ts for correctness'),
|
|
42
|
+
() => agent('Review src/b.ts for correctness'),
|
|
43
|
+
])
|
|
44
|
+
return { reviewed: reviews.length }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
脚本结算时,运行的 result 以返回值、结束原因和已启动的子 agent 数量兑现。脚本不返回值时得到 `null`。
|
|
48
|
+
|
|
49
|
+
### 编程方式运行
|
|
50
|
+
|
|
51
|
+
插件消费方可以直接启动运行:`ctx.workflowEngine.start({ script, meta, args?, parent, signal? })`。`parent` 把每个子 agent 归属于调用它的 agent;`signal` 在中止时取消运行。`start()` 在运行存在之前校验 meta 块并解析脚本,因此格式错误的请求会立即以违规清单失败。
|
|
52
|
+
|
|
53
|
+
返回的运行公开 `id`、`meta`、`result`、`cancel(reason?)` 与 `dispose()`。result 绝不拒绝:脚本失败以 `stopReason: 'error'` 兑现,取消以 `'cancelled'` 兑现。调用方拥有该运行——每条路径都要调用 `dispose()`;它会取消剩余工作,并在有界宽限期内等待脚本与子 agent 完全停稳。
|
|
54
|
+
|
|
55
|
+
### 失败与恢复
|
|
56
|
+
|
|
57
|
+
无法解析的脚本、格式错误的 meta 块、不可用的提供方路由或不受支持的单次运行限制,都会在运行存在之前被同步拒绝;`workflow` 工具把这些报告为模型可以修正的错误。执行期间,钩子误用——错误参数、未知选项、不支持的 schema、超出上限——会响亮地终止脚本,而不会溶解为逐项 `null`。普通子 agent 失败不是基础设施错误:`agent()` 以 `null` 兑现,由脚本决定如何处理。
|
|
58
|
+
|
|
59
|
+
-----
|
|
60
|
+
|
|
61
|
+
<a id="understand-the-implementation"></a>
|
|
62
|
+
## 理解实现
|
|
63
|
+
|
|
64
|
+
<details>
|
|
65
|
+
<summary>实现细节——点击展开</summary>
|
|
66
|
+
|
|
67
|
+
本节解释能力如何拆分、契约位于何处;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
68
|
+
|
|
69
|
+
### 设计理念
|
|
70
|
+
|
|
71
|
+
本包把脚本、运行、结果与事件契约同执行分开:任何引擎都可以在同一词汇背后实现 `ctx.workflowEngine`,一个上下文同时只有一个引擎——加载第二个引擎会立即失败,因此更换引擎意味着更改组合所加载的引擎插件。`workflow/*` 事件只供观察:payload 携带运行身份快照,绝不携带活动运行,因此监听器无法取得取消或 dispose 权限。
|
|
72
|
+
|
|
73
|
+
### 源码地图
|
|
74
|
+
|
|
75
|
+
| 文件 | 职责 |
|
|
76
|
+
|---|---|
|
|
77
|
+
| [`src/index.ts`](src/index.ts) | 服务定义、`workflow/*` 事件声明、`WorkflowError` 及其 fatal 标志 |
|
|
78
|
+
| [`src/types.ts`](src/types.ts) | 浏览器安全词汇:`WorkflowMeta`、`WorkflowResult`、运行与 agent 事件信息 |
|
|
79
|
+
| [`src/runtime-types.ts`](src/runtime-types.ts) | 仅宿主的 `WorkflowStartRequest` 与 `WorkflowRun` 句柄 |
|
|
80
|
+
| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件:事件配对与身份校验 |
|
|
81
|
+
|
|
82
|
+
### 生命周期与归属
|
|
83
|
+
|
|
84
|
+
运行由持有方负责:引擎插件卸载会阻止新的启动,但不会撤销已接受的运行,调用方必须 dispose 自己启动的每个运行。`dispose()` 在需要时取消,并在引擎文档规定的期限内等待脚本与子 agent 完全停稳,因此等待 `result` 的消费方绝不会因取消而卡死。
|
|
85
|
+
|
|
86
|
+
`workflow/start` 与 `workflow/end` 为运行配对;`workflow/phase` 与 `workflow/log` 携带脚本叙述;`workflow/agent-start` 与 `workflow/agent-end` 按 `seq` 为每次子 agent 调用配对。每个监听器都独立隔离:抛错的监听器只记录日志,不会饿死同级监听器或改变执行,并且每个监听器都会收到自己的 payload 副本。
|
|
87
|
+
|
|
88
|
+
### 失败纪律
|
|
89
|
+
|
|
90
|
+
`WorkflowError` 携带机器可路由的 code 与 `fatal` 标志;每个 code 都是致命的,`parallel()` 与 `pipeline()` 会重新抛出致命错误,而不是把条目映射为 `null`——拼错的选项必须响亮地终止脚本。code 覆盖启动失败、契约违规、超出上限、提供方与结果故障、不可序列化值与取消;完整集合与含义见 [`src/index.ts`](src/index.ts)。
|
|
91
|
+
|
|
92
|
+
逐项 `null` 只保留给子运行失败与阶段内普通脚本错误,因此以非完成结束原因正常结算的子 agent 不属于基础设施异常:`agent()` 返回 `null`,让脚本处理普通子 agent 失败。
|
|
93
|
+
|
|
94
|
+
</details>
|
|
95
|
+
|
|
96
|
+
-----
|
|
97
|
+
|
|
98
|
+
<a id="further-exploration"></a>
|
|
99
|
+
## 进一步探索
|
|
100
|
+
|
|
101
|
+
当包级契约不够用时阅读以下页面。它们从共享工作流模型逐步进入当前引擎与面向模型的消费方。
|
|
102
|
+
|
|
103
|
+
- [工作流子系统](../../../docs/subsystems/workflow.zh.md)——完整类型词汇、启动请求与事件载荷。
|
|
104
|
+
- [组地图](../README.zh.md)——工作流能力家族及其包。
|
|
105
|
+
- [workflow 工具](../tool-workflow/README.zh.md)——拥有调用 schema 与结果包络的模型侧消费方。
|
|
106
|
+
- [worker-thread 引擎](../workflow-worker-thread/README.zh.md)——当前执行引擎及其隔离边界。
|
|
107
|
+
- [动态工作流 Agent Note](../../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.zh.md)——seam 设计及其决策。
|
|
108
|
+
|
|
109
|
+
-----
|
|
110
|
+
|
|
111
|
+
<a id="model-experience"></a>
|
|
112
|
+
## 模型体验
|
|
113
|
+
|
|
114
|
+
间接地,通过其消费方 `dsh-tool-workflow` 与一个工作流引擎,由它们渲染父级工具结果与子 agent 请求。
|
|
115
|
+
|
|
116
|
+
#### KV Cache 影响
|
|
117
|
+
|
|
118
|
+
不会直接导致失效;请求前缀的任何变化均由上述消费方与引擎负责。
|
|
119
|
+
|
|
120
|
+
## 已知限制与延期工作
|
|
121
|
+
|
|
122
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
这些限制说明该能力尚未支持什么。它们是当前约束,不是任务积压。
|
|
126
|
+
|
|
127
|
+
- **仅支持前台收集**——调用方拥有一个活动运行并等待它;后台启动/轮询、spill 句柄与分离收集均暂缓。
|
|
128
|
+
- **没有日志化或恢复**——脚本、子 agent 进度与中间值均不设检查点,因此进程重启后无法继续运行。
|
|
129
|
+
- **没有已保存或嵌套工作流**——该能力只启动调用方提供的脚本,工作流脚本不会收到用于递归编排的 `workflow()` 钩子。
|
|
130
|
+
- **没有 token 预算词汇**——引擎限制并发、条目与子 agent,但请求与结果都不会统计跨子 agent 的模型 token。
|
|
131
|
+
- **运行由持有方负责,不由服务跟踪**——卸载引擎不会发现独立的活动句柄;每个消费方都必须 dispose 自己启动的运行。
|
|
132
|
+
|
|
133
|
+
<a id="dev-note"></a>
|
|
134
|
+
### 开发备注
|
|
135
|
+
|
|
136
|
+
<details>
|
|
137
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
138
|
+
|
|
139
|
+
本开发备注是维护者的工作上下文:尚未决定的开放方向。它明确不具权威性——已交付的行为、限制与既定理由以上文、包代码与相关 Agent Note 为准。
|
|
140
|
+
|
|
141
|
+
暂缓的方向:带 spill 句柄与分离收集的后台启动/轮询 API;已保存与嵌套工作流;跨子 agent 的 token 预算词汇;以及该 seam 的承诺——未来的进程或沙箱引擎可以在不改变模型侧表面的前提下替换 worker-thread 引擎。
|
|
142
|
+
|
|
143
|
+
</details>
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Service } from "@prettier-ai/cordis";
|
|
2
|
+
import { HarnessError } from "@prettier-ai/dsh-llm";
|
|
3
|
+
//#region lib/types/types.js
|
|
4
|
+
/**
|
|
5
|
+
* Workflow seam vocabulary: the request/run/result types a workflow engine
|
|
6
|
+
* consumes and produces, plus the fields in the `workflow/*` event payloads.
|
|
7
|
+
* Types only (plus the id-brand factory), per the package convention.
|
|
8
|
+
*
|
|
9
|
+
* @module @prettier-ai/dsh-workflow/types
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Brand a string as a {@link WorkflowRunId}.
|
|
13
|
+
* @param id - the raw id string (the engine mints UUIDs; tests may pass fixtures).
|
|
14
|
+
* @returns the same string, branded.
|
|
15
|
+
*/
|
|
16
|
+
function WorkflowRunId(id) {
|
|
17
|
+
return id;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region lib/types/index.js
|
|
21
|
+
/**
|
|
22
|
+
* Service Definition for the workflow capability seam. Service Providers execute orchestration scripts;
|
|
23
|
+
* observe-only lifecycle events never expose run control.
|
|
24
|
+
* @module @prettier-ai/dsh-workflow
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Typed error for workflow-seam failures. Extends {@link HarnessError}, so the
|
|
28
|
+
* `code` is machine-routable taxonomy. `fatal` drives the combinator
|
|
29
|
+
* discipline: `parallel()`/`pipeline()` re-throw a fatal error (a typo'd
|
|
30
|
+
* option or a tripped cap must kill the script loudly), and reserve the
|
|
31
|
+
* per-item `null` for child-run failures and ordinary in-stage script errors.
|
|
32
|
+
* Every {@link WorkflowErrorCode} is fatal; the flag exists so the
|
|
33
|
+
* distinction is explicit at every catch site rather than implied.
|
|
34
|
+
*/
|
|
35
|
+
var WorkflowError = class extends HarnessError {
|
|
36
|
+
/** Whether combinators must propagate this error instead of nulling the item. */
|
|
37
|
+
fatal;
|
|
38
|
+
constructor(message, code, options) {
|
|
39
|
+
super(message, code, options);
|
|
40
|
+
this.name = "WorkflowError";
|
|
41
|
+
this.fatal = options?.fatal ?? true;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Whether combinators must re-throw `error` instead of mapping the item to `null`.
|
|
46
|
+
* @param error - any thrown value; fatality is host `instanceof` (unforgeable from a script realm).
|
|
47
|
+
* @returns true iff `error` is a {@link WorkflowError} whose `fatal` flag is set.
|
|
48
|
+
*/
|
|
49
|
+
function isFatalWorkflowError(error) {
|
|
50
|
+
return error instanceof WorkflowError && error.fatal;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Workflow Service Definition contract. Invalid requests throw before publication; a live
|
|
54
|
+
* run is holder-owned, its result never rejects, cancellation and disposal are
|
|
55
|
+
* bounded, and disposal waits for child cleanup within that bound. Lifecycle
|
|
56
|
+
* listener failures are contained, and `workflow/end` fires exactly once as the
|
|
57
|
+
* result settles.
|
|
58
|
+
*/
|
|
59
|
+
var WorkflowEngine = class extends Service {
|
|
60
|
+
constructor(ctx) {
|
|
61
|
+
super(ctx, "workflowEngine");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Emit a lifecycle event while containing and logging each listener failure.
|
|
65
|
+
* @param name - the `workflow/*` event to dispatch.
|
|
66
|
+
* @param args - the event's payload, matching its declared signature.
|
|
67
|
+
*/
|
|
68
|
+
emitWorkflowEvent(name, ...args) {
|
|
69
|
+
for (const callback of this.ctx.events.dispatch("emit", [name, ...args])) try {
|
|
70
|
+
const returned = callback(...args);
|
|
71
|
+
Promise.resolve(returned).catch((error) => {
|
|
72
|
+
this.ctx.logger.warn(`workflow: ${name} listener rejected: ${renderListenerError(error)}`);
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
this.ctx.logger.warn(`workflow: ${name} listener threw: ${renderListenerError(error)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Render any thrown value without violating listener containment.
|
|
81
|
+
* @param error - any thrown value.
|
|
82
|
+
* @returns `String(error)`, or a fixed label when even coercion throws.
|
|
83
|
+
*/
|
|
84
|
+
function renderListenerError(error) {
|
|
85
|
+
try {
|
|
86
|
+
return String(error);
|
|
87
|
+
} catch {
|
|
88
|
+
return "[unrenderable thrown value]";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { WorkflowEngine, WorkflowEngine as default, WorkflowError, WorkflowRunId, isFatalWorkflowError };
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/** Package-owned workflow lifecycle invariants. @module @prettier-ai/dsh-workflow/invariant */
|
|
3
|
+
const PACKAGE_NAME = "@prettier-ai/dsh-workflow";
|
|
4
|
+
/** Cordis companion plugin name. */
|
|
5
|
+
const name = "workflow-invariant";
|
|
6
|
+
/** Service required before the companion can reserve package ownership. */
|
|
7
|
+
const inject = ["invariants"];
|
|
8
|
+
/** Require every event for a run to retain its validated identity snapshot. */
|
|
9
|
+
function traceFor(traces, info, fail) {
|
|
10
|
+
const trace = traces.get(info.id);
|
|
11
|
+
if (trace === void 0) fail(`workflow event has no matching workflow/start for run ${JSON.stringify(info.id)}`);
|
|
12
|
+
if (trace.meta !== JSON.stringify(info.meta)) fail(`workflow event meta diverges from workflow/start for run ${JSON.stringify(info.id)}`);
|
|
13
|
+
return trace;
|
|
14
|
+
}
|
|
15
|
+
/** Assert the immutable identity fields shared by an agent pair. */
|
|
16
|
+
function validateAgentEnd(start, end, fail) {
|
|
17
|
+
if (start.label !== end.label || start.phase !== end.phase || start.childId !== end.childId) fail(`workflow/agent-end identity diverges from workflow/agent-start for seq ${end.seq}`);
|
|
18
|
+
const outcome = end.outcome;
|
|
19
|
+
if (outcome !== "completed" && outcome !== "failed" && outcome !== "cancelled") fail(`workflow/agent-end carries unknown outcome ${JSON.stringify(outcome)}`);
|
|
20
|
+
}
|
|
21
|
+
/** Validate a terminal result against the accumulated run trace. */
|
|
22
|
+
function validateWorkflowEnd(trace, result, fail) {
|
|
23
|
+
if (trace.agents.size > 0) fail(`workflow/end has ${trace.agents.size} agent call(s) without workflow/agent-end`);
|
|
24
|
+
if (!Number.isSafeInteger(result.agentsStarted) || result.agentsStarted < trace.starts) fail("workflow/end agentsStarted must be a safe integer covering every observed agent start");
|
|
25
|
+
if (result.stopReason === "completed" ? result.error !== void 0 : typeof result.error !== "string") fail("workflow/end error must be absent exactly for completed runs");
|
|
26
|
+
}
|
|
27
|
+
/** Install workflow start/end and child-call pairing checks. */
|
|
28
|
+
const install = (ctx, fail) => {
|
|
29
|
+
const traces = /* @__PURE__ */ new Map();
|
|
30
|
+
const stagedStarts = /* @__PURE__ */ new WeakSet();
|
|
31
|
+
const stagedAgentStarts = /* @__PURE__ */ new WeakSet();
|
|
32
|
+
const stagedAgentEnds = /* @__PURE__ */ new WeakSet();
|
|
33
|
+
const stagedEnds = /* @__PURE__ */ new WeakSet();
|
|
34
|
+
ctx.on("internal/dispatch", (_mode, eventName, args) => {
|
|
35
|
+
if (eventName === "workflow/start") {
|
|
36
|
+
const info = args[0];
|
|
37
|
+
if (String(info.id).length === 0 || info.meta.name.length === 0 || info.meta.description.length === 0) fail("workflow/start id, meta.name, and meta.description must be non-empty");
|
|
38
|
+
if (traces.has(info.id)) fail(`workflow/start repeated run id ${JSON.stringify(info.id)}`);
|
|
39
|
+
stagedStarts.add(info);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!eventName.startsWith("workflow/")) return;
|
|
43
|
+
const info = args[0];
|
|
44
|
+
const trace = traceFor(traces, info, fail);
|
|
45
|
+
if (eventName === "workflow/agent-start") {
|
|
46
|
+
const agent = args[1];
|
|
47
|
+
if (!Number.isSafeInteger(agent.seq) || agent.seq < 1 || String(agent.childId).length === 0) fail("workflow/agent-start seq must be positive and childId must be non-empty");
|
|
48
|
+
if (trace.agents.has(agent.seq)) fail(`workflow/agent-start repeated seq ${agent.seq}`);
|
|
49
|
+
stagedAgentStarts.add(agent);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (eventName === "workflow/agent-end") {
|
|
53
|
+
const agent = args[1];
|
|
54
|
+
const start = trace.agents.get(agent.seq);
|
|
55
|
+
if (start === void 0) return fail(`workflow/agent-end has no matching start for seq ${agent.seq}`);
|
|
56
|
+
validateAgentEnd(start, agent, fail);
|
|
57
|
+
stagedAgentEnds.add(agent);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (eventName === "workflow/end") {
|
|
61
|
+
const result = args[1];
|
|
62
|
+
validateWorkflowEnd(trace, result, fail);
|
|
63
|
+
stagedEnds.add(result);
|
|
64
|
+
}
|
|
65
|
+
}, { global: true });
|
|
66
|
+
ctx.on("workflow/start", (info) => {
|
|
67
|
+
/* v8 ignore next -- internal/dispatch stages the same run-info object */
|
|
68
|
+
if (!stagedStarts.delete(info)) return;
|
|
69
|
+
traces.set(info.id, {
|
|
70
|
+
meta: JSON.stringify(info.meta),
|
|
71
|
+
agents: /* @__PURE__ */ new Map(),
|
|
72
|
+
starts: 0
|
|
73
|
+
});
|
|
74
|
+
}, { global: true });
|
|
75
|
+
ctx.on("workflow/agent-start", (info, agent) => {
|
|
76
|
+
/* v8 ignore next -- internal/dispatch stages the same agent object */
|
|
77
|
+
if (!stagedAgentStarts.delete(agent)) return;
|
|
78
|
+
const trace = traceFor(traces, info, fail);
|
|
79
|
+
trace.agents.set(agent.seq, agent);
|
|
80
|
+
trace.starts += 1;
|
|
81
|
+
}, { global: true });
|
|
82
|
+
ctx.on("workflow/agent-end", (info, agent) => {
|
|
83
|
+
/* v8 ignore next -- internal/dispatch stages the same agent object */
|
|
84
|
+
if (!stagedAgentEnds.delete(agent)) return;
|
|
85
|
+
traceFor(traces, info, fail).agents.delete(agent.seq);
|
|
86
|
+
}, { global: true });
|
|
87
|
+
ctx.on("workflow/end", (info, result) => {
|
|
88
|
+
/* v8 ignore next -- internal/dispatch stages the same result object */
|
|
89
|
+
if (!stagedEnds.delete(result)) return;
|
|
90
|
+
traces.delete(info.id);
|
|
91
|
+
}, { global: true });
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Register the workflow invariant companion.
|
|
95
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
96
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
97
|
+
*/
|
|
98
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
99
|
+
//#endregion
|
|
100
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Definition for the workflow capability seam. Service Providers execute orchestration scripts;
|
|
3
|
+
* observe-only lifecycle events never expose run control.
|
|
4
|
+
* @module @prettier-ai/dsh-workflow
|
|
5
|
+
*/
|
|
6
|
+
import { Context, Service } from '@prettier-ai/cordis';
|
|
7
|
+
import { HarnessError } from '@prettier-ai/dsh-llm';
|
|
8
|
+
import type { WorkflowAgentEndInfo, WorkflowAgentInfo, WorkflowResultInfo, WorkflowRunInfo } from './types.ts';
|
|
9
|
+
import type { WorkflowRun, WorkflowStartRequest } from './runtime-types.ts';
|
|
10
|
+
export { WorkflowRunId } from './types.ts';
|
|
11
|
+
export type { WorkflowAgentEndInfo, WorkflowAgentInfo, WorkflowAgentOutcome, WorkflowMeta, WorkflowPhase, WorkflowResult, WorkflowResultInfo, WorkflowRunInfo, WorkflowStopReason, } from './types.ts';
|
|
12
|
+
export type { WorkflowRun, WorkflowStartRequest } from './runtime-types.ts';
|
|
13
|
+
declare module '@prettier-ai/cordis' {
|
|
14
|
+
interface Context {
|
|
15
|
+
workflowEngine: WorkflowEngine;
|
|
16
|
+
}
|
|
17
|
+
interface Events {
|
|
18
|
+
/**
|
|
19
|
+
* A workflow run started — the script's meta block validated, the body
|
|
20
|
+
* about to execute. Paired with {@link Events['workflow/end']}.
|
|
21
|
+
* @param info - the run's identity snapshot (id + meta).
|
|
22
|
+
* @mode emit
|
|
23
|
+
*/
|
|
24
|
+
'workflow/start'(info: WorkflowRunInfo): void;
|
|
25
|
+
/**
|
|
26
|
+
* The script entered a phase (a `phase(title)` call) — progress grouping
|
|
27
|
+
* for observers; no execution semantics.
|
|
28
|
+
* @param info - the run's identity snapshot.
|
|
29
|
+
* @param title - the phase title, verbatim.
|
|
30
|
+
* @mode emit
|
|
31
|
+
*/
|
|
32
|
+
'workflow/phase'(info: WorkflowRunInfo, title: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* The script emitted a narration line (a `log(message)` call).
|
|
35
|
+
* @param info - the run's identity snapshot.
|
|
36
|
+
* @param message - the logged message, verbatim.
|
|
37
|
+
* @mode emit
|
|
38
|
+
*/
|
|
39
|
+
'workflow/log'(info: WorkflowRunInfo, message: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* One `agent()` call established a published child run. Paired with
|
|
42
|
+
* {@link Events['workflow/agent-end']} by `agent.seq`. A call that never
|
|
43
|
+
* receives a published run from the provider emits neither
|
|
44
|
+
* event in this pair.
|
|
45
|
+
* @param info - the run's identity snapshot.
|
|
46
|
+
* @param agent - the call's sequence number, label, phase, and child id.
|
|
47
|
+
* @mode emit
|
|
48
|
+
*/
|
|
49
|
+
'workflow/agent-start'(info: WorkflowRunInfo, agent: WorkflowAgentInfo): void;
|
|
50
|
+
/**
|
|
51
|
+
* One `agent()` call settled (clean result, child failure, or run
|
|
52
|
+
* cancellation). Paired with {@link Events['workflow/agent-start']} by
|
|
53
|
+
* `agent.seq`, exactly once per started call on every stop path — on an
|
|
54
|
+
* engine termination path (a worker killed past its grace) the end is
|
|
55
|
+
* engine-synthesized with outcome `'cancelled'`.
|
|
56
|
+
* @param info - the run's identity snapshot.
|
|
57
|
+
* @param agent - the call identity plus its outcome.
|
|
58
|
+
* @mode emit
|
|
59
|
+
*/
|
|
60
|
+
'workflow/agent-end'(info: WorkflowRunInfo, agent: WorkflowAgentEndInfo): void;
|
|
61
|
+
/**
|
|
62
|
+
* A workflow run settled (any stop reason). Fired when
|
|
63
|
+
* {@link WorkflowRun.result} resolves. Paired with
|
|
64
|
+
* {@link Events['workflow/start']}.
|
|
65
|
+
* @param info - the run's identity snapshot.
|
|
66
|
+
* @param result - the outcome data (stop reason, error, agent count) —
|
|
67
|
+
* deliberately WITHOUT the result value (see {@link WorkflowResultInfo}).
|
|
68
|
+
* @mode emit
|
|
69
|
+
*/
|
|
70
|
+
'workflow/end'(info: WorkflowRunInfo, result: WorkflowResultInfo): void;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** The full set of `workflow/*` event names {@link WorkflowEngine.emitWorkflowEvent} dispatches. */
|
|
74
|
+
export type WorkflowEventName = 'workflow/start' | 'workflow/phase' | 'workflow/log' | 'workflow/agent-start' | 'workflow/agent-end' | 'workflow/end';
|
|
75
|
+
/**
|
|
76
|
+
* Machine-routable fatal workflow failures: parse/meta/argument/schema errors,
|
|
77
|
+
* resource caps, subagent infrastructure failures, unserializable boundary
|
|
78
|
+
* values, and cancellation. An ordinary child failure resolves its item to
|
|
79
|
+
* `null` and is not one of these fatal codes.
|
|
80
|
+
*/
|
|
81
|
+
export type WorkflowErrorCode = 'SCRIPT_PARSE' | 'META_INVALID' | 'INVALID_ARGUMENT' | 'UNSUPPORTED_OPTION' | 'UNSUPPORTED_SCHEMA' | 'AGENT_CAP' | 'ITEM_CAP' | 'AGENT_START' | 'AGENT_RESULT' | 'RESULT_UNSERIALIZABLE' | 'CANCELLED';
|
|
82
|
+
/**
|
|
83
|
+
* Typed error for workflow-seam failures. Extends {@link HarnessError}, so the
|
|
84
|
+
* `code` is machine-routable taxonomy. `fatal` drives the combinator
|
|
85
|
+
* discipline: `parallel()`/`pipeline()` re-throw a fatal error (a typo'd
|
|
86
|
+
* option or a tripped cap must kill the script loudly), and reserve the
|
|
87
|
+
* per-item `null` for child-run failures and ordinary in-stage script errors.
|
|
88
|
+
* Every {@link WorkflowErrorCode} is fatal; the flag exists so the
|
|
89
|
+
* distinction is explicit at every catch site rather than implied.
|
|
90
|
+
*/
|
|
91
|
+
export declare class WorkflowError extends HarnessError {
|
|
92
|
+
/** Whether combinators must propagate this error instead of nulling the item. */
|
|
93
|
+
readonly fatal: boolean;
|
|
94
|
+
constructor(message: string, code: WorkflowErrorCode, options?: ErrorOptions & {
|
|
95
|
+
fatal?: boolean;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Whether combinators must re-throw `error` instead of mapping the item to `null`.
|
|
100
|
+
* @param error - any thrown value; fatality is host `instanceof` (unforgeable from a script realm).
|
|
101
|
+
* @returns true iff `error` is a {@link WorkflowError} whose `fatal` flag is set.
|
|
102
|
+
*/
|
|
103
|
+
export declare function isFatalWorkflowError(error: unknown): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Workflow Service Definition contract. Invalid requests throw before publication; a live
|
|
106
|
+
* run is holder-owned, its result never rejects, cancellation and disposal are
|
|
107
|
+
* bounded, and disposal waits for child cleanup within that bound. Lifecycle
|
|
108
|
+
* listener failures are contained, and `workflow/end` fires exactly once as the
|
|
109
|
+
* result settles.
|
|
110
|
+
*/
|
|
111
|
+
export declare abstract class WorkflowEngine extends Service {
|
|
112
|
+
constructor(ctx: Context);
|
|
113
|
+
/**
|
|
114
|
+
* Parse and execute a workflow script.
|
|
115
|
+
* @param request - the script, its `args`, the parent agent, and an
|
|
116
|
+
* optional cancel signal.
|
|
117
|
+
* @returns the live run; its `result` resolves when the script settles.
|
|
118
|
+
*/
|
|
119
|
+
abstract start(request: WorkflowStartRequest): WorkflowRun;
|
|
120
|
+
/**
|
|
121
|
+
* Emit a lifecycle event while containing and logging each listener failure.
|
|
122
|
+
* @param name - the `workflow/*` event to dispatch.
|
|
123
|
+
* @param args - the event's payload, matching its declared signature.
|
|
124
|
+
*/
|
|
125
|
+
protected emitWorkflowEvent(name: WorkflowEventName, ...args: unknown[]): void;
|
|
126
|
+
}
|
|
127
|
+
export default WorkflowEngine;
|
|
128
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Definition for the workflow capability seam. Service Providers execute orchestration scripts;
|
|
3
|
+
* observe-only lifecycle events never expose run control.
|
|
4
|
+
* @module @prettier-ai/dsh-workflow
|
|
5
|
+
*/
|
|
6
|
+
import { Service } from '@prettier-ai/cordis';
|
|
7
|
+
import { HarnessError } from '@prettier-ai/dsh-llm';
|
|
8
|
+
export { WorkflowRunId } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Typed error for workflow-seam failures. Extends {@link HarnessError}, so the
|
|
11
|
+
* `code` is machine-routable taxonomy. `fatal` drives the combinator
|
|
12
|
+
* discipline: `parallel()`/`pipeline()` re-throw a fatal error (a typo'd
|
|
13
|
+
* option or a tripped cap must kill the script loudly), and reserve the
|
|
14
|
+
* per-item `null` for child-run failures and ordinary in-stage script errors.
|
|
15
|
+
* Every {@link WorkflowErrorCode} is fatal; the flag exists so the
|
|
16
|
+
* distinction is explicit at every catch site rather than implied.
|
|
17
|
+
*/
|
|
18
|
+
export class WorkflowError extends HarnessError {
|
|
19
|
+
/** Whether combinators must propagate this error instead of nulling the item. */
|
|
20
|
+
fatal;
|
|
21
|
+
constructor(message, code, options) {
|
|
22
|
+
super(message, code, options);
|
|
23
|
+
this.name = 'WorkflowError';
|
|
24
|
+
this.fatal = options?.fatal ?? true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Whether combinators must re-throw `error` instead of mapping the item to `null`.
|
|
29
|
+
* @param error - any thrown value; fatality is host `instanceof` (unforgeable from a script realm).
|
|
30
|
+
* @returns true iff `error` is a {@link WorkflowError} whose `fatal` flag is set.
|
|
31
|
+
*/
|
|
32
|
+
export function isFatalWorkflowError(error) {
|
|
33
|
+
return error instanceof WorkflowError && error.fatal;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Workflow Service Definition contract. Invalid requests throw before publication; a live
|
|
37
|
+
* run is holder-owned, its result never rejects, cancellation and disposal are
|
|
38
|
+
* bounded, and disposal waits for child cleanup within that bound. Lifecycle
|
|
39
|
+
* listener failures are contained, and `workflow/end` fires exactly once as the
|
|
40
|
+
* result settles.
|
|
41
|
+
*/
|
|
42
|
+
export class WorkflowEngine extends Service {
|
|
43
|
+
constructor(ctx) {
|
|
44
|
+
super(ctx, 'workflowEngine');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Emit a lifecycle event while containing and logging each listener failure.
|
|
48
|
+
* @param name - the `workflow/*` event to dispatch.
|
|
49
|
+
* @param args - the event's payload, matching its declared signature.
|
|
50
|
+
*/
|
|
51
|
+
emitWorkflowEvent(name, ...args) {
|
|
52
|
+
for (const callback of this.ctx.events.dispatch('emit', [name, ...args])) {
|
|
53
|
+
try {
|
|
54
|
+
const returned = callback(...args);
|
|
55
|
+
void Promise.resolve(returned).catch((error) => {
|
|
56
|
+
this.ctx.logger.warn(`workflow: ${name} listener rejected: ${renderListenerError(error)}`);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
this.ctx.logger.warn(`workflow: ${name} listener threw: ${renderListenerError(error)}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Render any thrown value without violating listener containment.
|
|
67
|
+
* @param error - any thrown value.
|
|
68
|
+
* @returns `String(error)`, or a fixed label when even coercion throws.
|
|
69
|
+
*/
|
|
70
|
+
function renderListenerError(error) {
|
|
71
|
+
try {
|
|
72
|
+
return String(error);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// String coercion itself may throw.
|
|
76
|
+
return '[unrenderable thrown value]';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export default WorkflowEngine;
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Package-owned workflow lifecycle invariants. @module @prettier-ai/dsh-workflow/invariant */
|
|
2
|
+
import type { Context } from '@prettier-ai/cordis';
|
|
3
|
+
/** Cordis companion plugin name. */
|
|
4
|
+
export declare const name = "workflow-invariant";
|
|
5
|
+
/** Service required before the companion can reserve package ownership. */
|
|
6
|
+
export declare const inject: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Register the workflow invariant companion.
|
|
9
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
10
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
11
|
+
*/
|
|
12
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
13
|
+
//# sourceMappingURL=invariant.d.ts.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/** Package-owned workflow lifecycle invariants. @module @prettier-ai/dsh-workflow/invariant */
|
|
2
|
+
const PACKAGE_NAME = '@prettier-ai/dsh-workflow';
|
|
3
|
+
/** Cordis companion plugin name. */
|
|
4
|
+
export const name = 'workflow-invariant';
|
|
5
|
+
/** Service required before the companion can reserve package ownership. */
|
|
6
|
+
export const inject = ['invariants'];
|
|
7
|
+
/** Require every event for a run to retain its validated identity snapshot. */
|
|
8
|
+
function traceFor(traces, info, fail) {
|
|
9
|
+
const trace = traces.get(info.id);
|
|
10
|
+
if (trace === undefined)
|
|
11
|
+
fail(`workflow event has no matching workflow/start for run ${JSON.stringify(info.id)}`);
|
|
12
|
+
if (trace.meta !== JSON.stringify(info.meta)) {
|
|
13
|
+
fail(`workflow event meta diverges from workflow/start for run ${JSON.stringify(info.id)}`);
|
|
14
|
+
}
|
|
15
|
+
return trace;
|
|
16
|
+
}
|
|
17
|
+
/** Assert the immutable identity fields shared by an agent pair. */
|
|
18
|
+
function validateAgentEnd(start, end, fail) {
|
|
19
|
+
if (start.label !== end.label || start.phase !== end.phase || start.childId !== end.childId) {
|
|
20
|
+
fail(`workflow/agent-end identity diverges from workflow/agent-start for seq ${end.seq}`);
|
|
21
|
+
}
|
|
22
|
+
const outcome = end.outcome;
|
|
23
|
+
if (outcome !== 'completed' && outcome !== 'failed' && outcome !== 'cancelled') {
|
|
24
|
+
fail(`workflow/agent-end carries unknown outcome ${JSON.stringify(outcome)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Validate a terminal result against the accumulated run trace. */
|
|
28
|
+
function validateWorkflowEnd(trace, result, fail) {
|
|
29
|
+
if (trace.agents.size > 0)
|
|
30
|
+
fail(`workflow/end has ${trace.agents.size} agent call(s) without workflow/agent-end`);
|
|
31
|
+
if (!Number.isSafeInteger(result.agentsStarted) || result.agentsStarted < trace.starts) {
|
|
32
|
+
fail('workflow/end agentsStarted must be a safe integer covering every observed agent start');
|
|
33
|
+
}
|
|
34
|
+
if (result.stopReason === 'completed' ? result.error !== undefined : typeof result.error !== 'string') {
|
|
35
|
+
fail('workflow/end error must be absent exactly for completed runs');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Install workflow start/end and child-call pairing checks. */
|
|
39
|
+
const install = (ctx, fail) => {
|
|
40
|
+
const traces = new Map();
|
|
41
|
+
const stagedStarts = new WeakSet();
|
|
42
|
+
const stagedAgentStarts = new WeakSet();
|
|
43
|
+
const stagedAgentEnds = new WeakSet();
|
|
44
|
+
const stagedEnds = new WeakSet();
|
|
45
|
+
ctx.on('internal/dispatch', (_mode, eventName, args) => {
|
|
46
|
+
if (eventName === 'workflow/start') {
|
|
47
|
+
const info = args[0];
|
|
48
|
+
if (String(info.id).length === 0 || info.meta.name.length === 0 || info.meta.description.length === 0) {
|
|
49
|
+
fail('workflow/start id, meta.name, and meta.description must be non-empty');
|
|
50
|
+
}
|
|
51
|
+
if (traces.has(info.id))
|
|
52
|
+
fail(`workflow/start repeated run id ${JSON.stringify(info.id)}`);
|
|
53
|
+
stagedStarts.add(info);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!eventName.startsWith('workflow/'))
|
|
57
|
+
return;
|
|
58
|
+
const info = args[0];
|
|
59
|
+
const trace = traceFor(traces, info, fail);
|
|
60
|
+
if (eventName === 'workflow/agent-start') {
|
|
61
|
+
const agent = args[1];
|
|
62
|
+
if (!Number.isSafeInteger(agent.seq) || agent.seq < 1 || String(agent.childId).length === 0) {
|
|
63
|
+
fail('workflow/agent-start seq must be positive and childId must be non-empty');
|
|
64
|
+
}
|
|
65
|
+
if (trace.agents.has(agent.seq))
|
|
66
|
+
fail(`workflow/agent-start repeated seq ${agent.seq}`);
|
|
67
|
+
stagedAgentStarts.add(agent);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (eventName === 'workflow/agent-end') {
|
|
71
|
+
const agent = args[1];
|
|
72
|
+
const start = trace.agents.get(agent.seq);
|
|
73
|
+
if (start === undefined)
|
|
74
|
+
return fail(`workflow/agent-end has no matching start for seq ${agent.seq}`);
|
|
75
|
+
validateAgentEnd(start, agent, fail);
|
|
76
|
+
stagedAgentEnds.add(agent);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (eventName === 'workflow/end') {
|
|
80
|
+
const result = args[1];
|
|
81
|
+
validateWorkflowEnd(trace, result, fail);
|
|
82
|
+
stagedEnds.add(result);
|
|
83
|
+
}
|
|
84
|
+
}, { global: true });
|
|
85
|
+
ctx.on('workflow/start', (info) => {
|
|
86
|
+
/* v8 ignore next -- internal/dispatch stages the same run-info object */
|
|
87
|
+
if (!stagedStarts.delete(info))
|
|
88
|
+
return;
|
|
89
|
+
traces.set(info.id, { meta: JSON.stringify(info.meta), agents: new Map(), starts: 0 });
|
|
90
|
+
}, { global: true });
|
|
91
|
+
ctx.on('workflow/agent-start', (info, agent) => {
|
|
92
|
+
/* v8 ignore next -- internal/dispatch stages the same agent object */
|
|
93
|
+
if (!stagedAgentStarts.delete(agent))
|
|
94
|
+
return;
|
|
95
|
+
const trace = traceFor(traces, info, fail);
|
|
96
|
+
trace.agents.set(agent.seq, agent);
|
|
97
|
+
trace.starts += 1;
|
|
98
|
+
}, { global: true });
|
|
99
|
+
ctx.on('workflow/agent-end', (info, agent) => {
|
|
100
|
+
/* v8 ignore next -- internal/dispatch stages the same agent object */
|
|
101
|
+
if (!stagedAgentEnds.delete(agent))
|
|
102
|
+
return;
|
|
103
|
+
traceFor(traces, info, fail).agents.delete(agent.seq);
|
|
104
|
+
}, { global: true });
|
|
105
|
+
ctx.on('workflow/end', (info, result) => {
|
|
106
|
+
/* v8 ignore next -- internal/dispatch stages the same result object */
|
|
107
|
+
if (!stagedEnds.delete(result))
|
|
108
|
+
return;
|
|
109
|
+
traces.delete(info.id);
|
|
110
|
+
}, { global: true });
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Register the workflow invariant companion.
|
|
114
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
115
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
116
|
+
*/
|
|
117
|
+
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
118
|
+
//# sourceMappingURL=invariant.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-only workflow request and live-run handles. The browser-safe durable
|
|
3
|
+
* vocabulary remains in `./types` so Client programs never import Agent or
|
|
4
|
+
* host Cordis context declarations.
|
|
5
|
+
*
|
|
6
|
+
* @module @prettier-ai/dsh-workflow
|
|
7
|
+
*/
|
|
8
|
+
import type { Agent } from '@prettier-ai/dsh-agent';
|
|
9
|
+
import type { WorkflowMeta, WorkflowResult, WorkflowRunId } from './types.ts';
|
|
10
|
+
/**
|
|
11
|
+
* What a caller asks for when starting a workflow run. `meta` and `args` are
|
|
12
|
+
* plain JSON data by the seam contract. `parent` is required because every
|
|
13
|
+
* `agent()` spawned by the script is attributed to that live Agent.
|
|
14
|
+
*/
|
|
15
|
+
export interface WorkflowStartRequest {
|
|
16
|
+
/** The plain-JS script body (top-level await allowed; ends with `return <json-value>`). */
|
|
17
|
+
script: string;
|
|
18
|
+
/** The workflow's identity block, as plain JSON data (shape-validated by the engine). */
|
|
19
|
+
meta: WorkflowMeta;
|
|
20
|
+
/** Optional input exposed verbatim to the script as the `args` global. */
|
|
21
|
+
args?: unknown;
|
|
22
|
+
/** Optional engine-wide child-provider override for this run. */
|
|
23
|
+
subagentProvider?: string;
|
|
24
|
+
/** Optional per-run total-child ceiling. */
|
|
25
|
+
maxTotalAgents?: number;
|
|
26
|
+
/** The agent on whose behalf the run executes (parent of every child). */
|
|
27
|
+
parent: Agent;
|
|
28
|
+
/** Cancels the run when aborted. */
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Holder-owned live workflow. `result` never rejects; consumers may cancel
|
|
33
|
+
* and must call idempotent `dispose()` to await script and child quiescence.
|
|
34
|
+
*/
|
|
35
|
+
export interface WorkflowRun {
|
|
36
|
+
readonly id: WorkflowRunId;
|
|
37
|
+
/** The validated meta block available before the script body runs. */
|
|
38
|
+
readonly meta: WorkflowMeta;
|
|
39
|
+
readonly result: Promise<WorkflowResult>;
|
|
40
|
+
/** Cancel the run and its children. */
|
|
41
|
+
cancel(reason?: string): void;
|
|
42
|
+
/** Cancel if needed and await bounded settlement and cleanup. */
|
|
43
|
+
dispose(): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=runtime-types.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-only workflow request and live-run handles. The browser-safe durable
|
|
3
|
+
* vocabulary remains in `./types` so Client programs never import Agent or
|
|
4
|
+
* host Cordis context declarations.
|
|
5
|
+
*
|
|
6
|
+
* @module @prettier-ai/dsh-workflow
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=runtime-types.js.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow seam vocabulary: the request/run/result types a workflow engine
|
|
3
|
+
* consumes and produces, plus the fields in the `workflow/*` event payloads.
|
|
4
|
+
* Types only (plus the id-brand factory), per the package convention.
|
|
5
|
+
*
|
|
6
|
+
* @module @prettier-ai/dsh-workflow/types
|
|
7
|
+
*/
|
|
8
|
+
import type { Branded } from '@prettier-ai/dsh-brand';
|
|
9
|
+
import type { SessionId } from '@prettier-ai/dsh-session/types';
|
|
10
|
+
/** Identifies one workflow run. */
|
|
11
|
+
export type WorkflowRunId = Branded<'WorkflowRunId'>;
|
|
12
|
+
/**
|
|
13
|
+
* Brand a string as a {@link WorkflowRunId}.
|
|
14
|
+
* @param id - the raw id string (the engine mints UUIDs; tests may pass fixtures).
|
|
15
|
+
* @returns the same string, branded.
|
|
16
|
+
*/
|
|
17
|
+
export declare function WorkflowRunId(id: string): WorkflowRunId;
|
|
18
|
+
/**
|
|
19
|
+
* One phase declared in a script's `meta.phases` (progress vocabulary only —
|
|
20
|
+
* phases group agents in observers/UIs; they impose no execution structure).
|
|
21
|
+
*/
|
|
22
|
+
export interface WorkflowPhase {
|
|
23
|
+
/** The phase title; `phase()` calls match against it by exact string. */
|
|
24
|
+
title: string;
|
|
25
|
+
/** Optional one-line description of what the phase does. */
|
|
26
|
+
detail?: string;
|
|
27
|
+
/** Optional provider override this phase is expected to use (informational). */
|
|
28
|
+
provider?: string;
|
|
29
|
+
/** Optional model override this phase is expected to use (informational). */
|
|
30
|
+
model?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The script's identity block, provided as plain JSON data alongside the
|
|
34
|
+
* script body (the model-facing tool carries it as its `meta` parameter) and
|
|
35
|
+
* validated by the engine before the body runs. `name`/`description` are
|
|
36
|
+
* required; the rest is optional annotation. The field vocabulary matches the
|
|
37
|
+
* Claude Code dynamic-workflows meta block.
|
|
38
|
+
*/
|
|
39
|
+
export interface WorkflowMeta {
|
|
40
|
+
/** Short kebab-case workflow name (display + persistence key). */
|
|
41
|
+
name: string;
|
|
42
|
+
/** One-line description of what the workflow does. */
|
|
43
|
+
description: string;
|
|
44
|
+
/** Optional guidance on when this workflow applies (shown in listings). */
|
|
45
|
+
whenToUse?: string;
|
|
46
|
+
/** Optional phase declarations matched by `phase()` calls. */
|
|
47
|
+
phases?: WorkflowPhase[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Why a run settled. CLOSED union (engine-owned, consumers may exhaust):
|
|
51
|
+
* `completed` = the script ran to its final `return`; `cancelled` = the run
|
|
52
|
+
* was cancelled (caller `cancel()`/signal); `error` = the script threw, a
|
|
53
|
+
* fatal `WorkflowError` propagated, or the result failed materialization.
|
|
54
|
+
*/
|
|
55
|
+
export type WorkflowStopReason = 'completed' | 'cancelled' | 'error';
|
|
56
|
+
/**
|
|
57
|
+
* The outcome resolved by a live workflow run. `value` is
|
|
58
|
+
* the script's materialized return value (plain host-realm JSON data; `null`
|
|
59
|
+
* when the script returned `undefined`) — meaningful only for `completed`.
|
|
60
|
+
* A non-`completed` reason carries the failure in `error`; the consumer maps
|
|
61
|
+
* it to an `isError` tool result rather than reporting partial output.
|
|
62
|
+
*/
|
|
63
|
+
export interface WorkflowResult {
|
|
64
|
+
/** The script's return value (host JSON data; `null` for no return). */
|
|
65
|
+
value: unknown;
|
|
66
|
+
/** Why the run settled. */
|
|
67
|
+
stopReason: WorkflowStopReason;
|
|
68
|
+
/** The failure message (present iff `stopReason` is not `completed`). */
|
|
69
|
+
error?: string;
|
|
70
|
+
/**
|
|
71
|
+
* How many `agent()` calls the run accepted over its whole lifetime. On a
|
|
72
|
+
* graceful settlement this is the script-side count (calls still queued for
|
|
73
|
+
* a concurrency slot included); on a termination path (grace force-settle,
|
|
74
|
+
* worker death) it degrades to the host-observed count — calls queued
|
|
75
|
+
* inside a terminated script are unknowable then.
|
|
76
|
+
*/
|
|
77
|
+
agentsStarted: number;
|
|
78
|
+
}
|
|
79
|
+
/** Identifying detail for a run, carried by every `workflow/*` event as borrowed immutable data, never the live run. */
|
|
80
|
+
export interface WorkflowRunInfo {
|
|
81
|
+
/** The run's id. */
|
|
82
|
+
id: WorkflowRunId;
|
|
83
|
+
/** The run's validated meta block. */
|
|
84
|
+
meta: WorkflowMeta;
|
|
85
|
+
}
|
|
86
|
+
/** One `agent()` call's identity within a run (the `workflow/agent-start` payload). */
|
|
87
|
+
export interface WorkflowAgentInfo {
|
|
88
|
+
/** 1-based sequence number of this `agent()` call within the run. */
|
|
89
|
+
seq: number;
|
|
90
|
+
/** The display label (the `label` option, or a prompt snippet). */
|
|
91
|
+
label: string;
|
|
92
|
+
/** The phase this agent belongs to (the `phase` option, else the current `phase()` title). */
|
|
93
|
+
phase?: string;
|
|
94
|
+
/** The child agent's id on the subagent seam. */
|
|
95
|
+
childId: SessionId;
|
|
96
|
+
}
|
|
97
|
+
/** How one `agent()` call settled: clean result, child failure (script sees `null`), or run cancellation. */
|
|
98
|
+
export type WorkflowAgentOutcome = 'completed' | 'failed' | 'cancelled';
|
|
99
|
+
/** One `agent()` call's settlement (the `workflow/agent-end` payload). */
|
|
100
|
+
export interface WorkflowAgentEndInfo extends WorkflowAgentInfo {
|
|
101
|
+
/** How the call settled. */
|
|
102
|
+
outcome: WorkflowAgentOutcome;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A settled run's outcome as event data (the `workflow/end` payload): the
|
|
106
|
+
* {@link WorkflowResult} minus `value` (a listener observing outcomes must not
|
|
107
|
+
* receive a mutable alias of the caller's result value; a consumer that needs
|
|
108
|
+
* the value holds the run and awaits `result`).
|
|
109
|
+
*/
|
|
110
|
+
export interface WorkflowResultInfo {
|
|
111
|
+
/** Why the run settled. */
|
|
112
|
+
stopReason: WorkflowStopReason;
|
|
113
|
+
/** The failure message (present iff `stopReason` is not `completed`). */
|
|
114
|
+
error?: string;
|
|
115
|
+
/** How many `agent()` calls the run accepted (see {@link WorkflowResult.agentsStarted}). */
|
|
116
|
+
agentsStarted: number;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow seam vocabulary: the request/run/result types a workflow engine
|
|
3
|
+
* consumes and produces, plus the fields in the `workflow/*` event payloads.
|
|
4
|
+
* Types only (plus the id-brand factory), per the package convention.
|
|
5
|
+
*
|
|
6
|
+
* @module @prettier-ai/dsh-workflow/types
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Brand a string as a {@link WorkflowRunId}.
|
|
10
|
+
* @param id - the raw id string (the engine mints UUIDs; tests may pass fixtures).
|
|
11
|
+
* @returns the same string, branded.
|
|
12
|
+
*/
|
|
13
|
+
export function WorkflowRunId(id) {
|
|
14
|
+
return id;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prettier-ai/dsh-workflow",
|
|
3
|
+
"description": "Workflow capability seam: ctx.workflowEngine service, run vocabulary, and workflow/* events",
|
|
4
|
+
"version": "0.1.2-alpha.1",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/workflow/workflow"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./types": {
|
|
26
|
+
"types": "./lib/types/types.d.ts",
|
|
27
|
+
"default": "./lib/types/types.js"
|
|
28
|
+
},
|
|
29
|
+
"./src/*": "./src/*",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"lib/index.js",
|
|
34
|
+
"lib/invariant.js",
|
|
35
|
+
"lib/types/**/*.js",
|
|
36
|
+
"lib/types/**/*.d.ts"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@prettier-ai/dsh-agent": "^0.1.2-alpha.1",
|
|
41
|
+
"@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
|
|
42
|
+
"@prettier-ai/dsh-brand": "^0.1.2-alpha.1",
|
|
43
|
+
"@prettier-ai/dsh-llm": "^0.1.2-alpha.1",
|
|
44
|
+
"@prettier-ai/cordis": "^4.0.1",
|
|
45
|
+
"@prettier-ai/dsh-session": "^0.1.2-alpha.1"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@prettier-ai/dsh-agent": "^0.1.2-alpha.1",
|
|
49
|
+
"@prettier-ai/dsh-brand": "^0.1.2-alpha.1",
|
|
50
|
+
"@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
|
|
51
|
+
"@prettier-ai/cordis": "^4.0.1",
|
|
52
|
+
"@prettier-ai/dsh-session": "^0.1.2-alpha.1",
|
|
53
|
+
"@prettier-ai/dsh-llm": "^0.1.2-alpha.1"
|
|
54
|
+
}
|
|
55
|
+
}
|