workflow: rename dsh-workflow-vm to dsh-workflow-workerthread

Pure mechanical rename now that the package's internals are the
worker-thread engine: directory, package name, spec/e2e filenames,
module tags and logger prefixes, tsconfig/knip/run-gates/AGENTS.md
references, example cordis.yml plugin ids, doc links; catalogs
regenerated and the lockfile refreshed.
This commit is contained in:
imccyu
2026-07-09 19:33:08 +08:00
parent d5c65e2b4c
commit 0d11a3afc7
43 changed files with 59 additions and 59 deletions
+2 -2
View File
@@ -18,7 +18,7 @@ packages/ Harness packages at packages/<group>/<pkg>/, all named @deepseek-ai
web/ web seam + search/fetch providers + model-facing web tools
compact/ compaction seam + basic backend
subagent/ subagent seam + spawn/fork/ACP backends + delegation tool
workflow/ workflow seam + worker-thread script engine + the workflow tool
workflow/ workflow seam + worker-thread engine + the workflow tool
todo/ the todo_write tool
hooks/ Claude Code / Codex hook bridges + shared wire-protocol library
session-persistence/ persistence seam + JSONL/SQLite backends
@@ -70,7 +70,7 @@ printf '%s\n' "$out" | grep -q '\[tool call\] echo({"text":"ci smoke"})'
printf '%s\n' "$out" | grep -q '\[tool result\] ECHO: CI SMOKE'
ls .sessions/_no-cwd/main-session-*.jsonl >/dev/null
rm -rf .sessions
pnpm exec vitest run --config vitest.e2e.config.ts packages/ui/stdio-agent/tests/built-bin.e2e.ts packages/ui/acp-agent/tests/built-bin.e2e.ts packages/workflow/workflow-vm/tests/built-worker.e2e.ts
pnpm exec vitest run --config vitest.e2e.config.ts packages/ui/stdio-agent/tests/built-bin.e2e.ts packages/ui/acp-agent/tests/built-bin.e2e.ts packages/workflow/workflow-workerthread/tests/built-worker.e2e.ts
```
`test:coverage`, not `test`, is the gating run ([why](docs/testing.md)); a sign-off counts only for commands actually run.
+3 -3
View File
@@ -62,7 +62,7 @@ flowchart LR
pkg_web_fetch_local["web-fetch-local"]
pkg_workflow["workflow"]
svc_workflows["ctx.workflows<br/>Workflow script engine"]
pkg_workflow_vm["workflow-vm"]
pkg_workflow_workerthread["workflow-workerthread"]
pkg_tool_workflow["tool-workflow"]
pkg_agent --> svc_agents
pkg_agent_loop --> svc_agentLoop
@@ -93,7 +93,7 @@ flowchart LR
pkg_web_search_exa --> svc_web
pkg_web_search_perplexity --> svc_web
pkg_workflow --> svc_workflows
pkg_workflow_vm --> svc_workflows
pkg_workflow_workerthread --> svc_workflows
svc_agentLoop --> pkg_agent_core
svc_agents --> pkg_acp
svc_agents --> pkg_agent_loop
@@ -145,6 +145,6 @@ flowchart LR
| `ctx.compact` | `seam` | [`compact`](../packages/compact/compact) | [`compact-basic`](../packages/compact/compact-basic) | [`compact-basic`](../packages/compact/compact-basic) | - | The basic backend currently consumes the pre-step event directly; a model-facing compact tool remains deferred. |
| `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn`](../packages/subagent/subagent-spawn), [`subagent-fork`](../packages/subagent/subagent-fork), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-mock`](../packages/support/subagent-mock) | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
| `ctx.web` | `seam` | [`web`](../packages/web/web) | [`web-search-exa`](../packages/web/web-search-exa), [`web-search-perplexity`](../packages/web/web-search-perplexity), [`web-search-deepseek`](../packages/web/web-search-deepseek), [`web-fetch-local`](../packages/web/web-fetch-local) | [`tool-web`](../packages/web/tool-web) | - | Search and fetch providers register into one ctx.web seam; tool-web owns the stable model-facing names. |
| `ctx.workflows` | `seam` | [`workflow`](../packages/workflow/workflow) | [`workflow-vm`](../packages/workflow/workflow-vm) | [`tool-workflow`](../packages/workflow/tool-workflow) | - | One engine per context (bash shape, no named-provider registry); the worker-thread engine fans agent() calls out through ctx.subagents. |
| `ctx.workflows` | `seam` | [`workflow`](../packages/workflow/workflow) | [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | [`tool-workflow`](../packages/workflow/tool-workflow) | - | One engine per context (bash shape, no named-provider registry); the worker-thread engine fans agent() calls out through ctx.subagents. |
Maintenance mode: hybrid: services are discovered from Cordis declarations; interface/implementation/consumer roles are classified in `scripts/gen-doc-graphs.ts` with a completeness guard.
+2 -2
View File
@@ -800,7 +800,7 @@ export interface Config {
Source: [`packages/web/web-search-perplexity/src/index.ts:33`](../packages/web/web-search-perplexity/src/index.ts)
## `@deepseek-ai/dsh-workflow-vm`
## `@deepseek-ai/dsh-workflow-workerthread`
Requires: `subagents`
@@ -826,7 +826,7 @@ export interface Config {
}
```
Source: [`packages/workflow/workflow-vm/src/index.ts:69`](../packages/workflow/workflow-vm/src/index.ts)
Source: [`packages/workflow/workflow-workerthread/src/index.ts:69`](../packages/workflow/workflow-workerthread/src/index.ts)
## Loadable plugins with no config
+1 -1
View File
@@ -2,7 +2,7 @@
The workflow seam — an agent running a model-written orchestration SCRIPT that fans out subagents. Like [subagent](subagent.md) it is **one optional capability**, not part of the agent-loop spine, so its vocabulary lives here rather than in [core.md](core.md). Unlike the subagent registry it takes the bash shape: ONE engine implementation per context provides `ctx.workflows`; there is no named-provider registry (a second engine is a plugin swap, not a co-resident).
Interface: [dsh-workflow](../../packages/workflow/workflow) (`ctx.workflows` + the vocabulary below). The implementation is [dsh-workflow-vm](../../packages/workflow/workflow-vm) (a `node:worker_threads` engine — one worker per run, the script's vm context inside it); the model-facing consumer is [dsh-tool-workflow](../../packages/workflow/tool-workflow). The proposal and rationale: [the dynamic-workflows RFC](../rfc/implemented/feature/2026-07-05-dynamic-workflows.md).
Interface: [dsh-workflow](../../packages/workflow/workflow) (`ctx.workflows` + the vocabulary below). The implementation is [dsh-workflow-workerthread](../../packages/workflow/workflow-workerthread) (a `node:worker_threads` engine — one worker per run, the script's vm context inside it); the model-facing consumer is [dsh-tool-workflow](../../packages/workflow/tool-workflow). The proposal and rationale: [the dynamic-workflows RFC](../rfc/implemented/feature/2026-07-05-dynamic-workflows.md).
Source: [`packages/workflow/workflow/src/types.ts`](../../packages/workflow/workflow/src/types.ts)
+8 -8
View File
@@ -81,7 +81,7 @@ flowchart TD
subgraph group_workflow["packages/workflow"]
pkg_tool_workflow["tool-workflow"]
pkg_workflow["workflow"]
pkg_workflow_vm["workflow-vm"]
pkg_workflow_workerthread["workflow-workerthread"]
end
pkg_llm --> pkg_brand
pkg_bash --> pkg_brand
@@ -199,12 +199,12 @@ flowchart TD
pkg_subagent_mock --> pkg_agent
pkg_subagent_mock --> pkg_llm
pkg_subagent_mock --> pkg_subagent
pkg_workflow_vm --> pkg_agent
pkg_workflow_vm --> pkg_brand
pkg_workflow_vm --> pkg_llm
pkg_workflow_vm --> pkg_subagent
pkg_workflow_vm --> pkg_tools
pkg_workflow_vm --> pkg_workflow
pkg_workflow_workerthread --> pkg_agent
pkg_workflow_workerthread --> pkg_brand
pkg_workflow_workerthread --> pkg_llm
pkg_workflow_workerthread --> pkg_subagent
pkg_workflow_workerthread --> pkg_tools
pkg_workflow_workerthread --> pkg_workflow
pkg_subagent_fork --> pkg_agent
pkg_subagent_fork --> pkg_session
pkg_subagent_fork --> pkg_subagent
@@ -268,7 +268,7 @@ flowchart TD
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`hooks-claude`](../packages/hooks/hooks-claude) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`subagent-mock`](../packages/support/subagent-mock) | `support` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent) |
| [`workflow-vm`](../packages/workflow/workflow-vm) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-fork`](../packages/subagent/subagent-fork) | `subagent` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`subagent-spawn`](../packages/subagent/subagent-spawn) | `subagent` | [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`acp-agent`](../packages/ui/acp-agent) | `ui` | [`acp`](../packages/ui/acp), [`agent-core`](../packages/core/agent-core), [`app-boot`](../packages/ui/app-boot), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl) |
@@ -20,7 +20,7 @@ One deliberate strictness DIVERGENCE from CC: hook misuse — unknown or deferre
`ctx.workflows` is an abstract `WorkflowService` in the bash shape — one engine per context, no named-provider registry (engines are deployment swaps, not co-residents). `start(request)` throws synchronously for a script that cannot begin; a returned `WorkflowRun`'s `result` NEVER rejects (failures resolve as `stopReason: 'error' | 'cancelled'`). The `workflow/*` events are observe-only emits carrying DATA SNAPSHOTS (id + meta; `workflow/end` omits the result value), per-listener contained, mirroring `subagent/start`/`subagent/end` — control stays with the run's holder. Vocabulary details: [core-data-structures/workflow.md](../../../core-data-structures/workflow.md).
### The engine (dsh-workflow-vm): one worker thread per run
### The engine (dsh-workflow-workerthread): one worker thread per run
**Trust premise (governs every engine decision below)**: workflow scripts are MODEL-WRITTEN — the same trust level as the model's existing bash access — so the engine defends against BUGGY scripts, never hostile ones. In scope: `result` never rejects, no unhandled rejections from dropped hook promises, loud rejection of values JSON cannot carry, fatal-vs-null hook discipline, cancellation that always frees the caller. Out of scope, deliberately: adversarial values (throwing/spinning accessors, proxies with hostile traps, prototype forgery, `prepareStackTrace` hijack) AND Node-API escape from the script's context — the vm context shares object machinery with its surrounding realm, so a script can reach the `Function` constructor (`globalThis.constructor.constructor`) and from it `process` and every Node builtin; the absent globals are API surface, not containment, and a worker thread is NOT a security boundary (an escapee holds process-wide privileges — Node's permission model is per-process). Worker-side code MAY run script code while reading script values, and that is accepted: a synchronous spin costs the script its OWN thread (terminated at the post-cancel grace), never the host loop, so containing error VALUES would be cost without a threat model. Genuine sandboxing (isolated-vm, a separate process) remains an engine swap behind the seam, not incremental defenses here.
+3 -3
View File
@@ -31,8 +31,8 @@ flowchart LR
cfg --> plugin_acp_tool_subagent
plugin_acp_tool_subagent_fork["tool-subagent-fork<br/>@deepseek-ai/dsh-tool-subagent"]
cfg --> plugin_acp_tool_subagent_fork
plugin_acp_workflow_vm["workflow-vm<br/>@deepseek-ai/dsh-workflow-vm"]
cfg --> plugin_acp_workflow_vm
plugin_acp_workflow_workerthread["workflow-workerthread<br/>@deepseek-ai/dsh-workflow-workerthread"]
cfg --> plugin_acp_workflow_workerthread
plugin_acp_tool_workflow["tool-workflow<br/>@deepseek-ai/dsh-tool-workflow"]
cfg --> plugin_acp_tool_workflow
plugin_acp_tool_todo["tool-todo<br/>@deepseek-ai/dsh-tool-todo"]
@@ -59,7 +59,7 @@ flowchart LR
| `subagent-fork` | `@deepseek-ai/dsh-subagent-fork` |
| `tool-subagent` | `@deepseek-ai/dsh-tool-subagent` |
| `tool-subagent-fork` | `@deepseek-ai/dsh-tool-subagent` |
| `workflow-vm` | `@deepseek-ai/dsh-workflow-vm` |
| `workflow-workerthread` | `@deepseek-ai/dsh-workflow-workerthread` |
| `tool-workflow` | `@deepseek-ai/dsh-tool-workflow` |
| `tool-todo` | `@deepseek-ai/dsh-tool-todo` |
| `fs-local` | `@deepseek-ai/dsh-fs-local` |
+2 -2
View File
@@ -86,8 +86,8 @@
# subagent backend above, plus the model-facing `workflow` tool. The model
# writes a JavaScript orchestration script (meta + body); the engine runs it
# in its own worker thread and fans agent() calls out as spawn children.
- id: workflow-vm
name: '@deepseek-ai/dsh-workflow-vm'
- id: workflow-workerthread
name: '@deepseek-ai/dsh-workflow-workerthread'
config:
provider: spawn
+1 -1
View File
@@ -108,7 +108,7 @@ const SCENARIOS: Scenario[] = [
{ name: 'subagent-fork', hasModelTurn: true, recorded: true, childSessions: 1 },
{ name: 'subagent-mixed', hasModelTurn: true, recorded: true, childSessions: 2 },
// The workflow tool: the model writes a one-child orchestration script; the
// child runs as a spawn subagent inside the vm engine (its session is the
// child runs as a spawn subagent under the worker-thread engine (its session is the
// child fixture), and the tool result carries the script's return value.
{ name: 'workflow-run', hasModelTurn: true, recorded: true, childSessions: 1 },
// Hook matrix — one scenario per hook point × its headline Decision outcome,
+3 -3
View File
@@ -35,8 +35,8 @@ flowchart LR
cfg --> plugin_coding_tool_subagent
plugin_coding_tool_subagent_fork["tool-subagent-fork<br/>@deepseek-ai/dsh-tool-subagent"]
cfg --> plugin_coding_tool_subagent_fork
plugin_coding_workflow_vm["workflow-vm<br/>@deepseek-ai/dsh-workflow-vm"]
cfg --> plugin_coding_workflow_vm
plugin_coding_workflow_workerthread["workflow-workerthread<br/>@deepseek-ai/dsh-workflow-workerthread"]
cfg --> plugin_coding_workflow_workerthread
plugin_coding_tool_workflow["tool-workflow<br/>@deepseek-ai/dsh-tool-workflow"]
cfg --> plugin_coding_tool_workflow
plugin_coding_tool_todo["tool-todo<br/>@deepseek-ai/dsh-tool-todo"]
@@ -61,7 +61,7 @@ flowchart LR
| `subagent-fork` | `@deepseek-ai/dsh-subagent-fork` |
| `tool-subagent` | `@deepseek-ai/dsh-tool-subagent` |
| `tool-subagent-fork` | `@deepseek-ai/dsh-tool-subagent` |
| `workflow-vm` | `@deepseek-ai/dsh-workflow-vm` |
| `workflow-workerthread` | `@deepseek-ai/dsh-workflow-workerthread` |
| `tool-workflow` | `@deepseek-ai/dsh-tool-workflow` |
| `tool-todo` | `@deepseek-ai/dsh-tool-todo` |
| `fs-local` | `@deepseek-ai/dsh-fs-local` |
+2 -2
View File
@@ -107,8 +107,8 @@
# subagent backend above, plus the model-facing `workflow` tool. The model
# writes a JavaScript orchestration script (meta + body); the engine runs it
# in its own worker thread and fans agent() calls out as spawn children.
- id: workflow-vm
name: '@deepseek-ai/dsh-workflow-vm'
- id: workflow-workerthread
name: '@deepseek-ai/dsh-workflow-workerthread'
config:
provider: spawn
+1 -1
View File
@@ -41,7 +41,7 @@
"entry": ["tests/**/*.spec.ts", "tests/**/*.e2e.ts"],
"project": ["src/**/*.ts", "tests/**/*.ts"]
},
"packages/workflow/workflow-vm": {
"packages/workflow/workflow-workerthread": {
"entry": ["tests/**/*.spec.ts", "tests/**/*.e2e.ts"],
"project": ["src/**/*.ts", "tests/**/*.ts"]
},
+1 -1
View File
@@ -5,7 +5,7 @@ The workflow seam: a model-written JavaScript orchestration script that fans out
| Package | Role | ctx key |
|---|---|---|
| `workflow/` | Abstract workflow seam: service base class + run vocabulary + `workflow/*` events | `ctx.workflows` |
| `workflow-vm/` | `node:worker_threads` engine: one worker per run; the script's vm context lives inside the worker, `agent()` bridges to `ctx.subagents` over the message port | (provides `ctx.workflows`) |
| `workflow-workerthread/` | `node:worker_threads` engine: one worker per run; the script's vm context lives inside the worker, `agent()` bridges to `ctx.subagents` over the message port | (provides `ctx.workflows`) |
| `tool-workflow/` | Model-facing `workflow` tool over `ctx.workflows` | (registers on `ctx.tools`) |
The interface lives at `workflow/workflow/`. The engine's `agent()` hook rides the [subagent seam](../subagent/README.md) (any registered provider; the shipped examples use `spawn`), and `agent({ schema })` rides the structured-output support the in-process backends implement. The worker thread isolates the SCRIPT — the host never blocks on it, and a cancelled run's post-grace termination is real — but it is NOT a security boundary; an isolated-vm/separate-process engine (actual sandboxing) swaps in behind the same interface if that ever matters.
+1 -1
View File
@@ -40,7 +40,7 @@
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-workflow": "workspace:^",
"@deepseek-ai/dsh-workflow-vm": "workspace:^",
"@deepseek-ai/dsh-workflow-workerthread": "workspace:^",
"cordis": "^4.0.0-rc.6"
}
}
@@ -10,7 +10,7 @@ import { WorkflowRunId, WorkflowService } from '@deepseek-ai/dsh-workflow'
import type { WorkflowResult, WorkflowRun, WorkflowStartRequest } from '@deepseek-ai/dsh-workflow'
import { CallId } from '@deepseek-ai/dsh-llm'
import SubagentService from '@deepseek-ai/dsh-subagent'
import WorkerWorkflowEngine from '@deepseek-ai/dsh-workflow-vm'
import WorkerWorkflowEngine from '@deepseek-ai/dsh-workflow-workerthread'
import * as toolWorkflow from '../src/index.ts'
/** A controllable engine standing in behind ctx.workflows (the tool's only seam). */
@@ -1,4 +1,4 @@
# @deepseek-ai/dsh-workflow-vm
# @deepseek-ai/dsh-workflow-workerthread
The [`WorkflowService`](../workflow/README.md) implementation, on **`node:worker_threads`**: each run gets its OWN worker thread (one run = one worker, no pooling — a run is heavyweight, so the ~tens-of-ms thread spin-up is noise), the script executes in a vm context INSIDE that worker with the workflow hooks injected, and every `agent()` call bridges back over the message port to [`ctx.subagents`](../../subagent/README.md) on the host. Child agents are I/O-bound LLM loops and stay on the host event loop; the thread isolates the SCRIPT, the only part that can spin synchronously.
@@ -1,5 +1,5 @@
{
"name": "@deepseek-ai/dsh-workflow-vm",
"name": "@deepseek-ai/dsh-workflow-workerthread",
"description": "worker-thread workflow engine: executes model-written orchestration scripts off the host event loop, bridging agent() calls back to ctx.subagents",
"version": "0.0.1",
"private": true,
@@ -22,7 +22,7 @@
* still queued worker-side for a concurrency slot are unknowable then; the
* worker's own count rides the result message on every graceful path.
*
* @module @deepseek-ai/dsh-workflow-vm/host
* @module @deepseek-ai/dsh-workflow-workerthread/host
*/
import { fileURLToPath } from 'node:url'
@@ -190,7 +190,7 @@ export class WorkerRun implements WorkflowRun {
// data, so serialization cannot fail); there is nothing left to
// deliver to — log and move on.
/* v8 ignore next -- postMessage teardown race (a throw between exit and its event): not constructible in-process */
this.ctx.logger.warn(`workflow-vm: postMessage failed: ${renderThrown(error)}`)
this.ctx.logger.warn(`workflow-workerthread: postMessage failed: ${renderThrown(error)}`)
}
}
@@ -293,7 +293,7 @@ export class WorkerRun implements WorkflowRun {
// The subagent seam's dispose() is not supposed to reject; a backend
// that does anyway must not wedge the script's finally (which awaits
// the ack) — ack and move on.
this.ctx.logger.warn(`workflow-vm: child dispose failed: ${renderThrown(error)}`)
this.ctx.logger.warn(`workflow-workerthread: child dispose failed: ${renderThrown(error)}`)
this.finishChild(callId)
this.post(HostToWorkerType.ChildDisposed, { callId })
},
@@ -322,7 +322,7 @@ export class WorkerRun implements WorkflowRun {
void run.dispose().then(
() => { this.finishChild(callId) },
(error: unknown) => {
this.ctx.logger.warn(`workflow-vm: child dispose failed during reap: ${renderThrown(error)}`)
this.ctx.logger.warn(`workflow-workerthread: child dispose failed during reap: ${renderThrown(error)}`)
this.finishChild(callId)
},
)
@@ -36,7 +36,7 @@
* Plugin export shape: a default-exported {@link WorkflowService} subclass
* (the class-based service form, like `dsh-bash-local`).
*
* @module @deepseek-ai/dsh-workflow-vm
* @module @deepseek-ai/dsh-workflow-workerthread
*/
import { randomUUID } from 'node:crypto'
@@ -14,7 +14,7 @@
* result not the source is the contract: it must materialize to plain
* JSON data and pass the shape validation).
*
* @module @deepseek-ai/dsh-workflow-vm/meta
* @module @deepseek-ai/dsh-workflow-workerthread/meta
*/
import * as vm from 'node:vm'
@@ -13,7 +13,7 @@
* `post(type, payload)` whose payload parameter is looked up from the map,
* so a tag/payload mismatch is a compile error at the call site.
*
* @module @deepseek-ai/dsh-workflow-vm/protocol
* @module @deepseek-ai/dsh-workflow-workerthread/protocol
*/
import type { WorkflowAgentEndInfo, WorkflowAgentInfo, WorkflowResult } from '@deepseek-ai/dsh-workflow'
@@ -27,7 +27,7 @@
* thrown by a hook is built OUTSIDE the script's vm context, so an in-script
* `instanceof Error` check is false; read `name`/`code`/`message` instead.
*
* @module @deepseek-ai/dsh-workflow-vm/realm
* @module @deepseek-ai/dsh-workflow-workerthread/realm
*/
/** Thrown by {@link materializeFromRealm}; the caller wraps it into the right `WorkflowError` code. */
@@ -33,7 +33,7 @@
* the settles-within-grace guarantee by force-settling `cancelled` and
* terminating the worker the real kill an in-process engine could not have.
*
* @module @deepseek-ai/dsh-workflow-vm/runtime
* @module @deepseek-ai/dsh-workflow-workerthread/runtime
*/
import * as vm from 'node:vm'
@@ -14,7 +14,7 @@
* A `cancel` arriving instead of `go` still releases the gate: `drive()`
* sees the cancelled state and settles without running the body.
*
* @module @deepseek-ai/dsh-workflow-vm/session
* @module @deepseek-ai/dsh-workflow-workerthread/session
*/
import type { MessagePort } from 'node:worker_threads'
@@ -6,7 +6,7 @@
* JSON data by construction, so the structured-clone hop never meets a value
* it cannot carry. Types only, per the package convention.
*
* @module @deepseek-ai/dsh-workflow-vm/types
* @module @deepseek-ai/dsh-workflow-workerthread/types
*/
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
@@ -6,7 +6,7 @@
* coverage); loading this module on the main thread throws via
* `requireParentPort`, which is how the suite covers the file itself.
*
* @module @deepseek-ai/dsh-workflow-vm/worker
* @module @deepseek-ai/dsh-workflow-workerthread/worker
*/
import { parentPort, workerData } from 'node:worker_threads'
@@ -28,7 +28,7 @@ describe.skipIf(!existsSync(builtIndex) || !existsSync(builtWorker))('built work
await writeFile(driver, `
import { Context } from 'cordis'
import SubagentService from '@deepseek-ai/dsh-subagent'
import WorkerWorkflowEngine from '@deepseek-ai/dsh-workflow-vm'
import WorkerWorkflowEngine from '@deepseek-ai/dsh-workflow-workerthread'
const ctx = new Context()
await ctx.plugin(SubagentService)
@@ -41,7 +41,7 @@ async function setup(script: Script) {
return { ctx, parent, adapter }
}
describe('dsh-workflow-vm over the real in-process stack', () => {
describe('dsh-workflow-workerthread over the real in-process stack', () => {
it('runs a two-stage workflow: a plain child, then a schema child through the structured runtime', async () => {
const { ctx, parent } = await setup([
textResponse('the file list is a.ts'),
@@ -119,7 +119,7 @@ async function run(ctx: Context, parent: Agent, source: string, args?: unknown):
}
}
describe('dsh-workflow-vm', () => {
describe('dsh-workflow-workerthread', () => {
describe('script execution over a real worker thread', () => {
it('runs a script end-to-end: agent() text results, phases, log, args, return value, events', async () => {
const { ctx, parent, provider } = await setup({ reply: (_request, index) => text(`answer-${index}`) })
+1 -1
View File
@@ -1,6 +1,6 @@
# @deepseek-ai/dsh-workflow
The **workflow seam** (`ctx.workflows`): an abstract service defining WHAT a workflow engine does — execute a model-written orchestration script that fans out subagents — without saying HOW. The bash-shaped third of the [workflow family](../README.md): implementations subclass `WorkflowService` and register as the `workflows` service (one per context); [`dsh-workflow-vm`](../workflow-vm/README.md) (one worker thread per run) is the implementation, and [`dsh-tool-workflow`](../tool-workflow/README.md) is the model-facing consumer.
The **workflow seam** (`ctx.workflows`): an abstract service defining WHAT a workflow engine does — execute a model-written orchestration script that fans out subagents — without saying HOW. The bash-shaped third of the [workflow family](../README.md): implementations subclass `WorkflowService` and register as the `workflows` service (one per context); [`dsh-workflow-workerthread`](../workflow-workerthread/README.md) (one worker thread per run) is the implementation, and [`dsh-tool-workflow`](../tool-workflow/README.md) is the model-facing consumer.
## Service: `WorkflowService` (abstract)
+1 -1
View File
@@ -4,7 +4,7 @@
* that fans out subagents — without saying HOW. Implementations subclass
* {@link WorkflowService} and register as the `workflows` service (one
* implementation per context, cordis' standard duplicate-service behavior);
* the implementation is `@deepseek-ai/dsh-workflow-vm`, which runs each
* the implementation is `@deepseek-ai/dsh-workflow-workerthread`, which runs each
* script in its own worker thread. Hardened engines (an isolated-vm or
* separate-process sandbox) swap in without touching the model-facing tool
* that consumes them (`@deepseek-ai/dsh-tool-workflow`).
+3 -3
View File
@@ -1092,9 +1092,9 @@ importers:
'@deepseek-ai/dsh-workflow':
specifier: workspace:^
version: link:../workflow
'@deepseek-ai/dsh-workflow-vm':
'@deepseek-ai/dsh-workflow-workerthread':
specifier: workspace:^
version: link:../workflow-vm
version: link:../workflow-workerthread
cordis:
specifier: ^4.0.0-rc.6
version: 4.0.0-rc.6(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.4)
@@ -1117,7 +1117,7 @@ importers:
specifier: ^4.0.0-rc.6
version: 4.0.0-rc.6(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.4)
packages/workflow/workflow-vm:
packages/workflow/workflow-workerthread:
dependencies:
schemastery:
specifier: ^3.18.0
+1 -1
View File
@@ -191,7 +191,7 @@ const SERVICE_ROLES: ServiceRole[] = [
pkg: 'workflow',
title: 'Workflow script engine',
mode: 'seam',
implementations: ['workflow-vm'],
implementations: ['workflow-workerthread'],
consumers: ['tool-workflow'],
note: 'One engine per context (bash shape, no named-provider registry); the worker-thread engine fans agent() calls out through ctx.subagents.',
},
+1 -1
View File
@@ -51,7 +51,7 @@ import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
import * as ToolTodo from '@deepseek-ai/dsh-tool-todo'
import * as ToolSubagent from '@deepseek-ai/dsh-tool-subagent'
import * as ToolWeb from '@deepseek-ai/dsh-tool-web'
import VmWorkflowEngine from '@deepseek-ai/dsh-workflow-vm'
import VmWorkflowEngine from '@deepseek-ai/dsh-workflow-workerthread'
import * as ToolWorkflow from '@deepseek-ai/dsh-tool-workflow'
const root = resolve(import.meta.dirname, '..')
+1 -1
View File
@@ -313,7 +313,7 @@ function builtBinSmokeGate(): Gate {
// The workflow engine's built worker bundle: the only automated proof
// that lib/index.js resolves its sibling lib/worker.js under plain node
// (the e2e lane runs unbuilt, so this file self-skips there).
'packages/workflow/workflow-vm/tests/built-worker.e2e.ts',
'packages/workflow/workflow-workerthread/tests/built-worker.e2e.ts',
], {
label: 'built-bin smoke',
needs: ['build'],
+1 -1
View File
@@ -52,7 +52,7 @@
{ "path": "./packages/subagent/subagent-fork" },
{ "path": "./packages/subagent/subagent-acp" },
{ "path": "./packages/workflow/workflow" },
{ "path": "./packages/workflow/workflow-vm" },
{ "path": "./packages/workflow/workflow-workerthread" },
{ "path": "./packages/workflow/tool-workflow" },
{ "path": "./packages/todo/tool-todo" },
{ "path": "./packages/hooks/hook-protocol" },
+1 -1
View File
@@ -63,7 +63,7 @@
{ "path": "./packages/subagent/subagent-fork" },
{ "path": "./packages/subagent/subagent-acp" },
{ "path": "./packages/workflow/workflow" },
{ "path": "./packages/workflow/workflow-vm" },
{ "path": "./packages/workflow/workflow-workerthread" },
{ "path": "./packages/workflow/tool-workflow" },
{ "path": "./packages/todo/tool-todo" },
{ "path": "./packages/hooks/hook-protocol" },