@mamdouh-aboammar/agentic-workflow 1.2.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/.claude-plugin/plugin.json +10 -0
- package/.codex-plugin/plugin.json +13 -0
- package/.skills.json +19 -0
- package/AGENTS.md +1344 -0
- package/CLAUDE.md +178 -0
- package/GEMINI.md +102 -0
- package/LICENSE +21 -0
- package/README.md +350 -0
- package/SKILL.md +132 -0
- package/bin/agentic-hooks.sh +79 -0
- package/bin/cli.js +1060 -0
- package/core/__init__.py +52 -0
- package/core/ai_evaluator.py +117 -0
- package/core/autopilot_engine.py +368 -0
- package/core/clean_code_guard.py +188 -0
- package/core/engine_py/__init__.py +29 -0
- package/core/engine_py/agent_worker.py +136 -0
- package/core/engine_py/decider.py +150 -0
- package/core/engine_py/energy.py +45 -0
- package/core/engine_py/event_bus.py +63 -0
- package/core/engine_py/executor.py +186 -0
- package/core/engine_py/models.py +193 -0
- package/core/engine_py/queue.py +314 -0
- package/core/engine_py/runner.py +116 -0
- package/core/engine_py/system_workers.py +70 -0
- package/core/engine_py/toon_adapter.py +586 -0
- package/core/engine_py/verification_controller.py +208 -0
- package/core/engine_py/worker.py +167 -0
- package/core/engine_spec/event_schema.json +65 -0
- package/core/engine_spec/example_workflow.yaml +73 -0
- package/core/engine_spec/workflow_schema.json +127 -0
- package/core/hooks/__init__.py +29 -0
- package/core/hooks/adapters/__init__.py +25 -0
- package/core/hooks/adapters/claude_adapter.py +83 -0
- package/core/hooks/adapters/cli_agent_adapter.py +82 -0
- package/core/hooks/adapters/codex_adapter.py +78 -0
- package/core/hooks/adapters/cursor_adapter.py +73 -0
- package/core/hooks/adapters/gemini_adapter.py +93 -0
- package/core/hooks/adapters/homebrew_adapter.py +69 -0
- package/core/hooks/adapters/mcp_proxy.py +133 -0
- package/core/hooks/adapters/shell_adapter.py +65 -0
- package/core/hooks/dispatcher.py +118 -0
- package/core/hooks/policy_engine.py +375 -0
- package/core/hooks/session_end.py +141 -0
- package/core/hooks/types.py +147 -0
- package/core/integrations/__init__.py +28 -0
- package/core/integrations/installer.py +225 -0
- package/core/integrations/lifecycle_director.py +175 -0
- package/core/integrations/registry.py +105 -0
- package/core/multi_agent_system.py +164 -0
- package/core/skills_indexer.py +742 -0
- package/core/system/__init__.py +25 -0
- package/core/system/announcements.py +72 -0
- package/core/system/dependencies.py +69 -0
- package/core/system/doctor.py +171 -0
- package/core/system/health.py +144 -0
- package/core/system/installer.py +137 -0
- package/core/system/notifications.py +97 -0
- package/core/system/refresher.py +110 -0
- package/core/system/updater.py +167 -0
- package/core/system/version_tracker.py +65 -0
- package/docs/architecture_plan.md +7 -0
- package/docs/guides/failure-recovery.md +714 -0
- package/docs/implementation_summary.md +10 -0
- package/docs/protocols/autopilot-execution.md +148 -0
- package/docs/protocols/code-change-protocol.md +49 -0
- package/docs/protocols/context-preservation-detail.md +114 -0
- package/docs/protocols/quality-gates.md +110 -0
- package/docs/protocols/ulw-mode.md +60 -0
- package/docs/research_findings.md +10 -0
- package/docs/solutions/autonomous-autopilot-engine-architecture.md +38 -0
- package/install.sh +111 -0
- package/marketplace.json +37 -0
- package/package.json +81 -0
- package/skills/agentic-workflow/SKILL.md +132 -0
- package/skills/agentic-workflow/skill-spec.json +100 -0
- package/soul.md +445 -0
- package/src/engine_ts/decider.ts +186 -0
- package/src/engine_ts/event-bus.ts +57 -0
- package/src/engine_ts/executor.ts +262 -0
- package/src/engine_ts/index.ts +12 -0
- package/src/engine_ts/queue.ts +93 -0
- package/src/engine_ts/runner.ts +108 -0
- package/src/engine_ts/skills-indexer.ts +264 -0
- package/src/engine_ts/toon-adapter.ts +91 -0
- package/src/engine_ts/types.ts +134 -0
- package/src/engine_ts/verification-controller.ts +204 -0
- package/src/engine_ts/worker.ts +280 -0
- package/src/hooks/adapters/claude-adapter.ts +54 -0
- package/src/hooks/adapters/cli-agent-adapter.ts +46 -0
- package/src/hooks/adapters/codex-adapter.ts +69 -0
- package/src/hooks/adapters/cursor-adapter.ts +60 -0
- package/src/hooks/adapters/gemini-adapter.ts +71 -0
- package/src/hooks/adapters/homebrew-adapter.ts +36 -0
- package/src/hooks/adapters/mcp-proxy.ts +66 -0
- package/src/hooks/adapters/shell-adapter.ts +42 -0
- package/src/hooks/dispatcher.ts +113 -0
- package/src/hooks/index.ts +16 -0
- package/src/hooks/policy-engine.ts +376 -0
- package/src/hooks/session-end.ts +125 -0
- package/src/hooks/types.ts +61 -0
- package/src/index.d.ts +34 -0
- package/src/index.ts +23 -0
- package/src/integrations/index.ts +7 -0
- package/src/integrations/installer.ts +208 -0
- package/src/integrations/lifecycle-director.ts +139 -0
- package/src/integrations/registry.ts +82 -0
- package/src/system/announcements.ts +143 -0
- package/src/system/dependencies.ts +176 -0
- package/src/system/doctor.ts +374 -0
- package/src/system/health.ts +270 -0
- package/src/system/index.ts +14 -0
- package/src/system/installer.ts +262 -0
- package/src/system/notifications.ts +180 -0
- package/src/system/refresher.ts +207 -0
- package/src/system/types.ts +268 -0
- package/src/system/updater.ts +219 -0
- package/src/system/version-tracker.ts +137 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* executor.ts ā Main TypeScript Engine Orchestrator & SOT Coordinator.
|
|
3
|
+
*
|
|
4
|
+
* Enforces Absolute Criterion 2: Single-File SOT (state.yaml) atomic updates.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "node:fs";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import {
|
|
10
|
+
WorkflowDefinition,
|
|
11
|
+
WorkflowInstance,
|
|
12
|
+
WorkflowStatus,
|
|
13
|
+
EngineEvent
|
|
14
|
+
} from "./types.js";
|
|
15
|
+
import { AgenticDecider } from "./decider.js";
|
|
16
|
+
import { TaskQueue } from "./queue.js";
|
|
17
|
+
import { AsyncEventBus } from "./event-bus.js";
|
|
18
|
+
import { BaseWorker } from "./worker.js";
|
|
19
|
+
|
|
20
|
+
export class AgenticExecutor {
|
|
21
|
+
public workflowDef: WorkflowDefinition;
|
|
22
|
+
public queue: TaskQueue;
|
|
23
|
+
public eventBus: AsyncEventBus;
|
|
24
|
+
public projectDir: string;
|
|
25
|
+
public sotPath: string;
|
|
26
|
+
public decider: AgenticDecider;
|
|
27
|
+
public instance: WorkflowInstance;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
workflowDef: WorkflowDefinition,
|
|
31
|
+
queue: TaskQueue,
|
|
32
|
+
eventBus: AsyncEventBus,
|
|
33
|
+
projectDir: string = ".",
|
|
34
|
+
sotFilename: string = "state.yaml"
|
|
35
|
+
) {
|
|
36
|
+
this.workflowDef = workflowDef;
|
|
37
|
+
this.queue = queue;
|
|
38
|
+
this.eventBus = eventBus;
|
|
39
|
+
this.projectDir = path.resolve(projectDir);
|
|
40
|
+
this.sotPath = path.join(this.projectDir, sotFilename);
|
|
41
|
+
this.decider = new AgenticDecider();
|
|
42
|
+
|
|
43
|
+
const traceId = `ts_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
44
|
+
this.instance = {
|
|
45
|
+
workflow_id: `wf_${Math.random().toString(36).slice(2, 10)}`,
|
|
46
|
+
workflow_def: workflowDef,
|
|
47
|
+
trace_id: traceId,
|
|
48
|
+
status: WorkflowStatus.RUNNING,
|
|
49
|
+
current_stage_index: 0,
|
|
50
|
+
tasks: {},
|
|
51
|
+
variables: { ...(workflowDef.input_parameters || {}) },
|
|
52
|
+
outputs: {},
|
|
53
|
+
started_at: Date.now(),
|
|
54
|
+
autopilot_enabled: true
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
this.writeSot();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public writeSot(): void {
|
|
61
|
+
const currentStage = this.workflowDef.stages[this.instance.current_stage_index];
|
|
62
|
+
const totalStages = this.workflowDef.stages.length;
|
|
63
|
+
|
|
64
|
+
// Build structured SOT ā mirrors Python engine's _write_sot() field set exactly
|
|
65
|
+
const sotData: Record<string, any> = {
|
|
66
|
+
workflow: {
|
|
67
|
+
title: this.workflowDef.name,
|
|
68
|
+
version: this.workflowDef.version,
|
|
69
|
+
workflow_id: this.instance.workflow_id,
|
|
70
|
+
trace_id: this.instance.trace_id,
|
|
71
|
+
status: this.instance.status,
|
|
72
|
+
current_stage: currentStage ? currentStage.id : "COMPLETED",
|
|
73
|
+
current_stage_name: currentStage ? currentStage.name : "All Stages Finished",
|
|
74
|
+
total_stages: totalStages,
|
|
75
|
+
autopilot: {
|
|
76
|
+
enabled: this.instance.autopilot_enabled,
|
|
77
|
+
mode: this.instance.autopilot_enabled ? "auto" : "manual",
|
|
78
|
+
current_step: this.instance.current_stage_index
|
|
79
|
+
},
|
|
80
|
+
energy_budget: {
|
|
81
|
+
initial: 100,
|
|
82
|
+
remaining: 100,
|
|
83
|
+
consumed: 0
|
|
84
|
+
},
|
|
85
|
+
runtime: "typescript_bun"
|
|
86
|
+
},
|
|
87
|
+
tasks: {} as Record<string, any>,
|
|
88
|
+
outputs: this.instance.outputs ?? {}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
for (const [tid, t] of Object.entries(this.instance.tasks)) {
|
|
92
|
+
sotData.tasks[tid] = {
|
|
93
|
+
task_id: t.task_id,
|
|
94
|
+
stage_id: t.stage_id,
|
|
95
|
+
type: t.task_def.type,
|
|
96
|
+
role: t.task_def.role,
|
|
97
|
+
status: t.status,
|
|
98
|
+
attempt: t.attempt,
|
|
99
|
+
pacs_score: t.pacs_score ?? 0,
|
|
100
|
+
gate_verdict: t.gate_verdict ?? "PENDING",
|
|
101
|
+
deliverable: t.task_def.deliverable_path ?? "",
|
|
102
|
+
error: t.error_message ?? null
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// āā YAML serialiser āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
107
|
+
const ind = (n: number) => " ".repeat(n);
|
|
108
|
+
const yamlStr = (v: any): string => {
|
|
109
|
+
if (v === null || v === undefined) return "null";
|
|
110
|
+
if (typeof v === "boolean") return v ? "true" : "false";
|
|
111
|
+
if (typeof v === "number") return String(v);
|
|
112
|
+
if (typeof v === "string") {
|
|
113
|
+
if (v === "" || /[:#\[\]{},|>&*!%@`]/.test(v) || v.includes('"')) {
|
|
114
|
+
return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
115
|
+
}
|
|
116
|
+
return v;
|
|
117
|
+
}
|
|
118
|
+
return `"${String(v)}"`;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const lines: string[] = [];
|
|
122
|
+
const wf = sotData.workflow;
|
|
123
|
+
|
|
124
|
+
lines.push("workflow:");
|
|
125
|
+
lines.push(`${ind(1)}title: ${yamlStr(wf.title)}`);
|
|
126
|
+
lines.push(`${ind(1)}version: ${yamlStr(wf.version)}`);
|
|
127
|
+
lines.push(`${ind(1)}workflow_id: ${yamlStr(wf.workflow_id)}`);
|
|
128
|
+
lines.push(`${ind(1)}trace_id: ${yamlStr(wf.trace_id)}`);
|
|
129
|
+
lines.push(`${ind(1)}status: ${yamlStr(wf.status)}`);
|
|
130
|
+
lines.push(`${ind(1)}current_stage: ${yamlStr(wf.current_stage)}`);
|
|
131
|
+
lines.push(`${ind(1)}current_stage_name: ${yamlStr(wf.current_stage_name)}`);
|
|
132
|
+
lines.push(`${ind(1)}total_stages: ${wf.total_stages}`);
|
|
133
|
+
lines.push(`${ind(1)}autopilot:`);
|
|
134
|
+
lines.push(`${ind(2)}enabled: ${wf.autopilot.enabled}`);
|
|
135
|
+
lines.push(`${ind(2)}mode: ${yamlStr(wf.autopilot.mode)}`);
|
|
136
|
+
lines.push(`${ind(2)}current_step: ${wf.autopilot.current_step}`);
|
|
137
|
+
lines.push(`${ind(1)}energy_budget:`);
|
|
138
|
+
lines.push(`${ind(2)}initial: ${wf.energy_budget.initial}`);
|
|
139
|
+
lines.push(`${ind(2)}remaining: ${wf.energy_budget.remaining}`);
|
|
140
|
+
lines.push(`${ind(2)}consumed: ${wf.energy_budget.consumed}`);
|
|
141
|
+
lines.push(`${ind(1)}runtime: ${yamlStr(wf.runtime)}`);
|
|
142
|
+
|
|
143
|
+
lines.push("tasks:");
|
|
144
|
+
for (const [tid, t] of Object.entries(sotData.tasks)) {
|
|
145
|
+
const task = t as Record<string, any>;
|
|
146
|
+
lines.push(`${ind(1)}${tid}:`);
|
|
147
|
+
lines.push(`${ind(2)}task_id: ${yamlStr(task.task_id)}`);
|
|
148
|
+
lines.push(`${ind(2)}stage_id: ${yamlStr(task.stage_id)}`);
|
|
149
|
+
lines.push(`${ind(2)}type: ${yamlStr(task.type)}`);
|
|
150
|
+
lines.push(`${ind(2)}role: ${yamlStr(task.role)}`);
|
|
151
|
+
lines.push(`${ind(2)}status: ${yamlStr(task.status)}`);
|
|
152
|
+
lines.push(`${ind(2)}attempt: ${task.attempt}`);
|
|
153
|
+
lines.push(`${ind(2)}pacs_score: ${task.pacs_score}`);
|
|
154
|
+
lines.push(`${ind(2)}gate_verdict: ${yamlStr(task.gate_verdict)}`);
|
|
155
|
+
lines.push(`${ind(2)}deliverable: ${yamlStr(task.deliverable)}`);
|
|
156
|
+
lines.push(`${ind(2)}error: ${yamlStr(task.error)}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
lines.push("outputs:");
|
|
160
|
+
for (const [key, val] of Object.entries(sotData.outputs)) {
|
|
161
|
+
lines.push(`${ind(1)}${key}: ${yamlStr(String(val))}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Atomic write: .tmp ā rename (mirrors Python's os.replace)
|
|
165
|
+
const tempPath = `${this.sotPath}.tmp`;
|
|
166
|
+
fs.writeFileSync(tempPath, lines.join("\n") + "\n", "utf-8");
|
|
167
|
+
fs.renameSync(tempPath, this.sotPath);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public async step(): Promise<boolean> {
|
|
171
|
+
this.queue.reclaimExpired();
|
|
172
|
+
const res = this.decider.evaluate(this.instance);
|
|
173
|
+
|
|
174
|
+
for (const task of res.tasks_to_schedule) {
|
|
175
|
+
this.queue.push(task);
|
|
176
|
+
await this.eventBus.publish({
|
|
177
|
+
event_id: `evt_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
|
178
|
+
event_type: "task.scheduled",
|
|
179
|
+
timestamp: Date.now(),
|
|
180
|
+
trace_id: this.instance.trace_id,
|
|
181
|
+
workflow_id: this.instance.workflow_id,
|
|
182
|
+
stage_id: task.stage_id,
|
|
183
|
+
task_id: task.task_id,
|
|
184
|
+
payload: { type: task.task_def.type, role: task.task_def.role }
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
for (const task of res.tasks_to_retry) {
|
|
189
|
+
this.queue.push(task);
|
|
190
|
+
await this.eventBus.publish({
|
|
191
|
+
event_id: `evt_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
|
192
|
+
event_type: "task.retrying",
|
|
193
|
+
timestamp: Date.now(),
|
|
194
|
+
trace_id: this.instance.trace_id,
|
|
195
|
+
workflow_id: this.instance.workflow_id,
|
|
196
|
+
stage_id: task.stage_id,
|
|
197
|
+
task_id: task.task_id,
|
|
198
|
+
payload: { attempt: task.attempt }
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (res.stage_transitioned) {
|
|
203
|
+
await this.eventBus.publish({
|
|
204
|
+
event_id: `evt_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
|
205
|
+
event_type: "stage.transitioned",
|
|
206
|
+
timestamp: Date.now(),
|
|
207
|
+
trace_id: this.instance.trace_id,
|
|
208
|
+
workflow_id: this.instance.workflow_id,
|
|
209
|
+
stage_id: res.new_stage_id
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.writeSot();
|
|
214
|
+
|
|
215
|
+
if (res.is_terminal) {
|
|
216
|
+
const eventType =
|
|
217
|
+
res.workflow_status === WorkflowStatus.COMPLETED ? "workflow.completed" : "workflow.failed";
|
|
218
|
+
await this.eventBus.publish({
|
|
219
|
+
event_id: `evt_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
|
220
|
+
event_type: eventType,
|
|
221
|
+
timestamp: Date.now(),
|
|
222
|
+
trace_id: this.instance.trace_id,
|
|
223
|
+
workflow_id: this.instance.workflow_id,
|
|
224
|
+
payload: { status: res.workflow_status, error: res.error_message }
|
|
225
|
+
});
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public async runUntilComplete(
|
|
233
|
+
workers: BaseWorker[],
|
|
234
|
+
pollIntervalMs: number = 20,
|
|
235
|
+
maxIterations: number = 100
|
|
236
|
+
): Promise<boolean> {
|
|
237
|
+
await this.eventBus.publish({
|
|
238
|
+
event_id: `evt_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
|
239
|
+
event_type: "workflow.started",
|
|
240
|
+
timestamp: Date.now(),
|
|
241
|
+
trace_id: this.instance.trace_id,
|
|
242
|
+
workflow_id: this.instance.workflow_id,
|
|
243
|
+
payload: { name: this.workflowDef.name }
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
let iterations = 0;
|
|
247
|
+
while (iterations < maxIterations) {
|
|
248
|
+
iterations++;
|
|
249
|
+
|
|
250
|
+
for (const w of workers) {
|
|
251
|
+
await w.runOnce();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const active = await this.step();
|
|
255
|
+
if (!active) break;
|
|
256
|
+
|
|
257
|
+
await new Promise(r => setTimeout(r, pollIntervalMs));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return this.instance.status === WorkflowStatus.COMPLETED;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* index.ts ā TypeScript Agentic Workflow Engine entry point.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from "./types.js";
|
|
6
|
+
export * from "./event-bus.js";
|
|
7
|
+
export * from "./queue.js";
|
|
8
|
+
export * from "./decider.js";
|
|
9
|
+
export * from "./verification-controller.js";
|
|
10
|
+
export * from "./worker.js";
|
|
11
|
+
export * from "./executor.js";
|
|
12
|
+
export * from "./skills-indexer.js";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* queue.ts ā Pluggable Task Queue for TypeScript / Bun Engine.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { TaskInstance, TaskStatus } from "./types.js";
|
|
6
|
+
|
|
7
|
+
export interface TaskQueue {
|
|
8
|
+
push(task: TaskInstance): void;
|
|
9
|
+
poll(taskTypes: string[], workerId: string, leaseSeconds?: number): TaskInstance | null;
|
|
10
|
+
ack(taskId: string): void;
|
|
11
|
+
nack(taskId: string, requeue?: boolean): void;
|
|
12
|
+
heartbeat(taskId: string, workerId: string, extendSeconds?: number): boolean;
|
|
13
|
+
reclaimExpired(): string[];
|
|
14
|
+
size(): number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class InMemoryTaskQueue implements TaskQueue {
|
|
18
|
+
private tasks: Map<string, TaskInstance> = new Map();
|
|
19
|
+
private queue: string[] = [];
|
|
20
|
+
|
|
21
|
+
public push(task: TaskInstance): void {
|
|
22
|
+
this.tasks.set(task.task_id, task);
|
|
23
|
+
if (!this.queue.includes(task.task_id)) {
|
|
24
|
+
this.queue.push(task.task_id);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public poll(taskTypes: string[], workerId: string, leaseSeconds: number = 60): TaskInstance | null {
|
|
29
|
+
for (let i = 0; i < this.queue.length; i++) {
|
|
30
|
+
const taskId = this.queue[i];
|
|
31
|
+
const task = this.tasks.get(taskId);
|
|
32
|
+
if (task && (taskTypes.length === 0 || taskTypes.includes(task.task_def.type))) {
|
|
33
|
+
this.queue.splice(i, 1);
|
|
34
|
+
task.status = TaskStatus.POLLED;
|
|
35
|
+
task.worker_id = workerId;
|
|
36
|
+
task.lease_expires_at = Date.now() + leaseSeconds * 1000;
|
|
37
|
+
return task;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public ack(taskId: string): void {
|
|
44
|
+
this.tasks.delete(taskId);
|
|
45
|
+
const idx = this.queue.indexOf(taskId);
|
|
46
|
+
if (idx !== -1) this.queue.splice(idx, 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public nack(taskId: string, requeue: boolean = true): void {
|
|
50
|
+
const task = this.tasks.get(taskId);
|
|
51
|
+
if (task) {
|
|
52
|
+
task.worker_id = null;
|
|
53
|
+
task.lease_expires_at = null;
|
|
54
|
+
if (requeue && !this.queue.includes(taskId)) {
|
|
55
|
+
task.status = TaskStatus.SCHEDULED;
|
|
56
|
+
this.queue.push(taskId);
|
|
57
|
+
} else if (!requeue) {
|
|
58
|
+
task.status = TaskStatus.FAILED;
|
|
59
|
+
this.tasks.delete(taskId);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public heartbeat(taskId: string, workerId: string, extendSeconds: number = 60): boolean {
|
|
65
|
+
const task = this.tasks.get(taskId);
|
|
66
|
+
if (task && task.worker_id === workerId) {
|
|
67
|
+
task.lease_expires_at = Date.now() + extendSeconds * 1000;
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public reclaimExpired(): string[] {
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
const reclaimed: string[] = [];
|
|
76
|
+
for (const [taskId, task] of this.tasks.entries()) {
|
|
77
|
+
if (task.worker_id && task.lease_expires_at && task.lease_expires_at < now) {
|
|
78
|
+
task.worker_id = null;
|
|
79
|
+
task.lease_expires_at = null;
|
|
80
|
+
task.status = TaskStatus.SCHEDULED;
|
|
81
|
+
if (!this.queue.includes(taskId)) {
|
|
82
|
+
this.queue.push(taskId);
|
|
83
|
+
}
|
|
84
|
+
reclaimed.push(taskId);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return reclaimed;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public size(): number {
|
|
91
|
+
return this.queue.length;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runner.ts ā CLI entry point for TypeScript Agentic Engine.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import { WorkflowDefinition, AgentRole } from "./types.js";
|
|
8
|
+
import { InMemoryTaskQueue } from "./queue.js";
|
|
9
|
+
import { AsyncEventBus } from "./event-bus.js";
|
|
10
|
+
import { VerificationController } from "./verification-controller.js";
|
|
11
|
+
import { SystemWorker, AgentWorker } from "./worker.js";
|
|
12
|
+
import { AgenticExecutor } from "./executor.js";
|
|
13
|
+
import { LifecycleDirector } from "../integrations/index.ts";
|
|
14
|
+
|
|
15
|
+
async function main() {
|
|
16
|
+
const projectDir = process.cwd();
|
|
17
|
+
console.log(`š [engine-ts] Starting Event-Driven Agentic Engine (TypeScript / Bun)...`);
|
|
18
|
+
|
|
19
|
+
const defaultWorkflow: WorkflowDefinition = {
|
|
20
|
+
name: "autonomous_agentic_pipeline",
|
|
21
|
+
version: "1.0.0",
|
|
22
|
+
stages: [
|
|
23
|
+
{
|
|
24
|
+
id: "stage_01_research",
|
|
25
|
+
name: "Stage 1: Research",
|
|
26
|
+
tasks: [
|
|
27
|
+
{
|
|
28
|
+
id: "task_research_01",
|
|
29
|
+
name: "Analyze System Architecture and Dependencies",
|
|
30
|
+
type: "agent.task",
|
|
31
|
+
role: AgentRole.RESEARCHER,
|
|
32
|
+
deliverable_path: "docs/research_findings.md",
|
|
33
|
+
criteria: ["Analyze architecture patterns", "Establish baseline"]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "stage_02_planning",
|
|
39
|
+
name: "Stage 2: Planning",
|
|
40
|
+
tasks: [
|
|
41
|
+
{
|
|
42
|
+
id: "task_plan_approval",
|
|
43
|
+
name: "Autopilot Plan Approval",
|
|
44
|
+
type: "agent.human",
|
|
45
|
+
role: AgentRole.ORCHESTRATOR
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "stage_03_implementation",
|
|
51
|
+
name: "Stage 3: Implementation",
|
|
52
|
+
tasks: [
|
|
53
|
+
{
|
|
54
|
+
id: "task_code_impl",
|
|
55
|
+
name: "Implement Core Production Logic",
|
|
56
|
+
type: "agent.task",
|
|
57
|
+
role: AgentRole.ENGINEER,
|
|
58
|
+
deliverable_path: "docs/implementation_summary.md",
|
|
59
|
+
criteria: ["Pass unit tests", "Generate comprehensive documentation"]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "task_review_01",
|
|
63
|
+
name: "Adversarial Code & Fact-Check Review",
|
|
64
|
+
type: "agent.review",
|
|
65
|
+
role: AgentRole.REVIEWER,
|
|
66
|
+
deliverable_path: "review-logs/step-3-review.md",
|
|
67
|
+
criteria: ["Zero critical vulnerabilities"]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const queue = new InMemoryTaskQueue();
|
|
75
|
+
const ledgerPath = path.join(projectDir, "ledger.jsonl");
|
|
76
|
+
const eventBus = new AsyncEventBus(ledgerPath);
|
|
77
|
+
const verifier = new VerificationController(projectDir);
|
|
78
|
+
|
|
79
|
+
const sysWorker = new SystemWorker("sys_worker_1", queue, eventBus, verifier);
|
|
80
|
+
const agentWorker = new AgentWorker("agent_worker_1", queue, eventBus, verifier, projectDir);
|
|
81
|
+
|
|
82
|
+
const executor = new AgenticExecutor(defaultWorkflow, queue, eventBus, projectDir, "state.yaml");
|
|
83
|
+
|
|
84
|
+
const lifecycleDirector = new LifecycleDirector(projectDir);
|
|
85
|
+
console.log(`š§ [lifecycle-director] Lifecycle Director active (Ponytail, TOON, Fable, Caveman).`);
|
|
86
|
+
console.log(`ā” Trace ID: ${executor.instance.trace_id}`);
|
|
87
|
+
console.log(`ā” Workflow ID: ${executor.instance.workflow_id}`);
|
|
88
|
+
console.log(`ā” SOT Path: ${executor.sotPath}`);
|
|
89
|
+
|
|
90
|
+
const success = await executor.runUntilComplete([sysWorker, agentWorker], 20, 50);
|
|
91
|
+
|
|
92
|
+
if (success) {
|
|
93
|
+
lifecycleDirector.executePostPhaseActions("handoff", {
|
|
94
|
+
trace_id: executor.instance.trace_id,
|
|
95
|
+
next_action: "TypeScript Event-Driven Engine Workflow execution completed successfully."
|
|
96
|
+
});
|
|
97
|
+
console.log(`\nš [engine-ts] Workflow Completed Successfully with All Gates Verified!`);
|
|
98
|
+
process.exit(0);
|
|
99
|
+
} else {
|
|
100
|
+
console.error(`\nā [engine-ts] Workflow execution halted or failed gates.`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
main().catch(err => {
|
|
106
|
+
console.error("Fatal error in engine runner:", err);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
});
|