@rtrvr-ai/rover 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/embed.js +39 -17
- package/dist/index.d.ts +6 -0
- package/dist/rover.js +648 -69
- package/dist/worker/rover-worker.js +103 -22
- package/package.json +1 -1
|
@@ -1458,6 +1458,18 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
|
|
|
1458
1458
|
if (Array.isArray(functionCalls) && functionCalls.length > 0) {
|
|
1459
1459
|
const askUserCall = functionCalls.find((call) => String(call?.name || "").trim().toLowerCase() === "ask_user");
|
|
1460
1460
|
if (askUserCall) {
|
|
1461
|
+
const deferredSiblingCalls = functionCalls.filter((call) => call && call !== askUserCall && typeof call.name === "string" && call.name.trim()).map((call) => ({
|
|
1462
|
+
name: String(call.name || "unknown"),
|
|
1463
|
+
args: call.args || {},
|
|
1464
|
+
response: {
|
|
1465
|
+
status: "Failure",
|
|
1466
|
+
error: "Skipped because ask_user was called. Wait for user answers before executing other tools.",
|
|
1467
|
+
allowFallback: true,
|
|
1468
|
+
output: {
|
|
1469
|
+
status: "deferred_after_ask_user"
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}));
|
|
1461
1473
|
const questions = normalizeAskUserQuestions(askUserCall?.args?.questions_to_ask);
|
|
1462
1474
|
if (!questions.length) {
|
|
1463
1475
|
prevSteps.push({
|
|
@@ -1474,7 +1486,8 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
|
|
|
1474
1486
|
error: "Missing/invalid 'questions_to_ask'",
|
|
1475
1487
|
allowFallback: false
|
|
1476
1488
|
}
|
|
1477
|
-
}
|
|
1489
|
+
},
|
|
1490
|
+
...deferredSiblingCalls
|
|
1478
1491
|
]
|
|
1479
1492
|
});
|
|
1480
1493
|
limitPrevSteps(prevSteps);
|
|
@@ -1489,7 +1502,11 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
|
|
|
1489
1502
|
{
|
|
1490
1503
|
name: "ask_user",
|
|
1491
1504
|
args: {
|
|
1492
|
-
questions_to_ask: questions.map((question) => ({
|
|
1505
|
+
questions_to_ask: questions.map((question) => ({
|
|
1506
|
+
key: question.key,
|
|
1507
|
+
query: question.query,
|
|
1508
|
+
...question.required === false ? { required: false } : {}
|
|
1509
|
+
}))
|
|
1493
1510
|
},
|
|
1494
1511
|
response: {
|
|
1495
1512
|
status: "Success",
|
|
@@ -1499,7 +1516,8 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
|
|
|
1499
1516
|
questions
|
|
1500
1517
|
}
|
|
1501
1518
|
}
|
|
1502
|
-
}
|
|
1519
|
+
},
|
|
1520
|
+
...deferredSiblingCalls
|
|
1503
1521
|
]
|
|
1504
1522
|
});
|
|
1505
1523
|
limitPrevSteps(prevSteps);
|
|
@@ -1608,12 +1626,16 @@ function normalizeAskUserQuestions(rawQuestions) {
|
|
|
1608
1626
|
if (seenKeys.has(rawKey))
|
|
1609
1627
|
continue;
|
|
1610
1628
|
seenKeys.add(rawKey);
|
|
1629
|
+
const hasRequired = typeof item.required === "boolean";
|
|
1630
|
+
const hasOptional = typeof item.optional === "boolean";
|
|
1631
|
+
const required = hasRequired ? !!item.required : hasOptional ? !item.optional : true;
|
|
1611
1632
|
out.push({
|
|
1612
1633
|
key: rawKey,
|
|
1613
1634
|
query,
|
|
1614
1635
|
...typeof item.question === "string" && item.question.trim() ? { question: String(item.question).trim() } : {},
|
|
1615
1636
|
...typeof item.id === "string" && item.id.trim() ? { id: String(item.id).trim() } : {},
|
|
1616
|
-
...Array.isArray(item.choices) ? { choices: item.choices } : {}
|
|
1637
|
+
...Array.isArray(item.choices) ? { choices: item.choices } : {},
|
|
1638
|
+
required
|
|
1617
1639
|
});
|
|
1618
1640
|
}
|
|
1619
1641
|
return out.slice(0, 6);
|
|
@@ -4574,12 +4596,16 @@ function normalizePlannerQuestion(input, index) {
|
|
|
4574
4596
|
const key = keyCandidate || `clarification_${index + 1}`;
|
|
4575
4597
|
if (!queryCandidate)
|
|
4576
4598
|
return void 0;
|
|
4599
|
+
const hasRequired = typeof input.required === "boolean";
|
|
4600
|
+
const hasOptional = typeof input.optional === "boolean";
|
|
4601
|
+
const required = hasRequired ? !!input.required : hasOptional ? !input.optional : true;
|
|
4577
4602
|
return {
|
|
4578
4603
|
key,
|
|
4579
4604
|
query: queryCandidate,
|
|
4580
4605
|
...typeof input.id === "string" && input.id.trim() ? { id: input.id.trim() } : {},
|
|
4581
4606
|
...typeof input.question === "string" && input.question.trim() ? { question: input.question.trim() } : {},
|
|
4582
|
-
...Array.isArray(input.choices) ? { choices: input.choices } : {}
|
|
4607
|
+
...Array.isArray(input.choices) ? { choices: input.choices } : {},
|
|
4608
|
+
required
|
|
4583
4609
|
};
|
|
4584
4610
|
}
|
|
4585
4611
|
function normalizePlannerQuestions(input) {
|
|
@@ -5078,36 +5104,41 @@ function extractLatestPrevStepsFromPlanner(toolResults) {
|
|
|
5078
5104
|
}
|
|
5079
5105
|
return void 0;
|
|
5080
5106
|
}
|
|
5081
|
-
async function waitForNewTabReady(
|
|
5082
|
-
if (!bridgeRpc)
|
|
5083
|
-
return false;
|
|
5107
|
+
async function waitForNewTabReady(openedTab, timeoutMs = 1e4) {
|
|
5108
|
+
if (!bridgeRpc) {
|
|
5109
|
+
return { ready: false, attached: false, external: !!openedTab.external };
|
|
5110
|
+
}
|
|
5084
5111
|
const pollInterval = 500;
|
|
5085
5112
|
const start = Date.now();
|
|
5086
5113
|
while (Date.now() - start < timeoutMs) {
|
|
5087
5114
|
try {
|
|
5088
5115
|
const tabs = await bridgeRpc("listSessionTabs");
|
|
5089
5116
|
if (Array.isArray(tabs)) {
|
|
5090
|
-
const target = tabs.find((t) => Number(t?.logicalTabId) === logicalTabId);
|
|
5117
|
+
const target = tabs.find((t) => Number(t?.logicalTabId) === openedTab.logicalTabId);
|
|
5091
5118
|
if (target?.external) {
|
|
5092
|
-
return true;
|
|
5119
|
+
return { ready: true, attached: false, external: true };
|
|
5093
5120
|
}
|
|
5094
5121
|
if (target?.runtimeId) {
|
|
5095
5122
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
5096
|
-
return true;
|
|
5123
|
+
return { ready: true, attached: true, external: false };
|
|
5097
5124
|
}
|
|
5098
5125
|
}
|
|
5099
5126
|
} catch {
|
|
5100
5127
|
}
|
|
5101
5128
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
5102
5129
|
}
|
|
5103
|
-
return false;
|
|
5130
|
+
return { ready: false, attached: false, external: !!openedTab.external };
|
|
5104
5131
|
}
|
|
5105
5132
|
function detectOpenedTabFromToolResult(result) {
|
|
5106
5133
|
if (!result || typeof result !== "object")
|
|
5107
5134
|
return void 0;
|
|
5108
5135
|
const output = result.output ?? result;
|
|
5109
5136
|
if (output?.openedInNewTab && typeof output?.logicalTabId === "number") {
|
|
5110
|
-
return
|
|
5137
|
+
return {
|
|
5138
|
+
logicalTabId: output.logicalTabId,
|
|
5139
|
+
url: typeof output?.url === "string" ? output.url : void 0,
|
|
5140
|
+
external: output?.external === true
|
|
5141
|
+
};
|
|
5111
5142
|
}
|
|
5112
5143
|
return void 0;
|
|
5113
5144
|
}
|
|
@@ -5240,11 +5271,13 @@ function clearTaskScopedContextAfterCompletion() {
|
|
|
5240
5271
|
pendingAskUser = void 0;
|
|
5241
5272
|
}
|
|
5242
5273
|
async function maybeWaitForNewTab(result) {
|
|
5243
|
-
const
|
|
5244
|
-
if (
|
|
5274
|
+
const openedTab = detectOpenedTabFromToolResult(result);
|
|
5275
|
+
if (openedTab) {
|
|
5245
5276
|
postStatus("Waiting for new tab to load...", void 0, "execute");
|
|
5246
|
-
await waitForNewTabReady(
|
|
5277
|
+
const readyState = await waitForNewTabReady(openedTab);
|
|
5278
|
+
return { openedTab, readyState };
|
|
5247
5279
|
}
|
|
5280
|
+
return {};
|
|
5248
5281
|
}
|
|
5249
5282
|
async function handleUserMessage(text, options) {
|
|
5250
5283
|
if (!config)
|
|
@@ -5274,11 +5307,22 @@ async function handleUserMessage(text, options) {
|
|
|
5274
5307
|
applyAgentPrevSteps([
|
|
5275
5308
|
...agentPrevSteps,
|
|
5276
5309
|
{
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5310
|
+
functions: [{
|
|
5311
|
+
name: "ask_user",
|
|
5312
|
+
args: {
|
|
5313
|
+
questions_to_ask: pendingAskUser.questions.map((q) => ({
|
|
5314
|
+
key: q.key,
|
|
5315
|
+
query: q.query
|
|
5316
|
+
}))
|
|
5317
|
+
},
|
|
5318
|
+
response: {
|
|
5319
|
+
status: "Success",
|
|
5320
|
+
output: {
|
|
5321
|
+
ask_user_answers: normalizedAnswers.answersByKey,
|
|
5322
|
+
raw_user_reply: normalizedAnswers.rawText
|
|
5323
|
+
}
|
|
5324
|
+
}
|
|
5325
|
+
}]
|
|
5282
5326
|
}
|
|
5283
5327
|
], { snapshot: false });
|
|
5284
5328
|
pendingAskUser = void 0;
|
|
@@ -5418,7 +5462,44 @@ async function handleUserMessage(text, options) {
|
|
|
5418
5462
|
return { route: result.route, taskComplete: true };
|
|
5419
5463
|
}
|
|
5420
5464
|
if (result.directToolResult) {
|
|
5421
|
-
await maybeWaitForNewTab(result.directToolResult);
|
|
5465
|
+
const newTabWait = await maybeWaitForNewTab(result.directToolResult);
|
|
5466
|
+
if (newTabWait.openedTab && newTabWait.readyState && !newTabWait.readyState.ready && !newTabWait.readyState.external) {
|
|
5467
|
+
const fallbackUrl = String(newTabWait.openedTab.url || "").trim();
|
|
5468
|
+
if (fallbackUrl && bridgeRpc) {
|
|
5469
|
+
postStatus("New tab did not attach; continuing in current tab", void 0, "execute");
|
|
5470
|
+
try {
|
|
5471
|
+
const tabCtx = await bridgeRpc("getTabContext");
|
|
5472
|
+
const localLogicalTabId = Number(tabCtx?.logicalTabId ?? tabCtx?.id);
|
|
5473
|
+
if (Number.isFinite(localLogicalTabId) && localLogicalTabId > 0) {
|
|
5474
|
+
await bridgeRpc("executeTool", {
|
|
5475
|
+
call: {
|
|
5476
|
+
name: "switch_tab",
|
|
5477
|
+
args: { tab_id: localLogicalTabId }
|
|
5478
|
+
},
|
|
5479
|
+
payload: {
|
|
5480
|
+
forceLocal: true,
|
|
5481
|
+
reason: "opened_tab_unattached_reset_active"
|
|
5482
|
+
}
|
|
5483
|
+
});
|
|
5484
|
+
}
|
|
5485
|
+
const fallbackResult = await bridgeRpc("executeTool", {
|
|
5486
|
+
call: {
|
|
5487
|
+
name: "goto_url",
|
|
5488
|
+
args: {
|
|
5489
|
+
tab_id: 0,
|
|
5490
|
+
url: fallbackUrl
|
|
5491
|
+
}
|
|
5492
|
+
},
|
|
5493
|
+
payload: {
|
|
5494
|
+
forceLocal: true,
|
|
5495
|
+
reason: "opened_tab_unattached_fallback"
|
|
5496
|
+
}
|
|
5497
|
+
});
|
|
5498
|
+
maybePostNavigationGuardrailFromToolResult(fallbackResult);
|
|
5499
|
+
} catch {
|
|
5500
|
+
}
|
|
5501
|
+
}
|
|
5502
|
+
}
|
|
5422
5503
|
postStatus("Verifying result", void 0, "verify");
|
|
5423
5504
|
maybePostNavigationGuardrailFromToolResult(result.directToolResult);
|
|
5424
5505
|
applyAgentPrevSteps(result.directToolResult.prevSteps, { snapshot: false });
|