@robota-sdk/agent-cli 3.0.0-beta.45 → 3.0.0-beta.47

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.
@@ -39,6 +39,7 @@ var import_node_fs3 = require("fs");
39
39
  var import_node_path5 = require("path");
40
40
  var import_node_url = require("url");
41
41
  var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
42
+ var import_agent_sessions = require("@robota-sdk/agent-sessions");
42
43
 
43
44
  // src/utils/cli-args.ts
44
45
  var import_node_util = require("util");
@@ -67,12 +68,17 @@ function parseCliArgs() {
67
68
  allowPositionals: true,
68
69
  options: {
69
70
  p: { type: "boolean", short: "p", default: false },
70
- c: { type: "boolean", short: "c", default: false },
71
- r: { type: "string", short: "r" },
71
+ continue: { type: "boolean", short: "c", default: false },
72
+ resume: { type: "string", short: "r" },
72
73
  model: { type: "string" },
73
74
  language: { type: "string" },
74
75
  "permission-mode": { type: "string" },
75
76
  "max-turns": { type: "string" },
77
+ "fork-session": { type: "boolean", default: false },
78
+ name: { type: "string", short: "n" },
79
+ "output-format": { type: "string" },
80
+ "system-prompt": { type: "string" },
81
+ "append-system-prompt": { type: "string" },
76
82
  version: { type: "boolean", default: false },
77
83
  reset: { type: "boolean", default: false }
78
84
  }
@@ -80,12 +86,17 @@ function parseCliArgs() {
80
86
  return {
81
87
  positional: positionals,
82
88
  printMode: values["p"] ?? false,
83
- continueMode: values["c"] ?? false,
84
- resumeId: values["r"],
89
+ continueMode: values["continue"] ?? false,
90
+ resumeId: values["resume"],
85
91
  model: values["model"],
86
92
  language: values["language"],
87
93
  permissionMode: parsePermissionMode(values["permission-mode"]),
88
94
  maxTurns: parseMaxTurns(values["max-turns"]),
95
+ forkSession: values["fork-session"] ?? false,
96
+ sessionName: values["name"],
97
+ outputFormat: values["output-format"],
98
+ systemPrompt: values["system-prompt"],
99
+ appendSystemPrompt: values["append-system-prompt"],
89
100
  version: values["version"] ?? false,
90
101
  reset: values["reset"] ?? false
91
102
  };
@@ -165,12 +176,15 @@ function createProviderFromSettings(cwd, modelOverride) {
165
176
  }
166
177
  }
167
178
 
179
+ // src/cli.ts
180
+ var import_agent_transport_headless = require("@robota-sdk/agent-transport-headless");
181
+
168
182
  // src/ui/render.tsx
169
- var import_ink14 = require("ink");
183
+ var import_ink15 = require("ink");
170
184
 
171
185
  // src/ui/App.tsx
172
- var import_react12 = require("react");
173
- var import_ink13 = require("ink");
186
+ var import_react13 = require("react");
187
+ var import_ink14 = require("ink");
174
188
  var import_agent_core4 = require("@robota-sdk/agent-core");
175
189
 
176
190
  // src/ui/hooks/useInteractiveSession.ts
@@ -286,7 +300,11 @@ function initializeSession(props, permissionHandler) {
286
300
  provider: props.provider,
287
301
  permissionMode: props.permissionMode,
288
302
  maxTurns: props.maxTurns,
289
- permissionHandler
303
+ permissionHandler,
304
+ sessionStore: props.sessionStore,
305
+ resumeSessionId: props.resumeSessionId,
306
+ forkSession: props.forkSession,
307
+ sessionName: props.sessionName
290
308
  });
291
309
  const registry = new import_agent_sdk.CommandRegistry();
292
310
  registry.addSource(new import_agent_sdk.BuiltinCommandSource());
@@ -341,6 +359,12 @@ function useInteractiveSession(props) {
341
359
  }
342
360
  const { interactiveSession, registry, manager } = stateRef.current;
343
361
  manager.onChange = () => forceRender((n) => n + 1);
362
+ if (manager.history.length === 0) {
363
+ const restored = interactiveSession.getFullHistory();
364
+ if (restored.length > 0) {
365
+ manager.syncHistory(restored);
366
+ }
367
+ }
344
368
  (0, import_react.useEffect)(() => {
345
369
  interactiveSession.on("text_delta", manager.onTextDelta);
346
370
  interactiveSession.on("tool_start", manager.onToolStart);
@@ -357,6 +381,10 @@ function useInteractiveSession(props) {
357
381
  usedTokens: ctx.usedTokens,
358
382
  maxTokens: ctx.maxTokens
359
383
  });
384
+ const restored = interactiveSession.getFullHistory();
385
+ if (restored.length > 0) {
386
+ manager.syncHistory(restored);
387
+ }
360
388
  clearInterval(initCheck);
361
389
  } catch {
362
390
  }
@@ -400,6 +428,14 @@ function useInteractiveSession(props) {
400
428
  effects._resetRequested = true;
401
429
  return;
402
430
  }
431
+ if (result.data?.triggerResumePicker) {
432
+ effects._triggerResumePicker = true;
433
+ return;
434
+ }
435
+ if (result.data?.name) {
436
+ effects._sessionName = result.data.name;
437
+ return;
438
+ }
403
439
  const ctx = interactiveSession.getContextState();
404
440
  manager.setContextState({
405
441
  percentage: ctx.usedPercentage,
@@ -803,7 +839,8 @@ function StatusBar({
803
839
  isThinking,
804
840
  contextPercentage,
805
841
  contextUsedTokens,
806
- contextMaxTokens
842
+ contextMaxTokens,
843
+ sessionName
807
844
  }) {
808
845
  const contextColor = getContextColor(contextPercentage);
809
846
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
@@ -819,6 +856,10 @@ function StatusBar({
819
856
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "cyan", bold: true, children: "Mode:" }),
820
857
  " ",
821
858
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { children: permissionMode }),
859
+ sessionName && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
860
+ " | ",
861
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "magenta", children: sessionName })
862
+ ] }),
822
863
  " | ",
823
864
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { dimColor: true, children: modelName }),
824
865
  " | ",
@@ -902,9 +943,7 @@ function CjkTextInput({
902
943
  const pasteBufferRef = (0, import_react4.useRef)("");
903
944
  if (value !== valueRef.current) {
904
945
  valueRef.current = value;
905
- if (cursorRef.current > value.length) {
906
- cursorRef.current = value.length;
907
- }
946
+ cursorRef.current = value.length;
908
947
  }
909
948
  (0, import_ink4.useInput)(
910
949
  (input, key) => {
@@ -1160,7 +1199,8 @@ function InputArea({
1160
1199
  isDisabled,
1161
1200
  isAborting,
1162
1201
  pendingPrompt,
1163
- registry
1202
+ registry,
1203
+ sessionName
1164
1204
  }) {
1165
1205
  const [value, setValue] = (0, import_react6.useState)("");
1166
1206
  const pasteStore = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
@@ -1184,23 +1224,23 @@ function InputArea({
1184
1224
  const label = `[Pasted text #${id} +${lineCount} lines]`;
1185
1225
  setValue((prev) => prev ? `${prev} ${label}` : label);
1186
1226
  }, []);
1187
- const handleSubmit = (0, import_react6.useCallback)(
1188
- (text) => {
1189
- const trimmed = text.trim();
1190
- if (trimmed.length === 0) return;
1191
- if (showPopup && filteredCommands[selectedIndex]) {
1192
- selectCommand(filteredCommands[selectedIndex]);
1227
+ const tabCompleteCommand = (0, import_react6.useCallback)(
1228
+ (cmd) => {
1229
+ const parsed = parseSlashInput(value);
1230
+ if (parsed.parentCommand) {
1231
+ setValue(`/${parsed.parentCommand} ${cmd.name} `);
1193
1232
  return;
1194
1233
  }
1195
- const expanded = expandPasteLabels(trimmed, pasteStore.current);
1196
- setValue("");
1197
- pasteStore.current.clear();
1198
- pasteIdRef.current = 0;
1199
- onSubmit(expanded);
1234
+ if (cmd.subcommands && cmd.subcommands.length > 0) {
1235
+ setValue(`/${cmd.name} `);
1236
+ setSelectedIndex(0);
1237
+ return;
1238
+ }
1239
+ setValue(`/${cmd.name} `);
1200
1240
  },
1201
- [showPopup, filteredCommands, selectedIndex, onSubmit]
1241
+ [value, setSelectedIndex]
1202
1242
  );
1203
- const selectCommand = (0, import_react6.useCallback)(
1243
+ const enterSelectCommand = (0, import_react6.useCallback)(
1204
1244
  (cmd) => {
1205
1245
  const parsed = parseSlashInput(value);
1206
1246
  if (parsed.parentCommand) {
@@ -1219,6 +1259,22 @@ function InputArea({
1219
1259
  },
1220
1260
  [value, onSubmit, setSelectedIndex]
1221
1261
  );
1262
+ const handleSubmit = (0, import_react6.useCallback)(
1263
+ (text) => {
1264
+ const trimmed = text.trim();
1265
+ if (trimmed.length === 0) return;
1266
+ if (showPopup && filteredCommands[selectedIndex]) {
1267
+ enterSelectCommand(filteredCommands[selectedIndex]);
1268
+ return;
1269
+ }
1270
+ const expanded = expandPasteLabels(trimmed, pasteStore.current);
1271
+ setValue("");
1272
+ pasteStore.current.clear();
1273
+ pasteIdRef.current = 0;
1274
+ onSubmit(expanded);
1275
+ },
1276
+ [showPopup, filteredCommands, selectedIndex, onSubmit, enterSelectCommand]
1277
+ );
1222
1278
  (0, import_ink7.useInput)(
1223
1279
  (_input, key) => {
1224
1280
  if (!showPopup) return;
@@ -1230,7 +1286,7 @@ function InputArea({
1230
1286
  setShowPopup(false);
1231
1287
  } else if (key.tab) {
1232
1288
  const cmd = filteredCommands[selectedIndex];
1233
- if (cmd) selectCommand(cmd);
1289
+ if (cmd) tabCompleteCommand(cmd);
1234
1290
  }
1235
1291
  },
1236
1292
  { isActive: showPopup && !isDisabled }
@@ -1243,6 +1299,17 @@ function InputArea({
1243
1299
  },
1244
1300
  { isActive: !!pendingPrompt }
1245
1301
  );
1302
+ const borderColor = isAborting ? "yellow" : pendingPrompt ? "cyan" : isDisabled ? "gray" : "green";
1303
+ const innerWidth = Math.max(1, terminalColumns - BORDER_HORIZONTAL);
1304
+ const topBorder = (() => {
1305
+ if (sessionName) {
1306
+ const label = ` "${sessionName}" `;
1307
+ const rightPad = 2;
1308
+ const leftLen = Math.max(0, innerWidth - label.length - rightPad);
1309
+ return { left: "\u250C" + "\u2500".repeat(leftLen), label, right: "\u2500".repeat(rightPad) + "\u2510" };
1310
+ }
1311
+ return { left: "\u250C" + "\u2500".repeat(innerWidth), label: "", right: "\u2510" };
1312
+ })();
1246
1313
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", children: [
1247
1314
  showPopup && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1248
1315
  SlashAutocomplete,
@@ -1253,34 +1320,31 @@ function InputArea({
1253
1320
  isSubcommandMode
1254
1321
  }
1255
1322
  ),
1256
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1257
- import_ink7.Box,
1258
- {
1259
- borderStyle: "single",
1260
- borderColor: isAborting ? "yellow" : pendingPrompt ? "cyan" : isDisabled ? "gray" : "green",
1261
- paddingLeft: 1,
1262
- children: isAborting ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: "cyan", children: [
1263
- " ",
1264
- "Queued: ",
1265
- pendingPrompt.length > 50 ? pendingPrompt.slice(0, 47) + "..." : pendingPrompt,
1266
- " ",
1267
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: "(Backspace to cancel)" })
1268
- ] }) : 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: [
1269
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "green", bold: true, children: "> " }),
1270
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1271
- CjkTextInput,
1272
- {
1273
- value,
1274
- onChange: setValue,
1275
- onSubmit: handleSubmit,
1276
- onPaste: handlePaste,
1277
- placeholder: "Type a message or /help",
1278
- availableWidth
1279
- }
1280
- )
1281
- ] })
1282
- }
1283
- )
1323
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: borderColor, children: [
1324
+ topBorder.left,
1325
+ topBorder.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { backgroundColor: borderColor, color: "black", bold: true, children: topBorder.label }) : null,
1326
+ topBorder.right
1327
+ ] }),
1328
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { borderStyle: "single", borderTop: false, borderColor, paddingLeft: 1, children: isAborting ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: "cyan", children: [
1329
+ " ",
1330
+ "Queued: ",
1331
+ pendingPrompt.length > 50 ? pendingPrompt.slice(0, 47) + "..." : pendingPrompt,
1332
+ " ",
1333
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: "(Backspace to cancel)" })
1334
+ ] }) : 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: [
1335
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "green", bold: true, children: "> " }),
1336
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1337
+ CjkTextInput,
1338
+ {
1339
+ value,
1340
+ onChange: setValue,
1341
+ onSubmit: handleSubmit,
1342
+ onPaste: handlePaste,
1343
+ placeholder: "Type a message or /help",
1344
+ availableWidth
1345
+ }
1346
+ )
1347
+ ] }) })
1284
1348
  ] });
1285
1349
  }
1286
1350
 
@@ -1885,11 +1949,91 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
1885
1949
  );
1886
1950
  }
1887
1951
 
1888
- // src/ui/App.tsx
1952
+ // src/ui/ListPicker.tsx
1953
+ var import_react12 = require("react");
1954
+ var import_ink13 = require("ink");
1889
1955
  var import_jsx_runtime14 = require("react/jsx-runtime");
1956
+ var DEFAULT_MAX_VISIBLE = 3;
1957
+ function ListPicker({
1958
+ items,
1959
+ renderItem,
1960
+ onSelect,
1961
+ onCancel,
1962
+ maxVisible = DEFAULT_MAX_VISIBLE
1963
+ }) {
1964
+ const [selectedIndex, setSelectedIndex] = (0, import_react12.useState)(0);
1965
+ const [scrollOffset, setScrollOffset] = (0, import_react12.useState)(0);
1966
+ const selectedRef = (0, import_react12.useRef)(0);
1967
+ const resolvedRef = (0, import_react12.useRef)(false);
1968
+ (0, import_ink13.useInput)((_input, key) => {
1969
+ if (resolvedRef.current) return;
1970
+ if (key.escape) {
1971
+ resolvedRef.current = true;
1972
+ onCancel();
1973
+ return;
1974
+ }
1975
+ if (items.length === 0) return;
1976
+ if (key.upArrow) {
1977
+ const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
1978
+ selectedRef.current = next;
1979
+ setSelectedIndex(next);
1980
+ if (next < scrollOffset) {
1981
+ setScrollOffset(next);
1982
+ }
1983
+ } else if (key.downArrow) {
1984
+ const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
1985
+ selectedRef.current = next;
1986
+ setSelectedIndex(next);
1987
+ if (next >= scrollOffset + maxVisible) {
1988
+ setScrollOffset(next - maxVisible + 1);
1989
+ }
1990
+ } else if (key.return) {
1991
+ const item = items[selectedRef.current];
1992
+ if (item !== void 0) {
1993
+ resolvedRef.current = true;
1994
+ onSelect(item);
1995
+ }
1996
+ }
1997
+ });
1998
+ if (items.length === 0) {
1999
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, {});
2000
+ }
2001
+ const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
2002
+ const hasMore = scrollOffset + maxVisible < items.length;
2003
+ const hasLess = scrollOffset > 0;
2004
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
2005
+ hasLess && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
2006
+ " \u2191 ",
2007
+ scrollOffset,
2008
+ " more above"
2009
+ ] }),
2010
+ visibleItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
2011
+ hasMore && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
2012
+ " \u2193 ",
2013
+ items.length - scrollOffset - maxVisible,
2014
+ " more below"
2015
+ ] })
2016
+ ] });
2017
+ }
2018
+
2019
+ // src/ui/App.tsx
2020
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1890
2021
  var EXIT_DELAY_MS = 500;
2022
+ var SESSION_ID_DISPLAY_LENGTH = 8;
1891
2023
  function App(props) {
1892
- const { exit } = (0, import_ink13.useApp)();
2024
+ const [activeSessionId, setActiveSessionId] = (0, import_react13.useState)(props.resumeSessionId);
2025
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2026
+ AppInner,
2027
+ {
2028
+ ...props,
2029
+ resumeSessionId: activeSessionId,
2030
+ onSessionSwitch: (sessionId) => setActiveSessionId(sessionId)
2031
+ },
2032
+ activeSessionId ?? "__new__"
2033
+ );
2034
+ }
2035
+ function AppInner(props) {
2036
+ const { exit } = (0, import_ink14.useApp)();
1893
2037
  const cwd = props.cwd;
1894
2038
  const {
1895
2039
  interactiveSession,
@@ -1910,12 +2054,28 @@ function App(props) {
1910
2054
  cwd,
1911
2055
  provider: props.provider,
1912
2056
  permissionMode: props.permissionMode,
1913
- maxTurns: props.maxTurns
2057
+ maxTurns: props.maxTurns,
2058
+ sessionStore: props.sessionStore,
2059
+ resumeSessionId: props.resumeSessionId,
2060
+ forkSession: props.forkSession,
2061
+ sessionName: props.sessionName
1914
2062
  });
1915
2063
  const pluginCallbacks = usePluginCallbacks(cwd);
1916
- const [pendingModelId, setPendingModelId] = (0, import_react12.useState)(null);
1917
- const pendingModelChangeRef = (0, import_react12.useRef)(null);
1918
- const [showPluginTUI, setShowPluginTUI] = (0, import_react12.useState)(false);
2064
+ const [pendingModelId, setPendingModelId] = (0, import_react13.useState)(null);
2065
+ const pendingModelChangeRef = (0, import_react13.useRef)(null);
2066
+ const [showPluginTUI, setShowPluginTUI] = (0, import_react13.useState)(false);
2067
+ const [showSessionPicker, setShowSessionPicker] = (0, import_react13.useState)(
2068
+ props.resumeSessionId === "__picker__"
2069
+ );
2070
+ const [sessionName, setSessionName] = (0, import_react13.useState)(props.sessionName);
2071
+ (0, import_react13.useEffect)(() => {
2072
+ const name = interactiveSession?.getName?.();
2073
+ if (name && !sessionName) setSessionName(name);
2074
+ }, [interactiveSession, sessionName]);
2075
+ (0, import_react13.useEffect)(() => {
2076
+ const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
2077
+ process.stdout.write(`\x1B]0;${title}\x07`);
2078
+ }, [sessionName]);
1919
2079
  const handleSubmit = async (input) => {
1920
2080
  await baseHandleSubmit(input);
1921
2081
  const sideEffects = interactiveSession;
@@ -1960,14 +2120,26 @@ function App(props) {
1960
2120
  setShowPluginTUI(true);
1961
2121
  return;
1962
2122
  }
2123
+ if (sideEffects._triggerResumePicker) {
2124
+ delete sideEffects._triggerResumePicker;
2125
+ setShowSessionPicker(true);
2126
+ return;
2127
+ }
2128
+ if (sideEffects._sessionName) {
2129
+ const name = sideEffects._sessionName;
2130
+ delete sideEffects._sessionName;
2131
+ interactiveSession.setName(name);
2132
+ setSessionName(name);
2133
+ return;
2134
+ }
1963
2135
  };
1964
- (0, import_ink13.useInput)(
2136
+ (0, import_ink14.useInput)(
1965
2137
  (_input, key) => {
1966
2138
  if (key.escape && isThinking) {
1967
2139
  handleAbort();
1968
2140
  }
1969
2141
  },
1970
- { isActive: !permissionRequest && !showPluginTUI }
2142
+ { isActive: !permissionRequest && !showPluginTUI && !showSessionPicker }
1971
2143
  );
1972
2144
  let permissionMode = props.permissionMode ?? "default";
1973
2145
  let sessionId = "";
@@ -1977,26 +2149,26 @@ function App(props) {
1977
2149
  sessionId = session.getSessionId();
1978
2150
  } catch {
1979
2151
  }
1980
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
1981
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1982
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { color: "cyan", bold: true, children: `
2152
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", children: [
2153
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2154
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { color: "cyan", bold: true, children: `
1983
2155
  ____ ___ ____ ___ _____ _
1984
2156
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
1985
2157
  | |_) | | | | _ \\| | | || | / _ \\
1986
2158
  | _ <| |_| | |_) | |_| || |/ ___ \\
1987
2159
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
1988
2160
  ` }),
1989
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
2161
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { dimColor: true, children: [
1990
2162
  " v",
1991
2163
  props.version ?? "0.0.0"
1992
2164
  ] })
1993
2165
  ] }),
1994
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
1995
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageList, { history }),
1996
- (isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
2166
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2167
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MessageList, { history }),
2168
+ (isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
1997
2169
  ] }),
1998
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PermissionPrompt, { request: permissionRequest }),
1999
- pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2170
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PermissionPrompt, { request: permissionRequest }),
2171
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2000
2172
  ConfirmPrompt,
2001
2173
  {
2002
2174
  message: `Change model to ${(0, import_agent_core4.getModelName)(pendingModelId)}? This will restart the session.`,
@@ -2030,7 +2202,7 @@ function App(props) {
2030
2202
  }
2031
2203
  }
2032
2204
  ),
2033
- showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2205
+ showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2034
2206
  PluginTUI,
2035
2207
  {
2036
2208
  callbacks: pluginCallbacks,
@@ -2038,7 +2210,52 @@ function App(props) {
2038
2210
  addMessage: (msg) => addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(msg.content)))
2039
2211
  }
2040
2212
  ),
2041
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2213
+ showSessionPicker && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2214
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
2215
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2216
+ ListPicker,
2217
+ {
2218
+ items: (props.sessionStore?.list() ?? []).filter((s) => s.cwd === props.cwd),
2219
+ renderItem: (session, isSelected) => {
2220
+ const lastMsg = session.messages.slice().reverse().find((m) => {
2221
+ const msg = m;
2222
+ return msg.role === "assistant" && msg.content;
2223
+ });
2224
+ const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
2225
+ const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
2226
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { children: [
2227
+ isSelected ? "> " : " ",
2228
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
2229
+ " ",
2230
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
2231
+ month: "short",
2232
+ day: "numeric",
2233
+ hour: "2-digit",
2234
+ minute: "2-digit"
2235
+ }) }),
2236
+ " ",
2237
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { dimColor: true, children: [
2238
+ "msgs: ",
2239
+ session.messages.length
2240
+ ] }),
2241
+ preview ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
2242
+ "\n ",
2243
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { color: "gray", children: preview })
2244
+ ] }) : null
2245
+ ] });
2246
+ },
2247
+ onSelect: (session) => {
2248
+ setShowSessionPicker(false);
2249
+ props.onSessionSwitch(session.id);
2250
+ },
2251
+ onCancel: () => {
2252
+ setShowSessionPicker(false);
2253
+ addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Session resume cancelled.")));
2254
+ }
2255
+ }
2256
+ )
2257
+ ] }),
2258
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2042
2259
  StatusBar,
2043
2260
  {
2044
2261
  permissionMode,
@@ -2048,26 +2265,28 @@ function App(props) {
2048
2265
  isThinking,
2049
2266
  contextPercentage: contextState.percentage,
2050
2267
  contextUsedTokens: contextState.usedTokens,
2051
- contextMaxTokens: contextState.maxTokens
2268
+ contextMaxTokens: contextState.maxTokens,
2269
+ sessionName
2052
2270
  }
2053
2271
  ),
2054
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2272
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2055
2273
  InputArea,
2056
2274
  {
2057
2275
  onSubmit: handleSubmit,
2058
2276
  onCancelQueue: handleCancelQueue,
2059
- isDisabled: !!permissionRequest || showPluginTUI || isThinking && !!pendingPrompt,
2277
+ isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isThinking && !!pendingPrompt,
2060
2278
  isAborting,
2061
2279
  pendingPrompt,
2062
- registry
2280
+ registry,
2281
+ sessionName
2063
2282
  }
2064
2283
  ),
2065
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { children: " " })
2284
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { children: " " })
2066
2285
  ] });
2067
2286
  }
2068
2287
 
2069
2288
  // src/ui/render.tsx
2070
- var import_jsx_runtime15 = require("react/jsx-runtime");
2289
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2071
2290
  function renderApp(options) {
2072
2291
  process.on("unhandledRejection", (reason) => {
2073
2292
  process.stderr.write(`
@@ -2081,7 +2300,7 @@ function renderApp(options) {
2081
2300
  if (process.stdin.isTTY && process.stdout.isTTY) {
2082
2301
  process.stdout.write("\x1B[?2004h");
2083
2302
  }
2084
- const instance = (0, import_ink14.render)(/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(App, { ...options }), {
2303
+ const instance = (0, import_ink15.render)(/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(App, { ...options }), {
2085
2304
  exitOnCtrlC: true
2086
2305
  });
2087
2306
  instance.waitUntilExit().then(() => {
@@ -2251,9 +2470,38 @@ async function startCli() {
2251
2470
  const providerSettings = readProviderSettings(cwd);
2252
2471
  const modelId = args.model ?? providerSettings.model;
2253
2472
  const provider = createProviderFromSettings(cwd, args.model);
2473
+ const sessionStore = new import_agent_sessions.SessionStore();
2474
+ let resumeSessionId;
2475
+ if (args.continueMode) {
2476
+ const sessions = sessionStore.list().filter((s) => s.cwd === cwd);
2477
+ if (sessions.length > 0) {
2478
+ resumeSessionId = sessions[0].id;
2479
+ }
2480
+ } else if (args.resumeId !== void 0) {
2481
+ if (args.resumeId === "") {
2482
+ resumeSessionId = "__picker__";
2483
+ } else {
2484
+ const sessions = sessionStore.list();
2485
+ const match = sessions.find((s) => s.id === args.resumeId || s.name === args.resumeId);
2486
+ if (match) {
2487
+ resumeSessionId = match.id;
2488
+ } else {
2489
+ process.stderr.write(`Session not found: ${args.resumeId}
2490
+ `);
2491
+ process.exit(1);
2492
+ }
2493
+ }
2494
+ }
2254
2495
  if (args.printMode) {
2255
- const prompt = args.positional.join(" ").trim();
2256
- if (prompt.length === 0) {
2496
+ let prompt = args.positional.join(" ").trim();
2497
+ if (!prompt && !process.stdin.isTTY) {
2498
+ const chunks = [];
2499
+ for await (const chunk of process.stdin) {
2500
+ chunks.push(chunk);
2501
+ }
2502
+ prompt = Buffer.concat(chunks).toString("utf-8").trim();
2503
+ }
2504
+ if (!prompt) {
2257
2505
  process.stderr.write("Print mode (-p) requires a prompt argument.\n");
2258
2506
  process.exit(1);
2259
2507
  }
@@ -2261,21 +2509,17 @@ async function startCli() {
2261
2509
  cwd,
2262
2510
  provider,
2263
2511
  permissionMode: args.permissionMode ?? "bypassPermissions",
2264
- maxTurns: args.maxTurns
2512
+ maxTurns: args.maxTurns,
2513
+ sessionStore,
2514
+ sessionName: args.sessionName
2265
2515
  });
2266
- await new Promise((resolve, reject) => {
2267
- session.on("complete", (result) => {
2268
- process.stdout.write(result.response + "\n");
2269
- resolve();
2270
- });
2271
- session.on("interrupted", (result) => {
2272
- if (result.response) process.stdout.write(result.response + "\n");
2273
- resolve();
2274
- });
2275
- session.on("error", (err) => reject(err));
2276
- session.submit(prompt).catch(reject);
2516
+ const transport = (0, import_agent_transport_headless.createHeadlessTransport)({
2517
+ outputFormat: args.outputFormat ?? "text",
2518
+ prompt
2277
2519
  });
2278
- return;
2520
+ session.attachTransport(transport);
2521
+ await transport.start();
2522
+ process.exit(transport.getExitCode());
2279
2523
  }
2280
2524
  renderApp({
2281
2525
  cwd,
@@ -2284,7 +2528,11 @@ async function startCli() {
2284
2528
  language: args.language,
2285
2529
  permissionMode: args.permissionMode,
2286
2530
  maxTurns: args.maxTurns,
2287
- version: readVersion()
2531
+ version: readVersion(),
2532
+ sessionStore,
2533
+ resumeSessionId,
2534
+ forkSession: args.forkSession,
2535
+ sessionName: args.sessionName
2288
2536
  });
2289
2537
  }
2290
2538
  // Annotate the CommonJS export names for ESM import in node: