@sandagent/runner-cli 0.9.8 → 0.9.9-beta.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/bundle.mjs +191 -166
- package/package.json +3 -3
package/dist/bundle.mjs
CHANGED
|
@@ -1302,6 +1302,26 @@ function parseModelSpec(model) {
|
|
|
1302
1302
|
function getEnvValue(optionsEnv, name) {
|
|
1303
1303
|
return optionsEnv?.[name] ?? process.env[name];
|
|
1304
1304
|
}
|
|
1305
|
+
function pushRunnerEnvToProcess(env) {
|
|
1306
|
+
if (!env)
|
|
1307
|
+
return () => {
|
|
1308
|
+
};
|
|
1309
|
+
const backup = {};
|
|
1310
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1311
|
+
if (typeof value !== "string")
|
|
1312
|
+
continue;
|
|
1313
|
+
backup[key] = process.env[key];
|
|
1314
|
+
process.env[key] = value;
|
|
1315
|
+
}
|
|
1316
|
+
return () => {
|
|
1317
|
+
for (const [key, prev] of Object.entries(backup)) {
|
|
1318
|
+
if (prev === void 0)
|
|
1319
|
+
delete process.env[key];
|
|
1320
|
+
else
|
|
1321
|
+
process.env[key] = prev;
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1305
1325
|
function applyModelOverrides(model, provider, optionsEnv) {
|
|
1306
1326
|
if (model == null)
|
|
1307
1327
|
return;
|
|
@@ -1404,7 +1424,7 @@ function createPiRunner(options = {}) {
|
|
|
1404
1424
|
if (model == null) {
|
|
1405
1425
|
const baseUrlEnvKey = `${provider.toUpperCase().replace(/-/g, "_")}_BASE_URL`;
|
|
1406
1426
|
const apiKeyEnvKey = `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY`;
|
|
1407
|
-
const baseUrl =
|
|
1427
|
+
const baseUrl = getEnvValue(options.env, baseUrlEnvKey) ?? getEnvValue(options.env, "OPENAI_BASE_URL");
|
|
1408
1428
|
if (!baseUrl) {
|
|
1409
1429
|
throw new Error(`Pi runner: model "${modelSpec}" not found in built-in catalog. Set ${baseUrlEnvKey} (or OPENAI_BASE_URL) to auto-register it.`);
|
|
1410
1430
|
}
|
|
@@ -1433,213 +1453,218 @@ function createPiRunner(options = {}) {
|
|
|
1433
1453
|
applyModelOverrides(model, provider, options.env);
|
|
1434
1454
|
return {
|
|
1435
1455
|
async *run(userInput) {
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1456
|
+
const restoreProcessEnv = pushRunnerEnvToProcess(options.env);
|
|
1457
|
+
try {
|
|
1458
|
+
const resume = options.sessionId?.trim();
|
|
1459
|
+
const sessionManager = await (async () => {
|
|
1460
|
+
if (resume !== void 0 && resume !== "") {
|
|
1461
|
+
if (resume.includes("/")) {
|
|
1462
|
+
return SessionManager.open(resume);
|
|
1463
|
+
}
|
|
1464
|
+
const sessions = await SessionManager.list(cwd);
|
|
1465
|
+
const found = sessions.find((s) => s.id === resume);
|
|
1466
|
+
return found ? SessionManager.open(found.path) : SessionManager.create(cwd);
|
|
1441
1467
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1468
|
+
return SessionManager.create(cwd);
|
|
1469
|
+
})();
|
|
1470
|
+
const resourceLoader = options.skillPaths ? new SandagentResourceLoader({ cwd, skillPaths: options.skillPaths }) : void 0;
|
|
1471
|
+
if (options.skillPaths && options.skillPaths.length > 0) {
|
|
1472
|
+
console.error(`${LOG_PREFIX2} runner: cwd=${cwd} skillPaths=${JSON.stringify(options.skillPaths)}`);
|
|
1445
1473
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
modelRegistry,
|
|
1460
|
-
resourceLoader
|
|
1461
|
-
});
|
|
1462
|
-
if (options.systemPrompt != null && options.systemPrompt !== "") {
|
|
1463
|
-
const existing = session.agent.state.systemPrompt ?? "";
|
|
1464
|
-
session.agent.setSystemPrompt(existing ? `${existing}
|
|
1474
|
+
if (resourceLoader) {
|
|
1475
|
+
await resourceLoader.reload();
|
|
1476
|
+
}
|
|
1477
|
+
const { session } = await createAgentSession({
|
|
1478
|
+
cwd,
|
|
1479
|
+
model,
|
|
1480
|
+
sessionManager,
|
|
1481
|
+
modelRegistry,
|
|
1482
|
+
resourceLoader
|
|
1483
|
+
});
|
|
1484
|
+
if (options.systemPrompt != null && options.systemPrompt !== "") {
|
|
1485
|
+
const existing = session.agent.state.systemPrompt ?? "";
|
|
1486
|
+
session.agent.setSystemPrompt(existing ? `${existing}
|
|
1465
1487
|
|
|
1466
1488
|
---
|
|
1467
1489
|
|
|
1468
1490
|
${options.systemPrompt}` : options.systemPrompt);
|
|
1469
|
-
}
|
|
1470
|
-
const eventQueue = [];
|
|
1471
|
-
let isComplete = false;
|
|
1472
|
-
let aborted = false;
|
|
1473
|
-
let wakeConsumer = null;
|
|
1474
|
-
const notify = () => {
|
|
1475
|
-
wakeConsumer?.();
|
|
1476
|
-
wakeConsumer = null;
|
|
1477
|
-
};
|
|
1478
|
-
const unsubscribe = session.subscribe((e) => {
|
|
1479
|
-
eventQueue.push(e);
|
|
1480
|
-
if (e.type === "agent_end") {
|
|
1481
|
-
isComplete = true;
|
|
1482
1491
|
}
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1492
|
+
const eventQueue = [];
|
|
1493
|
+
let isComplete = false;
|
|
1494
|
+
let aborted = false;
|
|
1495
|
+
let wakeConsumer = null;
|
|
1496
|
+
const notify = () => {
|
|
1497
|
+
wakeConsumer?.();
|
|
1498
|
+
wakeConsumer = null;
|
|
1499
|
+
};
|
|
1500
|
+
const unsubscribe = session.subscribe((e) => {
|
|
1501
|
+
eventQueue.push(e);
|
|
1502
|
+
if (e.type === "agent_end") {
|
|
1503
|
+
isComplete = true;
|
|
1504
|
+
}
|
|
1505
|
+
notify();
|
|
1506
|
+
});
|
|
1507
|
+
const abortSignal = options.abortController?.signal;
|
|
1508
|
+
const abortHandler = () => {
|
|
1509
|
+
aborted = true;
|
|
1510
|
+
isComplete = true;
|
|
1511
|
+
void session.abort();
|
|
1512
|
+
notify();
|
|
1513
|
+
};
|
|
1514
|
+
if (abortSignal) {
|
|
1515
|
+
abortSignal.addEventListener("abort", abortHandler);
|
|
1516
|
+
if (abortSignal.aborted) {
|
|
1517
|
+
abortHandler();
|
|
1518
|
+
}
|
|
1496
1519
|
}
|
|
1497
|
-
}
|
|
1498
|
-
try {
|
|
1499
|
-
traceRawMessage(cwd, null, true);
|
|
1500
|
-
let promptText = userInput;
|
|
1501
|
-
let images;
|
|
1502
1520
|
try {
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1521
|
+
traceRawMessage(cwd, null, true);
|
|
1522
|
+
let promptText = userInput;
|
|
1523
|
+
let images;
|
|
1524
|
+
try {
|
|
1525
|
+
if (userInput.startsWith("[") && userInput.endsWith("]")) {
|
|
1526
|
+
const parsed = JSON.parse(userInput);
|
|
1527
|
+
if (Array.isArray(parsed)) {
|
|
1528
|
+
promptText = parsed.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
1529
|
+
const imageParts = parsed.filter((p) => p.type === "image");
|
|
1530
|
+
if (imageParts.length > 0) {
|
|
1531
|
+
images = imageParts.map((p) => ({
|
|
1532
|
+
type: "image",
|
|
1533
|
+
data: p.data,
|
|
1534
|
+
mimeType: p.mimeType
|
|
1535
|
+
}));
|
|
1536
|
+
}
|
|
1514
1537
|
}
|
|
1515
1538
|
}
|
|
1539
|
+
} catch (_e) {
|
|
1516
1540
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
if (!hasStarted) {
|
|
1527
|
-
yield `data: ${JSON.stringify({ type: "start", messageId })}
|
|
1541
|
+
const promptPromise = session.prompt(promptText, images ? { images } : void 0);
|
|
1542
|
+
const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
1543
|
+
const textId = `text_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
1544
|
+
let hasStarted = false;
|
|
1545
|
+
let hasTextStarted = false;
|
|
1546
|
+
let hasFinished = false;
|
|
1547
|
+
const ensureStartEvent = async function* () {
|
|
1548
|
+
if (!hasStarted) {
|
|
1549
|
+
yield `data: ${JSON.stringify({ type: "start", messageId })}
|
|
1528
1550
|
|
|
1529
1551
|
`;
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1552
|
+
yield `data: ${JSON.stringify({
|
|
1553
|
+
type: "message-metadata",
|
|
1554
|
+
messageMetadata: { sessionId: session.sessionId }
|
|
1555
|
+
})}
|
|
1534
1556
|
|
|
1535
1557
|
`;
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1558
|
+
hasStarted = true;
|
|
1559
|
+
}
|
|
1560
|
+
};
|
|
1561
|
+
const finishSuccess = async function* (usage) {
|
|
1562
|
+
if (hasTextStarted) {
|
|
1563
|
+
yield `data: ${JSON.stringify({ type: "text-end", id: textId })}
|
|
1542
1564
|
|
|
1543
1565
|
`;
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1566
|
+
}
|
|
1567
|
+
const finishPayload = { type: "finish", finishReason: "stop" };
|
|
1568
|
+
if (usage != null) {
|
|
1569
|
+
finishPayload.messageMetadata = {
|
|
1570
|
+
usage: usageToMessageMetadata(usage)
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
yield `data: ${JSON.stringify(finishPayload)}
|
|
1552
1574
|
|
|
1553
1575
|
`;
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1576
|
+
yield "data: [DONE]\n\n";
|
|
1577
|
+
hasFinished = true;
|
|
1578
|
+
};
|
|
1579
|
+
const finishError = async function* (errorText) {
|
|
1580
|
+
for (const chunk of emitStreamError(errorText)) {
|
|
1581
|
+
yield chunk;
|
|
1582
|
+
}
|
|
1583
|
+
hasFinished = true;
|
|
1584
|
+
};
|
|
1585
|
+
while (!isComplete || eventQueue.length > 0) {
|
|
1586
|
+
while (eventQueue.length > 0) {
|
|
1587
|
+
const event = eventQueue.shift();
|
|
1588
|
+
traceRawMessage(cwd, event);
|
|
1589
|
+
yield* ensureStartEvent();
|
|
1590
|
+
if (event.type === "message_update") {
|
|
1591
|
+
if (event.assistantMessageEvent.type === "text_delta") {
|
|
1592
|
+
const delta = event.assistantMessageEvent.delta;
|
|
1593
|
+
if (delta) {
|
|
1594
|
+
if (!hasTextStarted) {
|
|
1595
|
+
yield `data: ${JSON.stringify({ type: "text-start", id: textId })}
|
|
1574
1596
|
|
|
1575
1597
|
`;
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1598
|
+
hasTextStarted = true;
|
|
1599
|
+
}
|
|
1600
|
+
yield `data: ${JSON.stringify({ type: "text-delta", id: textId, delta })}
|
|
1579
1601
|
|
|
1580
1602
|
`;
|
|
1603
|
+
}
|
|
1581
1604
|
}
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
yield `data: ${JSON.stringify({ type: "tool-input-start", toolCallId: event.toolCallId, toolName: event.toolName, dynamic: true, providerExecuted: true })}
|
|
1605
|
+
} else if (event.type === "tool_execution_start") {
|
|
1606
|
+
yield `data: ${JSON.stringify({ type: "tool-input-start", toolCallId: event.toolCallId, toolName: event.toolName, dynamic: true, providerExecuted: true })}
|
|
1585
1607
|
|
|
1586
1608
|
`;
|
|
1587
|
-
|
|
1609
|
+
yield `data: ${JSON.stringify({ type: "tool-input-available", toolCallId: event.toolCallId, toolName: event.toolName, input: event.args, dynamic: true, providerExecuted: true })}
|
|
1588
1610
|
|
|
1589
1611
|
`;
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1612
|
+
} else if (event.type === "tool_execution_end") {
|
|
1613
|
+
const output = extractToolResultText(event.result);
|
|
1614
|
+
yield `data: ${JSON.stringify({ type: "tool-output-available", toolCallId: event.toolCallId, output, isError: event.isError, dynamic: true, providerExecuted: true })}
|
|
1593
1615
|
|
|
1594
1616
|
`;
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
} else {
|
|
1599
|
-
const errorMsg = getErrorFromAgentEndMessages(event.messages);
|
|
1600
|
-
if (errorMsg) {
|
|
1601
|
-
yield* finishError(errorMsg);
|
|
1617
|
+
} else if (event.type === "agent_end") {
|
|
1618
|
+
if (aborted) {
|
|
1619
|
+
yield* finishError("Run aborted by signal.");
|
|
1602
1620
|
} else {
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1621
|
+
const errorMsg = getErrorFromAgentEndMessages(event.messages);
|
|
1622
|
+
if (errorMsg) {
|
|
1623
|
+
yield* finishError(errorMsg);
|
|
1624
|
+
} else {
|
|
1625
|
+
const usage = getUsageFromAgentEndMessages(event.messages);
|
|
1626
|
+
yield* finishSuccess(usage);
|
|
1627
|
+
}
|
|
1605
1628
|
}
|
|
1606
1629
|
}
|
|
1607
1630
|
}
|
|
1631
|
+
if (aborted && !hasFinished) {
|
|
1632
|
+
yield* ensureStartEvent();
|
|
1633
|
+
yield* finishError("Run aborted by signal.");
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
if (!isComplete && eventQueue.length === 0) {
|
|
1637
|
+
await new Promise((resolve4) => {
|
|
1638
|
+
wakeConsumer = resolve4;
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1608
1641
|
}
|
|
1609
|
-
if (
|
|
1610
|
-
|
|
1611
|
-
yield* finishError("Run aborted by signal.");
|
|
1612
|
-
break;
|
|
1642
|
+
if (hasFinished) {
|
|
1643
|
+
return;
|
|
1613
1644
|
}
|
|
1614
|
-
|
|
1615
|
-
await
|
|
1616
|
-
|
|
1617
|
-
|
|
1645
|
+
try {
|
|
1646
|
+
await promptPromise;
|
|
1647
|
+
} catch (error) {
|
|
1648
|
+
if (!hasFinished) {
|
|
1649
|
+
yield* ensureStartEvent();
|
|
1650
|
+
const message = error instanceof Error ? error.message : "Pi agent run failed.";
|
|
1651
|
+
yield* finishError(message);
|
|
1652
|
+
}
|
|
1653
|
+
return;
|
|
1618
1654
|
}
|
|
1619
|
-
|
|
1620
|
-
if (hasFinished) {
|
|
1621
|
-
return;
|
|
1622
|
-
}
|
|
1623
|
-
try {
|
|
1624
|
-
await promptPromise;
|
|
1625
|
-
} catch (error) {
|
|
1626
|
-
if (!hasFinished) {
|
|
1655
|
+
if (!hasFinished && session.agent.state.error) {
|
|
1627
1656
|
yield* ensureStartEvent();
|
|
1628
|
-
|
|
1629
|
-
yield* finishError(message);
|
|
1657
|
+
yield* finishError(session.agent.state.error);
|
|
1630
1658
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1659
|
+
} finally {
|
|
1660
|
+
if (abortSignal) {
|
|
1661
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
1662
|
+
}
|
|
1663
|
+
unsubscribe();
|
|
1664
|
+
session.dispose();
|
|
1636
1665
|
}
|
|
1637
1666
|
} finally {
|
|
1638
|
-
|
|
1639
|
-
abortSignal.removeEventListener("abort", abortHandler);
|
|
1640
|
-
}
|
|
1641
|
-
unsubscribe();
|
|
1642
|
-
session.dispose();
|
|
1667
|
+
restoreProcessEnv();
|
|
1643
1668
|
}
|
|
1644
1669
|
}
|
|
1645
1670
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sandagent/runner-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9-beta.1",
|
|
4
4
|
"description": "SandAgent Runner CLI - Like gemini-cli or claude-code, runs in your local terminal with AI SDK UI streaming",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -53,11 +53,11 @@
|
|
|
53
53
|
"esbuild": "^0.27.2",
|
|
54
54
|
"typescript": "^5.3.0",
|
|
55
55
|
"vitest": "^1.6.1",
|
|
56
|
-
"@sandagent/runner-core": "0.1.0",
|
|
57
56
|
"@sandagent/runner-claude": "0.6.2",
|
|
58
|
-
"@sandagent/runner-codex": "0.6.2",
|
|
59
57
|
"@sandagent/runner-gemini": "0.6.2",
|
|
58
|
+
"@sandagent/runner-core": "0.1.0",
|
|
60
59
|
"@sandagent/runner-opencode": "0.6.2",
|
|
60
|
+
"@sandagent/runner-codex": "0.6.2",
|
|
61
61
|
"@sandagent/runner-pi": "0.6.3"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|