@rowan-agent/agent 0.12.1 → 0.13.1

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
@@ -1326,25 +1326,25 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1326
1326
  }
1327
1327
 
1328
1328
  // src/harness/phases/interactions.ts
1329
- var PhaseInteractionCancelledError = class extends Error {
1330
- code = "phase_interaction_cancelled";
1329
+ var RunInteractionCancelledError = class extends Error {
1330
+ code = "run_interaction_cancelled";
1331
1331
  constructor() {
1332
- super("Phase interaction work was cancelled.");
1333
- this.name = "PhaseInteractionCancelledError";
1332
+ super("Run interaction work was cancelled.");
1333
+ this.name = "RunInteractionCancelledError";
1334
1334
  }
1335
1335
  };
1336
- var PhaseInteractionBoundary = class extends Error {
1336
+ var RunInteractionBoundary = class extends Error {
1337
1337
  constructor(state, interactions) {
1338
- super("Phase interactions require host input.");
1338
+ super("Run interactions require host input.");
1339
1339
  this.state = state;
1340
1340
  this.interactions = interactions;
1341
- this.name = "PhaseInteractionBoundary";
1341
+ this.name = "RunInteractionBoundary";
1342
1342
  }
1343
1343
  state;
1344
1344
  interactions;
1345
1345
  };
1346
- function createPhaseInteractionDriver(state, phase, signal) {
1347
- const stored = state.phaseInteractions;
1346
+ function createRunInteractionDriver(state, phase, signal) {
1347
+ const stored = state.runInteractions ?? state.phaseInteractions;
1348
1348
  const requests = new Map(
1349
1349
  stored?.requests.map((request) => [request.id, { ...request }]) ?? []
1350
1350
  );
@@ -1360,10 +1360,10 @@ function createPhaseInteractionDriver(state, phase, signal) {
1360
1360
  ...checkpoint === void 0 ? {} : { checkpoint }
1361
1361
  });
1362
1362
  const sync = () => {
1363
- state.phaseInteractions = snapshot();
1363
+ state.runInteractions = snapshot();
1364
1364
  };
1365
1365
  const assertActive = () => {
1366
- if (signal?.aborted) throw new PhaseInteractionCancelledError();
1366
+ if (signal?.aborted) throw new RunInteractionCancelledError();
1367
1367
  };
1368
1368
  const matchingStoredRequest = (input) => {
1369
1369
  const fingerprint = interactionFingerprint(input);
@@ -1374,21 +1374,21 @@ function createPhaseInteractionDriver(state, phase, signal) {
1374
1374
  request(input) {
1375
1375
  assertActive();
1376
1376
  if (input.id !== void 0 && input.id.trim().length === 0) {
1377
- throw new TypeError("Phase interaction id must be non-empty.");
1377
+ throw new TypeError("Run interaction id must be non-empty.");
1378
1378
  }
1379
1379
  if (input.prompt.trim().length === 0) {
1380
- throw new TypeError("Phase interaction prompt must be non-empty.");
1380
+ throw new TypeError("Run interaction prompt must be non-empty.");
1381
1381
  }
1382
- if (!PHASE_INTERACTION_KINDS.includes(input.kind)) {
1383
- throw new TypeError(`Unsupported Phase interaction kind: ${String(input.kind)}.`);
1382
+ if (!RUN_INTERACTION_KINDS.includes(input.kind)) {
1383
+ throw new TypeError(`Unsupported Run interaction kind: ${String(input.kind)}.`);
1384
1384
  }
1385
- if (input.payload !== void 0) assertJsonValue(input.payload, "Phase interaction payload");
1385
+ if (input.payload !== void 0) assertJsonValue(input.payload, "Run interaction payload");
1386
1386
  const matched = input.id === void 0 ? matchingStoredRequest(input) : void 0;
1387
1387
  const id = input.id ?? matched?.id ?? createId("interaction");
1388
1388
  const existing = requests.get(id);
1389
1389
  if (existing) {
1390
1390
  if (interactionFingerprint(existing) !== interactionFingerprint(input)) {
1391
- throw new Error(`Phase interaction ${id} was requested with different metadata.`);
1391
+ throw new Error(`Run interaction ${id} was requested with different metadata.`);
1392
1392
  }
1393
1393
  usedRequestIds.add(id);
1394
1394
  return {
@@ -1402,8 +1402,8 @@ function createPhaseInteractionDriver(state, phase, signal) {
1402
1402
  kind: input.kind,
1403
1403
  prompt: input.prompt,
1404
1404
  ...input.payload === void 0 ? {} : { payload: input.payload },
1405
- ...input.origin === void 0 ? {} : { origin: input.origin },
1406
1405
  ...input.toolCallId === void 0 ? {} : { toolCallId: input.toolCallId },
1406
+ ...input.result === void 0 ? {} : { result: input.result },
1407
1407
  createdAt: createTimestamp(),
1408
1408
  status: "pending"
1409
1409
  };
@@ -1428,15 +1428,15 @@ function createPhaseInteractionDriver(state, phase, signal) {
1428
1428
  suspend(input = {}) {
1429
1429
  assertActive();
1430
1430
  const pending = this.pending();
1431
- if (pending.length === 0) throw new Error("Cannot suspend without pending Phase interactions.");
1431
+ if (pending.length === 0) throw new Error("Cannot suspend without pending Run interactions.");
1432
1432
  if (input.checkpoint !== void 0) checkpoint = input.checkpoint;
1433
1433
  state.status = "suspended";
1434
1434
  sync();
1435
- throw new PhaseInteractionBoundary(state, pending);
1435
+ throw new RunInteractionBoundary(state, pending);
1436
1436
  }
1437
1437
  };
1438
1438
  }
1439
- var PHASE_INTERACTION_KINDS = [
1439
+ var RUN_INTERACTION_KINDS = [
1440
1440
  "user_input",
1441
1441
  "permission",
1442
1442
  "elicitation",
@@ -2697,6 +2697,7 @@ async function runPhaseLoop(config, state, registry) {
2697
2697
  }
2698
2698
  state.attempt++;
2699
2699
  state.metrics.iterations++;
2700
+ await config.onPhaseEntered?.(currentPhaseId);
2700
2701
  const phase = registry.phases.get(currentPhaseId);
2701
2702
  if (!phase) {
2702
2703
  throw new Error(`Phase "${currentPhaseId}" not found`);
@@ -3097,7 +3098,7 @@ async function executeToolCalls(input) {
3097
3098
  return results;
3098
3099
  }
3099
3100
  function createPhaseExecution(config, state, phase, messageManager, toolExecutionManager, registry) {
3100
- const interaction = createPhaseInteractionDriver(state, phase.name, config.signal);
3101
+ const interaction = createRunInteractionDriver(state, phase.name, config.signal);
3101
3102
  return {
3102
3103
  interaction,
3103
3104
  messages: messageManager,
@@ -3324,12 +3325,13 @@ function encodeExecutionCheckpoint(state) {
3324
3325
  ...state.continuation.pendingInstruction !== void 0 ? { pendingInstruction: state.continuation.pendingInstruction } : {},
3325
3326
  ...state.continuation.previousPhaseMessageId !== void 0 ? { previousPhaseMessageId: state.continuation.previousPhaseMessageId } : {}
3326
3327
  } : void 0;
3328
+ const runInteractions = state.runInteractions ?? state.phaseInteractions;
3327
3329
  const data = {
3328
3330
  currentPhase: state.currentPhase,
3329
3331
  attempt: state.attempt,
3330
3332
  metrics,
3331
3333
  ...continuation ? { continuation } : {},
3332
- ...state.phaseInteractions === void 0 ? {} : { phaseInteractions: state.phaseInteractions }
3334
+ ...runInteractions === void 0 ? {} : { runInteractions }
3333
3335
  };
3334
3336
  assertJsonValue(data, "execution checkpoint");
3335
3337
  return {
@@ -3347,6 +3349,7 @@ function decodeExecutionCheckpoint(checkpoint) {
3347
3349
  if (!isCheckpointData(checkpoint.data)) {
3348
3350
  throw new ExecutionCheckpointError("Execution checkpoint payload is invalid.");
3349
3351
  }
3352
+ const restoredInteractions = checkpoint.data.runInteractions ?? checkpoint.data.phaseInteractions;
3350
3353
  return {
3351
3354
  currentPhase: checkpoint.data.currentPhase,
3352
3355
  attempt: checkpoint.data.attempt,
@@ -3361,11 +3364,11 @@ function decodeExecutionCheckpoint(checkpoint) {
3361
3364
  previousResults: checkpoint.data.continuation.previousResults.map((result) => ({ ...result }))
3362
3365
  }
3363
3366
  } : {},
3364
- ...checkpoint.data.phaseInteractions ? {
3365
- phaseInteractions: {
3366
- requests: checkpoint.data.phaseInteractions.requests.map((request) => ({ ...request })),
3367
- answers: { ...checkpoint.data.phaseInteractions.answers },
3368
- ...checkpoint.data.phaseInteractions.checkpoint === void 0 ? {} : { checkpoint: checkpoint.data.phaseInteractions.checkpoint }
3367
+ ...restoredInteractions ? {
3368
+ runInteractions: {
3369
+ requests: restoredInteractions.requests.map((request) => ({ ...request })),
3370
+ answers: { ...restoredInteractions.answers },
3371
+ ...restoredInteractions.checkpoint === void 0 ? {} : { checkpoint: restoredInteractions.checkpoint }
3369
3372
  }
3370
3373
  } : {}
3371
3374
  };
@@ -3393,13 +3396,14 @@ async function executeOnce(input) {
3393
3396
  metrics: createMetrics()
3394
3397
  };
3395
3398
  if (input.interactionAnswers) {
3396
- state.phaseInteractions = {
3397
- requests: state.phaseInteractions?.requests ?? [],
3399
+ const existing = state.runInteractions ?? state.phaseInteractions;
3400
+ state.runInteractions = {
3401
+ requests: existing?.requests ?? [],
3398
3402
  answers: {
3399
- ...state.phaseInteractions?.answers ?? {},
3403
+ ...existing?.answers ?? {},
3400
3404
  ...input.interactionAnswers
3401
3405
  },
3402
- ...state.phaseInteractions?.checkpoint === void 0 ? {} : { checkpoint: state.phaseInteractions.checkpoint }
3406
+ ...existing?.checkpoint === void 0 ? {} : { checkpoint: existing.checkpoint }
3403
3407
  };
3404
3408
  }
3405
3409
  const config = {
@@ -3420,6 +3424,7 @@ async function executeOnce(input) {
3420
3424
  onThinkingDelta: input.onThinkingDelta,
3421
3425
  onOutcome: input.onOutcome,
3422
3426
  onModelTranscript: input.onModelTranscript,
3427
+ onPhaseEntered: input.onPhaseEntered,
3423
3428
  onPhaseStatus: input.onPhaseStatus,
3424
3429
  runtime: input.runtime,
3425
3430
  waitForInput: async (nextState, request) => {
@@ -3436,22 +3441,24 @@ async function executeOnce(input) {
3436
3441
  };
3437
3442
  } catch (error) {
3438
3443
  if (error instanceof InputRequiredBoundary) {
3444
+ const interaction = {
3445
+ id: createId("input"),
3446
+ kind: "user_input",
3447
+ phase: error.request.phase,
3448
+ prompt: error.request.prompt,
3449
+ status: "pending",
3450
+ createdAt: error.request.requestedAt
3451
+ };
3439
3452
  return {
3440
3453
  type: "input_required",
3441
- request: error.request,
3442
- interactions: [],
3454
+ interactions: [interaction],
3443
3455
  checkpoint: encodeExecutionCheckpoint(error.state),
3444
3456
  messages: snapshotMessages(context.messages)
3445
3457
  };
3446
3458
  }
3447
- if (error instanceof PhaseInteractionBoundary) {
3459
+ if (error instanceof RunInteractionBoundary) {
3448
3460
  return {
3449
3461
  type: "input_required",
3450
- request: {
3451
- phase: error.interactions[0]?.phase ?? state.currentPhase,
3452
- prompt: error.interactions.map((interaction) => interaction.prompt).join("\n"),
3453
- requestedAt: error.interactions[0]?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
3454
- },
3455
3462
  interactions: error.interactions,
3456
3463
  checkpoint: encodeExecutionCheckpoint(error.state),
3457
3464
  messages: snapshotMessages(context.messages)
@@ -3482,14 +3489,15 @@ function isCheckpointData(value) {
3482
3489
  if (!isRecord2(value) || typeof value.currentPhase !== "string" || !Number.isInteger(value.attempt)) return false;
3483
3490
  if (!isMetrics(value.metrics)) return false;
3484
3491
  if (value.continuation !== void 0 && !isContinuation(value.continuation)) return false;
3485
- if (value.phaseInteractions !== void 0 && !isPhaseInteractionState(value.phaseInteractions)) return false;
3492
+ if (value.runInteractions !== void 0 && !isRunInteractionState(value.runInteractions)) return false;
3493
+ if (value.phaseInteractions !== void 0 && !isRunInteractionState(value.phaseInteractions)) return false;
3486
3494
  return true;
3487
3495
  }
3488
- function isPhaseInteractionState(value) {
3496
+ function isRunInteractionState(value) {
3489
3497
  if (!isRecord2(value) || !Array.isArray(value.requests) || !isRecord2(value.answers)) return false;
3490
3498
  if (!Object.values(value.answers).every((answer) => isJsonValue(answer))) return false;
3491
3499
  if (value.checkpoint !== void 0 && !isJsonValue(value.checkpoint)) return false;
3492
- return value.requests.every((request) => isRecord2(request) && typeof request.id === "string" && typeof request.phase === "string" && ["user_input", "permission", "elicitation", "confirmation"].includes(request.kind) && typeof request.prompt === "string" && typeof request.createdAt === "string" && ["pending", "answered", "denied", "cancelled", "expired"].includes(request.status) && (request.payload === void 0 || isJsonValue(request.payload)));
3500
+ return value.requests.every((request) => isRecord2(request) && typeof request.id === "string" && typeof request.phase === "string" && ["user_input", "permission", "elicitation", "confirmation"].includes(request.kind) && typeof request.prompt === "string" && typeof request.createdAt === "string" && ["pending", "answered", "replied", "cancelled"].includes(request.status) && (request.payload === void 0 || isJsonValue(request.payload)) && (request.result === void 0 || isRecord2(request.result)) && (request.answer === void 0 || isJsonValue(request.answer)) && (request.reply === void 0 || typeof request.reply === "string"));
3493
3501
  }
3494
3502
  function isMetrics(value) {
3495
3503
  if (!isRecord2(value)) return false;
@@ -4180,10 +4188,71 @@ function invalidCursor(cursorType, reason) {
4180
4188
  }
4181
4189
 
4182
4190
  // src/runtime/model-context.ts
4191
+ var DEFAULT_INTERACTION_TEMPLATES = {
4192
+ user_input: {
4193
+ answered: 'The user answered "{{prompt}}": {{answer}}',
4194
+ replied: "The user replied instead: {{reply}}",
4195
+ cancelled: "The user cancelled input: {{prompt}}"
4196
+ },
4197
+ permission: {
4198
+ answered: "The user granted permission: {{prompt}}",
4199
+ replied: "The user replied instead: {{reply}}",
4200
+ cancelled: "The user did not grant permission: {{prompt}}"
4201
+ },
4202
+ elicitation: {
4203
+ answered: 'The user answered "{{prompt}}": {{answer}}',
4204
+ replied: "The user replied instead: {{reply}}",
4205
+ cancelled: "The user cancelled elicitation: {{prompt}}"
4206
+ },
4207
+ confirmation: {
4208
+ answered: "The user confirmed: {{prompt}}",
4209
+ replied: "The user replied instead: {{reply}}",
4210
+ cancelled: "The user cancelled: {{prompt}}"
4211
+ }
4212
+ };
4213
+ function renderInteractionText(record) {
4214
+ const template = record.result?.[record.status] ?? DEFAULT_INTERACTION_TEMPLATES[record.kind]?.[record.status] ?? "{{prompt}}";
4215
+ const answerStr = typeof record.answer === "string" ? record.answer : record.answer !== void 0 ? JSON.stringify(record.answer) : "";
4216
+ const replyStr = record.reply ?? "";
4217
+ return template.replaceAll("{{prompt}}", record.prompt).replaceAll("{{answer}}", answerStr).replaceAll("{{reply}}", replyStr);
4218
+ }
4183
4219
  function projectModelContext(input) {
4220
+ const toolInteractionTexts = /* @__PURE__ */ new Map();
4221
+ for (const message of input.messages) {
4222
+ if (message.role === "interaction" && message.toolCallId !== void 0) {
4223
+ const text = renderInteractionText(message);
4224
+ const existing = toolInteractionTexts.get(String(message.toolCallId));
4225
+ if (existing) {
4226
+ existing.push(text);
4227
+ } else {
4228
+ toolInteractionTexts.set(String(message.toolCallId), [text]);
4229
+ }
4230
+ }
4231
+ }
4232
+ const projectedMessages = [];
4233
+ for (const message of input.messages) {
4234
+ if (message.role === "interaction") {
4235
+ if (message.toolCallId !== void 0) {
4236
+ continue;
4237
+ }
4238
+ projectedMessages.push({
4239
+ id: message.id,
4240
+ role: "user",
4241
+ content: renderInteractionText(message),
4242
+ createdAt: message.createdAt,
4243
+ ...message.metadata ? { metadata: message.metadata } : {}
4244
+ });
4245
+ continue;
4246
+ }
4247
+ if (message.role === "tool") {
4248
+ projectedMessages.push(projectToolMessage(message, toolInteractionTexts));
4249
+ continue;
4250
+ }
4251
+ projectedMessages.push(projectMessage(message));
4252
+ }
4184
4253
  return {
4185
4254
  systemPrompt: input.context.systemPrompt,
4186
- messages: input.messages.map(projectMessage),
4255
+ messages: projectedMessages,
4187
4256
  tools: input.context.tools.map((tool) => projectTool(tool, input.agentId, input.runId)),
4188
4257
  skills: [...input.context.skills],
4189
4258
  ...input.context.phases ? { phases: input.context.phases } : {}
@@ -4214,8 +4283,35 @@ function projectMessage(message) {
4214
4283
  return { id: message.id, role: message.role, content: projectAssistantContent(message.content), createdAt: message.createdAt, ...message.metadata ? { metadata: message.metadata } : {} };
4215
4284
  case "tool":
4216
4285
  return { id: message.id, role: message.role, content: projectToolContent(message.content), createdAt: message.createdAt, ...message.metadata ? { metadata: message.metadata } : {} };
4286
+ case "interaction":
4287
+ return { id: message.id, role: "user", content: renderInteractionText(message), createdAt: message.createdAt, ...message.metadata ? { metadata: message.metadata } : {} };
4217
4288
  }
4218
4289
  }
4290
+ function projectToolMessage(message, toolInteractionTexts) {
4291
+ return {
4292
+ id: message.id,
4293
+ role: message.role,
4294
+ content: message.content.map((part) => {
4295
+ const rawResult = jsonText(part.result.content);
4296
+ const interactionTexts = toolInteractionTexts.get(String(part.toolCallId));
4297
+ let content = rawResult;
4298
+ if (interactionTexts && interactionTexts.length > 0) {
4299
+ const prefix = interactionTexts.join("\n\n");
4300
+ content = rawResult === "null" || rawResult.length === 0 ? prefix : `${prefix}
4301
+
4302
+ ${rawResult}`;
4303
+ }
4304
+ return {
4305
+ type: "tool_result",
4306
+ toolUseId: part.providerToolCallId ?? String(part.toolCallId),
4307
+ content,
4308
+ ...part.result.ok ? {} : { isError: true }
4309
+ };
4310
+ }),
4311
+ createdAt: message.createdAt,
4312
+ ...message.metadata ? { metadata: message.metadata } : {}
4313
+ };
4314
+ }
4219
4315
  function durableAssistantContent(content) {
4220
4316
  if (typeof content === "string") return content;
4221
4317
  const projected = [];
@@ -4248,6 +4344,25 @@ function projectToolContent(content) {
4248
4344
  ...part.result.ok ? {} : { isError: true }
4249
4345
  }));
4250
4346
  }
4347
+ function inertRunInteraction(signal) {
4348
+ const controller = signal ? void 0 : new AbortController();
4349
+ const state = {
4350
+ currentPhase: "default",
4351
+ attempt: 0,
4352
+ status: "running",
4353
+ metrics: { iterations: 0, phaseTransitions: [], compactionCount: 0, retryCount: 0, startedAt: (/* @__PURE__ */ new Date()).toISOString(), startedAtMs: Date.now() }
4354
+ };
4355
+ const driver = createRunInteractionDriver(state, "default", signal ?? controller.signal);
4356
+ return {
4357
+ ...driver,
4358
+ request: () => {
4359
+ throw new Error("Run interactions require an active interaction driver.");
4360
+ },
4361
+ suspend: () => {
4362
+ throw new Error("Run interactions require an active interaction driver.");
4363
+ }
4364
+ };
4365
+ }
4251
4366
  function projectTool(tool, agentId, runId) {
4252
4367
  return {
4253
4368
  name: tool.name,
@@ -4259,7 +4374,8 @@ function projectTool(tool, agentId, runId) {
4259
4374
  agentId,
4260
4375
  runId,
4261
4376
  toolCallId: context.toolCallId,
4262
- reportProgress: () => void 0
4377
+ reportProgress: () => void 0,
4378
+ interaction: context.interaction ?? inertRunInteraction(signal)
4263
4379
  }, signal ?? new AbortController().signal);
4264
4380
  return { toolCallId: context.toolCallId, toolName: tool.name, ...result };
4265
4381
  }
@@ -5817,7 +5933,7 @@ function adaptExtensionTool(input) {
5817
5933
  description: definition.description,
5818
5934
  parameters: definition.parameters,
5819
5935
  execute: async (args, context, signal) => {
5820
- const result = await definition.execute(args, signal);
5936
+ const result = await definition.execute(args, context, signal);
5821
5937
  const content = JSON.parse(JSON.stringify(result.content));
5822
5938
  return result.isError ? { ok: false, content, error: "Extension Tool failed." } : { ok: true, content };
5823
5939
  }
@@ -5832,6 +5948,46 @@ var RUN_STATE_CHECK_INTERVAL_MS = 1e3;
5832
5948
  var OWNER_LEASE_MS = 3e4;
5833
5949
  var OWNER_RENEWAL_MS = 1e4;
5834
5950
  var MAX_INLINE_TOOL_RESULT_BYTES = 16 * 1024;
5951
+ function createToolInteractionDriver(driver, input) {
5952
+ const requestedIds = /* @__PURE__ */ new Set();
5953
+ const stored = driver.checkpoint();
5954
+ const checkpointRecord = stored && typeof stored === "object" && !Array.isArray(stored) ? stored : void 0;
5955
+ const resumed = checkpointRecord?.kind === "tool_call_suspension" && checkpointRecord.toolCallId === input.toolCallId ? checkpointRecord : void 0;
5956
+ return {
5957
+ signal: driver.signal,
5958
+ request(request) {
5959
+ const interaction = driver.request({ ...request, toolCallId: input.toolCallId });
5960
+ requestedIds.add(interaction.id);
5961
+ return interaction;
5962
+ },
5963
+ pending: () => driver.pending(),
5964
+ answers: () => driver.answers(),
5965
+ checkpoint: () => resumed ? resumed.toolCheckpoint : stored,
5966
+ clearCheckpoint: () => driver.clearCheckpoint(),
5967
+ suspend(options = {}) {
5968
+ const interactionIds = [.../* @__PURE__ */ new Set([
5969
+ ...requestedIds,
5970
+ ...driver.pending().filter((interaction) => interaction.toolCallId === input.toolCallId).map(({ id }) => id)
5971
+ ])];
5972
+ const pendingToolCallIds = [...new Set([
5973
+ ...input.toolCallIds ?? [],
5974
+ ...input.toolCalls.slice(0, input.pendingIndex + 1).map((_, index) => index === input.pendingIndex ? input.toolCallId : void 0)
5975
+ ].filter((id) => id !== void 0))];
5976
+ driver.suspend({
5977
+ checkpoint: {
5978
+ kind: "tool_call_suspension",
5979
+ toolCalls: input.toolCalls,
5980
+ pendingIndex: input.pendingIndex,
5981
+ toolCallId: input.toolCallId,
5982
+ toolCallIds: pendingToolCallIds,
5983
+ interactionIds,
5984
+ ...options.checkpoint === void 0 ? {} : { toolCheckpoint: options.checkpoint },
5985
+ completedResults: input.completedResults
5986
+ }
5987
+ });
5988
+ }
5989
+ };
5990
+ }
5835
5991
  async function spillLargeToolResult(result, archiveDir, toolName) {
5836
5992
  if (!archiveDir) return result;
5837
5993
  const serialized = typeof result.content === "string" ? result.content : JSON.stringify(result.content, null, 2);
@@ -6090,22 +6246,13 @@ var AgentRuntime = class _AgentRuntime {
6090
6246
  this.assertOpen();
6091
6247
  return this.owned.history(agentId);
6092
6248
  }
6093
- async respond(runId, input) {
6094
- this.assertOpen();
6095
- const snapshot = await this.owned.snapshotRun(runId);
6096
- if (snapshot.state !== "input_required" || snapshot.request.id !== input.requestId) {
6097
- throw new RuntimeError("input_request_conflict", { runId, requestId: input.requestId, reason: "not_found" });
6098
- }
6099
- await this.owned.answerInput({ runId, requestId: input.requestId, expectedRevision: snapshot.revision, input: input.input });
6100
- void this.pump();
6101
- }
6102
6249
  async respondInteraction(runId, input) {
6103
6250
  this.assertOpen();
6104
6251
  const snapshot = await this.owned.snapshotRun(runId);
6105
6252
  if (snapshot.state !== "input_required" || !snapshot.interactions.some((interaction) => interaction.id === input.interactionId)) {
6106
- throw new RuntimeError("input_request_conflict", { runId, requestId: input.interactionId, reason: "not_found" });
6253
+ throw new RuntimeError("input_request_conflict", { runId, interactionId: input.interactionId, reason: "not_found" });
6107
6254
  }
6108
- await this.owned.answerInteraction({ runId, interactionId: input.interactionId, expectedRevision: snapshot.revision, input: input.input });
6255
+ await this.owned.answerInteraction({ runId, interactionId: input.interactionId, expectedRevision: snapshot.revision, input: input.input, cancel: input.cancel });
6109
6256
  void this.pump();
6110
6257
  }
6111
6258
  async cancel(runId, reason) {
@@ -6266,6 +6413,7 @@ var AgentRuntime = class _AgentRuntime {
6266
6413
  beforeToolCall: assembly.beforeToolCall,
6267
6414
  afterToolCall: assembly.afterToolCall
6268
6415
  };
6416
+ let phaseVisit = (await this.owned.listEvents()).filter((event) => event.runId === run.id && event.kind === "phase_entered").length;
6269
6417
  const executeModel = (context) => executeOnce({
6270
6418
  canonicalMessages: context.messages,
6271
6419
  context,
@@ -6287,6 +6435,18 @@ var AgentRuntime = class _AgentRuntime {
6287
6435
  beforePhase: assembly.beforePhase,
6288
6436
  afterPhase: assembly.afterPhase,
6289
6437
  beforePrompt: assembly.beforePrompt,
6438
+ onPhaseEntered: async (phaseId) => {
6439
+ const active = this.executions.get(run.id);
6440
+ if (active?.executionId !== claim.execution.executionId) return;
6441
+ const entered = await this.owned.commitPhaseEntered({
6442
+ runId: run.id,
6443
+ execution: claim.execution,
6444
+ expectedRevision: executionRevision,
6445
+ phaseId,
6446
+ visit: ++phaseVisit
6447
+ });
6448
+ executionRevision = entered.revision;
6449
+ },
6290
6450
  onPhaseStatus: (phaseId, status) => {
6291
6451
  const active = this.executions.get(run.id);
6292
6452
  if (active?.executionId !== claim.execution.executionId) return;
@@ -6404,23 +6564,30 @@ var AgentRuntime = class _AgentRuntime {
6404
6564
  return;
6405
6565
  }
6406
6566
  if (result.type === "input_required") {
6407
- const isToolCallInteraction = result.interactions?.some((i) => i.origin === "tool_call");
6408
- const output = result.interactions && result.interactions.length > 0 ? void 0 : latestAssistant(
6567
+ const resultData = result.checkpoint.data && typeof result.checkpoint.data === "object" && !Array.isArray(result.checkpoint.data) ? result.checkpoint.data : void 0;
6568
+ const interactionData = resultData?.runInteractions && typeof resultData.runInteractions === "object" && !Array.isArray(resultData.runInteractions) ? resultData.runInteractions : void 0;
6569
+ const interactionCheckpoint = interactionData?.checkpoint && typeof interactionData.checkpoint === "object" && !Array.isArray(interactionData.checkpoint) ? interactionData.checkpoint : void 0;
6570
+ let pendingToolCallIds = Array.isArray(interactionCheckpoint?.toolCallIds) ? interactionCheckpoint.toolCallIds : [];
6571
+ if (pendingToolCallIds.length === 0 && result.interactions.length > 0 && result.interactions.every(({ toolCallId }) => toolCallId !== void 0)) {
6572
+ pendingToolCallIds = result.interactions.map(({ toolCallId }) => toolCallId);
6573
+ }
6574
+ const isToolCallInteraction = result.interactions?.some((i) => i.toolCallId !== void 0);
6575
+ const output = isToolCallInteraction ? void 0 : latestAssistant(
6409
6576
  run,
6410
6577
  result.messages.slice(modelMessages.length),
6411
6578
  modelMessages.length
6412
6579
  );
6413
- const prompt = isToolCallInteraction ? void 0 : output ?? promptMessage(run, result.request.prompt, result.messages.length);
6580
+ const prompt = isToolCallInteraction ? void 0 : output ?? (result.interactions[0]?.prompt ? promptMessage(run, result.interactions[0].prompt, result.messages.length) : void 0);
6414
6581
  await this.owned.commitInputRequired({
6415
6582
  runId: run.id,
6416
6583
  execution: claim.execution,
6417
6584
  expectedRevision: executionRevision,
6418
- requestId: createId("input"),
6419
- phase: result.request.phase,
6585
+ phase: result.interactions[0]?.phase ?? "default",
6420
6586
  ...prompt ? { prompt } : {},
6421
6587
  checkpoint: result.checkpoint,
6422
6588
  interactions: result.interactions,
6423
- interactionAnswers: claim.run.interactionAnswers
6589
+ interactionAnswers: claim.run.interactionAnswers,
6590
+ ...pendingToolCallIds.length === 0 ? {} : { pendingToolCallIds }
6424
6591
  });
6425
6592
  this.transientEvents.clear(run.id, claim.execution.executionId);
6426
6593
  return;
@@ -6515,6 +6682,12 @@ var AgentRuntime = class _AgentRuntime {
6515
6682
  ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
6516
6683
  ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
6517
6684
  toolCallId,
6685
+ interaction: createRunInteractionDriver({
6686
+ currentPhase: "default",
6687
+ attempt: 0,
6688
+ status: "running",
6689
+ metrics: { iterations: 0, phaseTransitions: [], compactionCount: 0, retryCount: 0, startedAt: (/* @__PURE__ */ new Date()).toISOString(), startedAtMs: Date.now() }
6690
+ }, "default", input.signal),
6518
6691
  reportProgress: () => {
6519
6692
  }
6520
6693
  };
@@ -6595,6 +6768,13 @@ var AgentRuntime = class _AgentRuntime {
6595
6768
  ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
6596
6769
  ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
6597
6770
  toolCallId,
6771
+ interaction: createToolInteractionDriver(input.driver, {
6772
+ toolCallId,
6773
+ toolCallIds,
6774
+ toolCalls: input.toolCalls,
6775
+ pendingIndex: i,
6776
+ completedResults: results2
6777
+ }),
6598
6778
  reportProgress: (progress) => {
6599
6779
  const active = this.executions.get(input.run.id);
6600
6780
  if (!progressActive || this.closed || input.signal.aborted || active?.executionId !== input.execution.executionId || !isJsonValue(progress)) return;
@@ -6649,12 +6829,24 @@ var AgentRuntime = class _AgentRuntime {
6649
6829
  result
6650
6830
  });
6651
6831
  setRevision(committed.run.revision);
6832
+ const projectedResult = await this.foldToolInteractionRecords(result, toolCallId, input.run.agentId);
6652
6833
  results2.push({
6653
6834
  toolCallId: providerToolCallId,
6654
6835
  toolName: toolCall.name,
6655
- ...result
6836
+ ...projectedResult
6656
6837
  });
6657
6838
  } catch (error) {
6839
+ if (error instanceof RunInteractionBoundary) {
6840
+ const suspended = await this.owned.suspendToolCall({
6841
+ runId: input.run.id,
6842
+ execution: input.execution,
6843
+ expectedRevision: revision,
6844
+ toolCallId
6845
+ });
6846
+ setRevision(suspended.run.revision);
6847
+ throw error;
6848
+ }
6849
+ if (error instanceof RunInteractionCancelledError) throw error;
6658
6850
  const reason = error instanceof Error ? error.message : "Tool execution outcome is indeterminate.";
6659
6851
  const indeterminate = await this.owned.commitToolResult({
6660
6852
  runId: input.run.id,
@@ -6681,7 +6873,7 @@ var AgentRuntime = class _AgentRuntime {
6681
6873
  const toolCall = input.toolCalls[index];
6682
6874
  const durableTool = input.toolConfig.tools.find((candidate) => candidate.name === toolCall.name);
6683
6875
  const providerToolCallId = toolCall.id;
6684
- const toolCallId = suspension && index === suspension.pendingIndex && suspension.toolCallId ? suspension.toolCallId : createId("tool");
6876
+ const toolCallId = suspension && index >= suspension.pendingIndex && suspension.toolCallIds?.[index] ? suspension.toolCallIds[index] : suspension && index === suspension.pendingIndex && suspension.toolCallId ? suspension.toolCallId : createId("tool");
6685
6877
  const protocolFailure = (error) => ({
6686
6878
  toolCallId: providerToolCallId,
6687
6879
  toolName: toolCall.name,
@@ -6716,12 +6908,26 @@ var AgentRuntime = class _AgentRuntime {
6716
6908
  continue;
6717
6909
  }
6718
6910
  let progressActive = false;
6911
+ const resumedStart = suspension && index === suspension.pendingIndex ? await this.owned.startToolCall({
6912
+ runId: input.run.id,
6913
+ execution: input.execution,
6914
+ expectedRevision: revision,
6915
+ toolCallId
6916
+ }) : void 0;
6917
+ if (resumedStart) setRevision(resumedStart.run.revision);
6719
6918
  const context = {
6720
6919
  agentId: input.run.agentId,
6721
6920
  runId: input.run.id,
6722
6921
  ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
6723
6922
  ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
6724
6923
  toolCallId,
6924
+ interaction: createToolInteractionDriver(input.driver, {
6925
+ toolCallId,
6926
+ ...suspension?.toolCallIds === void 0 ? {} : { toolCallIds: suspension.toolCallIds },
6927
+ toolCalls: input.toolCalls,
6928
+ pendingIndex: index,
6929
+ completedResults: results
6930
+ }),
6725
6931
  reportProgress: (progress) => {
6726
6932
  const active = this.executions.get(input.run.id);
6727
6933
  if (!progressActive || this.closed || input.signal.aborted || active?.executionId !== input.execution.executionId || !isJsonValue(progress)) return;
@@ -6742,7 +6948,7 @@ var AgentRuntime = class _AgentRuntime {
6742
6948
  });
6743
6949
  }
6744
6950
  };
6745
- const answer = suspension && index === suspension.pendingIndex && suspension.interactionId ? input.driver?.answers().get(suspension.interactionId) : void 0;
6951
+ const answer = suspension && index === suspension.pendingIndex ? suspension.interactionIds?.map((id) => input.driver?.answers().get(id)).find((value) => value !== void 0) : void 0;
6746
6952
  try {
6747
6953
  if (input.toolConfig.beforeToolCall) {
6748
6954
  const decision = await input.toolConfig.beforeToolCall({
@@ -6756,39 +6962,30 @@ var AgentRuntime = class _AgentRuntime {
6756
6962
  if (!input.driver) {
6757
6963
  throw new Error("Tool call requested interaction but no interaction driver is active.");
6758
6964
  }
6759
- const interactionRequest = input.driver.request({
6760
- id: decision.interaction.id,
6761
- kind: decision.interaction.kind,
6762
- prompt: decision.interaction.prompt,
6763
- payload: decision.interaction.payload,
6764
- origin: "tool_call",
6765
- toolCallId
6766
- });
6767
- input.driver.suspend({
6768
- checkpoint: {
6769
- kind: "tool_call_suspension",
6770
- toolCalls: input.toolCalls,
6771
- pendingIndex: index,
6772
- toolCallId,
6773
- interactionId: interactionRequest.id,
6774
- completedResults: results
6775
- }
6776
- });
6965
+ context.interaction.request(decision.interaction);
6966
+ if (!(suspension && index === suspension.pendingIndex)) {
6967
+ const reserved2 = await this.owned.reserveToolCalls({
6968
+ runId: input.run.id,
6969
+ execution: input.execution,
6970
+ expectedRevision: revision,
6971
+ requestMessageId: createId("msg"),
6972
+ calls: [{ toolCallId, providerToolCallId, name: toolCall.name, args: toJsonValue(toolCall.args) }]
6973
+ });
6974
+ setRevision(reserved2.run.revision);
6975
+ }
6976
+ context.interaction.suspend();
6777
6977
  }
6778
6978
  if ("allow" in decision && !decision.allow) {
6779
- const reserved2 = await this.owned.reserveToolCalls({
6780
- runId: input.run.id,
6781
- execution: input.execution,
6782
- expectedRevision: revision,
6783
- requestMessageId: createId("msg"),
6784
- calls: [{
6785
- toolCallId,
6786
- providerToolCallId,
6787
- name: toolCall.name,
6788
- args: toJsonValue(toolCall.args)
6789
- }]
6790
- });
6791
- setRevision(reserved2.run.revision);
6979
+ if (!(suspension && index === suspension.pendingIndex)) {
6980
+ const reserved2 = await this.owned.reserveToolCalls({
6981
+ runId: input.run.id,
6982
+ execution: input.execution,
6983
+ expectedRevision: revision,
6984
+ requestMessageId: createId("msg"),
6985
+ calls: [{ toolCallId, providerToolCallId, name: toolCall.name, args: toJsonValue(toolCall.args) }]
6986
+ });
6987
+ setRevision(reserved2.run.revision);
6988
+ }
6792
6989
  const failed = await this.owned.commitToolResult({
6793
6990
  runId: input.run.id,
6794
6991
  execution: input.execution,
@@ -6803,23 +7000,20 @@ var AgentRuntime = class _AgentRuntime {
6803
7000
  }
6804
7001
  }
6805
7002
  } catch (error) {
6806
- if (error instanceof PhaseInteractionBoundary || error instanceof PhaseInteractionCancelledError) {
7003
+ if (error instanceof RunInteractionBoundary || error instanceof RunInteractionCancelledError) {
6807
7004
  throw error;
6808
7005
  }
6809
7006
  const reason = error instanceof Error ? error.message : "Tool policy rejected the call.";
6810
- const reserved2 = await this.owned.reserveToolCalls({
6811
- runId: input.run.id,
6812
- execution: input.execution,
6813
- expectedRevision: revision,
6814
- requestMessageId: createId("msg"),
6815
- calls: [{
6816
- toolCallId,
6817
- providerToolCallId,
6818
- name: toolCall.name,
6819
- args: toJsonValue(toolCall.args)
6820
- }]
6821
- });
6822
- setRevision(reserved2.run.revision);
7007
+ if (!(suspension && index === suspension.pendingIndex)) {
7008
+ const reserved2 = await this.owned.reserveToolCalls({
7009
+ runId: input.run.id,
7010
+ execution: input.execution,
7011
+ expectedRevision: revision,
7012
+ requestMessageId: createId("msg"),
7013
+ calls: [{ toolCallId, providerToolCallId, name: toolCall.name, args: toJsonValue(toolCall.args) }]
7014
+ });
7015
+ setRevision(reserved2.run.revision);
7016
+ }
6823
7017
  const failed = await this.owned.commitToolResult({
6824
7018
  runId: input.run.id,
6825
7019
  execution: input.execution,
@@ -6832,26 +7026,23 @@ var AgentRuntime = class _AgentRuntime {
6832
7026
  results.push(protocolFailure(reason));
6833
7027
  continue;
6834
7028
  }
6835
- const reserved = await this.owned.reserveToolCalls({
6836
- runId: input.run.id,
6837
- execution: input.execution,
6838
- expectedRevision: revision,
6839
- requestMessageId: createId("msg"),
6840
- calls: [{
6841
- toolCallId,
6842
- providerToolCallId,
6843
- name: toolCall.name,
6844
- args: toJsonValue(toolCall.args)
6845
- }]
6846
- });
6847
- setRevision(reserved.run.revision);
6848
- const started = await this.owned.startToolCall({
6849
- runId: input.run.id,
6850
- execution: input.execution,
6851
- expectedRevision: revision,
6852
- toolCallId
6853
- });
6854
- setRevision(started.run.revision);
7029
+ const reserved = resumedStart ?? await (async () => {
7030
+ const committed = await this.owned.reserveToolCalls({
7031
+ runId: input.run.id,
7032
+ execution: input.execution,
7033
+ expectedRevision: revision,
7034
+ requestMessageId: createId("msg"),
7035
+ calls: [{ toolCallId, providerToolCallId, name: toolCall.name, args: toJsonValue(toolCall.args) }]
7036
+ });
7037
+ setRevision(committed.run.revision);
7038
+ return this.owned.startToolCall({
7039
+ runId: input.run.id,
7040
+ execution: input.execution,
7041
+ expectedRevision: revision,
7042
+ toolCallId
7043
+ });
7044
+ })();
7045
+ if (reserved) setRevision(reserved.run.revision);
6855
7046
  progressActive = true;
6856
7047
  try {
6857
7048
  let result = await durableTool.execute(toJsonValue(toolCall.args), context, input.signal);
@@ -6879,12 +7070,24 @@ var AgentRuntime = class _AgentRuntime {
6879
7070
  result
6880
7071
  });
6881
7072
  setRevision(committed.run.revision);
7073
+ const projectedResult = await this.foldToolInteractionRecords(result, toolCallId, input.run.agentId);
6882
7074
  results.push({
6883
7075
  toolCallId: providerToolCallId,
6884
7076
  toolName: toolCall.name,
6885
- ...result
7077
+ ...projectedResult
6886
7078
  });
6887
7079
  } catch (error) {
7080
+ if (error instanceof RunInteractionBoundary) {
7081
+ const suspended = await this.owned.suspendToolCall({
7082
+ runId: input.run.id,
7083
+ execution: input.execution,
7084
+ expectedRevision: revision,
7085
+ toolCallId
7086
+ });
7087
+ setRevision(suspended.run.revision);
7088
+ throw error;
7089
+ }
7090
+ if (error instanceof RunInteractionCancelledError) throw error;
6888
7091
  const reason = error instanceof Error ? error.message : "Tool execution outcome is indeterminate.";
6889
7092
  const indeterminate = await this.owned.commitToolResult({
6890
7093
  runId: input.run.id,
@@ -6905,6 +7108,20 @@ var AgentRuntime = class _AgentRuntime {
6905
7108
  input.driver?.clearCheckpoint();
6906
7109
  return { results, revision };
6907
7110
  }
7111
+ async foldToolInteractionRecords(result, toolCallId, agentId) {
7112
+ const interactionMessages = (await this.owned.history(agentId)).filter(
7113
+ (message) => message.role === "interaction" && message.toolCallId === toolCallId
7114
+ );
7115
+ if (interactionMessages.length === 0) return result;
7116
+ const interactionText = interactionMessages.filter((message) => message.role === "interaction").map(renderInteractionText).join("\n\n");
7117
+ const content = typeof result.content === "string" ? result.content : JSON.stringify(result.content) ?? "null";
7118
+ return {
7119
+ ...result,
7120
+ content: content === "null" || content.length === 0 ? interactionText : `${interactionText}
7121
+
7122
+ ${content}`
7123
+ };
7124
+ }
6908
7125
  async requireAgent(agentId) {
6909
7126
  const agent = (await this.owned.listAgents()).find((candidate) => candidate.id === agentId);
6910
7127
  if (!agent) throw new RuntimeError("agent_not_found", { agentId });
@@ -6975,7 +7192,17 @@ var AgentRuntime = class _AgentRuntime {
6975
7192
  }
6976
7193
  }
6977
7194
  const events = await this.owned.listEvents(cursor ? { after: cursor } : {});
7195
+ const transientBeforeDurable = subscription.shift();
7196
+ if (transientBeforeDurable) {
7197
+ yield transientBeforeDurable;
7198
+ continue;
7199
+ }
6978
7200
  for (const event of events) {
7201
+ const transientBeforeEvent = subscription.shift();
7202
+ if (transientBeforeEvent) {
7203
+ yield transientBeforeEvent;
7204
+ continue;
7205
+ }
6979
7206
  cursor = event.cursor;
6980
7207
  if (event.runId !== runId) continue;
6981
7208
  if (event.kind === "message_committed") {
@@ -7046,9 +7273,6 @@ var DurableRun = class {
7046
7273
  wait(options) {
7047
7274
  return this.runtime.wait(this.id, options);
7048
7275
  }
7049
- respond(input) {
7050
- return this.runtime.respond(this.id, input);
7051
- }
7052
7276
  respondInteraction(input) {
7053
7277
  return this.runtime.respondInteraction(this.id, input);
7054
7278
  }
@@ -7080,9 +7304,6 @@ function boundaryFromSnapshot(snapshot) {
7080
7304
  case "input_required":
7081
7305
  return {
7082
7306
  type: "input_required",
7083
- requestId: snapshot.request.id,
7084
- phase: snapshot.request.phase,
7085
- ...snapshot.request.prompt ? { prompt: snapshot.request.prompt } : {},
7086
7307
  interactions: snapshot.interactions,
7087
7308
  answers: snapshot.answers
7088
7309
  };
@@ -7215,7 +7436,24 @@ var InMemoryStore = class _InMemoryStore {
7215
7436
  static fromState(state) {
7216
7437
  const store = new _InMemoryStore({ incarnation: state.incarnation });
7217
7438
  for (const agent of state.agents) store.agents.set(agent.id, clone(agent));
7218
- for (const run of state.runs) store.runs.set(run.id, clone(run));
7439
+ for (const run of state.runs) {
7440
+ const clonedRun = clone(run);
7441
+ if (clonedRun.openInputRequest) {
7442
+ delete clonedRun.openInputRequest;
7443
+ delete clonedRun.checkpoint;
7444
+ delete clonedRun.execution;
7445
+ delete clonedRun.openInteractions;
7446
+ delete clonedRun.interactionAnswers;
7447
+ clonedRun.state = "cancelled";
7448
+ clonedRun.cancellationReason = "Input Request retired in v0.13";
7449
+ clonedRun.revision += 1;
7450
+ clonedRun.updatedAt = createTimestamp();
7451
+ store.runs.set(clonedRun.id, clonedRun);
7452
+ store.appendTransition(clonedRun, "input_required", "cancelled", { reason: clonedRun.cancellationReason });
7453
+ } else {
7454
+ store.runs.set(clonedRun.id, clonedRun);
7455
+ }
7456
+ }
7219
7457
  for (const message of state.messages) store.messages.set(message.id, clone(message));
7220
7458
  for (const toolCall of state.toolCalls ?? []) store.toolCalls.set(toolCall.id, clone(toolCall));
7221
7459
  store.events.push(...clone(state.events));
@@ -7274,7 +7512,7 @@ var InMemoryStore = class _InMemoryStore {
7274
7512
  delete run.execution;
7275
7513
  run.revision += 1;
7276
7514
  run.updatedAt = createTimestamp();
7277
- this.appendTransition(run, "running", "failed", void 0, void 0, void 0, failure);
7515
+ this.appendTransition(run, "running", "failed", { failure });
7278
7516
  }
7279
7517
  }
7280
7518
  async openOwner(input) {
@@ -7498,7 +7736,8 @@ var InMemoryStore = class _InMemoryStore {
7498
7736
  const openToolCalls = [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.state === "pending" || toolCall.state === "running"));
7499
7737
  for (const toolCall of openToolCalls) this.interruptToolCall(run, toolCall, "Run superseded by Message revision.");
7500
7738
  } else {
7501
- delete run.openInputRequest;
7739
+ delete run.openInteractions;
7740
+ delete run.interactionAnswers;
7502
7741
  delete run.checkpoint;
7503
7742
  }
7504
7743
  this.dropClaimReceipt(run.execution);
@@ -7507,7 +7746,7 @@ var InMemoryStore = class _InMemoryStore {
7507
7746
  run.cancellationReason = "Run superseded by Message revision.";
7508
7747
  run.revision += 1;
7509
7748
  run.updatedAt = timestamp;
7510
- this.appendTransition(run, previousState, "cancelled", void 0, void 0, void 0, void 0, run.cancellationReason);
7749
+ this.appendTransition(run, previousState, "cancelled", { reason: run.cancellationReason });
7511
7750
  } else {
7512
7751
  run.revision += 1;
7513
7752
  run.updatedAt = timestamp;
@@ -7551,6 +7790,29 @@ var InMemoryStore = class _InMemoryStore {
7551
7790
  this.writeOperationReceipt(operationKey, operationPayload, result);
7552
7791
  return clone(result);
7553
7792
  }
7793
+ commitInteractionRecord(run, interaction, status, answer, reply) {
7794
+ const now = createTimestamp();
7795
+ const record = {
7796
+ id: interaction.id,
7797
+ agentId: run.agentId,
7798
+ runId: run.id,
7799
+ role: "interaction",
7800
+ interactionId: interaction.id,
7801
+ kind: interaction.kind,
7802
+ prompt: interaction.prompt,
7803
+ phase: interaction.phase,
7804
+ status,
7805
+ ...answer !== void 0 ? { answer: clone(answer) } : {},
7806
+ ...reply !== void 0 ? { reply } : {},
7807
+ ...interaction.toolCallId !== void 0 ? { toolCallId: interaction.toolCallId } : {},
7808
+ ...interaction.result !== void 0 ? { result: clone(interaction.result) } : {},
7809
+ sequenceWithinRun: this.nextMessageSequence(run.id),
7810
+ createdAt: now
7811
+ };
7812
+ this.messages.set(record.id, record);
7813
+ this.appendMessage(run, record);
7814
+ return record;
7815
+ }
7554
7816
  createRun(lease, input) {
7555
7817
  this.assertOwner(lease);
7556
7818
  this.requireAgent(input.agentId);
@@ -7559,6 +7821,45 @@ var InMemoryStore = class _InMemoryStore {
7559
7821
  const payload = canonicalStartRunRequest(normalizedInput, input.metadata);
7560
7822
  const replay = this.replay(scope, payload);
7561
7823
  if (replay) return clone(replay);
7824
+ const waitingRun = isControlMetadata(input.metadata) ? void 0 : [...this.runs.values()].find((candidate) => candidate.agentId === input.agentId && candidate.state === "input_required");
7825
+ if (waitingRun) {
7826
+ const replyText = typeof normalizedInput === "string" ? normalizedInput : typeof normalizedInput.content === "string" ? normalizedInput.content : normalizedInput.content.map((c) => c.type === "text" ? c.text : "").join(" ");
7827
+ const openInteractions = waitingRun.openInteractions ?? [];
7828
+ for (const interaction of openInteractions) {
7829
+ this.commitInteractionRecord(waitingRun, interaction, "replied", void 0, replyText);
7830
+ }
7831
+ delete waitingRun.openInteractions;
7832
+ delete waitingRun.interactionAnswers;
7833
+ if (waitingRun.checkpoint?.data && typeof waitingRun.checkpoint.data === "object") {
7834
+ const stored = waitingRun.checkpoint.data.runInteractions ?? waitingRun.checkpoint.data.phaseInteractions;
7835
+ if (stored?.requests && Array.isArray(stored.requests)) {
7836
+ for (const req of stored.requests) {
7837
+ req.status = "replied";
7838
+ req.reply = replyText;
7839
+ }
7840
+ }
7841
+ }
7842
+ const now = createTimestamp();
7843
+ const message = {
7844
+ id: createId("msg"),
7845
+ agentId: waitingRun.agentId,
7846
+ runId: waitingRun.id,
7847
+ role: "user",
7848
+ content: userInputContent(normalizedInput),
7849
+ ...userInputMetadata(normalizedInput) ? { metadata: clone(userInputMetadata(normalizedInput)) } : {},
7850
+ sequenceWithinRun: this.nextMessageSequence(waitingRun.id),
7851
+ createdAt: now
7852
+ };
7853
+ this.messages.set(message.id, message);
7854
+ this.appendMessage(waitingRun, message);
7855
+ waitingRun.readySequence = this.nextReady(waitingRun.agentId);
7856
+ waitingRun.state = "queued";
7857
+ waitingRun.revision += 1;
7858
+ waitingRun.updatedAt = now;
7859
+ this.appendTransition(waitingRun, "input_required", "queued");
7860
+ this.writeReceipt(scope, payload, waitingRun);
7861
+ return clone(waitingRun);
7862
+ }
7562
7863
  const timestamp = createTimestamp();
7563
7864
  const run = {
7564
7865
  id: createId("run"),
@@ -7646,7 +7947,26 @@ var InMemoryStore = class _InMemoryStore {
7646
7947
  run.failure = clone(input.failure);
7647
7948
  run.revision += 1;
7648
7949
  run.updatedAt = createTimestamp();
7649
- this.appendTransition(run, "queued", "failed", void 0, void 0, void 0, input.failure);
7950
+ this.appendTransition(run, "queued", "failed", { failure: input.failure });
7951
+ const result = clone(run);
7952
+ this.writeOperationReceipt(operationKey, operationPayload, result);
7953
+ return result;
7954
+ }
7955
+ commitPhaseEntered(lease, input) {
7956
+ this.assertOwner(lease);
7957
+ if (input.phaseId.length === 0) throw new TypeError("phaseId must be non-empty");
7958
+ const operationKey = `phase_entered:${input.runId}:${input.execution.executionId}:${input.visit}`;
7959
+ const operationPayload = canonicalJson([input.phaseId, input.expectedRevision]);
7960
+ const replay = this.replayOperation(operationKey, operationPayload);
7961
+ if (replay) return clone(replay);
7962
+ const run = this.requireRun(input.runId);
7963
+ this.assertExecution(run, input.execution, input.expectedRevision);
7964
+ this.assertState(run, ["running"]);
7965
+ run.currentPhaseId = input.phaseId;
7966
+ run.revision += 1;
7967
+ run.updatedAt = createTimestamp();
7968
+ const visit2 = this.events.filter((event) => event.runId === run.id && event.kind === "phase_entered").length + 1;
7969
+ this.events.push(this.baseEvent(run, { kind: "phase_entered", executionId: input.execution.executionId, phaseId: input.phaseId, visit: visit2 }));
7650
7970
  const result = clone(run);
7651
7971
  this.writeOperationReceipt(operationKey, operationPayload, result);
7652
7972
  return result;
@@ -7654,8 +7974,7 @@ var InMemoryStore = class _InMemoryStore {
7654
7974
  commitInputRequired(lease, input) {
7655
7975
  this.assertOwner(lease);
7656
7976
  if (typeof input.phase !== "string" || input.phase.length === 0) throw new TypeError("phase must be non-empty");
7657
- const requestId = input.requestId ?? createId("input");
7658
- const operationKey = `input_required:${requestId}`;
7977
+ const operationKey = `input_required:${input.runId}:${input.execution.executionId}`;
7659
7978
  const interactions = input.interactions ?? [];
7660
7979
  const operationPayload = canonicalJson([input.runId, input.expectedRevision, input.phase, input.prompt ? input.prompt.id : null, input.checkpoint, interactions, input.interactionAnswers ?? null]);
7661
7980
  const replay = this.replayOperation(operationKey, operationPayload);
@@ -7663,8 +7982,8 @@ var InMemoryStore = class _InMemoryStore {
7663
7982
  const run = this.requireRun(input.runId);
7664
7983
  this.assertExecution(run, input.execution, input.expectedRevision);
7665
7984
  this.assertState(run, ["running"]);
7666
- this.assertNoOpenTools(run);
7667
- const isToolCallInteraction = interactions.length > 0 && interactions.some((interaction) => interaction.origin === "tool_call");
7985
+ this.assertNoOpenTools(run, input.pendingToolCallIds ?? []);
7986
+ const isToolCallInteraction = interactions.length > 0 && interactions.some((interaction) => interaction.toolCallId !== void 0);
7668
7987
  let prompt;
7669
7988
  if (input.prompt && !isToolCallInteraction) {
7670
7989
  prompt = {
@@ -7673,15 +7992,8 @@ var InMemoryStore = class _InMemoryStore {
7673
7992
  };
7674
7993
  this.messages.set(prompt.id, prompt);
7675
7994
  }
7676
- const request = {
7677
- id: requestId,
7678
- phase: input.phase,
7679
- ...prompt ? { messageId: prompt.id } : {},
7680
- createdAt: createTimestamp()
7681
- };
7682
7995
  run.state = "input_required";
7683
7996
  run.checkpoint = clone(input.checkpoint);
7684
- run.openInputRequest = request;
7685
7997
  if (interactions.length > 0) {
7686
7998
  run.openInteractions = clone(interactions);
7687
7999
  run.interactionAnswers = clone(input.interactionAnswers ?? run.interactionAnswers ?? {});
@@ -7696,50 +8008,7 @@ var InMemoryStore = class _InMemoryStore {
7696
8008
  if (prompt) {
7697
8009
  this.appendMessage(run, prompt);
7698
8010
  }
7699
- this.appendTransition(run, "running", "input_required", request, prompt, void 0, void 0, void 0, interactions, run.interactionAnswers ?? {});
7700
- const result = {
7701
- run: clone(run),
7702
- ...prompt ? { prompt: clone(prompt) } : {},
7703
- request: clone(request),
7704
- interactions: clone(interactions)
7705
- };
7706
- this.writeOperationReceipt(operationKey, operationPayload, result);
7707
- return result;
7708
- }
7709
- answerInput(lease, input) {
7710
- this.assertOwner(lease);
7711
- const normalizedInput = normalizeUserInput(input.input);
7712
- const operationKey = `answer:${input.runId}:${input.requestId}`;
7713
- const operationPayload = canonicalStartRunRequest(normalizedInput);
7714
- const replay = this.replayOperation(operationKey, operationPayload);
7715
- if (replay) return clone(replay);
7716
- const run = this.requireRun(input.runId);
7717
- this.assertRevision(run, input.expectedRevision);
7718
- this.assertState(run, ["input_required"]);
7719
- if (!run.openInputRequest || run.openInputRequest.id !== input.requestId) {
7720
- throw new RuntimeError("input_request_conflict", { runId: run.id, requestId: input.requestId, reason: "not_found" });
7721
- }
7722
- if (run.openInteractions && run.openInteractions.length > 0) {
7723
- throw new RuntimeError("input_request_conflict", { runId: run.id, requestId: input.requestId, reason: "not_found" });
7724
- }
7725
- const message = {
7726
- id: input.messageId ?? createId("msg"),
7727
- agentId: run.agentId,
7728
- runId: run.id,
7729
- role: "user",
7730
- content: userInputContent(normalizedInput),
7731
- ...userInputMetadata(normalizedInput) ? { metadata: clone(userInputMetadata(normalizedInput)) } : {},
7732
- sequenceWithinRun: this.nextMessageSequence(run.id),
7733
- createdAt: createTimestamp()
7734
- };
7735
- this.messages.set(message.id, message);
7736
- delete run.openInputRequest;
7737
- run.readySequence = this.nextReady(run.agentId);
7738
- run.state = "queued";
7739
- run.revision += 1;
7740
- run.updatedAt = createTimestamp();
7741
- this.appendMessage(run, message);
7742
- this.appendTransition(run, "input_required", "queued");
8011
+ this.appendTransition(run, "running", "input_required", { prompt, interactions, answers: run.interactionAnswers ?? {} });
7743
8012
  const result = clone(run);
7744
8013
  this.writeOperationReceipt(operationKey, operationPayload, result);
7745
8014
  return result;
@@ -7747,8 +8016,9 @@ var InMemoryStore = class _InMemoryStore {
7747
8016
  answerInteraction(lease, input) {
7748
8017
  this.assertOwner(lease);
7749
8018
  if (input.interactionId.trim().length === 0) throw new TypeError("interactionId must be non-empty");
8019
+ if (!input.cancel && input.input === void 0) throw new TypeError("respondInteraction requires input or cancel: true");
7750
8020
  const operationKey = `interaction_answer:${input.runId}:${input.interactionId}`;
7751
- const operationPayload = canonicalJson(input.input);
8021
+ const operationPayload = canonicalJson([input.input ?? null, input.cancel ?? false]);
7752
8022
  const replay = this.replayOperation(operationKey, operationPayload);
7753
8023
  if (replay) return clone(replay);
7754
8024
  const run = this.requireRun(input.runId);
@@ -7757,34 +8027,33 @@ var InMemoryStore = class _InMemoryStore {
7757
8027
  const openInteractions = run.openInteractions ?? [];
7758
8028
  const targetInteraction = openInteractions.find((interaction) => interaction.id === input.interactionId);
7759
8029
  if (!targetInteraction) {
7760
- throw new RuntimeError("input_request_conflict", { runId: run.id, requestId: input.interactionId, reason: "not_found" });
8030
+ throw new RuntimeError("input_request_conflict", { runId: run.id, interactionId: input.interactionId, reason: "not_found" });
7761
8031
  }
7762
- const isToolCall = targetInteraction.origin === "tool_call";
8032
+ const isCancel = input.cancel === true;
8033
+ const status = isCancel ? "cancelled" : "answered";
7763
8034
  const now = createTimestamp();
7764
- if (!isToolCall) {
7765
- const message = {
7766
- id: createId("msg"),
7767
- agentId: run.agentId,
7768
- runId: run.id,
7769
- role: "user",
7770
- content: typeof input.input === "string" ? input.input : JSON.stringify(input.input),
7771
- metadata: { kind: "phase_interaction", interactionId: input.interactionId },
7772
- sequenceWithinRun: this.nextMessageSequence(run.id),
7773
- createdAt: now
7774
- };
7775
- this.messages.set(message.id, message);
7776
- this.appendMessage(run, message);
8035
+ this.commitInteractionRecord(run, targetInteraction, status, isCancel ? void 0 : input.input);
8036
+ if (run.checkpoint?.data && typeof run.checkpoint.data === "object") {
8037
+ const stored = run.checkpoint.data.runInteractions ?? run.checkpoint.data.phaseInteractions;
8038
+ if (stored?.requests && Array.isArray(stored.requests)) {
8039
+ const req = stored.requests.find((r) => r.id === input.interactionId);
8040
+ if (req) {
8041
+ req.status = status;
8042
+ if (!isCancel && input.input !== void 0) req.answer = clone(input.input);
8043
+ }
8044
+ }
7777
8045
  }
7778
8046
  run.openInteractions = openInteractions.filter((interaction) => interaction.id !== input.interactionId);
7779
- run.interactionAnswers = {
7780
- ...run.interactionAnswers ?? {},
7781
- [input.interactionId]: clone(input.input)
7782
- };
8047
+ if (!isCancel && input.input !== void 0) {
8048
+ run.interactionAnswers = {
8049
+ ...run.interactionAnswers ?? {},
8050
+ [input.interactionId]: clone(input.input)
8051
+ };
8052
+ }
7783
8053
  run.revision += 1;
7784
8054
  run.updatedAt = now;
7785
8055
  if (run.openInteractions.length === 0) {
7786
8056
  delete run.openInteractions;
7787
- delete run.openInputRequest;
7788
8057
  run.readySequence = this.nextReady(run.agentId);
7789
8058
  run.state = "queued";
7790
8059
  this.appendTransition(run, "input_required", "queued");
@@ -7884,7 +8153,7 @@ var InMemoryStore = class _InMemoryStore {
7884
8153
  }
7885
8154
  startToolCall(lease, input) {
7886
8155
  this.assertOwner(lease);
7887
- const operationKey = `tool_start:${input.toolCallId}`;
8156
+ const operationKey = `tool_start:${input.toolCallId}:${input.execution.executionId}`;
7888
8157
  const operationPayload = canonicalJson([input.runId, input.expectedRevision, input.execution.executionId]);
7889
8158
  const replay = this.replayOperation(operationKey, operationPayload);
7890
8159
  if (replay) return clone(replay);
@@ -7892,8 +8161,9 @@ var InMemoryStore = class _InMemoryStore {
7892
8161
  this.assertExecution(run, input.execution, input.expectedRevision);
7893
8162
  const toolCall = this.requireToolCall(input.toolCallId, run);
7894
8163
  if (toolCall.state !== "pending") throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
7895
- if (toolCall.executionId !== input.execution.executionId) throw new RuntimeError("runtime_ownership_lost", { reason: "epoch_advanced", expectedEpoch: input.execution.ownerEpoch, actualEpoch: this.ownerEpoch });
7896
8164
  toolCall.state = "running";
8165
+ toolCall.executionId = input.execution.executionId;
8166
+ toolCall.executionId = input.execution.executionId;
7897
8167
  toolCall.updatedAt = createTimestamp();
7898
8168
  run.revision += 1;
7899
8169
  run.updatedAt = toolCall.updatedAt;
@@ -7902,6 +8172,27 @@ var InMemoryStore = class _InMemoryStore {
7902
8172
  this.writeOperationReceipt(operationKey, operationPayload, result);
7903
8173
  return result;
7904
8174
  }
8175
+ suspendToolCall(lease, input) {
8176
+ this.assertOwner(lease);
8177
+ const operationKey = `tool_suspend:${input.toolCallId}:${input.execution.executionId}`;
8178
+ const operationPayload = canonicalJson([input.runId, input.expectedRevision, input.execution.executionId]);
8179
+ const replay = this.replayOperation(operationKey, operationPayload);
8180
+ if (replay) return clone(replay);
8181
+ const run = this.requireRun(input.runId);
8182
+ this.assertExecution(run, input.execution, input.expectedRevision);
8183
+ const toolCall = this.requireToolCall(input.toolCallId, run);
8184
+ if (toolCall.state !== "running" || toolCall.executionId !== input.execution.executionId) {
8185
+ throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
8186
+ }
8187
+ toolCall.state = "pending";
8188
+ toolCall.updatedAt = createTimestamp();
8189
+ run.revision += 1;
8190
+ run.updatedAt = toolCall.updatedAt;
8191
+ this.appendToolTransition(run, { from: "running", to: "pending" }, toolCall);
8192
+ const result = { run: clone(run), toolCall: clone(toolCall) };
8193
+ this.writeOperationReceipt(operationKey, operationPayload, result);
8194
+ return result;
8195
+ }
7905
8196
  commitToolResult(lease, input) {
7906
8197
  this.assertOwner(lease);
7907
8198
  assertToolExecutionResult(input.result);
@@ -7960,7 +8251,7 @@ var InMemoryStore = class _InMemoryStore {
7960
8251
  delete run.execution;
7961
8252
  run.revision += 1;
7962
8253
  run.updatedAt = createTimestamp();
7963
- this.appendTransition(run, "running", "failed", void 0, void 0, void 0, failure);
8254
+ this.appendTransition(run, "running", "failed", { failure });
7964
8255
  }
7965
8256
  const result = { run: clone(run), toolCall: clone(toolCall) };
7966
8257
  this.writeOperationReceipt(operationKey, operationPayload, result);
@@ -7993,7 +8284,7 @@ var InMemoryStore = class _InMemoryStore {
7993
8284
  delete run.interactionAnswers;
7994
8285
  run.revision += 1;
7995
8286
  run.updatedAt = createTimestamp();
7996
- this.appendTransition(run, "running", nextState, void 0, output, input.outcome, input.failure);
8287
+ this.appendTransition(run, "running", nextState, { outcome: input.outcome, failure: input.failure, output });
7997
8288
  const result = clone(run);
7998
8289
  this.writeOperationReceipt(operationKey, operationPayload, result);
7999
8290
  return result;
@@ -8017,7 +8308,13 @@ var InMemoryStore = class _InMemoryStore {
8017
8308
  return result2;
8018
8309
  }
8019
8310
  const from = run.state;
8020
- const activeToolCalls = run.execution ? [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.state === "pending" || toolCall.state === "running")) : [];
8311
+ 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) ? (() => {
8312
+ const interactionState = run.checkpoint.data.runInteractions;
8313
+ const interactionCheckpoint = interactionState && typeof interactionState === "object" && !Array.isArray(interactionState) ? interactionState.checkpoint : void 0;
8314
+ const ids = interactionCheckpoint && typeof interactionCheckpoint === "object" && !Array.isArray(interactionCheckpoint) ? interactionCheckpoint.toolCallIds : void 0;
8315
+ const allowed = new Set(Array.isArray(ids) ? ids : []);
8316
+ return [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.state === "pending" && allowed.has(toolCall.id));
8317
+ })() : [];
8021
8318
  const indeterminateToolCallIds = activeToolCalls.filter((toolCall) => toolCall.state === "running").map((toolCall) => toolCall.id);
8022
8319
  for (const toolCall of activeToolCalls) this.interruptToolCall(run, toolCall, input.reason ?? "The Run was cancelled.");
8023
8320
  if (input.output && (typeof input.output.content === "string" ? input.output.content.length > 0 : input.output.content.length > 0)) {
@@ -8025,6 +8322,11 @@ var InMemoryStore = class _InMemoryStore {
8025
8322
  this.messages.set(output.id, output);
8026
8323
  this.appendMessage(run, output);
8027
8324
  }
8325
+ if (run.openInteractions && run.openInteractions.length > 0) {
8326
+ for (const interaction of run.openInteractions) {
8327
+ this.commitInteractionRecord(run, interaction, "cancelled");
8328
+ }
8329
+ }
8028
8330
  if (indeterminateToolCallIds.length > 0) {
8029
8331
  const failure = {
8030
8332
  code: "tool_indeterminate",
@@ -8036,12 +8338,11 @@ var InMemoryStore = class _InMemoryStore {
8036
8338
  delete run.cancellationReason;
8037
8339
  this.dropClaimReceipt(run.execution);
8038
8340
  delete run.execution;
8039
- delete run.openInputRequest;
8040
8341
  delete run.openInteractions;
8041
8342
  delete run.interactionAnswers;
8042
8343
  run.revision += 1;
8043
8344
  run.updatedAt = createTimestamp();
8044
- this.appendTransition(run, from, "failed", void 0, void 0, void 0, failure);
8345
+ this.appendTransition(run, from, "failed", { failure });
8045
8346
  const result2 = clone(run);
8046
8347
  if (input.expectedRevision !== void 0) this.writeOperationReceipt(operationKey, operationPayload, result2);
8047
8348
  return result2;
@@ -8050,12 +8351,11 @@ var InMemoryStore = class _InMemoryStore {
8050
8351
  run.cancellationReason = input.reason;
8051
8352
  this.dropClaimReceipt(run.execution);
8052
8353
  delete run.execution;
8053
- delete run.openInputRequest;
8054
8354
  delete run.openInteractions;
8055
8355
  delete run.interactionAnswers;
8056
8356
  run.revision += 1;
8057
8357
  run.updatedAt = createTimestamp();
8058
- this.appendTransition(run, from, "cancelled", void 0, void 0, void 0, void 0, input.reason);
8358
+ this.appendTransition(run, from, "cancelled", { reason: input.reason });
8059
8359
  const result = clone(run);
8060
8360
  if (input.expectedRevision !== void 0) this.writeOperationReceipt(operationKey, operationPayload, result);
8061
8361
  return result;
@@ -8072,6 +8372,7 @@ var InMemoryStore = class _InMemoryStore {
8072
8372
  ...run.metadata ? { metadata: clone(run.metadata) } : {},
8073
8373
  messageCount: this.messagesForRun(run.id).length,
8074
8374
  toolCallCount: [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id).length,
8375
+ ...run.currentPhaseId === void 0 ? {} : { currentPhaseId: run.currentPhaseId },
8075
8376
  createdAt: run.createdAt,
8076
8377
  updatedAt: run.updatedAt,
8077
8378
  cursor: this.cursorForRun(run.id)
@@ -8081,17 +8382,9 @@ var InMemoryStore = class _InMemoryStore {
8081
8382
  case "running":
8082
8383
  return { ...base, state: run.state };
8083
8384
  case "input_required": {
8084
- const request = run.openInputRequest;
8085
- const prompt = request?.messageId ? this.messages.get(request.messageId) : void 0;
8086
- if (request?.messageId && (!prompt || prompt.role !== "assistant")) throw new Error(`Run ${run.id} has an invalid input prompt.`);
8087
8385
  return {
8088
8386
  ...base,
8089
8387
  state: "input_required",
8090
- request: {
8091
- id: request?.id ?? "",
8092
- phase: request?.phase ?? "",
8093
- ...prompt ? { prompt: clone(prompt) } : {}
8094
- },
8095
8388
  interactions: clone(run.openInteractions ?? []),
8096
8389
  answers: clone(run.interactionAnswers ?? {})
8097
8390
  };
@@ -8266,8 +8559,9 @@ var InMemoryStore = class _InMemoryStore {
8266
8559
  if (!toolCall || toolCall.runId !== run.id) throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
8267
8560
  return toolCall;
8268
8561
  }
8269
- assertNoOpenTools(run) {
8270
- if ([...this.toolCalls.values()].some((toolCall) => toolCall.runId === run.id && (toolCall.state === "pending" || toolCall.state === "running"))) {
8562
+ assertNoOpenTools(run, allowedPendingToolCallIds = []) {
8563
+ const allowed = new Set(allowedPendingToolCallIds);
8564
+ if ([...this.toolCalls.values()].some((toolCall) => toolCall.runId === run.id && (toolCall.state === "running" || toolCall.state === "pending" && !allowed.has(toolCall.id)))) {
8271
8565
  throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
8272
8566
  }
8273
8567
  }
@@ -8407,23 +8701,16 @@ ${record.summary}`,
8407
8701
  message
8408
8702
  }));
8409
8703
  }
8410
- appendTransition(run, from, to, request, prompt, outcome, failure, reason, interactions, answers) {
8704
+ appendTransition(run, from, to, options) {
8411
8705
  const transition = {
8412
8706
  ...this.baseEvent(run),
8413
8707
  kind: "run_state_changed",
8414
8708
  from,
8415
8709
  to,
8416
- ...to === "input_required" && request ? {
8417
- request: {
8418
- id: request.id,
8419
- phase: request.phase,
8420
- ...prompt ? { prompt } : {}
8421
- }
8422
- } : {},
8423
- ...to === "input_required" ? { interactions: clone(interactions ?? []), answers: clone(answers ?? {}) } : {},
8424
- ...to === "completed" && outcome ? { outcome } : {},
8425
- ...to === "failed" && failure ? { failure } : {},
8426
- ...to === "cancelled" && reason ? { reason } : {}
8710
+ ...to === "input_required" ? { interactions: clone(options?.interactions ?? []), answers: clone(options?.answers ?? {}) } : {},
8711
+ ...to === "completed" && options?.outcome ? { outcome: options.outcome, ...options.output ? { output: options.output } : {} } : {},
8712
+ ...to === "failed" && options?.failure ? { failure: options.failure } : {},
8713
+ ...to === "cancelled" && options?.reason ? { reason: options.reason } : {}
8427
8714
  };
8428
8715
  this.events.push(transition);
8429
8716
  }
@@ -8501,12 +8788,12 @@ var MemoryOwnedStore = class {
8501
8788
  async failQueuedRun(input) {
8502
8789
  return this.store.failQueuedRun(this.lease, input);
8503
8790
  }
8791
+ async commitPhaseEntered(input) {
8792
+ return this.store.commitPhaseEntered(this.lease, input);
8793
+ }
8504
8794
  async commitInputRequired(input) {
8505
8795
  return this.store.commitInputRequired(this.lease, input);
8506
8796
  }
8507
- async answerInput(input) {
8508
- return this.store.answerInput(this.lease, input);
8509
- }
8510
8797
  async answerInteraction(input) {
8511
8798
  return this.store.answerInteraction(this.lease, input);
8512
8799
  }
@@ -8522,6 +8809,9 @@ var MemoryOwnedStore = class {
8522
8809
  async startToolCall(input) {
8523
8810
  return this.store.startToolCall(this.lease, input);
8524
8811
  }
8812
+ async suspendToolCall(input) {
8813
+ return this.store.suspendToolCall(this.lease, input);
8814
+ }
8525
8815
  async commitToolResult(input) {
8526
8816
  return this.store.commitToolResult(this.lease, input);
8527
8817
  }
@@ -8585,26 +8875,37 @@ function hasUserInput(input) {
8585
8875
  function userInputMetadata(input) {
8586
8876
  return typeof input === "string" ? void 0 : input.metadata;
8587
8877
  }
8588
- function isControlRun(run) {
8589
- const rowan = run.metadata?.rowan;
8878
+ function isControlMetadata(metadata) {
8879
+ const rowan = metadata?.rowan;
8590
8880
  return typeof rowan === "object" && rowan !== null && typeof rowan.kind === "string" && rowan.kind.length > 0;
8591
8881
  }
8882
+ function isControlRun(run) {
8883
+ return isControlMetadata(run.metadata);
8884
+ }
8592
8885
  function estimateMessageTokens(messages) {
8593
8886
  let characters = 0;
8594
8887
  for (const message of messages) {
8595
- characters += messageContentText2(message.content).length;
8888
+ if (message.role === "interaction") {
8889
+ characters += (message.prompt?.length ?? 0) + (message.answer !== void 0 ? JSON.stringify(message.answer).length : 0) + (message.reply?.length ?? 0);
8890
+ } else {
8891
+ characters += messageContentText2(message.content).length;
8892
+ }
8596
8893
  characters += 20;
8597
8894
  }
8598
8895
  return Math.ceil(characters / 4);
8599
8896
  }
8600
8897
  function messageContentText2(content) {
8898
+ if (!content) return "";
8601
8899
  if (typeof content === "string") return content;
8900
+ if (!Array.isArray(content)) return "";
8602
8901
  return content.map((part) => {
8603
- if (part.type === "text") return part.text;
8604
- if (part.type === "thinking") return part.thinking;
8605
- if (part.type === "tool_use") return JSON.stringify(part.input);
8606
- if (part.type === "tool_result") return JSON.stringify(part.result);
8607
- if (part.type === "image") return `[image:${part.mimeType}]`;
8902
+ if (part && typeof part === "object") {
8903
+ if (part.type === "text") return part.text ?? "";
8904
+ if (part.type === "thinking") return part.thinking ?? "";
8905
+ if (part.type === "tool_use") return JSON.stringify(part.input);
8906
+ if (part.type === "tool_result") return JSON.stringify(part.result);
8907
+ if (part.type === "image") return `[image:${part.mimeType}]`;
8908
+ }
8608
8909
  return "";
8609
8910
  }).join("\n");
8610
8911
  }
@@ -8616,6 +8917,16 @@ function materializeHistorySeed(agentId, source) {
8616
8917
  const seedRunId = createId("seed");
8617
8918
  const toolIds = /* @__PURE__ */ new Map();
8618
8919
  return source.map((message, sequenceWithinRun) => {
8920
+ if (message.role === "interaction") {
8921
+ return {
8922
+ ...clone(message),
8923
+ id: createId("msg"),
8924
+ agentId,
8925
+ runId: seedRunId,
8926
+ sequenceWithinRun,
8927
+ createdAt: createTimestamp()
8928
+ };
8929
+ }
8619
8930
  const content = remapSeedContent(message.content, toolIds);
8620
8931
  return {
8621
8932
  ...clone(message),
@@ -8854,12 +9165,12 @@ var SqliteStore = class {
8854
9165
  async failQueuedRun(lease, input) {
8855
9166
  return this.invoke(lease, (store, current) => store.failQueuedRun(current, input));
8856
9167
  }
9168
+ async commitPhaseEntered(lease, input) {
9169
+ return this.invoke(lease, (store, current) => store.commitPhaseEntered(current, input));
9170
+ }
8857
9171
  async commitInputRequired(lease, input) {
8858
9172
  return this.invoke(lease, (store, current) => store.commitInputRequired(current, input));
8859
9173
  }
8860
- async answerInput(lease, input) {
8861
- return this.invoke(lease, (store, current) => store.answerInput(current, input));
8862
- }
8863
9174
  async answerInteraction(lease, input) {
8864
9175
  return this.invoke(lease, (store, current) => store.answerInteraction(current, input));
8865
9176
  }
@@ -8875,6 +9186,9 @@ var SqliteStore = class {
8875
9186
  async startToolCall(lease, input) {
8876
9187
  return this.invoke(lease, (store, current) => store.startToolCall(current, input));
8877
9188
  }
9189
+ async suspendToolCall(lease, input) {
9190
+ return this.invoke(lease, (store, current) => store.suspendToolCall(current, input));
9191
+ }
8878
9192
  async commitToolResult(lease, input) {
8879
9193
  return this.invoke(lease, (store, current) => store.commitToolResult(current, input));
8880
9194
  }
@@ -9244,12 +9558,12 @@ var SqliteOwnedStore = class {
9244
9558
  failQueuedRun(input) {
9245
9559
  return this.store.failQueuedRun(this.lease, input);
9246
9560
  }
9561
+ commitPhaseEntered(input) {
9562
+ return this.store.commitPhaseEntered(this.lease, input);
9563
+ }
9247
9564
  commitInputRequired(input) {
9248
9565
  return this.store.commitInputRequired(this.lease, input);
9249
9566
  }
9250
- answerInput(input) {
9251
- return this.store.answerInput(this.lease, input);
9252
- }
9253
9567
  answerInteraction(input) {
9254
9568
  return this.store.answerInteraction(this.lease, input);
9255
9569
  }
@@ -9265,6 +9579,9 @@ var SqliteOwnedStore = class {
9265
9579
  startToolCall(input) {
9266
9580
  return this.store.startToolCall(this.lease, input);
9267
9581
  }
9582
+ suspendToolCall(input) {
9583
+ return this.store.suspendToolCall(this.lease, input);
9584
+ }
9268
9585
  commitToolResult(input) {
9269
9586
  return this.store.commitToolResult(this.lease, input);
9270
9587
  }
@@ -9349,10 +9666,10 @@ export {
9349
9666
  ExtensionLifetimeError,
9350
9667
  InMemoryConfigProvider,
9351
9668
  InMemoryStore,
9352
- PhaseInteractionBoundary,
9353
- PhaseInteractionCancelledError,
9354
9669
  ResourceRegistry,
9355
9670
  ResourceRegistryError,
9671
+ RunInteractionBoundary,
9672
+ RunInteractionCancelledError,
9356
9673
  RuntimeBootstrapRegistry,
9357
9674
  RuntimeError,
9358
9675
  RuntimeExtensionLifetime,