@robota-sdk/agent-cli 3.0.0-beta.35 → 3.0.0-beta.36

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/node/bin.cjs CHANGED
@@ -168,7 +168,8 @@ var import_ink14 = require("ink");
168
168
  // src/ui/App.tsx
169
169
  var import_react16 = require("react");
170
170
  var import_ink13 = require("ink");
171
- var import_agent_core3 = require("@robota-sdk/agent-core");
171
+ var import_agent_core6 = require("@robota-sdk/agent-core");
172
+ var import_agent_core7 = require("@robota-sdk/agent-core");
172
173
 
173
174
  // src/ui/hooks/useSession.ts
174
175
  var import_react = require("react");
@@ -264,6 +265,7 @@ var NOOP_TERMINAL = {
264
265
  function useSession(props) {
265
266
  const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
266
267
  const [streamingText, setStreamingText] = (0, import_react.useState)("");
268
+ const streamingTextRef = (0, import_react.useRef)("");
267
269
  const [activeTools, setActiveTools] = (0, import_react.useState)([]);
268
270
  const permissionQueueRef = (0, import_react.useRef)([]);
269
271
  const processingRef = (0, import_react.useRef)(false);
@@ -295,8 +297,15 @@ function useSession(props) {
295
297
  processNextPermission();
296
298
  });
297
299
  };
300
+ let flushTimer = null;
298
301
  const onTextDelta = (delta) => {
299
- setStreamingText((prev) => prev + delta);
302
+ streamingTextRef.current += delta;
303
+ if (!flushTimer) {
304
+ flushTimer = setTimeout(() => {
305
+ setStreamingText(streamingTextRef.current);
306
+ flushTimer = null;
307
+ }, 16);
308
+ }
300
309
  };
301
310
  const onToolExecution = (event) => {
302
311
  if (event.type === "start") {
@@ -375,6 +384,7 @@ function useSession(props) {
375
384
  }
376
385
  const clearStreamingText = (0, import_react.useCallback)(() => {
377
386
  setStreamingText("");
387
+ streamingTextRef.current = "";
378
388
  setActiveTools([]);
379
389
  }, []);
380
390
  return {
@@ -389,16 +399,11 @@ function useSession(props) {
389
399
  // src/ui/hooks/useMessages.ts
390
400
  var import_react2 = require("react");
391
401
  var MAX_RENDERED_MESSAGES = 100;
392
- var msgIdCounter = 0;
393
- function nextId() {
394
- msgIdCounter += 1;
395
- return `msg_${msgIdCounter}`;
396
- }
397
402
  function useMessages() {
398
403
  const [messages, setMessages] = (0, import_react2.useState)([]);
399
404
  const addMessage = (0, import_react2.useCallback)((msg) => {
400
405
  setMessages((prev) => {
401
- const updated = [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }];
406
+ const updated = [...prev, msg];
402
407
  if (updated.length > MAX_RENDERED_MESSAGES) {
403
408
  return updated.slice(-MAX_RENDERED_MESSAGES);
404
409
  }
@@ -410,6 +415,7 @@ function useMessages() {
410
415
 
411
416
  // src/ui/hooks/useSlashCommands.ts
412
417
  var import_react3 = require("react");
418
+ var import_agent_core = require("@robota-sdk/agent-core");
413
419
 
414
420
  // src/commands/slash-executor.ts
415
421
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -676,11 +682,14 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
676
682
  const cmd = parts[0]?.toLowerCase() ?? "";
677
683
  const args = parts.slice(1).join(" ");
678
684
  const clearMessages = () => setMessages([]);
685
+ const slashAddMessage = (msg) => {
686
+ addMessage((0, import_agent_core.createSystemMessage)(msg.content));
687
+ };
679
688
  const result = await executeSlashCommand(
680
689
  cmd,
681
690
  args,
682
691
  session,
683
- addMessage,
692
+ slashAddMessage,
684
693
  clearMessages,
685
694
  registry,
686
695
  pluginCallbacks
@@ -713,7 +722,9 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
713
722
 
714
723
  // src/ui/hooks/useSubmitHandler.ts
715
724
  var import_react4 = require("react");
725
+ var import_node_crypto = require("crypto");
716
726
  var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
727
+ var import_agent_core2 = require("@robota-sdk/agent-core");
717
728
 
718
729
  // src/utils/tool-call-extractor.ts
719
730
  var TOOL_ARG_MAX_LENGTH = 80;
@@ -870,21 +881,50 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
870
881
  historyBefore
871
882
  );
872
883
  if (toolSummaries.length > 0) {
873
- addMessage({
874
- role: "tool",
875
- content: JSON.stringify(toolSummaries),
876
- toolName: `${toolSummaries.length} tools`
877
- });
884
+ addMessage(
885
+ (0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
886
+ toolCallId: (0, import_node_crypto.randomUUID)(),
887
+ name: `${toolSummaries.length} tools`
888
+ })
889
+ );
878
890
  }
879
- addMessage({ role: "assistant", content: response || "(empty response)" });
891
+ addMessage((0, import_agent_core2.createAssistantMessage)(response || "(empty response)"));
880
892
  syncContextState(session, setContextState);
881
893
  } catch (err) {
882
894
  clearStreamingText();
883
- if (err instanceof DOMException && err.name === "AbortError") {
884
- addMessage({ role: "system", content: "Cancelled." });
895
+ const isAbortError = err instanceof DOMException && err.name === "AbortError" || err instanceof Error && (err.message.includes("aborted") || err.message.includes("abort"));
896
+ if (isAbortError) {
897
+ const history = session.getHistory();
898
+ const toolSummaries = extractToolCallsWithDiff(
899
+ history,
900
+ historyBefore
901
+ );
902
+ if (toolSummaries.length > 0) {
903
+ addMessage(
904
+ (0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
905
+ toolCallId: (0, import_node_crypto.randomUUID)(),
906
+ name: `${toolSummaries.length} tools`
907
+ })
908
+ );
909
+ }
910
+ const assistantParts = [];
911
+ let lastAssistantState = "complete";
912
+ for (let i = historyBefore; i < history.length; i++) {
913
+ const msg = history[i];
914
+ if (msg && msg.role === "assistant" && msg.content) {
915
+ assistantParts.push(msg.content);
916
+ if (msg.state === "interrupted") lastAssistantState = "interrupted";
917
+ }
918
+ }
919
+ if (assistantParts.length > 0) {
920
+ addMessage(
921
+ (0, import_agent_core2.createAssistantMessage)(assistantParts.join("\n\n"), { state: lastAssistantState })
922
+ );
923
+ }
924
+ addMessage((0, import_agent_core2.createSystemMessage)("Interrupted by user."));
885
925
  } else {
886
926
  const errMsg = err instanceof Error ? err.message : String(err);
887
- addMessage({ role: "system", content: `Error: ${errMsg}` });
927
+ addMessage((0, import_agent_core2.createSystemMessage)(`Error: ${errMsg}`));
888
928
  }
889
929
  } finally {
890
930
  setIsThinking(false);
@@ -937,7 +977,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
937
977
  const runInFork = createForkRunner(session);
938
978
  const result = await executeSkill(skill, args, { runInFork });
939
979
  if (result.mode === "fork") {
940
- addMessage({ role: "assistant", content: result.result ?? "(empty response)" });
980
+ addMessage((0, import_agent_core2.createAssistantMessage)(result.result ?? "(empty response)"));
941
981
  syncContextState(session, setContextState);
942
982
  return;
943
983
  }
@@ -972,7 +1012,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
972
1012
  hookInput
973
1013
  );
974
1014
  }
975
- addMessage({ role: "user", content: input });
1015
+ addMessage((0, import_agent_core2.createUserMessage)(input));
976
1016
  return runSessionPrompt(
977
1017
  input,
978
1018
  session,
@@ -1039,16 +1079,16 @@ var CommandRegistry = class {
1039
1079
  };
1040
1080
 
1041
1081
  // src/commands/builtin-source.ts
1042
- var import_agent_core = require("@robota-sdk/agent-core");
1082
+ var import_agent_core3 = require("@robota-sdk/agent-core");
1043
1083
  function buildModelSubcommands() {
1044
1084
  const seen = /* @__PURE__ */ new Set();
1045
1085
  const commands = [];
1046
- for (const model of Object.values(import_agent_core.CLAUDE_MODELS)) {
1086
+ for (const model of Object.values(import_agent_core3.CLAUDE_MODELS)) {
1047
1087
  if (seen.has(model.name)) continue;
1048
1088
  seen.add(model.name);
1049
1089
  commands.push({
1050
1090
  name: model.id,
1051
- description: `${model.name} (${(0, import_agent_core.formatTokenCount)(model.contextWindow).toUpperCase()})`,
1091
+ description: `${model.name} (${(0, import_agent_core3.formatTokenCount)(model.contextWindow).toUpperCase()})`,
1052
1092
  source: "builtin"
1053
1093
  });
1054
1094
  }
@@ -1443,6 +1483,7 @@ function usePluginCallbacks(cwd) {
1443
1483
  // src/ui/MessageList.tsx
1444
1484
  var import_react7 = __toESM(require("react"), 1);
1445
1485
  var import_ink2 = require("ink");
1486
+ var import_agent_core4 = require("@robota-sdk/agent-core");
1446
1487
 
1447
1488
  // src/ui/render-markdown.ts
1448
1489
  var import_marked = require("marked");
@@ -1528,9 +1569,14 @@ function RoleLabel({ role }) {
1528
1569
  }
1529
1570
  }
1530
1571
  function ToolMessage({ message }) {
1572
+ if (!(0, import_agent_core4.isToolMessage)(message)) {
1573
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
1574
+ }
1575
+ const toolName = message.name;
1576
+ const content = message.content;
1531
1577
  let summaries = null;
1532
1578
  try {
1533
- const parsed = JSON.parse(message.content);
1579
+ const parsed = JSON.parse(content);
1534
1580
  if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === "string") {
1535
1581
  summaries = parsed;
1536
1582
  }
@@ -1543,9 +1589,9 @@ function ToolMessage({ message }) {
1543
1589
  "Tool:",
1544
1590
  " "
1545
1591
  ] }),
1546
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1592
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1547
1593
  "[",
1548
- message.toolName,
1594
+ toolName,
1549
1595
  "]"
1550
1596
  ] })
1551
1597
  ] }),
@@ -1561,16 +1607,16 @@ function ToolMessage({ message }) {
1561
1607
  ] }, i))
1562
1608
  ] });
1563
1609
  }
1564
- const lines = message.content.split("\n").filter((l) => l.trim());
1610
+ const lines = content.split("\n").filter((l) => l.trim());
1565
1611
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
1566
1612
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
1567
1613
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
1568
1614
  "Tool:",
1569
1615
  " "
1570
1616
  ] }),
1571
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1617
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1572
1618
  "[",
1573
- message.toolName,
1619
+ toolName,
1574
1620
  "]"
1575
1621
  ] })
1576
1622
  ] }),
@@ -1586,21 +1632,15 @@ function ToolMessage({ message }) {
1586
1632
  var MessageItem = import_react7.default.memo(function MessageItem2({
1587
1633
  message
1588
1634
  }) {
1589
- if (message.role === "tool") {
1635
+ if ((0, import_agent_core4.isToolMessage)(message)) {
1590
1636
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
1591
1637
  }
1638
+ const content = message.content ?? "";
1639
+ const isInterrupted = message.state === "interrupted";
1592
1640
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
1593
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
1594
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }),
1595
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "magenta", dimColor: true, children: [
1596
- "[",
1597
- message.toolName,
1598
- "]",
1599
- " "
1600
- ] })
1601
- ] }),
1641
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
1602
1642
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
1603
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: message.role === "assistant" ? renderMarkdown(message.content) : message.content }) })
1643
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: (0, import_agent_core4.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
1604
1644
  ] });
1605
1645
  });
1606
1646
  function MessageList({ messages }) {
@@ -1609,7 +1649,7 @@ function MessageList({ messages }) {
1609
1649
 
1610
1650
  // src/ui/StatusBar.tsx
1611
1651
  var import_ink3 = require("ink");
1612
- var import_agent_core2 = require("@robota-sdk/agent-core");
1652
+ var import_agent_core5 = require("@robota-sdk/agent-core");
1613
1653
  var import_jsx_runtime3 = require("react/jsx-runtime");
1614
1654
  var CONTEXT_YELLOW_THRESHOLD = 70;
1615
1655
  var CONTEXT_RED_THRESHOLD = 90;
@@ -1649,9 +1689,9 @@ function StatusBar({
1649
1689
  "Context: ",
1650
1690
  Math.round(contextPercentage),
1651
1691
  "% (",
1652
- (0, import_agent_core2.formatTokenCount)(contextUsedTokens),
1692
+ (0, import_agent_core5.formatTokenCount)(contextUsedTokens),
1653
1693
  "/",
1654
- (0, import_agent_core2.formatTokenCount)(contextMaxTokens),
1694
+ (0, import_agent_core5.formatTokenCount)(contextMaxTokens),
1655
1695
  ")"
1656
1696
  ] })
1657
1697
  ] }),
@@ -1979,7 +2019,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1979
2019
  isSubcommandMode
1980
2020
  }
1981
2021
  ),
1982
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { borderStyle: "single", borderColor: isDisabled ? "gray" : "green", paddingLeft: 1, children: isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WaveText, { text: " Waiting for response..." }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { children: [
2022
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { borderStyle: "single", borderColor: isDisabled ? "gray" : "green", paddingLeft: 1, children: isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { children: [
1983
2023
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "green", bold: true, children: "> " }),
1984
2024
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1985
2025
  CjkTextInput,
@@ -2115,7 +2155,7 @@ function StreamingIndicator({ text, activeTools }) {
2115
2155
  const hasTools = activeTools.length > 0;
2116
2156
  const hasText = text.length > 0;
2117
2157
  if (!hasTools && !hasText) {
2118
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "yellow", children: "Thinking..." });
2158
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, {});
2119
2159
  }
2120
2160
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
2121
2161
  hasTools && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: [
@@ -2689,7 +2729,7 @@ function App(props) {
2689
2729
  pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2690
2730
  ConfirmPrompt,
2691
2731
  {
2692
- message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
2732
+ message: `Change model to ${(0, import_agent_core6.getModelName)(pendingModelId)}? This will restart the session.`,
2693
2733
  onSelect: (index) => {
2694
2734
  setPendingModelId(null);
2695
2735
  pendingModelChangeRef.current = null;
@@ -2697,19 +2737,21 @@ function App(props) {
2697
2737
  try {
2698
2738
  const settingsPath = getUserSettingsPath();
2699
2739
  updateModelInSettings(settingsPath, pendingModelId);
2700
- addMessage({
2701
- role: "system",
2702
- content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...`
2703
- });
2740
+ addMessage(
2741
+ (0, import_agent_core7.createSystemMessage)(
2742
+ `Model changed to ${(0, import_agent_core6.getModelName)(pendingModelId)}. Restarting...`
2743
+ )
2744
+ );
2704
2745
  setTimeout(() => exit(), EXIT_DELAY_MS2);
2705
2746
  } catch (err) {
2706
- addMessage({
2707
- role: "system",
2708
- content: `Failed: ${err instanceof Error ? err.message : String(err)}`
2709
- });
2747
+ addMessage(
2748
+ (0, import_agent_core7.createSystemMessage)(
2749
+ `Failed: ${err instanceof Error ? err.message : String(err)}`
2750
+ )
2751
+ );
2710
2752
  }
2711
2753
  } else {
2712
- addMessage({ role: "system", content: "Model change cancelled." });
2754
+ addMessage((0, import_agent_core7.createSystemMessage)("Model change cancelled."));
2713
2755
  }
2714
2756
  }
2715
2757
  }
@@ -2719,14 +2761,14 @@ function App(props) {
2719
2761
  {
2720
2762
  callbacks: pluginCallbacks,
2721
2763
  onClose: () => setShowPluginTUI(false),
2722
- addMessage: (msg) => addMessage(msg)
2764
+ addMessage: (msg) => addMessage((0, import_agent_core7.createSystemMessage)(msg.content))
2723
2765
  }
2724
2766
  ),
2725
2767
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2726
2768
  StatusBar,
2727
2769
  {
2728
2770
  permissionMode: session.getPermissionMode(),
2729
- modelName: (0, import_agent_core3.getModelName)(props.config.provider.model),
2771
+ modelName: (0, import_agent_core6.getModelName)(props.config.provider.model),
2730
2772
  sessionId: session.getSessionId(),
2731
2773
  messageCount: messages.length,
2732
2774
  isThinking,
package/dist/node/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startCli
4
- } from "./chunk-27OPEZHA.js";
4
+ } from "./chunk-ERF646KY.js";
5
5
 
6
6
  // src/bin.ts
7
7
  process.on("uncaughtException", (err) => {
@@ -152,6 +152,7 @@ import { render } from "ink";
152
152
  import { useState as useState10, useRef as useRef8 } from "react";
153
153
  import { Box as Box11, Text as Text13, useApp, useInput as useInput7 } from "ink";
154
154
  import { getModelName } from "@robota-sdk/agent-core";
155
+ import { createSystemMessage as createSystemMessage3 } from "@robota-sdk/agent-core";
155
156
 
156
157
  // src/ui/hooks/useSession.ts
157
158
  import { useState, useCallback, useRef } from "react";
@@ -247,6 +248,7 @@ var NOOP_TERMINAL = {
247
248
  function useSession(props) {
248
249
  const [permissionRequest, setPermissionRequest] = useState(null);
249
250
  const [streamingText, setStreamingText] = useState("");
251
+ const streamingTextRef = useRef("");
250
252
  const [activeTools, setActiveTools] = useState([]);
251
253
  const permissionQueueRef = useRef([]);
252
254
  const processingRef = useRef(false);
@@ -278,8 +280,15 @@ function useSession(props) {
278
280
  processNextPermission();
279
281
  });
280
282
  };
283
+ let flushTimer = null;
281
284
  const onTextDelta = (delta) => {
282
- setStreamingText((prev) => prev + delta);
285
+ streamingTextRef.current += delta;
286
+ if (!flushTimer) {
287
+ flushTimer = setTimeout(() => {
288
+ setStreamingText(streamingTextRef.current);
289
+ flushTimer = null;
290
+ }, 16);
291
+ }
283
292
  };
284
293
  const onToolExecution = (event) => {
285
294
  if (event.type === "start") {
@@ -358,6 +367,7 @@ function useSession(props) {
358
367
  }
359
368
  const clearStreamingText = useCallback(() => {
360
369
  setStreamingText("");
370
+ streamingTextRef.current = "";
361
371
  setActiveTools([]);
362
372
  }, []);
363
373
  return {
@@ -372,16 +382,11 @@ function useSession(props) {
372
382
  // src/ui/hooks/useMessages.ts
373
383
  import { useState as useState2, useCallback as useCallback2 } from "react";
374
384
  var MAX_RENDERED_MESSAGES = 100;
375
- var msgIdCounter = 0;
376
- function nextId() {
377
- msgIdCounter += 1;
378
- return `msg_${msgIdCounter}`;
379
- }
380
385
  function useMessages() {
381
386
  const [messages, setMessages] = useState2([]);
382
387
  const addMessage = useCallback2((msg) => {
383
388
  setMessages((prev) => {
384
- const updated = [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }];
389
+ const updated = [...prev, msg];
385
390
  if (updated.length > MAX_RENDERED_MESSAGES) {
386
391
  return updated.slice(-MAX_RENDERED_MESSAGES);
387
392
  }
@@ -393,6 +398,7 @@ function useMessages() {
393
398
 
394
399
  // src/ui/hooks/useSlashCommands.ts
395
400
  import { useCallback as useCallback3 } from "react";
401
+ import { createSystemMessage } from "@robota-sdk/agent-core";
396
402
 
397
403
  // src/commands/slash-executor.ts
398
404
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -659,11 +665,14 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
659
665
  const cmd = parts[0]?.toLowerCase() ?? "";
660
666
  const args = parts.slice(1).join(" ");
661
667
  const clearMessages = () => setMessages([]);
668
+ const slashAddMessage = (msg) => {
669
+ addMessage(createSystemMessage(msg.content));
670
+ };
662
671
  const result = await executeSlashCommand(
663
672
  cmd,
664
673
  args,
665
674
  session,
666
- addMessage,
675
+ slashAddMessage,
667
676
  clearMessages,
668
677
  registry,
669
678
  pluginCallbacks
@@ -696,11 +705,18 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
696
705
 
697
706
  // src/ui/hooks/useSubmitHandler.ts
698
707
  import { useCallback as useCallback4 } from "react";
708
+ import { randomUUID } from "crypto";
699
709
  import {
700
710
  createSubagentSession,
701
711
  getBuiltInAgent,
702
712
  retrieveAgentToolDeps
703
713
  } from "@robota-sdk/agent-sdk";
714
+ import {
715
+ createUserMessage,
716
+ createAssistantMessage,
717
+ createSystemMessage as createSystemMessage2,
718
+ createToolMessage
719
+ } from "@robota-sdk/agent-core";
704
720
 
705
721
  // src/utils/tool-call-extractor.ts
706
722
  var TOOL_ARG_MAX_LENGTH = 80;
@@ -857,21 +873,50 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
857
873
  historyBefore
858
874
  );
859
875
  if (toolSummaries.length > 0) {
860
- addMessage({
861
- role: "tool",
862
- content: JSON.stringify(toolSummaries),
863
- toolName: `${toolSummaries.length} tools`
864
- });
876
+ addMessage(
877
+ createToolMessage(JSON.stringify(toolSummaries), {
878
+ toolCallId: randomUUID(),
879
+ name: `${toolSummaries.length} tools`
880
+ })
881
+ );
865
882
  }
866
- addMessage({ role: "assistant", content: response || "(empty response)" });
883
+ addMessage(createAssistantMessage(response || "(empty response)"));
867
884
  syncContextState(session, setContextState);
868
885
  } catch (err) {
869
886
  clearStreamingText();
870
- if (err instanceof DOMException && err.name === "AbortError") {
871
- addMessage({ role: "system", content: "Cancelled." });
887
+ const isAbortError = err instanceof DOMException && err.name === "AbortError" || err instanceof Error && (err.message.includes("aborted") || err.message.includes("abort"));
888
+ if (isAbortError) {
889
+ const history = session.getHistory();
890
+ const toolSummaries = extractToolCallsWithDiff(
891
+ history,
892
+ historyBefore
893
+ );
894
+ if (toolSummaries.length > 0) {
895
+ addMessage(
896
+ createToolMessage(JSON.stringify(toolSummaries), {
897
+ toolCallId: randomUUID(),
898
+ name: `${toolSummaries.length} tools`
899
+ })
900
+ );
901
+ }
902
+ const assistantParts = [];
903
+ let lastAssistantState = "complete";
904
+ for (let i = historyBefore; i < history.length; i++) {
905
+ const msg = history[i];
906
+ if (msg && msg.role === "assistant" && msg.content) {
907
+ assistantParts.push(msg.content);
908
+ if (msg.state === "interrupted") lastAssistantState = "interrupted";
909
+ }
910
+ }
911
+ if (assistantParts.length > 0) {
912
+ addMessage(
913
+ createAssistantMessage(assistantParts.join("\n\n"), { state: lastAssistantState })
914
+ );
915
+ }
916
+ addMessage(createSystemMessage2("Interrupted by user."));
872
917
  } else {
873
918
  const errMsg = err instanceof Error ? err.message : String(err);
874
- addMessage({ role: "system", content: `Error: ${errMsg}` });
919
+ addMessage(createSystemMessage2(`Error: ${errMsg}`));
875
920
  }
876
921
  } finally {
877
922
  setIsThinking(false);
@@ -924,7 +969,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
924
969
  const runInFork = createForkRunner(session);
925
970
  const result = await executeSkill(skill, args, { runInFork });
926
971
  if (result.mode === "fork") {
927
- addMessage({ role: "assistant", content: result.result ?? "(empty response)" });
972
+ addMessage(createAssistantMessage(result.result ?? "(empty response)"));
928
973
  syncContextState(session, setContextState);
929
974
  return;
930
975
  }
@@ -959,7 +1004,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
959
1004
  hookInput
960
1005
  );
961
1006
  }
962
- addMessage({ role: "user", content: input });
1007
+ addMessage(createUserMessage(input));
963
1008
  return runSessionPrompt(
964
1009
  input,
965
1010
  session,
@@ -1435,6 +1480,7 @@ function usePluginCallbacks(cwd) {
1435
1480
  // src/ui/MessageList.tsx
1436
1481
  import React2 from "react";
1437
1482
  import { Box as Box2, Text as Text2 } from "ink";
1483
+ import { isToolMessage, isAssistantMessage } from "@robota-sdk/agent-core";
1438
1484
 
1439
1485
  // src/ui/render-markdown.ts
1440
1486
  import { marked } from "marked";
@@ -1494,7 +1540,7 @@ function DiffBlock({ file, lines }) {
1494
1540
  }
1495
1541
 
1496
1542
  // src/ui/MessageList.tsx
1497
- import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
1543
+ import { Fragment, jsx, jsxs as jsxs2 } from "react/jsx-runtime";
1498
1544
  function RoleLabel({ role }) {
1499
1545
  switch (role) {
1500
1546
  case "user":
@@ -1520,9 +1566,14 @@ function RoleLabel({ role }) {
1520
1566
  }
1521
1567
  }
1522
1568
  function ToolMessage({ message }) {
1569
+ if (!isToolMessage(message)) {
1570
+ return /* @__PURE__ */ jsx(Fragment, {});
1571
+ }
1572
+ const toolName = message.name;
1573
+ const content = message.content;
1523
1574
  let summaries = null;
1524
1575
  try {
1525
- const parsed = JSON.parse(message.content);
1576
+ const parsed = JSON.parse(content);
1526
1577
  if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === "string") {
1527
1578
  summaries = parsed;
1528
1579
  }
@@ -1535,9 +1586,9 @@ function ToolMessage({ message }) {
1535
1586
  "Tool:",
1536
1587
  " "
1537
1588
  ] }),
1538
- message.toolName && /* @__PURE__ */ jsxs2(Text2, { color: "white", dimColor: true, children: [
1589
+ toolName && /* @__PURE__ */ jsxs2(Text2, { color: "white", dimColor: true, children: [
1539
1590
  "[",
1540
- message.toolName,
1591
+ toolName,
1541
1592
  "]"
1542
1593
  ] })
1543
1594
  ] }),
@@ -1553,16 +1604,16 @@ function ToolMessage({ message }) {
1553
1604
  ] }, i))
1554
1605
  ] });
1555
1606
  }
1556
- const lines = message.content.split("\n").filter((l) => l.trim());
1607
+ const lines = content.split("\n").filter((l) => l.trim());
1557
1608
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
1558
1609
  /* @__PURE__ */ jsxs2(Box2, { children: [
1559
1610
  /* @__PURE__ */ jsxs2(Text2, { color: "white", bold: true, children: [
1560
1611
  "Tool:",
1561
1612
  " "
1562
1613
  ] }),
1563
- message.toolName && /* @__PURE__ */ jsxs2(Text2, { color: "white", dimColor: true, children: [
1614
+ toolName && /* @__PURE__ */ jsxs2(Text2, { color: "white", dimColor: true, children: [
1564
1615
  "[",
1565
- message.toolName,
1616
+ toolName,
1566
1617
  "]"
1567
1618
  ] })
1568
1619
  ] }),
@@ -1578,21 +1629,15 @@ function ToolMessage({ message }) {
1578
1629
  var MessageItem = React2.memo(function MessageItem2({
1579
1630
  message
1580
1631
  }) {
1581
- if (message.role === "tool") {
1632
+ if (isToolMessage(message)) {
1582
1633
  return /* @__PURE__ */ jsx(ToolMessage, { message });
1583
1634
  }
1635
+ const content = message.content ?? "";
1636
+ const isInterrupted = message.state === "interrupted";
1584
1637
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
1585
- /* @__PURE__ */ jsxs2(Box2, { children: [
1586
- /* @__PURE__ */ jsx(RoleLabel, { role: message.role }),
1587
- message.toolName && /* @__PURE__ */ jsxs2(Text2, { color: "magenta", dimColor: true, children: [
1588
- "[",
1589
- message.toolName,
1590
- "]",
1591
- " "
1592
- ] })
1593
- ] }),
1638
+ /* @__PURE__ */ jsx(Box2, { children: /* @__PURE__ */ jsx(RoleLabel, { role: message.role }) }),
1594
1639
  /* @__PURE__ */ jsx(Text2, { children: " " }),
1595
- /* @__PURE__ */ jsx(Box2, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text2, { wrap: "wrap", children: message.role === "assistant" ? renderMarkdown(message.content) : message.content }) })
1640
+ /* @__PURE__ */ jsx(Box2, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text2, { wrap: "wrap", children: isAssistantMessage(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
1596
1641
  ] });
1597
1642
  });
1598
1643
  function MessageList({ messages }) {
@@ -1971,7 +2016,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1971
2016
  isSubcommandMode
1972
2017
  }
1973
2018
  ),
1974
- /* @__PURE__ */ jsx6(Box5, { borderStyle: "single", borderColor: isDisabled ? "gray" : "green", paddingLeft: 1, children: isDisabled ? /* @__PURE__ */ jsx6(WaveText, { text: " Waiting for response..." }) : /* @__PURE__ */ jsxs5(Box5, { children: [
2019
+ /* @__PURE__ */ jsx6(Box5, { borderStyle: "single", borderColor: isDisabled ? "gray" : "green", paddingLeft: 1, children: isDisabled ? /* @__PURE__ */ jsx6(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ jsxs5(Box5, { children: [
1975
2020
  /* @__PURE__ */ jsx6(Text7, { color: "green", bold: true, children: "> " }),
1976
2021
  /* @__PURE__ */ jsx6(
1977
2022
  CjkTextInput,
@@ -2096,7 +2141,7 @@ function PermissionPrompt({ request }) {
2096
2141
 
2097
2142
  // src/ui/StreamingIndicator.tsx
2098
2143
  import { Box as Box8, Text as Text10 } from "ink";
2099
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2144
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2100
2145
  function getToolStyle(t) {
2101
2146
  if (t.isRunning) return { color: "yellow", icon: "\u27F3", strikethrough: false };
2102
2147
  if (t.result === "error") return { color: "red", icon: "\u2717", strikethrough: true };
@@ -2107,7 +2152,7 @@ function StreamingIndicator({ text, activeTools }) {
2107
2152
  const hasTools = activeTools.length > 0;
2108
2153
  const hasText = text.length > 0;
2109
2154
  if (!hasTools && !hasText) {
2110
- return /* @__PURE__ */ jsx9(Text10, { color: "yellow", children: "Thinking..." });
2155
+ return /* @__PURE__ */ jsx9(Fragment2, {});
2111
2156
  }
2112
2157
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2113
2158
  hasTools && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
@@ -2689,19 +2734,21 @@ function App(props) {
2689
2734
  try {
2690
2735
  const settingsPath = getUserSettingsPath();
2691
2736
  updateModelInSettings(settingsPath, pendingModelId);
2692
- addMessage({
2693
- role: "system",
2694
- content: `Model changed to ${getModelName(pendingModelId)}. Restarting...`
2695
- });
2737
+ addMessage(
2738
+ createSystemMessage3(
2739
+ `Model changed to ${getModelName(pendingModelId)}. Restarting...`
2740
+ )
2741
+ );
2696
2742
  setTimeout(() => exit(), EXIT_DELAY_MS2);
2697
2743
  } catch (err) {
2698
- addMessage({
2699
- role: "system",
2700
- content: `Failed: ${err instanceof Error ? err.message : String(err)}`
2701
- });
2744
+ addMessage(
2745
+ createSystemMessage3(
2746
+ `Failed: ${err instanceof Error ? err.message : String(err)}`
2747
+ )
2748
+ );
2702
2749
  }
2703
2750
  } else {
2704
- addMessage({ role: "system", content: "Model change cancelled." });
2751
+ addMessage(createSystemMessage3("Model change cancelled."));
2705
2752
  }
2706
2753
  }
2707
2754
  }
@@ -2711,7 +2758,7 @@ function App(props) {
2711
2758
  {
2712
2759
  callbacks: pluginCallbacks,
2713
2760
  onClose: () => setShowPluginTUI(false),
2714
- addMessage: (msg) => addMessage(msg)
2761
+ addMessage: (msg) => addMessage(createSystemMessage3(msg.content))
2715
2762
  }
2716
2763
  ),
2717
2764
  /* @__PURE__ */ jsx13(
@@ -184,7 +184,8 @@ var import_ink14 = require("ink");
184
184
  // src/ui/App.tsx
185
185
  var import_react16 = require("react");
186
186
  var import_ink13 = require("ink");
187
- var import_agent_core3 = require("@robota-sdk/agent-core");
187
+ var import_agent_core6 = require("@robota-sdk/agent-core");
188
+ var import_agent_core7 = require("@robota-sdk/agent-core");
188
189
 
189
190
  // src/ui/hooks/useSession.ts
190
191
  var import_react = require("react");
@@ -280,6 +281,7 @@ var NOOP_TERMINAL = {
280
281
  function useSession(props) {
281
282
  const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
282
283
  const [streamingText, setStreamingText] = (0, import_react.useState)("");
284
+ const streamingTextRef = (0, import_react.useRef)("");
283
285
  const [activeTools, setActiveTools] = (0, import_react.useState)([]);
284
286
  const permissionQueueRef = (0, import_react.useRef)([]);
285
287
  const processingRef = (0, import_react.useRef)(false);
@@ -311,8 +313,15 @@ function useSession(props) {
311
313
  processNextPermission();
312
314
  });
313
315
  };
316
+ let flushTimer = null;
314
317
  const onTextDelta = (delta) => {
315
- setStreamingText((prev) => prev + delta);
318
+ streamingTextRef.current += delta;
319
+ if (!flushTimer) {
320
+ flushTimer = setTimeout(() => {
321
+ setStreamingText(streamingTextRef.current);
322
+ flushTimer = null;
323
+ }, 16);
324
+ }
316
325
  };
317
326
  const onToolExecution = (event) => {
318
327
  if (event.type === "start") {
@@ -391,6 +400,7 @@ function useSession(props) {
391
400
  }
392
401
  const clearStreamingText = (0, import_react.useCallback)(() => {
393
402
  setStreamingText("");
403
+ streamingTextRef.current = "";
394
404
  setActiveTools([]);
395
405
  }, []);
396
406
  return {
@@ -405,16 +415,11 @@ function useSession(props) {
405
415
  // src/ui/hooks/useMessages.ts
406
416
  var import_react2 = require("react");
407
417
  var MAX_RENDERED_MESSAGES = 100;
408
- var msgIdCounter = 0;
409
- function nextId() {
410
- msgIdCounter += 1;
411
- return `msg_${msgIdCounter}`;
412
- }
413
418
  function useMessages() {
414
419
  const [messages, setMessages] = (0, import_react2.useState)([]);
415
420
  const addMessage = (0, import_react2.useCallback)((msg) => {
416
421
  setMessages((prev) => {
417
- const updated = [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }];
422
+ const updated = [...prev, msg];
418
423
  if (updated.length > MAX_RENDERED_MESSAGES) {
419
424
  return updated.slice(-MAX_RENDERED_MESSAGES);
420
425
  }
@@ -426,6 +431,7 @@ function useMessages() {
426
431
 
427
432
  // src/ui/hooks/useSlashCommands.ts
428
433
  var import_react3 = require("react");
434
+ var import_agent_core = require("@robota-sdk/agent-core");
429
435
 
430
436
  // src/commands/slash-executor.ts
431
437
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -692,11 +698,14 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
692
698
  const cmd = parts[0]?.toLowerCase() ?? "";
693
699
  const args = parts.slice(1).join(" ");
694
700
  const clearMessages = () => setMessages([]);
701
+ const slashAddMessage = (msg) => {
702
+ addMessage((0, import_agent_core.createSystemMessage)(msg.content));
703
+ };
695
704
  const result = await executeSlashCommand(
696
705
  cmd,
697
706
  args,
698
707
  session,
699
- addMessage,
708
+ slashAddMessage,
700
709
  clearMessages,
701
710
  registry,
702
711
  pluginCallbacks
@@ -729,7 +738,9 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
729
738
 
730
739
  // src/ui/hooks/useSubmitHandler.ts
731
740
  var import_react4 = require("react");
741
+ var import_node_crypto = require("crypto");
732
742
  var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
743
+ var import_agent_core2 = require("@robota-sdk/agent-core");
733
744
 
734
745
  // src/utils/tool-call-extractor.ts
735
746
  var TOOL_ARG_MAX_LENGTH = 80;
@@ -886,21 +897,50 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
886
897
  historyBefore
887
898
  );
888
899
  if (toolSummaries.length > 0) {
889
- addMessage({
890
- role: "tool",
891
- content: JSON.stringify(toolSummaries),
892
- toolName: `${toolSummaries.length} tools`
893
- });
900
+ addMessage(
901
+ (0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
902
+ toolCallId: (0, import_node_crypto.randomUUID)(),
903
+ name: `${toolSummaries.length} tools`
904
+ })
905
+ );
894
906
  }
895
- addMessage({ role: "assistant", content: response || "(empty response)" });
907
+ addMessage((0, import_agent_core2.createAssistantMessage)(response || "(empty response)"));
896
908
  syncContextState(session, setContextState);
897
909
  } catch (err) {
898
910
  clearStreamingText();
899
- if (err instanceof DOMException && err.name === "AbortError") {
900
- addMessage({ role: "system", content: "Cancelled." });
911
+ const isAbortError = err instanceof DOMException && err.name === "AbortError" || err instanceof Error && (err.message.includes("aborted") || err.message.includes("abort"));
912
+ if (isAbortError) {
913
+ const history = session.getHistory();
914
+ const toolSummaries = extractToolCallsWithDiff(
915
+ history,
916
+ historyBefore
917
+ );
918
+ if (toolSummaries.length > 0) {
919
+ addMessage(
920
+ (0, import_agent_core2.createToolMessage)(JSON.stringify(toolSummaries), {
921
+ toolCallId: (0, import_node_crypto.randomUUID)(),
922
+ name: `${toolSummaries.length} tools`
923
+ })
924
+ );
925
+ }
926
+ const assistantParts = [];
927
+ let lastAssistantState = "complete";
928
+ for (let i = historyBefore; i < history.length; i++) {
929
+ const msg = history[i];
930
+ if (msg && msg.role === "assistant" && msg.content) {
931
+ assistantParts.push(msg.content);
932
+ if (msg.state === "interrupted") lastAssistantState = "interrupted";
933
+ }
934
+ }
935
+ if (assistantParts.length > 0) {
936
+ addMessage(
937
+ (0, import_agent_core2.createAssistantMessage)(assistantParts.join("\n\n"), { state: lastAssistantState })
938
+ );
939
+ }
940
+ addMessage((0, import_agent_core2.createSystemMessage)("Interrupted by user."));
901
941
  } else {
902
942
  const errMsg = err instanceof Error ? err.message : String(err);
903
- addMessage({ role: "system", content: `Error: ${errMsg}` });
943
+ addMessage((0, import_agent_core2.createSystemMessage)(`Error: ${errMsg}`));
904
944
  }
905
945
  } finally {
906
946
  setIsThinking(false);
@@ -953,7 +993,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
953
993
  const runInFork = createForkRunner(session);
954
994
  const result = await executeSkill(skill, args, { runInFork });
955
995
  if (result.mode === "fork") {
956
- addMessage({ role: "assistant", content: result.result ?? "(empty response)" });
996
+ addMessage((0, import_agent_core2.createAssistantMessage)(result.result ?? "(empty response)"));
957
997
  syncContextState(session, setContextState);
958
998
  return;
959
999
  }
@@ -988,7 +1028,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
988
1028
  hookInput
989
1029
  );
990
1030
  }
991
- addMessage({ role: "user", content: input });
1031
+ addMessage((0, import_agent_core2.createUserMessage)(input));
992
1032
  return runSessionPrompt(
993
1033
  input,
994
1034
  session,
@@ -1055,16 +1095,16 @@ var CommandRegistry = class {
1055
1095
  };
1056
1096
 
1057
1097
  // src/commands/builtin-source.ts
1058
- var import_agent_core = require("@robota-sdk/agent-core");
1098
+ var import_agent_core3 = require("@robota-sdk/agent-core");
1059
1099
  function buildModelSubcommands() {
1060
1100
  const seen = /* @__PURE__ */ new Set();
1061
1101
  const commands = [];
1062
- for (const model of Object.values(import_agent_core.CLAUDE_MODELS)) {
1102
+ for (const model of Object.values(import_agent_core3.CLAUDE_MODELS)) {
1063
1103
  if (seen.has(model.name)) continue;
1064
1104
  seen.add(model.name);
1065
1105
  commands.push({
1066
1106
  name: model.id,
1067
- description: `${model.name} (${(0, import_agent_core.formatTokenCount)(model.contextWindow).toUpperCase()})`,
1107
+ description: `${model.name} (${(0, import_agent_core3.formatTokenCount)(model.contextWindow).toUpperCase()})`,
1068
1108
  source: "builtin"
1069
1109
  });
1070
1110
  }
@@ -1459,6 +1499,7 @@ function usePluginCallbacks(cwd) {
1459
1499
  // src/ui/MessageList.tsx
1460
1500
  var import_react7 = __toESM(require("react"), 1);
1461
1501
  var import_ink2 = require("ink");
1502
+ var import_agent_core4 = require("@robota-sdk/agent-core");
1462
1503
 
1463
1504
  // src/ui/render-markdown.ts
1464
1505
  var import_marked = require("marked");
@@ -1544,9 +1585,14 @@ function RoleLabel({ role }) {
1544
1585
  }
1545
1586
  }
1546
1587
  function ToolMessage({ message }) {
1588
+ if (!(0, import_agent_core4.isToolMessage)(message)) {
1589
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
1590
+ }
1591
+ const toolName = message.name;
1592
+ const content = message.content;
1547
1593
  let summaries = null;
1548
1594
  try {
1549
- const parsed = JSON.parse(message.content);
1595
+ const parsed = JSON.parse(content);
1550
1596
  if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === "string") {
1551
1597
  summaries = parsed;
1552
1598
  }
@@ -1559,9 +1605,9 @@ function ToolMessage({ message }) {
1559
1605
  "Tool:",
1560
1606
  " "
1561
1607
  ] }),
1562
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1608
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1563
1609
  "[",
1564
- message.toolName,
1610
+ toolName,
1565
1611
  "]"
1566
1612
  ] })
1567
1613
  ] }),
@@ -1577,16 +1623,16 @@ function ToolMessage({ message }) {
1577
1623
  ] }, i))
1578
1624
  ] });
1579
1625
  }
1580
- const lines = message.content.split("\n").filter((l) => l.trim());
1626
+ const lines = content.split("\n").filter((l) => l.trim());
1581
1627
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
1582
1628
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
1583
1629
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
1584
1630
  "Tool:",
1585
1631
  " "
1586
1632
  ] }),
1587
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1633
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1588
1634
  "[",
1589
- message.toolName,
1635
+ toolName,
1590
1636
  "]"
1591
1637
  ] })
1592
1638
  ] }),
@@ -1602,21 +1648,15 @@ function ToolMessage({ message }) {
1602
1648
  var MessageItem = import_react7.default.memo(function MessageItem2({
1603
1649
  message
1604
1650
  }) {
1605
- if (message.role === "tool") {
1651
+ if ((0, import_agent_core4.isToolMessage)(message)) {
1606
1652
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
1607
1653
  }
1654
+ const content = message.content ?? "";
1655
+ const isInterrupted = message.state === "interrupted";
1608
1656
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
1609
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
1610
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }),
1611
- message.toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "magenta", dimColor: true, children: [
1612
- "[",
1613
- message.toolName,
1614
- "]",
1615
- " "
1616
- ] })
1617
- ] }),
1657
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
1618
1658
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
1619
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: message.role === "assistant" ? renderMarkdown(message.content) : message.content }) })
1659
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: (0, import_agent_core4.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
1620
1660
  ] });
1621
1661
  });
1622
1662
  function MessageList({ messages }) {
@@ -1625,7 +1665,7 @@ function MessageList({ messages }) {
1625
1665
 
1626
1666
  // src/ui/StatusBar.tsx
1627
1667
  var import_ink3 = require("ink");
1628
- var import_agent_core2 = require("@robota-sdk/agent-core");
1668
+ var import_agent_core5 = require("@robota-sdk/agent-core");
1629
1669
  var import_jsx_runtime3 = require("react/jsx-runtime");
1630
1670
  var CONTEXT_YELLOW_THRESHOLD = 70;
1631
1671
  var CONTEXT_RED_THRESHOLD = 90;
@@ -1665,9 +1705,9 @@ function StatusBar({
1665
1705
  "Context: ",
1666
1706
  Math.round(contextPercentage),
1667
1707
  "% (",
1668
- (0, import_agent_core2.formatTokenCount)(contextUsedTokens),
1708
+ (0, import_agent_core5.formatTokenCount)(contextUsedTokens),
1669
1709
  "/",
1670
- (0, import_agent_core2.formatTokenCount)(contextMaxTokens),
1710
+ (0, import_agent_core5.formatTokenCount)(contextMaxTokens),
1671
1711
  ")"
1672
1712
  ] })
1673
1713
  ] }),
@@ -1995,7 +2035,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
1995
2035
  isSubcommandMode
1996
2036
  }
1997
2037
  ),
1998
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { borderStyle: "single", borderColor: isDisabled ? "gray" : "green", paddingLeft: 1, children: isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WaveText, { text: " Waiting for response..." }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { children: [
2038
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { borderStyle: "single", borderColor: isDisabled ? "gray" : "green", paddingLeft: 1, children: isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { children: [
1999
2039
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "green", bold: true, children: "> " }),
2000
2040
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2001
2041
  CjkTextInput,
@@ -2131,7 +2171,7 @@ function StreamingIndicator({ text, activeTools }) {
2131
2171
  const hasTools = activeTools.length > 0;
2132
2172
  const hasText = text.length > 0;
2133
2173
  if (!hasTools && !hasText) {
2134
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "yellow", children: "Thinking..." });
2174
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, {});
2135
2175
  }
2136
2176
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
2137
2177
  hasTools && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: [
@@ -2705,7 +2745,7 @@ function App(props) {
2705
2745
  pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2706
2746
  ConfirmPrompt,
2707
2747
  {
2708
- message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
2748
+ message: `Change model to ${(0, import_agent_core6.getModelName)(pendingModelId)}? This will restart the session.`,
2709
2749
  onSelect: (index) => {
2710
2750
  setPendingModelId(null);
2711
2751
  pendingModelChangeRef.current = null;
@@ -2713,19 +2753,21 @@ function App(props) {
2713
2753
  try {
2714
2754
  const settingsPath = getUserSettingsPath();
2715
2755
  updateModelInSettings(settingsPath, pendingModelId);
2716
- addMessage({
2717
- role: "system",
2718
- content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...`
2719
- });
2756
+ addMessage(
2757
+ (0, import_agent_core7.createSystemMessage)(
2758
+ `Model changed to ${(0, import_agent_core6.getModelName)(pendingModelId)}. Restarting...`
2759
+ )
2760
+ );
2720
2761
  setTimeout(() => exit(), EXIT_DELAY_MS2);
2721
2762
  } catch (err) {
2722
- addMessage({
2723
- role: "system",
2724
- content: `Failed: ${err instanceof Error ? err.message : String(err)}`
2725
- });
2763
+ addMessage(
2764
+ (0, import_agent_core7.createSystemMessage)(
2765
+ `Failed: ${err instanceof Error ? err.message : String(err)}`
2766
+ )
2767
+ );
2726
2768
  }
2727
2769
  } else {
2728
- addMessage({ role: "system", content: "Model change cancelled." });
2770
+ addMessage((0, import_agent_core7.createSystemMessage)("Model change cancelled."));
2729
2771
  }
2730
2772
  }
2731
2773
  }
@@ -2735,14 +2777,14 @@ function App(props) {
2735
2777
  {
2736
2778
  callbacks: pluginCallbacks,
2737
2779
  onClose: () => setShowPluginTUI(false),
2738
- addMessage: (msg) => addMessage(msg)
2780
+ addMessage: (msg) => addMessage((0, import_agent_core7.createSystemMessage)(msg.content))
2739
2781
  }
2740
2782
  ),
2741
2783
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2742
2784
  StatusBar,
2743
2785
  {
2744
2786
  permissionMode: session.getPermissionMode(),
2745
- modelName: (0, import_agent_core3.getModelName)(props.config.provider.model),
2787
+ modelName: (0, import_agent_core6.getModelName)(props.config.provider.model),
2746
2788
  sessionId: session.getSessionId(),
2747
2789
  messageCount: messages.length,
2748
2790
  isThinking,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startCli
3
- } from "./chunk-27OPEZHA.js";
3
+ } from "./chunk-ERF646KY.js";
4
4
 
5
5
  // src/index.ts
6
6
  import { Session, SessionStore, query, TRUST_TO_MODE } from "@robota-sdk/agent-sdk";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-cli",
3
- "version": "3.0.0-beta.35",
3
+ "version": "3.0.0-beta.36",
4
4
  "description": "AI coding assistant CLI built on Robota SDK",
5
5
  "type": "module",
6
6
  "bin": {
@@ -35,8 +35,8 @@
35
35
  "marked-terminal": "^7.3.0",
36
36
  "react": "19.2.4",
37
37
  "string-width": "^8.2.0",
38
- "@robota-sdk/agent-sdk": "3.0.0-beta.33",
39
- "@robota-sdk/agent-core": "3.0.0-beta.33"
38
+ "@robota-sdk/agent-core": "3.0.0-beta.33",
39
+ "@robota-sdk/agent-sdk": "3.0.0-beta.33"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/marked": "^6.0.0",