@agent-relay/sdk 2.3.14 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -838
- package/bin/agent-relay-broker +0 -0
- package/dist/__tests__/contract-fixtures.test.d.ts +2 -0
- package/dist/__tests__/contract-fixtures.test.d.ts.map +1 -0
- package/dist/__tests__/contract-fixtures.test.js +85 -0
- package/dist/__tests__/contract-fixtures.test.js.map +1 -0
- package/dist/__tests__/facade.test.d.ts +2 -0
- package/dist/__tests__/facade.test.d.ts.map +1 -0
- package/dist/__tests__/facade.test.js +257 -0
- package/dist/__tests__/facade.test.js.map +1 -0
- package/dist/__tests__/integration.test.d.ts +2 -0
- package/dist/__tests__/integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration.test.js +164 -0
- package/dist/__tests__/integration.test.js.map +1 -0
- package/dist/__tests__/pty.test.d.ts +2 -0
- package/dist/__tests__/pty.test.d.ts.map +1 -0
- package/dist/__tests__/pty.test.js +20 -0
- package/dist/__tests__/pty.test.js.map +1 -0
- package/dist/__tests__/quickstart.test.d.ts +2 -0
- package/dist/__tests__/quickstart.test.d.ts.map +1 -0
- package/dist/__tests__/quickstart.test.js +176 -0
- package/dist/__tests__/quickstart.test.js.map +1 -0
- package/dist/__tests__/spawn-from-env.test.d.ts +2 -0
- package/dist/__tests__/spawn-from-env.test.d.ts.map +1 -0
- package/dist/__tests__/spawn-from-env.test.js +206 -0
- package/dist/__tests__/spawn-from-env.test.js.map +1 -0
- package/dist/__tests__/unit.test.d.ts +2 -0
- package/dist/__tests__/unit.test.d.ts.map +1 -0
- package/dist/__tests__/unit.test.js +311 -0
- package/dist/__tests__/unit.test.js.map +1 -0
- package/dist/browser.d.ts +16 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +19 -0
- package/dist/browser.js.map +1 -0
- package/dist/client.d.ts +138 -526
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +407 -1509
- package/dist/client.js.map +1 -1
- package/dist/consensus-helpers.d.ts +103 -0
- package/dist/consensus-helpers.d.ts.map +1 -0
- package/dist/consensus-helpers.js +147 -0
- package/dist/consensus-helpers.js.map +1 -0
- package/dist/consensus.d.ts +72 -0
- package/dist/consensus.d.ts.map +1 -0
- package/dist/consensus.js +378 -0
- package/dist/consensus.js.map +1 -0
- package/dist/examples/demo.d.ts +2 -0
- package/dist/examples/demo.d.ts.map +1 -0
- package/dist/examples/demo.js +63 -0
- package/dist/examples/demo.js.map +1 -0
- package/dist/examples/example.d.ts +2 -0
- package/dist/examples/example.d.ts.map +1 -0
- package/dist/examples/example.js +80 -0
- package/dist/examples/example.js.map +1 -0
- package/dist/examples/quickstart.d.ts +2 -0
- package/dist/examples/quickstart.d.ts.map +1 -0
- package/dist/examples/quickstart.js +56 -0
- package/dist/examples/quickstart.js.map +1 -0
- package/dist/examples/ralph-loop.d.ts +2 -0
- package/dist/examples/ralph-loop.d.ts.map +1 -0
- package/dist/examples/ralph-loop.js +281 -0
- package/dist/examples/ralph-loop.js.map +1 -0
- package/dist/examples/workflow-superiority.d.ts +32 -0
- package/dist/examples/workflow-superiority.d.ts.map +1 -0
- package/dist/examples/workflow-superiority.js +1421 -0
- package/dist/examples/workflow-superiority.js.map +1 -0
- package/dist/index.d.ts +13 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -26
- package/dist/index.js.map +1 -1
- package/dist/logs.d.ts +70 -25
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +238 -42
- package/dist/logs.js.map +1 -1
- package/dist/models.d.ts +9 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +17 -0
- package/dist/models.js.map +1 -0
- package/dist/protocol.d.ts +366 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +2 -0
- package/dist/protocol.js.map +1 -0
- package/dist/pty.d.ts +8 -0
- package/dist/pty.d.ts.map +1 -0
- package/dist/pty.js +26 -0
- package/dist/pty.js.map +1 -0
- package/dist/relay-adapter.d.ts +139 -0
- package/dist/relay-adapter.d.ts.map +1 -0
- package/dist/relay-adapter.js +210 -0
- package/dist/relay-adapter.js.map +1 -0
- package/dist/relay.d.ts +277 -0
- package/dist/relay.d.ts.map +1 -0
- package/dist/relay.js +853 -0
- package/dist/relay.js.map +1 -0
- package/dist/shadow.d.ts +101 -0
- package/dist/shadow.d.ts.map +1 -0
- package/dist/shadow.js +174 -0
- package/dist/shadow.js.map +1 -0
- package/dist/spawn-from-env.d.ts +77 -0
- package/dist/spawn-from-env.d.ts.map +1 -0
- package/dist/spawn-from-env.js +172 -0
- package/dist/spawn-from-env.js.map +1 -0
- package/dist/workflows/barrier.d.ts +72 -0
- package/dist/workflows/barrier.d.ts.map +1 -0
- package/dist/workflows/barrier.js +162 -0
- package/dist/workflows/barrier.js.map +1 -0
- package/dist/workflows/builder.d.ts +114 -0
- package/dist/workflows/builder.d.ts.map +1 -0
- package/dist/workflows/builder.js +201 -0
- package/dist/workflows/builder.js.map +1 -0
- package/dist/workflows/cli.d.ts +11 -0
- package/dist/workflows/cli.d.ts.map +1 -0
- package/dist/workflows/cli.js +144 -0
- package/dist/workflows/cli.js.map +1 -0
- package/dist/workflows/coordinator.d.ts +73 -0
- package/dist/workflows/coordinator.d.ts.map +1 -0
- package/dist/workflows/coordinator.js +647 -0
- package/dist/workflows/coordinator.js.map +1 -0
- package/dist/workflows/custom-steps.d.ts +73 -0
- package/dist/workflows/custom-steps.d.ts.map +1 -0
- package/dist/workflows/custom-steps.js +321 -0
- package/dist/workflows/custom-steps.js.map +1 -0
- package/dist/workflows/dry-run-format.d.ts +6 -0
- package/dist/workflows/dry-run-format.d.ts.map +1 -0
- package/dist/workflows/dry-run-format.js +68 -0
- package/dist/workflows/dry-run-format.js.map +1 -0
- package/dist/workflows/file-db.d.ts +33 -0
- package/dist/workflows/file-db.d.ts.map +1 -0
- package/dist/workflows/file-db.js +108 -0
- package/dist/workflows/file-db.js.map +1 -0
- package/dist/workflows/index.d.ts +15 -0
- package/dist/workflows/index.d.ts.map +1 -0
- package/dist/workflows/index.js +15 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/memory-db.d.ts +17 -0
- package/dist/workflows/memory-db.d.ts.map +1 -0
- package/dist/workflows/memory-db.js +33 -0
- package/dist/workflows/memory-db.js.map +1 -0
- package/dist/workflows/run.d.ts +38 -0
- package/dist/workflows/run.d.ts.map +1 -0
- package/dist/workflows/run.js +25 -0
- package/dist/workflows/run.js.map +1 -0
- package/dist/workflows/runner.d.ts +320 -0
- package/dist/workflows/runner.d.ts.map +1 -0
- package/dist/workflows/runner.js +2821 -0
- package/dist/workflows/runner.js.map +1 -0
- package/dist/workflows/state.d.ts +77 -0
- package/dist/workflows/state.d.ts.map +1 -0
- package/dist/workflows/state.js +140 -0
- package/dist/workflows/state.js.map +1 -0
- package/dist/workflows/templates.d.ts +47 -0
- package/dist/workflows/templates.d.ts.map +1 -0
- package/dist/workflows/templates.js +405 -0
- package/dist/workflows/templates.js.map +1 -0
- package/dist/workflows/trajectory.d.ts +87 -0
- package/dist/workflows/trajectory.d.ts.map +1 -0
- package/dist/workflows/trajectory.js +441 -0
- package/dist/workflows/trajectory.js.map +1 -0
- package/dist/workflows/types.d.ts +306 -0
- package/dist/workflows/types.d.ts.map +1 -0
- package/dist/workflows/types.js +23 -0
- package/dist/workflows/types.js.map +1 -0
- package/dist/workflows/validator.d.ts +11 -0
- package/dist/workflows/validator.d.ts.map +1 -0
- package/dist/workflows/validator.js +128 -0
- package/dist/workflows/validator.js.map +1 -0
- package/package.json +59 -53
- package/dist/discovery.d.ts +0 -10
- package/dist/discovery.d.ts.map +0 -1
- package/dist/discovery.js +0 -22
- package/dist/discovery.js.map +0 -1
- package/dist/errors.d.ts +0 -9
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -9
- package/dist/errors.js.map +0 -1
- package/dist/protocol/index.d.ts +0 -8
- package/dist/protocol/index.d.ts.map +0 -1
- package/dist/protocol/index.js +0 -8
- package/dist/protocol/index.js.map +0 -1
|
@@ -0,0 +1,1421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Superiority — Multi-Agent Implementation Campaign
|
|
3
|
+
*
|
|
4
|
+
* A fully specified DAG workflow that orchestrates Claude (lead + reviewer)
|
|
5
|
+
* and Codex workers across five implementation tiers to make the relay
|
|
6
|
+
* broker-sdk workflow system decisively superior to Agno and Swarms AI.
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* - Claude lead: orchestrates each phase, approves trajectories, makes
|
|
10
|
+
* final architectural decisions
|
|
11
|
+
* - Codex workers: implement code changes, one per specialization domain
|
|
12
|
+
* - Claude code-reviewer: independent review after every implementation phase
|
|
13
|
+
* (separate from lead to avoid confirmation bias)
|
|
14
|
+
*
|
|
15
|
+
* DAG phases:
|
|
16
|
+
* Phase 0 → Codebase analysis + spec approval
|
|
17
|
+
* Phase 1 → Type system extension (condition/loop/router/hitl/sub-workflow)
|
|
18
|
+
* Phase 2 → Execution engine (runner handles new primitives + session)
|
|
19
|
+
* Phase 3 → Meta-orchestration (parallel with Phase 4)
|
|
20
|
+
* Phase 4 → Storage backends (parallel with Phase 3)
|
|
21
|
+
* Phase 5 → Deployment & observability
|
|
22
|
+
* Phase 6 → Integration validation + final lead sign-off
|
|
23
|
+
*
|
|
24
|
+
* Run:
|
|
25
|
+
* npx tsx src/examples/workflow-superiority.ts
|
|
26
|
+
*
|
|
27
|
+
* Environment:
|
|
28
|
+
* RELAY_API_KEY — optional. If absent the runner auto-provisions a
|
|
29
|
+
* fresh Relaycast workspace for each run (fully isolated, no caching).
|
|
30
|
+
*/
|
|
31
|
+
import { workflow } from '../workflows/builder.js';
|
|
32
|
+
// ── Spec constants ────────────────────────────────────────────────────────────
|
|
33
|
+
const WORKFLOW_ROOT = 'packages/sdk/src/workflows';
|
|
34
|
+
const TYPES_FILE = `${WORKFLOW_ROOT}/types.ts`;
|
|
35
|
+
const RUNNER_FILE = `${WORKFLOW_ROOT}/runner.ts`;
|
|
36
|
+
const BUILDER_FILE = `${WORKFLOW_ROOT}/builder.ts`;
|
|
37
|
+
const SCHEMA_FILE = `${WORKFLOW_ROOT}/schema.json`;
|
|
38
|
+
const INDEX_FILE = `${WORKFLOW_ROOT}/index.ts`;
|
|
39
|
+
const MEMORY_DB = `${WORKFLOW_ROOT}/memory-db.ts`;
|
|
40
|
+
const COORDINATOR = `${WORKFLOW_ROOT}/coordinator.ts`;
|
|
41
|
+
const BARRIER_FILE = `${WORKFLOW_ROOT}/barrier.ts`;
|
|
42
|
+
const TEMPLATES = `${WORKFLOW_ROOT}/templates.ts`;
|
|
43
|
+
// NOTE: No withExit() wrapper needed — the WorkflowRunner automatically
|
|
44
|
+
// appends self-termination instructions in spawnAndWait() with the agent's
|
|
45
|
+
// actual runtime name. Adding a second exit instruction wastes tokens.
|
|
46
|
+
// ── Event handler ─────────────────────────────────────────────────────────────
|
|
47
|
+
const onEvent = (event) => {
|
|
48
|
+
const ts = new Date().toISOString();
|
|
49
|
+
switch (event.type) {
|
|
50
|
+
case 'run:started':
|
|
51
|
+
console.log(`[${ts}] 🚀 run started runId=${event.runId}`);
|
|
52
|
+
break;
|
|
53
|
+
case 'run:completed':
|
|
54
|
+
console.log(`[${ts}] ✅ run complete runId=${event.runId}`);
|
|
55
|
+
break;
|
|
56
|
+
case 'run:failed':
|
|
57
|
+
console.error(`[${ts}] ❌ run failed runId=${event.runId} error=${event.error}`);
|
|
58
|
+
break;
|
|
59
|
+
case 'step:started':
|
|
60
|
+
console.log(`[${ts}] → ${event.stepName}`);
|
|
61
|
+
break;
|
|
62
|
+
case 'step:completed':
|
|
63
|
+
console.log(`[${ts}] ✓ ${event.stepName}`);
|
|
64
|
+
break;
|
|
65
|
+
case 'step:failed':
|
|
66
|
+
console.error(`[${ts}] ✗ ${event.stepName}: ${event.error}`);
|
|
67
|
+
break;
|
|
68
|
+
case 'step:skipped':
|
|
69
|
+
console.log(`[${ts}] ⊘ ${event.stepName} (skipped)`);
|
|
70
|
+
break;
|
|
71
|
+
case 'step:retrying':
|
|
72
|
+
console.log(`[${ts}] ↺ ${event.stepName} attempt=${event.attempt}`);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
// ── Workflow definition ───────────────────────────────────────────────────────
|
|
77
|
+
const result = await workflow('broker-sdk-superiority')
|
|
78
|
+
.description('Five-phase multi-agent campaign to make relay broker-sdk workflow system ' +
|
|
79
|
+
'decisively superior to Agno and Swarms AI. Claude leads; Codex implements; ' +
|
|
80
|
+
'Claude reviews after every phase.')
|
|
81
|
+
.pattern('dag')
|
|
82
|
+
.channel('wf-broker-sdk-superiority')
|
|
83
|
+
.maxConcurrency(3)
|
|
84
|
+
.timeout(28_800_000) // 8 hours — this is a large implementation campaign
|
|
85
|
+
// ── Agents ────────────────────────────────────────────────────────────────
|
|
86
|
+
.agent('lead', {
|
|
87
|
+
cli: 'claude',
|
|
88
|
+
role: 'Lead architect. Sets direction, reviews each phase output, approves ' +
|
|
89
|
+
'trajectories, and resolves architectural conflicts. Has final say on ' +
|
|
90
|
+
'all design decisions.',
|
|
91
|
+
retries: 2,
|
|
92
|
+
})
|
|
93
|
+
.agent('code-reviewer', {
|
|
94
|
+
cli: 'claude',
|
|
95
|
+
role: 'Independent code reviewer. Reviews implementation quality, correctness, ' +
|
|
96
|
+
'TypeScript type safety, test coverage, and integration coherence after ' +
|
|
97
|
+
'every phase. Catches issues the lead may have missed.',
|
|
98
|
+
retries: 2,
|
|
99
|
+
})
|
|
100
|
+
.agent('spec-analyst', {
|
|
101
|
+
cli: 'codex',
|
|
102
|
+
role: 'Codebase analyst. Reads the existing workflow source files and produces ' +
|
|
103
|
+
'a precise, file-by-file implementation plan for all five improvement tiers.',
|
|
104
|
+
retries: 2,
|
|
105
|
+
})
|
|
106
|
+
.agent('schema-implementer', {
|
|
107
|
+
cli: 'codex',
|
|
108
|
+
role: 'Type system specialist. Extends TypeScript interfaces and JSON Schema ' +
|
|
109
|
+
'definitions to support new workflow primitives.',
|
|
110
|
+
retries: 2,
|
|
111
|
+
})
|
|
112
|
+
.agent('engine-implementer', {
|
|
113
|
+
cli: 'codex',
|
|
114
|
+
role: 'Execution engine specialist. Implements new step-type execution logic ' +
|
|
115
|
+
'inside WorkflowRunner, adds session concept, and expands the event system.',
|
|
116
|
+
retries: 3,
|
|
117
|
+
})
|
|
118
|
+
.agent('meta-implementer', {
|
|
119
|
+
cli: 'codex',
|
|
120
|
+
role: 'Meta-orchestration specialist. Implements sub-workflow composition, ' +
|
|
121
|
+
'AutoWorkflowBuilder, and semantic pattern selection.',
|
|
122
|
+
retries: 2,
|
|
123
|
+
})
|
|
124
|
+
.agent('storage-implementer', {
|
|
125
|
+
cli: 'codex',
|
|
126
|
+
role: 'Storage backend specialist. Implements PostgresWorkflowDb, ' +
|
|
127
|
+
'SqliteWorkflowDb, and RedisWorkflowDb adapters.',
|
|
128
|
+
retries: 2,
|
|
129
|
+
})
|
|
130
|
+
.agent('deploy-implementer', {
|
|
131
|
+
cli: 'codex',
|
|
132
|
+
role: 'Deployment and observability specialist. Implements relay workflow serve ' +
|
|
133
|
+
'HTTP server, OTel tracing integration, and CLI improvements.',
|
|
134
|
+
retries: 2,
|
|
135
|
+
})
|
|
136
|
+
.agent('test-validator', {
|
|
137
|
+
cli: 'codex',
|
|
138
|
+
role: 'Integration test specialist. Validates all phases compile, tests pass, ' + 'and exports are correct.',
|
|
139
|
+
retries: 2,
|
|
140
|
+
})
|
|
141
|
+
// ── Phase 0: Codebase Analysis ────────────────────────────────────────────
|
|
142
|
+
.step('codebase-analysis', {
|
|
143
|
+
agent: 'spec-analyst',
|
|
144
|
+
task: `
|
|
145
|
+
You are the first step in a large improvement campaign for the relay broker-sdk
|
|
146
|
+
workflow system. Your job is to read the existing source files and produce a
|
|
147
|
+
concrete, file-by-file implementation plan.
|
|
148
|
+
|
|
149
|
+
READ THESE FILES THOROUGHLY:
|
|
150
|
+
- ${TYPES_FILE}
|
|
151
|
+
- ${RUNNER_FILE}
|
|
152
|
+
- ${BUILDER_FILE}
|
|
153
|
+
- ${SCHEMA_FILE}
|
|
154
|
+
- ${INDEX_FILE}
|
|
155
|
+
- ${MEMORY_DB}
|
|
156
|
+
- ${COORDINATOR}
|
|
157
|
+
- ${BARRIER_FILE}
|
|
158
|
+
- ${TEMPLATES}
|
|
159
|
+
|
|
160
|
+
PRODUCE A DETAILED PLAN covering the following improvements (in priority order):
|
|
161
|
+
|
|
162
|
+
TIER 1 — Schema Primitives (new step types in types.ts + schema.json):
|
|
163
|
+
a) condition step: WorkflowStep gains optional "condition" string (CEL expr)
|
|
164
|
+
and optional "type" discriminant. When condition evaluates false → skip.
|
|
165
|
+
b) loop step: new LoopStepGroup interface { type:"loop", name, steps[],
|
|
166
|
+
until?: string (CEL expr on step outputs), maxIterations: number }
|
|
167
|
+
c) router step: new RouterStepGroup interface { type:"router", name,
|
|
168
|
+
selector: string (CEL expr), routes: RouterRoute[], default?: string[] }
|
|
169
|
+
RouterRoute: { match: string, steps: string[] }
|
|
170
|
+
d) parallel step group: new ParallelStepGroup interface { type:"parallel",
|
|
171
|
+
name, barrier?: "all"|"any"|"majority", timeout?: number, steps[] }
|
|
172
|
+
e) hitl step: new HitlStep interface { type:"hitl", name, message: string,
|
|
173
|
+
channel?: string, timeout?: number, onTimeout?: "skip"|"fail" }
|
|
174
|
+
f) flow shorthand: RelayYamlConfig gains optional "flow?: string" field,
|
|
175
|
+
parsed as "A -> B, C -> D" notation generating dependsOn edges
|
|
176
|
+
g) sub-workflow step: new SubWorkflowStep interface { type:"sub-workflow",
|
|
177
|
+
name, workflow: string (path or registry name), vars?: Record<string,string> }
|
|
178
|
+
h) Promote retries + timeoutMs to top-level WorkflowStep fields (they already
|
|
179
|
+
exist — verify they are exported and used correctly in runner.ts)
|
|
180
|
+
|
|
181
|
+
TIER 2 — Execution Engine (runner.ts):
|
|
182
|
+
a) Condition evaluation: before executing a step, if step.condition is set,
|
|
183
|
+
evaluate the CEL expression; if false, mark step skipped
|
|
184
|
+
b) Loop execution: LoopStepGroup runs its steps[].steps repeatedly until
|
|
185
|
+
until-condition is true or maxIterations reached
|
|
186
|
+
c) Router execution: evaluate selector, match route, execute branch steps
|
|
187
|
+
d) Parallel group: run steps concurrently with barrier semantics
|
|
188
|
+
e) HITL execution: pause run, write hitl-pending file, poll for response file
|
|
189
|
+
f) Sub-workflow: recursively create a WorkflowRunner and execute the referenced
|
|
190
|
+
config, return its output as the step output
|
|
191
|
+
g) Session concept: WorkflowRunner gains optional sessionId param; a session
|
|
192
|
+
groups multiple runs and shares state across them via SessionStore
|
|
193
|
+
h) Input schema validation: RelayYamlConfig gains optional inputSchema field
|
|
194
|
+
(JSON Schema object); validate vars before execution begins
|
|
195
|
+
i) Fallback agent: WorkflowStep gains optional fallbackAgent?: string;
|
|
196
|
+
if primary agent fails all retries, retry once with fallbackAgent
|
|
197
|
+
j) Expanded events: add 15+ new WorkflowEvent union members for loop iterations,
|
|
198
|
+
condition evaluations, router selections, hitl pauses, session updates
|
|
199
|
+
|
|
200
|
+
TIER 3 — Meta-Orchestration:
|
|
201
|
+
a) AutoWorkflowBuilder: new exported class that takes a task string and
|
|
202
|
+
available CLIs, calls a meta-agent to generate RelayYamlConfig, validates
|
|
203
|
+
it, and returns it (or optionally executes immediately)
|
|
204
|
+
b) Workflow registry: simple JSON file-based registry at
|
|
205
|
+
~/.agent-relay/workflow-registry.json that maps name→path; CLI commands
|
|
206
|
+
relay workflow list, search, install, publish
|
|
207
|
+
|
|
208
|
+
TIER 4 — Storage Backends:
|
|
209
|
+
a) PostgresWorkflowDb: implements WorkflowDb interface using node-postgres (pg)
|
|
210
|
+
with schema migration on first connect
|
|
211
|
+
b) SqliteWorkflowDb: implements WorkflowDb using better-sqlite3 (sync API
|
|
212
|
+
wrapped in async interface)
|
|
213
|
+
c) RedisWorkflowDb: implements WorkflowDb using ioredis with JSON serialization
|
|
214
|
+
and optional TTL on run records
|
|
215
|
+
All three: exported from packages/sdk/src/workflows/db/ submodule
|
|
216
|
+
|
|
217
|
+
TIER 5 — Deployment & Observability:
|
|
218
|
+
a) relay workflow serve: new CLI subcommand that starts an Express/Fastify HTTP
|
|
219
|
+
server exposing POST /run, GET /runs/:id, GET /runs/:id/events (SSE),
|
|
220
|
+
POST /runs/:id/hitl/:step, POST /runs/:id/abort, GET /health
|
|
221
|
+
b) OTel tracing: optional otel?: { exportTo: string, endpoint: string,
|
|
222
|
+
serviceName: string } in RelayYamlConfig; runner creates spans for
|
|
223
|
+
run start/end and each step start/end using @opentelemetry/sdk-node
|
|
224
|
+
c) CLI improvements: relay workflow dry-run, relay workflow inspect,
|
|
225
|
+
relay workflow replay --from <step>
|
|
226
|
+
|
|
227
|
+
For each tier, specify:
|
|
228
|
+
1. Which existing files change and what lines/sections to modify
|
|
229
|
+
2. Which new files to create and their full path
|
|
230
|
+
3. New TypeScript interface/type definitions (exact syntax)
|
|
231
|
+
4. New function signatures with JSDoc
|
|
232
|
+
|
|
233
|
+
Output a structured plan with clear section headers.
|
|
234
|
+
End your output with: ANALYSIS_COMPLETE
|
|
235
|
+
`,
|
|
236
|
+
retries: 2,
|
|
237
|
+
verification: { type: 'output_contains', value: 'ANALYSIS_COMPLETE' },
|
|
238
|
+
})
|
|
239
|
+
.step('spec-approval', {
|
|
240
|
+
agent: 'lead',
|
|
241
|
+
task: `
|
|
242
|
+
Review the codebase analysis produced by the spec-analyst:
|
|
243
|
+
|
|
244
|
+
{{steps.codebase-analysis.output}}
|
|
245
|
+
|
|
246
|
+
Your job:
|
|
247
|
+
1. Validate that the plan covers all five tiers correctly
|
|
248
|
+
2. Identify any architectural risks or conflicts (e.g., breaking changes to
|
|
249
|
+
WorkflowStep that would break existing templates)
|
|
250
|
+
3. Clarify the execution order for each tier
|
|
251
|
+
4. Approve or amend the plan — note any changes clearly
|
|
252
|
+
5. Establish a non-negotiable constraint: ALL existing tests in
|
|
253
|
+
packages/sdk/src/__tests__/ must remain passing after each phase
|
|
254
|
+
|
|
255
|
+
Key architectural decisions to make explicit:
|
|
256
|
+
- How should WorkflowStep handle the new type discriminant without breaking
|
|
257
|
+
existing YAML that has no "type" field? (answer: type defaults to "agent")
|
|
258
|
+
- Should LoopStepGroup and RouterStepGroup be separate from WorkflowStep in
|
|
259
|
+
the types, or unified via a discriminated union?
|
|
260
|
+
- Is the session concept stored in the same WorkflowDb or a separate SessionDb?
|
|
261
|
+
- For the hitl step, what is the polling mechanism? (file-based, HTTP endpoint,
|
|
262
|
+
or webhook?) Choose the simplest that works without requiring a server.
|
|
263
|
+
|
|
264
|
+
Output a concise approved plan with your decisions. End with: SPEC_APPROVED
|
|
265
|
+
`,
|
|
266
|
+
dependsOn: ['codebase-analysis'],
|
|
267
|
+
retries: 2,
|
|
268
|
+
verification: { type: 'output_contains', value: 'SPEC_APPROVED' },
|
|
269
|
+
})
|
|
270
|
+
// ── Phase 1: Type System Extension ────────────────────────────────────────
|
|
271
|
+
.step('p1-type-system', {
|
|
272
|
+
agent: 'schema-implementer',
|
|
273
|
+
task: `
|
|
274
|
+
Phase 1a: Extend the TypeScript type system for new workflow primitives.
|
|
275
|
+
|
|
276
|
+
Approved spec context:
|
|
277
|
+
{{steps.spec-approval.output}}
|
|
278
|
+
|
|
279
|
+
YOUR TASK: Modify ${TYPES_FILE} to add all new types.
|
|
280
|
+
|
|
281
|
+
SPECIFIC CHANGES:
|
|
282
|
+
|
|
283
|
+
1. Add a StepType discriminant:
|
|
284
|
+
export type StepType = "agent" | "condition" | "loop" | "router" | "parallel" | "hitl" | "sub-workflow";
|
|
285
|
+
|
|
286
|
+
2. Extend WorkflowStep to include new optional fields:
|
|
287
|
+
- type?: StepType (default "agent" when absent)
|
|
288
|
+
- condition?: string (CEL expression; step skipped if evaluates false)
|
|
289
|
+
- fallbackAgent?: string (agent name to use if primary fails all retries)
|
|
290
|
+
|
|
291
|
+
3. Add new composite step group interfaces:
|
|
292
|
+
|
|
293
|
+
export interface LoopStepGroup {
|
|
294
|
+
type: "loop";
|
|
295
|
+
name: string;
|
|
296
|
+
description?: string;
|
|
297
|
+
steps: WorkflowStep[];
|
|
298
|
+
until?: string; // CEL expression evaluated after each iteration
|
|
299
|
+
maxIterations: number; // required — prevents runaway loops
|
|
300
|
+
timeoutMs?: number;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export interface RouterRoute {
|
|
304
|
+
match: string; // CEL expression or substring match
|
|
305
|
+
steps: string[]; // names of steps to execute for this route
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export interface RouterStepGroup {
|
|
309
|
+
type: "router";
|
|
310
|
+
name: string;
|
|
311
|
+
description?: string;
|
|
312
|
+
selector: string; // CEL expression producing a string value
|
|
313
|
+
routes: RouterRoute[];
|
|
314
|
+
default?: string[]; // step names to run if no route matches
|
|
315
|
+
timeoutMs?: number;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export interface ParallelStepGroup {
|
|
319
|
+
type: "parallel";
|
|
320
|
+
name: string;
|
|
321
|
+
description?: string;
|
|
322
|
+
barrier?: "all" | "any" | "majority"; // default "all"
|
|
323
|
+
steps: WorkflowStep[];
|
|
324
|
+
timeoutMs?: number;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export interface HitlStep {
|
|
328
|
+
type: "hitl";
|
|
329
|
+
name: string;
|
|
330
|
+
description?: string;
|
|
331
|
+
message: string; // human-readable prompt shown to approver
|
|
332
|
+
channel?: string; // notification target e.g. "slack:#approvals"
|
|
333
|
+
timeoutMs?: number; // how long to wait before applying onTimeout
|
|
334
|
+
onTimeout?: "skip" | "fail" | "use-default"; // default "fail"
|
|
335
|
+
defaultResponse?: string; // used when onTimeout is "use-default"
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export interface SubWorkflowStep {
|
|
339
|
+
type: "sub-workflow";
|
|
340
|
+
name: string;
|
|
341
|
+
description?: string;
|
|
342
|
+
workflow: string; // path to relay.yaml or registry name
|
|
343
|
+
vars?: Record<string, string>; // variable substitutions for the sub-workflow
|
|
344
|
+
timeoutMs?: number;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
4. Create a union type for any step or step group:
|
|
348
|
+
export type AnyWorkflowStep =
|
|
349
|
+
| WorkflowStep
|
|
350
|
+
| LoopStepGroup
|
|
351
|
+
| RouterStepGroup
|
|
352
|
+
| ParallelStepGroup
|
|
353
|
+
| HitlStep
|
|
354
|
+
| SubWorkflowStep;
|
|
355
|
+
|
|
356
|
+
5. Update WorkflowDefinition.steps to use AnyWorkflowStep[]:
|
|
357
|
+
steps: AnyWorkflowStep[];
|
|
358
|
+
|
|
359
|
+
6. Add session types:
|
|
360
|
+
export interface SessionConfig {
|
|
361
|
+
persist?: boolean; // default false
|
|
362
|
+
historyRuns?: number; // how many prior runs to inject as context
|
|
363
|
+
ttlMs?: number;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export interface SessionRow {
|
|
367
|
+
id: string;
|
|
368
|
+
workflowName: string;
|
|
369
|
+
runIds: string[];
|
|
370
|
+
stateSnapshot: Record<string, unknown>;
|
|
371
|
+
createdAt: string;
|
|
372
|
+
updatedAt: string;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
7. Add inputSchema to RelayYamlConfig:
|
|
376
|
+
inputSchema?: Record<string, unknown>; // JSON Schema object
|
|
377
|
+
|
|
378
|
+
8. Add session to RelayYamlConfig:
|
|
379
|
+
session?: SessionConfig;
|
|
380
|
+
|
|
381
|
+
9. Expand WorkflowEvent union in runner.ts — ADD to the existing union
|
|
382
|
+
(note: WorkflowEvent is defined in runner.ts, not types.ts, so create a
|
|
383
|
+
comment in types.ts pointing to runner.ts where these will be added):
|
|
384
|
+
// New events will be added to WorkflowEvent in runner.ts:
|
|
385
|
+
// loop:iteration-started, loop:iteration-completed, loop:ended,
|
|
386
|
+
// condition:evaluated, condition:skipped, router:evaluated,
|
|
387
|
+
// parallel:branch-started, parallel:branch-completed,
|
|
388
|
+
// hitl:paused, hitl:responded, hitl:timeout,
|
|
389
|
+
// subworkflow:started, subworkflow:completed,
|
|
390
|
+
// session:state-updated, validation:failed, fallback:agent-switched
|
|
391
|
+
|
|
392
|
+
10. Export all new types from ${INDEX_FILE}
|
|
393
|
+
|
|
394
|
+
Make ALL changes. Run: npx tsc --noEmit to verify no type errors.
|
|
395
|
+
End your output with: TYPES_COMPLETE
|
|
396
|
+
`,
|
|
397
|
+
dependsOn: ['spec-approval'],
|
|
398
|
+
retries: 2,
|
|
399
|
+
verification: { type: 'output_contains', value: 'TYPES_COMPLETE' },
|
|
400
|
+
})
|
|
401
|
+
.step('p1-json-schema', {
|
|
402
|
+
agent: 'schema-implementer',
|
|
403
|
+
task: `
|
|
404
|
+
Phase 1b: Update the JSON Schema to match the new TypeScript types.
|
|
405
|
+
|
|
406
|
+
Prior type changes:
|
|
407
|
+
{{steps.p1-type-system.output}}
|
|
408
|
+
|
|
409
|
+
YOUR TASK: Update ${SCHEMA_FILE} to add definitions for all new step group types.
|
|
410
|
+
|
|
411
|
+
Add new $defs entries:
|
|
412
|
+
- StepType enum definition
|
|
413
|
+
- LoopStepGroup object with required: [type, name, steps, maxIterations]
|
|
414
|
+
- RouterRoute object
|
|
415
|
+
- RouterStepGroup object with required: [type, name, selector, routes]
|
|
416
|
+
- ParallelStepGroup object with required: [type, name, steps]
|
|
417
|
+
- HitlStep object with required: [type, name, message]
|
|
418
|
+
- SubWorkflowStep object with required: [type, name, workflow]
|
|
419
|
+
- SessionConfig object
|
|
420
|
+
- AnyWorkflowStep as oneOf the above plus existing WorkflowStep
|
|
421
|
+
|
|
422
|
+
Update workflow.steps array to use anyOf: [WorkflowStep, AnyWorkflowStep]
|
|
423
|
+
Update top-level RelayYamlConfig to include inputSchema and session properties.
|
|
424
|
+
|
|
425
|
+
Also update packages/sdk/src/workflows/builder.ts:
|
|
426
|
+
- Add a new builder method for sessions:
|
|
427
|
+
session(config: SessionConfig): this
|
|
428
|
+
- Add a new builder method for input schema:
|
|
429
|
+
inputSchema(schema: Record<string, unknown>): this
|
|
430
|
+
|
|
431
|
+
Verify with: cat ${SCHEMA_FILE} | python3 -m json.tool (or equivalent JSON lint)
|
|
432
|
+
End your output with: SCHEMA_COMPLETE
|
|
433
|
+
`,
|
|
434
|
+
dependsOn: ['p1-type-system'],
|
|
435
|
+
retries: 2,
|
|
436
|
+
verification: { type: 'output_contains', value: 'SCHEMA_COMPLETE' },
|
|
437
|
+
})
|
|
438
|
+
.step('p1-lead-review', {
|
|
439
|
+
agent: 'lead',
|
|
440
|
+
task: `
|
|
441
|
+
Phase 1 Lead Review: Validate the type system and schema changes.
|
|
442
|
+
|
|
443
|
+
Type system changes:
|
|
444
|
+
{{steps.p1-type-system.output}}
|
|
445
|
+
|
|
446
|
+
Schema changes:
|
|
447
|
+
{{steps.p1-json-schema.output}}
|
|
448
|
+
|
|
449
|
+
REVIEW CRITERIA:
|
|
450
|
+
1. Do the new TypeScript interfaces correctly model the intended semantics?
|
|
451
|
+
2. Is the AnyWorkflowStep discriminated union correctly structured? Each variant
|
|
452
|
+
must have a unique "type" literal so TypeScript can narrow the type.
|
|
453
|
+
3. Does the WorkflowStep backward compatibility hold? (existing YAML with no
|
|
454
|
+
"type" field should still work — type defaults to "agent")
|
|
455
|
+
4. Are the new fields in RelayYamlConfig (inputSchema, session) properly optional?
|
|
456
|
+
5. Does the builder extension make sense? Are the new methods ergonomic?
|
|
457
|
+
6. Any naming inconsistencies between the TypeScript types and JSON Schema?
|
|
458
|
+
|
|
459
|
+
If you find issues, describe them specifically with file:line references.
|
|
460
|
+
If the phase is acceptable, state what should be fixed in the engine phase.
|
|
461
|
+
End with: PHASE_1_APPROVED (even if you request minor fixes — fixes go to
|
|
462
|
+
the code-reviewer, who will direct the implementer if needed)
|
|
463
|
+
`,
|
|
464
|
+
dependsOn: ['p1-json-schema'],
|
|
465
|
+
retries: 1,
|
|
466
|
+
verification: { type: 'output_contains', value: 'PHASE_1_APPROVED' },
|
|
467
|
+
})
|
|
468
|
+
.step('p1-code-review', {
|
|
469
|
+
agent: 'code-reviewer',
|
|
470
|
+
task: `
|
|
471
|
+
Phase 1 Independent Code Review: TypeScript type system extension.
|
|
472
|
+
(Running in parallel with lead review — review the code independently.)
|
|
473
|
+
|
|
474
|
+
JSON Schema changes:
|
|
475
|
+
{{steps.p1-json-schema.output}}
|
|
476
|
+
|
|
477
|
+
YOUR INDEPENDENT REVIEW of ${TYPES_FILE}, ${SCHEMA_FILE}, ${BUILDER_FILE}:
|
|
478
|
+
|
|
479
|
+
Check:
|
|
480
|
+
1. TypeScript strict-mode compliance — no implicit any, all fields typed
|
|
481
|
+
2. JSDoc on every exported interface and type
|
|
482
|
+
3. Correct use of discriminated unions (each variant's type field is a
|
|
483
|
+
string literal, not just string)
|
|
484
|
+
4. AnyWorkflowStep is correctly exported from ${INDEX_FILE}
|
|
485
|
+
5. Builder methods follow existing patterns (return this for chaining)
|
|
486
|
+
6. JSON Schema $defs are correctly referenced in anyOf arrays
|
|
487
|
+
7. No circular references in the type definitions
|
|
488
|
+
8. Run: npx tsc --noEmit from packages/sdk and confirm zero errors
|
|
489
|
+
|
|
490
|
+
List any issues found. For each: file, line (if known), problem, fix required.
|
|
491
|
+
If zero issues: explicitly state "No issues found."
|
|
492
|
+
End with: CODE_REVIEW_1_COMPLETE
|
|
493
|
+
`,
|
|
494
|
+
dependsOn: ['p1-json-schema'],
|
|
495
|
+
retries: 1,
|
|
496
|
+
verification: { type: 'output_contains', value: 'CODE_REVIEW_1_COMPLETE' },
|
|
497
|
+
})
|
|
498
|
+
// ── Phase 2: Execution Engine ─────────────────────────────────────────────
|
|
499
|
+
.step('p2-condition-loop', {
|
|
500
|
+
agent: 'engine-implementer',
|
|
501
|
+
task: `
|
|
502
|
+
Phase 2a: Implement condition + loop execution in the WorkflowRunner.
|
|
503
|
+
|
|
504
|
+
Phase 1 review context:
|
|
505
|
+
{{steps.p1-code-review.output}}
|
|
506
|
+
|
|
507
|
+
YOUR TASK: Modify ${RUNNER_FILE} to handle condition and loop step types.
|
|
508
|
+
|
|
509
|
+
CONDITION STEP EXECUTION:
|
|
510
|
+
In the executeStep method (or wherever individual steps are dispatched):
|
|
511
|
+
1. Check if step.type === "condition" OR if step.condition is set on a regular step
|
|
512
|
+
2. If step.condition exists, evaluate the CEL expression using a lightweight
|
|
513
|
+
evaluator. Use the cel-js npm package (add to package.json if not present)
|
|
514
|
+
OR implement a minimal evaluator that handles:
|
|
515
|
+
- String contains: "X in Y.output"
|
|
516
|
+
- String equality: "steps.X.output == 'VALUE'"
|
|
517
|
+
- Boolean AND/OR
|
|
518
|
+
The expression context object should include:
|
|
519
|
+
{ steps: Record<name, { output: string, status: string }>,
|
|
520
|
+
vars: VariableContext }
|
|
521
|
+
3. If condition evaluates to false: mark step as "skipped", emit step:skipped event
|
|
522
|
+
4. If condition evaluates to true (or no condition): proceed normally
|
|
523
|
+
|
|
524
|
+
Also add to WorkflowEvent union in runner.ts:
|
|
525
|
+
| { type: "condition:evaluated"; runId: string; stepName: string; result: boolean; expression: string }
|
|
526
|
+
| { type: "condition:skipped"; runId: string; stepName: string }
|
|
527
|
+
|
|
528
|
+
LOOP STEP GROUP EXECUTION:
|
|
529
|
+
Add a new method: private async executeLoopGroup(loop: LoopStepGroup, ...): Promise<string>
|
|
530
|
+
1. Run loop.steps sequentially up to loop.maxIterations times
|
|
531
|
+
2. After each iteration, evaluate loop.until CEL expression if provided
|
|
532
|
+
3. If until evaluates to true, break and return last step output
|
|
533
|
+
4. If maxIterations reached without satisfaction, fail with descriptive error
|
|
534
|
+
5. Track step outputs by iteration: steps in context include "loop.STEP.output"
|
|
535
|
+
for the current iteration
|
|
536
|
+
|
|
537
|
+
Add to WorkflowEvent:
|
|
538
|
+
| { type: "loop:iteration-started"; runId: string; loopName: string; iteration: number }
|
|
539
|
+
| { type: "loop:iteration-completed"; runId: string; loopName: string; iteration: number; continuing: boolean }
|
|
540
|
+
| { type: "loop:ended"; runId: string; loopName: string; reason: "condition-met" | "max-iterations" }
|
|
541
|
+
|
|
542
|
+
In the main execution loop (findReadySteps / executeSteps), detect when a step
|
|
543
|
+
is a LoopStepGroup and route it to executeLoopGroup.
|
|
544
|
+
|
|
545
|
+
Run: npx tsc --noEmit to verify.
|
|
546
|
+
End your output with: CONDITION_LOOP_COMPLETE
|
|
547
|
+
`,
|
|
548
|
+
dependsOn: ['p1-code-review'],
|
|
549
|
+
retries: 2,
|
|
550
|
+
verification: { type: 'output_contains', value: 'CONDITION_LOOP_COMPLETE' },
|
|
551
|
+
})
|
|
552
|
+
.step('p2-input-validation', {
|
|
553
|
+
agent: 'schema-implementer',
|
|
554
|
+
task: `
|
|
555
|
+
Phase 2b: Implement input schema validation (runs in parallel with p2-condition-loop).
|
|
556
|
+
|
|
557
|
+
Phase 1 review context:
|
|
558
|
+
{{steps.p1-code-review.output}}
|
|
559
|
+
|
|
560
|
+
YOUR TASK: Add input schema validation to WorkflowRunner before execution starts.
|
|
561
|
+
|
|
562
|
+
In ${RUNNER_FILE}, in the execute() method, BEFORE the first findReadySteps call:
|
|
563
|
+
|
|
564
|
+
1. If config.inputSchema is defined:
|
|
565
|
+
a. Import or inline a minimal JSON Schema validator. Use the ajv npm package
|
|
566
|
+
(add to package.json dependencies if not present).
|
|
567
|
+
b. Compile the schema: const validate = ajv.compile(config.inputSchema)
|
|
568
|
+
c. Validate the vars object against the schema
|
|
569
|
+
d. If invalid: emit { type: "validation:failed", runId, errors: AjvError[] }
|
|
570
|
+
and throw WorkflowValidationError with a human-readable message listing
|
|
571
|
+
each field error
|
|
572
|
+
|
|
573
|
+
2. Add WorkflowValidationError class (extends Error) to runner.ts:
|
|
574
|
+
export class WorkflowValidationError extends Error {
|
|
575
|
+
constructor(public readonly errors: unknown[]) {
|
|
576
|
+
super("Workflow input validation failed: " + JSON.stringify(errors));
|
|
577
|
+
this.name = "WorkflowValidationError";
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
3. Add to WorkflowEvent:
|
|
582
|
+
| { type: "validation:failed"; runId: string; errors: unknown[] }
|
|
583
|
+
| { type: "validation:passed"; runId: string }
|
|
584
|
+
|
|
585
|
+
4. Export WorkflowValidationError from ${INDEX_FILE}
|
|
586
|
+
|
|
587
|
+
5. Update builder.ts: add the inputSchema method to WorkflowBuilder that sets
|
|
588
|
+
config.inputSchema (this was noted in Phase 1 but implement it now if not done)
|
|
589
|
+
|
|
590
|
+
Run: npx tsc --noEmit to verify.
|
|
591
|
+
End your output with: INPUT_VALIDATION_COMPLETE
|
|
592
|
+
`,
|
|
593
|
+
dependsOn: ['p1-code-review'],
|
|
594
|
+
retries: 2,
|
|
595
|
+
verification: { type: 'output_contains', value: 'INPUT_VALIDATION_COMPLETE' },
|
|
596
|
+
})
|
|
597
|
+
.step('p2-router-hitl', {
|
|
598
|
+
agent: 'engine-implementer',
|
|
599
|
+
task: `
|
|
600
|
+
Phase 2c: Implement router + HITL step execution.
|
|
601
|
+
|
|
602
|
+
Condition/loop implementation:
|
|
603
|
+
{{steps.p2-condition-loop.output}}
|
|
604
|
+
|
|
605
|
+
YOUR TASK: Add router and HITL execution to ${RUNNER_FILE}.
|
|
606
|
+
|
|
607
|
+
ROUTER STEP GROUP EXECUTION:
|
|
608
|
+
Add: private async executeRouterGroup(router: RouterStepGroup, context, ...): Promise<string>
|
|
609
|
+
1. Evaluate router.selector CEL expression to get a string value
|
|
610
|
+
2. Iterate router.routes, find first route where match expression is true
|
|
611
|
+
(match can be substring check: selectorValue.includes(route.match) or
|
|
612
|
+
full CEL evaluation depending on complexity)
|
|
613
|
+
3. Collect the branch step names to execute (from route.steps or router.default)
|
|
614
|
+
4. Execute those steps (they must exist in the parent workflow's step list)
|
|
615
|
+
5. Return concatenated outputs of the executed branch steps
|
|
616
|
+
|
|
617
|
+
Add to WorkflowEvent:
|
|
618
|
+
| { type: "router:evaluated"; runId: string; routerName: string; selectorValue: string; matchedRoute: string | null }
|
|
619
|
+
| { type: "router:branch-started"; runId: string; routerName: string; stepName: string }
|
|
620
|
+
|
|
621
|
+
HITL STEP EXECUTION:
|
|
622
|
+
Add: private async executeHitlStep(hitl: HitlStep, runId: string, ...): Promise<string>
|
|
623
|
+
1. Write a file: {summaryDir}/{runId}/hitl-{hitl.name}-pending.json containing:
|
|
624
|
+
{ runId, stepName: hitl.name, message: hitl.message, channel: hitl.channel,
|
|
625
|
+
pendingSince: ISO timestamp, timeoutMs: hitl.timeoutMs }
|
|
626
|
+
2. Emit { type: "hitl:paused", runId, stepName, message, channel }
|
|
627
|
+
3. Poll every 5 seconds for a response file:
|
|
628
|
+
{summaryDir}/{runId}/hitl-{hitl.name}-response.json
|
|
629
|
+
The response file should contain: { response: string, respondedBy?: string }
|
|
630
|
+
4. If timeoutMs elapses without response:
|
|
631
|
+
- If onTimeout === "skip": mark step skipped, emit hitl:timeout, return ""
|
|
632
|
+
- If onTimeout === "use-default": emit hitl:timeout, return hitl.defaultResponse ?? ""
|
|
633
|
+
- Otherwise (default "fail"): throw Error("HITL step timed out")
|
|
634
|
+
5. On response: delete pending file, emit hitl:responded, return response.response
|
|
635
|
+
|
|
636
|
+
Add to WorkflowEvent:
|
|
637
|
+
| { type: "hitl:paused"; runId: string; stepName: string; message: string; channel?: string }
|
|
638
|
+
| { type: "hitl:responded"; runId: string; stepName: string; response: string; respondedBy?: string }
|
|
639
|
+
| { type: "hitl:timeout"; runId: string; stepName: string; action: string }
|
|
640
|
+
|
|
641
|
+
Run: npx tsc --noEmit
|
|
642
|
+
End your output with: ROUTER_HITL_COMPLETE
|
|
643
|
+
`,
|
|
644
|
+
dependsOn: ['p2-condition-loop'],
|
|
645
|
+
retries: 2,
|
|
646
|
+
verification: { type: 'output_contains', value: 'ROUTER_HITL_COMPLETE' },
|
|
647
|
+
})
|
|
648
|
+
.step('p2-session-fallback', {
|
|
649
|
+
agent: 'engine-implementer',
|
|
650
|
+
task: `
|
|
651
|
+
Phase 2d: Implement session concept and fallback agent switching.
|
|
652
|
+
|
|
653
|
+
Router/HITL implementation:
|
|
654
|
+
{{steps.p2-router-hitl.output}}
|
|
655
|
+
|
|
656
|
+
YOUR TASK: Extend WorkflowRunner with session support and fallback agents.
|
|
657
|
+
|
|
658
|
+
SESSION CONCEPT:
|
|
659
|
+
A session groups multiple workflow runs, shares state across them, and can
|
|
660
|
+
inject prior run outputs as context into new runs.
|
|
661
|
+
|
|
662
|
+
1. Extend WorkflowRunnerOptions in runner.ts:
|
|
663
|
+
sessionId?: string; // if provided, this run joins a named session
|
|
664
|
+
|
|
665
|
+
2. Add a simple SessionStore (in-memory, backed by WorkflowDb if available):
|
|
666
|
+
- getSession(sessionId): returns { runIds: string[], stateSnapshot: Record }
|
|
667
|
+
- addRunToSession(sessionId, runId): appends runId to session run list
|
|
668
|
+
- getSessionHistory(sessionId, n): returns last n run outputs as context string
|
|
669
|
+
|
|
670
|
+
3. In execute(), if sessionId is provided:
|
|
671
|
+
a. Load session history (last config.session?.historyRuns ?? 3 runs)
|
|
672
|
+
b. Prepend history context to the first step's task:
|
|
673
|
+
"[Session history - prior runs]\n{history}\n[Current task]\n{task}"
|
|
674
|
+
c. After run completes, call addRunToSession(sessionId, runId)
|
|
675
|
+
|
|
676
|
+
4. Add to WorkflowEvent:
|
|
677
|
+
| { type: "session:run-added"; sessionId: string; runId: string }
|
|
678
|
+
| { type: "session:history-injected"; sessionId: string; historyRuns: number }
|
|
679
|
+
|
|
680
|
+
FALLBACK AGENT:
|
|
681
|
+
In the step execution method, after all retries on the primary agent are
|
|
682
|
+
exhausted:
|
|
683
|
+
1. Check if step.fallbackAgent is defined
|
|
684
|
+
2. If yes: look up the fallback agent definition by name from config.agents
|
|
685
|
+
3. Re-attempt execution once with the fallback agent
|
|
686
|
+
4. Emit { type: "fallback:agent-switched"; runId, stepName, fromAgent, toAgent }
|
|
687
|
+
5. If fallback also fails: mark step as failed
|
|
688
|
+
|
|
689
|
+
Add to WorkflowEvent:
|
|
690
|
+
| { type: "fallback:agent-switched"; runId: string; stepName: string; fromAgent: string; toAgent: string }
|
|
691
|
+
|
|
692
|
+
Also implement the flow shorthand parser (if RelayYamlConfig.flow is set):
|
|
693
|
+
parseFlowString("A -> B, C -> D"): WorkflowStep[]
|
|
694
|
+
- "A -> B" means B.dependsOn = [A]
|
|
695
|
+
- "B, C" means B and C are in parallel (no dependency between them)
|
|
696
|
+
- "A, B -> C" means C.dependsOn = [A, B]
|
|
697
|
+
- Call this in execute() before building the step graph if config.flow is set
|
|
698
|
+
|
|
699
|
+
Run: npx tsc --noEmit
|
|
700
|
+
End your output with: SESSION_FALLBACK_COMPLETE
|
|
701
|
+
`,
|
|
702
|
+
dependsOn: ['p2-router-hitl'],
|
|
703
|
+
retries: 2,
|
|
704
|
+
verification: { type: 'output_contains', value: 'SESSION_FALLBACK_COMPLETE' },
|
|
705
|
+
})
|
|
706
|
+
.step('p2-lead-review', {
|
|
707
|
+
agent: 'lead',
|
|
708
|
+
task: `
|
|
709
|
+
Phase 2 Lead Review: Execution engine implementation.
|
|
710
|
+
|
|
711
|
+
Input validation:
|
|
712
|
+
{{steps.p2-input-validation.output}}
|
|
713
|
+
|
|
714
|
+
Session + fallback:
|
|
715
|
+
{{steps.p2-session-fallback.output}}
|
|
716
|
+
|
|
717
|
+
REVIEW:
|
|
718
|
+
1. Is the CEL condition evaluator robust enough? Does it handle the common
|
|
719
|
+
patterns we need (output contains, equality, AND/OR)?
|
|
720
|
+
2. Is the loop execution correctly isolated — do step names inside a loop
|
|
721
|
+
conflict with top-level step names in the output context?
|
|
722
|
+
3. Is the HITL polling approach acceptable? (file-based polling every 5s)
|
|
723
|
+
Or should it use a simple readline/stdin approach instead?
|
|
724
|
+
4. Does the session history injection make sense — will agents be overwhelmed
|
|
725
|
+
by injected context if historyRuns is large?
|
|
726
|
+
5. Is the flow string parser correct for all cases:
|
|
727
|
+
"A -> B, C -> D" and "A, B -> C"?
|
|
728
|
+
6. Are all new WorkflowEvent variants added to the union consistently?
|
|
729
|
+
|
|
730
|
+
Note any critical fixes needed before Phase 3 begins.
|
|
731
|
+
End with: PHASE_2_APPROVED
|
|
732
|
+
`,
|
|
733
|
+
dependsOn: ['p2-session-fallback', 'p2-input-validation'],
|
|
734
|
+
retries: 1,
|
|
735
|
+
verification: { type: 'output_contains', value: 'PHASE_2_APPROVED' },
|
|
736
|
+
})
|
|
737
|
+
.step('p2-code-review', {
|
|
738
|
+
agent: 'code-reviewer',
|
|
739
|
+
task: `
|
|
740
|
+
Phase 2 Independent Code Review: Execution engine.
|
|
741
|
+
|
|
742
|
+
Lead's review:
|
|
743
|
+
{{steps.p2-lead-review.output}}
|
|
744
|
+
|
|
745
|
+
INDEPENDENT REVIEW of ${RUNNER_FILE}:
|
|
746
|
+
|
|
747
|
+
1. Correctness: does the condition evaluator correctly handle edge cases
|
|
748
|
+
(undefined step output, empty strings, non-boolean CEL results)?
|
|
749
|
+
2. Loop safety: is there a guard against infinite loops if maxIterations is 0?
|
|
750
|
+
3. HITL polling: does the polling correctly clean up pending files on both
|
|
751
|
+
success and timeout paths? No file handle leaks?
|
|
752
|
+
4. Fallback agent: is the agent definition lookup null-safe? What if
|
|
753
|
+
fallbackAgent name doesn't exist in config.agents?
|
|
754
|
+
5. Session history injection: is it correctly skipped when sessionId is absent?
|
|
755
|
+
6. Flow parser: does it handle edge cases — empty string, single agent,
|
|
756
|
+
spaces around arrows?
|
|
757
|
+
7. TypeScript: run npx tsc --noEmit and report results
|
|
758
|
+
8. Existing tests: run npx jest packages/sdk and report pass/fail counts
|
|
759
|
+
|
|
760
|
+
List all issues with specific fixes. State "No issues" for clean sections.
|
|
761
|
+
End with: CODE_REVIEW_2_COMPLETE
|
|
762
|
+
`,
|
|
763
|
+
dependsOn: ['p2-lead-review'],
|
|
764
|
+
retries: 1,
|
|
765
|
+
verification: { type: 'output_contains', value: 'CODE_REVIEW_2_COMPLETE' },
|
|
766
|
+
})
|
|
767
|
+
// ── Phase 3: Meta-Orchestration (parallel with Phase 4) ───────────────────
|
|
768
|
+
.step('p3-sub-workflow', {
|
|
769
|
+
agent: 'meta-implementer',
|
|
770
|
+
task: `
|
|
771
|
+
Phase 3a: Implement sub-workflow step execution.
|
|
772
|
+
|
|
773
|
+
Phase 2 review context:
|
|
774
|
+
{{steps.p2-code-review.output}}
|
|
775
|
+
|
|
776
|
+
YOUR TASK: Add sub-workflow composition to ${RUNNER_FILE}.
|
|
777
|
+
|
|
778
|
+
SUB-WORKFLOW EXECUTION:
|
|
779
|
+
Add: private async executeSubWorkflow(step: SubWorkflowStep, vars, runId, ...): Promise<string>
|
|
780
|
+
1. Resolve the workflow reference:
|
|
781
|
+
- If step.workflow starts with "./" or "/": treat as file path
|
|
782
|
+
- Otherwise: look up in the workflow registry at
|
|
783
|
+
~/.agent-relay/workflow-registry.json (create if absent; plain JSON map)
|
|
784
|
+
2. Load and parse the referenced relay.yaml file
|
|
785
|
+
3. Merge step.vars with the current run's vars (step.vars take precedence)
|
|
786
|
+
4. Create a new WorkflowRunner instance (child runner), share the same DB
|
|
787
|
+
5. Execute the sub-workflow config via child runner
|
|
788
|
+
6. Return the sub-workflow's run output (join all step outputs)
|
|
789
|
+
|
|
790
|
+
Add to WorkflowEvent:
|
|
791
|
+
| { type: "subworkflow:started"; runId: string; stepName: string; workflowRef: string }
|
|
792
|
+
| { type: "subworkflow:completed"; runId: string; stepName: string; output: string }
|
|
793
|
+
| { type: "subworkflow:failed"; runId: string; stepName: string; error: string }
|
|
794
|
+
|
|
795
|
+
WORKFLOW REGISTRY:
|
|
796
|
+
Create packages/sdk/src/workflows/registry.ts:
|
|
797
|
+
|
|
798
|
+
export interface WorkflowRegistryEntry {
|
|
799
|
+
name: string;
|
|
800
|
+
path: string;
|
|
801
|
+
description?: string;
|
|
802
|
+
tags?: string[];
|
|
803
|
+
installedAt: string;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
export class WorkflowRegistry {
|
|
807
|
+
private readonly registryPath: string;
|
|
808
|
+
// Load registry from ~/.agent-relay/workflow-registry.json
|
|
809
|
+
async list(): Promise<WorkflowRegistryEntry[]>
|
|
810
|
+
async get(name: string): Promise<WorkflowRegistryEntry | null>
|
|
811
|
+
async register(entry: WorkflowRegistryEntry): Promise<void>
|
|
812
|
+
async unregister(name: string): Promise<void>
|
|
813
|
+
async resolvePath(nameOrPath: string): Promise<string>
|
|
814
|
+
// If starts with ./ or / return as-is; else look up in registry
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
Export WorkflowRegistry from ${INDEX_FILE}.
|
|
818
|
+
|
|
819
|
+
Run: npx tsc --noEmit
|
|
820
|
+
End with: SUB_WORKFLOW_COMPLETE
|
|
821
|
+
`,
|
|
822
|
+
dependsOn: ['p2-code-review'],
|
|
823
|
+
retries: 2,
|
|
824
|
+
verification: { type: 'output_contains', value: 'SUB_WORKFLOW_COMPLETE' },
|
|
825
|
+
})
|
|
826
|
+
.step('p3-auto-builder', {
|
|
827
|
+
agent: 'meta-implementer',
|
|
828
|
+
task: `
|
|
829
|
+
Phase 3b: Implement AutoWorkflowBuilder.
|
|
830
|
+
|
|
831
|
+
Sub-workflow implementation:
|
|
832
|
+
{{steps.p3-sub-workflow.output}}
|
|
833
|
+
|
|
834
|
+
YOUR TASK: Create packages/sdk/src/workflows/auto-builder.ts
|
|
835
|
+
|
|
836
|
+
The AutoWorkflowBuilder analyzes a task description and uses a meta-agent
|
|
837
|
+
to generate a complete RelayYamlConfig automatically.
|
|
838
|
+
|
|
839
|
+
export interface AutoBuildOptions {
|
|
840
|
+
availableClis?: AgentCli[]; // defaults to ["claude", "codex"]
|
|
841
|
+
maxAgents?: number; // defaults to 5
|
|
842
|
+
maxSteps?: number; // defaults to 10
|
|
843
|
+
preferredPattern?: SwarmPattern;
|
|
844
|
+
dryRun?: boolean; // if true, return config without executing
|
|
845
|
+
metaCli?: AgentCli; // CLI to use for the meta-agent (default "claude")
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
export interface AutoBuildResult {
|
|
849
|
+
config: RelayYamlConfig;
|
|
850
|
+
yaml: string;
|
|
851
|
+
reasoning: string; // why the meta-agent chose this structure
|
|
852
|
+
run?: WorkflowRunRow; // present if dryRun is false
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
export class AutoWorkflowBuilder {
|
|
856
|
+
constructor(private readonly options: AutoBuildOptions = {}) {}
|
|
857
|
+
|
|
858
|
+
async build(task: string): Promise<AutoBuildResult> {
|
|
859
|
+
// 1. Construct a meta-prompt that instructs the meta-agent to:
|
|
860
|
+
// a. Analyze the task
|
|
861
|
+
// b. Select the best swarm pattern
|
|
862
|
+
// c. Define agents (using available CLIs)
|
|
863
|
+
// d. Define workflow steps with appropriate dependencies
|
|
864
|
+
// e. Output a valid relay.yaml string (fenced in \`\`\`yaml...\`\`\`)
|
|
865
|
+
// f. Explain its reasoning
|
|
866
|
+
// 2. Spawn the meta-agent via AgentRelay
|
|
867
|
+
// 3. Extract the YAML from the response (find \`\`\`yaml ... \`\`\` block)
|
|
868
|
+
// 4. Parse and validate the config using WorkflowRunner.parseYamlString
|
|
869
|
+
// 5. If dryRun: return { config, yaml, reasoning }
|
|
870
|
+
// 6. Else: execute via WorkflowRunner and return { config, yaml, reasoning, run }
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Convenience: build and run immediately
|
|
874
|
+
async run(task: string): Promise<WorkflowRunRow> {
|
|
875
|
+
const result = await this.build(task);
|
|
876
|
+
if (!result.run) throw new Error("Set dryRun: false to execute");
|
|
877
|
+
return result.run;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// Convenience export
|
|
882
|
+
export async function autoWorkflow(task: string, options?: AutoBuildOptions): Promise<AutoBuildResult> {
|
|
883
|
+
return new AutoWorkflowBuilder(options).build(task);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
Export AutoWorkflowBuilder and autoWorkflow from ${INDEX_FILE}.
|
|
887
|
+
|
|
888
|
+
Also add the meta-workflow type to types.ts if not already done:
|
|
889
|
+
export type MetaWorkflowConfig = RelayYamlConfig & { type: "meta-workflow" };
|
|
890
|
+
(A meta-workflow is a relay.yaml where steps are sub-workflow steps)
|
|
891
|
+
|
|
892
|
+
Run: npx tsc --noEmit
|
|
893
|
+
End with: AUTO_BUILDER_COMPLETE
|
|
894
|
+
`,
|
|
895
|
+
dependsOn: ['p3-sub-workflow'],
|
|
896
|
+
retries: 2,
|
|
897
|
+
verification: { type: 'output_contains', value: 'AUTO_BUILDER_COMPLETE' },
|
|
898
|
+
})
|
|
899
|
+
// ── Phase 4: Storage Backends (parallel with Phase 3) ────────────────────
|
|
900
|
+
.step('p4-db-adapters', {
|
|
901
|
+
agent: 'storage-implementer',
|
|
902
|
+
task: `
|
|
903
|
+
Phase 4: Implement production-ready WorkflowDb adapters.
|
|
904
|
+
(Runs in parallel with Phase 3 — no dependency between them.)
|
|
905
|
+
|
|
906
|
+
Phase 2 review context:
|
|
907
|
+
{{steps.p2-code-review.output}}
|
|
908
|
+
|
|
909
|
+
Create a new directory: packages/sdk/src/workflows/db/
|
|
910
|
+
|
|
911
|
+
Create these files:
|
|
912
|
+
|
|
913
|
+
1. packages/sdk/src/workflows/db/postgres.ts
|
|
914
|
+
export class PostgresWorkflowDb implements WorkflowDb {
|
|
915
|
+
constructor(options: { connectionString: string; tablePrefix?: string })
|
|
916
|
+
// Creates tables on first connect if they don't exist:
|
|
917
|
+
// {prefix}workflow_runs, {prefix}workflow_steps
|
|
918
|
+
// Schema mirrors WorkflowRunRow and WorkflowStepRow
|
|
919
|
+
// Uses node-postgres (pg) — add to package.json if absent
|
|
920
|
+
async insertRun(run: WorkflowRunRow): Promise<void>
|
|
921
|
+
async updateRun(id: string, patch: Partial<WorkflowRunRow>): Promise<void>
|
|
922
|
+
async getRun(id: string): Promise<WorkflowRunRow | null>
|
|
923
|
+
async insertStep(step: WorkflowStepRow): Promise<void>
|
|
924
|
+
async updateStep(id: string, patch: Partial<WorkflowStepRow>): Promise<void>
|
|
925
|
+
async getStepsByRunId(runId: string): Promise<WorkflowStepRow[]>
|
|
926
|
+
async close(): Promise<void>
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
2. packages/sdk/src/workflows/db/sqlite.ts
|
|
930
|
+
export class SqliteWorkflowDb implements WorkflowDb {
|
|
931
|
+
constructor(options: { path: string; tablePrefix?: string })
|
|
932
|
+
// Uses better-sqlite3 — synchronous API wrapped in async methods
|
|
933
|
+
// Same schema as PostgresWorkflowDb
|
|
934
|
+
// Creates tables if they don't exist on construction
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
3. packages/sdk/src/workflows/db/redis.ts
|
|
938
|
+
export class RedisWorkflowDb implements WorkflowDb {
|
|
939
|
+
constructor(options: {
|
|
940
|
+
url: string;
|
|
941
|
+
keyPrefix?: string; // default "relay:workflow:"
|
|
942
|
+
runTtlMs?: number; // optional TTL on run records
|
|
943
|
+
})
|
|
944
|
+
// Uses ioredis — add to package.json if absent
|
|
945
|
+
// Stores runs as JSON strings at key: {prefix}run:{id}
|
|
946
|
+
// Stores step lists at key: {prefix}steps:{runId} (Redis list)
|
|
947
|
+
// Stores individual steps at: {prefix}step:{id}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
4. packages/sdk/src/workflows/db/index.ts
|
|
951
|
+
export { PostgresWorkflowDb } from './postgres.js';
|
|
952
|
+
export { SqliteWorkflowDb } from './sqlite.js';
|
|
953
|
+
export { RedisWorkflowDb } from './redis.js';
|
|
954
|
+
|
|
955
|
+
5. Update packages/sdk/src/workflows/index.ts to export from db/:
|
|
956
|
+
export * from './db/index.js';
|
|
957
|
+
|
|
958
|
+
IMPORTANT: Add the three packages to package.json as optional peer dependencies
|
|
959
|
+
with peerDependenciesMeta markings optional: true, so users only need to install
|
|
960
|
+
the adapter they use.
|
|
961
|
+
|
|
962
|
+
Run: npx tsc --noEmit (adapters will have type errors only if packages are absent;
|
|
963
|
+
mark them as type-only imports with // @ts-expect-error if needed with clear comment)
|
|
964
|
+
End with: DB_ADAPTERS_COMPLETE
|
|
965
|
+
`,
|
|
966
|
+
dependsOn: ['p2-code-review'],
|
|
967
|
+
retries: 2,
|
|
968
|
+
verification: { type: 'output_contains', value: 'DB_ADAPTERS_COMPLETE' },
|
|
969
|
+
})
|
|
970
|
+
// ── Phase 3+4 Combined Review ─────────────────────────────────────────────
|
|
971
|
+
.step('p34-lead-review', {
|
|
972
|
+
agent: 'lead',
|
|
973
|
+
task: `
|
|
974
|
+
Phase 3+4 Lead Review: Meta-orchestration and storage backends.
|
|
975
|
+
|
|
976
|
+
Phase 3 — AutoBuilder:
|
|
977
|
+
{{steps.p3-auto-builder.output}}
|
|
978
|
+
|
|
979
|
+
Phase 4 — DB adapters:
|
|
980
|
+
{{steps.p4-db-adapters.output}}
|
|
981
|
+
|
|
982
|
+
REVIEW:
|
|
983
|
+
|
|
984
|
+
META-ORCHESTRATION:
|
|
985
|
+
1. Is the sub-workflow execution correctly isolated? (child runner should not
|
|
986
|
+
share the parent runner's event listeners, but should share the DB)
|
|
987
|
+
2. Does the WorkflowRegistry file path (~/.agent-relay/) correctly use os.homedir()?
|
|
988
|
+
3. Is the AutoWorkflowBuilder meta-prompt clear enough to reliably generate
|
|
989
|
+
valid relay.yaml configs? What guardrails are needed?
|
|
990
|
+
4. Does the YAML extraction from meta-agent output handle cases where the
|
|
991
|
+
agent outputs multiple code blocks?
|
|
992
|
+
|
|
993
|
+
DB ADAPTERS:
|
|
994
|
+
5. Are the SQL schemas in PostgresWorkflowDb and SqliteWorkflowDb correct?
|
|
995
|
+
(JSON columns for config/stateSnapshot, TEXT for status, ISO timestamps)
|
|
996
|
+
6. Is the Redis adapter correctly handling concurrent updates?
|
|
997
|
+
(Multiple parallel steps updating the same run record — race condition risk)
|
|
998
|
+
7. Are optional peer dependencies correctly marked in package.json?
|
|
999
|
+
8. Do the adapters correctly handle NULL/missing optional fields?
|
|
1000
|
+
|
|
1001
|
+
State fixes needed for the code reviewer to validate.
|
|
1002
|
+
End with: PHASE_34_APPROVED
|
|
1003
|
+
`,
|
|
1004
|
+
dependsOn: ['p3-auto-builder', 'p4-db-adapters'],
|
|
1005
|
+
retries: 1,
|
|
1006
|
+
verification: { type: 'output_contains', value: 'PHASE_34_APPROVED' },
|
|
1007
|
+
})
|
|
1008
|
+
.step('p34-code-review', {
|
|
1009
|
+
agent: 'code-reviewer',
|
|
1010
|
+
task: `
|
|
1011
|
+
Phase 3+4 Independent Code Review.
|
|
1012
|
+
|
|
1013
|
+
Lead's notes:
|
|
1014
|
+
{{steps.p34-lead-review.output}}
|
|
1015
|
+
|
|
1016
|
+
REVIEW packages/sdk/src/workflows/registry.ts,
|
|
1017
|
+
packages/sdk/src/workflows/auto-builder.ts,
|
|
1018
|
+
packages/sdk/src/workflows/db/:
|
|
1019
|
+
|
|
1020
|
+
1. Registry: does resolvePath correctly return file paths unchanged and look up
|
|
1021
|
+
by name for non-path strings? Is the registry JSON correctly pretty-printed?
|
|
1022
|
+
2. AutoBuilder: is the meta-prompt templating safe from injection if the task
|
|
1023
|
+
string contains special characters or YAML-like content?
|
|
1024
|
+
3. PostgresWorkflowDb: are SQL queries parameterized? (No string interpolation
|
|
1025
|
+
in SQL — security requirement)
|
|
1026
|
+
4. SqliteWorkflowDb: does the sync API wrap correctly without blocking the event
|
|
1027
|
+
loop for extended periods?
|
|
1028
|
+
5. RedisWorkflowDb: is the JSON serialization of WorkflowRunRow round-trip safe?
|
|
1029
|
+
(dates become strings, Record<string,unknown> stays correct)
|
|
1030
|
+
6. All three adapters: does getStepsByRunId return steps in consistent order?
|
|
1031
|
+
7. Run npx tsc --noEmit and report results
|
|
1032
|
+
|
|
1033
|
+
List issues with specific file + line references.
|
|
1034
|
+
End with: CODE_REVIEW_34_COMPLETE
|
|
1035
|
+
`,
|
|
1036
|
+
dependsOn: ['p34-lead-review'],
|
|
1037
|
+
retries: 1,
|
|
1038
|
+
verification: { type: 'output_contains', value: 'CODE_REVIEW_34_COMPLETE' },
|
|
1039
|
+
})
|
|
1040
|
+
// ── Phase 5: Deployment and Observability ─────────────────────────────────
|
|
1041
|
+
.step('p5-serve-command', {
|
|
1042
|
+
agent: 'deploy-implementer',
|
|
1043
|
+
task: `
|
|
1044
|
+
Phase 5a: Implement "relay workflow serve" HTTP server.
|
|
1045
|
+
|
|
1046
|
+
Phase 3+4 review context:
|
|
1047
|
+
{{steps.p34-code-review.output}}
|
|
1048
|
+
|
|
1049
|
+
YOUR TASK: Create packages/sdk/src/workflows/server.ts
|
|
1050
|
+
|
|
1051
|
+
Implement a lightweight HTTP server (use Node.js built-in http module — no
|
|
1052
|
+
Express/Fastify dependency) that exposes:
|
|
1053
|
+
|
|
1054
|
+
POST /run
|
|
1055
|
+
Body: { workflowPath: string, vars?: VariableContext, sessionId?: string }
|
|
1056
|
+
Response: { runId: string, status: "started" }
|
|
1057
|
+
Behavior: parse the relay.yaml at workflowPath, start execution (non-blocking),
|
|
1058
|
+
return runId immediately
|
|
1059
|
+
|
|
1060
|
+
GET /runs/:runId
|
|
1061
|
+
Response: WorkflowRunRow | { error: "not found" }
|
|
1062
|
+
|
|
1063
|
+
GET /runs/:runId/events
|
|
1064
|
+
Response: text/event-stream (Server-Sent Events)
|
|
1065
|
+
Each WorkflowEvent becomes a SSE data: {json}\n\n line
|
|
1066
|
+
Connection stays open until run completes or client disconnects
|
|
1067
|
+
|
|
1068
|
+
POST /runs/:runId/hitl/:stepName/respond
|
|
1069
|
+
Body: { response: string, respondedBy?: string }
|
|
1070
|
+
Behavior: write {summaryDir}/{runId}/hitl-{stepName}-response.json
|
|
1071
|
+
Response: { ok: true }
|
|
1072
|
+
|
|
1073
|
+
POST /runs/:runId/abort
|
|
1074
|
+
Behavior: call runner.abort(runId) if supported
|
|
1075
|
+
Response: { ok: true }
|
|
1076
|
+
|
|
1077
|
+
GET /health
|
|
1078
|
+
Response: { status: "ok", uptime: process.uptime() }
|
|
1079
|
+
|
|
1080
|
+
Also create packages/sdk/src/workflows/serve.ts — the CLI entry point:
|
|
1081
|
+
export async function serveWorkflows(options: {
|
|
1082
|
+
port?: number; // default 3747
|
|
1083
|
+
host?: string; // default "0.0.0.0"
|
|
1084
|
+
db?: WorkflowDb;
|
|
1085
|
+
}): Promise<void>
|
|
1086
|
+
|
|
1087
|
+
Export serveWorkflows from ${INDEX_FILE}.
|
|
1088
|
+
|
|
1089
|
+
The serve command will be integrated into the relay CLI in the next step.
|
|
1090
|
+
|
|
1091
|
+
Run: npx tsc --noEmit
|
|
1092
|
+
End with: SERVE_COMMAND_COMPLETE
|
|
1093
|
+
`,
|
|
1094
|
+
dependsOn: ['p34-code-review'],
|
|
1095
|
+
retries: 2,
|
|
1096
|
+
verification: { type: 'output_contains', value: 'SERVE_COMMAND_COMPLETE' },
|
|
1097
|
+
})
|
|
1098
|
+
.step('p5-otel-tracing', {
|
|
1099
|
+
agent: 'deploy-implementer',
|
|
1100
|
+
task: `
|
|
1101
|
+
Phase 5b: Implement OpenTelemetry tracing integration.
|
|
1102
|
+
(Running in parallel with p5-serve-command — these are independent.)
|
|
1103
|
+
|
|
1104
|
+
Phase 3+4 review context:
|
|
1105
|
+
{{steps.p34-code-review.output}}
|
|
1106
|
+
|
|
1107
|
+
YOUR TASK: Add optional OTel tracing to WorkflowRunner.
|
|
1108
|
+
|
|
1109
|
+
1. Add otel config to RelayYamlConfig in types.ts (if not already present):
|
|
1110
|
+
telemetry?: {
|
|
1111
|
+
otel?: boolean; // enable OTel tracing
|
|
1112
|
+
endpoint?: string; // OTLP endpoint, default "http://localhost:4318"
|
|
1113
|
+
serviceName?: string; // default "relay-workflows"
|
|
1114
|
+
exportTo?: "otlp" | "console" | "none"; // default "otlp"
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
2. Create packages/sdk/src/workflows/tracing.ts:
|
|
1118
|
+
|
|
1119
|
+
export interface TracingOptions {
|
|
1120
|
+
enabled: boolean;
|
|
1121
|
+
endpoint?: string;
|
|
1122
|
+
serviceName?: string;
|
|
1123
|
+
exportTo?: "otlp" | "console" | "none";
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
export class WorkflowTracer {
|
|
1127
|
+
constructor(options: TracingOptions) {}
|
|
1128
|
+
|
|
1129
|
+
// Create root span for workflow run
|
|
1130
|
+
startRun(runId: string, workflowName: string, pattern: string): Span
|
|
1131
|
+
|
|
1132
|
+
// Create child span for a step
|
|
1133
|
+
startStep(parentSpan: Span, stepName: string, agentName: string): Span
|
|
1134
|
+
|
|
1135
|
+
// Record events on spans
|
|
1136
|
+
recordEvent(span: Span, event: WorkflowEvent): void
|
|
1137
|
+
|
|
1138
|
+
// End spans
|
|
1139
|
+
endSpan(span: Span, status: "ok" | "error", error?: string): void
|
|
1140
|
+
|
|
1141
|
+
// Shutdown exporter cleanly
|
|
1142
|
+
async shutdown(): Promise<void>
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
Use @opentelemetry/sdk-node and @opentelemetry/api (add as optional peer deps
|
|
1146
|
+
with peerDependenciesMeta optional: true in package.json).
|
|
1147
|
+
|
|
1148
|
+
Guard all OTel imports with a try/catch or dynamic import so the runner works
|
|
1149
|
+
without OTel installed:
|
|
1150
|
+
let tracer: WorkflowTracer | null = null;
|
|
1151
|
+
try {
|
|
1152
|
+
const { WorkflowTracer } = await import('./tracing.js');
|
|
1153
|
+
tracer = new WorkflowTracer(config.telemetry?.otel ? { enabled: true, ...config.telemetry } : { enabled: false });
|
|
1154
|
+
} catch {
|
|
1155
|
+
// OTel packages not installed — tracing disabled
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
3. In WorkflowRunner.execute():
|
|
1159
|
+
- If tracer enabled: create root run span
|
|
1160
|
+
- In executeStep(): create child step span, record start/complete/fail events
|
|
1161
|
+
- On run complete: end root span
|
|
1162
|
+
|
|
1163
|
+
Export WorkflowTracer from ${INDEX_FILE}.
|
|
1164
|
+
|
|
1165
|
+
Run: npx tsc --noEmit
|
|
1166
|
+
End with: OTEL_TRACING_COMPLETE
|
|
1167
|
+
`,
|
|
1168
|
+
dependsOn: ['p34-code-review'],
|
|
1169
|
+
retries: 2,
|
|
1170
|
+
verification: { type: 'output_contains', value: 'OTEL_TRACING_COMPLETE' },
|
|
1171
|
+
})
|
|
1172
|
+
.step('p5-cli-improvements', {
|
|
1173
|
+
agent: 'deploy-implementer',
|
|
1174
|
+
task: `
|
|
1175
|
+
Phase 5c: Implement CLI improvements.
|
|
1176
|
+
|
|
1177
|
+
OTel tracing implementation:
|
|
1178
|
+
{{steps.p5-otel-tracing.output}}
|
|
1179
|
+
|
|
1180
|
+
YOUR TASK: Find the CLI entry point for the relay workflow commands and add:
|
|
1181
|
+
|
|
1182
|
+
First, locate the CLI code (likely in packages/sdk/src/workflows/cli.ts):
|
|
1183
|
+
cat packages/sdk/src/workflows/cli.ts
|
|
1184
|
+
|
|
1185
|
+
Add these subcommands to the workflow CLI:
|
|
1186
|
+
|
|
1187
|
+
1. relay workflow dry-run <yaml-path> [--var KEY=VALUE...]
|
|
1188
|
+
- Parse and validate the YAML (run validateConfig)
|
|
1189
|
+
- Resolve variable templates (show substituted values)
|
|
1190
|
+
- Show the resolved DAG: step names, dependencies, agent assignments
|
|
1191
|
+
- Show which steps can run in parallel
|
|
1192
|
+
- Do NOT actually execute — print "DRY RUN: would execute N steps across M agents"
|
|
1193
|
+
|
|
1194
|
+
2. relay workflow inspect <yaml-path>
|
|
1195
|
+
- Show full config parsed and pretty-printed as JSON
|
|
1196
|
+
- Show detected swarm pattern with reason
|
|
1197
|
+
- Show agent topology (edges from SwarmCoordinator)
|
|
1198
|
+
- Show barrier definitions
|
|
1199
|
+
- Show coordination config
|
|
1200
|
+
|
|
1201
|
+
3. relay workflow replay <runId> --from <step-name> [--db-path <sqlite-path>]
|
|
1202
|
+
- Load existing run record from DB (requires SqliteWorkflowDb)
|
|
1203
|
+
- Skip steps that completed successfully before the target step
|
|
1204
|
+
- Re-execute from the specified step name onwards
|
|
1205
|
+
- Useful for resuming failed runs without restarting from scratch
|
|
1206
|
+
|
|
1207
|
+
4. relay workflow serve [--port <N>] [--db sqlite:<path>|postgres:<url>|redis:<url>]
|
|
1208
|
+
- Starts the HTTP server from p5-serve-command
|
|
1209
|
+
- Accepts DB connection string via --db flag, parses the scheme prefix
|
|
1210
|
+
|
|
1211
|
+
Also update the README at packages/sdk/src/workflows/README.md to document
|
|
1212
|
+
all new CLI commands with usage examples.
|
|
1213
|
+
|
|
1214
|
+
Run: npx tsc --noEmit
|
|
1215
|
+
End with: CLI_IMPROVEMENTS_COMPLETE
|
|
1216
|
+
`,
|
|
1217
|
+
dependsOn: ['p5-serve-command', 'p5-otel-tracing'],
|
|
1218
|
+
retries: 2,
|
|
1219
|
+
verification: { type: 'output_contains', value: 'CLI_IMPROVEMENTS_COMPLETE' },
|
|
1220
|
+
})
|
|
1221
|
+
.step('p5-lead-review', {
|
|
1222
|
+
agent: 'lead',
|
|
1223
|
+
task: `
|
|
1224
|
+
Phase 5 Lead Review: Deployment and observability.
|
|
1225
|
+
|
|
1226
|
+
Serve command:
|
|
1227
|
+
{{steps.p5-serve-command.output}}
|
|
1228
|
+
|
|
1229
|
+
OTel tracing:
|
|
1230
|
+
{{steps.p5-otel-tracing.output}}
|
|
1231
|
+
|
|
1232
|
+
CLI improvements:
|
|
1233
|
+
{{steps.p5-cli-improvements.output}}
|
|
1234
|
+
|
|
1235
|
+
REVIEW:
|
|
1236
|
+
1. HTTP server: is SSE implemented correctly? (headers: Content-Type: text/event-stream,
|
|
1237
|
+
Cache-Control: no-cache, Connection: keep-alive; proper flushing with res.write)
|
|
1238
|
+
2. HTTP server: is the HITL respond endpoint correctly writing the response file
|
|
1239
|
+
to the same path the runner is polling?
|
|
1240
|
+
3. OTel: is the dynamic import guard (try/catch) robust? Will it work in CJS builds?
|
|
1241
|
+
4. OTel: are span hierarchies correct — run span is parent, step spans are children?
|
|
1242
|
+
5. CLI dry-run: does it show enough information to be useful for debugging?
|
|
1243
|
+
6. CLI replay: what happens if a replay step depends on a step that was NOT
|
|
1244
|
+
completed in the prior run? (Should fail with clear error message.)
|
|
1245
|
+
7. Is the README updated with accurate examples?
|
|
1246
|
+
|
|
1247
|
+
Note critical fixes. End with: PHASE_5_APPROVED
|
|
1248
|
+
`,
|
|
1249
|
+
dependsOn: ['p5-cli-improvements'],
|
|
1250
|
+
retries: 1,
|
|
1251
|
+
verification: { type: 'output_contains', value: 'PHASE_5_APPROVED' },
|
|
1252
|
+
})
|
|
1253
|
+
.step('p5-code-review', {
|
|
1254
|
+
agent: 'code-reviewer',
|
|
1255
|
+
task: `
|
|
1256
|
+
Phase 5 Independent Code Review: Deployment and observability.
|
|
1257
|
+
(Running in parallel with lead review — review the code independently.)
|
|
1258
|
+
|
|
1259
|
+
CLI improvements:
|
|
1260
|
+
{{steps.p5-cli-improvements.output}}
|
|
1261
|
+
|
|
1262
|
+
REVIEW packages/sdk/src/workflows/server.ts,
|
|
1263
|
+
packages/sdk/src/workflows/tracing.ts,
|
|
1264
|
+
packages/sdk/src/workflows/cli.ts:
|
|
1265
|
+
|
|
1266
|
+
1. HTTP server: no prototype pollution risk in request body parsing?
|
|
1267
|
+
(Validate Content-Type, parse JSON safely with try/catch)
|
|
1268
|
+
2. SSE endpoint: does it correctly handle client disconnect without leaving
|
|
1269
|
+
zombie event listeners on the WorkflowRunner?
|
|
1270
|
+
3. HITL respond: path traversal risk? (runId and stepName used in file path —
|
|
1271
|
+
must sanitize to alphanumeric + hyphen only)
|
|
1272
|
+
4. OTel: does shutdown() await the exporter flush before process.exit?
|
|
1273
|
+
5. CLI replay: is the step-skip logic correct? (A skipped-previously-completed
|
|
1274
|
+
step should return its stored output for downstream template resolution)
|
|
1275
|
+
6. Run npx jest packages/sdk and report pass/fail counts
|
|
1276
|
+
7. Run npx tsc --noEmit and report results
|
|
1277
|
+
|
|
1278
|
+
End with: CODE_REVIEW_5_COMPLETE
|
|
1279
|
+
`,
|
|
1280
|
+
dependsOn: ['p5-cli-improvements'],
|
|
1281
|
+
retries: 1,
|
|
1282
|
+
verification: { type: 'output_contains', value: 'CODE_REVIEW_5_COMPLETE' },
|
|
1283
|
+
})
|
|
1284
|
+
// ── Phase 6: Integration Validation + Final Sign-Off ─────────────────────
|
|
1285
|
+
.step('integration-validation', {
|
|
1286
|
+
agent: 'test-validator',
|
|
1287
|
+
task: `
|
|
1288
|
+
Phase 6: Integration validation across all five tiers.
|
|
1289
|
+
|
|
1290
|
+
Phase 5 review:
|
|
1291
|
+
{{steps.p5-code-review.output}}
|
|
1292
|
+
|
|
1293
|
+
YOUR TASK: Run comprehensive validation of the full implementation.
|
|
1294
|
+
|
|
1295
|
+
1. TYPE CHECK:
|
|
1296
|
+
cd packages/sdk && npx tsc --noEmit
|
|
1297
|
+
Report: zero errors or list all errors
|
|
1298
|
+
|
|
1299
|
+
2. EXISTING TESTS:
|
|
1300
|
+
npx jest packages/sdk/src/__tests__/
|
|
1301
|
+
Report: pass count, fail count, any failures
|
|
1302
|
+
|
|
1303
|
+
3. BUILD:
|
|
1304
|
+
cd packages/sdk && npm run build
|
|
1305
|
+
Report: success or errors
|
|
1306
|
+
|
|
1307
|
+
4. INTEGRATION SMOKE TESTS — run each of these and report output:
|
|
1308
|
+
|
|
1309
|
+
a. Condition step test:
|
|
1310
|
+
Create a temporary relay.yaml with a condition step that checks if
|
|
1311
|
+
"SKIP" is in a prior step's output, run it via the WorkflowRunner
|
|
1312
|
+
in TypeScript (programmatic, not CLI), verify the step is skipped.
|
|
1313
|
+
|
|
1314
|
+
b. Loop step test:
|
|
1315
|
+
Create a temporary relay.yaml with a loop that runs max 3 iterations,
|
|
1316
|
+
verify it runs exactly 3 times when until-condition is never met.
|
|
1317
|
+
|
|
1318
|
+
c. Input validation test:
|
|
1319
|
+
Create a RelayYamlConfig with inputSchema requiring a "task" field,
|
|
1320
|
+
call runner.execute() without "task" in vars, verify WorkflowValidationError
|
|
1321
|
+
is thrown.
|
|
1322
|
+
|
|
1323
|
+
d. Flow shorthand test:
|
|
1324
|
+
Parse the flow string "planner -> developer, reviewer -> lead" and verify
|
|
1325
|
+
that developer.dependsOn = ["planner"], reviewer.dependsOn = ["planner"],
|
|
1326
|
+
lead.dependsOn = ["developer", "reviewer"].
|
|
1327
|
+
|
|
1328
|
+
e. SqliteWorkflowDb test:
|
|
1329
|
+
Create an in-memory SQLite DB, insert a run, update it, retrieve it,
|
|
1330
|
+
verify round-trip fidelity of all fields.
|
|
1331
|
+
|
|
1332
|
+
5. EXPORTS CHECK:
|
|
1333
|
+
Verify that the following are exported from packages/sdk/src/workflows/index.ts:
|
|
1334
|
+
- WorkflowBuilder, workflow
|
|
1335
|
+
- WorkflowRunner, WorkflowRunnerOptions, WorkflowEvent, WorkflowEventListener
|
|
1336
|
+
- WorkflowValidationError
|
|
1337
|
+
- AutoWorkflowBuilder, autoWorkflow
|
|
1338
|
+
- WorkflowRegistry
|
|
1339
|
+
- SqliteWorkflowDb, PostgresWorkflowDb, RedisWorkflowDb
|
|
1340
|
+
- WorkflowTracer
|
|
1341
|
+
- All new types: AnyWorkflowStep, LoopStepGroup, RouterStepGroup,
|
|
1342
|
+
ParallelStepGroup, HitlStep, SubWorkflowStep, SessionConfig
|
|
1343
|
+
|
|
1344
|
+
Report full results. End with: INTEGRATION_VALIDATED
|
|
1345
|
+
`,
|
|
1346
|
+
dependsOn: ['p5-code-review', 'p5-lead-review'],
|
|
1347
|
+
retries: 2,
|
|
1348
|
+
verification: { type: 'output_contains', value: 'INTEGRATION_VALIDATED' },
|
|
1349
|
+
})
|
|
1350
|
+
.step('final-lead-review', {
|
|
1351
|
+
agent: 'lead',
|
|
1352
|
+
task: `
|
|
1353
|
+
FINAL LEAD REVIEW: Complete broker-sdk workflow superiority implementation.
|
|
1354
|
+
|
|
1355
|
+
Integration validation results:
|
|
1356
|
+
{{steps.integration-validation.output}}
|
|
1357
|
+
|
|
1358
|
+
This is the culmination of a five-phase implementation campaign. Your job:
|
|
1359
|
+
|
|
1360
|
+
1. CAPABILITY AUDIT — verify we now have all of these (check integration results):
|
|
1361
|
+
□ condition step type (CEL-based conditional execution)
|
|
1362
|
+
□ loop step type (iterative with until-condition)
|
|
1363
|
+
□ router step type (runtime branch selection)
|
|
1364
|
+
□ parallel step group (explicit with any/majority barriers)
|
|
1365
|
+
□ HITL step type (human-in-the-loop with file-based pause/resume)
|
|
1366
|
+
□ sub-workflow step (workflow composition)
|
|
1367
|
+
□ flow shorthand (string notation "A -> B, C")
|
|
1368
|
+
□ session concept (multi-run state sharing)
|
|
1369
|
+
□ input schema validation (JSON Schema via Ajv)
|
|
1370
|
+
□ fallback agent switching
|
|
1371
|
+
□ AutoWorkflowBuilder (LLM-generated workflows)
|
|
1372
|
+
□ WorkflowRegistry (name-to-path resolution)
|
|
1373
|
+
□ PostgresWorkflowDb, SqliteWorkflowDb, RedisWorkflowDb
|
|
1374
|
+
□ HTTP serve command with SSE event streaming
|
|
1375
|
+
□ OTel tracing (optional, dynamic import)
|
|
1376
|
+
□ CLI: dry-run, inspect, replay, serve
|
|
1377
|
+
□ 30+ WorkflowEvent types
|
|
1378
|
+
□ All exports correct from index.ts
|
|
1379
|
+
|
|
1380
|
+
2. COMPETITIVE POSITION — confirm we now surpass:
|
|
1381
|
+
AGNO: We have everything Agno has (condition=Condition, loop=Loop,
|
|
1382
|
+
router=Router, session, input validation, serve, OTel) PLUS barriers,
|
|
1383
|
+
consensus, HITL, polyglot backends, YAML portability, sub-workflow composition.
|
|
1384
|
+
|
|
1385
|
+
SWARMS: We have equivalent patterns PLUS out-of-process PTY isolation,
|
|
1386
|
+
YAML portability, true relay protocol, HITL, OTel, sub-workflow composition,
|
|
1387
|
+
HTTP serve. Swarms has more built-in swarm types (15+ vs our 10) but we
|
|
1388
|
+
cover all critical execution patterns with richer composition primitives.
|
|
1389
|
+
|
|
1390
|
+
3. REMAINING GAPS (if any) — list anything that was not fully implemented
|
|
1391
|
+
and should be tracked as future work.
|
|
1392
|
+
|
|
1393
|
+
4. DOCUMENTATION — confirm README.md in the workflows directory is updated
|
|
1394
|
+
with examples for all new features.
|
|
1395
|
+
|
|
1396
|
+
Produce a final capability report with the checklist above filled in.
|
|
1397
|
+
End with: IMPLEMENTATION_COMPLETE
|
|
1398
|
+
`,
|
|
1399
|
+
dependsOn: ['integration-validation'],
|
|
1400
|
+
retries: 1,
|
|
1401
|
+
verification: { type: 'output_contains', value: 'IMPLEMENTATION_COMPLETE' },
|
|
1402
|
+
})
|
|
1403
|
+
.onError('retry', { maxRetries: 2, retryDelayMs: 10_000 })
|
|
1404
|
+
.run({
|
|
1405
|
+
onEvent,
|
|
1406
|
+
vars: {
|
|
1407
|
+
// Override these at runtime if needed:
|
|
1408
|
+
// workflowRoot: 'packages/sdk/src/workflows',
|
|
1409
|
+
},
|
|
1410
|
+
});
|
|
1411
|
+
console.log('\n── Run complete ─────────────────────────────────────────────────');
|
|
1412
|
+
console.log(`status: ${result.status}`);
|
|
1413
|
+
console.log(`runId: ${result.id}`);
|
|
1414
|
+
console.log(`workflow: ${result.workflowName}`);
|
|
1415
|
+
console.log(`pattern: ${result.pattern}`);
|
|
1416
|
+
console.log(`started: ${result.startedAt}`);
|
|
1417
|
+
console.log(`completed: ${result.completedAt ?? '—'}`);
|
|
1418
|
+
if (result.error) {
|
|
1419
|
+
console.error(`error: ${result.error}`);
|
|
1420
|
+
}
|
|
1421
|
+
//# sourceMappingURL=workflow-superiority.js.map
|