@ekairos/story 1.21.41-beta.0 → 1.21.52-beta.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/dist/ekairos.config.js +1 -16
- package/dist/index.d.ts +6 -6
- package/dist/index.js +5 -5
- package/dist/runtime.d.ts +1 -2
- package/dist/runtime.js +1 -2
- package/dist/steps/reaction.steps.d.ts +26 -0
- package/dist/steps/reaction.steps.js +137 -0
- package/dist/steps/store.steps.d.ts +15 -28
- package/dist/steps/store.steps.js +35 -67
- package/dist/steps/stream.steps.d.ts +7 -0
- package/dist/steps/stream.steps.js +15 -0
- package/dist/stores/instant.document-parser.d.ts +1 -1
- package/dist/stores/instant.document-parser.js +175 -39
- package/dist/stores/instant.documents.js +82 -6
- package/dist/stores/instant.store.d.ts +2 -0
- package/dist/stores/instant.store.js +16 -1
- package/dist/story.builder.d.ts +4 -4
- package/dist/story.builder.js +2 -2
- package/dist/story.config.d.ts +1 -2
- package/dist/story.config.js +31 -39
- package/dist/story.d.ts +2 -2
- package/dist/story.engine.d.ts +10 -2
- package/dist/story.engine.js +99 -29
- package/dist/story.js +2 -2
- package/package.json +16 -2
package/dist/story.engine.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Tool } from "ai";
|
|
2
|
-
import type { StoryEnvironment } from "./story.config";
|
|
3
|
-
import type { ContextEvent, ContextIdentifier, StoredContext } from "./story.store";
|
|
2
|
+
import type { StoryEnvironment } from "./story.config.js";
|
|
3
|
+
import type { ContextEvent, ContextIdentifier, StoredContext } from "./story.store.js";
|
|
4
4
|
export interface StoryOptions<Context = any, Env extends StoryEnvironment = StoryEnvironment> {
|
|
5
5
|
onContextCreated?: (args: {
|
|
6
6
|
env: Env;
|
|
@@ -39,6 +39,14 @@ export interface StoryStreamOptions {
|
|
|
39
39
|
* Default: true.
|
|
40
40
|
*/
|
|
41
41
|
sendFinish?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* If true, the story loop runs silently (no UI streaming output).
|
|
44
|
+
*
|
|
45
|
+
* Persistence (contexts/events/executions) still happens normally.
|
|
46
|
+
*
|
|
47
|
+
* Default: false.
|
|
48
|
+
*/
|
|
49
|
+
silent?: boolean;
|
|
42
50
|
}
|
|
43
51
|
/**
|
|
44
52
|
* Model initializer (DurableAgent-style).
|
package/dist/story.engine.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import { applyToolExecutionResultToParts } from "./story.toolcalls";
|
|
2
|
-
import {
|
|
3
|
-
import { toolsToModelTools } from "./tools-to-model-tools";
|
|
4
|
-
import { closeStoryStream, writeContextSubstate, writeToolOutputs } from "./steps/stream.steps";
|
|
5
|
-
import { completeExecution,
|
|
1
|
+
import { applyToolExecutionResultToParts } from "./story.toolcalls.js";
|
|
2
|
+
import { executeReaction } from "./steps/reaction.steps.js";
|
|
3
|
+
import { toolsToModelTools } from "./tools-to-model-tools.js";
|
|
4
|
+
import { closeStoryStream, writeContextSubstate, writeStoryPing, writeToolOutputs } from "./steps/stream.steps.js";
|
|
5
|
+
import { completeExecution, createReactionEvent, initializeContext, saveReactionEvent, saveTriggerEvent, updateContextContent, updateContextStatus, updateEvent, } from "./steps/store.steps.js";
|
|
6
|
+
function storyEngineInfo(message, ...args) {
|
|
7
|
+
// CRITICAL: static string log messages only. Dynamic values go in args.
|
|
8
|
+
console.log(message, ...args);
|
|
9
|
+
}
|
|
10
|
+
function storyEngineDebug(message, ...args) {
|
|
11
|
+
// CRITICAL: static string log messages only. Dynamic values go in args.
|
|
12
|
+
console.debug(message, ...args);
|
|
13
|
+
}
|
|
6
14
|
export class Story {
|
|
7
15
|
constructor(opts = {}) {
|
|
8
16
|
this.opts = opts;
|
|
@@ -58,9 +66,18 @@ export class Story {
|
|
|
58
66
|
const maxModelSteps = params.options?.maxModelSteps ?? 1;
|
|
59
67
|
const preventClose = params.options?.preventClose ?? false;
|
|
60
68
|
const sendFinish = params.options?.sendFinish ?? true;
|
|
69
|
+
const silent = params.options?.silent ?? false;
|
|
70
|
+
storyEngineInfo("[ekairos/story] story.engine react begin");
|
|
71
|
+
storyEngineInfo("[ekairos/story] story.engine react contextIdentifier", params.contextIdentifier);
|
|
72
|
+
storyEngineInfo("[ekairos/story] story.engine react maxIterations", maxIterations);
|
|
73
|
+
storyEngineInfo("[ekairos/story] story.engine react maxModelSteps", maxModelSteps);
|
|
74
|
+
storyEngineInfo("[ekairos/story] story.engine react silent", silent);
|
|
61
75
|
// 1) Ensure context exists (step)
|
|
62
|
-
const ctxResult = await
|
|
76
|
+
const ctxResult = await initializeContext(params.env, params.contextIdentifier, { silent });
|
|
63
77
|
const currentContext = ctxResult.context;
|
|
78
|
+
storyEngineInfo("[ekairos/story] story.engine initializeContext ok");
|
|
79
|
+
storyEngineInfo("[ekairos/story] story.engine initializeContext contextId", currentContext.id);
|
|
80
|
+
storyEngineInfo("[ekairos/story] story.engine initializeContext isNew", ctxResult.isNew);
|
|
64
81
|
const contextSelector = params.contextIdentifier?.id
|
|
65
82
|
? { id: String(params.contextIdentifier.id) }
|
|
66
83
|
: params.contextIdentifier?.key
|
|
@@ -70,12 +87,23 @@ export class Story {
|
|
|
70
87
|
await this.opts.onContextCreated?.({ env: params.env, context: currentContext });
|
|
71
88
|
}
|
|
72
89
|
// 2) Persist trigger event + create execution shell (steps)
|
|
73
|
-
const persistedTriggerEvent = await
|
|
90
|
+
const persistedTriggerEvent = await saveTriggerEvent(params.env, contextSelector, triggerEvent);
|
|
74
91
|
const triggerEventId = persistedTriggerEvent.id;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
92
|
+
storyEngineInfo("[ekairos/story] story.engine saveTriggerEvent ok");
|
|
93
|
+
storyEngineInfo("[ekairos/story] story.engine saveTriggerEvent triggerEventId", triggerEventId);
|
|
94
|
+
const { reactionEventId, executionId } = await createReactionEvent({
|
|
95
|
+
env: params.env,
|
|
96
|
+
contextIdentifier: contextSelector,
|
|
97
|
+
triggerEventId,
|
|
98
|
+
});
|
|
99
|
+
storyEngineInfo("[ekairos/story] story.engine createReactionEvent ok");
|
|
100
|
+
storyEngineInfo("[ekairos/story] story.engine createReactionEvent reactionEventId", reactionEventId);
|
|
101
|
+
storyEngineInfo("[ekairos/story] story.engine createReactionEvent executionId", executionId);
|
|
102
|
+
// Emit a simple ping chunk early so clients can validate that streaming works end-to-end.
|
|
103
|
+
// This should be ignored safely by clients that don't care about it.
|
|
104
|
+
if (!silent) {
|
|
105
|
+
await writeStoryPing({ label: "story-start" });
|
|
106
|
+
}
|
|
79
107
|
let reactionEvent = null;
|
|
80
108
|
// Latest persisted context state for this run (we keep it in memory; store is updated via steps).
|
|
81
109
|
let updatedContext = currentContext;
|
|
@@ -87,7 +115,9 @@ export class Story {
|
|
|
87
115
|
// noop
|
|
88
116
|
}
|
|
89
117
|
try {
|
|
90
|
-
|
|
118
|
+
if (!silent) {
|
|
119
|
+
await closeStoryStream({ preventClose, sendFinish });
|
|
120
|
+
}
|
|
91
121
|
}
|
|
92
122
|
catch {
|
|
93
123
|
// noop
|
|
@@ -95,34 +125,50 @@ export class Story {
|
|
|
95
125
|
};
|
|
96
126
|
try {
|
|
97
127
|
for (let iter = 0; iter < maxIterations; iter++) {
|
|
98
|
-
|
|
99
|
-
//
|
|
128
|
+
storyEngineInfo("[ekairos/story] story.engine === LOOP ITERATION ===", iter);
|
|
129
|
+
// Hook: Story DSL `context()` (implemented by subclasses via `initialize()`)
|
|
130
|
+
storyEngineInfo("[ekairos/story] >>> HOOK context() BEGIN", iter);
|
|
100
131
|
const nextContent = await this.initialize(updatedContext, params.env);
|
|
132
|
+
storyEngineInfo("[ekairos/story] <<< HOOK context() END", iter);
|
|
101
133
|
updatedContext = await updateContextContent(params.env, contextSelector, nextContent);
|
|
134
|
+
storyEngineInfo("[ekairos/story] story.engine updateContextContent ok");
|
|
102
135
|
await this.opts.onContextUpdated?.({ env: params.env, context: updatedContext });
|
|
136
|
+
// Hook: Story DSL `narrative()` (implemented by subclasses via `buildSystemPrompt()`)
|
|
137
|
+
storyEngineInfo("[ekairos/story] >>> HOOK narrative() BEGIN", iter);
|
|
103
138
|
const systemPrompt = await this.buildSystemPrompt(updatedContext, params.env);
|
|
139
|
+
storyEngineInfo("[ekairos/story] <<< HOOK narrative() END", iter);
|
|
140
|
+
// Hook: Story DSL `actions()` (implemented by subclasses via `buildTools()`)
|
|
141
|
+
storyEngineInfo("[ekairos/story] >>> HOOK actions() BEGIN", iter);
|
|
104
142
|
const toolsAll = await this.buildTools(updatedContext, params.env);
|
|
143
|
+
storyEngineInfo("[ekairos/story] <<< HOOK actions() END", iter);
|
|
105
144
|
// IMPORTANT: step args must be serializable.
|
|
106
145
|
// Match DurableAgent behavior: convert tool input schemas to plain JSON Schema in workflow context.
|
|
107
146
|
const toolsForModel = toolsToModelTools(toolsAll);
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
147
|
+
const { assistantEvent, toolCalls, messagesForModel } = await executeReaction({
|
|
148
|
+
env: params.env,
|
|
149
|
+
contextIdentifier: contextSelector,
|
|
111
150
|
model: this.getModel(updatedContext, params.env),
|
|
112
151
|
system: systemPrompt,
|
|
113
|
-
messages: messagesForModel,
|
|
114
152
|
tools: toolsForModel,
|
|
115
153
|
eventId: reactionEventId,
|
|
116
154
|
maxSteps: maxModelSteps,
|
|
117
155
|
// Only emit a `start` chunk once per story turn.
|
|
118
|
-
sendStart: iter === 0 && reactionEvent === null,
|
|
156
|
+
sendStart: !silent && iter === 0 && reactionEvent === null,
|
|
157
|
+
silent,
|
|
119
158
|
});
|
|
159
|
+
storyEngineInfo("[ekairos/story] story.engine executeReaction ok");
|
|
160
|
+
storyEngineInfo("[ekairos/story] story.engine executeReaction toolCallsCount", toolCalls.length);
|
|
161
|
+
if (toolCalls.length) {
|
|
162
|
+
storyEngineInfo("[ekairos/story] >>> TOOL_CALLS requested", toolCalls.map((tc) => tc?.toolName).filter(Boolean));
|
|
163
|
+
storyEngineDebug("[ekairos/story] >>> TOOL_CALLS payload", toolCalls);
|
|
164
|
+
}
|
|
120
165
|
// Persist/append the assistant event for this iteration
|
|
121
166
|
if (!reactionEvent) {
|
|
122
|
-
reactionEvent = await
|
|
167
|
+
reactionEvent = await saveReactionEvent(params.env, contextSelector, {
|
|
123
168
|
...assistantEvent,
|
|
124
169
|
status: "pending",
|
|
125
170
|
});
|
|
171
|
+
storyEngineInfo("[ekairos/story] story.engine saveReactionEvent ok");
|
|
126
172
|
}
|
|
127
173
|
else {
|
|
128
174
|
reactionEvent = await updateEvent(params.env, reactionEvent.id, {
|
|
@@ -135,11 +181,14 @@ export class Story {
|
|
|
135
181
|
},
|
|
136
182
|
status: "pending",
|
|
137
183
|
});
|
|
184
|
+
storyEngineInfo("[ekairos/story] story.engine updateEvent appendAssistantParts ok");
|
|
138
185
|
}
|
|
139
186
|
this.opts.onEventCreated?.(assistantEvent);
|
|
140
187
|
// Done: no tool calls requested by the model
|
|
141
188
|
if (!toolCalls.length) {
|
|
189
|
+
storyEngineInfo("[ekairos/story] >>> HOOK onEnd() BEGIN", iter);
|
|
142
190
|
const endResult = await this.callOnEnd(assistantEvent);
|
|
191
|
+
storyEngineInfo("[ekairos/story] <<< HOOK onEnd() END", iter, endResult);
|
|
143
192
|
if (endResult) {
|
|
144
193
|
// Mark reaction event completed
|
|
145
194
|
await updateEvent(params.env, reactionEventId, {
|
|
@@ -148,7 +197,9 @@ export class Story {
|
|
|
148
197
|
});
|
|
149
198
|
await updateContextStatus(params.env, contextSelector, "open");
|
|
150
199
|
await completeExecution(params.env, contextSelector, executionId, "completed");
|
|
151
|
-
|
|
200
|
+
if (!silent) {
|
|
201
|
+
await closeStoryStream({ preventClose, sendFinish });
|
|
202
|
+
}
|
|
152
203
|
return {
|
|
153
204
|
contextId: currentContext.id,
|
|
154
205
|
context: updatedContext,
|
|
@@ -159,12 +210,13 @@ export class Story {
|
|
|
159
210
|
}
|
|
160
211
|
}
|
|
161
212
|
// Execute tool calls (workflow context; tool implementations decide step vs workflow)
|
|
162
|
-
if (toolCalls.length) {
|
|
213
|
+
if (!silent && toolCalls.length) {
|
|
163
214
|
await writeContextSubstate({ key: "actions", transient: true });
|
|
164
215
|
}
|
|
165
216
|
const executionResults = await Promise.all(toolCalls.map(async (tc) => {
|
|
166
217
|
const toolDef = toolsAll[tc.toolName];
|
|
167
218
|
if (!toolDef || typeof toolDef.execute !== "function") {
|
|
219
|
+
storyEngineInfo("[ekairos/story] story.engine toolExecution missingTool", tc?.toolName);
|
|
168
220
|
return {
|
|
169
221
|
tc,
|
|
170
222
|
success: false,
|
|
@@ -172,6 +224,8 @@ export class Story {
|
|
|
172
224
|
errorText: `Tool "${tc.toolName}" not found or has no execute().`,
|
|
173
225
|
};
|
|
174
226
|
}
|
|
227
|
+
storyEngineInfo("[ekairos/story] >>> TOOL_EXEC BEGIN", tc?.toolName, tc?.toolCallId);
|
|
228
|
+
storyEngineDebug("[ekairos/story] >>> TOOL_EXEC input", tc?.toolName, tc?.toolCallId, tc?.args);
|
|
175
229
|
try {
|
|
176
230
|
const output = await toolDef.execute(tc.args, {
|
|
177
231
|
toolCallId: tc.toolCallId,
|
|
@@ -181,9 +235,13 @@ export class Story {
|
|
|
181
235
|
triggerEventId,
|
|
182
236
|
contextId: currentContext.id,
|
|
183
237
|
});
|
|
238
|
+
storyEngineInfo("[ekairos/story] <<< TOOL_EXEC OK", tc?.toolName, tc?.toolCallId);
|
|
239
|
+
storyEngineDebug("[ekairos/story] <<< TOOL_EXEC output", tc?.toolName, tc?.toolCallId, output);
|
|
184
240
|
return { tc, success: true, output };
|
|
185
241
|
}
|
|
186
242
|
catch (e) {
|
|
243
|
+
storyEngineInfo("[ekairos/story] <<< TOOL_EXEC FAILED", tc?.toolName, tc?.toolCallId);
|
|
244
|
+
storyEngineDebug("[ekairos/story] <<< TOOL_EXEC error", tc?.toolName, tc?.toolCallId, e instanceof Error ? e.message : String(e));
|
|
187
245
|
return {
|
|
188
246
|
tc,
|
|
189
247
|
success: false,
|
|
@@ -192,14 +250,21 @@ export class Story {
|
|
|
192
250
|
};
|
|
193
251
|
}
|
|
194
252
|
}));
|
|
253
|
+
storyEngineInfo("[ekairos/story] story.engine toolExecution resultsCount", executionResults.length);
|
|
195
254
|
// Emit tool outputs to the workflow stream (step)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
255
|
+
if (!silent) {
|
|
256
|
+
await writeToolOutputs({
|
|
257
|
+
results: executionResults.map((r) => r.success
|
|
258
|
+
? { toolCallId: r.tc.toolCallId, success: true, output: r.output }
|
|
259
|
+
: {
|
|
260
|
+
toolCallId: r.tc.toolCallId,
|
|
261
|
+
success: false,
|
|
262
|
+
errorText: r.errorText,
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
201
266
|
// Clear action status once tool execution results have been emitted.
|
|
202
|
-
if (toolCalls.length) {
|
|
267
|
+
if (!silent && toolCalls.length) {
|
|
203
268
|
await writeContextSubstate({ key: null, transient: true });
|
|
204
269
|
}
|
|
205
270
|
// Merge tool results into persisted parts (so next LLM call can see them)
|
|
@@ -217,6 +282,7 @@ export class Story {
|
|
|
217
282
|
content: { parts },
|
|
218
283
|
status: "pending",
|
|
219
284
|
});
|
|
285
|
+
storyEngineInfo("[ekairos/story] story.engine updateEvent mergeToolResults ok");
|
|
220
286
|
}
|
|
221
287
|
// Callback for observability/integration
|
|
222
288
|
for (const r of executionResults) {
|
|
@@ -232,6 +298,7 @@ export class Story {
|
|
|
232
298
|
// Stop/continue boundary: allow the Story to decide if the loop should continue.
|
|
233
299
|
// IMPORTANT: we call this after tool results have been merged into the persisted `reactionEvent`,
|
|
234
300
|
// so stories can inspect `reactionEvent.content.parts` deterministically.
|
|
301
|
+
storyEngineInfo("[ekairos/story] >>> HOOK shouldContinue() BEGIN", iter);
|
|
235
302
|
const continueLoop = await this.shouldContinue({
|
|
236
303
|
env: params.env,
|
|
237
304
|
context: updatedContext,
|
|
@@ -240,6 +307,7 @@ export class Story {
|
|
|
240
307
|
toolCalls,
|
|
241
308
|
toolExecutionResults: executionResults,
|
|
242
309
|
});
|
|
310
|
+
storyEngineInfo("[ekairos/story] <<< HOOK shouldContinue() END", iter, continueLoop);
|
|
243
311
|
if (continueLoop === false) {
|
|
244
312
|
await updateEvent(params.env, reactionEventId, {
|
|
245
313
|
...reactionEvent,
|
|
@@ -247,7 +315,9 @@ export class Story {
|
|
|
247
315
|
});
|
|
248
316
|
await updateContextStatus(params.env, contextSelector, "open");
|
|
249
317
|
await completeExecution(params.env, contextSelector, executionId, "completed");
|
|
250
|
-
|
|
318
|
+
if (!silent) {
|
|
319
|
+
await closeStoryStream({ preventClose, sendFinish });
|
|
320
|
+
}
|
|
251
321
|
return {
|
|
252
322
|
contextId: currentContext.id,
|
|
253
323
|
context: updatedContext,
|
package/dist/story.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekairos/story",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.52-beta.0",
|
|
4
4
|
"description": "Pulzar Story - Workflow-based AI Stories",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
".": {
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"import": "./dist/index.js",
|
|
24
|
+
"require": "./dist/index.js",
|
|
24
25
|
"default": "./dist/index.js"
|
|
25
26
|
},
|
|
26
27
|
"./runtime": {
|
|
@@ -39,6 +40,19 @@
|
|
|
39
40
|
"default": "./dist/stores/instant.store.js"
|
|
40
41
|
}
|
|
41
42
|
},
|
|
43
|
+
"typesVersions": {
|
|
44
|
+
"*": {
|
|
45
|
+
"runtime": [
|
|
46
|
+
"dist/runtime.d.ts"
|
|
47
|
+
],
|
|
48
|
+
"next": [
|
|
49
|
+
"dist/next.d.ts"
|
|
50
|
+
],
|
|
51
|
+
"instant": [
|
|
52
|
+
"dist/stores/instant.store.d.ts"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
},
|
|
42
56
|
"scripts": {
|
|
43
57
|
"build": "tsc -p tsconfig.json",
|
|
44
58
|
"dev": "tsc -p tsconfig.json --watch",
|
|
@@ -48,7 +62,7 @@
|
|
|
48
62
|
},
|
|
49
63
|
"dependencies": {
|
|
50
64
|
"@ai-sdk/openai": "^2.0.52",
|
|
51
|
-
"@ekairos/domain": "^1.21.
|
|
65
|
+
"@ekairos/domain": "^1.21.52-beta.0",
|
|
52
66
|
"@instantdb/admin": "^0.22.13",
|
|
53
67
|
"@instantdb/core": "^0.22.13",
|
|
54
68
|
"@vercel/sandbox": "^0.0.23",
|