@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.
package/dist/node/bin.cjs CHANGED
@@ -164,8 +164,123 @@ var PrintTerminal = class {
164
164
  var import_ink11 = require("ink");
165
165
 
166
166
  // src/ui/App.tsx
167
- var import_react6 = require("react");
167
+ var import_react11 = require("react");
168
168
  var import_ink10 = require("ink");
169
+ var import_agent_core3 = require("@robota-sdk/agent-core");
170
+
171
+ // src/ui/hooks/useSession.ts
172
+ var import_react = require("react");
173
+ var import_agent_sdk = require("@robota-sdk/agent-sdk");
174
+ var TOOL_ARG_DISPLAY_MAX = 80;
175
+ var TOOL_ARG_TRUNCATE_AT = 77;
176
+ var NOOP_TERMINAL = {
177
+ write: () => {
178
+ },
179
+ writeLine: () => {
180
+ },
181
+ writeMarkdown: () => {
182
+ },
183
+ writeError: () => {
184
+ },
185
+ prompt: () => Promise.resolve(""),
186
+ select: () => Promise.resolve(0),
187
+ spinner: () => ({ stop: () => {
188
+ }, update: () => {
189
+ } })
190
+ };
191
+ function useSession(props) {
192
+ const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
193
+ const [streamingText, setStreamingText] = (0, import_react.useState)("");
194
+ const [activeTools, setActiveTools] = (0, import_react.useState)([]);
195
+ const permissionQueueRef = (0, import_react.useRef)([]);
196
+ const processingRef = (0, import_react.useRef)(false);
197
+ const processNextPermission = (0, import_react.useCallback)(() => {
198
+ if (processingRef.current) return;
199
+ const next = permissionQueueRef.current[0];
200
+ if (!next) {
201
+ setPermissionRequest(null);
202
+ return;
203
+ }
204
+ processingRef.current = true;
205
+ setPermissionRequest({
206
+ toolName: next.toolName,
207
+ toolArgs: next.toolArgs,
208
+ resolve: (result) => {
209
+ permissionQueueRef.current.shift();
210
+ processingRef.current = false;
211
+ setPermissionRequest(null);
212
+ next.resolve(result);
213
+ setTimeout(() => processNextPermission(), 0);
214
+ }
215
+ });
216
+ }, []);
217
+ const sessionRef = (0, import_react.useRef)(null);
218
+ if (sessionRef.current === null) {
219
+ const permissionHandler = (toolName, toolArgs) => {
220
+ return new Promise((resolve) => {
221
+ permissionQueueRef.current.push({ toolName, toolArgs, resolve });
222
+ processNextPermission();
223
+ });
224
+ };
225
+ const onTextDelta = (delta) => {
226
+ setStreamingText((prev) => prev + delta);
227
+ };
228
+ const onToolExecution = (event) => {
229
+ if (event.type === "start") {
230
+ let firstArg = "";
231
+ if (event.toolArgs) {
232
+ const firstVal = Object.values(event.toolArgs)[0];
233
+ const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
234
+ firstArg = raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_TRUNCATE_AT) + "..." : raw;
235
+ }
236
+ setActiveTools((prev) => [...prev, { toolName: event.toolName, firstArg, isRunning: true }]);
237
+ } else {
238
+ setActiveTools(
239
+ (prev) => prev.map(
240
+ (t) => t.toolName === event.toolName && t.isRunning ? { ...t, isRunning: false } : t
241
+ )
242
+ );
243
+ }
244
+ };
245
+ const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
246
+ sessionRef.current = (0, import_agent_sdk.createSession)({
247
+ config: props.config,
248
+ context: props.context,
249
+ terminal: NOOP_TERMINAL,
250
+ sessionLogger: new import_agent_sdk.FileSessionLogger(paths.logs),
251
+ projectInfo: props.projectInfo,
252
+ sessionStore: props.sessionStore,
253
+ permissionMode: props.permissionMode,
254
+ maxTurns: props.maxTurns,
255
+ permissionHandler,
256
+ onTextDelta,
257
+ onToolExecution
258
+ });
259
+ }
260
+ const clearStreamingText = (0, import_react.useCallback)(() => {
261
+ setStreamingText("");
262
+ setActiveTools([]);
263
+ }, []);
264
+ return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText, activeTools };
265
+ }
266
+
267
+ // src/ui/hooks/useMessages.ts
268
+ var import_react2 = require("react");
269
+ var msgIdCounter = 0;
270
+ function nextId() {
271
+ msgIdCounter += 1;
272
+ return `msg_${msgIdCounter}`;
273
+ }
274
+ function useMessages() {
275
+ const [messages, setMessages] = (0, import_react2.useState)([]);
276
+ const addMessage = (0, import_react2.useCallback)((msg) => {
277
+ setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
278
+ }, []);
279
+ return { messages, setMessages, addMessage };
280
+ }
281
+
282
+ // src/ui/hooks/useSlashCommands.ts
283
+ var import_react3 = require("react");
169
284
 
170
285
  // src/commands/slash-executor.ts
171
286
  var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
@@ -290,9 +405,133 @@ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages
290
405
  }
291
406
  }
292
407
 
293
- // src/ui/App.tsx
294
- var import_agent_sdk = require("@robota-sdk/agent-sdk");
295
- var import_agent_core3 = require("@robota-sdk/agent-core");
408
+ // src/ui/hooks/useSlashCommands.ts
409
+ var EXIT_DELAY_MS = 500;
410
+ function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
411
+ return (0, import_react3.useCallback)(
412
+ async (input) => {
413
+ const parts = input.slice(1).split(/\s+/);
414
+ const cmd = parts[0]?.toLowerCase() ?? "";
415
+ const args = parts.slice(1).join(" ");
416
+ const clearMessages = () => setMessages([]);
417
+ const result = await executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry);
418
+ if (result.pendingModelId) {
419
+ pendingModelChangeRef.current = result.pendingModelId;
420
+ setPendingModelId(result.pendingModelId);
421
+ }
422
+ if (result.exitRequested) {
423
+ setTimeout(() => exit(), EXIT_DELAY_MS);
424
+ }
425
+ return result.handled;
426
+ },
427
+ [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
428
+ );
429
+ }
430
+
431
+ // src/ui/hooks/useSubmitHandler.ts
432
+ var import_react4 = require("react");
433
+
434
+ // src/utils/tool-call-extractor.ts
435
+ var TOOL_ARG_MAX_LENGTH = 80;
436
+ var TOOL_ARG_TRUNCATE_LENGTH = 77;
437
+ function extractToolCalls(history, startIndex) {
438
+ const lines = [];
439
+ for (let i = startIndex; i < history.length; i++) {
440
+ const msg = history[i];
441
+ if (msg.role === "assistant" && msg.toolCalls) {
442
+ for (const tc of msg.toolCalls) {
443
+ const value = parseFirstArgValue(tc.function.arguments);
444
+ const truncated = value.length > TOOL_ARG_MAX_LENGTH ? value.slice(0, TOOL_ARG_TRUNCATE_LENGTH) + "..." : value;
445
+ lines.push(`${tc.function.name}(${truncated})`);
446
+ }
447
+ }
448
+ }
449
+ return lines;
450
+ }
451
+ function parseFirstArgValue(argsJson) {
452
+ try {
453
+ const parsed = JSON.parse(argsJson);
454
+ const firstVal = Object.values(parsed)[0];
455
+ return typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
456
+ } catch {
457
+ return argsJson;
458
+ }
459
+ }
460
+
461
+ // src/utils/skill-prompt.ts
462
+ function buildSkillPrompt(input, registry) {
463
+ const parts = input.slice(1).split(/\s+/);
464
+ const cmd = parts[0]?.toLowerCase() ?? "";
465
+ const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
466
+ if (!skillCmd) return null;
467
+ const args = parts.slice(1).join(" ").trim();
468
+ const userInstruction = args || skillCmd.description;
469
+ if (skillCmd.skillContent) {
470
+ return `<skill name="${cmd}">
471
+ ${skillCmd.skillContent}
472
+ </skill>
473
+
474
+ Execute the "${cmd}" skill: ${userInstruction}`;
475
+ }
476
+ return `Use the "${cmd}" skill: ${userInstruction}`;
477
+ }
478
+
479
+ // src/ui/hooks/useSubmitHandler.ts
480
+ function syncContextState(session, setter) {
481
+ const ctx = session.getContextState();
482
+ setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
483
+ }
484
+ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState) {
485
+ setIsThinking(true);
486
+ clearStreamingText();
487
+ const historyBefore = session.getHistory().length;
488
+ try {
489
+ const response = await session.run(prompt);
490
+ clearStreamingText();
491
+ const history = session.getHistory();
492
+ const toolLines = extractToolCalls(
493
+ history,
494
+ historyBefore
495
+ );
496
+ if (toolLines.length > 0) {
497
+ addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
498
+ }
499
+ addMessage({ role: "assistant", content: response || "(empty response)" });
500
+ syncContextState(session, setContextState);
501
+ } catch (err) {
502
+ clearStreamingText();
503
+ if (err instanceof DOMException && err.name === "AbortError") {
504
+ addMessage({ role: "system", content: "Cancelled." });
505
+ } else {
506
+ const errMsg = err instanceof Error ? err.message : String(err);
507
+ addMessage({ role: "system", content: `Error: ${errMsg}` });
508
+ }
509
+ } finally {
510
+ setIsThinking(false);
511
+ }
512
+ }
513
+ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
514
+ return (0, import_react4.useCallback)(
515
+ async (input) => {
516
+ if (input.startsWith("/")) {
517
+ const handled = await handleSlashCommand(input);
518
+ if (handled) {
519
+ syncContextState(session, setContextState);
520
+ return;
521
+ }
522
+ const prompt = buildSkillPrompt(input, registry);
523
+ if (!prompt) return;
524
+ return runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState);
525
+ }
526
+ addMessage({ role: "user", content: input });
527
+ return runSessionPrompt(input, session, addMessage, clearStreamingText, setIsThinking, setContextState);
528
+ },
529
+ [session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry]
530
+ );
531
+ }
532
+
533
+ // src/ui/hooks/useCommandRegistry.ts
534
+ var import_react5 = require("react");
296
535
 
297
536
  // src/commands/command-registry.ts
298
537
  var CommandRegistry = class {
@@ -446,6 +685,18 @@ var SkillCommandSource = class {
446
685
  }
447
686
  };
448
687
 
688
+ // src/ui/hooks/useCommandRegistry.ts
689
+ function useCommandRegistry(cwd) {
690
+ const registryRef = (0, import_react5.useRef)(null);
691
+ if (registryRef.current === null) {
692
+ const registry = new CommandRegistry();
693
+ registry.addSource(new BuiltinCommandSource());
694
+ registry.addSource(new SkillCommandSource(cwd));
695
+ registryRef.current = registry;
696
+ }
697
+ return registryRef.current;
698
+ }
699
+
449
700
  // src/ui/MessageList.tsx
450
701
  var import_ink = require("ink");
451
702
 
@@ -566,11 +817,11 @@ function StatusBar({
566
817
  }
567
818
 
568
819
  // src/ui/InputArea.tsx
569
- var import_react3 = __toESM(require("react"), 1);
820
+ var import_react8 = __toESM(require("react"), 1);
570
821
  var import_ink6 = require("ink");
571
822
 
572
823
  // src/ui/CjkTextInput.tsx
573
- var import_react = require("react");
824
+ var import_react6 = require("react");
574
825
  var import_ink3 = require("ink");
575
826
  var import_chalk = __toESM(require("chalk"), 1);
576
827
  var import_jsx_runtime3 = require("react/jsx-runtime");
@@ -590,9 +841,9 @@ function CjkTextInput({
590
841
  focus = true,
591
842
  showCursor = true
592
843
  }) {
593
- const valueRef = (0, import_react.useRef)(value);
594
- const cursorRef = (0, import_react.useRef)(value.length);
595
- const [, forceRender] = (0, import_react.useState)(0);
844
+ const valueRef = (0, import_react6.useRef)(value);
845
+ const cursorRef = (0, import_react6.useRef)(value.length);
846
+ const [, forceRender] = (0, import_react6.useState)(0);
596
847
  if (value !== valueRef.current) {
597
848
  valueRef.current = value;
598
849
  if (cursorRef.current > value.length) {
@@ -669,15 +920,15 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
669
920
  }
670
921
 
671
922
  // src/ui/WaveText.tsx
672
- var import_react2 = require("react");
923
+ var import_react7 = require("react");
673
924
  var import_ink4 = require("ink");
674
925
  var import_jsx_runtime4 = require("react/jsx-runtime");
675
926
  var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
676
927
  var INTERVAL_MS = 400;
677
928
  var CHARS_PER_GROUP = 4;
678
929
  function WaveText({ text }) {
679
- const [tick, setTick] = (0, import_react2.useState)(0);
680
- (0, import_react2.useEffect)(() => {
930
+ const [tick, setTick] = (0, import_react7.useState)(0);
931
+ (0, import_react7.useEffect)(() => {
681
932
  const timer = setInterval(() => {
682
933
  setTick((prev) => prev + 1);
683
934
  }, INTERVAL_MS);
@@ -743,16 +994,16 @@ function parseSlashInput(value) {
743
994
  return { isSlash: true, parentCommand: parent, filter: rest };
744
995
  }
745
996
  function useAutocomplete(value, registry) {
746
- const [selectedIndex, setSelectedIndex] = (0, import_react3.useState)(0);
747
- const [dismissed, setDismissed] = (0, import_react3.useState)(false);
748
- const prevValueRef = import_react3.default.useRef(value);
997
+ const [selectedIndex, setSelectedIndex] = (0, import_react8.useState)(0);
998
+ const [dismissed, setDismissed] = (0, import_react8.useState)(false);
999
+ const prevValueRef = import_react8.default.useRef(value);
749
1000
  if (prevValueRef.current !== value) {
750
1001
  prevValueRef.current = value;
751
1002
  if (dismissed) setDismissed(false);
752
1003
  }
753
1004
  const parsed = parseSlashInput(value);
754
1005
  const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
755
- const filteredCommands = (0, import_react3.useMemo)(() => {
1006
+ const filteredCommands = (0, import_react8.useMemo)(() => {
756
1007
  if (!registry || !parsed.isSlash || dismissed) return [];
757
1008
  if (isSubcommandMode) {
758
1009
  const subs = registry.getSubcommands(parsed.parentCommand);
@@ -786,7 +1037,7 @@ function useAutocomplete(value, registry) {
786
1037
  };
787
1038
  }
788
1039
  function InputArea({ onSubmit, isDisabled, registry }) {
789
- const [value, setValue] = (0, import_react3.useState)("");
1040
+ const [value, setValue] = (0, import_react8.useState)("");
790
1041
  const {
791
1042
  showPopup,
792
1043
  filteredCommands,
@@ -795,7 +1046,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
795
1046
  isSubcommandMode,
796
1047
  setShowPopup
797
1048
  } = useAutocomplete(value, registry);
798
- const handleSubmit = (0, import_react3.useCallback)(
1049
+ const handleSubmit = (0, import_react8.useCallback)(
799
1050
  (text) => {
800
1051
  const trimmed = text.trim();
801
1052
  if (trimmed.length === 0) return;
@@ -808,7 +1059,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
808
1059
  },
809
1060
  [showPopup, filteredCommands, selectedIndex, onSubmit]
810
1061
  );
811
- const selectCommand = (0, import_react3.useCallback)(
1062
+ const selectCommand = (0, import_react8.useCallback)(
812
1063
  (cmd) => {
813
1064
  const parsed = parseSlashInput(value);
814
1065
  if (parsed.parentCommand) {
@@ -869,7 +1120,7 @@ function InputArea({ onSubmit, isDisabled, registry }) {
869
1120
  }
870
1121
 
871
1122
  // src/ui/ConfirmPrompt.tsx
872
- var import_react4 = require("react");
1123
+ var import_react9 = require("react");
873
1124
  var import_ink7 = require("ink");
874
1125
  var import_jsx_runtime7 = require("react/jsx-runtime");
875
1126
  function ConfirmPrompt({
@@ -877,9 +1128,9 @@ function ConfirmPrompt({
877
1128
  options = ["Yes", "No"],
878
1129
  onSelect
879
1130
  }) {
880
- const [selected, setSelected] = (0, import_react4.useState)(0);
881
- const resolvedRef = (0, import_react4.useRef)(false);
882
- const doSelect = (0, import_react4.useCallback)(
1131
+ const [selected, setSelected] = (0, import_react9.useState)(0);
1132
+ const resolvedRef = (0, import_react9.useRef)(false);
1133
+ const doSelect = (0, import_react9.useCallback)(
883
1134
  (index) => {
884
1135
  if (resolvedRef.current) return;
885
1136
  resolvedRef.current = true;
@@ -912,7 +1163,7 @@ function ConfirmPrompt({
912
1163
  }
913
1164
 
914
1165
  // src/ui/PermissionPrompt.tsx
915
- var import_react5 = __toESM(require("react"), 1);
1166
+ var import_react10 = __toESM(require("react"), 1);
916
1167
  var import_ink8 = require("ink");
917
1168
  var import_jsx_runtime8 = require("react/jsx-runtime");
918
1169
  var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
@@ -922,15 +1173,15 @@ function formatArgs(args) {
922
1173
  return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
923
1174
  }
924
1175
  function PermissionPrompt({ request }) {
925
- const [selected, setSelected] = import_react5.default.useState(0);
926
- const resolvedRef = import_react5.default.useRef(false);
927
- const prevRequestRef = import_react5.default.useRef(request);
1176
+ const [selected, setSelected] = import_react10.default.useState(0);
1177
+ const resolvedRef = import_react10.default.useRef(false);
1178
+ const prevRequestRef = import_react10.default.useRef(request);
928
1179
  if (prevRequestRef.current !== request) {
929
1180
  prevRequestRef.current = request;
930
1181
  resolvedRef.current = false;
931
1182
  setSelected(0);
932
1183
  }
933
- const doResolve = import_react5.default.useCallback(
1184
+ const doResolve = import_react10.default.useCallback(
934
1185
  (index) => {
935
1186
  if (resolvedRef.current) return;
936
1187
  resolvedRef.current = true;
@@ -975,33 +1226,6 @@ function PermissionPrompt({ request }) {
975
1226
  ] });
976
1227
  }
977
1228
 
978
- // src/utils/tool-call-extractor.ts
979
- var TOOL_ARG_MAX_LENGTH = 80;
980
- var TOOL_ARG_TRUNCATE_LENGTH = 77;
981
- function extractToolCalls(history, startIndex) {
982
- const lines = [];
983
- for (let i = startIndex; i < history.length; i++) {
984
- const msg = history[i];
985
- if (msg.role === "assistant" && msg.toolCalls) {
986
- for (const tc of msg.toolCalls) {
987
- const value = parseFirstArgValue(tc.function.arguments);
988
- const truncated = value.length > TOOL_ARG_MAX_LENGTH ? value.slice(0, TOOL_ARG_TRUNCATE_LENGTH) + "..." : value;
989
- lines.push(`${tc.function.name}(${truncated})`);
990
- }
991
- }
992
- }
993
- return lines;
994
- }
995
- function parseFirstArgValue(argsJson) {
996
- try {
997
- const parsed = JSON.parse(argsJson);
998
- const firstVal = Object.values(parsed)[0];
999
- return typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
1000
- } catch {
1001
- return argsJson;
1002
- }
1003
- }
1004
-
1005
1229
  // src/ui/StreamingIndicator.tsx
1006
1230
  var import_ink9 = require("ink");
1007
1231
  var import_jsx_runtime9 = require("react/jsx-runtime");
@@ -1035,240 +1259,21 @@ function StreamingIndicator({ text, activeTools }) {
1035
1259
 
1036
1260
  // src/ui/App.tsx
1037
1261
  var import_jsx_runtime10 = require("react/jsx-runtime");
1038
- var msgIdCounter = 0;
1039
- function nextId() {
1040
- msgIdCounter += 1;
1041
- return `msg_${msgIdCounter}`;
1042
- }
1043
- var NOOP_TERMINAL = {
1044
- write: () => {
1045
- },
1046
- writeLine: () => {
1047
- },
1048
- writeMarkdown: () => {
1049
- },
1050
- writeError: () => {
1051
- },
1052
- prompt: () => Promise.resolve(""),
1053
- select: () => Promise.resolve(0),
1054
- spinner: () => ({ stop: () => {
1055
- }, update: () => {
1056
- } })
1057
- };
1058
- function useSession(props) {
1059
- const [permissionRequest, setPermissionRequest] = (0, import_react6.useState)(null);
1060
- const [streamingText, setStreamingText] = (0, import_react6.useState)("");
1061
- const [activeTools, setActiveTools] = (0, import_react6.useState)([]);
1062
- const permissionQueueRef = (0, import_react6.useRef)([]);
1063
- const processingRef = (0, import_react6.useRef)(false);
1064
- const processNextPermission = (0, import_react6.useCallback)(() => {
1065
- if (processingRef.current) return;
1066
- const next = permissionQueueRef.current[0];
1067
- if (!next) {
1068
- setPermissionRequest(null);
1069
- return;
1070
- }
1071
- processingRef.current = true;
1072
- setPermissionRequest({
1073
- toolName: next.toolName,
1074
- toolArgs: next.toolArgs,
1075
- resolve: (result) => {
1076
- permissionQueueRef.current.shift();
1077
- processingRef.current = false;
1078
- setPermissionRequest(null);
1079
- next.resolve(result);
1080
- setTimeout(() => processNextPermission(), 0);
1081
- }
1082
- });
1083
- }, []);
1084
- const sessionRef = (0, import_react6.useRef)(null);
1085
- if (sessionRef.current === null) {
1086
- const permissionHandler = (toolName, toolArgs) => {
1087
- return new Promise((resolve) => {
1088
- permissionQueueRef.current.push({ toolName, toolArgs, resolve });
1089
- processNextPermission();
1090
- });
1091
- };
1092
- const onTextDelta = (delta) => {
1093
- setStreamingText((prev) => prev + delta);
1094
- };
1095
- const TOOL_ARG_DISPLAY_MAX = 80;
1096
- const TOOL_ARG_TRUNCATE_AT = 77;
1097
- const onToolExecution = (event) => {
1098
- if (event.type === "start") {
1099
- let firstArg = "";
1100
- if (event.toolArgs) {
1101
- const firstVal = Object.values(event.toolArgs)[0];
1102
- const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
1103
- firstArg = raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_TRUNCATE_AT) + "..." : raw;
1104
- }
1105
- setActiveTools((prev) => [...prev, { toolName: event.toolName, firstArg, isRunning: true }]);
1106
- } else {
1107
- setActiveTools(
1108
- (prev) => prev.map(
1109
- (t) => t.toolName === event.toolName && t.isRunning ? { ...t, isRunning: false } : t
1110
- )
1111
- );
1112
- }
1113
- };
1114
- const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
1115
- sessionRef.current = (0, import_agent_sdk.createSession)({
1116
- config: props.config,
1117
- context: props.context,
1118
- terminal: NOOP_TERMINAL,
1119
- sessionLogger: new import_agent_sdk.FileSessionLogger(paths.logs),
1120
- projectInfo: props.projectInfo,
1121
- sessionStore: props.sessionStore,
1122
- permissionMode: props.permissionMode,
1123
- maxTurns: props.maxTurns,
1124
- permissionHandler,
1125
- onTextDelta,
1126
- onToolExecution
1127
- });
1128
- }
1129
- const clearStreamingText = (0, import_react6.useCallback)(() => {
1130
- setStreamingText("");
1131
- setActiveTools([]);
1132
- }, []);
1133
- return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText, activeTools };
1134
- }
1135
- function useMessages() {
1136
- const [messages, setMessages] = (0, import_react6.useState)([]);
1137
- const addMessage = (0, import_react6.useCallback)((msg) => {
1138
- setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
1139
- }, []);
1140
- return { messages, setMessages, addMessage };
1141
- }
1142
- var EXIT_DELAY_MS = 500;
1143
- function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
1144
- return (0, import_react6.useCallback)(
1145
- async (input) => {
1146
- const parts = input.slice(1).split(/\s+/);
1147
- const cmd = parts[0]?.toLowerCase() ?? "";
1148
- const args = parts.slice(1).join(" ");
1149
- const clearMessages = () => setMessages([]);
1150
- const result = await executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry);
1151
- if (result.pendingModelId) {
1152
- pendingModelChangeRef.current = result.pendingModelId;
1153
- setPendingModelId(result.pendingModelId);
1154
- }
1155
- if (result.exitRequested) {
1156
- setTimeout(() => exit(), EXIT_DELAY_MS);
1157
- }
1158
- return result.handled;
1159
- },
1160
- [session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
1161
- );
1162
- }
1163
- function syncContextState(session, setter) {
1164
- const ctx = session.getContextState();
1165
- setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
1166
- }
1167
- async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState) {
1168
- setIsThinking(true);
1169
- clearStreamingText();
1170
- const historyBefore = session.getHistory().length;
1171
- try {
1172
- const response = await session.run(prompt);
1173
- clearStreamingText();
1174
- const history = session.getHistory();
1175
- const toolLines = extractToolCalls(
1176
- history,
1177
- historyBefore
1178
- );
1179
- if (toolLines.length > 0) {
1180
- addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
1181
- }
1182
- addMessage({ role: "assistant", content: response || "(empty response)" });
1183
- syncContextState(session, setContextState);
1184
- } catch (err) {
1185
- clearStreamingText();
1186
- if (err instanceof DOMException && err.name === "AbortError") {
1187
- addMessage({ role: "system", content: "Cancelled." });
1188
- } else {
1189
- const errMsg = err instanceof Error ? err.message : String(err);
1190
- addMessage({ role: "system", content: `Error: ${errMsg}` });
1191
- }
1192
- } finally {
1193
- setIsThinking(false);
1194
- }
1195
- }
1196
- function buildSkillPrompt(input, registry) {
1197
- const parts = input.slice(1).split(/\s+/);
1198
- const cmd = parts[0]?.toLowerCase() ?? "";
1199
- const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
1200
- if (!skillCmd) return null;
1201
- const args = parts.slice(1).join(" ").trim();
1202
- const userInstruction = args || skillCmd.description;
1203
- if (skillCmd.skillContent) {
1204
- return `<skill name="${cmd}">
1205
- ${skillCmd.skillContent}
1206
- </skill>
1207
-
1208
- Execute the "${cmd}" skill: ${userInstruction}`;
1209
- }
1210
- return `Use the "${cmd}" skill: ${userInstruction}`;
1211
- }
1212
- function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
1213
- return (0, import_react6.useCallback)(
1214
- async (input) => {
1215
- if (input.startsWith("/")) {
1216
- const handled = await handleSlashCommand(input);
1217
- if (handled) {
1218
- syncContextState(session, setContextState);
1219
- return;
1220
- }
1221
- const prompt = buildSkillPrompt(input, registry);
1222
- if (!prompt) return;
1223
- return runSessionPrompt(
1224
- prompt,
1225
- session,
1226
- addMessage,
1227
- clearStreamingText,
1228
- setIsThinking,
1229
- setContextState
1230
- );
1231
- }
1232
- addMessage({ role: "user", content: input });
1233
- return runSessionPrompt(
1234
- input,
1235
- session,
1236
- addMessage,
1237
- clearStreamingText,
1238
- setIsThinking,
1239
- setContextState
1240
- );
1241
- },
1242
- [
1243
- session,
1244
- addMessage,
1245
- handleSlashCommand,
1246
- clearStreamingText,
1247
- setIsThinking,
1248
- setContextState,
1249
- registry
1250
- ]
1251
- );
1252
- }
1253
- function useCommandRegistry(cwd) {
1254
- const registryRef = (0, import_react6.useRef)(null);
1255
- if (registryRef.current === null) {
1256
- const registry = new CommandRegistry();
1257
- registry.addSource(new BuiltinCommandSource());
1258
- registry.addSource(new SkillCommandSource(cwd));
1259
- registryRef.current = registry;
1260
- }
1261
- return registryRef.current;
1262
- }
1262
+ var EXIT_DELAY_MS2 = 500;
1263
1263
  function App(props) {
1264
1264
  const { exit } = (0, import_ink10.useApp)();
1265
1265
  const { session, permissionRequest, streamingText, clearStreamingText, activeTools } = useSession(props);
1266
1266
  const { messages, setMessages, addMessage } = useMessages();
1267
- const [isThinking, setIsThinking] = (0, import_react6.useState)(false);
1268
- const [contextState, setContextState] = (0, import_react6.useState)({ percentage: 0, usedTokens: 0, maxTokens: 0 });
1267
+ const [isThinking, setIsThinking] = (0, import_react11.useState)(false);
1268
+ const initialCtx = session.getContextState();
1269
+ const [contextState, setContextState] = (0, import_react11.useState)({
1270
+ percentage: initialCtx.usedPercentage,
1271
+ usedTokens: initialCtx.usedTokens,
1272
+ maxTokens: initialCtx.maxTokens
1273
+ });
1269
1274
  const registry = useCommandRegistry(props.cwd ?? process.cwd());
1270
- const pendingModelChangeRef = (0, import_react6.useRef)(null);
1271
- const [pendingModelId, setPendingModelId] = (0, import_react6.useState)(null);
1275
+ const pendingModelChangeRef = (0, import_react11.useRef)(null);
1276
+ const [pendingModelId, setPendingModelId] = (0, import_react11.useState)(null);
1272
1277
  const handleSlashCommand = useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId);
1273
1278
  const handleSubmit = useSubmitHandler(
1274
1279
  session,
@@ -1317,7 +1322,7 @@ function App(props) {
1317
1322
  const settingsPath = getUserSettingsPath();
1318
1323
  updateModelInSettings(settingsPath, pendingModelId);
1319
1324
  addMessage({ role: "system", content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...` });
1320
- setTimeout(() => exit(), 500);
1325
+ setTimeout(() => exit(), EXIT_DELAY_MS2);
1321
1326
  } catch (err) {
1322
1327
  addMessage({ role: "system", content: `Failed: ${err instanceof Error ? err.message : String(err)}` });
1323
1328
  }