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:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"]
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
-1
@@ -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
-1
@@ -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,
|
||||
+4
-4
@@ -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)
|
||||
},
|
||||
)
|
||||
+1
-1
@@ -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'
|
||||
+1
-1
@@ -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'
|
||||
+1
-1
@@ -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'
|
||||
+1
-1
@@ -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. */
|
||||
+1
-1
@@ -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'
|
||||
+1
-1
@@ -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'
|
||||
+1
-1
@@ -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'
|
||||
+1
-1
@@ -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'
|
||||
+1
-1
@@ -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)
|
||||
+1
-1
@@ -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'),
|
||||
+1
-1
@@ -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,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)
|
||||
|
||||
|
||||
@@ -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`).
|
||||
|
||||
Generated
+3
-3
@@ -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
|
||||
|
||||
@@ -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.',
|
||||
},
|
||||
|
||||
@@ -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, '..')
|
||||
|
||||
@@ -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
@@ -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
@@ -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" },
|
||||
|
||||
Reference in New Issue
Block a user