@ekairos/events 1.22.35-beta.development.0 → 1.22.35
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 +5 -3
- package/dist/codex.d.ts +11 -2
- package/dist/codex.js +16 -8
- package/dist/context.action-calls.d.ts +48 -0
- package/dist/context.action-calls.js +123 -0
- package/dist/context.action.d.ts +55 -0
- package/dist/context.action.js +25 -0
- package/dist/context.builder.d.ts +71 -43
- package/dist/context.builder.js +123 -28
- package/dist/context.config.d.ts +2 -1
- package/dist/context.config.js +8 -3
- package/dist/context.contract.d.ts +2 -4
- package/dist/context.contract.js +3 -9
- package/dist/context.d.ts +3 -2
- package/dist/context.engine.d.ts +60 -52
- package/dist/context.engine.js +506 -297
- package/dist/context.events.js +28 -87
- package/dist/context.js +1 -0
- package/dist/context.part-identity.d.ts +40 -0
- package/dist/context.part-identity.js +270 -0
- package/dist/context.parts.d.ts +389 -164
- package/dist/context.parts.js +343 -218
- package/dist/context.registry.d.ts +1 -1
- package/dist/context.runtime.d.ts +14 -4
- package/dist/context.runtime.js +21 -3
- package/dist/context.step-stream.d.ts +16 -2
- package/dist/context.step-stream.js +58 -16
- package/dist/context.store.d.ts +55 -10
- package/dist/context.stream.d.ts +14 -4
- package/dist/context.stream.js +31 -3
- package/dist/domain.d.ts +1 -0
- package/dist/domain.js +1 -0
- package/dist/index.d.ts +13 -10
- package/dist/index.js +7 -6
- package/dist/react.context-event-parts.d.ts +18 -0
- package/dist/react.context-event-parts.js +509 -0
- package/dist/react.d.ts +7 -42
- package/dist/react.js +4 -87
- package/dist/react.step-stream.d.ts +39 -0
- package/dist/react.step-stream.js +625 -0
- package/dist/react.types.d.ts +121 -0
- package/dist/react.types.js +2 -0
- package/dist/react.use-context.d.ts +7 -0
- package/dist/react.use-context.js +867 -0
- package/dist/reactors/ai-sdk.chunk-map.d.ts +1 -0
- package/dist/reactors/ai-sdk.chunk-map.js +56 -5
- package/dist/reactors/ai-sdk.reactor.d.ts +8 -9
- package/dist/reactors/ai-sdk.reactor.js +6 -9
- package/dist/reactors/ai-sdk.step.d.ts +4 -5
- package/dist/reactors/ai-sdk.step.js +24 -17
- package/dist/reactors/scripted.reactor.d.ts +7 -4
- package/dist/reactors/types.d.ts +19 -10
- package/dist/runtime.d.ts +6 -0
- package/dist/runtime.js +9 -0
- package/dist/runtime.step.js +1 -1
- package/dist/schema.d.ts +268 -2
- package/dist/schema.js +4 -9
- package/dist/steps/do-context-stream-step.js +4 -4
- package/dist/steps/durable.steps.d.ts +28 -0
- package/dist/steps/durable.steps.js +34 -0
- package/dist/steps/store.steps.d.ts +64 -22
- package/dist/steps/store.steps.js +192 -35
- package/dist/steps/stream.steps.d.ts +32 -0
- package/dist/steps/stream.steps.js +124 -6
- package/dist/steps/trace.steps.d.ts +4 -4
- package/dist/steps/trace.steps.js +21 -6
- package/dist/stores/instant.store.d.ts +11 -11
- package/dist/stores/instant.store.js +136 -6
- package/dist/tools-to-model-tools.d.ts +4 -2
- package/dist/tools-to-model-tools.js +30 -11
- package/package.json +18 -7
- package/dist/context.toolcalls.d.ts +0 -60
- package/dist/context.toolcalls.js +0 -117
package/dist/context.engine.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { getContextRuntimeServices } from "./context.runtime.js";
|
|
2
2
|
import { OUTPUT_ITEM_TYPE, WEB_CHANNEL } from "./context.events.js";
|
|
3
|
-
import {
|
|
3
|
+
import { applyActionExecutionResultToParts } from "./context.action-calls.js";
|
|
4
4
|
import { isContextPartEnvelope, normalizePartsForPersistence, } from "./context.parts.js";
|
|
5
|
-
import { actionsToActionSpecs } from "./tools-to-model-tools.js";
|
|
6
5
|
import { createAiSdkReactor, } from "./context.reactor.js";
|
|
7
|
-
import { abortPersistedContextStepStream,
|
|
8
|
-
import { completeExecution, createContextStep, initializeContext,
|
|
6
|
+
import { abortPersistedContextStepStream, closeContextStream, createPersistedContextStepStreamForRuntime, finalizePersistedContextStepStreamForRuntime, writeActionResultPartChunksToSession, } from "./steps/stream.steps.js";
|
|
7
|
+
import { completeExecution, completeExecutionStep, createContextStep, getContextItems, initializeContext, openExecutionStep, openExecution, saveExecutionStepOutput, updateContextContent, updateContextDefinition, updateContextReactor, updateContextStatus, updateItem, updateContextStep, updateExecutionWorkflowRun, upsertContextResources, } from "./steps/store.steps.js";
|
|
8
|
+
import { readContextDurableWorkflowReturnValue, readContextDurableWorkflowStatus, resumeContextReturnValueHook, startContextDurableWorkflow, } from "./steps/durable.steps.js";
|
|
9
9
|
import { getClientResumeHookUrl, toolApprovalHookToken, toolApprovalWebhookToken, } from "./context.hooks.js";
|
|
10
10
|
import { getContextDurableWorkflow } from "./context.durable.js";
|
|
11
11
|
export async function runContextReactionDirect(context, triggerEvent, params) {
|
|
12
12
|
return await ContextEngine.runDirect(context, triggerEvent, params);
|
|
13
13
|
}
|
|
14
|
+
async function resolveReactRuntime(params) {
|
|
15
|
+
if (params.runtime)
|
|
16
|
+
return params.runtime;
|
|
17
|
+
throw new Error("ContextEngine.react requires runtime.");
|
|
18
|
+
}
|
|
14
19
|
export { toolApprovalHookToken, toolApprovalWebhookToken, getClientResumeHookUrl };
|
|
15
20
|
function nowIso() {
|
|
16
21
|
return new Date().toISOString();
|
|
@@ -24,13 +29,21 @@ function summarizePartPreview(part) {
|
|
|
24
29
|
if (!part || typeof part !== "object")
|
|
25
30
|
return {};
|
|
26
31
|
if (isContextPartEnvelope(part)) {
|
|
27
|
-
const preview = part.
|
|
28
|
-
? part.content[0]
|
|
29
|
-
:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const preview = part.type === "message"
|
|
33
|
+
? part.content.text ?? JSON.stringify(part.content.blocks?.[0] ?? part)
|
|
34
|
+
: part.type === "reasoning"
|
|
35
|
+
? part.content.text
|
|
36
|
+
: part.type === "source"
|
|
37
|
+
? JSON.stringify(part.content.sources[0] ?? part)
|
|
38
|
+
: part.content.status === "failed"
|
|
39
|
+
? part.content.error.message
|
|
40
|
+
: JSON.stringify(part.content);
|
|
41
|
+
const state = part.type === "reasoning"
|
|
42
|
+
? part.content.state
|
|
43
|
+
: part.type === "action"
|
|
44
|
+
? part.content.status
|
|
45
|
+
: undefined;
|
|
46
|
+
const toolCallId = part.type === "action" ? part.content.actionCallId : undefined;
|
|
34
47
|
return {
|
|
35
48
|
partPreview: preview ? clipPreview(preview) : undefined,
|
|
36
49
|
partState: state,
|
|
@@ -89,6 +102,37 @@ async function readActiveWorkflowRunId() {
|
|
|
89
102
|
return null;
|
|
90
103
|
}
|
|
91
104
|
}
|
|
105
|
+
function serializeContextReturnValueError(error) {
|
|
106
|
+
if (error instanceof Error) {
|
|
107
|
+
return {
|
|
108
|
+
name: error.name,
|
|
109
|
+
message: error.message,
|
|
110
|
+
stack: error.stack,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
message: String(error),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function unwrapContextReturnValueHookPayload(payload) {
|
|
118
|
+
if (payload.ok)
|
|
119
|
+
return payload.result;
|
|
120
|
+
const error = new Error(payload.error.message);
|
|
121
|
+
if (payload.error.name) {
|
|
122
|
+
error.name = payload.error.name;
|
|
123
|
+
}
|
|
124
|
+
if (payload.error.stack) {
|
|
125
|
+
error.stack = payload.error.stack;
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
function isEmptyContextContent(content) {
|
|
130
|
+
if (content == null)
|
|
131
|
+
return true;
|
|
132
|
+
if (typeof content !== "object")
|
|
133
|
+
return false;
|
|
134
|
+
return Object.keys(content).length === 0;
|
|
135
|
+
}
|
|
92
136
|
async function createRuntimeOps(runtimeHandle, benchmark) {
|
|
93
137
|
const runtime = await getContextRuntimeServices(runtimeHandle);
|
|
94
138
|
const { db } = runtime;
|
|
@@ -144,6 +188,8 @@ async function createRuntimeOps(runtimeHandle, benchmark) {
|
|
|
144
188
|
return { context, isNew: true };
|
|
145
189
|
},
|
|
146
190
|
updateContextContent: async (contextIdentifier, content) => await store.updateContextContent(contextIdentifier, content),
|
|
191
|
+
updateContextDefinition: async (contextIdentifier, definition) => await store.updateContextDefinition(contextIdentifier, definition),
|
|
192
|
+
upsertContextResources: async (contextIdentifier, resources) => await store.upsertContextResources(contextIdentifier, resources),
|
|
147
193
|
updateContextReactor: async (contextIdentifier, reactor) => await store.updateContextReactor(contextIdentifier, reactor),
|
|
148
194
|
updateContextStatus: async (contextIdentifier, status) => await instrumentedDb.transact([
|
|
149
195
|
instrumentedDb.tx.event_contexts[requireContextId(contextIdentifier)].update({
|
|
@@ -151,7 +197,7 @@ async function createRuntimeOps(runtimeHandle, benchmark) {
|
|
|
151
197
|
updatedAt: new Date(),
|
|
152
198
|
}),
|
|
153
199
|
]),
|
|
154
|
-
|
|
200
|
+
openExecution: async ({ contextIdentifier, triggerEvent }) => {
|
|
155
201
|
const contextId = requireContextId(contextIdentifier);
|
|
156
202
|
const triggerId = String(triggerEvent.id);
|
|
157
203
|
const reactionId = makeRuntimeId();
|
|
@@ -209,6 +255,31 @@ async function createRuntimeOps(runtimeHandle, benchmark) {
|
|
|
209
255
|
},
|
|
210
256
|
};
|
|
211
257
|
},
|
|
258
|
+
openExecutionStep: async ({ contextIdentifier, content, executionId, iteration }) => {
|
|
259
|
+
const stepId = makeRuntimeId();
|
|
260
|
+
const now = new Date();
|
|
261
|
+
await instrumentedDb.transact([
|
|
262
|
+
instrumentedDb.tx.event_steps[stepId].create({
|
|
263
|
+
createdAt: now,
|
|
264
|
+
updatedAt: now,
|
|
265
|
+
status: "running",
|
|
266
|
+
iteration,
|
|
267
|
+
}),
|
|
268
|
+
instrumentedDb.tx.event_steps[stepId].link({ execution: executionId }),
|
|
269
|
+
]);
|
|
270
|
+
const stream = await createPersistedContextStepStreamForRuntime({ db: instrumentedDb }, {
|
|
271
|
+
executionId,
|
|
272
|
+
stepId,
|
|
273
|
+
});
|
|
274
|
+
const context = await store.updateContextContent(contextIdentifier, content);
|
|
275
|
+
const events = await store.getItems(contextIdentifier);
|
|
276
|
+
return {
|
|
277
|
+
stepId,
|
|
278
|
+
stream,
|
|
279
|
+
context,
|
|
280
|
+
events,
|
|
281
|
+
};
|
|
282
|
+
},
|
|
212
283
|
createContextStep: async ({ executionId, iteration }) => {
|
|
213
284
|
const stepId = makeRuntimeId();
|
|
214
285
|
await instrumentedDb.transact([
|
|
@@ -223,16 +294,67 @@ async function createRuntimeOps(runtimeHandle, benchmark) {
|
|
|
223
294
|
return { stepId };
|
|
224
295
|
},
|
|
225
296
|
updateContextStep: async (params) => {
|
|
297
|
+
const update = { updatedAt: new Date() };
|
|
298
|
+
if (params.patch.status !== undefined)
|
|
299
|
+
update.status = params.patch.status;
|
|
300
|
+
if (params.patch.errorText !== undefined)
|
|
301
|
+
update.errorText = params.patch.errorText;
|
|
226
302
|
await instrumentedDb.transact([
|
|
227
|
-
instrumentedDb.tx.event_steps[params.stepId].update(
|
|
228
|
-
...params.patch,
|
|
229
|
-
updatedAt: new Date(),
|
|
230
|
-
}),
|
|
303
|
+
instrumentedDb.tx.event_steps[params.stepId].update(update),
|
|
231
304
|
]);
|
|
232
305
|
},
|
|
233
|
-
|
|
306
|
+
completeExecutionStep: async (params) => {
|
|
307
|
+
const actionResultChunkEvents = await writeActionResultPartChunksToSession({
|
|
308
|
+
session: params.session,
|
|
309
|
+
contextId: String(params.contextId ?? ""),
|
|
310
|
+
executionId: String(params.executionId ?? ""),
|
|
311
|
+
itemId: String(params.reactionEventId ?? ""),
|
|
312
|
+
actionResults: params.actionResults ?? [],
|
|
313
|
+
});
|
|
314
|
+
if (params.parts) {
|
|
315
|
+
await store.saveStepParts({ stepId: params.stepId, parts: params.parts });
|
|
316
|
+
}
|
|
317
|
+
if (params.session) {
|
|
318
|
+
await finalizePersistedContextStepStreamForRuntime({
|
|
319
|
+
runtime: { db: instrumentedDb },
|
|
320
|
+
session: params.session,
|
|
321
|
+
mode: "close",
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
const update = { updatedAt: new Date() };
|
|
325
|
+
update.status = params.stepStatus ?? "completed";
|
|
326
|
+
if (params.errorText !== undefined)
|
|
327
|
+
update.errorText = params.errorText;
|
|
328
|
+
await instrumentedDb.transact([
|
|
329
|
+
instrumentedDb.tx.event_steps[params.stepId].update(update),
|
|
330
|
+
]);
|
|
331
|
+
if (!params.reactionEventId || !params.reactionEvent) {
|
|
332
|
+
return { actionResultChunkEvents };
|
|
333
|
+
}
|
|
334
|
+
await instrumentedDb.transact([
|
|
335
|
+
instrumentedDb.tx.event_items[params.reactionEventId].update(params.reactionEvent),
|
|
336
|
+
]);
|
|
337
|
+
return {
|
|
338
|
+
reactionEvent: {
|
|
339
|
+
...params.reactionEvent,
|
|
340
|
+
id: params.reactionEventId,
|
|
341
|
+
},
|
|
342
|
+
actionResultChunkEvents,
|
|
343
|
+
};
|
|
344
|
+
},
|
|
345
|
+
saveExecutionStepOutput: async (params) => {
|
|
234
346
|
await store.saveStepParts({ stepId: params.stepId, parts: params.parts });
|
|
347
|
+
await instrumentedDb.transact([
|
|
348
|
+
instrumentedDb.tx.event_items[params.reactionEventId].update(params.reactionEvent),
|
|
349
|
+
]);
|
|
350
|
+
return {
|
|
351
|
+
reactionEvent: {
|
|
352
|
+
...params.reactionEvent,
|
|
353
|
+
id: params.reactionEventId,
|
|
354
|
+
},
|
|
355
|
+
};
|
|
235
356
|
},
|
|
357
|
+
getItems: async (contextIdentifier) => await store.getItems(contextIdentifier),
|
|
236
358
|
updateItem: async (itemId, item) => {
|
|
237
359
|
await instrumentedDb.transact([instrumentedDb.tx.event_items[itemId].update(item)]);
|
|
238
360
|
return {
|
|
@@ -240,9 +362,9 @@ async function createRuntimeOps(runtimeHandle, benchmark) {
|
|
|
240
362
|
id: itemId,
|
|
241
363
|
};
|
|
242
364
|
},
|
|
243
|
-
completeExecution: async (contextIdentifier, executionId, status) => {
|
|
365
|
+
completeExecution: async (contextIdentifier, executionId, status, opts) => {
|
|
244
366
|
const contextId = requireContextId(contextIdentifier);
|
|
245
|
-
|
|
367
|
+
const txs = [
|
|
246
368
|
instrumentedDb.tx.event_executions[executionId].update({
|
|
247
369
|
status,
|
|
248
370
|
updatedAt: new Date(),
|
|
@@ -251,23 +373,40 @@ async function createRuntimeOps(runtimeHandle, benchmark) {
|
|
|
251
373
|
status: "closed",
|
|
252
374
|
updatedAt: new Date(),
|
|
253
375
|
}),
|
|
254
|
-
]
|
|
376
|
+
];
|
|
377
|
+
if (opts?.reactionEventId && opts.reactionEvent) {
|
|
378
|
+
txs.push(instrumentedDb.tx.event_items[opts.reactionEventId].update(opts.reactionEvent));
|
|
379
|
+
}
|
|
380
|
+
await instrumentedDb.transact(txs);
|
|
381
|
+
return opts?.reactionEventId && opts.reactionEvent
|
|
382
|
+
? {
|
|
383
|
+
reactionEvent: {
|
|
384
|
+
...opts.reactionEvent,
|
|
385
|
+
id: opts.reactionEventId,
|
|
386
|
+
},
|
|
387
|
+
}
|
|
388
|
+
: {};
|
|
255
389
|
},
|
|
256
390
|
};
|
|
257
391
|
}
|
|
258
392
|
async function createWorkflowOps(runtime) {
|
|
259
393
|
const env = runtime.env;
|
|
260
394
|
return {
|
|
261
|
-
initializeContext: async (contextIdentifier
|
|
395
|
+
initializeContext: async (contextIdentifier) => await initializeContext({ runtime, contextIdentifier }),
|
|
262
396
|
updateContextContent: async (contextIdentifier, content) => await updateContextContent({ runtime, contextIdentifier, content }),
|
|
397
|
+
updateContextDefinition: async (contextIdentifier, definition) => await updateContextDefinition({ runtime, contextIdentifier, definition }),
|
|
398
|
+
upsertContextResources: async (contextIdentifier, resources) => await upsertContextResources({ runtime, contextIdentifier, resources }),
|
|
263
399
|
updateContextReactor: async (contextIdentifier, reactor) => await updateContextReactor({ runtime, contextIdentifier, reactor }),
|
|
264
400
|
updateContextStatus: async (contextIdentifier, status) => await updateContextStatus({ runtime, contextIdentifier, status }),
|
|
265
|
-
|
|
401
|
+
openExecution: async ({ contextIdentifier, triggerEvent }) => await openExecution({ runtime, contextIdentifier, triggerEvent }),
|
|
402
|
+
openExecutionStep: async (params) => await openExecutionStep({ runtime, ...params }),
|
|
266
403
|
createContextStep: async ({ executionId, iteration }) => await createContextStep({ runtime, executionId, iteration }),
|
|
267
404
|
updateContextStep: async (params) => await updateContextStep({ runtime, ...params }),
|
|
268
|
-
|
|
405
|
+
completeExecutionStep: async (params) => await completeExecutionStep({ runtime, ...params }),
|
|
406
|
+
saveExecutionStepOutput: async (params) => await saveExecutionStepOutput({ runtime, ...params }),
|
|
407
|
+
getItems: async (contextIdentifier) => await getContextItems({ runtime, contextIdentifier }),
|
|
269
408
|
updateItem: async (itemId, item, opts) => await updateItem({ runtime, eventId: itemId, event: item, opts }),
|
|
270
|
-
completeExecution: async (contextIdentifier, executionId, status) => await completeExecution({ runtime, contextIdentifier, executionId, status }),
|
|
409
|
+
completeExecution: async (contextIdentifier, executionId, status, opts) => await completeExecution({ runtime, contextIdentifier, executionId, status, ...opts }),
|
|
271
410
|
};
|
|
272
411
|
}
|
|
273
412
|
async function getContextEngineOps(runtime, benchmark) {
|
|
@@ -281,9 +420,20 @@ async function getContextEngineOps(runtime, benchmark) {
|
|
|
281
420
|
export class ContextEngine {
|
|
282
421
|
constructor(opts = {}, reactor) {
|
|
283
422
|
this.opts = opts;
|
|
284
|
-
this.reactor =
|
|
423
|
+
this.reactor =
|
|
424
|
+
reactor ??
|
|
425
|
+
createAiSdkReactor();
|
|
426
|
+
}
|
|
427
|
+
async describeContext(_content, _context, _env, _runtime) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
async defineGoal(_content, _context, _env, _runtime) {
|
|
431
|
+
return null;
|
|
285
432
|
}
|
|
286
|
-
async
|
|
433
|
+
async defineResources(_content, _context, _env, _runtime) {
|
|
434
|
+
return [];
|
|
435
|
+
}
|
|
436
|
+
async buildSkills(_context, _env, _runtime) {
|
|
287
437
|
return [];
|
|
288
438
|
}
|
|
289
439
|
/**
|
|
@@ -303,13 +453,13 @@ export class ContextEngine {
|
|
|
303
453
|
* the builder) so results are durable and replay-safe.
|
|
304
454
|
* - If it’s pure/deterministic, it can run in workflow context.
|
|
305
455
|
*/
|
|
306
|
-
async expandEvents(events, _context, _env) {
|
|
456
|
+
async expandEvents(events, _context, _env, _runtime) {
|
|
307
457
|
return events;
|
|
308
458
|
}
|
|
309
|
-
getModel(_context, _env) {
|
|
459
|
+
getModel(_context, _env, _runtime) {
|
|
310
460
|
return "openai/gpt-5";
|
|
311
461
|
}
|
|
312
|
-
getReactor(_context, _env) {
|
|
462
|
+
getReactor(_context, _env, _runtime) {
|
|
313
463
|
return this.reactor;
|
|
314
464
|
}
|
|
315
465
|
/**
|
|
@@ -324,31 +474,36 @@ export class ContextEngine {
|
|
|
324
474
|
return true;
|
|
325
475
|
}
|
|
326
476
|
async react(triggerEvent, params) {
|
|
327
|
-
if (params.durable) {
|
|
328
|
-
return await ContextEngine.
|
|
477
|
+
if (params.durable === false) {
|
|
478
|
+
return await ContextEngine.runDirect(this, triggerEvent, params);
|
|
329
479
|
}
|
|
330
|
-
return await ContextEngine.
|
|
480
|
+
return await ContextEngine.startDurable(this, triggerEvent, params);
|
|
331
481
|
}
|
|
332
482
|
static async prepareExecutionShell(story, triggerEvent, params) {
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
const
|
|
336
|
-
const ctxResult = await measureBenchmark(params.__benchmark, "react.initializeContextMs", async () => await ops.initializeContext(params.context ?? null
|
|
483
|
+
const runtimeHandle = await resolveReactRuntime(params);
|
|
484
|
+
const env = runtimeHandle.env;
|
|
485
|
+
const ops = await measureBenchmark(params.__benchmark, "react.resolveOpsMs", async () => await getContextEngineOps(runtimeHandle, params.__benchmark));
|
|
486
|
+
const ctxResult = await measureBenchmark(params.__benchmark, "react.initializeContextMs", async () => await ops.initializeContext(params.context ?? null));
|
|
337
487
|
let currentContext = ctxResult.context;
|
|
338
488
|
const contextSelector = { id: String(currentContext.id) };
|
|
339
489
|
if (ctxResult.isNew) {
|
|
340
|
-
await story.opts.onContextCreated?.({
|
|
490
|
+
await story.opts.onContextCreated?.({
|
|
491
|
+
env,
|
|
492
|
+
runtime: runtimeHandle,
|
|
493
|
+
context: currentContext,
|
|
494
|
+
});
|
|
341
495
|
}
|
|
342
496
|
if (currentContext.status === "closed") {
|
|
343
497
|
await measureBenchmark(params.__benchmark, "react.reopenClosedContextMs", async () => await ops.updateContextStatus(contextSelector, "open_idle"));
|
|
344
498
|
currentContext = { ...currentContext, status: "open_idle" };
|
|
345
499
|
}
|
|
346
|
-
const shell = await measureBenchmark(params.__benchmark, "react.
|
|
500
|
+
const shell = await measureBenchmark(params.__benchmark, "react.openExecutionMs", async () => await ops.openExecution({
|
|
347
501
|
contextIdentifier: contextSelector,
|
|
348
502
|
triggerEvent,
|
|
349
503
|
}));
|
|
350
504
|
currentContext = { ...currentContext, status: "open_streaming" };
|
|
351
505
|
return {
|
|
506
|
+
runtimeHandle,
|
|
352
507
|
contextSelector,
|
|
353
508
|
currentContext,
|
|
354
509
|
trigger: shell.triggerEvent,
|
|
@@ -357,7 +512,8 @@ export class ContextEngine {
|
|
|
357
512
|
};
|
|
358
513
|
}
|
|
359
514
|
static async startDurable(story, triggerEvent, params) {
|
|
360
|
-
const
|
|
515
|
+
const runtimeHandle = await resolveReactRuntime(params);
|
|
516
|
+
const env = runtimeHandle.env;
|
|
361
517
|
if (params.options?.writable) {
|
|
362
518
|
throw new Error("ContextEngine.react: durable runs manage their own workflow stream");
|
|
363
519
|
}
|
|
@@ -367,50 +523,89 @@ export class ContextEngine {
|
|
|
367
523
|
}
|
|
368
524
|
const workflow = getContextDurableWorkflow();
|
|
369
525
|
if (typeof workflow !== "function") {
|
|
370
|
-
|
|
526
|
+
const contextKeyLabel = contextKey || "(missing)";
|
|
527
|
+
throw new Error([
|
|
528
|
+
"ContextEngine.react(..., { durable: true }) needs a registered durable context workflow.",
|
|
529
|
+
"Call configureContextDurableWorkflow(contextDurableWorkflow) during server/workflow bootstrap.",
|
|
530
|
+
"If you want inline execution inside the current workflow step, pass durable: false.",
|
|
531
|
+
`Context key: ${contextKeyLabel}.`,
|
|
532
|
+
].join(" "));
|
|
371
533
|
}
|
|
372
534
|
const shell = await ContextEngine.prepareExecutionShell(story, triggerEvent, params);
|
|
535
|
+
if (params.__initialContent !== undefined &&
|
|
536
|
+
isEmptyContextContent(shell.currentContext.content)) {
|
|
537
|
+
const ops = await getContextEngineOps(runtimeHandle, params.__benchmark);
|
|
538
|
+
const initialContent = params.__initialContent;
|
|
539
|
+
shell.currentContext = await ops.updateContextContent(shell.contextSelector, initialContent);
|
|
540
|
+
}
|
|
373
541
|
let run;
|
|
374
542
|
try {
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
{
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
543
|
+
const parentWorkflowRunId = await readActiveWorkflowRunId();
|
|
544
|
+
let returnValueHook = null;
|
|
545
|
+
let returnValueHookPromise = null;
|
|
546
|
+
if (parentWorkflowRunId) {
|
|
547
|
+
try {
|
|
548
|
+
const { createHook } = await import("workflow");
|
|
549
|
+
const hook = createHook({
|
|
550
|
+
token: `context:return:${shell.execution.id}`,
|
|
551
|
+
metadata: {
|
|
552
|
+
kind: "context.returnValue",
|
|
553
|
+
contextId: shell.currentContext.id,
|
|
554
|
+
executionId: shell.execution.id,
|
|
555
|
+
parentWorkflowRunId,
|
|
556
|
+
},
|
|
557
|
+
});
|
|
558
|
+
returnValueHook = {
|
|
559
|
+
token: hook.token,
|
|
560
|
+
parentWorkflowRunId,
|
|
561
|
+
};
|
|
562
|
+
returnValueHookPromise = Promise.resolve(hook);
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
566
|
+
if (!message.includes("can only be called inside a workflow function")) {
|
|
567
|
+
throw error;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const payload = {
|
|
572
|
+
contextKey,
|
|
573
|
+
runtime: runtimeHandle,
|
|
574
|
+
context: params.context ?? null,
|
|
575
|
+
triggerEvent,
|
|
576
|
+
options: {
|
|
577
|
+
maxIterations: params.options?.maxIterations,
|
|
578
|
+
maxModelSteps: params.options?.maxModelSteps,
|
|
579
|
+
preventClose: params.options?.preventClose,
|
|
580
|
+
sendFinish: params.options?.sendFinish,
|
|
397
581
|
},
|
|
398
|
-
|
|
582
|
+
bootstrap: {
|
|
583
|
+
contextId: shell.currentContext.id,
|
|
584
|
+
trigger: shell.trigger,
|
|
585
|
+
reaction: shell.reaction,
|
|
586
|
+
execution: shell.execution,
|
|
587
|
+
returnValueHookToken: returnValueHook?.token ?? null,
|
|
588
|
+
},
|
|
589
|
+
};
|
|
590
|
+
const startedRun = await measureBenchmark(params.__benchmark, "react.durable.startWorkflowMs", async () => await startContextDurableWorkflow({ payload }));
|
|
399
591
|
run = {
|
|
400
592
|
runId: String(startedRun.runId),
|
|
401
|
-
status: startedRun.
|
|
402
|
-
returnValue:
|
|
593
|
+
status: readContextDurableWorkflowStatus({ runId: String(startedRun.runId) }),
|
|
594
|
+
returnValue: returnValueHookPromise
|
|
595
|
+
? returnValueHookPromise.then(unwrapContextReturnValueHookPayload)
|
|
596
|
+
: readContextDurableWorkflowReturnValue({
|
|
597
|
+
runId: String(startedRun.runId),
|
|
598
|
+
}),
|
|
599
|
+
returnValueHook,
|
|
403
600
|
};
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}),
|
|
410
|
-
]);
|
|
601
|
+
await measureBenchmark(params.__benchmark, "react.durable.persistWorkflowRunIdMs", async () => await updateExecutionWorkflowRun({
|
|
602
|
+
runtime: runtimeHandle,
|
|
603
|
+
executionId: shell.execution.id,
|
|
604
|
+
workflowRunId: String(startedRun.runId),
|
|
605
|
+
}));
|
|
411
606
|
}
|
|
412
607
|
catch (error) {
|
|
413
|
-
const ops = await getContextEngineOps(
|
|
608
|
+
const ops = await getContextEngineOps(runtimeHandle, params.__benchmark);
|
|
414
609
|
await ops.completeExecution(shell.contextSelector, shell.execution.id, "failed").catch(() => null);
|
|
415
610
|
throw error;
|
|
416
611
|
}
|
|
@@ -423,19 +618,48 @@ export class ContextEngine {
|
|
|
423
618
|
};
|
|
424
619
|
}
|
|
425
620
|
static async runDirect(story, triggerEvent, params) {
|
|
426
|
-
|
|
427
|
-
|
|
621
|
+
if (!params.__bootstrap) {
|
|
622
|
+
const shell = await ContextEngine.prepareExecutionShell(story, triggerEvent, params);
|
|
623
|
+
const run = ContextEngine.runDirect(story, triggerEvent, {
|
|
624
|
+
...params,
|
|
625
|
+
runtime: shell.runtimeHandle,
|
|
626
|
+
__bootstrap: {
|
|
627
|
+
contextId: shell.currentContext.id,
|
|
628
|
+
trigger: shell.trigger,
|
|
629
|
+
reaction: shell.reaction,
|
|
630
|
+
execution: shell.execution,
|
|
631
|
+
},
|
|
632
|
+
});
|
|
633
|
+
return {
|
|
634
|
+
context: shell.currentContext,
|
|
635
|
+
trigger: shell.trigger,
|
|
636
|
+
reaction: shell.reaction,
|
|
637
|
+
execution: shell.execution,
|
|
638
|
+
run,
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
const runtimeHandle = await resolveReactRuntime(params);
|
|
642
|
+
const env = runtimeHandle.env;
|
|
643
|
+
const ops = await measureBenchmark(params.__benchmark, "react.resolveOpsMs", async () => await getContextEngineOps(runtimeHandle, params.__benchmark));
|
|
428
644
|
const maxIterations = params.options?.maxIterations ?? 20;
|
|
429
645
|
const maxModelSteps = params.options?.maxModelSteps ?? 1;
|
|
430
646
|
const preventClose = params.options?.preventClose ?? false;
|
|
431
647
|
const sendFinish = params.options?.sendFinish ?? true;
|
|
432
|
-
const silent = params.options?.silent ?? false;
|
|
433
648
|
const writable = params.options?.writable;
|
|
434
649
|
const bootstrapped = params.__bootstrap;
|
|
650
|
+
const returnValueHookToken = bootstrapped?.returnValueHookToken ?? null;
|
|
651
|
+
const resumeReturnValueHook = async (payload) => {
|
|
652
|
+
if (!returnValueHookToken)
|
|
653
|
+
return;
|
|
654
|
+
await resumeContextReturnValueHook({
|
|
655
|
+
token: returnValueHookToken,
|
|
656
|
+
payload,
|
|
657
|
+
});
|
|
658
|
+
};
|
|
435
659
|
const shell = bootstrapped
|
|
436
660
|
? {
|
|
437
661
|
contextSelector: { id: String(bootstrapped.contextId) },
|
|
438
|
-
currentContext: (await measureBenchmark(params.__benchmark, "react.bootstrapContextLookupMs", async () => await ops.initializeContext({ id: String(bootstrapped.contextId) }
|
|
662
|
+
currentContext: (await measureBenchmark(params.__benchmark, "react.bootstrapContextLookupMs", async () => await ops.initializeContext({ id: String(bootstrapped.contextId) }))).context,
|
|
439
663
|
trigger: bootstrapped.trigger,
|
|
440
664
|
reaction: bootstrapped.reaction,
|
|
441
665
|
execution: bootstrapped.execution,
|
|
@@ -458,7 +682,6 @@ export class ContextEngine {
|
|
|
458
682
|
execution = { ...execution, status: "failed" };
|
|
459
683
|
updatedContext = { ...updatedContext, status: "closed" };
|
|
460
684
|
await emitContextEvents({
|
|
461
|
-
silent,
|
|
462
685
|
writable,
|
|
463
686
|
events: [
|
|
464
687
|
{
|
|
@@ -481,9 +704,7 @@ export class ContextEngine {
|
|
|
481
704
|
// noop
|
|
482
705
|
}
|
|
483
706
|
try {
|
|
484
|
-
|
|
485
|
-
await closeContextStream({ preventClose, sendFinish, writable });
|
|
486
|
-
}
|
|
707
|
+
await closeContextStream({ preventClose, sendFinish, writable });
|
|
487
708
|
}
|
|
488
709
|
catch {
|
|
489
710
|
// noop
|
|
@@ -491,55 +712,102 @@ export class ContextEngine {
|
|
|
491
712
|
};
|
|
492
713
|
try {
|
|
493
714
|
for (let iter = 0; iter < maxIterations; iter++) {
|
|
494
|
-
// Create a persisted step per iteration (IDs generated in step runtime for replay safety)
|
|
495
715
|
const stagePrefix = `react.iteration.${iter}`;
|
|
496
|
-
|
|
716
|
+
runtimeHandle.__ekairosContextRun = {
|
|
717
|
+
contextId: currentContext.id,
|
|
497
718
|
executionId,
|
|
719
|
+
triggerEventId,
|
|
720
|
+
reactionEventId,
|
|
498
721
|
iteration: iter,
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
722
|
+
};
|
|
723
|
+
// Hook: Context DSL `context()` (implemented by subclasses via `initialize()`)
|
|
724
|
+
const nextContent = await measureBenchmark(params.__benchmark, `${stagePrefix}.contextMs`, async () => await story.initialize(updatedContext, env, runtimeHandle));
|
|
725
|
+
const openedStep = await measureBenchmark(params.__benchmark, `${stagePrefix}.openExecutionStepMs`, async () => await ops.openExecutionStep({
|
|
726
|
+
contextIdentifier: activeContextSelector,
|
|
727
|
+
content: nextContent,
|
|
503
728
|
executionId,
|
|
504
|
-
|
|
505
|
-
});
|
|
729
|
+
iteration: iter,
|
|
730
|
+
}));
|
|
731
|
+
currentStepId = openedStep.stepId;
|
|
732
|
+
currentStepStream = openedStep.stream;
|
|
733
|
+
updatedContext = openedStep.context;
|
|
734
|
+
const rawEvents = openedStep.events;
|
|
735
|
+
const previousResources = updatedContext.resources ?? [];
|
|
736
|
+
const resources = await measureBenchmark(params.__benchmark, `${stagePrefix}.resourcesMs`, async () => await story.defineResources(nextContent, updatedContext, env, runtimeHandle));
|
|
737
|
+
const shouldPersistResources = resources.length > 0 || previousResources.length > 0;
|
|
738
|
+
let contextResources = previousResources;
|
|
739
|
+
if (shouldPersistResources) {
|
|
740
|
+
contextResources = await measureBenchmark(params.__benchmark, `${stagePrefix}.contextResourcesMs`, async () => await ops.upsertContextResources(activeContextSelector, resources));
|
|
741
|
+
updatedContext = {
|
|
742
|
+
...updatedContext,
|
|
743
|
+
resources: contextResources,
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
updatedContext = {
|
|
748
|
+
...updatedContext,
|
|
749
|
+
resources: [],
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
const description = await measureBenchmark(params.__benchmark, `${stagePrefix}.descriptionMs`, async () => await story.describeContext(nextContent, updatedContext, env, runtimeHandle));
|
|
753
|
+
const goal = await measureBenchmark(params.__benchmark, `${stagePrefix}.goalMs`, async () => await story.defineGoal(nextContent, updatedContext, env, runtimeHandle));
|
|
754
|
+
if (description !== null || goal !== null) {
|
|
755
|
+
updatedContext = await measureBenchmark(params.__benchmark, `${stagePrefix}.contextDefinitionMs`, async () => await ops.updateContextDefinition(activeContextSelector, {
|
|
756
|
+
...(description !== null ? { description } : {}),
|
|
757
|
+
...(goal !== null ? { goal } : {}),
|
|
758
|
+
}));
|
|
759
|
+
updatedContext = {
|
|
760
|
+
...updatedContext,
|
|
761
|
+
resources: contextResources,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
506
764
|
await emitContextEvents({
|
|
507
|
-
silent,
|
|
508
765
|
writable,
|
|
509
766
|
events: [
|
|
510
767
|
{
|
|
511
768
|
type: "step.created",
|
|
512
769
|
at: nowIso(),
|
|
513
|
-
stepId: String(
|
|
770
|
+
stepId: String(openedStep.stepId),
|
|
514
771
|
executionId,
|
|
515
772
|
iteration: iter,
|
|
516
773
|
status: "running",
|
|
517
774
|
},
|
|
518
|
-
],
|
|
519
|
-
});
|
|
520
|
-
// Hook: Context DSL `context()` (implemented by subclasses via `initialize()`)
|
|
521
|
-
const nextContent = await measureBenchmark(params.__benchmark, `${stagePrefix}.contextMs`, async () => await story.initialize(updatedContext, env));
|
|
522
|
-
updatedContext = await measureBenchmark(params.__benchmark, `${stagePrefix}.persistContextMs`, async () => await ops.updateContextContent(activeContextSelector, nextContent));
|
|
523
|
-
await emitContextEvents({
|
|
524
|
-
silent,
|
|
525
|
-
writable,
|
|
526
|
-
events: [
|
|
527
775
|
{
|
|
528
776
|
type: "context.content_updated",
|
|
529
777
|
at: nowIso(),
|
|
530
778
|
contextId: String(updatedContext.id),
|
|
531
779
|
},
|
|
780
|
+
...(description !== null || goal !== null
|
|
781
|
+
? [
|
|
782
|
+
{
|
|
783
|
+
type: "context.definition_updated",
|
|
784
|
+
at: nowIso(),
|
|
785
|
+
contextId: String(updatedContext.id),
|
|
786
|
+
},
|
|
787
|
+
]
|
|
788
|
+
: []),
|
|
789
|
+
...(shouldPersistResources
|
|
790
|
+
? [
|
|
791
|
+
{
|
|
792
|
+
type: "context.resources_updated",
|
|
793
|
+
at: nowIso(),
|
|
794
|
+
contextId: String(updatedContext.id),
|
|
795
|
+
},
|
|
796
|
+
]
|
|
797
|
+
: []),
|
|
532
798
|
],
|
|
533
799
|
});
|
|
534
|
-
await story.opts.onContextUpdated?.({
|
|
800
|
+
await story.opts.onContextUpdated?.({
|
|
801
|
+
env,
|
|
802
|
+
runtime: runtimeHandle,
|
|
803
|
+
context: updatedContext,
|
|
804
|
+
});
|
|
535
805
|
// Hook: Context DSL `narrative()` (implemented by subclasses via `buildSystemPrompt()`)
|
|
536
|
-
const systemPrompt = await measureBenchmark(params.__benchmark, `${stagePrefix}.narrativeMs`, async () => await story.buildSystemPrompt(updatedContext, env));
|
|
806
|
+
const systemPrompt = await measureBenchmark(params.__benchmark, `${stagePrefix}.narrativeMs`, async () => await story.buildSystemPrompt(updatedContext, env, runtimeHandle));
|
|
537
807
|
// Hook: Context DSL `actions()` (implemented by subclasses via `buildTools()`)
|
|
538
|
-
const toolsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.actionsMs`, async () => await story.buildTools(updatedContext, env));
|
|
539
|
-
const skillsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.skillsMs`, async () => await story.buildSkills(updatedContext, env));
|
|
540
|
-
|
|
541
|
-
// Match DurableAgent behavior: convert tool input schemas to plain JSON Schema in workflow context.
|
|
542
|
-
const actionSpecs = actionsToActionSpecs(toolsAll);
|
|
808
|
+
const toolsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.actionsMs`, async () => await story.buildTools(updatedContext, env, runtimeHandle));
|
|
809
|
+
const skillsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.skillsMs`, async () => await story.buildSkills(updatedContext, env, runtimeHandle));
|
|
810
|
+
const expandedEvents = await measureBenchmark(params.__benchmark, `${stagePrefix}.expandEventsMs`, async () => await story.expandEvents(rawEvents, updatedContext, env, runtimeHandle));
|
|
543
811
|
// Execute model reaction for this iteration using the stable reaction event id.
|
|
544
812
|
//
|
|
545
813
|
// IMPORTANT:
|
|
@@ -547,7 +815,7 @@ export class ContextEngine {
|
|
|
547
815
|
// If we stream with a per-step id, the UI will render an optimistic assistant message
|
|
548
816
|
// (step id) and then a second persisted assistant message (reaction id) with the same
|
|
549
817
|
// content once InstantDB updates.
|
|
550
|
-
const reactor = story.getReactor(updatedContext, env);
|
|
818
|
+
const reactor = story.getReactor(updatedContext, env, runtimeHandle);
|
|
551
819
|
const reactionPartsBeforeStep = Array.isArray(reactionEvent.content?.parts)
|
|
552
820
|
? [...reactionEvent.content.parts]
|
|
553
821
|
: [];
|
|
@@ -558,42 +826,43 @@ export class ContextEngine {
|
|
|
558
826
|
if (nextSignature === persistedReactionPartsSignature)
|
|
559
827
|
return;
|
|
560
828
|
persistedReactionPartsSignature = nextSignature;
|
|
561
|
-
await ops.
|
|
562
|
-
stepId:
|
|
829
|
+
const saved = await ops.saveExecutionStepOutput({
|
|
830
|
+
stepId: openedStep.stepId,
|
|
563
831
|
parts: normalizedParts,
|
|
832
|
+
reactionEventId: reactionEvent.id,
|
|
833
|
+
reactionEvent: {
|
|
834
|
+
...reactionEvent,
|
|
835
|
+
content: {
|
|
836
|
+
...reactionEvent.content,
|
|
837
|
+
parts: [...reactionPartsBeforeStep, ...normalizedParts],
|
|
838
|
+
},
|
|
839
|
+
status: "pending",
|
|
840
|
+
},
|
|
564
841
|
executionId,
|
|
565
842
|
contextId: String(currentContext.id),
|
|
566
843
|
iteration: iter,
|
|
567
844
|
});
|
|
568
|
-
reactionEvent =
|
|
569
|
-
...reactionEvent,
|
|
570
|
-
content: {
|
|
571
|
-
...reactionEvent.content,
|
|
572
|
-
parts: [...reactionPartsBeforeStep, ...normalizedParts],
|
|
573
|
-
},
|
|
574
|
-
status: "pending",
|
|
575
|
-
}, { executionId, contextId: String(currentContext.id) });
|
|
845
|
+
reactionEvent = saved.reactionEvent;
|
|
576
846
|
};
|
|
577
847
|
const reactionResult = await measureBenchmark(params.__benchmark, `${stagePrefix}.reactorMs`, async () => await reactor({
|
|
578
|
-
runtime:
|
|
579
|
-
env,
|
|
848
|
+
runtime: runtimeHandle,
|
|
580
849
|
context: updatedContext,
|
|
581
850
|
contextIdentifier: activeContextSelector,
|
|
851
|
+
resources: contextResources,
|
|
852
|
+
events: expandedEvents,
|
|
582
853
|
triggerEvent,
|
|
583
|
-
model: story.getModel(updatedContext, env),
|
|
854
|
+
model: story.getModel(updatedContext, env, runtimeHandle),
|
|
584
855
|
systemPrompt,
|
|
585
856
|
actions: toolsAll,
|
|
586
|
-
actionSpecs,
|
|
587
857
|
skills: skillsAll,
|
|
588
858
|
eventId: reactionEventId,
|
|
589
859
|
executionId,
|
|
590
860
|
contextId: String(currentContext.id),
|
|
591
|
-
stepId: String(
|
|
861
|
+
stepId: String(openedStep.stepId),
|
|
592
862
|
iteration: iter,
|
|
593
863
|
maxModelSteps,
|
|
594
864
|
// Only emit a `start` chunk once per story turn.
|
|
595
|
-
sendStart:
|
|
596
|
-
silent,
|
|
865
|
+
sendStart: iter === 0,
|
|
597
866
|
contextStepStream: currentStepStream?.stream,
|
|
598
867
|
writable,
|
|
599
868
|
persistReactionParts,
|
|
@@ -627,21 +896,34 @@ export class ContextEngine {
|
|
|
627
896
|
parts: stepParts,
|
|
628
897
|
},
|
|
629
898
|
};
|
|
630
|
-
|
|
631
|
-
|
|
899
|
+
const nextAssistantParts = Array.isArray(assistantEventEffective.content?.parts)
|
|
900
|
+
? assistantEventEffective.content.parts
|
|
901
|
+
: [];
|
|
902
|
+
const nextReactionEvent = {
|
|
903
|
+
...reactionEvent,
|
|
904
|
+
content: {
|
|
905
|
+
...reactionEvent.content,
|
|
906
|
+
parts: [...reactionPartsBeforeStep, ...nextAssistantParts],
|
|
907
|
+
},
|
|
908
|
+
status: "pending",
|
|
909
|
+
};
|
|
910
|
+
const appendedReactorOutput = await measureBenchmark(params.__benchmark, `${stagePrefix}.saveExecutionStepOutputMs`, async () => await ops.saveExecutionStepOutput({
|
|
911
|
+
stepId: openedStep.stepId,
|
|
632
912
|
parts: stepParts,
|
|
913
|
+
reactionEventId: reactionEvent.id,
|
|
914
|
+
reactionEvent: nextReactionEvent,
|
|
633
915
|
executionId,
|
|
634
916
|
contextId: String(currentContext.id),
|
|
635
917
|
iteration: iter,
|
|
636
918
|
}));
|
|
919
|
+
reactionEvent = appendedReactorOutput.reactionEvent;
|
|
637
920
|
await emitContextEvents({
|
|
638
|
-
silent,
|
|
639
921
|
writable,
|
|
640
922
|
events: stepParts.map((part, idx) => ({
|
|
641
923
|
type: "part.created",
|
|
642
924
|
at: nowIso(),
|
|
643
|
-
partKey: `${String(
|
|
644
|
-
stepId: String(
|
|
925
|
+
partKey: `${String(openedStep.stepId)}:${idx}`,
|
|
926
|
+
stepId: String(openedStep.stepId),
|
|
645
927
|
idx,
|
|
646
928
|
partType: part && typeof part.type === "string"
|
|
647
929
|
? String(part.type)
|
|
@@ -649,19 +931,6 @@ export class ContextEngine {
|
|
|
649
931
|
...summarizePartPreview(part),
|
|
650
932
|
})),
|
|
651
933
|
});
|
|
652
|
-
// Persist/append the aggregated reaction event (stable `reactionEventId` for the execution).
|
|
653
|
-
const nextAssistantParts = Array.isArray(assistantEventEffective.content?.parts)
|
|
654
|
-
? assistantEventEffective.content.parts
|
|
655
|
-
: [];
|
|
656
|
-
const nextReactionEvent = {
|
|
657
|
-
...reactionEvent,
|
|
658
|
-
content: {
|
|
659
|
-
...reactionEvent.content,
|
|
660
|
-
parts: [...reactionPartsBeforeStep, ...nextAssistantParts],
|
|
661
|
-
},
|
|
662
|
-
status: "pending",
|
|
663
|
-
};
|
|
664
|
-
reactionEvent = await measureBenchmark(params.__benchmark, `${stagePrefix}.persistAssistantReactionMs`, async () => await ops.updateItem(reactionEvent.id, nextReactionEvent, { executionId, contextId: String(currentContext.id) }));
|
|
665
934
|
if (reactionResult.reactor?.kind) {
|
|
666
935
|
updatedContext = await measureBenchmark(params.__benchmark, `${stagePrefix}.persistReactorStateMs`, async () => await ops.updateContextReactor(activeContextSelector, {
|
|
667
936
|
kind: reactionResult.reactor.kind,
|
|
@@ -671,47 +940,17 @@ export class ContextEngine {
|
|
|
671
940
|
},
|
|
672
941
|
}));
|
|
673
942
|
}
|
|
674
|
-
if (currentStepStream) {
|
|
675
|
-
await closePersistedContextStepStream({
|
|
676
|
-
runtime: params.runtime,
|
|
677
|
-
session: currentStepStream,
|
|
678
|
-
});
|
|
679
|
-
currentStepStream = null;
|
|
680
|
-
}
|
|
681
943
|
story.opts.onEventCreated?.(assistantEventEffective);
|
|
682
|
-
const firstActionRequest = actionRequests?.[0];
|
|
683
|
-
await measureBenchmark(params.__benchmark, `${stagePrefix}.markStepRunningMs`, async () => await ops.updateContextStep({
|
|
684
|
-
stepId: stepCreate.stepId,
|
|
685
|
-
patch: firstActionRequest
|
|
686
|
-
? {
|
|
687
|
-
kind: "action_execute",
|
|
688
|
-
actionName: typeof firstActionRequest.actionName === "string"
|
|
689
|
-
? firstActionRequest.actionName
|
|
690
|
-
: undefined,
|
|
691
|
-
actionInput: firstActionRequest.input,
|
|
692
|
-
}
|
|
693
|
-
: {
|
|
694
|
-
kind: "message",
|
|
695
|
-
},
|
|
696
|
-
executionId,
|
|
697
|
-
contextId: String(currentContext.id),
|
|
698
|
-
iteration: iter,
|
|
699
|
-
}));
|
|
700
944
|
await emitContextEvents({
|
|
701
|
-
silent,
|
|
702
945
|
writable,
|
|
703
946
|
events: [
|
|
704
947
|
{
|
|
705
948
|
type: "step.updated",
|
|
706
949
|
at: nowIso(),
|
|
707
|
-
stepId: String(
|
|
950
|
+
stepId: String(openedStep.stepId),
|
|
708
951
|
executionId,
|
|
709
952
|
iteration: iter,
|
|
710
953
|
status: "running",
|
|
711
|
-
kind: firstActionRequest ? "action_execute" : "message",
|
|
712
|
-
actionName: firstActionRequest && typeof firstActionRequest.actionName === "string"
|
|
713
|
-
? firstActionRequest.actionName
|
|
714
|
-
: undefined,
|
|
715
954
|
},
|
|
716
955
|
],
|
|
717
956
|
});
|
|
@@ -719,50 +958,45 @@ export class ContextEngine {
|
|
|
719
958
|
if (!actionRequests.length) {
|
|
720
959
|
const endResult = await story.callOnEnd(assistantEventEffective);
|
|
721
960
|
if (endResult) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
961
|
+
const completedReactionEvent = {
|
|
962
|
+
...reactionEvent,
|
|
963
|
+
status: "completed",
|
|
964
|
+
};
|
|
965
|
+
const finalized = await measureBenchmark(params.__benchmark, `${stagePrefix}.completeExecutionStepMs`, async () => await ops.completeExecutionStep({
|
|
966
|
+
session: currentStepStream,
|
|
967
|
+
stepId: openedStep.stepId,
|
|
968
|
+
stepStatus: "completed",
|
|
969
|
+
reactionEventId,
|
|
970
|
+
reactionEvent: completedReactionEvent,
|
|
732
971
|
executionId,
|
|
733
972
|
contextId: String(currentContext.id),
|
|
734
973
|
iteration: iter,
|
|
735
974
|
}));
|
|
975
|
+
currentStepStream = null;
|
|
976
|
+
currentStepId = null;
|
|
977
|
+
reactionEvent = finalized.reactionEvent ?? completedReactionEvent;
|
|
736
978
|
await emitContextEvents({
|
|
737
|
-
silent,
|
|
738
979
|
writable,
|
|
739
980
|
events: [
|
|
740
981
|
{
|
|
741
982
|
type: "step.updated",
|
|
742
983
|
at: nowIso(),
|
|
743
|
-
stepId: String(
|
|
984
|
+
stepId: String(openedStep.stepId),
|
|
744
985
|
executionId,
|
|
745
986
|
iteration: iter,
|
|
746
987
|
status: "completed",
|
|
747
|
-
kind: "message",
|
|
748
988
|
},
|
|
749
989
|
{
|
|
750
990
|
type: "step.completed",
|
|
751
991
|
at: nowIso(),
|
|
752
|
-
stepId: String(
|
|
992
|
+
stepId: String(openedStep.stepId),
|
|
753
993
|
executionId,
|
|
754
994
|
iteration: iter,
|
|
755
995
|
status: "completed",
|
|
756
996
|
},
|
|
757
997
|
],
|
|
758
998
|
});
|
|
759
|
-
// Mark reaction event completed
|
|
760
|
-
await measureBenchmark(params.__benchmark, `${stagePrefix}.completeReactionMs`, async () => await ops.updateItem(reactionEventId, {
|
|
761
|
-
...reactionEvent,
|
|
762
|
-
status: "completed",
|
|
763
|
-
}, { executionId, contextId: String(currentContext.id) }));
|
|
764
999
|
await emitContextEvents({
|
|
765
|
-
silent,
|
|
766
1000
|
writable,
|
|
767
1001
|
events: [
|
|
768
1002
|
{
|
|
@@ -779,7 +1013,6 @@ export class ContextEngine {
|
|
|
779
1013
|
execution = { ...execution, status: "completed" };
|
|
780
1014
|
updatedContext = { ...updatedContext, status: "closed" };
|
|
781
1015
|
await emitContextEvents({
|
|
782
|
-
silent,
|
|
783
1016
|
writable,
|
|
784
1017
|
events: [
|
|
785
1018
|
{
|
|
@@ -797,19 +1030,15 @@ export class ContextEngine {
|
|
|
797
1030
|
},
|
|
798
1031
|
],
|
|
799
1032
|
});
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
}
|
|
803
|
-
reactionEvent = {
|
|
804
|
-
...reactionEvent,
|
|
805
|
-
status: "completed",
|
|
806
|
-
};
|
|
807
|
-
return {
|
|
1033
|
+
await closeContextStream({ preventClose, sendFinish, writable });
|
|
1034
|
+
const result = {
|
|
808
1035
|
context: updatedContext,
|
|
809
1036
|
trigger,
|
|
810
1037
|
reaction: reactionEvent,
|
|
811
1038
|
execution,
|
|
812
1039
|
};
|
|
1040
|
+
await resumeReturnValueHook({ ok: true, result });
|
|
1041
|
+
return result;
|
|
813
1042
|
}
|
|
814
1043
|
}
|
|
815
1044
|
// Execute actions (workflow context; action implementations decide step vs workflow)
|
|
@@ -852,20 +1081,21 @@ export class ContextEngine {
|
|
|
852
1081
|
actionInput = approval.args;
|
|
853
1082
|
}
|
|
854
1083
|
}
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1084
|
+
const executeAction = toolDef.execute;
|
|
1085
|
+
const output = await Reflect.apply(executeAction, undefined, [actionInput, {
|
|
1086
|
+
runtime: runtimeHandle,
|
|
1087
|
+
context: updatedContext,
|
|
1088
|
+
contextIdentifier: activeContextSelector,
|
|
1089
|
+
toolCallId: actionRequest.actionRef,
|
|
1090
|
+
messages: messagesForModel,
|
|
1091
|
+
eventId: reactionEventId,
|
|
1092
|
+
executionId,
|
|
1093
|
+
triggerEventId,
|
|
1094
|
+
contextId: currentContext.id,
|
|
1095
|
+
stepId: String(openedStep.stepId),
|
|
1096
|
+
iteration: iter,
|
|
1097
|
+
contextStepStream: currentStepStream?.stream,
|
|
1098
|
+
}]);
|
|
869
1099
|
return { actionRequest, success: true, output };
|
|
870
1100
|
}
|
|
871
1101
|
catch (e) {
|
|
@@ -877,26 +1107,19 @@ export class ContextEngine {
|
|
|
877
1107
|
};
|
|
878
1108
|
}
|
|
879
1109
|
})));
|
|
880
|
-
// Merge action results into
|
|
1110
|
+
// Merge action results into step parts so the next reaction can see them.
|
|
881
1111
|
let finalizedStepParts = Array.isArray(stepParts) ? [...stepParts] : [];
|
|
882
1112
|
for (const r of actionResults) {
|
|
883
|
-
finalizedStepParts =
|
|
884
|
-
|
|
885
|
-
|
|
1113
|
+
finalizedStepParts = applyActionExecutionResultToParts(finalizedStepParts, {
|
|
1114
|
+
actionCallId: r.actionRequest.actionRef,
|
|
1115
|
+
actionName: r.actionRequest.actionName,
|
|
886
1116
|
}, {
|
|
887
1117
|
success: Boolean(r.success),
|
|
888
1118
|
result: r.output,
|
|
889
1119
|
message: r.errorText,
|
|
890
1120
|
});
|
|
891
1121
|
}
|
|
892
|
-
|
|
893
|
-
stepId: stepCreate.stepId,
|
|
894
|
-
parts: finalizedStepParts,
|
|
895
|
-
executionId,
|
|
896
|
-
contextId: String(currentContext.id),
|
|
897
|
-
iteration: iter,
|
|
898
|
-
}));
|
|
899
|
-
reactionEvent = {
|
|
1122
|
+
const pendingReactionEvent = {
|
|
900
1123
|
...reactionEvent,
|
|
901
1124
|
content: {
|
|
902
1125
|
...reactionEvent.content,
|
|
@@ -906,85 +1129,70 @@ export class ContextEngine {
|
|
|
906
1129
|
},
|
|
907
1130
|
status: "pending",
|
|
908
1131
|
};
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
success: r.success,
|
|
914
|
-
output: r.output,
|
|
915
|
-
errorText: r.errorText,
|
|
916
|
-
eventId: reactionEventId,
|
|
917
|
-
executionId,
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
// Stop/continue boundary: allow the Context to decide if the loop should continue.
|
|
921
|
-
// IMPORTANT: we call this after tool results have been merged into the persisted `reactionEvent`,
|
|
922
|
-
// so stories can inspect `reactionEvent.content.parts` deterministically.
|
|
923
|
-
const continueLoop = await measureBenchmark(params.__benchmark, `${stagePrefix}.shouldContinueMs`, async () => await story.shouldContinue({
|
|
924
|
-
env,
|
|
925
|
-
context: updatedContext,
|
|
926
|
-
reactionEvent,
|
|
927
|
-
assistantEvent: assistantEventEffective,
|
|
928
|
-
actionRequests,
|
|
1132
|
+
const completedStep = await measureBenchmark(params.__benchmark, `${stagePrefix}.completeExecutionStepMs`, async () => await ops.completeExecutionStep({
|
|
1133
|
+
session: currentStepStream,
|
|
1134
|
+
stepId: openedStep.stepId,
|
|
1135
|
+
parts: finalizedStepParts,
|
|
929
1136
|
actionResults: actionResults,
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
stepId: stepCreate.stepId,
|
|
934
|
-
patch: {
|
|
935
|
-
status: "completed",
|
|
936
|
-
kind: actionRequests?.length ? "action_result" : "message",
|
|
937
|
-
actionName: typeof actionResults?.[0]?.actionRequest?.actionName === "string"
|
|
938
|
-
? actionResults[0].actionRequest.actionName
|
|
939
|
-
: undefined,
|
|
940
|
-
actionInput: actionResults?.[0]?.actionRequest?.input,
|
|
941
|
-
actionOutput: actionResults?.[0]?.success === true
|
|
942
|
-
? actionResults[0]?.output
|
|
943
|
-
: undefined,
|
|
944
|
-
actionError: actionResults?.[0]?.success === false
|
|
945
|
-
? String(actionResults[0]?.errorText ?? "action_execution_failed")
|
|
946
|
-
: undefined,
|
|
947
|
-
actionRequests,
|
|
948
|
-
actionResults,
|
|
949
|
-
continueLoop: continueLoop !== false,
|
|
950
|
-
},
|
|
1137
|
+
stepStatus: "completed",
|
|
1138
|
+
reactionEventId,
|
|
1139
|
+
reactionEvent: pendingReactionEvent,
|
|
951
1140
|
executionId,
|
|
952
1141
|
contextId: String(currentContext.id),
|
|
953
1142
|
iteration: iter,
|
|
954
1143
|
}));
|
|
1144
|
+
currentStepStream = null;
|
|
1145
|
+
currentStepId = null;
|
|
1146
|
+
reactionEvent = completedStep.reactionEvent ?? pendingReactionEvent;
|
|
1147
|
+
await emitContextEvents({
|
|
1148
|
+
writable,
|
|
1149
|
+
events: completedStep.actionResultChunkEvents,
|
|
1150
|
+
});
|
|
955
1151
|
await emitContextEvents({
|
|
956
|
-
silent,
|
|
957
1152
|
writable,
|
|
958
1153
|
events: [
|
|
959
1154
|
{
|
|
960
1155
|
type: "step.updated",
|
|
961
1156
|
at: nowIso(),
|
|
962
|
-
stepId: String(
|
|
1157
|
+
stepId: String(openedStep.stepId),
|
|
963
1158
|
executionId,
|
|
964
1159
|
iteration: iter,
|
|
965
1160
|
status: "completed",
|
|
966
|
-
kind: actionRequests?.length ? "action_result" : "message",
|
|
967
|
-
actionName: typeof actionResults?.[0]?.actionRequest?.actionName === "string"
|
|
968
|
-
? actionResults[0].actionRequest.actionName
|
|
969
|
-
: undefined,
|
|
970
1161
|
},
|
|
971
1162
|
{
|
|
972
1163
|
type: "step.completed",
|
|
973
1164
|
at: nowIso(),
|
|
974
|
-
stepId: String(
|
|
1165
|
+
stepId: String(openedStep.stepId),
|
|
975
1166
|
executionId,
|
|
976
1167
|
iteration: iter,
|
|
977
1168
|
status: "completed",
|
|
978
1169
|
},
|
|
979
1170
|
],
|
|
980
1171
|
});
|
|
1172
|
+
// Callback for observability/integration
|
|
1173
|
+
for (const r of actionResults) {
|
|
1174
|
+
await story.opts.onActionExecuted?.({
|
|
1175
|
+
actionRequest: r.actionRequest,
|
|
1176
|
+
success: r.success,
|
|
1177
|
+
output: r.output,
|
|
1178
|
+
errorText: r.errorText,
|
|
1179
|
+
eventId: reactionEventId,
|
|
1180
|
+
executionId,
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
// Stop/continue boundary: allow the Context to decide if the loop should continue.
|
|
1184
|
+
// Tool results are already persisted in the completed reaction step here.
|
|
1185
|
+
const continueLoop = await measureBenchmark(params.__benchmark, `${stagePrefix}.shouldContinueMs`, async () => await story.shouldContinue({
|
|
1186
|
+
env,
|
|
1187
|
+
runtime: runtimeHandle,
|
|
1188
|
+
context: updatedContext,
|
|
1189
|
+
reactionEvent,
|
|
1190
|
+
assistantEvent: assistantEventEffective,
|
|
1191
|
+
actionRequests,
|
|
1192
|
+
actionResults: actionResults,
|
|
1193
|
+
}));
|
|
981
1194
|
if (continueLoop !== false) {
|
|
982
|
-
reactionEvent = await measureBenchmark(params.__benchmark, `${stagePrefix}.persistPendingReactionMs`, async () => await ops.updateItem(reactionEventId, {
|
|
983
|
-
...reactionEvent,
|
|
984
|
-
status: "pending",
|
|
985
|
-
}, { executionId, contextId: String(currentContext.id) }));
|
|
986
1195
|
await emitContextEvents({
|
|
987
|
-
silent,
|
|
988
1196
|
writable,
|
|
989
1197
|
events: [
|
|
990
1198
|
{
|
|
@@ -999,12 +1207,11 @@ export class ContextEngine {
|
|
|
999
1207
|
});
|
|
1000
1208
|
}
|
|
1001
1209
|
if (continueLoop === false) {
|
|
1002
|
-
|
|
1210
|
+
reactionEvent = {
|
|
1003
1211
|
...reactionEvent,
|
|
1004
1212
|
status: "completed",
|
|
1005
|
-
}
|
|
1213
|
+
};
|
|
1006
1214
|
await emitContextEvents({
|
|
1007
|
-
silent,
|
|
1008
1215
|
writable,
|
|
1009
1216
|
events: [
|
|
1010
1217
|
{
|
|
@@ -1017,11 +1224,14 @@ export class ContextEngine {
|
|
|
1017
1224
|
},
|
|
1018
1225
|
],
|
|
1019
1226
|
});
|
|
1020
|
-
await measureBenchmark(params.__benchmark, `${stagePrefix}.completeExecutionMs`, async () => await ops.completeExecution(activeContextSelector, executionId, "completed"
|
|
1227
|
+
await measureBenchmark(params.__benchmark, `${stagePrefix}.completeExecutionMs`, async () => await ops.completeExecution(activeContextSelector, executionId, "completed", {
|
|
1228
|
+
contextId: String(currentContext.id),
|
|
1229
|
+
reactionEventId,
|
|
1230
|
+
reactionEvent,
|
|
1231
|
+
}));
|
|
1021
1232
|
execution = { ...execution, status: "completed" };
|
|
1022
1233
|
updatedContext = { ...updatedContext, status: "closed" };
|
|
1023
1234
|
await emitContextEvents({
|
|
1024
|
-
silent,
|
|
1025
1235
|
writable,
|
|
1026
1236
|
events: [
|
|
1027
1237
|
{
|
|
@@ -1039,19 +1249,15 @@ export class ContextEngine {
|
|
|
1039
1249
|
},
|
|
1040
1250
|
],
|
|
1041
1251
|
});
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
}
|
|
1045
|
-
reactionEvent = {
|
|
1046
|
-
...reactionEvent,
|
|
1047
|
-
status: "completed",
|
|
1048
|
-
};
|
|
1049
|
-
return {
|
|
1252
|
+
await closeContextStream({ preventClose, sendFinish, writable });
|
|
1253
|
+
const result = {
|
|
1050
1254
|
context: updatedContext,
|
|
1051
1255
|
trigger,
|
|
1052
1256
|
reaction: reactionEvent,
|
|
1053
1257
|
execution,
|
|
1054
1258
|
};
|
|
1259
|
+
await resumeReturnValueHook({ ok: true, result });
|
|
1260
|
+
return result;
|
|
1055
1261
|
}
|
|
1056
1262
|
}
|
|
1057
1263
|
throw new Error(`ContextEngine: maxIterations reached (${maxIterations}) without completion`);
|
|
@@ -1060,7 +1266,7 @@ export class ContextEngine {
|
|
|
1060
1266
|
if (currentStepStream) {
|
|
1061
1267
|
try {
|
|
1062
1268
|
await abortPersistedContextStepStream({
|
|
1063
|
-
runtime:
|
|
1269
|
+
runtime: runtimeHandle,
|
|
1064
1270
|
session: currentStepStream,
|
|
1065
1271
|
reason: error instanceof Error ? error.message : String(error),
|
|
1066
1272
|
});
|
|
@@ -1086,7 +1292,6 @@ export class ContextEngine {
|
|
|
1086
1292
|
contextId: String(currentContext.id),
|
|
1087
1293
|
}));
|
|
1088
1294
|
await emitContextEvents({
|
|
1089
|
-
silent,
|
|
1090
1295
|
writable,
|
|
1091
1296
|
events: [
|
|
1092
1297
|
{
|
|
@@ -1105,6 +1310,10 @@ export class ContextEngine {
|
|
|
1105
1310
|
}
|
|
1106
1311
|
}
|
|
1107
1312
|
await failExecution();
|
|
1313
|
+
await resumeReturnValueHook({
|
|
1314
|
+
ok: false,
|
|
1315
|
+
error: serializeContextReturnValueError(error),
|
|
1316
|
+
}).catch(() => null);
|
|
1108
1317
|
throw error;
|
|
1109
1318
|
}
|
|
1110
1319
|
}
|