@kortyx/agent 0.14.1 → 0.15.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/CHANGELOG.md +32 -0
- package/LICENSE +51 -43
- package/dist/index.d.ts +55 -5
- package/dist/index.js +852 -34
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -21,11 +21,15 @@ var index_exports = {};
|
|
|
21
21
|
__export(index_exports, {
|
|
22
22
|
createAgent: () => createAgent,
|
|
23
23
|
createChatRouteHandler: () => createChatRouteHandler,
|
|
24
|
+
createCheckpointRouteHandler: () => createCheckpointRouteHandler,
|
|
24
25
|
extractLatestUserMessage: () => extractLatestUserMessage,
|
|
25
26
|
handleChatRequestBody: () => handleChatRequestBody,
|
|
27
|
+
handleCheckpointRequestBody: () => handleCheckpointRequestBody,
|
|
26
28
|
orchestrateGraphStream: () => orchestrateGraphStream,
|
|
27
29
|
parseChatRequestBody: () => parseChatRequestBody,
|
|
30
|
+
parseCheckpointRequestBody: () => parseCheckpointRequestBody,
|
|
28
31
|
parseResumeMeta: () => parseResumeMeta,
|
|
32
|
+
projectWorkflowTopology: () => projectWorkflowTopology,
|
|
29
33
|
streamChat: () => streamChat,
|
|
30
34
|
streamChatFromRoute: () => streamChatFromRoute,
|
|
31
35
|
transformGraphStreamForUI: () => transformGraphStreamForUI,
|
|
@@ -50,6 +54,25 @@ var chatRequestBodySchema = import_zod.z.looseObject({
|
|
|
50
54
|
context: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
|
|
51
55
|
messages: import_zod.z.array(chatMessageSchema)
|
|
52
56
|
});
|
|
57
|
+
var checkpointRequestBodySchema = import_zod.z.discriminatedUnion("action", [
|
|
58
|
+
import_zod.z.object({
|
|
59
|
+
action: import_zod.z.literal("list"),
|
|
60
|
+
sessionId: import_zod.z.string().min(1)
|
|
61
|
+
}).strict(),
|
|
62
|
+
import_zod.z.object({
|
|
63
|
+
action: import_zod.z.literal("get"),
|
|
64
|
+
checkpointId: import_zod.z.string().min(1)
|
|
65
|
+
}).strict(),
|
|
66
|
+
import_zod.z.object({
|
|
67
|
+
action: import_zod.z.literal("rollback"),
|
|
68
|
+
checkpointId: import_zod.z.string().min(1)
|
|
69
|
+
}).strict(),
|
|
70
|
+
import_zod.z.object({
|
|
71
|
+
action: import_zod.z.literal("fork"),
|
|
72
|
+
checkpointId: import_zod.z.string().min(1),
|
|
73
|
+
newSessionId: import_zod.z.string().min(1).optional()
|
|
74
|
+
}).strict()
|
|
75
|
+
]);
|
|
53
76
|
var toErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
54
77
|
function parseChatRequestBody(value) {
|
|
55
78
|
const parsed = chatRequestBodySchema.safeParse(value);
|
|
@@ -67,6 +90,12 @@ function parseChatRequestBody(value) {
|
|
|
67
90
|
messages: parsed.data.messages
|
|
68
91
|
};
|
|
69
92
|
}
|
|
93
|
+
function parseCheckpointRequestBody(value) {
|
|
94
|
+
const parsed = checkpointRequestBodySchema.safeParse(value);
|
|
95
|
+
if (parsed.success) return parsed.data;
|
|
96
|
+
const [firstIssue] = parsed.error.issues;
|
|
97
|
+
throw new Error(firstIssue.message);
|
|
98
|
+
}
|
|
70
99
|
async function handleChatRequestBody(args) {
|
|
71
100
|
const { agent, body } = args;
|
|
72
101
|
const stream = await agent.streamChat(body.messages, {
|
|
@@ -105,6 +134,38 @@ function createChatRouteHandler(args) {
|
|
|
105
134
|
}
|
|
106
135
|
};
|
|
107
136
|
}
|
|
137
|
+
async function handleCheckpointRequestBody(args) {
|
|
138
|
+
const { agent, body } = args;
|
|
139
|
+
const result = body.action === "list" ? await agent.listCheckpoints(body.sessionId) : body.action === "get" ? await agent.getCheckpoint(body.checkpointId) : body.action === "rollback" ? await agent.rollbackTo(body.checkpointId) : await agent.fork(body.checkpointId, {
|
|
140
|
+
...body.newSessionId ? { newSessionId: body.newSessionId } : {}
|
|
141
|
+
});
|
|
142
|
+
return new Response(JSON.stringify(result), {
|
|
143
|
+
headers: {
|
|
144
|
+
"content-type": "application/json"
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function createCheckpointRouteHandler(args) {
|
|
149
|
+
const { agent, errorStatus = 400 } = args;
|
|
150
|
+
return async function POST(request) {
|
|
151
|
+
try {
|
|
152
|
+
const body = parseCheckpointRequestBody(await request.json());
|
|
153
|
+
return await handleCheckpointRequestBody({ agent, body });
|
|
154
|
+
} catch (error) {
|
|
155
|
+
return new Response(
|
|
156
|
+
JSON.stringify({
|
|
157
|
+
error: toErrorMessage(error)
|
|
158
|
+
}),
|
|
159
|
+
{
|
|
160
|
+
status: errorStatus,
|
|
161
|
+
headers: {
|
|
162
|
+
"content-type": "application/json"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
108
169
|
|
|
109
170
|
// src/adapters/http-client.ts
|
|
110
171
|
var import_browser = require("@kortyx/stream/browser");
|
|
@@ -127,6 +188,266 @@ var import_providers = require("@kortyx/providers");
|
|
|
127
188
|
var import_runtime4 = require("@kortyx/runtime");
|
|
128
189
|
var import_zod2 = require("zod");
|
|
129
190
|
|
|
191
|
+
// src/telemetry/events.ts
|
|
192
|
+
var import_node_crypto = require("crypto");
|
|
193
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
194
|
+
var createId = () => {
|
|
195
|
+
try {
|
|
196
|
+
return (0, import_node_crypto.randomUUID)();
|
|
197
|
+
} catch {
|
|
198
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
var shouldCaptureTelemetryContent = (value, side) => {
|
|
202
|
+
if (value === true) return true;
|
|
203
|
+
return Boolean(
|
|
204
|
+
value && typeof value === "object" && !Array.isArray(value) && value[side] === true
|
|
205
|
+
);
|
|
206
|
+
};
|
|
207
|
+
var emitTelemetryEvent = (args) => {
|
|
208
|
+
const telemetry = isRecord(args.config.telemetry) ? args.config.telemetry : void 0;
|
|
209
|
+
const base = telemetry?.correlation;
|
|
210
|
+
const runId = args.correlation?.runId ?? base?.runId;
|
|
211
|
+
const workflowId = args.correlation?.workflowId ?? base?.workflowId;
|
|
212
|
+
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !runId || !workflowId) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const context = isRecord(args.config.context) ? args.config.context : {};
|
|
216
|
+
const event = {
|
|
217
|
+
schemaVersion: 1,
|
|
218
|
+
eventId: createId(),
|
|
219
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
220
|
+
environment: telemetry.environment,
|
|
221
|
+
service: telemetry.service,
|
|
222
|
+
correlation: {
|
|
223
|
+
runId,
|
|
224
|
+
workflowId,
|
|
225
|
+
...base?.sessionId ? { sessionId: base.sessionId } : {},
|
|
226
|
+
...base?.workflowRevisionId ? { workflowRevisionId: base.workflowRevisionId } : {},
|
|
227
|
+
...base?.topologyHash ? { topologyHash: base.topologyHash } : {},
|
|
228
|
+
...base?.nodeId ? { nodeId: base.nodeId } : {},
|
|
229
|
+
...args.correlation ?? {}
|
|
230
|
+
},
|
|
231
|
+
...telemetry.metadata || telemetry.tags || context.userId || context.tenantId ? {
|
|
232
|
+
context: {
|
|
233
|
+
...typeof context.userId === "string" ? { userId: context.userId } : {},
|
|
234
|
+
...typeof context.tenantId === "string" ? { tenantId: context.tenantId } : {},
|
|
235
|
+
...telemetry.tags ? { tags: telemetry.tags } : {},
|
|
236
|
+
...telemetry.metadata ? { metadata: telemetry.metadata } : {}
|
|
237
|
+
}
|
|
238
|
+
} : {},
|
|
239
|
+
type: args.type,
|
|
240
|
+
payload: args.payload
|
|
241
|
+
};
|
|
242
|
+
try {
|
|
243
|
+
void Promise.resolve(telemetry.reporter.emit([event])).then(async () => {
|
|
244
|
+
if (!args.flush) return;
|
|
245
|
+
const flush = telemetry.flush;
|
|
246
|
+
if (typeof flush === "function") await flush.call(telemetry);
|
|
247
|
+
}).catch(() => {
|
|
248
|
+
});
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// src/telemetry/topology.ts
|
|
254
|
+
var import_node_crypto2 = require("crypto");
|
|
255
|
+
var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
256
|
+
var stableValue = (value) => {
|
|
257
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
258
|
+
if (!isRecord2(value)) return value;
|
|
259
|
+
return Object.fromEntries(
|
|
260
|
+
Object.entries(value).filter(([, nested]) => typeof nested !== "function").sort(([a], [b]) => a.localeCompare(b)).map(([key, nested]) => [key, stableValue(nested)])
|
|
261
|
+
);
|
|
262
|
+
};
|
|
263
|
+
var asString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
|
|
264
|
+
var metadata = (value) => isRecord2(value) ? stableValue(value) : void 0;
|
|
265
|
+
var tagsFrom = (workflow) => {
|
|
266
|
+
const raw = isRecord2(workflow.metadata) ? workflow.metadata.tags : void 0;
|
|
267
|
+
if (!Array.isArray(raw)) return void 0;
|
|
268
|
+
const tags = raw.filter((tag) => typeof tag === "string").sort();
|
|
269
|
+
return tags.length > 0 ? tags : void 0;
|
|
270
|
+
};
|
|
271
|
+
var wordsFrom = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1-$2").split(/[^a-zA-Z0-9]+/).map((word) => word.toLowerCase()).filter(Boolean);
|
|
272
|
+
var camelFromWorkflowId = (workflowId) => {
|
|
273
|
+
const [first = "", ...rest] = wordsFrom(workflowId);
|
|
274
|
+
return [
|
|
275
|
+
first,
|
|
276
|
+
...rest.map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
|
|
277
|
+
].join("");
|
|
278
|
+
};
|
|
279
|
+
var resolveMemberTransitionTarget = (memberName, knownWorkflowIds) => {
|
|
280
|
+
const exact = knownWorkflowIds.find(
|
|
281
|
+
(workflowId) => camelFromWorkflowId(workflowId) === memberName
|
|
282
|
+
);
|
|
283
|
+
if (exact) return exact;
|
|
284
|
+
const memberWords = new Set(wordsFrom(memberName));
|
|
285
|
+
const candidates = knownWorkflowIds.map((workflowId) => {
|
|
286
|
+
const workflowWords = wordsFrom(workflowId);
|
|
287
|
+
const matched = workflowWords.filter((word) => memberWords.has(word));
|
|
288
|
+
return {
|
|
289
|
+
workflowId,
|
|
290
|
+
matched: matched.length,
|
|
291
|
+
total: workflowWords.length
|
|
292
|
+
};
|
|
293
|
+
}).filter(
|
|
294
|
+
(candidate) => candidate.total > 0 && candidate.matched === candidate.total
|
|
295
|
+
).sort(
|
|
296
|
+
(a, b) => b.matched - a.matched || a.workflowId.localeCompare(b.workflowId)
|
|
297
|
+
);
|
|
298
|
+
if (candidates.length !== 1) return void 0;
|
|
299
|
+
return candidates[0]?.workflowId;
|
|
300
|
+
};
|
|
301
|
+
var transitionTargetsFromRun = (run, knownWorkflowIds) => {
|
|
302
|
+
if (typeof run !== "function") return [];
|
|
303
|
+
const source = Function.prototype.toString.call(run);
|
|
304
|
+
const targets = /* @__PURE__ */ new Set();
|
|
305
|
+
for (const match of source.matchAll(
|
|
306
|
+
/\btransitionTo\s*:\s*(['"`])([^'"`$]+)\1/g
|
|
307
|
+
)) {
|
|
308
|
+
const target = match[2]?.trim();
|
|
309
|
+
if (target) targets.add(target);
|
|
310
|
+
}
|
|
311
|
+
for (const match of source.matchAll(
|
|
312
|
+
/\btransitionTo\s*:\s*(?:[A-Za-z_$][\w$]*\.)+([A-Za-z_$][\w$]*)/g
|
|
313
|
+
)) {
|
|
314
|
+
const memberName = match[1];
|
|
315
|
+
const target = resolveMemberTransitionTarget(memberName, knownWorkflowIds);
|
|
316
|
+
if (target) targets.add(target);
|
|
317
|
+
}
|
|
318
|
+
for (const match of source.matchAll(
|
|
319
|
+
/\btransitionTo\s*:\s*(?:[A-Za-z_$][\w$]*\.)*[A-Za-z_$][\w$]*\[['"`]([A-Za-z_$][\w$]*)['"`]\]/g
|
|
320
|
+
)) {
|
|
321
|
+
const memberName = match[1];
|
|
322
|
+
const target = resolveMemberTransitionTarget(memberName, knownWorkflowIds);
|
|
323
|
+
if (target) targets.add(target);
|
|
324
|
+
}
|
|
325
|
+
return [...targets].sort();
|
|
326
|
+
};
|
|
327
|
+
var transitionsFromNodes = (workflow, knownWorkflowIds) => {
|
|
328
|
+
const transitions = /* @__PURE__ */ new Map();
|
|
329
|
+
for (const [sourceNodeId, node] of Object.entries(
|
|
330
|
+
workflow.nodes
|
|
331
|
+
)) {
|
|
332
|
+
for (const targetWorkflowId of transitionTargetsFromRun(
|
|
333
|
+
node.run,
|
|
334
|
+
knownWorkflowIds
|
|
335
|
+
)) {
|
|
336
|
+
if (targetWorkflowId === workflow.id) continue;
|
|
337
|
+
const key = `${sourceNodeId}:${targetWorkflowId}`;
|
|
338
|
+
transitions.set(key, { sourceNodeId, targetWorkflowId });
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return [...transitions.values()].sort(
|
|
342
|
+
(a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b))
|
|
343
|
+
);
|
|
344
|
+
};
|
|
345
|
+
var projectWorkflowTopology = (args) => {
|
|
346
|
+
const nodes = Object.entries(
|
|
347
|
+
args.workflow.nodes
|
|
348
|
+
).map(([id, node]) => {
|
|
349
|
+
const nodeMetadata = metadata(node.metadata);
|
|
350
|
+
const params = metadata(node.params);
|
|
351
|
+
const model = isRecord2(node.params) ? isRecord2(node.params.model) ? node.params.model : void 0 : void 0;
|
|
352
|
+
const provider = isRecord2(model) ? asString(model.provider) ?? asString(model.providerId) : void 0;
|
|
353
|
+
const modelName = isRecord2(model) ? asString(model.name) ?? asString(model.modelId) : void 0;
|
|
354
|
+
return {
|
|
355
|
+
id,
|
|
356
|
+
...asString(nodeMetadata?.label) ? { label: asString(nodeMetadata?.label) } : {},
|
|
357
|
+
...asString(nodeMetadata?.type) ? { type: asString(nodeMetadata?.type) } : {},
|
|
358
|
+
...provider ? { provider } : {},
|
|
359
|
+
...modelName ? { model: modelName } : {},
|
|
360
|
+
...nodeMetadata ? { metadata: nodeMetadata } : {},
|
|
361
|
+
// These fields feed the hash but are intentionally not sent as Studio
|
|
362
|
+
// display metadata. They are stripped below after hashing.
|
|
363
|
+
_execution: {
|
|
364
|
+
...typeof node.run === "string" ? { run: node.run } : {},
|
|
365
|
+
...params ? { params } : {},
|
|
366
|
+
...node.behavior ? { behavior: stableValue(node.behavior) } : {}
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}).sort((a, b) => a.id.localeCompare(b.id));
|
|
370
|
+
const edges = args.workflow.edges.map((edge) => {
|
|
371
|
+
const condition = isRecord2(edge[2]) ? asString(edge[2].when) : void 0;
|
|
372
|
+
return {
|
|
373
|
+
sourceNodeId: edge[0],
|
|
374
|
+
targetNodeId: edge[1],
|
|
375
|
+
...condition ? { condition } : {}
|
|
376
|
+
};
|
|
377
|
+
}).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
378
|
+
const transitions = transitionsFromNodes(
|
|
379
|
+
args.workflow,
|
|
380
|
+
args.knownWorkflowIds ?? []
|
|
381
|
+
);
|
|
382
|
+
const tags = tagsFrom(args.workflow);
|
|
383
|
+
const hashInput = stableValue({
|
|
384
|
+
workflowId: args.workflow.id,
|
|
385
|
+
nodes: nodes.map((node) => ({ id: node.id, ...node._execution })),
|
|
386
|
+
edges,
|
|
387
|
+
transitions
|
|
388
|
+
});
|
|
389
|
+
const topologyHash = (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(hashInput)).digest("hex");
|
|
390
|
+
return {
|
|
391
|
+
schemaVersion: 1,
|
|
392
|
+
environment: args.environment,
|
|
393
|
+
service: args.service,
|
|
394
|
+
workflow: {
|
|
395
|
+
id: args.workflow.id,
|
|
396
|
+
declaredVersion: args.workflow.version,
|
|
397
|
+
...args.workflow.description ? { description: args.workflow.description } : {},
|
|
398
|
+
...tags ? { tags } : {},
|
|
399
|
+
topologyHash,
|
|
400
|
+
nodes: nodes.map(({ _execution: _ignored, ...node }) => node),
|
|
401
|
+
edges,
|
|
402
|
+
...transitions.length > 0 ? { transitions } : {}
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
var prepareWorkflowTelemetry = (args) => {
|
|
407
|
+
const telemetry = isRecord2(args.config.telemetry) ? args.config.telemetry : void 0;
|
|
408
|
+
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service) {
|
|
409
|
+
return args.config;
|
|
410
|
+
}
|
|
411
|
+
let snapshot;
|
|
412
|
+
try {
|
|
413
|
+
snapshot = projectWorkflowTopology({
|
|
414
|
+
workflow: args.workflow,
|
|
415
|
+
environment: telemetry.environment,
|
|
416
|
+
service: telemetry.service,
|
|
417
|
+
knownWorkflowIds: args.knownWorkflowIds
|
|
418
|
+
});
|
|
419
|
+
} catch {
|
|
420
|
+
return args.config;
|
|
421
|
+
}
|
|
422
|
+
let workflowRevisionId;
|
|
423
|
+
try {
|
|
424
|
+
workflowRevisionId = telemetry.reporter.getWorkflowRevisionId?.({
|
|
425
|
+
environment: snapshot.environment,
|
|
426
|
+
workflowId: snapshot.workflow.id,
|
|
427
|
+
topologyHash: snapshot.workflow.topologyHash
|
|
428
|
+
});
|
|
429
|
+
void Promise.resolve(
|
|
430
|
+
telemetry.reporter.ensureWorkflowTopology(snapshot)
|
|
431
|
+
).catch(() => {
|
|
432
|
+
});
|
|
433
|
+
} catch {
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
...args.config,
|
|
437
|
+
telemetry: {
|
|
438
|
+
...telemetry,
|
|
439
|
+
correlation: {
|
|
440
|
+
...telemetry.correlation ?? {},
|
|
441
|
+
runId: args.runId,
|
|
442
|
+
...args.sessionId ? { sessionId: args.sessionId } : {},
|
|
443
|
+
workflowId: snapshot.workflow.id,
|
|
444
|
+
topologyHash: snapshot.workflow.topologyHash,
|
|
445
|
+
...workflowRevisionId ? { workflowRevisionId } : {}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
};
|
|
450
|
+
|
|
130
451
|
// src/chat/process-chat.ts
|
|
131
452
|
var import_runtime3 = require("@kortyx/runtime");
|
|
132
453
|
|
|
@@ -139,10 +460,12 @@ var import_runtime = require("@kortyx/runtime");
|
|
|
139
460
|
var import_langgraph = require("@langchain/langgraph");
|
|
140
461
|
|
|
141
462
|
// src/stream/transform-graph-stream-for-ui.ts
|
|
463
|
+
var isGraphStateOutput = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && "currentWorkflow" in value;
|
|
142
464
|
async function* transformGraphStreamForUI(stream, options = {}) {
|
|
143
465
|
const { debug = false, emitStatus = debug } = options;
|
|
144
466
|
const startedNodes = /* @__PURE__ */ new Set();
|
|
145
467
|
const endedNodes = /* @__PURE__ */ new Set();
|
|
468
|
+
let latestGraphState = null;
|
|
146
469
|
for await (const event of stream) {
|
|
147
470
|
const { event: type, name, data } = event ?? {};
|
|
148
471
|
if (debug) console.log(`[debug:event]`, JSON.stringify(event, null, 2));
|
|
@@ -167,6 +490,7 @@ async function* transformGraphStreamForUI(stream, options = {}) {
|
|
|
167
490
|
JSON.stringify(output, null, 2)
|
|
168
491
|
);
|
|
169
492
|
if (!output || nodeName?.startsWith("ChannelWrite")) break;
|
|
493
|
+
if (isGraphStateOutput(output)) latestGraphState = output;
|
|
170
494
|
if (nodeName !== "__start__" && nodeName !== "__end__") {
|
|
171
495
|
if (!endedNodes.has(nodeName)) {
|
|
172
496
|
if (emitStatus) {
|
|
@@ -183,7 +507,11 @@ async function* transformGraphStreamForUI(stream, options = {}) {
|
|
|
183
507
|
case "on_graph_end": {
|
|
184
508
|
if (debug)
|
|
185
509
|
console.log(`[debug:on_graph_end]`, JSON.stringify(data, null, 2));
|
|
186
|
-
|
|
510
|
+
const output = data?.output;
|
|
511
|
+
yield {
|
|
512
|
+
type: "done",
|
|
513
|
+
data: isGraphStateOutput(output) ? output : latestGraphState
|
|
514
|
+
};
|
|
187
515
|
break;
|
|
188
516
|
}
|
|
189
517
|
default:
|
|
@@ -196,42 +524,71 @@ async function* transformGraphStreamForUI(stream, options = {}) {
|
|
|
196
524
|
}
|
|
197
525
|
|
|
198
526
|
// src/orchestrator.ts
|
|
199
|
-
var
|
|
527
|
+
var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
528
|
+
var isGraphState = (value) => isRecord3(value) && typeof value.currentWorkflow === "string";
|
|
529
|
+
var getGraphCheckpointId = (snapshot) => {
|
|
530
|
+
if (!isRecord3(snapshot)) return void 0;
|
|
531
|
+
const config = isRecord3(snapshot.config) ? snapshot.config : void 0;
|
|
532
|
+
const configurable = config && isRecord3(config.configurable) ? config.configurable : void 0;
|
|
533
|
+
const checkpointId = configurable?.checkpoint_id;
|
|
534
|
+
return typeof checkpointId === "string" && checkpointId.length > 0 ? checkpointId : void 0;
|
|
535
|
+
};
|
|
200
536
|
var getTraceAdapter = (config) => {
|
|
201
|
-
const telemetry =
|
|
537
|
+
const telemetry = isRecord3(config.telemetry) ? config.telemetry : void 0;
|
|
202
538
|
const traceAdapter = telemetry?.trace ?? config.reasonTrace;
|
|
203
|
-
return
|
|
539
|
+
return isRecord3(traceAdapter) ? traceAdapter : void 0;
|
|
204
540
|
};
|
|
205
541
|
async function orchestrateGraphStream({
|
|
206
542
|
sessionId,
|
|
207
543
|
runId,
|
|
208
544
|
graph,
|
|
209
545
|
state,
|
|
210
|
-
config,
|
|
546
|
+
config: initialConfig,
|
|
211
547
|
selectWorkflow,
|
|
548
|
+
knownWorkflowIds,
|
|
212
549
|
frameworkAdapter
|
|
213
550
|
}) {
|
|
214
551
|
const out = new import_node_stream.PassThrough({ objectMode: true });
|
|
552
|
+
let config = initialConfig;
|
|
215
553
|
let currentGraph = graph;
|
|
216
554
|
let currentState = state;
|
|
217
555
|
let finished = false;
|
|
218
556
|
let structuredSeq = 0;
|
|
219
557
|
const debugEnabled = Boolean(config?.features?.tracing);
|
|
220
558
|
const traceAdapter = getTraceAdapter(config);
|
|
221
|
-
const contextMeta =
|
|
222
|
-
const telemetryConfig =
|
|
559
|
+
const contextMeta = isRecord3(config.context) ? config.context : {};
|
|
560
|
+
const telemetryConfig = isRecord3(config.telemetry) ? config.telemetry : {};
|
|
561
|
+
const emitResumeFailure = (resumeError) => {
|
|
562
|
+
const interruptId = typeof config.telemetryInterruptId === "string" ? config.telemetryInterruptId : void 0;
|
|
563
|
+
if (!interruptId) return;
|
|
564
|
+
emitTelemetryEvent({
|
|
565
|
+
config,
|
|
566
|
+
type: "interrupt.resolved",
|
|
567
|
+
correlation: typeof config.telemetryInterruptNodeId === "string" ? { nodeId: config.telemetryInterruptNodeId } : void 0,
|
|
568
|
+
payload: {
|
|
569
|
+
interruptId,
|
|
570
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
571
|
+
resumeOutcome: "failed",
|
|
572
|
+
resumeError: resumeError instanceof Error ? resumeError.message : String(resumeError)
|
|
573
|
+
},
|
|
574
|
+
flush: true
|
|
575
|
+
});
|
|
576
|
+
};
|
|
577
|
+
const telemetryCorrelation = isRecord3(telemetryConfig.correlation) ? telemetryConfig.correlation : {};
|
|
223
578
|
const runSpanArgs = {
|
|
224
579
|
name: "kortyx.run",
|
|
225
580
|
attributes: {
|
|
226
581
|
...sessionId ? { sessionId } : {},
|
|
227
582
|
runId,
|
|
228
583
|
workflowId: state.currentWorkflow,
|
|
584
|
+
...typeof telemetryCorrelation.topologyHash === "string" ? { topologyHash: telemetryCorrelation.topologyHash } : {},
|
|
585
|
+
...typeof telemetryCorrelation.workflowRevisionId === "string" ? { workflowRevisionId: telemetryCorrelation.workflowRevisionId } : {},
|
|
229
586
|
...typeof contextMeta.userId === "string" ? { userId: contextMeta.userId } : {},
|
|
230
587
|
...typeof contextMeta.tenantId === "string" ? { tenantId: contextMeta.tenantId } : {}
|
|
231
588
|
},
|
|
232
589
|
telemetry: {
|
|
233
590
|
metadata: {
|
|
234
|
-
...
|
|
591
|
+
...isRecord3(telemetryConfig.metadata) ? telemetryConfig.metadata : {},
|
|
235
592
|
...contextMeta
|
|
236
593
|
},
|
|
237
594
|
tags: Array.isArray(telemetryConfig.tags) ? telemetryConfig.tags : void 0,
|
|
@@ -240,6 +597,10 @@ async function orchestrateGraphStream({
|
|
|
240
597
|
}
|
|
241
598
|
};
|
|
242
599
|
const namespacesUsed = /* @__PURE__ */ new Set();
|
|
600
|
+
const touchedNodes = /* @__PURE__ */ new Set();
|
|
601
|
+
const structuredStreamIds = /* @__PURE__ */ new Set();
|
|
602
|
+
const activePendingRequests = /* @__PURE__ */ new Map();
|
|
603
|
+
const pendingRequestWrites = [];
|
|
243
604
|
try {
|
|
244
605
|
const sid = config?.session?.id;
|
|
245
606
|
if (sid && typeof sid === "string") {
|
|
@@ -249,7 +610,9 @@ async function orchestrateGraphStream({
|
|
|
249
610
|
}
|
|
250
611
|
const pending = {
|
|
251
612
|
to: null,
|
|
252
|
-
payload: {}
|
|
613
|
+
payload: {},
|
|
614
|
+
sourceNodeId: void 0,
|
|
615
|
+
sourceWorkflowId: void 0
|
|
253
616
|
};
|
|
254
617
|
let lastStatusMsg = "";
|
|
255
618
|
let lastStatusAt = 0;
|
|
@@ -315,10 +678,12 @@ async function orchestrateGraphStream({
|
|
|
315
678
|
createdAt: Date.now(),
|
|
316
679
|
ttlMs: pendingTtlMs
|
|
317
680
|
};
|
|
681
|
+
activePendingRequests.set(record.token, record);
|
|
318
682
|
if (pendingStore) {
|
|
319
|
-
pendingStore.save(record).catch((error) => {
|
|
683
|
+
const savePromise = pendingStore.save(record).catch((error) => {
|
|
320
684
|
console.error("[orchestrator] failed to save pending request", error);
|
|
321
685
|
});
|
|
686
|
+
pendingRequestWrites.push(savePromise);
|
|
322
687
|
}
|
|
323
688
|
const clientMeta = record.schema.meta && typeof record.schema.meta === "object" && !Array.isArray(record.schema.meta) ? Object.fromEntries(
|
|
324
689
|
Object.entries(record.schema.meta).filter(
|
|
@@ -349,6 +714,34 @@ async function orchestrateGraphStream({
|
|
|
349
714
|
}))
|
|
350
715
|
}
|
|
351
716
|
});
|
|
717
|
+
emitTelemetryEvent({
|
|
718
|
+
config,
|
|
719
|
+
type: "interrupt.created",
|
|
720
|
+
correlation: { nodeId: record.node },
|
|
721
|
+
payload: {
|
|
722
|
+
interruptId: record.requestId,
|
|
723
|
+
requestId: record.requestId,
|
|
724
|
+
kind: record.schema.kind,
|
|
725
|
+
interactionMode: record.schema.kind === "text" ? "freeform" : record.options.length > 0 ? "static-options" : record.schema.schemaId ? "dynamic-picker" : "unknown",
|
|
726
|
+
...record.schema.schemaId ? { schemaId: record.schema.schemaId } : {},
|
|
727
|
+
...record.schema.schemaVersion ? { schemaVersion: record.schema.schemaVersion } : {},
|
|
728
|
+
...typeof record.schema.question === "string" && shouldCaptureTelemetryContent(telemetryConfig.captureContent, "output") ? { question: record.schema.question } : {},
|
|
729
|
+
...shouldCaptureTelemetryContent(
|
|
730
|
+
telemetryConfig.captureContent,
|
|
731
|
+
"output"
|
|
732
|
+
) ? {
|
|
733
|
+
options: record.options.map((option) => ({
|
|
734
|
+
id: option.id,
|
|
735
|
+
label: option.label,
|
|
736
|
+
...option.description ? { description: option.description } : {}
|
|
737
|
+
}))
|
|
738
|
+
} : {},
|
|
739
|
+
optionCount: record.options.length,
|
|
740
|
+
nodeId: record.node,
|
|
741
|
+
expiresAt: new Date(record.createdAt + record.ttlMs).toISOString()
|
|
742
|
+
},
|
|
743
|
+
flush: true
|
|
744
|
+
});
|
|
352
745
|
wroteHumanInput = true;
|
|
353
746
|
};
|
|
354
747
|
const forwardEmit = (event, payload) => {
|
|
@@ -376,6 +769,7 @@ async function orchestrateGraphStream({
|
|
|
376
769
|
if (event === "text-start") {
|
|
377
770
|
const node = payload?.node;
|
|
378
771
|
if (!node) return;
|
|
772
|
+
touchedNodes.add(node);
|
|
379
773
|
out.write({
|
|
380
774
|
type: "text-start",
|
|
381
775
|
node,
|
|
@@ -389,6 +783,7 @@ async function orchestrateGraphStream({
|
|
|
389
783
|
const node = payload?.node;
|
|
390
784
|
const delta = String(payload?.delta ?? "");
|
|
391
785
|
if (!node || !delta) return;
|
|
786
|
+
touchedNodes.add(node);
|
|
392
787
|
appendAccumulatedOutput(payload, node, delta);
|
|
393
788
|
out.write({
|
|
394
789
|
type: "text-delta",
|
|
@@ -403,6 +798,7 @@ async function orchestrateGraphStream({
|
|
|
403
798
|
if (event === "text-end") {
|
|
404
799
|
const node = payload?.node;
|
|
405
800
|
if (!node) return;
|
|
801
|
+
touchedNodes.add(node);
|
|
406
802
|
out.write({
|
|
407
803
|
type: "text-end",
|
|
408
804
|
node,
|
|
@@ -426,6 +822,7 @@ async function orchestrateGraphStream({
|
|
|
426
822
|
...payloadObj.opId ? { opId: payloadObj.opId } : {},
|
|
427
823
|
...payloadObj.input !== void 0 ? { input: payloadObj.input } : {}
|
|
428
824
|
});
|
|
825
|
+
if (payloadObj.node) touchedNodes.add(payloadObj.node);
|
|
429
826
|
return;
|
|
430
827
|
}
|
|
431
828
|
if (event === "tool-call-result") {
|
|
@@ -444,6 +841,7 @@ async function orchestrateGraphStream({
|
|
|
444
841
|
...payloadObj.structuredContent !== void 0 ? { structuredContent: payloadObj.structuredContent } : {},
|
|
445
842
|
...typeof payloadObj.isError === "boolean" ? { isError: payloadObj.isError } : {}
|
|
446
843
|
});
|
|
844
|
+
if (payloadObj.node) touchedNodes.add(payloadObj.node);
|
|
447
845
|
return;
|
|
448
846
|
}
|
|
449
847
|
if (event === "tool-call-error") {
|
|
@@ -460,21 +858,26 @@ async function orchestrateGraphStream({
|
|
|
460
858
|
...payloadObj.opId ? { opId: payloadObj.opId } : {},
|
|
461
859
|
message: String(payloadObj.message ?? "")
|
|
462
860
|
});
|
|
861
|
+
if (payloadObj.node) touchedNodes.add(payloadObj.node);
|
|
463
862
|
return;
|
|
464
863
|
}
|
|
465
864
|
if (event === "message") {
|
|
466
865
|
const node = payload?.node;
|
|
467
866
|
const text = String(payload?.content ?? "");
|
|
867
|
+
if (node) touchedNodes.add(node);
|
|
468
868
|
out.write({ type: "message", node, content: text });
|
|
469
869
|
return;
|
|
470
870
|
}
|
|
471
871
|
if (event === "structured_data") {
|
|
472
872
|
const payloadObj = payload;
|
|
473
873
|
const kind = payloadObj.kind === "set" || payloadObj.kind === "append" || payloadObj.kind === "text-delta" || payloadObj.kind === "final" ? payloadObj.kind : "final";
|
|
874
|
+
const streamId = typeof payloadObj.streamId === "string" && payloadObj.streamId ? payloadObj.streamId : nextStructuredStreamId();
|
|
875
|
+
structuredStreamIds.add(streamId);
|
|
876
|
+
if (payloadObj.node) touchedNodes.add(payloadObj.node);
|
|
474
877
|
out.write({
|
|
475
878
|
type: "structured-data",
|
|
476
879
|
node: payloadObj.node,
|
|
477
|
-
streamId
|
|
880
|
+
streamId,
|
|
478
881
|
dataType: typeof payloadObj.dataType === "string" && payloadObj.dataType ? payloadObj.dataType : "generic",
|
|
479
882
|
kind,
|
|
480
883
|
...typeof payloadObj.schemaId === "string" ? { schemaId: payloadObj.schemaId } : {},
|
|
@@ -504,10 +907,14 @@ async function orchestrateGraphStream({
|
|
|
504
907
|
});
|
|
505
908
|
pending.to = payload?.transitionTo ?? null;
|
|
506
909
|
pending.payload = payload?.payload ?? {};
|
|
910
|
+
const transition = payload;
|
|
911
|
+
pending.sourceNodeId = typeof transition.node === "string" ? transition.node : void 0;
|
|
912
|
+
pending.sourceWorkflowId = typeof transition.workflow === "string" ? transition.workflow : void 0;
|
|
507
913
|
return;
|
|
508
914
|
}
|
|
509
915
|
if (event === "interrupt") {
|
|
510
916
|
const p = payload;
|
|
917
|
+
if (typeof p?.node === "string") touchedNodes.add(p.node);
|
|
511
918
|
const local = {
|
|
512
919
|
node: p?.node,
|
|
513
920
|
workflow: p?.workflow,
|
|
@@ -522,11 +929,39 @@ async function orchestrateGraphStream({
|
|
|
522
929
|
const runLoop = async (runTraceSpan) => {
|
|
523
930
|
while (true) {
|
|
524
931
|
let workflowFinalState = null;
|
|
932
|
+
let workflowFinalGraphCheckpointId;
|
|
933
|
+
let sawWorkflowDone = false;
|
|
934
|
+
let workflowDoneData;
|
|
525
935
|
currentGraph.config = currentGraph.config || {};
|
|
526
936
|
currentGraph.config.emit = forwardEmit;
|
|
527
937
|
const threadId = currentGraph.config?.session?.id || sessionId || "anonymous-session";
|
|
528
|
-
const checkpointNs =
|
|
938
|
+
const checkpointNs = "";
|
|
529
939
|
namespacesUsed.add(checkpointNs);
|
|
940
|
+
const readLatestGraphCheckpointId = async () => {
|
|
941
|
+
const checkpointer = currentGraph.config?.checkpointer;
|
|
942
|
+
return checkpointer?.getLatestCheckpointId?.(runId, checkpointNs);
|
|
943
|
+
};
|
|
944
|
+
const readLatestGraphSnapshot = async () => {
|
|
945
|
+
if (typeof currentGraph.getState !== "function") {
|
|
946
|
+
const checkpointId2 = await readLatestGraphCheckpointId();
|
|
947
|
+
return checkpointId2 ? { state: currentState, checkpointId: checkpointId2 } : null;
|
|
948
|
+
}
|
|
949
|
+
const snapshot = await currentGraph.getState({
|
|
950
|
+
configurable: {
|
|
951
|
+
thread_id: runId,
|
|
952
|
+
checkpoint_ns: checkpointNs
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
const values = isRecord3(snapshot) && "values" in snapshot ? snapshot.values : snapshot;
|
|
956
|
+
const checkpointId = getGraphCheckpointId(snapshot) ?? await readLatestGraphCheckpointId();
|
|
957
|
+
if (!isGraphState(values)) {
|
|
958
|
+
return checkpointId ? { state: currentState, checkpointId } : null;
|
|
959
|
+
}
|
|
960
|
+
return {
|
|
961
|
+
state: values,
|
|
962
|
+
...checkpointId ? { checkpointId } : {}
|
|
963
|
+
};
|
|
964
|
+
};
|
|
530
965
|
if (debugEnabled) {
|
|
531
966
|
out.write({
|
|
532
967
|
type: "status",
|
|
@@ -536,13 +971,14 @@ async function orchestrateGraphStream({
|
|
|
536
971
|
const isResume = Boolean(currentGraph.config?.resume);
|
|
537
972
|
const resumeUpdate = currentGraph.config?.resumeUpdate;
|
|
538
973
|
const resumeValue = currentGraph.config?.resumeValue;
|
|
974
|
+
const resumeCheckpointId = currentGraph.config?.resumeCheckpointId;
|
|
539
975
|
const invokeState = isResume ? resumeValue !== void 0 && resumeUpdate ? new import_langgraph.Command({ resume: resumeValue, update: resumeUpdate }) : resumeValue !== void 0 ? new import_langgraph.Command({ resume: resumeValue }) : resumeUpdate ? new import_langgraph.Command({ update: resumeUpdate }) : null : currentState;
|
|
540
976
|
const runtimeStream = currentGraph.streamEvents(invokeState, {
|
|
541
977
|
version: "v2",
|
|
542
978
|
configurable: {
|
|
543
979
|
thread_id: runId,
|
|
544
|
-
|
|
545
|
-
|
|
980
|
+
checkpoint_ns: checkpointNs,
|
|
981
|
+
...isResume && resumeCheckpointId ? { checkpoint_id: resumeCheckpointId } : {}
|
|
546
982
|
}
|
|
547
983
|
});
|
|
548
984
|
if (debugEnabled) {
|
|
@@ -557,20 +993,58 @@ async function orchestrateGraphStream({
|
|
|
557
993
|
});
|
|
558
994
|
for await (const chunk of uiStream) {
|
|
559
995
|
if (finished) break;
|
|
560
|
-
|
|
996
|
+
if ("node" in chunk && typeof chunk.node === "string") {
|
|
997
|
+
touchedNodes.add(chunk.node);
|
|
998
|
+
}
|
|
561
999
|
if (chunk.type === "done") {
|
|
562
|
-
|
|
563
|
-
|
|
1000
|
+
sawWorkflowDone = true;
|
|
1001
|
+
workflowDoneData = chunk.data;
|
|
1002
|
+
continue;
|
|
564
1003
|
}
|
|
1004
|
+
out.write(chunk);
|
|
565
1005
|
}
|
|
566
1006
|
if (finished) return;
|
|
1007
|
+
if (sawWorkflowDone) {
|
|
1008
|
+
const graphSnapshot = await readLatestGraphSnapshot();
|
|
1009
|
+
workflowFinalGraphCheckpointId = graphSnapshot?.checkpointId;
|
|
1010
|
+
workflowFinalState = isGraphState(workflowDoneData) ? workflowDoneData : graphSnapshot?.state ?? currentState;
|
|
1011
|
+
}
|
|
567
1012
|
const transitionTo = pending.to;
|
|
568
1013
|
const transitionPayload = pending.payload;
|
|
1014
|
+
const sourceNodeId = pending.sourceNodeId;
|
|
1015
|
+
const sourceWorkflowId = pending.sourceWorkflowId;
|
|
569
1016
|
pending.to = null;
|
|
570
1017
|
pending.payload = {};
|
|
1018
|
+
pending.sourceNodeId = void 0;
|
|
1019
|
+
pending.sourceWorkflowId = void 0;
|
|
571
1020
|
if (transitionTo) {
|
|
572
1021
|
try {
|
|
573
1022
|
const nextWorkflow = await selectWorkflow(transitionTo);
|
|
1023
|
+
const sourceTelemetry = isRecord3(config.telemetry) ? config.telemetry : {};
|
|
1024
|
+
config = prepareWorkflowTelemetry({
|
|
1025
|
+
config,
|
|
1026
|
+
workflow: nextWorkflow,
|
|
1027
|
+
runId,
|
|
1028
|
+
sessionId,
|
|
1029
|
+
knownWorkflowIds
|
|
1030
|
+
});
|
|
1031
|
+
const targetTelemetry = isRecord3(config.telemetry) ? config.telemetry : {};
|
|
1032
|
+
emitTelemetryEvent({
|
|
1033
|
+
config,
|
|
1034
|
+
type: "workflow.transitioned",
|
|
1035
|
+
...sourceNodeId ? { correlation: { nodeId: sourceNodeId } } : {},
|
|
1036
|
+
payload: {
|
|
1037
|
+
sourceNodeId: sourceNodeId ?? "",
|
|
1038
|
+
sourceWorkflowId: sourceWorkflowId ?? String(currentState.currentWorkflow),
|
|
1039
|
+
...typeof sourceTelemetry.correlation === "object" && sourceTelemetry.correlation && typeof sourceTelemetry.correlation.workflowRevisionId === "string" ? {
|
|
1040
|
+
sourceWorkflowRevisionId: sourceTelemetry.correlation.workflowRevisionId
|
|
1041
|
+
} : {},
|
|
1042
|
+
targetWorkflowId: transitionTo,
|
|
1043
|
+
...typeof targetTelemetry.correlation === "object" && targetTelemetry.correlation && typeof targetTelemetry.correlation.workflowRevisionId === "string" ? {
|
|
1044
|
+
targetWorkflowRevisionId: targetTelemetry.correlation.workflowRevisionId
|
|
1045
|
+
} : {}
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
574
1048
|
const nextGraph = await (0, import_runtime.createExecutionGraph)(nextWorkflow, {
|
|
575
1049
|
...config,
|
|
576
1050
|
emit: forwardEmit
|
|
@@ -585,6 +1059,7 @@ async function orchestrateGraphStream({
|
|
|
585
1059
|
currentState = {
|
|
586
1060
|
...currentState,
|
|
587
1061
|
currentWorkflow: transitionTo,
|
|
1062
|
+
config,
|
|
588
1063
|
input: newInput,
|
|
589
1064
|
data: mergedData,
|
|
590
1065
|
ui: {}
|
|
@@ -607,12 +1082,27 @@ async function orchestrateGraphStream({
|
|
|
607
1082
|
}
|
|
608
1083
|
if (workflowFinalState) {
|
|
609
1084
|
if (workflowFinalState && pendingRecordToken) {
|
|
1085
|
+
if (pendingRequestWrites.length > 0) {
|
|
1086
|
+
await Promise.all(pendingRequestWrites);
|
|
1087
|
+
}
|
|
610
1088
|
if (pendingStore) {
|
|
611
1089
|
await pendingStore.update(pendingRecordToken, {
|
|
612
|
-
state: workflowFinalState
|
|
1090
|
+
state: workflowFinalState,
|
|
1091
|
+
...workflowFinalGraphCheckpointId ? { graphCheckpointId: workflowFinalGraphCheckpointId } : {}
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
const pendingRequest = activePendingRequests.get(pendingRecordToken);
|
|
1095
|
+
if (pendingRequest) {
|
|
1096
|
+
activePendingRequests.set(pendingRecordToken, {
|
|
1097
|
+
...pendingRequest,
|
|
1098
|
+
state: workflowFinalState,
|
|
1099
|
+
...workflowFinalGraphCheckpointId ? { graphCheckpointId: workflowFinalGraphCheckpointId } : {}
|
|
613
1100
|
});
|
|
614
1101
|
}
|
|
615
1102
|
}
|
|
1103
|
+
if (!pendingRecordToken && pendingRequestWrites.length > 0) {
|
|
1104
|
+
await Promise.all(pendingRequestWrites);
|
|
1105
|
+
}
|
|
616
1106
|
const shouldKeepFrameworkState = Boolean(pendingRecordToken) || Boolean(workflowFinalState?.awaitingHumanInput);
|
|
617
1107
|
if (!shouldKeepFrameworkState) {
|
|
618
1108
|
try {
|
|
@@ -637,15 +1127,106 @@ async function orchestrateGraphStream({
|
|
|
637
1127
|
"kortyx.run.final_workflow": String(currentState.currentWorkflow)
|
|
638
1128
|
});
|
|
639
1129
|
runTraceSpan.end?.(runTraceEndArgs());
|
|
1130
|
+
const resolvedSessionId2 = config?.session?.id || sessionId || "";
|
|
1131
|
+
if (resolvedSessionId2 && frameworkAdapter?.sessionCheckpoints) {
|
|
1132
|
+
try {
|
|
1133
|
+
const checkpoint = await frameworkAdapter.sessionCheckpoints.append(
|
|
1134
|
+
{
|
|
1135
|
+
sessionId: resolvedSessionId2,
|
|
1136
|
+
runId,
|
|
1137
|
+
...workflowFinalGraphCheckpointId ? { graphCheckpointId: workflowFinalGraphCheckpointId } : {},
|
|
1138
|
+
workflow: String(workflowFinalState.currentWorkflow),
|
|
1139
|
+
state: workflowFinalState,
|
|
1140
|
+
nodes: Array.from(touchedNodes),
|
|
1141
|
+
structuredStreamIds: Array.from(structuredStreamIds),
|
|
1142
|
+
pendingRequests: Array.from(activePendingRequests.values())
|
|
1143
|
+
}
|
|
1144
|
+
);
|
|
1145
|
+
out.write({
|
|
1146
|
+
type: "checkpoint",
|
|
1147
|
+
id: checkpoint.id,
|
|
1148
|
+
sessionId: checkpoint.sessionId,
|
|
1149
|
+
turnIndex: checkpoint.turnIndex,
|
|
1150
|
+
...checkpoint.label ? { label: checkpoint.label } : {}
|
|
1151
|
+
});
|
|
1152
|
+
emitTelemetryEvent({
|
|
1153
|
+
config,
|
|
1154
|
+
type: "session.checkpointed",
|
|
1155
|
+
payload: {
|
|
1156
|
+
checkpointId: checkpoint.id,
|
|
1157
|
+
turnIndex: checkpoint.turnIndex,
|
|
1158
|
+
...checkpoint.parentCheckpointId ? { parentCheckpointId: checkpoint.parentCheckpointId } : {},
|
|
1159
|
+
nodes: checkpoint.nodes
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
} catch (error) {
|
|
1163
|
+
console.error("[orchestrator] session checkpoint failed", error);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
640
1166
|
out.write({ type: "done", data: workflowFinalState });
|
|
641
1167
|
out.end();
|
|
642
1168
|
return;
|
|
643
1169
|
}
|
|
1170
|
+
const naturalGraphSnapshot = await readLatestGraphSnapshot();
|
|
1171
|
+
const naturalFinalState = naturalGraphSnapshot?.state ?? currentState;
|
|
1172
|
+
if (pendingRequestWrites.length > 0) {
|
|
1173
|
+
await Promise.all(pendingRequestWrites);
|
|
1174
|
+
}
|
|
1175
|
+
if (pendingRecordToken && naturalGraphSnapshot?.checkpointId) {
|
|
1176
|
+
if (pendingStore) {
|
|
1177
|
+
await pendingStore.update(pendingRecordToken, {
|
|
1178
|
+
state: naturalFinalState,
|
|
1179
|
+
graphCheckpointId: naturalGraphSnapshot.checkpointId
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
const pendingRequest = activePendingRequests.get(pendingRecordToken);
|
|
1183
|
+
if (pendingRequest) {
|
|
1184
|
+
activePendingRequests.set(pendingRecordToken, {
|
|
1185
|
+
...pendingRequest,
|
|
1186
|
+
state: naturalFinalState,
|
|
1187
|
+
graphCheckpointId: naturalGraphSnapshot.checkpointId
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
const resolvedSessionId = config?.session?.id || sessionId || "";
|
|
1192
|
+
if (resolvedSessionId && frameworkAdapter?.sessionCheckpoints) {
|
|
1193
|
+
try {
|
|
1194
|
+
const checkpoint = await frameworkAdapter.sessionCheckpoints.append({
|
|
1195
|
+
sessionId: resolvedSessionId,
|
|
1196
|
+
runId,
|
|
1197
|
+
...naturalGraphSnapshot?.checkpointId ? { graphCheckpointId: naturalGraphSnapshot.checkpointId } : {},
|
|
1198
|
+
workflow: String(naturalFinalState.currentWorkflow),
|
|
1199
|
+
state: naturalFinalState,
|
|
1200
|
+
nodes: Array.from(touchedNodes),
|
|
1201
|
+
structuredStreamIds: Array.from(structuredStreamIds),
|
|
1202
|
+
pendingRequests: Array.from(activePendingRequests.values())
|
|
1203
|
+
});
|
|
1204
|
+
out.write({
|
|
1205
|
+
type: "checkpoint",
|
|
1206
|
+
id: checkpoint.id,
|
|
1207
|
+
sessionId: checkpoint.sessionId,
|
|
1208
|
+
turnIndex: checkpoint.turnIndex,
|
|
1209
|
+
...checkpoint.label ? { label: checkpoint.label } : {}
|
|
1210
|
+
});
|
|
1211
|
+
emitTelemetryEvent({
|
|
1212
|
+
config,
|
|
1213
|
+
type: "session.checkpointed",
|
|
1214
|
+
payload: {
|
|
1215
|
+
checkpointId: checkpoint.id,
|
|
1216
|
+
turnIndex: checkpoint.turnIndex,
|
|
1217
|
+
...checkpoint.parentCheckpointId ? { parentCheckpointId: checkpoint.parentCheckpointId } : {},
|
|
1218
|
+
nodes: checkpoint.nodes
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
console.error("[orchestrator] session checkpoint failed", error);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
644
1225
|
runTraceSpan.setAttributes?.({
|
|
645
|
-
"kortyx.run.final_workflow": String(
|
|
1226
|
+
"kortyx.run.final_workflow": String(naturalFinalState.currentWorkflow)
|
|
646
1227
|
});
|
|
647
1228
|
runTraceSpan.end?.(runTraceEndArgs());
|
|
648
|
-
out.write({ type: "done" });
|
|
1229
|
+
out.write({ type: "done", data: naturalFinalState });
|
|
649
1230
|
out.end();
|
|
650
1231
|
return;
|
|
651
1232
|
}
|
|
@@ -671,6 +1252,7 @@ async function orchestrateGraphStream({
|
|
|
671
1252
|
}) : runLoop(fallbackRunSpan ?? {});
|
|
672
1253
|
runPromise.catch((err) => {
|
|
673
1254
|
fallbackRunSpan?.fail?.(err);
|
|
1255
|
+
emitResumeFailure(err);
|
|
674
1256
|
console.error("[error:orchestrateGraphStream]", err);
|
|
675
1257
|
out.write({
|
|
676
1258
|
type: "error",
|
|
@@ -683,11 +1265,16 @@ async function orchestrateGraphStream({
|
|
|
683
1265
|
}
|
|
684
1266
|
|
|
685
1267
|
// src/interrupt/resume-handler.ts
|
|
686
|
-
var
|
|
1268
|
+
var isRecord4 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1269
|
+
var responseFromSelection = (selected) => {
|
|
1270
|
+
if (selected.length === 0) return void 0;
|
|
1271
|
+
if (selected.length === 1) return selected[0];
|
|
1272
|
+
return JSON.stringify(selected);
|
|
1273
|
+
};
|
|
687
1274
|
function parseResumeMeta(msg) {
|
|
688
1275
|
if (!msg || !msg.metadata) return null;
|
|
689
1276
|
const raw = msg.metadata.resume;
|
|
690
|
-
if (!
|
|
1277
|
+
if (!isRecord4(raw)) return null;
|
|
691
1278
|
const token = typeof raw.token === "string" ? raw.token : "";
|
|
692
1279
|
const requestId = typeof raw.requestId === "string" ? raw.requestId : "";
|
|
693
1280
|
const cancel = raw.cancel === true;
|
|
@@ -695,10 +1282,10 @@ function parseResumeMeta(msg) {
|
|
|
695
1282
|
const rawSelected = raw.selected;
|
|
696
1283
|
if (typeof rawSelected === "string") selected = [rawSelected];
|
|
697
1284
|
else if (Array.isArray(rawSelected)) selected = rawSelected.map(String);
|
|
698
|
-
else if (
|
|
1285
|
+
else if (isRecord4(raw.choice) && typeof raw.choice.id === "string")
|
|
699
1286
|
selected = [raw.choice.id];
|
|
700
1287
|
else if (Array.isArray(raw.choices))
|
|
701
|
-
selected = raw.choices.map((c) =>
|
|
1288
|
+
selected = raw.choices.map((c) => isRecord4(c) ? c.id : void 0).filter((id) => typeof id === "string");
|
|
702
1289
|
if (!token || !requestId) return null;
|
|
703
1290
|
return { token, requestId, selected, cancel };
|
|
704
1291
|
}
|
|
@@ -707,6 +1294,7 @@ async function tryPrepareResumeStream({
|
|
|
707
1294
|
sessionId,
|
|
708
1295
|
config,
|
|
709
1296
|
selectWorkflow,
|
|
1297
|
+
knownWorkflowIds,
|
|
710
1298
|
defaultWorkflowId,
|
|
711
1299
|
applyResumeSelection,
|
|
712
1300
|
frameworkAdapter
|
|
@@ -724,6 +1312,21 @@ async function tryPrepareResumeStream({
|
|
|
724
1312
|
}
|
|
725
1313
|
if (meta.cancel) {
|
|
726
1314
|
await store.delete(pending.token);
|
|
1315
|
+
emitTelemetryEvent({
|
|
1316
|
+
config,
|
|
1317
|
+
type: "interrupt.cancelled",
|
|
1318
|
+
correlation: {
|
|
1319
|
+
runId: pending.runId,
|
|
1320
|
+
sessionId,
|
|
1321
|
+
workflowId: pending.workflow,
|
|
1322
|
+
nodeId: pending.node
|
|
1323
|
+
},
|
|
1324
|
+
payload: {
|
|
1325
|
+
interruptId: pending.requestId,
|
|
1326
|
+
reason: "cancelled_by_client"
|
|
1327
|
+
},
|
|
1328
|
+
flush: true
|
|
1329
|
+
});
|
|
727
1330
|
return null;
|
|
728
1331
|
}
|
|
729
1332
|
console.log(
|
|
@@ -732,10 +1335,10 @@ async function tryPrepareResumeStream({
|
|
|
732
1335
|
)} sessionId=${sessionId}`
|
|
733
1336
|
);
|
|
734
1337
|
const resumeData = applyResumeSelection ? applyResumeSelection({ pending, selected: meta.selected }) : meta.selected?.length ? { coordinates: String(meta.selected[0]) } : {};
|
|
735
|
-
const resumeDataPatch =
|
|
736
|
-
const pendingMeta =
|
|
737
|
-
const resumeStatePatch =
|
|
738
|
-
const pendingData =
|
|
1338
|
+
const resumeDataPatch = isRecord4(resumeData) ? resumeData : {};
|
|
1339
|
+
const pendingMeta = isRecord4(pending.schema?.meta) ? pending.schema.meta : {};
|
|
1340
|
+
const resumeStatePatch = isRecord4(pendingMeta.__kortyxResumeStatePatch) ? pendingMeta.__kortyxResumeStatePatch : void 0;
|
|
1341
|
+
const pendingData = isRecord4(pending.state?.data) ? pending.state?.data : {};
|
|
739
1342
|
const workflowId = typeof pending.workflow === "string" && pending.workflow.trim() ? pending.workflow : typeof defaultWorkflowId === "string" && defaultWorkflowId.trim() ? defaultWorkflowId : "job-search";
|
|
740
1343
|
const resumedState = {
|
|
741
1344
|
// For static breakpoints, resume with null input (set in orchestrator),
|
|
@@ -753,6 +1356,14 @@ async function tryPrepareResumeStream({
|
|
|
753
1356
|
}
|
|
754
1357
|
};
|
|
755
1358
|
const wf = await selectWorkflow(resumedState.currentWorkflow);
|
|
1359
|
+
const telemetryConfig = prepareWorkflowTelemetry({
|
|
1360
|
+
config,
|
|
1361
|
+
workflow: wf,
|
|
1362
|
+
runId: pending.runId,
|
|
1363
|
+
sessionId,
|
|
1364
|
+
knownWorkflowIds
|
|
1365
|
+
});
|
|
1366
|
+
resumedState.config = telemetryConfig;
|
|
756
1367
|
const resumeUpdate = {};
|
|
757
1368
|
if (Object.keys(resumeDataPatch).length > 0) {
|
|
758
1369
|
resumeUpdate.data = {
|
|
@@ -765,25 +1376,54 @@ async function tryPrepareResumeStream({
|
|
|
765
1376
|
}
|
|
766
1377
|
const hasResumeUpdate = Object.keys(resumeUpdate).length > 0;
|
|
767
1378
|
const resumeValue = meta.selected?.length && pending.schema.kind === "multi-choice" ? meta.selected.map((x) => String(x)) : meta.selected?.length ? String(meta.selected[0]) : void 0;
|
|
1379
|
+
const resumeCheckpointId = typeof pending.graphCheckpointId === "string" && pending.graphCheckpointId.length > 0 ? pending.graphCheckpointId : void 0;
|
|
768
1380
|
const resumedGraph = await (0, import_runtime2.createExecutionGraph)(wf, {
|
|
769
|
-
...
|
|
1381
|
+
...telemetryConfig,
|
|
770
1382
|
resume: true,
|
|
771
1383
|
...resumeValue !== void 0 ? { resumeValue } : {},
|
|
1384
|
+
...resumeCheckpointId ? { resumeCheckpointId } : {},
|
|
772
1385
|
...hasResumeUpdate ? { resumeUpdate } : {}
|
|
773
1386
|
});
|
|
774
1387
|
await store.delete(pending.token);
|
|
1388
|
+
const response = responseFromSelection(meta.selected);
|
|
1389
|
+
const telemetry = isRecord4(telemetryConfig.telemetry) ? telemetryConfig.telemetry : {};
|
|
1390
|
+
const responseCaptured = Boolean(
|
|
1391
|
+
response && shouldCaptureTelemetryContent(telemetry.captureContent, "input")
|
|
1392
|
+
);
|
|
1393
|
+
emitTelemetryEvent({
|
|
1394
|
+
config: telemetryConfig,
|
|
1395
|
+
type: "interrupt.resolved",
|
|
1396
|
+
correlation: {
|
|
1397
|
+
runId: pending.runId,
|
|
1398
|
+
sessionId,
|
|
1399
|
+
workflowId,
|
|
1400
|
+
nodeId: pending.node
|
|
1401
|
+
},
|
|
1402
|
+
payload: {
|
|
1403
|
+
interruptId: pending.requestId,
|
|
1404
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1405
|
+
resumeOutcome: "resumed",
|
|
1406
|
+
responseCaptured,
|
|
1407
|
+
...responseCaptured && response ? { response } : {}
|
|
1408
|
+
},
|
|
1409
|
+
flush: true
|
|
1410
|
+
});
|
|
775
1411
|
const args = {
|
|
776
1412
|
sessionId,
|
|
777
1413
|
runId: pending.runId,
|
|
778
1414
|
graph: resumedGraph,
|
|
779
1415
|
state: resumedState,
|
|
780
1416
|
config: {
|
|
781
|
-
...
|
|
1417
|
+
...telemetryConfig,
|
|
782
1418
|
resume: true,
|
|
1419
|
+
telemetryInterruptId: pending.requestId,
|
|
1420
|
+
telemetryInterruptNodeId: pending.node,
|
|
783
1421
|
...resumeValue !== void 0 ? { resumeValue } : {},
|
|
1422
|
+
...resumeCheckpointId ? { resumeCheckpointId } : {},
|
|
784
1423
|
...hasResumeUpdate ? { resumeUpdate } : {}
|
|
785
1424
|
},
|
|
786
1425
|
selectWorkflow,
|
|
1426
|
+
knownWorkflowIds,
|
|
787
1427
|
frameworkAdapter
|
|
788
1428
|
};
|
|
789
1429
|
const stream = await orchestrateGraphStream(args);
|
|
@@ -811,12 +1451,13 @@ async function streamChat({
|
|
|
811
1451
|
loadRuntimeConfig,
|
|
812
1452
|
selectWorkflow,
|
|
813
1453
|
workflowRegistry,
|
|
1454
|
+
knownWorkflowIds,
|
|
814
1455
|
frameworkAdapter,
|
|
815
1456
|
getProvider,
|
|
816
1457
|
applyResumeSelection
|
|
817
1458
|
}) {
|
|
818
1459
|
const config = await loadRuntimeConfig(options);
|
|
819
|
-
|
|
1460
|
+
let runtimeConfig = {
|
|
820
1461
|
...config,
|
|
821
1462
|
getProvider,
|
|
822
1463
|
...frameworkAdapter ? { checkpointer: frameworkAdapter.checkpointer } : {}
|
|
@@ -859,22 +1500,60 @@ async function streamChat({
|
|
|
859
1500
|
sessionId: resolvedSessionId,
|
|
860
1501
|
config: runtimeConfig,
|
|
861
1502
|
selectWorkflow: workflowSelector,
|
|
1503
|
+
knownWorkflowIds,
|
|
862
1504
|
...frameworkAdapter ? { frameworkAdapter } : {},
|
|
863
1505
|
...defaultWorkflowId ? { defaultWorkflowId } : {},
|
|
864
1506
|
...applyResumeSelection ? { applyResumeSelection } : {}
|
|
865
1507
|
});
|
|
866
1508
|
if (resumeStream) return resumeStream;
|
|
867
1509
|
const runId = (0, import_runtime3.makeRequestId)("run");
|
|
868
|
-
|
|
1510
|
+
let headCheckpoint = !isResumeRequest && frameworkAdapter?.sessionCheckpoints ? await frameworkAdapter.sessionCheckpoints.getHead(resolvedSessionId) : null;
|
|
1511
|
+
if (!isResumeRequest && !headCheckpoint && frameworkAdapter?.sessionCheckpoints) {
|
|
1512
|
+
headCheckpoint = await frameworkAdapter.sessionCheckpoints.append({
|
|
1513
|
+
sessionId: resolvedSessionId,
|
|
1514
|
+
runId,
|
|
1515
|
+
workflow: String(baseState.currentWorkflow),
|
|
1516
|
+
state: {
|
|
1517
|
+
...baseState,
|
|
1518
|
+
input: "",
|
|
1519
|
+
awaitingHumanInput: false
|
|
1520
|
+
},
|
|
1521
|
+
nodes: [],
|
|
1522
|
+
structuredStreamIds: [],
|
|
1523
|
+
pendingRequests: [],
|
|
1524
|
+
label: "session start"
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
const currentWorkflow = requestedWorkflowId ? baseState.currentWorkflow : headCheckpoint?.state.currentWorkflow ?? baseState.currentWorkflow;
|
|
869
1528
|
const selectedWorkflow = await workflowSelector(currentWorkflow);
|
|
1529
|
+
runtimeConfig = prepareWorkflowTelemetry({
|
|
1530
|
+
config: runtimeConfig,
|
|
1531
|
+
workflow: selectedWorkflow,
|
|
1532
|
+
runId,
|
|
1533
|
+
sessionId: resolvedSessionId,
|
|
1534
|
+
knownWorkflowIds
|
|
1535
|
+
});
|
|
870
1536
|
const graph = await (0, import_runtime3.createExecutionGraph)(selectedWorkflow, runtimeConfig);
|
|
1537
|
+
const initialState = headCheckpoint ? {
|
|
1538
|
+
...headCheckpoint.state,
|
|
1539
|
+
input,
|
|
1540
|
+
config: runtimeConfig,
|
|
1541
|
+
currentWorkflow,
|
|
1542
|
+
awaitingHumanInput: false,
|
|
1543
|
+
runtime: {
|
|
1544
|
+
...headCheckpoint.state.runtime ?? {},
|
|
1545
|
+
...runtime.requestedWorkflow ? { requestedWorkflow: runtime.requestedWorkflow } : {},
|
|
1546
|
+
...runtime.priorMessages ? { priorMessages: runtime.priorMessages } : {}
|
|
1547
|
+
}
|
|
1548
|
+
} : { ...baseState, config: runtimeConfig, currentWorkflow };
|
|
871
1549
|
const orchestratedStream = await orchestrateGraphStream({
|
|
872
1550
|
sessionId: resolvedSessionId,
|
|
873
1551
|
runId,
|
|
874
1552
|
graph,
|
|
875
|
-
state:
|
|
1553
|
+
state: initialState,
|
|
876
1554
|
config: runtimeConfig,
|
|
877
1555
|
selectWorkflow: workflowSelector,
|
|
1556
|
+
knownWorkflowIds,
|
|
878
1557
|
...frameworkAdapter ? { frameworkAdapter } : {}
|
|
879
1558
|
});
|
|
880
1559
|
return orchestratedStream;
|
|
@@ -928,6 +1607,32 @@ var parseAgentProcessOptions = (value) => {
|
|
|
928
1607
|
if (value === void 0) return void 0;
|
|
929
1608
|
return parseSchema(agentProcessOptionsSchema, value);
|
|
930
1609
|
};
|
|
1610
|
+
var hydratePendingGraphCheckpoint = async (frameworkAdapter, request) => {
|
|
1611
|
+
if (request.graphCheckpointId) return request;
|
|
1612
|
+
const checkpointer = frameworkAdapter.checkpointer;
|
|
1613
|
+
const graphCheckpointId = await checkpointer?.getLatestCheckpointId?.(
|
|
1614
|
+
request.runId,
|
|
1615
|
+
request.workflow
|
|
1616
|
+
) ?? await checkpointer?.getLatestCheckpointId?.(request.runId, "");
|
|
1617
|
+
return graphCheckpointId ? { ...request, graphCheckpointId } : request;
|
|
1618
|
+
};
|
|
1619
|
+
var clearPendingGraphWrites = async (frameworkAdapter, request) => {
|
|
1620
|
+
if (!request.graphCheckpointId) return;
|
|
1621
|
+
const checkpointer = frameworkAdapter.checkpointer;
|
|
1622
|
+
if (!checkpointer?.deleteCheckpointWrites) return;
|
|
1623
|
+
await Promise.all([
|
|
1624
|
+
checkpointer.deleteCheckpointWrites(
|
|
1625
|
+
request.runId,
|
|
1626
|
+
request.workflow,
|
|
1627
|
+
request.graphCheckpointId
|
|
1628
|
+
),
|
|
1629
|
+
checkpointer.deleteCheckpointWrites(
|
|
1630
|
+
request.runId,
|
|
1631
|
+
"",
|
|
1632
|
+
request.graphCheckpointId
|
|
1633
|
+
)
|
|
1634
|
+
]);
|
|
1635
|
+
};
|
|
931
1636
|
function createAgent(args) {
|
|
932
1637
|
const parsedArgs = parseCreateAgentArgs(args);
|
|
933
1638
|
const {
|
|
@@ -962,6 +1667,22 @@ function createAgent(args) {
|
|
|
962
1667
|
fallbackId: resolvedDefaultWorkflowId ?? "general-chat"
|
|
963
1668
|
});
|
|
964
1669
|
})();
|
|
1670
|
+
const registeredWorkflowsPromise = registryPromise.then(
|
|
1671
|
+
(registry) => registry?.list?.()
|
|
1672
|
+
);
|
|
1673
|
+
const projectTopology = async (options) => {
|
|
1674
|
+
const registeredWorkflows = await registeredWorkflowsPromise;
|
|
1675
|
+
if (!registeredWorkflows?.length) return [];
|
|
1676
|
+
const knownWorkflowIds = registeredWorkflows.map((workflow) => workflow.id);
|
|
1677
|
+
return registeredWorkflows.map(
|
|
1678
|
+
(workflow) => projectWorkflowTopology({
|
|
1679
|
+
workflow,
|
|
1680
|
+
environment: options.environment,
|
|
1681
|
+
service: options.service,
|
|
1682
|
+
knownWorkflowIds
|
|
1683
|
+
})
|
|
1684
|
+
);
|
|
1685
|
+
};
|
|
965
1686
|
const streamChat2 = async (messages, options) => {
|
|
966
1687
|
const parsedOptions = parseAgentProcessOptions(options);
|
|
967
1688
|
const registry = await registryPromise;
|
|
@@ -970,11 +1691,18 @@ function createAgent(args) {
|
|
|
970
1691
|
"createAgent requires workflows, workflowsDir, or workflowRegistry."
|
|
971
1692
|
);
|
|
972
1693
|
}
|
|
1694
|
+
const registeredWorkflows = await registeredWorkflowsPromise.catch(
|
|
1695
|
+
() => void 0
|
|
1696
|
+
);
|
|
1697
|
+
const knownWorkflowIds = registeredWorkflows?.map(
|
|
1698
|
+
(workflow) => workflow.id
|
|
1699
|
+
);
|
|
973
1700
|
return streamChat({
|
|
974
1701
|
...resolvedDefaultWorkflowId ? { defaultWorkflowId: resolvedDefaultWorkflowId } : {},
|
|
975
1702
|
messages,
|
|
976
1703
|
options: parsedOptions,
|
|
977
1704
|
workflowRegistry: registry,
|
|
1705
|
+
...knownWorkflowIds ? { knownWorkflowIds } : {},
|
|
978
1706
|
frameworkAdapter: resolvedFrameworkAdapter,
|
|
979
1707
|
getProvider: resolvedGetProvider,
|
|
980
1708
|
loadRuntimeConfig: (runtimeOptions) => ({
|
|
@@ -988,17 +1716,107 @@ function createAgent(args) {
|
|
|
988
1716
|
})
|
|
989
1717
|
});
|
|
990
1718
|
};
|
|
991
|
-
|
|
1719
|
+
const listCheckpoints = (sessionId) => resolvedFrameworkAdapter.sessionCheckpoints.list(sessionId);
|
|
1720
|
+
const getCheckpoint = (id) => resolvedFrameworkAdapter.sessionCheckpoints.get(id);
|
|
1721
|
+
const rollbackTo = async (id) => {
|
|
1722
|
+
const target = await resolvedFrameworkAdapter.sessionCheckpoints.get(id);
|
|
1723
|
+
const result = await resolvedFrameworkAdapter.sessionCheckpoints.rollbackTo(id);
|
|
1724
|
+
const activePendingRequests = await Promise.all(
|
|
1725
|
+
result.activePendingRequests.map(
|
|
1726
|
+
(request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
|
|
1727
|
+
)
|
|
1728
|
+
);
|
|
1729
|
+
await Promise.all(
|
|
1730
|
+
activePendingRequests.map(
|
|
1731
|
+
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|
|
1732
|
+
)
|
|
1733
|
+
);
|
|
1734
|
+
await Promise.all([
|
|
1735
|
+
...result.invalidatedInterruptTokens.map(
|
|
1736
|
+
(token) => resolvedFrameworkAdapter.pendingRequests.delete(token)
|
|
1737
|
+
),
|
|
1738
|
+
...activePendingRequests.map(
|
|
1739
|
+
(request) => resolvedFrameworkAdapter.pendingRequests.save(request)
|
|
1740
|
+
)
|
|
1741
|
+
]);
|
|
1742
|
+
if (target) {
|
|
1743
|
+
emitTelemetryEvent({
|
|
1744
|
+
config: { ...telemetry ? { telemetry } : {} },
|
|
1745
|
+
type: "session.rolled_back",
|
|
1746
|
+
correlation: {
|
|
1747
|
+
runId: target.runId,
|
|
1748
|
+
sessionId: target.sessionId,
|
|
1749
|
+
workflowId: target.workflow
|
|
1750
|
+
},
|
|
1751
|
+
payload: {
|
|
1752
|
+
checkpointId: target.id,
|
|
1753
|
+
invalidatedInterruptIds: result.invalidatedInterruptTokens
|
|
1754
|
+
}
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
return { ...result, activePendingRequests };
|
|
1758
|
+
};
|
|
1759
|
+
const fork = async (id, options) => {
|
|
1760
|
+
const result = await resolvedFrameworkAdapter.sessionCheckpoints.fork(
|
|
1761
|
+
id,
|
|
1762
|
+
options
|
|
1763
|
+
);
|
|
1764
|
+
const activePendingRequests = await Promise.all(
|
|
1765
|
+
result.checkpoint.activePendingRequests.map(
|
|
1766
|
+
(request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
|
|
1767
|
+
)
|
|
1768
|
+
);
|
|
1769
|
+
await Promise.all(
|
|
1770
|
+
activePendingRequests.map(
|
|
1771
|
+
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|
|
1772
|
+
)
|
|
1773
|
+
);
|
|
1774
|
+
await Promise.all(
|
|
1775
|
+
activePendingRequests.map(
|
|
1776
|
+
(request) => resolvedFrameworkAdapter.pendingRequests.save(request)
|
|
1777
|
+
)
|
|
1778
|
+
);
|
|
1779
|
+
emitTelemetryEvent({
|
|
1780
|
+
config: { ...telemetry ? { telemetry } : {} },
|
|
1781
|
+
type: "session.forked",
|
|
1782
|
+
correlation: {
|
|
1783
|
+
runId: result.checkpoint.runId,
|
|
1784
|
+
sessionId: result.checkpoint.sessionId,
|
|
1785
|
+
workflowId: result.checkpoint.workflow
|
|
1786
|
+
},
|
|
1787
|
+
payload: {
|
|
1788
|
+
newSessionId: result.sessionId,
|
|
1789
|
+
parentSessionId: result.parentSessionId,
|
|
1790
|
+
forkedFromCheckpointId: result.forkedFrom
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
return {
|
|
1794
|
+
...result,
|
|
1795
|
+
checkpoint: { ...result.checkpoint, activePendingRequests }
|
|
1796
|
+
};
|
|
1797
|
+
};
|
|
1798
|
+
return {
|
|
1799
|
+
projectTopology,
|
|
1800
|
+
streamChat: streamChat2,
|
|
1801
|
+
listCheckpoints,
|
|
1802
|
+
getCheckpoint,
|
|
1803
|
+
rollbackTo,
|
|
1804
|
+
fork
|
|
1805
|
+
};
|
|
992
1806
|
}
|
|
993
1807
|
// Annotate the CommonJS export names for ESM import in node:
|
|
994
1808
|
0 && (module.exports = {
|
|
995
1809
|
createAgent,
|
|
996
1810
|
createChatRouteHandler,
|
|
1811
|
+
createCheckpointRouteHandler,
|
|
997
1812
|
extractLatestUserMessage,
|
|
998
1813
|
handleChatRequestBody,
|
|
1814
|
+
handleCheckpointRequestBody,
|
|
999
1815
|
orchestrateGraphStream,
|
|
1000
1816
|
parseChatRequestBody,
|
|
1817
|
+
parseCheckpointRequestBody,
|
|
1001
1818
|
parseResumeMeta,
|
|
1819
|
+
projectWorkflowTopology,
|
|
1002
1820
|
streamChat,
|
|
1003
1821
|
streamChatFromRoute,
|
|
1004
1822
|
transformGraphStreamForUI,
|