@ekairos/thread 1.21.88-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/README.md +363 -0
- package/dist/codex.d.ts +95 -0
- package/dist/codex.js +91 -0
- package/dist/env.d.ts +12 -0
- package/dist/env.js +62 -0
- package/dist/events.d.ts +35 -0
- package/dist/events.js +102 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -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 +62 -0
- package/dist/react.js +101 -0
- package/dist/runtime.d.ts +17 -0
- package/dist/runtime.js +23 -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 +200 -0
- package/dist/steps/do-story-stream-step.d.ts +29 -0
- package/dist/steps/do-story-stream-step.js +89 -0
- package/dist/steps/do-thread-stream-step.d.ts +29 -0
- package/dist/steps/do-thread-stream-step.js +90 -0
- package/dist/steps/mirror.steps.d.ts +6 -0
- package/dist/steps/mirror.steps.js +48 -0
- package/dist/steps/reaction.steps.d.ts +43 -0
- package/dist/steps/reaction.steps.js +354 -0
- package/dist/steps/store.steps.d.ts +98 -0
- package/dist/steps/store.steps.js +512 -0
- package/dist/steps/stream.steps.d.ts +41 -0
- package/dist/steps/stream.steps.js +99 -0
- package/dist/steps/trace.steps.d.ts +37 -0
- package/dist/steps/trace.steps.js +265 -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 +78 -0
- package/dist/stores/instant.store.js +530 -0
- package/dist/story.actions.d.ts +60 -0
- package/dist/story.actions.js +120 -0
- package/dist/story.builder.d.ts +115 -0
- package/dist/story.builder.js +130 -0
- package/dist/story.config.d.ts +54 -0
- package/dist/story.config.js +125 -0
- package/dist/story.d.ts +2 -0
- package/dist/story.engine.d.ts +224 -0
- package/dist/story.engine.js +464 -0
- package/dist/story.hooks.d.ts +21 -0
- package/dist/story.hooks.js +31 -0
- package/dist/story.js +6 -0
- package/dist/story.registry.d.ts +21 -0
- package/dist/story.registry.js +30 -0
- package/dist/story.store.d.ts +107 -0
- package/dist/story.store.js +1 -0
- package/dist/story.toolcalls.d.ts +60 -0
- package/dist/story.toolcalls.js +73 -0
- package/dist/thread.builder.d.ts +118 -0
- package/dist/thread.builder.js +134 -0
- package/dist/thread.config.d.ts +15 -0
- package/dist/thread.config.js +30 -0
- package/dist/thread.d.ts +3 -0
- package/dist/thread.engine.d.ts +229 -0
- package/dist/thread.engine.js +471 -0
- package/dist/thread.events.d.ts +35 -0
- package/dist/thread.events.js +105 -0
- package/dist/thread.hooks.d.ts +21 -0
- package/dist/thread.hooks.js +31 -0
- package/dist/thread.js +7 -0
- package/dist/thread.reactor.d.ts +82 -0
- package/dist/thread.reactor.js +65 -0
- package/dist/thread.registry.d.ts +21 -0
- package/dist/thread.registry.js +30 -0
- package/dist/thread.store.d.ts +121 -0
- package/dist/thread.store.js +1 -0
- package/dist/thread.toolcalls.d.ts +60 -0
- package/dist/thread.toolcalls.js +73 -0
- package/dist/tools-to-model-tools.d.ts +19 -0
- package/dist/tools-to-model-tools.js +21 -0
- package/package.json +133 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import { writeThreadTraceEvents } from "./trace.steps.js";
|
|
2
|
+
import { getClientResumeHookUrl, toolApprovalHookToken, toolApprovalWebhookToken, } from "../thread.hooks.js";
|
|
3
|
+
async function maybeWriteTraceEvents(env, traceEvents) {
|
|
4
|
+
if (!traceEvents?.length)
|
|
5
|
+
return;
|
|
6
|
+
try {
|
|
7
|
+
await writeThreadTraceEvents({ env, events: traceEvents });
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
if (process.env.PLAYWRIGHT_TEST === "1") {
|
|
11
|
+
// eslint-disable-next-line no-console
|
|
12
|
+
console.warn("[thread/trace] emit failed");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function readWorkflowMetadata() {
|
|
17
|
+
try {
|
|
18
|
+
const { getWorkflowMetadata } = await import("workflow");
|
|
19
|
+
return getWorkflowMetadata?.() ?? null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function parseRunIdFromTriggerId(triggerEventId) {
|
|
26
|
+
if (typeof triggerEventId !== "string")
|
|
27
|
+
return undefined;
|
|
28
|
+
if (!triggerEventId.startsWith("e2e-trigger:"))
|
|
29
|
+
return undefined;
|
|
30
|
+
const parsed = triggerEventId.slice("e2e-trigger:".length).trim();
|
|
31
|
+
return parsed || undefined;
|
|
32
|
+
}
|
|
33
|
+
async function resolveWorkflowRunId(params) {
|
|
34
|
+
const meta = await readWorkflowMetadata();
|
|
35
|
+
let runId = meta && meta.workflowRunId !== undefined && meta.workflowRunId !== null
|
|
36
|
+
? String(meta.workflowRunId)
|
|
37
|
+
: "";
|
|
38
|
+
if (!runId) {
|
|
39
|
+
const envRunId = params.env?.workflowRunId;
|
|
40
|
+
if (typeof envRunId === "string" && envRunId.trim()) {
|
|
41
|
+
runId = envRunId.trim();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!runId && params.triggerEventId) {
|
|
45
|
+
const parsed = parseRunIdFromTriggerId(params.triggerEventId);
|
|
46
|
+
if (parsed)
|
|
47
|
+
runId = parsed;
|
|
48
|
+
}
|
|
49
|
+
if (!runId && params.executionId && params.db) {
|
|
50
|
+
try {
|
|
51
|
+
const q = await params.db.query({
|
|
52
|
+
thread_executions: {
|
|
53
|
+
$: { where: { id: String(params.executionId) }, limit: 1 },
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
const row = q?.thread_executions?.[0];
|
|
57
|
+
if (row?.workflowRunId) {
|
|
58
|
+
runId = String(row.workflowRunId);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// ignore
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { runId: runId || undefined, meta };
|
|
66
|
+
}
|
|
67
|
+
function inferDirection(item) {
|
|
68
|
+
const type = typeof item?.type === "string" ? item.type : "";
|
|
69
|
+
if (type.startsWith("output") || type.startsWith("tool") || type.startsWith("assistant")) {
|
|
70
|
+
return "outbound";
|
|
71
|
+
}
|
|
72
|
+
if (type.startsWith("input") || type.startsWith("user")) {
|
|
73
|
+
return "inbound";
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Initializes/ensures the story context exists and emits a single `data-context-id` chunk.
|
|
79
|
+
*
|
|
80
|
+
* This is the "context init" boundary for the story engine.
|
|
81
|
+
*/
|
|
82
|
+
export async function initializeContext(env, contextIdentifier, opts) {
|
|
83
|
+
"use step";
|
|
84
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
85
|
+
const runtime = await getThreadRuntime(env);
|
|
86
|
+
const { store, db } = runtime;
|
|
87
|
+
// Detect creation explicitly so the engine can run onContextCreated hooks.
|
|
88
|
+
let result;
|
|
89
|
+
if (!contextIdentifier) {
|
|
90
|
+
const context = await store.getOrCreateContext(null);
|
|
91
|
+
result = { context, isNew: true };
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const existing = await store.getContext(contextIdentifier);
|
|
95
|
+
if (existing) {
|
|
96
|
+
result = { context: existing, isNew: false };
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const created = await store.getOrCreateContext(contextIdentifier);
|
|
100
|
+
result = { context: created, isNew: true };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// If we're running in a non-streaming context (e.g. tests or headless usage),
|
|
104
|
+
// we skip writing stream chunks entirely.
|
|
105
|
+
if (!opts?.silent && opts?.writable) {
|
|
106
|
+
const writer = opts.writable.getWriter();
|
|
107
|
+
try {
|
|
108
|
+
await writer.write({
|
|
109
|
+
type: "data-context-id",
|
|
110
|
+
id: String(result.context.id),
|
|
111
|
+
data: { contextId: String(result.context.id) },
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
writer.releaseLock();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const { runId } = await resolveWorkflowRunId({ env, db });
|
|
119
|
+
if (runId) {
|
|
120
|
+
await maybeWriteTraceEvents(env, [
|
|
121
|
+
{
|
|
122
|
+
workflowRunId: runId,
|
|
123
|
+
eventId: `thread_context:${String(result.context.id)}`,
|
|
124
|
+
eventKind: "thread.context",
|
|
125
|
+
eventAt: new Date().toISOString(),
|
|
126
|
+
contextId: String(result.context.id),
|
|
127
|
+
contextKey: result.context.key ?? undefined,
|
|
128
|
+
payload: {
|
|
129
|
+
...result.context,
|
|
130
|
+
action: result.isNew ? "created" : "updated",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
]);
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
export async function updateContextContent(env, contextIdentifier, content) {
|
|
138
|
+
"use step";
|
|
139
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
140
|
+
const { store } = await getThreadRuntime(env);
|
|
141
|
+
return await store.updateContextContent(contextIdentifier, content);
|
|
142
|
+
}
|
|
143
|
+
export async function updateContextStatus(env, contextIdentifier, status) {
|
|
144
|
+
"use step";
|
|
145
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
146
|
+
const { store } = await getThreadRuntime(env);
|
|
147
|
+
await store.updateContextStatus(contextIdentifier, status);
|
|
148
|
+
}
|
|
149
|
+
export async function saveTriggerItem(env, contextIdentifier, event) {
|
|
150
|
+
"use step";
|
|
151
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
152
|
+
const { store } = await getThreadRuntime(env);
|
|
153
|
+
const saved = await store.saveItem(contextIdentifier, event);
|
|
154
|
+
return saved;
|
|
155
|
+
}
|
|
156
|
+
export async function emitContextIdChunk(params) {
|
|
157
|
+
"use step";
|
|
158
|
+
if (!params.writable)
|
|
159
|
+
return;
|
|
160
|
+
const writer = params.writable.getWriter();
|
|
161
|
+
try {
|
|
162
|
+
await writer.write({
|
|
163
|
+
type: "data-context-id",
|
|
164
|
+
id: String(params.contextId),
|
|
165
|
+
data: { contextId: String(params.contextId) },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
writer.releaseLock();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export async function saveTriggerAndCreateExecution(params) {
|
|
173
|
+
"use step";
|
|
174
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
175
|
+
const runtime = await getThreadRuntime(params.env);
|
|
176
|
+
const { store, db } = runtime;
|
|
177
|
+
const saved = await store.saveItem(params.contextIdentifier, params.triggerEvent);
|
|
178
|
+
const uuid = globalThis.crypto?.randomUUID?.();
|
|
179
|
+
const reactionEventId = typeof uuid === "string"
|
|
180
|
+
? uuid
|
|
181
|
+
: `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
182
|
+
await store.updateContextStatus(params.contextIdentifier, "streaming");
|
|
183
|
+
const execution = await store.createExecution(params.contextIdentifier, saved.id, reactionEventId);
|
|
184
|
+
const { runId, meta } = await resolveWorkflowRunId({
|
|
185
|
+
env: params.env,
|
|
186
|
+
db,
|
|
187
|
+
triggerEventId: saved.id,
|
|
188
|
+
executionId: execution.id,
|
|
189
|
+
});
|
|
190
|
+
if (runId && db) {
|
|
191
|
+
try {
|
|
192
|
+
await db.transact([
|
|
193
|
+
db.tx.thread_executions[execution.id].update({
|
|
194
|
+
workflowRunId: runId,
|
|
195
|
+
updatedAt: new Date(),
|
|
196
|
+
}),
|
|
197
|
+
]);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// ignore
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (runId) {
|
|
204
|
+
let contextKey;
|
|
205
|
+
let contextId;
|
|
206
|
+
try {
|
|
207
|
+
const ctx = await store.getContext(params.contextIdentifier);
|
|
208
|
+
contextKey = ctx?.key ?? undefined;
|
|
209
|
+
contextId = ctx?.id ? String(ctx.id) : undefined;
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// ignore
|
|
213
|
+
}
|
|
214
|
+
const events = [
|
|
215
|
+
{
|
|
216
|
+
workflowRunId: runId,
|
|
217
|
+
eventId: `workflow_run:${String(runId)}`,
|
|
218
|
+
eventKind: "workflow.run",
|
|
219
|
+
eventAt: new Date().toISOString(),
|
|
220
|
+
payload: meta ?? null,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
workflowRunId: runId,
|
|
224
|
+
eventId: `thread_run:${String(execution.id)}`,
|
|
225
|
+
eventKind: "thread.run",
|
|
226
|
+
eventAt: new Date().toISOString(),
|
|
227
|
+
contextId,
|
|
228
|
+
executionId: String(execution.id),
|
|
229
|
+
payload: {
|
|
230
|
+
contextKey: contextKey ?? null,
|
|
231
|
+
contextId: contextId ?? null,
|
|
232
|
+
triggerEventId: saved.id,
|
|
233
|
+
reactionEventId,
|
|
234
|
+
workflowMeta: meta ?? null,
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
workflowRunId: runId,
|
|
239
|
+
eventId: `thread_execution:${String(execution.id)}`,
|
|
240
|
+
eventKind: "thread.execution",
|
|
241
|
+
eventAt: new Date().toISOString(),
|
|
242
|
+
contextId,
|
|
243
|
+
executionId: String(execution.id),
|
|
244
|
+
payload: {
|
|
245
|
+
status: "started",
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
workflowRunId: runId,
|
|
250
|
+
eventId: `thread_item:${String(saved.id)}`,
|
|
251
|
+
eventKind: "thread.item",
|
|
252
|
+
eventAt: new Date().toISOString(),
|
|
253
|
+
contextId,
|
|
254
|
+
contextEventId: String(saved.id),
|
|
255
|
+
payload: {
|
|
256
|
+
...saved,
|
|
257
|
+
direction: "inbound",
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
];
|
|
261
|
+
await maybeWriteTraceEvents(params.env, events);
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
triggerEvent: saved,
|
|
265
|
+
triggerEventId: saved.id,
|
|
266
|
+
reactionEventId,
|
|
267
|
+
executionId: execution.id,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
export async function saveReactionItem(env, contextIdentifier, event, opts) {
|
|
271
|
+
"use step";
|
|
272
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
273
|
+
const runtime = await getThreadRuntime(env);
|
|
274
|
+
const { store, db } = runtime;
|
|
275
|
+
const saved = await store.saveItem(contextIdentifier, event);
|
|
276
|
+
const contextId = opts?.contextId ??
|
|
277
|
+
(typeof contextIdentifier?.id === "string"
|
|
278
|
+
? String(contextIdentifier.id)
|
|
279
|
+
: undefined);
|
|
280
|
+
const { runId, meta } = await resolveWorkflowRunId({
|
|
281
|
+
env,
|
|
282
|
+
db,
|
|
283
|
+
executionId: opts?.executionId,
|
|
284
|
+
});
|
|
285
|
+
if (runId) {
|
|
286
|
+
const events = [
|
|
287
|
+
{
|
|
288
|
+
workflowRunId: runId,
|
|
289
|
+
eventId: `thread_item:${String(saved.id)}`,
|
|
290
|
+
eventKind: "thread.item",
|
|
291
|
+
eventAt: new Date().toISOString(),
|
|
292
|
+
contextId,
|
|
293
|
+
executionId: opts?.executionId,
|
|
294
|
+
contextEventId: String(saved.id),
|
|
295
|
+
payload: {
|
|
296
|
+
...saved,
|
|
297
|
+
direction: "outbound",
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
];
|
|
301
|
+
if (opts?.executionId && opts.reviewRequests?.length) {
|
|
302
|
+
const resumeHookUrl = getClientResumeHookUrl();
|
|
303
|
+
const workflowUrl = meta && typeof meta.url === "string" && meta.url.trim()
|
|
304
|
+
? String(meta.url)
|
|
305
|
+
: undefined;
|
|
306
|
+
for (const rr of opts.reviewRequests) {
|
|
307
|
+
const toolCallId = String(rr.toolCallId);
|
|
308
|
+
events.push({
|
|
309
|
+
workflowRunId: runId,
|
|
310
|
+
eventId: `thread_review:${String(opts.executionId)}:${toolCallId}`,
|
|
311
|
+
eventKind: "thread.review",
|
|
312
|
+
eventAt: new Date().toISOString(),
|
|
313
|
+
contextId,
|
|
314
|
+
executionId: String(opts.executionId),
|
|
315
|
+
toolCallId,
|
|
316
|
+
payload: {
|
|
317
|
+
status: "in_review",
|
|
318
|
+
toolName: rr.toolName ?? "",
|
|
319
|
+
hookToken: toolApprovalHookToken({
|
|
320
|
+
executionId: String(opts.executionId),
|
|
321
|
+
toolCallId,
|
|
322
|
+
}),
|
|
323
|
+
webhookToken: toolApprovalWebhookToken({
|
|
324
|
+
executionId: String(opts.executionId),
|
|
325
|
+
toolCallId,
|
|
326
|
+
}),
|
|
327
|
+
resumeHookUrl,
|
|
328
|
+
workflowUrl,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
await maybeWriteTraceEvents(env, events);
|
|
334
|
+
}
|
|
335
|
+
return saved;
|
|
336
|
+
}
|
|
337
|
+
export async function updateItem(env, eventId, event, opts) {
|
|
338
|
+
"use step";
|
|
339
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
340
|
+
const runtime = await getThreadRuntime(env);
|
|
341
|
+
const { store, db } = runtime;
|
|
342
|
+
const saved = await store.updateItem(eventId, event);
|
|
343
|
+
const { runId } = await resolveWorkflowRunId({
|
|
344
|
+
env,
|
|
345
|
+
db,
|
|
346
|
+
executionId: opts?.executionId,
|
|
347
|
+
});
|
|
348
|
+
if (runId) {
|
|
349
|
+
await maybeWriteTraceEvents(env, [
|
|
350
|
+
{
|
|
351
|
+
workflowRunId: runId,
|
|
352
|
+
eventId: `thread_item:${String(saved.id)}`,
|
|
353
|
+
eventKind: "thread.item",
|
|
354
|
+
eventAt: new Date().toISOString(),
|
|
355
|
+
contextId: opts?.contextId,
|
|
356
|
+
executionId: opts?.executionId,
|
|
357
|
+
contextEventId: String(saved.id),
|
|
358
|
+
payload: {
|
|
359
|
+
...saved,
|
|
360
|
+
direction: inferDirection(saved),
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
]);
|
|
364
|
+
}
|
|
365
|
+
return saved;
|
|
366
|
+
}
|
|
367
|
+
export async function createExecution(env, contextIdentifier, triggerEventId, reactionEventId) {
|
|
368
|
+
"use step";
|
|
369
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
370
|
+
const { store } = await getThreadRuntime(env);
|
|
371
|
+
return await store.createExecution(contextIdentifier, triggerEventId, reactionEventId);
|
|
372
|
+
}
|
|
373
|
+
export async function createReactionItem(params) {
|
|
374
|
+
"use step";
|
|
375
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
376
|
+
const { store } = await getThreadRuntime(params.env);
|
|
377
|
+
// Generate a new reaction event id inside the step boundary.
|
|
378
|
+
const uuid = globalThis.crypto?.randomUUID?.();
|
|
379
|
+
const reactionEventId = typeof uuid === "string"
|
|
380
|
+
? uuid
|
|
381
|
+
: `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
382
|
+
await store.updateContextStatus(params.contextIdentifier, "streaming");
|
|
383
|
+
const execution = await store.createExecution(params.contextIdentifier, params.triggerEventId, reactionEventId);
|
|
384
|
+
return { reactionEventId, executionId: execution.id };
|
|
385
|
+
}
|
|
386
|
+
export async function completeExecution(env, contextIdentifier, executionId, status) {
|
|
387
|
+
"use step";
|
|
388
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
389
|
+
const runtime = await getThreadRuntime(env);
|
|
390
|
+
const { store, db } = runtime;
|
|
391
|
+
await store.completeExecution(contextIdentifier, executionId, status);
|
|
392
|
+
const contextId = typeof contextIdentifier?.id === "string"
|
|
393
|
+
? String(contextIdentifier.id)
|
|
394
|
+
: undefined;
|
|
395
|
+
const { runId } = await resolveWorkflowRunId({
|
|
396
|
+
env,
|
|
397
|
+
db,
|
|
398
|
+
executionId,
|
|
399
|
+
});
|
|
400
|
+
if (runId) {
|
|
401
|
+
await maybeWriteTraceEvents(env, [
|
|
402
|
+
{
|
|
403
|
+
workflowRunId: runId,
|
|
404
|
+
eventId: `thread_execution:${String(executionId)}:${status}`,
|
|
405
|
+
eventKind: "thread.execution",
|
|
406
|
+
eventAt: new Date().toISOString(),
|
|
407
|
+
contextId,
|
|
408
|
+
executionId: String(executionId),
|
|
409
|
+
payload: {
|
|
410
|
+
status,
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
]);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
export async function updateExecutionWorkflowRun(params) {
|
|
417
|
+
"use step";
|
|
418
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
419
|
+
const runtime = await getThreadRuntime(params.env);
|
|
420
|
+
const db = runtime?.db;
|
|
421
|
+
if (db) {
|
|
422
|
+
await db.transact([
|
|
423
|
+
db.tx.thread_executions[params.executionId].update({
|
|
424
|
+
workflowRunId: params.workflowRunId,
|
|
425
|
+
updatedAt: new Date(),
|
|
426
|
+
}),
|
|
427
|
+
]);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
export async function createThreadStep(params) {
|
|
431
|
+
"use step";
|
|
432
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
433
|
+
const { store } = await getThreadRuntime(params.env);
|
|
434
|
+
const res = await store.createStep({
|
|
435
|
+
executionId: params.executionId,
|
|
436
|
+
iteration: params.iteration,
|
|
437
|
+
});
|
|
438
|
+
return { stepId: res.id, eventId: res.eventId };
|
|
439
|
+
}
|
|
440
|
+
export async function updateThreadStep(params) {
|
|
441
|
+
"use step";
|
|
442
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
443
|
+
const runtime = await getThreadRuntime(params.env);
|
|
444
|
+
const { store, db } = runtime;
|
|
445
|
+
await store.updateStep(params.stepId, {
|
|
446
|
+
...params.patch,
|
|
447
|
+
updatedAt: new Date(),
|
|
448
|
+
});
|
|
449
|
+
const { runId } = await resolveWorkflowRunId({
|
|
450
|
+
env: params.env,
|
|
451
|
+
db,
|
|
452
|
+
executionId: params.executionId,
|
|
453
|
+
});
|
|
454
|
+
if (runId) {
|
|
455
|
+
await maybeWriteTraceEvents(params.env, [
|
|
456
|
+
{
|
|
457
|
+
workflowRunId: runId,
|
|
458
|
+
eventId: `thread_step:${String(params.stepId)}`,
|
|
459
|
+
eventKind: "thread.step",
|
|
460
|
+
eventAt: new Date().toISOString(),
|
|
461
|
+
contextId: params.contextId,
|
|
462
|
+
executionId: params.executionId,
|
|
463
|
+
stepId: String(params.stepId),
|
|
464
|
+
payload: {
|
|
465
|
+
status: params.patch.status,
|
|
466
|
+
iteration: params.iteration,
|
|
467
|
+
toolCalls: params.patch.toolCalls,
|
|
468
|
+
toolExecutionResults: params.patch.toolExecutionResults,
|
|
469
|
+
continueLoop: params.patch.continueLoop,
|
|
470
|
+
errorText: params.patch.errorText,
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
]);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
export async function linkItemToExecutionStep(params) {
|
|
477
|
+
"use step";
|
|
478
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
479
|
+
const { store } = await getThreadRuntime(params.env);
|
|
480
|
+
await store.linkItemToExecution({ itemId: params.itemId, executionId: params.executionId });
|
|
481
|
+
}
|
|
482
|
+
export async function saveThreadPartsStep(params) {
|
|
483
|
+
"use step";
|
|
484
|
+
const { getThreadRuntime } = await import("@ekairos/thread/runtime");
|
|
485
|
+
const runtime = await getThreadRuntime(params.env);
|
|
486
|
+
const { store, db } = runtime;
|
|
487
|
+
await store.saveStepParts({ stepId: params.stepId, parts: params.parts });
|
|
488
|
+
const { runId } = await resolveWorkflowRunId({
|
|
489
|
+
env: params.env,
|
|
490
|
+
db,
|
|
491
|
+
executionId: params.executionId,
|
|
492
|
+
});
|
|
493
|
+
if (runId && params.parts?.length) {
|
|
494
|
+
const events = [];
|
|
495
|
+
for (let idx = 0; idx < params.parts.length; idx += 1) {
|
|
496
|
+
const part = params.parts[idx];
|
|
497
|
+
events.push({
|
|
498
|
+
workflowRunId: runId,
|
|
499
|
+
eventId: `thread_part:${String(params.stepId)}:${idx}`,
|
|
500
|
+
eventKind: "thread.part",
|
|
501
|
+
eventAt: new Date().toISOString(),
|
|
502
|
+
contextId: params.contextId,
|
|
503
|
+
executionId: params.executionId,
|
|
504
|
+
stepId: String(params.stepId),
|
|
505
|
+
partKey: `${String(params.stepId)}:${idx}`,
|
|
506
|
+
partIdx: idx,
|
|
507
|
+
payload: part,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
await maybeWriteTraceEvents(params.env, events);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { UIMessageChunk } from "ai";
|
|
2
|
+
export declare function writeContextSubstate(params: {
|
|
3
|
+
/**
|
|
4
|
+
* Ephemeral substate key for the UI (thread engine internal state).
|
|
5
|
+
*
|
|
6
|
+
* - Provide a string key like "actions" to set it
|
|
7
|
+
* - Provide null to clear it
|
|
8
|
+
*/
|
|
9
|
+
key: string | null;
|
|
10
|
+
transient?: boolean;
|
|
11
|
+
writable?: WritableStream<UIMessageChunk>;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
export declare function writeContextIdChunk(params: {
|
|
14
|
+
contextId: string;
|
|
15
|
+
writable?: WritableStream<UIMessageChunk>;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
export declare function writeThreadPing(params: {
|
|
18
|
+
/**
|
|
19
|
+
* Simple ping event to validate that the workflow stream is alive.
|
|
20
|
+
* This is intentionally generic so clients can ignore it safely.
|
|
21
|
+
*/
|
|
22
|
+
label?: string;
|
|
23
|
+
writable?: WritableStream<UIMessageChunk>;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
export declare function writeToolOutputs(params: {
|
|
26
|
+
results: Array<{
|
|
27
|
+
toolCallId: string;
|
|
28
|
+
success: true;
|
|
29
|
+
output: unknown;
|
|
30
|
+
} | {
|
|
31
|
+
toolCallId: string;
|
|
32
|
+
success: false;
|
|
33
|
+
errorText: string;
|
|
34
|
+
}>;
|
|
35
|
+
writable?: WritableStream<UIMessageChunk>;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
export declare function closeThreadStream(params: {
|
|
38
|
+
preventClose?: boolean;
|
|
39
|
+
sendFinish?: boolean;
|
|
40
|
+
writable?: WritableStream<UIMessageChunk>;
|
|
41
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export async function writeContextSubstate(params) {
|
|
2
|
+
"use step";
|
|
3
|
+
const writable = params.writable;
|
|
4
|
+
if (!writable)
|
|
5
|
+
return;
|
|
6
|
+
const writer = writable.getWriter();
|
|
7
|
+
try {
|
|
8
|
+
await writer.write({
|
|
9
|
+
type: "data-context-substate",
|
|
10
|
+
data: { key: params.key },
|
|
11
|
+
transient: params.transient ?? true,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
writer.releaseLock();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function writeContextIdChunk(params) {
|
|
19
|
+
"use step";
|
|
20
|
+
const writable = params.writable;
|
|
21
|
+
if (!writable)
|
|
22
|
+
return;
|
|
23
|
+
const writer = writable.getWriter();
|
|
24
|
+
try {
|
|
25
|
+
await writer.write({
|
|
26
|
+
type: "data-context-id",
|
|
27
|
+
id: params.contextId,
|
|
28
|
+
data: { contextId: params.contextId },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
writer.releaseLock();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export async function writeThreadPing(params) {
|
|
36
|
+
"use step";
|
|
37
|
+
const writable = params.writable;
|
|
38
|
+
if (!writable)
|
|
39
|
+
return;
|
|
40
|
+
const writer = writable.getWriter();
|
|
41
|
+
try {
|
|
42
|
+
await writer.write({
|
|
43
|
+
type: "data-thread-ping",
|
|
44
|
+
data: { label: params.label ?? "thread-ping" },
|
|
45
|
+
transient: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
writer.releaseLock();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function writeToolOutputs(params) {
|
|
53
|
+
"use step";
|
|
54
|
+
const writable = params.writable;
|
|
55
|
+
if (!writable)
|
|
56
|
+
return;
|
|
57
|
+
const writer = writable.getWriter();
|
|
58
|
+
try {
|
|
59
|
+
for (const r of params.results) {
|
|
60
|
+
if (r.success) {
|
|
61
|
+
await writer.write({
|
|
62
|
+
type: "tool-output-available",
|
|
63
|
+
toolCallId: r.toolCallId,
|
|
64
|
+
output: r.output,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
await writer.write({
|
|
69
|
+
type: "tool-output-error",
|
|
70
|
+
toolCallId: r.toolCallId,
|
|
71
|
+
errorText: r.errorText,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
writer.releaseLock();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export async function closeThreadStream(params) {
|
|
81
|
+
"use step";
|
|
82
|
+
const sendFinish = params.sendFinish ?? true;
|
|
83
|
+
const preventClose = params.preventClose ?? false;
|
|
84
|
+
const writable = params.writable;
|
|
85
|
+
if (!writable)
|
|
86
|
+
return;
|
|
87
|
+
if (sendFinish) {
|
|
88
|
+
const writer = writable.getWriter();
|
|
89
|
+
try {
|
|
90
|
+
await writer.write({ type: "finish" });
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
writer.releaseLock();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!preventClose) {
|
|
97
|
+
await writable.close();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import "../polyfills/dom-events.js";
|
|
2
|
+
import type { ThreadEnvironment } from "../thread.config.js";
|
|
3
|
+
export type ThreadTraceEventWrite = {
|
|
4
|
+
workflowRunId: string;
|
|
5
|
+
eventId: string;
|
|
6
|
+
eventKind: string;
|
|
7
|
+
seq?: number;
|
|
8
|
+
eventAt?: string;
|
|
9
|
+
contextKey?: string;
|
|
10
|
+
spanId?: string;
|
|
11
|
+
parentSpanId?: string;
|
|
12
|
+
contextId?: string;
|
|
13
|
+
executionId?: string;
|
|
14
|
+
stepId?: string;
|
|
15
|
+
contextEventId?: string;
|
|
16
|
+
toolCallId?: string;
|
|
17
|
+
partKey?: string;
|
|
18
|
+
partIdx?: number;
|
|
19
|
+
isDeleted?: boolean;
|
|
20
|
+
aiProvider?: string;
|
|
21
|
+
aiModel?: string;
|
|
22
|
+
promptTokens?: number;
|
|
23
|
+
promptTokensCached?: number;
|
|
24
|
+
promptTokensUncached?: number;
|
|
25
|
+
completionTokens?: number;
|
|
26
|
+
totalTokens?: number;
|
|
27
|
+
latencyMs?: number;
|
|
28
|
+
cacheCostUsd?: number;
|
|
29
|
+
computeCostUsd?: number;
|
|
30
|
+
costUsd?: number;
|
|
31
|
+
payload?: unknown;
|
|
32
|
+
testId?: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function writeThreadTraceEvents(params: {
|
|
35
|
+
env: ThreadEnvironment;
|
|
36
|
+
events: ThreadTraceEventWrite[];
|
|
37
|
+
}): Promise<void>;
|