@harness-kernel/core 0.2.1 → 0.4.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 (58) hide show
  1. package/dist/agent/context.d.ts +2 -3
  2. package/dist/agent/context.js +1 -1
  3. package/dist/agent/event.d.ts +3 -2
  4. package/dist/agent/event.js +6 -2
  5. package/dist/agent/hook.d.ts +3 -4
  6. package/dist/agent/hook.js +1 -1
  7. package/dist/agent/mode.d.ts +2 -3
  8. package/dist/agent/mode.js +1 -1
  9. package/dist/agent/role.js +1 -1
  10. package/dist/agent/session.d.ts +3 -4
  11. package/dist/agent/session.js +8 -2
  12. package/dist/agent/tool.d.ts +39 -3
  13. package/dist/agent/tool.js +8 -2
  14. package/dist/agent.d.ts +6 -6
  15. package/dist/{approval-D_G2w-fW.d.ts → approval-7e8HD-35.d.ts} +20 -15
  16. package/dist/chunk-2NQ3JL7K.js +502 -0
  17. package/dist/chunk-2NQ3JL7K.js.map +1 -0
  18. package/dist/{chunk-JIJHGB6H.js → chunk-3QOAFCCZ.js} +24 -2
  19. package/dist/chunk-3QOAFCCZ.js.map +1 -0
  20. package/dist/{chunk-QEVKKJ7N.js → chunk-BLLQSHQI.js} +391 -151
  21. package/dist/chunk-BLLQSHQI.js.map +1 -0
  22. package/dist/{chunk-ZU6ADDET.js → chunk-IWHCNKHK.js} +1 -1
  23. package/dist/chunk-IWHCNKHK.js.map +1 -0
  24. package/dist/chunk-NCSQEUQC.js +37 -0
  25. package/dist/chunk-NCSQEUQC.js.map +1 -0
  26. package/dist/{chunk-B4Q6CPYO.js → chunk-TCHZYK5Y.js} +3 -1
  27. package/dist/{chunk-B4Q6CPYO.js.map → chunk-TCHZYK5Y.js.map} +1 -1
  28. package/dist/{context-BfpLqV11.d.ts → context-Bz9C_rL9.d.ts} +88 -3
  29. package/dist/errors-DHHy8V3O.d.ts +6 -0
  30. package/dist/errors-hpkW3tfR.d.ts +112 -0
  31. package/dist/{event-CKV4EeZ3.d.ts → event-DMBY-R6h.d.ts} +33 -4
  32. package/dist/{hook-CfBbhUQf.d.ts → hook-DD7uuzE3.d.ts} +2 -2
  33. package/dist/index.d.ts +25 -18
  34. package/dist/index.js +20 -7
  35. package/dist/metrics-DMN8bfY6.d.ts +23 -0
  36. package/dist/{model-provider-Ch7tzk1x.d.ts → model-provider-DGkLQJWP.d.ts} +6 -4
  37. package/dist/runner/approval.d.ts +7 -7
  38. package/dist/runner/event.d.ts +3 -2
  39. package/dist/runner/event.js +6 -2
  40. package/dist/runner/logging.d.ts +7 -3
  41. package/dist/runner/logging.js +11 -1
  42. package/dist/runner/model-provider.d.ts +6 -6
  43. package/dist/runner/sandbox.d.ts +12 -2
  44. package/dist/runner/sandbox.js +1 -1
  45. package/dist/runner/storage.d.ts +181 -4
  46. package/dist/{runner-B41JEovO.d.ts → runner-BYG9ARAV.d.ts} +11 -6
  47. package/dist/runner.d.ts +9 -8
  48. package/dist/runner.js +12 -6
  49. package/package.json +2 -2
  50. package/dist/chunk-JIJHGB6H.js.map +0 -1
  51. package/dist/chunk-Q44U2CMM.js +0 -239
  52. package/dist/chunk-Q44U2CMM.js.map +0 -1
  53. package/dist/chunk-QEVKKJ7N.js.map +0 -1
  54. package/dist/chunk-ZU6ADDET.js.map +0 -1
  55. package/dist/events-D4xcDi53.d.ts +0 -69
  56. package/dist/storage-DCZE_hES.d.ts +0 -200
  57. package/dist/tool-errors-CygY1Nba.d.ts +0 -27
  58. package/dist/types-BPmsw-mF.d.ts +0 -80
@@ -9,19 +9,26 @@ import {
9
9
  AgentWarnLog,
10
10
  ConsoleLogSink,
11
11
  HarnessLog,
12
+ annotateHarnessError,
13
+ normalizeHarnessError,
12
14
  normalizeHarnessLog,
13
15
  randomId,
16
+ sanitizeHarnessError,
14
17
  shouldWriteLog,
15
18
  summarizeValue
16
- } from "./chunk-Q44U2CMM.js";
19
+ } from "./chunk-2NQ3JL7K.js";
17
20
  import {
18
21
  HarnessSandboxSession,
19
22
  NoopSandbox
20
- } from "./chunk-ZU6ADDET.js";
23
+ } from "./chunk-IWHCNKHK.js";
21
24
  import {
22
25
  HarnessSessionStorage,
23
26
  MemorySessionStorage
24
27
  } from "./chunk-ONYDIU4X.js";
28
+ import {
29
+ createToolErrorPayload,
30
+ createToolErrorResult
31
+ } from "./chunk-NCSQEUQC.js";
25
32
  import {
26
33
  ContextReadyEvent,
27
34
  ErrorEvent,
@@ -31,7 +38,9 @@ import {
31
38
  ModeChangedEvent,
32
39
  ModelAfterEvent,
33
40
  ModelBeforeEvent,
41
+ RunAbortedEvent,
34
42
  RunEndEvent,
43
+ RunFailedEvent,
35
44
  RunStartEvent,
36
45
  SnapshotCreatedEvent,
37
46
  SnapshotDeletedEvent,
@@ -44,7 +53,7 @@ import {
44
53
  TurnEndEvent,
45
54
  TurnStartEvent,
46
55
  runtimeEventClasses
47
- } from "./chunk-JIJHGB6H.js";
56
+ } from "./chunk-3QOAFCCZ.js";
48
57
  import {
49
58
  normalizeSchema
50
59
  } from "./chunk-OBKS4AJR.js";
@@ -59,21 +68,7 @@ import {
59
68
  systemRole,
60
69
  toolRole,
61
70
  userRole
62
- } from "./chunk-B4Q6CPYO.js";
63
-
64
- // src/logging/tool-errors.ts
65
- function createToolErrorPayload(input) {
66
- return {
67
- ok: false,
68
- error: {
69
- code: input.code,
70
- message: input.message,
71
- toolName: input.toolName,
72
- invalidFields: input.invalidFields,
73
- metadata: input.metadata
74
- }
75
- };
76
- }
71
+ } from "./chunk-TCHZYK5Y.js";
77
72
 
78
73
  // src/logging/runtime-logs.ts
79
74
  var SessionCreatedLog = class extends HarnessLog {
@@ -100,8 +95,11 @@ var RunCompletedLog = class extends HarnessLog {
100
95
  var RunFailedLog = class extends HarnessLog {
101
96
  level = "error";
102
97
  category = "run";
103
- message() {
104
- return "run.failed";
98
+ levelFor(fields) {
99
+ return fields.error.severity === "warn" ? "warn" : "error";
100
+ }
101
+ message(fields) {
102
+ return `run.failed code=${fields.error.code}`;
105
103
  }
106
104
  };
107
105
  var TurnStartedLog = class extends HarnessLog {
@@ -135,8 +133,11 @@ var ContextBuildCompletedLog = class extends HarnessLog {
135
133
  var ContextProviderFailedLog = class extends HarnessLog {
136
134
  level = "error";
137
135
  category = "context";
136
+ levelFor(fields) {
137
+ return fields.error.severity === "warn" ? "warn" : "error";
138
+ }
138
139
  message(fields) {
139
- return `context.provider.failed ${fields.providerType}`;
140
+ return `context.provider.failed ${fields.providerType} code=${fields.error.code}`;
140
141
  }
141
142
  };
142
143
  var ModelCallStartedLog = class extends HarnessLog {
@@ -156,8 +157,11 @@ var ModelCallCompletedLog = class extends HarnessLog {
156
157
  var ModelCallFailedLog = class extends HarnessLog {
157
158
  level = "error";
158
159
  category = "model";
160
+ levelFor(fields) {
161
+ return fields.error.severity === "warn" ? "warn" : "error";
162
+ }
159
163
  message(fields) {
160
- return `model.failed model=${fields.model}`;
164
+ return `model.failed model=${fields.model} code=${fields.error.code}`;
161
165
  }
162
166
  };
163
167
  var ModelDeltaLog = class extends HarnessLog {
@@ -198,8 +202,11 @@ var ToolCompletedLog = class extends HarnessLog {
198
202
  var ToolFailedLog = class extends HarnessLog {
199
203
  level = "error";
200
204
  category = "tool";
205
+ levelFor(fields) {
206
+ return fields.error?.severity === "warn" ? "warn" : "error";
207
+ }
201
208
  message(fields) {
202
- return `tool.failed ${fields.toolName}`;
209
+ return `tool.failed ${fields.toolName}${fields.error ? ` code=${fields.error.code}` : ""}`;
203
210
  }
204
211
  };
205
212
  var ToolApprovalRequestedLog = class extends HarnessLog {
@@ -244,7 +251,7 @@ var SandboxExecFailedLog = class extends HarnessLog {
244
251
  level = "error";
245
252
  category = "tool";
246
253
  message(fields) {
247
- return `sandbox.exec.failed ${fields.sandboxId}`;
254
+ return `sandbox.exec.failed ${fields.sandboxId}${fields.error ? ` code=${fields.error.code}` : ""}`;
248
255
  }
249
256
  };
250
257
  var SandboxClosedLog = class extends HarnessLog {
@@ -300,7 +307,7 @@ var StorageWriteFailedLog = class extends HarnessLog {
300
307
  level = "error";
301
308
  category = "storage";
302
309
  message(fields) {
303
- return `storage.write_failed ${fields.operation}`;
310
+ return `storage.write_failed ${fields.operation} code=${fields.error.code}`;
304
311
  }
305
312
  };
306
313
 
@@ -583,11 +590,11 @@ var SandboxManager = class {
583
590
  this.input.logOpened({ sandboxId: opened.id, workDir: opened.workDir });
584
591
  return this.session;
585
592
  }
586
- async close() {
593
+ async close(input = { reason: "close" }) {
587
594
  const sandbox = this.session;
588
595
  this.session = void 0;
589
596
  if (!sandbox) return;
590
- await sandbox.close?.();
597
+ await sandbox.close?.(input);
591
598
  this.input.logClosed({ sandboxId: sandbox.id });
592
599
  }
593
600
  wrap(base) {
@@ -622,8 +629,8 @@ var SandboxManager = class {
622
629
  throw error;
623
630
  }
624
631
  }
625
- async close() {
626
- await base.close?.();
632
+ async close(input2) {
633
+ await base.close?.(input2);
627
634
  }
628
635
  }();
629
636
  }
@@ -666,7 +673,6 @@ var RunStorageCoordinator = class {
666
673
  await this.write("save_transcript", (store) => store.saveTranscript(messages));
667
674
  }
668
675
  async saveMetrics(metrics) {
669
- if (this.input.storage instanceof HarnessSessionStorage) return;
670
676
  await this.write("save_metrics", (store) => store.saveMetrics(metrics));
671
677
  }
672
678
  async saveSnapshot(snapshot) {
@@ -1175,6 +1181,9 @@ function ensureToolMetric(metrics, name) {
1175
1181
  metrics.tools[name] = metric;
1176
1182
  return metric;
1177
1183
  }
1184
+ function toolResultErrorCode(value) {
1185
+ return value === "tool.args.invalid_schema" || value === "tool.approval.denied" || value === "sandbox.exec.failed" || value === "tool.failed" ? value : "tool.failed";
1186
+ }
1178
1187
  var ToolExecutor = class {
1179
1188
  constructor(input) {
1180
1189
  this.input = input;
@@ -1219,6 +1228,13 @@ var ToolExecutor = class {
1219
1228
  };
1220
1229
  const parsedArgs = safeParseWithSchema(input.tool.inputSchema, input.args);
1221
1230
  if (!parsedArgs.ok) {
1231
+ const error = this.toolError({
1232
+ error: parsedArgs.error,
1233
+ code: "tool.args.invalid_schema",
1234
+ message: "Tool arguments did not match schema.",
1235
+ source: toolSource,
1236
+ details: { invalidFields: parsedArgs.invalidFields }
1237
+ });
1222
1238
  const result = this.structuredToolError({
1223
1239
  toolName: input.tool.name,
1224
1240
  code: "tool.args.invalid_schema",
@@ -1228,7 +1244,7 @@ var ToolExecutor = class {
1228
1244
  const durationMs = Math.round(performance.now() - start);
1229
1245
  metric.errorCount++;
1230
1246
  metric.totalDurationMs += durationMs;
1231
- metrics.errors.push(result.content);
1247
+ metrics.errors.push(sanitizeHarnessError(error, this.input.errorPolicy));
1232
1248
  this.input.log(
1233
1249
  ToolInvalidSchemaLog,
1234
1250
  { toolName: input.tool.name, issues: parsedArgs.issues },
@@ -1238,6 +1254,7 @@ var ToolExecutor = class {
1238
1254
  { durationMs }
1239
1255
  );
1240
1256
  await this.input.addToolResultMessage(input.tool, result, id);
1257
+ await this.emitToolError(error, toolEventOptions);
1241
1258
  await this.input.emitInternal(ToolEndEvent, { id, name: input.tool.name, durationMs, result }, callerEventOptions);
1242
1259
  this.input.throwIfTurnHandoffRequested();
1243
1260
  return result;
@@ -1257,6 +1274,13 @@ var ToolExecutor = class {
1257
1274
  callerEventOptions
1258
1275
  );
1259
1276
  if (!approved) {
1277
+ const error = this.toolError({
1278
+ error: new Error(`Tool '${input.tool.name}' was denied by runner policy.`),
1279
+ code: "tool.approval.denied",
1280
+ message: `Tool '${input.tool.name}' was denied by runner policy.`,
1281
+ source: toolSource,
1282
+ severity: "warn"
1283
+ });
1260
1284
  const denied = this.structuredToolError({
1261
1285
  toolName: input.tool.name,
1262
1286
  code: "tool.approval.denied",
@@ -1266,12 +1290,15 @@ var ToolExecutor = class {
1266
1290
  metric.errorCount++;
1267
1291
  const durationMs = Math.round(performance.now() - start);
1268
1292
  metric.totalDurationMs += durationMs;
1293
+ metrics.errors.push(sanitizeHarnessError(error, this.input.errorPolicy));
1269
1294
  this.input.log(ToolFailedLog, {
1270
1295
  toolName: input.tool.name,
1271
1296
  durationMs,
1297
+ error,
1272
1298
  result: summarizeValue(denied)
1273
1299
  }, toolSource, id, startEvent.id, { durationMs });
1274
1300
  await this.input.addToolResultMessage(input.tool, denied, id);
1301
+ await this.emitToolError(error, toolEventOptions);
1275
1302
  await this.input.emitInternal(ToolEndEvent, { id, name: input.tool.name, durationMs, result: denied }, callerEventOptions);
1276
1303
  this.input.throwIfTurnHandoffRequested();
1277
1304
  return denied;
@@ -1285,9 +1312,19 @@ var ToolExecutor = class {
1285
1312
  metric.totalDurationMs += durationMs;
1286
1313
  if (result.isError) metric.errorCount++;
1287
1314
  if (result.isError) {
1315
+ const errorCode = toolResultErrorCode(result.metadata?.errorCode);
1316
+ const error = this.toolError({
1317
+ error: new Error(result.content || "Tool execution failed."),
1318
+ code: errorCode,
1319
+ message: result.content || "Tool execution failed.",
1320
+ source: toolSource,
1321
+ details: result.data
1322
+ });
1323
+ metrics.errors.push(sanitizeHarnessError(error, this.input.errorPolicy));
1288
1324
  this.input.log(ToolFailedLog, {
1289
1325
  toolName: input.tool.name,
1290
1326
  durationMs,
1327
+ error,
1291
1328
  result: summarizeValue(result)
1292
1329
  }, toolSource, id, startEvent.id, { durationMs });
1293
1330
  } else {
@@ -1303,7 +1340,13 @@ var ToolExecutor = class {
1303
1340
  this.input.throwIfTurnHandoffRequested();
1304
1341
  return result;
1305
1342
  } catch (error) {
1306
- const message = error instanceof Error ? error.message : String(error);
1343
+ const normalized = this.toolError({
1344
+ error,
1345
+ code: "tool.failed",
1346
+ message: "Tool execution failed.",
1347
+ source: toolSource,
1348
+ details: { toolName: input.tool.name }
1349
+ });
1307
1350
  const result = this.structuredToolError({
1308
1351
  toolName: input.tool.name,
1309
1352
  code: "tool.failed",
@@ -1313,14 +1356,15 @@ var ToolExecutor = class {
1313
1356
  const durationMs = Math.round(performance.now() - start);
1314
1357
  metric.errorCount++;
1315
1358
  metric.totalDurationMs += durationMs;
1316
- metrics.errors.push(message);
1359
+ metrics.errors.push(sanitizeHarnessError(normalized, this.input.errorPolicy));
1317
1360
  this.input.log(ToolFailedLog, {
1318
1361
  toolName: input.tool.name,
1319
1362
  durationMs,
1320
- error
1363
+ error: normalized,
1364
+ internalError: error
1321
1365
  }, toolSource, id, startEvent.id, { durationMs });
1322
1366
  await this.input.addToolResultMessage(input.tool, result, id);
1323
- await this.input.emitInternal(ErrorEvent, { message, details: error }, toolEventOptions);
1367
+ await this.emitToolError(normalized, toolEventOptions);
1324
1368
  await this.input.emitInternal(ToolEndEvent, { id, name: input.tool.name, durationMs, result }, callerEventOptions);
1325
1369
  this.input.throwIfTurnHandoffRequested();
1326
1370
  return result;
@@ -1329,21 +1373,7 @@ var ToolExecutor = class {
1329
1373
  }
1330
1374
  }
1331
1375
  structuredToolError(input) {
1332
- return {
1333
- content: input.message,
1334
- data: createToolErrorPayload({
1335
- code: input.code,
1336
- message: input.message,
1337
- toolName: input.toolName,
1338
- invalidFields: input.invalidFields
1339
- }),
1340
- isError: true,
1341
- metadata: {
1342
- errorCode: input.code,
1343
- ...input.invalidFields ? { invalidFields: input.invalidFields } : {},
1344
- ...input.metadata ?? {}
1345
- }
1346
- };
1376
+ return createToolErrorResult(input);
1347
1377
  }
1348
1378
  ensureStructuredToolErrorResult(tool, result) {
1349
1379
  if (!result.isError || result.data !== void 0) return result;
@@ -1360,6 +1390,29 @@ var ToolExecutor = class {
1360
1390
  }
1361
1391
  };
1362
1392
  }
1393
+ toolError(input) {
1394
+ const normalized = normalizeHarnessError(input.error, {
1395
+ code: input.code,
1396
+ category: input.code === "tool.approval.denied" ? "approval" : input.code === "sandbox.exec.failed" ? "sandbox" : "tool",
1397
+ severity: input.severity ?? (input.code === "tool.approval.denied" ? "warn" : "error"),
1398
+ recoverable: true,
1399
+ source: input.source,
1400
+ message: input.message,
1401
+ details: input.details
1402
+ }, this.input.errorPolicy);
1403
+ annotateHarnessError(input.error, normalized);
1404
+ return normalized;
1405
+ }
1406
+ async emitToolError(error, options) {
1407
+ const sanitized = sanitizeHarnessError(error, this.input.errorPolicy);
1408
+ await this.input.emitInternal(ErrorEvent, {
1409
+ error: sanitized,
1410
+ message: sanitized.message,
1411
+ code: sanitized.code,
1412
+ recoverable: sanitized.recoverable,
1413
+ details: sanitized.details
1414
+ }, options);
1415
+ }
1363
1416
  async approveTool(request, tool, args, eventOptions) {
1364
1417
  const policy = this.input.getToolApprovalMode() ?? "tool-default";
1365
1418
  if (policy === "auto") return this.recordApprovalResolution(request, "approved", eventOptions);
@@ -1520,6 +1573,20 @@ var SnapshotManager = class {
1520
1573
  };
1521
1574
 
1522
1575
  // src/runtime/model-pipeline.ts
1576
+ function retryDelayMs(attempt, backoffMs, maxBackoffMs) {
1577
+ return Math.min(maxBackoffMs, backoffMs * 2 ** Math.max(0, attempt - 1));
1578
+ }
1579
+ function delay(ms, signal) {
1580
+ if (ms <= 0) return Promise.resolve();
1581
+ if (signal?.aborted) return Promise.reject(new Error("Run aborted."));
1582
+ return new Promise((resolve, reject) => {
1583
+ const timeout = setTimeout(resolve, ms);
1584
+ signal?.addEventListener("abort", () => {
1585
+ clearTimeout(timeout);
1586
+ reject(new Error("Run aborted."));
1587
+ }, { once: true });
1588
+ });
1589
+ }
1523
1590
  var ModelPipeline = class {
1524
1591
  constructor(input) {
1525
1592
  this.input = input;
@@ -1532,62 +1599,83 @@ var ModelPipeline = class {
1532
1599
  const model = this.input.getModel();
1533
1600
  const resolved = this.input.resolveModelProvider(model);
1534
1601
  const source = this.modelProviderSource(resolved);
1535
- try {
1536
- const modelStart = performance.now();
1602
+ const modelStart = performance.now();
1603
+ const retry = this.input.errorPolicy?.retry?.model;
1604
+ const maxAttempts = Math.max(1, retry?.attempts ?? 1);
1605
+ const backoffMs = Math.max(0, retry?.backoffMs ?? 250);
1606
+ const maxBackoffMs = Math.max(backoffMs, retry?.maxBackoffMs ?? 5e3);
1607
+ let result;
1608
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1609
+ let toolExecuted = false;
1610
+ let providerEventEmitted = false;
1537
1611
  this.input.log(ModelCallStartedLog, { model, messageCount: messages.length }, source);
1538
- const result = await resolved.provider.run({
1539
- runId: this.input.getRunId(),
1540
- turnId: this.input.getTurnId(),
1541
- modeId: this.input.getModeId(),
1542
- modelRef: resolved.modelRef,
1543
- provider: resolved.namespace,
1544
- model: resolved.modelId,
1545
- systemPrompt,
1546
- messages,
1547
- roles: this.input.roles,
1548
- tools: input.tools,
1549
- maxTurns: input.mode.maxTurns ?? 20,
1550
- signal: input.options.signal,
1551
- prepareContext: () => this.prepareContext(input.mode, input.userMessage),
1552
- emit: async (eventClass, payload2, options) => {
1553
- const event = await this.input.emit(
1554
- eventClass,
1555
- payload2,
1556
- this.input.withEmitDefaults(source, void 0, void 0, options)
1557
- );
1558
- this.input.throwIfTurnHandoffRequested();
1559
- return event;
1560
- },
1561
- executeTool: (tool, args, callId) => this.input.executeTool(tool, args, callId, source)
1562
- });
1563
- const assistantMessage = await this.input.addAssistantMessage(result.content, {
1564
- usage: result.usage,
1565
- finishReason: result.finishReason
1566
- });
1567
- this.input.setFinalAnswer(result.content);
1568
- this.input.getMetrics().usage = result.usage;
1569
- await this.input.emitInternal(MessageEndEvent, { message: assistantMessage });
1570
- await this.input.markMessageEventCursor(assistantMessage.id);
1571
- this.input.log(ModelCallCompletedLog, {
1572
- model,
1573
- durationMs: Math.round(performance.now() - modelStart),
1574
- finishReason: result.finishReason
1575
- }, source);
1576
- await this.input.emitInternal(ModelAfterEvent, {
1577
- model,
1578
- content: result.content,
1579
- usage: result.usage,
1580
- finishReason: result.finishReason
1581
- });
1582
- this.input.throwIfTurnHandoffRequested();
1583
- } catch (error) {
1584
- if (this.input.isTurnHandoffSignal(error)) return;
1585
- const message = error instanceof Error ? error.message : String(error);
1586
- this.input.getMetrics().errors.push(message);
1587
- this.input.log(ModelCallFailedLog, { model, error }, source);
1588
- await this.input.emitInternal(ErrorEvent, { message, details: error });
1589
- throw error;
1612
+ try {
1613
+ result = await resolved.provider.run({
1614
+ runId: this.input.getRunId(),
1615
+ turnId: this.input.getTurnId(),
1616
+ modeId: this.input.getModeId(),
1617
+ modelRef: resolved.modelRef,
1618
+ provider: resolved.namespace,
1619
+ model: resolved.modelId,
1620
+ systemPrompt,
1621
+ messages,
1622
+ roles: this.input.roles,
1623
+ tools: input.tools,
1624
+ maxTurns: input.mode.maxTurns ?? 20,
1625
+ signal: input.options.signal,
1626
+ prepareContext: () => this.prepareContext(input.mode, input.userMessage),
1627
+ emit: async (eventClass, payload2, options) => {
1628
+ providerEventEmitted = true;
1629
+ const event = await this.input.emit(
1630
+ eventClass,
1631
+ payload2,
1632
+ this.input.withEmitDefaults(source, void 0, void 0, options)
1633
+ );
1634
+ this.input.throwIfTurnHandoffRequested();
1635
+ return event;
1636
+ },
1637
+ executeTool: async (tool, args, callId) => {
1638
+ toolExecuted = true;
1639
+ return this.input.executeTool(tool, args, callId, source);
1640
+ }
1641
+ });
1642
+ break;
1643
+ } catch (error) {
1644
+ if (this.input.isTurnHandoffSignal(error)) return;
1645
+ const normalized = normalizeHarnessError(error, {
1646
+ category: "model",
1647
+ severity: "error",
1648
+ recoverable: false,
1649
+ source
1650
+ }, this.input.errorPolicy);
1651
+ annotateHarnessError(error, normalized);
1652
+ this.input.log(ModelCallFailedLog, { model, error: normalized, internalError: error, attempt }, source);
1653
+ const canRetry = attempt < maxAttempts && normalized.category === "model" && !toolExecuted && !providerEventEmitted && input.options.signal?.aborted !== true;
1654
+ if (!canRetry) throw error;
1655
+ await delay(retryDelayMs(attempt, backoffMs, maxBackoffMs), input.options.signal);
1656
+ }
1590
1657
  }
1658
+ if (!result) throw new Error("Model provider did not return a result.");
1659
+ const assistantMessage = await this.input.addAssistantMessage(result.content, {
1660
+ usage: result.usage,
1661
+ finishReason: result.finishReason
1662
+ });
1663
+ this.input.setFinalAnswer(result.content);
1664
+ this.input.getMetrics().usage = result.usage;
1665
+ await this.input.emitInternal(MessageEndEvent, { message: assistantMessage });
1666
+ await this.input.markMessageEventCursor(assistantMessage.id);
1667
+ this.input.log(ModelCallCompletedLog, {
1668
+ model,
1669
+ durationMs: Math.round(performance.now() - modelStart),
1670
+ finishReason: result.finishReason
1671
+ }, source);
1672
+ await this.input.emitInternal(ModelAfterEvent, {
1673
+ model,
1674
+ content: result.content,
1675
+ usage: result.usage,
1676
+ finishReason: result.finishReason
1677
+ });
1678
+ this.input.throwIfTurnHandoffRequested();
1591
1679
  }
1592
1680
  modelProviderSource(resolved) {
1593
1681
  const id = modelProviderId(resolved.provider);
@@ -1776,7 +1864,16 @@ var AgentSessionRunner = class {
1776
1864
  mode: () => this.currentMode,
1777
1865
  openExistingRun: Boolean(options.initialRunId),
1778
1866
  logOpened: (fields) => this.log(RunStorageOpenedLog, fields),
1779
- logFailed: (fields) => this.log(StorageWriteFailedLog, fields)
1867
+ logFailed: (fields) => {
1868
+ const error = this.normalizeError(fields.error, {
1869
+ code: "storage.write_failed",
1870
+ category: "storage",
1871
+ severity: "error",
1872
+ recoverable: true,
1873
+ details: { operation: fields.operation }
1874
+ });
1875
+ this.log(StorageWriteFailedLog, { operation: fields.operation, error, internalError: fields.error });
1876
+ }
1780
1877
  });
1781
1878
  this.snapshotManager = new SnapshotManager({
1782
1879
  now: () => nowIso4(),
@@ -1870,13 +1967,28 @@ var AgentSessionRunner = class {
1870
1967
  logClosed: (fields) => this.log(SandboxClosedLog, fields),
1871
1968
  logExecStarted: (fields) => this.log(SandboxExecStartedLog, fields),
1872
1969
  logExecCompleted: (fields) => this.log(SandboxExecCompletedLog, fields),
1873
- logExecFailed: (fields) => this.log(SandboxExecFailedLog, fields)
1970
+ logExecFailed: (fields) => {
1971
+ const error = this.normalizeError(fields.error, {
1972
+ code: "sandbox.exec.failed",
1973
+ category: "sandbox",
1974
+ severity: "error",
1975
+ recoverable: true,
1976
+ details: { sandboxId: fields.sandboxId, durationMs: fields.durationMs }
1977
+ });
1978
+ this.log(SandboxExecFailedLog, {
1979
+ sandboxId: fields.sandboxId,
1980
+ durationMs: fields.durationMs,
1981
+ error,
1982
+ internalError: fields.error
1983
+ });
1984
+ }
1874
1985
  });
1875
1986
  this.toolExecutor = new ToolExecutor({
1876
1987
  getMetrics: () => this.metrics,
1877
1988
  getCurrentMode: () => this.currentMode,
1878
1989
  getToolApprovalMode: () => this.getModeDefinition(this.currentMode).toolApproval,
1879
1990
  approveTool: this.options.approveTool,
1991
+ errorPolicy: this.options.errorPolicy,
1880
1992
  ensureSandboxOpen: () => this.ensureSandboxOpen(),
1881
1993
  buildActionSession: (tool, source, correlationId, causationId) => this.buildActionSession(tool, source, correlationId, causationId),
1882
1994
  addToolCallMessage: (tool, args, toolCallId, source) => this.addToolCallMessage(tool, args, toolCallId, source),
@@ -1907,7 +2019,8 @@ var AgentSessionRunner = class {
1907
2019
  withEmitDefaults: (source, correlationId, causationId, eventOptions) => this.withEmitDefaults(source, correlationId, causationId, eventOptions),
1908
2020
  log: (logClass, fields, source, correlationId, causationId, overrides) => this.log(logClass, fields, source, correlationId, causationId, overrides),
1909
2021
  throwIfTurnHandoffRequested: () => this.throwIfTurnHandoffRequested(),
1910
- isTurnHandoffSignal: (error) => error instanceof TurnHandoffSignal
2022
+ isTurnHandoffSignal: (error) => error instanceof TurnHandoffSignal,
2023
+ errorPolicy: this.options.errorPolicy
1911
2024
  });
1912
2025
  this.state = this.createInitialState(this.agent);
1913
2026
  for (const eventClass of runtimeEventClasses) eventType(eventClass);
@@ -1945,6 +2058,14 @@ var AgentSessionRunner = class {
1945
2058
  hookDepth = 0;
1946
2059
  turnHandoffRequested = false;
1947
2060
  restoredFromRun = false;
2061
+ normalizeError(error, context = {}) {
2062
+ const normalized = normalizeHarnessError(error, context, this.options.errorPolicy);
2063
+ annotateHarnessError(error, normalized);
2064
+ return normalized;
2065
+ }
2066
+ publicError(error) {
2067
+ return sanitizeHarnessError(error, this.options.errorPolicy);
2068
+ }
1948
2069
  logSource(source) {
1949
2070
  if (!source) return { kind: "runtime" };
1950
2071
  return {
@@ -2008,8 +2129,8 @@ var AgentSessionRunner = class {
2008
2129
  async ensureSandboxOpen() {
2009
2130
  await this.sandboxManager.ensureOpen();
2010
2131
  }
2011
- async closeSandbox() {
2012
- await this.sandboxManager.close();
2132
+ async closeSandbox(input = { reason: "close" }) {
2133
+ await this.sandboxManager.close(input);
2013
2134
  }
2014
2135
  async closeStore() {
2015
2136
  await this.storageCoordinator.close();
@@ -2041,7 +2162,7 @@ var AgentSessionRunner = class {
2041
2162
  }
2042
2163
  async beginNewRun() {
2043
2164
  if (this.started || this.restoredFromRun) {
2044
- await this.closeSandbox();
2165
+ await this.closeSandbox({ reason: "close" });
2045
2166
  this.runIdValue = randomId();
2046
2167
  await this.storageCoordinator.beginRun(this.runIdValue);
2047
2168
  this.started = false;
@@ -2053,12 +2174,12 @@ var AgentSessionRunner = class {
2053
2174
  this.turnHandoffRequested = false;
2054
2175
  }
2055
2176
  async run(message, options = {}) {
2056
- if (options.signal?.aborted) throw new Error("Run aborted.");
2057
2177
  await this.beginNewRun();
2058
2178
  this.runModelOverride = void 0;
2059
2179
  this.runModelOverride = this.normalizeModelOverride(options.model);
2060
2180
  let runStarted = false;
2061
2181
  try {
2182
+ if (options.signal?.aborted) throw new Error("Run aborted.");
2062
2183
  await this.start();
2063
2184
  runStarted = true;
2064
2185
  this.pendingInputs.push({
@@ -2102,22 +2223,61 @@ var AgentSessionRunner = class {
2102
2223
  outputDir: this.storeRunDir()
2103
2224
  };
2104
2225
  } catch (error) {
2105
- this.log(RunFailedLog, { error });
2226
+ const normalized = this.normalizeError(error, {
2227
+ code: "run.failed",
2228
+ category: "run",
2229
+ severity: "fatal",
2230
+ recoverable: false,
2231
+ source: { kind: "runtime" }
2232
+ });
2233
+ const publicError = this.publicError(normalized);
2234
+ this.finalizeMetrics();
2235
+ this.metrics.errors.push(publicError);
2236
+ await this.tryFailureWrite(() => this.storageCoordinator.saveTranscript(this.transcriptManager.allMessages));
2237
+ await this.tryFailureWrite(() => this.emitInternal(ErrorEvent, {
2238
+ error: publicError,
2239
+ message: publicError.message,
2240
+ code: publicError.code,
2241
+ recoverable: publicError.recoverable,
2242
+ details: publicError.details
2243
+ }, { skipHooks: true }));
2244
+ const terminalEvent = publicError.code === "run.aborted" ? RunAbortedEvent : RunFailedEvent;
2245
+ await this.tryFailureWrite(() => this.emitInternal(terminalEvent, {
2246
+ error: publicError,
2247
+ metrics: cloneJSON4({ ...this.metrics, eventCount: this.eventRecorder.count + 1 }),
2248
+ finalAnswer: this.finalAnswer || void 0
2249
+ }, { skipHooks: true }));
2250
+ this.contextRegistry.expireScope("run" /* Run */);
2251
+ this.finalizeMetrics();
2252
+ await this.tryFailureWrite(() => this.storageCoordinator.saveMetrics(this.metrics));
2253
+ this.log(RunFailedLog, { error: normalized, internalError: error });
2106
2254
  throw error;
2107
2255
  } finally {
2108
- if (runStarted) await this.closeSandbox();
2256
+ if (runStarted) await this.closeSandbox({ reason: "close" });
2109
2257
  this.runModelOverride = void 0;
2110
2258
  }
2111
2259
  }
2112
2260
  async prompt(message, options = {}) {
2113
2261
  return this.run(message, options);
2114
2262
  }
2263
+ finalizeMetrics() {
2264
+ this.metrics.completedAt = this.metrics.completedAt ?? nowIso4();
2265
+ this.metrics.durationMs = this.startedAtPerf > 0 ? Math.round(performance.now() - this.startedAtPerf) : 0;
2266
+ this.metrics.finalMode = this.currentMode;
2267
+ this.metrics.eventCount = this.eventRecorder.count;
2268
+ }
2269
+ async tryFailureWrite(write) {
2270
+ try {
2271
+ await write();
2272
+ } catch {
2273
+ }
2274
+ }
2115
2275
  async hydrate() {
2116
2276
  if (!this.restoredFromRun) return;
2117
2277
  await this.ensureStoreInitialized();
2118
2278
  }
2119
- async close() {
2120
- await this.closeSandbox();
2279
+ async close(input = { reason: "close" }) {
2280
+ await this.closeSandbox(input);
2121
2281
  await this.closeStore();
2122
2282
  }
2123
2283
  getTranscript(options) {
@@ -2602,7 +2762,11 @@ var AgentSessionRunner = class {
2602
2762
  this.log(ContextBuildStartedLog, { providerCount: activeBindings.length }, { kind: "runtime" });
2603
2763
  const providers = [];
2604
2764
  for (const binding of activeBindings) {
2605
- providers.push(await this.loadContextProvider(binding));
2765
+ try {
2766
+ providers.push(await this.loadContextProvider(binding));
2767
+ } catch (error) {
2768
+ if (!this.handleContextProviderFailure(binding, error)) throw error;
2769
+ }
2606
2770
  }
2607
2771
  const dynamicEntries = this.dynamicContextEntriesFor(trigger);
2608
2772
  const contributions = [
@@ -2654,18 +2818,40 @@ var AgentSessionRunner = class {
2654
2818
  binding: summary,
2655
2819
  contributions: this.normalizeContextOutput(provider, output)
2656
2820
  };
2657
- } catch (error) {
2658
- this.log(
2659
- ContextProviderFailedLog,
2660
- { providerType: summary.type, error },
2661
- { kind: "context_provider", id: summary.type, name: summary.label },
2662
- this.currentTurnId
2663
- );
2664
- throw error;
2665
2821
  } finally {
2666
2822
  this.providerStack.pop();
2667
2823
  }
2668
2824
  }
2825
+ handleContextProviderFailure(binding, error) {
2826
+ let summary = { type: "unknown" };
2827
+ let required = true;
2828
+ try {
2829
+ const { provider, options } = this.providerFromReference(binding, true);
2830
+ summary = contextProviderSummary(provider, options);
2831
+ required = isContextProviderBinding(binding) && typeof binding.required === "boolean" ? binding.required : provider.required ?? true;
2832
+ } catch {
2833
+ }
2834
+ const modeFailure = this.getModeDefinition(this.currentMode).contextFailure;
2835
+ const policyAllowsSkip = this.options.errorPolicy?.contextFailure === "warn-and-skip";
2836
+ const skip = policyAllowsSkip && (required === false || modeFailure === "warn-and-skip");
2837
+ const source = { kind: "context_provider", id: summary.type, name: summary.label };
2838
+ const normalized = this.normalizeError(error, {
2839
+ code: "context.provider.failed",
2840
+ category: "context",
2841
+ severity: skip ? "warn" : "error",
2842
+ recoverable: skip,
2843
+ source,
2844
+ details: { providerType: summary.type }
2845
+ });
2846
+ this.log(
2847
+ ContextProviderFailedLog,
2848
+ { providerType: summary.type, error: normalized, internalError: error },
2849
+ source,
2850
+ this.currentTurnId
2851
+ );
2852
+ if (skip) this.metrics.errors.push(this.publicError(normalized));
2853
+ return skip;
2854
+ }
2669
2855
  normalizeContextContribution(input, context = {}) {
2670
2856
  if (typeof input === "string") {
2671
2857
  const role2 = this.resolveRole(context.defaultRole ?? systemRole);
@@ -3078,6 +3264,20 @@ var SessionEventHub = class {
3078
3264
  runId: record.runId,
3079
3265
  mode: String(payload2.modeId ?? record.modeId ?? "")
3080
3266
  });
3267
+ } else if (record.type === RunFailedEvent.type) {
3268
+ events.push({
3269
+ type: "run.failed",
3270
+ runId: record.runId,
3271
+ error: payload2.error,
3272
+ metrics: payload2.metrics
3273
+ });
3274
+ } else if (record.type === RunAbortedEvent.type) {
3275
+ events.push({
3276
+ type: "run.aborted",
3277
+ runId: record.runId,
3278
+ error: payload2.error,
3279
+ metrics: payload2.metrics
3280
+ });
3081
3281
  } else if (record.type === MessageDeltaEvent.type) {
3082
3282
  events.push({
3083
3283
  type: "assistant.delta",
@@ -3224,14 +3424,17 @@ var SessionQueue = class {
3224
3424
  if (!targetType) return "none";
3225
3425
  if (record.type === targetType) {
3226
3426
  this.pendingSendTriggers.shift();
3227
- return record.type === RunEndEvent.type ? "cleared" : "handoff";
3427
+ return this.isTerminalRunEvent(record.type) ? "cleared" : "handoff";
3228
3428
  }
3229
- if (record.type === RunEndEvent.type) {
3429
+ if (this.isTerminalRunEvent(record.type)) {
3230
3430
  this.pendingSendTriggers.shift();
3231
3431
  return "cleared";
3232
3432
  }
3233
3433
  return "none";
3234
3434
  }
3435
+ isTerminalRunEvent(type) {
3436
+ return type === RunEndEvent.type || type === RunFailedEvent.type || type === RunAbortedEvent.type;
3437
+ }
3235
3438
  };
3236
3439
 
3237
3440
  // src/session/stream.ts
@@ -3358,9 +3561,19 @@ var SessionStatusTracker = class {
3358
3561
  this.currentTurnIdValue = void 0;
3359
3562
  } else if (record.type === RunEndEvent.type) {
3360
3563
  this.phaseValue = "completed" /* Completed */;
3361
- } else if (record.type === ErrorEvent.type) {
3564
+ } else if (record.type === RunFailedEvent.type || record.type === RunAbortedEvent.type) {
3362
3565
  this.phaseValue = "error" /* Error */;
3363
- this.lastErrorValue = { message: String(payload2.message ?? "Unknown error") };
3566
+ this.lastErrorValue = payload2.error;
3567
+ } else if (record.type === ErrorEvent.type) {
3568
+ const error = payload2.error;
3569
+ this.lastErrorValue = error ?? {
3570
+ code: "runtime.failed",
3571
+ category: "runtime",
3572
+ severity: "fatal",
3573
+ recoverable: false,
3574
+ message: String(payload2.message ?? "Unknown error")
3575
+ };
3576
+ if (!this.lastErrorValue.recoverable) this.phaseValue = "error" /* Error */;
3364
3577
  }
3365
3578
  if (record.turnId) this.currentTurnIdValue = record.turnId;
3366
3579
  }
@@ -3385,17 +3598,6 @@ function nowIso6() {
3385
3598
  function normalizeInput(input) {
3386
3599
  return typeof input === "string" ? { content: input } : input;
3387
3600
  }
3388
- function toErrorShape(error) {
3389
- if (error instanceof Error) {
3390
- return {
3391
- name: error.name,
3392
- message: error.message,
3393
- stack: error.stack,
3394
- cause: error.cause
3395
- };
3396
- }
3397
- return { message: String(error) };
3398
- }
3399
3601
  var HarnessSessionImpl = class {
3400
3602
  constructor(config, input) {
3401
3603
  this.config = config;
@@ -3424,7 +3626,8 @@ var HarnessSessionImpl = class {
3424
3626
  initialRunId: input.restoredSession?.latestRunId,
3425
3627
  resources: config.resources,
3426
3628
  approveTool: (request) => this.requestToolApproval(request),
3427
- logger: this.logger
3629
+ logger: this.logger,
3630
+ errorPolicy: config.errorPolicy
3428
3631
  });
3429
3632
  this.logger.emit(SessionCreatedLog, { sessionId: this.id }, { sessionId: this.id, source: { kind: "runtime" } });
3430
3633
  this.unsubscribeRunner = this.runner.subscribe((record) => {
@@ -3517,11 +3720,18 @@ var HarnessSessionImpl = class {
3517
3720
  await this.logger.flush();
3518
3721
  return sendResult;
3519
3722
  } catch (error) {
3520
- const shaped = toErrorShape(error);
3723
+ const shaped = sanitizeHarnessError(normalizeHarnessError(error, {
3724
+ code: "run.failed",
3725
+ category: "run",
3726
+ severity: "fatal",
3727
+ recoverable: false,
3728
+ source: { kind: "runtime" }
3729
+ }, this.config.errorPolicy), this.config.errorPolicy);
3521
3730
  this.status.failRun(shaped);
3522
3731
  this.notify({ type: "error", error: shaped });
3523
3732
  this.notifyStatus();
3524
3733
  await this.logger.flush();
3734
+ if (this.config.errorPolicy?.closeSessionOnFatal) await this.closeAfterFatal();
3525
3735
  throw error;
3526
3736
  } finally {
3527
3737
  unsubscribe();
@@ -3620,13 +3830,13 @@ var HarnessSessionImpl = class {
3620
3830
  waitForEvent(eventClass, options = {}) {
3621
3831
  return this.events.waitForEvent(eventClass, options);
3622
3832
  }
3623
- async close() {
3833
+ async close(input = { reason: "close" }) {
3624
3834
  if (this.closed) return;
3625
3835
  this.closed = true;
3626
3836
  this.status.close();
3627
3837
  await this.cancelActiveRun("Session closed.");
3628
3838
  this.unsubscribeRunner();
3629
- await this.runner.close();
3839
+ await this.runner.close(input);
3630
3840
  this.notifyStatus();
3631
3841
  this.events.clear();
3632
3842
  await this.logger.close();
@@ -3676,6 +3886,17 @@ var HarnessSessionImpl = class {
3676
3886
  this.activeAbort?.abort(reason);
3677
3887
  this.approvals.denyAll();
3678
3888
  }
3889
+ async closeAfterFatal() {
3890
+ if (this.closed) return;
3891
+ this.closed = true;
3892
+ this.status.close();
3893
+ this.approvals.denyAll();
3894
+ this.unsubscribeRunner();
3895
+ await this.runner.close({ reason: "close" });
3896
+ this.notifyStatus();
3897
+ this.events.clear();
3898
+ await this.logger.close();
3899
+ }
3679
3900
  };
3680
3901
  async function createHarnessSession(config, options = {}) {
3681
3902
  const agent = await resolveAgent(config.agent);
@@ -3709,6 +3930,7 @@ function mergeConfig(base, overrides) {
3709
3930
  providers: overrides?.providers ?? base.providers,
3710
3931
  resources: overrides?.resources ?? base.resources,
3711
3932
  logging: overrides?.logging ?? base.logging,
3933
+ errorPolicy: overrides?.errorPolicy ?? base.errorPolicy,
3712
3934
  storage: overrides?.storage ?? base.storage
3713
3935
  };
3714
3936
  }
@@ -3764,6 +3986,7 @@ var HarnessSessionStoreImpl = class {
3764
3986
  sessions = /* @__PURE__ */ new Map();
3765
3987
  unsubscriptions = /* @__PURE__ */ new Map();
3766
3988
  latestRunIds = /* @__PURE__ */ new Map();
3989
+ sessionSandboxes = /* @__PURE__ */ new Map();
3767
3990
  listeners = /* @__PURE__ */ new Set();
3768
3991
  storage;
3769
3992
  closed = false;
@@ -3778,6 +4001,7 @@ var HarnessSessionStoreImpl = class {
3778
4001
  const config = mergeConfig({ ...this.config, storage: this.storage }, overrides);
3779
4002
  const stored = await this.storage.getSession(id);
3780
4003
  const session = await createHarnessSession(config, { sessionId: id, restoredSession: stored });
4004
+ this.sessionSandboxes.set(id, config.sandbox);
3781
4005
  const status = session.getStatus();
3782
4006
  if (stored?.latestRunId) this.latestRunIds.set(id, stored.latestRunId);
3783
4007
  else this.latestRunIds.delete(id);
@@ -3794,6 +4018,7 @@ var HarnessSessionStoreImpl = class {
3794
4018
  this.unsubscriptions.set(id, session.on((event) => {
3795
4019
  if (event.type === "run.started") this.latestRunIds.set(id, event.runId);
3796
4020
  if (event.type === "run.completed") this.latestRunIds.set(id, event.result.runId);
4021
+ if (event.type === "run.failed" || event.type === "run.aborted") this.latestRunIds.set(id, event.runId);
3797
4022
  if (event.type === "session.status") {
3798
4023
  void this.storage.touchSession({
3799
4024
  sessionId: id,
@@ -3830,17 +4055,26 @@ var HarnessSessionStoreImpl = class {
3830
4055
  }
3831
4056
  const session = this.sessions.get(sessionId);
3832
4057
  if (!session) return false;
3833
- this.sessions.delete(sessionId);
3834
- this.latestRunIds.delete(sessionId);
3835
- this.unsubscriptions.get(sessionId)?.();
3836
- this.unsubscriptions.delete(sessionId);
3837
- await session.close();
4058
+ await this.closeTrackedSession(sessionId, session, { reason: "close" });
3838
4059
  return true;
3839
4060
  }
3840
4061
  async delete(sessionId) {
3841
- await this.close(sessionId);
4062
+ const session = this.sessions.get(sessionId);
4063
+ if (session) {
4064
+ await this.closeTrackedSession(sessionId, session, { reason: "delete" });
4065
+ } else {
4066
+ const stored = await this.storage.getSession(sessionId);
4067
+ const sandbox = this.sessionSandboxes.get(sessionId) ?? this.config.sandbox;
4068
+ if (stored || this.sessionSandboxes.has(sessionId)) {
4069
+ await sandbox?.destroy?.({
4070
+ sessionId,
4071
+ agentKey: stored?.agentKey
4072
+ });
4073
+ }
4074
+ }
3842
4075
  this.latestRunIds.delete(sessionId);
3843
4076
  const deleted = await this.storage.deleteSession(sessionId);
4077
+ this.sessionSandboxes.delete(sessionId);
3844
4078
  if (deleted) this.notify({ type: "session.deleted", sessionId });
3845
4079
  return deleted;
3846
4080
  }
@@ -3883,6 +4117,13 @@ var HarnessSessionStoreImpl = class {
3883
4117
  notify(event) {
3884
4118
  for (const listener of this.listeners) void listener(event);
3885
4119
  }
4120
+ async closeTrackedSession(sessionId, session, input) {
4121
+ this.sessions.delete(sessionId);
4122
+ this.latestRunIds.delete(sessionId);
4123
+ this.unsubscriptions.get(sessionId)?.();
4124
+ this.unsubscriptions.delete(sessionId);
4125
+ await session.close(input);
4126
+ }
3886
4127
  };
3887
4128
  async function createHarnessSessionStore(config) {
3888
4129
  const store = new HarnessSessionStoreImpl(config);
@@ -3891,7 +4132,6 @@ async function createHarnessSessionStore(config) {
3891
4132
  }
3892
4133
 
3893
4134
  export {
3894
- createToolErrorPayload,
3895
4135
  AgentSessionRunner,
3896
4136
  HarnessSessionPhase,
3897
4137
  HarnessSessionImpl,
@@ -3899,4 +4139,4 @@ export {
3899
4139
  HarnessSessionStoreImpl,
3900
4140
  createHarnessSessionStore
3901
4141
  };
3902
- //# sourceMappingURL=chunk-QEVKKJ7N.js.map
4142
+ //# sourceMappingURL=chunk-BLLQSHQI.js.map