@ekairos/events 1.22.4-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 +115 -0
- package/dist/codex.d.ts +95 -0
- package/dist/codex.js +91 -0
- package/dist/context.builder.d.ts +62 -0
- package/dist/context.builder.js +143 -0
- package/dist/context.config.d.ts +9 -0
- package/dist/context.config.js +30 -0
- package/dist/context.contract.d.ts +47 -0
- package/dist/context.contract.js +132 -0
- package/dist/context.d.ts +4 -0
- package/dist/context.durable.d.ts +5 -0
- package/dist/context.durable.js +13 -0
- package/dist/context.engine.d.ts +216 -0
- package/dist/context.engine.js +1098 -0
- package/dist/context.events.d.ts +55 -0
- package/dist/context.events.js +431 -0
- package/dist/context.hooks.d.ts +21 -0
- package/dist/context.hooks.js +31 -0
- package/dist/context.js +3 -0
- package/dist/context.parts.d.ts +241 -0
- package/dist/context.parts.js +360 -0
- package/dist/context.reactor.d.ts +3 -0
- package/dist/context.reactor.js +2 -0
- package/dist/context.registry.d.ts +13 -0
- package/dist/context.registry.js +30 -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 +85 -0
- package/dist/context.store.js +1 -0
- package/dist/context.stream.d.ts +148 -0
- package/dist/context.stream.js +141 -0
- package/dist/context.toolcalls.d.ts +60 -0
- package/dist/context.toolcalls.js +117 -0
- package/dist/env.d.ts +3 -0
- package/dist/env.js +53 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +11 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +1 -0
- package/dist/mirror.d.ts +41 -0
- package/dist/mirror.js +1 -0
- package/dist/oidc.d.ts +7 -0
- package/dist/oidc.js +25 -0
- package/dist/polyfills/dom-events.d.ts +1 -0
- package/dist/polyfills/dom-events.js +89 -0
- package/dist/react.d.ts +42 -0
- package/dist/react.js +88 -0
- package/dist/reactors/ai-sdk.chunk-map.d.ts +12 -0
- package/dist/reactors/ai-sdk.chunk-map.js +143 -0
- package/dist/reactors/ai-sdk.reactor.d.ts +33 -0
- package/dist/reactors/ai-sdk.reactor.js +65 -0
- package/dist/reactors/ai-sdk.step.d.ts +48 -0
- package/dist/reactors/ai-sdk.step.js +343 -0
- package/dist/reactors/scripted.reactor.d.ts +17 -0
- package/dist/reactors/scripted.reactor.js +51 -0
- package/dist/reactors/types.d.ts +52 -0
- package/dist/reactors/types.js +1 -0
- package/dist/runtime.d.ts +19 -0
- package/dist/runtime.js +26 -0
- package/dist/runtime.step.d.ts +9 -0
- package/dist/runtime.step.js +7 -0
- package/dist/schema.d.ts +2 -0
- package/dist/schema.js +191 -0
- package/dist/steps/do-context-stream-step.d.ts +34 -0
- package/dist/steps/do-context-stream-step.js +96 -0
- package/dist/steps/mirror.steps.d.ts +6 -0
- package/dist/steps/mirror.steps.js +48 -0
- package/dist/steps/store.steps.d.ts +96 -0
- package/dist/steps/store.steps.js +595 -0
- package/dist/steps/stream.steps.d.ts +86 -0
- package/dist/steps/stream.steps.js +270 -0
- package/dist/steps/trace.steps.d.ts +38 -0
- package/dist/steps/trace.steps.js +270 -0
- package/dist/stores/instant.document-parser.d.ts +6 -0
- package/dist/stores/instant.document-parser.js +210 -0
- package/dist/stores/instant.documents.d.ts +16 -0
- package/dist/stores/instant.documents.js +152 -0
- package/dist/stores/instant.store.d.ts +66 -0
- package/dist/stores/instant.store.js +575 -0
- package/dist/tools-to-model-tools.d.ts +19 -0
- package/dist/tools-to-model-tools.js +21 -0
- package/package.json +142 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
import "../polyfills/dom-events.js";
|
|
2
|
+
import { id, lookup } from "@instantdb/admin";
|
|
3
|
+
import { convertItemToModelMessages } from "../context.events.js";
|
|
4
|
+
import { assertContextTransition, assertExecutionTransition, assertItemTransition, assertStepTransition, } from "../context.contract.js";
|
|
5
|
+
export { parseAndStoreDocument } from "./instant.document-parser.js";
|
|
6
|
+
import { expandEventsWithInstantDocuments } from "./instant.documents.js";
|
|
7
|
+
export { coerceDocumentTextPages, expandEventsWithInstantDocuments, } from "./instant.documents.js";
|
|
8
|
+
import { mergeContextPartEnvelope, normalizePartsForPersistence, splitContextPartEnvelope, } from "../context.parts.js";
|
|
9
|
+
function shouldDebugInstantStore() {
|
|
10
|
+
return (process.env.EKAIROS_CONTEXT_DEBUG === "1" ||
|
|
11
|
+
process.env.EKAIROS_CONTEXT_DEBUG === "1" ||
|
|
12
|
+
process.env.PLAYWRIGHT_TEST === "1");
|
|
13
|
+
}
|
|
14
|
+
function clipText(value, max = 500) {
|
|
15
|
+
if (value.length <= max)
|
|
16
|
+
return value;
|
|
17
|
+
return `${value.slice(0, max)}...<truncated:${value.length - max}>`;
|
|
18
|
+
}
|
|
19
|
+
function simplifyForLog(value, depth = 0) {
|
|
20
|
+
if (value === null || value === undefined)
|
|
21
|
+
return value;
|
|
22
|
+
if (typeof value === "string")
|
|
23
|
+
return clipText(value);
|
|
24
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
25
|
+
return value;
|
|
26
|
+
if (value instanceof Date)
|
|
27
|
+
return value.toISOString();
|
|
28
|
+
if (depth >= 2)
|
|
29
|
+
return "[max-depth]";
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
const maxItems = 8;
|
|
32
|
+
const items = value
|
|
33
|
+
.slice(0, maxItems)
|
|
34
|
+
.map((item) => simplifyForLog(item, depth + 1));
|
|
35
|
+
if (value.length > maxItems) {
|
|
36
|
+
items.push(`...+${value.length - maxItems} more`);
|
|
37
|
+
}
|
|
38
|
+
return items;
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === "object") {
|
|
41
|
+
const entries = Object.entries(value);
|
|
42
|
+
const maxEntries = 16;
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const [key, entryValue] of entries.slice(0, maxEntries)) {
|
|
45
|
+
out[key] = simplifyForLog(entryValue, depth + 1);
|
|
46
|
+
}
|
|
47
|
+
if (entries.length > maxEntries) {
|
|
48
|
+
out.__truncatedKeys = entries.length - maxEntries;
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
return String(value);
|
|
53
|
+
}
|
|
54
|
+
function summarizeError(error) {
|
|
55
|
+
const err = error;
|
|
56
|
+
return {
|
|
57
|
+
name: err?.name,
|
|
58
|
+
message: err?.message,
|
|
59
|
+
code: err?.code,
|
|
60
|
+
status: err?.status ?? err?.statusCode,
|
|
61
|
+
details: simplifyForLog(err?.details ?? err?.body ?? err?.response ?? err?.data),
|
|
62
|
+
stack: typeof err?.stack === "string"
|
|
63
|
+
? clipText(err.stack, 1500)
|
|
64
|
+
: undefined,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function ensureValidEntityId(value, label) {
|
|
68
|
+
const normalized = typeof value === "string" ? value.trim() : "";
|
|
69
|
+
if (!normalized) {
|
|
70
|
+
throw new Error(`InstantStore: ${label} is required`);
|
|
71
|
+
}
|
|
72
|
+
return normalized;
|
|
73
|
+
}
|
|
74
|
+
function logInstantTransactFailure(params) {
|
|
75
|
+
if (!shouldDebugInstantStore())
|
|
76
|
+
return;
|
|
77
|
+
const payload = {
|
|
78
|
+
action: params.action,
|
|
79
|
+
meta: simplifyForLog(params.meta ?? {}),
|
|
80
|
+
txs: simplifyForLog(params.txs),
|
|
81
|
+
error: summarizeError(params.error),
|
|
82
|
+
};
|
|
83
|
+
// eslint-disable-next-line no-console
|
|
84
|
+
console.error("[context][instant.store] transact failed", payload);
|
|
85
|
+
}
|
|
86
|
+
function sortItems(items) {
|
|
87
|
+
const toEpoch = (value) => {
|
|
88
|
+
if (value instanceof Date)
|
|
89
|
+
return value.getTime();
|
|
90
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
91
|
+
const ms = new Date(value).getTime();
|
|
92
|
+
return Number.isFinite(ms) ? ms : 0;
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
};
|
|
96
|
+
return [...items].sort((a, b) => {
|
|
97
|
+
const ta = toEpoch(a?.createdAt ?? a?.updatedAt);
|
|
98
|
+
const tb = toEpoch(b?.createdAt ?? b?.updatedAt);
|
|
99
|
+
if (ta !== tb)
|
|
100
|
+
return ta - tb;
|
|
101
|
+
return String(a?.id ?? "").localeCompare(String(b?.id ?? ""));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
export class InstantStore {
|
|
105
|
+
constructor(db) {
|
|
106
|
+
this.db = db;
|
|
107
|
+
}
|
|
108
|
+
normalizeContext(row) {
|
|
109
|
+
return {
|
|
110
|
+
id: String(row?.id ?? ""),
|
|
111
|
+
key: typeof row?.key === "string" && row.key.length > 0 ? row.key : null,
|
|
112
|
+
name: typeof row?.name === "string" ? row.name : null,
|
|
113
|
+
status: (typeof row?.status === "string" ? row.status : "open_idle"),
|
|
114
|
+
createdAt: row?.createdAt instanceof Date ? row.createdAt : new Date(row?.createdAt ?? Date.now()),
|
|
115
|
+
updatedAt: row?.updatedAt instanceof Date
|
|
116
|
+
? row.updatedAt
|
|
117
|
+
: row?.updatedAt
|
|
118
|
+
? new Date(row.updatedAt)
|
|
119
|
+
: undefined,
|
|
120
|
+
content: row?.content ?? null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async ensureContextKey(context, expectedKey) {
|
|
124
|
+
if (!expectedKey)
|
|
125
|
+
return context;
|
|
126
|
+
if (context.key === expectedKey)
|
|
127
|
+
return context;
|
|
128
|
+
await this.db.transact([
|
|
129
|
+
this.db.tx.event_contexts[context.id].update({
|
|
130
|
+
key: expectedKey,
|
|
131
|
+
updatedAt: new Date(),
|
|
132
|
+
}),
|
|
133
|
+
]);
|
|
134
|
+
const refreshed = await this.getContext({ id: context.id });
|
|
135
|
+
return refreshed ?? { ...context, key: expectedKey };
|
|
136
|
+
}
|
|
137
|
+
async createContextRecord(contextIdentifier) {
|
|
138
|
+
const contextId = contextIdentifier && "id" in contextIdentifier ? String(contextIdentifier.id) : id();
|
|
139
|
+
const key = contextIdentifier && "key" in contextIdentifier ? contextIdentifier.key : undefined;
|
|
140
|
+
await this.db.transact([
|
|
141
|
+
this.db.tx.event_contexts[contextId].create({
|
|
142
|
+
createdAt: new Date(),
|
|
143
|
+
updatedAt: new Date(),
|
|
144
|
+
key,
|
|
145
|
+
status: "open_idle",
|
|
146
|
+
content: {},
|
|
147
|
+
}),
|
|
148
|
+
]);
|
|
149
|
+
const context = await this.getContext({ id: contextId });
|
|
150
|
+
if (!context) {
|
|
151
|
+
throw new Error("InstantStore: failed to create context");
|
|
152
|
+
}
|
|
153
|
+
return context;
|
|
154
|
+
}
|
|
155
|
+
async getOrCreateContext(contextIdentifier) {
|
|
156
|
+
if (!contextIdentifier) {
|
|
157
|
+
return await this.createContextRecord(null);
|
|
158
|
+
}
|
|
159
|
+
const existing = await this.getContext(contextIdentifier);
|
|
160
|
+
if (existing) {
|
|
161
|
+
if ("key" in contextIdentifier) {
|
|
162
|
+
return await this.ensureContextKey(existing, contextIdentifier.key);
|
|
163
|
+
}
|
|
164
|
+
return existing;
|
|
165
|
+
}
|
|
166
|
+
return await this.createContextRecord(contextIdentifier);
|
|
167
|
+
}
|
|
168
|
+
async getContext(contextIdentifier) {
|
|
169
|
+
try {
|
|
170
|
+
if ("id" in contextIdentifier) {
|
|
171
|
+
const res = await this.db.query({
|
|
172
|
+
event_contexts: {
|
|
173
|
+
$: { where: { id: contextIdentifier.id }, limit: 1 },
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
const row = res?.event_contexts?.[0];
|
|
177
|
+
return row ? this.normalizeContext(row) : null;
|
|
178
|
+
}
|
|
179
|
+
const res = await this.db.query({
|
|
180
|
+
event_contexts: {
|
|
181
|
+
$: { where: { key: contextIdentifier.key }, limit: 1 },
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
const row = res?.event_contexts?.[0];
|
|
185
|
+
return row ? this.normalizeContext(row) : null;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
throw new Error("InstantStore: Error getting context: " + error.message);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async updateContextContent(contextIdentifier, content) {
|
|
192
|
+
const context = await this.getContext(contextIdentifier);
|
|
193
|
+
if (!context?.id)
|
|
194
|
+
throw new Error("InstantStore: context not found");
|
|
195
|
+
await this.db.transact([
|
|
196
|
+
this.db.tx.event_contexts[context.id].update({
|
|
197
|
+
content: content,
|
|
198
|
+
updatedAt: new Date(),
|
|
199
|
+
}),
|
|
200
|
+
]);
|
|
201
|
+
const updated = await this.getContext({ id: context.id });
|
|
202
|
+
if (!updated)
|
|
203
|
+
throw new Error("InstantStore: context not found after update");
|
|
204
|
+
return updated;
|
|
205
|
+
}
|
|
206
|
+
async updateContextStatus(contextIdentifier, status) {
|
|
207
|
+
const context = await this.getContext(contextIdentifier);
|
|
208
|
+
if (!context?.id)
|
|
209
|
+
throw new Error("InstantStore: context not found");
|
|
210
|
+
if (context.status !== status) {
|
|
211
|
+
assertContextTransition(context.status, status);
|
|
212
|
+
}
|
|
213
|
+
await this.db.transact([
|
|
214
|
+
this.db.tx.event_contexts[context.id].update({
|
|
215
|
+
status,
|
|
216
|
+
updatedAt: new Date(),
|
|
217
|
+
}),
|
|
218
|
+
]);
|
|
219
|
+
}
|
|
220
|
+
async resolveContext(contextIdentifier) {
|
|
221
|
+
const context = await this.getContext(contextIdentifier);
|
|
222
|
+
if (!context?.id)
|
|
223
|
+
throw new Error("InstantStore: context not found");
|
|
224
|
+
return context;
|
|
225
|
+
}
|
|
226
|
+
async saveItem(contextIdentifier, event) {
|
|
227
|
+
const eventId = ensureValidEntityId(event?.id, "event.id");
|
|
228
|
+
const context = await this.resolveContext(contextIdentifier);
|
|
229
|
+
const existing = await this.getItem(eventId);
|
|
230
|
+
if (existing?.status && existing.status !== "stored") {
|
|
231
|
+
assertItemTransition(existing.status, "stored");
|
|
232
|
+
}
|
|
233
|
+
const txs = [
|
|
234
|
+
this.db.tx.event_items[eventId].update({
|
|
235
|
+
...event,
|
|
236
|
+
id: eventId,
|
|
237
|
+
status: "stored",
|
|
238
|
+
}),
|
|
239
|
+
this.db.tx.event_items[eventId].link({ context: context.id }),
|
|
240
|
+
];
|
|
241
|
+
try {
|
|
242
|
+
await this.db.transact(txs);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
logInstantTransactFailure({
|
|
246
|
+
action: "saveItem",
|
|
247
|
+
meta: {
|
|
248
|
+
contextIdentifier: simplifyForLog(contextIdentifier),
|
|
249
|
+
contextId: context.id,
|
|
250
|
+
eventId,
|
|
251
|
+
eventType: event?.type,
|
|
252
|
+
eventChannel: event?.channel,
|
|
253
|
+
eventCreatedAt: event?.createdAt,
|
|
254
|
+
},
|
|
255
|
+
txs,
|
|
256
|
+
error,
|
|
257
|
+
});
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
...event,
|
|
262
|
+
id: eventId,
|
|
263
|
+
status: "stored",
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
async updateItem(eventId, event) {
|
|
267
|
+
const current = await this.getItem(eventId);
|
|
268
|
+
if (current?.status && event.status && current.status !== event.status) {
|
|
269
|
+
assertItemTransition(current.status, event.status);
|
|
270
|
+
}
|
|
271
|
+
await this.db.transact([this.db.tx.event_items[eventId].update(event)]);
|
|
272
|
+
return {
|
|
273
|
+
...current,
|
|
274
|
+
...event,
|
|
275
|
+
id: eventId,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
async getItem(eventId) {
|
|
279
|
+
const res = await this.db.query({
|
|
280
|
+
event_items: {
|
|
281
|
+
$: { where: { id: eventId } },
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
return res.event_items?.[0] ?? null;
|
|
285
|
+
}
|
|
286
|
+
async getItems(contextIdentifier) {
|
|
287
|
+
const context = await this.resolveContext(contextIdentifier);
|
|
288
|
+
let res;
|
|
289
|
+
try {
|
|
290
|
+
res = await this.db.query({
|
|
291
|
+
event_items: {
|
|
292
|
+
$: {
|
|
293
|
+
where: { context: context.id },
|
|
294
|
+
limit: 1000,
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
res = await this.db.query({
|
|
301
|
+
event_items: {
|
|
302
|
+
$: {
|
|
303
|
+
where: { "context.id": context.id },
|
|
304
|
+
limit: 1000,
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return sortItems((res.event_items ?? []));
|
|
310
|
+
}
|
|
311
|
+
async getExecutionIdForItem(itemId) {
|
|
312
|
+
const directResult = await this.db.query({
|
|
313
|
+
event_items: {
|
|
314
|
+
$: { where: { id: itemId }, limit: 1 },
|
|
315
|
+
execution: {},
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
const directRow = directResult?.event_items?.[0];
|
|
319
|
+
const directExecutionId = directRow?.execution?.id;
|
|
320
|
+
if (typeof directExecutionId === "string" &&
|
|
321
|
+
directExecutionId.trim().length > 0) {
|
|
322
|
+
return directExecutionId;
|
|
323
|
+
}
|
|
324
|
+
const reverseResult = await this.db.query({
|
|
325
|
+
event_executions: {
|
|
326
|
+
$: { where: { "reaction.id": itemId }, limit: 1 },
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
const reverseRow = reverseResult?.event_executions?.[0];
|
|
330
|
+
const reverseExecutionId = reverseRow?.id;
|
|
331
|
+
return typeof reverseExecutionId === "string" && reverseExecutionId.trim().length > 0
|
|
332
|
+
? reverseExecutionId
|
|
333
|
+
: null;
|
|
334
|
+
}
|
|
335
|
+
async getProjectedPartsForOutputItem(itemId) {
|
|
336
|
+
const executionId = await this.getExecutionIdForItem(itemId);
|
|
337
|
+
if (!executionId) {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
const stepResult = await this.db.query({
|
|
341
|
+
event_steps: {
|
|
342
|
+
$: {
|
|
343
|
+
where: { "execution.id": executionId },
|
|
344
|
+
limit: 200,
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
const steps = sortItems((stepResult?.event_steps ?? [])).sort((a, b) => {
|
|
349
|
+
const ai = typeof a?.iteration === "number" ? a.iteration : 0;
|
|
350
|
+
const bi = typeof b?.iteration === "number" ? b.iteration : 0;
|
|
351
|
+
if (ai !== bi)
|
|
352
|
+
return ai - bi;
|
|
353
|
+
return String(a?.id ?? "").localeCompare(String(b?.id ?? ""));
|
|
354
|
+
});
|
|
355
|
+
if (steps.length === 0) {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
const projectedParts = [];
|
|
359
|
+
for (const step of steps) {
|
|
360
|
+
const stepId = typeof step?.id === "string" ? step.id : "";
|
|
361
|
+
if (!stepId)
|
|
362
|
+
continue;
|
|
363
|
+
const partResult = await this.db.query({
|
|
364
|
+
event_parts: {
|
|
365
|
+
$: {
|
|
366
|
+
where: { stepId: stepId },
|
|
367
|
+
limit: 500,
|
|
368
|
+
order: { idx: "asc" },
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
const partRows = (partResult?.event_parts ?? []).sort((a, b) => {
|
|
373
|
+
const ai = typeof a?.idx === "number" ? a.idx : 0;
|
|
374
|
+
const bi = typeof b?.idx === "number" ? b.idx : 0;
|
|
375
|
+
if (ai !== bi)
|
|
376
|
+
return ai - bi;
|
|
377
|
+
return 0;
|
|
378
|
+
});
|
|
379
|
+
projectedParts.push(...partRows.map((row) => mergeContextPartEnvelope({
|
|
380
|
+
part: row?.part,
|
|
381
|
+
metadata: row?.metadata,
|
|
382
|
+
})));
|
|
383
|
+
}
|
|
384
|
+
return projectedParts;
|
|
385
|
+
}
|
|
386
|
+
async createExecution(contextIdentifier, triggerEventId, reactionEventId) {
|
|
387
|
+
const normalizedTriggerEventId = ensureValidEntityId(triggerEventId, "triggerEventId");
|
|
388
|
+
const normalizedReactionEventId = ensureValidEntityId(reactionEventId, "reactionEventId");
|
|
389
|
+
const context = await this.resolveContext(contextIdentifier);
|
|
390
|
+
const executionId = id();
|
|
391
|
+
const currentStatus = context.status;
|
|
392
|
+
if (currentStatus === "closed") {
|
|
393
|
+
throw new Error("InstantStore: context must be reopened before creating an execution");
|
|
394
|
+
}
|
|
395
|
+
if (currentStatus !== "open_streaming") {
|
|
396
|
+
assertContextTransition(currentStatus, "open_streaming");
|
|
397
|
+
}
|
|
398
|
+
const txs = [
|
|
399
|
+
this.db.tx.event_executions[executionId].create({
|
|
400
|
+
createdAt: new Date(),
|
|
401
|
+
status: "executing",
|
|
402
|
+
}),
|
|
403
|
+
this.db.tx.event_contexts[context.id].update({
|
|
404
|
+
status: "open_streaming",
|
|
405
|
+
updatedAt: new Date(),
|
|
406
|
+
}),
|
|
407
|
+
this.db.tx.event_executions[executionId].link({ context: context.id }),
|
|
408
|
+
this.db.tx.event_contexts[context.id].link({ currentExecution: executionId }),
|
|
409
|
+
this.db.tx.event_executions[executionId].link({ trigger: normalizedTriggerEventId }),
|
|
410
|
+
this.db.tx.event_executions[executionId].link({ reaction: normalizedReactionEventId }),
|
|
411
|
+
];
|
|
412
|
+
try {
|
|
413
|
+
await this.db.transact(txs);
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
logInstantTransactFailure({
|
|
417
|
+
action: "createExecution",
|
|
418
|
+
meta: {
|
|
419
|
+
contextIdentifier: simplifyForLog(contextIdentifier),
|
|
420
|
+
contextId: context.id,
|
|
421
|
+
executionId,
|
|
422
|
+
triggerEventId: normalizedTriggerEventId,
|
|
423
|
+
reactionEventId: normalizedReactionEventId,
|
|
424
|
+
},
|
|
425
|
+
txs,
|
|
426
|
+
error,
|
|
427
|
+
});
|
|
428
|
+
throw error;
|
|
429
|
+
}
|
|
430
|
+
return { id: executionId };
|
|
431
|
+
}
|
|
432
|
+
async completeExecution(contextIdentifier, executionId, status) {
|
|
433
|
+
const context = await this.resolveContext(contextIdentifier);
|
|
434
|
+
const executionResult = await this.db.query({
|
|
435
|
+
event_executions: {
|
|
436
|
+
$: { where: { id: executionId }, limit: 1 },
|
|
437
|
+
},
|
|
438
|
+
});
|
|
439
|
+
const executionRow = executionResult?.event_executions?.[0];
|
|
440
|
+
if (!executionRow)
|
|
441
|
+
throw new Error("InstantStore: execution not found");
|
|
442
|
+
const currentExecutionStatus = String(executionRow.status ?? "executing");
|
|
443
|
+
if (currentExecutionStatus !== status) {
|
|
444
|
+
assertExecutionTransition(currentExecutionStatus, status);
|
|
445
|
+
}
|
|
446
|
+
if (context.status !== "closed") {
|
|
447
|
+
assertContextTransition(context.status, "closed");
|
|
448
|
+
}
|
|
449
|
+
await this.db.transact([
|
|
450
|
+
this.db.tx.event_executions[executionId].update({ status, updatedAt: new Date() }),
|
|
451
|
+
this.db.tx.event_contexts[context.id].update({
|
|
452
|
+
status: "closed",
|
|
453
|
+
updatedAt: new Date(),
|
|
454
|
+
}),
|
|
455
|
+
]);
|
|
456
|
+
}
|
|
457
|
+
async createStep(params) {
|
|
458
|
+
const stepId = id();
|
|
459
|
+
const txs = [
|
|
460
|
+
this.db.tx.event_steps[stepId].create({
|
|
461
|
+
createdAt: new Date(),
|
|
462
|
+
status: "running",
|
|
463
|
+
iteration: params.iteration,
|
|
464
|
+
}),
|
|
465
|
+
this.db.tx.event_steps[stepId].link({ execution: params.executionId }),
|
|
466
|
+
];
|
|
467
|
+
try {
|
|
468
|
+
await this.db.transact(txs);
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
logInstantTransactFailure({
|
|
472
|
+
action: "createStep",
|
|
473
|
+
meta: {
|
|
474
|
+
executionId: params.executionId,
|
|
475
|
+
iteration: params.iteration,
|
|
476
|
+
stepId,
|
|
477
|
+
},
|
|
478
|
+
txs,
|
|
479
|
+
error,
|
|
480
|
+
});
|
|
481
|
+
throw error;
|
|
482
|
+
}
|
|
483
|
+
return { id: stepId };
|
|
484
|
+
}
|
|
485
|
+
async updateStep(stepId, patch) {
|
|
486
|
+
if (patch.status) {
|
|
487
|
+
const stepResult = await this.db.query({
|
|
488
|
+
event_steps: {
|
|
489
|
+
$: { where: { id: stepId }, limit: 1 },
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
const stepRow = stepResult?.event_steps?.[0];
|
|
493
|
+
if (!stepRow)
|
|
494
|
+
throw new Error("InstantStore: step not found");
|
|
495
|
+
const currentStepStatus = String(stepRow.status ?? "running");
|
|
496
|
+
if (currentStepStatus !== patch.status) {
|
|
497
|
+
assertStepTransition(currentStepStatus, patch.status);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
const update = {
|
|
501
|
+
...patch,
|
|
502
|
+
updatedAt: patch.updatedAt ?? new Date(),
|
|
503
|
+
};
|
|
504
|
+
await this.db.transact([this.db.tx.event_steps[stepId].update(update)]);
|
|
505
|
+
}
|
|
506
|
+
async linkItemToExecution(params) {
|
|
507
|
+
await this.db.transact([
|
|
508
|
+
this.db.tx.event_items[params.itemId].link({ execution: params.executionId }),
|
|
509
|
+
]);
|
|
510
|
+
}
|
|
511
|
+
async saveStepParts(params) {
|
|
512
|
+
const parts = normalizePartsForPersistence(Array.isArray(params.parts) ? params.parts : []);
|
|
513
|
+
if (parts.length === 0)
|
|
514
|
+
return;
|
|
515
|
+
const txs = parts.map((part, idx) => {
|
|
516
|
+
const key = `${params.stepId}:${idx}`;
|
|
517
|
+
const split = splitContextPartEnvelope(part);
|
|
518
|
+
return this.db.tx.event_parts[lookup("key", key)]
|
|
519
|
+
.update({
|
|
520
|
+
stepId: params.stepId,
|
|
521
|
+
idx,
|
|
522
|
+
type: typeof split.part?.type === "string" ? String(split.part.type) : undefined,
|
|
523
|
+
part: split.part,
|
|
524
|
+
metadata: split.metadata,
|
|
525
|
+
updatedAt: new Date(),
|
|
526
|
+
})
|
|
527
|
+
.link({ step: params.stepId });
|
|
528
|
+
});
|
|
529
|
+
await this.db.transact(txs);
|
|
530
|
+
}
|
|
531
|
+
async itemsToModelMessages(events) {
|
|
532
|
+
const expanded = await expandEventsWithInstantDocuments({
|
|
533
|
+
db: this.db,
|
|
534
|
+
events,
|
|
535
|
+
derivedEventType: "output",
|
|
536
|
+
});
|
|
537
|
+
const messages = [];
|
|
538
|
+
for (const event of expanded) {
|
|
539
|
+
const isOutputItem = event?.type === "output";
|
|
540
|
+
const projectedParts = isOutputItem
|
|
541
|
+
? await this.getProjectedPartsForOutputItem(String(event?.id ?? ""))
|
|
542
|
+
: null;
|
|
543
|
+
const projectedEvent = projectedParts && projectedParts.length > 0
|
|
544
|
+
? {
|
|
545
|
+
...event,
|
|
546
|
+
content: {
|
|
547
|
+
...(event?.content ?? {}),
|
|
548
|
+
parts: projectedParts,
|
|
549
|
+
},
|
|
550
|
+
}
|
|
551
|
+
: event;
|
|
552
|
+
messages.push(await convertItemToModelMessages(projectedEvent));
|
|
553
|
+
}
|
|
554
|
+
return messages.flat();
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
export function createInstantStoreRuntime(params) {
|
|
558
|
+
const storesByOrg = new Map();
|
|
559
|
+
return async (env) => {
|
|
560
|
+
const orgId = params.getOrgId?.(env) ??
|
|
561
|
+
(typeof env?.orgId === "string" ? String(env.orgId) : "");
|
|
562
|
+
if (!orgId) {
|
|
563
|
+
throw new Error("[instant] Missing orgId in env. Provide env.orgId (or customize getOrgId).");
|
|
564
|
+
}
|
|
565
|
+
const cached = storesByOrg.get(orgId);
|
|
566
|
+
if (cached)
|
|
567
|
+
return cached;
|
|
568
|
+
const db = await params.getDb(orgId);
|
|
569
|
+
const store = new InstantStore(db);
|
|
570
|
+
const concreteDomain = params.domain?.fromDB ? params.domain.fromDB(db) : undefined;
|
|
571
|
+
const runtime = { store, db, domain: concreteDomain };
|
|
572
|
+
storesByOrg.set(orgId, runtime);
|
|
573
|
+
return runtime;
|
|
574
|
+
};
|
|
575
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Tool } from "ai";
|
|
2
|
+
/**
|
|
3
|
+
* Serializable "tool" shape to pass across the Workflow step boundary.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors Workflow DevKit's DurableAgent strategy:
|
|
6
|
+
* - Keep Zod/function values out of step arguments
|
|
7
|
+
* - Convert tool input schemas to plain JSON Schema in workflow context
|
|
8
|
+
*/
|
|
9
|
+
export type SerializableToolForModel = {
|
|
10
|
+
description?: string;
|
|
11
|
+
inputSchema: unknown;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Convert AI SDK tools to a serializable representation that can be passed to `"use-step"` functions.
|
|
15
|
+
*
|
|
16
|
+
* This matches DurableAgent's internal `toolsToModelTools` behavior:
|
|
17
|
+
* `inputSchema: asSchema(tool.inputSchema).jsonSchema`
|
|
18
|
+
*/
|
|
19
|
+
export declare function toolsToModelTools(tools: Record<string, Tool>): Record<string, SerializableToolForModel>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { asSchema } from "ai";
|
|
2
|
+
/**
|
|
3
|
+
* Convert AI SDK tools to a serializable representation that can be passed to `"use-step"` functions.
|
|
4
|
+
*
|
|
5
|
+
* This matches DurableAgent's internal `toolsToModelTools` behavior:
|
|
6
|
+
* `inputSchema: asSchema(tool.inputSchema).jsonSchema`
|
|
7
|
+
*/
|
|
8
|
+
export function toolsToModelTools(tools) {
|
|
9
|
+
const out = {};
|
|
10
|
+
for (const [name, tool] of Object.entries(tools)) {
|
|
11
|
+
const inputSchema = tool?.inputSchema;
|
|
12
|
+
if (!inputSchema) {
|
|
13
|
+
throw new Error(`Context: tool "${name}" is missing inputSchema (required for model tool calls)`);
|
|
14
|
+
}
|
|
15
|
+
out[name] = {
|
|
16
|
+
description: tool?.description,
|
|
17
|
+
inputSchema: asSchema(inputSchema).jsonSchema,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|