@sandagent/runner-cli 0.9.9-beta.2 → 0.9.10

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