@ekairos/events 1.22.15-beta.development.0 → 1.22.16-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.
@@ -2,10 +2,10 @@ 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) {
@@ -144,6 +144,7 @@ async function createRuntimeOps(runtimeHandle, benchmark) {
144
144
  return { context, isNew: true };
145
145
  },
146
146
  updateContextContent: async (contextIdentifier, content) => await store.updateContextContent(contextIdentifier, content),
147
+ updateContextReactor: async (contextIdentifier, reactor) => await store.updateContextReactor(contextIdentifier, reactor),
147
148
  updateContextStatus: async (contextIdentifier, status) => await instrumentedDb.transact([
148
149
  instrumentedDb.tx.event_contexts[requireContextId(contextIdentifier)].update({
149
150
  status,
@@ -259,6 +260,7 @@ async function createWorkflowOps(runtime) {
259
260
  return {
260
261
  initializeContext: async (contextIdentifier, opts) => await initializeContext({ runtime, contextIdentifier, opts }),
261
262
  updateContextContent: async (contextIdentifier, content) => await updateContextContent({ runtime, contextIdentifier, content }),
263
+ updateContextReactor: async (contextIdentifier, reactor) => await updateContextReactor({ runtime, contextIdentifier, reactor }),
262
264
  updateContextStatus: async (contextIdentifier, status) => await updateContextStatus({ runtime, contextIdentifier, status }),
263
265
  saveTriggerAndCreateExecution: async ({ contextIdentifier, triggerEvent }) => await saveTriggerAndCreateExecution({ runtime, contextIdentifier, triggerEvent }),
264
266
  createContextStep: async ({ executionId, iteration }) => await createContextStep({ runtime, executionId, iteration }),
@@ -537,7 +539,7 @@ export class ContextEngine {
537
539
  const skillsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.skillsMs`, async () => await story.buildSkills(updatedContext, env));
538
540
  // IMPORTANT: step args must be serializable.
539
541
  // Match DurableAgent behavior: convert tool input schemas to plain JSON Schema in workflow context.
540
- const toolsForModel = toolsToModelTools(toolsAll);
542
+ const actionSpecs = actionsToActionSpecs(toolsAll);
541
543
  // Execute model reaction for this iteration using the stable reaction event id.
542
544
  //
543
545
  // IMPORTANT:
@@ -572,7 +574,7 @@ export class ContextEngine {
572
574
  status: "pending",
573
575
  }, { executionId, contextId: String(currentContext.id) });
574
576
  };
575
- const { assistantEvent, actionRequests, messagesForModel } = await measureBenchmark(params.__benchmark, `${stagePrefix}.reactorMs`, async () => await reactor({
577
+ const reactionResult = await measureBenchmark(params.__benchmark, `${stagePrefix}.reactorMs`, async () => await reactor({
576
578
  runtime: params.runtime,
577
579
  env,
578
580
  context: updatedContext,
@@ -581,7 +583,7 @@ export class ContextEngine {
581
583
  model: story.getModel(updatedContext, env),
582
584
  systemPrompt,
583
585
  actions: toolsAll,
584
- toolsForModel,
586
+ actionSpecs,
585
587
  skills: skillsAll,
586
588
  eventId: reactionEventId,
587
589
  executionId,
@@ -596,6 +598,7 @@ export class ContextEngine {
596
598
  writable,
597
599
  persistReactionParts,
598
600
  }));
601
+ const { assistantEvent, actionRequests, messagesForModel } = reactionResult;
599
602
  const reviewRequests = actionRequests.length > 0
600
603
  ? actionRequests.flatMap((actionRequest) => {
601
604
  const toolDef = toolsAll[actionRequest.actionName];
@@ -659,6 +662,15 @@ export class ContextEngine {
659
662
  status: "pending",
660
663
  };
661
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
+ }
662
674
  if (currentStepStream) {
663
675
  await closePersistedContextStepStream({
664
676
  runtime: params.runtime,
@@ -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>;
@@ -43,7 +43,7 @@ export function createAiSdkReactor(options) {
43
43
  contextIdentifier: params.contextIdentifier,
44
44
  model,
45
45
  system: params.systemPrompt,
46
- tools: params.toolsForModel,
46
+ tools: params.actionSpecs,
47
47
  eventId: params.eventId,
48
48
  iteration: params.iteration,
49
49
  maxSteps,
@@ -2,7 +2,7 @@ import type { ModelMessage, UIMessageChunk } from "ai";
2
2
  import type { ContextEnvironment } from "../context.config.js";
3
3
  import type { ContextModelInit } from "../context.engine.js";
4
4
  import type { ContextItem, ContextIdentifier } from "../context.store.js";
5
- import type { SerializableToolForModel } from "../tools-to-model-tools.js";
5
+ import { type SerializableActionSpec } from "../tools-to-model-tools.js";
6
6
  /**
7
7
  * AI SDK-backed reaction execution inside a single workflow step.
8
8
  *
@@ -19,7 +19,7 @@ export declare function executeAiSdkReaction(params: {
19
19
  contextIdentifier: ContextIdentifier;
20
20
  model: ContextModelInit;
21
21
  system: string;
22
- tools: Record<string, SerializableToolForModel>;
22
+ tools: Record<string, SerializableActionSpec>;
23
23
  eventId: string;
24
24
  iteration?: number;
25
25
  maxSteps: number;
@@ -2,6 +2,7 @@ import { getContextRuntimeServices } from "../context.runtime.js";
2
2
  import { OUTPUT_ITEM_TYPE } from "../context.events.js";
3
3
  import { createContextStepStreamChunk, encodeContextStepStreamChunk, } from "../context.step-stream.js";
4
4
  import { mapAiSdkChunkToContextEvent } from "./ai-sdk.chunk-map.js";
5
+ import { actionSpecToAiSdkTool, } from "../tools-to-model-tools.js";
5
6
  import { writeContextTraceEvents } from "../steps/trace.steps.js";
6
7
  async function readWorkflowMetadata() {
7
8
  try {
@@ -111,10 +112,7 @@ export async function executeAiSdkReaction(params) {
111
112
  })();
112
113
  const toolsForStreamText = {};
113
114
  for (const [name, t] of Object.entries(params.tools)) {
114
- toolsForStreamText[name] = {
115
- description: t?.description,
116
- inputSchema: jsonSchema(t.inputSchema),
117
- };
115
+ toolsForStreamText[name] = actionSpecToAiSdkTool(name, t, jsonSchema);
118
116
  }
119
117
  const startedAtMs = Date.now();
120
118
  const result = streamText({
@@ -4,7 +4,7 @@ import type { ContextRuntime } from "../context.runtime.js";
4
4
  import type { ContextModelInit } from "../context.engine.js";
5
5
  import type { ContextIdentifier, StoredContext, ContextItem } from "../context.store.js";
6
6
  import type { ContextSkillPackage } from "../context.skill.js";
7
- import type { SerializableToolForModel } from "../tools-to-model-tools.js";
7
+ import type { SerializableActionSpec } from "../tools-to-model-tools.js";
8
8
  export type ContextActionRequest = {
9
9
  actionRef: string;
10
10
  actionName: string;
@@ -27,6 +27,10 @@ export type ContextReactionResult = {
27
27
  actionRequests: ContextActionRequest[];
28
28
  messagesForModel: ModelMessage[];
29
29
  llm?: ContextReactionLLM;
30
+ reactor?: {
31
+ kind: string;
32
+ state?: Record<string, unknown> | null;
33
+ };
30
34
  };
31
35
  export type ContextReactorParams<Context = unknown, Env extends ContextEnvironment = ContextEnvironment> = {
32
36
  runtime: ContextRuntime<Env>;
@@ -37,7 +41,7 @@ export type ContextReactorParams<Context = unknown, Env extends ContextEnvironme
37
41
  model: ContextModelInit;
38
42
  systemPrompt: string;
39
43
  actions: Record<string, unknown>;
40
- toolsForModel: Record<string, SerializableToolForModel>;
44
+ actionSpecs: Record<string, SerializableActionSpec>;
41
45
  skills: ContextSkillPackage[];
42
46
  eventId: string;
43
47
  executionId: string;
package/dist/schema.js CHANGED
@@ -10,6 +10,7 @@ export const eventsDomain = domain("events")
10
10
  name: i.string().optional(),
11
11
  status: i.string().optional().indexed(), // open_idle | open_streaming | closed
12
12
  content: i.any().optional(),
13
+ reactor: i.json().optional(),
13
14
  }),
14
15
  event_items: i.entity({
15
16
  channel: i.string().indexed(),
@@ -1,6 +1,6 @@
1
1
  import type { ModelMessage } from "ai";
2
2
  import type { ContextItem } from "../context.store.js";
3
- import type { SerializableToolForModel } from "../tools-to-model-tools.js";
3
+ import { type SerializableActionSpec } from "../tools-to-model-tools.js";
4
4
  import type { ContextModelInit } from "../context.engine.js";
5
5
  /**
6
6
  * Runs a single LLM streaming step as a Workflow step.
@@ -13,7 +13,7 @@ export declare function doContextStreamStep(params: {
13
13
  model: ContextModelInit;
14
14
  system: string;
15
15
  messages: ModelMessage[];
16
- tools: Record<string, SerializableToolForModel>;
16
+ tools: Record<string, SerializableActionSpec>;
17
17
  eventId: string;
18
18
  maxSteps: number;
19
19
  /**
@@ -1,4 +1,5 @@
1
1
  import { OUTPUT_ITEM_TYPE } from "../context.events.js";
2
+ import { actionSpecToAiSdkTool, } from "../tools-to-model-tools.js";
2
3
  /**
3
4
  * Runs a single LLM streaming step as a Workflow step.
4
5
  *
@@ -28,10 +29,7 @@ export async function doContextStreamStep(params) {
28
29
  // `jsonSchema(...)` so the AI SDK does not attempt Zod conversion at runtime.
29
30
  const toolsForStreamText = {};
30
31
  for (const [name, t] of Object.entries(params.tools)) {
31
- toolsForStreamText[name] = {
32
- description: t?.description,
33
- inputSchema: jsonSchema(t.inputSchema),
34
- };
32
+ toolsForStreamText[name] = actionSpecToAiSdkTool(name, t, jsonSchema);
35
33
  }
36
34
  const result = streamText({
37
35
  model: resolvedModel,
@@ -28,6 +28,13 @@ export declare function updateContextContent<C>(params: RuntimeParams & {
28
28
  contextIdentifier: ContextIdentifier;
29
29
  content: C;
30
30
  }): Promise<StoredContext<C>>;
31
+ export declare function updateContextReactor<C>(params: RuntimeParams & {
32
+ contextIdentifier: ContextIdentifier;
33
+ reactor: {
34
+ kind: string;
35
+ state?: Record<string, unknown> | null;
36
+ };
37
+ }): Promise<StoredContext<C>>;
31
38
  export declare function updateContextStatus(params: RuntimeParams & {
32
39
  contextIdentifier: ContextIdentifier;
33
40
  status: ContextStatus;
@@ -142,6 +142,11 @@ export async function updateContextContent(params) {
142
142
  const { runtime } = await getRuntimeAndEnv(params);
143
143
  return await runtime.store.updateContextContent(params.contextIdentifier, params.content);
144
144
  }
145
+ export async function updateContextReactor(params) {
146
+ "use step";
147
+ const { runtime } = await getRuntimeAndEnv(params);
148
+ return await runtime.store.updateContextReactor(params.contextIdentifier, params.reactor);
149
+ }
145
150
  export async function updateContextStatus(params) {
146
151
  "use step";
147
152
  const { runtime } = await getRuntimeAndEnv(params);
@@ -14,6 +14,10 @@ export declare class InstantStore implements ContextStore {
14
14
  getOrCreateContext<C>(contextIdentifier: ContextIdentifier | null): Promise<StoredContext<C>>;
15
15
  getContext<C>(contextIdentifier: ContextIdentifier): Promise<StoredContext<C> | null>;
16
16
  updateContextContent<C>(contextIdentifier: ContextIdentifier, content: C): Promise<StoredContext<C>>;
17
+ updateContextReactor<C>(contextIdentifier: ContextIdentifier, reactor: {
18
+ kind: string;
19
+ state?: Record<string, unknown> | null;
20
+ }): Promise<StoredContext<C>>;
17
21
  updateContextStatus(contextIdentifier: ContextIdentifier, status: ContextStatus): Promise<void>;
18
22
  private resolveContext;
19
23
  saveItem(contextIdentifier: ContextIdentifier, event: ContextItem): Promise<ContextItem>;
@@ -118,6 +118,9 @@ export class InstantStore {
118
118
  ? new Date(row.updatedAt)
119
119
  : undefined,
120
120
  content: row?.content ?? null,
121
+ reactor: row?.reactor && typeof row.reactor === "object"
122
+ ? row.reactor
123
+ : null,
121
124
  };
122
125
  }
123
126
  async ensureContextKey(context, expectedKey) {
@@ -144,6 +147,7 @@ export class InstantStore {
144
147
  key,
145
148
  status: "open_idle",
146
149
  content: {},
150
+ reactor: undefined,
147
151
  }),
148
152
  ]);
149
153
  const context = await this.getContext({ id: contextId });
@@ -203,6 +207,21 @@ export class InstantStore {
203
207
  throw new Error("InstantStore: context not found after update");
204
208
  return updated;
205
209
  }
210
+ async updateContextReactor(contextIdentifier, reactor) {
211
+ const context = await this.getContext(contextIdentifier);
212
+ if (!context?.id)
213
+ throw new Error("InstantStore: context not found");
214
+ await this.db.transact([
215
+ this.db.tx.event_contexts[context.id].update({
216
+ reactor: reactor,
217
+ updatedAt: new Date(),
218
+ }),
219
+ ]);
220
+ const updated = await this.getContext({ id: context.id });
221
+ if (!updated)
222
+ throw new Error("InstantStore: context not found after reactor update");
223
+ return updated;
224
+ }
206
225
  async updateContextStatus(contextIdentifier, status) {
207
226
  const context = await this.getContext(contextIdentifier);
208
227
  if (!context?.id)
@@ -6,14 +6,48 @@ import { type Tool } from "ai";
6
6
  * - Keep Zod/function values out of step arguments
7
7
  * - Convert tool input schemas to plain JSON Schema in workflow context
8
8
  */
9
- export type SerializableToolForModel = {
9
+ export type SerializableFunctionActionSpec = {
10
+ type?: "function";
10
11
  description?: string;
11
12
  inputSchema: unknown;
13
+ providerOptions?: unknown;
12
14
  };
15
+ export type SerializableProviderDefinedActionSpec = {
16
+ type: "provider-defined";
17
+ id: string;
18
+ name?: string;
19
+ args?: Record<string, unknown>;
20
+ };
21
+ export type SerializableActionSpec = SerializableFunctionActionSpec | SerializableProviderDefinedActionSpec;
22
+ /**
23
+ * @deprecated Use SerializableActionSpec.
24
+ */
25
+ export type SerializableToolForModel = SerializableActionSpec;
13
26
  /**
14
27
  * Convert AI SDK tools to a serializable representation that can be passed to `"use-step"` functions.
15
28
  *
16
29
  * This matches DurableAgent's internal `toolsToModelTools` behavior:
17
30
  * `inputSchema: asSchema(tool.inputSchema).jsonSchema`
18
31
  */
19
- export declare function toolsToModelTools(tools: Record<string, Tool>): Record<string, SerializableToolForModel>;
32
+ export declare function actionsToActionSpecs(tools: Record<string, Tool>): Record<string, SerializableActionSpec>;
33
+ export declare function actionSpecToAiSdkTool(name: string, spec: SerializableActionSpec, wrapJsonSchema: (schema: unknown) => unknown): {
34
+ type: "provider-defined";
35
+ id: string;
36
+ name: string;
37
+ args: Record<string, unknown>;
38
+ description?: undefined;
39
+ inputSchema?: undefined;
40
+ providerOptions?: undefined;
41
+ } | {
42
+ type: "function";
43
+ description: string | undefined;
44
+ inputSchema: unknown;
45
+ providerOptions: unknown;
46
+ id?: undefined;
47
+ name?: undefined;
48
+ args?: undefined;
49
+ };
50
+ /**
51
+ * @deprecated Use actionsToActionSpecs.
52
+ */
53
+ export declare const toolsToModelTools: typeof actionsToActionSpecs;
@@ -1,21 +1,59 @@
1
1
  import { asSchema } from "ai";
2
+ function isProviderDefinedTool(tool) {
3
+ return (Boolean(tool) &&
4
+ typeof tool === "object" &&
5
+ tool.type === "provider-defined" &&
6
+ typeof tool.id === "string" &&
7
+ tool.id.trim().length > 0);
8
+ }
2
9
  /**
3
10
  * Convert AI SDK tools to a serializable representation that can be passed to `"use-step"` functions.
4
11
  *
5
12
  * This matches DurableAgent's internal `toolsToModelTools` behavior:
6
13
  * `inputSchema: asSchema(tool.inputSchema).jsonSchema`
7
14
  */
8
- export function toolsToModelTools(tools) {
15
+ export function actionsToActionSpecs(tools) {
9
16
  const out = {};
10
17
  for (const [name, tool] of Object.entries(tools)) {
18
+ if (isProviderDefinedTool(tool)) {
19
+ out[name] = {
20
+ type: "provider-defined",
21
+ id: tool.id,
22
+ name: tool.name,
23
+ args: tool.args,
24
+ };
25
+ continue;
26
+ }
11
27
  const inputSchema = tool?.inputSchema;
12
28
  if (!inputSchema) {
13
29
  throw new Error(`Context: tool "${name}" is missing inputSchema (required for model tool calls)`);
14
30
  }
15
31
  out[name] = {
32
+ type: "function",
16
33
  description: tool?.description,
17
34
  inputSchema: asSchema(inputSchema).jsonSchema,
35
+ providerOptions: tool?.providerOptions,
18
36
  };
19
37
  }
20
38
  return out;
21
39
  }
40
+ export function actionSpecToAiSdkTool(name, spec, wrapJsonSchema) {
41
+ if (spec.type === "provider-defined") {
42
+ return {
43
+ type: "provider-defined",
44
+ id: spec.id,
45
+ name: spec.name ?? name,
46
+ args: spec.args ?? {},
47
+ };
48
+ }
49
+ return {
50
+ type: "function",
51
+ description: spec.description,
52
+ inputSchema: wrapJsonSchema(spec.inputSchema),
53
+ providerOptions: spec.providerOptions,
54
+ };
55
+ }
56
+ /**
57
+ * @deprecated Use actionsToActionSpecs.
58
+ */
59
+ export const toolsToModelTools = actionsToActionSpecs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekairos/events",
3
- "version": "1.22.15-beta.development.0",
3
+ "version": "1.22.16-beta.development.0",
4
4
  "description": "Ekairos Events - Context-first workflow runtime",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -117,7 +117,7 @@
117
117
  },
118
118
  "dependencies": {
119
119
  "@ai-sdk/openai": "^2.0.52",
120
- "@ekairos/domain": "^1.22.15-beta.development.0",
120
+ "@ekairos/domain": "^1.22.16-beta.development.0",
121
121
  "@instantdb/admin": "0.22.158",
122
122
  "@instantdb/core": "0.22.142",
123
123
  "@vercel/mcp-adapter": "^1.0.0",