@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.
Files changed (2) hide show
  1. package/dist/bundle.mjs +191 -166
  2. 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 = process.env[baseUrlEnvKey] ?? process.env.OPENAI_BASE_URL;
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 resume = options.sessionId?.trim();
1437
- const sessionManager = await (async () => {
1438
- if (resume !== void 0 && resume !== "") {
1439
- if (resume.includes("/")) {
1440
- return SessionManager.open(resume);
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
- 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);
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
- 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}
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
- 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();
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
- 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
- }));
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
- } 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 })}
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
- yield `data: ${JSON.stringify({
1531
- type: "message-metadata",
1532
- messageMetadata: { sessionId: session.sessionId }
1533
- })}
1552
+ yield `data: ${JSON.stringify({
1553
+ type: "message-metadata",
1554
+ messageMetadata: { sessionId: session.sessionId }
1555
+ })}
1534
1556
 
1535
1557
  `;
1536
- hasStarted = true;
1537
- }
1538
- };
1539
- const finishSuccess = async function* (usage) {
1540
- if (hasTextStarted) {
1541
- yield `data: ${JSON.stringify({ type: "text-end", id: textId })}
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
- 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)}
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
- 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 })}
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
- hasTextStarted = true;
1577
- }
1578
- yield `data: ${JSON.stringify({ type: "text-delta", id: textId, delta })}
1598
+ hasTextStarted = true;
1599
+ }
1600
+ yield `data: ${JSON.stringify({ type: "text-delta", id: textId, delta })}
1579
1601
 
1580
1602
  `;
1603
+ }
1581
1604
  }
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 })}
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
- yield `data: ${JSON.stringify({ type: "tool-input-available", toolCallId: event.toolCallId, toolName: event.toolName, input: event.args, dynamic: true, providerExecuted: true })}
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
- } 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 })}
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
- } 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);
1617
+ } else if (event.type === "agent_end") {
1618
+ if (aborted) {
1619
+ yield* finishError("Run aborted by signal.");
1602
1620
  } else {
1603
- const usage = getUsageFromAgentEndMessages(event.messages);
1604
- yield* finishSuccess(usage);
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 (aborted && !hasFinished) {
1610
- yield* ensureStartEvent();
1611
- yield* finishError("Run aborted by signal.");
1612
- break;
1642
+ if (hasFinished) {
1643
+ return;
1613
1644
  }
1614
- if (!isComplete && eventQueue.length === 0) {
1615
- await new Promise((resolve4) => {
1616
- wakeConsumer = resolve4;
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
- const message = error instanceof Error ? error.message : "Pi agent run failed.";
1629
- yield* finishError(message);
1657
+ yield* finishError(session.agent.state.error);
1630
1658
  }
1631
- return;
1632
- }
1633
- if (!hasFinished && session.agent.state.error) {
1634
- yield* ensureStartEvent();
1635
- yield* finishError(session.agent.state.error);
1659
+ } finally {
1660
+ if (abortSignal) {
1661
+ abortSignal.removeEventListener("abort", abortHandler);
1662
+ }
1663
+ unsubscribe();
1664
+ session.dispose();
1636
1665
  }
1637
1666
  } finally {
1638
- if (abortSignal) {
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.8",
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": {