@yesod/core 0.0.2 → 0.1.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 +7 -4
- package/dist/index.cjs +570 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +398 -25
- package/dist/index.d.ts +398 -25
- package/dist/index.js +566 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,20 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Core engine for Yesod — orchestration toolkit that makes AI agents delegate better and lets humans see why.
|
|
4
4
|
|
|
5
|
-
Provides the event bus, subscription engine (Vantage), TaskSpec/ResultSpec for structured delegation, RuntimeDecision tracking, and all shared interfaces.
|
|
5
|
+
Provides the event bus, orchestration engine, scheduler, subscription engine (Vantage), TaskSpec/ResultSpec for structured delegation, RuntimeDecision tracking, and all shared interfaces.
|
|
6
6
|
|
|
7
7
|
## Key Exports
|
|
8
8
|
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
9
|
+
- **OrchestrationEngine** — executes workflows by coordinating the scheduler, runtime adapter, and event bus
|
|
10
|
+
- **EventBus** — publish/subscribe for orchestration events with type and field filtering
|
|
11
|
+
- **SimpleScheduler** — DAG-based step scheduling with dependency resolution
|
|
12
|
+
- **VantageEngine** — compound condition evaluation with barrier mode and TTL
|
|
11
13
|
- **TaskSpec / ResultSpec** — structured task delegation with typed result formats
|
|
12
14
|
- **RuntimeDecision** — required metadata recording why the agent chose a runtime
|
|
13
15
|
- **StepResult / StructuredOutput** — typed results (diff, review, test, summary) flowing back to orchestrators
|
|
16
|
+
- **YESOD_TOOLS** — MCP tool JSON schemas for agent-facing tools
|
|
14
17
|
- All type definitions and interfaces for adapters
|
|
15
18
|
|
|
16
19
|
## Status
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
Phase 0 complete. Core orchestration loop is implemented and tested (29 tests).
|
|
19
22
|
|
|
20
23
|
## Links
|
|
21
24
|
|
package/dist/index.cjs
CHANGED
|
@@ -23,22 +23,35 @@ __export(index_exports, {
|
|
|
23
23
|
EventBus: () => EventBus,
|
|
24
24
|
EventType: () => EventType,
|
|
25
25
|
InMemoryStorageAdapter: () => InMemoryStorageAdapter,
|
|
26
|
-
|
|
26
|
+
OrchestrationEngine: () => OrchestrationEngine,
|
|
27
|
+
SimpleScheduler: () => SimpleScheduler,
|
|
28
|
+
VantageEngine: () => VantageEngine,
|
|
29
|
+
YESOD_TOOLS: () => YESOD_TOOLS
|
|
27
30
|
});
|
|
28
31
|
module.exports = __toCommonJS(index_exports);
|
|
29
32
|
|
|
30
33
|
// src/events.ts
|
|
31
34
|
var EventType = /* @__PURE__ */ ((EventType2) => {
|
|
32
35
|
EventType2["OrchestrationStarted"] = "orchestration.started";
|
|
36
|
+
EventType2["OrchestrationCompleted"] = "orchestration.completed";
|
|
37
|
+
EventType2["WorkflowStarted"] = "workflow.started";
|
|
38
|
+
EventType2["WorkflowCompleted"] = "workflow.completed";
|
|
39
|
+
EventType2["WorkflowFailed"] = "workflow.failed";
|
|
40
|
+
EventType2["WorkflowStepStarted"] = "workflow.step.started";
|
|
41
|
+
EventType2["WorkflowStepCompleted"] = "workflow.step.completed";
|
|
42
|
+
EventType2["WorkflowStepFailed"] = "workflow.step.failed";
|
|
33
43
|
EventType2["TaskCreated"] = "task.created";
|
|
34
44
|
EventType2["TaskDependency"] = "task.dependency";
|
|
45
|
+
EventType2["TaskCompleted"] = "task.completed";
|
|
35
46
|
EventType2["SessionSpawned"] = "session.spawned";
|
|
47
|
+
EventType2["SessionMessage"] = "session.message";
|
|
36
48
|
EventType2["SessionEvent"] = "session.event";
|
|
37
49
|
EventType2["SessionSteered"] = "session.steered";
|
|
38
50
|
EventType2["SessionArtifact"] = "session.artifact";
|
|
39
51
|
EventType2["SessionCompleted"] = "session.completed";
|
|
40
|
-
EventType2["
|
|
41
|
-
EventType2["
|
|
52
|
+
EventType2["SessionFailed"] = "session.failed";
|
|
53
|
+
EventType2["SessionKilled"] = "session.killed";
|
|
54
|
+
EventType2["CostIncurred"] = "cost.incurred";
|
|
42
55
|
EventType2["SubscriptionTriggered"] = "subscription.triggered";
|
|
43
56
|
return EventType2;
|
|
44
57
|
})(EventType || {});
|
|
@@ -52,6 +65,9 @@ var EventBus = class {
|
|
|
52
65
|
this.handlers.set(id, { filter, handler });
|
|
53
66
|
return id;
|
|
54
67
|
}
|
|
68
|
+
subscribeAll(handler) {
|
|
69
|
+
return this.subscribe({}, handler);
|
|
70
|
+
}
|
|
55
71
|
unsubscribe(id) {
|
|
56
72
|
this.handlers.delete(id);
|
|
57
73
|
}
|
|
@@ -68,9 +84,18 @@ var EventBus = class {
|
|
|
68
84
|
await Promise.all(promises);
|
|
69
85
|
}
|
|
70
86
|
matches(event, filter) {
|
|
71
|
-
if (filter.type
|
|
87
|
+
if (filter.type) {
|
|
88
|
+
if (Array.isArray(filter.type)) {
|
|
89
|
+
if (!filter.type.includes(event.type)) return false;
|
|
90
|
+
} else {
|
|
91
|
+
if (event.type !== filter.type) return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
72
94
|
if (filter.sessionId && event.sessionId !== filter.sessionId) return false;
|
|
73
|
-
if (filter.orchestrationId && event.orchestrationId !== filter.orchestrationId)
|
|
95
|
+
if (filter.orchestrationId && event.orchestrationId !== filter.orchestrationId)
|
|
96
|
+
return false;
|
|
97
|
+
if (filter.workflowId && event.workflowId !== filter.workflowId)
|
|
98
|
+
return false;
|
|
74
99
|
return true;
|
|
75
100
|
}
|
|
76
101
|
};
|
|
@@ -78,15 +103,50 @@ var EventBus = class {
|
|
|
78
103
|
// src/vantage.ts
|
|
79
104
|
var VantageEngine = class {
|
|
80
105
|
subscriptions = /* @__PURE__ */ new Map();
|
|
106
|
+
barrierState = /* @__PURE__ */ new Map();
|
|
81
107
|
register(subscription) {
|
|
82
|
-
|
|
108
|
+
const sub = { ...subscription, registeredAt: subscription.registeredAt ?? Date.now() };
|
|
109
|
+
this.subscriptions.set(sub.id, sub);
|
|
110
|
+
if (sub.mode === "all") {
|
|
111
|
+
this.barrierState.set(sub.id, /* @__PURE__ */ new Map());
|
|
112
|
+
}
|
|
83
113
|
}
|
|
84
114
|
remove(subscriptionId) {
|
|
85
115
|
this.subscriptions.delete(subscriptionId);
|
|
116
|
+
this.barrierState.delete(subscriptionId);
|
|
86
117
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
118
|
+
evaluate(event) {
|
|
119
|
+
const notifications = [];
|
|
120
|
+
const toRemove = [];
|
|
121
|
+
for (const [id, sub] of this.subscriptions) {
|
|
122
|
+
if (sub.ttl && sub.registeredAt) {
|
|
123
|
+
if (Date.now() - sub.registeredAt > sub.ttl) {
|
|
124
|
+
toRemove.push(id);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (sub.mode === "any") {
|
|
129
|
+
const matched = this.matchAny(event, sub);
|
|
130
|
+
if (matched) {
|
|
131
|
+
notifications.push({
|
|
132
|
+
type: "subscription.triggered",
|
|
133
|
+
subscriptionId: id,
|
|
134
|
+
matchedEvents: [event]
|
|
135
|
+
});
|
|
136
|
+
if (sub.once) toRemove.push(id);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
const notification = this.matchBarrier(event, sub);
|
|
140
|
+
if (notification) {
|
|
141
|
+
notifications.push(notification);
|
|
142
|
+
if (sub.once) toRemove.push(id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const id of toRemove) {
|
|
147
|
+
this.remove(id);
|
|
148
|
+
}
|
|
149
|
+
return notifications;
|
|
90
150
|
}
|
|
91
151
|
getSubscription(id) {
|
|
92
152
|
return this.subscriptions.get(id);
|
|
@@ -94,6 +154,48 @@ var VantageEngine = class {
|
|
|
94
154
|
listSubscriptions() {
|
|
95
155
|
return Array.from(this.subscriptions.values());
|
|
96
156
|
}
|
|
157
|
+
activeCount() {
|
|
158
|
+
return this.subscriptions.size;
|
|
159
|
+
}
|
|
160
|
+
matchAny(event, sub) {
|
|
161
|
+
return sub.conditions.some((cond) => this.matchCondition(event, cond));
|
|
162
|
+
}
|
|
163
|
+
matchBarrier(event, sub) {
|
|
164
|
+
let barrier = this.barrierState.get(sub.id);
|
|
165
|
+
if (!barrier) {
|
|
166
|
+
barrier = /* @__PURE__ */ new Map();
|
|
167
|
+
this.barrierState.set(sub.id, barrier);
|
|
168
|
+
}
|
|
169
|
+
for (let i = 0; i < sub.conditions.length; i++) {
|
|
170
|
+
if (barrier.has(i)) continue;
|
|
171
|
+
if (this.matchCondition(event, sub.conditions[i])) {
|
|
172
|
+
barrier.set(i, [event]);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (barrier.size === sub.conditions.length) {
|
|
176
|
+
const allEvents = [];
|
|
177
|
+
for (const events of barrier.values()) {
|
|
178
|
+
allEvents.push(...events);
|
|
179
|
+
}
|
|
180
|
+
this.barrierState.delete(sub.id);
|
|
181
|
+
return {
|
|
182
|
+
type: "subscription.triggered",
|
|
183
|
+
subscriptionId: sub.id,
|
|
184
|
+
matchedEvents: allEvents
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
matchCondition(event, cond) {
|
|
190
|
+
if (event.type !== cond.eventType) return false;
|
|
191
|
+
if (cond.sessionId && event.sessionId !== cond.sessionId) return false;
|
|
192
|
+
if (cond.filter) {
|
|
193
|
+
for (const [key, value] of Object.entries(cond.filter)) {
|
|
194
|
+
if (event.payload[key] !== value) return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
97
199
|
};
|
|
98
200
|
|
|
99
201
|
// src/storage.ts
|
|
@@ -112,11 +214,469 @@ var InMemoryStorageAdapter = class {
|
|
|
112
214
|
return this.store.delete(key);
|
|
113
215
|
}
|
|
114
216
|
};
|
|
217
|
+
|
|
218
|
+
// src/scheduler.ts
|
|
219
|
+
var SimpleScheduler = class {
|
|
220
|
+
stepStates = /* @__PURE__ */ new Map();
|
|
221
|
+
stepMap = /* @__PURE__ */ new Map();
|
|
222
|
+
constructor(steps) {
|
|
223
|
+
for (const step of steps) {
|
|
224
|
+
this.stepMap.set(step.id, step);
|
|
225
|
+
this.stepStates.set(step.id, "pending");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
getReadySteps() {
|
|
229
|
+
const ready = [];
|
|
230
|
+
for (const [id, state] of this.stepStates) {
|
|
231
|
+
if (state !== "pending") continue;
|
|
232
|
+
const step = this.stepMap.get(id);
|
|
233
|
+
const deps = step.dependsOn ?? [];
|
|
234
|
+
const allDepsCompleted = deps.every(
|
|
235
|
+
(depId) => this.stepStates.get(depId) === "completed"
|
|
236
|
+
);
|
|
237
|
+
if (allDepsCompleted) {
|
|
238
|
+
const reason = deps.length === 0 ? "No dependencies" : `Dependencies satisfied: ${deps.join(", ")}`;
|
|
239
|
+
ready.push({ step, reason });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return ready;
|
|
243
|
+
}
|
|
244
|
+
markRunning(stepId) {
|
|
245
|
+
this.stepStates.set(stepId, "running");
|
|
246
|
+
}
|
|
247
|
+
markCompleted(stepId) {
|
|
248
|
+
this.stepStates.set(stepId, "completed");
|
|
249
|
+
}
|
|
250
|
+
markFailed(stepId) {
|
|
251
|
+
this.stepStates.set(stepId, "failed");
|
|
252
|
+
}
|
|
253
|
+
getState(stepId) {
|
|
254
|
+
return this.stepStates.get(stepId);
|
|
255
|
+
}
|
|
256
|
+
isComplete() {
|
|
257
|
+
for (const state of this.stepStates.values()) {
|
|
258
|
+
if (state !== "completed" && state !== "failed") return false;
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
allSucceeded() {
|
|
263
|
+
for (const state of this.stepStates.values()) {
|
|
264
|
+
if (state !== "completed") return false;
|
|
265
|
+
}
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Mark any pending step whose dependencies include a failed step as failed.
|
|
270
|
+
* Returns IDs of newly failed steps.
|
|
271
|
+
*/
|
|
272
|
+
markUnreachable() {
|
|
273
|
+
const failed = [];
|
|
274
|
+
let changed = true;
|
|
275
|
+
while (changed) {
|
|
276
|
+
changed = false;
|
|
277
|
+
for (const [id, state] of this.stepStates) {
|
|
278
|
+
if (state !== "pending") continue;
|
|
279
|
+
const step = this.stepMap.get(id);
|
|
280
|
+
const deps = step.dependsOn ?? [];
|
|
281
|
+
const hasFailedDep = deps.some(
|
|
282
|
+
(depId) => this.stepStates.get(depId) === "failed"
|
|
283
|
+
);
|
|
284
|
+
if (hasFailedDep) {
|
|
285
|
+
this.stepStates.set(id, "failed");
|
|
286
|
+
failed.push(id);
|
|
287
|
+
changed = true;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return failed;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
// src/engine.ts
|
|
296
|
+
var idCounter = 0;
|
|
297
|
+
function uid() {
|
|
298
|
+
return `evt-${Date.now()}-${++idCounter}`;
|
|
299
|
+
}
|
|
300
|
+
var OrchestrationEngine = class {
|
|
301
|
+
constructor(adapter, bus, storage) {
|
|
302
|
+
this.adapter = adapter;
|
|
303
|
+
this.bus = bus;
|
|
304
|
+
this.storage = storage;
|
|
305
|
+
this.adapter.onEvent((event) => this.handleAdapterEvent(event));
|
|
306
|
+
}
|
|
307
|
+
adapter;
|
|
308
|
+
bus;
|
|
309
|
+
storage;
|
|
310
|
+
executions = /* @__PURE__ */ new Map();
|
|
311
|
+
createWorkflow(def) {
|
|
312
|
+
const workflow = {
|
|
313
|
+
...def,
|
|
314
|
+
id: def.id || uid(),
|
|
315
|
+
state: "created",
|
|
316
|
+
createdAt: Date.now()
|
|
317
|
+
};
|
|
318
|
+
const scheduler = new SimpleScheduler(workflow.steps);
|
|
319
|
+
this.executions.set(workflow.id, {
|
|
320
|
+
workflow,
|
|
321
|
+
state: "created",
|
|
322
|
+
scheduler,
|
|
323
|
+
stepResults: /* @__PURE__ */ new Map(),
|
|
324
|
+
activeSessions: /* @__PURE__ */ new Map(),
|
|
325
|
+
startedAt: 0
|
|
326
|
+
});
|
|
327
|
+
return workflow;
|
|
328
|
+
}
|
|
329
|
+
async startWorkflow(workflowId) {
|
|
330
|
+
const exec = this.executions.get(workflowId);
|
|
331
|
+
if (!exec) throw new Error(`Unknown workflow: ${workflowId}`);
|
|
332
|
+
if (exec.state !== "created") {
|
|
333
|
+
throw new Error(`Workflow ${workflowId} is already ${exec.state}`);
|
|
334
|
+
}
|
|
335
|
+
exec.state = "running";
|
|
336
|
+
exec.startedAt = Date.now();
|
|
337
|
+
await this.bus.emit({
|
|
338
|
+
id: uid(),
|
|
339
|
+
type: "workflow.started" /* WorkflowStarted */,
|
|
340
|
+
source: "engine",
|
|
341
|
+
timestamp: Date.now(),
|
|
342
|
+
workflowId,
|
|
343
|
+
payload: { name: exec.workflow.name }
|
|
344
|
+
});
|
|
345
|
+
await this.advanceWorkflow(workflowId);
|
|
346
|
+
}
|
|
347
|
+
async cancelWorkflow(workflowId) {
|
|
348
|
+
const exec = this.executions.get(workflowId);
|
|
349
|
+
if (!exec) throw new Error(`Unknown workflow: ${workflowId}`);
|
|
350
|
+
for (const [stepId, handle] of exec.activeSessions) {
|
|
351
|
+
try {
|
|
352
|
+
await this.adapter.kill(handle);
|
|
353
|
+
} catch {
|
|
354
|
+
}
|
|
355
|
+
exec.scheduler.markFailed(stepId);
|
|
356
|
+
}
|
|
357
|
+
exec.activeSessions.clear();
|
|
358
|
+
exec.state = "failed";
|
|
359
|
+
exec.completedAt = Date.now();
|
|
360
|
+
await this.bus.emit({
|
|
361
|
+
id: uid(),
|
|
362
|
+
type: "workflow.failed" /* WorkflowFailed */,
|
|
363
|
+
source: "engine",
|
|
364
|
+
timestamp: Date.now(),
|
|
365
|
+
workflowId,
|
|
366
|
+
payload: { reason: "cancelled" }
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
getWorkflowStatus(workflowId) {
|
|
370
|
+
const exec = this.executions.get(workflowId);
|
|
371
|
+
if (!exec) throw new Error(`Unknown workflow: ${workflowId}`);
|
|
372
|
+
return {
|
|
373
|
+
workflowId,
|
|
374
|
+
state: exec.state,
|
|
375
|
+
steps: exec.workflow.steps.map((step) => ({
|
|
376
|
+
stepId: step.id,
|
|
377
|
+
state: exec.scheduler.getState(step.id) ?? "pending",
|
|
378
|
+
result: exec.stepResults.get(step.id)
|
|
379
|
+
})),
|
|
380
|
+
startedAt: exec.startedAt,
|
|
381
|
+
completedAt: exec.completedAt
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
getCostSummary(workflowId) {
|
|
385
|
+
let results = [];
|
|
386
|
+
if (workflowId) {
|
|
387
|
+
const exec = this.executions.get(workflowId);
|
|
388
|
+
if (exec) {
|
|
389
|
+
results = Array.from(exec.stepResults.values());
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
for (const exec of this.executions.values()) {
|
|
393
|
+
results.push(...exec.stepResults.values());
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
let totalUsd = 0;
|
|
397
|
+
let totalInput = 0;
|
|
398
|
+
let totalOutput = 0;
|
|
399
|
+
const perStep = [];
|
|
400
|
+
for (const r of results) {
|
|
401
|
+
totalUsd += r.meta.cost.usd;
|
|
402
|
+
totalInput += r.meta.cost.tokens.input;
|
|
403
|
+
totalOutput += r.meta.cost.tokens.output;
|
|
404
|
+
perStep.push({
|
|
405
|
+
stepId: r.stepId,
|
|
406
|
+
cost: r.meta.cost,
|
|
407
|
+
durationMs: r.meta.durationMs
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
totalUsd,
|
|
412
|
+
totalTokens: { input: totalInput, output: totalOutput },
|
|
413
|
+
perStep
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
// --- Internal ---
|
|
417
|
+
async advanceWorkflow(workflowId) {
|
|
418
|
+
const exec = this.executions.get(workflowId);
|
|
419
|
+
if (!exec || exec.state !== "running") return;
|
|
420
|
+
const readySteps = exec.scheduler.getReadySteps();
|
|
421
|
+
for (const { step } of readySteps) {
|
|
422
|
+
exec.scheduler.markRunning(step.id);
|
|
423
|
+
try {
|
|
424
|
+
await this.spawnStep(exec, step, workflowId);
|
|
425
|
+
} catch (err) {
|
|
426
|
+
exec.scheduler.markFailed(step.id);
|
|
427
|
+
await this.bus.emit({
|
|
428
|
+
id: uid(),
|
|
429
|
+
type: "workflow.step.failed" /* WorkflowStepFailed */,
|
|
430
|
+
source: "engine",
|
|
431
|
+
timestamp: Date.now(),
|
|
432
|
+
workflowId,
|
|
433
|
+
stepId: step.id,
|
|
434
|
+
payload: {
|
|
435
|
+
error: err instanceof Error ? err.message : String(err)
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
exec.scheduler.markUnreachable();
|
|
441
|
+
if (exec.scheduler.isComplete()) {
|
|
442
|
+
exec.state = exec.scheduler.allSucceeded() ? "completed" : "failed";
|
|
443
|
+
exec.completedAt = Date.now();
|
|
444
|
+
const eventType = exec.state === "completed" ? "workflow.completed" /* WorkflowCompleted */ : "workflow.failed" /* WorkflowFailed */;
|
|
445
|
+
await this.bus.emit({
|
|
446
|
+
id: uid(),
|
|
447
|
+
type: eventType,
|
|
448
|
+
source: "engine",
|
|
449
|
+
timestamp: Date.now(),
|
|
450
|
+
workflowId,
|
|
451
|
+
payload: {
|
|
452
|
+
duration: exec.completedAt - exec.startedAt
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
async spawnStep(exec, step, workflowId) {
|
|
458
|
+
const priorContext = [];
|
|
459
|
+
for (const depId of step.dependsOn ?? []) {
|
|
460
|
+
const depResult = exec.stepResults.get(depId);
|
|
461
|
+
if (depResult) priorContext.push(depResult);
|
|
462
|
+
}
|
|
463
|
+
const taskSpec = {
|
|
464
|
+
instruction: step.task,
|
|
465
|
+
expectedResult: step.expectedResult,
|
|
466
|
+
priorContext: priorContext.length > 0 ? priorContext : void 0
|
|
467
|
+
};
|
|
468
|
+
const spawnOpts = {
|
|
469
|
+
task: taskSpec,
|
|
470
|
+
runtime: step.runtime,
|
|
471
|
+
decision: step.decision,
|
|
472
|
+
timeout: step.timeout,
|
|
473
|
+
context: { workflowId, stepId: step.id, attempt: 1 }
|
|
474
|
+
};
|
|
475
|
+
await this.bus.emit({
|
|
476
|
+
id: uid(),
|
|
477
|
+
type: "workflow.step.started" /* WorkflowStepStarted */,
|
|
478
|
+
source: "engine",
|
|
479
|
+
timestamp: Date.now(),
|
|
480
|
+
workflowId,
|
|
481
|
+
stepId: step.id,
|
|
482
|
+
payload: { runtime: step.runtime }
|
|
483
|
+
});
|
|
484
|
+
const handle = await this.adapter.spawn(spawnOpts);
|
|
485
|
+
exec.activeSessions.set(step.id, handle);
|
|
486
|
+
}
|
|
487
|
+
async handleAdapterEvent(event) {
|
|
488
|
+
await this.bus.emit(event);
|
|
489
|
+
if (event.type === "session.completed" /* SessionCompleted */ && event.stepId) {
|
|
490
|
+
await this.handleStepCompleted(event);
|
|
491
|
+
} else if (event.type === "session.failed" /* SessionFailed */ && event.stepId) {
|
|
492
|
+
await this.handleStepFailed(event);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
async handleStepCompleted(event) {
|
|
496
|
+
const workflowId = event.workflowId;
|
|
497
|
+
const stepId = event.stepId;
|
|
498
|
+
if (!workflowId || !stepId) return;
|
|
499
|
+
const exec = this.executions.get(workflowId);
|
|
500
|
+
if (!exec) return;
|
|
501
|
+
const stepResult = event.payload.stepResult;
|
|
502
|
+
if (stepResult) {
|
|
503
|
+
exec.stepResults.set(stepId, stepResult);
|
|
504
|
+
}
|
|
505
|
+
exec.activeSessions.delete(stepId);
|
|
506
|
+
exec.scheduler.markCompleted(stepId);
|
|
507
|
+
await this.bus.emit({
|
|
508
|
+
id: uid(),
|
|
509
|
+
type: "workflow.step.completed" /* WorkflowStepCompleted */,
|
|
510
|
+
source: "engine",
|
|
511
|
+
timestamp: Date.now(),
|
|
512
|
+
workflowId,
|
|
513
|
+
stepId,
|
|
514
|
+
payload: { result: stepResult }
|
|
515
|
+
});
|
|
516
|
+
await this.advanceWorkflow(workflowId);
|
|
517
|
+
}
|
|
518
|
+
async handleStepFailed(event) {
|
|
519
|
+
const workflowId = event.workflowId;
|
|
520
|
+
const stepId = event.stepId;
|
|
521
|
+
if (!workflowId || !stepId) return;
|
|
522
|
+
const exec = this.executions.get(workflowId);
|
|
523
|
+
if (!exec) return;
|
|
524
|
+
exec.activeSessions.delete(stepId);
|
|
525
|
+
exec.scheduler.markFailed(stepId);
|
|
526
|
+
await this.bus.emit({
|
|
527
|
+
id: uid(),
|
|
528
|
+
type: "workflow.step.failed" /* WorkflowStepFailed */,
|
|
529
|
+
source: "engine",
|
|
530
|
+
timestamp: Date.now(),
|
|
531
|
+
workflowId,
|
|
532
|
+
stepId,
|
|
533
|
+
payload: { error: event.payload.error }
|
|
534
|
+
});
|
|
535
|
+
await this.advanceWorkflow(workflowId);
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// src/tools.ts
|
|
540
|
+
var YESOD_TOOLS = {
|
|
541
|
+
yesod_create_workflow: {
|
|
542
|
+
name: "yesod_create_workflow",
|
|
543
|
+
description: "Create and start a multi-step workflow. Define the steps, their dependencies, and expected output formats. Yesod will orchestrate execution, spawn child agents, and chain results automatically.",
|
|
544
|
+
inputSchema: {
|
|
545
|
+
type: "object",
|
|
546
|
+
properties: {
|
|
547
|
+
name: {
|
|
548
|
+
type: "string",
|
|
549
|
+
description: "Human-readable name for the workflow"
|
|
550
|
+
},
|
|
551
|
+
steps: {
|
|
552
|
+
type: "array",
|
|
553
|
+
description: "Workflow steps to execute",
|
|
554
|
+
items: {
|
|
555
|
+
type: "object",
|
|
556
|
+
properties: {
|
|
557
|
+
id: {
|
|
558
|
+
type: "string",
|
|
559
|
+
description: "Unique step identifier"
|
|
560
|
+
},
|
|
561
|
+
name: {
|
|
562
|
+
type: "string",
|
|
563
|
+
description: "Human-readable step name"
|
|
564
|
+
},
|
|
565
|
+
task: {
|
|
566
|
+
type: "string",
|
|
567
|
+
description: "Instruction for the agent performing this step"
|
|
568
|
+
},
|
|
569
|
+
expectedResult: {
|
|
570
|
+
type: "object",
|
|
571
|
+
description: "Expected output format",
|
|
572
|
+
properties: {
|
|
573
|
+
format: {
|
|
574
|
+
type: "string",
|
|
575
|
+
enum: ["summary", "structured", "diff", "review", "test"]
|
|
576
|
+
},
|
|
577
|
+
fields: {
|
|
578
|
+
type: "array",
|
|
579
|
+
items: { type: "string" },
|
|
580
|
+
description: "Fields to include (for structured format)"
|
|
581
|
+
},
|
|
582
|
+
maxLength: {
|
|
583
|
+
type: "number",
|
|
584
|
+
description: "Max output length in characters"
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
required: ["format"]
|
|
588
|
+
},
|
|
589
|
+
runtime: {
|
|
590
|
+
type: "string",
|
|
591
|
+
description: "Runtime to use (e.g. claude-code, codex)"
|
|
592
|
+
},
|
|
593
|
+
decision: {
|
|
594
|
+
type: "object",
|
|
595
|
+
description: "Why this runtime was chosen",
|
|
596
|
+
properties: {
|
|
597
|
+
reason: { type: "string" },
|
|
598
|
+
considered: {
|
|
599
|
+
type: "array",
|
|
600
|
+
items: { type: "string" }
|
|
601
|
+
},
|
|
602
|
+
chosen: { type: "string" }
|
|
603
|
+
},
|
|
604
|
+
required: ["reason", "considered", "chosen"]
|
|
605
|
+
},
|
|
606
|
+
dependsOn: {
|
|
607
|
+
type: "array",
|
|
608
|
+
items: { type: "string" },
|
|
609
|
+
description: "Step IDs this step depends on"
|
|
610
|
+
},
|
|
611
|
+
timeout: {
|
|
612
|
+
type: "number",
|
|
613
|
+
description: "Timeout in milliseconds"
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
required: [
|
|
617
|
+
"id",
|
|
618
|
+
"name",
|
|
619
|
+
"task",
|
|
620
|
+
"expectedResult",
|
|
621
|
+
"runtime",
|
|
622
|
+
"decision"
|
|
623
|
+
]
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
required: ["name", "steps"]
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
yesod_workflow_status: {
|
|
631
|
+
name: "yesod_workflow_status",
|
|
632
|
+
description: "Check the current status of a running workflow, including per-step progress and results.",
|
|
633
|
+
inputSchema: {
|
|
634
|
+
type: "object",
|
|
635
|
+
properties: {
|
|
636
|
+
workflowId: {
|
|
637
|
+
type: "string",
|
|
638
|
+
description: "ID of the workflow to check"
|
|
639
|
+
}
|
|
640
|
+
},
|
|
641
|
+
required: ["workflowId"]
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
yesod_cancel_workflow: {
|
|
645
|
+
name: "yesod_cancel_workflow",
|
|
646
|
+
description: "Cancel a running workflow. Kills all active sessions and marks the workflow as failed.",
|
|
647
|
+
inputSchema: {
|
|
648
|
+
type: "object",
|
|
649
|
+
properties: {
|
|
650
|
+
workflowId: {
|
|
651
|
+
type: "string",
|
|
652
|
+
description: "ID of the workflow to cancel"
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
required: ["workflowId"]
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
yesod_cost_summary: {
|
|
659
|
+
name: "yesod_cost_summary",
|
|
660
|
+
description: "Get a cost breakdown showing token usage and USD cost, optionally filtered to a specific workflow.",
|
|
661
|
+
inputSchema: {
|
|
662
|
+
type: "object",
|
|
663
|
+
properties: {
|
|
664
|
+
workflowId: {
|
|
665
|
+
type: "string",
|
|
666
|
+
description: "Optional: filter to a specific workflow"
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
};
|
|
115
672
|
// Annotate the CommonJS export names for ESM import in node:
|
|
116
673
|
0 && (module.exports = {
|
|
117
674
|
EventBus,
|
|
118
675
|
EventType,
|
|
119
676
|
InMemoryStorageAdapter,
|
|
120
|
-
|
|
677
|
+
OrchestrationEngine,
|
|
678
|
+
SimpleScheduler,
|
|
679
|
+
VantageEngine,
|
|
680
|
+
YESOD_TOOLS
|
|
121
681
|
});
|
|
122
682
|
//# sourceMappingURL=index.cjs.map
|