@harness-kernel/core 0.1.0-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.
Files changed (83) hide show
  1. package/NOTICE +3 -0
  2. package/README.md +12 -0
  3. package/dist/agent/context.d.ts +5 -0
  4. package/dist/agent/context.js +11 -0
  5. package/dist/agent/context.js.map +1 -0
  6. package/dist/agent/event.d.ts +3 -0
  7. package/dist/agent/event.js +50 -0
  8. package/dist/agent/event.js.map +1 -0
  9. package/dist/agent/hook.d.ts +6 -0
  10. package/dist/agent/hook.js +7 -0
  11. package/dist/agent/hook.js.map +1 -0
  12. package/dist/agent/mode.d.ts +5 -0
  13. package/dist/agent/mode.js +7 -0
  14. package/dist/agent/mode.js.map +1 -0
  15. package/dist/agent/role.d.ts +1 -0
  16. package/dist/agent/role.js +27 -0
  17. package/dist/agent/role.js.map +1 -0
  18. package/dist/agent/session.d.ts +6 -0
  19. package/dist/agent/session.js +13 -0
  20. package/dist/agent/session.js.map +1 -0
  21. package/dist/agent/tool.d.ts +5 -0
  22. package/dist/agent/tool.js +7 -0
  23. package/dist/agent/tool.js.map +1 -0
  24. package/dist/agent.d.ts +12 -0
  25. package/dist/agent.js +7 -0
  26. package/dist/agent.js.map +1 -0
  27. package/dist/approval-DfvjpbFs.d.ts +247 -0
  28. package/dist/chunk-4A2P4QU5.js +179 -0
  29. package/dist/chunk-4A2P4QU5.js.map +1 -0
  30. package/dist/chunk-4SYLFKIX.js +207 -0
  31. package/dist/chunk-4SYLFKIX.js.map +1 -0
  32. package/dist/chunk-4WWSQAWA.js +3778 -0
  33. package/dist/chunk-4WWSQAWA.js.map +1 -0
  34. package/dist/chunk-AD3BCYWU.js +37 -0
  35. package/dist/chunk-AD3BCYWU.js.map +1 -0
  36. package/dist/chunk-AZVA22HW.js +135 -0
  37. package/dist/chunk-AZVA22HW.js.map +1 -0
  38. package/dist/chunk-OBKS4AJR.js +529 -0
  39. package/dist/chunk-OBKS4AJR.js.map +1 -0
  40. package/dist/chunk-Q44U2CMM.js +239 -0
  41. package/dist/chunk-Q44U2CMM.js.map +1 -0
  42. package/dist/chunk-Q73WH5D7.js +54 -0
  43. package/dist/chunk-Q73WH5D7.js.map +1 -0
  44. package/dist/chunk-RRWJUHJG.js +9 -0
  45. package/dist/chunk-RRWJUHJG.js.map +1 -0
  46. package/dist/context-75mlon5x.d.ts +394 -0
  47. package/dist/event-CKV4EeZ3.d.ts +230 -0
  48. package/dist/events-D4xcDi53.d.ts +69 -0
  49. package/dist/hook-DMb9fw9Z.d.ts +20 -0
  50. package/dist/index.d.ts +164 -0
  51. package/dist/index.js +174 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/model-provider-BrZ2RRmS.d.ts +130 -0
  54. package/dist/role-BN6KhQxx.d.ts +68 -0
  55. package/dist/runner/approval.d.ts +9 -0
  56. package/dist/runner/approval.js +1 -0
  57. package/dist/runner/approval.js.map +1 -0
  58. package/dist/runner/event.d.ts +3 -0
  59. package/dist/runner/event.js +50 -0
  60. package/dist/runner/event.js.map +1 -0
  61. package/dist/runner/logging.d.ts +31 -0
  62. package/dist/runner/logging.js +15 -0
  63. package/dist/runner/logging.js.map +1 -0
  64. package/dist/runner/model-provider.d.ts +8 -0
  65. package/dist/runner/model-provider.js +11 -0
  66. package/dist/runner/model-provider.js.map +1 -0
  67. package/dist/runner/sandbox.d.ts +47 -0
  68. package/dist/runner/sandbox.js +13 -0
  69. package/dist/runner/sandbox.js.map +1 -0
  70. package/dist/runner/storage.d.ts +6 -0
  71. package/dist/runner/storage.js +17 -0
  72. package/dist/runner/storage.js.map +1 -0
  73. package/dist/runner-Dxo7ALtp.d.ts +87 -0
  74. package/dist/runner.d.ts +10 -0
  75. package/dist/runner.js +18 -0
  76. package/dist/runner.js.map +1 -0
  77. package/dist/schema.d.ts +146 -0
  78. package/dist/schema.js +39 -0
  79. package/dist/schema.js.map +1 -0
  80. package/dist/storage-BmOEwW-p.d.ts +118 -0
  81. package/dist/tool-errors-CygY1Nba.d.ts +27 -0
  82. package/dist/types-BPmsw-mF.d.ts +80 -0
  83. package/package.json +114 -0
@@ -0,0 +1,3778 @@
1
+ import {
2
+ HarnessModelProviderRegistry,
3
+ modelProviderId
4
+ } from "./chunk-Q73WH5D7.js";
5
+ import {
6
+ AgentDebugLog,
7
+ AgentErrorLog,
8
+ AgentInfoLog,
9
+ AgentWarnLog,
10
+ ConsoleLogSink,
11
+ HarnessLog,
12
+ normalizeHarnessLog,
13
+ randomId,
14
+ shouldWriteLog,
15
+ summarizeValue
16
+ } from "./chunk-Q44U2CMM.js";
17
+ import {
18
+ HarnessSandboxSession,
19
+ NoopSandbox
20
+ } from "./chunk-AD3BCYWU.js";
21
+ import {
22
+ NoopRunStorage
23
+ } from "./chunk-AZVA22HW.js";
24
+ import {
25
+ ContextReadyEvent,
26
+ ErrorEvent,
27
+ MessageDeltaEvent,
28
+ MessageEndEvent,
29
+ MessageStartEvent,
30
+ ModeChangedEvent,
31
+ ModelAfterEvent,
32
+ ModelBeforeEvent,
33
+ RunEndEvent,
34
+ RunStartEvent,
35
+ SnapshotCreatedEvent,
36
+ SnapshotDeletedEvent,
37
+ SnapshotRestoredEvent,
38
+ ToolApprovalRequestedEvent,
39
+ ToolApprovalResolvedEvent,
40
+ ToolEndEvent,
41
+ ToolStartEvent,
42
+ TranscriptCursorChangedEvent,
43
+ TurnEndEvent,
44
+ TurnStartEvent,
45
+ runtimeEventClasses
46
+ } from "./chunk-4SYLFKIX.js";
47
+ import {
48
+ normalizeSchema
49
+ } from "./chunk-OBKS4AJR.js";
50
+ import {
51
+ HarnessContextProvider,
52
+ HarnessEvent,
53
+ HarnessHook,
54
+ HarnessMode,
55
+ HarnessRole,
56
+ HarnessTool,
57
+ assistantRole,
58
+ systemRole,
59
+ toolRole,
60
+ userRole
61
+ } from "./chunk-4A2P4QU5.js";
62
+
63
+ // src/logging/tool-errors.ts
64
+ function createToolErrorPayload(input) {
65
+ return {
66
+ ok: false,
67
+ error: {
68
+ code: input.code,
69
+ message: input.message,
70
+ toolName: input.toolName,
71
+ invalidFields: input.invalidFields,
72
+ metadata: input.metadata
73
+ }
74
+ };
75
+ }
76
+
77
+ // src/logging/runtime-logs.ts
78
+ var SessionCreatedLog = class extends HarnessLog {
79
+ level = "info";
80
+ category = "session";
81
+ message() {
82
+ return "session.created";
83
+ }
84
+ };
85
+ var RunStartedLog = class extends HarnessLog {
86
+ level = "info";
87
+ category = "run";
88
+ message(fields) {
89
+ return `run.started mode=${fields.modeId} model=${fields.model}`;
90
+ }
91
+ };
92
+ var RunCompletedLog = class extends HarnessLog {
93
+ level = "info";
94
+ category = "run";
95
+ message(fields) {
96
+ return `run.completed durationMs=${fields.durationMs} messages=${fields.messageCount} events=${fields.eventCount}`;
97
+ }
98
+ };
99
+ var RunFailedLog = class extends HarnessLog {
100
+ level = "error";
101
+ category = "run";
102
+ message() {
103
+ return "run.failed";
104
+ }
105
+ };
106
+ var TurnStartedLog = class extends HarnessLog {
107
+ level = "info";
108
+ category = "turn";
109
+ message(fields) {
110
+ return `turn.started ${fields.turnId}`;
111
+ }
112
+ };
113
+ var TurnCompletedLog = class extends HarnessLog {
114
+ level = "info";
115
+ category = "turn";
116
+ message() {
117
+ return "turn.completed";
118
+ }
119
+ };
120
+ var ContextBuildStartedLog = class extends HarnessLog {
121
+ level = "debug";
122
+ category = "context";
123
+ message(fields) {
124
+ return `context.started providers=${fields.providerCount}`;
125
+ }
126
+ };
127
+ var ContextBuildCompletedLog = class extends HarnessLog {
128
+ level = "info";
129
+ category = "context";
130
+ message(fields) {
131
+ return `context.ready providers=${fields.providerCount} contributions=${fields.contributionCount} durationMs=${fields.durationMs}`;
132
+ }
133
+ };
134
+ var ContextProviderFailedLog = class extends HarnessLog {
135
+ level = "error";
136
+ category = "context";
137
+ message(fields) {
138
+ return `context.provider.failed ${fields.providerType}`;
139
+ }
140
+ };
141
+ var ModelCallStartedLog = class extends HarnessLog {
142
+ level = "info";
143
+ category = "model";
144
+ message(fields) {
145
+ return `model.started model=${fields.model} messages=${fields.messageCount}`;
146
+ }
147
+ };
148
+ var ModelCallCompletedLog = class extends HarnessLog {
149
+ level = "info";
150
+ category = "model";
151
+ message(fields) {
152
+ return `model.completed model=${fields.model} durationMs=${fields.durationMs}${fields.finishReason ? ` finish=${fields.finishReason}` : ""}`;
153
+ }
154
+ };
155
+ var ModelCallFailedLog = class extends HarnessLog {
156
+ level = "error";
157
+ category = "model";
158
+ message(fields) {
159
+ return `model.failed model=${fields.model}`;
160
+ }
161
+ };
162
+ var ModelDeltaLog = class extends HarnessLog {
163
+ level = "info";
164
+ category = "model";
165
+ message(fields) {
166
+ return `model.delta length=${fields.length}`;
167
+ }
168
+ };
169
+ var ToolStartedLog = class extends HarnessLog {
170
+ level = "info";
171
+ category = "tool";
172
+ message(fields) {
173
+ return `tool.started ${fields.toolName}`;
174
+ }
175
+ };
176
+ var ToolArgsEmptyLog = class extends HarnessLog {
177
+ level = "warn";
178
+ category = "tool";
179
+ message(fields) {
180
+ return `tool.args.empty ${fields.toolName}`;
181
+ }
182
+ };
183
+ var ToolInvalidSchemaLog = class extends HarnessLog {
184
+ level = "error";
185
+ category = "tool";
186
+ message(fields) {
187
+ return `tool.args.invalid_schema ${fields.toolName}`;
188
+ }
189
+ };
190
+ var ToolCompletedLog = class extends HarnessLog {
191
+ level = "info";
192
+ category = "tool";
193
+ message(fields) {
194
+ return `tool.completed ${fields.toolName} durationMs=${fields.durationMs} isError=${fields.isError}`;
195
+ }
196
+ };
197
+ var ToolFailedLog = class extends HarnessLog {
198
+ level = "error";
199
+ category = "tool";
200
+ message(fields) {
201
+ return `tool.failed ${fields.toolName}`;
202
+ }
203
+ };
204
+ var ToolApprovalRequestedLog = class extends HarnessLog {
205
+ level = "warn";
206
+ category = "approval";
207
+ message(fields) {
208
+ return `tool.approval.requested ${fields.toolName}`;
209
+ }
210
+ };
211
+ var ToolApprovalResolvedLog = class extends HarnessLog {
212
+ level = "info";
213
+ category = "approval";
214
+ levelFor(fields) {
215
+ return fields.approved ? "info" : "warn";
216
+ }
217
+ message(fields) {
218
+ return fields.approved ? `tool.approval.approved ${fields.toolName}` : `tool.approval.denied ${fields.toolName}`;
219
+ }
220
+ };
221
+ var SandboxOpenedLog = class extends HarnessLog {
222
+ level = "info";
223
+ category = "tool";
224
+ message(fields) {
225
+ return `sandbox.opened ${fields.sandboxId}`;
226
+ }
227
+ };
228
+ var SandboxExecStartedLog = class extends HarnessLog {
229
+ level = "debug";
230
+ category = "tool";
231
+ message(fields) {
232
+ return `sandbox.exec.started ${fields.sandboxId}`;
233
+ }
234
+ };
235
+ var SandboxExecCompletedLog = class extends HarnessLog {
236
+ level = "debug";
237
+ category = "tool";
238
+ message(fields) {
239
+ return `sandbox.exec.completed ${fields.sandboxId} exitCode=${fields.exitCode ?? "null"} durationMs=${fields.durationMs}`;
240
+ }
241
+ };
242
+ var SandboxExecFailedLog = class extends HarnessLog {
243
+ level = "error";
244
+ category = "tool";
245
+ message(fields) {
246
+ return `sandbox.exec.failed ${fields.sandboxId}`;
247
+ }
248
+ };
249
+ var SandboxClosedLog = class extends HarnessLog {
250
+ level = "info";
251
+ category = "tool";
252
+ message(fields) {
253
+ return `sandbox.closed ${fields.sandboxId}`;
254
+ }
255
+ };
256
+ var RunStorageOpenedLog = class extends HarnessLog {
257
+ level = "debug";
258
+ category = "storage";
259
+ message(fields) {
260
+ return `storage.opened ${fields.storageId} run=${fields.runId}`;
261
+ }
262
+ };
263
+ var SnapshotCreatedLog = class extends HarnessLog {
264
+ level = "info";
265
+ category = "snapshot";
266
+ message() {
267
+ return "snapshot.created";
268
+ }
269
+ };
270
+ var SnapshotRestoredLog = class extends HarnessLog {
271
+ level = "info";
272
+ category = "snapshot";
273
+ message() {
274
+ return "snapshot.restored";
275
+ }
276
+ };
277
+ var SnapshotRestoreRejectedLog = class extends HarnessLog {
278
+ level = "warn";
279
+ category = "snapshot";
280
+ message() {
281
+ return "snapshot.restore_rejected";
282
+ }
283
+ };
284
+ var SnapshotDeletedLog = class extends HarnessLog {
285
+ level = "info";
286
+ category = "snapshot";
287
+ message() {
288
+ return "snapshot.deleted";
289
+ }
290
+ };
291
+ var TranscriptCursorChangedLog = class extends HarnessLog {
292
+ level = "debug";
293
+ category = "transcript";
294
+ message() {
295
+ return "transcript.cursor_changed";
296
+ }
297
+ };
298
+ var StorageWriteFailedLog = class extends HarnessLog {
299
+ level = "error";
300
+ category = "storage";
301
+ message(fields) {
302
+ return `storage.write_failed ${fields.operation}`;
303
+ }
304
+ };
305
+
306
+ // src/runtime/constructs.ts
307
+ function constructClass(value) {
308
+ return typeof value === "function" ? value : value.constructor;
309
+ }
310
+ function stripConstructSuffix(name) {
311
+ return name.replace(/(ContextProvider|Provider|Tool|Mode|Hook|Role|Event)$/u, "");
312
+ }
313
+ function wordsFromName(name) {
314
+ const spaced = name.replace(/[_:-]+/gu, " ").replace(/([a-z0-9])([A-Z])/gu, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/gu, "$1 $2").trim();
315
+ return spaced ? spaced.split(/\s+/u) : [name];
316
+ }
317
+ function snakeFromType(type) {
318
+ return wordsFromName(stripConstructSuffix(type)).map((word) => word.toLowerCase()).join("_");
319
+ }
320
+ function labelFromType(type) {
321
+ return wordsFromName(stripConstructSuffix(type)).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
322
+ }
323
+ function getConstructType(constructOrClass) {
324
+ const cls = constructClass(constructOrClass);
325
+ return cls.type ?? cls.name ?? "AnonymousConstruct";
326
+ }
327
+ function getConstructLabel(constructOrClass) {
328
+ const instanceLabel = typeof constructOrClass === "object" && "label" in constructOrClass && typeof constructOrClass.label === "string" ? constructOrClass.label : void 0;
329
+ if (instanceLabel) return instanceLabel;
330
+ const cls = constructClass(constructOrClass);
331
+ return cls.label ?? labelFromType(getConstructType(constructOrClass));
332
+ }
333
+ function getToolName(toolOrClass) {
334
+ if (typeof toolOrClass === "object" && typeof toolOrClass.name === "string") return toolOrClass.name;
335
+ const cls = constructClass(toolOrClass);
336
+ return cls.name ?? snakeFromType(getConstructType(toolOrClass));
337
+ }
338
+ function getRoleName(role) {
339
+ return role.name ?? role.nativeRole ?? snakeFromType(getConstructType(role));
340
+ }
341
+ function modeSummary(mode) {
342
+ return {
343
+ type: getConstructType(mode),
344
+ label: getConstructLabel(mode)
345
+ };
346
+ }
347
+ function contextProviderSummary(provider, options) {
348
+ return {
349
+ type: getConstructType(provider),
350
+ label: getConstructLabel(provider),
351
+ options
352
+ };
353
+ }
354
+ function isConstructInstance(value, BaseClass) {
355
+ return value instanceof BaseClass;
356
+ }
357
+ function isModeInstance(value) {
358
+ return isConstructInstance(value, HarnessMode);
359
+ }
360
+ function isToolInstance(value) {
361
+ return isConstructInstance(value, HarnessTool);
362
+ }
363
+ function isContextProviderInstance(value) {
364
+ return isConstructInstance(value, HarnessContextProvider);
365
+ }
366
+ function isRoleInstance(value) {
367
+ return isConstructInstance(value, HarnessRole);
368
+ }
369
+ function isHookInstance(value) {
370
+ return isConstructInstance(value, HarnessHook);
371
+ }
372
+ function isClassLike(value) {
373
+ return typeof value === "function" && typeof value.prototype === "object";
374
+ }
375
+ function isModeClass(value) {
376
+ return isClassLike(value) && HarnessMode.prototype.isPrototypeOf(value.prototype);
377
+ }
378
+ function isToolClass(value) {
379
+ return isClassLike(value) && HarnessTool.prototype.isPrototypeOf(value.prototype);
380
+ }
381
+ function isRoleClass(value) {
382
+ return isClassLike(value) && HarnessRole.prototype.isPrototypeOf(value.prototype);
383
+ }
384
+ function isContextProviderClass(value) {
385
+ return isClassLike(value) && HarnessContextProvider.prototype.isPrototypeOf(value.prototype);
386
+ }
387
+ function modeMatchesSelector(mode, selector) {
388
+ if (typeof selector === "string") return getConstructType(mode) === selector;
389
+ if (isModeClass(selector)) return mode instanceof selector || getConstructType(mode) === getConstructType(selector);
390
+ return mode === selector || mode.constructor === selector.constructor || getConstructType(mode) === getConstructType(selector);
391
+ }
392
+ function toolMatchesSelector(tool, selector) {
393
+ if (typeof selector === "string") return getToolName(tool) === selector || getConstructType(tool) === selector;
394
+ if (isToolClass(selector)) return tool instanceof selector || getConstructType(tool) === getConstructType(selector);
395
+ return tool === selector || tool.constructor === selector.constructor || getConstructType(tool) === getConstructType(selector);
396
+ }
397
+ function roleMatchesSelector(role, selector) {
398
+ if (typeof selector === "string") {
399
+ return getRoleName(role) === selector || getConstructType(role) === selector || role.nativeRole === selector;
400
+ }
401
+ if (isRoleClass(selector)) return role instanceof selector || getConstructType(role) === getConstructType(selector);
402
+ return role === selector || role.constructor === selector.constructor || getConstructType(role) === getConstructType(selector);
403
+ }
404
+ function contextProviderMatchesSelector(provider, selector) {
405
+ if (typeof selector === "string") return getConstructType(provider) === selector;
406
+ if (isContextProviderClass(selector)) {
407
+ return provider instanceof selector || getConstructType(provider) === getConstructType(selector);
408
+ }
409
+ return provider === selector || provider.constructor === selector.constructor || getConstructType(provider) === getConstructType(selector);
410
+ }
411
+ function hookEventClass(hook) {
412
+ const eventClass = hook.eventClass;
413
+ if (eventClass) return eventClass;
414
+ const legacyEvents = hook.events;
415
+ if (legacyEvents !== void 0) {
416
+ throw new Error(
417
+ `Hook '${getConstructType(hook)}' uses the old events array. Extend HarnessHook.for(EventClass) instead.`
418
+ );
419
+ }
420
+ return void 0;
421
+ }
422
+ function eventType(eventClass) {
423
+ return getConstructType(eventClass);
424
+ }
425
+ function isContextProviderBinding(value) {
426
+ return typeof value === "object" && value !== null && "provider" in value && (isContextProviderInstance(value.provider) || isContextProviderClass(value.provider));
427
+ }
428
+ function isContextProviderReference(value) {
429
+ return isContextProviderInstance(value) || isContextProviderClass(value) || isContextProviderBinding(value);
430
+ }
431
+ function assertNoAuthorId(construct, kind) {
432
+ if (Object.prototype.hasOwnProperty.call(construct, "id")) {
433
+ throw new Error(`${kind} '${getConstructType(construct)}' declares an author id. Use the class type instead.`);
434
+ }
435
+ }
436
+
437
+ // src/runtime/context-registry.ts
438
+ function nowIso() {
439
+ return (/* @__PURE__ */ new Date()).toISOString();
440
+ }
441
+ function cloneJSON(value) {
442
+ return JSON.parse(JSON.stringify(value));
443
+ }
444
+ var ContextRegistry = class {
445
+ snapshots = [];
446
+ currentSnapshot;
447
+ entries = [];
448
+ get current() {
449
+ return this.currentSnapshot;
450
+ }
451
+ get allEntries() {
452
+ return this.entries;
453
+ }
454
+ restore(input) {
455
+ this.entries = cloneJSON(input.entries);
456
+ this.currentSnapshot = input.snapshot ? cloneJSON(input.snapshot) : void 0;
457
+ }
458
+ recordSnapshot(snapshot) {
459
+ this.currentSnapshot = snapshot;
460
+ this.snapshots.push(snapshot);
461
+ }
462
+ loadSnapshots(snapshots) {
463
+ this.snapshots = cloneJSON(snapshots);
464
+ this.currentSnapshot = this.snapshots.at(-1);
465
+ }
466
+ filter(filter) {
467
+ let entries = [...this.entries];
468
+ if (filter?.id) entries = entries.filter((entry) => entry.id === filter.id);
469
+ if (filter?.scope) entries = entries.filter((entry) => entry.scope === filter.scope);
470
+ if (filter?.consume) entries = entries.filter((entry) => entry.consume === filter.consume);
471
+ if (filter?.providerId) entries = entries.filter((entry) => entry.contribution.providerId === filter.providerId);
472
+ if (filter?.role) entries = entries.filter((entry) => entry.contribution.authorRole === filter.role || entry.contribution.role === filter.role);
473
+ if (filter?.on) {
474
+ const on = eventType(filter.on);
475
+ entries = entries.filter((entry) => entry.on === on);
476
+ }
477
+ return entries;
478
+ }
479
+ entriesFor(eventClass, runId, turnId) {
480
+ const on = eventType(eventClass);
481
+ return this.entries.filter(
482
+ (entry) => this.isActive(entry, runId, turnId) && (entry.on === on || entry.activatedAt !== void 0)
483
+ );
484
+ }
485
+ activateFor(eventClass, record, runId, turnId) {
486
+ const on = eventType(eventClass);
487
+ this.entries = this.entries.map((entry) => {
488
+ if (entry.on !== on || entry.activatedAt !== void 0 || !this.isActive(entry, runId, turnId)) return entry;
489
+ return {
490
+ ...entry,
491
+ activatedAt: record.at,
492
+ activatedByEventId: record.id,
493
+ activatedByEventType: record.type
494
+ };
495
+ });
496
+ }
497
+ consume(entries) {
498
+ const consumed = new Set(entries.filter((entry) => entry.consume === "once" /* Once */).map((entry) => entry.id));
499
+ if (!consumed.size) return;
500
+ this.entries = this.entries.filter((entry) => !consumed.has(entry.id));
501
+ }
502
+ expireScope(scope, turnId) {
503
+ this.entries = this.entries.filter((entry) => {
504
+ if (entry.scope !== scope) return true;
505
+ if (scope === "turn" /* Turn */ && turnId) return entry.turnId !== turnId;
506
+ return false;
507
+ });
508
+ }
509
+ addContribution(input) {
510
+ const options = input.options ?? {};
511
+ const entry = {
512
+ id: options.id ?? randomId(),
513
+ scope: options.scope ?? "run" /* Run */,
514
+ on: eventType(options.on ?? ModelBeforeEvent),
515
+ consume: options.consume ?? "once" /* Once */,
516
+ createdAt: nowIso(),
517
+ runId: input.runId,
518
+ turnId: input.turnId,
519
+ modeId: input.modeId,
520
+ contribution: input.contribution,
521
+ metadata: options.metadata
522
+ };
523
+ if (options.replace) {
524
+ this.entries = this.entries.filter((candidate) => candidate.id !== entry.id);
525
+ } else if (this.entries.some((candidate) => candidate.id === entry.id)) {
526
+ throw new Error(`Context entry '${entry.id}' already exists. Use replace: true to overwrite it.`);
527
+ }
528
+ this.entries.push(entry);
529
+ return cloneJSON(entry);
530
+ }
531
+ remove(id) {
532
+ const before = this.entries.length;
533
+ this.entries = this.entries.filter((entry) => entry.id !== id);
534
+ return this.entries.length !== before;
535
+ }
536
+ clear(filter) {
537
+ const targets = new Set(this.filter(filter).map((entry) => entry.id));
538
+ this.entries = this.entries.filter((entry) => !targets.has(entry.id));
539
+ return targets.size;
540
+ }
541
+ normalizeProviderInput(input, options, provider, normalize, runId, turnId, modeId) {
542
+ return this.addContribution({
543
+ contribution: normalize(input, {
544
+ providerId: provider?.type,
545
+ providerLabel: provider?.label
546
+ }),
547
+ options,
548
+ runId,
549
+ turnId,
550
+ modeId
551
+ });
552
+ }
553
+ isActive(entry, runId, turnId) {
554
+ if (entry.scope === "session" /* Session */) return true;
555
+ if (entry.scope === "run" /* Run */) return entry.runId === runId;
556
+ if (entry.scope === "turn" /* Turn */) return entry.turnId === turnId;
557
+ return false;
558
+ }
559
+ };
560
+
561
+ // src/runtime/sandbox-manager.ts
562
+ var SandboxManager = class {
563
+ constructor(input) {
564
+ this.input = input;
565
+ }
566
+ input;
567
+ session;
568
+ get current() {
569
+ return this.session;
570
+ }
571
+ async ensureOpen() {
572
+ if (this.session) return this.session;
573
+ const opened = await this.input.sandbox.open({
574
+ sessionId: this.input.sessionId,
575
+ runId: this.input.getRunId(),
576
+ agentKey: this.input.agentKey,
577
+ workDir: this.input.workDir,
578
+ outputDir: this.input.getOutputDir(),
579
+ services: this.input.services
580
+ });
581
+ this.session = this.wrap(opened);
582
+ this.input.logOpened({ sandboxId: opened.id, workDir: opened.workDir });
583
+ return this.session;
584
+ }
585
+ async close() {
586
+ const sandbox = this.session;
587
+ this.session = void 0;
588
+ if (!sandbox) return;
589
+ await sandbox.close?.();
590
+ this.input.logClosed({ sandboxId: sandbox.id });
591
+ }
592
+ wrap(base) {
593
+ const input = this.input;
594
+ return new class LoggedHarnessSandboxSession extends HarnessSandboxSession {
595
+ id = base.id;
596
+ workDir = base.workDir;
597
+ async exec(commandInput) {
598
+ const started = performance.now();
599
+ input.logExecStarted({
600
+ sandboxId: base.id,
601
+ command: summarizeValue(commandInput.command),
602
+ cwd: commandInput.cwd,
603
+ timeoutMs: commandInput.timeoutMs
604
+ });
605
+ try {
606
+ const result = await base.exec(commandInput);
607
+ input.logExecCompleted({
608
+ sandboxId: base.id,
609
+ exitCode: result.exitCode,
610
+ signal: result.signal,
611
+ timedOut: result.timedOut,
612
+ durationMs: result.durationMs
613
+ });
614
+ return result;
615
+ } catch (error) {
616
+ input.logExecFailed({
617
+ sandboxId: base.id,
618
+ error,
619
+ durationMs: Math.round(performance.now() - started)
620
+ });
621
+ throw error;
622
+ }
623
+ }
624
+ async close() {
625
+ await base.close?.();
626
+ }
627
+ }();
628
+ }
629
+ };
630
+
631
+ // src/runtime/storage-coordinator.ts
632
+ var RunStorageCoordinator = class {
633
+ constructor(input) {
634
+ this.input = input;
635
+ this.runIdValue = input.runId;
636
+ this.storePromise = this.createStore(this.runIdValue);
637
+ }
638
+ input;
639
+ runIdValue;
640
+ store;
641
+ storePromise;
642
+ initialized = false;
643
+ runtimeLoaded = false;
644
+ get runDir() {
645
+ return this.store?.runDir;
646
+ }
647
+ async ensureInitialized() {
648
+ if (this.initialized) return;
649
+ await this.write("init", (store) => store.init());
650
+ this.initialized = true;
651
+ }
652
+ async loadRuntimeState() {
653
+ if (this.runtimeLoaded) return void 0;
654
+ this.runtimeLoaded = true;
655
+ return {
656
+ snapshots: await this.write("load_snapshots", (store) => store.loadSnapshots()),
657
+ contextSnapshots: await this.write("load_context_snapshots", (store) => store.loadContextSnapshots()),
658
+ transcript: await this.write("load_transcript", (store) => store.loadTranscript()),
659
+ events: await this.write("load_events", (store) => store.loadEvents()),
660
+ cursors: await this.write("load_cursors", (store) => store.loadCursors())
661
+ };
662
+ }
663
+ async saveTranscript(messages) {
664
+ await this.write("save_transcript", (store) => store.saveTranscript(messages));
665
+ }
666
+ async saveMetrics(metrics) {
667
+ await this.write("save_metrics", (store) => store.saveMetrics(metrics));
668
+ }
669
+ async saveSnapshot(snapshot) {
670
+ await this.write("save_snapshot", (store) => store.saveSnapshot(snapshot));
671
+ }
672
+ async deleteSnapshot(id) {
673
+ await this.write("delete_snapshot", (store) => store.deleteSnapshot(id));
674
+ }
675
+ async saveContextSnapshot(snapshot) {
676
+ await this.write("save_context_snapshot", (store) => store.saveContextSnapshot(snapshot));
677
+ }
678
+ async recordEvent(event) {
679
+ await this.write("record_event", (store) => store.recordEvent(event));
680
+ }
681
+ async saveCursors(cursors) {
682
+ await this.write("save_cursors", (store) => store.saveCursors(cursors));
683
+ }
684
+ async beginRun(runId) {
685
+ await this.close();
686
+ this.runIdValue = runId;
687
+ this.store = void 0;
688
+ this.storePromise = this.createStore(runId);
689
+ this.initialized = false;
690
+ this.runtimeLoaded = false;
691
+ }
692
+ async close() {
693
+ const store = this.store ?? await this.storePromise;
694
+ await store.close?.();
695
+ }
696
+ async createStore(runId) {
697
+ try {
698
+ const store = await this.input.storage.openRun({
699
+ runId,
700
+ sessionId: this.input.sessionId,
701
+ agentKey: this.input.agentKey,
702
+ outputDir: this.input.outputDir
703
+ });
704
+ this.input.logOpened({ storageId: this.input.storage.id, runId, runDir: store.runDir });
705
+ return store;
706
+ } catch (error) {
707
+ this.input.logFailed({ operation: "open_run", error });
708
+ throw error;
709
+ }
710
+ }
711
+ async getStore() {
712
+ if (this.store) return this.store;
713
+ this.store = await this.storePromise;
714
+ return this.store;
715
+ }
716
+ async write(operation, write) {
717
+ try {
718
+ return await write(await this.getStore());
719
+ } catch (error) {
720
+ this.input.logFailed({ operation, error });
721
+ throw error;
722
+ }
723
+ }
724
+ };
725
+
726
+ // src/runtime/event-recorder.ts
727
+ function nowIso2() {
728
+ return (/* @__PURE__ */ new Date()).toISOString();
729
+ }
730
+ var RehydratedHarnessEvent = class extends HarnessEvent {
731
+ };
732
+ var EventRecorder = class {
733
+ events = [];
734
+ eventSeq = 0;
735
+ listeners = /* @__PURE__ */ new Set();
736
+ get count() {
737
+ return this.events.length;
738
+ }
739
+ subscribe(listener) {
740
+ this.listeners.add(listener);
741
+ return () => this.listeners.delete(listener);
742
+ }
743
+ load(records) {
744
+ if (records.length === 0) return;
745
+ this.events = records.map((record) => new RehydratedHarnessEvent(record));
746
+ this.eventSeq = Math.max(0, ...records.map((event) => event.seq));
747
+ }
748
+ has(id) {
749
+ return this.events.some((event) => event.id === id);
750
+ }
751
+ latestForBranch(branchId) {
752
+ let latest;
753
+ for (const event of this.events) {
754
+ if (event.record.branchId !== branchId) continue;
755
+ if (!latest || event.record.seq > latest.record.seq) latest = event;
756
+ }
757
+ return latest;
758
+ }
759
+ record(input) {
760
+ const record = {
761
+ id: randomId(),
762
+ seq: ++this.eventSeq,
763
+ branchId: input.branchId,
764
+ type: input.type,
765
+ eventClassId: input.type,
766
+ at: nowIso2(),
767
+ source: input.source,
768
+ payload: input.payload,
769
+ runId: input.runId,
770
+ turnId: input.turnId,
771
+ modeId: input.modeId,
772
+ correlationId: input.correlationId,
773
+ causationId: input.causationId,
774
+ hidden: true,
775
+ metadata: input.metadata
776
+ };
777
+ const event = new input.eventClass(record);
778
+ this.events.push(event);
779
+ return event;
780
+ }
781
+ query(filter, activeSegments) {
782
+ let events = this.events;
783
+ if (!filter?.includeInactive) {
784
+ events = events.filter((event) => {
785
+ const maxSeq = activeSegments.get(event.record.branchId);
786
+ return maxSeq !== void 0 && event.record.seq <= maxSeq;
787
+ });
788
+ }
789
+ if (filter?.event) events = events.filter((event) => event.type === eventType(filter.event));
790
+ if (filter?.type) events = events.filter((event) => event.type === filter.type);
791
+ if (filter?.sourceKind) events = events.filter((event) => event.record.source.kind === filter.sourceKind);
792
+ if (filter?.since) events = events.filter((event) => event.at >= filter.since);
793
+ if (filter?.until) events = events.filter((event) => event.at <= filter.until);
794
+ if (typeof filter?.limit === "number" && filter.limit > 0) events = events.slice(-filter.limit);
795
+ return events;
796
+ }
797
+ async notify(record) {
798
+ for (const listener of this.listeners) await listener(record);
799
+ }
800
+ };
801
+
802
+ // src/runtime/transcript-manager.ts
803
+ var rootBranchId = "main";
804
+ function nowIso3() {
805
+ return (/* @__PURE__ */ new Date()).toISOString();
806
+ }
807
+ function cloneJSON2(value) {
808
+ return JSON.parse(JSON.stringify(value));
809
+ }
810
+ function createTranscriptCursor(input) {
811
+ return {
812
+ id: randomId(),
813
+ branchId: input?.branchId ?? rootBranchId,
814
+ headMessageId: input?.headMessageId,
815
+ seq: input?.seq ?? 0,
816
+ updatedAt: nowIso3()
817
+ };
818
+ }
819
+ function createEventCursor(input) {
820
+ return {
821
+ id: randomId(),
822
+ branchId: input?.branchId ?? rootBranchId,
823
+ headEventId: input?.headEventId,
824
+ seq: input?.seq ?? 0,
825
+ updatedAt: nowIso3()
826
+ };
827
+ }
828
+ var TranscriptManager = class {
829
+ messages = [];
830
+ messageSeq = 0;
831
+ transcriptCursor = createTranscriptCursor();
832
+ eventCursor = createEventCursor();
833
+ branches = /* @__PURE__ */ new Map([
834
+ [rootBranchId, { id: rootBranchId, createdAt: nowIso3() }]
835
+ ]);
836
+ get count() {
837
+ return this.messages.length;
838
+ }
839
+ get activeTranscriptCursor() {
840
+ return this.transcriptCursor;
841
+ }
842
+ get activeEventCursor() {
843
+ return this.eventCursor;
844
+ }
845
+ get allMessages() {
846
+ return this.messages;
847
+ }
848
+ get allBranches() {
849
+ return [...this.branches.values()];
850
+ }
851
+ addBranches(branches) {
852
+ for (const branch of branches) {
853
+ if (!this.branches.has(branch.id)) this.branches.set(branch.id, cloneJSON2(branch));
854
+ }
855
+ }
856
+ loadTranscript(messages) {
857
+ if (messages.length === 0) return;
858
+ this.messages = cloneJSON2(messages);
859
+ this.messageSeq = Math.max(0, ...messages.map((message) => message.seq));
860
+ }
861
+ loadCursors(input) {
862
+ this.addBranches(input.branches ?? []);
863
+ const transcriptHead = input.transcriptCursor.headMessageId;
864
+ const eventHead = input.eventCursor.headEventId;
865
+ if (!transcriptHead || this.messages.some((message) => message.id === transcriptHead)) {
866
+ this.transcriptCursor = cloneJSON2(input.transcriptCursor);
867
+ }
868
+ if (!eventHead || input.eventExists(eventHead)) {
869
+ this.eventCursor = cloneJSON2(input.eventCursor);
870
+ }
871
+ }
872
+ restoreCursors(input) {
873
+ this.addBranches(input.branches ?? []);
874
+ this.transcriptCursor = cloneJSON2(input.transcriptCursor);
875
+ this.eventCursor = cloneJSON2(input.eventCursor);
876
+ }
877
+ appendMessage(input) {
878
+ const branchId = input.branchId ?? this.transcriptCursor.branchId;
879
+ const message = {
880
+ id: input.id ?? randomId(),
881
+ seq: ++this.messageSeq,
882
+ branchId,
883
+ parentMessageId: this.transcriptCursor.headMessageId,
884
+ role: input.role,
885
+ authorRole: input.roleInfo?.authorRole,
886
+ roleType: input.roleInfo?.roleType,
887
+ content: input.content,
888
+ createdAt: input.createdAt ?? nowIso3(),
889
+ modeId: input.modeId,
890
+ turnId: input.turnId,
891
+ hidden: input.hidden,
892
+ eventCursor: cloneJSON2(this.eventCursor),
893
+ metadata: input.metadata
894
+ };
895
+ this.messages.push(message);
896
+ this.transcriptCursor = createTranscriptCursor({
897
+ branchId,
898
+ headMessageId: message.id,
899
+ seq: message.seq
900
+ });
901
+ return message;
902
+ }
903
+ markMessageEventCursor(messageId) {
904
+ const message = this.messages.find((candidate) => candidate.id === messageId);
905
+ if (!message) return false;
906
+ message.eventCursor = cloneJSON2(this.eventCursor);
907
+ return true;
908
+ }
909
+ ensureBranchForAppend() {
910
+ const cursor = this.transcriptCursor;
911
+ const latest = this.branchLatestMessage(cursor.branchId);
912
+ if (!latest || latest.id === cursor.headMessageId) return;
913
+ const branchId = randomId();
914
+ const branch = {
915
+ id: branchId,
916
+ createdAt: nowIso3(),
917
+ parentBranchId: cursor.branchId,
918
+ parentMessageId: cursor.headMessageId,
919
+ parentMessageSeq: cursor.seq,
920
+ parentEventSeq: this.eventCursor.seq
921
+ };
922
+ this.branches.set(branchId, branch);
923
+ this.transcriptCursor = createTranscriptCursor({
924
+ branchId,
925
+ headMessageId: cursor.headMessageId,
926
+ seq: cursor.seq
927
+ });
928
+ this.eventCursor = createEventCursor({
929
+ branchId,
930
+ headEventId: this.eventCursor.headEventId,
931
+ seq: this.eventCursor.seq
932
+ });
933
+ }
934
+ ensureBranchForEventAppend(latestEvent) {
935
+ if (!latestEvent || latestEvent.record.seq <= this.eventCursor.seq) return;
936
+ const branchId = randomId();
937
+ const branch = {
938
+ id: branchId,
939
+ createdAt: nowIso3(),
940
+ parentBranchId: this.eventCursor.branchId,
941
+ parentMessageId: this.transcriptCursor.headMessageId,
942
+ parentMessageSeq: this.transcriptCursor.seq,
943
+ parentEventSeq: this.eventCursor.seq
944
+ };
945
+ this.branches.set(branchId, branch);
946
+ this.transcriptCursor = createTranscriptCursor({
947
+ branchId,
948
+ headMessageId: this.transcriptCursor.headMessageId,
949
+ seq: this.transcriptCursor.seq
950
+ });
951
+ this.eventCursor = createEventCursor({
952
+ branchId,
953
+ headEventId: this.eventCursor.headEventId,
954
+ seq: this.eventCursor.seq
955
+ });
956
+ }
957
+ advanceEventCursor(record) {
958
+ this.eventCursor = createEventCursor({
959
+ branchId: record.branchId,
960
+ headEventId: record.id,
961
+ seq: record.seq
962
+ });
963
+ }
964
+ filter(options, currentTurnId) {
965
+ const activeIds = options?.includeInactive ? void 0 : this.activeMessageIds();
966
+ let messages = activeIds ? this.messages.filter((message) => activeIds.has(message.id)) : [...this.messages];
967
+ if (!options?.includeHidden) messages = messages.filter((message) => !message.hidden);
968
+ if (options?.beforeCurrentTurn && currentTurnId) {
969
+ messages = messages.filter((message) => message.turnId !== currentTurnId);
970
+ }
971
+ if (options?.roles?.length) {
972
+ const allowed = new Set(options.roles);
973
+ messages = messages.filter((message) => allowed.has(message.role));
974
+ }
975
+ messages.sort((a, b) => a.seq - b.seq);
976
+ if (typeof options?.limit === "number" && options.limit > 0) messages = messages.slice(-options.limit);
977
+ return messages;
978
+ }
979
+ resolveSeekTarget(target) {
980
+ if (target === "start") {
981
+ return {
982
+ transcriptCursor: createTranscriptCursor({ branchId: rootBranchId, seq: 0 }),
983
+ eventCursor: createEventCursor({ branchId: rootBranchId, seq: 0 })
984
+ };
985
+ }
986
+ if (target === "latest") {
987
+ const transcriptCursor = this.latestCursorForBranch(this.transcriptCursor.branchId);
988
+ const head2 = transcriptCursor.headMessageId ? this.messages.find((message) => message.id === transcriptCursor.headMessageId) : void 0;
989
+ return {
990
+ transcriptCursor,
991
+ eventCursor: head2 ? this.eventCursorForMessage(head2) : this.eventCursor
992
+ };
993
+ }
994
+ if ("messageId" in target) {
995
+ const message = this.messages.find((candidate) => candidate.id === target.messageId);
996
+ if (!message) throw new Error(`Transcript message '${target.messageId}' was not found.`);
997
+ return {
998
+ transcriptCursor: createTranscriptCursor({
999
+ branchId: message.branchId,
1000
+ headMessageId: message.id,
1001
+ seq: message.seq
1002
+ }),
1003
+ eventCursor: this.eventCursorForMessage(message)
1004
+ };
1005
+ }
1006
+ const head = target.cursor.headMessageId ? this.messages.find((message) => message.id === target.cursor.headMessageId) : void 0;
1007
+ return {
1008
+ transcriptCursor: cloneJSON2(target.cursor),
1009
+ eventCursor: head ? this.eventCursorForMessage(head) : createEventCursor({
1010
+ branchId: target.cursor.branchId,
1011
+ seq: 0
1012
+ })
1013
+ };
1014
+ }
1015
+ applyResolvedSeek(resolved) {
1016
+ this.transcriptCursor = resolved.transcriptCursor;
1017
+ this.eventCursor = resolved.eventCursor;
1018
+ }
1019
+ activeEventSegments(cursor = this.eventCursor) {
1020
+ const segments = /* @__PURE__ */ new Map();
1021
+ let branchId = cursor.branchId;
1022
+ let maxSeq = cursor.seq;
1023
+ while (branchId) {
1024
+ const existing = segments.get(branchId);
1025
+ segments.set(branchId, existing === void 0 ? maxSeq : Math.max(existing, maxSeq));
1026
+ const branch = this.branches.get(branchId);
1027
+ if (!branch?.parentBranchId) break;
1028
+ branchId = branch.parentBranchId;
1029
+ maxSeq = branch.parentEventSeq ?? 0;
1030
+ }
1031
+ return segments;
1032
+ }
1033
+ activeMessageIds(cursor = this.transcriptCursor) {
1034
+ const byId = new Map(this.messages.map((message) => [message.id, message]));
1035
+ const active = /* @__PURE__ */ new Set();
1036
+ let current = cursor.headMessageId ? byId.get(cursor.headMessageId) : void 0;
1037
+ while (current) {
1038
+ if (current.seq > cursor.seq) break;
1039
+ active.add(current.id);
1040
+ current = current.parentMessageId ? byId.get(current.parentMessageId) : void 0;
1041
+ }
1042
+ return active;
1043
+ }
1044
+ branchLatestMessage(branchId) {
1045
+ let latest;
1046
+ for (const message of this.messages) {
1047
+ if (message.branchId !== branchId) continue;
1048
+ if (!latest || message.seq > latest.seq) latest = message;
1049
+ }
1050
+ return latest;
1051
+ }
1052
+ eventCursorForMessage(message) {
1053
+ return message.eventCursor ? cloneJSON2(message.eventCursor) : createEventCursor({
1054
+ branchId: message.branchId,
1055
+ seq: this.eventCursor.seq,
1056
+ headEventId: this.eventCursor.headEventId
1057
+ });
1058
+ }
1059
+ latestCursorForBranch(branchId) {
1060
+ const latest = this.branchLatestMessage(branchId);
1061
+ const branch = this.branches.get(branchId);
1062
+ return createTranscriptCursor({
1063
+ branchId,
1064
+ headMessageId: latest?.id ?? branch?.parentMessageId,
1065
+ seq: latest?.seq ?? branch?.parentMessageSeq ?? 0
1066
+ });
1067
+ }
1068
+ };
1069
+
1070
+ // src/runtime/role-resolver.ts
1071
+ var RoleResolver = class {
1072
+ constructor(roles, input) {
1073
+ this.roles = roles;
1074
+ this.input = input;
1075
+ }
1076
+ roles;
1077
+ input;
1078
+ resolve(selector) {
1079
+ const definition = this.definitionFromSelector(selector);
1080
+ if (!definition) {
1081
+ if (typeof selector !== "string") throw new Error(`Unknown role '${getConstructType(selector)}'.`);
1082
+ return {
1083
+ role: selector,
1084
+ authorRole: selector,
1085
+ roleType: selector,
1086
+ target: selector === "system" /* System */ ? "system" /* System */ : "messages" /* Messages */
1087
+ };
1088
+ }
1089
+ return {
1090
+ role: definition.nativeRole ?? getRoleName(definition),
1091
+ authorRole: getRoleName(definition),
1092
+ roleType: getConstructType(definition),
1093
+ target: definition.target,
1094
+ definition
1095
+ };
1096
+ }
1097
+ assertModelProviderSupportsMessages(messages) {
1098
+ for (const message of messages) {
1099
+ if (message.hidden || message.role === "event") continue;
1100
+ this.assertModelProviderSupportsRole(message.role);
1101
+ }
1102
+ }
1103
+ summary(role) {
1104
+ return {
1105
+ type: getConstructType(role),
1106
+ name: getRoleName(role),
1107
+ label: getConstructLabel(role),
1108
+ target: role.target,
1109
+ nativeRole: role.nativeRole,
1110
+ default: role.default,
1111
+ description: role.description
1112
+ };
1113
+ }
1114
+ definitionFromSelector(selector) {
1115
+ const registered = this.roles.find((role) => roleMatchesSelector(role, selector));
1116
+ if (registered) return registered;
1117
+ if (typeof selector === "string") return void 0;
1118
+ if (isRoleInstance(selector)) return selector;
1119
+ if (isRoleClass(selector)) {
1120
+ try {
1121
+ return new selector();
1122
+ } catch {
1123
+ throw new Error(`Role '${getConstructType(selector)}' must be registered as an instance or have a zero-argument constructor.`);
1124
+ }
1125
+ }
1126
+ return void 0;
1127
+ }
1128
+ assertModelProviderSupportsRole(role) {
1129
+ const supportsRole = this.input.supportsRole;
1130
+ if (supportsRole && !supportsRole(role)) {
1131
+ throw new Error(`Model provider '${this.input.modelProviderId}' does not support native role '${role}'.`);
1132
+ }
1133
+ }
1134
+ };
1135
+
1136
+ // src/runtime/tool-executor.ts
1137
+ function invalidFieldsFromIssues(issues) {
1138
+ return issues.map((entry) => ({
1139
+ path: entry.path,
1140
+ message: entry.message,
1141
+ code: entry.code,
1142
+ expected: entry.expected,
1143
+ received: entry.received
1144
+ }));
1145
+ }
1146
+ function safeParseWithSchema(schema, payload2) {
1147
+ const normalized = normalizeSchema(schema);
1148
+ const result = normalized.safeParse(payload2);
1149
+ if (result.success) return { ok: true, data: result.data };
1150
+ const issues = normalized.issuesFromError(result.error);
1151
+ return { ok: false, error: result.error, issues, invalidFields: invalidFieldsFromIssues(issues) };
1152
+ }
1153
+ function isEmptyToolArgs(args) {
1154
+ return args == null || typeof args === "object" && !Array.isArray(args) && Object.keys(args).length === 0;
1155
+ }
1156
+ function ensureToolMetric(metrics, name) {
1157
+ const existing = metrics.tools[name];
1158
+ if (existing) return existing;
1159
+ const metric = { name, count: 0, errorCount: 0, totalDurationMs: 0 };
1160
+ metrics.tools[name] = metric;
1161
+ return metric;
1162
+ }
1163
+ var ToolExecutor = class {
1164
+ constructor(input) {
1165
+ this.input = input;
1166
+ }
1167
+ input;
1168
+ depth = 0;
1169
+ async execute(input) {
1170
+ if (this.depth > 8) throw new Error("Tool invocation depth exceeded.");
1171
+ const source = input.source ?? { kind: "runtime" };
1172
+ const id = input.callId ?? randomId();
1173
+ const toolSource = { kind: "tool", id, name: input.tool.name };
1174
+ const start = performance.now();
1175
+ const metrics = this.input.getMetrics();
1176
+ const metric = ensureToolMetric(metrics, input.tool.name);
1177
+ metric.count++;
1178
+ metrics.toolCallCount++;
1179
+ await this.input.addToolCallMessage(input.tool, input.args, id, source);
1180
+ this.input.log(
1181
+ ToolStartedLog,
1182
+ { toolName: input.tool.name, args: summarizeValue(input.args), risk: input.tool.risk },
1183
+ toolSource,
1184
+ id,
1185
+ input.parentCausationId ?? input.parentCorrelationId
1186
+ );
1187
+ if (isEmptyToolArgs(input.args)) {
1188
+ this.input.log(ToolArgsEmptyLog, { toolName: input.tool.name }, toolSource, id, input.parentCausationId ?? input.parentCorrelationId);
1189
+ }
1190
+ const startEvent = await this.input.emitInternal(ToolStartEvent, { id, name: input.tool.name, args: input.args }, {
1191
+ source,
1192
+ correlationId: id,
1193
+ causationId: input.parentCausationId ?? input.parentCorrelationId
1194
+ });
1195
+ const toolEventOptions = {
1196
+ source: toolSource,
1197
+ correlationId: id,
1198
+ causationId: startEvent.id
1199
+ };
1200
+ const callerEventOptions = {
1201
+ source,
1202
+ correlationId: id,
1203
+ causationId: startEvent.id
1204
+ };
1205
+ const parsedArgs = safeParseWithSchema(input.tool.inputSchema, input.args);
1206
+ if (!parsedArgs.ok) {
1207
+ const result = this.structuredToolError({
1208
+ toolName: input.tool.name,
1209
+ code: "tool.args.invalid_schema",
1210
+ message: "Tool arguments did not match schema.",
1211
+ invalidFields: parsedArgs.invalidFields
1212
+ });
1213
+ const durationMs = Math.round(performance.now() - start);
1214
+ metric.errorCount++;
1215
+ metric.totalDurationMs += durationMs;
1216
+ metrics.errors.push(result.content);
1217
+ this.input.log(
1218
+ ToolInvalidSchemaLog,
1219
+ { toolName: input.tool.name, issues: parsedArgs.issues },
1220
+ toolSource,
1221
+ id,
1222
+ startEvent.id,
1223
+ { durationMs }
1224
+ );
1225
+ await this.input.addToolResultMessage(input.tool, result, id);
1226
+ await this.input.emitInternal(ToolEndEvent, { id, name: input.tool.name, durationMs, result }, callerEventOptions);
1227
+ this.input.throwIfTurnHandoffRequested();
1228
+ return result;
1229
+ }
1230
+ const approved = await this.approveTool(
1231
+ { id, name: input.tool.name, args: parsedArgs.data, modeId: this.input.getCurrentMode(), risk: input.tool.risk, permissions: input.tool.permissions },
1232
+ input.tool,
1233
+ parsedArgs.data,
1234
+ callerEventOptions
1235
+ );
1236
+ if (!approved) {
1237
+ const denied = this.structuredToolError({
1238
+ toolName: input.tool.name,
1239
+ code: "tool.approval.denied",
1240
+ message: `Tool '${input.tool.name}' was denied by runner policy.`,
1241
+ metadata: { denied: true }
1242
+ });
1243
+ metric.errorCount++;
1244
+ const durationMs = Math.round(performance.now() - start);
1245
+ metric.totalDurationMs += durationMs;
1246
+ this.input.log(ToolFailedLog, {
1247
+ toolName: input.tool.name,
1248
+ durationMs,
1249
+ result: summarizeValue(denied)
1250
+ }, toolSource, id, startEvent.id, { durationMs });
1251
+ await this.input.addToolResultMessage(input.tool, denied, id);
1252
+ await this.input.emitInternal(ToolEndEvent, { id, name: input.tool.name, durationMs, result: denied }, callerEventOptions);
1253
+ this.input.throwIfTurnHandoffRequested();
1254
+ return denied;
1255
+ }
1256
+ this.depth++;
1257
+ try {
1258
+ await this.input.ensureSandboxOpen();
1259
+ const executed = await input.tool.execute(parsedArgs.data, this.input.buildActionSession({ id, name: input.tool.name }, toolSource, id, startEvent.id));
1260
+ const result = this.ensureStructuredToolErrorResult(input.tool, executed);
1261
+ const durationMs = Math.round(performance.now() - start);
1262
+ metric.totalDurationMs += durationMs;
1263
+ if (result.isError) metric.errorCount++;
1264
+ if (result.isError) {
1265
+ this.input.log(ToolFailedLog, {
1266
+ toolName: input.tool.name,
1267
+ durationMs,
1268
+ result: summarizeValue(result)
1269
+ }, toolSource, id, startEvent.id, { durationMs });
1270
+ } else {
1271
+ this.input.log(ToolCompletedLog, {
1272
+ toolName: input.tool.name,
1273
+ durationMs,
1274
+ isError: false,
1275
+ result: summarizeValue(result)
1276
+ }, toolSource, id, startEvent.id, { durationMs });
1277
+ }
1278
+ await this.input.addToolResultMessage(input.tool, result, id);
1279
+ await this.input.emitInternal(ToolEndEvent, { id, name: input.tool.name, durationMs, result }, callerEventOptions);
1280
+ this.input.throwIfTurnHandoffRequested();
1281
+ return result;
1282
+ } catch (error) {
1283
+ const message = error instanceof Error ? error.message : String(error);
1284
+ const result = this.structuredToolError({
1285
+ toolName: input.tool.name,
1286
+ code: "tool.failed",
1287
+ message: "Tool execution failed.",
1288
+ metadata: { error: true }
1289
+ });
1290
+ const durationMs = Math.round(performance.now() - start);
1291
+ metric.errorCount++;
1292
+ metric.totalDurationMs += durationMs;
1293
+ metrics.errors.push(message);
1294
+ this.input.log(ToolFailedLog, {
1295
+ toolName: input.tool.name,
1296
+ durationMs,
1297
+ error
1298
+ }, toolSource, id, startEvent.id, { durationMs });
1299
+ await this.input.addToolResultMessage(input.tool, result, id);
1300
+ await this.input.emitInternal(ErrorEvent, { message, details: error }, toolEventOptions);
1301
+ await this.input.emitInternal(ToolEndEvent, { id, name: input.tool.name, durationMs, result }, callerEventOptions);
1302
+ this.input.throwIfTurnHandoffRequested();
1303
+ return result;
1304
+ } finally {
1305
+ this.depth--;
1306
+ }
1307
+ }
1308
+ structuredToolError(input) {
1309
+ return {
1310
+ content: input.message,
1311
+ data: createToolErrorPayload({
1312
+ code: input.code,
1313
+ message: input.message,
1314
+ toolName: input.toolName,
1315
+ invalidFields: input.invalidFields
1316
+ }),
1317
+ isError: true,
1318
+ metadata: {
1319
+ errorCode: input.code,
1320
+ ...input.invalidFields ? { invalidFields: input.invalidFields } : {},
1321
+ ...input.metadata ?? {}
1322
+ }
1323
+ };
1324
+ }
1325
+ ensureStructuredToolErrorResult(tool, result) {
1326
+ if (!result.isError || result.data !== void 0) return result;
1327
+ return {
1328
+ ...result,
1329
+ data: createToolErrorPayload({
1330
+ code: "tool.failed",
1331
+ message: result.content || "Tool execution failed.",
1332
+ toolName: tool.name
1333
+ }),
1334
+ metadata: {
1335
+ ...result.metadata,
1336
+ errorCode: result.metadata?.errorCode ?? "tool.failed"
1337
+ }
1338
+ };
1339
+ }
1340
+ async approveTool(request, tool, args, eventOptions) {
1341
+ const policy = this.input.getToolApprovalMode() ?? "tool-default";
1342
+ if (policy === "auto") return this.recordApprovalResolution(request, "approved", eventOptions);
1343
+ if (policy === "deny") return this.recordApprovalResolution(request, "denied", eventOptions);
1344
+ const requiresApproval = await this.toolRequiresApproval(tool, args, request.id, eventOptions.causationId);
1345
+ if (policy === "tool-default" && !requiresApproval) return true;
1346
+ await this.input.emitInternal(
1347
+ ToolApprovalRequestedEvent,
1348
+ {
1349
+ id: request.id,
1350
+ name: request.name,
1351
+ args: request.args,
1352
+ modeId: request.modeId,
1353
+ risk: request.risk,
1354
+ permissions: request.permissions
1355
+ },
1356
+ eventOptions
1357
+ );
1358
+ this.input.log(
1359
+ ToolApprovalRequestedLog,
1360
+ { toolName: request.name, risk: request.risk, permissions: request.permissions },
1361
+ eventOptions.source,
1362
+ eventOptions.correlationId,
1363
+ eventOptions.causationId
1364
+ );
1365
+ if (!this.input.approveTool) return this.recordApprovalResolution(request, "denied", eventOptions);
1366
+ const decision = await this.input.approveTool(request);
1367
+ return this.recordApprovalResolution(request, decision === true || decision === "approved" ? "approved" : "denied", eventOptions);
1368
+ }
1369
+ async toolRequiresApproval(tool, args, toolCallId, causationId) {
1370
+ if (typeof tool.requiresApproval === "function") {
1371
+ await this.input.ensureSandboxOpen();
1372
+ return tool.requiresApproval(
1373
+ args,
1374
+ this.input.buildActionSession(
1375
+ { id: toolCallId, name: tool.name },
1376
+ { kind: "tool", id: toolCallId, name: tool.name },
1377
+ toolCallId,
1378
+ causationId
1379
+ )
1380
+ );
1381
+ }
1382
+ return tool.requiresApproval === true;
1383
+ }
1384
+ async recordApprovalResolution(request, decision, eventOptions) {
1385
+ await this.input.emitInternal(
1386
+ ToolApprovalResolvedEvent,
1387
+ { id: request.id, name: request.name, args: request.args, decision, modeId: request.modeId },
1388
+ eventOptions
1389
+ );
1390
+ this.input.log(
1391
+ ToolApprovalResolvedLog,
1392
+ { toolName: request.name, approved: decision === "approved" },
1393
+ eventOptions.source,
1394
+ eventOptions.correlationId,
1395
+ eventOptions.causationId
1396
+ );
1397
+ return decision === "approved";
1398
+ }
1399
+ };
1400
+
1401
+ // src/runtime/snapshot-manager.ts
1402
+ function cloneJSON3(value) {
1403
+ return JSON.parse(JSON.stringify(value));
1404
+ }
1405
+ var SnapshotManager = class {
1406
+ constructor(input) {
1407
+ this.input = input;
1408
+ }
1409
+ input;
1410
+ snapshots = [];
1411
+ load(snapshot) {
1412
+ if (!this.snapshots.some((candidate) => candidate.id === snapshot.id)) {
1413
+ this.snapshots.push(cloneJSON3(snapshot));
1414
+ }
1415
+ }
1416
+ add(snapshot) {
1417
+ this.snapshots.push(snapshot);
1418
+ }
1419
+ get(id) {
1420
+ const snapshot = this.snapshots.find((candidate) => candidate.id === id);
1421
+ return snapshot ? cloneJSON3(snapshot) : void 0;
1422
+ }
1423
+ getRaw(id) {
1424
+ return this.snapshots.find((candidate) => candidate.id === id);
1425
+ }
1426
+ async create(input = {}, eventOptions) {
1427
+ const manager = this.requireInput();
1428
+ await manager.ensureStoreInitialized();
1429
+ const snapshot = {
1430
+ id: randomId(),
1431
+ label: input.label,
1432
+ createdAt: manager.now(),
1433
+ ...cloneJSON3(manager.readState()),
1434
+ metadata: input.metadata
1435
+ };
1436
+ this.add(snapshot);
1437
+ await manager.saveSnapshot(snapshot);
1438
+ const summary = this.summary(snapshot);
1439
+ await manager.emitCreated(summary, eventOptions);
1440
+ manager.logCreated(summary, eventOptions);
1441
+ return cloneJSON3(snapshot);
1442
+ }
1443
+ async restore(id, eventOptions) {
1444
+ const manager = this.requireInput();
1445
+ await manager.ensureStoreInitialized();
1446
+ const snapshot = this.getRaw(id);
1447
+ if (!snapshot) {
1448
+ manager.logRestoreRejected(id, "not_found", eventOptions);
1449
+ throw new Error(`Snapshot '${id}' was not found.`);
1450
+ }
1451
+ manager.restoreState(snapshot);
1452
+ const summary = this.summary(snapshot);
1453
+ await manager.emitRestored(summary, eventOptions);
1454
+ manager.logRestored(summary, eventOptions);
1455
+ await manager.persistCursors();
1456
+ return cloneJSON3(snapshot);
1457
+ }
1458
+ list() {
1459
+ return cloneJSON3(this.snapshots.map((snapshot) => this.summary(snapshot)));
1460
+ }
1461
+ delete(id) {
1462
+ const snapshot = this.snapshots.find((candidate) => candidate.id === id);
1463
+ if (!snapshot) return void 0;
1464
+ this.snapshots = this.snapshots.filter((candidate) => candidate.id !== id);
1465
+ return snapshot;
1466
+ }
1467
+ async deletePersisted(id, eventOptions) {
1468
+ const manager = this.requireInput();
1469
+ await manager.ensureStoreInitialized();
1470
+ const snapshot = this.delete(id);
1471
+ if (!snapshot) return false;
1472
+ await manager.deleteSnapshot(id);
1473
+ const summary = this.summary(snapshot);
1474
+ await manager.emitDeleted(summary, eventOptions);
1475
+ manager.logDeleted(summary, eventOptions);
1476
+ return true;
1477
+ }
1478
+ summary(snapshot) {
1479
+ return {
1480
+ id: snapshot.id,
1481
+ label: snapshot.label,
1482
+ createdAt: snapshot.createdAt,
1483
+ agentKey: snapshot.agentKey,
1484
+ runId: snapshot.runId,
1485
+ turnId: snapshot.turnId,
1486
+ modeId: snapshot.modeId,
1487
+ model: snapshot.model,
1488
+ transcriptCursor: cloneJSON3(snapshot.transcriptCursor),
1489
+ eventCursor: cloneJSON3(snapshot.eventCursor),
1490
+ metadata: snapshot.metadata
1491
+ };
1492
+ }
1493
+ requireInput() {
1494
+ if (!this.input) throw new Error("SnapshotManager operation requires runtime dependencies.");
1495
+ return this.input;
1496
+ }
1497
+ };
1498
+
1499
+ // src/runtime/model-pipeline.ts
1500
+ var ModelPipeline = class {
1501
+ constructor(input) {
1502
+ this.input = input;
1503
+ }
1504
+ input;
1505
+ async run(input) {
1506
+ const prepared = await this.prepareContext(input.mode, input.userMessage);
1507
+ const systemPrompt = prepared.systemPrompt;
1508
+ const messages = prepared.messages;
1509
+ const model = this.input.getModel();
1510
+ const resolved = this.input.resolveModelProvider(model);
1511
+ const source = this.modelProviderSource(resolved);
1512
+ try {
1513
+ const modelStart = performance.now();
1514
+ this.input.log(ModelCallStartedLog, { model, messageCount: messages.length }, source);
1515
+ const result = await resolved.provider.run({
1516
+ runId: this.input.getRunId(),
1517
+ turnId: this.input.getTurnId(),
1518
+ modeId: this.input.getModeId(),
1519
+ modelRef: resolved.modelRef,
1520
+ provider: resolved.namespace,
1521
+ model: resolved.modelId,
1522
+ systemPrompt,
1523
+ messages,
1524
+ roles: this.input.roles,
1525
+ tools: input.tools,
1526
+ maxTurns: input.mode.maxTurns ?? 20,
1527
+ signal: input.options.signal,
1528
+ prepareContext: () => this.prepareContext(input.mode, input.userMessage),
1529
+ emit: async (eventClass, payload2, options) => {
1530
+ const event = await this.input.emit(
1531
+ eventClass,
1532
+ payload2,
1533
+ this.input.withEmitDefaults(source, void 0, void 0, options)
1534
+ );
1535
+ this.input.throwIfTurnHandoffRequested();
1536
+ return event;
1537
+ },
1538
+ executeTool: (tool, args, callId) => this.input.executeTool(tool, args, callId, source)
1539
+ });
1540
+ const assistantMessage = await this.input.addAssistantMessage(result.content, {
1541
+ usage: result.usage,
1542
+ finishReason: result.finishReason
1543
+ });
1544
+ this.input.setFinalAnswer(result.content);
1545
+ this.input.getMetrics().usage = result.usage;
1546
+ await this.input.emitInternal(MessageEndEvent, { message: assistantMessage });
1547
+ await this.input.markMessageEventCursor(assistantMessage.id);
1548
+ this.input.log(ModelCallCompletedLog, {
1549
+ model,
1550
+ durationMs: Math.round(performance.now() - modelStart),
1551
+ finishReason: result.finishReason
1552
+ }, source);
1553
+ await this.input.emitInternal(ModelAfterEvent, {
1554
+ model,
1555
+ content: result.content,
1556
+ usage: result.usage,
1557
+ finishReason: result.finishReason
1558
+ });
1559
+ this.input.throwIfTurnHandoffRequested();
1560
+ } catch (error) {
1561
+ if (this.input.isTurnHandoffSignal(error)) return;
1562
+ const message = error instanceof Error ? error.message : String(error);
1563
+ this.input.getMetrics().errors.push(message);
1564
+ this.input.log(ModelCallFailedLog, { model, error }, source);
1565
+ await this.input.emitInternal(ErrorEvent, { message, details: error });
1566
+ throw error;
1567
+ }
1568
+ }
1569
+ modelProviderSource(resolved) {
1570
+ const id = modelProviderId(resolved.provider);
1571
+ return { kind: "model_provider", id, name: id };
1572
+ }
1573
+ async resolveModePrompt(mode) {
1574
+ const source = { kind: "mode", id: getConstructType(mode), name: getConstructLabel(mode) };
1575
+ if (typeof mode.getPrompt === "function") return mode.getPrompt(this.input.buildReadSession(source));
1576
+ if (typeof mode.prompt === "function") return mode.prompt(this.input.buildReadSession(source));
1577
+ return mode.prompt ?? "";
1578
+ }
1579
+ async prepareContext(mode, userMessage) {
1580
+ const contextSnapshot = await this.input.buildContextSnapshot(ModelBeforeEvent);
1581
+ const prompt = await this.resolveModePrompt(mode);
1582
+ const systemPrompt = [contextSnapshot.systemPrompt, prompt].filter(Boolean).join("\n\n---\n\n");
1583
+ const messages = [...contextSnapshot.messages, userMessage];
1584
+ this.input.assertModelProviderSupportsMessages(messages);
1585
+ await this.input.emitInternal(ContextReadyEvent, {
1586
+ snapshotId: contextSnapshot.id,
1587
+ providerCount: contextSnapshot.providers.length,
1588
+ contributionCount: contextSnapshot.contributions.length
1589
+ });
1590
+ await this.input.emitInternal(ModelBeforeEvent, { model: this.input.getModel(), messageCount: messages.length });
1591
+ this.input.throwIfTurnHandoffRequested();
1592
+ return {
1593
+ systemPrompt,
1594
+ messages,
1595
+ snapshot: contextSnapshot
1596
+ };
1597
+ }
1598
+ };
1599
+
1600
+ // src/runtime/runner.ts
1601
+ function nowIso4() {
1602
+ return (/* @__PURE__ */ new Date()).toISOString();
1603
+ }
1604
+ function cloneJSON4(value) {
1605
+ return JSON.parse(JSON.stringify(value));
1606
+ }
1607
+ var TurnHandoffSignal = class extends Error {
1608
+ constructor() {
1609
+ super("Turn handoff requested.");
1610
+ this.name = "TurnHandoffSignal";
1611
+ }
1612
+ };
1613
+ function defaultRoles() {
1614
+ return [systemRole, userRole, assistantRole, toolRole];
1615
+ }
1616
+ function emptyMetrics() {
1617
+ return {
1618
+ startedAt: nowIso4(),
1619
+ durationMs: 0,
1620
+ turnCount: 0,
1621
+ messageCount: 0,
1622
+ eventCount: 0,
1623
+ toolCallCount: 0,
1624
+ tools: {},
1625
+ errors: []
1626
+ };
1627
+ }
1628
+ function stringifyContent(content) {
1629
+ if (typeof content === "string") return content;
1630
+ if (content == null) return "";
1631
+ return JSON.stringify(content, null, 2);
1632
+ }
1633
+ function normalizeAgentMessageInput(input) {
1634
+ return typeof input === "string" ? { content: input } : input;
1635
+ }
1636
+ var noopAgentLogSession = {
1637
+ debug: () => void 0,
1638
+ info: () => void 0,
1639
+ warn: () => void 0,
1640
+ error: () => void 0,
1641
+ emit: () => void 0
1642
+ };
1643
+ function keyFromLabel(label) {
1644
+ const key = label.trim().replace(/([a-z0-9])([A-Z])/gu, "$1-$2").replace(/[^a-zA-Z0-9]+/gu, "-").replace(/^-|-$/gu, "").toLowerCase();
1645
+ return key || "agent";
1646
+ }
1647
+ function ensureNoLegacyAgentShape(agent) {
1648
+ const candidate = agent;
1649
+ if ("id" in candidate) throw new Error("Agent definitions no longer declare id. Use optional key for packaging metadata.");
1650
+ if ("events" in candidate) throw new Error("Agent definitions no longer declare events. Emit and query event classes directly.");
1651
+ if ("contextProviders" in candidate) {
1652
+ throw new Error("Agent definitions no longer declare contextProviders. Attach providers to modes directly.");
1653
+ }
1654
+ if (!Array.isArray(candidate.modes)) {
1655
+ throw new Error("Agent modes must be an array of HarnessMode instances. Object-literal mode maps are not supported.");
1656
+ }
1657
+ if (typeof candidate.initialMode === "string") {
1658
+ throw new Error("Agent initialMode must be a HarnessMode class or instance, not a string id.");
1659
+ }
1660
+ }
1661
+ function validateMode(mode) {
1662
+ if (!isModeInstance(mode)) throw new Error("Agent modes must extend HarnessMode.");
1663
+ assertNoAuthorId(mode, "Mode");
1664
+ if ("context" in mode) {
1665
+ throw new Error(`Mode '${getConstructType(mode)}' uses context. Declare providers directly on the mode class.`);
1666
+ }
1667
+ if ("lifecycle" in mode) {
1668
+ throw new Error(`Mode '${getConstructType(mode)}' uses lifecycle. Put onEnter/onExit methods directly on the class.`);
1669
+ }
1670
+ if (mode.prompt === void 0 && typeof mode.getPrompt !== "function") {
1671
+ throw new Error(`Mode '${getConstructType(mode)}' must define prompt or getPrompt(ctx).`);
1672
+ }
1673
+ }
1674
+ function validateRoles(roles) {
1675
+ if (!roles) return void 0;
1676
+ for (const role of roles) {
1677
+ if (!isRoleInstance(role)) throw new Error("Agent roles must extend HarnessRole.");
1678
+ assertNoAuthorId(role, "Role");
1679
+ }
1680
+ return roles;
1681
+ }
1682
+ function validateHooks(hooks) {
1683
+ if (!hooks) return void 0;
1684
+ for (const hook of hooks) {
1685
+ if (!isHookInstance(hook)) throw new Error("Agent hooks must extend HarnessHook.");
1686
+ assertNoAuthorId(hook, "Hook");
1687
+ if (!hookEventClass(hook)) {
1688
+ throw new Error(`Hook '${getConstructType(hook)}' must extend HarnessHook.for(EventClass).`);
1689
+ }
1690
+ }
1691
+ return hooks;
1692
+ }
1693
+ function validateDeclaredEvents(events) {
1694
+ if (!events) return void 0;
1695
+ for (const eventClass of events) eventType(eventClass);
1696
+ return events;
1697
+ }
1698
+ function resolveInitialModeType(selector, modes) {
1699
+ for (const mode of Object.values(modes)) {
1700
+ if (modeMatchesSelector(mode, selector)) return getConstructType(mode);
1701
+ }
1702
+ throw new Error(`Unknown initial mode '${getConstructType(selector)}'.`);
1703
+ }
1704
+ function normalizeAgent(definition) {
1705
+ ensureNoLegacyAgentShape(definition);
1706
+ const modes = {};
1707
+ for (const mode of definition.modes) {
1708
+ validateMode(mode);
1709
+ const type = getConstructType(mode);
1710
+ if (modes[type]) throw new Error(`Duplicate mode type '${type}'.`);
1711
+ modes[type] = mode;
1712
+ }
1713
+ const initialMode = resolveInitialModeType(definition.initialMode, modes);
1714
+ const key = definition.key ?? keyFromLabel(definition.label);
1715
+ return {
1716
+ key,
1717
+ label: definition.label,
1718
+ initialMode,
1719
+ modes,
1720
+ sharedState: definition.sharedState,
1721
+ roles: validateRoles(definition.roles),
1722
+ hooks: validateHooks(definition.hooks),
1723
+ declaredEvents: validateDeclaredEvents(definition.declaredEvents)
1724
+ };
1725
+ }
1726
+ var AgentSessionRunner = class {
1727
+ constructor(options) {
1728
+ this.options = options;
1729
+ this.agent = normalizeAgent(options.agent);
1730
+ this.sessionIdValue = options.sessionId ?? randomId();
1731
+ this.workDir = options.workDir ?? ".";
1732
+ this.outputDir = options.outputDir ?? ".harness-kernel/runs";
1733
+ const storage = options.storage ?? new NoopRunStorage();
1734
+ const sandbox = options.sandbox ?? new NoopSandbox();
1735
+ this.providerRegistry = new HarnessModelProviderRegistry(options.providers);
1736
+ this.currentMode = options.initialMode ? this.resolveModeType(options.initialMode) : this.agent.initialMode;
1737
+ this.services = options.services ?? {};
1738
+ this.roles = validateRoles(options.roles) ?? this.agent.roles ?? defaultRoles();
1739
+ const initialProvider = this.resolveModelProvider(options.defaultModel).provider;
1740
+ this.roleResolver = new RoleResolver(this.roles, {
1741
+ modelProviderId: modelProviderId(initialProvider),
1742
+ supportsRole: (roleId) => this.resolveModelProvider(this.getActiveModel()).provider.supportsRole?.(roleId) ?? true
1743
+ });
1744
+ this.defaultModel = options.defaultModel;
1745
+ this.storageCoordinator = new RunStorageCoordinator({
1746
+ storage,
1747
+ runId: this.runIdValue,
1748
+ sessionId: this.sessionIdValue,
1749
+ agentKey: this.agent.key,
1750
+ outputDir: this.outputDir,
1751
+ logOpened: (fields) => this.log(RunStorageOpenedLog, fields),
1752
+ logFailed: (fields) => this.log(StorageWriteFailedLog, fields)
1753
+ });
1754
+ this.snapshotManager = new SnapshotManager({
1755
+ now: () => nowIso4(),
1756
+ ensureStoreInitialized: () => this.ensureStoreInitialized(),
1757
+ readState: () => ({
1758
+ agentKey: this.agent.key,
1759
+ runId: this.started ? this.runId : void 0,
1760
+ turnId: this.currentTurnId,
1761
+ modeId: this.currentMode,
1762
+ model: this.getActiveModel(),
1763
+ transcriptCursor: cloneJSON4(this.transcriptManager.activeTranscriptCursor),
1764
+ eventCursor: cloneJSON4(this.transcriptManager.activeEventCursor),
1765
+ state: cloneJSON4(this.state),
1766
+ contextEntries: cloneJSON4(this.contextRegistry.allEntries),
1767
+ contextSnapshot: this.contextRegistry.current ? cloneJSON4(this.contextRegistry.current) : void 0,
1768
+ branches: cloneJSON4(this.transcriptManager.allBranches)
1769
+ }),
1770
+ restoreState: (snapshot) => {
1771
+ this.currentMode = snapshot.modeId;
1772
+ this.modelOverride = snapshot.model;
1773
+ this.state = cloneJSON4(snapshot.state);
1774
+ this.contextRegistry.restore({
1775
+ entries: snapshot.contextEntries,
1776
+ snapshot: snapshot.contextSnapshot
1777
+ });
1778
+ this.transcriptManager.restoreCursors({
1779
+ transcriptCursor: snapshot.transcriptCursor,
1780
+ eventCursor: snapshot.eventCursor,
1781
+ branches: snapshot.branches
1782
+ });
1783
+ },
1784
+ persistCursors: () => this.persistCursors(),
1785
+ saveSnapshot: (snapshot) => this.storageCoordinator.saveSnapshot(snapshot),
1786
+ deleteSnapshot: (id) => this.storageCoordinator.deleteSnapshot(id),
1787
+ emitCreated: async (summary, eventOptions) => {
1788
+ await this.emitInternal(SnapshotCreatedEvent, { snapshot: summary }, {
1789
+ ...eventOptions,
1790
+ hiddenTranscript: false
1791
+ });
1792
+ },
1793
+ emitRestored: async (summary, eventOptions) => {
1794
+ await this.emitInternal(SnapshotRestoredEvent, { snapshot: summary }, {
1795
+ ...eventOptions,
1796
+ hiddenTranscript: false
1797
+ });
1798
+ },
1799
+ emitDeleted: async (summary, eventOptions) => {
1800
+ await this.emitInternal(SnapshotDeletedEvent, { snapshot: summary }, {
1801
+ ...eventOptions,
1802
+ hiddenTranscript: false
1803
+ });
1804
+ },
1805
+ logCreated: (summary, eventOptions) => this.log(
1806
+ SnapshotCreatedLog,
1807
+ { snapshotId: summary.id, label: summary.label },
1808
+ eventOptions?.source,
1809
+ eventOptions?.correlationId,
1810
+ eventOptions?.causationId
1811
+ ),
1812
+ logRestored: (summary, eventOptions) => this.log(
1813
+ SnapshotRestoredLog,
1814
+ { snapshotId: summary.id, label: summary.label },
1815
+ eventOptions?.source,
1816
+ eventOptions?.correlationId,
1817
+ eventOptions?.causationId
1818
+ ),
1819
+ logDeleted: (summary, eventOptions) => this.log(
1820
+ SnapshotDeletedLog,
1821
+ { snapshotId: summary.id, label: summary.label },
1822
+ eventOptions?.source,
1823
+ eventOptions?.correlationId,
1824
+ eventOptions?.causationId
1825
+ ),
1826
+ logRestoreRejected: (snapshotId, reason, eventOptions) => this.log(
1827
+ SnapshotRestoreRejectedLog,
1828
+ { snapshotId, reason },
1829
+ eventOptions?.source,
1830
+ eventOptions?.correlationId,
1831
+ eventOptions?.causationId
1832
+ )
1833
+ });
1834
+ this.sandboxManager = new SandboxManager({
1835
+ sandbox,
1836
+ sessionId: this.sessionIdValue,
1837
+ agentKey: this.agent.key,
1838
+ workDir: this.workDir,
1839
+ services: this.services,
1840
+ getRunId: () => this.runId,
1841
+ getOutputDir: () => this.storeRunDir(),
1842
+ logOpened: (fields) => this.log(SandboxOpenedLog, fields),
1843
+ logClosed: (fields) => this.log(SandboxClosedLog, fields),
1844
+ logExecStarted: (fields) => this.log(SandboxExecStartedLog, fields),
1845
+ logExecCompleted: (fields) => this.log(SandboxExecCompletedLog, fields),
1846
+ logExecFailed: (fields) => this.log(SandboxExecFailedLog, fields)
1847
+ });
1848
+ this.toolExecutor = new ToolExecutor({
1849
+ getMetrics: () => this.metrics,
1850
+ getCurrentMode: () => this.currentMode,
1851
+ getToolApprovalMode: () => this.options.toolApproval ?? this.getModeDefinition(this.currentMode).toolApproval,
1852
+ approveTool: this.options.approveTool,
1853
+ ensureSandboxOpen: () => this.ensureSandboxOpen(),
1854
+ buildActionSession: (tool, source, correlationId, causationId) => this.buildActionSession(tool, source, correlationId, causationId),
1855
+ addToolCallMessage: (tool, args, toolCallId, source) => this.addToolCallMessage(tool, args, toolCallId, source),
1856
+ addToolResultMessage: (tool, result, toolCallId) => this.addToolResultMessage(tool, result, toolCallId),
1857
+ emitInternal: (eventClass, payload2, options2) => this.emitInternal(eventClass, payload2, options2),
1858
+ log: (logClass, fields, source, correlationId, causationId, overrides) => this.log(logClass, fields, source, correlationId, causationId, overrides),
1859
+ throwIfTurnHandoffRequested: () => this.throwIfTurnHandoffRequested()
1860
+ });
1861
+ this.modelPipeline = new ModelPipeline({
1862
+ resolveModelProvider: (model) => this.resolveModelProvider(model),
1863
+ roles: this.roles,
1864
+ getRunId: () => this.runId,
1865
+ getTurnId: () => this.currentTurnId,
1866
+ getModeId: () => this.currentMode,
1867
+ getModel: () => this.getActiveModel(),
1868
+ getMetrics: () => this.metrics,
1869
+ setFinalAnswer: (answer) => {
1870
+ this.finalAnswer = answer;
1871
+ },
1872
+ buildReadSession: (source) => this.buildReadSession(source),
1873
+ buildContextSnapshot: (trigger) => this.buildContextSnapshot(trigger),
1874
+ assertModelProviderSupportsMessages: (messages) => this.assertModelProviderSupportsMessages(messages),
1875
+ addAssistantMessage: (content, metadata) => this.addMessage("assistant", content, metadata),
1876
+ markMessageEventCursor: (messageId) => this.markMessageEventCursor(messageId),
1877
+ executeTool: (tool, args, callId, source) => this.executeTool(tool, args, callId, source),
1878
+ emitInternal: (eventClass, payload2, eventOptions) => this.emitInternal(eventClass, payload2, eventOptions),
1879
+ emit: (eventClass, payload2, eventOptions) => this.emit(eventClass, payload2, eventOptions),
1880
+ withEmitDefaults: (source, correlationId, causationId, eventOptions) => this.withEmitDefaults(source, correlationId, causationId, eventOptions),
1881
+ log: (logClass, fields, source, correlationId, causationId, overrides) => this.log(logClass, fields, source, correlationId, causationId, overrides),
1882
+ throwIfTurnHandoffRequested: () => this.throwIfTurnHandoffRequested(),
1883
+ isTurnHandoffSignal: (error) => error instanceof TurnHandoffSignal
1884
+ });
1885
+ this.state = this.createInitialState(this.agent);
1886
+ for (const eventClass of runtimeEventClasses) eventType(eventClass);
1887
+ }
1888
+ options;
1889
+ agent;
1890
+ workDir;
1891
+ outputDir;
1892
+ sessionIdValue;
1893
+ storageCoordinator;
1894
+ sandboxManager;
1895
+ runIdValue = randomId();
1896
+ transcriptManager = new TranscriptManager();
1897
+ eventRecorder = new EventRecorder();
1898
+ contextRegistry = new ContextRegistry();
1899
+ snapshotManager;
1900
+ toolExecutor;
1901
+ modelPipeline;
1902
+ services;
1903
+ roles;
1904
+ roleResolver;
1905
+ providerRegistry;
1906
+ defaultModel;
1907
+ modelOverride;
1908
+ runModelOverride;
1909
+ currentMode;
1910
+ state;
1911
+ metrics = emptyMetrics();
1912
+ startedAtPerf = 0;
1913
+ started = false;
1914
+ pendingInputs = [];
1915
+ finalAnswer = "";
1916
+ currentTurnId;
1917
+ providerStack = [];
1918
+ hookDepth = 0;
1919
+ turnHandoffRequested = false;
1920
+ logSource(source) {
1921
+ if (!source) return { kind: "runtime" };
1922
+ return {
1923
+ kind: source.kind,
1924
+ type: source.id,
1925
+ name: source.name,
1926
+ label: source.name
1927
+ };
1928
+ }
1929
+ logContext(source, correlationId, causationId, overrides = {}) {
1930
+ return {
1931
+ sessionId: this.sessionIdValue,
1932
+ runId: this.runId,
1933
+ turnId: this.currentTurnId,
1934
+ modeId: this.currentMode,
1935
+ branchId: this.transcriptManager.activeTranscriptCursor.branchId,
1936
+ source: this.logSource(source),
1937
+ correlationId,
1938
+ causationId,
1939
+ ...overrides
1940
+ };
1941
+ }
1942
+ log(logClass, fields, source, correlationId, causationId, overrides) {
1943
+ this.options.logger?.emit(logClass, fields, this.logContext(source, correlationId, causationId, overrides));
1944
+ }
1945
+ logModelDelta(payload2, options) {
1946
+ const policy = this.options.logger?.modelDeltas ?? "none";
1947
+ if (policy === "none") return;
1948
+ const text = payload2 && typeof payload2 === "object" && "text" in payload2 ? String(payload2.text ?? "") : "";
1949
+ this.log(
1950
+ ModelDeltaLog,
1951
+ {
1952
+ length: text.length,
1953
+ ...policy === "full" ? { text } : {}
1954
+ },
1955
+ options?.source ?? this.modelProviderSource(),
1956
+ options?.correlationId,
1957
+ options?.causationId
1958
+ );
1959
+ }
1960
+ get mode() {
1961
+ return this.currentMode;
1962
+ }
1963
+ get runId() {
1964
+ return this.runIdValue;
1965
+ }
1966
+ subscribe(listener) {
1967
+ return this.eventRecorder.subscribe(listener);
1968
+ }
1969
+ requestTurnHandoff() {
1970
+ this.turnHandoffRequested = true;
1971
+ }
1972
+ storeRunDir() {
1973
+ return this.storageCoordinator.runDir;
1974
+ }
1975
+ async ensureStoreInitialized() {
1976
+ await this.storageCoordinator.ensureInitialized();
1977
+ const stored = await this.storageCoordinator.loadRuntimeState();
1978
+ if (stored) this.applyStoredRuntimeState(stored);
1979
+ }
1980
+ async ensureSandboxOpen() {
1981
+ await this.sandboxManager.ensureOpen();
1982
+ }
1983
+ async closeSandbox() {
1984
+ await this.sandboxManager.close();
1985
+ }
1986
+ async closeStore() {
1987
+ await this.storageCoordinator.close();
1988
+ }
1989
+ applyStoredRuntimeState(stored) {
1990
+ this.contextRegistry.loadSnapshots(stored.contextSnapshots);
1991
+ for (const snapshot of stored.snapshots) {
1992
+ this.snapshotManager.load(snapshot);
1993
+ this.transcriptManager.addBranches(snapshot.branches);
1994
+ }
1995
+ this.transcriptManager.loadTranscript(stored.transcript);
1996
+ this.metrics.messageCount = this.transcriptManager.count;
1997
+ this.eventRecorder.load(stored.events);
1998
+ this.metrics.eventCount = this.eventRecorder.count;
1999
+ if (stored.cursors) {
2000
+ this.transcriptManager.loadCursors({
2001
+ ...stored.cursors,
2002
+ eventExists: (eventId) => this.eventRecorder.has(eventId)
2003
+ });
2004
+ }
2005
+ }
2006
+ async persistCursors() {
2007
+ await this.ensureStoreInitialized();
2008
+ await this.storageCoordinator.saveCursors({
2009
+ transcriptCursor: cloneJSON4(this.transcriptManager.activeTranscriptCursor),
2010
+ eventCursor: cloneJSON4(this.transcriptManager.activeEventCursor),
2011
+ branches: cloneJSON4(this.transcriptManager.allBranches)
2012
+ });
2013
+ }
2014
+ async beginNewRun() {
2015
+ if (this.started) {
2016
+ await this.closeSandbox();
2017
+ this.runIdValue = randomId();
2018
+ await this.storageCoordinator.beginRun(this.runIdValue);
2019
+ this.started = false;
2020
+ }
2021
+ this.startedAtPerf = 0;
2022
+ this.metrics = emptyMetrics();
2023
+ this.finalAnswer = "";
2024
+ this.turnHandoffRequested = false;
2025
+ }
2026
+ async run(message, options = {}) {
2027
+ if (options.signal?.aborted) throw new Error("Run aborted.");
2028
+ await this.beginNewRun();
2029
+ this.runModelOverride = void 0;
2030
+ this.runModelOverride = this.normalizeModelOverride(options.model);
2031
+ let runStarted = false;
2032
+ try {
2033
+ await this.start();
2034
+ runStarted = true;
2035
+ this.pendingInputs.push({
2036
+ id: options.userInputId,
2037
+ content: message,
2038
+ metadata: options.userMetadata,
2039
+ role: options.userRole,
2040
+ external: true
2041
+ });
2042
+ const maxRunnerTurns = this.options.maxTurns ?? 5;
2043
+ while (this.pendingInputs.length > 0 && this.metrics.turnCount < maxRunnerTurns) {
2044
+ if (options.signal?.aborted) throw new Error("Run aborted.");
2045
+ const input = this.pendingInputs.shift();
2046
+ await this.runTurn(input, options);
2047
+ }
2048
+ const completedAt = nowIso4();
2049
+ this.metrics.completedAt = completedAt;
2050
+ this.metrics.durationMs = Math.round(performance.now() - this.startedAtPerf);
2051
+ this.metrics.finalMode = this.currentMode;
2052
+ await this.storageCoordinator.saveTranscript(this.transcriptManager.allMessages);
2053
+ await this.emitInternal(RunEndEvent, {
2054
+ metrics: cloneJSON4({ ...this.metrics, eventCount: this.eventRecorder.count + 1 }),
2055
+ finalAnswer: this.finalAnswer
2056
+ });
2057
+ this.contextRegistry.expireScope("run" /* Run */);
2058
+ this.metrics.durationMs = Math.round(performance.now() - this.startedAtPerf);
2059
+ await this.storageCoordinator.saveMetrics(this.metrics);
2060
+ this.log(RunCompletedLog, {
2061
+ durationMs: this.metrics.durationMs,
2062
+ messageCount: this.metrics.messageCount,
2063
+ eventCount: this.metrics.eventCount
2064
+ });
2065
+ return {
2066
+ runId: this.runId,
2067
+ agentKey: this.agent.key,
2068
+ finalAnswer: this.finalAnswer,
2069
+ transcript: cloneJSON4(this.filterTranscript()),
2070
+ events: this.queryEvents().map((event) => cloneJSON4(event.record)),
2071
+ metrics: cloneJSON4(this.metrics),
2072
+ outputDir: this.storeRunDir()
2073
+ };
2074
+ } catch (error) {
2075
+ this.log(RunFailedLog, { error });
2076
+ throw error;
2077
+ } finally {
2078
+ if (runStarted) await this.closeSandbox();
2079
+ this.runModelOverride = void 0;
2080
+ }
2081
+ }
2082
+ async prompt(message, options = {}) {
2083
+ return this.run(message, options);
2084
+ }
2085
+ async close() {
2086
+ await this.closeSandbox();
2087
+ await this.closeStore();
2088
+ }
2089
+ getTranscript(options) {
2090
+ return cloneJSON4(this.filterTranscript(options));
2091
+ }
2092
+ getMetrics() {
2093
+ return cloneJSON4({
2094
+ ...this.metrics,
2095
+ durationMs: this.startedAtPerf > 0 ? Math.round(performance.now() - this.startedAtPerf) : 0,
2096
+ finalMode: this.currentMode
2097
+ });
2098
+ }
2099
+ getRunInfo() {
2100
+ return {
2101
+ runId: this.runId,
2102
+ agentKey: this.agent.key,
2103
+ workDir: this.workDir,
2104
+ outputDir: this.storeRunDir(),
2105
+ started: this.started,
2106
+ startedAt: this.metrics.startedAt
2107
+ };
2108
+ }
2109
+ getModel() {
2110
+ return this.getActiveModel();
2111
+ }
2112
+ setModel(model) {
2113
+ this.modelOverride = this.normalizeModelOverride(model);
2114
+ }
2115
+ clearModelOverride() {
2116
+ this.modelOverride = void 0;
2117
+ }
2118
+ getModelProviderInfo() {
2119
+ const resolved = this.resolveModelProvider(this.getActiveModel());
2120
+ return resolved.provider.getInfo?.() ?? {
2121
+ id: modelProviderId(resolved.provider),
2122
+ provider: resolved.namespace
2123
+ };
2124
+ }
2125
+ getAvailableModels() {
2126
+ return this.providerRegistry.list().flatMap((provider) => {
2127
+ const models = provider.getModels?.() ?? [];
2128
+ return models.map((model) => ({
2129
+ ...model,
2130
+ id: model.id.includes("/") ? model.id : `${provider.namespace}/${model.id}`,
2131
+ provider: model.provider ?? provider.namespace
2132
+ }));
2133
+ });
2134
+ }
2135
+ getAgentManifest() {
2136
+ return {
2137
+ key: this.agent.key,
2138
+ label: this.agent.label,
2139
+ initialMode: this.agent.initialMode,
2140
+ currentMode: this.currentMode,
2141
+ modes: Object.values(this.agent.modes).map((mode) => modeSummary(mode)),
2142
+ roles: this.roles.map((role) => this.roleResolver.summary(role)),
2143
+ hooks: (this.agent.hooks ?? []).map((hook) => this.hookSummary(hook)),
2144
+ contextProviders: this.getContextProviders().map((provider) => contextProviderSummary(provider)),
2145
+ tools: this.getAllTools().map((tool) => this.toToolCatalogEntry(tool)),
2146
+ events: this.eventSummaries()
2147
+ };
2148
+ }
2149
+ getEvents(filter) {
2150
+ return this.queryEvents(filter).map((event) => cloneJSON4(event.record));
2151
+ }
2152
+ getTranscriptCursor() {
2153
+ return cloneJSON4(this.transcriptManager.activeTranscriptCursor);
2154
+ }
2155
+ async seekTranscript(target) {
2156
+ return this.applyTranscriptSeek(target);
2157
+ }
2158
+ async latestTranscript() {
2159
+ return this.applyTranscriptSeek("latest");
2160
+ }
2161
+ getState() {
2162
+ return cloneJSON4(this.state);
2163
+ }
2164
+ updateState(patch) {
2165
+ if (patch && typeof patch === "object" && !Array.isArray(patch)) {
2166
+ Object.assign(this.state, patch);
2167
+ } else {
2168
+ this.state.value = patch;
2169
+ }
2170
+ }
2171
+ replaceState(next) {
2172
+ this.state = cloneJSON4(next);
2173
+ }
2174
+ async createSnapshot(input, eventOptions) {
2175
+ return this.snapshotManager.create(input, eventOptions);
2176
+ }
2177
+ listSnapshots() {
2178
+ return this.snapshotManager.list();
2179
+ }
2180
+ getSnapshot(id) {
2181
+ return this.snapshotManager.get(id);
2182
+ }
2183
+ async restoreSnapshot(id, eventOptions) {
2184
+ return this.snapshotManager.restore(id, eventOptions);
2185
+ }
2186
+ async deleteSnapshot(id, eventOptions) {
2187
+ return this.snapshotManager.deletePersisted(id, eventOptions);
2188
+ }
2189
+ getContextSnapshot() {
2190
+ return this.contextRegistry.current ? cloneJSON4(this.contextRegistry.current) : void 0;
2191
+ }
2192
+ getContextEntries(filter) {
2193
+ return cloneJSON4(this.contextRegistry.filter(filter));
2194
+ }
2195
+ async switchMode(mode, input) {
2196
+ const modeId = this.resolveModeType(mode);
2197
+ const previousModeId = this.currentMode;
2198
+ const previous = this.getModeDefinition(previousModeId);
2199
+ if (previousModeId !== modeId) {
2200
+ await this.ensureSandboxOpen();
2201
+ await previous.onExit?.(
2202
+ this.buildActionSession(
2203
+ void 0,
2204
+ { kind: "mode", id: previousModeId, name: getConstructLabel(previous) }
2205
+ ),
2206
+ modeSummary(this.getModeDefinition(modeId))
2207
+ );
2208
+ this.currentMode = modeId;
2209
+ const next = this.getModeDefinition(modeId);
2210
+ await next.onEnter?.(
2211
+ this.buildActionSession(
2212
+ void 0,
2213
+ { kind: "mode", id: modeId, name: getConstructLabel(next) }
2214
+ ),
2215
+ input
2216
+ );
2217
+ await this.emitInternal(ModeChangedEvent, {
2218
+ previousMode: previousModeId,
2219
+ mode: modeId,
2220
+ input
2221
+ });
2222
+ }
2223
+ if (input !== void 0) {
2224
+ this.pendingInputs.push({
2225
+ content: typeof input === "string" ? input : JSON.stringify(input, null, 2),
2226
+ external: false
2227
+ });
2228
+ }
2229
+ }
2230
+ async start() {
2231
+ if (this.started) return;
2232
+ await this.ensureStoreInitialized();
2233
+ await this.ensureSandboxOpen();
2234
+ this.started = true;
2235
+ this.startedAtPerf = performance.now();
2236
+ this.log(RunStartedLog, { modeId: this.currentMode, model: this.getActiveModel() });
2237
+ await this.emitInternal(RunStartEvent, {
2238
+ agentKey: this.agent.key,
2239
+ modeId: this.currentMode,
2240
+ workDir: this.workDir,
2241
+ outputDir: this.storeRunDir()
2242
+ });
2243
+ }
2244
+ async runTurn(input, options) {
2245
+ if (input.external !== false) {
2246
+ await this.createSnapshot({
2247
+ label: "Before user message",
2248
+ metadata: {
2249
+ kind: "before_user_message",
2250
+ automatic: true,
2251
+ userInputId: input.id
2252
+ }
2253
+ }, { hiddenTranscript: false });
2254
+ this.transcriptManager.ensureBranchForAppend();
2255
+ }
2256
+ this.metrics.turnCount++;
2257
+ this.currentTurnId = randomId();
2258
+ const turnStart = performance.now();
2259
+ this.log(TurnStartedLog, { turnId: this.currentTurnId });
2260
+ await this.emitInternal(TurnStartEvent, { turnId: this.currentTurnId, input: input.content });
2261
+ const inputRole = this.resolveRole(input.role ?? userRole);
2262
+ await this.emitInternal(MessageStartEvent, { role: inputRole.role });
2263
+ const userMessage = await this.addMessage(inputRole.role, input.content, input.metadata, inputRole, input.id);
2264
+ await this.emitInternal(MessageEndEvent, { message: userMessage });
2265
+ await this.markMessageEventCursor(userMessage.id);
2266
+ const mode = this.getModeDefinition(this.currentMode);
2267
+ const tools = await this.resolveTools();
2268
+ await this.emitInternal(MessageStartEvent, { role: "assistant" });
2269
+ try {
2270
+ await this.modelPipeline.run({
2271
+ mode,
2272
+ userMessage,
2273
+ tools,
2274
+ options
2275
+ });
2276
+ } catch (error) {
2277
+ if (error instanceof TurnHandoffSignal) return;
2278
+ throw error;
2279
+ } finally {
2280
+ this.log(TurnCompletedLog, {
2281
+ turnId: this.currentTurnId,
2282
+ durationMs: Math.round(performance.now() - turnStart)
2283
+ });
2284
+ await this.emitInternal(TurnEndEvent, { turnId: this.currentTurnId, finalAnswer: this.finalAnswer });
2285
+ this.contextRegistry.expireScope("turn" /* Turn */, this.currentTurnId);
2286
+ this.currentTurnId = void 0;
2287
+ }
2288
+ }
2289
+ async addMessage(role, content, metadata, roleInfo, id) {
2290
+ const message = this.transcriptManager.appendMessage({
2291
+ id,
2292
+ role,
2293
+ content,
2294
+ modeId: this.currentMode,
2295
+ turnId: this.currentTurnId,
2296
+ metadata,
2297
+ roleInfo
2298
+ });
2299
+ this.metrics.messageCount = this.transcriptManager.count;
2300
+ await this.storageCoordinator.saveTranscript(this.transcriptManager.allMessages);
2301
+ await this.persistCursors();
2302
+ return message;
2303
+ }
2304
+ async addHiddenEventMessage(record) {
2305
+ this.transcriptManager.appendMessage({
2306
+ branchId: record.branchId,
2307
+ role: "event",
2308
+ content: record,
2309
+ createdAt: record.at,
2310
+ modeId: record.modeId,
2311
+ turnId: record.turnId,
2312
+ hidden: true,
2313
+ metadata: { eventId: record.id, eventType: record.type }
2314
+ });
2315
+ this.metrics.messageCount = this.transcriptManager.count;
2316
+ await this.storageCoordinator.saveTranscript(this.transcriptManager.allMessages);
2317
+ await this.persistCursors();
2318
+ }
2319
+ async addToolCallMessage(tool, args, toolCallId, source) {
2320
+ return this.addMessage("assistant", [{
2321
+ type: "tool-call",
2322
+ toolCallId,
2323
+ toolName: tool.name,
2324
+ input: args
2325
+ }], {
2326
+ toolCallId,
2327
+ toolName: tool.name,
2328
+ source
2329
+ });
2330
+ }
2331
+ async addToolResultMessage(tool, result, toolCallId) {
2332
+ await this.addMessage("tool", [{
2333
+ type: "tool-result",
2334
+ toolCallId,
2335
+ toolName: tool.name,
2336
+ output: result.data ?? result.content
2337
+ }], {
2338
+ toolCallId,
2339
+ toolName: tool.name,
2340
+ isError: result.isError,
2341
+ ...result.metadata
2342
+ });
2343
+ }
2344
+ async markMessageEventCursor(messageId) {
2345
+ if (!this.transcriptManager.markMessageEventCursor(messageId)) return;
2346
+ await this.storageCoordinator.saveTranscript(this.transcriptManager.allMessages);
2347
+ }
2348
+ createInitialState(agent) {
2349
+ const initial = agent.sharedState?.initial;
2350
+ if (typeof initial === "function") return cloneJSON4(initial());
2351
+ if (initial && typeof initial === "object") return cloneJSON4(initial);
2352
+ return {};
2353
+ }
2354
+ getModeDefinition(modeId) {
2355
+ const mode = this.agent.modes[modeId];
2356
+ if (!mode) throw new Error(`Unknown mode '${modeId}' for agent '${this.agent.key}'.`);
2357
+ return mode;
2358
+ }
2359
+ resolveModeType(selector) {
2360
+ for (const mode of Object.values(this.agent.modes)) {
2361
+ if (modeMatchesSelector(mode, selector)) return getConstructType(mode);
2362
+ }
2363
+ throw new Error(`Unknown mode '${getConstructType(selector)}' for agent '${this.agent.key}'.`);
2364
+ }
2365
+ buildReadSession(source = { kind: "runtime" }, correlationId, causationId) {
2366
+ const log = this.options.logger?.agent(this.logContext(source, correlationId, causationId)) ?? noopAgentLogSession;
2367
+ return {
2368
+ runId: this.runId,
2369
+ turnId: this.currentTurnId,
2370
+ agentKey: this.agent.key,
2371
+ workDir: this.workDir,
2372
+ outputDir: this.storeRunDir(),
2373
+ services: this.services,
2374
+ state: {
2375
+ get: () => cloneJSON4(this.state)
2376
+ },
2377
+ history: {
2378
+ get: (options) => this.getTranscript({ includeHidden: false, ...options })
2379
+ },
2380
+ events: {
2381
+ query: (filter) => this.queryEvents(filter).map((event) => cloneJSON4(event.record))
2382
+ },
2383
+ mode: {
2384
+ current: () => modeSummary(this.getModeDefinition(this.currentMode))
2385
+ },
2386
+ context: {
2387
+ get: (filter) => this.getContextEntries(filter),
2388
+ snapshot: () => this.getContextSnapshot()
2389
+ },
2390
+ log
2391
+ };
2392
+ }
2393
+ buildActionSession(tool, source = tool ? { kind: "tool", id: tool.id, name: tool.name } : { kind: "runtime" }, correlationId, causationId) {
2394
+ const readSession = this.buildReadSession(source, correlationId, causationId);
2395
+ const sandbox = this.sandboxManager.current;
2396
+ if (!sandbox) throw new Error("Sandbox session has not been opened.");
2397
+ const applyEmitDefaults = (options) => this.withEmitDefaults(source, correlationId, causationId, options);
2398
+ return {
2399
+ ...readSession,
2400
+ sandbox,
2401
+ state: {
2402
+ get: () => cloneJSON4(this.state),
2403
+ update: (patch) => this.updateState(patch),
2404
+ set: (next) => this.replaceState(next)
2405
+ },
2406
+ events: {
2407
+ ...readSession.events,
2408
+ emit: (eventClass, payload2, options) => this.emit(eventClass, payload2, applyEmitDefaults(options))
2409
+ },
2410
+ mode: {
2411
+ ...readSession.mode,
2412
+ switch: async (mode, input) => this.switchMode(mode, input)
2413
+ },
2414
+ tools: {
2415
+ invoke: (tool2, args) => this.invokeTool(tool2, args, source, correlationId, causationId)
2416
+ },
2417
+ context: {
2418
+ ...readSession.context,
2419
+ add: (input, options) => Promise.resolve(this.addDynamicContext(input, options)),
2420
+ render: (binding, options) => this.renderDynamicContext(binding, options),
2421
+ remove: (id) => Promise.resolve(this.removeDynamicContext(id)),
2422
+ clear: (filter) => Promise.resolve(this.clearDynamicContext(filter))
2423
+ },
2424
+ messages: {
2425
+ enqueue: async (input, options) => {
2426
+ const message = normalizeAgentMessageInput(input);
2427
+ this.pendingInputs.push({
2428
+ id: message.id,
2429
+ content: message.content,
2430
+ metadata: { ...message.metadata, ...options?.metadata },
2431
+ role: message.role,
2432
+ external: false
2433
+ });
2434
+ }
2435
+ },
2436
+ snapshots: {
2437
+ create: (input) => this.createSnapshot(input, applyEmitDefaults({ hiddenTranscript: false }))
2438
+ },
2439
+ toolCall: tool
2440
+ };
2441
+ }
2442
+ withEmitDefaults(source, correlationId, causationId, options) {
2443
+ return {
2444
+ ...options,
2445
+ source: options?.source ?? source,
2446
+ correlationId: options?.correlationId ?? correlationId,
2447
+ causationId: options?.causationId ?? causationId
2448
+ };
2449
+ }
2450
+ throwIfTurnHandoffRequested() {
2451
+ if (!this.turnHandoffRequested) return;
2452
+ this.turnHandoffRequested = false;
2453
+ throw new TurnHandoffSignal();
2454
+ }
2455
+ normalizeModelOverride(model) {
2456
+ if (model === void 0) return void 0;
2457
+ const next = model.trim();
2458
+ if (!next) throw new Error("Model must not be empty.");
2459
+ this.resolveModelProvider(next);
2460
+ return next;
2461
+ }
2462
+ getActiveModel() {
2463
+ return this.runModelOverride ?? this.modelOverride ?? this.getModeDefinition(this.currentMode).model ?? this.defaultModel;
2464
+ }
2465
+ resolveModelProvider(model) {
2466
+ return this.providerRegistry.resolve(model);
2467
+ }
2468
+ modelProviderSource() {
2469
+ const resolved = this.resolveModelProvider(this.getActiveModel());
2470
+ const id = modelProviderId(resolved.provider);
2471
+ return { kind: "model_provider", id, name: id };
2472
+ }
2473
+ filterTranscript(options) {
2474
+ return this.transcriptManager.filter(options, this.currentTurnId);
2475
+ }
2476
+ getActiveContextProviderBindings() {
2477
+ const mode = this.getModeDefinition(this.currentMode);
2478
+ if (!mode.providers) return [];
2479
+ if (mode.providers === "all") {
2480
+ return this.getContextProviders().filter((provider) => !mode.excludeProviders?.includes(getConstructType(provider))).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
2481
+ }
2482
+ return mode.providers.filter((binding) => !mode.excludeProviders?.includes(this.bindingToSummary(binding).type));
2483
+ }
2484
+ getContextProviders() {
2485
+ const providers = /* @__PURE__ */ new Map();
2486
+ for (const mode of Object.values(this.agent.modes)) {
2487
+ if (!mode.providers || mode.providers === "all") continue;
2488
+ for (const binding of mode.providers) {
2489
+ const provider = this.providerFromReference(binding, false).provider;
2490
+ providers.set(getConstructType(provider), provider);
2491
+ }
2492
+ }
2493
+ return [...providers.values()];
2494
+ }
2495
+ providerFromReference(reference, allowClassSelector) {
2496
+ if (!isContextProviderReference(reference)) {
2497
+ throw new Error("Invalid context provider reference. Use a provider instance or provider.with(options).");
2498
+ }
2499
+ const selector = isContextProviderBinding(reference) ? reference.provider : reference;
2500
+ const options = isContextProviderBinding(reference) ? reference.options : void 0;
2501
+ if (isContextProviderInstance(selector)) {
2502
+ assertNoAuthorId(selector, "Context provider");
2503
+ return { provider: selector, options };
2504
+ }
2505
+ if (isContextProviderClass(selector)) {
2506
+ if (!allowClassSelector) {
2507
+ throw new Error(
2508
+ `Context provider '${getConstructType(selector)}' is a class selector. Register an instance in mode.providers.`
2509
+ );
2510
+ }
2511
+ const provider = this.getContextProviders().find((candidate) => contextProviderMatchesSelector(candidate, selector));
2512
+ if (!provider) throw new Error(`Unknown context provider '${getConstructType(selector)}'.`);
2513
+ return { provider, options };
2514
+ }
2515
+ throw new Error("Invalid context provider reference. Provider ids and strings are not supported.");
2516
+ }
2517
+ bindingToSummary(binding) {
2518
+ const { provider, options } = this.providerFromReference(binding, true);
2519
+ return contextProviderSummary(provider, options);
2520
+ }
2521
+ dynamicContextEntriesFor(eventClass) {
2522
+ return this.contextRegistry.entriesFor(eventClass, this.runId, this.currentTurnId);
2523
+ }
2524
+ activateDynamicContextFor(eventClass, record) {
2525
+ this.contextRegistry.activateFor(eventClass, record, this.runId, this.currentTurnId);
2526
+ }
2527
+ consumeDynamicContext(entries) {
2528
+ this.contextRegistry.consume(entries);
2529
+ }
2530
+ addDynamicContext(input, options = {}, provider) {
2531
+ const contribution = this.normalizeContextContribution(
2532
+ input,
2533
+ {
2534
+ providerId: provider?.type,
2535
+ providerLabel: provider?.label
2536
+ }
2537
+ );
2538
+ return this.addDynamicContextContribution(contribution, options);
2539
+ }
2540
+ addDynamicContextContribution(contribution, options = {}) {
2541
+ return this.contextRegistry.addContribution({
2542
+ contribution,
2543
+ options,
2544
+ runId: this.runId,
2545
+ turnId: this.currentTurnId,
2546
+ modeId: this.currentMode
2547
+ });
2548
+ }
2549
+ async renderDynamicContext(binding, options) {
2550
+ const rendered = await this.loadContextProvider(binding);
2551
+ return rendered.contributions.map((contribution, index) => this.addDynamicContextContribution(
2552
+ contribution,
2553
+ {
2554
+ ...options,
2555
+ id: options?.id && rendered.contributions.length === 1 ? options.id : options?.id ? `${options.id}:${index}` : void 0
2556
+ }
2557
+ ));
2558
+ }
2559
+ removeDynamicContext(id) {
2560
+ return this.contextRegistry.remove(id);
2561
+ }
2562
+ clearDynamicContext(filter) {
2563
+ return this.contextRegistry.clear(filter);
2564
+ }
2565
+ async buildContextSnapshot(trigger = ModelBeforeEvent) {
2566
+ const started = performance.now();
2567
+ const activeBindings = this.getActiveContextProviderBindings();
2568
+ this.log(ContextBuildStartedLog, { providerCount: activeBindings.length }, { kind: "runtime" });
2569
+ const providers = [];
2570
+ for (const binding of activeBindings) {
2571
+ providers.push(await this.loadContextProvider(binding));
2572
+ }
2573
+ const dynamicEntries = this.dynamicContextEntriesFor(trigger);
2574
+ const contributions = [
2575
+ ...providers.flatMap((provider) => provider.contributions),
2576
+ ...dynamicEntries.map((entry) => entry.contribution)
2577
+ ];
2578
+ const systemContributions = contributions.filter((contribution) => this.resolveRole(contribution.role).target === "system" /* System */);
2579
+ const messageContributions = contributions.filter((contribution) => this.resolveRole(contribution.role).target === "messages" /* Messages */);
2580
+ const systemPrompt = this.renderSystemContributions(systemContributions);
2581
+ const messages = messageContributions.map((contribution) => this.contributionToMessage(contribution));
2582
+ const snapshot = {
2583
+ id: randomId(),
2584
+ turnId: this.currentTurnId,
2585
+ modeId: this.currentMode,
2586
+ createdAt: nowIso4(),
2587
+ providers,
2588
+ contributions,
2589
+ systemPrompt,
2590
+ messages
2591
+ };
2592
+ this.contextRegistry.recordSnapshot(snapshot);
2593
+ await this.storageCoordinator.saveContextSnapshot(snapshot);
2594
+ this.consumeDynamicContext(dynamicEntries);
2595
+ this.log(ContextBuildCompletedLog, {
2596
+ providerCount: providers.length,
2597
+ contributionCount: contributions.length,
2598
+ durationMs: Math.round(performance.now() - started)
2599
+ }, { kind: "runtime" });
2600
+ return snapshot;
2601
+ }
2602
+ async loadContextProvider(binding) {
2603
+ const { provider, options } = this.providerFromReference(binding, true);
2604
+ const summary = contextProviderSummary(provider, options);
2605
+ if (this.providerStack.includes(summary.type)) {
2606
+ throw new Error(`Context provider cycle detected: ${[...this.providerStack, summary.type].join(" -> ")}`);
2607
+ }
2608
+ this.providerStack.push(summary.type);
2609
+ try {
2610
+ const output = await provider.render(
2611
+ this.buildReadSession(
2612
+ { kind: "context_provider", id: summary.type, name: summary.label },
2613
+ this.currentTurnId
2614
+ ),
2615
+ options
2616
+ );
2617
+ return {
2618
+ providerId: summary.type,
2619
+ providerLabel: summary.label,
2620
+ binding: summary,
2621
+ contributions: this.normalizeContextOutput(provider, output)
2622
+ };
2623
+ } catch (error) {
2624
+ this.log(
2625
+ ContextProviderFailedLog,
2626
+ { providerType: summary.type, error },
2627
+ { kind: "context_provider", id: summary.type, name: summary.label },
2628
+ this.currentTurnId
2629
+ );
2630
+ throw error;
2631
+ } finally {
2632
+ this.providerStack.pop();
2633
+ }
2634
+ }
2635
+ normalizeContextContribution(input, context = {}) {
2636
+ if (typeof input === "string") {
2637
+ const role2 = this.resolveRole(context.defaultRole ?? systemRole);
2638
+ return {
2639
+ providerId: context.providerId,
2640
+ providerLabel: context.providerLabel,
2641
+ role: role2.authorRole,
2642
+ authorRole: role2.authorRole,
2643
+ roleType: role2.roleType,
2644
+ content: input
2645
+ };
2646
+ }
2647
+ const role = this.resolveRole(input.role ?? context.defaultRole ?? systemRole);
2648
+ return {
2649
+ providerId: context.providerId,
2650
+ providerLabel: context.providerLabel,
2651
+ role: role.authorRole,
2652
+ authorRole: role.authorRole,
2653
+ roleType: role.roleType,
2654
+ content: input.content,
2655
+ metadata: input.metadata
2656
+ };
2657
+ }
2658
+ normalizeContextOutput(provider, output) {
2659
+ const summary = contextProviderSummary(provider);
2660
+ if (output == null) return [];
2661
+ const entries = Array.isArray(output) ? output : [output];
2662
+ return entries.map((entry) => {
2663
+ if (typeof entry === "string" && !entry.trim()) return void 0;
2664
+ if (typeof entry !== "string" && (entry.content == null || typeof entry.content === "string" && !entry.content.trim())) {
2665
+ return void 0;
2666
+ }
2667
+ return this.normalizeContextContribution(entry, {
2668
+ providerId: summary.type,
2669
+ providerLabel: summary.label,
2670
+ defaultRole: provider.role
2671
+ });
2672
+ }).filter(Boolean);
2673
+ }
2674
+ resolveRole(selector) {
2675
+ return this.roleResolver.resolve(selector);
2676
+ }
2677
+ assertModelProviderSupportsMessages(messages) {
2678
+ this.roleResolver.assertModelProviderSupportsMessages(messages);
2679
+ }
2680
+ renderSystemContributions(contributions) {
2681
+ if (!contributions.length) return "";
2682
+ const rendered = contributions.map((contribution) => {
2683
+ const label = contribution.providerLabel ?? contribution.providerId ?? contribution.role;
2684
+ return `## ${label}
2685
+
2686
+ ${stringifyContent(contribution.content)}`;
2687
+ });
2688
+ return `# Runtime Context
2689
+
2690
+ ${rendered.join("\n\n")}`;
2691
+ }
2692
+ contributionToMessage(contribution) {
2693
+ const role = this.resolveRole(contribution.role);
2694
+ return {
2695
+ id: randomId(),
2696
+ seq: 0,
2697
+ branchId: this.transcriptManager.activeTranscriptCursor.branchId,
2698
+ role: role.role,
2699
+ authorRole: contribution.authorRole ?? role.authorRole,
2700
+ roleType: contribution.roleType ?? role.roleType,
2701
+ content: contribution.content,
2702
+ createdAt: nowIso4(),
2703
+ modeId: this.currentMode,
2704
+ turnId: this.currentTurnId,
2705
+ eventCursor: cloneJSON4(this.transcriptManager.activeEventCursor),
2706
+ metadata: {
2707
+ contextProviderId: contribution.providerId,
2708
+ contextRole: contribution.authorRole ?? contribution.role,
2709
+ contextRoleType: contribution.roleType,
2710
+ ...contribution.metadata
2711
+ }
2712
+ };
2713
+ }
2714
+ async resolveTools() {
2715
+ const mode = this.getModeDefinition(this.currentMode);
2716
+ const tools = [];
2717
+ for (const source of mode.tools ?? []) {
2718
+ if (!isToolInstance(source)) throw new Error("Mode tools must extend HarnessTool. Tool factories and object literals are not supported.");
2719
+ assertNoAuthorId(source, "Tool");
2720
+ tools.push(source);
2721
+ }
2722
+ const names = /* @__PURE__ */ new Set();
2723
+ for (const tool of tools) {
2724
+ if (names.has(tool.name)) throw new Error(`Duplicate toolName '${tool.name}' in mode '${this.currentMode}'.`);
2725
+ names.add(tool.name);
2726
+ }
2727
+ return tools;
2728
+ }
2729
+ getAllTools() {
2730
+ const tools = /* @__PURE__ */ new Map();
2731
+ for (const mode of Object.values(this.agent.modes)) {
2732
+ for (const source of mode.tools ?? []) {
2733
+ if (!isToolInstance(source)) continue;
2734
+ tools.set(source.name, source);
2735
+ }
2736
+ }
2737
+ return [...tools.values()];
2738
+ }
2739
+ toToolCatalogEntry(tool) {
2740
+ return {
2741
+ name: tool.name,
2742
+ description: tool.description,
2743
+ risk: tool.risk,
2744
+ permissions: tool.permissions,
2745
+ requiresApproval: typeof tool.requiresApproval === "boolean" ? tool.requiresApproval : void 0
2746
+ };
2747
+ }
2748
+ hookSummary(hook) {
2749
+ const eventClass = hookEventClass(hook);
2750
+ const type = eventType(eventClass);
2751
+ return {
2752
+ type: getConstructType(hook),
2753
+ label: getConstructLabel(hook),
2754
+ eventType: type,
2755
+ eventClassId: type
2756
+ };
2757
+ }
2758
+ eventSummaries() {
2759
+ const summaries = /* @__PURE__ */ new Map();
2760
+ const add = (eventClass, builtIn = false) => {
2761
+ const type = eventType(eventClass);
2762
+ summaries.set(type, {
2763
+ type,
2764
+ label: getConstructLabel(eventClass),
2765
+ className: getConstructType(eventClass),
2766
+ builtIn
2767
+ });
2768
+ };
2769
+ for (const eventClass of runtimeEventClasses) add(eventClass, true);
2770
+ for (const hook of this.agent.hooks ?? []) add(hookEventClass(hook));
2771
+ for (const eventClass of this.agent.declaredEvents ?? []) add(eventClass);
2772
+ return [...summaries.values()];
2773
+ }
2774
+ async invokeTool(selector, args, source = { kind: "runtime" }, parentCorrelationId, parentCausationId) {
2775
+ const tool = (await this.resolveTools()).find((candidate) => toolMatchesSelector(candidate, selector));
2776
+ if (!tool) {
2777
+ const label = getConstructType(selector);
2778
+ throw new Error(`Unknown tool '${label}' in mode '${this.currentMode}'.`);
2779
+ }
2780
+ return this.executeTool(tool, args, void 0, source, parentCorrelationId, parentCausationId);
2781
+ }
2782
+ async executeTool(tool, args, callId, source = { kind: "runtime" }, parentCorrelationId, parentCausationId) {
2783
+ return this.toolExecutor.execute({
2784
+ tool,
2785
+ args,
2786
+ callId,
2787
+ source,
2788
+ parentCorrelationId,
2789
+ parentCausationId
2790
+ });
2791
+ }
2792
+ async applyTranscriptSeek(target) {
2793
+ const previousCursor = cloneJSON4(this.transcriptManager.activeTranscriptCursor);
2794
+ const resolved = this.transcriptManager.resolveSeekTarget(target);
2795
+ this.transcriptManager.applyResolvedSeek(resolved);
2796
+ await this.emitInternal(TranscriptCursorChangedEvent, {
2797
+ previousCursor,
2798
+ cursor: this.transcriptManager.activeTranscriptCursor
2799
+ }, { hiddenTranscript: false });
2800
+ this.log(TranscriptCursorChangedLog, { cursorId: this.transcriptManager.activeTranscriptCursor.id });
2801
+ this.transcriptManager.applyResolvedSeek(resolved);
2802
+ await this.persistCursors();
2803
+ return cloneJSON4(this.transcriptManager.activeTranscriptCursor);
2804
+ }
2805
+ async emitInternal(eventClass, payload2, options) {
2806
+ return this.recordHarnessEvent(eventClass, payload2, {
2807
+ source: { kind: "runtime" },
2808
+ ...options
2809
+ });
2810
+ }
2811
+ async emit(eventClass, payload2, options) {
2812
+ return this.recordHarnessEvent(eventClass, payload2, options);
2813
+ }
2814
+ async recordHarnessEvent(eventClass, payload2, options) {
2815
+ const type = eventType(eventClass);
2816
+ const parsedPayload = normalizeSchema(eventClass.schema).parse(payload2);
2817
+ if (type === MessageDeltaEvent.type) this.logModelDelta(parsedPayload, options);
2818
+ this.transcriptManager.ensureBranchForEventAppend(
2819
+ this.eventRecorder.latestForBranch(this.transcriptManager.activeEventCursor.branchId)
2820
+ );
2821
+ const branchId = this.transcriptManager.activeTranscriptCursor.branchId;
2822
+ const event = this.eventRecorder.record({
2823
+ eventClass,
2824
+ branchId,
2825
+ type,
2826
+ source: options?.source ?? { kind: "custom" },
2827
+ payload: parsedPayload,
2828
+ runId: this.runId,
2829
+ turnId: this.currentTurnId,
2830
+ modeId: this.currentMode,
2831
+ correlationId: options?.correlationId,
2832
+ causationId: options?.causationId,
2833
+ metadata: options?.metadata
2834
+ });
2835
+ const record = event.record;
2836
+ this.transcriptManager.advanceEventCursor(record);
2837
+ this.metrics.eventCount = this.eventRecorder.count;
2838
+ await this.ensureStoreInitialized();
2839
+ await this.storageCoordinator.recordEvent(record);
2840
+ await this.persistCursors();
2841
+ this.activateDynamicContextFor(eventClass, record);
2842
+ if (options?.hiddenTranscript !== false) await this.addHiddenEventMessage(record);
2843
+ await this.eventRecorder.notify(record);
2844
+ if (!options?.skipHooks) await this.dispatchHooks(event);
2845
+ return event;
2846
+ }
2847
+ queryEvents(filter) {
2848
+ return this.eventRecorder.query(filter, this.transcriptManager.activeEventSegments());
2849
+ }
2850
+ async dispatchHooks(event) {
2851
+ if (this.hookDepth > 25) throw new Error("Hook dispatch depth exceeded.");
2852
+ const hooks = (this.agent.hooks ?? []).filter((hook) => {
2853
+ const eventClass = hookEventClass(hook);
2854
+ return eventClass ? eventType(eventClass) === event.type : false;
2855
+ });
2856
+ if (!hooks.length) return;
2857
+ await this.ensureSandboxOpen();
2858
+ this.hookDepth++;
2859
+ try {
2860
+ for (const hook of hooks) {
2861
+ await hook.onActive(
2862
+ this.buildActionSession(
2863
+ void 0,
2864
+ { kind: "hook", id: getConstructType(hook), name: getConstructLabel(hook) },
2865
+ event.record.correlationId,
2866
+ event.id
2867
+ ),
2868
+ event
2869
+ );
2870
+ }
2871
+ } finally {
2872
+ this.hookDepth--;
2873
+ }
2874
+ }
2875
+ };
2876
+
2877
+ // src/session/types.ts
2878
+ var HarnessSessionPhase = /* @__PURE__ */ ((HarnessSessionPhase2) => {
2879
+ HarnessSessionPhase2["Idle"] = "idle";
2880
+ HarnessSessionPhase2["Queued"] = "queued";
2881
+ HarnessSessionPhase2["Starting"] = "starting";
2882
+ HarnessSessionPhase2["BuildingContext"] = "building_context";
2883
+ HarnessSessionPhase2["WaitingModel"] = "waiting_model";
2884
+ HarnessSessionPhase2["RunningTool"] = "running_tool";
2885
+ HarnessSessionPhase2["WaitingApproval"] = "waiting_approval";
2886
+ HarnessSessionPhase2["ClosingTurn"] = "closing_turn";
2887
+ HarnessSessionPhase2["Completed"] = "completed";
2888
+ HarnessSessionPhase2["Error"] = "error";
2889
+ HarnessSessionPhase2["Closed"] = "closed";
2890
+ return HarnessSessionPhase2;
2891
+ })(HarnessSessionPhase || {});
2892
+
2893
+ // src/session/approvals.ts
2894
+ function nowIso5() {
2895
+ return (/* @__PURE__ */ new Date()).toISOString();
2896
+ }
2897
+ function createToolApprovalHandle(input) {
2898
+ const createdAt = nowIso5();
2899
+ const expiresAt = input.timeoutMs > 0 ? new Date(Date.now() + input.timeoutMs).toISOString() : void 0;
2900
+ const id = randomId();
2901
+ return {
2902
+ id,
2903
+ sessionId: input.sessionId,
2904
+ runId: input.runId,
2905
+ toolCallId: input.request.id,
2906
+ name: input.request.name,
2907
+ args: input.request.args,
2908
+ modeId: input.request.modeId,
2909
+ risk: input.request.risk,
2910
+ permissions: input.request.permissions,
2911
+ createdAt,
2912
+ expiresAt,
2913
+ approve: () => input.approve(id),
2914
+ deny: (reason) => input.deny(id, reason)
2915
+ };
2916
+ }
2917
+
2918
+ // src/session/approval-controller.ts
2919
+ var ApprovalController = class {
2920
+ constructor(input) {
2921
+ this.input = input;
2922
+ }
2923
+ input;
2924
+ pendingApprovals = /* @__PURE__ */ new Map();
2925
+ approvalIdsByToolCallId = /* @__PURE__ */ new Map();
2926
+ get count() {
2927
+ return this.pendingApprovals.size;
2928
+ }
2929
+ getPending() {
2930
+ return [...this.pendingApprovals.values()].map((approval) => approval.handle);
2931
+ }
2932
+ hydrateApprovalId(toolCallId) {
2933
+ const approvalId = this.approvalIdsByToolCallId.get(toolCallId) ?? toolCallId;
2934
+ this.approvalIdsByToolCallId.delete(toolCallId);
2935
+ return approvalId;
2936
+ }
2937
+ request(request) {
2938
+ return new Promise((resolve) => {
2939
+ const handle = createToolApprovalHandle({
2940
+ sessionId: this.input.sessionId,
2941
+ runId: this.input.getRunId(),
2942
+ request,
2943
+ timeoutMs: this.input.timeoutMs,
2944
+ approve: async (id) => this.resolve(id, true),
2945
+ deny: async (id, reason) => this.resolve(id, false, reason)
2946
+ });
2947
+ const timeout = setTimeout(() => {
2948
+ this.resolve(handle.id, false);
2949
+ }, this.input.timeoutMs);
2950
+ this.pendingApprovals.set(handle.id, {
2951
+ handle,
2952
+ timeout,
2953
+ resolve: (approved) => resolve(approved ? "approved" : "denied")
2954
+ });
2955
+ this.approvalIdsByToolCallId.set(handle.toolCallId, handle.id);
2956
+ this.input.notifyRequested(handle);
2957
+ });
2958
+ }
2959
+ resolve(approvalId, approved, _reason) {
2960
+ const pending = this.pendingApprovals.get(approvalId);
2961
+ if (!pending) throw new Error(`Tool approval '${approvalId}' was not found.`);
2962
+ if (pending.timeout) clearTimeout(pending.timeout);
2963
+ this.pendingApprovals.delete(approvalId);
2964
+ pending.resolve(approved);
2965
+ }
2966
+ denyAll() {
2967
+ for (const approval of [...this.pendingApprovals.values()]) {
2968
+ this.resolve(approval.handle.id, false);
2969
+ }
2970
+ }
2971
+ };
2972
+
2973
+ // src/session/engine.ts
2974
+ async function resolveAgent(input) {
2975
+ return input.definition;
2976
+ }
2977
+ function resolveWorkDir(workDir) {
2978
+ return workDir ?? ".";
2979
+ }
2980
+ function resolveStorage(storage) {
2981
+ return storage ?? new NoopRunStorage();
2982
+ }
2983
+
2984
+ // src/session/event-hub.ts
2985
+ function payloadObject(record) {
2986
+ return record.payload && typeof record.payload === "object" ? record.payload : {};
2987
+ }
2988
+ var SessionEventHub = class {
2989
+ constructor(options) {
2990
+ this.options = options;
2991
+ }
2992
+ options;
2993
+ listeners = /* @__PURE__ */ new Set();
2994
+ on(listener) {
2995
+ this.listeners.add(listener);
2996
+ return () => this.listeners.delete(listener);
2997
+ }
2998
+ onEvent(eventClass, listener) {
2999
+ const targetType = eventType(eventClass);
3000
+ return this.on((event) => {
3001
+ if (event.type !== "event" || event.event.type !== targetType) return;
3002
+ void listener(new eventClass(event.event));
3003
+ });
3004
+ }
3005
+ waitForEvent(eventClass, options = {}) {
3006
+ return new Promise((resolve, reject) => {
3007
+ let timeout;
3008
+ const cleanup = this.onEvent(eventClass, (event) => {
3009
+ if (timeout) clearTimeout(timeout);
3010
+ cleanup();
3011
+ resolve(event);
3012
+ });
3013
+ if (options.signal) {
3014
+ if (options.signal.aborted) {
3015
+ cleanup();
3016
+ reject(options.signal.reason ?? new Error("waitForEvent aborted."));
3017
+ return;
3018
+ }
3019
+ options.signal.addEventListener("abort", () => {
3020
+ if (timeout) clearTimeout(timeout);
3021
+ cleanup();
3022
+ reject(options.signal?.reason ?? new Error("waitForEvent aborted."));
3023
+ }, { once: true });
3024
+ }
3025
+ if (options.timeoutMs !== void 0) {
3026
+ timeout = setTimeout(() => {
3027
+ cleanup();
3028
+ reject(new Error(`Timed out waiting for event '${eventType(eventClass)}'.`));
3029
+ }, options.timeoutMs);
3030
+ }
3031
+ });
3032
+ }
3033
+ notify(event) {
3034
+ for (const listener of this.listeners) void listener(event);
3035
+ }
3036
+ notifyRunnerRecord(record) {
3037
+ for (const event of this.hydrateRuntimeRecord(record)) this.notify(event);
3038
+ }
3039
+ hydrateRuntimeRecord(record) {
3040
+ const events = [];
3041
+ const payload2 = payloadObject(record);
3042
+ if (record.type === RunStartEvent.type) {
3043
+ events.push({
3044
+ type: "run.started",
3045
+ sessionId: this.options.sessionId,
3046
+ runId: record.runId,
3047
+ mode: String(payload2.modeId ?? record.modeId ?? "")
3048
+ });
3049
+ } else if (record.type === MessageDeltaEvent.type) {
3050
+ events.push({
3051
+ type: "assistant.delta",
3052
+ text: String(payload2.text ?? ""),
3053
+ event: record
3054
+ });
3055
+ } else if (record.type === MessageEndEvent.type) {
3056
+ const message = payload2.message;
3057
+ if (message?.role === "user") events.push({ type: "user.message", message });
3058
+ if (message?.role === "assistant") events.push({ type: "assistant.message", message });
3059
+ } else if (record.type === ToolStartEvent.type) {
3060
+ events.push({
3061
+ type: "tool.started",
3062
+ toolCallId: String(payload2.id ?? ""),
3063
+ name: String(payload2.name ?? ""),
3064
+ args: payload2.args
3065
+ });
3066
+ } else if (record.type === ToolApprovalResolvedEvent.type) {
3067
+ const approvalId = String(payload2.id ?? "");
3068
+ events.push({
3069
+ type: "tool.approval.resolved",
3070
+ approvalId: this.options.hydrateApprovalId?.(approvalId) ?? approvalId,
3071
+ approved: payload2.decision === "approved"
3072
+ });
3073
+ } else if (record.type === ToolEndEvent.type) {
3074
+ events.push({
3075
+ type: "tool.ended",
3076
+ toolCallId: String(payload2.id ?? ""),
3077
+ name: String(payload2.name ?? ""),
3078
+ result: payload2.result
3079
+ });
3080
+ } else if (record.type === ModeChangedEvent.type) {
3081
+ events.push({
3082
+ type: "mode.changed",
3083
+ previousMode: String(payload2.previousMode ?? ""),
3084
+ mode: String(payload2.mode ?? "")
3085
+ });
3086
+ }
3087
+ events.push({ type: "event", event: record });
3088
+ return events;
3089
+ }
3090
+ clear() {
3091
+ this.listeners.clear();
3092
+ }
3093
+ };
3094
+
3095
+ // src/logging/logger.ts
3096
+ function normalizeModelDeltas(value) {
3097
+ if (value === true) return "full";
3098
+ if (value === false || value === void 0) return "none";
3099
+ return value;
3100
+ }
3101
+ function configuredSinks(config) {
3102
+ if (config.sinks?.length) return config.sinks;
3103
+ if (config.level && config.level !== "silent") {
3104
+ return [new ConsoleLogSink({ format: config.format ?? "pretty" })];
3105
+ }
3106
+ return [];
3107
+ }
3108
+ function messageFromUnknown(errorOrMessage) {
3109
+ if (errorOrMessage instanceof Error) return errorOrMessage.message;
3110
+ return String(errorOrMessage);
3111
+ }
3112
+ function payload(message, fields, error) {
3113
+ return {
3114
+ ...fields ?? {},
3115
+ message,
3116
+ ...error === void 0 ? {} : { error }
3117
+ };
3118
+ }
3119
+ function createHarnessLogger(config = {}) {
3120
+ const level = config.level ?? "silent";
3121
+ const sinks = configuredSinks(config);
3122
+ const modelDeltas = normalizeModelDeltas(config.modelDeltas);
3123
+ const emit = (logClass, fields, context = {}) => {
3124
+ const record = normalizeHarnessLog(logClass, fields, context, config.redact);
3125
+ if (!shouldWriteLog(record.level, level)) return;
3126
+ for (const sink of sinks) {
3127
+ try {
3128
+ void Promise.resolve(sink.write(record)).catch(() => void 0);
3129
+ } catch {
3130
+ }
3131
+ }
3132
+ };
3133
+ const logger = {
3134
+ modelDeltas,
3135
+ emit,
3136
+ debug(message, fields, context) {
3137
+ emit(AgentDebugLog, payload(message, fields), context);
3138
+ },
3139
+ info(message, fields, context) {
3140
+ emit(AgentInfoLog, payload(message, fields), context);
3141
+ },
3142
+ warn(message, fields, context) {
3143
+ emit(AgentWarnLog, payload(message, fields), context);
3144
+ },
3145
+ error(errorOrMessage, fields, context) {
3146
+ emit(AgentErrorLog, payload(messageFromUnknown(errorOrMessage), fields, errorOrMessage), context);
3147
+ },
3148
+ agent(context) {
3149
+ return {
3150
+ debug: (message, fields) => logger.debug(message, fields, context),
3151
+ info: (message, fields) => logger.info(message, fields, context),
3152
+ warn: (message, fields) => logger.warn(message, fields, context),
3153
+ error: (errorOrMessage, fields) => logger.error(errorOrMessage, fields, context),
3154
+ emit: (logClass, fields) => logger.emit(logClass, fields, context)
3155
+ };
3156
+ },
3157
+ async flush() {
3158
+ await Promise.all(sinks.map(async (sink) => {
3159
+ try {
3160
+ await sink.flush?.();
3161
+ } catch {
3162
+ }
3163
+ }));
3164
+ },
3165
+ async close() {
3166
+ await Promise.all(sinks.map(async (sink) => {
3167
+ try {
3168
+ await sink.close?.();
3169
+ } catch {
3170
+ }
3171
+ }));
3172
+ }
3173
+ };
3174
+ return logger;
3175
+ }
3176
+
3177
+ // src/session/queue.ts
3178
+ var SessionQueue = class {
3179
+ queue = Promise.resolve();
3180
+ pendingSendTriggers = [];
3181
+ enqueue(execute) {
3182
+ const previous = this.queue;
3183
+ const next = previous.then(execute, execute);
3184
+ this.queue = next.catch(() => void 0);
3185
+ return next;
3186
+ }
3187
+ addPendingSendTrigger(after) {
3188
+ this.pendingSendTriggers.push(eventType(after ?? ToolEndEvent));
3189
+ }
3190
+ applyPendingSendTrigger(record) {
3191
+ const targetType = this.pendingSendTriggers[0];
3192
+ if (!targetType) return "none";
3193
+ if (record.type === targetType) {
3194
+ this.pendingSendTriggers.shift();
3195
+ return record.type === RunEndEvent.type ? "cleared" : "handoff";
3196
+ }
3197
+ if (record.type === RunEndEvent.type) {
3198
+ this.pendingSendTriggers.shift();
3199
+ return "cleared";
3200
+ }
3201
+ return "none";
3202
+ }
3203
+ };
3204
+
3205
+ // src/session/stream.ts
3206
+ var AsyncEventQueue = class {
3207
+ values = [];
3208
+ waiters = [];
3209
+ closed = false;
3210
+ push(value) {
3211
+ if (this.closed) return;
3212
+ const waiter = this.waiters.shift();
3213
+ if (waiter) waiter({ value, done: false });
3214
+ else this.values.push(value);
3215
+ }
3216
+ close() {
3217
+ if (this.closed) return;
3218
+ this.closed = true;
3219
+ for (const waiter of this.waiters.splice(0)) waiter({ value: void 0, done: true });
3220
+ }
3221
+ [Symbol.asyncIterator]() {
3222
+ return {
3223
+ next: () => {
3224
+ const value = this.values.shift();
3225
+ if (value) return Promise.resolve({ value, done: false });
3226
+ if (this.closed) return Promise.resolve({ value: void 0, done: true });
3227
+ return new Promise((resolve) => this.waiters.push(resolve));
3228
+ }
3229
+ };
3230
+ }
3231
+ };
3232
+ function createHarnessRunStream(start, cancel) {
3233
+ const queue = new AsyncEventQueue();
3234
+ const controller = {
3235
+ push: (event) => queue.push(event),
3236
+ close: () => queue.close()
3237
+ };
3238
+ const result = start(controller).finally(() => queue.close());
3239
+ return {
3240
+ id: randomId(),
3241
+ result,
3242
+ cancel,
3243
+ [Symbol.asyncIterator]: () => queue[Symbol.asyncIterator]()
3244
+ };
3245
+ }
3246
+
3247
+ // src/session/status.ts
3248
+ function payloadObject2(record) {
3249
+ return record.payload && typeof record.payload === "object" ? record.payload : {};
3250
+ }
3251
+ var SessionStatusTracker = class {
3252
+ runningValue = false;
3253
+ closedValue = false;
3254
+ phaseValue = "idle" /* Idle */;
3255
+ queuedInputCountValue = 0;
3256
+ currentTurnIdValue;
3257
+ activeToolValue;
3258
+ lastEventAtValue;
3259
+ lastErrorValue;
3260
+ get running() {
3261
+ return this.runningValue;
3262
+ }
3263
+ get queuedInputCount() {
3264
+ return this.queuedInputCountValue;
3265
+ }
3266
+ get activeTool() {
3267
+ return this.activeToolValue;
3268
+ }
3269
+ get phase() {
3270
+ return this.closedValue ? "closed" /* Closed */ : this.phaseValue;
3271
+ }
3272
+ enqueueSend() {
3273
+ this.queuedInputCountValue++;
3274
+ if (this.runningValue) this.phaseValue = "queued" /* Queued */;
3275
+ }
3276
+ beginRun() {
3277
+ this.runningValue = true;
3278
+ this.queuedInputCountValue = Math.max(0, this.queuedInputCountValue - 1);
3279
+ this.phaseValue = "starting" /* Starting */;
3280
+ }
3281
+ completeRun() {
3282
+ this.phaseValue = "completed" /* Completed */;
3283
+ }
3284
+ failRun(error) {
3285
+ this.lastErrorValue = error;
3286
+ this.phaseValue = "error" /* Error */;
3287
+ }
3288
+ finishRun() {
3289
+ this.runningValue = false;
3290
+ if (!this.closedValue && this.phaseValue !== "error" /* Error */) {
3291
+ this.phaseValue = this.queuedInputCountValue > 0 ? "queued" /* Queued */ : "idle" /* Idle */;
3292
+ }
3293
+ }
3294
+ close() {
3295
+ this.closedValue = true;
3296
+ this.phaseValue = "closed" /* Closed */;
3297
+ }
3298
+ markClosingTurn() {
3299
+ this.phaseValue = "closing_turn" /* ClosingTurn */;
3300
+ }
3301
+ applyRunnerRecord(record) {
3302
+ this.lastEventAtValue = record.at;
3303
+ const payload2 = payloadObject2(record);
3304
+ if (record.type === RunStartEvent.type) {
3305
+ this.phaseValue = "starting" /* Starting */;
3306
+ this.currentTurnIdValue = void 0;
3307
+ this.activeToolValue = void 0;
3308
+ this.lastErrorValue = void 0;
3309
+ } else if (record.type === ContextReadyEvent.type) {
3310
+ this.phaseValue = "building_context" /* BuildingContext */;
3311
+ } else if (record.type === ModelBeforeEvent.type) {
3312
+ this.phaseValue = "waiting_model" /* WaitingModel */;
3313
+ } else if (record.type === ToolStartEvent.type) {
3314
+ this.phaseValue = "running_tool" /* RunningTool */;
3315
+ this.activeToolValue = {
3316
+ id: String(payload2.id ?? ""),
3317
+ name: String(payload2.name ?? "")
3318
+ };
3319
+ } else if (record.type === ToolApprovalRequestedEvent.type) {
3320
+ this.phaseValue = "waiting_approval" /* WaitingApproval */;
3321
+ } else if (record.type === ToolEndEvent.type || record.type === ToolApprovalResolvedEvent.type) {
3322
+ this.activeToolValue = void 0;
3323
+ this.phaseValue = "waiting_model" /* WaitingModel */;
3324
+ } else if (record.type === TurnEndEvent.type) {
3325
+ this.phaseValue = "closing_turn" /* ClosingTurn */;
3326
+ this.currentTurnIdValue = void 0;
3327
+ } else if (record.type === RunEndEvent.type) {
3328
+ this.phaseValue = "completed" /* Completed */;
3329
+ } else if (record.type === ErrorEvent.type) {
3330
+ this.phaseValue = "error" /* Error */;
3331
+ this.lastErrorValue = { message: String(payload2.message ?? "Unknown error") };
3332
+ }
3333
+ if (record.turnId) this.currentTurnIdValue = record.turnId;
3334
+ }
3335
+ snapshot() {
3336
+ return {
3337
+ running: this.runningValue,
3338
+ phase: this.phase,
3339
+ queuedInputCount: this.queuedInputCountValue,
3340
+ currentTurnId: this.currentTurnIdValue,
3341
+ activeTool: this.activeToolValue,
3342
+ lastEventAt: this.lastEventAtValue,
3343
+ lastError: this.lastErrorValue
3344
+ };
3345
+ }
3346
+ };
3347
+
3348
+ // src/session/session.ts
3349
+ var defaultApprovalTimeoutMs = 5 * 60 * 1e3;
3350
+ function nowIso6() {
3351
+ return (/* @__PURE__ */ new Date()).toISOString();
3352
+ }
3353
+ function normalizeInput(input) {
3354
+ return typeof input === "string" ? { content: input } : input;
3355
+ }
3356
+ function resolveInitialMode(agent, mode) {
3357
+ if (mode === void 0) return void 0;
3358
+ if (typeof mode !== "string") return mode;
3359
+ if (!Array.isArray(agent.modes)) return void 0;
3360
+ const resolved = agent.modes.find((candidate) => getConstructType(candidate) === mode);
3361
+ if (!resolved) throw new Error(`Unknown initial mode '${mode}'.`);
3362
+ return resolved;
3363
+ }
3364
+ function toErrorShape(error) {
3365
+ if (error instanceof Error) {
3366
+ return {
3367
+ name: error.name,
3368
+ message: error.message,
3369
+ stack: error.stack,
3370
+ cause: error.cause
3371
+ };
3372
+ }
3373
+ return { message: String(error) };
3374
+ }
3375
+ var HarnessSessionImpl = class {
3376
+ constructor(config, input) {
3377
+ this.config = config;
3378
+ this.id = input.sessionId ?? randomId();
3379
+ this.events = new SessionEventHub({
3380
+ sessionId: this.id,
3381
+ hydrateApprovalId: (toolCallId) => this.approvals.hydrateApprovalId(toolCallId)
3382
+ });
3383
+ this.logger = createHarnessLogger(config.logging);
3384
+ this.approvals = new ApprovalController({
3385
+ sessionId: this.id,
3386
+ getRunId: () => this.runner.runId,
3387
+ timeoutMs: this.config.toolApprovalTimeoutMs ?? defaultApprovalTimeoutMs,
3388
+ notifyRequested: (approval) => this.notify({ type: "tool.approval.requested", approval })
3389
+ });
3390
+ const storage = resolveStorage(config.storage);
3391
+ this.runner = new AgentSessionRunner({
3392
+ sessionId: this.id,
3393
+ agent: input.agent,
3394
+ providers: config.providers,
3395
+ defaultModel: config.defaultModel,
3396
+ sandbox: config.sandbox,
3397
+ workDir: input.workDir,
3398
+ storage,
3399
+ initialMode: resolveInitialMode(input.agent, config.initialMode),
3400
+ toolApproval: config.toolApproval,
3401
+ maxTurns: config.maxTurns,
3402
+ services: config.services,
3403
+ approveTool: (request) => this.requestToolApproval(request),
3404
+ logger: this.logger
3405
+ });
3406
+ this.logger.emit(SessionCreatedLog, { sessionId: this.id }, { sessionId: this.id, source: { kind: "runtime" } });
3407
+ this.unsubscribeRunner = this.runner.subscribe((record) => {
3408
+ this.status.applyRunnerRecord(record);
3409
+ this.applyPendingSendTrigger(record);
3410
+ this.events.notifyRunnerRecord(record);
3411
+ this.notifyStatus();
3412
+ });
3413
+ this.transcript = {
3414
+ get: (options) => this.runner.getTranscript(options),
3415
+ getCursor: () => this.runner.getTranscriptCursor(),
3416
+ seek: (target) => this.runner.seekTranscript(target),
3417
+ latest: () => this.runner.latestTranscript()
3418
+ };
3419
+ this.snapshots = {
3420
+ create: (input2) => this.runner.createSnapshot(input2, { hiddenTranscript: false }),
3421
+ list: () => this.runner.listSnapshots(),
3422
+ get: (id) => this.runner.getSnapshot(id),
3423
+ restore: (id) => {
3424
+ this.assertSnapshotRestoreAllowed();
3425
+ return this.runner.restoreSnapshot(id, { hiddenTranscript: false });
3426
+ },
3427
+ delete: (id) => this.runner.deleteSnapshot(id, { hiddenTranscript: false })
3428
+ };
3429
+ }
3430
+ config;
3431
+ id;
3432
+ transcript;
3433
+ snapshots;
3434
+ runner;
3435
+ events;
3436
+ approvals;
3437
+ queue = new SessionQueue();
3438
+ status = new SessionStatusTracker();
3439
+ createdAt = nowIso6();
3440
+ logger;
3441
+ lastActiveAt = this.createdAt;
3442
+ closed = false;
3443
+ activeAbort;
3444
+ lastResult;
3445
+ unsubscribeRunner;
3446
+ async send(input, options) {
3447
+ const stream = this.stream(input, options);
3448
+ for await (const _event of stream) {
3449
+ }
3450
+ return stream.result;
3451
+ }
3452
+ stream(input, options = {}) {
3453
+ const userInput = normalizeInput(input);
3454
+ const externalSignal = options.signal;
3455
+ const controller = new AbortController();
3456
+ if (externalSignal) {
3457
+ if (externalSignal.aborted) controller.abort(externalSignal.reason);
3458
+ else externalSignal.addEventListener("abort", () => controller.abort(externalSignal.reason), { once: true });
3459
+ }
3460
+ this.status.enqueueSend();
3461
+ if (this.status.running) this.queue.addPendingSendTrigger(options.after);
3462
+ this.notifyStatus();
3463
+ const run = async (streamController) => {
3464
+ const execute = async () => {
3465
+ if (this.closed) throw new Error(`Harness session '${this.id}' is closed.`);
3466
+ const unsubscribe = this.on((event) => streamController.push(event));
3467
+ this.status.beginRun();
3468
+ this.activeAbort = controller;
3469
+ this.lastActiveAt = nowIso6();
3470
+ this.notifyStatus();
3471
+ try {
3472
+ const result = await this.runner.run(userInput.content, {
3473
+ signal: controller.signal,
3474
+ model: options.model,
3475
+ userInputId: userInput.id,
3476
+ userMetadata: userInput.metadata,
3477
+ userRole: userInput.role
3478
+ });
3479
+ const sendResult = {
3480
+ sessionId: this.id,
3481
+ runId: result.runId,
3482
+ agentKey: result.agentKey,
3483
+ answer: result.finalAnswer,
3484
+ mode: this.runner.mode,
3485
+ outputDir: result.outputDir,
3486
+ metrics: result.metrics,
3487
+ transcript: result.transcript,
3488
+ events: result.events
3489
+ };
3490
+ this.lastResult = sendResult;
3491
+ this.status.completeRun();
3492
+ this.notify({ type: "run.completed", result: sendResult });
3493
+ this.notifyStatus();
3494
+ await this.logger.flush();
3495
+ return sendResult;
3496
+ } catch (error) {
3497
+ const shaped = toErrorShape(error);
3498
+ this.status.failRun(shaped);
3499
+ this.notify({ type: "error", error: shaped });
3500
+ this.notifyStatus();
3501
+ await this.logger.flush();
3502
+ throw error;
3503
+ } finally {
3504
+ unsubscribe();
3505
+ this.status.finishRun();
3506
+ if (this.activeAbort === controller) this.activeAbort = void 0;
3507
+ this.lastActiveAt = nowIso6();
3508
+ this.notifyStatus();
3509
+ }
3510
+ };
3511
+ return this.queue.enqueue(execute);
3512
+ };
3513
+ return createHarnessRunStream(run, async (reason) => {
3514
+ controller.abort(reason);
3515
+ await this.cancelActiveRun(reason);
3516
+ });
3517
+ }
3518
+ getStatus() {
3519
+ const runInfo = this.runner.getRunInfo();
3520
+ const status = this.status.snapshot();
3521
+ return {
3522
+ sessionId: this.id,
3523
+ agentKey: this.runner.agent.key,
3524
+ mode: this.runner.mode,
3525
+ model: this.runner.getModel(),
3526
+ provider: this.runner.getModelProviderInfo(),
3527
+ createdAt: this.createdAt,
3528
+ lastActiveAt: this.lastActiveAt,
3529
+ running: status.running,
3530
+ phase: status.phase,
3531
+ queuedInputCount: status.queuedInputCount,
3532
+ currentTurnId: status.currentTurnId,
3533
+ activeTool: status.activeTool,
3534
+ lastEventAt: status.lastEventAt,
3535
+ lastError: status.lastError,
3536
+ pendingApprovalCount: this.approvals.count,
3537
+ runId: runInfo.runId,
3538
+ outputDir: this.lastResult?.outputDir ?? runInfo.outputDir,
3539
+ metrics: this.lastResult?.metrics ?? this.runner.getMetrics()
3540
+ };
3541
+ }
3542
+ getMode() {
3543
+ return this.runner.mode;
3544
+ }
3545
+ getModel() {
3546
+ return this.runner.getModel();
3547
+ }
3548
+ setModel(model) {
3549
+ this.runner.setModel(model);
3550
+ this.lastActiveAt = nowIso6();
3551
+ this.notifyStatus();
3552
+ }
3553
+ clearModelOverride() {
3554
+ this.runner.clearModelOverride();
3555
+ this.lastActiveAt = nowIso6();
3556
+ this.notifyStatus();
3557
+ }
3558
+ async switchMode(mode, input) {
3559
+ await this.runner.switchMode(this.resolveModeSelector(mode), input);
3560
+ this.lastActiveAt = nowIso6();
3561
+ }
3562
+ getState() {
3563
+ return this.runner.getState();
3564
+ }
3565
+ updateState(patch) {
3566
+ this.runner.updateState(patch);
3567
+ this.lastActiveAt = nowIso6();
3568
+ }
3569
+ replaceState(next) {
3570
+ this.runner.replaceState(next);
3571
+ this.lastActiveAt = nowIso6();
3572
+ }
3573
+ getEvents(filter) {
3574
+ return this.runner.getEvents(filter);
3575
+ }
3576
+ getContextSnapshot() {
3577
+ return this.runner.getContextSnapshot();
3578
+ }
3579
+ getAgentManifest() {
3580
+ return this.runner.getAgentManifest();
3581
+ }
3582
+ getPendingApprovals() {
3583
+ return this.approvals.getPending();
3584
+ }
3585
+ async approveTool(approvalId) {
3586
+ this.approvals.resolve(approvalId, true);
3587
+ }
3588
+ async denyTool(approvalId, _reason) {
3589
+ this.approvals.resolve(approvalId, false);
3590
+ }
3591
+ on(listener) {
3592
+ return this.events.on(listener);
3593
+ }
3594
+ onEvent(eventClass, listener) {
3595
+ return this.events.onEvent(eventClass, listener);
3596
+ }
3597
+ waitForEvent(eventClass, options = {}) {
3598
+ return this.events.waitForEvent(eventClass, options);
3599
+ }
3600
+ async close() {
3601
+ if (this.closed) return;
3602
+ this.closed = true;
3603
+ this.status.close();
3604
+ await this.cancelActiveRun("Session closed.");
3605
+ this.unsubscribeRunner();
3606
+ await this.runner.close();
3607
+ this.notifyStatus();
3608
+ this.events.clear();
3609
+ await this.logger.close();
3610
+ }
3611
+ notify(event) {
3612
+ this.events.notify(event);
3613
+ }
3614
+ notifyStatus() {
3615
+ this.notify({ type: "session.status", status: this.getStatus() });
3616
+ }
3617
+ assertSnapshotRestoreAllowed() {
3618
+ const reject = (reason) => {
3619
+ this.logger.emit(
3620
+ SnapshotRestoreRejectedLog,
3621
+ { reason },
3622
+ { sessionId: this.id, runId: this.runner.runId, source: { kind: "runtime" } }
3623
+ );
3624
+ throw new Error(reason);
3625
+ };
3626
+ if (this.closed) reject(`Harness session '${this.id}' is closed.`);
3627
+ if (this.status.running || this.status.queuedInputCount > 0 || this.approvals.count > 0 || this.status.activeTool) {
3628
+ reject("Snapshots can only be restored while the session is idle and has no pending approvals.");
3629
+ }
3630
+ if (this.status.phase !== "idle" /* Idle */) {
3631
+ reject("Snapshots can only be restored while the session is idle and has no pending approvals.");
3632
+ }
3633
+ }
3634
+ applyPendingSendTrigger(record) {
3635
+ if (this.queue.applyPendingSendTrigger(record) === "handoff") {
3636
+ this.status.markClosingTurn();
3637
+ this.runner.requestTurnHandoff();
3638
+ }
3639
+ }
3640
+ resolveModeSelector(mode) {
3641
+ if (typeof mode !== "string") return mode;
3642
+ const resolved = Object.values(this.runner.agent.modes).find((candidate) => getConstructType(candidate) === mode);
3643
+ if (!resolved) throw new Error(`Unknown mode '${mode}'.`);
3644
+ return resolved;
3645
+ }
3646
+ requestToolApproval(request) {
3647
+ return this.approvals.request(request);
3648
+ }
3649
+ async cancelActiveRun(reason) {
3650
+ this.activeAbort?.abort(reason);
3651
+ this.approvals.denyAll();
3652
+ }
3653
+ };
3654
+ async function createHarnessSession(config, options = {}) {
3655
+ const workDir = resolveWorkDir(config.workDir);
3656
+ const agent = await resolveAgent(config.agent);
3657
+ return new HarnessSessionImpl(config, {
3658
+ sessionId: options.sessionId,
3659
+ agent,
3660
+ workDir
3661
+ });
3662
+ }
3663
+
3664
+ // src/session/store.ts
3665
+ function mergeConfig(base, overrides) {
3666
+ return {
3667
+ ...base,
3668
+ ...overrides,
3669
+ agent: overrides?.agent ?? base.agent,
3670
+ providers: overrides?.providers ?? base.providers,
3671
+ services: overrides?.services ?? base.services,
3672
+ logging: overrides?.logging ?? base.logging
3673
+ };
3674
+ }
3675
+ var HarnessSessionStoreImpl = class {
3676
+ constructor(config) {
3677
+ this.config = config;
3678
+ }
3679
+ config;
3680
+ sessions = /* @__PURE__ */ new Map();
3681
+ unsubscriptions = /* @__PURE__ */ new Map();
3682
+ listeners = /* @__PURE__ */ new Set();
3683
+ closed = false;
3684
+ async getOrCreate(sessionId, overrides) {
3685
+ this.cleanupExpired();
3686
+ if (this.closed) throw new Error("Harness session store is closed.");
3687
+ const id = sessionId?.trim() || randomId();
3688
+ const existing = this.sessions.get(id);
3689
+ if (existing) return existing;
3690
+ const session = await createHarnessSession(mergeConfig(this.config, overrides), { sessionId: id });
3691
+ this.sessions.set(id, session);
3692
+ this.unsubscriptions.set(id, session.on((event) => this.notify({ type: "session.event", sessionId: id, event })));
3693
+ this.notify({ type: "session.created", sessionId: id, status: session.getStatus() });
3694
+ return session;
3695
+ }
3696
+ get(sessionId) {
3697
+ this.cleanupExpired();
3698
+ return this.sessions.get(sessionId);
3699
+ }
3700
+ list() {
3701
+ this.cleanupExpired();
3702
+ return [...this.sessions.values()].map((session) => session.getStatus());
3703
+ }
3704
+ async delete(sessionId) {
3705
+ const session = this.sessions.get(sessionId);
3706
+ if (!session) return false;
3707
+ this.sessions.delete(sessionId);
3708
+ this.unsubscriptions.get(sessionId)?.();
3709
+ this.unsubscriptions.delete(sessionId);
3710
+ await session.close();
3711
+ this.notify({ type: "session.deleted", sessionId });
3712
+ return true;
3713
+ }
3714
+ async clear() {
3715
+ for (const sessionId of [...this.sessions.keys()]) await this.delete(sessionId);
3716
+ this.notify({ type: "session.cleared" });
3717
+ }
3718
+ async send(sessionId, input, options) {
3719
+ const session = await this.getOrCreate(sessionId);
3720
+ return session.send(input, options);
3721
+ }
3722
+ async stream(sessionId, input, options) {
3723
+ const session = await this.getOrCreate(sessionId);
3724
+ return session.stream(input, options);
3725
+ }
3726
+ getPendingApprovals(sessionId) {
3727
+ if (sessionId) return this.sessions.get(sessionId)?.getPendingApprovals() ?? [];
3728
+ return [...this.sessions.values()].flatMap((session) => session.getPendingApprovals());
3729
+ }
3730
+ async approveTool(sessionId, approvalId) {
3731
+ const session = this.sessions.get(sessionId);
3732
+ if (!session) throw new Error(`Harness session '${sessionId}' was not found.`);
3733
+ await session.approveTool(approvalId);
3734
+ }
3735
+ async denyTool(sessionId, approvalId, reason) {
3736
+ const session = this.sessions.get(sessionId);
3737
+ if (!session) throw new Error(`Harness session '${sessionId}' was not found.`);
3738
+ await session.denyTool(approvalId, reason);
3739
+ }
3740
+ getAgentManifest(sessionId) {
3741
+ return this.sessions.get(sessionId)?.getAgentManifest();
3742
+ }
3743
+ on(listener) {
3744
+ this.listeners.add(listener);
3745
+ return () => this.listeners.delete(listener);
3746
+ }
3747
+ async close() {
3748
+ if (this.closed) return;
3749
+ this.closed = true;
3750
+ await this.clear();
3751
+ this.listeners.clear();
3752
+ }
3753
+ notify(event) {
3754
+ for (const listener of this.listeners) void listener(event);
3755
+ }
3756
+ cleanupExpired() {
3757
+ const ttl = this.config.sessionTtlMs;
3758
+ if (!ttl || ttl <= 0) return;
3759
+ const cutoff = Date.now() - ttl;
3760
+ for (const session of [...this.sessions.values()]) {
3761
+ if (Date.parse(session.getStatus().lastActiveAt) < cutoff) void this.delete(session.id);
3762
+ }
3763
+ }
3764
+ };
3765
+ async function createHarnessSessionStore(config) {
3766
+ return new HarnessSessionStoreImpl(config);
3767
+ }
3768
+
3769
+ export {
3770
+ createToolErrorPayload,
3771
+ AgentSessionRunner,
3772
+ HarnessSessionPhase,
3773
+ HarnessSessionImpl,
3774
+ createHarnessSession,
3775
+ HarnessSessionStoreImpl,
3776
+ createHarnessSessionStore
3777
+ };
3778
+ //# sourceMappingURL=chunk-4WWSQAWA.js.map