@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.
- package/README.md +67 -13
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-Y6VSMUKG.js → chunk-74WEPZG2.js} +346 -98
- package/dist/node/index.cjs +345 -97
- package/dist/node/index.js +1 -1
- package/package.json +6 -4
package/dist/node/index.cjs
CHANGED
|
@@ -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
|
-
|
|
71
|
-
|
|
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["
|
|
84
|
-
resumeId: values["
|
|
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
|
|
183
|
+
var import_ink15 = require("ink");
|
|
170
184
|
|
|
171
185
|
// src/ui/App.tsx
|
|
172
|
-
var
|
|
173
|
-
var
|
|
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
|
-
|
|
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
|
|
1188
|
-
(
|
|
1189
|
-
const
|
|
1190
|
-
if (
|
|
1191
|
-
|
|
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
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
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
|
-
[
|
|
1241
|
+
[value, setSelectedIndex]
|
|
1202
1242
|
);
|
|
1203
|
-
const
|
|
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)
|
|
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.
|
|
1257
|
-
|
|
1258
|
-
{
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
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/
|
|
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
|
|
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,
|
|
1917
|
-
const pendingModelChangeRef = (0,
|
|
1918
|
-
const [showPluginTUI, setShowPluginTUI] = (0,
|
|
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,
|
|
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,
|
|
1981
|
-
/* @__PURE__ */ (0,
|
|
1982
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
1995
|
-
/* @__PURE__ */ (0,
|
|
1996
|
-
(isThinking || activeTools.length > 0) && /* @__PURE__ */ (0,
|
|
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,
|
|
1999
|
-
pendingModelId && /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
-
|
|
2256
|
-
if (prompt
|
|
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
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
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
|
-
|
|
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:
|