@rowan-agent/agent 0.16.2 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -945,7 +945,7 @@ function createRuntimeCoreTools(input = {}) {
945
945
  import { mkdir as mkdir2, chmod as chmod2, writeFile as writeFile2 } from "fs/promises";
946
946
  import { randomUUID } from "crypto";
947
947
  import { join as join5 } from "path";
948
- import { createModelStream } from "@rowan-agent/models";
948
+ import { createModelStream, isValidToolProgress } from "@rowan-agent/models";
949
949
 
950
950
  // src/utils.ts
951
951
  function createId(prefix) {
@@ -961,6 +961,49 @@ function createTimestamp(date = /* @__PURE__ */ new Date()) {
961
961
  const mm = String(abs % 60).padStart(2, "0");
962
962
  return `${iso}${sign}${hh}:${mm}`;
963
963
  }
964
+ function combineSignals(signalA, signalB) {
965
+ if (!signalA && !signalB) return new AbortController().signal;
966
+ if (!signalA) return signalB;
967
+ if (!signalB) return signalA;
968
+ if (typeof AbortSignal.any === "function") {
969
+ return AbortSignal.any([signalA, signalB]);
970
+ }
971
+ if (signalA.aborted) return signalA;
972
+ if (signalB.aborted) return signalB;
973
+ const controller = new AbortController();
974
+ const onAbort = (ev) => {
975
+ const target = ev.target;
976
+ controller.abort(target.reason);
977
+ };
978
+ signalA.addEventListener("abort", onAbort, { once: true });
979
+ signalB.addEventListener("abort", onAbort, { once: true });
980
+ return controller.signal;
981
+ }
982
+ function withAbort(promise, signal) {
983
+ if (!signal) return promise;
984
+ if (signal.aborted) {
985
+ const error = signal.reason instanceof Error ? signal.reason : new DOMException(typeof signal.reason === "string" ? signal.reason : "Tool execution aborted.", "AbortError");
986
+ return Promise.reject(error);
987
+ }
988
+ return new Promise((resolve9, reject) => {
989
+ const onAbort = () => {
990
+ signal.removeEventListener("abort", onAbort);
991
+ const error = signal.reason instanceof Error ? signal.reason : new DOMException(typeof signal.reason === "string" ? signal.reason : "Tool execution aborted.", "AbortError");
992
+ reject(error);
993
+ };
994
+ signal.addEventListener("abort", onAbort, { once: true });
995
+ promise.then(
996
+ (val) => {
997
+ signal.removeEventListener("abort", onAbort);
998
+ resolve9(val);
999
+ },
1000
+ (err) => {
1001
+ signal.removeEventListener("abort", onAbort);
1002
+ reject(err);
1003
+ }
1004
+ );
1005
+ });
1006
+ }
964
1007
 
965
1008
  // src/loop/state.ts
966
1009
  function snapshotMessage(message) {
@@ -991,12 +1034,18 @@ function contentBlocksToMessageContent(blocks) {
991
1034
  if (block.type === "thinking") {
992
1035
  return { type: "thinking", thinking: block.thinking, ...block.signature ? { signature: block.signature } : {} };
993
1036
  }
994
- let input = block.args;
995
- try {
996
- input = JSON.parse(block.args);
997
- } catch {
1037
+ if (block.type === "image") {
1038
+ return { type: "image", data: block.data, mimeType: block.mimeType };
1039
+ }
1040
+ if (block.type === "tool_call") {
1041
+ let input = block.args;
1042
+ try {
1043
+ input = JSON.parse(block.args);
1044
+ } catch {
1045
+ }
1046
+ return { type: "tool_use", id: block.id, name: block.name, input };
998
1047
  }
999
- return { type: "tool_use", id: block.id, name: block.name, input };
1048
+ return { type: "text", text: JSON.stringify(block) };
1000
1049
  });
1001
1050
  }
1002
1051
 
@@ -1009,6 +1058,296 @@ function validateMaxAttempts(value) {
1009
1058
  return value;
1010
1059
  }
1011
1060
 
1061
+ // src/loop/partial-json.ts
1062
+ var COMPLETE_NUMBER_REGEX = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
1063
+ function escapeRawControlChars(s) {
1064
+ let result = "";
1065
+ for (let i = 0; i < s.length; i++) {
1066
+ const ch = s[i];
1067
+ const code = ch.charCodeAt(0);
1068
+ if (code === 10) result += "\\n";
1069
+ else if (code === 13) result += "\\r";
1070
+ else if (code === 9) result += "\\t";
1071
+ else if (code < 32) result += "\\u" + code.toString(16).padStart(4, "0");
1072
+ else result += ch;
1073
+ }
1074
+ return result;
1075
+ }
1076
+ function cleanOpenString(raw) {
1077
+ let s = raw;
1078
+ let backslashes = 0;
1079
+ while (s.length - 1 - backslashes >= 0 && s[s.length - 1 - backslashes] === "\\") {
1080
+ backslashes++;
1081
+ }
1082
+ if (backslashes % 2 === 1) {
1083
+ s = s.slice(0, -1);
1084
+ } else {
1085
+ s = s.replace(/(^|[^\\])(?:\\\\)*\\u[0-9a-fA-F]{0,3}$/, (match) => {
1086
+ const uIndex = match.lastIndexOf("\\u");
1087
+ return match.slice(0, uIndex);
1088
+ });
1089
+ }
1090
+ return escapeRawControlChars(s) + '"';
1091
+ }
1092
+ function repairPartialJson(input) {
1093
+ const stack = [];
1094
+ let i = 0;
1095
+ let hasTopLevelValue = false;
1096
+ let topLevelSafeEnd = 0;
1097
+ function finishWithSafeEnd(safeEnd) {
1098
+ let result = input.slice(0, safeEnd);
1099
+ for (let j = stack.length - 1; j >= 0; j--) {
1100
+ result += stack[j].kind === "object" ? "}" : "]";
1101
+ }
1102
+ return result;
1103
+ }
1104
+ function closeStackWithPrefix(prefix) {
1105
+ let result = prefix;
1106
+ for (let j = stack.length - 1; j >= 0; j--) {
1107
+ result += stack[j].kind === "object" ? "}" : "]";
1108
+ }
1109
+ return result;
1110
+ }
1111
+ function popContainer(endIndex) {
1112
+ stack.pop();
1113
+ const parent = stack[stack.length - 1];
1114
+ if (parent) {
1115
+ parent.safeEnd = endIndex;
1116
+ parent.state = "after_value";
1117
+ } else {
1118
+ hasTopLevelValue = true;
1119
+ topLevelSafeEnd = endIndex;
1120
+ }
1121
+ }
1122
+ while (i < input.length) {
1123
+ const ch = input[i];
1124
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
1125
+ i++;
1126
+ continue;
1127
+ }
1128
+ const current = stack[stack.length - 1];
1129
+ if (!current) {
1130
+ if (hasTopLevelValue) break;
1131
+ if (ch === "{") {
1132
+ stack.push({ kind: "object", safeEnd: i + 1, state: "expect_key" });
1133
+ i++;
1134
+ } else if (ch === "[") {
1135
+ stack.push({ kind: "array", safeEnd: i + 1, state: "expect_value" });
1136
+ i++;
1137
+ } else if (ch === '"') {
1138
+ const strStart = i;
1139
+ i++;
1140
+ let closed = false;
1141
+ while (i < input.length) {
1142
+ if (input[i] === "\\") {
1143
+ i += 2;
1144
+ } else if (input[i] === '"') {
1145
+ i++;
1146
+ closed = true;
1147
+ break;
1148
+ } else {
1149
+ i++;
1150
+ }
1151
+ }
1152
+ if (closed) {
1153
+ hasTopLevelValue = true;
1154
+ topLevelSafeEnd = i;
1155
+ } else {
1156
+ return cleanOpenString(input.slice(strStart));
1157
+ }
1158
+ } else {
1159
+ const tokStart = i;
1160
+ while (i < input.length && !/[\s,}\]]/.test(input[i])) {
1161
+ i++;
1162
+ }
1163
+ const tok = input.slice(tokStart, i);
1164
+ if (tok === "true" || tok === "false" || tok === "null" || COMPLETE_NUMBER_REGEX.test(tok)) {
1165
+ hasTopLevelValue = true;
1166
+ topLevelSafeEnd = i;
1167
+ } else {
1168
+ return void 0;
1169
+ }
1170
+ }
1171
+ continue;
1172
+ }
1173
+ if (current.kind === "object") {
1174
+ if (current.state === "expect_key") {
1175
+ if (ch === "}") {
1176
+ i++;
1177
+ popContainer(i);
1178
+ } else if (ch === '"') {
1179
+ i++;
1180
+ let closed = false;
1181
+ while (i < input.length) {
1182
+ if (input[i] === "\\") {
1183
+ i += 2;
1184
+ } else if (input[i] === '"') {
1185
+ i++;
1186
+ closed = true;
1187
+ break;
1188
+ } else {
1189
+ i++;
1190
+ }
1191
+ }
1192
+ if (closed) {
1193
+ current.state = "expect_colon";
1194
+ } else {
1195
+ return finishWithSafeEnd(current.safeEnd);
1196
+ }
1197
+ } else {
1198
+ return finishWithSafeEnd(current.safeEnd);
1199
+ }
1200
+ } else if (current.state === "expect_colon") {
1201
+ if (ch === ":") {
1202
+ current.state = "expect_value";
1203
+ i++;
1204
+ } else {
1205
+ return finishWithSafeEnd(current.safeEnd);
1206
+ }
1207
+ } else if (current.state === "expect_value") {
1208
+ if (ch === '"') {
1209
+ const valStart = i;
1210
+ i++;
1211
+ let closed = false;
1212
+ while (i < input.length) {
1213
+ if (input[i] === "\\") {
1214
+ i += 2;
1215
+ } else if (input[i] === '"') {
1216
+ i++;
1217
+ closed = true;
1218
+ break;
1219
+ } else {
1220
+ i++;
1221
+ }
1222
+ }
1223
+ if (closed) {
1224
+ current.safeEnd = i;
1225
+ current.state = "after_value";
1226
+ } else {
1227
+ const prefix = input.slice(0, valStart);
1228
+ const repairedStr = cleanOpenString(input.slice(valStart));
1229
+ return closeStackWithPrefix(prefix + repairedStr);
1230
+ }
1231
+ } else if (ch === "{") {
1232
+ stack.push({ kind: "object", safeEnd: i + 1, state: "expect_key" });
1233
+ i++;
1234
+ } else if (ch === "[") {
1235
+ stack.push({ kind: "array", safeEnd: i + 1, state: "expect_value" });
1236
+ i++;
1237
+ } else if (ch === "}" || ch === "]") {
1238
+ return finishWithSafeEnd(current.safeEnd);
1239
+ } else {
1240
+ const tokStart = i;
1241
+ while (i < input.length && !/[\s,}\]]/.test(input[i])) {
1242
+ i++;
1243
+ }
1244
+ const tok = input.slice(tokStart, i);
1245
+ if (tok === "true" || tok === "false" || tok === "null" || COMPLETE_NUMBER_REGEX.test(tok)) {
1246
+ current.safeEnd = i;
1247
+ current.state = "after_value";
1248
+ } else {
1249
+ return finishWithSafeEnd(current.safeEnd);
1250
+ }
1251
+ }
1252
+ } else if (current.state === "after_value") {
1253
+ if (ch === ",") {
1254
+ current.state = "expect_key";
1255
+ i++;
1256
+ } else if (ch === "}") {
1257
+ i++;
1258
+ popContainer(i);
1259
+ } else {
1260
+ return finishWithSafeEnd(current.safeEnd);
1261
+ }
1262
+ }
1263
+ } else {
1264
+ if (current.state === "expect_value") {
1265
+ if (ch === "]") {
1266
+ i++;
1267
+ popContainer(i);
1268
+ } else if (ch === '"') {
1269
+ const valStart = i;
1270
+ i++;
1271
+ let closed = false;
1272
+ while (i < input.length) {
1273
+ if (input[i] === "\\") {
1274
+ i += 2;
1275
+ } else if (input[i] === '"') {
1276
+ i++;
1277
+ closed = true;
1278
+ break;
1279
+ } else {
1280
+ i++;
1281
+ }
1282
+ }
1283
+ if (closed) {
1284
+ current.safeEnd = i;
1285
+ current.state = "after_value";
1286
+ } else {
1287
+ const prefix = input.slice(0, valStart);
1288
+ const repairedStr = cleanOpenString(input.slice(valStart));
1289
+ return closeStackWithPrefix(prefix + repairedStr);
1290
+ }
1291
+ } else if (ch === "{") {
1292
+ stack.push({ kind: "object", safeEnd: i + 1, state: "expect_key" });
1293
+ i++;
1294
+ } else if (ch === "[") {
1295
+ stack.push({ kind: "array", safeEnd: i + 1, state: "expect_value" });
1296
+ i++;
1297
+ } else if (ch === "}") {
1298
+ return finishWithSafeEnd(current.safeEnd);
1299
+ } else {
1300
+ const tokStart = i;
1301
+ while (i < input.length && !/[\s,}\]]/.test(input[i])) {
1302
+ i++;
1303
+ }
1304
+ const tok = input.slice(tokStart, i);
1305
+ if (tok === "true" || tok === "false" || tok === "null" || COMPLETE_NUMBER_REGEX.test(tok)) {
1306
+ current.safeEnd = i;
1307
+ current.state = "after_value";
1308
+ } else {
1309
+ return finishWithSafeEnd(current.safeEnd);
1310
+ }
1311
+ }
1312
+ } else if (current.state === "after_value") {
1313
+ if (ch === ",") {
1314
+ current.state = "expect_value";
1315
+ i++;
1316
+ } else if (ch === "]") {
1317
+ i++;
1318
+ popContainer(i);
1319
+ } else {
1320
+ return finishWithSafeEnd(current.safeEnd);
1321
+ }
1322
+ }
1323
+ }
1324
+ }
1325
+ if (stack.length > 0) {
1326
+ const current = stack[stack.length - 1];
1327
+ return finishWithSafeEnd(current.safeEnd);
1328
+ }
1329
+ if (hasTopLevelValue) {
1330
+ return input.slice(0, topLevelSafeEnd);
1331
+ }
1332
+ return void 0;
1333
+ }
1334
+ function parsePartialJson(input) {
1335
+ if (typeof input !== "string") return void 0;
1336
+ if (!input.trim()) return void 0;
1337
+ const source = input.trimStart();
1338
+ try {
1339
+ return JSON.parse(source);
1340
+ } catch {
1341
+ }
1342
+ const repaired = repairPartialJson(source);
1343
+ if (repaired === void 0) return void 0;
1344
+ try {
1345
+ return JSON.parse(repaired);
1346
+ } catch {
1347
+ return void 0;
1348
+ }
1349
+ }
1350
+
1012
1351
  // src/loop/errors.ts
1013
1352
  var EmptyResponseError = class extends Error {
1014
1353
  code = "empty_response";
@@ -1039,14 +1378,50 @@ var LoopGuard = {
1039
1378
  // src/loop/stream-collector.ts
1040
1379
  var MAX_STREAMED_OUTPUT_CHARACTERS = 1024 * 1024;
1041
1380
  async function invokeModel(input) {
1042
- const events = input.config.stream(input.request, { signal: input.config.signal });
1381
+ const callContext = input.callContext ?? {
1382
+ signal: input.config.signal ?? new AbortController().signal,
1383
+ run: {
1384
+ id: input.config.execution.runId,
1385
+ agentId: input.config.execution.agentId,
1386
+ scope: input.config.execution.scope ?? [],
1387
+ cwd: input.config.execution.cwd ?? input.config.context.cwd ?? ""
1388
+ },
1389
+ interact: async (request) => {
1390
+ const handler = input.config.runtime?.interact ?? input.config.interact;
1391
+ if (!handler) {
1392
+ throw new Error("Run interactions are not supported in this runtime.");
1393
+ }
1394
+ return handler(request);
1395
+ },
1396
+ tools: {
1397
+ list: () => {
1398
+ return input.config.context.tools.map((t) => ({
1399
+ name: t.name,
1400
+ description: t.description,
1401
+ parameters: t.parameters,
1402
+ ...t._meta !== void 0 ? { _meta: t._meta } : {}
1403
+ }));
1404
+ },
1405
+ call: async (_name, _args, _options) => {
1406
+ throw new Error("Tool calling is not available in standalone stream collector without callContext.");
1407
+ },
1408
+ report: (update) => {
1409
+ input.config.onToolReport?.({
1410
+ runId: input.config.execution.runId,
1411
+ update
1412
+ });
1413
+ }
1414
+ }
1415
+ };
1416
+ const events = input.config.stream(input.request, callContext);
1043
1417
  const result = await collectStreamResult({
1044
1418
  config: input.config,
1045
1419
  message: input.message,
1046
1420
  events,
1047
1421
  metadataPhase: input.phaseId,
1048
1422
  output: input.output,
1049
- onThinkingDelta: input.onThinkingDelta
1423
+ onThinkingDelta: input.onThinkingDelta,
1424
+ onToolCallDelta: input.onToolCallDelta
1050
1425
  });
1051
1426
  const response = result.doneResponse ?? {
1052
1427
  content: result.text,
@@ -1077,6 +1452,8 @@ async function collectStreamResult(input) {
1077
1452
  let streamedOutputCharacters = 0;
1078
1453
  const toolCallsWithDeltas = /* @__PURE__ */ new Set();
1079
1454
  const thinkingOffsets = /* @__PURE__ */ new Map();
1455
+ const toolNames = /* @__PURE__ */ new Map();
1456
+ const toolArguments = /* @__PURE__ */ new Map();
1080
1457
  for await (const event of input.events) {
1081
1458
  if (event.type === "text_delta") {
1082
1459
  streamedOutputCharacters += event.text.length;
@@ -1127,6 +1504,26 @@ async function collectStreamResult(input) {
1127
1504
  }
1128
1505
  lastPartial = event.partial;
1129
1506
  }
1507
+ if (event.type === "tool_call_start") {
1508
+ toolNames.set(event.id, event.name);
1509
+ }
1510
+ if (event.type === "tool_call_delta") {
1511
+ const accumulated = (toolArguments.get(event.id) ?? "") + event.arguments;
1512
+ toolArguments.set(event.id, accumulated);
1513
+ const toolName = toolNames.get(event.id) ?? event.partial.contentBlocks.find((b) => b.type === "tool_call" && b.id === event.id)?.name ?? "";
1514
+ if (toolName && !toolNames.has(event.id)) {
1515
+ toolNames.set(event.id, toolName);
1516
+ }
1517
+ if (persistAssistant && activeMessageId) {
1518
+ input.onToolCallDelta?.({
1519
+ messageId: activeMessageId,
1520
+ providerToolCallId: event.id,
1521
+ toolName,
1522
+ arguments: accumulated,
1523
+ args: parsePartialJson(accumulated)
1524
+ });
1525
+ }
1526
+ }
1130
1527
  if (event.type === "thinking_delta") {
1131
1528
  if (persistAssistant && !activeMessageId) {
1132
1529
  activeMessageId = input.message.reserve("assistant", {
@@ -1239,16 +1636,17 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1239
1636
  let outputPayload = phaseIn?.state?.payload;
1240
1637
  let nextPhase;
1241
1638
  let outputMessage;
1242
- let settingsProvider;
1243
1639
  return {
1244
- on: (eventType, handler) => {
1245
- assertActive();
1246
- hooks?.on(eventType, handler);
1247
- trackCleanup?.(() => hooks?.off(eventType, handler));
1248
- },
1249
- off: (eventType, handler) => {
1250
- assertActive();
1251
- hooks?.off(eventType, handler);
1640
+ hooks: {
1641
+ on: (eventType, handler) => {
1642
+ assertActive();
1643
+ hooks?.on(eventType, handler);
1644
+ trackCleanup?.(() => hooks?.off(eventType, handler));
1645
+ },
1646
+ off: (eventType, handler) => {
1647
+ assertActive();
1648
+ hooks?.off(eventType, handler);
1649
+ }
1252
1650
  },
1253
1651
  config: {
1254
1652
  get: async (scope) => {
@@ -1298,9 +1696,28 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1298
1696
  return store?.delete(key);
1299
1697
  }
1300
1698
  };
1699
+ },
1700
+ global: () => {
1701
+ assertActive();
1702
+ const store = options?.state?.global();
1703
+ return {
1704
+ get: async (key) => {
1705
+ assertActive();
1706
+ return store?.get(key);
1707
+ },
1708
+ set: async (key, value) => {
1709
+ assertActive();
1710
+ assertJsonValue(value);
1711
+ return store?.set(key, value);
1712
+ },
1713
+ delete: async (key) => {
1714
+ assertActive();
1715
+ return store?.delete(key);
1716
+ }
1717
+ };
1301
1718
  }
1302
1719
  },
1303
- tool: {
1720
+ tools: {
1304
1721
  register: (tool) => {
1305
1722
  assertActive();
1306
1723
  options?.registerTool?.(tool);
@@ -1329,14 +1746,16 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1329
1746
  };
1330
1747
  }
1331
1748
  },
1332
- registerProvider: (config) => {
1333
- assertActive();
1334
- options?.registerProvider?.(config);
1335
- trackCleanup?.(() => options?.unregisterProvider?.(config.id));
1336
- },
1337
- unregisterProvider: (name) => {
1338
- assertActive();
1339
- options?.unregisterProvider?.(name);
1749
+ providers: {
1750
+ register: (config) => {
1751
+ assertActive();
1752
+ options?.registerProvider?.(config);
1753
+ trackCleanup?.(() => options?.unregisterProvider?.(config.id));
1754
+ },
1755
+ unregister: (id) => {
1756
+ assertActive();
1757
+ options?.unregisterProvider?.(id);
1758
+ }
1340
1759
  },
1341
1760
  manifest: options?.manifest,
1342
1761
  utils: {
@@ -1358,7 +1777,7 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1358
1777
  }, off: () => {
1359
1778
  }, emit: () => {
1360
1779
  }, has: () => false, count: () => 0 },
1361
- phase: {
1780
+ phases: {
1362
1781
  register: async (registration) => {
1363
1782
  assertActive();
1364
1783
  await options?.registerPhase?.(registration);
@@ -1379,19 +1798,37 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1379
1798
  nextPhase = id;
1380
1799
  },
1381
1800
  getNextPhase: () => nextPhase,
1382
- getMessage: () => outputMessage,
1383
- settings: {
1384
- register: (provider) => {
1385
- assertActive();
1386
- if (typeof provider !== "function") {
1387
- throw new Error("Phase Settings registration requires a provider function.");
1801
+ getMessage: () => outputMessage
1802
+ },
1803
+ ui: {
1804
+ contribute: (contribution) => {
1805
+ assertActive();
1806
+ if (!contribution || typeof contribution !== "object") {
1807
+ throw new TypeError("UI contribution must be an object.");
1808
+ }
1809
+ if (contribution.slot !== "settings" && contribution.slot !== "model-picker") {
1810
+ throw new TypeError(`Unsupported UI contribution slot: "${contribution.slot}".`);
1811
+ }
1812
+ if (typeof contribution.id !== "string" || contribution.id.trim() === "") {
1813
+ throw new TypeError("UI contribution requires a non-empty id.");
1814
+ }
1815
+ if (contribution.slot === "settings") {
1816
+ if (typeof contribution.title !== "string") {
1817
+ throw new TypeError("Settings UI contribution requires a title.");
1388
1818
  }
1389
- if (settingsProvider) {
1390
- throw new Error("A Phase may register only one Settings provider.");
1819
+ if (!contribution.settings || typeof contribution.settings !== "object") {
1820
+ throw new TypeError("Settings UI contribution requires a settings definition.");
1821
+ }
1822
+ }
1823
+ if (contribution.slot === "model-picker") {
1824
+ if (typeof contribution.provider !== "string" || contribution.provider.trim() === "") {
1825
+ throw new TypeError("Model-picker UI contribution requires a provider.");
1391
1826
  }
1392
- settingsProvider = provider;
1393
- options?.registerSettings?.(provider);
1394
1827
  }
1828
+ const dispose = options?.contributeUi ? options.contributeUi(contribution) : () => {
1829
+ };
1830
+ trackCleanup?.(dispose);
1831
+ return dispose;
1395
1832
  }
1396
1833
  }
1397
1834
  };
@@ -1400,8 +1837,8 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1400
1837
  // src/harness/phases/interactions.ts
1401
1838
  var RunInteractionCancelledError = class extends Error {
1402
1839
  code = "run_interaction_cancelled";
1403
- constructor() {
1404
- super("Run interaction work was cancelled.");
1840
+ constructor(message = "Run interaction work was cancelled.") {
1841
+ super(message);
1405
1842
  this.name = "RunInteractionCancelledError";
1406
1843
  }
1407
1844
  };
@@ -1872,17 +2309,6 @@ async function loadPhase(targetPath) {
1872
2309
  }
1873
2310
  return phase;
1874
2311
  }
1875
- async function loadPhaseSettings(phase, context) {
1876
- if (!phase.factory) return void 0;
1877
- let provider;
1878
- const api = createExtensionAPI(void 0, {
1879
- registerSettings: (candidate) => {
1880
- provider = candidate;
1881
- }
1882
- });
1883
- await phase.factory(api);
1884
- return provider ? provider(context) : void 0;
1885
- }
1886
2312
  async function loadPhaseSkills(baseDir) {
1887
2313
  const entries = await readdir2(baseDir, { withFileTypes: true });
1888
2314
  const skills = [];
@@ -2324,9 +2750,11 @@ function extractToolResults(transcript) {
2324
2750
  }
2325
2751
  for (const part of msg.content) {
2326
2752
  if (part.type !== "tool_result") continue;
2327
- const result = parseToolResult(part.content);
2328
- if (result) {
2329
- results.push(result);
2753
+ if (typeof part.content === "string") {
2754
+ const result = parseToolResult(part.content);
2755
+ if (result) {
2756
+ results.push(result);
2757
+ }
2330
2758
  }
2331
2759
  }
2332
2760
  }
@@ -3069,9 +3497,8 @@ async function runPhaseLoop(config, state, registry) {
3069
3497
  }
3070
3498
  throw new Error("Phase machine exited without a stop or abort transition.");
3071
3499
  }
3072
- var DEFAULT_MAX_RETRIES = 3;
3073
- var DEFAULT_BASE_DELAY_MS = 1e3;
3074
- var DEFAULT_MAX_DELAY_MS = 3e4;
3500
+ var DEFAULT_MAX_RETRIES = 10;
3501
+ var DEFAULT_RETRY_DELAY_MS = 5e3;
3075
3502
  function isRetryableError(error) {
3076
3503
  if (!(error instanceof Error)) return false;
3077
3504
  const message = error.message.toLowerCase();
@@ -3082,21 +3509,36 @@ function isRetryableError(error) {
3082
3509
  if (message.includes("service unavailable") || message.includes("503")) return true;
3083
3510
  if (message.includes("gateway timeout") || message.includes("504")) return true;
3084
3511
  if (message.includes("econnreset") || message.includes("econnrefused")) return true;
3512
+ if (message.includes("socket connection was closed") || message.includes("socket hang up")) return true;
3513
+ if (message.includes("fetch failed") || message.includes("etimedout")) return true;
3514
+ if (message.includes("network") || message.includes("connection reset")) return true;
3085
3515
  if ("code" in error) {
3086
3516
  const code = error.code;
3087
3517
  if (code === "invalid_model_schema" || code === "empty_response") return true;
3088
3518
  }
3089
3519
  return false;
3090
3520
  }
3091
- function getRetryDelay(attempt, baseMs, maxMs) {
3092
- const exponential = baseMs * Math.pow(2, attempt);
3093
- const jitter = exponential * (0.5 + Math.random() * 0.5);
3094
- return Math.min(jitter, maxMs);
3521
+ function sleepAbortable(delayMs, signal) {
3522
+ return new Promise((resolve9) => {
3523
+ if (signal?.aborted) {
3524
+ resolve9();
3525
+ return;
3526
+ }
3527
+ const onAbort = () => {
3528
+ clearTimeout(timer);
3529
+ signal?.removeEventListener("abort", onAbort);
3530
+ resolve9();
3531
+ };
3532
+ const timer = setTimeout(() => {
3533
+ signal?.removeEventListener("abort", onAbort);
3534
+ resolve9();
3535
+ }, delayMs);
3536
+ signal?.addEventListener("abort", onAbort, { once: true });
3537
+ });
3095
3538
  }
3096
3539
  async function withRetry(fn, options = {}) {
3097
3540
  const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
3098
- const baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
3099
- const maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
3541
+ const retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
3100
3542
  let lastError;
3101
3543
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
3102
3544
  try {
@@ -3109,9 +3551,9 @@ async function withRetry(fn, options = {}) {
3109
3551
  if (options.signal?.aborted) {
3110
3552
  throw error;
3111
3553
  }
3112
- const delayMs = getRetryDelay(attempt, baseDelayMs, maxDelayMs);
3113
- options.onRetry?.(attempt, error, delayMs);
3114
- await new Promise((resolve9) => setTimeout(resolve9, delayMs));
3554
+ const retryAttempt = attempt + 1;
3555
+ options.onRetry?.(retryAttempt, error, retryDelayMs, maxRetries);
3556
+ await sleepAbortable(retryDelayMs, options.signal);
3115
3557
  if (options.signal?.aborted) {
3116
3558
  throw lastError;
3117
3559
  }
@@ -3161,10 +3603,10 @@ async function executePhase(ctx) {
3161
3603
  });
3162
3604
  await phase.factory(api);
3163
3605
  return {
3164
- message: api.phase.getMessage() ?? `${phase.name} phase completed.`,
3165
- route: api.phase.getNextPhase() || "stop",
3606
+ message: api.phases.getMessage() ?? `${phase.name} phase completed.`,
3607
+ route: api.phases.getNextPhase() || "stop",
3166
3608
  phase: phase.name,
3167
- payload: api.phase.getPayload()
3609
+ payload: api.phases.getPayload()
3168
3610
  };
3169
3611
  }
3170
3612
  return executePhaseWithModel(ctx);
@@ -3176,7 +3618,8 @@ async function executeToolCall(input) {
3176
3618
  config: input.config,
3177
3619
  toolCall: input.toolCall,
3178
3620
  contentBlocks: input.contentBlocks,
3179
- driver: input.driver
3621
+ driver: input.driver,
3622
+ options: input.options
3180
3623
  });
3181
3624
  } else {
3182
3625
  const toolContext = {
@@ -3189,7 +3632,7 @@ async function executeToolCall(input) {
3189
3632
  toolContext,
3190
3633
  beforeToolCall: input.config.beforeToolCall,
3191
3634
  afterToolCall: input.config.afterToolCall,
3192
- signal: input.config.signal
3635
+ signal: combineSignals(input.config.signal, input.options?.signal)
3193
3636
  });
3194
3637
  }
3195
3638
  return {
@@ -3221,6 +3664,28 @@ async function executeToolCalls(input) {
3221
3664
  }
3222
3665
  function createPhaseExecution(config, state, phase, messageManager, toolExecutionManager, registry) {
3223
3666
  const interaction = createRunInteractionDriver(state, phase.name, config.signal);
3667
+ const executePhaseTool = async (phaseContext, toolCall, options) => {
3668
+ return runTurn(async () => {
3669
+ await toolExecutionManager.start(toolCall.id, toolCall.name, toolCall.args);
3670
+ const tools = phaseContext.tools.filter((tool) => tool.name !== PhaseRouteTool);
3671
+ const result = await executeToolCall({
3672
+ config: {
3673
+ ...config,
3674
+ context: {
3675
+ ...config.context,
3676
+ tools,
3677
+ skills: phaseContext.skills
3678
+ }
3679
+ },
3680
+ tools,
3681
+ toolCall,
3682
+ driver: interaction,
3683
+ options
3684
+ });
3685
+ await toolExecutionManager.end(result.toolCallId, result.toolName, result, !result.ok);
3686
+ return result;
3687
+ });
3688
+ };
3224
3689
  return {
3225
3690
  interaction,
3226
3691
  messages: messageManager,
@@ -3264,6 +3729,51 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3264
3729
  }));
3265
3730
  }
3266
3731
  }
3732
+ const scope = config.execution.scope ?? [];
3733
+ const cwd = config.execution.cwd ?? config.context.cwd ?? "";
3734
+ const callContext = {
3735
+ signal: config.signal ?? new AbortController().signal,
3736
+ run: {
3737
+ id: config.execution.runId,
3738
+ agentId: config.execution.agentId,
3739
+ scope,
3740
+ cwd
3741
+ },
3742
+ interact: async (interactionReq) => {
3743
+ const handler = config.runtime?.interact ?? config.interact;
3744
+ if (!handler) {
3745
+ throw new Error("Run interactions are not supported in this runtime.");
3746
+ }
3747
+ return handler(interactionReq);
3748
+ },
3749
+ tools: {
3750
+ list: () => {
3751
+ const tools = phaseContext.tools.filter((tool) => tool.name !== PhaseRouteTool);
3752
+ return tools.map((t) => ({
3753
+ name: t.name,
3754
+ description: t.description,
3755
+ parameters: t.parameters,
3756
+ ...t._meta !== void 0 ? { _meta: t._meta } : {}
3757
+ }));
3758
+ },
3759
+ call: async (name, args, options2) => {
3760
+ const toolCallId = createId("call");
3761
+ const toolCall = {
3762
+ id: toolCallId,
3763
+ name,
3764
+ args
3765
+ };
3766
+ const res = await executePhaseTool(phaseContext, toolCall, options2);
3767
+ return res.ok ? { ok: true, content: res.content } : { ok: false, error: res.error ?? "Tool execution failed", ...res.content !== void 0 ? { content: res.content } : {} };
3768
+ },
3769
+ report: (update) => {
3770
+ config.onToolReport?.({
3771
+ runId: config.execution.runId,
3772
+ update
3773
+ });
3774
+ }
3775
+ }
3776
+ };
3267
3777
  const result = await runTurn(
3268
3778
  () => withRetry(
3269
3779
  () => invokeModel({
@@ -3272,12 +3782,21 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3272
3782
  request,
3273
3783
  phaseId: phase.name,
3274
3784
  output: options.output,
3275
- onThinkingDelta: config.onThinkingDelta
3785
+ onThinkingDelta: config.onThinkingDelta,
3786
+ onToolCallDelta: config.onToolCallDelta,
3787
+ callContext
3276
3788
  }),
3277
3789
  {
3278
3790
  signal: config.signal,
3279
- onRetry: () => {
3791
+ retryDelayMs: config.retryDelayMs,
3792
+ onRetry: (attempt, error, delayMs, maxRetries) => {
3280
3793
  state.metrics.retryCount++;
3794
+ config.onModelRetry?.({
3795
+ attempt,
3796
+ maxRetries,
3797
+ delayMs,
3798
+ error: error instanceof Error ? error.message : String(error)
3799
+ });
3281
3800
  }
3282
3801
  }
3283
3802
  )
@@ -3288,27 +3807,7 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3288
3807
  async reportStatus(status) {
3289
3808
  await config.onPhaseStatus?.(phase.name, status);
3290
3809
  },
3291
- async executeTool(phaseContext, toolCall) {
3292
- return runTurn(async () => {
3293
- await toolExecutionManager.start(toolCall.id, toolCall.name, toolCall.args);
3294
- const tools = phaseContext.tools.filter((tool) => tool.name !== PhaseRouteTool);
3295
- const result = await executeToolCall({
3296
- config: {
3297
- ...config,
3298
- context: {
3299
- ...config.context,
3300
- tools,
3301
- skills: phaseContext.skills
3302
- }
3303
- },
3304
- tools,
3305
- toolCall,
3306
- driver: interaction
3307
- });
3308
- await toolExecutionManager.end(result.toolCallId, result.toolName, result, !result.ok);
3309
- return result;
3310
- });
3311
- },
3810
+ executeTool: executePhaseTool,
3312
3811
  async executeTools(phaseContext, toolCalls, contentBlocks) {
3313
3812
  return runTurn(async () => {
3314
3813
  const tools = phaseContext.tools.filter((tool) => tool.name !== PhaseRouteTool);
@@ -3571,10 +4070,14 @@ async function executeOnce(input) {
3571
4070
  onParallelPhaseOutput: input.onParallelPhaseOutput,
3572
4071
  onMessageDelta: input.onMessageDelta,
3573
4072
  onThinkingDelta: input.onThinkingDelta,
4073
+ onToolCallDelta: input.onToolCallDelta,
4074
+ onModelRetry: input.onModelRetry,
4075
+ retryDelayMs: input.retryDelayMs,
3574
4076
  onOutcome: input.onOutcome,
3575
4077
  onModelTranscript: input.onModelTranscript,
3576
4078
  onPhaseEntered: input.onPhaseEntered,
3577
4079
  onPhaseStatus: input.onPhaseStatus,
4080
+ onToolReport: input.onToolReport,
3578
4081
  runtime: input.runtime,
3579
4082
  waitForInput: async (nextState, request) => {
3580
4083
  if (!nextState || !request) throw new Error("Input boundary did not include execution state.");
@@ -3665,6 +4168,46 @@ function isContinuation(value) {
3665
4168
  return value.previousResults.every((result) => isRecord2(result) && typeof result.name === "string" && (result.output === void 0 || isJsonValue(result.output))) && (value.pendingInstruction === void 0 || typeof value.pendingInstruction === "string") && (value.previousPhaseMessageId === void 0 || typeof value.previousPhaseMessageId === "string");
3666
4169
  }
3667
4170
 
4171
+ // src/runtime/tool-call-merge.ts
4172
+ function isValidPresentationOutput(output) {
4173
+ if (typeof output !== "object" || output === null || Array.isArray(output)) return false;
4174
+ const o = output;
4175
+ if (o.title !== void 0 && typeof o.title !== "string") return false;
4176
+ if (o.locations !== void 0) {
4177
+ if (!Array.isArray(o.locations)) return false;
4178
+ for (const loc of o.locations) {
4179
+ if (typeof loc !== "object" || loc === null || typeof loc.path !== "string") return false;
4180
+ if (loc.line !== void 0 && typeof loc.line !== "number") return false;
4181
+ }
4182
+ }
4183
+ if (o.content !== void 0) {
4184
+ if (!Array.isArray(o.content)) return false;
4185
+ for (const c of o.content) {
4186
+ if (typeof c !== "object" || c === null || typeof c.type !== "string") return false;
4187
+ }
4188
+ }
4189
+ if (o._meta !== void 0) {
4190
+ if (typeof o._meta !== "object" || o._meta === null || Array.isArray(o._meta)) return false;
4191
+ }
4192
+ return true;
4193
+ }
4194
+ function mergeToolCall(target, patch) {
4195
+ if (patch.title !== void 0) target.title = patch.title;
4196
+ if (patch.kind !== void 0) target.kind = patch.kind;
4197
+ if (patch.status !== void 0) target.status = patch.status;
4198
+ if (patch.locations !== void 0) target.locations = structuredClone(patch.locations);
4199
+ if (patch.content !== void 0) target.content = structuredClone(patch.content);
4200
+ if (patch.rawInput !== void 0) target.rawInput = structuredClone(patch.rawInput);
4201
+ if (patch.rawOutput !== void 0) target.rawOutput = structuredClone(patch.rawOutput);
4202
+ if (patch._meta !== void 0) {
4203
+ target._meta = {
4204
+ ...target._meta ?? {},
4205
+ ...structuredClone(patch._meta)
4206
+ };
4207
+ }
4208
+ return target;
4209
+ }
4210
+
3668
4211
  // src/runtime/contracts.ts
3669
4212
  var THINKING_LEVELS = [
3670
4213
  "off",
@@ -3675,23 +4218,8 @@ var THINKING_LEVELS = [
3675
4218
  "xhigh",
3676
4219
  "max"
3677
4220
  ];
3678
- function thinkingLevelFromMetadata(metadata) {
3679
- const record = isRecord3(metadata) ? metadata : void 0;
3680
- const mori = record?.mori;
3681
- if (!isRecord3(mori)) return void 0;
3682
- const level = mori.thinkingLevel;
3683
- return typeof level === "string" && THINKING_LEVELS.includes(level) ? level : void 0;
3684
- }
3685
- function thinkingLevelFromMessages(messages) {
3686
- for (let index = messages.length - 1; index >= 0; index -= 1) {
3687
- const message = messages[index];
3688
- if (message?.role !== "user") continue;
3689
- const metadata = message.metadata;
3690
- const kind = isRecord3(metadata) && typeof metadata.kind === "string" ? metadata.kind : void 0;
3691
- if (kind === "phase_prompt" || kind === "phase_input") continue;
3692
- return thinkingLevelFromMetadata(metadata);
3693
- }
3694
- return void 0;
4221
+ function thinkingLevelFromUserInput(input) {
4222
+ return typeof input === "string" ? void 0 : input.thinkingLevel;
3695
4223
  }
3696
4224
  var METADATA_LIMIT = 64 * 1024;
3697
4225
  var IDENTITY_LIMIT = 256;
@@ -3747,8 +4275,13 @@ function isAssistantContent(value) {
3747
4275
  }
3748
4276
  function normalizeUserInput(input) {
3749
4277
  const normalized = typeof input === "string" ? { content: input } : input;
3750
- if (!isRecord3(normalized) || !hasOnlyKeys(normalized, ["content", "metadata"]) || !isUserContent(normalized.content)) throw new TypeError("input must contain only valid UserContent");
4278
+ if (!isRecord3(normalized) || !hasOnlyKeys(normalized, ["content", "metadata", "thinkingLevel"]) || !isUserContent(normalized.content)) {
4279
+ throw new TypeError("input must contain only valid UserContent");
4280
+ }
3751
4281
  if (normalized.metadata !== void 0) assertMetadata(normalized.metadata, "input.metadata");
4282
+ if (normalized.thinkingLevel !== void 0 && !THINKING_LEVELS.includes(normalized.thinkingLevel)) {
4283
+ throw new TypeError("input.thinkingLevel must be a valid ThinkingLevel");
4284
+ }
3752
4285
  assertJsonValue(normalized, "input");
3753
4286
  return normalized;
3754
4287
  }
@@ -3756,9 +4289,16 @@ function isAssistantMessage(value) {
3756
4289
  return isRecord3(value) && hasOnlyKeys(value, ["id", "agentId", "runId", "messageRevision", "role", "content", "metadata", "sequenceWithinRun", "createdAt", "interrupted"]) && typeof value.id === "string" && typeof value.agentId === "string" && typeof value.runId === "string" && value.role === "assistant" && (value.messageRevision === void 0 || Number.isInteger(value.messageRevision) && value.messageRevision >= 0) && Number.isInteger(value.sequenceWithinRun) && value.sequenceWithinRun >= 0 && typeof value.createdAt === "string" && isAssistantContent(value.content) && (value.metadata === void 0 || isMetadata(value.metadata)) && (value.interrupted === void 0 || typeof value.interrupted === "boolean");
3757
4290
  }
3758
4291
  function isToolResult(value) {
3759
- if (!isRecord3(value) || !isJsonValue(value.content) || typeof value.ok !== "boolean") return false;
4292
+ if (!isRecord3(value)) return false;
4293
+ if (value.structuredContent !== void 0 && !isJsonValue(value.structuredContent)) return false;
4294
+ if (Array.isArray(value.content)) {
4295
+ if (value.isError !== void 0 && typeof value.isError !== "boolean") return false;
4296
+ return true;
4297
+ }
4298
+ if (!isJsonValue(value.content) || typeof value.ok !== "boolean") return false;
4299
+ const keys = Object.keys(value).filter((k) => k !== "structuredContent").sort();
3760
4300
  const expected = value.ok ? ["content", "ok"] : ["content", "error", "ok"];
3761
- if (JSON.stringify(Object.keys(value).sort()) !== JSON.stringify(expected)) return false;
4301
+ if (JSON.stringify(keys) !== JSON.stringify(expected)) return false;
3762
4302
  return value.ok || typeof value.error === "string";
3763
4303
  }
3764
4304
  function assertAgentConfiguration(config) {
@@ -3931,17 +4471,20 @@ function encodeIdempotencyScope(storeIncarnation, scope) {
3931
4471
  if (typeof storeIncarnation !== "string" || storeIncarnation.length === 0) throw new TypeError("storeIncarnation must be non-empty");
3932
4472
  return canonicalJson([storeIncarnation, ...scope]);
3933
4473
  }
3934
- function canonicalStartRunRequest(input, metadata, phasePayload, pinnedConfigToken, entryPhases) {
4474
+ function canonicalStartRunRequest(input, metadata, phasePayload, pinnedConfigToken, entryPhases, scope, cwd) {
3935
4475
  const normalized = normalizeUserInput(input);
3936
4476
  if (metadata !== void 0) assertUtf8ByteLimit(canonicalJson(metadata), METADATA_JSON_BYTES, "run.metadata");
3937
4477
  if (phasePayload !== void 0) assertUtf8ByteLimit(canonicalJson(phasePayload), MESSAGE_CONTENT_JSON_BYTES, "run.phasePayload");
3938
4478
  if (entryPhases !== void 0) assertUtf8ByteLimit(canonicalJson(entryPhases), MESSAGE_CONTENT_JSON_BYTES, "run.entryPhases");
4479
+ if (cwd !== void 0 && typeof cwd !== "string") throw new TypeError("run.cwd must be a string");
3939
4480
  return canonicalJson({
3940
4481
  input: normalized,
3941
4482
  ...metadata === void 0 ? {} : { metadata },
3942
4483
  ...phasePayload === void 0 ? {} : { phasePayload },
3943
4484
  ...entryPhases === void 0 ? {} : { entryPhases },
3944
- ...pinnedConfigToken === void 0 ? {} : { pinnedConfigToken }
4485
+ ...pinnedConfigToken === void 0 ? {} : { pinnedConfigToken },
4486
+ ...scope === void 0 ? {} : { scope },
4487
+ ...cwd === void 0 ? {} : { cwd }
3945
4488
  });
3946
4489
  }
3947
4490
 
@@ -4483,7 +5026,8 @@ function projectToolMessage(message, toolInteractionTexts) {
4483
5026
  id: message.id,
4484
5027
  role: message.role,
4485
5028
  content: message.content.map((part) => {
4486
- const rawResult = jsonText(part.result.content);
5029
+ const isError = "isError" in part.result ? Boolean(part.result.isError) : !part.result.ok;
5030
+ const rawResult = Array.isArray(part.result.content) ? part.result.content.map((block) => block?.type === "text" ? block.text : JSON.stringify(block)).join("\n\n") : jsonText(part.result.content);
4487
5031
  const interactionTexts = toolInteractionTexts.get(String(part.toolCallId));
4488
5032
  let content = rawResult;
4489
5033
  if (interactionTexts && interactionTexts.length > 0) {
@@ -4496,7 +5040,7 @@ ${rawResult}`;
4496
5040
  type: "tool_result",
4497
5041
  toolUseId: part.providerToolCallId ?? String(part.toolCallId),
4498
5042
  content,
4499
- ...part.result.ok ? {} : { isError: true }
5043
+ ...isError ? { isError: true } : {}
4500
5044
  };
4501
5045
  }),
4502
5046
  createdAt: message.createdAt,
@@ -4528,12 +5072,16 @@ function projectAssistantContent(content) {
4528
5072
  });
4529
5073
  }
4530
5074
  function projectToolContent(content) {
4531
- return content.map((part) => ({
4532
- type: "tool_result",
4533
- toolUseId: part.providerToolCallId ?? part.toolCallId,
4534
- content: jsonText(part.result.content),
4535
- ...part.result.ok ? {} : { isError: true }
4536
- }));
5075
+ return content.map((part) => {
5076
+ const isError = "isError" in part.result ? Boolean(part.result.isError) : !part.result.ok;
5077
+ const textContent = Array.isArray(part.result.content) ? part.result.content.map((block) => block?.type === "text" ? block.text : JSON.stringify(block)).join("\n\n") : jsonText(part.result.content);
5078
+ return {
5079
+ type: "tool_result",
5080
+ toolUseId: part.providerToolCallId ?? part.toolCallId,
5081
+ content: textContent,
5082
+ ...isError ? { isError: true } : {}
5083
+ };
5084
+ });
4537
5085
  }
4538
5086
  function inertRunInteraction(signal) {
4539
5087
  const controller = signal ? void 0 : new AbortController();
@@ -4562,15 +5110,18 @@ function projectTool(tool, agentId, runId) {
4562
5110
  parameters: tool.parameters,
4563
5111
  ...tool.promptSnippet ? { promptSnippet: tool.promptSnippet } : {},
4564
5112
  ...tool.promptGuidelines ? { promptGuidelines: [...tool.promptGuidelines] } : {},
5113
+ ...tool._meta !== void 0 ? { _meta: tool._meta } : {},
4565
5114
  execute: async (args, context, signal) => {
4566
5115
  const result = await tool.execute(args, {
4567
5116
  agentId,
4568
5117
  runId,
4569
5118
  toolCallId: context.toolCallId,
5119
+ providerToolCallId: context.toolCallId,
4570
5120
  reportProgress: () => void 0,
4571
5121
  interaction: context.interaction ?? inertRunInteraction(signal)
4572
5122
  }, signal ?? new AbortController().signal);
4573
- return { toolCallId: context.toolCallId, toolName: tool.name, ...result };
5123
+ const ok = "ok" in result && typeof result.ok === "boolean" ? result.ok : !result.isError;
5124
+ return { toolCallId: context.toolCallId, toolName: tool.name, ok, ...result };
4574
5125
  }
4575
5126
  };
4576
5127
  }
@@ -4665,20 +5216,38 @@ function resolveDefinitionContext(snapshot, assembled) {
4665
5216
  systemPrompt: [snapshot.definition.prompt, buildContextDescription(contexts)].filter((section) => section.length > 0).join("\n\n"),
4666
5217
  tools,
4667
5218
  skills,
4668
- phases: { phases, entryPhaseId }
5219
+ phases: { phases, entryPhaseId },
5220
+ ...snapshot.cwd ? { cwd: snapshot.cwd } : {}
4669
5221
  };
4670
5222
  }
4671
5223
  function toLoopResult(result, toolCallId, toolName) {
5224
+ const ok = "ok" in result && typeof result.ok === "boolean" ? result.ok : !result.isError;
5225
+ const error = "error" in result && typeof result.error === "string" ? result.error : void 0;
4672
5226
  return {
4673
5227
  toolCallId,
4674
5228
  toolName,
4675
- ok: result.ok,
5229
+ ok,
4676
5230
  content: result.content,
4677
- ...!result.ok ? { error: result.error } : {}
5231
+ ...!ok && error ? { error } : {},
5232
+ ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {},
5233
+ ...result.isError !== void 0 ? { isError: result.isError } : {}
4678
5234
  };
4679
5235
  }
4680
5236
  function fromLoopResult(result) {
4681
- return result.ok ? { ok: true, content: result.content } : { ok: false, content: result.content, error: result.error ?? "Extension hook rejected the Tool result." };
5237
+ if (Array.isArray(result.content)) {
5238
+ return {
5239
+ content: result.content,
5240
+ ...result.ok !== void 0 ? { ok: result.ok } : {},
5241
+ ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {},
5242
+ ...result.isError !== void 0 ? { isError: result.isError } : !result.ok ? { isError: true } : {},
5243
+ ...result.error !== void 0 ? { error: result.error } : {}
5244
+ };
5245
+ }
5246
+ return {
5247
+ ...result.ok ? { ok: true } : { ok: false, error: result.error ?? "Extension hook rejected the Tool result." },
5248
+ content: result.content,
5249
+ ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {}
5250
+ };
4682
5251
  }
4683
5252
 
4684
5253
  // src/runtime/transient-run-events.ts
@@ -4709,6 +5278,34 @@ var TransientRunEventSubscription = class {
4709
5278
  ...previous,
4710
5279
  text: previous.text + event.text
4711
5280
  };
5281
+ } else if (
5282
+ // `arguments` accumulates, so the latest delta of a call replaces the one before.
5283
+ previous?.kind === "tool_call_delta" && event.kind === "tool_call_delta" && previous.executionId === event.executionId && previous.providerToolCallId === event.providerToolCallId
5284
+ ) {
5285
+ this.queue[this.queue.length - 1] = event;
5286
+ } else if (event.kind === "tool_state_changed") {
5287
+ const toolCallId = event.toolCall.toolCallId;
5288
+ let index = -1;
5289
+ for (let i = this.queue.length - 1; i >= 0; i -= 1) {
5290
+ const existing = this.queue[i];
5291
+ if (existing && existing.kind === "tool_state_changed" && existing.runId === event.runId && existing.toolCall.toolCallId === toolCallId) {
5292
+ index = i;
5293
+ break;
5294
+ }
5295
+ }
5296
+ if (index !== -1) {
5297
+ const existing = this.queue[index];
5298
+ const mergedCall = { ...existing.toolCall };
5299
+ mergeToolCall(mergedCall, event.toolCall);
5300
+ this.queue[index] = {
5301
+ ...existing,
5302
+ transition: { from: existing.transition.from, to: event.transition.to },
5303
+ toolCall: mergedCall
5304
+ };
5305
+ } else {
5306
+ if (this.queue.length >= MAX_BUFFERED_EVENTS) this.queue.shift();
5307
+ this.queue.push(event);
5308
+ }
4712
5309
  } else {
4713
5310
  if (this.queue.length >= MAX_BUFFERED_EVENTS) this.queue.shift();
4714
5311
  this.queue.push(event);
@@ -4724,7 +5321,7 @@ var TransientRunEventSubscription = class {
4724
5321
  else {
4725
5322
  for (let index = this.queue.length - 1; index >= 0; index -= 1) {
4726
5323
  const event = this.queue[index];
4727
- if (event?.executionId === executionId && !(preserveCompletedPhaseStatus && event.kind === "phase_status" && event.status.state === "completed")) {
5324
+ if (event && "executionId" in event && event.executionId === executionId && !(preserveCompletedPhaseStatus && event.kind === "phase_status" && event.status.state === "completed")) {
4728
5325
  this.queue.splice(index, 1);
4729
5326
  }
4730
5327
  }
@@ -4734,14 +5331,14 @@ var TransientRunEventSubscription = class {
4734
5331
  clearMessage(messageId) {
4735
5332
  for (let index = this.queue.length - 1; index >= 0; index -= 1) {
4736
5333
  const event = this.queue[index];
4737
- if ((event?.kind === "message_delta" || event?.kind === "thinking_delta") && event.messageId === messageId) this.queue.splice(index, 1);
5334
+ if ((event?.kind === "message_delta" || event?.kind === "thinking_delta" || event?.kind === "tool_call_delta") && event.messageId === messageId) this.queue.splice(index, 1);
4738
5335
  }
4739
5336
  this.notify();
4740
5337
  }
4741
5338
  clearTool(toolCallId) {
4742
5339
  for (let index = this.queue.length - 1; index >= 0; index -= 1) {
4743
5340
  const event = this.queue[index];
4744
- if (event?.kind === "tool_progress" && event.toolCallId === toolCallId) this.queue.splice(index, 1);
5341
+ if (event?.kind === "tool_state_changed" && event.toolCall.toolCallId === toolCallId) this.queue.splice(index, 1);
4745
5342
  }
4746
5343
  this.notify();
4747
5344
  }
@@ -4885,6 +5482,7 @@ var HooksManager = class {
4885
5482
  var InMemoryExtensionHost = class {
4886
5483
  configs = /* @__PURE__ */ new Map();
4887
5484
  agentState = /* @__PURE__ */ new Map();
5485
+ globalState = /* @__PURE__ */ new Map();
4888
5486
  listeners = /* @__PURE__ */ new Set();
4889
5487
  constructor(options = {}) {
4890
5488
  if (options.configs) {
@@ -4911,8 +5509,18 @@ var InMemoryExtensionHost = class {
4911
5509
  this.agentState.set(agentId, agentMap);
4912
5510
  }
4913
5511
  }
5512
+ if (options.globalState) {
5513
+ for (const [extId, stateMap] of Object.entries(options.globalState)) {
5514
+ const map = /* @__PURE__ */ new Map();
5515
+ for (const [key, val] of Object.entries(stateMap)) {
5516
+ assertJsonValue(val);
5517
+ map.set(key, structuredClone(val));
5518
+ }
5519
+ this.globalState.set(extId, map);
5520
+ }
5521
+ }
4914
5522
  }
4915
- setConfig(extensionId, config, scope = { kind: "global" }) {
5523
+ setConfig(extensionId, config, scope = []) {
4916
5524
  const scopeKey = scopeToKey(scope);
4917
5525
  if (!this.configs.has(scopeKey)) {
4918
5526
  this.configs.set(scopeKey, /* @__PURE__ */ new Map());
@@ -4926,19 +5534,18 @@ var InMemoryExtensionHost = class {
4926
5534
  }
4927
5535
  this.notifyConfigChanged(extensionId, scope);
4928
5536
  }
4929
- getConfig(extensionId, scope = { kind: "global" }) {
5537
+ getConfig(extensionId, scope = []) {
5538
+ let merged = null;
4930
5539
  const globalConfig = this.configs.get("global")?.get(extensionId);
4931
- let merged = globalConfig ? { ...globalConfig } : null;
4932
- if (scope.kind === "team" || scope.kind === "project") {
4933
- const teamConfig = this.configs.get(`team:${scope.teamId}`)?.get(extensionId);
4934
- if (teamConfig) {
4935
- merged = { ...merged ?? {}, ...teamConfig };
4936
- }
5540
+ if (globalConfig) {
5541
+ merged = { ...globalConfig };
4937
5542
  }
4938
- if (scope.kind === "project") {
4939
- const projectConfig = this.configs.get(`project:${scope.teamId}:${scope.projectId}`)?.get(extensionId);
4940
- if (projectConfig) {
4941
- merged = { ...merged ?? {}, ...projectConfig };
5543
+ for (let i = 1; i <= scope.length; i++) {
5544
+ const subScope = scope.slice(0, i);
5545
+ const key = scopeToKey(subScope);
5546
+ const layerConfig = this.configs.get(key)?.get(extensionId);
5547
+ if (layerConfig) {
5548
+ merged = { ...merged ?? {}, ...layerConfig };
4942
5549
  }
4943
5550
  }
4944
5551
  return merged ? structuredClone(merged) : null;
@@ -4947,7 +5554,7 @@ var InMemoryExtensionHost = class {
4947
5554
  this.listeners.add(listener);
4948
5555
  return () => this.listeners.delete(listener);
4949
5556
  }
4950
- notifyConfigChanged(extensionId, scope = { kind: "global" }) {
5557
+ notifyConfigChanged(extensionId, scope = []) {
4951
5558
  for (const listener of this.listeners) {
4952
5559
  try {
4953
5560
  listener(extensionId, scope);
@@ -4976,29 +5583,26 @@ var InMemoryExtensionHost = class {
4976
5583
  deleteAgentState(extensionId, agentId, key) {
4977
5584
  this.agentState.get(agentId)?.get(extensionId)?.delete(key);
4978
5585
  }
4979
- };
4980
- function scopeToKey(scope) {
4981
- switch (scope.kind) {
4982
- case "global":
4983
- return "global";
4984
- case "team":
4985
- return `team:${scope.teamId}`;
4986
- case "project":
4987
- return `project:${scope.teamId}:${scope.projectId}`;
5586
+ getGlobalState(extensionId, key) {
5587
+ const val = this.globalState.get(extensionId)?.get(key);
5588
+ return val !== void 0 ? structuredClone(val) : void 0;
4988
5589
  }
4989
- }
4990
- function resolveScopeFromMetadata(metadata) {
4991
- if (metadata) {
4992
- const teamId = typeof metadata.teamId === "string" ? metadata.teamId : void 0;
4993
- const projectId = typeof metadata.projectId === "string" ? metadata.projectId : void 0;
4994
- if (teamId && projectId) {
4995
- return { kind: "project", teamId, projectId };
4996
- }
4997
- if (teamId) {
4998
- return { kind: "team", teamId };
5590
+ setGlobalState(extensionId, key, value) {
5591
+ assertJsonValue(value);
5592
+ let extMap = this.globalState.get(extensionId);
5593
+ if (!extMap) {
5594
+ extMap = /* @__PURE__ */ new Map();
5595
+ this.globalState.set(extensionId, extMap);
4999
5596
  }
5597
+ extMap.set(key, structuredClone(value));
5598
+ }
5599
+ deleteGlobalState(extensionId, key) {
5600
+ this.globalState.get(extensionId)?.delete(key);
5000
5601
  }
5001
- return { kind: "global" };
5602
+ };
5603
+ function scopeToKey(scope) {
5604
+ if (scope.length === 0) return "global";
5605
+ return scope.map((layer) => `${layer.kind}:${layer.id}`).join("/");
5002
5606
  }
5003
5607
 
5004
5608
  // src/extensions/runner.ts
@@ -5112,8 +5716,9 @@ async function execCommand(command, args, cwd, options) {
5112
5716
  });
5113
5717
  }
5114
5718
  function applyProviderRegistration(config) {
5115
- if (config.streamSimple) {
5116
- registerApiProvider({ protocol: config.protocol, stream: config.streamSimple });
5719
+ unregisterProviderModels(config.id);
5720
+ if (config.stream) {
5721
+ registerApiProvider({ protocol: config.protocol, stream: config.stream });
5117
5722
  }
5118
5723
  for (const modelConfig of config.models) {
5119
5724
  registerModel({
@@ -5123,12 +5728,13 @@ function applyProviderRegistration(config) {
5123
5728
  provider: config.id,
5124
5729
  baseUrl: config.baseUrl,
5125
5730
  apiKey: config.apiKey,
5126
- reasoning: modelConfig.reasoning,
5731
+ reasoning: modelConfig.reasoning ?? false,
5127
5732
  ...modelConfig.thinkingLevel !== void 0 ? { thinkingLevel: modelConfig.thinkingLevel } : {},
5128
- input: modelConfig.input,
5129
- cost: modelConfig.cost,
5130
- contextWindow: modelConfig.contextWindow,
5131
- maxTokens: modelConfig.maxTokens,
5733
+ ...modelConfig.thinkingLevels !== void 0 ? { thinkingLevels: modelConfig.thinkingLevels } : {},
5734
+ input: modelConfig.input ?? ["text"],
5735
+ cost: modelConfig.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
5736
+ contextWindow: modelConfig.contextWindow ?? 128e3,
5737
+ maxTokens: modelConfig.maxTokens ?? 4096,
5132
5738
  ...config.headers || modelConfig.headers ? { headers: { ...config.headers, ...modelConfig.headers } } : {},
5133
5739
  ...config.timeoutMs !== void 0 ? { timeoutMs: config.timeoutMs } : {},
5134
5740
  ...config.maxRetries !== void 0 ? { maxRetries: config.maxRetries } : {},
@@ -5164,6 +5770,8 @@ var ExtensionRunner = class {
5164
5770
  errorListeners = /* @__PURE__ */ new Set();
5165
5771
  // Capability change listeners
5166
5772
  capabilityListeners = /* @__PURE__ */ new Set();
5773
+ // UI contribution change listeners
5774
+ uiContributionListeners = /* @__PURE__ */ new Set();
5167
5775
  /** Current agent context — set by the agent before each phase */
5168
5776
  currentContext;
5169
5777
  constructor(options) {
@@ -5267,6 +5875,7 @@ var ExtensionRunner = class {
5267
5875
  tools: /* @__PURE__ */ new Map(),
5268
5876
  phases: /* @__PURE__ */ new Set(),
5269
5877
  capabilities: /* @__PURE__ */ new Map(),
5878
+ uiContributions: /* @__PURE__ */ new Map(),
5270
5879
  cleanup: [],
5271
5880
  runtime: createExtensionRuntime()
5272
5881
  };
@@ -5279,6 +5888,9 @@ var ExtensionRunner = class {
5279
5888
  if (extension.capabilities.size > 0) {
5280
5889
  this.notifyCapabilitiesChanged();
5281
5890
  }
5891
+ if (extension.uiContributions.size > 0) {
5892
+ this.notifyUiContributionsChanged();
5893
+ }
5282
5894
  } catch (error) {
5283
5895
  await this.rollbackExtension(extension);
5284
5896
  const message = error instanceof Error ? error.message : String(error);
@@ -5407,9 +6019,30 @@ var ExtensionRunner = class {
5407
6019
  this.capabilityListeners.add(listener);
5408
6020
  return () => this.capabilityListeners.delete(listener);
5409
6021
  }
5410
- // ---------------------------------------------------------------------------
5411
- // Phase management
5412
- // ---------------------------------------------------------------------------
6022
+ /** Get all contributed UI elements across all extensions. */
6023
+ getUiContributions() {
6024
+ const result = [];
6025
+ for (const ext of this.extensions) {
6026
+ for (const contrib of ext.uiContributions.values()) {
6027
+ result.push(contrib);
6028
+ }
6029
+ }
6030
+ return Object.freeze(result);
6031
+ }
6032
+ /**
6033
+ * Subscribe to UI contribution changes (contributions added, removed, or extensions disposed).
6034
+ * Returns an unsubscribe function.
6035
+ */
6036
+ onUiContributionsChanged(listener) {
6037
+ this.uiContributionListeners.add(listener);
6038
+ return () => this.uiContributionListeners.delete(listener);
6039
+ }
6040
+ triggerUiAction(event) {
6041
+ this.events.emit("ui.action", event);
6042
+ }
6043
+ // ---------------------------------------------------------------------------
6044
+ // Phase management
6045
+ // ---------------------------------------------------------------------------
5413
6046
  getPhase(name) {
5414
6047
  const reg = this.getRegisteredPhase(name);
5415
6048
  if (!reg) return void 0;
@@ -5524,7 +6157,7 @@ var ExtensionRunner = class {
5524
6157
  }
5525
6158
  }
5526
6159
  async emitBeforeToolCall(tool, args, context) {
5527
- const scope = context?.scope ?? resolveScopeFromMetadata(context?.metadata);
6160
+ const scope = context?.scope ?? [];
5528
6161
  const turn = context?.turn ?? {};
5529
6162
  const result = await this.emitHook("before_tool_call", {
5530
6163
  type: "before_tool_call",
@@ -5541,7 +6174,7 @@ var ExtensionRunner = class {
5541
6174
  return result ?? { allow: true };
5542
6175
  }
5543
6176
  async emitAfterToolCall(tool, result, context) {
5544
- const scope = context?.scope ?? resolveScopeFromMetadata(context?.metadata);
6177
+ const scope = context?.scope ?? [];
5545
6178
  const turn = context?.turn ?? {};
5546
6179
  const hookResult = await this.emitHook("after_tool_call", {
5547
6180
  type: "after_tool_call",
@@ -5621,12 +6254,13 @@ var ExtensionRunner = class {
5621
6254
  registerTool: (tool) => this.registerTool(extension, tool),
5622
6255
  unregisterTool: (toolName) => this.unregisterTool(extension, toolName),
5623
6256
  contributeCapability: (contribution) => this.contributeCapability(extension, contribution),
6257
+ contributeUi: (contribution) => this.contributeUi(extension, contribution),
5624
6258
  context: extContext,
5625
6259
  manifest,
5626
6260
  trackCleanup: (cleanup) => extension.cleanup.push(cleanup),
5627
6261
  config: {
5628
6262
  get: async (scope) => {
5629
- const effectiveScope = scope ?? { kind: "global" };
6263
+ const effectiveScope = scope ?? [];
5630
6264
  const res = await runner.host.getConfig(extension.id, effectiveScope);
5631
6265
  return res ?? null;
5632
6266
  },
@@ -5655,6 +6289,11 @@ var ExtensionRunner = class {
5655
6289
  get: async (key) => runner.host.getAgentState(extension.id, agentId, key),
5656
6290
  set: async (key, value) => runner.host.setAgentState(extension.id, agentId, key, value),
5657
6291
  delete: async (key) => runner.host.deleteAgentState(extension.id, agentId, key)
6292
+ }),
6293
+ global: () => ({
6294
+ get: async (key) => runner.host.getGlobalState(extension.id, key),
6295
+ set: async (key, value) => runner.host.setGlobalState(extension.id, key, value),
6296
+ delete: async (key) => runner.host.deleteGlobalState(extension.id, key)
5658
6297
  })
5659
6298
  }
5660
6299
  }, extension.runtime, this.events);
@@ -5725,6 +6364,44 @@ var ExtensionRunner = class {
5725
6364
  }
5726
6365
  }
5727
6366
  }
6367
+ contributeUi(extension, contribution) {
6368
+ const key = `${contribution.slot}:${contribution.id}`;
6369
+ const entry = {
6370
+ ...contribution
6371
+ };
6372
+ extension.uiContributions.set(key, entry);
6373
+ if (this.extensions.includes(extension)) {
6374
+ this.notifyUiContributionsChanged();
6375
+ }
6376
+ let disposed = false;
6377
+ const dispose = () => {
6378
+ if (disposed) return;
6379
+ disposed = true;
6380
+ if (extension.uiContributions.get(key) === entry) {
6381
+ extension.uiContributions.delete(key);
6382
+ if (this.extensions.includes(extension)) {
6383
+ this.notifyUiContributionsChanged();
6384
+ }
6385
+ }
6386
+ };
6387
+ extension.cleanup.push(dispose);
6388
+ return dispose;
6389
+ }
6390
+ notifyUiContributionsChanged() {
6391
+ const contributions = this.getUiContributions();
6392
+ for (const listener of this.uiContributionListeners) {
6393
+ try {
6394
+ listener(contributions);
6395
+ } catch (error) {
6396
+ this.emitError({
6397
+ extensionPath: "<runtime>",
6398
+ event: "ui_contributions_changed",
6399
+ error: error instanceof Error ? error.message : String(error),
6400
+ stack: error instanceof Error ? error.stack : void 0
6401
+ });
6402
+ }
6403
+ }
6404
+ }
5728
6405
  async registerPhase(extension, registration) {
5729
6406
  if (typeof registration === "string") {
5730
6407
  if (registration.length === 0) {
@@ -5813,6 +6490,8 @@ var ExtensionRunner = class {
5813
6490
  for (const name of extension.phases) this.phases.delete(name);
5814
6491
  const hadCapabilities = extension.capabilities.size > 0;
5815
6492
  extension.capabilities.clear();
6493
+ const hadUiContributions = extension.uiContributions.size > 0;
6494
+ extension.uiContributions.clear();
5816
6495
  for (const cleanup of [...extension.cleanup].reverse()) {
5817
6496
  await Promise.resolve().then(() => cleanup()).catch(() => void 0);
5818
6497
  }
@@ -5823,6 +6502,9 @@ var ExtensionRunner = class {
5823
6502
  if (hadCapabilities) {
5824
6503
  this.notifyCapabilitiesChanged();
5825
6504
  }
6505
+ if (hadUiContributions) {
6506
+ this.notifyUiContributionsChanged();
6507
+ }
5826
6508
  }
5827
6509
  };
5828
6510
  function createExtensionRunner(options) {
@@ -6515,11 +7197,11 @@ function adaptExtensionTool(input) {
6515
7197
  parameters: definition.parameters,
6516
7198
  ...definition.promptSnippet ? { promptSnippet: definition.promptSnippet } : {},
6517
7199
  ...definition.promptGuidelines ? { promptGuidelines: definition.promptGuidelines } : {},
6518
- execute: async (args, context, signal) => {
6519
- const result = await definition.execute(args, context, signal);
6520
- const content = JSON.parse(JSON.stringify(result.content));
6521
- return result.isError ? { ok: false, content, error: "Extension Tool failed." } : { ok: true, content };
6522
- }
7200
+ ...definition.kind ? { kind: definition.kind } : {},
7201
+ ...definition.annotations ? { annotations: definition.annotations } : {},
7202
+ ...definition._meta !== void 0 ? { _meta: definition._meta } : {},
7203
+ ...definition.present ? { present: definition.present } : {},
7204
+ execute: async (args, context, signal) => definition.execute(args, context, signal)
6523
7205
  };
6524
7206
  }
6525
7207
 
@@ -6587,12 +7269,53 @@ async function spillLargeToolResult(result, archiveDir, toolName) {
6587
7269
  await writeFile2(path, serialized, { encoding: "utf8", mode: 384 });
6588
7270
  await chmod2(path, 384).catch(() => void 0);
6589
7271
  const preview = new TextDecoder().decode(bytes.subarray(0, MAX_INLINE_TOOL_RESULT_BYTES));
6590
- return {
6591
- ...result,
6592
- content: `${preview}
7272
+ const truncatedText = `${preview}
6593
7273
  [truncated]
6594
7274
 
6595
- [Full result: ${path}, offset 0]`
7275
+ [Full result: ${path}, offset 0]`;
7276
+ if (Array.isArray(result.content)) {
7277
+ return {
7278
+ ...result,
7279
+ content: [{ type: "text", text: truncatedText }]
7280
+ };
7281
+ }
7282
+ if (result.ok) {
7283
+ return {
7284
+ ok: true,
7285
+ content: truncatedText,
7286
+ ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {}
7287
+ };
7288
+ }
7289
+ return {
7290
+ ok: false,
7291
+ content: truncatedText,
7292
+ error: result.error ?? "Tool execution failed.",
7293
+ ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {}
7294
+ };
7295
+ }
7296
+ async function presentToolCall(tool, call) {
7297
+ if (!tool.present) return void 0;
7298
+ try {
7299
+ const res = await tool.present(call);
7300
+ if (!res) return void 0;
7301
+ if (!isValidPresentationOutput(res)) {
7302
+ console.warn(`Tool "${tool.name}" present() returned invalid shape for status "${call.status}":`, res);
7303
+ return void 0;
7304
+ }
7305
+ return res;
7306
+ } catch (err) {
7307
+ console.warn(`Tool "${tool.name}" present() threw for status "${call.status}":`, err);
7308
+ return void 0;
7309
+ }
7310
+ }
7311
+ async function presentToolPending(tool, args) {
7312
+ const pres = await presentToolCall(tool, { status: "pending", args });
7313
+ return {
7314
+ title: pres?.title ?? tool.annotations?.title ?? tool.name,
7315
+ kind: tool.kind ?? "other",
7316
+ ...pres?.locations !== void 0 ? { locations: pres.locations } : {},
7317
+ ...pres?.content !== void 0 ? { content: pres.content } : {},
7318
+ ...pres?._meta !== void 0 ? { _meta: pres._meta } : {}
6596
7319
  };
6597
7320
  }
6598
7321
  var AgentRuntime = class _AgentRuntime {
@@ -6611,9 +7334,11 @@ var AgentRuntime = class _AgentRuntime {
6611
7334
  host;
6612
7335
  resources;
6613
7336
  startedRuns = /* @__PURE__ */ new Set();
7337
+ liveInteractions = /* @__PURE__ */ new Map();
6614
7338
  heartbeat;
6615
7339
  pumping = false;
6616
7340
  closed = false;
7341
+ retryDelayMs;
6617
7342
  constructor(options, owned, resources) {
6618
7343
  this.host = options.host;
6619
7344
  this.owned = owned;
@@ -6621,6 +7346,7 @@ var AgentRuntime = class _AgentRuntime {
6621
7346
  this.storeIncarnation = String(owned.lease.token).split(":")[0];
6622
7347
  this.concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
6623
7348
  this.resources = resources;
7349
+ this.retryDelayMs = options.retryDelayMs;
6624
7350
  }
6625
7351
  static async init(options) {
6626
7352
  if (!options.store) throw new TypeError("AgentRuntime requires a DurableStore");
@@ -6644,6 +7370,13 @@ var AgentRuntime = class _AgentRuntime {
6644
7370
  }
6645
7371
  }
6646
7372
  async handleRunEnd(runId, agentId, outcome) {
7373
+ const runLive = this.liveInteractions.get(runId);
7374
+ if (runLive) {
7375
+ for (const live of runLive.values()) {
7376
+ live.reject(new RunInteractionCancelledError("Run ended."));
7377
+ }
7378
+ this.liveInteractions.delete(runId);
7379
+ }
6647
7380
  if (this.startedRuns.has(runId)) {
6648
7381
  this.startedRuns.delete(runId);
6649
7382
  try {
@@ -6787,6 +7520,8 @@ var AgentRuntime = class _AgentRuntime {
6787
7520
  agentId,
6788
7521
  input,
6789
7522
  ...options.metadata === void 0 ? {} : { metadata: options.metadata },
7523
+ ...options.scope === void 0 ? {} : { scope: options.scope },
7524
+ ...options.cwd === void 0 ? {} : { cwd: options.cwd },
6790
7525
  ...phasePayload === void 0 ? {} : { phasePayload },
6791
7526
  ...options.entryPhases === void 0 ? {} : { entryPhases: options.entryPhases },
6792
7527
  ...pinnedConfigToken === agent.currentConfigToken ? {} : { pinnedConfigToken },
@@ -6875,12 +7610,72 @@ var AgentRuntime = class _AgentRuntime {
6875
7610
  this.assertOpen();
6876
7611
  return this.resources.extensionRunner.onCapabilitiesChanged(listener);
6877
7612
  }
7613
+ listUiContributions() {
7614
+ this.assertOpen();
7615
+ return this.resources.extensionRunner.getUiContributions();
7616
+ }
7617
+ onUiContributionsChanged(listener) {
7618
+ this.assertOpen();
7619
+ return this.resources.extensionRunner.onUiContributionsChanged(listener);
7620
+ }
7621
+ triggerUiAction(event) {
7622
+ this.assertOpen();
7623
+ this.resources.extensionRunner.triggerUiAction(event);
7624
+ }
7625
+ liveInteract(runId, request, signal) {
7626
+ if (signal.aborted) {
7627
+ return Promise.reject(signal.reason ?? new Error("Run aborted"));
7628
+ }
7629
+ const interactionId = request.id ?? createId("interaction");
7630
+ const interaction = {
7631
+ id: interactionId,
7632
+ phase: "default",
7633
+ kind: request.kind,
7634
+ prompt: request.prompt,
7635
+ payload: request.payload,
7636
+ toolCallId: request.toolCallId,
7637
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
7638
+ status: "pending",
7639
+ result: request.result
7640
+ };
7641
+ let runLive = this.liveInteractions.get(runId);
7642
+ if (!runLive) {
7643
+ runLive = /* @__PURE__ */ new Map();
7644
+ this.liveInteractions.set(runId, runLive);
7645
+ }
7646
+ return new Promise((resolve9, reject) => {
7647
+ const onAbort = () => {
7648
+ runLive?.delete(interactionId);
7649
+ if (runLive && runLive.size === 0) this.liveInteractions.delete(runId);
7650
+ signal.removeEventListener("abort", onAbort);
7651
+ reject(signal.reason ?? new Error("Run aborted"));
7652
+ };
7653
+ signal.addEventListener("abort", onAbort, { once: true });
7654
+ runLive.set(interactionId, {
7655
+ interaction,
7656
+ resolve: (val) => {
7657
+ signal.removeEventListener("abort", onAbort);
7658
+ resolve9(val);
7659
+ },
7660
+ reject: (err) => {
7661
+ signal.removeEventListener("abort", onAbort);
7662
+ reject(err);
7663
+ }
7664
+ });
7665
+ });
7666
+ }
6878
7667
  async close() {
6879
7668
  if (this.closed) return;
6880
7669
  this.closed = true;
6881
7670
  if (this.heartbeat) clearInterval(this.heartbeat);
6882
7671
  for (const execution of this.executions.values()) execution.controller.abort();
6883
7672
  this.executions.clear();
7673
+ for (const runLive of this.liveInteractions.values()) {
7674
+ for (const live of runLive.values()) {
7675
+ live.reject(new RunInteractionCancelledError("Runtime closed."));
7676
+ }
7677
+ }
7678
+ this.liveInteractions.clear();
6884
7679
  this.transientEvents.close();
6885
7680
  for (const subscription of this.consumers.values()) subscription.controller.abort();
6886
7681
  await this.resources.closeExtensions();
@@ -6888,7 +7683,15 @@ var AgentRuntime = class _AgentRuntime {
6888
7683
  }
6889
7684
  async snapshot(runId) {
6890
7685
  this.assertOpen();
6891
- return this.owned.snapshotRun(runId);
7686
+ const snap = await this.owned.snapshotRun(runId);
7687
+ const runLive = this.liveInteractions.get(runId);
7688
+ if (runLive && runLive.size > 0 && snap.state === "running") {
7689
+ return {
7690
+ ...snap,
7691
+ interactions: [...runLive.values()].map((l) => l.interaction)
7692
+ };
7693
+ }
7694
+ return snap;
6892
7695
  }
6893
7696
  async history(agentId) {
6894
7697
  this.assertOpen();
@@ -6896,6 +7699,20 @@ var AgentRuntime = class _AgentRuntime {
6896
7699
  }
6897
7700
  async respondInteraction(runId, input) {
6898
7701
  this.assertOpen();
7702
+ const runLive = this.liveInteractions.get(runId);
7703
+ const live = runLive?.get(input.interactionId);
7704
+ if (live) {
7705
+ runLive.delete(input.interactionId);
7706
+ if (runLive.size === 0) this.liveInteractions.delete(runId);
7707
+ if (input.cancel) {
7708
+ live.reject(new RunInteractionCancelledError("Run interaction cancelled."));
7709
+ } else if (input.input !== void 0) {
7710
+ live.resolve(input.input);
7711
+ } else {
7712
+ live.reject(new TypeError("respondInteraction requires input or cancel: true"));
7713
+ }
7714
+ return;
7715
+ }
6899
7716
  const snapshot = await this.owned.snapshotRun(runId);
6900
7717
  if (snapshot.state !== "input_required" || !snapshot.interactions.some((interaction) => interaction.id === input.interactionId)) {
6901
7718
  throw new RuntimeError("input_request_conflict", { runId, interactionId: input.interactionId, reason: "not_found" });
@@ -6905,6 +7722,13 @@ var AgentRuntime = class _AgentRuntime {
6905
7722
  }
6906
7723
  async cancel(runId, reason) {
6907
7724
  this.assertOpen();
7725
+ const runLive = this.liveInteractions.get(runId);
7726
+ if (runLive) {
7727
+ for (const live of runLive.values()) {
7728
+ live.reject(new RunInteractionCancelledError(reason ?? "Agent run stopped."));
7729
+ }
7730
+ this.liveInteractions.delete(runId);
7731
+ }
6908
7732
  const done = this.executionDone.get(runId);
6909
7733
  if (done) {
6910
7734
  this.cancellationRequested.add(runId);
@@ -6963,6 +7787,7 @@ var AgentRuntime = class _AgentRuntime {
6963
7787
  async execute(run) {
6964
7788
  let claim;
6965
7789
  let executionRevision = run.revision;
7790
+ let toolQueue = Promise.resolve();
6966
7791
  try {
6967
7792
  const agent = await this.requireAgent(run.agentId);
6968
7793
  let token = run.pinnedConfigToken ?? agent.currentConfigToken;
@@ -7050,7 +7875,7 @@ var AgentRuntime = class _AgentRuntime {
7050
7875
  const assembly = assembleRegisteredExtensions(config, this.resources.extensionRunner, {
7051
7876
  toolArchiveDir: this.archiveDirFor(run.agentId)
7052
7877
  });
7053
- let toolQueue = Promise.resolve();
7878
+ toolQueue = Promise.resolve();
7054
7879
  const model = "stream" in config && config.stream ? config.model : { provider: config.model.provider, id: config.model.id };
7055
7880
  const stream = "stream" in config && config.stream ? config.stream : createModelStream(config.model);
7056
7881
  const phaseRegistry = normalizePhaseRegistry(assembly.context.phases);
@@ -7089,10 +7914,12 @@ var AgentRuntime = class _AgentRuntime {
7089
7914
  executionId: claim.execution.executionId,
7090
7915
  input: typeof run.input === "string" ? run.input : run.input.content,
7091
7916
  ...agent.metadata === void 0 ? {} : { agentMetadata: agent.metadata },
7092
- ...run.metadata === void 0 ? {} : { runMetadata: run.metadata }
7917
+ ...run.metadata === void 0 ? {} : { runMetadata: run.metadata },
7918
+ ...run.scope === void 0 ? {} : { scope: run.scope },
7919
+ ...run.cwd === void 0 ? {} : { cwd: run.cwd }
7093
7920
  },
7094
7921
  model,
7095
- thinkingLevel: thinkingLevelFromMessages(context.messages),
7922
+ thinkingLevel: thinkingLevelFromUserInput(run.input),
7096
7923
  stream,
7097
7924
  maxAttempts: config.maxAttempts,
7098
7925
  checkpoint: claim.run.checkpoint,
@@ -7101,9 +7928,9 @@ var AgentRuntime = class _AgentRuntime {
7101
7928
  interactionAnswers: claim.run.interactionAnswers,
7102
7929
  signal: controller.signal,
7103
7930
  beforePhase: assembly.beforePhase,
7104
- afterPhase: async (phaseId, output) => {
7105
- const extAfter = assembly.afterPhase ? await assembly.afterPhase(phaseId, output) : {};
7106
- const effectiveOutput = extAfter.output ?? output;
7931
+ afterPhase: async (phaseId, output2) => {
7932
+ const extAfter = assembly.afterPhase ? await assembly.afterPhase(phaseId, output2) : {};
7933
+ const effectiveOutput = extAfter.output ?? output2;
7107
7934
  if (!extAfter.abort && (phaseId === COMPACT_PHASE_ID || isCompactionOutcome(effectiveOutput.payload))) {
7108
7935
  const active = this.executions.get(run.id);
7109
7936
  if (active?.executionId === claim.execution.executionId && !this.cancellationRequested.has(run.id) && !controller.signal.aborted) {
@@ -7155,7 +7982,7 @@ var AgentRuntime = class _AgentRuntime {
7155
7982
  const active = this.executions.get(run.id);
7156
7983
  if (this.closed || controller.signal.aborted || active?.executionId !== claim.execution.executionId) return;
7157
7984
  const projected = projectAssistantMessage(message, run.agentId, run.id, 0);
7158
- const committed = await this.owned.commitPhaseOutput({
7985
+ const committed2 = await this.owned.commitPhaseOutput({
7159
7986
  runId: run.id,
7160
7987
  execution: claim.execution,
7161
7988
  expectedRevision: executionRevision,
@@ -7172,7 +7999,7 @@ var AgentRuntime = class _AgentRuntime {
7172
7999
  }
7173
8000
  }
7174
8001
  });
7175
- executionRevision = committed.revision;
8002
+ executionRevision = committed2.revision;
7176
8003
  });
7177
8004
  toolQueue = task.then(() => void 0, () => void 0);
7178
8005
  return task;
@@ -7204,9 +8031,65 @@ var AgentRuntime = class _AgentRuntime {
7204
8031
  text: event.text
7205
8032
  });
7206
8033
  },
8034
+ onToolCallDelta: (event) => {
8035
+ const active = this.executions.get(run.id);
8036
+ if (this.closed || controller.signal.aborted || active?.executionId !== claim.execution.executionId) return;
8037
+ this.transientEvents.publish({
8038
+ kind: "tool_call_delta",
8039
+ durability: "transient",
8040
+ runId: run.id,
8041
+ executionId: claim.execution.executionId,
8042
+ messageId: event.messageId,
8043
+ providerToolCallId: event.providerToolCallId,
8044
+ toolName: event.toolName,
8045
+ arguments: event.arguments,
8046
+ args: event.args
8047
+ });
8048
+ },
8049
+ onModelRetry: (event) => {
8050
+ const active = this.executions.get(run.id);
8051
+ if (this.closed || controller.signal.aborted || active?.executionId !== claim.execution.executionId) return;
8052
+ this.transientEvents.publish({
8053
+ kind: "model_retry",
8054
+ durability: "transient",
8055
+ runId: run.id,
8056
+ executionId: claim.execution.executionId,
8057
+ attempt: event.attempt,
8058
+ maxRetries: event.maxRetries,
8059
+ delayMs: event.delayMs,
8060
+ error: event.error
8061
+ });
8062
+ },
8063
+ onToolReport: (event) => {
8064
+ const active = this.executions.get(run.id);
8065
+ if (this.closed || controller.signal.aborted || active?.executionId !== claim.execution.executionId) return;
8066
+ this.transientEvents.publish({
8067
+ kind: "tool_state_changed",
8068
+ durability: "transient",
8069
+ runId: event.runId,
8070
+ executionId: claim.execution.executionId,
8071
+ transition: { from: null, to: event.update.status ?? "in_progress" },
8072
+ toolCall: event.update,
8073
+ external: true
8074
+ });
8075
+ const task = toolQueue.then(async () => {
8076
+ const active2 = this.executions.get(run.id);
8077
+ if (this.closed || controller.signal.aborted || active2?.executionId !== claim.execution.executionId) return;
8078
+ const recorded = await this.owned.recordToolCall({
8079
+ runId: run.id,
8080
+ execution: claim.execution,
8081
+ update: event.update,
8082
+ external: true
8083
+ });
8084
+ executionRevision = recorded.runRevision;
8085
+ });
8086
+ toolQueue = task.then(() => void 0, () => void 0);
8087
+ },
8088
+ retryDelayMs: this.retryDelayMs,
7207
8089
  onContext: assembly.setContext,
7208
8090
  runtime: {
7209
- tools: ({ toolCall, contentBlocks, driver }) => {
8091
+ interact: (request) => this.liveInteract(run.id, request, controller.signal),
8092
+ tools: ({ toolCall, contentBlocks, driver, options }) => {
7210
8093
  const task = toolQueue.then(async () => {
7211
8094
  const execution = await this.executeToolBatch({
7212
8095
  run,
@@ -7218,6 +8101,7 @@ var AgentRuntime = class _AgentRuntime {
7218
8101
  contentBlocks,
7219
8102
  signal: controller.signal,
7220
8103
  driver,
8104
+ options,
7221
8105
  onRevision: (rev) => {
7222
8106
  executionRevision = rev;
7223
8107
  }
@@ -7228,7 +8112,7 @@ var AgentRuntime = class _AgentRuntime {
7228
8112
  toolQueue = task.then(() => void 0, () => void 0);
7229
8113
  return task;
7230
8114
  },
7231
- toolsBatch: ({ toolCalls, contentBlocks, driver }) => {
8115
+ toolsBatch: ({ toolCalls, contentBlocks, driver, options }) => {
7232
8116
  const task = toolQueue.then(async () => {
7233
8117
  const execution = await this.executeToolBatch({
7234
8118
  run,
@@ -7240,6 +8124,7 @@ var AgentRuntime = class _AgentRuntime {
7240
8124
  contentBlocks,
7241
8125
  signal: controller.signal,
7242
8126
  driver,
8127
+ options,
7243
8128
  onRevision: (rev) => {
7244
8129
  executionRevision = rev;
7245
8130
  }
@@ -7263,14 +8148,15 @@ var AgentRuntime = class _AgentRuntime {
7263
8148
  result = await executeModel(executionContext);
7264
8149
  }
7265
8150
  }
8151
+ await toolQueue;
7266
8152
  if (this.cancellationRequested.has(run.id) || controller.signal.aborted) {
7267
- const output = latestAssistant(run, result.messages, executionContext.messages.length, true);
8153
+ const output2 = latestAssistant(run, result.messages, executionContext.messages.length, true);
7268
8154
  const reason = this.cancellationReasons.get(run.id) ?? "Agent run stopped.";
7269
8155
  await this.owned.cancelRun({
7270
8156
  runId: run.id,
7271
8157
  expectedRevision: executionRevision,
7272
8158
  reason,
7273
- ...output && hasVisibleAssistantText(output) ? { output } : {}
8159
+ ...output2 && hasVisibleAssistantText(output2) ? { output: output2 } : {}
7274
8160
  });
7275
8161
  this.transientEvents.clear(run.id, claim.execution.executionId);
7276
8162
  await this.handleRunEnd(run.id, run.agentId, {
@@ -7289,12 +8175,12 @@ var AgentRuntime = class _AgentRuntime {
7289
8175
  pendingToolCallIds = result.interactions.map(({ toolCallId }) => toolCallId);
7290
8176
  }
7291
8177
  const isToolCallInteraction = result.interactions?.some((i) => i.toolCallId !== void 0);
7292
- const output = isToolCallInteraction ? void 0 : latestAssistant(
8178
+ const output2 = isToolCallInteraction ? void 0 : latestAssistant(
7293
8179
  run,
7294
8180
  result.messages,
7295
8181
  executionContext.messages.length
7296
8182
  );
7297
- const prompt = isToolCallInteraction ? void 0 : output ?? (result.interactions[0]?.prompt ? promptMessage(run, result.interactions[0].prompt, result.messages.length) : void 0);
8183
+ const prompt = isToolCallInteraction ? void 0 : output2 ?? (result.interactions[0]?.prompt ? promptMessage(run, result.interactions[0].prompt, result.messages.length) : void 0);
7298
8184
  await this.owned.commitInputRequired({
7299
8185
  runId: run.id,
7300
8186
  execution: claim.execution,
@@ -7310,24 +8196,32 @@ var AgentRuntime = class _AgentRuntime {
7310
8196
  return;
7311
8197
  }
7312
8198
  if (result.type === "completed") {
7313
- const output = latestAssistant(run, result.messages, executionContext.messages.length);
7314
- await this.owned.commitOutcome({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, outcome: durableOutcome(result.outcome), ...output ? { output } : {} });
8199
+ const output2 = latestAssistant(run, result.messages, executionContext.messages.length);
8200
+ await this.owned.commitOutcome({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, outcome: durableOutcome(result.outcome), ...output2 ? { output: output2 } : {} });
7315
8201
  this.transientEvents.clear(run.id, claim.execution.executionId, true);
7316
8202
  await this.handleRunEnd(run.id, run.agentId, result.outcome);
7317
8203
  return;
7318
8204
  }
8205
+ const output = latestAssistant(run, result.messages, executionContext.messages.length);
7319
8206
  const failure = { code: "execution_failed", message: result.error instanceof Error ? result.error.message : "Execution failed." };
7320
- await this.owned.commitOutcome({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, failure });
8207
+ const committed = await this.owned.commitOutcome({
8208
+ runId: run.id,
8209
+ execution: claim.execution,
8210
+ expectedRevision: executionRevision,
8211
+ failure,
8212
+ ...output ? { output } : {}
8213
+ });
7321
8214
  this.transientEvents.clear(run.id, claim.execution.executionId);
7322
8215
  await this.handleRunEnd(run.id, run.agentId, {
7323
8216
  id: createId("out"),
7324
8217
  status: "failed",
7325
8218
  message: failure.message,
7326
- error: failure
8219
+ error: committed.failure ?? failure
7327
8220
  });
7328
8221
  } catch (error) {
7329
8222
  if (!claim && error instanceof RuntimeError && ["run_state_conflict", "runtime_ownership_lost", "run_not_found"].includes(error.code)) return;
7330
8223
  if (claim) {
8224
+ await toolQueue;
7331
8225
  if (this.cancellationRequested.has(run.id)) {
7332
8226
  const reason = this.cancellationReasons.get(run.id) ?? "Agent run stopped.";
7333
8227
  await this.owned.cancelRun({
@@ -7343,7 +8237,7 @@ var AgentRuntime = class _AgentRuntime {
7343
8237
  return;
7344
8238
  }
7345
8239
  const failureMessage = error instanceof Error ? error.message : "Execution failed.";
7346
- await this.owned.commitOutcome({
8240
+ const committed = await this.owned.commitOutcome({
7347
8241
  runId: run.id,
7348
8242
  execution: claim.execution,
7349
8243
  expectedRevision: executionRevision,
@@ -7353,12 +8247,13 @@ var AgentRuntime = class _AgentRuntime {
7353
8247
  runId: run.id,
7354
8248
  reason: commitError instanceof Error ? commitError.message : "Run failure could not be committed."
7355
8249
  }).catch(() => void 0);
8250
+ return void 0;
7356
8251
  });
7357
8252
  await this.handleRunEnd(run.id, run.agentId, {
7358
8253
  id: createId("out"),
7359
8254
  status: "failed",
7360
8255
  message: failureMessage,
7361
- error: { code: "execution_failed", message: failureMessage }
8256
+ error: committed?.failure ?? { code: "execution_failed", message: failureMessage }
7362
8257
  });
7363
8258
  return;
7364
8259
  }
@@ -7382,7 +8277,8 @@ var AgentRuntime = class _AgentRuntime {
7382
8277
  return { result: batch.results[0], revision: batch.revision };
7383
8278
  }
7384
8279
  async executeToolBatch(input) {
7385
- const scope = resolveScopeFromMetadata(input.run.metadata);
8280
+ const scope = input.run.scope ?? [];
8281
+ const cwd = input.run.cwd;
7386
8282
  const turn = typeof input.run.input === "object" && input.run.input !== null && !Array.isArray(input.run.input) ? input.run.input : {};
7387
8283
  const checkpoint = input.driver?.checkpoint();
7388
8284
  const isSuspensionResume = checkpoint && typeof checkpoint === "object" && checkpoint.kind === "tool_call_suspension";
@@ -7410,7 +8306,9 @@ var AgentRuntime = class _AgentRuntime {
7410
8306
  ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
7411
8307
  ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
7412
8308
  toolCallId,
8309
+ providerToolCallId: toolCall.id,
7413
8310
  scope,
8311
+ ...cwd ? { cwd } : {},
7414
8312
  turn,
7415
8313
  interaction: createRunInteractionDriver({
7416
8314
  currentPhase: "default",
@@ -7438,6 +8336,12 @@ var AgentRuntime = class _AgentRuntime {
7438
8336
  }
7439
8337
  }
7440
8338
  if (!hasInteraction) {
8339
+ const startPresentations = await Promise.all(
8340
+ input.toolCalls.map(async (tc) => {
8341
+ const t = input.toolConfig.tools.find((c) => c.name === tc.name);
8342
+ return t ? presentToolPending(t, toJsonValue(tc.args)) : void 0;
8343
+ })
8344
+ );
7441
8345
  const reserved = await this.owned.reserveToolCalls({
7442
8346
  runId: input.run.id,
7443
8347
  execution: input.execution,
@@ -7448,7 +8352,8 @@ var AgentRuntime = class _AgentRuntime {
7448
8352
  toolCallId: toolCallIds[i],
7449
8353
  providerToolCallId: tc.id,
7450
8354
  name: tc.name,
7451
- args: toJsonValue(tc.args)
8355
+ args: toJsonValue(tc.args),
8356
+ ...startPresentations[i] ?? {}
7452
8357
  }))
7453
8358
  });
7454
8359
  setRevision(reserved.run.revision);
@@ -7472,7 +8377,9 @@ var AgentRuntime = class _AgentRuntime {
7472
8377
  expectedRevision: revision,
7473
8378
  toolCallId,
7474
8379
  state: "failed",
7475
- result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available.` }
8380
+ result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available.` },
8381
+ title: toolCall.name,
8382
+ kind: "other"
7476
8383
  });
7477
8384
  setRevision(failed.run.revision);
7478
8385
  results2.push(protocolFailure(`Tool ${toolCall.name} is not available.`));
@@ -7486,116 +8393,64 @@ var AgentRuntime = class _AgentRuntime {
7486
8393
  expectedRevision: revision,
7487
8394
  toolCallId,
7488
8395
  state: "failed",
7489
- result: { ok: false, content: null, error: decision.reason }
8396
+ result: { ok: false, content: null, error: decision.reason },
8397
+ title: durableTool.annotations?.title ?? durableTool.name,
8398
+ kind: durableTool.kind ?? "other"
7490
8399
  });
7491
8400
  setRevision(failed.run.revision);
7492
8401
  results2.push(protocolFailure(decision.reason));
7493
8402
  continue;
7494
8403
  }
7495
- let progressActive = false;
7496
- const context = {
7497
- agentId: input.run.agentId,
7498
- runId: input.run.id,
7499
- ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
7500
- ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
7501
- toolCallId,
7502
- scope,
7503
- turn,
7504
- interaction: createToolInteractionDriver(input.driver, {
7505
- toolCallId,
7506
- toolCallIds,
7507
- toolCalls: input.toolCalls,
7508
- pendingIndex: i,
7509
- completedResults: results2
7510
- }),
7511
- reportProgress: (progress) => {
7512
- const active = this.executions.get(input.run.id);
7513
- if (!progressActive || this.closed || input.signal.aborted || active?.executionId !== input.execution.executionId || !isJsonValue(progress)) return;
7514
- let copy;
7515
- try {
7516
- if (JSON.stringify(progress).length > 64 * 1024) return;
7517
- copy = structuredClone(progress);
7518
- } catch {
7519
- return;
7520
- }
7521
- this.transientEvents.publish({
7522
- kind: "tool_progress",
7523
- durability: "transient",
7524
- runId: input.run.id,
7525
- executionId: input.execution.executionId,
7526
- toolCallId,
7527
- progress: copy
7528
- });
7529
- }
8404
+ const currentToolState = {
8405
+ ...startPresentations[i] ?? {
8406
+ title: durableTool.annotations?.title ?? durableTool.name,
8407
+ kind: durableTool.kind ?? "other"
8408
+ },
8409
+ rawInput: toJsonValue(toolCall.args)
7530
8410
  };
8411
+ const inProgressPres = await presentToolCall(durableTool, {
8412
+ status: "in_progress",
8413
+ args: toJsonValue(toolCall.args)
8414
+ });
8415
+ if (inProgressPres) mergeToolCall(currentToolState, inProgressPres);
7531
8416
  const started = await this.owned.startToolCall({
7532
8417
  runId: input.run.id,
7533
8418
  execution: input.execution,
7534
8419
  expectedRevision: revision,
7535
- toolCallId
8420
+ toolCallId,
8421
+ title: currentToolState.title ?? durableTool.annotations?.title ?? durableTool.name,
8422
+ kind: currentToolState.kind ?? durableTool.kind ?? "other",
8423
+ ...currentToolState.locations !== void 0 ? { locations: currentToolState.locations } : {},
8424
+ ...currentToolState.content !== void 0 ? { content: currentToolState.content } : {},
8425
+ ...currentToolState._meta !== void 0 ? { _meta: currentToolState._meta } : {}
7536
8426
  });
7537
8427
  setRevision(started.run.revision);
7538
- progressActive = true;
7539
- try {
7540
- let result = await durableTool.execute(toJsonValue(toolCall.args), context, input.signal);
7541
- assertToolExecutionResult(result);
7542
- if (input.toolConfig.afterToolCall) {
7543
- result = await input.toolConfig.afterToolCall({
7544
- tool: durableTool,
7545
- result,
7546
- context,
7547
- signal: input.signal
7548
- });
7549
- }
7550
- assertToolExecutionResult(result);
7551
- result = await spillLargeToolResult(
7552
- result,
7553
- this.archiveDirFor(input.run.agentId),
7554
- toolCall.name
7555
- );
7556
- const committed = await this.owned.commitToolResult({
7557
- runId: input.run.id,
7558
- execution: input.execution,
7559
- expectedRevision: revision,
7560
- toolCallId,
7561
- state: result.ok ? "completed" : "failed",
7562
- result
7563
- });
7564
- setRevision(committed.run.revision);
7565
- const projectedResult = await this.foldToolInteractionRecords(result, toolCallId, input.run.agentId);
7566
- results2.push({
7567
- toolCallId: providerToolCallId,
7568
- toolName: toolCall.name,
7569
- ...projectedResult
7570
- });
7571
- } catch (error) {
7572
- if (error instanceof RunInteractionBoundary) {
7573
- const suspended = await this.owned.suspendToolCall({
7574
- runId: input.run.id,
7575
- execution: input.execution,
7576
- expectedRevision: revision,
7577
- toolCallId
7578
- });
7579
- setRevision(suspended.run.revision);
7580
- throw error;
7581
- }
7582
- if (error instanceof RunInteractionCancelledError) throw error;
7583
- const reason = error instanceof Error ? error.message : "Tool execution outcome is indeterminate.";
7584
- const indeterminate = await this.owned.commitToolResult({
7585
- runId: input.run.id,
7586
- execution: input.execution,
7587
- expectedRevision: revision,
7588
- toolCallId,
7589
- state: "indeterminate",
7590
- reason,
7591
- result: { ok: false, content: null, error: reason }
7592
- });
7593
- setRevision(indeterminate.run.revision);
7594
- results2.push(protocolFailure(reason));
7595
- } finally {
7596
- progressActive = false;
7597
- this.transientEvents.clearTool(input.run.id, toolCallId);
7598
- }
8428
+ const toolResult2 = await this.executeAndCommitTool({
8429
+ runId: input.run.id,
8430
+ agentId: input.run.agentId,
8431
+ agentMetadata: input.agentMetadata,
8432
+ runMetadata: input.run.metadata,
8433
+ execution: input.execution,
8434
+ revision,
8435
+ setRevision,
8436
+ toolCall,
8437
+ durableTool,
8438
+ toolCallId,
8439
+ providerToolCallId,
8440
+ scope,
8441
+ cwd,
8442
+ turn,
8443
+ driver: input.driver,
8444
+ toolCalls: input.toolCalls,
8445
+ toolCallIds,
8446
+ pendingIndex: i,
8447
+ completedResults: results2,
8448
+ signal: input.signal,
8449
+ afterToolCall: input.toolConfig.afterToolCall,
8450
+ currentToolState,
8451
+ options: input.options
8452
+ });
8453
+ results2.push(toolResult2);
7599
8454
  }
7600
8455
  return { results: results2, revision };
7601
8456
  }
@@ -7615,7 +8470,7 @@ var AgentRuntime = class _AgentRuntime {
7615
8470
  error
7616
8471
  });
7617
8472
  if (!durableTool) {
7618
- const reserved2 = await this.owned.reserveToolCalls({
8473
+ const reserved = await this.owned.reserveToolCalls({
7619
8474
  runId: input.run.id,
7620
8475
  execution: input.execution,
7621
8476
  expectedRevision: revision,
@@ -7628,60 +8483,70 @@ var AgentRuntime = class _AgentRuntime {
7628
8483
  args: toJsonValue(toolCall.args)
7629
8484
  }]
7630
8485
  });
7631
- setRevision(reserved2.run.revision);
8486
+ setRevision(reserved.run.revision);
7632
8487
  const failed = await this.owned.commitToolResult({
7633
8488
  runId: input.run.id,
7634
8489
  execution: input.execution,
7635
8490
  expectedRevision: revision,
7636
8491
  toolCallId,
7637
8492
  state: "failed",
7638
- result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available.` }
8493
+ result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available.` },
8494
+ title: toolCall.name,
8495
+ kind: "other"
7639
8496
  });
7640
8497
  setRevision(failed.run.revision);
7641
8498
  results.push(protocolFailure(`Tool ${toolCall.name} is not available.`));
7642
8499
  continue;
7643
8500
  }
7644
- let progressActive = false;
7645
- const resumedStart = suspension && index === suspension.pendingIndex ? await this.owned.startToolCall({
7646
- runId: input.run.id,
7647
- execution: input.execution,
7648
- expectedRevision: revision,
7649
- toolCallId
7650
- }) : void 0;
8501
+ const pendingPres = await presentToolPending(durableTool, toJsonValue(toolCall.args));
8502
+ const currentToolState = {
8503
+ ...pendingPres,
8504
+ rawInput: toJsonValue(toolCall.args)
8505
+ };
8506
+ const resumedStart = suspension && index === suspension.pendingIndex ? await (async () => {
8507
+ const inProgressPres = await presentToolCall(durableTool, {
8508
+ status: "in_progress",
8509
+ args: toJsonValue(toolCall.args)
8510
+ });
8511
+ if (inProgressPres) mergeToolCall(currentToolState, inProgressPres);
8512
+ return this.owned.startToolCall({
8513
+ runId: input.run.id,
8514
+ execution: input.execution,
8515
+ expectedRevision: revision,
8516
+ toolCallId,
8517
+ title: currentToolState.title ?? durableTool.annotations?.title ?? durableTool.name,
8518
+ kind: currentToolState.kind ?? durableTool.kind ?? "other",
8519
+ ...currentToolState.locations !== void 0 ? { locations: currentToolState.locations } : {},
8520
+ ...currentToolState.content !== void 0 ? { content: currentToolState.content } : {},
8521
+ ...currentToolState._meta !== void 0 ? { _meta: currentToolState._meta } : {}
8522
+ });
8523
+ })() : void 0;
7651
8524
  if (resumedStart) setRevision(resumedStart.run.revision);
8525
+ const baseDriver = input.driver ?? createRunInteractionDriver({
8526
+ currentPhase: "default",
8527
+ attempt: 0,
8528
+ status: "running",
8529
+ metrics: { iterations: 0, phaseTransitions: [], compactionCount: 0, retryCount: 0, startedAt: (/* @__PURE__ */ new Date()).toISOString(), startedAtMs: Date.now() }
8530
+ }, "default", input.signal);
8531
+ const interactionDriver = createToolInteractionDriver(baseDriver, {
8532
+ toolCallId,
8533
+ ...suspension?.toolCallIds === void 0 ? {} : { toolCallIds: suspension.toolCallIds },
8534
+ toolCalls: input.toolCalls,
8535
+ pendingIndex: index,
8536
+ completedResults: results
8537
+ });
7652
8538
  const context = {
7653
8539
  agentId: input.run.agentId,
7654
8540
  runId: input.run.id,
7655
8541
  ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
7656
8542
  ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
7657
8543
  toolCallId,
8544
+ providerToolCallId,
7658
8545
  scope,
8546
+ ...cwd ? { cwd } : {},
7659
8547
  turn,
7660
- interaction: createToolInteractionDriver(input.driver, {
7661
- toolCallId,
7662
- ...suspension?.toolCallIds === void 0 ? {} : { toolCallIds: suspension.toolCallIds },
7663
- toolCalls: input.toolCalls,
7664
- pendingIndex: index,
7665
- completedResults: results
7666
- }),
7667
- reportProgress: (progress) => {
7668
- const active = this.executions.get(input.run.id);
7669
- if (!progressActive || this.closed || input.signal.aborted || active?.executionId !== input.execution.executionId || !isJsonValue(progress)) return;
7670
- let copy;
7671
- try {
7672
- if (JSON.stringify(progress).length > 64 * 1024) return;
7673
- copy = structuredClone(progress);
7674
- } catch {
7675
- return;
7676
- }
7677
- this.transientEvents.publish({
7678
- kind: "tool_progress",
7679
- durability: "transient",
7680
- runId: input.run.id,
7681
- executionId: input.execution.executionId,
7682
- toolCallId,
7683
- progress: copy
7684
- });
8548
+ interaction: interactionDriver,
8549
+ reportProgress: () => {
7685
8550
  }
7686
8551
  };
7687
8552
  const answer = suspension && index === suspension.pendingIndex ? suspension.interactionIds?.map((id) => input.driver?.answers().get(id)).find((value) => value !== void 0) : void 0;
@@ -7698,31 +8563,43 @@ var AgentRuntime = class _AgentRuntime {
7698
8563
  if (!input.driver) {
7699
8564
  throw new Error("Tool call requested interaction but no interaction driver is active.");
7700
8565
  }
7701
- context.interaction.request(decision.interaction);
7702
- if (!(suspension && index === suspension.pendingIndex)) {
7703
- const reserved2 = await this.owned.reserveToolCalls({
8566
+ interactionDriver.request(decision.interaction);
8567
+ if (!resumedStart) {
8568
+ const reserved = await this.owned.reserveToolCalls({
7704
8569
  runId: input.run.id,
7705
8570
  execution: input.execution,
7706
8571
  expectedRevision: revision,
7707
8572
  requestMessageId: createId("msg"),
7708
8573
  contentBlocks: input.contentBlocks,
7709
- calls: [{ toolCallId, providerToolCallId, name: toolCall.name, args: toJsonValue(toolCall.args) }]
8574
+ calls: [{
8575
+ toolCallId,
8576
+ providerToolCallId,
8577
+ name: toolCall.name,
8578
+ args: toJsonValue(toolCall.args),
8579
+ ...pendingPres
8580
+ }]
7710
8581
  });
7711
- setRevision(reserved2.run.revision);
8582
+ setRevision(reserved.run.revision);
7712
8583
  }
7713
- context.interaction.suspend();
8584
+ interactionDriver.suspend();
7714
8585
  }
7715
8586
  if ("allow" in decision && !decision.allow) {
7716
- if (!(suspension && index === suspension.pendingIndex)) {
7717
- const reserved2 = await this.owned.reserveToolCalls({
8587
+ if (!resumedStart) {
8588
+ const reserved = await this.owned.reserveToolCalls({
7718
8589
  runId: input.run.id,
7719
8590
  execution: input.execution,
7720
8591
  expectedRevision: revision,
7721
8592
  requestMessageId: createId("msg"),
7722
8593
  contentBlocks: input.contentBlocks,
7723
- calls: [{ toolCallId, providerToolCallId, name: toolCall.name, args: toJsonValue(toolCall.args) }]
8594
+ calls: [{
8595
+ toolCallId,
8596
+ providerToolCallId,
8597
+ name: toolCall.name,
8598
+ args: toJsonValue(toolCall.args),
8599
+ ...pendingPres
8600
+ }]
7724
8601
  });
7725
- setRevision(reserved2.run.revision);
8602
+ setRevision(reserved.run.revision);
7726
8603
  }
7727
8604
  const failed = await this.owned.commitToolResult({
7728
8605
  runId: input.run.id,
@@ -7730,9 +8607,27 @@ var AgentRuntime = class _AgentRuntime {
7730
8607
  expectedRevision: revision,
7731
8608
  toolCallId,
7732
8609
  state: "failed",
7733
- result: { ok: false, content: null, error: decision.reason }
8610
+ result: { ok: false, content: null, error: decision.reason },
8611
+ title: currentToolState.title ?? durableTool.annotations?.title ?? durableTool.name,
8612
+ kind: currentToolState.kind ?? durableTool.kind ?? "other",
8613
+ locations: currentToolState.locations,
8614
+ content: currentToolState.content,
8615
+ _meta: currentToolState._meta
7734
8616
  });
7735
8617
  setRevision(failed.run.revision);
8618
+ if (input.options?.onUpdate) {
8619
+ try {
8620
+ input.options.onUpdate(createMergedToolCall(
8621
+ toolCallId,
8622
+ "failed",
8623
+ currentToolState,
8624
+ durableTool.annotations?.title ?? durableTool.name,
8625
+ durableTool.kind ?? "other"
8626
+ ));
8627
+ } catch (err) {
8628
+ console.warn(`Error in tool call onUpdate callback:`, err);
8629
+ }
8630
+ }
7736
8631
  results.push(protocolFailure(decision.reason));
7737
8632
  continue;
7738
8633
  }
@@ -7742,16 +8637,22 @@ var AgentRuntime = class _AgentRuntime {
7742
8637
  throw error;
7743
8638
  }
7744
8639
  const reason = error instanceof Error ? error.message : "Tool policy rejected the call.";
7745
- if (!(suspension && index === suspension.pendingIndex)) {
7746
- const reserved2 = await this.owned.reserveToolCalls({
8640
+ if (!resumedStart) {
8641
+ const reserved = await this.owned.reserveToolCalls({
7747
8642
  runId: input.run.id,
7748
8643
  execution: input.execution,
7749
8644
  expectedRevision: revision,
7750
8645
  requestMessageId: createId("msg"),
7751
8646
  contentBlocks: input.contentBlocks,
7752
- calls: [{ toolCallId, providerToolCallId, name: toolCall.name, args: toJsonValue(toolCall.args) }]
8647
+ calls: [{
8648
+ toolCallId,
8649
+ providerToolCallId,
8650
+ name: toolCall.name,
8651
+ args: toJsonValue(toolCall.args),
8652
+ ...pendingPres
8653
+ }]
7753
8654
  });
7754
- setRevision(reserved2.run.revision);
8655
+ setRevision(reserved.run.revision);
7755
8656
  }
7756
8657
  const failed = await this.owned.commitToolResult({
7757
8658
  runId: input.run.id,
@@ -7759,94 +8660,316 @@ var AgentRuntime = class _AgentRuntime {
7759
8660
  expectedRevision: revision,
7760
8661
  toolCallId,
7761
8662
  state: "failed",
7762
- result: { ok: false, content: null, error: reason }
8663
+ result: { ok: false, content: null, error: reason },
8664
+ title: currentToolState.title ?? durableTool.annotations?.title ?? durableTool.name,
8665
+ kind: currentToolState.kind ?? durableTool.kind ?? "other",
8666
+ locations: currentToolState.locations,
8667
+ content: currentToolState.content,
8668
+ _meta: currentToolState._meta
7763
8669
  });
7764
8670
  setRevision(failed.run.revision);
8671
+ if (input.options?.onUpdate) {
8672
+ try {
8673
+ input.options.onUpdate(createMergedToolCall(
8674
+ toolCallId,
8675
+ "failed",
8676
+ currentToolState,
8677
+ durableTool.annotations?.title ?? durableTool.name,
8678
+ durableTool.kind ?? "other"
8679
+ ));
8680
+ } catch (err) {
8681
+ console.warn(`Error in tool call onUpdate callback:`, err);
8682
+ }
8683
+ }
7765
8684
  results.push(protocolFailure(reason));
7766
8685
  continue;
7767
8686
  }
7768
- const reserved = resumedStart ?? await (async () => {
7769
- const committed = await this.owned.reserveToolCalls({
8687
+ if (!resumedStart) {
8688
+ const reserved = await this.owned.reserveToolCalls({
7770
8689
  runId: input.run.id,
7771
8690
  execution: input.execution,
7772
8691
  expectedRevision: revision,
7773
8692
  requestMessageId: createId("msg"),
7774
8693
  contentBlocks: input.contentBlocks,
7775
- calls: [{ toolCallId, providerToolCallId, name: toolCall.name, args: toJsonValue(toolCall.args) }]
8694
+ calls: [{
8695
+ toolCallId,
8696
+ providerToolCallId,
8697
+ name: toolCall.name,
8698
+ args: toJsonValue(toolCall.args),
8699
+ ...pendingPres
8700
+ }]
7776
8701
  });
7777
- setRevision(committed.run.revision);
7778
- return this.owned.startToolCall({
7779
- runId: input.run.id,
7780
- execution: input.execution,
7781
- expectedRevision: revision,
7782
- toolCallId
8702
+ setRevision(reserved.run.revision);
8703
+ const inProgressPres = await presentToolCall(durableTool, {
8704
+ status: "in_progress",
8705
+ args: toJsonValue(toolCall.args)
7783
8706
  });
7784
- })();
7785
- if (reserved) setRevision(reserved.run.revision);
7786
- progressActive = true;
7787
- try {
7788
- let result = await durableTool.execute(toJsonValue(toolCall.args), context, input.signal);
7789
- assertToolExecutionResult(result);
7790
- if (input.toolConfig.afterToolCall) {
7791
- result = await input.toolConfig.afterToolCall({
7792
- tool: durableTool,
7793
- result,
7794
- context,
7795
- signal: input.signal
7796
- });
7797
- }
7798
- assertToolExecutionResult(result);
7799
- result = await spillLargeToolResult(
7800
- result,
7801
- this.archiveDirFor(input.run.agentId),
7802
- toolCall.name
7803
- );
7804
- const committed = await this.owned.commitToolResult({
8707
+ if (inProgressPres) mergeToolCall(currentToolState, inProgressPres);
8708
+ const started = await this.owned.startToolCall({
7805
8709
  runId: input.run.id,
7806
8710
  execution: input.execution,
7807
8711
  expectedRevision: revision,
7808
8712
  toolCallId,
7809
- state: result.ok ? "completed" : "failed",
7810
- result
8713
+ title: currentToolState.title ?? durableTool.annotations?.title ?? durableTool.name,
8714
+ kind: currentToolState.kind ?? durableTool.kind ?? "other",
8715
+ ...currentToolState.locations !== void 0 ? { locations: currentToolState.locations } : {},
8716
+ ...currentToolState.content !== void 0 ? { content: currentToolState.content } : {},
8717
+ ...currentToolState._meta !== void 0 ? { _meta: currentToolState._meta } : {}
7811
8718
  });
7812
- setRevision(committed.run.revision);
7813
- const projectedResult = await this.foldToolInteractionRecords(result, toolCallId, input.run.agentId);
7814
- results.push({
7815
- toolCallId: providerToolCallId,
7816
- toolName: toolCall.name,
7817
- ...projectedResult
7818
- });
7819
- } catch (error) {
7820
- if (error instanceof RunInteractionBoundary) {
7821
- const suspended = await this.owned.suspendToolCall({
7822
- runId: input.run.id,
7823
- execution: input.execution,
7824
- expectedRevision: revision,
7825
- toolCallId
7826
- });
7827
- setRevision(suspended.run.revision);
7828
- throw error;
8719
+ setRevision(started.run.revision);
8720
+ }
8721
+ const toolResult2 = await this.executeAndCommitTool({
8722
+ runId: input.run.id,
8723
+ agentId: input.run.agentId,
8724
+ agentMetadata: input.agentMetadata,
8725
+ runMetadata: input.run.metadata,
8726
+ execution: input.execution,
8727
+ revision,
8728
+ setRevision,
8729
+ toolCall,
8730
+ durableTool,
8731
+ toolCallId,
8732
+ providerToolCallId,
8733
+ scope,
8734
+ cwd,
8735
+ turn,
8736
+ driver: input.driver,
8737
+ toolCalls: input.toolCalls,
8738
+ toolCallIds: suspension?.toolCallIds,
8739
+ pendingIndex: index,
8740
+ completedResults: results,
8741
+ signal: input.signal,
8742
+ afterToolCall: input.toolConfig.afterToolCall,
8743
+ currentToolState,
8744
+ options: input.options
8745
+ });
8746
+ results.push(toolResult2);
8747
+ }
8748
+ input.driver?.clearCheckpoint();
8749
+ return { results, revision };
8750
+ }
8751
+ async executeAndCommitTool(input) {
8752
+ let progressActive = true;
8753
+ let progressQueue = Promise.resolve();
8754
+ const notifyUpdate = (toolCall, progress) => {
8755
+ if (input.options?.onUpdate) {
8756
+ try {
8757
+ input.options.onUpdate(toolCall, progress);
8758
+ } catch (err) {
8759
+ console.warn(`Error in tool call onUpdate callback:`, err);
7829
8760
  }
7830
- if (error instanceof RunInteractionCancelledError) throw error;
7831
- const reason = error instanceof Error ? error.message : "Tool execution outcome is indeterminate.";
7832
- const indeterminate = await this.owned.commitToolResult({
7833
- runId: input.run.id,
8761
+ }
8762
+ };
8763
+ const startToolCall = createMergedToolCall(
8764
+ input.toolCallId,
8765
+ "in_progress",
8766
+ input.currentToolState,
8767
+ input.durableTool.annotations?.title ?? input.durableTool.name,
8768
+ input.durableTool.kind ?? "other"
8769
+ );
8770
+ notifyUpdate(startToolCall);
8771
+ const effectiveSignal = combineSignals(input.signal, input.options?.signal);
8772
+ const baseDriver = input.driver ?? createRunInteractionDriver({
8773
+ currentPhase: "default",
8774
+ attempt: 0,
8775
+ status: "running",
8776
+ metrics: { iterations: 0, phaseTransitions: [], compactionCount: 0, retryCount: 0, startedAt: (/* @__PURE__ */ new Date()).toISOString(), startedAtMs: Date.now() }
8777
+ }, "default", effectiveSignal);
8778
+ const interactionDriver = createToolInteractionDriver(baseDriver, {
8779
+ toolCallId: input.toolCallId,
8780
+ ...input.toolCallIds === void 0 ? {} : { toolCallIds: input.toolCallIds },
8781
+ toolCalls: input.toolCalls,
8782
+ pendingIndex: input.pendingIndex,
8783
+ completedResults: input.completedResults
8784
+ });
8785
+ const context = {
8786
+ agentId: input.agentId,
8787
+ runId: input.runId,
8788
+ ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
8789
+ ...input.runMetadata === void 0 ? {} : { runMetadata: input.runMetadata },
8790
+ toolCallId: input.toolCallId,
8791
+ providerToolCallId: input.providerToolCallId,
8792
+ scope: input.scope,
8793
+ ...input.cwd ? { cwd: input.cwd } : {},
8794
+ turn: input.turn,
8795
+ interaction: interactionDriver,
8796
+ reportProgress: (progress) => {
8797
+ const active = this.executions.get(input.runId);
8798
+ if (!progressActive || this.closed || effectiveSignal.aborted || active?.executionId !== input.execution.executionId) return;
8799
+ if (!isValidToolProgress(progress)) {
8800
+ console.warn(`Invalid progress report for tool "${input.durableTool.name}": expected valid ToolProgress shape with finite numbers.`);
8801
+ return;
8802
+ }
8803
+ let copy;
8804
+ try {
8805
+ if (JSON.stringify(progress).length > 64 * 1024) return;
8806
+ copy = structuredClone(progress);
8807
+ } catch {
8808
+ return;
8809
+ }
8810
+ progressQueue = progressQueue.then(async () => {
8811
+ if (!progressActive || this.closed || effectiveSignal.aborted) return;
8812
+ const pres = await presentToolCall(input.durableTool, {
8813
+ status: "in_progress",
8814
+ args: toJsonValue(input.toolCall.args),
8815
+ progress: copy
8816
+ });
8817
+ if (pres) {
8818
+ mergeToolCall(input.currentToolState, pres);
8819
+ const progressToolCall = createMergedToolCall(
8820
+ input.toolCallId,
8821
+ "in_progress",
8822
+ input.currentToolState,
8823
+ input.durableTool.annotations?.title ?? input.durableTool.name,
8824
+ input.durableTool.kind ?? "other"
8825
+ );
8826
+ this.transientEvents.publish({
8827
+ kind: "tool_state_changed",
8828
+ durability: "transient",
8829
+ runId: input.runId,
8830
+ executionId: input.execution.executionId,
8831
+ transition: { from: "in_progress", to: "in_progress" },
8832
+ toolCall: progressToolCall
8833
+ });
8834
+ notifyUpdate(progressToolCall, copy);
8835
+ }
8836
+ }).catch((err) => {
8837
+ console.warn(`Error handling progress report for tool "${input.durableTool.name}":`, err);
8838
+ });
8839
+ }
8840
+ };
8841
+ try {
8842
+ if (input.options?.signal?.aborted) {
8843
+ const error = input.options.signal.reason instanceof Error ? input.options.signal.reason : new DOMException(
8844
+ typeof input.options.signal.reason === "string" ? input.options.signal.reason : "Tool execution aborted.",
8845
+ "AbortError"
8846
+ );
8847
+ throw error;
8848
+ }
8849
+ let result = await withAbort(
8850
+ input.durableTool.execute(toJsonValue(input.toolCall.args), context, effectiveSignal),
8851
+ input.options?.signal
8852
+ );
8853
+ assertToolExecutionResult(result);
8854
+ if (input.afterToolCall) {
8855
+ result = await input.afterToolCall({
8856
+ tool: input.durableTool,
8857
+ result,
8858
+ context,
8859
+ signal: effectiveSignal
8860
+ });
8861
+ }
8862
+ assertToolExecutionResult(result);
8863
+ result = await spillLargeToolResult(
8864
+ result,
8865
+ this.archiveDirFor(input.agentId),
8866
+ input.toolCall.name
8867
+ );
8868
+ await progressQueue.catch(() => {
8869
+ });
8870
+ const finalState = result.ok ? "completed" : "failed";
8871
+ const completionPres = await presentToolCall(input.durableTool, {
8872
+ status: finalState,
8873
+ args: toJsonValue(input.toolCall.args),
8874
+ result
8875
+ });
8876
+ if (completionPres) {
8877
+ mergeToolCall(input.currentToolState, completionPres);
8878
+ }
8879
+ const rawOutput = "structuredContent" in result && result.structuredContent !== void 0 ? toJsonValue(result.structuredContent) : toJsonValue(result.content);
8880
+ if (input.currentToolState.rawOutput === void 0) {
8881
+ input.currentToolState.rawOutput = rawOutput;
8882
+ }
8883
+ const committed = await this.owned.commitToolResult({
8884
+ runId: input.runId,
8885
+ execution: input.execution,
8886
+ expectedRevision: input.revision,
8887
+ toolCallId: input.toolCallId,
8888
+ state: finalState,
8889
+ result,
8890
+ title: input.currentToolState.title ?? input.durableTool.annotations?.title ?? input.durableTool.name,
8891
+ kind: input.currentToolState.kind ?? input.durableTool.kind ?? "other",
8892
+ locations: input.currentToolState.locations,
8893
+ content: input.currentToolState.content,
8894
+ rawOutput: input.currentToolState.rawOutput,
8895
+ _meta: input.currentToolState._meta
8896
+ });
8897
+ input.setRevision(committed.run.revision);
8898
+ const finalToolCall = createMergedToolCall(
8899
+ input.toolCallId,
8900
+ finalState,
8901
+ input.currentToolState,
8902
+ input.durableTool.annotations?.title ?? input.durableTool.name,
8903
+ input.durableTool.kind ?? "other"
8904
+ );
8905
+ notifyUpdate(finalToolCall);
8906
+ const projectedResult = await this.foldToolInteractionRecords(result, input.toolCallId, input.agentId);
8907
+ const ok = "ok" in projectedResult && typeof projectedResult.ok === "boolean" ? projectedResult.ok : !projectedResult.isError;
8908
+ return {
8909
+ toolCallId: input.providerToolCallId,
8910
+ toolName: input.toolCall.name,
8911
+ ...projectedResult,
8912
+ ok
8913
+ };
8914
+ } catch (error) {
8915
+ if (error instanceof RunInteractionBoundary) {
8916
+ const suspended = await this.owned.suspendToolCall({
8917
+ runId: input.runId,
7834
8918
  execution: input.execution,
7835
- expectedRevision: revision,
7836
- toolCallId,
7837
- state: "indeterminate",
7838
- reason,
7839
- result: { ok: false, content: null, error: reason }
8919
+ expectedRevision: input.revision,
8920
+ toolCallId: input.toolCallId
7840
8921
  });
7841
- setRevision(indeterminate.run.revision);
7842
- results.push(protocolFailure(reason));
7843
- } finally {
7844
- progressActive = false;
7845
- this.transientEvents.clearTool(input.run.id, toolCallId);
8922
+ input.setRevision(suspended.run.revision);
8923
+ throw error;
7846
8924
  }
8925
+ if (error instanceof RunInteractionCancelledError) throw error;
8926
+ const isPerCallAbort = Boolean(input.options?.signal?.aborted && !input.signal.aborted);
8927
+ const reason = error instanceof Error ? error.message : isPerCallAbort ? "Tool execution aborted." : "Tool execution outcome is indeterminate.";
8928
+ await progressQueue.catch(() => {
8929
+ });
8930
+ const failurePres = await presentToolCall(input.durableTool, {
8931
+ status: "failed",
8932
+ args: toJsonValue(input.toolCall.args),
8933
+ result: { ok: false, content: null, error: reason }
8934
+ });
8935
+ if (failurePres) {
8936
+ mergeToolCall(input.currentToolState, failurePres);
8937
+ }
8938
+ const commitState = isPerCallAbort ? "failed" : "indeterminate";
8939
+ const committed = await this.owned.commitToolResult({
8940
+ runId: input.runId,
8941
+ execution: input.execution,
8942
+ expectedRevision: input.revision,
8943
+ toolCallId: input.toolCallId,
8944
+ state: commitState,
8945
+ ...isPerCallAbort ? {} : { reason },
8946
+ result: { ok: false, content: null, error: reason },
8947
+ title: input.currentToolState.title ?? input.durableTool.annotations?.title ?? input.durableTool.name,
8948
+ kind: input.currentToolState.kind ?? input.durableTool.kind ?? "other",
8949
+ locations: input.currentToolState.locations,
8950
+ content: input.currentToolState.content,
8951
+ _meta: input.currentToolState._meta
8952
+ });
8953
+ input.setRevision(committed.run.revision);
8954
+ const finalToolCall = createMergedToolCall(
8955
+ input.toolCallId,
8956
+ "failed",
8957
+ input.currentToolState,
8958
+ input.durableTool.annotations?.title ?? input.durableTool.name,
8959
+ input.durableTool.kind ?? "other"
8960
+ );
8961
+ notifyUpdate(finalToolCall);
8962
+ return {
8963
+ toolCallId: input.providerToolCallId,
8964
+ toolName: input.toolCall.name,
8965
+ ok: false,
8966
+ content: null,
8967
+ error: reason
8968
+ };
8969
+ } finally {
8970
+ progressActive = false;
8971
+ this.transientEvents.clearTool(input.runId, input.toolCallId);
7847
8972
  }
7848
- input.driver?.clearCheckpoint();
7849
- return { results, revision };
7850
8973
  }
7851
8974
  async foldToolInteractionRecords(result, toolCallId, agentId) {
7852
8975
  const interactionMessages = (await this.owned.history(agentId)).filter(
@@ -7855,11 +8978,27 @@ var AgentRuntime = class _AgentRuntime {
7855
8978
  if (interactionMessages.length === 0) return result;
7856
8979
  const interactionText = interactionMessages.filter((message) => message.role === "interaction").map(renderInteractionText).join("\n\n");
7857
8980
  const content = typeof result.content === "string" ? result.content : JSON.stringify(result.content) ?? "null";
7858
- return {
7859
- ...result,
7860
- content: content === "null" || content.length === 0 ? interactionText : `${interactionText}
8981
+ const appendedText = content === "null" || content.length === 0 ? interactionText : `${interactionText}
7861
8982
 
7862
- ${content}`
8983
+ ${content}`;
8984
+ if (Array.isArray(result.content)) {
8985
+ return {
8986
+ ...result,
8987
+ content: [{ type: "text", text: appendedText }]
8988
+ };
8989
+ }
8990
+ if (result.ok) {
8991
+ return {
8992
+ ok: true,
8993
+ content: appendedText,
8994
+ ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {}
8995
+ };
8996
+ }
8997
+ return {
8998
+ ok: false,
8999
+ content: appendedText,
9000
+ error: result.error ?? "Tool execution failed.",
9001
+ ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {}
7863
9002
  };
7864
9003
  }
7865
9004
  async requireAgent(agentId) {
@@ -7947,8 +9086,8 @@ ${content}`
7947
9086
  if (event.runId !== runId) continue;
7948
9087
  if (event.kind === "message_committed") {
7949
9088
  subscription.clearMessage(event.message.id);
7950
- } else if (event.kind === "tool_state_changed" && ["completed", "failed", "indeterminate"].includes(event.transition.to)) {
7951
- subscription.clearTool(event.toolCall.id);
9089
+ } else if (event.kind === "tool_state_changed" && ["completed", "failed"].includes(event.transition.to)) {
9090
+ subscription.clearTool(event.toolCall.toolCallId);
7952
9091
  }
7953
9092
  if (event.kind === "run_state_changed" && ["completed", "failed", "cancelled"].includes(event.to)) {
7954
9093
  while (true) {
@@ -8146,6 +9285,19 @@ function toJsonValue(value) {
8146
9285
  assertJsonValue(value, "tool arguments");
8147
9286
  return value;
8148
9287
  }
9288
+ function createMergedToolCall(toolCallId, status, current, fallbackTitle, fallbackKind = "other") {
9289
+ return {
9290
+ toolCallId,
9291
+ title: current.title ?? fallbackTitle,
9292
+ kind: current.kind ?? fallbackKind,
9293
+ status,
9294
+ ...current.locations !== void 0 ? { locations: current.locations } : {},
9295
+ ...current.content !== void 0 ? { content: current.content } : {},
9296
+ ...current.rawInput !== void 0 ? { rawInput: current.rawInput } : {},
9297
+ ...current.rawOutput !== void 0 ? { rawOutput: current.rawOutput } : {},
9298
+ ...current._meta !== void 0 ? { _meta: current._meta } : {}
9299
+ };
9300
+ }
8149
9301
  function deferred2() {
8150
9302
  let resolve9;
8151
9303
  let reject;
@@ -8158,6 +9310,24 @@ function deferred2() {
8158
9310
 
8159
9311
  // src/runtime/durable-store.ts
8160
9312
  import { createHash } from "crypto";
9313
+ function defaultToolCallContent(result) {
9314
+ if ("content" in result && Array.isArray(result.content)) {
9315
+ return result.content.map((block) => {
9316
+ if (typeof block === "object" && block !== null && "type" in block && (block.type === "content" || block.type === "diff" || block.type === "terminal")) {
9317
+ return block;
9318
+ }
9319
+ return {
9320
+ type: "content",
9321
+ content: block
9322
+ };
9323
+ });
9324
+ }
9325
+ const content = result.content;
9326
+ if (typeof content === "string") {
9327
+ return [{ type: "content", content: { type: "text", text: content } }];
9328
+ }
9329
+ return [{ type: "content", content: { type: "text", text: JSON.stringify(content ?? null) } }];
9330
+ }
8161
9331
  var InMemoryStore = class _InMemoryStore {
8162
9332
  incarnation;
8163
9333
  agents = /* @__PURE__ */ new Map();
@@ -8276,7 +9446,7 @@ var InMemoryStore = class _InMemoryStore {
8276
9446
  }
8277
9447
  }
8278
9448
  for (const message of state.messages) store.messages.set(message.id, clone(message));
8279
- for (const toolCall of state.toolCalls ?? []) store.toolCalls.set(toolCall.id, clone(toolCall));
9449
+ for (const toolCall of state.toolCalls ?? []) store.toolCalls.set(toolCall.id ?? toolCall.toolCallId, clone(toolCall));
8280
9450
  store.events.push(...clone(state.events));
8281
9451
  for (const [key, receipt] of state.idempotency) store.idempotency.set(key, clone(receipt));
8282
9452
  for (const [key, receipt] of state.operationReceipts) {
@@ -8341,10 +9511,10 @@ var InMemoryStore = class _InMemoryStore {
8341
9511
  this.ownerEpoch = Math.max(this.ownerEpoch, ownerEpoch);
8342
9512
  for (const run of this.runs.values()) {
8343
9513
  if (run.state !== "running" || run.execution?.ownerEpoch !== ownerEpoch) continue;
8344
- const activeToolCalls = [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.state === "pending" || toolCall.state === "running"));
9514
+ const activeToolCalls = [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.status === "pending" || toolCall.status === "in_progress" || toolCall.state === "pending" || toolCall.state === "in_progress"));
8345
9515
  const indeterminateToolCallIds = [];
8346
9516
  for (const toolCall of activeToolCalls) {
8347
- if (toolCall.state === "running") indeterminateToolCallIds.push(toolCall.id);
9517
+ if (toolCall.status === "in_progress" || toolCall.state === "in_progress") indeterminateToolCallIds.push(toolCall.id);
8348
9518
  this.interruptToolCall(run, toolCall, message);
8349
9519
  }
8350
9520
  const failure = indeterminateToolCallIds.length > 0 ? { code: "tool_indeterminate", message, toolCallIds: indeterminateToolCallIds } : { code: "runtime_interrupted", message, ownerEpoch };
@@ -8604,7 +9774,7 @@ var InMemoryStore = class _InMemoryStore {
8604
9774
  run.invalidatedBy = { messageId: original.id, messageRevision: actualRevision + 1 };
8605
9775
  if (run.state === "running" || run.state === "queued" || run.state === "input_required") {
8606
9776
  if (run.state === "running") {
8607
- const openToolCalls = [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.state === "pending" || toolCall.state === "running"));
9777
+ const openToolCalls = [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.status === "pending" || toolCall.status === "in_progress" || toolCall.state === "pending" || toolCall.state === "in_progress"));
8608
9778
  for (const toolCall of openToolCalls) this.interruptToolCall(run, toolCall, "Run superseded by Message revision.");
8609
9779
  } else {
8610
9780
  delete run.openInteractions;
@@ -8700,7 +9870,7 @@ var InMemoryStore = class _InMemoryStore {
8700
9870
  if (input.phasePayload !== void 0) assertJsonValue(input.phasePayload, "run.phasePayload");
8701
9871
  if (input.entryPhases !== void 0) assertEntryPhases(input.entryPhases, "run.entryPhases");
8702
9872
  const scope = createIdempotencyScope("start_run", input.agentId, input.idempotencyKey);
8703
- const payload = canonicalStartRunRequest(normalizedInput, input.metadata, input.phasePayload, input.pinnedConfigToken, input.entryPhases);
9873
+ const payload = canonicalStartRunRequest(normalizedInput, input.metadata, input.phasePayload, input.pinnedConfigToken, input.entryPhases, input.scope, input.cwd);
8704
9874
  const replay = this.replay(scope, payload);
8705
9875
  if (replay) return clone(replay);
8706
9876
  const waitingRun = isControlMetadata(input.metadata) ? void 0 : [...this.runs.values()].find((candidate) => candidate.agentId === input.agentId && candidate.state === "input_required");
@@ -8756,6 +9926,8 @@ var InMemoryStore = class _InMemoryStore {
8756
9926
  state: "queued",
8757
9927
  input: clone(normalizedInput),
8758
9928
  ...input.metadata ? { metadata: clone(input.metadata) } : {},
9929
+ ...input.scope !== void 0 ? { scope: clone(input.scope) } : {},
9930
+ ...input.cwd !== void 0 ? { cwd: input.cwd } : {},
8759
9931
  ...input.phasePayload === void 0 ? {} : { phasePayload: clone(input.phasePayload) },
8760
9932
  ...input.entryPhases === void 0 ? {} : { entryPhases: clone(input.entryPhases) },
8761
9933
  ...input.pinnedConfigToken === void 0 ? {} : { pinnedConfigToken: input.pinnedConfigToken },
@@ -9031,19 +10203,30 @@ var InMemoryStore = class _InMemoryStore {
9031
10203
  this.assertExecution(run, input.execution, input.expectedRevision);
9032
10204
  if (this.messages.has(input.requestMessageId)) throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
9033
10205
  const timestamp = createTimestamp();
9034
- const toolCalls = input.calls.map((call) => ({
9035
- id: call.toolCallId ?? createId("tool"),
9036
- providerToolCallId: call.providerToolCallId,
9037
- agentId: run.agentId,
9038
- runId: run.id,
9039
- executionId: input.execution.executionId,
9040
- requestMessageId: input.requestMessageId,
9041
- name: call.name,
9042
- args: clone(call.args),
9043
- state: "pending",
9044
- createdAt: timestamp,
9045
- updatedAt: timestamp
9046
- }));
10206
+ const toolCalls = input.calls.map((call) => {
10207
+ const toolCallId = call.providerToolCallId || (call.toolCallId ?? createId("tool"));
10208
+ return {
10209
+ id: call.toolCallId ?? createId("tool"),
10210
+ toolCallId,
10211
+ ...call.providerToolCallId ? { providerToolCallId: call.providerToolCallId } : {},
10212
+ agentId: run.agentId,
10213
+ runId: run.id,
10214
+ executionId: input.execution.executionId,
10215
+ requestMessageId: input.requestMessageId,
10216
+ name: call.name,
10217
+ title: call.title ?? call.name,
10218
+ kind: call.kind ?? "other",
10219
+ status: "pending",
10220
+ state: "pending",
10221
+ ...call.content ? { content: clone(call.content) } : {},
10222
+ ...call.locations ? { locations: clone(call.locations) } : {},
10223
+ rawInput: clone(call.args),
10224
+ ...call._meta ? { _meta: clone(call._meta) } : {},
10225
+ args: clone(call.args),
10226
+ createdAt: timestamp,
10227
+ updatedAt: timestamp
10228
+ };
10229
+ });
9047
10230
  for (const toolCall of toolCalls) {
9048
10231
  if (this.toolCalls.has(toolCall.id)) throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
9049
10232
  }
@@ -9079,15 +10262,27 @@ var InMemoryStore = class _InMemoryStore {
9079
10262
  const run = this.requireRun(input.runId);
9080
10263
  this.assertExecution(run, input.execution, input.expectedRevision);
9081
10264
  const toolCall = this.requireToolCall(input.toolCallId, run);
9082
- if (toolCall.state !== "pending") throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
9083
- toolCall.state = "running";
10265
+ if (toolCall.status !== "pending" && toolCall.state !== "pending") throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
10266
+ const from = toolCall.status ?? "pending";
10267
+ toolCall.status = "in_progress";
10268
+ toolCall.state = "in_progress";
9084
10269
  toolCall.executionId = input.execution.executionId;
10270
+ if (input.title !== void 0) toolCall.title = input.title;
10271
+ if (input.kind !== void 0) toolCall.kind = input.kind;
10272
+ if (input.locations !== void 0) toolCall.locations = clone(input.locations);
10273
+ if (input.content !== void 0) toolCall.content = clone(input.content);
10274
+ if (input._meta !== void 0) {
10275
+ toolCall._meta = {
10276
+ ...toolCall._meta ?? {},
10277
+ ...clone(input._meta)
10278
+ };
10279
+ }
9085
10280
  toolCall.updatedAt = createTimestamp();
9086
10281
  this.storeToolCall(toolCall);
9087
10282
  run.revision += 1;
9088
10283
  run.updatedAt = toolCall.updatedAt;
9089
10284
  this.touchRun(run);
9090
- this.appendToolTransition(run, { from: "pending", to: "running" }, toolCall);
10285
+ this.appendToolTransition(run, { from, to: "in_progress" }, toolCall);
9091
10286
  const result = { run: clone(run), toolCall: clone(toolCall) };
9092
10287
  this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
9093
10288
  return result;
@@ -9101,16 +10296,18 @@ var InMemoryStore = class _InMemoryStore {
9101
10296
  const run = this.requireRun(input.runId);
9102
10297
  this.assertExecution(run, input.execution, input.expectedRevision);
9103
10298
  const toolCall = this.requireToolCall(input.toolCallId, run);
9104
- if (toolCall.state !== "running" || toolCall.executionId !== input.execution.executionId) {
10299
+ if (toolCall.status !== "in_progress" && toolCall.state !== "in_progress" || toolCall.executionId !== input.execution.executionId) {
9105
10300
  throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
9106
10301
  }
10302
+ const from = toolCall.status ?? "in_progress";
10303
+ toolCall.status = "pending";
9107
10304
  toolCall.state = "pending";
9108
10305
  toolCall.updatedAt = createTimestamp();
9109
10306
  this.storeToolCall(toolCall);
9110
10307
  run.revision += 1;
9111
10308
  run.updatedAt = toolCall.updatedAt;
9112
10309
  this.touchRun(run);
9113
- this.appendToolTransition(run, { from: "running", to: "pending" }, toolCall);
10310
+ this.appendToolTransition(run, { from, to: "pending" }, toolCall);
9114
10311
  const result = { run: clone(run), toolCall: clone(toolCall) };
9115
10312
  this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
9116
10313
  return result;
@@ -9119,8 +10316,9 @@ var InMemoryStore = class _InMemoryStore {
9119
10316
  this.assertOwner(lease);
9120
10317
  assertToolExecutionResult(input.result);
9121
10318
  assertToolValue(input.result, "tool.result");
9122
- if (input.state === "completed" && !input.result.ok) throw new TypeError("completed ToolCall requires a successful result");
9123
- if (input.state !== "completed" && input.result.ok) throw new TypeError(`${input.state} ToolCall requires a failed result`);
10319
+ const isCompleted = "ok" in input.result ? input.result.ok : !input.result.isError;
10320
+ if (input.state === "completed" && !isCompleted) throw new TypeError("completed ToolCall requires a successful result");
10321
+ if (input.state !== "completed" && isCompleted) throw new TypeError(`${input.state} ToolCall requires a failed result`);
9124
10322
  if (input.state === "indeterminate" && (!input.reason || input.reason.trim().length === 0)) throw new TypeError("indeterminate ToolCall requires a reason");
9125
10323
  const operationKey = `tool_commit:${input.toolCallId}`;
9126
10324
  const operationPayload = canonicalJson([input.runId, input.expectedRevision, input.execution.executionId, input.state, input.result, input.reason ?? null]);
@@ -9130,10 +10328,9 @@ var InMemoryStore = class _InMemoryStore {
9130
10328
  this.assertExecution(run, input.execution, input.expectedRevision);
9131
10329
  const toolCall = this.requireToolCall(input.toolCallId, run);
9132
10330
  if (toolCall.executionId !== input.execution.executionId) throw new RuntimeError("runtime_ownership_lost", { reason: "epoch_advanced", expectedEpoch: input.execution.ownerEpoch, actualEpoch: this.ownerEpoch });
9133
- const from = toolCall.state;
9134
- if (from === "pending" && input.state !== "failed") throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
9135
- if (from !== "pending" && from !== "running") throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
9136
- const durableResult = { toolCallId: toolCall.id, toolName: toolCall.name, ...clone(input.result) };
10331
+ const fromStatus = toolCall.status ?? (toolCall.state === "in_progress" ? "in_progress" : toolCall.state);
10332
+ const toStatus = input.state === "completed" ? "completed" : "failed";
10333
+ const durableResult = { toolCallId: toolCall.id, toolName: toolCall.name ?? toolCall.title, ...clone(input.result) };
9137
10334
  const resultMessageId = createId("msg");
9138
10335
  const message = {
9139
10336
  id: resultMessageId,
@@ -9145,7 +10342,21 @@ var InMemoryStore = class _InMemoryStore {
9145
10342
  createdAt: createTimestamp()
9146
10343
  };
9147
10344
  this.storeMessage(message);
9148
- toolCall.state = input.state;
10345
+ toolCall.status = toStatus;
10346
+ toolCall.state = toStatus;
10347
+ if (input.title !== void 0) toolCall.title = input.title;
10348
+ if (input.kind !== void 0) toolCall.kind = input.kind;
10349
+ if (input.locations !== void 0) toolCall.locations = clone(input.locations);
10350
+ if (input.content !== void 0) toolCall.content = clone(input.content);
10351
+ else if (toolCall.content === void 0) toolCall.content = defaultToolCallContent(input.result);
10352
+ if (input.rawOutput !== void 0) toolCall.rawOutput = clone(input.rawOutput);
10353
+ else if (toolCall.rawOutput === void 0) toolCall.rawOutput = "structuredContent" in input.result && input.result.structuredContent !== void 0 ? clone(input.result.structuredContent) : clone(input.result.content);
10354
+ if (input._meta !== void 0) {
10355
+ toolCall._meta = {
10356
+ ...toolCall._meta ?? {},
10357
+ ...clone(input._meta)
10358
+ };
10359
+ }
9149
10360
  toolCall.result = durableResult;
9150
10361
  toolCall.resultMessageId = resultMessageId;
9151
10362
  if (input.state === "indeterminate") toolCall.reason = input.reason;
@@ -9155,13 +10366,13 @@ var InMemoryStore = class _InMemoryStore {
9155
10366
  run.revision += 1;
9156
10367
  run.updatedAt = message.createdAt;
9157
10368
  this.touchRun(run);
9158
- this.appendToolTransition(run, { from, to: input.state }, toolCall);
10369
+ this.appendToolTransition(run, { from: fromStatus, to: toStatus }, toolCall);
9159
10370
  this.appendMessage(run, message);
9160
10371
  if (input.state === "indeterminate") {
9161
- const openToolCalls = [...this.toolCalls.values()].filter((candidate) => candidate.runId === run.id && candidate.executionId === input.execution.executionId && candidate.id !== toolCall.id && (candidate.state === "pending" || candidate.state === "running"));
10372
+ const openToolCalls = [...this.toolCalls.values()].filter((candidate) => candidate.runId === run.id && candidate.executionId === input.execution.executionId && candidate.id !== toolCall.id && (candidate.status === "pending" || candidate.status === "in_progress" || candidate.state === "pending" || candidate.state === "in_progress"));
9162
10373
  const indeterminateToolCallIds = [toolCall.id];
9163
10374
  for (const candidate of openToolCalls) {
9164
- if (candidate.state === "running") indeterminateToolCallIds.push(candidate.id);
10375
+ if (candidate.status === "in_progress" || candidate.state === "in_progress") indeterminateToolCallIds.push(candidate.id);
9165
10376
  this.interruptToolCall(run, candidate, input.reason);
9166
10377
  }
9167
10378
  const failure = {
@@ -9203,7 +10414,11 @@ var InMemoryStore = class _InMemoryStore {
9203
10414
  }
9204
10415
  run.state = nextState;
9205
10416
  if (input.outcome) run.outcome = clone(input.outcome);
9206
- if (input.failure) run.failure = clone(input.failure);
10417
+ let failure = input.failure ? clone(input.failure) : void 0;
10418
+ if (nextState === "failed" && failure) {
10419
+ failure = this.withdrawUserInputIfNoOutput(run, failure, output);
10420
+ run.failure = failure;
10421
+ }
9207
10422
  this.dropClaimReceipt(run.execution);
9208
10423
  delete run.execution;
9209
10424
  delete run.openInteractions;
@@ -9211,7 +10426,7 @@ var InMemoryStore = class _InMemoryStore {
9211
10426
  run.revision += 1;
9212
10427
  run.updatedAt = createTimestamp();
9213
10428
  this.touchRun(run);
9214
- this.appendTransition(run, "running", nextState, { outcome: input.outcome, failure: input.failure, output });
10429
+ this.appendTransition(run, "running", nextState, { outcome: input.outcome, failure, output });
9215
10430
  this.dropRunOperationReceipts(run.id);
9216
10431
  const result = clone(run);
9217
10432
  return result;
@@ -9236,14 +10451,14 @@ var InMemoryStore = class _InMemoryStore {
9236
10451
  return result2;
9237
10452
  }
9238
10453
  const from = run.state;
9239
- const activeToolCalls = run.execution ? [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.state === "pending" || toolCall.state === "running")) : run.state === "input_required" && run.checkpoint?.data && typeof run.checkpoint.data === "object" && !Array.isArray(run.checkpoint.data) ? (() => {
10454
+ const activeToolCalls = run.execution ? [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.status === "pending" || toolCall.status === "in_progress" || toolCall.state === "pending" || toolCall.state === "in_progress")) : run.state === "input_required" && run.checkpoint?.data && typeof run.checkpoint.data === "object" && !Array.isArray(run.checkpoint.data) ? (() => {
9240
10455
  const interactionState = run.checkpoint.data.runInteractions;
9241
10456
  const interactionCheckpoint = interactionState && typeof interactionState === "object" && !Array.isArray(interactionState) ? interactionState.checkpoint : void 0;
9242
10457
  const ids = interactionCheckpoint && typeof interactionCheckpoint === "object" && !Array.isArray(interactionCheckpoint) ? interactionCheckpoint.toolCallIds : void 0;
9243
10458
  const allowed = new Set(Array.isArray(ids) ? ids : []);
9244
- return [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.state === "pending" && allowed.has(toolCall.id));
10459
+ return [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && (toolCall.status === "pending" || toolCall.state === "pending") && allowed.has(toolCall.id));
9245
10460
  })() : [];
9246
- const indeterminateToolCallIds = activeToolCalls.filter((toolCall) => toolCall.state === "running").map((toolCall) => toolCall.id);
10461
+ const indeterminateToolCallIds = activeToolCalls.filter((toolCall) => toolCall.status === "in_progress" || toolCall.state === "in_progress").map((toolCall) => toolCall.id);
9247
10462
  for (const toolCall of activeToolCalls) this.interruptToolCall(run, toolCall, input.reason ?? "The Run was cancelled.");
9248
10463
  if (input.output && (typeof input.output.content === "string" ? input.output.content.length > 0 : input.output.content.length > 0)) {
9249
10464
  const output = { ...clone(input.output), sequenceWithinRun: this.nextMessageSequence(run.id) };
@@ -9290,6 +10505,74 @@ var InMemoryStore = class _InMemoryStore {
9290
10505
  const result = clone(run);
9291
10506
  return result;
9292
10507
  }
10508
+ recordToolCall(lease, input) {
10509
+ this.assertOwner(lease);
10510
+ const run = this.requireRun(input.runId);
10511
+ if (input.execution) {
10512
+ if (run.state !== "running" || !run.execution || run.execution.executionId !== input.execution.executionId || run.execution.ownerEpoch !== input.execution.ownerEpoch) {
10513
+ throw new RuntimeError("runtime_ownership_lost", {
10514
+ reason: "epoch_advanced",
10515
+ expectedEpoch: input.execution.ownerEpoch,
10516
+ actualEpoch: this.ownerEpoch
10517
+ });
10518
+ }
10519
+ }
10520
+ const toolCallId = input.update.toolCallId;
10521
+ const existing = this.toolCalls.get(toolCallId);
10522
+ const fromStatus = existing ? existing.status : null;
10523
+ const toStatus = input.update.status ?? (existing ? existing.status : "in_progress");
10524
+ const timestamp = createTimestamp();
10525
+ let stored;
10526
+ if (existing) {
10527
+ mergeToolCall(existing, input.update);
10528
+ existing.status = toStatus;
10529
+ existing.state = toStatus;
10530
+ if (input.external !== void 0) existing.external = input.external;
10531
+ existing.updatedAt = timestamp;
10532
+ stored = existing;
10533
+ } else {
10534
+ stored = {
10535
+ id: toolCallId,
10536
+ toolCallId,
10537
+ providerToolCallId: toolCallId,
10538
+ agentId: run.agentId,
10539
+ runId: run.id,
10540
+ executionId: input.execution?.executionId,
10541
+ title: "title" in input.update && input.update.title ? input.update.title : "external_tool",
10542
+ kind: "kind" in input.update && input.update.kind ? input.update.kind : "other",
10543
+ status: toStatus,
10544
+ state: toStatus,
10545
+ external: input.external ?? false,
10546
+ createdAt: timestamp,
10547
+ updatedAt: timestamp
10548
+ };
10549
+ mergeToolCall(stored, input.update);
10550
+ this.toolCalls.set(stored.id, stored);
10551
+ }
10552
+ this.storeToolCall(stored);
10553
+ run.revision += 1;
10554
+ run.updatedAt = timestamp;
10555
+ this.touchRun(run);
10556
+ const acpToolCall = {
10557
+ toolCallId: stored.toolCallId,
10558
+ title: stored.title,
10559
+ kind: stored.kind,
10560
+ status: stored.status,
10561
+ ...stored.content !== void 0 ? { content: clone(stored.content) } : {},
10562
+ ...stored.locations !== void 0 ? { locations: clone(stored.locations) } : {},
10563
+ ...stored.rawInput !== void 0 ? { rawInput: clone(stored.rawInput) } : {},
10564
+ ...stored.rawOutput !== void 0 ? { rawOutput: clone(stored.rawOutput) } : {},
10565
+ ...stored._meta !== void 0 ? { _meta: clone(stored._meta) } : {}
10566
+ };
10567
+ const event = this.baseEvent(run, {
10568
+ kind: "tool_state_changed",
10569
+ transition: { from: fromStatus, to: toStatus },
10570
+ toolCall: acpToolCall,
10571
+ ...input.external ? { external: true } : {}
10572
+ });
10573
+ this.appendEvent(event);
10574
+ return event;
10575
+ }
9293
10576
  snapshotRun(lease, runId) {
9294
10577
  this.assertOwner(lease);
9295
10578
  const run = this.requireRun(runId);
@@ -9300,6 +10583,8 @@ var InMemoryStore = class _InMemoryStore {
9300
10583
  revision: run.revision,
9301
10584
  input: clone(run.input),
9302
10585
  ...run.metadata ? { metadata: clone(run.metadata) } : {},
10586
+ ...run.scope !== void 0 ? { scope: clone(run.scope) } : {},
10587
+ ...run.cwd !== void 0 ? { cwd: run.cwd } : {},
9303
10588
  ...run.phasePayload === void 0 ? {} : { phasePayload: clone(run.phasePayload) },
9304
10589
  ...run.entryPhases === void 0 ? {} : { entryPhases: clone(run.entryPhases) },
9305
10590
  messageCount: this.messagesForRun(run.id).length,
@@ -9386,7 +10671,7 @@ var InMemoryStore = class _InMemoryStore {
9386
10671
  if (run.state === "running") blockedRunIds.add(run.id);
9387
10672
  }
9388
10673
  for (const tool of this.toolCalls.values()) {
9389
- if (tool.state === "pending" || tool.state === "running" || tool.state === "indeterminate") {
10674
+ if (tool.status === "pending" || tool.status === "in_progress" || tool.state === "pending" || tool.state === "in_progress") {
9390
10675
  blockedRunIds.add(tool.runId);
9391
10676
  }
9392
10677
  }
@@ -9527,13 +10812,13 @@ var InMemoryStore = class _InMemoryStore {
9527
10812
  }
9528
10813
  assertNoOpenTools(run, allowedPendingToolCallIds = []) {
9529
10814
  const allowed = new Set(allowedPendingToolCallIds);
9530
- if ([...this.toolCalls.values()].some((toolCall) => toolCall.runId === run.id && (toolCall.state === "running" || toolCall.state === "pending" && !allowed.has(toolCall.id)))) {
10815
+ if ([...this.toolCalls.values()].some((toolCall) => toolCall.runId === run.id && (toolCall.status === "in_progress" || toolCall.state === "in_progress" || (toolCall.status === "pending" || toolCall.state === "pending") && !allowed.has(toolCall.id)))) {
9531
10816
  throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
9532
10817
  }
9533
10818
  }
9534
10819
  interruptToolCall(run, toolCall, reason) {
9535
10820
  const result = { ok: false, content: null, error: reason };
9536
- const durableResult = { toolCallId: toolCall.id, toolName: toolCall.name, ...result };
10821
+ const durableResult = { toolCallId: toolCall.id, toolName: toolCall.name ?? toolCall.title, ...result };
9537
10822
  const message = {
9538
10823
  id: createId("msg"),
9539
10824
  agentId: run.agentId,
@@ -9543,13 +10828,14 @@ var InMemoryStore = class _InMemoryStore {
9543
10828
  sequenceWithinRun: this.nextMessageSequence(run.id),
9544
10829
  createdAt: createTimestamp()
9545
10830
  };
9546
- const from = toolCall.state;
9547
- const to = from === "running" ? "indeterminate" : "failed";
10831
+ const from = toolCall.status ?? toolCall.state ?? "in_progress";
10832
+ const to = "failed";
9548
10833
  this.storeMessage(message);
9549
10834
  toolCall.state = to;
10835
+ toolCall.status = to;
9550
10836
  toolCall.result = durableResult;
9551
10837
  toolCall.resultMessageId = message.id;
9552
- if (to === "indeterminate") toolCall.reason = reason;
10838
+ toolCall.reason = reason;
9553
10839
  toolCall.updatedAt = message.createdAt;
9554
10840
  this.storeToolCall(toolCall);
9555
10841
  run.revision += 1;
@@ -9586,6 +10872,28 @@ var InMemoryStore = class _InMemoryStore {
9586
10872
  messagesForRun(runId) {
9587
10873
  return [...this.messages.values()].filter((message) => message.runId === runId).sort((a, b) => a.sequenceWithinRun - b.sequenceWithinRun);
9588
10874
  }
10875
+ withdrawUserInputIfNoOutput(run, failure, output) {
10876
+ const runMessages = this.messagesForRun(run.id);
10877
+ const hasAssistantMessage = runMessages.some((m) => m.role === "assistant") || output !== void 0;
10878
+ const hasToolMessage = runMessages.some((m) => m.role === "tool");
10879
+ const hasToolCalls = [...this.toolCalls.values()].some((tc) => tc.runId === run.id);
10880
+ if (hasAssistantMessage || hasToolMessage || hasToolCalls) {
10881
+ return failure;
10882
+ }
10883
+ const userInputMessage = runMessages.find((m) => m.role === "user");
10884
+ if (!userInputMessage) {
10885
+ return failure;
10886
+ }
10887
+ this.messages.delete(userInputMessage.id);
10888
+ this.recorder?.onMessageDelete(userInputMessage.id);
10889
+ return {
10890
+ ...failure,
10891
+ withdrawnInput: {
10892
+ messageId: userInputMessage.id,
10893
+ content: clone(userInputMessage.content)
10894
+ }
10895
+ };
10896
+ }
9589
10897
  history(lease, agentId) {
9590
10898
  this.assertOwner(lease);
9591
10899
  this.requireAgent(agentId);
@@ -9678,18 +10986,34 @@ ${record.summary}`,
9678
10986
  to,
9679
10987
  ...to === "input_required" ? { interactions: clone(options?.interactions ?? []), answers: clone(options?.answers ?? {}) } : {},
9680
10988
  ...to === "completed" && options?.outcome ? { outcome: options.outcome, ...options.output ? { output: options.output } : {} } : {},
9681
- ...to === "failed" && options?.failure ? { failure: options.failure } : {},
10989
+ ...to === "failed" && options?.failure ? {
10990
+ failure: options.failure,
10991
+ ...options.failure.withdrawnInput ? { withdrawnInput: options.failure.withdrawnInput } : {}
10992
+ } : {},
9682
10993
  ...to === "cancelled" && options?.reason ? { reason: options.reason } : {}
9683
10994
  };
9684
10995
  this.touchRun(run);
9685
10996
  this.appendEvent(transition);
9686
10997
  }
9687
10998
  appendToolTransition(run, transition, toolCall) {
9688
- this.appendEvent(this.baseEvent(run, {
10999
+ const acpToolCall = {
11000
+ toolCallId: toolCall.toolCallId ?? toolCall.id,
11001
+ title: toolCall.title ?? toolCall.name ?? toolCall.toolCallId,
11002
+ kind: toolCall.kind ?? "other",
11003
+ status: toolCall.status ?? toolCall.state,
11004
+ ...toolCall.content ? { content: clone(toolCall.content) } : {},
11005
+ ...toolCall.locations ? { locations: clone(toolCall.locations) } : {},
11006
+ ...toolCall.rawInput !== void 0 ? { rawInput: clone(toolCall.rawInput) } : {},
11007
+ ...toolCall.rawOutput !== void 0 ? { rawOutput: clone(toolCall.rawOutput) } : {},
11008
+ ...toolCall._meta ? { _meta: clone(toolCall._meta) } : {}
11009
+ };
11010
+ this.appendEvent({
11011
+ ...this.baseEvent(run),
9689
11012
  kind: "tool_state_changed",
9690
11013
  transition,
9691
- toolCall: clone(toolCall)
9692
- }));
11014
+ toolCall: acpToolCall,
11015
+ ...toolCall.external ? { external: true } : {}
11016
+ });
9693
11017
  }
9694
11018
  baseEvent(run, extra) {
9695
11019
  this.eventSequence += 1;
@@ -9791,6 +11115,9 @@ var MemoryOwnedStore = class {
9791
11115
  async cancelRun(input) {
9792
11116
  return this.store.cancelRun(this.lease, input);
9793
11117
  }
11118
+ async recordToolCall(input) {
11119
+ return this.store.recordToolCall(this.lease, input);
11120
+ }
9794
11121
  async snapshotRun(runId) {
9795
11122
  return this.store.snapshotRun(this.lease, runId);
9796
11123
  }
@@ -9934,19 +11261,19 @@ function requestMessageContent(toolCalls, contentBlocks) {
9934
11261
  const toolUse = (toolCall) => ({
9935
11262
  type: "tool_use",
9936
11263
  toolCallId: toolCall.id,
9937
- providerToolCallId: toolCall.providerToolCallId,
9938
- name: toolCall.name,
9939
- input: clone(toolCall.args)
11264
+ ...toolCall.providerToolCallId !== void 0 ? { providerToolCallId: toolCall.providerToolCallId } : {},
11265
+ name: toolCall.name ?? toolCall.title ?? "tool",
11266
+ input: clone(toolCall.args !== void 0 ? toolCall.args : toolCall.rawInput !== void 0 ? toolCall.rawInput : {})
9940
11267
  });
9941
11268
  if (!contentBlocks) return toolCalls.map(toolUse);
9942
- const reserved = new Map(toolCalls.map((toolCall) => [toolCall.providerToolCallId, toolCall]));
11269
+ const reserved = new Map(toolCalls.map((toolCall) => [toolCall.providerToolCallId ?? toolCall.id, toolCall]));
9943
11270
  const parts = [];
9944
11271
  for (const block of contentBlocks) {
9945
11272
  if (block.type === "text") {
9946
11273
  parts.push({ type: "text", text: block.text });
9947
11274
  } else if (block.type === "thinking") {
9948
11275
  parts.push({ type: "thinking", thinking: block.thinking, ...block.signature ? { signature: block.signature } : {} });
9949
- } else {
11276
+ } else if (block.type === "tool_call") {
9950
11277
  const toolCall = reserved.get(block.id);
9951
11278
  if (!toolCall) continue;
9952
11279
  reserved.delete(block.id);
@@ -10343,6 +11670,9 @@ var SqliteStore = class {
10343
11670
  async cancelRun(lease, input) {
10344
11671
  return this.invoke(lease, (store, current) => store.cancelRun(current, input));
10345
11672
  }
11673
+ async recordToolCall(lease, input) {
11674
+ return this.invoke(lease, (store, current) => store.recordToolCall(current, input));
11675
+ }
10346
11676
  async snapshotRun(lease, runId) {
10347
11677
  return this.invoke(lease, (store, current) => store.snapshotRun(current, runId), false);
10348
11678
  }
@@ -10643,7 +11973,9 @@ var SqliteStore = class {
10643
11973
  }
10644
11974
  getStmtInsertEvent() {
10645
11975
  return this.stmtInsertEvent ??= this.database.prepare(
10646
- "INSERT INTO run_events (id, run_id, payload_json, created_at) VALUES (?, ?, ?, ?)"
11976
+ `INSERT INTO run_events (id, run_id, payload_json, created_at)
11977
+ VALUES (?, ?, ?, ?)
11978
+ ON CONFLICT(id) DO UPDATE SET payload_json = excluded.payload_json`
10647
11979
  );
10648
11980
  }
10649
11981
  getStmtDeleteEvent() {
@@ -10726,7 +12058,7 @@ var SqliteStore = class {
10726
12058
  toolCall.id,
10727
12059
  toolCall.runId,
10728
12060
  toolCall.executionId,
10729
- toolCall.state,
12061
+ toolCall.status ?? toolCall.state ?? "in_progress",
10730
12062
  JSON.stringify(toolCall)
10731
12063
  );
10732
12064
  }
@@ -11114,6 +12446,9 @@ var SqliteOwnedStore = class {
11114
12446
  cancelRun(input) {
11115
12447
  return this.store.cancelRun(this.lease, input);
11116
12448
  }
12449
+ recordToolCall(input) {
12450
+ return this.store.recordToolCall(this.lease, input);
12451
+ }
11117
12452
  snapshotRun(runId) {
11118
12453
  return this.store.snapshotRun(this.lease, runId);
11119
12454
  }
@@ -11199,10 +12534,10 @@ export {
11199
12534
  isRuntimeError,
11200
12535
  loadExtensionsFromPath as loadExtensions,
11201
12536
  loadPhase,
11202
- loadPhaseSettings,
11203
12537
  loadPhases,
11204
12538
  loadSkill,
11205
12539
  loadSkills,
12540
+ mergeToolCall,
11206
12541
  parseAgentDefinition,
11207
12542
  parseFrontmatter,
11208
12543
  resolveConfigurationSnapshot