@rtrvr-ai/rover 1.1.2 → 1.2.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.
@@ -758,13 +758,13 @@ function isAllowedByRules(url, rules) {
758
758
  if (!host)
759
759
  return { allowed: false, reason: "invalid_url" };
760
760
  if (rules.denyDomains.some((rule) => matchesDomainRule(host, rule))) {
761
- return { allowed: false, reason: `deny_rule:${host}` };
761
+ return { allowed: false, reason: "deny_rule" };
762
762
  }
763
763
  if (!rules.allowDomains.length)
764
764
  return { allowed: true };
765
765
  if (rules.allowDomains.some((rule) => matchesDomainRule(host, rule)))
766
766
  return { allowed: true };
767
- return { allowed: false, reason: `not_in_allowlist:${host}` };
767
+ return { allowed: false, reason: "not_in_allowlist" };
768
768
  }
769
769
  function normalizeRuntimeExternalTabs(input) {
770
770
  if (!Array.isArray(input))
@@ -1456,6 +1456,61 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
1456
1456
  return { needsRetry: false, data };
1457
1457
  }
1458
1458
  if (Array.isArray(functionCalls) && functionCalls.length > 0) {
1459
+ const askUserCall = functionCalls.find((call) => String(call?.name || "").trim().toLowerCase() === "ask_user");
1460
+ if (askUserCall) {
1461
+ const questions = normalizeAskUserQuestions(askUserCall?.args?.questions_to_ask);
1462
+ if (!questions.length) {
1463
+ prevSteps.push({
1464
+ accTreeId,
1465
+ thought,
1466
+ modelParts,
1467
+ fail: "ask_user called with invalid or empty 'questions_to_ask'",
1468
+ functions: [
1469
+ {
1470
+ name: "ask_user",
1471
+ args: askUserCall?.args || {},
1472
+ response: {
1473
+ status: "Failure",
1474
+ error: "Missing/invalid 'questions_to_ask'",
1475
+ allowFallback: false
1476
+ }
1477
+ }
1478
+ ]
1479
+ });
1480
+ limitPrevSteps(prevSteps);
1481
+ onPrevStepsUpdate?.(prevSteps);
1482
+ return { needsRetry: true };
1483
+ }
1484
+ prevSteps.push({
1485
+ accTreeId,
1486
+ thought,
1487
+ modelParts,
1488
+ functions: [
1489
+ {
1490
+ name: "ask_user",
1491
+ args: {
1492
+ questions_to_ask: questions.map((question) => ({ key: question.key, query: question.query }))
1493
+ },
1494
+ response: {
1495
+ status: "Success",
1496
+ output: {
1497
+ status: "waiting_input",
1498
+ needsUserInput: true,
1499
+ questions
1500
+ }
1501
+ }
1502
+ }
1503
+ ]
1504
+ });
1505
+ limitPrevSteps(prevSteps);
1506
+ onPrevStepsUpdate?.(prevSteps);
1507
+ onStatusUpdate?.("Need user clarification to continue", thought, "verify");
1508
+ return {
1509
+ needsRetry: false,
1510
+ needsUserInput: true,
1511
+ questions
1512
+ };
1513
+ }
1459
1514
  const systemCalls = [];
1460
1515
  const externalCalls = [];
1461
1516
  for (const call of functionCalls) {
@@ -1538,6 +1593,39 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
1538
1593
  onPrevStepsUpdate?.(prevSteps);
1539
1594
  return { needsRetry: true };
1540
1595
  }
1596
+ function normalizeAskUserQuestions(rawQuestions) {
1597
+ if (!Array.isArray(rawQuestions))
1598
+ return [];
1599
+ const out = [];
1600
+ const seenKeys = /* @__PURE__ */ new Set();
1601
+ for (const item of rawQuestions) {
1602
+ if (!item || typeof item !== "object")
1603
+ continue;
1604
+ const rawKey = String(item.key || "").trim();
1605
+ const query = resolveQuestionText(item);
1606
+ if (!rawKey || !query)
1607
+ continue;
1608
+ if (seenKeys.has(rawKey))
1609
+ continue;
1610
+ seenKeys.add(rawKey);
1611
+ out.push({
1612
+ key: rawKey,
1613
+ query,
1614
+ ...typeof item.question === "string" && item.question.trim() ? { question: String(item.question).trim() } : {},
1615
+ ...typeof item.id === "string" && item.id.trim() ? { id: String(item.id).trim() } : {},
1616
+ ...Array.isArray(item.choices) ? { choices: item.choices } : {}
1617
+ });
1618
+ }
1619
+ return out.slice(0, 6);
1620
+ }
1621
+ function resolveQuestionText(raw) {
1622
+ if (!raw || typeof raw !== "object")
1623
+ return "";
1624
+ const question = String(raw.query || raw.question || "").trim();
1625
+ if (!question)
1626
+ return "";
1627
+ return question;
1628
+ }
1541
1629
  function limitPrevSteps(prevSteps) {
1542
1630
  if (prevSteps.length <= MAX_PREV_STEPS)
1543
1631
  return;
@@ -1838,6 +1926,17 @@ async function executeAgenticSeek(options) {
1838
1926
  warnings: allWarnings
1839
1927
  };
1840
1928
  }
1929
+ if (processResult.needsUserInput && Array.isArray(processResult.questions) && processResult.questions.length > 0) {
1930
+ managePrevStepsSize(accumulatedPrevSteps);
1931
+ onPrevStepsUpdate?.(accumulatedPrevSteps);
1932
+ return {
1933
+ prevSteps: accumulatedPrevSteps,
1934
+ creditsUsed: totalCreditsUsed,
1935
+ warnings: allWarnings,
1936
+ needsUserInput: true,
1937
+ questions: processResult.questions
1938
+ };
1939
+ }
1841
1940
  if (processResult.functionCalls?.length) {
1842
1941
  const functionCallsWithIds = processResult.functionCalls.map((fc, index) => ({
1843
1942
  ...fc,
@@ -2592,8 +2691,7 @@ function resolvePath(obj, path) {
2592
2691
  }
2593
2692
 
2594
2693
  // dist/agent/toolExecutor.js
2595
- var MAX_AGENT_CHATLOG_ENTRIES = 24;
2596
- var MAX_AGENT_CHATLOG_MESSAGE_CHARS = 1e3;
2694
+ var MAX_AGENT_CHATLOG_ENTRIES = 12;
2597
2695
  function unsupportedToolResult(toolName, message) {
2598
2696
  return {
2599
2697
  error: message,
@@ -2624,17 +2722,31 @@ function buildStructuredErrorOutput(envelope) {
2624
2722
  function normalizeAgentLog(agentLog) {
2625
2723
  const prevSteps = Array.isArray(agentLog?.prevSteps) ? agentLog.prevSteps : [];
2626
2724
  const sanitizeMessage = (value) => {
2627
- const normalized = String(value || "").replace(/\s+/g, " ").trim();
2628
- if (!normalized)
2629
- return "";
2630
- if (normalized.length <= MAX_AGENT_CHATLOG_MESSAGE_CHARS)
2631
- return normalized;
2632
- return `${normalized.slice(0, MAX_AGENT_CHATLOG_MESSAGE_CHARS - 1)}\u2026`;
2725
+ return String(value || "").replace(/\s+/g, " ").trim();
2633
2726
  };
2634
- const chatLog = Array.isArray(agentLog?.chatLog) ? agentLog.chatLog.map((entry) => ({
2727
+ const normalizedChatLog = Array.isArray(agentLog?.chatLog) ? agentLog.chatLog.map((entry) => ({
2635
2728
  role: entry?.role === "user" ? "user" : "model",
2636
2729
  message: typeof entry?.message === "string" ? sanitizeMessage(entry.message) : ""
2637
- })).filter((entry) => !!entry.message).slice(-MAX_AGENT_CHATLOG_ENTRIES) : [];
2730
+ })).filter((entry) => !!entry.message) : [];
2731
+ const dedupedChatLog = [];
2732
+ for (const entry of normalizedChatLog) {
2733
+ const previous = dedupedChatLog[dedupedChatLog.length - 1];
2734
+ if (previous && previous.role === entry.role && previous.message === entry.message)
2735
+ continue;
2736
+ dedupedChatLog.push(entry);
2737
+ }
2738
+ const firstUser = dedupedChatLog.find((entry) => entry.role === "user");
2739
+ const tailBudget = firstUser ? Math.max(1, MAX_AGENT_CHATLOG_ENTRIES - 1) : MAX_AGENT_CHATLOG_ENTRIES;
2740
+ let chatLog = dedupedChatLog.slice(-tailBudget);
2741
+ if (firstUser) {
2742
+ const hasAnchor = chatLog.some((entry) => entry.role === "user" && entry.message === firstUser.message);
2743
+ if (!hasAnchor) {
2744
+ chatLog = [firstUser, ...chatLog];
2745
+ }
2746
+ }
2747
+ if (chatLog.length > MAX_AGENT_CHATLOG_ENTRIES) {
2748
+ chatLog = chatLog.slice(-MAX_AGENT_CHATLOG_ENTRIES);
2749
+ }
2638
2750
  return { prevSteps, chatLog };
2639
2751
  }
2640
2752
  async function executeToolFromPlan(context) {
@@ -2667,7 +2779,12 @@ async function executeToolFromPlan(context) {
2667
2779
  ctx: effectiveCtx,
2668
2780
  onPrevStepsUpdate
2669
2781
  });
2670
- return { ...actResult, output: actResult.data };
2782
+ const actOutput = actResult.data ?? (actResult.needsUserInput ? {
2783
+ status: "waiting_input",
2784
+ needsUserInput: true,
2785
+ questions: Array.isArray(actResult.questions) ? actResult.questions : []
2786
+ } : void 0);
2787
+ return { ...actResult, output: actOutput };
2671
2788
  }
2672
2789
  case PLANNER_FUNCTION_CALLS.EXTRACT: {
2673
2790
  const prompt = toolArgs?.user_input || toolArgs?.prompt || toolArgs?.task_instruction || userInput;
@@ -3452,18 +3569,34 @@ async function executeCustomToolGenerator({ userInput, plannerPrevSteps, files,
3452
3569
 
3453
3570
  // dist/agent/plannerAgent.js
3454
3571
  var MAX_PLANNER_DEPTH = 15;
3455
- var MAX_CHATLOG_ENTRIES = 24;
3456
- var MAX_CHATLOG_MESSAGE_CHARS = 1e3;
3572
+ var MAX_CHATLOG_ENTRIES = 12;
3457
3573
  function normalizeChatLog(entries) {
3458
3574
  if (!Array.isArray(entries) || !entries.length)
3459
3575
  return [];
3460
- return entries.map((entry) => ({
3576
+ const normalized = entries.map((entry) => ({
3461
3577
  role: entry?.role === "user" ? "user" : "model",
3462
3578
  message: String(entry?.message || "").replace(/\s+/g, " ").trim()
3463
- })).filter((entry) => !!entry.message).map((entry) => ({
3464
- role: entry.role,
3465
- message: entry.message.length > MAX_CHATLOG_MESSAGE_CHARS ? `${entry.message.slice(0, MAX_CHATLOG_MESSAGE_CHARS - 1)}\u2026` : entry.message
3466
- })).slice(-MAX_CHATLOG_ENTRIES);
3579
+ })).filter((entry) => !!entry.message);
3580
+ const deduped = [];
3581
+ for (const entry of normalized) {
3582
+ const previous = deduped[deduped.length - 1];
3583
+ if (previous && previous.role === entry.role && previous.message === entry.message)
3584
+ continue;
3585
+ deduped.push(entry);
3586
+ }
3587
+ const firstUser = deduped.find((entry) => entry.role === "user");
3588
+ const tailBudget = firstUser ? Math.max(1, MAX_CHATLOG_ENTRIES - 1) : MAX_CHATLOG_ENTRIES;
3589
+ let selected = deduped.slice(-tailBudget);
3590
+ if (firstUser) {
3591
+ const hasAnchor = selected.some((entry) => entry.role === "user" && entry.message === firstUser.message);
3592
+ if (!hasAnchor) {
3593
+ selected = [firstUser, ...selected];
3594
+ }
3595
+ }
3596
+ if (selected.length > MAX_CHATLOG_ENTRIES) {
3597
+ selected = selected.slice(-MAX_CHATLOG_ENTRIES);
3598
+ }
3599
+ return selected;
3467
3600
  }
3468
3601
  async function executePlanner(options) {
3469
3602
  const { userInput, tabs, previousMessages = [], trajectoryId: trajectoryId2, previousSteps = [], files, continuePlanning = false, recordingContext, driveAuthToken, agentLog, lastToolPreviousSteps, ctx, bridgeRpc: bridgeRpc2, functionDeclarations } = options;
@@ -4163,20 +4296,19 @@ var bridgeRpc = null;
4163
4296
  var toolRegistry = new ToolRegistry();
4164
4297
  var plannerHistory = [];
4165
4298
  var agentPrevSteps = [];
4299
+ var pendingAskUser;
4166
4300
  var trajectoryId = crypto.randomUUID();
4167
4301
  var tabularStore = null;
4168
4302
  var PLANNER_TOOL_NAME_SET = new Set(Object.values(PLANNER_FUNCTION_CALLS));
4169
4303
  var activeRun = null;
4170
- var cancelledRunId = null;
4304
+ var cancelledRunIds = /* @__PURE__ */ new Set();
4171
4305
  var activeAbortController = null;
4172
4306
  var lastStatusKey = "";
4173
4307
  var seenStatusKeys = /* @__PURE__ */ new Set();
4174
- var completedRunIds = /* @__PURE__ */ new Set();
4175
- var completedRunOutcomes = /* @__PURE__ */ new Map();
4308
+ var terminalRuns = /* @__PURE__ */ new Map();
4176
4309
  var RPC_TIMEOUT_MS = 3e4;
4177
4310
  var DETACHED_EXTERNAL_TAB_MAX_AGE_MS = 9e4;
4178
- var MAX_CHATLOG_ENTRIES2 = 24;
4179
- var MAX_CHATLOG_MESSAGE_CHARS2 = 1e3;
4311
+ var MAX_CHATLOG_ENTRIES2 = 12;
4180
4312
  function resolveAgentName(config2) {
4181
4313
  const raw = String(config2?.ui?.agent?.name || "").trim();
4182
4314
  if (!raw)
@@ -4392,11 +4524,6 @@ function postStatus(message, thought, stage) {
4392
4524
  self.postMessage({ type: "status", message, thought, stage: resolvedStage, compactThought: compact, runId: activeRun?.runId });
4393
4525
  postStateSnapshot();
4394
4526
  }
4395
- function truncateText(value, max = 8e3) {
4396
- if (value.length <= max)
4397
- return value;
4398
- return `${value.slice(0, max)}\u2026`;
4399
- }
4400
4527
  function cloneUnknown(value) {
4401
4528
  if (value == null)
4402
4529
  return value;
@@ -4439,6 +4566,90 @@ function sanitizeAgentPrevStepsForPersist(input) {
4439
4566
  }
4440
4567
  return out;
4441
4568
  }
4569
+ function normalizePlannerQuestion(input, index) {
4570
+ if (!input || typeof input !== "object")
4571
+ return void 0;
4572
+ const keyCandidate = String(input.key || input.id || "").trim();
4573
+ const queryCandidate = String(input.query || input.question || "").trim();
4574
+ const key = keyCandidate || `clarification_${index + 1}`;
4575
+ if (!queryCandidate)
4576
+ return void 0;
4577
+ return {
4578
+ key,
4579
+ query: queryCandidate,
4580
+ ...typeof input.id === "string" && input.id.trim() ? { id: input.id.trim() } : {},
4581
+ ...typeof input.question === "string" && input.question.trim() ? { question: input.question.trim() } : {},
4582
+ ...Array.isArray(input.choices) ? { choices: input.choices } : {}
4583
+ };
4584
+ }
4585
+ function normalizePlannerQuestions(input) {
4586
+ if (!Array.isArray(input))
4587
+ return [];
4588
+ const out = [];
4589
+ const seen = /* @__PURE__ */ new Set();
4590
+ for (let i = 0; i < input.length; i += 1) {
4591
+ const question = normalizePlannerQuestion(input[i], i);
4592
+ if (!question)
4593
+ continue;
4594
+ const key = `${question.key}::${question.query}`.toLowerCase();
4595
+ if (seen.has(key))
4596
+ continue;
4597
+ seen.add(key);
4598
+ out.push(question);
4599
+ }
4600
+ return out.slice(0, 6);
4601
+ }
4602
+ function questionToDisplayText(question) {
4603
+ return String(question.query || question.question || "").trim();
4604
+ }
4605
+ function normalizeAskUserAnswerMeta(raw, questions, fallbackText) {
4606
+ const validKeys = new Set(questions.map((question) => question.key));
4607
+ const answersByKey = {};
4608
+ if (raw?.answersByKey && typeof raw.answersByKey === "object") {
4609
+ for (const [key, value] of Object.entries(raw.answersByKey)) {
4610
+ if (!validKeys.has(key))
4611
+ continue;
4612
+ const normalizedValue = String(value || "").trim();
4613
+ if (!normalizedValue)
4614
+ continue;
4615
+ answersByKey[key] = normalizedValue;
4616
+ }
4617
+ }
4618
+ const fallback = String(fallbackText || "").trim();
4619
+ if (Object.keys(answersByKey).length === 0 && fallback) {
4620
+ if (questions.length === 1) {
4621
+ answersByKey[questions[0].key] = fallback;
4622
+ } else {
4623
+ const lines = fallback.split("\n").map((line) => line.trim()).filter(Boolean);
4624
+ for (const line of lines) {
4625
+ const splitIndex = line.indexOf(":");
4626
+ if (splitIndex <= 0)
4627
+ continue;
4628
+ const key = line.slice(0, splitIndex).trim();
4629
+ const value = line.slice(splitIndex + 1).trim();
4630
+ if (!key || !value || !validKeys.has(key))
4631
+ continue;
4632
+ answersByKey[key] = value;
4633
+ }
4634
+ }
4635
+ }
4636
+ const rawText = String(raw?.rawText || fallback || "").trim();
4637
+ if (!rawText && Object.keys(answersByKey).length === 0)
4638
+ return void 0;
4639
+ return { answersByKey, rawText };
4640
+ }
4641
+ function buildAskUserAnswerContext(questions, answers) {
4642
+ const lines = ["[ASK_USER_ANSWERS]"];
4643
+ for (const question of questions) {
4644
+ const answer = String(answers.answersByKey[question.key] || "").trim();
4645
+ lines.push(`${question.key}: ${answer || "(no answer provided)"}`);
4646
+ }
4647
+ if (answers.rawText) {
4648
+ lines.push("[RAW_USER_REPLY]");
4649
+ lines.push(answers.rawText);
4650
+ }
4651
+ return lines.join("\n");
4652
+ }
4442
4653
  function buildPersistedState() {
4443
4654
  const safePrevSteps = sanitizeAgentPrevStepsForPersist(Array.isArray(agentPrevSteps) ? agentPrevSteps : []);
4444
4655
  return {
@@ -4446,7 +4657,12 @@ function buildPersistedState() {
4446
4657
  history: sanitizeHistoryForPersist(history),
4447
4658
  plannerHistory: sanitizePlannerHistoryForPersist(Array.isArray(plannerHistory) ? plannerHistory : []),
4448
4659
  agentPrevSteps: safePrevSteps,
4449
- lastToolPreviousSteps: safePrevSteps
4660
+ lastToolPreviousSteps: safePrevSteps,
4661
+ pendingAskUser: pendingAskUser ? {
4662
+ questions: normalizePlannerQuestions(pendingAskUser.questions),
4663
+ source: pendingAskUser.source,
4664
+ askedAt: Number(pendingAskUser.askedAt) || Date.now()
4665
+ } : void 0
4450
4666
  };
4451
4667
  }
4452
4668
  function postStateSnapshot() {
@@ -4479,6 +4695,16 @@ function hydrateState(raw) {
4479
4695
  } else if (Array.isArray(snapshot.lastToolPreviousSteps)) {
4480
4696
  agentPrevSteps = sanitizeAgentPrevStepsForPersist(snapshot.lastToolPreviousSteps);
4481
4697
  }
4698
+ const hydratedQuestions = normalizePlannerQuestions(snapshot.pendingAskUser?.questions);
4699
+ if (hydratedQuestions.length > 0) {
4700
+ pendingAskUser = {
4701
+ questions: hydratedQuestions,
4702
+ source: snapshot.pendingAskUser?.source === "planner" ? "planner" : "act",
4703
+ askedAt: Number(snapshot.pendingAskUser?.askedAt) || Date.now()
4704
+ };
4705
+ } else {
4706
+ pendingAskUser = void 0;
4707
+ }
4482
4708
  if (typeof snapshot.trajectoryId === "string" && snapshot.trajectoryId.trim()) {
4483
4709
  trajectoryId = snapshot.trajectoryId.trim();
4484
4710
  }
@@ -4487,146 +4713,131 @@ function hydrateState(raw) {
4487
4713
  }
4488
4714
  postStateSnapshot();
4489
4715
  }
4490
- function shortText(value, max = 240) {
4491
- if (value == null)
4492
- return "";
4493
- const text = typeof value === "string" ? value : String(value);
4494
- const clean = text.trim();
4495
- if (!clean)
4496
- return "";
4497
- return clean.length <= max ? clean : `${clean.slice(0, max - 1)}\u2026`;
4498
- }
4499
- function formatInlineValue(value) {
4500
- if (value == null)
4501
- return "";
4502
- if (typeof value === "string")
4503
- return shortText(value, 200);
4504
- if (typeof value === "number" || typeof value === "boolean")
4505
- return String(value);
4506
- if (Array.isArray(value))
4507
- return `${value.length} item(s)`;
4508
- if (typeof value === "object") {
4509
- if (typeof value.message === "string")
4510
- return shortText(value.message, 200);
4511
- const keys = Object.keys(value);
4512
- if (!keys.length)
4513
- return "{}";
4514
- return `{ ${keys.slice(0, 4).join(", ")}${keys.length > 4 ? ", \u2026" : ""} }`;
4515
- }
4516
- return shortText(value, 200);
4517
- }
4518
- function formatObjectBlock(value) {
4519
- const preferredKeys = [
4520
- "message",
4521
- "summary",
4522
- "status",
4523
- "result",
4524
- "url",
4525
- "title",
4526
- "name",
4527
- "count",
4528
- "total",
4529
- "next_action"
4530
- ];
4531
- const entries = Object.entries(value).filter(([, v]) => v !== void 0 && v !== null && !(typeof v === "string" && !v.trim()));
4532
- if (!entries.length)
4533
- return "";
4534
- const sorted = entries.sort((a, b) => {
4535
- const ai = preferredKeys.indexOf(a[0]);
4536
- const bi = preferredKeys.indexOf(b[0]);
4537
- const ar = ai === -1 ? preferredKeys.length : ai;
4538
- const br = bi === -1 ? preferredKeys.length : bi;
4539
- return ar - br;
4540
- });
4541
- const lines = [];
4542
- const limit = 7;
4543
- const CONTENT_KEYS = /* @__PURE__ */ new Set(["response", "message", "summary", "result", "output", "text", "content", "description"]);
4544
- const URL_KEYS = /* @__PURE__ */ new Set(["url", "href", "link", "sheetUrl", "downloadUrl", "storageUrl"]);
4545
- for (const [key, raw] of sorted.slice(0, limit)) {
4546
- const isContent = CONTENT_KEYS.has(key);
4547
- const isUrl = URL_KEYS.has(key) || typeof raw === "string" && /^https?:\/\/.+/.test(raw.trim());
4548
- let rendered;
4549
- if (typeof raw === "string" && isUrl) {
4550
- const url = raw.trim();
4551
- rendered = `[${url}](${url})`;
4552
- } else if (typeof raw === "string" && isContent) {
4553
- rendered = shortText(raw, 2e3);
4554
- } else {
4555
- rendered = formatInlineValue(raw);
4556
- }
4557
- if (!rendered)
4558
- continue;
4559
- lines.push(`**${key}:** ${rendered}`);
4560
- }
4561
- if (sorted.length > limit) {
4562
- lines.push(`\u2026 ${sorted.length - limit} more field(s)`);
4563
- }
4564
- return lines.join("\n");
4565
- }
4566
- function formatArrayBlock(value) {
4567
- if (!value.length)
4568
- return "";
4569
- const lines = [];
4570
- const limit = 6;
4571
- for (const item of value.slice(0, limit)) {
4572
- if (typeof item === "string") {
4573
- const text = shortText(item, 260);
4574
- if (text)
4575
- lines.push(`- ${text}`);
4716
+ function sanitizeAssistantBlocks(input) {
4717
+ if (!Array.isArray(input))
4718
+ return void 0;
4719
+ const out = [];
4720
+ for (const raw of input) {
4721
+ if (!raw || typeof raw !== "object")
4576
4722
  continue;
4577
- }
4578
- if (item && typeof item === "object") {
4579
- const block = formatObjectBlock(item);
4580
- if (block) {
4581
- if (lines.length > 0)
4582
- lines.push("---");
4583
- lines.push(block);
4584
- }
4723
+ const type = raw.type;
4724
+ if (type === "text") {
4725
+ const text = String(raw.text || "").trim();
4726
+ if (!text)
4727
+ continue;
4728
+ out.push({ type: "text", text });
4585
4729
  continue;
4586
4730
  }
4587
- if (item != null) {
4588
- lines.push(`- ${shortText(item, 260)}`);
4731
+ if (type === "tool_output" || type === "json") {
4732
+ out.push({
4733
+ type,
4734
+ data: cloneUnknown(raw.data),
4735
+ label: typeof raw.label === "string" ? raw.label : void 0,
4736
+ toolName: typeof raw.toolName === "string" ? raw.toolName : void 0
4737
+ });
4589
4738
  }
4590
4739
  }
4591
- if (value.length > limit) {
4592
- lines.push(`- \u2026 ${value.length - limit} more item(s)`);
4593
- }
4594
- return lines.join("\n");
4740
+ return out.length ? out : void 0;
4595
4741
  }
4596
- function formatToolOutput(output) {
4742
+ function summarizeOutputText(output) {
4597
4743
  if (output == null)
4598
- return null;
4744
+ return void 0;
4599
4745
  if (typeof output === "string") {
4600
4746
  const clean = output.trim();
4601
- return clean ? truncateText(clean, 12e3) : null;
4747
+ return clean || void 0;
4602
4748
  }
4603
4749
  if (typeof output === "number" || typeof output === "boolean") {
4604
4750
  return String(output);
4605
4751
  }
4606
4752
  if (Array.isArray(output)) {
4607
- const block = formatArrayBlock(output);
4608
- if (block)
4609
- return truncateText(block, 12e3);
4610
- } else if (typeof output === "object") {
4611
- if (output.success === false && output.error) {
4612
- const message = shortText(output.error?.message || output.error, 300) || "Operation failed";
4613
- const nextAction = shortText(output.next_action || output.error?.next_action, 220);
4614
- const base = `[error] ${message}`;
4615
- return nextAction ? `${base}
4616
- [next] ${nextAction}` : base;
4617
- }
4618
- const block = formatObjectBlock(output);
4619
- if (block)
4620
- return truncateText(block, 12e3);
4753
+ const lines = [];
4754
+ for (const item of output.slice(0, 4)) {
4755
+ const candidate = summarizeOutputText(item);
4756
+ if (candidate)
4757
+ lines.push(candidate);
4758
+ if (lines.length >= 3)
4759
+ break;
4760
+ }
4761
+ if (lines.length)
4762
+ return lines.join("\n");
4763
+ return `Received ${output.length} item(s).`;
4621
4764
  }
4622
- try {
4623
- return truncateText(JSON.stringify(output, null, 2), 12e3);
4624
- } catch {
4625
- return truncateText(String(output), 12e3);
4765
+ if (typeof output === "object") {
4766
+ const preferredKeys = ["response", "message", "summary", "text", "content", "result", "description"];
4767
+ for (const key of preferredKeys) {
4768
+ const value = output[key];
4769
+ if (typeof value === "string" && value.trim()) {
4770
+ return value.trim();
4771
+ }
4772
+ }
4773
+ const keys = Object.keys(output);
4774
+ if (!keys.length)
4775
+ return void 0;
4776
+ return `Received ${keys.length} field(s).`;
4626
4777
  }
4778
+ return void 0;
4627
4779
  }
4628
- function postAssistantMessage(text) {
4629
- self.postMessage({ type: "assistant", text, runId: activeRun?.runId });
4780
+ function isSingleTextWrapperObject(value) {
4781
+ if (!value || typeof value !== "object" || Array.isArray(value))
4782
+ return false;
4783
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0 && v !== null);
4784
+ if (entries.length !== 1)
4785
+ return false;
4786
+ const [key, raw] = entries[0];
4787
+ if (typeof raw !== "string" || !raw.trim())
4788
+ return false;
4789
+ const textKeys = /* @__PURE__ */ new Set(["response", "message", "summary", "text", "content", "result", "description"]);
4790
+ return textKeys.has(key);
4791
+ }
4792
+ function shouldAttachStructuredBlock(output, summaryText) {
4793
+ if (output == null)
4794
+ return false;
4795
+ if (typeof output === "string") {
4796
+ const clean = output.trim();
4797
+ if (!clean)
4798
+ return false;
4799
+ return !summaryText || clean !== summaryText.trim();
4800
+ }
4801
+ if (Array.isArray(output)) {
4802
+ const meaningful = output.filter((item) => item !== void 0 && item !== null);
4803
+ if (meaningful.length === 1) {
4804
+ const first = meaningful[0];
4805
+ if (typeof first === "string" || typeof first === "number" || typeof first === "boolean") {
4806
+ return false;
4807
+ }
4808
+ if (isSingleTextWrapperObject(first)) {
4809
+ return false;
4810
+ }
4811
+ }
4812
+ return true;
4813
+ }
4814
+ if (isSingleTextWrapperObject(output)) {
4815
+ return false;
4816
+ }
4817
+ return true;
4818
+ }
4819
+ function buildAssistantPayloadFromToolOutput(output, options) {
4820
+ const summaryText = summarizeOutputText(output);
4821
+ const text = summaryText || options?.fallbackText || "Done.";
4822
+ const blocks = [];
4823
+ if (shouldAttachStructuredBlock(output, summaryText)) {
4824
+ blocks.push({
4825
+ type: "tool_output",
4826
+ label: options?.label,
4827
+ toolName: options?.toolName,
4828
+ data: cloneUnknown(output)
4829
+ });
4830
+ }
4831
+ return { text, blocks: blocks.length ? blocks : void 0 };
4832
+ }
4833
+ function postAssistantMessage(payload) {
4834
+ const text = typeof payload === "string" ? String(payload || "").trim() : String(payload?.text || "").trim();
4835
+ const blocks = typeof payload === "string" ? void 0 : sanitizeAssistantBlocks(payload.blocks);
4836
+ const firstTextBlock = blocks?.find((block) => block.type === "text");
4837
+ const firstStructuredBlock = blocks?.find((block) => block.type === "tool_output" || block.type === "json");
4838
+ const resolvedText = text || firstTextBlock?.text || summarizeOutputText(firstStructuredBlock?.data) || "Done.";
4839
+ self.postMessage({ type: "assistant", text: resolvedText, blocks, runId: activeRun?.runId });
4840
+ return resolvedText;
4630
4841
  }
4631
4842
  function dedupeFunctionDeclarations(declarations) {
4632
4843
  const seen = /* @__PURE__ */ new Set();
@@ -4664,11 +4875,6 @@ function toStructuredErrorPayload(err, fallbackMessage = "Operation failed") {
4664
4875
  };
4665
4876
  return payload;
4666
4877
  }
4667
- function formatStructuredErrorForAssistant(payload) {
4668
- const summary = `${payload.error.code}: ${payload.error.message}`;
4669
- return `${summary}
4670
- ${JSON.stringify(payload, null, 2)}`;
4671
- }
4672
4878
  function extractStructuredErrorFromToolResult(result) {
4673
4879
  if (!result || typeof result !== "object")
4674
4880
  return void 0;
@@ -4711,25 +4917,13 @@ function maybePostNavigationGuardrailFromToolResult(toolResult) {
4711
4917
  });
4712
4918
  }
4713
4919
  function buildChatLogFromHistory(input, currentUserInput) {
4714
- const sanitizeChatText = (raw, role) => {
4715
- let text = String(raw || "").replace(/\s+/g, " ").trim();
4716
- if (!text)
4717
- return "";
4718
- if (role === "assistant") {
4719
- if (/^\w[\w_]*:\s*\{/.test(text) || /^\[error\]/i.test(text) || /"success":\s*false/.test(text)) {
4720
- const firstSentence = text.split(/(?<=\.)\s+/)[0] || text;
4721
- text = firstSentence.trim();
4722
- }
4723
- }
4724
- if (text.length > MAX_CHATLOG_MESSAGE_CHARS2) {
4725
- text = `${text.slice(0, MAX_CHATLOG_MESSAGE_CHARS2 - 1)}\u2026`;
4726
- }
4727
- return text;
4920
+ const sanitizeChatText = (raw) => {
4921
+ return String(raw || "").replace(/\s+/g, " ").trim();
4728
4922
  };
4729
- const normalizedCurrentUserInput = currentUserInput ? sanitizeChatText(currentUserInput, "user") : "";
4923
+ const normalizedCurrentUserInput = currentUserInput ? sanitizeChatText(currentUserInput) : "";
4730
4924
  const entries = input.filter((message) => message.role === "user" || message.role === "assistant").map((message) => ({
4731
4925
  role: message.role,
4732
- content: sanitizeChatText(String(message.content || ""), message.role)
4926
+ content: sanitizeChatText(String(message.content || ""))
4733
4927
  })).filter((message) => !!message.content);
4734
4928
  if (normalizedCurrentUserInput) {
4735
4929
  for (let i = entries.length - 1; i >= 0; i -= 1) {
@@ -4746,11 +4940,25 @@ function buildChatLogFromHistory(input, currentUserInput) {
4746
4940
  continue;
4747
4941
  deduped.push(entry);
4748
4942
  }
4749
- const tail = deduped.slice(-MAX_CHATLOG_ENTRIES2);
4750
- while (tail.length > 1 && tail[0]?.role !== "user") {
4751
- tail.shift();
4943
+ const firstUser = deduped.find((message) => message.role === "user");
4944
+ const tailBudget = firstUser ? Math.max(1, MAX_CHATLOG_ENTRIES2 - 1) : MAX_CHATLOG_ENTRIES2;
4945
+ let selected = deduped.slice(-tailBudget);
4946
+ if (firstUser) {
4947
+ const hasAnchor = selected.some((message) => message.role === "user" && message.content === firstUser.content);
4948
+ if (!hasAnchor) {
4949
+ selected = [firstUser, ...selected];
4950
+ }
4951
+ }
4952
+ if (selected.length > MAX_CHATLOG_ENTRIES2) {
4953
+ selected = selected.slice(-MAX_CHATLOG_ENTRIES2);
4752
4954
  }
4753
- return tail.map((message) => ({
4955
+ if (selected.length > 1 && selected[0]?.role !== "user") {
4956
+ const firstUserIndex = selected.findIndex((message) => message.role === "user");
4957
+ if (firstUserIndex > 0) {
4958
+ selected = selected.slice(firstUserIndex);
4959
+ }
4960
+ }
4961
+ return selected.map((message) => ({
4754
4962
  role: message.role === "user" ? "user" : "model",
4755
4963
  message: message.content
4756
4964
  }));
@@ -4799,33 +5007,65 @@ function extractArtifactLinks(toolResult) {
4799
5007
  }
4800
5008
  return links;
4801
5009
  }
4802
- function formatPlannerToolResults(toolResults) {
4803
- if (!Array.isArray(toolResults) || toolResults.length === 0)
4804
- return null;
4805
- const sections = [];
5010
+ function buildPlannerToolResultBlocks(toolResults) {
5011
+ if (!Array.isArray(toolResults) || !toolResults.length)
5012
+ return void 0;
5013
+ const blocks = [];
4806
5014
  for (let i = 0; i < toolResults.length; i += 1) {
4807
5015
  const result = toolResults[i];
4808
5016
  if (!result)
4809
5017
  continue;
5018
+ const stepLabel = `Step ${i + 1}`;
4810
5019
  const output = result.output ?? result.generatedContentRef ?? result.schemaHeaderSheetInfo;
4811
- const outputText = formatToolOutput(output);
5020
+ const summary = summarizeOutputText(output);
5021
+ if (output !== void 0 && shouldAttachStructuredBlock(output, summary)) {
5022
+ blocks.push({
5023
+ type: "tool_output",
5024
+ label: result.toolName ? `${stepLabel}: ${result.toolName}` : stepLabel,
5025
+ toolName: typeof result.toolName === "string" ? result.toolName : void 0,
5026
+ data: cloneUnknown(output)
5027
+ });
5028
+ }
5029
+ if (result.error || result.errorDetails) {
5030
+ blocks.push({
5031
+ type: "json",
5032
+ label: `${stepLabel} error`,
5033
+ data: cloneUnknown({
5034
+ error: result.error,
5035
+ errorDetails: result.errorDetails
5036
+ })
5037
+ });
5038
+ }
4812
5039
  const links = extractArtifactLinks(result);
4813
- const lines = [];
4814
- if (outputText)
4815
- lines.push(outputText);
4816
- if (result.error)
4817
- lines.push(`Error: ${String(result.error)}`);
4818
- if (links.length)
4819
- lines.push(`Artifacts:
4820
- ${links.map((link) => `- ${link}`).join("\n")}`);
4821
- if (!lines.length)
5040
+ if (links.length) {
5041
+ blocks.push({
5042
+ type: "text",
5043
+ text: `${stepLabel} artifacts:
5044
+ ${links.map((link) => `- ${link}`).join("\n")}`
5045
+ });
5046
+ }
5047
+ }
5048
+ return blocks.length ? blocks : void 0;
5049
+ }
5050
+ function summarizePlannerToolResults(toolResults) {
5051
+ if (!Array.isArray(toolResults) || !toolResults.length)
5052
+ return void 0;
5053
+ const lines = [];
5054
+ for (let i = 0; i < toolResults.length; i += 1) {
5055
+ const result = toolResults[i];
5056
+ if (!result)
4822
5057
  continue;
4823
- sections.push(`Step ${i + 1}
4824
- ${lines.join("\n")}`);
5058
+ const output = result.output ?? result.generatedContentRef ?? result.schemaHeaderSheetInfo;
5059
+ const summary = summarizeOutputText(output);
5060
+ if (summary) {
5061
+ lines.push(summary);
5062
+ if (lines.length >= 3)
5063
+ break;
5064
+ }
4825
5065
  }
4826
- if (!sections.length)
4827
- return null;
4828
- return truncateText(sections.join("\n\n"), 12e3);
5066
+ if (!lines.length)
5067
+ return void 0;
5068
+ return lines.join("\n\n");
4829
5069
  }
4830
5070
  function extractLatestPrevStepsFromPlanner(toolResults) {
4831
5071
  if (!Array.isArray(toolResults) || !toolResults.length)
@@ -4871,16 +5111,48 @@ function detectOpenedTabFromToolResult(result) {
4871
5111
  }
4872
5112
  return void 0;
4873
5113
  }
5114
+ function extractQuestionsFromResult(result) {
5115
+ if (!result || typeof result !== "object")
5116
+ return void 0;
5117
+ const topLevel = normalizePlannerQuestions(result.questions);
5118
+ if (topLevel.length)
5119
+ return topLevel;
5120
+ const output = result.output;
5121
+ const outputQuestions = normalizePlannerQuestions(output?.questions);
5122
+ if (outputQuestions.length)
5123
+ return outputQuestions;
5124
+ return void 0;
5125
+ }
5126
+ function extractPlannerQuestionsFromToolResults(toolResults) {
5127
+ if (!Array.isArray(toolResults) || toolResults.length === 0)
5128
+ return [];
5129
+ const combined = [];
5130
+ const seen = /* @__PURE__ */ new Set();
5131
+ for (const toolResult of toolResults) {
5132
+ const questions = extractQuestionsFromResult(toolResult);
5133
+ if (!questions || !questions.length)
5134
+ continue;
5135
+ for (const question of questions) {
5136
+ const key = `${question.key}::${question.query}`.toLowerCase();
5137
+ if (seen.has(key))
5138
+ continue;
5139
+ seen.add(key);
5140
+ combined.push(question);
5141
+ }
5142
+ }
5143
+ return combined.slice(0, 6);
5144
+ }
4874
5145
  function deriveDirectToolRunOutcome(result) {
4875
5146
  if (!result || typeof result !== "object") {
4876
5147
  return { taskComplete: false };
4877
5148
  }
5149
+ const questions = extractQuestionsFromResult(result);
4878
5150
  const topLevelStatus = String(result.status || "").trim().toLowerCase();
4879
5151
  if (topLevelStatus === "failure" || topLevelStatus === "failed" || topLevelStatus === "error") {
4880
5152
  return { taskComplete: false };
4881
5153
  }
4882
5154
  if (topLevelStatus === "waiting_input" || topLevelStatus === "needs_input" || topLevelStatus === "pending_user_input") {
4883
- return { taskComplete: false, needsUserInput: true };
5155
+ return { taskComplete: false, needsUserInput: true, questions };
4884
5156
  }
4885
5157
  if (result.error) {
4886
5158
  return { taskComplete: false };
@@ -4891,10 +5163,10 @@ function deriveDirectToolRunOutcome(result) {
4891
5163
  return { taskComplete: true };
4892
5164
  }
4893
5165
  if (output.needsUserInput === true || output.waitingForUserInput === true) {
4894
- return { taskComplete: false, needsUserInput: true };
5166
+ return { taskComplete: false, needsUserInput: true, questions };
4895
5167
  }
4896
5168
  if (Array.isArray(output.questions) && output.questions.length > 0) {
4897
- return { taskComplete: false, needsUserInput: true };
5169
+ return { taskComplete: false, needsUserInput: true, questions };
4898
5170
  }
4899
5171
  if (output.error) {
4900
5172
  return { taskComplete: false };
@@ -4927,7 +5199,7 @@ function deriveDirectToolRunOutcome(result) {
4927
5199
  if (output != null) {
4928
5200
  return { taskComplete: true };
4929
5201
  }
4930
- return { taskComplete: false };
5202
+ return { taskComplete: true };
4931
5203
  }
4932
5204
  function normalizeRunOutcome(outcome) {
4933
5205
  if (!outcome || typeof outcome !== "object") {
@@ -4935,16 +5207,37 @@ function normalizeRunOutcome(outcome) {
4935
5207
  }
4936
5208
  const needsUserInput = outcome.needsUserInput === true;
4937
5209
  const taskComplete = outcome.taskComplete === true && !needsUserInput;
5210
+ const questions = normalizePlannerQuestions(outcome.questions);
4938
5211
  return {
4939
5212
  route: outcome.route,
4940
5213
  taskComplete,
4941
- needsUserInput
5214
+ needsUserInput,
5215
+ questions: questions.length ? questions : void 0
4942
5216
  };
4943
5217
  }
5218
+ function rememberTerminalRun(runId, result) {
5219
+ terminalRuns.set(runId, result);
5220
+ while (terminalRuns.size > 80) {
5221
+ const oldest = terminalRuns.keys().next().value;
5222
+ if (!oldest)
5223
+ break;
5224
+ terminalRuns.delete(oldest);
5225
+ }
5226
+ }
5227
+ function rememberCancelledRun(runId) {
5228
+ cancelledRunIds.add(runId);
5229
+ while (cancelledRunIds.size > 80) {
5230
+ const oldest = cancelledRunIds.values().next().value;
5231
+ if (!oldest)
5232
+ break;
5233
+ cancelledRunIds.delete(oldest);
5234
+ }
5235
+ }
4944
5236
  function clearTaskScopedContextAfterCompletion() {
4945
5237
  history.length = 0;
4946
5238
  plannerHistory = [];
4947
5239
  agentPrevSteps = [];
5240
+ pendingAskUser = void 0;
4948
5241
  }
4949
5242
  async function maybeWaitForNewTab(result) {
4950
5243
  const logicalTabId = detectOpenedTabFromToolResult(result);
@@ -4964,6 +5257,34 @@ async function handleUserMessage(text, options) {
4964
5257
  history.push({ role: "user", content: text });
4965
5258
  postStateSnapshot();
4966
5259
  }
5260
+ let effectiveUserInput = text;
5261
+ if (pendingAskUser?.questions?.length) {
5262
+ const normalizedAnswers = normalizeAskUserAnswerMeta(options?.askUserAnswers, pendingAskUser.questions, text);
5263
+ if (normalizedAnswers) {
5264
+ const answerContext = buildAskUserAnswerContext(pendingAskUser.questions, normalizedAnswers);
5265
+ effectiveUserInput = answerContext;
5266
+ plannerHistory = sanitizePlannerHistoryForPersist([
5267
+ ...plannerHistory,
5268
+ {
5269
+ thought: "User provided clarification answers.",
5270
+ questionsAsked: pendingAskUser.questions,
5271
+ userAnswers: normalizedAnswers.answersByKey
5272
+ }
5273
+ ]);
5274
+ applyAgentPrevSteps([
5275
+ ...agentPrevSteps,
5276
+ {
5277
+ thought: "User provided clarification answers.",
5278
+ data: JSON.stringify({
5279
+ ask_user_answers: normalizedAnswers.answersByKey,
5280
+ raw_user_reply: normalizedAnswers.rawText
5281
+ })
5282
+ }
5283
+ ], { snapshot: false });
5284
+ pendingAskUser = void 0;
5285
+ postStateSnapshot();
5286
+ }
5287
+ }
4967
5288
  const tabs = await getKnownTabs();
4968
5289
  const fallbackTabs = tabs.length > 0 ? tabs : [
4969
5290
  {
@@ -5010,7 +5331,7 @@ async function handleUserMessage(text, options) {
5010
5331
  }
5011
5332
  }, bridgeRpc, tabularStore);
5012
5333
  const currentRunId = activeRun?.runId;
5013
- ctx.isCancelled = () => cancelledRunId === currentRunId;
5334
+ ctx.isCancelled = () => !!(currentRunId && cancelledRunIds.has(currentRunId));
5014
5335
  const functionDeclarations = dedupeFunctionDeclarations(removePlannerNameCollisions(toolRegistry.getFunctionDeclarations()));
5015
5336
  const toolFunctions = toolRegistry.getToolFunctions();
5016
5337
  const chatLog = buildChatLogFromHistory(history, text);
@@ -5021,7 +5342,7 @@ async function handleUserMessage(text, options) {
5021
5342
  plannerHistory = sanitizePlannerHistoryForPersist(Array.isArray(steps) ? steps : []);
5022
5343
  postStateSnapshot();
5023
5344
  };
5024
- const result = await handleSendMessageWithFunctions(text, {
5345
+ const result = await handleSendMessageWithFunctions(effectiveUserInput, {
5025
5346
  tabs: tabsForRun,
5026
5347
  previousMessages: history,
5027
5348
  trajectoryId,
@@ -5035,7 +5356,7 @@ async function handleUserMessage(text, options) {
5035
5356
  chatLog
5036
5357
  },
5037
5358
  lastToolPreviousSteps: agentPrevSteps,
5038
- taskRouting: config.taskRouting,
5359
+ taskRouting: options?.routing ? { ...config.taskRouting, mode: options.routing } : config.taskRouting,
5039
5360
  ctx,
5040
5361
  bridgeRpc,
5041
5362
  functionDeclarations,
@@ -5047,8 +5368,16 @@ async function handleUserMessage(text, options) {
5047
5368
  if (errorPayload.error.requires_api_key) {
5048
5369
  postAuthRequired(errorPayload.error);
5049
5370
  }
5050
- const errorMsg = formatStructuredErrorForAssistant(errorPayload);
5051
- postAssistantMessage(errorMsg);
5371
+ const errorMsg = postAssistantMessage({
5372
+ text: `${errorPayload.error.code}: ${errorPayload.error.message}`,
5373
+ blocks: [
5374
+ {
5375
+ type: "json",
5376
+ label: "Error details",
5377
+ data: cloneUnknown(errorPayload)
5378
+ }
5379
+ ]
5380
+ });
5052
5381
  history.push({ role: "assistant", content: errorMsg });
5053
5382
  postStatus("Execution failed", errorPayload.error.message, "complete");
5054
5383
  postStateSnapshot();
@@ -5058,12 +5387,31 @@ async function handleUserMessage(text, options) {
5058
5387
  for (const fn of result.executedFunctions) {
5059
5388
  applyAgentPrevSteps(fn.prevSteps, { snapshot: false });
5060
5389
  }
5061
- const lines = result.executedFunctions.map((fn) => {
5062
- const out = formatToolOutput(fn.result);
5063
- return out ? `@${fn.name}: ${out}` : `@${fn.name}: ${fn.error || "ok"}`;
5390
+ const blocks = [];
5391
+ const lines = [];
5392
+ for (const fn of result.executedFunctions) {
5393
+ const summary = summarizeOutputText(fn.result);
5394
+ if (fn.result !== void 0 && shouldAttachStructuredBlock(fn.result, summary)) {
5395
+ blocks.push({
5396
+ type: "tool_output",
5397
+ toolName: fn.name,
5398
+ label: `${fn.name} output`,
5399
+ data: cloneUnknown(fn.result)
5400
+ });
5401
+ }
5402
+ if (fn.error) {
5403
+ blocks.push({
5404
+ type: "json",
5405
+ label: `${fn.name} error`,
5406
+ data: cloneUnknown({ error: fn.error })
5407
+ });
5408
+ }
5409
+ lines.push(summary ? `@${fn.name}: ${summary}` : `@${fn.name}: ${fn.error || "ok"}`);
5410
+ }
5411
+ const msg = postAssistantMessage({
5412
+ text: lines.join("\n") || "Done.",
5413
+ blocks
5064
5414
  });
5065
- const msg = lines.join("\n");
5066
- postAssistantMessage(msg);
5067
5415
  history.push({ role: "assistant", content: msg });
5068
5416
  postStatus("Execution completed", "Function calls finished", "complete");
5069
5417
  postStateSnapshot();
@@ -5074,55 +5422,121 @@ async function handleUserMessage(text, options) {
5074
5422
  postStatus("Verifying result", void 0, "verify");
5075
5423
  maybePostNavigationGuardrailFromToolResult(result.directToolResult);
5076
5424
  applyAgentPrevSteps(result.directToolResult.prevSteps, { snapshot: false });
5425
+ const outcome = deriveDirectToolRunOutcome(result.directToolResult);
5426
+ const questions = normalizePlannerQuestions(outcome.questions);
5427
+ if (outcome.needsUserInput && questions.length > 0) {
5428
+ pendingAskUser = {
5429
+ questions,
5430
+ source: "act",
5431
+ askedAt: Date.now()
5432
+ };
5433
+ plannerHistory = sanitizePlannerHistoryForPersist([
5434
+ ...plannerHistory,
5435
+ {
5436
+ thought: "Need user clarification before continuing act workflow.",
5437
+ questionsAsked: questions
5438
+ }
5439
+ ]);
5440
+ const qText = questions.map((question) => `- ${question.key}: ${questionToDisplayText(question)}`).join("\n");
5441
+ const msg2 = postAssistantMessage(`I need a bit more info before continuing:
5442
+ ${qText}`);
5443
+ history.push({ role: "assistant", content: msg2 });
5444
+ postStatus("Need more input to continue", void 0, "verify");
5445
+ postStateSnapshot();
5446
+ return {
5447
+ route: result.route,
5448
+ taskComplete: false,
5449
+ needsUserInput: true,
5450
+ questions
5451
+ };
5452
+ }
5453
+ pendingAskUser = void 0;
5077
5454
  const output = result.directToolResult.output ?? result.directToolResult.generatedContentRef ?? result.directToolResult.schemaHeaderSheetInfo;
5078
- const formattedOutput = output ? formatToolOutput(output) : null;
5079
5455
  const structuredError = extractStructuredErrorFromToolResult(result.directToolResult);
5080
5456
  if (structuredError?.error.requires_api_key) {
5081
5457
  postAuthRequired(structuredError.error);
5082
5458
  }
5083
- const msg = structuredError ? formatStructuredErrorForAssistant(structuredError) : formattedOutput ?? "Done.";
5084
- postAssistantMessage(msg);
5459
+ const msg = structuredError ? postAssistantMessage({
5460
+ text: `${structuredError.error.code}: ${structuredError.error.message}`,
5461
+ blocks: [
5462
+ {
5463
+ type: "json",
5464
+ label: "Error details",
5465
+ data: cloneUnknown(structuredError)
5466
+ }
5467
+ ]
5468
+ }) : postAssistantMessage(buildAssistantPayloadFromToolOutput(output, {
5469
+ label: "Tool output",
5470
+ fallbackText: "Done."
5471
+ }));
5085
5472
  history.push({ role: "assistant", content: msg });
5086
5473
  postStatus("Execution completed", structuredError?.error.message, "complete");
5087
5474
  postStateSnapshot();
5088
- const outcome = deriveDirectToolRunOutcome(result.directToolResult);
5089
5475
  return {
5090
5476
  route: result.route,
5091
5477
  taskComplete: outcome.taskComplete,
5092
- needsUserInput: outcome.needsUserInput
5478
+ needsUserInput: outcome.needsUserInput,
5479
+ questions: normalizePlannerQuestions(outcome.questions)
5093
5480
  };
5094
5481
  }
5095
5482
  if (result.plannerResponse) {
5096
5483
  postStatus("Verifying planner output", void 0, "verify");
5097
5484
  const response = result.plannerResponse.response;
5485
+ const toolResults = result.plannerResponse.toolResults || [];
5098
5486
  if (result.plannerResponse.previousSteps) {
5099
5487
  plannerHistory = result.plannerResponse.previousSteps;
5100
5488
  postStateSnapshot();
5101
5489
  }
5102
- const latestToolPrevSteps = extractLatestPrevStepsFromPlanner(result.plannerResponse.toolResults);
5490
+ const latestToolPrevSteps = extractLatestPrevStepsFromPlanner(toolResults);
5103
5491
  applyAgentPrevSteps(latestToolPrevSteps, { snapshot: false });
5104
- if (response.questions?.length) {
5105
- const qText = response.questions.map((q) => `- ${q.question}`).join("\n");
5492
+ const responseQuestions = normalizePlannerQuestions(response.questions);
5493
+ const fallbackQuestions = responseQuestions.length ? [] : extractPlannerQuestionsFromToolResults(toolResults);
5494
+ const plannerQuestions = responseQuestions.length ? responseQuestions : fallbackQuestions;
5495
+ if (plannerQuestions.length) {
5496
+ const questions = plannerQuestions;
5497
+ pendingAskUser = questions.length ? {
5498
+ questions,
5499
+ source: "planner",
5500
+ askedAt: Date.now()
5501
+ } : void 0;
5502
+ const qText = questions.map((question) => `- ${question.key}: ${questionToDisplayText(question)}`).join("\n");
5106
5503
  const msg2 = `I need a bit more info:
5107
5504
  ${qText}`;
5108
5505
  postAssistantMessage(msg2);
5109
5506
  history.push({ role: "assistant", content: msg2 });
5110
5507
  postStatus("Planner needs user input", void 0, "verify");
5111
5508
  postStateSnapshot();
5112
- return { route: result.route, taskComplete: false, needsUserInput: true };
5509
+ return {
5510
+ route: result.route,
5511
+ taskComplete: false,
5512
+ needsUserInput: true,
5513
+ questions
5514
+ };
5113
5515
  }
5114
- const toolResults = result.plannerResponse.toolResults || [];
5516
+ pendingAskUser = void 0;
5115
5517
  for (const toolResult of toolResults) {
5116
5518
  await maybeWaitForNewTab(toolResult);
5117
5519
  maybePostNavigationGuardrailFromToolResult(toolResult);
5118
5520
  }
5119
- const formattedOutput = formatPlannerToolResults(toolResults);
5521
+ const toolBlocks = buildPlannerToolResultBlocks(toolResults);
5120
5522
  const responseError = response.error || response.errorDetails ? toStructuredErrorPayload(response.errorDetails || { message: response.error }, "Planner failed") : void 0;
5121
5523
  if (responseError?.error.requires_api_key) {
5122
5524
  postAuthRequired(responseError.error);
5123
5525
  }
5124
- const msg = formattedOutput ?? (responseError ? formatStructuredErrorForAssistant(responseError) : response.overallThought ?? "Done.");
5125
- postAssistantMessage(msg);
5526
+ const msg = responseError ? postAssistantMessage({
5527
+ text: `${responseError.error.code}: ${responseError.error.message}`,
5528
+ blocks: [
5529
+ {
5530
+ type: "json",
5531
+ label: "Planner error",
5532
+ data: cloneUnknown(responseError)
5533
+ },
5534
+ ...toolBlocks || []
5535
+ ]
5536
+ }) : postAssistantMessage({
5537
+ text: String(response.overallThought || summarizePlannerToolResults(toolResults) || "Done."),
5538
+ blocks: toolBlocks
5539
+ });
5126
5540
  history.push({ role: "assistant", content: msg });
5127
5541
  postStatus("Planner execution completed", response.overallThought, "complete");
5128
5542
  postStateSnapshot();
@@ -5132,24 +5546,37 @@ ${qText}`;
5132
5546
  needsUserInput: false
5133
5547
  };
5134
5548
  }
5135
- postAssistantMessage("Done.");
5136
- history.push({ role: "assistant", content: "Done." });
5549
+ const doneMsg = postAssistantMessage("Done.");
5550
+ history.push({ role: "assistant", content: doneMsg });
5137
5551
  postStatus("Completed", void 0, "complete");
5138
5552
  postStateSnapshot();
5139
5553
  return { route: result.route, taskComplete: true };
5140
5554
  }
5141
5555
  async function runUserMessage(text, meta) {
5142
5556
  const runId = meta?.runId || crypto.randomUUID();
5143
- if (completedRunIds.has(runId)) {
5144
- const cachedOutcome = normalizeRunOutcome(completedRunOutcomes.get(runId));
5145
- self.postMessage({
5146
- type: "run_completed",
5147
- runId,
5148
- ok: true,
5149
- route: cachedOutcome.route,
5150
- taskComplete: cachedOutcome.taskComplete,
5151
- needsUserInput: cachedOutcome.needsUserInput
5152
- });
5557
+ const terminal = terminalRuns.get(runId);
5558
+ if (terminal) {
5559
+ if (terminal.ok) {
5560
+ const cachedOutcome = normalizeRunOutcome(terminal.outcome);
5561
+ self.postMessage({
5562
+ type: "run_completed",
5563
+ runId,
5564
+ ok: true,
5565
+ route: cachedOutcome.route,
5566
+ taskComplete: cachedOutcome.taskComplete,
5567
+ needsUserInput: cachedOutcome.needsUserInput,
5568
+ questions: cachedOutcome.questions
5569
+ });
5570
+ } else {
5571
+ self.postMessage({
5572
+ type: "run_completed",
5573
+ runId,
5574
+ ok: false,
5575
+ error: terminal.error,
5576
+ taskComplete: false,
5577
+ needsUserInput: false
5578
+ });
5579
+ }
5153
5580
  return;
5154
5581
  }
5155
5582
  if (activeRun && activeRun.runId === runId) {
@@ -5158,20 +5585,26 @@ async function runUserMessage(text, meta) {
5158
5585
  const resume = !!meta?.resume;
5159
5586
  lastStatusKey = "";
5160
5587
  seenStatusKeys = /* @__PURE__ */ new Set();
5588
+ cancelledRunIds.delete(runId);
5161
5589
  activeAbortController = new AbortController();
5162
5590
  activeRun = { runId, text, startedAt: Date.now(), resume };
5163
5591
  self.postMessage({ type: "run_started", runId, text, resume });
5164
5592
  postStateSnapshot();
5165
5593
  try {
5166
- const outcome = normalizeRunOutcome(await handleUserMessage(text, { resume }));
5167
- completedRunOutcomes.set(runId, outcome);
5594
+ const outcome = normalizeRunOutcome(await handleUserMessage(text, {
5595
+ resume,
5596
+ routing: meta?.routing,
5597
+ askUserAnswers: meta?.askUserAnswers
5598
+ }));
5599
+ rememberTerminalRun(runId, { ok: true, outcome });
5168
5600
  self.postMessage({
5169
5601
  type: "run_completed",
5170
5602
  runId,
5171
5603
  ok: true,
5172
5604
  route: outcome.route,
5173
5605
  taskComplete: outcome.taskComplete,
5174
- needsUserInput: outcome.needsUserInput
5606
+ needsUserInput: outcome.needsUserInput,
5607
+ questions: outcome.questions
5175
5608
  });
5176
5609
  if (outcome.taskComplete && !outcome.needsUserInput) {
5177
5610
  clearTaskScopedContextAfterCompletion();
@@ -5179,6 +5612,12 @@ async function runUserMessage(text, meta) {
5179
5612
  }
5180
5613
  } catch (error) {
5181
5614
  if (error?.name === "AbortError") {
5615
+ rememberTerminalRun(runId, {
5616
+ ok: false,
5617
+ error: "Run cancelled",
5618
+ taskComplete: false,
5619
+ needsUserInput: false
5620
+ });
5182
5621
  self.postMessage({
5183
5622
  type: "run_completed",
5184
5623
  runId,
@@ -5188,6 +5627,12 @@ async function runUserMessage(text, meta) {
5188
5627
  needsUserInput: false
5189
5628
  });
5190
5629
  } else {
5630
+ rememberTerminalRun(runId, {
5631
+ ok: false,
5632
+ error: error?.message || String(error),
5633
+ taskComplete: false,
5634
+ needsUserInput: false
5635
+ });
5191
5636
  self.postMessage({
5192
5637
  type: "run_completed",
5193
5638
  runId,
@@ -5201,14 +5646,6 @@ async function runUserMessage(text, meta) {
5201
5646
  } finally {
5202
5647
  activeAbortController = null;
5203
5648
  activeRun = null;
5204
- completedRunIds.add(runId);
5205
- if (completedRunIds.size > 50) {
5206
- const oldest = completedRunIds.values().next().value;
5207
- if (oldest) {
5208
- completedRunIds.delete(oldest);
5209
- completedRunOutcomes.delete(oldest);
5210
- }
5211
- }
5212
5649
  postStateSnapshot();
5213
5650
  }
5214
5651
  }
@@ -5256,6 +5693,9 @@ self.onmessage = async (ev) => {
5256
5693
  trajectoryId = partial.sessionId.trim();
5257
5694
  plannerHistory = [];
5258
5695
  agentPrevSteps = [];
5696
+ pendingAskUser = void 0;
5697
+ terminalRuns.clear();
5698
+ cancelledRunIds.clear();
5259
5699
  tabularStore = new TabularStore(`rover-${trajectoryId}`);
5260
5700
  }
5261
5701
  const tools = partial.tools;
@@ -5279,29 +5719,45 @@ self.onmessage = async (ev) => {
5279
5719
  if (!config)
5280
5720
  throw new Error("Worker not initialized");
5281
5721
  const nextTaskId = typeof data.taskId === "string" && data.taskId.trim() ? data.taskId.trim() : crypto.randomUUID();
5722
+ activeAbortController?.abort();
5282
5723
  history.length = 0;
5283
5724
  plannerHistory = [];
5284
5725
  agentPrevSteps = [];
5726
+ pendingAskUser = void 0;
5727
+ terminalRuns.clear();
5728
+ cancelledRunIds.clear();
5285
5729
  trajectoryId = nextTaskId;
5286
5730
  tabularStore = new TabularStore(`rover-${trajectoryId}`);
5287
5731
  activeRun = null;
5732
+ activeAbortController = null;
5288
5733
  postStateSnapshot();
5289
5734
  self.postMessage({ type: "task_started", taskId: nextTaskId });
5290
5735
  return;
5291
5736
  }
5292
5737
  if (data.type === "cancel_run") {
5293
5738
  if (typeof data.runId === "string" && data.runId) {
5294
- cancelledRunId = data.runId;
5739
+ rememberCancelledRun(data.runId);
5295
5740
  activeAbortController?.abort();
5296
5741
  }
5742
+ pendingAskUser = void 0;
5743
+ postStateSnapshot();
5297
5744
  return;
5298
5745
  }
5299
5746
  if (data.type === "run") {
5300
- await runUserMessage(String(data.text || ""), { runId: data.runId, resume: !!data.resume });
5747
+ await runUserMessage(String(data.text || ""), {
5748
+ runId: data.runId,
5749
+ resume: !!data.resume,
5750
+ routing: data.routing,
5751
+ askUserAnswers: data.askUserAnswers
5752
+ });
5301
5753
  return;
5302
5754
  }
5303
5755
  if (data.type === "user") {
5304
- await runUserMessage(String(data.text || ""), { runId: data.runId, resume: !!data.resume });
5756
+ await runUserMessage(String(data.text || ""), {
5757
+ runId: data.runId,
5758
+ resume: !!data.resume,
5759
+ askUserAnswers: data.askUserAnswers
5760
+ });
5305
5761
  return;
5306
5762
  }
5307
5763
  } catch (err) {