@ekairos/events 1.22.34-beta.development.0 → 1.22.35-beta.development.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,114 +2,87 @@
2
2
 
3
3
  Context-first durable execution runtime for Ekairos.
4
4
 
5
- ## Surface
5
+ ## What this package does
6
6
 
7
- - `createContext`, `context`, `ContextEngine`
8
- - `createAiSdkReactor`, `createScriptedReactor`
9
- - `useContext`
7
+ - creates durable contexts with `createContext(...)`
8
+ - persists executions, steps, parts, and items
9
+ - runs direct or durable `react(...)` loops
10
+ - adapts model/tool output into canonical `event_parts`
11
+
12
+ ## Main APIs
13
+
14
+ - `createContext`
15
+ - `ContextEngine`
16
+ - `createAiSdkReactor`
17
+ - `createScriptedReactor`
18
+ - `runContextReactionDirect`
10
19
  - `eventsDomain`
11
- - `getContextRuntime`, `getContextEnv`, `registerContextEnv`
12
20
 
13
21
  ## Runtime model
14
22
 
23
+ Canonical entities:
24
+
15
25
  - `event_contexts`
16
26
  - `event_items`
17
27
  - `event_executions`
18
28
  - `event_steps`
19
29
  - `event_parts`
20
- - `event_trace_events`
21
- - `event_trace_runs`
22
- - `event_trace_spans`
23
-
24
- The aggregate is `context`. Executions, steps, parts, and items are scoped to a context.
25
-
26
- ## Canonical Parts
30
+ - `event_trace_*`
27
31
 
28
- `event_parts` is the canonical content model for produced output.
32
+ `event_parts` is the source of truth for replay.
29
33
 
30
- Rules:
31
-
32
- - `event_parts.part` is the source of truth for replay and inspection.
33
- - `event_items.content.parts` on output items is maintained as a compatibility mirror and is deprecated as a replay source.
34
- - Provider/model-specific values must live under `metadata`, never as first-class semantic fields.
35
-
36
- Canonical part kinds:
37
-
38
- - `content`
39
- - `reasoning`
40
- - `source`
41
- - `tool-call`
42
- - `tool-result`
34
+ ## Example
43
35
 
44
- Each canonical part stores a `content` array. The entries inside that array define the payload type:
36
+ ```ts
37
+ import { createContext } from "@ekairos/events";
45
38
 
46
- - `text`
47
- - `file`
48
- - `json`
49
- - `source-url`
50
- - `source-document`
39
+ const supportContext = createContext<{ orgId: string }>("support.agent")
40
+ .context((stored, env) => ({
41
+ ...stored.content,
42
+ orgId: env.orgId,
43
+ }))
44
+ .narrative(() => "You are a precise assistant.")
45
+ .actions(() => ({}))
46
+ .build();
47
+ ```
51
48
 
52
- Example tool result:
49
+ Run directly:
53
50
 
54
51
  ```ts
55
- {
56
- type: "tool-result",
57
- toolCallId: "call_123",
58
- toolName: "inspectCanvasRegion",
59
- state: "output-available",
60
- content: [
61
- {
62
- type: "text",
63
- text: "Zoomed crop of the requested region.",
64
- },
65
- {
66
- type: "file",
67
- mediaType: "image/png",
68
- filename: "inspect-region.png",
69
- data: "iVBORw0KGgoAAAANSUhEUgAA...",
70
- },
71
- ],
72
- metadata: {
73
- provider: {
74
- itemId: "fc_041cb...",
75
- },
76
- },
77
- }
52
+ await supportContext.react(triggerEvent, {
53
+ runtime,
54
+ context: { key: "support:org_123" },
55
+ });
78
56
  ```
79
57
 
80
- The AI SDK bridge projects canonical parts to:
58
+ Run durably:
81
59
 
82
- - assistant messages with text/file/reasoning/source/tool-call parts
83
- - tool messages with `tool-result` or `tool-error`
84
-
85
- That means multipart tool outputs are replayed from `event_parts` instead of relying on the deprecated output-item mirror.
86
-
87
- ## Install
60
+ ```ts
61
+ const shell = await supportContext.react(triggerEvent, {
62
+ runtime,
63
+ context: { key: "support:org_123" },
64
+ durable: true,
65
+ });
88
66
 
89
- ```bash
90
- pnpm add @ekairos/events
67
+ const final = await shell.run?.returnValue;
91
68
  ```
92
69
 
93
- ## Example
70
+ ## Tool execution model
94
71
 
95
- ```ts
96
- import { createContext, createAiSdkReactor } from "@ekairos/events";
97
-
98
- type Env = { orgId: string };
72
+ Context tools now receive runtime-aware execution context.
73
+ That lets a tool do this inside `"use step"`:
99
74
 
100
- export const supportContext = createContext<Env>("support.agent")
101
- .context((stored, env) => ({
102
- orgId: env.orgId,
103
- ...stored.content,
104
- }))
105
- .narrative(() => "You are a precise assistant.")
106
- .actions(() => ({}))
107
- .reactor(createAiSdkReactor())
108
- .build();
75
+ ```ts
76
+ async function execute(input, ctx) {
77
+ "use step";
78
+ const domain = await ctx.runtime.use(myDomain);
79
+ return await domain.actions.doSomething(input);
80
+ }
109
81
  ```
110
82
 
111
- ## Notes
83
+ ## Tests
112
84
 
113
- - Public continuity is context-based.
114
- - Provider-specific IDs such as `providerContextId` may still exist when an upstream provider requires them.
115
- - Runtime wiring for stores lives under `@ekairos/events/runtime`.
85
+ ```bash
86
+ pnpm --filter @ekairos/events test
87
+ pnpm --filter @ekairos/events test:workflow
88
+ ```
package/dist/context.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { ContextEngine, type ContextOptions, type ContextStreamOptions, type ShouldContinue, type ContextShouldContinueArgs, type ContextReactParams, type ContextReactResult, type ContextDurableWorkflowPayload, type ContextDurableWorkflowFunction, type ContextModelInit, type ContextTool, runContextReactionDirect, } from "./context.engine.js";
1
+ export { ContextEngine, type ContextOptions, type ContextStreamOptions, type ShouldContinue, type ContextShouldContinueArgs, type ContextReactParams, type ContextReactResult, type ContextWorkflowRun, type ContextDurableWorkflowPayload, type ContextDurableWorkflowFunction, type ContextModelInit, type ContextTool, type ContextToolExecuteContext, runContextReactionDirect, } from "./context.engine.js";
2
2
  export { context, createContext, type ContextConfig, type ContextInstance, type RegistrableContextBuilder, } from "./context.builder.js";
3
3
  export { createAiSdkReactor, createScriptedReactor, type CreateAiSdkReactorOptions, type CreateScriptedReactorOptions, type ScriptedReactorStep, type ContextReactor, type ContextReactorParams, type ContextReactionResult, type ContextActionRequest, type ContextReactionLLM, } from "./context.reactor.js";
4
4
  export type { ContextSkillPackage, ContextSkillPackageFile, } from "./context.skill.js";
@@ -1,5 +1,6 @@
1
- import type { Tool, UIMessageChunk } from "ai";
1
+ import type { ModelMessage, Tool, UIMessageChunk } from "ai";
2
2
  import type { ContextEnvironment } from "./context.config.js";
3
+ import type { ContextRuntime } from "./context.runtime.js";
3
4
  import type { ContextExecution, ContextItem, ContextIdentifier, StoredContext } from "./context.store.js";
4
5
  import type { ContextSkillPackage } from "./context.skill.js";
5
6
  import { type ContextReactor } from "./context.reactor.js";
@@ -68,7 +69,7 @@ export interface ContextStreamOptions {
68
69
  */
69
70
  export type ContextModelInit = string | (() => Promise<any>);
70
71
  export type ContextReactParams<Env extends ContextEnvironment = ContextEnvironment> = {
71
- env: Env;
72
+ runtime: ContextRuntime<Env>;
72
73
  /**
73
74
  * Context selector (exclusive: `{ id }` OR `{ key }`).
74
75
  * - `{ id }` resolves a concrete context id.
@@ -95,16 +96,36 @@ export type ContextReactResult<Context = any> = {
95
96
  trigger: ContextItem;
96
97
  reaction: ContextItem;
97
98
  execution: ContextExecution;
99
+ run?: ContextWorkflowRun<Context>;
100
+ };
101
+ export type ContextWorkflowRun<Context = any> = {
102
+ runId: string;
103
+ status: Promise<"pending" | "running" | "completed" | "failed" | "cancelled">;
104
+ returnValue: Promise<ContextReactResult<Context>>;
98
105
  };
99
106
  export type ContextDurableWorkflowPayload<Env extends ContextEnvironment = ContextEnvironment> = {
100
107
  contextKey: string;
101
- env: Env;
108
+ runtime: ContextRuntime<Env>;
102
109
  context?: ContextIdentifier | null;
103
110
  triggerEvent: ContextItem;
104
111
  options?: Omit<ContextStreamOptions, "writable">;
105
112
  bootstrap: NonNullable<ContextReactParams<Env>["__bootstrap"]>;
106
113
  };
107
114
  export type ContextDurableWorkflowFunction<Context = any, Env extends ContextEnvironment = ContextEnvironment> = (payload: ContextDurableWorkflowPayload<Env>) => Promise<ContextReactResult<Context>>;
115
+ export type ContextToolExecuteContext<Context = any, Env extends ContextEnvironment = ContextEnvironment> = {
116
+ runtime: ContextRuntime<Env>;
117
+ env: Env;
118
+ context: StoredContext<Context>;
119
+ contextIdentifier: ContextIdentifier;
120
+ toolCallId: string;
121
+ messages: ModelMessage[];
122
+ eventId: string;
123
+ executionId: string;
124
+ triggerEventId: string;
125
+ contextId: string;
126
+ stepId: string;
127
+ iteration: number;
128
+ };
108
129
  export { toolApprovalHookToken, toolApprovalWebhookToken, getClientResumeHookUrl };
109
130
  /**
110
131
  * Context-level tool type.
@@ -1,11 +1,11 @@
1
- import { registerContextEnv } from "./env.js";
1
+ import { getContextRuntimeServices } from "./context.runtime.js";
2
2
  import { OUTPUT_ITEM_TYPE, WEB_CHANNEL } from "./context.events.js";
3
3
  import { applyToolExecutionResultToParts } from "./context.toolcalls.js";
4
4
  import { isContextPartEnvelope, normalizePartsForPersistence, } from "./context.parts.js";
5
- import { toolsToModelTools } from "./tools-to-model-tools.js";
5
+ import { actionsToActionSpecs } from "./tools-to-model-tools.js";
6
6
  import { createAiSdkReactor, } from "./context.reactor.js";
7
7
  import { abortPersistedContextStepStream, closePersistedContextStepStream, createPersistedContextStepStream, closeContextStream, } from "./steps/stream.steps.js";
8
- import { completeExecution, createContextStep, initializeContext, saveTriggerAndCreateExecution, saveContextPartsStep, updateContextContent, updateContextStatus, updateItem, updateContextStep, } from "./steps/store.steps.js";
8
+ import { completeExecution, createContextStep, initializeContext, saveTriggerAndCreateExecution, saveContextPartsStep, updateContextContent, updateContextReactor, updateContextStatus, updateItem, updateContextStep, } from "./steps/store.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) {
@@ -89,9 +89,8 @@ async function readActiveWorkflowRunId() {
89
89
  return null;
90
90
  }
91
91
  }
92
- async function createRuntimeOps(env, benchmark) {
93
- const { getContextRuntime } = await import("./runtime.js");
94
- const runtime = await getContextRuntime(env);
92
+ async function createRuntimeOps(runtimeHandle, benchmark) {
93
+ const runtime = await getContextRuntimeServices(runtimeHandle);
95
94
  const { db } = runtime;
96
95
  const { InstantStore } = await import("./stores/instant.store.js");
97
96
  const requireContextId = (contextIdentifier) => {
@@ -145,6 +144,7 @@ async function createRuntimeOps(env, benchmark) {
145
144
  return { context, isNew: true };
146
145
  },
147
146
  updateContextContent: async (contextIdentifier, content) => await store.updateContextContent(contextIdentifier, content),
147
+ updateContextReactor: async (contextIdentifier, reactor) => await store.updateContextReactor(contextIdentifier, reactor),
148
148
  updateContextStatus: async (contextIdentifier, status) => await instrumentedDb.transact([
149
149
  instrumentedDb.tx.event_contexts[requireContextId(contextIdentifier)].update({
150
150
  status,
@@ -255,27 +255,28 @@ async function createRuntimeOps(env, benchmark) {
255
255
  },
256
256
  };
257
257
  }
258
- async function createWorkflowOps(env) {
258
+ async function createWorkflowOps(runtime) {
259
+ const env = runtime.env;
259
260
  return {
260
- initializeContext: async (contextIdentifier, opts) => await initializeContext(env, contextIdentifier, opts),
261
- updateContextContent: async (contextIdentifier, content) => await updateContextContent(env, contextIdentifier, content),
262
- updateContextStatus: async (contextIdentifier, status) => await updateContextStatus(env, contextIdentifier, status),
263
- saveTriggerAndCreateExecution: async ({ contextIdentifier, triggerEvent }) => await saveTriggerAndCreateExecution({ env, contextIdentifier, triggerEvent }),
264
- createContextStep: async ({ executionId, iteration }) => await createContextStep({ env, executionId, iteration }),
265
- updateContextStep: async (params) => await updateContextStep({ env, ...params }),
266
- saveContextPartsStep: async (params) => await saveContextPartsStep({ env, ...params }),
267
- updateItem: async (itemId, item, opts) => await updateItem(env, itemId, item, opts),
268
- completeExecution: async (contextIdentifier, executionId, status) => await completeExecution(env, contextIdentifier, executionId, status),
261
+ initializeContext: async (contextIdentifier, opts) => await initializeContext({ runtime, contextIdentifier, opts }),
262
+ updateContextContent: async (contextIdentifier, content) => await updateContextContent({ runtime, contextIdentifier, content }),
263
+ updateContextReactor: async (contextIdentifier, reactor) => await updateContextReactor({ runtime, contextIdentifier, reactor }),
264
+ updateContextStatus: async (contextIdentifier, status) => await updateContextStatus({ runtime, contextIdentifier, status }),
265
+ saveTriggerAndCreateExecution: async ({ contextIdentifier, triggerEvent }) => await saveTriggerAndCreateExecution({ runtime, contextIdentifier, triggerEvent }),
266
+ createContextStep: async ({ executionId, iteration }) => await createContextStep({ runtime, executionId, iteration }),
267
+ updateContextStep: async (params) => await updateContextStep({ runtime, ...params }),
268
+ saveContextPartsStep: async (params) => await saveContextPartsStep({ runtime, ...params }),
269
+ 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 }),
269
271
  };
270
272
  }
271
- async function getContextEngineOps(env, benchmark) {
273
+ async function getContextEngineOps(runtime, benchmark) {
274
+ const env = runtime.env;
272
275
  const workflowRunId = await readActiveWorkflowRunId();
273
276
  if (workflowRunId) {
274
- registerContextEnv(env, workflowRunId);
275
- return await createWorkflowOps(env);
277
+ return await createWorkflowOps(runtime);
276
278
  }
277
- registerContextEnv(env);
278
- return await createRuntimeOps(env, benchmark);
279
+ return await createRuntimeOps(runtime, benchmark);
279
280
  }
280
281
  export class ContextEngine {
281
282
  constructor(opts = {}, reactor) {
@@ -329,13 +330,14 @@ export class ContextEngine {
329
330
  return await ContextEngine.runDirect(this, triggerEvent, params);
330
331
  }
331
332
  static async prepareExecutionShell(story, triggerEvent, params) {
332
- const ops = await measureBenchmark(params.__benchmark, "react.resolveOpsMs", async () => await getContextEngineOps(params.env, params.__benchmark));
333
+ const env = params.runtime.env;
334
+ const ops = await measureBenchmark(params.__benchmark, "react.resolveOpsMs", async () => await getContextEngineOps(params.runtime, params.__benchmark));
333
335
  const silent = params.options?.silent ?? false;
334
336
  const ctxResult = await measureBenchmark(params.__benchmark, "react.initializeContextMs", async () => await ops.initializeContext(params.context ?? null, { silent }));
335
337
  let currentContext = ctxResult.context;
336
338
  const contextSelector = { id: String(currentContext.id) };
337
339
  if (ctxResult.isNew) {
338
- await story.opts.onContextCreated?.({ env: params.env, context: currentContext });
340
+ await story.opts.onContextCreated?.({ env, context: currentContext });
339
341
  }
340
342
  if (currentContext.status === "closed") {
341
343
  await measureBenchmark(params.__benchmark, "react.reopenClosedContextMs", async () => await ops.updateContextStatus(contextSelector, "open_idle"));
@@ -355,6 +357,7 @@ export class ContextEngine {
355
357
  };
356
358
  }
357
359
  static async startDurable(story, triggerEvent, params) {
360
+ const env = params.runtime.env;
358
361
  if (params.options?.writable) {
359
362
  throw new Error("ContextEngine.react: durable runs manage their own workflow stream");
360
363
  }
@@ -367,14 +370,15 @@ export class ContextEngine {
367
370
  throw new Error("ContextEngine.react: durable workflow is not configured. Call configureContextDurableWorkflow(...) in runtime bootstrap.");
368
371
  }
369
372
  const shell = await ContextEngine.prepareExecutionShell(story, triggerEvent, params);
373
+ let run;
370
374
  try {
371
375
  const [{ start }] = await Promise.all([
372
376
  import("workflow/api"),
373
377
  ]);
374
- const run = await start(workflow, [
378
+ const startedRun = await start(workflow, [
375
379
  {
376
380
  contextKey,
377
- env: params.env,
381
+ runtime: params.runtime,
378
382
  context: params.context ?? null,
379
383
  triggerEvent,
380
384
  options: {
@@ -392,16 +396,21 @@ export class ContextEngine {
392
396
  },
393
397
  },
394
398
  ]);
395
- const runtime = await createRuntimeOps(params.env);
399
+ run = {
400
+ runId: String(startedRun.runId),
401
+ status: startedRun.status,
402
+ returnValue: startedRun.returnValue,
403
+ };
404
+ const runtime = await createRuntimeOps(params.runtime);
396
405
  await runtime.db.transact([
397
406
  runtime.db.tx.event_executions[shell.execution.id].update({
398
- workflowRunId: run.runId,
407
+ workflowRunId: startedRun.runId,
399
408
  updatedAt: new Date(),
400
409
  }),
401
410
  ]);
402
411
  }
403
412
  catch (error) {
404
- const ops = await getContextEngineOps(params.env, params.__benchmark);
413
+ const ops = await getContextEngineOps(params.runtime, params.__benchmark);
405
414
  await ops.completeExecution(shell.contextSelector, shell.execution.id, "failed").catch(() => null);
406
415
  throw error;
407
416
  }
@@ -410,10 +419,12 @@ export class ContextEngine {
410
419
  trigger: shell.trigger,
411
420
  reaction: shell.reaction,
412
421
  execution: shell.execution,
422
+ run,
413
423
  };
414
424
  }
415
425
  static async runDirect(story, triggerEvent, params) {
416
- const ops = await measureBenchmark(params.__benchmark, "react.resolveOpsMs", async () => await getContextEngineOps(params.env, params.__benchmark));
426
+ const env = params.runtime.env;
427
+ const ops = await measureBenchmark(params.__benchmark, "react.resolveOpsMs", async () => await getContextEngineOps(params.runtime, params.__benchmark));
417
428
  const maxIterations = params.options?.maxIterations ?? 20;
418
429
  const maxModelSteps = params.options?.maxModelSteps ?? 1;
419
430
  const preventClose = params.options?.preventClose ?? false;
@@ -488,7 +499,7 @@ export class ContextEngine {
488
499
  }));
489
500
  currentStepId = stepCreate.stepId;
490
501
  currentStepStream = await createPersistedContextStepStream({
491
- env: params.env,
502
+ runtime: params.runtime,
492
503
  executionId,
493
504
  stepId: stepCreate.stepId,
494
505
  });
@@ -507,7 +518,7 @@ export class ContextEngine {
507
518
  ],
508
519
  });
509
520
  // Hook: Context DSL `context()` (implemented by subclasses via `initialize()`)
510
- const nextContent = await measureBenchmark(params.__benchmark, `${stagePrefix}.contextMs`, async () => await story.initialize(updatedContext, params.env));
521
+ const nextContent = await measureBenchmark(params.__benchmark, `${stagePrefix}.contextMs`, async () => await story.initialize(updatedContext, env));
511
522
  updatedContext = await measureBenchmark(params.__benchmark, `${stagePrefix}.persistContextMs`, async () => await ops.updateContextContent(activeContextSelector, nextContent));
512
523
  await emitContextEvents({
513
524
  silent,
@@ -520,15 +531,15 @@ export class ContextEngine {
520
531
  },
521
532
  ],
522
533
  });
523
- await story.opts.onContextUpdated?.({ env: params.env, context: updatedContext });
534
+ await story.opts.onContextUpdated?.({ env, context: updatedContext });
524
535
  // Hook: Context DSL `narrative()` (implemented by subclasses via `buildSystemPrompt()`)
525
- const systemPrompt = await measureBenchmark(params.__benchmark, `${stagePrefix}.narrativeMs`, async () => await story.buildSystemPrompt(updatedContext, params.env));
536
+ const systemPrompt = await measureBenchmark(params.__benchmark, `${stagePrefix}.narrativeMs`, async () => await story.buildSystemPrompt(updatedContext, env));
526
537
  // Hook: Context DSL `actions()` (implemented by subclasses via `buildTools()`)
527
- const toolsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.actionsMs`, async () => await story.buildTools(updatedContext, params.env));
528
- const skillsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.skillsMs`, async () => await story.buildSkills(updatedContext, params.env));
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));
529
540
  // IMPORTANT: step args must be serializable.
530
541
  // Match DurableAgent behavior: convert tool input schemas to plain JSON Schema in workflow context.
531
- const toolsForModel = toolsToModelTools(toolsAll);
542
+ const actionSpecs = actionsToActionSpecs(toolsAll);
532
543
  // Execute model reaction for this iteration using the stable reaction event id.
533
544
  //
534
545
  // IMPORTANT:
@@ -536,7 +547,7 @@ export class ContextEngine {
536
547
  // If we stream with a per-step id, the UI will render an optimistic assistant message
537
548
  // (step id) and then a second persisted assistant message (reaction id) with the same
538
549
  // content once InstantDB updates.
539
- const reactor = story.getReactor(updatedContext, params.env);
550
+ const reactor = story.getReactor(updatedContext, env);
540
551
  const reactionPartsBeforeStep = Array.isArray(reactionEvent.content?.parts)
541
552
  ? [...reactionEvent.content.parts]
542
553
  : [];
@@ -563,15 +574,16 @@ export class ContextEngine {
563
574
  status: "pending",
564
575
  }, { executionId, contextId: String(currentContext.id) });
565
576
  };
566
- const { assistantEvent, actionRequests, messagesForModel } = await measureBenchmark(params.__benchmark, `${stagePrefix}.reactorMs`, async () => await reactor({
567
- env: params.env,
577
+ const reactionResult = await measureBenchmark(params.__benchmark, `${stagePrefix}.reactorMs`, async () => await reactor({
578
+ runtime: params.runtime,
579
+ env,
568
580
  context: updatedContext,
569
581
  contextIdentifier: activeContextSelector,
570
582
  triggerEvent,
571
- model: story.getModel(updatedContext, params.env),
583
+ model: story.getModel(updatedContext, env),
572
584
  systemPrompt,
573
585
  actions: toolsAll,
574
- toolsForModel,
586
+ actionSpecs,
575
587
  skills: skillsAll,
576
588
  eventId: reactionEventId,
577
589
  executionId,
@@ -586,6 +598,7 @@ export class ContextEngine {
586
598
  writable,
587
599
  persistReactionParts,
588
600
  }));
601
+ const { assistantEvent, actionRequests, messagesForModel } = reactionResult;
589
602
  const reviewRequests = actionRequests.length > 0
590
603
  ? actionRequests.flatMap((actionRequest) => {
591
604
  const toolDef = toolsAll[actionRequest.actionName];
@@ -649,9 +662,18 @@ export class ContextEngine {
649
662
  status: "pending",
650
663
  };
651
664
  reactionEvent = await measureBenchmark(params.__benchmark, `${stagePrefix}.persistAssistantReactionMs`, async () => await ops.updateItem(reactionEvent.id, nextReactionEvent, { executionId, contextId: String(currentContext.id) }));
665
+ if (reactionResult.reactor?.kind) {
666
+ updatedContext = await measureBenchmark(params.__benchmark, `${stagePrefix}.persistReactorStateMs`, async () => await ops.updateContextReactor(activeContextSelector, {
667
+ kind: reactionResult.reactor.kind,
668
+ state: {
669
+ ...(reactionResult.reactor.state ?? {}),
670
+ updatedAt: nowIso(),
671
+ },
672
+ }));
673
+ }
652
674
  if (currentStepStream) {
653
675
  await closePersistedContextStepStream({
654
- env: params.env,
676
+ runtime: params.runtime,
655
677
  session: currentStepStream,
656
678
  });
657
679
  currentStepStream = null;
@@ -807,9 +829,8 @@ export class ContextEngine {
807
829
  const { createHook, createWebhook } = await import("workflow");
808
830
  const toolCallId = String(actionRequest.actionRef);
809
831
  const hookToken = toolApprovalHookToken({ executionId, toolCallId });
810
- const webhookToken = toolApprovalWebhookToken({ executionId, toolCallId });
811
832
  const hook = createHook({ token: hookToken });
812
- const webhook = createWebhook({ token: webhookToken });
833
+ const webhook = createWebhook();
813
834
  const approvalOrRequest = await Promise.race([
814
835
  hook.then((approval) => ({ source: "hook", approval })),
815
836
  webhook.then((request) => ({ source: "webhook", request })),
@@ -832,12 +853,18 @@ export class ContextEngine {
832
853
  }
833
854
  }
834
855
  const output = await toolDef.execute(actionInput, {
856
+ runtime: params.runtime,
857
+ env,
858
+ context: updatedContext,
859
+ contextIdentifier: activeContextSelector,
835
860
  toolCallId: actionRequest.actionRef,
836
861
  messages: messagesForModel,
837
862
  eventId: reactionEventId,
838
863
  executionId,
839
864
  triggerEventId,
840
865
  contextId: currentContext.id,
866
+ stepId: String(stepCreate.stepId),
867
+ iteration: iter,
841
868
  });
842
869
  return { actionRequest, success: true, output };
843
870
  }
@@ -894,7 +921,7 @@ export class ContextEngine {
894
921
  // IMPORTANT: we call this after tool results have been merged into the persisted `reactionEvent`,
895
922
  // so stories can inspect `reactionEvent.content.parts` deterministically.
896
923
  const continueLoop = await measureBenchmark(params.__benchmark, `${stagePrefix}.shouldContinueMs`, async () => await story.shouldContinue({
897
- env: params.env,
924
+ env,
898
925
  context: updatedContext,
899
926
  reactionEvent,
900
927
  assistantEvent: assistantEventEffective,
@@ -1033,7 +1060,7 @@ export class ContextEngine {
1033
1060
  if (currentStepStream) {
1034
1061
  try {
1035
1062
  await abortPersistedContextStepStream({
1036
- env: params.env,
1063
+ runtime: params.runtime,
1037
1064
  session: currentStepStream,
1038
1065
  reason: error instanceof Error ? error.message : String(error),
1039
1066
  });
@@ -0,0 +1,11 @@
1
+ import type { ConcreteDomain } from "@ekairos/domain";
2
+ import type { ExplicitRuntimeLike } from "@ekairos/domain/runtime";
3
+ import type { ContextEnvironment } from "./context.config.js";
4
+ import type { ContextStore } from "./context.store.js";
5
+ export type ContextRuntime<Env extends ContextEnvironment = ContextEnvironment> = ExplicitRuntimeLike<Env, any, any>;
6
+ export type ContextRuntimeServices = {
7
+ db: any;
8
+ store: ContextStore;
9
+ domain?: ConcreteDomain<any, any>;
10
+ };
11
+ export declare function getContextRuntimeServices(runtime: ContextRuntime<any>): Promise<ContextRuntimeServices>;
@@ -0,0 +1,21 @@
1
+ const storeByDb = new WeakMap();
2
+ export async function getContextRuntimeServices(runtime) {
3
+ const db = await runtime.db();
4
+ if (!db) {
5
+ throw new Error("Context runtime did not provide a database instance.");
6
+ }
7
+ let store = typeof db === "object" && db !== null ? storeByDb.get(db) : undefined;
8
+ if (!store) {
9
+ const { InstantStore } = await import("./stores/instant.store.js");
10
+ store = new InstantStore(db);
11
+ if (typeof db === "object" && db !== null) {
12
+ storeByDb.set(db, store);
13
+ }
14
+ }
15
+ const resolved = await runtime.resolve();
16
+ return {
17
+ db,
18
+ store,
19
+ domain: typeof resolved === "object" && resolved !== null ? resolved.meta?.()?.domain : undefined,
20
+ };
21
+ }
@@ -16,6 +16,10 @@ export type StoredContext<Context> = {
16
16
  createdAt: Date;
17
17
  updatedAt?: Date;
18
18
  content: Context | null;
19
+ reactor?: {
20
+ kind: string;
21
+ state?: Record<string, unknown> | null;
22
+ } | null;
19
23
  };
20
24
  export type ContextItem = {
21
25
  id: string;
@@ -57,6 +61,10 @@ export interface ContextStore {
57
61
  getOrCreateContext<C>(contextIdentifier: ContextIdentifier | null): Promise<StoredContext<C>>;
58
62
  getContext<C>(contextIdentifier: ContextIdentifier): Promise<StoredContext<C> | null>;
59
63
  updateContextContent<C>(contextIdentifier: ContextIdentifier, content: C): Promise<StoredContext<C>>;
64
+ updateContextReactor<C>(contextIdentifier: ContextIdentifier, reactor: {
65
+ kind: string;
66
+ state?: Record<string, unknown> | null;
67
+ }): Promise<StoredContext<C>>;
60
68
  updateContextStatus(contextIdentifier: ContextIdentifier, status: ContextStatus): Promise<void>;
61
69
  saveItem(contextIdentifier: ContextIdentifier, item: ContextItem): Promise<ContextItem>;
62
70
  updateItem(itemId: string, item: ContextItem): Promise<ContextItem>;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { context, createContext, createAiSdkReactor, createScriptedReactor, type CreateAiSdkReactorOptions, type CreateScriptedReactorOptions, type ScriptedReactorStep, type ContextConfig, type ContextInstance, type ContextOptions, type ContextStreamOptions, type ContextReactor, type ContextReactorParams, type ContextReactionResult, type ContextActionRequest, type ContextReactionLLM, ContextEngine, type RegistrableContextBuilder, type ContextReactParams, type ContextReactResult, type ContextDurableWorkflowPayload, type ContextDurableWorkflowFunction, type ContextModelInit, type ContextTool, runContextReactionDirect, } from "./context.js";
1
+ export { context, createContext, createAiSdkReactor, createScriptedReactor, type CreateAiSdkReactorOptions, type CreateScriptedReactorOptions, type ScriptedReactorStep, type ContextConfig, type ContextInstance, type ContextOptions, type ContextStreamOptions, type ContextReactor, type ContextReactorParams, type ContextReactionResult, type ContextActionRequest, type ContextReactionLLM, ContextEngine, type RegistrableContextBuilder, type ContextReactParams, type ContextReactResult, type ContextWorkflowRun, type ContextDurableWorkflowPayload, type ContextDurableWorkflowFunction, type ContextModelInit, type ContextTool, type ContextToolExecuteContext, runContextReactionDirect, } from "./context.js";
2
2
  export type { ContextStore, ContextIdentifier, StoredContext, ContextItem, ContextExecution, } from "./context.store.js";
3
3
  export type { WireDate, ContextMirrorContext, ContextMirrorExecution, ContextMirrorWrite, ContextMirrorRequest, } from "./mirror.js";
4
4
  export { registerContext, getContext, getContextFactory, hasContext, listContexts, type ContextKey, } from "./context.registry.js";
@@ -1,9 +1,11 @@
1
1
  import type { ContextEnvironment } from "../context.config.js";
2
+ import type { ContextRuntime } from "../context.runtime.js";
2
3
  import type { ContextModelInit } from "../context.engine.js";
3
4
  import type { ContextIdentifier, StoredContext, ContextItem } from "../context.store.js";
4
5
  import type { ContextReactor } from "./types.js";
5
6
  export type CreateAiSdkReactorOptions<Context = unknown, Env extends ContextEnvironment = ContextEnvironment, Config = unknown> = {
6
7
  resolveConfig?: (params: {
8
+ runtime: ContextRuntime<Env>;
7
9
  env: Env;
8
10
  context: StoredContext<Context>;
9
11
  contextIdentifier: ContextIdentifier;
@@ -16,6 +18,7 @@ export type CreateAiSdkReactorOptions<Context = unknown, Env extends ContextEnvi
16
18
  iteration: number;
17
19
  }) => Promise<Config> | Config;
18
20
  selectModel?: (params: {
21
+ runtime: ContextRuntime<Env>;
19
22
  env: Env;
20
23
  context: StoredContext<Context>;
21
24
  triggerEvent: ContextItem;
@@ -23,6 +26,7 @@ export type CreateAiSdkReactorOptions<Context = unknown, Env extends ContextEnvi
23
26
  config: Config;
24
27
  }) => Promise<ContextModelInit> | ContextModelInit;
25
28
  selectMaxModelSteps?: (params: {
29
+ runtime: ContextRuntime<Env>;
26
30
  env: Env;
27
31
  context: StoredContext<Context>;
28
32
  triggerEvent: ContextItem;