@robota-sdk/agent-cli 3.0.0-beta.18 → 3.0.0-beta.19

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.
@@ -180,8 +180,123 @@ var PrintTerminal = class {
180
180
  var import_ink11 = require("ink");
181
181
 
182
182
  // src/ui/App.tsx
183
- var import_react6 = require("react");
183
+ var import_react11 = require("react");
184
184
  var import_ink10 = require("ink");
185
+ var import_agent_core3 = require("@robota-sdk/agent-core");
186
+
187
+ // src/ui/hooks/useSession.ts
188
+ var import_react = require("react");
189
+ var import_agent_sdk = require("@robota-sdk/agent-sdk");
190
+ var TOOL_ARG_DISPLAY_MAX = 80;
191
+ var TOOL_ARG_TRUNCATE_AT = 77;
192
+ var NOOP_TERMINAL = {
193
+ write: () => {
194
+ },
195
+ writeLine: () => {
196
+ },
197
+ writeMarkdown: () => {
198
+ },
199
+ writeError: () => {
200
+ },
201
+ prompt: () => Promise.resolve(""),
202
+ select: () => Promise.resolve(0),
203
+ spinner: () => ({ stop: () => {
204
+ }, update: () => {
205
+ } })
206
+ };
207
+ function useSession(props) {
208
+ const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
209
+ const [streamingText, setStreamingText] = (0, import_react.useState)("");
210
+ const [activeTools, setActiveTools] = (0, import_react.useState)([]);
211
+ const permissionQueueRef = (0, import_react.useRef)([]);
212
+ const processingRef = (0, import_react.useRef)(false);
213
+ const processNextPermission = (0, import_react.useCallback)(() => {
214
+ if (processingRef.current) return;
215
+ const next = permissionQueueRef.current[0];
216
+ if (!next) {
217
+ setPermissionRequest(null);
218
+ return;
219
+ }
220
+ processingRef.current = true;
221
+ setPermissionRequest({
222
+ toolName: next.toolName,
223
+ toolArgs: next.toolArgs,
224
+ resolve: (result) => {
225
+ permissionQueueRef.current.shift();
226
+ processingRef.current = false;
227
+ setPermissionRequest(null);
228
+ next.resolve(result);
229
+ setTimeout(() => processNextPermission(), 0);
230
+ }
231
+ });
232
+ }, []);
233
+ const sessionRef = (0, import_react.useRef)(null);
234
+ if (sessionRef.current === null) {
235
+ const permissionHandler = (toolName, toolArgs) => {
236
+ return new Promise((resolve) => {
237
+ permissionQueueRef.current.push({ toolName, toolArgs, resolve });
238
+ processNextPermission();
239
+ });
240
+ };
241
+ const onTextDelta = (delta) => {
242
+ setStreamingText((prev) => prev + delta);
243
+ };
244
+ const onToolExecution = (event) => {
245
+ if (event.type === "start") {
246
+ let firstArg = "";
247
+ if (event.toolArgs) {
248
+ const firstVal = Object.values(event.toolArgs)[0];
249
+ const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
250
+ firstArg = raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_TRUNCATE_AT) + "..." : raw;
251
+ }
252
+ setActiveTools((prev) => [...prev, { toolName: event.toolName, firstArg, isRunning: true }]);
253
+ } else {
254
+ setActiveTools(
255
+ (prev) => prev.map(
256
+ (t) => t.toolName === event.toolName && t.isRunning ? { ...t, isRunning: false } : t
257
+ )
258
+ );
259
+ }
260
+ };
261
+ const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
262
+ sessionRef.current = (0, import_agent_sdk.createSession)({
263
+ config: props.config,
264
+ context: props.context,
265
+ terminal: NOOP_TERMINAL,
266
+ sessionLogger: new import_agent_sdk.FileSessionLogger(paths.logs),
267
+ projectInfo: props.projectInfo,
268
+ sessionStore: props.sessionStore,
269
+ permissionMode: props.permissionMode,
270
+ maxTurns: props.maxTurns,
271
+ permissionHandler,
272
+ onTextDelta,
273
+ onToolExecution
274
+ });
275
+ }
276
+ const clearStreamingText = (0, import_react.useCallback)(() => {
277
+ setStreamingText("");
278
+ setActiveTools([]);
279
+ }, []);
280
+ return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText, activeTools };
281
+ }
282
+
283
+ // src/ui/hooks/useMessages.ts
284
+ var import_react2 = require("react");
285
+ var msgIdCounter = 0;
286
+ function nextId() {
287
+ msgIdCounter += 1;
288
+ return `msg_${msgIdCounter}`;
289
+ }
290
+ function useMessages() {
291
+ const [messages, setMessages] = (0, import_react2.useState)([]);
292
+ const addMessage = (0, import_react2.useCallback)((msg) => {
293
+ setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
294
+ }, []);
295
+ return { messages, setMessages, addMessage };
296
+ }
297
+
298
+ // src/ui/hooks/useSlashCommands.ts
299
+ var import_react3 = require("react");
185
300
 
186
301
  // src/commands/slash-executor.ts
187
302
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -306,9 +421,133 @@ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages
306
421
  }
307
422
  }
308
423
 
309
- // src/ui/App.tsx
310
- var import_agent_sdk = require("@robota-sdk/agent-sdk");
311
- var import_agent_core3 = require("@robota-sdk/agent-core");
424
+ // src/ui/hooks/useSlashCommands.ts
425
+ var EXIT_DELAY_MS = 500;
426
+ function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
427
+ return (0, import_react3.useCallback)(
428
+ async (input) => {
429
+ const parts = input.slice(1).split(/\s+/);
430
+ const cmd = parts[0]?.toLowerCase() ?? "";
431
+ const args = parts.slice(1).join(" ");
432
+ const clearMessages = () => setMessages([]);
433
+ const result = await executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry);
434
+ if (result.pendingModelId) {
435
+ pendingModelChangeRef.current = result.pendingModelId;
436
+ setPendingModelId(result.pendingModelId);
437
+ }
438
+ if (result.exitRequested) {
439
+ setTimeout(() => exit(), EXIT_DELAY_MS);
440
+ }
441
+ return result.handled;
442
+ },
443
+ [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
444
+ );
445
+ }
446
+
447
+ // src/ui/hooks/useSubmitHandler.ts
448
+ var import_react4 = require("react");
449
+
450
+ // src/utils/tool-call-extractor.ts
451
+ var TOOL_ARG_MAX_LENGTH = 80;
452
+ var TOOL_ARG_TRUNCATE_LENGTH = 77;
453
+ function extractToolCalls(history, startIndex) {
454
+ const lines = [];
455
+ for (let i = startIndex; i < history.length; i++) {
456
+ const msg = history[i];
457
+ if (msg.role === "assistant" && msg.toolCalls) {
458
+ for (const tc of msg.toolCalls) {
459
+ const value = parseFirstArgValue(tc.function.arguments);
460
+ const truncated = value.length > TOOL_ARG_MAX_LENGTH ? value.slice(0, TOOL_ARG_TRUNCATE_LENGTH) + "..." : value;
461
+ lines.push(`${tc.function.name}(${truncated})`);
462
+ }
463
+ }
464
+ }
465
+ return lines;
466
+ }
467
+ function parseFirstArgValue(argsJson) {
468
+ try {
469
+ const parsed = JSON.parse(argsJson);
470
+ const firstVal = Object.values(parsed)[0];
471
+ return typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
472
+ } catch {
473
+ return argsJson;
474
+ }
475
+ }
476
+
477
+ // src/utils/skill-prompt.ts
478
+ function buildSkillPrompt(input, registry) {
479
+ const parts = input.slice(1).split(/\s+/);
480
+ const cmd = parts[0]?.toLowerCase() ?? "";
481
+ const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
482
+ if (!skillCmd) return null;
483
+ const args = parts.slice(1).join(" ").trim();
484
+ const userInstruction = args || skillCmd.description;
485
+ if (skillCmd.skillContent) {
486
+ return `<skill name="${cmd}">
487
+ ${skillCmd.skillContent}
488
+ </skill>
489
+
490
+ Execute the "${cmd}" skill: ${userInstruction}`;
491
+ }
492
+ return `Use the "${cmd}" skill: ${userInstruction}`;
493
+ }
494
+
495
+ // src/ui/hooks/useSubmitHandler.ts
496
+ function syncContextState(session, setter) {
497
+ const ctx = session.getContextState();
498
+ setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
499
+ }
500
+ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState) {
501
+ setIsThinking(true);
502
+ clearStreamingText();
503
+ const historyBefore = session.getHistory().length;
504
+ try {
505
+ const response = await session.run(prompt);
506
+ clearStreamingText();
507
+ const history = session.getHistory();
508
+ const toolLines = extractToolCalls(
509
+ history,
510
+ historyBefore
511
+ );
512
+ if (toolLines.length > 0) {
513
+ addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
514
+ }
515
+ addMessage({ role: "assistant", content: response || "(empty response)" });
516
+ syncContextState(session, setContextState);
517
+ } catch (err) {
518
+ clearStreamingText();
519
+ if (err instanceof DOMException && err.name === "AbortError") {
520
+ addMessage({ role: "system", content: "Cancelled." });
521
+ } else {
522
+ const errMsg = err instanceof Error ? err.message : String(err);
523
+ addMessage({ role: "system", content: `Error: ${errMsg}` });
524
+ }
525
+ } finally {
526
+ setIsThinking(false);
527
+ }
528
+ }
529
+ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
530
+ return (0, import_react4.useCallback)(
531
+ async (input) => {
532
+ if (input.startsWith("/")) {
533
+ const handled = await handleSlashCommand(input);
534
+ if (handled) {
535
+ syncContextState(session, setContextState);
536
+ return;
537
+ }
538
+ const prompt = buildSkillPrompt(input, registry);
539
+ if (!prompt) return;
540
+ return runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState);
541
+ }
542
+ addMessage({ role: "user", content: input });
543
+ return runSessionPrompt(input, session, addMessage, clearStreamingText, setIsThinking, setContextState);
544
+ },
545
+ [session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry]
546
+ );
547
+ }
548
+
549
+ // src/ui/hooks/useCommandRegistry.ts
550
+ var import_react5 = require("react");
312
551
 
313
552
  // src/commands/command-registry.ts
314
553
  var CommandRegistry = class {
@@ -462,6 +701,18 @@ var SkillCommandSource = class {
462
701
  }
463
702
  };
464
703
 
704
+ // src/ui/hooks/useCommandRegistry.ts
705
+ function useCommandRegistry(cwd) {
706
+ const registryRef = (0, import_react5.useRef)(null);
707
+ if (registryRef.current === null) {
708
+ const registry = new CommandRegistry();
709
+ registry.addSource(new BuiltinCommandSource());
710
+ registry.addSource(new SkillCommandSource(cwd));
711
+ registryRef.current = registry;
712
+ }
713
+ return registryRef.current;
714
+ }
715
+
465
716
  // src/ui/MessageList.tsx
466
717
  var import_ink = require("ink");
467
718
 
@@ -582,11 +833,11 @@ function StatusBar({
582
833
  }
583
834
 
584
835
  // src/ui/InputArea.tsx
585
- var import_react3 = __toESM(require("react"), 1);
836
+ var import_react8 = __toESM(require("react"), 1);
586
837
  var import_ink6 = require("ink");
587
838
 
588
839
  // src/ui/CjkTextInput.tsx
589
- var import_react = require("react");
840
+ var import_react6 = require("react");
590
841
  var import_ink3 = require("ink");
591
842
  var import_chalk = __toESM(require("chalk"), 1);
592
843
  var import_jsx_runtime3 = require("react/jsx-runtime");
@@ -606,9 +857,9 @@ function CjkTextInput({
606
857
  focus = true,
607
858
  showCursor = true
608
859
  }) {
609
- const valueRef = (0, import_react.useRef)(value);
610
- const cursorRef = (0, import_react.useRef)(value.length);
611
- const [, forceRender] = (0, import_react.useState)(0);
860
+ const valueRef = (0, import_react6.useRef)(value);
861
+ const cursorRef = (0, import_react6.useRef)(value.length);
862
+ const [, forceRender] = (0, import_react6.useState)(0);
612
863
  if (value !== valueRef.current) {
613
864
  valueRef.current = value;
614
865
  if (cursorRef.current > value.length) {
@@ -685,15 +936,15 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
685
936
  }
686
937
 
687
938
  // src/ui/WaveText.tsx
688
- var import_react2 = require("react");
939
+ var import_react7 = require("react");
689
940
  var import_ink4 = require("ink");
690
941
  var import_jsx_runtime4 = require("react/jsx-runtime");
691
942
  var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
692
943
  var INTERVAL_MS = 400;
693
944
  var CHARS_PER_GROUP = 4;
694
945
  function WaveText({ text }) {
695
- const [tick, setTick] = (0, import_react2.useState)(0);
696
- (0, import_react2.useEffect)(() => {
946
+ const [tick, setTick] = (0, import_react7.useState)(0);
947
+ (0, import_react7.useEffect)(() => {
697
948
  const timer = setInterval(() => {
698
949
  setTick((prev) => prev + 1);
699
950
  }, INTERVAL_MS);
@@ -759,16 +1010,16 @@ function parseSlashInput(value) {
759
1010
  return { isSlash: true, parentCommand: parent, filter: rest };
760
1011
  }
761
1012
  function useAutocomplete(value, registry) {
762
- const [selectedIndex, setSelectedIndex] = (0, import_react3.useState)(0);
763
- const [dismissed, setDismissed] = (0, import_react3.useState)(false);
764
- const prevValueRef = import_react3.default.useRef(value);
1013
+ const [selectedIndex, setSelectedIndex] = (0, import_react8.useState)(0);
1014
+ const [dismissed, setDismissed] = (0, import_react8.useState)(false);
1015
+ const prevValueRef = import_react8.default.useRef(value);
765
1016
  if (prevValueRef.current !== value) {
766
1017
  prevValueRef.current = value;
767
1018
  if (dismissed) setDismissed(false);
768
1019
  }
769
1020
  const parsed = parseSlashInput(value);
770
1021
  const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
771
- const filteredCommands = (0, import_react3.useMemo)(() => {
1022
+ const filteredCommands = (0, import_react8.useMemo)(() => {
772
1023
  if (!registry || !parsed.isSlash || dismissed) return [];
773
1024
  if (isSubcommandMode) {
774
1025
  const subs = registry.getSubcommands(parsed.parentCommand);
@@ -802,7 +1053,7 @@ function useAutocomplete(value, registry) {
802
1053
  };
803
1054
  }
804
1055
  function InputArea({ onSubmit, isDisabled, registry }) {
805
- const [value, setValue] = (0, import_react3.useState)("");
1056
+ const [value, setValue] = (0, import_react8.useState)("");
806
1057
  const {
807
1058
  showPopup,
808
1059
  filteredCommands,
@@ -811,7 +1062,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
811
1062
  isSubcommandMode,
812
1063
  setShowPopup
813
1064
  } = useAutocomplete(value, registry);
814
- const handleSubmit = (0, import_react3.useCallback)(
1065
+ const handleSubmit = (0, import_react8.useCallback)(
815
1066
  (text) => {
816
1067
  const trimmed = text.trim();
817
1068
  if (trimmed.length === 0) return;
@@ -824,7 +1075,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
824
1075
  },
825
1076
  [showPopup, filteredCommands, selectedIndex, onSubmit]
826
1077
  );
827
- const selectCommand = (0, import_react3.useCallback)(
1078
+ const selectCommand = (0, import_react8.useCallback)(
828
1079
  (cmd) => {
829
1080
  const parsed = parseSlashInput(value);
830
1081
  if (parsed.parentCommand) {
@@ -885,7 +1136,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
885
1136
  }
886
1137
 
887
1138
  // src/ui/ConfirmPrompt.tsx
888
- var import_react4 = require("react");
1139
+ var import_react9 = require("react");
889
1140
  var import_ink7 = require("ink");
890
1141
  var import_jsx_runtime7 = require("react/jsx-runtime");
891
1142
  function ConfirmPrompt({
@@ -893,9 +1144,9 @@ function ConfirmPrompt({
893
1144
  options = ["Yes", "No"],
894
1145
  onSelect
895
1146
  }) {
896
- const [selected, setSelected] = (0, import_react4.useState)(0);
897
- const resolvedRef = (0, import_react4.useRef)(false);
898
- const doSelect = (0, import_react4.useCallback)(
1147
+ const [selected, setSelected] = (0, import_react9.useState)(0);
1148
+ const resolvedRef = (0, import_react9.useRef)(false);
1149
+ const doSelect = (0, import_react9.useCallback)(
899
1150
  (index) => {
900
1151
  if (resolvedRef.current) return;
901
1152
  resolvedRef.current = true;
@@ -928,7 +1179,7 @@ function ConfirmPrompt({
928
1179
  }
929
1180
 
930
1181
  // src/ui/PermissionPrompt.tsx
931
- var import_react5 = __toESM(require("react"), 1);
1182
+ var import_react10 = __toESM(require("react"), 1);
932
1183
  var import_ink8 = require("ink");
933
1184
  var import_jsx_runtime8 = require("react/jsx-runtime");
934
1185
  var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
@@ -938,15 +1189,15 @@ function formatArgs(args) {
938
1189
  return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
939
1190
  }
940
1191
  function PermissionPrompt({ request }) {
941
- const [selected, setSelected] = import_react5.default.useState(0);
942
- const resolvedRef = import_react5.default.useRef(false);
943
- const prevRequestRef = import_react5.default.useRef(request);
1192
+ const [selected, setSelected] = import_react10.default.useState(0);
1193
+ const resolvedRef = import_react10.default.useRef(false);
1194
+ const prevRequestRef = import_react10.default.useRef(request);
944
1195
  if (prevRequestRef.current !== request) {
945
1196
  prevRequestRef.current = request;
946
1197
  resolvedRef.current = false;
947
1198
  setSelected(0);
948
1199
  }
949
- const doResolve = import_react5.default.useCallback(
1200
+ const doResolve = import_react10.default.useCallback(
950
1201
  (index) => {
951
1202
  if (resolvedRef.current) return;
952
1203
  resolvedRef.current = true;
@@ -991,33 +1242,6 @@ function PermissionPrompt({ request }) {
991
1242
  ] });
992
1243
  }
993
1244
 
994
- // src/utils/tool-call-extractor.ts
995
- var TOOL_ARG_MAX_LENGTH = 80;
996
- var TOOL_ARG_TRUNCATE_LENGTH = 77;
997
- function extractToolCalls(history, startIndex) {
998
- const lines = [];
999
- for (let i = startIndex; i < history.length; i++) {
1000
- const msg = history[i];
1001
- if (msg.role === "assistant" && msg.toolCalls) {
1002
- for (const tc of msg.toolCalls) {
1003
- const value = parseFirstArgValue(tc.function.arguments);
1004
- const truncated = value.length > TOOL_ARG_MAX_LENGTH ? value.slice(0, TOOL_ARG_TRUNCATE_LENGTH) + "..." : value;
1005
- lines.push(`${tc.function.name}(${truncated})`);
1006
- }
1007
- }
1008
- }
1009
- return lines;
1010
- }
1011
- function parseFirstArgValue(argsJson) {
1012
- try {
1013
- const parsed = JSON.parse(argsJson);
1014
- const firstVal = Object.values(parsed)[0];
1015
- return typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
1016
- } catch {
1017
- return argsJson;
1018
- }
1019
- }
1020
-
1021
1245
  // src/ui/StreamingIndicator.tsx
1022
1246
  var import_ink9 = require("ink");
1023
1247
  var import_jsx_runtime9 = require("react/jsx-runtime");
@@ -1051,240 +1275,21 @@ function StreamingIndicator({ text, activeTools }) {
1051
1275
 
1052
1276
  // src/ui/App.tsx
1053
1277
  var import_jsx_runtime10 = require("react/jsx-runtime");
1054
- var msgIdCounter = 0;
1055
- function nextId() {
1056
- msgIdCounter += 1;
1057
- return `msg_${msgIdCounter}`;
1058
- }
1059
- var NOOP_TERMINAL = {
1060
- write: () => {
1061
- },
1062
- writeLine: () => {
1063
- },
1064
- writeMarkdown: () => {
1065
- },
1066
- writeError: () => {
1067
- },
1068
- prompt: () => Promise.resolve(""),
1069
- select: () => Promise.resolve(0),
1070
- spinner: () => ({ stop: () => {
1071
- }, update: () => {
1072
- } })
1073
- };
1074
- function useSession(props) {
1075
- const [permissionRequest, setPermissionRequest] = (0, import_react6.useState)(null);
1076
- const [streamingText, setStreamingText] = (0, import_react6.useState)("");
1077
- const [activeTools, setActiveTools] = (0, import_react6.useState)([]);
1078
- const permissionQueueRef = (0, import_react6.useRef)([]);
1079
- const processingRef = (0, import_react6.useRef)(false);
1080
- const processNextPermission = (0, import_react6.useCallback)(() => {
1081
- if (processingRef.current) return;
1082
- const next = permissionQueueRef.current[0];
1083
- if (!next) {
1084
- setPermissionRequest(null);
1085
- return;
1086
- }
1087
- processingRef.current = true;
1088
- setPermissionRequest({
1089
- toolName: next.toolName,
1090
- toolArgs: next.toolArgs,
1091
- resolve: (result) => {
1092
- permissionQueueRef.current.shift();
1093
- processingRef.current = false;
1094
- setPermissionRequest(null);
1095
- next.resolve(result);
1096
- setTimeout(() => processNextPermission(), 0);
1097
- }
1098
- });
1099
- }, []);
1100
- const sessionRef = (0, import_react6.useRef)(null);
1101
- if (sessionRef.current === null) {
1102
- const permissionHandler = (toolName, toolArgs) => {
1103
- return new Promise((resolve) => {
1104
- permissionQueueRef.current.push({ toolName, toolArgs, resolve });
1105
- processNextPermission();
1106
- });
1107
- };
1108
- const onTextDelta = (delta) => {
1109
- setStreamingText((prev) => prev + delta);
1110
- };
1111
- const TOOL_ARG_DISPLAY_MAX = 80;
1112
- const TOOL_ARG_TRUNCATE_AT = 77;
1113
- const onToolExecution = (event) => {
1114
- if (event.type === "start") {
1115
- let firstArg = "";
1116
- if (event.toolArgs) {
1117
- const firstVal = Object.values(event.toolArgs)[0];
1118
- const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
1119
- firstArg = raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_TRUNCATE_AT) + "..." : raw;
1120
- }
1121
- setActiveTools((prev) => [...prev, { toolName: event.toolName, firstArg, isRunning: true }]);
1122
- } else {
1123
- setActiveTools(
1124
- (prev) => prev.map(
1125
- (t) => t.toolName === event.toolName && t.isRunning ? { ...t, isRunning: false } : t
1126
- )
1127
- );
1128
- }
1129
- };
1130
- const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
1131
- sessionRef.current = (0, import_agent_sdk.createSession)({
1132
- config: props.config,
1133
- context: props.context,
1134
- terminal: NOOP_TERMINAL,
1135
- sessionLogger: new import_agent_sdk.FileSessionLogger(paths.logs),
1136
- projectInfo: props.projectInfo,
1137
- sessionStore: props.sessionStore,
1138
- permissionMode: props.permissionMode,
1139
- maxTurns: props.maxTurns,
1140
- permissionHandler,
1141
- onTextDelta,
1142
- onToolExecution
1143
- });
1144
- }
1145
- const clearStreamingText = (0, import_react6.useCallback)(() => {
1146
- setStreamingText("");
1147
- setActiveTools([]);
1148
- }, []);
1149
- return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText, activeTools };
1150
- }
1151
- function useMessages() {
1152
- const [messages, setMessages] = (0, import_react6.useState)([]);
1153
- const addMessage = (0, import_react6.useCallback)((msg) => {
1154
- setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
1155
- }, []);
1156
- return { messages, setMessages, addMessage };
1157
- }
1158
- var EXIT_DELAY_MS = 500;
1159
- function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
1160
- return (0, import_react6.useCallback)(
1161
- async (input) => {
1162
- const parts = input.slice(1).split(/\s+/);
1163
- const cmd = parts[0]?.toLowerCase() ?? "";
1164
- const args = parts.slice(1).join(" ");
1165
- const clearMessages = () => setMessages([]);
1166
- const result = await executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry);
1167
- if (result.pendingModelId) {
1168
- pendingModelChangeRef.current = result.pendingModelId;
1169
- setPendingModelId(result.pendingModelId);
1170
- }
1171
- if (result.exitRequested) {
1172
- setTimeout(() => exit(), EXIT_DELAY_MS);
1173
- }
1174
- return result.handled;
1175
- },
1176
- [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
1177
- );
1178
- }
1179
- function syncContextState(session, setter) {
1180
- const ctx = session.getContextState();
1181
- setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
1182
- }
1183
- async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState) {
1184
- setIsThinking(true);
1185
- clearStreamingText();
1186
- const historyBefore = session.getHistory().length;
1187
- try {
1188
- const response = await session.run(prompt);
1189
- clearStreamingText();
1190
- const history = session.getHistory();
1191
- const toolLines = extractToolCalls(
1192
- history,
1193
- historyBefore
1194
- );
1195
- if (toolLines.length > 0) {
1196
- addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
1197
- }
1198
- addMessage({ role: "assistant", content: response || "(empty response)" });
1199
- syncContextState(session, setContextState);
1200
- } catch (err) {
1201
- clearStreamingText();
1202
- if (err instanceof DOMException && err.name === "AbortError") {
1203
- addMessage({ role: "system", content: "Cancelled." });
1204
- } else {
1205
- const errMsg = err instanceof Error ? err.message : String(err);
1206
- addMessage({ role: "system", content: `Error: ${errMsg}` });
1207
- }
1208
- } finally {
1209
- setIsThinking(false);
1210
- }
1211
- }
1212
- function buildSkillPrompt(input, registry) {
1213
- const parts = input.slice(1).split(/\s+/);
1214
- const cmd = parts[0]?.toLowerCase() ?? "";
1215
- const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
1216
- if (!skillCmd) return null;
1217
- const args = parts.slice(1).join(" ").trim();
1218
- const userInstruction = args || skillCmd.description;
1219
- if (skillCmd.skillContent) {
1220
- return `<skill name="${cmd}">
1221
- ${skillCmd.skillContent}
1222
- </skill>
1223
-
1224
- Execute the "${cmd}" skill: ${userInstruction}`;
1225
- }
1226
- return `Use the "${cmd}" skill: ${userInstruction}`;
1227
- }
1228
- function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
1229
- return (0, import_react6.useCallback)(
1230
- async (input) => {
1231
- if (input.startsWith("/")) {
1232
- const handled = await handleSlashCommand(input);
1233
- if (handled) {
1234
- syncContextState(session, setContextState);
1235
- return;
1236
- }
1237
- const prompt = buildSkillPrompt(input, registry);
1238
- if (!prompt) return;
1239
- return runSessionPrompt(
1240
- prompt,
1241
- session,
1242
- addMessage,
1243
- clearStreamingText,
1244
- setIsThinking,
1245
- setContextState
1246
- );
1247
- }
1248
- addMessage({ role: "user", content: input });
1249
- return runSessionPrompt(
1250
- input,
1251
- session,
1252
- addMessage,
1253
- clearStreamingText,
1254
- setIsThinking,
1255
- setContextState
1256
- );
1257
- },
1258
- [
1259
- session,
1260
- addMessage,
1261
- handleSlashCommand,
1262
- clearStreamingText,
1263
- setIsThinking,
1264
- setContextState,
1265
- registry
1266
- ]
1267
- );
1268
- }
1269
- function useCommandRegistry(cwd) {
1270
- const registryRef = (0, import_react6.useRef)(null);
1271
- if (registryRef.current === null) {
1272
- const registry = new CommandRegistry();
1273
- registry.addSource(new BuiltinCommandSource());
1274
- registry.addSource(new SkillCommandSource(cwd));
1275
- registryRef.current = registry;
1276
- }
1277
- return registryRef.current;
1278
- }
1278
+ var EXIT_DELAY_MS2 = 500;
1279
1279
  function App(props) {
1280
1280
  const { exit } = (0, import_ink10.useApp)();
1281
1281
  const { session, permissionRequest, streamingText, clearStreamingText, activeTools } = useSession(props);
1282
1282
  const { messages, setMessages, addMessage } = useMessages();
1283
- const [isThinking, setIsThinking] = (0, import_react6.useState)(false);
1284
- const [contextState, setContextState] = (0, import_react6.useState)({ percentage: 0, usedTokens: 0, maxTokens: 0 });
1283
+ const [isThinking, setIsThinking] = (0, import_react11.useState)(false);
1284
+ const initialCtx = session.getContextState();
1285
+ const [contextState, setContextState] = (0, import_react11.useState)({
1286
+ percentage: initialCtx.usedPercentage,
1287
+ usedTokens: initialCtx.usedTokens,
1288
+ maxTokens: initialCtx.maxTokens
1289
+ });
1285
1290
  const registry = useCommandRegistry(props.cwd ?? process.cwd());
1286
- const pendingModelChangeRef = (0, import_react6.useRef)(null);
1287
- const [pendingModelId, setPendingModelId] = (0, import_react6.useState)(null);
1291
+ const pendingModelChangeRef = (0, import_react11.useRef)(null);
1292
+ const [pendingModelId, setPendingModelId] = (0, import_react11.useState)(null);
1288
1293
  const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId);
1289
1294
  const handleSubmit = useSubmitHandler(
1290
1295
  session,
@@ -1333,7 +1338,7 @@ function App(props) {
1333
1338
  const settingsPath = getUserSettingsPath();
1334
1339
  updateModelInSettings(settingsPath, pendingModelId);
1335
1340
  addMessage({ role: "system", content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...` });
1336
- setTimeout(() => exit(), 500);
1341
+ setTimeout(() => exit(), EXIT_DELAY_MS2);
1337
1342
  } catch (err) {
1338
1343
  addMessage({ role: "system", content: `Failed: ${err instanceof Error ? err.message : String(err)}` });
1339
1344
  }