@kalphq/core 0.0.0-dev-20260507190243
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/.turbo/turbo-build.log +22 -0
- package/CHANGELOG.md +15 -0
- package/dist/chunk-G4LOF3MT.js +957 -0
- package/dist/chunk-G4LOF3MT.js.map +1 -0
- package/dist/factory.d.ts +59 -0
- package/dist/factory.js +18 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/reactor-Brv_eUBE.d.ts +742 -0
- package/eslint.config.js +33 -0
- package/package.json +30 -0
- package/src/adapters/interfaces.ts +248 -0
- package/src/engine/context-builder.ts +151 -0
- package/src/engine/execution-log.ts +73 -0
- package/src/engine/primitives/actions.ts +364 -0
- package/src/engine/primitives/agent-meta.ts +37 -0
- package/src/engine/primitives/ai.ts +86 -0
- package/src/engine/primitives/date.ts +144 -0
- package/src/engine/primitives/http.ts +56 -0
- package/src/engine/primitives/math.ts +173 -0
- package/src/engine/primitives/mcp.ts +59 -0
- package/src/engine/primitives/storage.ts +91 -0
- package/src/engine/reactor.ts +308 -0
- package/src/engine/types.ts +304 -0
- package/src/env.d.ts +142 -0
- package/src/factory.ts +69 -0
- package/src/index.ts +61 -0
- package/tsconfig.json +23 -0
- package/tsup.config.ts +9 -0
|
@@ -0,0 +1,957 @@
|
|
|
1
|
+
// src/engine/execution-log.ts
|
|
2
|
+
var ExecutionLog = class {
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new execution log backed by the given event store.
|
|
5
|
+
*
|
|
6
|
+
* @param eventStore - The store used to persist and load events.
|
|
7
|
+
*/
|
|
8
|
+
constructor(eventStore) {
|
|
9
|
+
this.eventStore = eventStore;
|
|
10
|
+
}
|
|
11
|
+
/** In-memory buffer of events emitted during the current processing cycle. */
|
|
12
|
+
buffer = [];
|
|
13
|
+
/**
|
|
14
|
+
* Emits a structured execution event.
|
|
15
|
+
*
|
|
16
|
+
* The event is immediately persisted via the adapter and buffered in memory
|
|
17
|
+
* for the current processing cycle.
|
|
18
|
+
*
|
|
19
|
+
* @param event - The execution event to emit.
|
|
20
|
+
*/
|
|
21
|
+
async emit(event) {
|
|
22
|
+
this.buffer.push(event);
|
|
23
|
+
await this.eventStore.append(event);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Loads all previously persisted events from the adapter.
|
|
27
|
+
*
|
|
28
|
+
* Used for replay, rehydration, and debugging.
|
|
29
|
+
*
|
|
30
|
+
* @returns An ordered array of all persisted execution events.
|
|
31
|
+
*/
|
|
32
|
+
async load() {
|
|
33
|
+
return this.eventStore.loadAll();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns events emitted during the current processing cycle (in-memory only).
|
|
37
|
+
*
|
|
38
|
+
* @returns The in-memory event buffer.
|
|
39
|
+
*/
|
|
40
|
+
getBuffer() {
|
|
41
|
+
return this.buffer;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Clears the in-memory buffer. Called between processing cycles.
|
|
45
|
+
*/
|
|
46
|
+
clearBuffer() {
|
|
47
|
+
this.buffer = [];
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// src/engine/primitives/ai.ts
|
|
52
|
+
function createAIPrimitive(provider, log, execCtx) {
|
|
53
|
+
const ids = {
|
|
54
|
+
executionId: execCtx?.executionId ?? "",
|
|
55
|
+
traceId: execCtx?.traceId ?? "",
|
|
56
|
+
threadId: execCtx?.threadId ?? ""
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
async generate(params) {
|
|
60
|
+
const result = await provider.generate(params);
|
|
61
|
+
await log.emit({
|
|
62
|
+
type: "primitive.invoked",
|
|
63
|
+
name: "ai.generate",
|
|
64
|
+
params: { model: params.model, prompt: params.prompt },
|
|
65
|
+
result,
|
|
66
|
+
...ids,
|
|
67
|
+
timestamp: Date.now()
|
|
68
|
+
});
|
|
69
|
+
return result;
|
|
70
|
+
},
|
|
71
|
+
stream(params) {
|
|
72
|
+
void log.emit({
|
|
73
|
+
type: "primitive.invoked",
|
|
74
|
+
name: "ai.stream",
|
|
75
|
+
params: { model: params.model, prompt: params.prompt },
|
|
76
|
+
result: "[stream]",
|
|
77
|
+
...ids,
|
|
78
|
+
timestamp: Date.now()
|
|
79
|
+
});
|
|
80
|
+
return provider.stream(params);
|
|
81
|
+
},
|
|
82
|
+
async classify(params) {
|
|
83
|
+
const result = await provider.classify(params);
|
|
84
|
+
await log.emit({
|
|
85
|
+
type: "primitive.invoked",
|
|
86
|
+
name: "ai.classify",
|
|
87
|
+
params: { input: params.input, labels: params.labels },
|
|
88
|
+
result,
|
|
89
|
+
...ids,
|
|
90
|
+
timestamp: Date.now()
|
|
91
|
+
});
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/engine/primitives/storage.ts
|
|
98
|
+
function createStoragePrimitive(stateStore, log, execCtx) {
|
|
99
|
+
const ids = {
|
|
100
|
+
executionId: execCtx?.executionId ?? "",
|
|
101
|
+
traceId: execCtx?.traceId ?? "",
|
|
102
|
+
threadId: execCtx?.threadId ?? ""
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
async get(key) {
|
|
106
|
+
const value = await stateStore.get(key);
|
|
107
|
+
await log.emit({
|
|
108
|
+
type: "state.read",
|
|
109
|
+
key,
|
|
110
|
+
value,
|
|
111
|
+
...ids,
|
|
112
|
+
timestamp: Date.now()
|
|
113
|
+
});
|
|
114
|
+
return value;
|
|
115
|
+
},
|
|
116
|
+
async put(key, value) {
|
|
117
|
+
await stateStore.set(key, value);
|
|
118
|
+
await log.emit({
|
|
119
|
+
type: "state.write",
|
|
120
|
+
key,
|
|
121
|
+
value,
|
|
122
|
+
...ids,
|
|
123
|
+
timestamp: Date.now()
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
async delete(key) {
|
|
127
|
+
await stateStore.delete(key);
|
|
128
|
+
await log.emit({
|
|
129
|
+
type: "state.write",
|
|
130
|
+
key,
|
|
131
|
+
value: void 0,
|
|
132
|
+
...ids,
|
|
133
|
+
timestamp: Date.now()
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
async increment(key, amount = 1) {
|
|
137
|
+
const newValue = await stateStore.increment(key, amount);
|
|
138
|
+
await log.emit({
|
|
139
|
+
type: "state.write",
|
|
140
|
+
key,
|
|
141
|
+
value: newValue,
|
|
142
|
+
...ids,
|
|
143
|
+
timestamp: Date.now()
|
|
144
|
+
});
|
|
145
|
+
return newValue;
|
|
146
|
+
},
|
|
147
|
+
async transaction(callback, _options) {
|
|
148
|
+
return stateStore.transaction(async (tx) => {
|
|
149
|
+
return callback(tx);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/engine/primitives/actions.ts
|
|
156
|
+
function parseDuration(duration) {
|
|
157
|
+
if (typeof duration === "number") return duration;
|
|
158
|
+
const match = duration.match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/i);
|
|
159
|
+
if (!match) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
`Invalid duration format: "${duration}". Expected e.g. "5m", "30s", "1h".`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
const value = parseFloat(match[1]);
|
|
165
|
+
const unit = match[2].toLowerCase();
|
|
166
|
+
const multipliers = {
|
|
167
|
+
ms: 1,
|
|
168
|
+
s: 1e3,
|
|
169
|
+
m: 6e4,
|
|
170
|
+
h: 36e5,
|
|
171
|
+
d: 864e5
|
|
172
|
+
};
|
|
173
|
+
return value * (multipliers[unit] ?? 1);
|
|
174
|
+
}
|
|
175
|
+
function createActionsPrimitive(log, scheduler, dispatch, execCtx) {
|
|
176
|
+
let loopCounter = 0;
|
|
177
|
+
const ids = {
|
|
178
|
+
executionId: execCtx?.executionId ?? "",
|
|
179
|
+
traceId: execCtx?.traceId ?? "",
|
|
180
|
+
threadId: execCtx?.threadId ?? ""
|
|
181
|
+
};
|
|
182
|
+
return {
|
|
183
|
+
/**
|
|
184
|
+
* Emits an `action.run` intent and suspends until the reactor resolves it.
|
|
185
|
+
* The handler receives a Promise that resolves with the step/tool result.
|
|
186
|
+
*/
|
|
187
|
+
async run(node, ...args) {
|
|
188
|
+
const target = `${node.kind}s.${node.id}`;
|
|
189
|
+
const input = args[0];
|
|
190
|
+
await log.emit({
|
|
191
|
+
type: "action.run",
|
|
192
|
+
target,
|
|
193
|
+
input,
|
|
194
|
+
...ids,
|
|
195
|
+
timestamp: Date.now()
|
|
196
|
+
});
|
|
197
|
+
const result = await dispatch(target, input);
|
|
198
|
+
await log.emit({
|
|
199
|
+
type: "action.run.completed",
|
|
200
|
+
target,
|
|
201
|
+
result,
|
|
202
|
+
...ids,
|
|
203
|
+
timestamp: Date.now()
|
|
204
|
+
});
|
|
205
|
+
return result;
|
|
206
|
+
},
|
|
207
|
+
/**
|
|
208
|
+
* Emits an `action.wait` event, schedules an alarm, and returns.
|
|
209
|
+
* The host adapter is responsible for re-entering the reactor when the alarm fires.
|
|
210
|
+
*/
|
|
211
|
+
async wait(duration) {
|
|
212
|
+
const ms = parseDuration(duration);
|
|
213
|
+
await log.emit({
|
|
214
|
+
type: "action.wait",
|
|
215
|
+
duration,
|
|
216
|
+
...ids,
|
|
217
|
+
timestamp: Date.now()
|
|
218
|
+
});
|
|
219
|
+
await scheduler.schedule(Date.now() + ms);
|
|
220
|
+
return { type: "timeout" };
|
|
221
|
+
},
|
|
222
|
+
/**
|
|
223
|
+
* Runtime-driven loop. Each iteration is a separate event, enabling
|
|
224
|
+
* persistence, rehydration, cut, pause, and per-iteration billing.
|
|
225
|
+
*/
|
|
226
|
+
loop(body) {
|
|
227
|
+
const loopId = `loop_${++loopCounter}_${Date.now()}`;
|
|
228
|
+
void (async () => {
|
|
229
|
+
await log.emit({
|
|
230
|
+
type: "action.loop.start",
|
|
231
|
+
loopId,
|
|
232
|
+
...ids,
|
|
233
|
+
timestamp: Date.now()
|
|
234
|
+
});
|
|
235
|
+
let iteration = 0;
|
|
236
|
+
while (true) {
|
|
237
|
+
await log.emit({
|
|
238
|
+
type: "action.loop.iteration",
|
|
239
|
+
loopId,
|
|
240
|
+
iteration,
|
|
241
|
+
...ids,
|
|
242
|
+
timestamp: Date.now()
|
|
243
|
+
});
|
|
244
|
+
try {
|
|
245
|
+
await body();
|
|
246
|
+
} catch (err) {
|
|
247
|
+
await log.emit({
|
|
248
|
+
type: "action.loop.end",
|
|
249
|
+
loopId,
|
|
250
|
+
reason: err instanceof Error ? err.message : "error",
|
|
251
|
+
...ids,
|
|
252
|
+
timestamp: Date.now()
|
|
253
|
+
});
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
iteration++;
|
|
257
|
+
}
|
|
258
|
+
})();
|
|
259
|
+
},
|
|
260
|
+
/**
|
|
261
|
+
* Intercepted fetch — logs the request and delegates to globalThis.fetch.
|
|
262
|
+
*/
|
|
263
|
+
async fetch(input, init) {
|
|
264
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
265
|
+
const method = init?.method ?? (input instanceof Request ? input.method : "GET");
|
|
266
|
+
const start = Date.now();
|
|
267
|
+
const response = await globalThis.fetch(input, init);
|
|
268
|
+
const durationMs = Date.now() - start;
|
|
269
|
+
await log.emit({
|
|
270
|
+
type: "action.fetch",
|
|
271
|
+
url,
|
|
272
|
+
method,
|
|
273
|
+
status: response.status,
|
|
274
|
+
durationMs,
|
|
275
|
+
...ids,
|
|
276
|
+
timestamp: Date.now()
|
|
277
|
+
});
|
|
278
|
+
return response;
|
|
279
|
+
},
|
|
280
|
+
async ask(_prompt, _schema, _options) {
|
|
281
|
+
await log.emit({
|
|
282
|
+
type: "action.ask",
|
|
283
|
+
...ids,
|
|
284
|
+
timestamp: Date.now()
|
|
285
|
+
});
|
|
286
|
+
return { text: "stub response" };
|
|
287
|
+
},
|
|
288
|
+
async requestApproval(_reason, _options) {
|
|
289
|
+
await log.emit({
|
|
290
|
+
type: "action.approval",
|
|
291
|
+
...ids,
|
|
292
|
+
timestamp: Date.now()
|
|
293
|
+
});
|
|
294
|
+
return true;
|
|
295
|
+
},
|
|
296
|
+
emit(eventName, payload, _options) {
|
|
297
|
+
void log.emit({
|
|
298
|
+
type: "action.emit",
|
|
299
|
+
event: eventName,
|
|
300
|
+
payload,
|
|
301
|
+
...ids,
|
|
302
|
+
timestamp: Date.now()
|
|
303
|
+
});
|
|
304
|
+
},
|
|
305
|
+
async callAgent(contract, input) {
|
|
306
|
+
await log.emit({
|
|
307
|
+
type: "action.call",
|
|
308
|
+
contract: contract.agentId,
|
|
309
|
+
input,
|
|
310
|
+
...ids,
|
|
311
|
+
timestamp: Date.now()
|
|
312
|
+
});
|
|
313
|
+
return {};
|
|
314
|
+
},
|
|
315
|
+
/**
|
|
316
|
+
* Pauses the agent until an external event occurs.
|
|
317
|
+
* Emits an action.wait event with duration and schedules a timeout alarm if specified.
|
|
318
|
+
*/
|
|
319
|
+
async waitForEvent(eventName, timeout) {
|
|
320
|
+
const duration = timeout ? parseDuration(timeout) : 0;
|
|
321
|
+
await log.emit({
|
|
322
|
+
type: "action.wait",
|
|
323
|
+
duration,
|
|
324
|
+
...ids,
|
|
325
|
+
timestamp: Date.now()
|
|
326
|
+
});
|
|
327
|
+
if (timeout) {
|
|
328
|
+
await scheduler.schedule(Date.now() + duration);
|
|
329
|
+
}
|
|
330
|
+
return { type: "event", eventName, payload: void 0 };
|
|
331
|
+
},
|
|
332
|
+
/**
|
|
333
|
+
* Pauses the agent until a specific date/time.
|
|
334
|
+
* Emits an action.wait event with the duration and schedules an alarm for the target time.
|
|
335
|
+
*/
|
|
336
|
+
async waitUntil(date) {
|
|
337
|
+
const targetTime = typeof date === "number" ? date : typeof date === "string" ? new Date(date).getTime() : date.getTime();
|
|
338
|
+
const now = Date.now();
|
|
339
|
+
const duration = Math.max(0, targetTime - now);
|
|
340
|
+
await log.emit({
|
|
341
|
+
type: "action.wait",
|
|
342
|
+
duration,
|
|
343
|
+
...ids,
|
|
344
|
+
timestamp: Date.now()
|
|
345
|
+
});
|
|
346
|
+
await scheduler.schedule(targetTime);
|
|
347
|
+
return {
|
|
348
|
+
type: "scheduled_time_reached",
|
|
349
|
+
scheduledAt: new Date(targetTime).toISOString()
|
|
350
|
+
};
|
|
351
|
+
},
|
|
352
|
+
/**
|
|
353
|
+
* Schedules a node for future non-blocking execution.
|
|
354
|
+
* Emits an action.schedule event and schedules the execution via the scheduler.
|
|
355
|
+
*/
|
|
356
|
+
async schedule(node, date, ...args) {
|
|
357
|
+
const targetTime = typeof date === "number" ? date : typeof date === "string" ? new Date(date).getTime() : date.getTime();
|
|
358
|
+
const target = `${node.kind}s.${node.id}`;
|
|
359
|
+
const input = args[0];
|
|
360
|
+
const scheduleId = `schedule_${target}_${targetTime}`;
|
|
361
|
+
await log.emit({
|
|
362
|
+
type: "action.schedule",
|
|
363
|
+
at: targetTime,
|
|
364
|
+
payload: { scheduleId, target, input },
|
|
365
|
+
...ids,
|
|
366
|
+
timestamp: Date.now()
|
|
367
|
+
});
|
|
368
|
+
await scheduler.schedule(targetTime);
|
|
369
|
+
return { scheduleId };
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/engine/primitives/mcp.ts
|
|
375
|
+
function createMcpPrimitive(log, execCtx) {
|
|
376
|
+
const ids = {
|
|
377
|
+
executionId: execCtx?.executionId ?? "",
|
|
378
|
+
traceId: execCtx?.traceId ?? "",
|
|
379
|
+
threadId: execCtx?.threadId ?? ""
|
|
380
|
+
};
|
|
381
|
+
return new Proxy({}, {
|
|
382
|
+
get(_target, serverName) {
|
|
383
|
+
return new Proxy(
|
|
384
|
+
{},
|
|
385
|
+
{
|
|
386
|
+
get(_target2, toolName) {
|
|
387
|
+
return async (input) => {
|
|
388
|
+
const timestamp = Date.now();
|
|
389
|
+
void log.emit({
|
|
390
|
+
type: "primitive.invoked",
|
|
391
|
+
name: "mcp." + serverName + "." + toolName,
|
|
392
|
+
params: input,
|
|
393
|
+
result: void 0,
|
|
394
|
+
...ids,
|
|
395
|
+
timestamp
|
|
396
|
+
});
|
|
397
|
+
return { error: "MCP not implemented in core yet" };
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/engine/primitives/date.ts
|
|
407
|
+
function createDatePrimitive(log, execCtx) {
|
|
408
|
+
const ids = {
|
|
409
|
+
executionId: execCtx?.executionId ?? "",
|
|
410
|
+
traceId: execCtx?.traceId ?? "",
|
|
411
|
+
threadId: execCtx?.threadId ?? ""
|
|
412
|
+
};
|
|
413
|
+
const baseTime = Date.now();
|
|
414
|
+
return {
|
|
415
|
+
/**
|
|
416
|
+
* Get the current time (event timestamp).
|
|
417
|
+
*/
|
|
418
|
+
now() {
|
|
419
|
+
return baseTime;
|
|
420
|
+
},
|
|
421
|
+
/**
|
|
422
|
+
* Format the current time as ISO string.
|
|
423
|
+
*/
|
|
424
|
+
toISOString() {
|
|
425
|
+
return new Date(baseTime).toISOString();
|
|
426
|
+
},
|
|
427
|
+
/**
|
|
428
|
+
* Check if the original event occurred after the given date.
|
|
429
|
+
*
|
|
430
|
+
* @param isoString - ISO 8601 date string to compare against.
|
|
431
|
+
*/
|
|
432
|
+
isAfter(isoString) {
|
|
433
|
+
const timestamp = Date.now();
|
|
434
|
+
void log.emit({
|
|
435
|
+
type: "primitive.invoked",
|
|
436
|
+
name: "date.isAfter",
|
|
437
|
+
params: isoString,
|
|
438
|
+
result: void 0,
|
|
439
|
+
...ids,
|
|
440
|
+
timestamp
|
|
441
|
+
});
|
|
442
|
+
return baseTime > new Date(isoString).getTime();
|
|
443
|
+
},
|
|
444
|
+
/**
|
|
445
|
+
* Add a duration to the current time.
|
|
446
|
+
*
|
|
447
|
+
* @param duration - Duration string (e.g., "1h", "30m", "24h") or milliseconds.
|
|
448
|
+
*/
|
|
449
|
+
add(duration) {
|
|
450
|
+
const timestamp = Date.now();
|
|
451
|
+
void log.emit({
|
|
452
|
+
type: "primitive.invoked",
|
|
453
|
+
name: "date.add",
|
|
454
|
+
params: duration,
|
|
455
|
+
result: void 0,
|
|
456
|
+
...ids,
|
|
457
|
+
timestamp
|
|
458
|
+
});
|
|
459
|
+
if (typeof duration === "number") {
|
|
460
|
+
return baseTime + duration;
|
|
461
|
+
}
|
|
462
|
+
const match = duration.match(/^(\d+)([smhd])$/);
|
|
463
|
+
if (!match) {
|
|
464
|
+
throw new Error(`Invalid duration format: ${duration}`);
|
|
465
|
+
}
|
|
466
|
+
const value = parseInt(match[1], 10);
|
|
467
|
+
const unit = match[2];
|
|
468
|
+
if (!unit) {
|
|
469
|
+
throw new Error(`Invalid duration format: ${duration}`);
|
|
470
|
+
}
|
|
471
|
+
const multipliers = {
|
|
472
|
+
s: 1e3,
|
|
473
|
+
m: 6e4,
|
|
474
|
+
h: 36e5,
|
|
475
|
+
d: 864e5
|
|
476
|
+
};
|
|
477
|
+
const multiplier = multipliers[unit];
|
|
478
|
+
if (!multiplier) {
|
|
479
|
+
throw new Error(`Invalid duration unit: ${unit}`);
|
|
480
|
+
}
|
|
481
|
+
return baseTime + value * multiplier;
|
|
482
|
+
},
|
|
483
|
+
/**
|
|
484
|
+
* Convert the event timestamp to a specific timezone.
|
|
485
|
+
*
|
|
486
|
+
* @param tz - IANA timezone identifier.
|
|
487
|
+
* @returns TimezoneFormatter for the converted time.
|
|
488
|
+
*/
|
|
489
|
+
timezone(tz) {
|
|
490
|
+
const formatter = {
|
|
491
|
+
format(format) {
|
|
492
|
+
const timestamp = Date.now();
|
|
493
|
+
void log.emit({
|
|
494
|
+
type: "primitive.invoked",
|
|
495
|
+
name: "date.timezone.format",
|
|
496
|
+
params: { tz, format },
|
|
497
|
+
result: void 0,
|
|
498
|
+
...ids,
|
|
499
|
+
timestamp
|
|
500
|
+
});
|
|
501
|
+
const date = new Date(baseTime);
|
|
502
|
+
return date.toISOString().replace("T", " ").slice(0, 19);
|
|
503
|
+
},
|
|
504
|
+
toISOString() {
|
|
505
|
+
return new Date(baseTime).toISOString();
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
return formatter;
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// src/engine/primitives/math.ts
|
|
514
|
+
function createMathPrimitive(log, execCtx) {
|
|
515
|
+
const ids = {
|
|
516
|
+
executionId: execCtx?.executionId ?? "",
|
|
517
|
+
traceId: execCtx?.traceId ?? "",
|
|
518
|
+
threadId: execCtx?.threadId ?? ""
|
|
519
|
+
};
|
|
520
|
+
let seed = execCtx?.executionId.split("").reduce((a, c) => a + c.charCodeAt(0), 0) ?? Date.now();
|
|
521
|
+
return {
|
|
522
|
+
/**
|
|
523
|
+
* Deterministic pseudo-random number generator.
|
|
524
|
+
* Seeded by the execution runId (ULID) for replay consistency.
|
|
525
|
+
*/
|
|
526
|
+
random() {
|
|
527
|
+
const timestamp = Date.now();
|
|
528
|
+
seed = (seed * 9301 + 49297) % 233280;
|
|
529
|
+
const result = seed / 233280;
|
|
530
|
+
void log.emit({
|
|
531
|
+
type: "primitive.invoked",
|
|
532
|
+
name: "math.random",
|
|
533
|
+
params: void 0,
|
|
534
|
+
result,
|
|
535
|
+
...ids,
|
|
536
|
+
timestamp
|
|
537
|
+
});
|
|
538
|
+
return result;
|
|
539
|
+
},
|
|
540
|
+
/** Round down to nearest integer. */
|
|
541
|
+
floor(x) {
|
|
542
|
+
const result = Math.floor(x);
|
|
543
|
+
const timestamp = Date.now();
|
|
544
|
+
void log.emit({
|
|
545
|
+
type: "primitive.invoked",
|
|
546
|
+
name: "math.floor",
|
|
547
|
+
params: x,
|
|
548
|
+
result,
|
|
549
|
+
...ids,
|
|
550
|
+
timestamp
|
|
551
|
+
});
|
|
552
|
+
return result;
|
|
553
|
+
},
|
|
554
|
+
/** Round up to nearest integer. */
|
|
555
|
+
ceil(x) {
|
|
556
|
+
const result = Math.ceil(x);
|
|
557
|
+
const timestamp = Date.now();
|
|
558
|
+
void log.emit({
|
|
559
|
+
type: "primitive.invoked",
|
|
560
|
+
name: "math.ceil",
|
|
561
|
+
params: x,
|
|
562
|
+
result,
|
|
563
|
+
...ids,
|
|
564
|
+
timestamp
|
|
565
|
+
});
|
|
566
|
+
return result;
|
|
567
|
+
},
|
|
568
|
+
/** Round to nearest integer. */
|
|
569
|
+
round(x) {
|
|
570
|
+
const result = Math.round(x);
|
|
571
|
+
const timestamp = Date.now();
|
|
572
|
+
void log.emit({
|
|
573
|
+
type: "primitive.invoked",
|
|
574
|
+
name: "math.round",
|
|
575
|
+
params: x,
|
|
576
|
+
result,
|
|
577
|
+
...ids,
|
|
578
|
+
timestamp
|
|
579
|
+
});
|
|
580
|
+
return result;
|
|
581
|
+
},
|
|
582
|
+
/** Return smallest of provided values. */
|
|
583
|
+
min(...values) {
|
|
584
|
+
const result = Math.min(...values);
|
|
585
|
+
const timestamp = Date.now();
|
|
586
|
+
void log.emit({
|
|
587
|
+
type: "primitive.invoked",
|
|
588
|
+
name: "math.min",
|
|
589
|
+
params: values,
|
|
590
|
+
result,
|
|
591
|
+
...ids,
|
|
592
|
+
timestamp
|
|
593
|
+
});
|
|
594
|
+
return result;
|
|
595
|
+
},
|
|
596
|
+
/** Return largest of provided values. */
|
|
597
|
+
max(...values) {
|
|
598
|
+
const result = Math.max(...values);
|
|
599
|
+
const timestamp = Date.now();
|
|
600
|
+
void log.emit({
|
|
601
|
+
type: "primitive.invoked",
|
|
602
|
+
name: "math.max",
|
|
603
|
+
params: values,
|
|
604
|
+
result,
|
|
605
|
+
...ids,
|
|
606
|
+
timestamp
|
|
607
|
+
});
|
|
608
|
+
return result;
|
|
609
|
+
},
|
|
610
|
+
/** Return absolute value. */
|
|
611
|
+
abs(x) {
|
|
612
|
+
const result = Math.abs(x);
|
|
613
|
+
const timestamp = Date.now();
|
|
614
|
+
void log.emit({
|
|
615
|
+
type: "primitive.invoked",
|
|
616
|
+
name: "math.abs",
|
|
617
|
+
params: x,
|
|
618
|
+
result,
|
|
619
|
+
...ids,
|
|
620
|
+
timestamp
|
|
621
|
+
});
|
|
622
|
+
return result;
|
|
623
|
+
},
|
|
624
|
+
/** Return base to the power of exponent. */
|
|
625
|
+
pow(base, exponent) {
|
|
626
|
+
const result = Math.pow(base, exponent);
|
|
627
|
+
const timestamp = Date.now();
|
|
628
|
+
void log.emit({
|
|
629
|
+
type: "primitive.invoked",
|
|
630
|
+
name: "math.pow",
|
|
631
|
+
params: { base, exponent },
|
|
632
|
+
result,
|
|
633
|
+
...ids,
|
|
634
|
+
timestamp
|
|
635
|
+
});
|
|
636
|
+
return result;
|
|
637
|
+
},
|
|
638
|
+
/** Return square root. */
|
|
639
|
+
sqrt(x) {
|
|
640
|
+
const result = Math.sqrt(x);
|
|
641
|
+
const timestamp = Date.now();
|
|
642
|
+
void log.emit({
|
|
643
|
+
type: "primitive.invoked",
|
|
644
|
+
name: "math.sqrt",
|
|
645
|
+
params: x,
|
|
646
|
+
result,
|
|
647
|
+
...ids,
|
|
648
|
+
timestamp
|
|
649
|
+
});
|
|
650
|
+
return result;
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/engine/primitives/agent-meta.ts
|
|
656
|
+
function createAgentMetaPrimitive(log, execCtx, agentConfig) {
|
|
657
|
+
return {
|
|
658
|
+
agentId: execCtx?.executionId ?? "unknown",
|
|
659
|
+
runId: execCtx?.traceId ?? "unknown",
|
|
660
|
+
name: agentConfig?.name ?? "unknown",
|
|
661
|
+
systemPrompt: agentConfig?.systemPrompt ?? "",
|
|
662
|
+
metadata: agentConfig?.metadata
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// src/engine/context-builder.ts
|
|
667
|
+
function buildHandlerContext(log, stateStore, scheduler, dispatch, providers, execCtx, agentConfig) {
|
|
668
|
+
const ids = {
|
|
669
|
+
executionId: execCtx?.executionId ?? "",
|
|
670
|
+
traceId: execCtx?.traceId ?? "",
|
|
671
|
+
threadId: execCtx?.threadId ?? ""
|
|
672
|
+
};
|
|
673
|
+
return {
|
|
674
|
+
ai: createAIPrimitive(providers.ai, log),
|
|
675
|
+
storage: createStoragePrimitive(stateStore, log, execCtx),
|
|
676
|
+
actions: createActionsPrimitive(log, scheduler, dispatch, execCtx),
|
|
677
|
+
auth: providers.auth,
|
|
678
|
+
memory: providers.memory,
|
|
679
|
+
vault: providers.vault,
|
|
680
|
+
mcp: createMcpPrimitive(log, execCtx),
|
|
681
|
+
agent: createAgentMetaPrimitive(log, execCtx, agentConfig),
|
|
682
|
+
date: createDatePrimitive(log, execCtx),
|
|
683
|
+
math: createMathPrimitive(log, execCtx),
|
|
684
|
+
log: {
|
|
685
|
+
debug: (msg, data) => void log.emit({
|
|
686
|
+
type: "log",
|
|
687
|
+
level: "debug",
|
|
688
|
+
msg,
|
|
689
|
+
data,
|
|
690
|
+
...ids,
|
|
691
|
+
timestamp: Date.now()
|
|
692
|
+
}),
|
|
693
|
+
info: (msg, data) => void log.emit({
|
|
694
|
+
type: "log",
|
|
695
|
+
level: "info",
|
|
696
|
+
msg,
|
|
697
|
+
data,
|
|
698
|
+
...ids,
|
|
699
|
+
timestamp: Date.now()
|
|
700
|
+
}),
|
|
701
|
+
warn: (msg, data) => void log.emit({
|
|
702
|
+
type: "log",
|
|
703
|
+
level: "warn",
|
|
704
|
+
msg,
|
|
705
|
+
data,
|
|
706
|
+
...ids,
|
|
707
|
+
timestamp: Date.now()
|
|
708
|
+
}),
|
|
709
|
+
error: (err, data) => {
|
|
710
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
711
|
+
const errorData = err instanceof Error ? { ...data, stack: err.stack } : data;
|
|
712
|
+
void log.emit({
|
|
713
|
+
type: "log",
|
|
714
|
+
level: "error",
|
|
715
|
+
msg,
|
|
716
|
+
data: errorData,
|
|
717
|
+
...ids,
|
|
718
|
+
timestamp: Date.now()
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// src/engine/reactor.ts
|
|
726
|
+
var OrchestrationReactor = class {
|
|
727
|
+
/** The execution log (single source of truth). */
|
|
728
|
+
log;
|
|
729
|
+
/** The IR graph (structural index only). */
|
|
730
|
+
ir;
|
|
731
|
+
/** Internal task queue. */
|
|
732
|
+
queue = [];
|
|
733
|
+
/** Bundled handler modules keyed by moduleRef. */
|
|
734
|
+
bundles;
|
|
735
|
+
/** Composite persistence adapter (state, events, idempotency, threads). */
|
|
736
|
+
persistence;
|
|
737
|
+
/** Scheduler adapter (alarms). */
|
|
738
|
+
scheduler;
|
|
739
|
+
/** External providers (ai, auth, memory, vault). */
|
|
740
|
+
providers;
|
|
741
|
+
/** Result of the last completed handler. */
|
|
742
|
+
lastResult = void 0;
|
|
743
|
+
/** Opaque thread identifier (set by adapter via RuntimeEvent.threadId). */
|
|
744
|
+
threadId = "";
|
|
745
|
+
/**
|
|
746
|
+
* Creates a new reactor instance.
|
|
747
|
+
*
|
|
748
|
+
* @param ir - The compiled IR graph.
|
|
749
|
+
* @param bundles - Map from moduleRef to bundled handler module.
|
|
750
|
+
* @param persistence - Persistence adapter for state and event log.
|
|
751
|
+
* @param scheduler - Scheduler adapter for deferred wake-ups.
|
|
752
|
+
* @param providers - External providers for ai, auth, memory, vault.
|
|
753
|
+
*/
|
|
754
|
+
constructor(ir, bundles, persistence, scheduler, providers) {
|
|
755
|
+
this.ir = ir;
|
|
756
|
+
this.bundles = bundles;
|
|
757
|
+
this.persistence = persistence;
|
|
758
|
+
this.scheduler = scheduler;
|
|
759
|
+
this.providers = providers;
|
|
760
|
+
this.log = new ExecutionLog(persistence.events);
|
|
761
|
+
}
|
|
762
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
763
|
+
// Public API (called by host adapter)
|
|
764
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
765
|
+
/**
|
|
766
|
+
* Handles an external event by resolving the IR entry and processing
|
|
767
|
+
* the handler chain until the queue is empty.
|
|
768
|
+
*
|
|
769
|
+
* @param event - The external runtime event.
|
|
770
|
+
* @returns The result of the last completed handler.
|
|
771
|
+
* @throws If no entry exists for the event type.
|
|
772
|
+
*/
|
|
773
|
+
async handleEvent(event) {
|
|
774
|
+
const entryId = this.ir.entries[event.type];
|
|
775
|
+
if (!entryId) {
|
|
776
|
+
throw new Error(`No entry for event: ${event.type}`);
|
|
777
|
+
}
|
|
778
|
+
if (event.threadId) {
|
|
779
|
+
this.threadId = event.threadId;
|
|
780
|
+
}
|
|
781
|
+
const traceId = crypto.randomUUID();
|
|
782
|
+
await this.log.emit({
|
|
783
|
+
type: "node.started",
|
|
784
|
+
nodeId: entryId,
|
|
785
|
+
executionId: "",
|
|
786
|
+
traceId,
|
|
787
|
+
threadId: this.threadId,
|
|
788
|
+
timestamp: Date.now()
|
|
789
|
+
});
|
|
790
|
+
this.queue.push({ nodeId: entryId, context: event.payload });
|
|
791
|
+
await this.processUntilIdle();
|
|
792
|
+
return this.lastResult;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Drains the task queue. Called internally after handleEvent and recursively
|
|
796
|
+
* after actions.run dispatches. When the queue is empty, control returns to
|
|
797
|
+
* the host adapter.
|
|
798
|
+
*/
|
|
799
|
+
async processUntilIdle() {
|
|
800
|
+
while (this.queue.length > 0) {
|
|
801
|
+
const task = this.queue.shift();
|
|
802
|
+
await this.processTask(task);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
806
|
+
// Internal task processing
|
|
807
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
808
|
+
/**
|
|
809
|
+
* Processes a single task from the queue.
|
|
810
|
+
*
|
|
811
|
+
* - **Entry nodes**: traverse sequential edges to find the handler.
|
|
812
|
+
* - **Handler nodes**: build context, execute handler, emit events.
|
|
813
|
+
*/
|
|
814
|
+
async processTask(task) {
|
|
815
|
+
const node = this.ir.nodes[task.nodeId];
|
|
816
|
+
if (!node) {
|
|
817
|
+
await this.log.emit({
|
|
818
|
+
type: "error",
|
|
819
|
+
nodeId: task.nodeId,
|
|
820
|
+
error: `Node "${task.nodeId}" not found in IR.`,
|
|
821
|
+
executionId: "",
|
|
822
|
+
traceId: "",
|
|
823
|
+
threadId: this.threadId,
|
|
824
|
+
timestamp: Date.now()
|
|
825
|
+
});
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
switch (node.kind) {
|
|
829
|
+
case "entry": {
|
|
830
|
+
this.enqueueSuccessors(task.nodeId, task.context);
|
|
831
|
+
break;
|
|
832
|
+
}
|
|
833
|
+
case "handler": {
|
|
834
|
+
const executionId = crypto.randomUUID();
|
|
835
|
+
await this.log.emit({
|
|
836
|
+
type: "node.started",
|
|
837
|
+
nodeId: node.id,
|
|
838
|
+
executionId,
|
|
839
|
+
traceId: "",
|
|
840
|
+
threadId: this.threadId,
|
|
841
|
+
timestamp: Date.now()
|
|
842
|
+
});
|
|
843
|
+
const handlerModule = this.bundles.get(node.moduleRef);
|
|
844
|
+
if (!handlerModule) {
|
|
845
|
+
await this.log.emit({
|
|
846
|
+
type: "error",
|
|
847
|
+
nodeId: node.id,
|
|
848
|
+
error: `Handler module "${node.moduleRef}" not found in bundles.`,
|
|
849
|
+
executionId,
|
|
850
|
+
traceId: "",
|
|
851
|
+
threadId: this.threadId,
|
|
852
|
+
timestamp: Date.now()
|
|
853
|
+
});
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const dispatch = this.createDispatch();
|
|
857
|
+
const ctx = buildHandlerContext(
|
|
858
|
+
this.log,
|
|
859
|
+
this.persistence.state,
|
|
860
|
+
this.scheduler,
|
|
861
|
+
dispatch,
|
|
862
|
+
this.providers,
|
|
863
|
+
{
|
|
864
|
+
executionId,
|
|
865
|
+
traceId: "",
|
|
866
|
+
threadId: this.threadId,
|
|
867
|
+
untrackedIOCount: 0,
|
|
868
|
+
untrackedIOByType: { network: 0, timer: 0, fs: 0, unknown: 0 },
|
|
869
|
+
hasUntrustedPlugins: false
|
|
870
|
+
}
|
|
871
|
+
);
|
|
872
|
+
try {
|
|
873
|
+
const result = await handlerModule.default(ctx, task.context);
|
|
874
|
+
await this.log.emit({
|
|
875
|
+
type: "node.completed",
|
|
876
|
+
nodeId: node.id,
|
|
877
|
+
result,
|
|
878
|
+
executionId,
|
|
879
|
+
traceId: "",
|
|
880
|
+
threadId: this.threadId,
|
|
881
|
+
timestamp: Date.now()
|
|
882
|
+
});
|
|
883
|
+
this.lastResult = result;
|
|
884
|
+
if (task.resolve) {
|
|
885
|
+
task.resolve(result);
|
|
886
|
+
}
|
|
887
|
+
this.enqueueSuccessors(task.nodeId, {
|
|
888
|
+
...task.context,
|
|
889
|
+
result
|
|
890
|
+
});
|
|
891
|
+
} catch (err) {
|
|
892
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
893
|
+
await this.log.emit({
|
|
894
|
+
type: "error",
|
|
895
|
+
nodeId: node.id,
|
|
896
|
+
error: message,
|
|
897
|
+
executionId,
|
|
898
|
+
traceId: "",
|
|
899
|
+
threadId: this.threadId,
|
|
900
|
+
timestamp: Date.now()
|
|
901
|
+
});
|
|
902
|
+
throw err;
|
|
903
|
+
}
|
|
904
|
+
break;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Enqueues all successor nodes connected by outgoing edges from the given node.
|
|
910
|
+
*
|
|
911
|
+
* @param nodeId - The source node ID.
|
|
912
|
+
* @param context - The context to pass to successor tasks.
|
|
913
|
+
*/
|
|
914
|
+
enqueueSuccessors(nodeId, context) {
|
|
915
|
+
const outEdges = this.ir.edges.filter((e) => e.from === nodeId);
|
|
916
|
+
for (const edge of outEdges) {
|
|
917
|
+
this.queue.push({ nodeId: edge.to, context });
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Creates a dispatch function for actions.run.
|
|
922
|
+
*
|
|
923
|
+
* When a handler calls `actions.run(step, input)`, this function:
|
|
924
|
+
* 1. Looks up the handler node by moduleRef in the IR.
|
|
925
|
+
* 2. Creates a task with a resolve callback.
|
|
926
|
+
* 3. Returns a Promise that resolves when the task completes.
|
|
927
|
+
*/
|
|
928
|
+
createDispatch() {
|
|
929
|
+
return (moduleRef, input) => {
|
|
930
|
+
const handlerNodeId = this.ir.handlerIndex[moduleRef];
|
|
931
|
+
if (!handlerNodeId) {
|
|
932
|
+
return Promise.reject(
|
|
933
|
+
new Error(
|
|
934
|
+
`Cannot dispatch: handler "${moduleRef}" not found in IR. Ensure it is statically imported at the top level.`
|
|
935
|
+
)
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
return new Promise((resolve) => {
|
|
939
|
+
this.queue.push({
|
|
940
|
+
nodeId: handlerNodeId,
|
|
941
|
+
context: input,
|
|
942
|
+
resolve
|
|
943
|
+
});
|
|
944
|
+
});
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
export {
|
|
950
|
+
ExecutionLog,
|
|
951
|
+
createAIPrimitive,
|
|
952
|
+
createStoragePrimitive,
|
|
953
|
+
createActionsPrimitive,
|
|
954
|
+
buildHandlerContext,
|
|
955
|
+
OrchestrationReactor
|
|
956
|
+
};
|
|
957
|
+
//# sourceMappingURL=chunk-G4LOF3MT.js.map
|