@ekairos/events 1.22.4-beta.feature-events-package-refactor.0 → 1.22.5-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 +59 -25
- package/dist/context.builder.d.ts +4 -0
- package/dist/context.builder.js +9 -0
- package/dist/context.d.ts +2 -1
- package/dist/context.engine.d.ts +26 -3
- package/dist/context.engine.js +115 -43
- package/dist/context.events.d.ts +20 -0
- package/dist/context.events.js +341 -7
- package/dist/context.parts.d.ts +241 -0
- package/dist/context.parts.js +360 -0
- package/dist/context.runtime.d.ts +11 -0
- package/dist/context.runtime.js +21 -0
- package/dist/context.skill.d.ts +9 -0
- package/dist/context.skill.js +1 -0
- package/dist/context.step-stream.d.ts +26 -0
- package/dist/context.step-stream.js +59 -0
- package/dist/context.store.d.ts +5 -0
- package/dist/context.toolcalls.js +55 -11
- package/dist/index.d.ts +7 -2
- package/dist/index.js +3 -1
- package/dist/reactors/ai-sdk.reactor.d.ts +4 -0
- package/dist/reactors/ai-sdk.reactor.js +7 -2
- package/dist/{steps/reaction.steps.d.ts → reactors/ai-sdk.step.d.ts} +11 -6
- package/dist/{steps/reaction.steps.js → reactors/ai-sdk.step.js} +102 -82
- package/dist/reactors/types.d.ts +5 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +1 -0
- package/dist/schema.js +24 -2
- package/dist/steps/store.steps.d.ts +55 -22
- package/dist/steps/store.steps.js +71 -78
- package/dist/steps/stream.steps.d.ts +76 -0
- package/dist/steps/stream.steps.js +239 -2
- package/dist/steps/trace.steps.d.ts +2 -0
- package/dist/steps/trace.steps.js +5 -2
- package/dist/stores/instant.store.d.ts +2 -0
- package/dist/stores/instant.store.js +100 -5
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -2,53 +2,87 @@
|
|
|
2
2
|
|
|
3
3
|
Context-first durable execution runtime for Ekairos.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## What this package does
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
- `
|
|
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
|
-
- `
|
|
21
|
-
- `event_trace_runs`
|
|
22
|
-
- `event_trace_spans`
|
|
23
|
-
|
|
24
|
-
The aggregate is `context`. Executions, steps, parts, and items are scoped to a context.
|
|
30
|
+
- `event_trace_*`
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
pnpm add @ekairos/events
|
|
30
|
-
```
|
|
32
|
+
`event_parts` is the source of truth for replay.
|
|
31
33
|
|
|
32
34
|
## Example
|
|
33
35
|
|
|
34
36
|
```ts
|
|
35
|
-
import { createContext
|
|
37
|
+
import { createContext } from "@ekairos/events";
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export const supportContext = createContext<Env>("support.agent")
|
|
39
|
+
const supportContext = createContext<{ orgId: string }>("support.agent")
|
|
40
40
|
.context((stored, env) => ({
|
|
41
|
-
orgId: env.orgId,
|
|
42
41
|
...stored.content,
|
|
42
|
+
orgId: env.orgId,
|
|
43
43
|
}))
|
|
44
44
|
.narrative(() => "You are a precise assistant.")
|
|
45
45
|
.actions(() => ({}))
|
|
46
|
-
.reactor(createAiSdkReactor())
|
|
47
46
|
.build();
|
|
48
47
|
```
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
Run directly:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
await supportContext.react(triggerEvent, {
|
|
53
|
+
runtime,
|
|
54
|
+
context: { key: "support:org_123" },
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Run durably:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const shell = await supportContext.react(triggerEvent, {
|
|
62
|
+
runtime,
|
|
63
|
+
context: { key: "support:org_123" },
|
|
64
|
+
durable: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const final = await shell.run?.returnValue;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Tool execution model
|
|
71
|
+
|
|
72
|
+
Context tools now receive runtime-aware execution context.
|
|
73
|
+
That lets a tool do this inside `"use step"`:
|
|
51
74
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Tests
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pnpm --filter @ekairos/events test
|
|
87
|
+
pnpm --filter @ekairos/events test:workflow
|
|
88
|
+
```
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContextEnvironment } from "./context.config.js";
|
|
2
|
+
import type { ContextSkillPackage } from "./context.skill.js";
|
|
2
3
|
import { ContextEngine, type ContextModelInit, type ContextOptions, type ContextTool, type ShouldContinue, type ContextShouldContinueArgs, type ContextReactParams } from "./context.engine.js";
|
|
3
4
|
import type { ContextReactor } from "./context.reactor.js";
|
|
4
5
|
import type { ContextItem, StoredContext } from "./context.store.js";
|
|
@@ -7,6 +8,7 @@ export interface ContextConfig<Context, Env extends ContextEnvironment = Context
|
|
|
7
8
|
context: (context: StoredContext<Context>, env: Env) => Promise<Context> | Context;
|
|
8
9
|
expandEvents?: (events: ContextItem[], context: StoredContext<Context>, env: Env) => Promise<ContextItem[]> | ContextItem[];
|
|
9
10
|
narrative: (context: StoredContext<Context>, env: Env) => Promise<string> | string;
|
|
11
|
+
skills?: (context: StoredContext<Context>, env: Env) => Promise<ContextSkillPackage[]> | ContextSkillPackage[];
|
|
10
12
|
actions: (context: StoredContext<Context>, env: Env) => Promise<Record<string, ContextTool>> | Record<string, ContextTool>;
|
|
11
13
|
/**
|
|
12
14
|
* @deprecated Use `actions()` instead.
|
|
@@ -25,6 +27,7 @@ export declare function context<Context, Env extends ContextEnvironment = Contex
|
|
|
25
27
|
type AnyContextInitializer<Env extends ContextEnvironment> = (context: StoredContext<any>, env: Env) => Promise<any> | any;
|
|
26
28
|
type InferContextFromInitializer<I extends AnyContextInitializer<any>> = Awaited<ReturnType<I>>;
|
|
27
29
|
type BuilderSystemPrompt<Context, Env extends ContextEnvironment> = (context: StoredContext<Context>, env: Env) => Promise<string> | string;
|
|
30
|
+
type BuilderSkills<Context, Env extends ContextEnvironment> = (context: StoredContext<Context>, env: Env) => Promise<ContextSkillPackage[]> | ContextSkillPackage[];
|
|
28
31
|
type BuilderTools<Context, Env extends ContextEnvironment> = (context: StoredContext<Context>, env: Env) => Promise<Record<string, ContextTool>> | Record<string, ContextTool>;
|
|
29
32
|
type BuilderExpandEvents<Context, Env extends ContextEnvironment> = (events: ContextItem[], context: StoredContext<Context>, env: Env) => Promise<ContextItem[]> | ContextItem[];
|
|
30
33
|
type BuilderShouldContinue<Context, Env extends ContextEnvironment> = (args: ContextShouldContinueArgs<Context, Env>) => Promise<ShouldContinue> | ShouldContinue;
|
|
@@ -38,6 +41,7 @@ type FluentContextBuilder<Context, Env extends ContextEnvironment> = {
|
|
|
38
41
|
expandEvents(fn: BuilderExpandEvents<Context, Env>): FluentContextBuilder<Context, Env>;
|
|
39
42
|
narrative(fn: BuilderSystemPrompt<Context, Env>): FluentContextBuilder<Context, Env>;
|
|
40
43
|
system(fn: BuilderSystemPrompt<Context, Env>): FluentContextBuilder<Context, Env>;
|
|
44
|
+
skills(fn: BuilderSkills<Context, Env>): FluentContextBuilder<Context, Env>;
|
|
41
45
|
actions(fn: BuilderTools<Context, Env>): FluentContextBuilder<Context, Env>;
|
|
42
46
|
tools(fn: BuilderTools<Context, Env>): FluentContextBuilder<Context, Env>;
|
|
43
47
|
model(model: BuilderModel<Context, Env>): FluentContextBuilder<Context, Env>;
|
package/dist/context.builder.js
CHANGED
|
@@ -22,6 +22,11 @@ export function context(config) {
|
|
|
22
22
|
return config.narrative(contextValue, env);
|
|
23
23
|
throw new Error("Context config is missing narrative()");
|
|
24
24
|
}
|
|
25
|
+
async buildSkills(contextValue, env) {
|
|
26
|
+
if (config.skills)
|
|
27
|
+
return config.skills(contextValue, env);
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
25
30
|
async buildTools(contextValue, env) {
|
|
26
31
|
if (config.actions)
|
|
27
32
|
return config.actions(contextValue, env);
|
|
@@ -84,6 +89,10 @@ export function createContext(key) {
|
|
|
84
89
|
fluentState.narrative = system;
|
|
85
90
|
return builder;
|
|
86
91
|
},
|
|
92
|
+
skills(skillsFactory) {
|
|
93
|
+
fluentState.skills = skillsFactory;
|
|
94
|
+
return builder;
|
|
95
|
+
},
|
|
87
96
|
actions(actionsFactory) {
|
|
88
97
|
fluentState.actions = actionsFactory;
|
|
89
98
|
return builder;
|
package/dist/context.d.ts
CHANGED
|
@@ -1,3 +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
|
+
export type { ContextSkillPackage, ContextSkillPackageFile, } from "./context.skill.js";
|
package/dist/context.engine.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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";
|
|
5
|
+
import type { ContextSkillPackage } from "./context.skill.js";
|
|
4
6
|
import { type ContextReactor } from "./context.reactor.js";
|
|
5
7
|
import { getClientResumeHookUrl, toolApprovalHookToken, toolApprovalWebhookToken } from "./context.hooks.js";
|
|
6
8
|
export interface ContextOptions<Context = any, Env extends ContextEnvironment = ContextEnvironment> {
|
|
@@ -67,7 +69,7 @@ export interface ContextStreamOptions {
|
|
|
67
69
|
*/
|
|
68
70
|
export type ContextModelInit = string | (() => Promise<any>);
|
|
69
71
|
export type ContextReactParams<Env extends ContextEnvironment = ContextEnvironment> = {
|
|
70
|
-
|
|
72
|
+
runtime: ContextRuntime<Env>;
|
|
71
73
|
/**
|
|
72
74
|
* Context selector (exclusive: `{ id }` OR `{ key }`).
|
|
73
75
|
* - `{ id }` resolves a concrete context id.
|
|
@@ -94,16 +96,36 @@ export type ContextReactResult<Context = any> = {
|
|
|
94
96
|
trigger: ContextItem;
|
|
95
97
|
reaction: ContextItem;
|
|
96
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>>;
|
|
97
105
|
};
|
|
98
106
|
export type ContextDurableWorkflowPayload<Env extends ContextEnvironment = ContextEnvironment> = {
|
|
99
107
|
contextKey: string;
|
|
100
|
-
|
|
108
|
+
runtime: ContextRuntime<Env>;
|
|
101
109
|
context?: ContextIdentifier | null;
|
|
102
110
|
triggerEvent: ContextItem;
|
|
103
111
|
options?: Omit<ContextStreamOptions, "writable">;
|
|
104
112
|
bootstrap: NonNullable<ContextReactParams<Env>["__bootstrap"]>;
|
|
105
113
|
};
|
|
106
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
|
+
};
|
|
107
129
|
export { toolApprovalHookToken, toolApprovalWebhookToken, getClientResumeHookUrl };
|
|
108
130
|
/**
|
|
109
131
|
* Context-level tool type.
|
|
@@ -168,6 +190,7 @@ export declare abstract class ContextEngine<Context, Env extends ContextEnvironm
|
|
|
168
190
|
protected abstract initialize(context: StoredContext<Context>, env: Env): Promise<Context> | Context;
|
|
169
191
|
protected abstract buildSystemPrompt(context: StoredContext<Context>, env: Env): Promise<string> | string;
|
|
170
192
|
protected abstract buildTools(context: StoredContext<Context>, env: Env): Promise<Record<string, ContextTool>> | Record<string, ContextTool>;
|
|
193
|
+
protected buildSkills(_context: StoredContext<Context>, _env: Env): Promise<ContextSkillPackage[]>;
|
|
171
194
|
/**
|
|
172
195
|
* First-class event expansion stage (runs on every iteration of the durable loop).
|
|
173
196
|
*
|
package/dist/context.engine.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
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
|
+
import { isContextPartEnvelope, normalizePartsForPersistence, } from "./context.parts.js";
|
|
4
5
|
import { toolsToModelTools } from "./tools-to-model-tools.js";
|
|
5
6
|
import { createAiSdkReactor, } from "./context.reactor.js";
|
|
6
|
-
import { closeContextStream, } from "./steps/stream.steps.js";
|
|
7
|
+
import { abortPersistedContextStepStream, closePersistedContextStepStream, createPersistedContextStepStream, closeContextStream, } from "./steps/stream.steps.js";
|
|
7
8
|
import { completeExecution, createContextStep, initializeContext, saveTriggerAndCreateExecution, saveContextPartsStep, updateContextContent, updateContextStatus, updateItem, updateContextStep, } from "./steps/store.steps.js";
|
|
8
9
|
import { getClientResumeHookUrl, toolApprovalHookToken, toolApprovalWebhookToken, } from "./context.hooks.js";
|
|
9
10
|
import { getContextDurableWorkflow } from "./context.durable.js";
|
|
@@ -22,6 +23,20 @@ function clipPreview(value, max = 240) {
|
|
|
22
23
|
function summarizePartPreview(part) {
|
|
23
24
|
if (!part || typeof part !== "object")
|
|
24
25
|
return {};
|
|
26
|
+
if (isContextPartEnvelope(part)) {
|
|
27
|
+
const preview = part.content[0]?.type === "text"
|
|
28
|
+
? part.content[0].text
|
|
29
|
+
: JSON.stringify(part.content[0] ?? part);
|
|
30
|
+
const state = "state" in part && typeof part.state === "string" ? part.state : undefined;
|
|
31
|
+
const toolCallId = "toolCallId" in part && typeof part.toolCallId === "string"
|
|
32
|
+
? part.toolCallId
|
|
33
|
+
: undefined;
|
|
34
|
+
return {
|
|
35
|
+
partPreview: preview ? clipPreview(preview) : undefined,
|
|
36
|
+
partState: state,
|
|
37
|
+
partToolCallId: toolCallId,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
25
40
|
const row = part;
|
|
26
41
|
const partType = typeof row.type === "string" ? row.type : "";
|
|
27
42
|
const partState = typeof row.state === "string" ? row.state : undefined;
|
|
@@ -74,9 +89,8 @@ async function readActiveWorkflowRunId() {
|
|
|
74
89
|
return null;
|
|
75
90
|
}
|
|
76
91
|
}
|
|
77
|
-
async function createRuntimeOps(
|
|
78
|
-
const
|
|
79
|
-
const runtime = await getContextRuntime(env);
|
|
92
|
+
async function createRuntimeOps(runtimeHandle, benchmark) {
|
|
93
|
+
const runtime = await getContextRuntimeServices(runtimeHandle);
|
|
80
94
|
const { db } = runtime;
|
|
81
95
|
const { InstantStore } = await import("./stores/instant.store.js");
|
|
82
96
|
const requireContextId = (contextIdentifier) => {
|
|
@@ -240,33 +254,36 @@ async function createRuntimeOps(env, benchmark) {
|
|
|
240
254
|
},
|
|
241
255
|
};
|
|
242
256
|
}
|
|
243
|
-
async function createWorkflowOps(
|
|
257
|
+
async function createWorkflowOps(runtime) {
|
|
258
|
+
const env = runtime.env;
|
|
244
259
|
return {
|
|
245
|
-
initializeContext: async (contextIdentifier, opts) => await initializeContext(
|
|
246
|
-
updateContextContent: async (contextIdentifier, content) => await updateContextContent(
|
|
247
|
-
updateContextStatus: async (contextIdentifier, status) => await updateContextStatus(
|
|
248
|
-
saveTriggerAndCreateExecution: async ({ contextIdentifier, triggerEvent }) => await saveTriggerAndCreateExecution({
|
|
249
|
-
createContextStep: async ({ executionId, iteration }) => await createContextStep({
|
|
250
|
-
updateContextStep: async (params) => await updateContextStep({
|
|
251
|
-
saveContextPartsStep: async (params) => await saveContextPartsStep({
|
|
252
|
-
updateItem: async (itemId, item, opts) => await updateItem(
|
|
253
|
-
completeExecution: async (contextIdentifier, executionId, status) => await completeExecution(
|
|
260
|
+
initializeContext: async (contextIdentifier, opts) => await initializeContext({ runtime, contextIdentifier, opts }),
|
|
261
|
+
updateContextContent: async (contextIdentifier, content) => await updateContextContent({ runtime, contextIdentifier, content }),
|
|
262
|
+
updateContextStatus: async (contextIdentifier, status) => await updateContextStatus({ runtime, contextIdentifier, status }),
|
|
263
|
+
saveTriggerAndCreateExecution: async ({ contextIdentifier, triggerEvent }) => await saveTriggerAndCreateExecution({ runtime, contextIdentifier, triggerEvent }),
|
|
264
|
+
createContextStep: async ({ executionId, iteration }) => await createContextStep({ runtime, executionId, iteration }),
|
|
265
|
+
updateContextStep: async (params) => await updateContextStep({ runtime, ...params }),
|
|
266
|
+
saveContextPartsStep: async (params) => await saveContextPartsStep({ runtime, ...params }),
|
|
267
|
+
updateItem: async (itemId, item, opts) => await updateItem({ runtime, eventId: itemId, event: item, opts }),
|
|
268
|
+
completeExecution: async (contextIdentifier, executionId, status) => await completeExecution({ runtime, contextIdentifier, executionId, status }),
|
|
254
269
|
};
|
|
255
270
|
}
|
|
256
|
-
async function getContextEngineOps(
|
|
271
|
+
async function getContextEngineOps(runtime, benchmark) {
|
|
272
|
+
const env = runtime.env;
|
|
257
273
|
const workflowRunId = await readActiveWorkflowRunId();
|
|
258
274
|
if (workflowRunId) {
|
|
259
|
-
|
|
260
|
-
return await createWorkflowOps(env);
|
|
275
|
+
return await createWorkflowOps(runtime);
|
|
261
276
|
}
|
|
262
|
-
|
|
263
|
-
return await createRuntimeOps(env, benchmark);
|
|
277
|
+
return await createRuntimeOps(runtime, benchmark);
|
|
264
278
|
}
|
|
265
279
|
export class ContextEngine {
|
|
266
280
|
constructor(opts = {}, reactor) {
|
|
267
281
|
this.opts = opts;
|
|
268
282
|
this.reactor = reactor ?? createAiSdkReactor();
|
|
269
283
|
}
|
|
284
|
+
async buildSkills(_context, _env) {
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
270
287
|
/**
|
|
271
288
|
* First-class event expansion stage (runs on every iteration of the durable loop).
|
|
272
289
|
*
|
|
@@ -311,13 +328,14 @@ export class ContextEngine {
|
|
|
311
328
|
return await ContextEngine.runDirect(this, triggerEvent, params);
|
|
312
329
|
}
|
|
313
330
|
static async prepareExecutionShell(story, triggerEvent, params) {
|
|
314
|
-
const
|
|
331
|
+
const env = params.runtime.env;
|
|
332
|
+
const ops = await measureBenchmark(params.__benchmark, "react.resolveOpsMs", async () => await getContextEngineOps(params.runtime, params.__benchmark));
|
|
315
333
|
const silent = params.options?.silent ?? false;
|
|
316
334
|
const ctxResult = await measureBenchmark(params.__benchmark, "react.initializeContextMs", async () => await ops.initializeContext(params.context ?? null, { silent }));
|
|
317
335
|
let currentContext = ctxResult.context;
|
|
318
336
|
const contextSelector = { id: String(currentContext.id) };
|
|
319
337
|
if (ctxResult.isNew) {
|
|
320
|
-
await story.opts.onContextCreated?.({ env
|
|
338
|
+
await story.opts.onContextCreated?.({ env, context: currentContext });
|
|
321
339
|
}
|
|
322
340
|
if (currentContext.status === "closed") {
|
|
323
341
|
await measureBenchmark(params.__benchmark, "react.reopenClosedContextMs", async () => await ops.updateContextStatus(contextSelector, "open_idle"));
|
|
@@ -337,6 +355,7 @@ export class ContextEngine {
|
|
|
337
355
|
};
|
|
338
356
|
}
|
|
339
357
|
static async startDurable(story, triggerEvent, params) {
|
|
358
|
+
const env = params.runtime.env;
|
|
340
359
|
if (params.options?.writable) {
|
|
341
360
|
throw new Error("ContextEngine.react: durable runs manage their own workflow stream");
|
|
342
361
|
}
|
|
@@ -349,14 +368,15 @@ export class ContextEngine {
|
|
|
349
368
|
throw new Error("ContextEngine.react: durable workflow is not configured. Call configureContextDurableWorkflow(...) in runtime bootstrap.");
|
|
350
369
|
}
|
|
351
370
|
const shell = await ContextEngine.prepareExecutionShell(story, triggerEvent, params);
|
|
371
|
+
let run;
|
|
352
372
|
try {
|
|
353
373
|
const [{ start }] = await Promise.all([
|
|
354
374
|
import("workflow/api"),
|
|
355
375
|
]);
|
|
356
|
-
const
|
|
376
|
+
const startedRun = await start(workflow, [
|
|
357
377
|
{
|
|
358
378
|
contextKey,
|
|
359
|
-
|
|
379
|
+
runtime: params.runtime,
|
|
360
380
|
context: params.context ?? null,
|
|
361
381
|
triggerEvent,
|
|
362
382
|
options: {
|
|
@@ -374,16 +394,21 @@ export class ContextEngine {
|
|
|
374
394
|
},
|
|
375
395
|
},
|
|
376
396
|
]);
|
|
377
|
-
|
|
397
|
+
run = {
|
|
398
|
+
runId: String(startedRun.runId),
|
|
399
|
+
status: startedRun.status,
|
|
400
|
+
returnValue: startedRun.returnValue,
|
|
401
|
+
};
|
|
402
|
+
const runtime = await createRuntimeOps(params.runtime);
|
|
378
403
|
await runtime.db.transact([
|
|
379
404
|
runtime.db.tx.event_executions[shell.execution.id].update({
|
|
380
|
-
workflowRunId:
|
|
405
|
+
workflowRunId: startedRun.runId,
|
|
381
406
|
updatedAt: new Date(),
|
|
382
407
|
}),
|
|
383
408
|
]);
|
|
384
409
|
}
|
|
385
410
|
catch (error) {
|
|
386
|
-
const ops = await getContextEngineOps(params.
|
|
411
|
+
const ops = await getContextEngineOps(params.runtime, params.__benchmark);
|
|
387
412
|
await ops.completeExecution(shell.contextSelector, shell.execution.id, "failed").catch(() => null);
|
|
388
413
|
throw error;
|
|
389
414
|
}
|
|
@@ -392,10 +417,12 @@ export class ContextEngine {
|
|
|
392
417
|
trigger: shell.trigger,
|
|
393
418
|
reaction: shell.reaction,
|
|
394
419
|
execution: shell.execution,
|
|
420
|
+
run,
|
|
395
421
|
};
|
|
396
422
|
}
|
|
397
423
|
static async runDirect(story, triggerEvent, params) {
|
|
398
|
-
const
|
|
424
|
+
const env = params.runtime.env;
|
|
425
|
+
const ops = await measureBenchmark(params.__benchmark, "react.resolveOpsMs", async () => await getContextEngineOps(params.runtime, params.__benchmark));
|
|
399
426
|
const maxIterations = params.options?.maxIterations ?? 20;
|
|
400
427
|
const maxModelSteps = params.options?.maxModelSteps ?? 1;
|
|
401
428
|
const preventClose = params.options?.preventClose ?? false;
|
|
@@ -422,6 +449,7 @@ export class ContextEngine {
|
|
|
422
449
|
const executionId = execution.id;
|
|
423
450
|
let updatedContext = { ...currentContext, status: "open_streaming" };
|
|
424
451
|
let currentStepId = null;
|
|
452
|
+
let currentStepStream = null;
|
|
425
453
|
const failExecution = async () => {
|
|
426
454
|
try {
|
|
427
455
|
await ops.completeExecution(activeContextSelector, executionId, "failed");
|
|
@@ -468,6 +496,11 @@ export class ContextEngine {
|
|
|
468
496
|
iteration: iter,
|
|
469
497
|
}));
|
|
470
498
|
currentStepId = stepCreate.stepId;
|
|
499
|
+
currentStepStream = await createPersistedContextStepStream({
|
|
500
|
+
runtime: params.runtime,
|
|
501
|
+
executionId,
|
|
502
|
+
stepId: stepCreate.stepId,
|
|
503
|
+
});
|
|
471
504
|
await emitContextEvents({
|
|
472
505
|
silent,
|
|
473
506
|
writable,
|
|
@@ -483,7 +516,7 @@ export class ContextEngine {
|
|
|
483
516
|
],
|
|
484
517
|
});
|
|
485
518
|
// Hook: Context DSL `context()` (implemented by subclasses via `initialize()`)
|
|
486
|
-
const nextContent = await measureBenchmark(params.__benchmark, `${stagePrefix}.contextMs`, async () => await story.initialize(updatedContext,
|
|
519
|
+
const nextContent = await measureBenchmark(params.__benchmark, `${stagePrefix}.contextMs`, async () => await story.initialize(updatedContext, env));
|
|
487
520
|
updatedContext = await measureBenchmark(params.__benchmark, `${stagePrefix}.persistContextMs`, async () => await ops.updateContextContent(activeContextSelector, nextContent));
|
|
488
521
|
await emitContextEvents({
|
|
489
522
|
silent,
|
|
@@ -496,11 +529,12 @@ export class ContextEngine {
|
|
|
496
529
|
},
|
|
497
530
|
],
|
|
498
531
|
});
|
|
499
|
-
await story.opts.onContextUpdated?.({ env
|
|
532
|
+
await story.opts.onContextUpdated?.({ env, context: updatedContext });
|
|
500
533
|
// Hook: Context DSL `narrative()` (implemented by subclasses via `buildSystemPrompt()`)
|
|
501
|
-
const systemPrompt = await measureBenchmark(params.__benchmark, `${stagePrefix}.narrativeMs`, async () => await story.buildSystemPrompt(updatedContext,
|
|
534
|
+
const systemPrompt = await measureBenchmark(params.__benchmark, `${stagePrefix}.narrativeMs`, async () => await story.buildSystemPrompt(updatedContext, env));
|
|
502
535
|
// Hook: Context DSL `actions()` (implemented by subclasses via `buildTools()`)
|
|
503
|
-
const toolsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.actionsMs`, async () => await story.buildTools(updatedContext,
|
|
536
|
+
const toolsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.actionsMs`, async () => await story.buildTools(updatedContext, env));
|
|
537
|
+
const skillsAll = await measureBenchmark(params.__benchmark, `${stagePrefix}.skillsMs`, async () => await story.buildSkills(updatedContext, env));
|
|
504
538
|
// IMPORTANT: step args must be serializable.
|
|
505
539
|
// Match DurableAgent behavior: convert tool input schemas to plain JSON Schema in workflow context.
|
|
506
540
|
const toolsForModel = toolsToModelTools(toolsAll);
|
|
@@ -511,13 +545,13 @@ export class ContextEngine {
|
|
|
511
545
|
// If we stream with a per-step id, the UI will render an optimistic assistant message
|
|
512
546
|
// (step id) and then a second persisted assistant message (reaction id) with the same
|
|
513
547
|
// content once InstantDB updates.
|
|
514
|
-
const reactor = story.getReactor(updatedContext,
|
|
548
|
+
const reactor = story.getReactor(updatedContext, env);
|
|
515
549
|
const reactionPartsBeforeStep = Array.isArray(reactionEvent.content?.parts)
|
|
516
550
|
? [...reactionEvent.content.parts]
|
|
517
551
|
: [];
|
|
518
552
|
let persistedReactionPartsSignature = "";
|
|
519
553
|
const persistReactionParts = async (nextParts) => {
|
|
520
|
-
const normalizedParts = Array.isArray(nextParts) ? nextParts : [];
|
|
554
|
+
const normalizedParts = normalizePartsForPersistence(Array.isArray(nextParts) ? nextParts : []);
|
|
521
555
|
const nextSignature = JSON.stringify(normalizedParts);
|
|
522
556
|
if (nextSignature === persistedReactionPartsSignature)
|
|
523
557
|
return;
|
|
@@ -539,14 +573,16 @@ export class ContextEngine {
|
|
|
539
573
|
}, { executionId, contextId: String(currentContext.id) });
|
|
540
574
|
};
|
|
541
575
|
const { assistantEvent, actionRequests, messagesForModel } = await measureBenchmark(params.__benchmark, `${stagePrefix}.reactorMs`, async () => await reactor({
|
|
542
|
-
|
|
576
|
+
runtime: params.runtime,
|
|
577
|
+
env,
|
|
543
578
|
context: updatedContext,
|
|
544
579
|
contextIdentifier: activeContextSelector,
|
|
545
580
|
triggerEvent,
|
|
546
|
-
model: story.getModel(updatedContext,
|
|
581
|
+
model: story.getModel(updatedContext, env),
|
|
547
582
|
systemPrompt,
|
|
548
583
|
actions: toolsAll,
|
|
549
584
|
toolsForModel,
|
|
585
|
+
skills: skillsAll,
|
|
550
586
|
eventId: reactionEventId,
|
|
551
587
|
executionId,
|
|
552
588
|
contextId: String(currentContext.id),
|
|
@@ -556,6 +592,7 @@ export class ContextEngine {
|
|
|
556
592
|
// Only emit a `start` chunk once per story turn.
|
|
557
593
|
sendStart: !silent && iter === 0,
|
|
558
594
|
silent,
|
|
595
|
+
contextStepStream: currentStepStream?.stream,
|
|
559
596
|
writable,
|
|
560
597
|
persistReactionParts,
|
|
561
598
|
}));
|
|
@@ -579,7 +616,7 @@ export class ContextEngine {
|
|
|
579
616
|
// We intentionally do NOT persist the per-step LLM assistant event as a `context_event`.
|
|
580
617
|
// The story exposes a single visible `context_event` per turn (`reactionEventId`) so the UI
|
|
581
618
|
// doesn't render duplicate assistant messages (LLM-step + aggregated reaction).
|
|
582
|
-
const stepParts = (assistantEvent?.content?.parts ?? []);
|
|
619
|
+
const stepParts = normalizePartsForPersistence((assistantEvent?.content?.parts ?? []));
|
|
583
620
|
const assistantEventEffective = {
|
|
584
621
|
...assistantEvent,
|
|
585
622
|
content: {
|
|
@@ -622,6 +659,13 @@ export class ContextEngine {
|
|
|
622
659
|
status: "pending",
|
|
623
660
|
};
|
|
624
661
|
reactionEvent = await measureBenchmark(params.__benchmark, `${stagePrefix}.persistAssistantReactionMs`, async () => await ops.updateItem(reactionEvent.id, nextReactionEvent, { executionId, contextId: String(currentContext.id) }));
|
|
662
|
+
if (currentStepStream) {
|
|
663
|
+
await closePersistedContextStepStream({
|
|
664
|
+
runtime: params.runtime,
|
|
665
|
+
session: currentStepStream,
|
|
666
|
+
});
|
|
667
|
+
currentStepStream = null;
|
|
668
|
+
}
|
|
625
669
|
story.opts.onEventCreated?.(assistantEventEffective);
|
|
626
670
|
const firstActionRequest = actionRequests?.[0];
|
|
627
671
|
await measureBenchmark(params.__benchmark, `${stagePrefix}.markStepRunningMs`, async () => await ops.updateContextStep({
|
|
@@ -798,12 +842,18 @@ export class ContextEngine {
|
|
|
798
842
|
}
|
|
799
843
|
}
|
|
800
844
|
const output = await toolDef.execute(actionInput, {
|
|
845
|
+
runtime: params.runtime,
|
|
846
|
+
env,
|
|
847
|
+
context: updatedContext,
|
|
848
|
+
contextIdentifier: activeContextSelector,
|
|
801
849
|
toolCallId: actionRequest.actionRef,
|
|
802
850
|
messages: messagesForModel,
|
|
803
851
|
eventId: reactionEventId,
|
|
804
852
|
executionId,
|
|
805
853
|
triggerEventId,
|
|
806
854
|
contextId: currentContext.id,
|
|
855
|
+
stepId: String(stepCreate.stepId),
|
|
856
|
+
iteration: iter,
|
|
807
857
|
});
|
|
808
858
|
return { actionRequest, success: true, output };
|
|
809
859
|
}
|
|
@@ -817,11 +867,9 @@ export class ContextEngine {
|
|
|
817
867
|
}
|
|
818
868
|
})));
|
|
819
869
|
// Merge action results into persisted parts (so next LLM call can see them)
|
|
820
|
-
let
|
|
821
|
-
? [...reactionEvent.content.parts]
|
|
822
|
-
: [];
|
|
870
|
+
let finalizedStepParts = Array.isArray(stepParts) ? [...stepParts] : [];
|
|
823
871
|
for (const r of actionResults) {
|
|
824
|
-
|
|
872
|
+
finalizedStepParts = applyToolExecutionResultToParts(finalizedStepParts, {
|
|
825
873
|
toolCallId: r.actionRequest.actionRef,
|
|
826
874
|
toolName: r.actionRequest.actionName,
|
|
827
875
|
}, {
|
|
@@ -830,11 +878,20 @@ export class ContextEngine {
|
|
|
830
878
|
message: r.errorText,
|
|
831
879
|
});
|
|
832
880
|
}
|
|
881
|
+
await measureBenchmark(params.__benchmark, `${stagePrefix}.saveFinalStepPartsMs`, async () => await ops.saveContextPartsStep({
|
|
882
|
+
stepId: stepCreate.stepId,
|
|
883
|
+
parts: finalizedStepParts,
|
|
884
|
+
executionId,
|
|
885
|
+
contextId: String(currentContext.id),
|
|
886
|
+
iteration: iter,
|
|
887
|
+
}));
|
|
833
888
|
reactionEvent = {
|
|
834
889
|
...reactionEvent,
|
|
835
890
|
content: {
|
|
836
891
|
...reactionEvent.content,
|
|
837
|
-
|
|
892
|
+
// Deprecated mirror for compatibility. `event_parts` are the
|
|
893
|
+
// source of truth for replay and step inspection.
|
|
894
|
+
parts: [...reactionPartsBeforeStep, ...finalizedStepParts],
|
|
838
895
|
},
|
|
839
896
|
status: "pending",
|
|
840
897
|
};
|
|
@@ -853,7 +910,7 @@ export class ContextEngine {
|
|
|
853
910
|
// IMPORTANT: we call this after tool results have been merged into the persisted `reactionEvent`,
|
|
854
911
|
// so stories can inspect `reactionEvent.content.parts` deterministically.
|
|
855
912
|
const continueLoop = await measureBenchmark(params.__benchmark, `${stagePrefix}.shouldContinueMs`, async () => await story.shouldContinue({
|
|
856
|
-
env
|
|
913
|
+
env,
|
|
857
914
|
context: updatedContext,
|
|
858
915
|
reactionEvent,
|
|
859
916
|
assistantEvent: assistantEventEffective,
|
|
@@ -989,6 +1046,21 @@ export class ContextEngine {
|
|
|
989
1046
|
throw new Error(`ContextEngine: maxIterations reached (${maxIterations}) without completion`);
|
|
990
1047
|
}
|
|
991
1048
|
catch (error) {
|
|
1049
|
+
if (currentStepStream) {
|
|
1050
|
+
try {
|
|
1051
|
+
await abortPersistedContextStepStream({
|
|
1052
|
+
runtime: params.runtime,
|
|
1053
|
+
session: currentStepStream,
|
|
1054
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
catch {
|
|
1058
|
+
// noop
|
|
1059
|
+
}
|
|
1060
|
+
finally {
|
|
1061
|
+
currentStepStream = null;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
992
1064
|
// Best-effort: persist failure on the current iteration step (if any)
|
|
993
1065
|
if (currentStepId) {
|
|
994
1066
|
const failedStepId = currentStepId;
|
package/dist/context.events.d.ts
CHANGED
|
@@ -6,6 +6,26 @@ export declare const INPUT_TEXT_ITEM_TYPE = "input";
|
|
|
6
6
|
export declare const WEB_CHANNEL = "web";
|
|
7
7
|
export declare const AGENT_CHANNEL = "whatsapp";
|
|
8
8
|
export declare const EMAIL_CHANNEL = "email";
|
|
9
|
+
export type ContextOutputContentPart = {
|
|
10
|
+
type: "text";
|
|
11
|
+
text: string;
|
|
12
|
+
} | ({
|
|
13
|
+
type: "image-data";
|
|
14
|
+
data: string;
|
|
15
|
+
mediaType: string;
|
|
16
|
+
filename?: string;
|
|
17
|
+
} & Record<string, unknown>) | ({
|
|
18
|
+
type: string;
|
|
19
|
+
} & Record<string, unknown>);
|
|
20
|
+
export type ContextOutputPart = {
|
|
21
|
+
type: "json";
|
|
22
|
+
value: unknown;
|
|
23
|
+
} | {
|
|
24
|
+
type: "content";
|
|
25
|
+
value: ContextOutputContentPart[];
|
|
26
|
+
};
|
|
27
|
+
export declare function isContextOutputPart(value: unknown): value is ContextOutputPart;
|
|
28
|
+
export declare function normalizeContextOutputPart(value: unknown): ContextOutputPart;
|
|
9
29
|
export declare function createUserItemFromUIMessages(messages: UIMessage[]): ContextItem;
|
|
10
30
|
export declare function createAssistantItemFromUIMessages(itemId: string, messages: UIMessage[]): ContextItem;
|
|
11
31
|
export declare function convertToUIMessage(item: ContextItem): UIMessage;
|