diff --git a/AGENTS.md b/AGENTS.md index 5602b244ce..d0a7983b26 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,7 @@ packages/ Harness packages at packages///, 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. diff --git a/docs/capability-seams.md b/docs/capability-seams.md index 1a13bd2060..8f5f316916 100644 --- a/docs/capability-seams.md +++ b/docs/capability-seams.md @@ -62,7 +62,7 @@ flowchart LR pkg_web_fetch_local["web-fetch-local"] pkg_workflow["workflow"] svc_workflows["ctx.workflows
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. diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 300468dd70..1649ad4e26 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -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 diff --git a/docs/core-data-structures/workflow.md b/docs/core-data-structures/workflow.md index 4d880ea948..36ad3be330 100644 --- a/docs/core-data-structures/workflow.md +++ b/docs/core-data-structures/workflow.md @@ -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) diff --git a/docs/module-graph.md b/docs/module-graph.md index ae1d8ef63f..f084e7fe40 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -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) | diff --git a/docs/rfc/implemented/feature/2026-07-05-dynamic-workflows.md b/docs/rfc/implemented/feature/2026-07-05-dynamic-workflows.md index d0e436b17e..029e090cb1 100644 --- a/docs/rfc/implemented/feature/2026-07-05-dynamic-workflows.md +++ b/docs/rfc/implemented/feature/2026-07-05-dynamic-workflows.md @@ -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. diff --git a/examples/acp-agent/composition.md b/examples/acp-agent/composition.md index 57be4be05e..06290d326e 100644 --- a/examples/acp-agent/composition.md +++ b/examples/acp-agent/composition.md @@ -31,8 +31,8 @@ flowchart LR cfg --> plugin_acp_tool_subagent plugin_acp_tool_subagent_fork["tool-subagent-fork
@deepseek-ai/dsh-tool-subagent"] cfg --> plugin_acp_tool_subagent_fork - plugin_acp_workflow_vm["workflow-vm
@deepseek-ai/dsh-workflow-vm"] - cfg --> plugin_acp_workflow_vm + plugin_acp_workflow_workerthread["workflow-workerthread
@deepseek-ai/dsh-workflow-workerthread"] + cfg --> plugin_acp_workflow_workerthread plugin_acp_tool_workflow["tool-workflow
@deepseek-ai/dsh-tool-workflow"] cfg --> plugin_acp_tool_workflow plugin_acp_tool_todo["tool-todo
@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` | diff --git a/examples/acp-agent/cordis.yml b/examples/acp-agent/cordis.yml index f2015bc7c2..3a0d177005 100644 --- a/examples/acp-agent/cordis.yml +++ b/examples/acp-agent/cordis.yml @@ -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 diff --git a/examples/acp-agent/tests/acp.snapshot.ts b/examples/acp-agent/tests/acp.snapshot.ts index 1c3e524903..944ccd9b5b 100644 --- a/examples/acp-agent/tests/acp.snapshot.ts +++ b/examples/acp-agent/tests/acp.snapshot.ts @@ -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, diff --git a/examples/coding-agent/composition.md b/examples/coding-agent/composition.md index 4ebf4c838d..be9a457124 100644 --- a/examples/coding-agent/composition.md +++ b/examples/coding-agent/composition.md @@ -35,8 +35,8 @@ flowchart LR cfg --> plugin_coding_tool_subagent plugin_coding_tool_subagent_fork["tool-subagent-fork
@deepseek-ai/dsh-tool-subagent"] cfg --> plugin_coding_tool_subagent_fork - plugin_coding_workflow_vm["workflow-vm
@deepseek-ai/dsh-workflow-vm"] - cfg --> plugin_coding_workflow_vm + plugin_coding_workflow_workerthread["workflow-workerthread
@deepseek-ai/dsh-workflow-workerthread"] + cfg --> plugin_coding_workflow_workerthread plugin_coding_tool_workflow["tool-workflow
@deepseek-ai/dsh-tool-workflow"] cfg --> plugin_coding_tool_workflow plugin_coding_tool_todo["tool-todo
@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` | diff --git a/examples/coding-agent/cordis.yml b/examples/coding-agent/cordis.yml index ee55bced0b..cf2e267e06 100644 --- a/examples/coding-agent/cordis.yml +++ b/examples/coding-agent/cordis.yml @@ -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 diff --git a/knip.json b/knip.json index 717148a10a..5342aadc29 100644 --- a/knip.json +++ b/knip.json @@ -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"] }, diff --git a/packages/workflow/README.md b/packages/workflow/README.md index a0750d9fdf..98fa3cfe04 100644 --- a/packages/workflow/README.md +++ b/packages/workflow/README.md @@ -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. diff --git a/packages/workflow/tool-workflow/package.json b/packages/workflow/tool-workflow/package.json index 7fb0d1ec48..f5c2138d85 100644 --- a/packages/workflow/tool-workflow/package.json +++ b/packages/workflow/tool-workflow/package.json @@ -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" } } diff --git a/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts b/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts index 2b6503cd45..dafc39bf46 100644 --- a/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts +++ b/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts @@ -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). */ diff --git a/packages/workflow/workflow-vm/README.md b/packages/workflow/workflow-workerthread/README.md similarity index 99% rename from packages/workflow/workflow-vm/README.md rename to packages/workflow/workflow-workerthread/README.md index 9a0ab21fef..0e763bdee9 100644 --- a/packages/workflow/workflow-vm/README.md +++ b/packages/workflow/workflow-workerthread/README.md @@ -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. diff --git a/packages/workflow/workflow-vm/package.json b/packages/workflow/workflow-workerthread/package.json similarity index 97% rename from packages/workflow/workflow-vm/package.json rename to packages/workflow/workflow-workerthread/package.json index a174893b9c..f86c74c2f7 100644 --- a/packages/workflow/workflow-vm/package.json +++ b/packages/workflow/workflow-workerthread/package.json @@ -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, diff --git a/packages/workflow/workflow-vm/src/host.ts b/packages/workflow/workflow-workerthread/src/host.ts similarity index 97% rename from packages/workflow/workflow-vm/src/host.ts rename to packages/workflow/workflow-workerthread/src/host.ts index b329e0c7d2..9d98d00933 100644 --- a/packages/workflow/workflow-vm/src/host.ts +++ b/packages/workflow/workflow-workerthread/src/host.ts @@ -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) }, ) diff --git a/packages/workflow/workflow-vm/src/index.ts b/packages/workflow/workflow-workerthread/src/index.ts similarity index 99% rename from packages/workflow/workflow-vm/src/index.ts rename to packages/workflow/workflow-workerthread/src/index.ts index 1b65381bfb..538252d90a 100644 --- a/packages/workflow/workflow-vm/src/index.ts +++ b/packages/workflow/workflow-workerthread/src/index.ts @@ -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' diff --git a/packages/workflow/workflow-vm/src/meta.ts b/packages/workflow/workflow-workerthread/src/meta.ts similarity index 99% rename from packages/workflow/workflow-vm/src/meta.ts rename to packages/workflow/workflow-workerthread/src/meta.ts index 75958ba6f1..e818bc6f00 100644 --- a/packages/workflow/workflow-vm/src/meta.ts +++ b/packages/workflow/workflow-workerthread/src/meta.ts @@ -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' diff --git a/packages/workflow/workflow-vm/src/protocol.ts b/packages/workflow/workflow-workerthread/src/protocol.ts similarity index 98% rename from packages/workflow/workflow-vm/src/protocol.ts rename to packages/workflow/workflow-workerthread/src/protocol.ts index 293676706e..ed6b54950a 100644 --- a/packages/workflow/workflow-vm/src/protocol.ts +++ b/packages/workflow/workflow-workerthread/src/protocol.ts @@ -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' diff --git a/packages/workflow/workflow-vm/src/realm.ts b/packages/workflow/workflow-workerthread/src/realm.ts similarity index 99% rename from packages/workflow/workflow-vm/src/realm.ts rename to packages/workflow/workflow-workerthread/src/realm.ts index 017de76069..393716c279 100644 --- a/packages/workflow/workflow-vm/src/realm.ts +++ b/packages/workflow/workflow-workerthread/src/realm.ts @@ -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. */ diff --git a/packages/workflow/workflow-vm/src/runtime.ts b/packages/workflow/workflow-workerthread/src/runtime.ts similarity index 99% rename from packages/workflow/workflow-vm/src/runtime.ts rename to packages/workflow/workflow-workerthread/src/runtime.ts index 3005520fa0..28450977c3 100644 --- a/packages/workflow/workflow-vm/src/runtime.ts +++ b/packages/workflow/workflow-workerthread/src/runtime.ts @@ -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' diff --git a/packages/workflow/workflow-vm/src/session.ts b/packages/workflow/workflow-workerthread/src/session.ts similarity index 99% rename from packages/workflow/workflow-vm/src/session.ts rename to packages/workflow/workflow-workerthread/src/session.ts index 131f760ff9..718b8ac54d 100644 --- a/packages/workflow/workflow-vm/src/session.ts +++ b/packages/workflow/workflow-workerthread/src/session.ts @@ -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' diff --git a/packages/workflow/workflow-vm/src/types.ts b/packages/workflow/workflow-workerthread/src/types.ts similarity index 98% rename from packages/workflow/workflow-vm/src/types.ts rename to packages/workflow/workflow-workerthread/src/types.ts index c87fc4668c..329523ffc8 100644 --- a/packages/workflow/workflow-vm/src/types.ts +++ b/packages/workflow/workflow-workerthread/src/types.ts @@ -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' diff --git a/packages/workflow/workflow-vm/src/worker.ts b/packages/workflow/workflow-workerthread/src/worker.ts similarity index 93% rename from packages/workflow/workflow-vm/src/worker.ts rename to packages/workflow/workflow-workerthread/src/worker.ts index 3b20600d7d..f468ad9a53 100644 --- a/packages/workflow/workflow-vm/src/worker.ts +++ b/packages/workflow/workflow-workerthread/src/worker.ts @@ -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' diff --git a/packages/workflow/workflow-vm/tests/built-worker.e2e.ts b/packages/workflow/workflow-workerthread/tests/built-worker.e2e.ts similarity index 97% rename from packages/workflow/workflow-vm/tests/built-worker.e2e.ts rename to packages/workflow/workflow-workerthread/tests/built-worker.e2e.ts index 9bdd3865bb..d541b4c0ff 100644 --- a/packages/workflow/workflow-vm/tests/built-worker.e2e.ts +++ b/packages/workflow/workflow-workerthread/tests/built-worker.e2e.ts @@ -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) diff --git a/packages/workflow/workflow-vm/tests/integration.spec.ts b/packages/workflow/workflow-workerthread/tests/integration.spec.ts similarity index 98% rename from packages/workflow/workflow-vm/tests/integration.spec.ts rename to packages/workflow/workflow-workerthread/tests/integration.spec.ts index e53ea518bc..ae58a536f8 100644 --- a/packages/workflow/workflow-vm/tests/integration.spec.ts +++ b/packages/workflow/workflow-workerthread/tests/integration.spec.ts @@ -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'), diff --git a/packages/workflow/workflow-vm/tests/meta.spec.ts b/packages/workflow/workflow-workerthread/tests/meta.spec.ts similarity index 100% rename from packages/workflow/workflow-vm/tests/meta.spec.ts rename to packages/workflow/workflow-workerthread/tests/meta.spec.ts diff --git a/packages/workflow/workflow-vm/tests/realm.spec.ts b/packages/workflow/workflow-workerthread/tests/realm.spec.ts similarity index 100% rename from packages/workflow/workflow-vm/tests/realm.spec.ts rename to packages/workflow/workflow-workerthread/tests/realm.spec.ts diff --git a/packages/workflow/workflow-vm/tests/session.spec.ts b/packages/workflow/workflow-workerthread/tests/session.spec.ts similarity index 100% rename from packages/workflow/workflow-vm/tests/session.spec.ts rename to packages/workflow/workflow-workerthread/tests/session.spec.ts diff --git a/packages/workflow/workflow-vm/tests/workflow.e2e.ts b/packages/workflow/workflow-workerthread/tests/workflow-workerthread.e2e.ts similarity index 100% rename from packages/workflow/workflow-vm/tests/workflow.e2e.ts rename to packages/workflow/workflow-workerthread/tests/workflow-workerthread.e2e.ts diff --git a/packages/workflow/workflow-vm/tests/workflow-vm.spec.ts b/packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.ts similarity index 99% rename from packages/workflow/workflow-vm/tests/workflow-vm.spec.ts rename to packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.ts index 7264877f48..9e2218c167 100644 --- a/packages/workflow/workflow-vm/tests/workflow-vm.spec.ts +++ b/packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.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}`) }) diff --git a/packages/workflow/workflow-vm/tsconfig.json b/packages/workflow/workflow-workerthread/tsconfig.json similarity index 100% rename from packages/workflow/workflow-vm/tsconfig.json rename to packages/workflow/workflow-workerthread/tsconfig.json diff --git a/packages/workflow/workflow-vm/tsdown.config.ts b/packages/workflow/workflow-workerthread/tsdown.config.ts similarity index 100% rename from packages/workflow/workflow-vm/tsdown.config.ts rename to packages/workflow/workflow-workerthread/tsdown.config.ts diff --git a/packages/workflow/workflow/README.md b/packages/workflow/workflow/README.md index 96add5017e..317aaf9f81 100644 --- a/packages/workflow/workflow/README.md +++ b/packages/workflow/workflow/README.md @@ -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) diff --git a/packages/workflow/workflow/src/index.ts b/packages/workflow/workflow/src/index.ts index 2a57d143b6..6c2b797362 100644 --- a/packages/workflow/workflow/src/index.ts +++ b/packages/workflow/workflow/src/index.ts @@ -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`). diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3e1892e35..c3ce677f83 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts index 0abe1a34cf..31e010f55c 100644 --- a/scripts/gen-doc-graphs.ts +++ b/scripts/gen-doc-graphs.ts @@ -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.', }, diff --git a/scripts/gen-tool-catalog.ts b/scripts/gen-tool-catalog.ts index 27481a6304..42795ec00e 100644 --- a/scripts/gen-tool-catalog.ts +++ b/scripts/gen-tool-catalog.ts @@ -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, '..') diff --git a/scripts/run-gates.ts b/scripts/run-gates.ts index 25e08cf8d2..3b4a3568cb 100644 --- a/scripts/run-gates.ts +++ b/scripts/run-gates.ts @@ -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'], diff --git a/tsconfig.build.json b/tsconfig.build.json index 93cce8f4b9..018c29ec37 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -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" }, diff --git a/tsconfig.json b/tsconfig.json index ba5ccbac26..0cad85791c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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" },