@zhivex-ai/core 0.7.0 → 0.8.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.
Files changed (77) hide show
  1. package/README.md +5 -0
  2. package/dist/advanced-tool-registry.d.ts +112 -0
  3. package/dist/advanced-tool-registry.d.ts.map +1 -0
  4. package/dist/advanced-tool-registry.js +407 -0
  5. package/dist/advanced-tool-registry.js.map +1 -0
  6. package/dist/agent-evaluation.d.ts +176 -0
  7. package/dist/agent-evaluation.d.ts.map +1 -0
  8. package/dist/agent-evaluation.js +334 -0
  9. package/dist/agent-evaluation.js.map +1 -0
  10. package/dist/agent-store.d.ts.map +1 -1
  11. package/dist/agent-store.js +226 -8
  12. package/dist/agent-store.js.map +1 -1
  13. package/dist/agent-trace.d.ts +127 -0
  14. package/dist/agent-trace.d.ts.map +1 -0
  15. package/dist/agent-trace.js +331 -0
  16. package/dist/agent-trace.js.map +1 -0
  17. package/dist/agent.d.ts +12 -1
  18. package/dist/agent.d.ts.map +1 -1
  19. package/dist/agent.js +469 -36
  20. package/dist/agent.js.map +1 -1
  21. package/dist/api-stability.d.ts +9 -0
  22. package/dist/api-stability.d.ts.map +1 -0
  23. package/dist/api-stability.js +261 -0
  24. package/dist/api-stability.js.map +1 -0
  25. package/dist/artifact.d.ts +165 -0
  26. package/dist/artifact.d.ts.map +1 -0
  27. package/dist/artifact.js +994 -0
  28. package/dist/artifact.js.map +1 -0
  29. package/dist/errors.d.ts +2 -0
  30. package/dist/errors.d.ts.map +1 -1
  31. package/dist/errors.js +2 -0
  32. package/dist/errors.js.map +1 -1
  33. package/dist/generate-text.d.ts.map +1 -1
  34. package/dist/generate-text.js +6 -2
  35. package/dist/generate-text.js.map +1 -1
  36. package/dist/index.d.ts +27 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +14 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/live-agent.d.ts.map +1 -1
  41. package/dist/live-agent.js +7 -1
  42. package/dist/live-agent.js.map +1 -1
  43. package/dist/provider-parity.d.ts +63 -0
  44. package/dist/provider-parity.d.ts.map +1 -0
  45. package/dist/provider-parity.js +175 -0
  46. package/dist/provider-parity.js.map +1 -0
  47. package/dist/runner.d.ts +111 -0
  48. package/dist/runner.d.ts.map +1 -0
  49. package/dist/runner.js +635 -0
  50. package/dist/runner.js.map +1 -0
  51. package/dist/safety-policy.d.ts +65 -0
  52. package/dist/safety-policy.d.ts.map +1 -0
  53. package/dist/safety-policy.js +308 -0
  54. package/dist/safety-policy.js.map +1 -0
  55. package/dist/types.d.ts +119 -3
  56. package/dist/types.d.ts.map +1 -1
  57. package/dist/workflow-artifacts.d.ts +28 -0
  58. package/dist/workflow-artifacts.d.ts.map +1 -0
  59. package/dist/workflow-artifacts.js +86 -0
  60. package/dist/workflow-artifacts.js.map +1 -0
  61. package/dist/workflow-evaluation-diff.d.ts +51 -0
  62. package/dist/workflow-evaluation-diff.d.ts.map +1 -0
  63. package/dist/workflow-evaluation-diff.js +141 -0
  64. package/dist/workflow-evaluation-diff.js.map +1 -0
  65. package/dist/workflow-evaluation.d.ts +94 -0
  66. package/dist/workflow-evaluation.d.ts.map +1 -0
  67. package/dist/workflow-evaluation.js +210 -0
  68. package/dist/workflow-evaluation.js.map +1 -0
  69. package/dist/workflow-state-service.d.ts +67 -0
  70. package/dist/workflow-state-service.d.ts.map +1 -0
  71. package/dist/workflow-state-service.js +498 -0
  72. package/dist/workflow-state-service.js.map +1 -0
  73. package/dist/workflow.d.ts +206 -0
  74. package/dist/workflow.d.ts.map +1 -0
  75. package/dist/workflow.js +727 -0
  76. package/dist/workflow.js.map +1 -0
  77. package/package.json +1 -1
package/dist/runner.js ADDED
@@ -0,0 +1,635 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { resumeAgent, runAgent, streamAgent } from "./agent.js";
4
+ import { ConflictError, ValidationError } from "./errors.js";
5
+ import { normalizeMessages } from "./generate-text.js";
6
+ import { createTextMessage, serializeJsonValue } from "./messages.js";
7
+ const randomId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 10)}`;
8
+ const cloneJson = (value) => JSON.parse(JSON.stringify(value));
9
+ export const SESSION_SCHEMA_VERSION = 1;
10
+ const sessionKey = (input) => `${input.appName}:${input.userId}:${input.sessionId}`;
11
+ const identifierPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
12
+ const validateIdentifier = (value, fieldName) => {
13
+ if (!identifierPattern.test(value)) {
14
+ throw new ValidationError(`The "${fieldName}" option must match the SQL identifier pattern [A-Za-z_][A-Za-z0-9_]*.`);
15
+ }
16
+ return value;
17
+ };
18
+ const getRecordField = (value, candidates) => {
19
+ if (!value || typeof value !== "object") {
20
+ return undefined;
21
+ }
22
+ const record = value;
23
+ for (const candidate of candidates) {
24
+ if (candidate in record) {
25
+ return record[candidate];
26
+ }
27
+ }
28
+ return undefined;
29
+ };
30
+ const parseSessionJson = (value) => {
31
+ if (!value) {
32
+ return undefined;
33
+ }
34
+ if (typeof value === "string") {
35
+ return normalizeAgentSession(JSON.parse(value));
36
+ }
37
+ return normalizeAgentSession(value);
38
+ };
39
+ export const normalizeAgentSession = (value) => {
40
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
41
+ throw new ValidationError("AgentSession must be an object.");
42
+ }
43
+ const session = value;
44
+ if (session.schemaVersion !== undefined && session.schemaVersion > SESSION_SCHEMA_VERSION) {
45
+ throw new ValidationError(`Unsupported AgentSession schemaVersion ${session.schemaVersion}.`);
46
+ }
47
+ if (typeof session.appName !== "string" ||
48
+ typeof session.userId !== "string" ||
49
+ typeof session.sessionId !== "string" ||
50
+ typeof session.createdAt !== "number" ||
51
+ typeof session.updatedAt !== "number" ||
52
+ !Array.isArray(session.events)) {
53
+ throw new ValidationError("AgentSession is missing required fields.");
54
+ }
55
+ return {
56
+ schemaVersion: SESSION_SCHEMA_VERSION,
57
+ revision: typeof session.revision === "number" ? session.revision : 1,
58
+ appName: session.appName,
59
+ userId: session.userId,
60
+ sessionId: session.sessionId,
61
+ createdAt: session.createdAt,
62
+ updatedAt: session.updatedAt,
63
+ metadata: session.metadata ? cloneJson(session.metadata) : undefined,
64
+ events: cloneJson(session.events),
65
+ lastRunState: session.lastRunState ? cloneJson(session.lastRunState) : undefined
66
+ };
67
+ };
68
+ export const migrateAgentSessionRecord = (value, targetVersion = SESSION_SCHEMA_VERSION) => {
69
+ if (targetVersion !== SESSION_SCHEMA_VERSION) {
70
+ throw new ValidationError(`Unsupported AgentSession migration target ${targetVersion}.`);
71
+ }
72
+ return normalizeAgentSession(value);
73
+ };
74
+ const assertExpectedRevision = (current, expectedRevision, resource) => {
75
+ if (expectedRevision !== undefined && (current?.revision ?? 0) !== expectedRevision) {
76
+ throw new ConflictError(`${resource} revision conflict.`);
77
+ }
78
+ };
79
+ const sqliteMutationCount = (result) => {
80
+ if (!result || typeof result !== "object") {
81
+ return undefined;
82
+ }
83
+ const record = result;
84
+ const value = record.changes ?? record.changeset ?? record.rowCount;
85
+ return typeof value === "number" ? value : undefined;
86
+ };
87
+ const prepareSqliteStatement = (db, sql) => {
88
+ if (typeof db.prepare === "function") {
89
+ return db.prepare(sql);
90
+ }
91
+ if (typeof db.query === "function") {
92
+ return db.query(sql);
93
+ }
94
+ throw new ValidationError('The "db" option must expose either a "prepare()" or "query()" method.');
95
+ };
96
+ const ensurePostgresTable = (() => {
97
+ const initializedTables = new WeakMap();
98
+ return async (client, tableName, createSql) => {
99
+ let tables = initializedTables.get(client);
100
+ if (!tables) {
101
+ tables = new Map();
102
+ initializedTables.set(client, tables);
103
+ }
104
+ let initialization = tables.get(tableName);
105
+ if (!initialization) {
106
+ initialization = Promise.resolve(client.query(createSql, [])).then(() => undefined);
107
+ tables.set(tableName, initialization);
108
+ }
109
+ await initialization;
110
+ };
111
+ })();
112
+ const toSessionEvent = (type, input) => ({
113
+ ...input,
114
+ id: randomId("evt"),
115
+ type,
116
+ createdAt: Date.now()
117
+ });
118
+ const cloneSession = (session) => cloneJson(normalizeAgentSession(session));
119
+ export const createInMemorySessionService = () => {
120
+ const sessions = new Map();
121
+ return {
122
+ loadSession(input) {
123
+ const session = sessions.get(sessionKey(input));
124
+ return session ? cloneSession(session) : undefined;
125
+ },
126
+ createSession(input) {
127
+ const now = Date.now();
128
+ const session = {
129
+ schemaVersion: SESSION_SCHEMA_VERSION,
130
+ revision: 1,
131
+ appName: input.appName,
132
+ userId: input.userId,
133
+ sessionId: input.sessionId,
134
+ createdAt: now,
135
+ updatedAt: now,
136
+ metadata: input.metadata ? cloneJson(input.metadata) : undefined,
137
+ events: []
138
+ };
139
+ sessions.set(sessionKey(input), cloneSession(session));
140
+ return cloneSession(session);
141
+ },
142
+ saveSession(session, options) {
143
+ assertExpectedRevision(sessions.get(sessionKey(session)), options?.expectedRevision, "AgentSession");
144
+ const normalized = normalizeAgentSession({ ...session, updatedAt: Date.now() });
145
+ const current = sessions.get(sessionKey(normalized));
146
+ sessions.set(sessionKey(normalized), cloneSession({
147
+ ...normalized,
148
+ revision: (current?.revision ?? normalized.revision) + 1
149
+ }));
150
+ },
151
+ appendEvent(input) {
152
+ const existing = sessions.get(sessionKey(input));
153
+ const base = existing ??
154
+ {
155
+ schemaVersion: SESSION_SCHEMA_VERSION,
156
+ revision: 1,
157
+ appName: input.appName,
158
+ userId: input.userId,
159
+ sessionId: input.sessionId,
160
+ createdAt: Date.now(),
161
+ updatedAt: Date.now(),
162
+ events: []
163
+ };
164
+ const next = {
165
+ ...base,
166
+ revision: existing ? base.revision + 1 : 1,
167
+ updatedAt: Date.now(),
168
+ events: [...base.events, cloneJson(input.event)]
169
+ };
170
+ assertExpectedRevision(existing, input.expectedRevision, "AgentSession");
171
+ sessions.set(sessionKey(input), cloneSession(next));
172
+ return cloneSession(next);
173
+ }
174
+ };
175
+ };
176
+ const createEmptySession = (input) => {
177
+ const now = Date.now();
178
+ return {
179
+ schemaVersion: SESSION_SCHEMA_VERSION,
180
+ revision: 1,
181
+ appName: input.appName,
182
+ userId: input.userId,
183
+ sessionId: input.sessionId,
184
+ createdAt: now,
185
+ updatedAt: now,
186
+ metadata: input.metadata ? cloneJson(input.metadata) : undefined,
187
+ events: []
188
+ };
189
+ };
190
+ const appendEventToSession = (session, event) => ({
191
+ ...normalizeAgentSession(session),
192
+ schemaVersion: SESSION_SCHEMA_VERSION,
193
+ updatedAt: Date.now(),
194
+ events: [...session.events, cloneJson(event)]
195
+ });
196
+ const fileNameForSession = (input) => [input.appName, input.userId, input.sessionId].map((part) => encodeURIComponent(part)).join("__") + ".json";
197
+ export const createFileSessionService = (options) => {
198
+ const filePath = (input) => path.join(options.directory, fileNameForSession(input));
199
+ const load = async (input) => {
200
+ try {
201
+ const content = await fs.readFile(filePath(input), "utf8");
202
+ return normalizeAgentSession(JSON.parse(content));
203
+ }
204
+ catch (error) {
205
+ if (error.code === "ENOENT") {
206
+ return undefined;
207
+ }
208
+ throw error;
209
+ }
210
+ };
211
+ const save = async (session, saveOptions) => {
212
+ const existing = await load(session);
213
+ assertExpectedRevision(existing, saveOptions?.expectedRevision, "AgentSession");
214
+ const next = normalizeAgentSession({ ...session, schemaVersion: SESSION_SCHEMA_VERSION, updatedAt: Date.now() });
215
+ next.revision = existing ? existing.revision + 1 : next.revision;
216
+ await fs.mkdir(options.directory, { recursive: true });
217
+ await fs.writeFile(filePath(next), JSON.stringify(next, null, 2), "utf8");
218
+ };
219
+ return {
220
+ async loadSession(input) {
221
+ return load(input);
222
+ },
223
+ async createSession(input) {
224
+ const session = createEmptySession(input);
225
+ await save(session);
226
+ return cloneSession(session);
227
+ },
228
+ async saveSession(session, options) {
229
+ await save(session, options);
230
+ },
231
+ async appendEvent(input) {
232
+ const existing = await load(input);
233
+ assertExpectedRevision(existing, input.expectedRevision, "AgentSession");
234
+ const base = existing ?? createEmptySession(input);
235
+ const next = appendEventToSession(base, input.event);
236
+ await save(next);
237
+ return cloneSession(next);
238
+ }
239
+ };
240
+ };
241
+ export const pruneFileSessionStore = async (options) => {
242
+ const now = options.now ?? Date.now();
243
+ const dryRun = options.dryRun ?? true;
244
+ let entries;
245
+ try {
246
+ entries = await fs.readdir(options.directory);
247
+ }
248
+ catch (error) {
249
+ if (error.code === "ENOENT") {
250
+ return { directory: options.directory, dryRun, deletedSessionKeys: [], keptSessionKeys: [] };
251
+ }
252
+ throw error;
253
+ }
254
+ const sessions = [];
255
+ for (const entry of entries) {
256
+ if (!entry.endsWith(".json")) {
257
+ continue;
258
+ }
259
+ const filePath = path.join(options.directory, entry);
260
+ const session = normalizeAgentSession(JSON.parse(await fs.readFile(filePath, "utf8")));
261
+ sessions.push({
262
+ filePath,
263
+ key: sessionKey(session),
264
+ updatedAt: session.updatedAt
265
+ });
266
+ }
267
+ const sorted = sessions.sort((left, right) => right.updatedAt - left.updatedAt || left.key.localeCompare(right.key));
268
+ const keepByCount = new Set(options.keepLast === undefined ? [] : sorted.slice(0, Math.max(0, options.keepLast)).map((session) => session.key));
269
+ const shouldDelete = (session) => !keepByCount.has(session.key) &&
270
+ (options.olderThanMs !== undefined ? now - session.updatedAt > options.olderThanMs : options.keepLast !== undefined);
271
+ const deleted = sorted.filter(shouldDelete);
272
+ if (!dryRun) {
273
+ for (const session of deleted) {
274
+ await fs.unlink(session.filePath);
275
+ }
276
+ }
277
+ return {
278
+ directory: options.directory,
279
+ dryRun,
280
+ deletedSessionKeys: deleted.map((session) => session.key),
281
+ keptSessionKeys: sorted.filter((session) => !shouldDelete(session)).map((session) => session.key)
282
+ };
283
+ };
284
+ export const createSqliteSessionService = (options) => {
285
+ const tableName = validateIdentifier(options.tableName ?? "zhivex_agent_sessions", "tableName");
286
+ options.db.exec(`
287
+ CREATE TABLE IF NOT EXISTS ${tableName} (
288
+ session_key TEXT PRIMARY KEY,
289
+ app_name TEXT NOT NULL,
290
+ user_id TEXT NOT NULL,
291
+ session_id TEXT NOT NULL,
292
+ session_json TEXT NOT NULL,
293
+ updated_at_ms INTEGER NOT NULL
294
+ )
295
+ `);
296
+ const loadStatement = prepareSqliteStatement(options.db, `SELECT session_json FROM ${tableName} WHERE session_key = ?`);
297
+ const saveStatement = prepareSqliteStatement(options.db, `
298
+ INSERT INTO ${tableName} (session_key, app_name, user_id, session_id, session_json, updated_at_ms)
299
+ VALUES (?, ?, ?, ?, ?, ?)
300
+ ON CONFLICT(session_key) DO UPDATE SET
301
+ app_name = excluded.app_name,
302
+ user_id = excluded.user_id,
303
+ session_id = excluded.session_id,
304
+ session_json = excluded.session_json,
305
+ updated_at_ms = excluded.updated_at_ms
306
+ `);
307
+ const updateCasStatement = prepareSqliteStatement(options.db, `
308
+ UPDATE ${tableName}
309
+ SET app_name = ?,
310
+ user_id = ?,
311
+ session_id = ?,
312
+ session_json = ?,
313
+ updated_at_ms = ?
314
+ WHERE session_key = ?
315
+ AND updated_at_ms = ?
316
+ `);
317
+ const save = (session, saveOptions) => {
318
+ const existing = load(session);
319
+ assertExpectedRevision(existing, saveOptions?.expectedRevision, "AgentSession");
320
+ const next = cloneSession({ ...session, updatedAt: Date.now() });
321
+ next.revision = existing ? existing.revision + 1 : next.revision;
322
+ if (saveOptions?.expectedRevision !== undefined && existing) {
323
+ const result = updateCasStatement.run([
324
+ next.appName,
325
+ next.userId,
326
+ next.sessionId,
327
+ JSON.stringify(next),
328
+ next.updatedAt,
329
+ sessionKey(next),
330
+ existing.updatedAt
331
+ ]);
332
+ if (sqliteMutationCount(result) === 0) {
333
+ throw new ConflictError("AgentSession revision conflict.");
334
+ }
335
+ }
336
+ else {
337
+ saveStatement.run([
338
+ sessionKey(next),
339
+ next.appName,
340
+ next.userId,
341
+ next.sessionId,
342
+ JSON.stringify(next),
343
+ next.updatedAt
344
+ ]);
345
+ }
346
+ return next;
347
+ };
348
+ const load = (input) => {
349
+ const row = loadStatement.get([sessionKey(input)]);
350
+ return parseSessionJson(getRecordField(row, ["session_json", "sessionJson"]));
351
+ };
352
+ return {
353
+ loadSession(input) {
354
+ return load(input);
355
+ },
356
+ createSession(input) {
357
+ return save(createEmptySession(input));
358
+ },
359
+ saveSession(session, options) {
360
+ save(session, options);
361
+ },
362
+ appendEvent(input) {
363
+ const existing = load(input);
364
+ assertExpectedRevision(existing, input.expectedRevision, "AgentSession");
365
+ const base = existing ?? createEmptySession(input);
366
+ return save(appendEventToSession(base, input.event), input.expectedRevision === undefined ? undefined : { expectedRevision: input.expectedRevision });
367
+ }
368
+ };
369
+ };
370
+ export const createPostgresSessionService = (options) => {
371
+ const tableName = validateIdentifier(options.tableName ?? "zhivex_agent_sessions", "tableName");
372
+ const createSql = `
373
+ CREATE TABLE IF NOT EXISTS ${tableName} (
374
+ session_key TEXT PRIMARY KEY,
375
+ app_name TEXT NOT NULL,
376
+ user_id TEXT NOT NULL,
377
+ session_id TEXT NOT NULL,
378
+ session_json JSONB NOT NULL,
379
+ updated_at_ms BIGINT NOT NULL
380
+ )
381
+ `;
382
+ const load = async (input) => {
383
+ await ensurePostgresTable(options.client, tableName, createSql);
384
+ const result = await options.client.query(`SELECT session_json FROM ${tableName} WHERE session_key = $1`, [sessionKey(input)]);
385
+ return parseSessionJson(getRecordField(result.rows[0], ["session_json", "sessionJson"]));
386
+ };
387
+ const save = async (session, saveOptions) => {
388
+ await ensurePostgresTable(options.client, tableName, createSql);
389
+ const existing = await load(session);
390
+ assertExpectedRevision(existing, saveOptions?.expectedRevision, "AgentSession");
391
+ const next = cloneSession({ ...session, updatedAt: Date.now() });
392
+ next.revision = existing ? existing.revision + 1 : next.revision;
393
+ if (saveOptions?.expectedRevision !== undefined && existing) {
394
+ const result = await options.client.query(`UPDATE ${tableName}
395
+ SET app_name = $2,
396
+ user_id = $3,
397
+ session_id = $4,
398
+ session_json = $5::jsonb,
399
+ updated_at_ms = $6
400
+ WHERE session_key = $1
401
+ AND updated_at_ms = $7
402
+ RETURNING session_json`, [sessionKey(next), next.appName, next.userId, next.sessionId, JSON.stringify(next), next.updatedAt, existing.updatedAt]);
403
+ if (result.rows.length === 0) {
404
+ throw new ConflictError("AgentSession revision conflict.");
405
+ }
406
+ }
407
+ else {
408
+ await options.client.query(`INSERT INTO ${tableName} (session_key, app_name, user_id, session_id, session_json, updated_at_ms)
409
+ VALUES ($1, $2, $3, $4, $5::jsonb, $6)
410
+ ON CONFLICT(session_key) DO UPDATE SET
411
+ app_name = EXCLUDED.app_name,
412
+ user_id = EXCLUDED.user_id,
413
+ session_id = EXCLUDED.session_id,
414
+ session_json = EXCLUDED.session_json,
415
+ updated_at_ms = EXCLUDED.updated_at_ms`, [sessionKey(next), next.appName, next.userId, next.sessionId, JSON.stringify(next), next.updatedAt]);
416
+ }
417
+ return next;
418
+ };
419
+ return {
420
+ loadSession(input) {
421
+ return load(input);
422
+ },
423
+ createSession(input) {
424
+ return save(createEmptySession(input));
425
+ },
426
+ async saveSession(session, options) {
427
+ await save(session, options);
428
+ },
429
+ async appendEvent(input) {
430
+ const existing = await load(input);
431
+ assertExpectedRevision(existing, input.expectedRevision, "AgentSession");
432
+ const base = existing ?? createEmptySession(input);
433
+ return save(appendEventToSession(base, input.event), input.expectedRevision === undefined ? undefined : { expectedRevision: input.expectedRevision });
434
+ }
435
+ };
436
+ };
437
+ const loadOrCreateSession = async (sessionService, input) => {
438
+ const loaded = await sessionService.loadSession(input);
439
+ if (loaded) {
440
+ return loaded;
441
+ }
442
+ await sessionService.createSession(input);
443
+ const event = toSessionEvent("session-created", {
444
+ appName: input.appName,
445
+ userId: input.userId,
446
+ sessionId: input.sessionId,
447
+ metadata: input.metadata
448
+ });
449
+ return sessionService.appendEvent({ ...input, event });
450
+ };
451
+ const messagesFromSession = (session) => session.events.flatMap((event) => {
452
+ if (event.type === "user-message") {
453
+ return event.messages ? cloneJson(event.messages) : [];
454
+ }
455
+ if (event.type === "agent-run-finished" && event.outputText) {
456
+ return [createTextMessage("assistant", event.outputText)];
457
+ }
458
+ return [];
459
+ });
460
+ const messagesFromInput = (input) => normalizeMessages({
461
+ prompt: input.prompt,
462
+ messages: input.messages,
463
+ system: input.system
464
+ });
465
+ const appendEvent = async (service, session, event) => service.appendEvent({
466
+ appName: session.appName,
467
+ userId: session.userId,
468
+ sessionId: session.sessionId,
469
+ event
470
+ });
471
+ const saveRunState = async (service, session, state) => {
472
+ const next = {
473
+ ...session,
474
+ updatedAt: Date.now(),
475
+ lastRunState: cloneJson(state)
476
+ };
477
+ await service.saveSession(next);
478
+ return cloneSession(next);
479
+ };
480
+ const createRunStartedEvent = (session, metadata) => toSessionEvent("agent-run-started", {
481
+ appName: session.appName,
482
+ userId: session.userId,
483
+ sessionId: session.sessionId,
484
+ metadata
485
+ });
486
+ const createRunFinishedEvent = (session, output, metadata) => toSessionEvent("agent-run-finished", {
487
+ appName: session.appName,
488
+ userId: session.userId,
489
+ sessionId: session.sessionId,
490
+ runId: output.state.runId,
491
+ status: output.status,
492
+ outputText: output.steps.at(-1)?.response?.text ?? output.outputText,
493
+ metadata
494
+ });
495
+ const createRunFailedEvent = (session, error, metadata) => toSessionEvent("agent-run-failed", {
496
+ appName: session.appName,
497
+ userId: session.userId,
498
+ sessionId: session.sessionId,
499
+ error: {
500
+ message: error instanceof Error ? error.message : String(error)
501
+ },
502
+ metadata
503
+ });
504
+ const createApprovalEvent = (session, output, metadata) => {
505
+ if (!output.state.pendingApprovals.length) {
506
+ return undefined;
507
+ }
508
+ return toSessionEvent("approval-required", {
509
+ appName: session.appName,
510
+ userId: session.userId,
511
+ sessionId: session.sessionId,
512
+ runId: output.state.runId,
513
+ approvals: cloneJson(output.state.pendingApprovals),
514
+ metadata
515
+ });
516
+ };
517
+ const toSerializableMetadata = (metadata) => metadata ? serializeJsonValue(metadata) : undefined;
518
+ export const createRunner = (options) => {
519
+ const resolveSession = async (input) => loadOrCreateSession(options.sessionService, {
520
+ appName: options.appName,
521
+ userId: input.userId,
522
+ sessionId: input.sessionId ?? randomId("sess"),
523
+ metadata: toSerializableMetadata(input.sessionMetadata)
524
+ });
525
+ const createAgentInput = (session, input) => {
526
+ const { userId: _userId, sessionId: _sessionId, sessionMetadata: _sessionMetadata, eventMetadata: _eventMetadata, ...agentInput } = input;
527
+ const messages = messagesFromInput(input);
528
+ if (input.approvals?.length && session.lastRunState) {
529
+ return {
530
+ ...options.defaults,
531
+ ...agentInput,
532
+ state: session.lastRunState,
533
+ approvals: input.approvals
534
+ };
535
+ }
536
+ return {
537
+ ...options.defaults,
538
+ ...agentInput,
539
+ prompt: undefined,
540
+ system: undefined,
541
+ messages: [...messagesFromSession(session), ...messages]
542
+ };
543
+ };
544
+ const recordUserMessage = async (session, input) => {
545
+ if (input.approvals?.length && !input.prompt && !input.messages?.length) {
546
+ return session;
547
+ }
548
+ const messages = messagesFromInput(input).filter((message) => message.role !== "system");
549
+ if (!messages.length) {
550
+ return session;
551
+ }
552
+ return appendEvent(options.sessionService, session, toSessionEvent("user-message", {
553
+ appName: session.appName,
554
+ userId: session.userId,
555
+ sessionId: session.sessionId,
556
+ messages,
557
+ metadata: toSerializableMetadata(input.eventMetadata)
558
+ }));
559
+ };
560
+ const finalizeSuccessfulRun = async (session, output, metadata) => {
561
+ let next = await appendEvent(options.sessionService, session, createRunFinishedEvent(session, output, metadata));
562
+ const approvalEvent = createApprovalEvent(next, output, metadata);
563
+ if (approvalEvent) {
564
+ next = await appendEvent(options.sessionService, next, approvalEvent);
565
+ }
566
+ return saveRunState(options.sessionService, next, output.state);
567
+ };
568
+ return {
569
+ async run(input) {
570
+ const initialSession = await resolveSession(input);
571
+ let session = await recordUserMessage(initialSession, input);
572
+ session = await appendEvent(options.sessionService, session, createRunStartedEvent(session, toSerializableMetadata(input.eventMetadata)));
573
+ try {
574
+ const agentInput = createAgentInput(initialSession, input);
575
+ const output = input.approvals?.length && initialSession.lastRunState
576
+ ? await resumeAgent(options.agent, agentInput)
577
+ : await runAgent(options.agent, agentInput);
578
+ const finalSession = await finalizeSuccessfulRun(session, output, toSerializableMetadata(input.eventMetadata));
579
+ return {
580
+ session: finalSession,
581
+ output
582
+ };
583
+ }
584
+ catch (error) {
585
+ const failedSession = await appendEvent(options.sessionService, session, createRunFailedEvent(session, error, toSerializableMetadata(input.eventMetadata)));
586
+ throw Object.assign(error instanceof Error ? error : new Error(String(error)), {
587
+ session: failedSession
588
+ });
589
+ }
590
+ },
591
+ stream(input) {
592
+ const setup = (async () => {
593
+ const initialSession = await resolveSession(input);
594
+ let session = await recordUserMessage(initialSession, input);
595
+ session = await appendEvent(options.sessionService, session, createRunStartedEvent(session, toSerializableMetadata(input.eventMetadata)));
596
+ const agentInput = createAgentInput(initialSession, input);
597
+ const stream = input.approvals?.length && initialSession.lastRunState
598
+ ? streamAgent(options.agent, agentInput)
599
+ : streamAgent(options.agent, agentInput);
600
+ return { session, stream };
601
+ })();
602
+ const collect = async () => {
603
+ const { session, stream } = await setup;
604
+ try {
605
+ const output = await stream.collect();
606
+ const finalSession = await finalizeSuccessfulRun(session, output, toSerializableMetadata(input.eventMetadata));
607
+ return { session: finalSession, output };
608
+ }
609
+ catch (error) {
610
+ const failedSession = await appendEvent(options.sessionService, session, createRunFailedEvent(session, error, toSerializableMetadata(input.eventMetadata)));
611
+ throw Object.assign(error instanceof Error ? error : new Error(String(error)), {
612
+ session: failedSession
613
+ });
614
+ }
615
+ };
616
+ return {
617
+ session: setup.then(({ session }) => session),
618
+ eventStream: (async function* () {
619
+ const { stream } = await setup;
620
+ for await (const event of stream.eventStream) {
621
+ yield event;
622
+ }
623
+ })(),
624
+ textStream: (async function* () {
625
+ const { stream } = await setup;
626
+ for await (const chunk of stream.textStream) {
627
+ yield chunk;
628
+ }
629
+ })(),
630
+ collect
631
+ };
632
+ }
633
+ };
634
+ };
635
+ //# sourceMappingURL=runner.js.map