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