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:
@@ -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,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,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`).
|
||||
|
||||
Reference in New Issue
Block a user