@hydra-acp/cli 0.1.5 → 0.1.7
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 +15 -10
- package/dist/cli.js +543 -178
- package/dist/index.d.ts +10 -1
- package/dist/index.js +135 -37
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -32,6 +32,9 @@ var init_paths = __esm({
|
|
|
32
32
|
paths = {
|
|
33
33
|
home: hydraHome,
|
|
34
34
|
config: () => path.join(hydraHome(), "config.json"),
|
|
35
|
+
// Auth token lives in its own file so config.json can be version-
|
|
36
|
+
// controlled without leaking the secret. Raw string contents, mode 0600.
|
|
37
|
+
authToken: () => path.join(hydraHome(), "auth-token"),
|
|
35
38
|
pidFile: () => path.join(hydraHome(), "daemon.pid"),
|
|
36
39
|
logFile: () => path.join(hydraHome(), "daemon.log"),
|
|
37
40
|
currentLogFile: () => path.join(hydraHome(), "current.log"),
|
|
@@ -68,61 +71,95 @@ function extensionList(config) {
|
|
|
68
71
|
...body
|
|
69
72
|
}));
|
|
70
73
|
}
|
|
71
|
-
async function
|
|
72
|
-
const configPath = paths.config();
|
|
74
|
+
async function readConfigFile() {
|
|
73
75
|
let raw;
|
|
74
76
|
try {
|
|
75
|
-
raw = await fs.readFile(
|
|
77
|
+
raw = await fs.readFile(paths.config(), "utf8");
|
|
76
78
|
} catch (err) {
|
|
77
79
|
const e = err;
|
|
78
80
|
if (e.code === "ENOENT") {
|
|
79
|
-
|
|
80
|
-
`No config found at ${configPath}. Run \`hydra-acp init\` to create one.`
|
|
81
|
-
);
|
|
81
|
+
return {};
|
|
82
82
|
}
|
|
83
83
|
throw err;
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
return HydraConfig.parse(parsed);
|
|
85
|
+
return JSON.parse(raw);
|
|
87
86
|
}
|
|
88
|
-
async function
|
|
87
|
+
async function loadAuthToken() {
|
|
88
|
+
let tokenFile;
|
|
89
89
|
try {
|
|
90
|
-
await fs.
|
|
90
|
+
const text = await fs.readFile(paths.authToken(), "utf8");
|
|
91
|
+
const trimmed = text.trim();
|
|
92
|
+
if (trimmed.length > 0) {
|
|
93
|
+
tokenFile = trimmed;
|
|
94
|
+
}
|
|
91
95
|
} catch (err) {
|
|
92
96
|
const e = err;
|
|
93
97
|
if (e.code !== "ENOENT") {
|
|
94
98
|
throw err;
|
|
95
99
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
}
|
|
101
|
+
const raw = await readConfigFile();
|
|
102
|
+
const daemon = raw.daemon;
|
|
103
|
+
const legacy = daemon && typeof daemon.authToken === "string" ? daemon.authToken : void 0;
|
|
104
|
+
if (tokenFile && legacy) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Auth token present in both ${paths.authToken()} and ${paths.config()} (daemon.authToken). Remove daemon.authToken from config.json to resolve.`
|
|
100
107
|
);
|
|
101
|
-
return config;
|
|
102
108
|
}
|
|
103
|
-
|
|
109
|
+
if (tokenFile) {
|
|
110
|
+
return tokenFile;
|
|
111
|
+
}
|
|
112
|
+
if (legacy) {
|
|
113
|
+
await migrateLegacyAuthToken(raw, daemon, legacy);
|
|
114
|
+
return legacy;
|
|
115
|
+
}
|
|
116
|
+
return void 0;
|
|
104
117
|
}
|
|
105
|
-
async function
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
async function migrateLegacyAuthToken(raw, daemon, token) {
|
|
119
|
+
await writeAuthToken(token);
|
|
120
|
+
delete daemon.authToken;
|
|
121
|
+
if (Object.keys(daemon).length === 0) {
|
|
122
|
+
delete raw.daemon;
|
|
123
|
+
}
|
|
124
|
+
await fs.writeFile(paths.config(), JSON.stringify(raw, null, 2) + "\n", {
|
|
110
125
|
encoding: "utf8",
|
|
111
126
|
mode: 384
|
|
112
127
|
});
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
await fs.writeFile(path7, JSON.stringify(raw, null, 2) + "\n", {
|
|
128
|
+
process.stderr.write(
|
|
129
|
+
`hydra-acp: migrated auth token from ${paths.config()} to ${paths.authToken()}.
|
|
130
|
+
`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
async function writeAuthToken(token) {
|
|
134
|
+
await fs.mkdir(paths.home(), { recursive: true });
|
|
135
|
+
await fs.writeFile(paths.authToken(), token + "\n", {
|
|
122
136
|
encoding: "utf8",
|
|
123
137
|
mode: 384
|
|
124
138
|
});
|
|
125
139
|
}
|
|
140
|
+
async function loadConfig() {
|
|
141
|
+
const token = await loadAuthToken();
|
|
142
|
+
if (!token) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`No auth token found at ${paths.authToken()}. Run \`hydra-acp init\` to create one.`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const raw = await readConfigFile();
|
|
148
|
+
const daemon = raw.daemon ??= {};
|
|
149
|
+
daemon.authToken = token;
|
|
150
|
+
return HydraConfig.parse(raw);
|
|
151
|
+
}
|
|
152
|
+
async function ensureConfig() {
|
|
153
|
+
if (!await loadAuthToken()) {
|
|
154
|
+
const token = generateAuthToken();
|
|
155
|
+
await writeAuthToken(token);
|
|
156
|
+
process.stderr.write(
|
|
157
|
+
`hydra-acp: initialized ${paths.authToken()} with a fresh auth token.
|
|
158
|
+
`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
return loadConfig();
|
|
162
|
+
}
|
|
126
163
|
function generateAuthToken() {
|
|
127
164
|
const bytes = new Uint8Array(32);
|
|
128
165
|
crypto.getRandomValues(bytes);
|
|
@@ -177,7 +214,13 @@ var init_config = __esm({
|
|
|
177
214
|
// Cap on logical lines retained in the in-memory scrollback render
|
|
178
215
|
// buffer. Oldest lines are dropped on overflow. The on-disk session
|
|
179
216
|
// history is unaffected; this only bounds the TUI's local view buffer.
|
|
180
|
-
maxScrollbackLines: z.number().int().positive().default(1e4)
|
|
217
|
+
maxScrollbackLines: z.number().int().positive().default(1e4),
|
|
218
|
+
// When true (default), the TUI captures mouse events so the wheel can
|
|
219
|
+
// drive scrollback. The cost: terminals route clicks to the app, so
|
|
220
|
+
// text selection requires shift+drag to bypass mouse reporting. Set
|
|
221
|
+
// false to disable capture — wheel scrollback stops working, but
|
|
222
|
+
// plain click-drag selects text via the terminal emulator.
|
|
223
|
+
mouse: z.boolean().default(true)
|
|
181
224
|
});
|
|
182
225
|
ExtensionName = z.string().min(1).regex(/^[A-Za-z0-9._-]+$/, "extension name must be filename-safe");
|
|
183
226
|
ExtensionBody = z.object({
|
|
@@ -210,7 +253,11 @@ var init_config = __esm({
|
|
|
210
253
|
// recency and truncated to this count. `--all` overrides in the CLI.
|
|
211
254
|
sessionListColdLimit: z.number().int().nonnegative().default(20),
|
|
212
255
|
extensions: z.record(ExtensionName, ExtensionBody).default({}),
|
|
213
|
-
tui: TuiConfig.default({
|
|
256
|
+
tui: TuiConfig.default({
|
|
257
|
+
repaintThrottleMs: 1e3,
|
|
258
|
+
maxScrollbackLines: 1e4,
|
|
259
|
+
mouse: true
|
|
260
|
+
})
|
|
214
261
|
});
|
|
215
262
|
}
|
|
216
263
|
});
|
|
@@ -248,6 +295,9 @@ function extractHydraMeta(meta) {
|
|
|
248
295
|
out.resume = parsed.data;
|
|
249
296
|
}
|
|
250
297
|
}
|
|
298
|
+
if (typeof obj.model === "string") {
|
|
299
|
+
out.model = obj.model;
|
|
300
|
+
}
|
|
251
301
|
if (typeof obj.currentModel === "string") {
|
|
252
302
|
out.currentModel = obj.currentModel;
|
|
253
303
|
}
|
|
@@ -392,7 +442,7 @@ var init_connection = __esm({
|
|
|
392
442
|
"src/acp/connection.ts"() {
|
|
393
443
|
"use strict";
|
|
394
444
|
init_types();
|
|
395
|
-
JsonRpcConnection = class {
|
|
445
|
+
JsonRpcConnection = class _JsonRpcConnection {
|
|
396
446
|
constructor(stream) {
|
|
397
447
|
this.stream = stream;
|
|
398
448
|
this.stream.onMessage((m) => this.handleIncoming(m));
|
|
@@ -402,6 +452,16 @@ var init_connection = __esm({
|
|
|
402
452
|
requestHandlers = /* @__PURE__ */ new Map();
|
|
403
453
|
defaultRequestHandler;
|
|
404
454
|
notificationHandlers = /* @__PURE__ */ new Map();
|
|
455
|
+
// Notifications received before a handler was registered. Some agents
|
|
456
|
+
// (e.g. claude-acp) advertise their command list in the same chunk as
|
|
457
|
+
// the `session/new` response, which is processed before the consumer
|
|
458
|
+
// can attach its `session/update` handler. Without this buffer those
|
|
459
|
+
// notifications would be silently dropped, so e.g. `/model` would
|
|
460
|
+
// never appear in the TUI's slash-completion palette. Capped per
|
|
461
|
+
// method to keep the buffer from growing unboundedly when nothing
|
|
462
|
+
// ever subscribes.
|
|
463
|
+
bufferedNotifications = /* @__PURE__ */ new Map();
|
|
464
|
+
static MAX_BUFFERED_PER_METHOD = 64;
|
|
405
465
|
pending = /* @__PURE__ */ new Map();
|
|
406
466
|
closed = false;
|
|
407
467
|
closeHandlers = [];
|
|
@@ -413,6 +473,17 @@ var init_connection = __esm({
|
|
|
413
473
|
}
|
|
414
474
|
onNotification(method, handler) {
|
|
415
475
|
this.notificationHandlers.set(method, handler);
|
|
476
|
+
const queued = this.bufferedNotifications.get(method);
|
|
477
|
+
if (!queued) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
this.bufferedNotifications.delete(method);
|
|
481
|
+
for (const note of queued) {
|
|
482
|
+
try {
|
|
483
|
+
handler(note.params, note.method);
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
}
|
|
416
487
|
}
|
|
417
488
|
onClose(handler) {
|
|
418
489
|
this.closeHandlers.push(handler);
|
|
@@ -498,6 +569,16 @@ var init_connection = __esm({
|
|
|
498
569
|
const handler = this.notificationHandlers.get(note.method);
|
|
499
570
|
if (handler) {
|
|
500
571
|
handler(note.params, note.method);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
let queued = this.bufferedNotifications.get(note.method);
|
|
575
|
+
if (!queued) {
|
|
576
|
+
queued = [];
|
|
577
|
+
this.bufferedNotifications.set(note.method, queued);
|
|
578
|
+
}
|
|
579
|
+
queued.push(note);
|
|
580
|
+
if (queued.length > _JsonRpcConnection.MAX_BUFFERED_PER_METHOD) {
|
|
581
|
+
queued.shift();
|
|
501
582
|
}
|
|
502
583
|
}
|
|
503
584
|
handleResponse(res) {
|
|
@@ -554,12 +635,12 @@ var init_hydra_commands = __esm({
|
|
|
554
635
|
HYDRA_COMMANDS = [
|
|
555
636
|
{
|
|
556
637
|
verb: "title",
|
|
557
|
-
name: "
|
|
638
|
+
name: "hydra title",
|
|
558
639
|
description: "Regenerate the session title via the agent (or set manually with an arg)"
|
|
559
640
|
},
|
|
560
641
|
{
|
|
561
642
|
verb: "agent",
|
|
562
|
-
name: "
|
|
643
|
+
name: "hydra agent",
|
|
563
644
|
argsHint: "<agent>",
|
|
564
645
|
description: "Swap the agent backing this session, preserving context"
|
|
565
646
|
}
|
|
@@ -3434,6 +3515,7 @@ var init_screen = __esm({
|
|
|
3434
3515
|
streamingActive = false;
|
|
3435
3516
|
lastPromptRows = 0;
|
|
3436
3517
|
queuedTexts = [];
|
|
3518
|
+
lastQueueEditingIndex = -1;
|
|
3437
3519
|
repaintPaused = 0;
|
|
3438
3520
|
repaintPending = false;
|
|
3439
3521
|
lastRepaintAt = 0;
|
|
@@ -3490,12 +3572,14 @@ var init_screen = __esm({
|
|
|
3490
3572
|
pasteActive = false;
|
|
3491
3573
|
pasteBuffer = "";
|
|
3492
3574
|
rawStdinHandler;
|
|
3575
|
+
mouseEnabled;
|
|
3493
3576
|
constructor(opts) {
|
|
3494
3577
|
this.term = opts.term;
|
|
3495
3578
|
this.dispatcher = opts.dispatcher;
|
|
3496
3579
|
this.onKey = opts.onKey;
|
|
3497
3580
|
this.contentRepaintThrottleMs = opts.repaintThrottleMs ?? DEFAULT_CONTENT_REPAINT_THROTTLE_MS;
|
|
3498
3581
|
this.maxScrollbackLines = opts.maxScrollbackLines ?? DEFAULT_MAX_SCROLLBACK_LINES;
|
|
3582
|
+
this.mouseEnabled = opts.mouse ?? true;
|
|
3499
3583
|
this.resizeHandler = () => this.repaint();
|
|
3500
3584
|
this.keyHandler = (name, _matches, data) => this.handleKey(name, data);
|
|
3501
3585
|
this.mouseHandler = (name) => this.handleMouse(name);
|
|
@@ -3512,10 +3596,16 @@ var init_screen = __esm({
|
|
|
3512
3596
|
this.lastFrameH = 0;
|
|
3513
3597
|
this.lastWindowTitle = null;
|
|
3514
3598
|
process.stdout.write("\x1B[?7l");
|
|
3515
|
-
this.
|
|
3599
|
+
if (this.mouseEnabled) {
|
|
3600
|
+
this.term.grabInput({ mouse: "button" });
|
|
3601
|
+
} else {
|
|
3602
|
+
this.term.grabInput(true);
|
|
3603
|
+
}
|
|
3516
3604
|
this.term.hideCursor(false);
|
|
3517
3605
|
this.term.on("key", this.keyHandler);
|
|
3518
|
-
|
|
3606
|
+
if (this.mouseEnabled) {
|
|
3607
|
+
this.term.on("mouse", this.mouseHandler);
|
|
3608
|
+
}
|
|
3519
3609
|
this.term.on("resize", this.resizeHandler);
|
|
3520
3610
|
this.installBracketedPaste();
|
|
3521
3611
|
this.repaint();
|
|
@@ -3527,7 +3617,9 @@ var init_screen = __esm({
|
|
|
3527
3617
|
this.started = false;
|
|
3528
3618
|
this.uninstallBracketedPaste();
|
|
3529
3619
|
this.term.off("key", this.keyHandler);
|
|
3530
|
-
|
|
3620
|
+
if (this.mouseEnabled) {
|
|
3621
|
+
this.term.off("mouse", this.mouseHandler);
|
|
3622
|
+
}
|
|
3531
3623
|
this.term.off("resize", this.resizeHandler);
|
|
3532
3624
|
this.term.grabInput(false);
|
|
3533
3625
|
this.term.hideCursor(false);
|
|
@@ -3877,6 +3969,7 @@ var init_screen = __esm({
|
|
|
3877
3969
|
}
|
|
3878
3970
|
setQueuedPrompts(texts) {
|
|
3879
3971
|
this.queuedTexts = [...texts];
|
|
3972
|
+
this.lastQueueEditingIndex = this.dispatcher.state().queueIndex;
|
|
3880
3973
|
this.repaint();
|
|
3881
3974
|
}
|
|
3882
3975
|
// While a permission prompt is active, the prompt area is replaced with
|
|
@@ -3929,12 +4022,19 @@ var init_screen = __esm({
|
|
|
3929
4022
|
// row count changed (alt+enter added a line, backspace joined two), the
|
|
3930
4023
|
// separator and scrollback bottom shift, so we need a full repaint;
|
|
3931
4024
|
// otherwise an in-place prompt redraw is enough. (Queued-zone changes
|
|
3932
|
-
// already trigger a full repaint via setQueuedPrompts.)
|
|
4025
|
+
// already trigger a full repaint via setQueuedPrompts.) Queue-edit
|
|
4026
|
+
// navigation may also change which queued row is marked, so check
|
|
4027
|
+
// for that and redraw just that zone in-place.
|
|
3933
4028
|
refreshPrompt() {
|
|
3934
4029
|
if (this.promptRows() !== this.lastPromptRows) {
|
|
3935
4030
|
this.repaint();
|
|
3936
4031
|
return;
|
|
3937
4032
|
}
|
|
4033
|
+
const editingIndex = this.dispatcher.state().queueIndex;
|
|
4034
|
+
if (editingIndex !== this.lastQueueEditingIndex) {
|
|
4035
|
+
this.lastQueueEditingIndex = editingIndex;
|
|
4036
|
+
this.drawQueuedZone();
|
|
4037
|
+
}
|
|
3938
4038
|
this.drawPrompt();
|
|
3939
4039
|
this.placeCursor();
|
|
3940
4040
|
}
|
|
@@ -3985,6 +4085,14 @@ var init_screen = __esm({
|
|
|
3985
4085
|
this.scrollOffset = 0;
|
|
3986
4086
|
this.repaint();
|
|
3987
4087
|
}
|
|
4088
|
+
scrollToTop() {
|
|
4089
|
+
const max = this.maxScrollOffset();
|
|
4090
|
+
if (this.scrollOffset === max) {
|
|
4091
|
+
return;
|
|
4092
|
+
}
|
|
4093
|
+
this.scrollOffset = max;
|
|
4094
|
+
this.repaint();
|
|
4095
|
+
}
|
|
3988
4096
|
scrollPageSize() {
|
|
3989
4097
|
return Math.max(1, this.scrollbackVisibleRows() - 2);
|
|
3990
4098
|
}
|
|
@@ -4212,19 +4320,26 @@ var init_screen = __esm({
|
|
|
4212
4320
|
const separatorRow = this.term.height - promptRows - BANNER_ROWS;
|
|
4213
4321
|
const queuedBottom = separatorRow - 1;
|
|
4214
4322
|
const queuedTop = queuedBottom - rows + 1;
|
|
4323
|
+
const editingIndex = this.dispatcher.state().queueIndex;
|
|
4215
4324
|
for (let i = 0; i < rows; i++) {
|
|
4216
4325
|
const row = queuedTop + i;
|
|
4217
4326
|
const text = this.queuedTexts[i];
|
|
4218
4327
|
const isLast = i === rows - 1 && this.queuedTexts.length > MAX_QUEUED_ROWS;
|
|
4219
4328
|
const overflow = this.queuedTexts.length - MAX_QUEUED_ROWS;
|
|
4220
4329
|
const summary = text === void 0 ? "" : isLast ? `+ ${overflow + 1} more queued` : truncate(firstLine2(text), w - 4);
|
|
4221
|
-
const
|
|
4330
|
+
const editing = !isLast && i === editingIndex;
|
|
4331
|
+
const sig = text === void 0 ? `queued|${w}|empty` : `queued|${w}|${editing ? "edit" : isLast ? "ovf" : "row"}|${summary}`;
|
|
4222
4332
|
this.paintRow(row, sig, () => {
|
|
4223
4333
|
if (text === void 0) {
|
|
4224
4334
|
return;
|
|
4225
4335
|
}
|
|
4226
|
-
const
|
|
4227
|
-
const padded =
|
|
4336
|
+
const rest = `\u23F3 ${summary}`;
|
|
4337
|
+
const padded = rest + " ".repeat(Math.max(0, w - 1 - rest.length));
|
|
4338
|
+
if (editing) {
|
|
4339
|
+
this.term.bgBlue.brightYellow("\u25B8");
|
|
4340
|
+
} else {
|
|
4341
|
+
this.term.bgBlue(" ");
|
|
4342
|
+
}
|
|
4228
4343
|
this.term.bgBlue.brightWhite.noFormat(padded);
|
|
4229
4344
|
});
|
|
4230
4345
|
}
|
|
@@ -4530,8 +4645,17 @@ var init_input = __esm({
|
|
|
4530
4645
|
col = 0;
|
|
4531
4646
|
planMode = false;
|
|
4532
4647
|
historyIndex = -1;
|
|
4648
|
+
// Queue editing: when the user walks Up past row 0 with queued prompts
|
|
4649
|
+
// present, the most-recently-queued item lands in the buffer and
|
|
4650
|
+
// queueIndex tracks which slot of `queue` is being edited. Enter submits
|
|
4651
|
+
// the edit (queue-edit) or, on an empty buffer, drops the slot
|
|
4652
|
+
// (queue-remove). -1 means not editing a queue slot.
|
|
4653
|
+
queueIndex = -1;
|
|
4533
4654
|
savedDraft = null;
|
|
4534
4655
|
history = [];
|
|
4656
|
+
// Waiting queue snapshot (excludes the in-flight head). Newest item lives
|
|
4657
|
+
// at the end so Up walks the array right-to-left.
|
|
4658
|
+
queue = [];
|
|
4535
4659
|
turnRunning = false;
|
|
4536
4660
|
// Single-slot kill ring. The most recent killed text (^U, ^K, ^W) lands
|
|
4537
4661
|
// here so ^Y can yank it back. Standard readline keeps a stack; we
|
|
@@ -4547,7 +4671,8 @@ var init_input = __esm({
|
|
|
4547
4671
|
row: this.row,
|
|
4548
4672
|
col: this.col,
|
|
4549
4673
|
planMode: this.planMode,
|
|
4550
|
-
historyIndex: this.historyIndex
|
|
4674
|
+
historyIndex: this.historyIndex,
|
|
4675
|
+
queueIndex: this.queueIndex
|
|
4551
4676
|
};
|
|
4552
4677
|
}
|
|
4553
4678
|
setTurnRunning(running) {
|
|
@@ -4558,6 +4683,16 @@ var init_input = __esm({
|
|
|
4558
4683
|
this.historyIndex = -1;
|
|
4559
4684
|
this.savedDraft = null;
|
|
4560
4685
|
}
|
|
4686
|
+
// Snapshot of the waiting queue (head excluded). Called by the app after
|
|
4687
|
+
// every queue mutation so Up/Down can walk a fresh view. queueIndex is
|
|
4688
|
+
// only invalidated when it falls outside the new bounds — staying in
|
|
4689
|
+
// bounds preserves the user's edit if the queue grew or stayed put.
|
|
4690
|
+
setQueue(queue) {
|
|
4691
|
+
this.queue = [...queue];
|
|
4692
|
+
if (this.queueIndex >= this.queue.length) {
|
|
4693
|
+
this.queueIndex = -1;
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4561
4696
|
// Replace the contents of the first row, leaving subsequent rows alone.
|
|
4562
4697
|
// Used by slash-command completion: the partial /foo gets swapped for the
|
|
4563
4698
|
// matched command name. Cursor moves to the end of the replacement.
|
|
@@ -4567,6 +4702,15 @@ var init_input = __esm({
|
|
|
4567
4702
|
this.col = text.length;
|
|
4568
4703
|
}
|
|
4569
4704
|
}
|
|
4705
|
+
// Public seed for the buffer (used for Escape pre-fill). Treated like a
|
|
4706
|
+
// fresh draft: nav state and any saved draft are cleared, cursor lands
|
|
4707
|
+
// at the end so the user can edit immediately.
|
|
4708
|
+
setBuffer(text) {
|
|
4709
|
+
this.loadEntry(text);
|
|
4710
|
+
this.historyIndex = -1;
|
|
4711
|
+
this.queueIndex = -1;
|
|
4712
|
+
this.savedDraft = null;
|
|
4713
|
+
}
|
|
4570
4714
|
feed(event) {
|
|
4571
4715
|
if (event.type === "char") {
|
|
4572
4716
|
this.insertChar(event.ch);
|
|
@@ -4604,14 +4748,16 @@ var init_input = __esm({
|
|
|
4604
4748
|
case "right":
|
|
4605
4749
|
this.moveRight();
|
|
4606
4750
|
return [];
|
|
4607
|
-
case "home":
|
|
4608
4751
|
case "ctrl-a":
|
|
4609
4752
|
this.col = 0;
|
|
4610
4753
|
return [];
|
|
4611
|
-
case "end":
|
|
4612
4754
|
case "ctrl-e":
|
|
4613
4755
|
this.col = this.currentLine().length;
|
|
4614
4756
|
return [];
|
|
4757
|
+
case "home":
|
|
4758
|
+
return this.handleHome();
|
|
4759
|
+
case "end":
|
|
4760
|
+
return this.handleEnd();
|
|
4615
4761
|
case "ctrl-b":
|
|
4616
4762
|
this.moveLeft();
|
|
4617
4763
|
return [];
|
|
@@ -4634,7 +4780,11 @@ var init_input = __esm({
|
|
|
4634
4780
|
case "ctrl-c":
|
|
4635
4781
|
return this.handleCtrlC();
|
|
4636
4782
|
case "ctrl-d":
|
|
4637
|
-
|
|
4783
|
+
if (this.bufferIsEmpty()) {
|
|
4784
|
+
return [{ type: "exit" }];
|
|
4785
|
+
}
|
|
4786
|
+
this.deleteForward();
|
|
4787
|
+
return [];
|
|
4638
4788
|
case "ctrl-l":
|
|
4639
4789
|
return [{ type: "redraw" }];
|
|
4640
4790
|
case "ctrl-p":
|
|
@@ -4649,6 +4799,9 @@ var init_input = __esm({
|
|
|
4649
4799
|
this.yank();
|
|
4650
4800
|
return [];
|
|
4651
4801
|
case "escape":
|
|
4802
|
+
if (this.turnRunning) {
|
|
4803
|
+
return [{ type: "cancel", prefill: true }];
|
|
4804
|
+
}
|
|
4652
4805
|
return [];
|
|
4653
4806
|
}
|
|
4654
4807
|
}
|
|
@@ -4669,6 +4822,7 @@ var init_input = __esm({
|
|
|
4669
4822
|
this.row = 0;
|
|
4670
4823
|
this.col = 0;
|
|
4671
4824
|
this.historyIndex = -1;
|
|
4825
|
+
this.queueIndex = -1;
|
|
4672
4826
|
this.savedDraft = null;
|
|
4673
4827
|
}
|
|
4674
4828
|
insertChar(ch) {
|
|
@@ -4802,50 +4956,92 @@ var init_input = __esm({
|
|
|
4802
4956
|
this.col = 0;
|
|
4803
4957
|
}
|
|
4804
4958
|
}
|
|
4805
|
-
// Up
|
|
4806
|
-
//
|
|
4959
|
+
// Up walks the navigation stack from newest to oldest: pending queue
|
|
4960
|
+
// items first (so the user can edit something they just enqueued),
|
|
4961
|
+
// then prompt history. Cursor movement within a multi-line buffer
|
|
4962
|
+
// takes priority when not already navigating.
|
|
4807
4963
|
handleUp() {
|
|
4808
4964
|
if (this.row > 0) {
|
|
4809
4965
|
this.row -= 1;
|
|
4810
4966
|
this.col = Math.min(this.col, this.currentLine().length);
|
|
4811
4967
|
return [];
|
|
4812
4968
|
}
|
|
4813
|
-
if (this.
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4969
|
+
if (this.queueIndex === -1 && this.historyIndex === -1) {
|
|
4970
|
+
if (this.queue.length === 0 && this.history.length === 0) {
|
|
4971
|
+
return [];
|
|
4972
|
+
}
|
|
4817
4973
|
this.savedDraft = {
|
|
4818
4974
|
buffer: [...this.buffer],
|
|
4819
4975
|
row: this.row,
|
|
4820
4976
|
col: this.col
|
|
4821
4977
|
};
|
|
4978
|
+
if (this.queue.length > 0) {
|
|
4979
|
+
this.queueIndex = this.queue.length - 1;
|
|
4980
|
+
this.loadEntry(this.queue[this.queueIndex] ?? "");
|
|
4981
|
+
} else {
|
|
4982
|
+
this.historyIndex = this.history.length - 1;
|
|
4983
|
+
this.loadEntry(this.history[this.historyIndex] ?? "");
|
|
4984
|
+
}
|
|
4985
|
+
return [];
|
|
4986
|
+
}
|
|
4987
|
+
if (this.queueIndex >= 0) {
|
|
4988
|
+
if (this.queueIndex > 0) {
|
|
4989
|
+
this.queueIndex -= 1;
|
|
4990
|
+
this.loadEntry(this.queue[this.queueIndex] ?? "");
|
|
4991
|
+
return [];
|
|
4992
|
+
}
|
|
4993
|
+
if (this.history.length === 0) {
|
|
4994
|
+
return [];
|
|
4995
|
+
}
|
|
4996
|
+
this.queueIndex = -1;
|
|
4822
4997
|
this.historyIndex = this.history.length - 1;
|
|
4823
|
-
|
|
4824
|
-
this.historyIndex -= 1;
|
|
4825
|
-
} else {
|
|
4998
|
+
this.loadEntry(this.history[this.historyIndex] ?? "");
|
|
4826
4999
|
return [];
|
|
4827
5000
|
}
|
|
4828
|
-
|
|
5001
|
+
if (this.historyIndex > 0) {
|
|
5002
|
+
this.historyIndex -= 1;
|
|
5003
|
+
this.loadEntry(this.history[this.historyIndex] ?? "");
|
|
5004
|
+
}
|
|
4829
5005
|
return [];
|
|
4830
5006
|
}
|
|
4831
|
-
// Down
|
|
4832
|
-
//
|
|
4833
|
-
//
|
|
5007
|
+
// Down reverses the Up walk: history (older → newer), then queue
|
|
5008
|
+
// (oldest → newest), then restore the original draft. Within a
|
|
5009
|
+
// multi-line buffer, plain cursor movement still wins when no
|
|
5010
|
+
// navigation is in progress.
|
|
4834
5011
|
handleDown() {
|
|
4835
|
-
if (this.row < this.buffer.length - 1 && this.historyIndex === -1) {
|
|
5012
|
+
if (this.row < this.buffer.length - 1 && this.historyIndex === -1 && this.queueIndex === -1) {
|
|
4836
5013
|
this.row += 1;
|
|
4837
5014
|
this.col = Math.min(this.col, this.currentLine().length);
|
|
4838
5015
|
return [];
|
|
4839
5016
|
}
|
|
4840
|
-
if (this.historyIndex
|
|
5017
|
+
if (this.historyIndex >= 0) {
|
|
5018
|
+
if (this.historyIndex < this.history.length - 1) {
|
|
5019
|
+
this.historyIndex += 1;
|
|
5020
|
+
this.loadEntry(this.history[this.historyIndex] ?? "");
|
|
5021
|
+
return [];
|
|
5022
|
+
}
|
|
5023
|
+
this.historyIndex = -1;
|
|
5024
|
+
if (this.queue.length > 0) {
|
|
5025
|
+
this.queueIndex = 0;
|
|
5026
|
+
this.loadEntry(this.queue[this.queueIndex] ?? "");
|
|
5027
|
+
return [];
|
|
5028
|
+
}
|
|
5029
|
+
this.restoreDraft();
|
|
4841
5030
|
return [];
|
|
4842
5031
|
}
|
|
4843
|
-
if (this.
|
|
4844
|
-
this.
|
|
4845
|
-
|
|
5032
|
+
if (this.queueIndex >= 0) {
|
|
5033
|
+
if (this.queueIndex < this.queue.length - 1) {
|
|
5034
|
+
this.queueIndex += 1;
|
|
5035
|
+
this.loadEntry(this.queue[this.queueIndex] ?? "");
|
|
5036
|
+
return [];
|
|
5037
|
+
}
|
|
5038
|
+
this.queueIndex = -1;
|
|
5039
|
+
this.restoreDraft();
|
|
4846
5040
|
return [];
|
|
4847
5041
|
}
|
|
4848
|
-
|
|
5042
|
+
return [];
|
|
5043
|
+
}
|
|
5044
|
+
restoreDraft() {
|
|
4849
5045
|
if (this.savedDraft) {
|
|
4850
5046
|
this.buffer = [...this.savedDraft.buffer];
|
|
4851
5047
|
this.row = this.savedDraft.row;
|
|
@@ -4854,11 +5050,9 @@ var init_input = __esm({
|
|
|
4854
5050
|
} else {
|
|
4855
5051
|
this.clearBuffer();
|
|
4856
5052
|
}
|
|
4857
|
-
return [];
|
|
4858
5053
|
}
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
this.buffer = entry.split("\n");
|
|
5054
|
+
loadEntry(text) {
|
|
5055
|
+
this.buffer = text.split("\n");
|
|
4862
5056
|
if (this.buffer.length === 0) {
|
|
4863
5057
|
this.buffer = [""];
|
|
4864
5058
|
}
|
|
@@ -4867,6 +5061,14 @@ var init_input = __esm({
|
|
|
4867
5061
|
}
|
|
4868
5062
|
send() {
|
|
4869
5063
|
const text = this.bufferText();
|
|
5064
|
+
if (this.queueIndex >= 0 && this.queueIndex < this.queue.length) {
|
|
5065
|
+
const index = this.queueIndex;
|
|
5066
|
+
this.clearBuffer();
|
|
5067
|
+
if (text.trim().length === 0) {
|
|
5068
|
+
return [{ type: "queue-remove", index }];
|
|
5069
|
+
}
|
|
5070
|
+
return [{ type: "queue-edit", index, text }];
|
|
5071
|
+
}
|
|
4870
5072
|
if (text.trim().length === 0) {
|
|
4871
5073
|
return [];
|
|
4872
5074
|
}
|
|
@@ -4874,25 +5076,105 @@ var init_input = __esm({
|
|
|
4874
5076
|
this.clearBuffer();
|
|
4875
5077
|
return [{ type: "send", text, planMode }];
|
|
4876
5078
|
}
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
5079
|
+
// Home: jump to the very start of the prompt buffer. If we're already
|
|
5080
|
+
// there, fall through to scrolling the scrollback to its top.
|
|
5081
|
+
handleHome() {
|
|
5082
|
+
if (this.row !== 0 || this.col !== 0) {
|
|
5083
|
+
this.row = 0;
|
|
5084
|
+
this.col = 0;
|
|
5085
|
+
return [];
|
|
5086
|
+
}
|
|
5087
|
+
return [{ type: "scroll-to-top" }];
|
|
5088
|
+
}
|
|
5089
|
+
// End: jump to the end of the last line of the prompt buffer. Already
|
|
5090
|
+
// there → scroll the scrollback to the bottom (newest).
|
|
5091
|
+
handleEnd() {
|
|
5092
|
+
const lastRow = this.buffer.length - 1;
|
|
5093
|
+
const lastCol = (this.buffer[lastRow] ?? "").length;
|
|
5094
|
+
if (this.row !== lastRow || this.col !== lastCol) {
|
|
5095
|
+
this.row = lastRow;
|
|
5096
|
+
this.col = lastCol;
|
|
5097
|
+
return [];
|
|
4880
5098
|
}
|
|
5099
|
+
return [{ type: "scroll-to-bottom" }];
|
|
5100
|
+
}
|
|
5101
|
+
handleCtrlC() {
|
|
4881
5102
|
if (!this.bufferIsEmpty()) {
|
|
4882
|
-
this.
|
|
5103
|
+
this.buffer = [""];
|
|
5104
|
+
this.row = 0;
|
|
5105
|
+
this.col = 0;
|
|
5106
|
+
if (this.queueIndex === -1) {
|
|
5107
|
+
this.historyIndex = -1;
|
|
5108
|
+
this.savedDraft = null;
|
|
5109
|
+
}
|
|
4883
5110
|
return [];
|
|
4884
5111
|
}
|
|
5112
|
+
if (this.queueIndex >= 0) {
|
|
5113
|
+
this.queueIndex = -1;
|
|
5114
|
+
this.restoreDraft();
|
|
5115
|
+
return [];
|
|
5116
|
+
}
|
|
5117
|
+
if (this.turnRunning) {
|
|
5118
|
+
return [{ type: "cancel" }];
|
|
5119
|
+
}
|
|
4885
5120
|
return [{ type: "exit" }];
|
|
4886
5121
|
}
|
|
4887
5122
|
};
|
|
4888
5123
|
}
|
|
4889
5124
|
});
|
|
4890
5125
|
|
|
5126
|
+
// src/tui/completion.ts
|
|
5127
|
+
function longestCommonPrefix(names) {
|
|
5128
|
+
if (names.length === 0) {
|
|
5129
|
+
return "";
|
|
5130
|
+
}
|
|
5131
|
+
let prefix = names[0] ?? "";
|
|
5132
|
+
for (let i = 1; i < names.length; i++) {
|
|
5133
|
+
const n = names[i] ?? "";
|
|
5134
|
+
let j = 0;
|
|
5135
|
+
while (j < prefix.length && j < n.length && prefix[j] === n[j]) {
|
|
5136
|
+
j += 1;
|
|
5137
|
+
}
|
|
5138
|
+
prefix = prefix.slice(0, j);
|
|
5139
|
+
if (prefix.length === 0) {
|
|
5140
|
+
break;
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
return prefix;
|
|
5144
|
+
}
|
|
5145
|
+
function computeTabCompletion(args) {
|
|
5146
|
+
const { matches, firstLine: firstLine3 } = args;
|
|
5147
|
+
if (matches.length === 0) {
|
|
5148
|
+
return null;
|
|
5149
|
+
}
|
|
5150
|
+
const space = firstLine3.indexOf(" ");
|
|
5151
|
+
const typedPrefix = space === -1 ? firstLine3 : firstLine3.slice(0, space);
|
|
5152
|
+
const tail = space === -1 ? "" : firstLine3.slice(space);
|
|
5153
|
+
if (matches.length === 1) {
|
|
5154
|
+
const name = matches[0] ?? "";
|
|
5155
|
+
const suffix = tail.startsWith(" ") ? "" : " ";
|
|
5156
|
+
return name + suffix + tail;
|
|
5157
|
+
}
|
|
5158
|
+
const commonPrefix = longestCommonPrefix(matches);
|
|
5159
|
+
if (commonPrefix.length <= typedPrefix.length) {
|
|
5160
|
+
return null;
|
|
5161
|
+
}
|
|
5162
|
+
return commonPrefix + tail;
|
|
5163
|
+
}
|
|
5164
|
+
var init_completion = __esm({
|
|
5165
|
+
"src/tui/completion.ts"() {
|
|
5166
|
+
"use strict";
|
|
5167
|
+
}
|
|
5168
|
+
});
|
|
5169
|
+
|
|
4891
5170
|
// src/tui/render-update.ts
|
|
4892
5171
|
import stripAnsi from "strip-ansi";
|
|
4893
5172
|
function sanitizeWireText(text) {
|
|
4894
5173
|
return stripAnsi(text).replace(STRIP_CONTROLS, "");
|
|
4895
5174
|
}
|
|
5175
|
+
function sanitizeSingleLine(text) {
|
|
5176
|
+
return sanitizeWireText(text).replace(/[\n\t]+/g, " ").replace(/ +/g, " ").trim();
|
|
5177
|
+
}
|
|
4896
5178
|
function mapUpdate(update) {
|
|
4897
5179
|
if (!update || typeof update !== "object") {
|
|
4898
5180
|
return null;
|
|
@@ -4936,7 +5218,7 @@ function mapUpdate(update) {
|
|
|
4936
5218
|
}
|
|
4937
5219
|
function mapSessionInfo(u) {
|
|
4938
5220
|
const rawTitle = readString(u, "title");
|
|
4939
|
-
const title = rawTitle !== void 0 ?
|
|
5221
|
+
const title = rawTitle !== void 0 ? sanitizeSingleLine(rawTitle) : void 0;
|
|
4940
5222
|
const meta = u._meta;
|
|
4941
5223
|
let agentId;
|
|
4942
5224
|
if (meta && typeof meta === "object" && !Array.isArray(meta)) {
|
|
@@ -4960,10 +5242,9 @@ function mapSessionInfo(u) {
|
|
|
4960
5242
|
}
|
|
4961
5243
|
return event;
|
|
4962
5244
|
}
|
|
4963
|
-
function
|
|
4964
|
-
const list = u.availableCommands ?? u.commands;
|
|
5245
|
+
function normalizeAdvertisedCommands(list) {
|
|
4965
5246
|
if (!Array.isArray(list)) {
|
|
4966
|
-
return
|
|
5247
|
+
return [];
|
|
4967
5248
|
}
|
|
4968
5249
|
const out = [];
|
|
4969
5250
|
for (const raw of list) {
|
|
@@ -4975,13 +5256,20 @@ function mapAvailableCommands(u) {
|
|
|
4975
5256
|
continue;
|
|
4976
5257
|
}
|
|
4977
5258
|
const rawName = c.name.startsWith("/") ? c.name : `/${c.name}`;
|
|
4978
|
-
const cmd = { name:
|
|
5259
|
+
const cmd = { name: sanitizeSingleLine(rawName) };
|
|
4979
5260
|
if (typeof c.description === "string") {
|
|
4980
|
-
cmd.description =
|
|
5261
|
+
cmd.description = sanitizeSingleLine(c.description);
|
|
4981
5262
|
}
|
|
4982
5263
|
out.push(cmd);
|
|
4983
5264
|
}
|
|
4984
|
-
return
|
|
5265
|
+
return out;
|
|
5266
|
+
}
|
|
5267
|
+
function mapAvailableCommands(u) {
|
|
5268
|
+
const list = u.availableCommands ?? u.commands;
|
|
5269
|
+
if (!Array.isArray(list)) {
|
|
5270
|
+
return null;
|
|
5271
|
+
}
|
|
5272
|
+
return { kind: "available-commands", commands: normalizeAdvertisedCommands(list) };
|
|
4985
5273
|
}
|
|
4986
5274
|
function mapUsage(u) {
|
|
4987
5275
|
const event = { kind: "usage-update" };
|
|
@@ -5043,7 +5331,7 @@ function mapToolCall(u) {
|
|
|
5043
5331
|
return null;
|
|
5044
5332
|
}
|
|
5045
5333
|
const rawTitle = readString(u, "title") ?? readString(u, "name") ?? readString(u, "label") ?? "tool call";
|
|
5046
|
-
const title =
|
|
5334
|
+
const title = sanitizeSingleLine(rawTitle);
|
|
5047
5335
|
const status = readString(u, "status");
|
|
5048
5336
|
const rawKind = readString(u, "kind");
|
|
5049
5337
|
const event = { kind: "tool-call", toolCallId, title };
|
|
@@ -5061,7 +5349,7 @@ function mapToolCallUpdate(u) {
|
|
|
5061
5349
|
return null;
|
|
5062
5350
|
}
|
|
5063
5351
|
const rawTitle = readString(u, "title");
|
|
5064
|
-
const title = rawTitle !== void 0 ?
|
|
5352
|
+
const title = rawTitle !== void 0 ? sanitizeSingleLine(rawTitle) : void 0;
|
|
5065
5353
|
const status = readString(u, "status");
|
|
5066
5354
|
const meaningful = title !== void 0 || status === "completed" || status === "failed" || status === "rejected" || status === "cancelled";
|
|
5067
5355
|
if (!meaningful) {
|
|
@@ -5087,7 +5375,7 @@ function mapPlan(u) {
|
|
|
5087
5375
|
continue;
|
|
5088
5376
|
}
|
|
5089
5377
|
const e = raw;
|
|
5090
|
-
const content = typeof e.content === "string" ?
|
|
5378
|
+
const content = typeof e.content === "string" ? sanitizeSingleLine(e.content) : void 0;
|
|
5091
5379
|
if (!content) {
|
|
5092
5380
|
continue;
|
|
5093
5381
|
}
|
|
@@ -5107,14 +5395,14 @@ function mapMode(u) {
|
|
|
5107
5395
|
if (!mode) {
|
|
5108
5396
|
return null;
|
|
5109
5397
|
}
|
|
5110
|
-
return { kind: "mode-changed", mode:
|
|
5398
|
+
return { kind: "mode-changed", mode: sanitizeSingleLine(mode) };
|
|
5111
5399
|
}
|
|
5112
5400
|
function mapModel(u) {
|
|
5113
5401
|
const model = readString(u, "currentModel") ?? readString(u, "model");
|
|
5114
5402
|
if (!model) {
|
|
5115
5403
|
return null;
|
|
5116
5404
|
}
|
|
5117
|
-
return { kind: "model-changed", model:
|
|
5405
|
+
return { kind: "model-changed", model: sanitizeSingleLine(model) };
|
|
5118
5406
|
}
|
|
5119
5407
|
function mapTurnComplete(u) {
|
|
5120
5408
|
const stopReason = readString(u, "stopReason");
|
|
@@ -5420,8 +5708,17 @@ function formatPlan(event) {
|
|
|
5420
5708
|
}
|
|
5421
5709
|
];
|
|
5422
5710
|
}
|
|
5711
|
+
const allComplete = event.entries.every(
|
|
5712
|
+
(e) => (e.status ?? "pending") === "completed"
|
|
5713
|
+
);
|
|
5714
|
+
const headerStyle = allComplete ? "plan-done" : "plan";
|
|
5423
5715
|
const lines = [
|
|
5424
|
-
{
|
|
5716
|
+
{
|
|
5717
|
+
prefix: "\u25A3 ",
|
|
5718
|
+
prefixStyle: headerStyle,
|
|
5719
|
+
body: "Plan",
|
|
5720
|
+
bodyStyle: headerStyle
|
|
5721
|
+
}
|
|
5425
5722
|
];
|
|
5426
5723
|
for (const entry of event.entries) {
|
|
5427
5724
|
const status = entry.status ?? "pending";
|
|
@@ -5594,7 +5891,8 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
5594
5891
|
const { update } = params ?? {};
|
|
5595
5892
|
const event = mapUpdate(update);
|
|
5596
5893
|
debugLogUpdate(update, event);
|
|
5597
|
-
|
|
5894
|
+
const rawTag = update?.sessionUpdate;
|
|
5895
|
+
if (rawTag === "prompt_received") {
|
|
5598
5896
|
adjustPendingTurns(1);
|
|
5599
5897
|
} else if (event?.kind === "turn-complete") {
|
|
5600
5898
|
adjustPendingTurns(-1);
|
|
@@ -5665,11 +5963,11 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
5665
5963
|
const rawOptions = Array.isArray(p.options) ? p.options : [];
|
|
5666
5964
|
const options = rawOptions.map((o) => ({
|
|
5667
5965
|
optionId: o.optionId,
|
|
5668
|
-
name:
|
|
5966
|
+
name: sanitizeSingleLine(o.name ?? ""),
|
|
5669
5967
|
...o.kind !== void 0 ? { kind: o.kind } : {}
|
|
5670
5968
|
}));
|
|
5671
5969
|
const rawTitle = p.toolCall?.title ?? p.toolCall?.name ?? "tool";
|
|
5672
|
-
const title =
|
|
5970
|
+
const title = sanitizeSingleLine(rawTitle);
|
|
5673
5971
|
const toolCallId = p.toolCall?.toolCallId;
|
|
5674
5972
|
if (options.length === 0) {
|
|
5675
5973
|
screen.appendLines([
|
|
@@ -5718,10 +6016,17 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
5718
6016
|
let initialCommands;
|
|
5719
6017
|
let initialTurnStartedAt;
|
|
5720
6018
|
if (ctx.sessionId === "__new__") {
|
|
6019
|
+
const hydraNewMeta = {};
|
|
6020
|
+
if (opts.name) {
|
|
6021
|
+
hydraNewMeta.name = opts.name;
|
|
6022
|
+
}
|
|
6023
|
+
if (opts.model) {
|
|
6024
|
+
hydraNewMeta.model = opts.model;
|
|
6025
|
+
}
|
|
5721
6026
|
const created = await conn.request("session/new", {
|
|
5722
6027
|
cwd: ctx.cwd,
|
|
5723
6028
|
...opts.agentId ? { agentId: opts.agentId } : {},
|
|
5724
|
-
...
|
|
6029
|
+
...Object.keys(hydraNewMeta).length > 0 ? { _meta: { [HYDRA_META_KEY]: hydraNewMeta } } : {}
|
|
5725
6030
|
});
|
|
5726
6031
|
resolvedSessionId = created.sessionId;
|
|
5727
6032
|
exitHint.sessionId = resolvedSessionId;
|
|
@@ -5740,9 +6045,7 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
5740
6045
|
initialMode = hydraMeta.currentMode;
|
|
5741
6046
|
initialTurnStartedAt = hydraMeta.turnStartedAt;
|
|
5742
6047
|
if (hydraMeta.availableCommands) {
|
|
5743
|
-
initialCommands = hydraMeta.availableCommands
|
|
5744
|
-
(c) => c.description !== void 0 ? { name: c.name, description: c.description } : { name: c.name }
|
|
5745
|
-
);
|
|
6048
|
+
initialCommands = normalizeAdvertisedCommands(hydraMeta.availableCommands);
|
|
5746
6049
|
}
|
|
5747
6050
|
} else {
|
|
5748
6051
|
const attached = await conn.request("session/attach", {
|
|
@@ -5767,9 +6070,7 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
5767
6070
|
initialMode = hydraMeta.currentMode;
|
|
5768
6071
|
initialTurnStartedAt = hydraMeta.turnStartedAt;
|
|
5769
6072
|
if (hydraMeta.availableCommands) {
|
|
5770
|
-
initialCommands = hydraMeta.availableCommands
|
|
5771
|
-
(c) => c.description !== void 0 ? { name: c.name, description: c.description } : { name: c.name }
|
|
5772
|
-
);
|
|
6073
|
+
initialCommands = normalizeAdvertisedCommands(hydraMeta.availableCommands);
|
|
5773
6074
|
}
|
|
5774
6075
|
}
|
|
5775
6076
|
const historyFile = paths.tuiHistoryFile(resolvedSessionId);
|
|
@@ -5780,11 +6081,13 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
5780
6081
|
dispatcher.setTurnRunning(true);
|
|
5781
6082
|
}
|
|
5782
6083
|
let turnInFlight = null;
|
|
6084
|
+
let pendingPrefill = null;
|
|
5783
6085
|
const screen = new Screen({
|
|
5784
6086
|
term,
|
|
5785
6087
|
dispatcher,
|
|
5786
6088
|
repaintThrottleMs: config.tui.repaintThrottleMs,
|
|
5787
6089
|
maxScrollbackLines: config.tui.maxScrollbackLines,
|
|
6090
|
+
mouse: config.tui.mouse,
|
|
5788
6091
|
onKey: (events) => {
|
|
5789
6092
|
for (const ev of events) {
|
|
5790
6093
|
if (pendingPermission && tryHandlePermissionKey(ev)) {
|
|
@@ -5811,6 +6114,7 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
5811
6114
|
{ name: "/quit", description: "Exit the TUI" },
|
|
5812
6115
|
{ name: "/clear", description: "Clear scrollback" },
|
|
5813
6116
|
{ name: "/sessions", description: "List sessions" },
|
|
6117
|
+
{ name: "/model", description: "Switch model: /model <model-id>" },
|
|
5814
6118
|
{ name: "/demo-plan", description: "Inject synthetic plan events (UI test)" },
|
|
5815
6119
|
{ name: "/demo-tool", description: "Inject a synthetic tool-call sequence (UI test)" }
|
|
5816
6120
|
];
|
|
@@ -5845,48 +6149,24 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
5845
6149
|
screen.setCompletions(currentCompletions());
|
|
5846
6150
|
};
|
|
5847
6151
|
const tryHandleCompletionKey = (ev) => {
|
|
5848
|
-
if (ev.type !== "key") {
|
|
6152
|
+
if (ev.type !== "key" || ev.name !== "tab") {
|
|
5849
6153
|
return false;
|
|
5850
6154
|
}
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
const typedPrefix = space === -1 ? firstLine3 : firstLine3.slice(0, space);
|
|
5862
|
-
const tail = space === -1 ? "" : firstLine3.slice(space);
|
|
5863
|
-
let next = commonPrefix;
|
|
5864
|
-
if (commonPrefix.length <= typedPrefix.length || matches.length === 1) {
|
|
5865
|
-
next = first.name + (tail.startsWith(" ") ? "" : " ");
|
|
5866
|
-
}
|
|
5867
|
-
dispatcher.replaceFirstLine(next + tail);
|
|
6155
|
+
const matches = currentCompletions();
|
|
6156
|
+
if (matches.length === 0) {
|
|
6157
|
+
return false;
|
|
6158
|
+
}
|
|
6159
|
+
const firstLine3 = dispatcher.state().buffer[0] ?? "";
|
|
6160
|
+
const next = computeTabCompletion({
|
|
6161
|
+
matches: matches.map((m) => m.name),
|
|
6162
|
+
firstLine: firstLine3
|
|
6163
|
+
});
|
|
6164
|
+
if (next === null) {
|
|
5868
6165
|
return true;
|
|
5869
6166
|
}
|
|
5870
|
-
|
|
6167
|
+
dispatcher.replaceFirstLine(next);
|
|
6168
|
+
return true;
|
|
5871
6169
|
};
|
|
5872
|
-
function longestCommonPrefix(names) {
|
|
5873
|
-
if (names.length === 0) {
|
|
5874
|
-
return "";
|
|
5875
|
-
}
|
|
5876
|
-
let prefix = names[0] ?? "";
|
|
5877
|
-
for (let i = 1; i < names.length; i++) {
|
|
5878
|
-
const n = names[i] ?? "";
|
|
5879
|
-
let j = 0;
|
|
5880
|
-
while (j < prefix.length && j < n.length && prefix[j] === n[j]) {
|
|
5881
|
-
j += 1;
|
|
5882
|
-
}
|
|
5883
|
-
prefix = prefix.slice(0, j);
|
|
5884
|
-
if (prefix.length === 0) {
|
|
5885
|
-
break;
|
|
5886
|
-
}
|
|
5887
|
-
}
|
|
5888
|
-
return prefix;
|
|
5889
|
-
}
|
|
5890
6170
|
const tryHandlePermissionKey = (ev) => {
|
|
5891
6171
|
if (!pendingPermission) {
|
|
5892
6172
|
return false;
|
|
@@ -6093,22 +6373,45 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
6093
6373
|
}
|
|
6094
6374
|
resume(nextOpts);
|
|
6095
6375
|
};
|
|
6376
|
+
const queueHeadOffset = () => workerActive ? 1 : 0;
|
|
6096
6377
|
const handleEffect = (effect) => {
|
|
6097
6378
|
switch (effect.type) {
|
|
6098
6379
|
case "send":
|
|
6099
6380
|
enqueuePrompt(effect.text, effect.planMode);
|
|
6100
6381
|
return;
|
|
6101
|
-
case "
|
|
6382
|
+
case "queue-edit": {
|
|
6383
|
+
const realIdx = effect.index + queueHeadOffset();
|
|
6384
|
+
const existing = promptQueue[realIdx];
|
|
6385
|
+
if (existing) {
|
|
6386
|
+
promptQueue[realIdx] = { text: effect.text, planMode: existing.planMode };
|
|
6387
|
+
refreshQueueDisplay();
|
|
6388
|
+
}
|
|
6389
|
+
return;
|
|
6390
|
+
}
|
|
6391
|
+
case "queue-remove": {
|
|
6392
|
+
const realIdx = effect.index + queueHeadOffset();
|
|
6393
|
+
if (realIdx >= 0 && realIdx < promptQueue.length) {
|
|
6394
|
+
promptQueue.splice(realIdx, 1);
|
|
6395
|
+
refreshQueueDisplay();
|
|
6396
|
+
}
|
|
6397
|
+
return;
|
|
6398
|
+
}
|
|
6399
|
+
case "cancel": {
|
|
6400
|
+
if (effect.prefill && turnInFlight) {
|
|
6401
|
+
const headOffset = workerActive ? 1 : 0;
|
|
6402
|
+
const waitingEmpty = promptQueue.length <= headOffset;
|
|
6403
|
+
const bufferEmpty = dispatcher.state().buffer.every((line) => line === "");
|
|
6404
|
+
if (waitingEmpty && bufferEmpty) {
|
|
6405
|
+
pendingPrefill = turnInFlight.text;
|
|
6406
|
+
}
|
|
6407
|
+
}
|
|
6102
6408
|
if (turnInFlight) {
|
|
6103
6409
|
turnInFlight.cancel();
|
|
6104
6410
|
} else if (pendingTurns > 0) {
|
|
6105
6411
|
cancelRemoteTurn();
|
|
6106
6412
|
}
|
|
6107
|
-
if (promptQueue.length > (workerActive ? 1 : 0)) {
|
|
6108
|
-
promptQueue.length = workerActive ? 1 : 0;
|
|
6109
|
-
refreshQueueDisplay();
|
|
6110
|
-
}
|
|
6111
6413
|
return;
|
|
6414
|
+
}
|
|
6112
6415
|
case "exit":
|
|
6113
6416
|
void requestExit();
|
|
6114
6417
|
return;
|
|
@@ -6121,6 +6424,12 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
6121
6424
|
case "redraw":
|
|
6122
6425
|
screen.fullRedraw();
|
|
6123
6426
|
return;
|
|
6427
|
+
case "scroll-to-top":
|
|
6428
|
+
screen.scrollToTop();
|
|
6429
|
+
return;
|
|
6430
|
+
case "scroll-to-bottom":
|
|
6431
|
+
screen.scrollToBottom();
|
|
6432
|
+
return;
|
|
6124
6433
|
case "switch-session":
|
|
6125
6434
|
void switchSession();
|
|
6126
6435
|
return;
|
|
@@ -6136,6 +6445,7 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
6136
6445
|
const waiting = promptQueue.slice(workerActive ? 1 : 0);
|
|
6137
6446
|
screen.setQueuedPrompts(waiting.map((p) => p.text));
|
|
6138
6447
|
screen.setBanner({ queued: waiting.length });
|
|
6448
|
+
dispatcher.setQueue(waiting.map((p) => p.text));
|
|
6139
6449
|
};
|
|
6140
6450
|
const enqueuePrompt = (text, planMode) => {
|
|
6141
6451
|
screen.scrollToBottom();
|
|
@@ -6264,6 +6574,40 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
6264
6574
|
}
|
|
6265
6575
|
]);
|
|
6266
6576
|
return true;
|
|
6577
|
+
case "/model": {
|
|
6578
|
+
const arg = space === -1 ? "" : trimmed.slice(space + 1).trim();
|
|
6579
|
+
if (arg === "") {
|
|
6580
|
+
screen.appendLines([
|
|
6581
|
+
{
|
|
6582
|
+
prefix: " ",
|
|
6583
|
+
body: "Usage: /model <model-id>",
|
|
6584
|
+
bodyStyle: "info"
|
|
6585
|
+
}
|
|
6586
|
+
]);
|
|
6587
|
+
return true;
|
|
6588
|
+
}
|
|
6589
|
+
conn.request("session/set_model", {
|
|
6590
|
+
sessionId: resolvedSessionId,
|
|
6591
|
+
modelId: arg
|
|
6592
|
+
}).then(() => {
|
|
6593
|
+
screen.appendLines([
|
|
6594
|
+
{
|
|
6595
|
+
prefix: " ",
|
|
6596
|
+
body: `model set to ${arg}`,
|
|
6597
|
+
bodyStyle: "system"
|
|
6598
|
+
}
|
|
6599
|
+
]);
|
|
6600
|
+
}).catch((err) => {
|
|
6601
|
+
screen.appendLines([
|
|
6602
|
+
{
|
|
6603
|
+
prefix: " ",
|
|
6604
|
+
body: `set_model failed: ${err.message}`,
|
|
6605
|
+
bodyStyle: "tool-status-fail"
|
|
6606
|
+
}
|
|
6607
|
+
]);
|
|
6608
|
+
});
|
|
6609
|
+
return true;
|
|
6610
|
+
}
|
|
6267
6611
|
default:
|
|
6268
6612
|
return false;
|
|
6269
6613
|
}
|
|
@@ -6283,6 +6627,15 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
6283
6627
|
} finally {
|
|
6284
6628
|
workerActive = false;
|
|
6285
6629
|
refreshQueueDisplay();
|
|
6630
|
+
if (pendingPrefill !== null) {
|
|
6631
|
+
const text = pendingPrefill;
|
|
6632
|
+
pendingPrefill = null;
|
|
6633
|
+
const bufferEmpty = dispatcher.state().buffer.every((line) => line === "");
|
|
6634
|
+
if (bufferEmpty) {
|
|
6635
|
+
dispatcher.setBuffer(text);
|
|
6636
|
+
screen.refreshPrompt();
|
|
6637
|
+
}
|
|
6638
|
+
}
|
|
6286
6639
|
}
|
|
6287
6640
|
};
|
|
6288
6641
|
const processPrompt = async (text, planMode) => {
|
|
@@ -6292,6 +6645,7 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
6292
6645
|
appendRender({ kind: "user-text", text });
|
|
6293
6646
|
let cancelled = false;
|
|
6294
6647
|
turnInFlight = {
|
|
6648
|
+
text,
|
|
6295
6649
|
cancel: () => {
|
|
6296
6650
|
if (cancelled) {
|
|
6297
6651
|
return;
|
|
@@ -6388,12 +6742,13 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
6388
6742
|
}
|
|
6389
6743
|
summary = parts.join(" \xB7 ");
|
|
6390
6744
|
}
|
|
6745
|
+
const pureThinking = total === 0 && inProgress;
|
|
6391
6746
|
const lines = [
|
|
6392
6747
|
{
|
|
6393
6748
|
prefix: "\u2692 ",
|
|
6394
|
-
prefixStyle: "tool",
|
|
6749
|
+
prefixStyle: pureThinking ? "tool-status-running" : "tool",
|
|
6395
6750
|
body: summary,
|
|
6396
|
-
bodyStyle: "dim"
|
|
6751
|
+
bodyStyle: pureThinking ? "tool-status-running" : "dim"
|
|
6397
6752
|
}
|
|
6398
6753
|
];
|
|
6399
6754
|
for (const id of visibleIds) {
|
|
@@ -6569,6 +6924,8 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
6569
6924
|
}, 1e3);
|
|
6570
6925
|
}
|
|
6571
6926
|
startToolsBlock();
|
|
6927
|
+
} else if (initialTurnStartedAt === void 0 && pendingTurns > 0) {
|
|
6928
|
+
adjustPendingTurns(-pendingTurns);
|
|
6572
6929
|
}
|
|
6573
6930
|
const resetInFlightUiState = () => {
|
|
6574
6931
|
if (pendingPermission) {
|
|
@@ -6770,6 +7127,7 @@ var init_app = __esm({
|
|
|
6770
7127
|
init_picker();
|
|
6771
7128
|
init_screen();
|
|
6772
7129
|
init_input();
|
|
7130
|
+
init_completion();
|
|
6773
7131
|
init_render_update();
|
|
6774
7132
|
init_format();
|
|
6775
7133
|
PLAN_PREFIX_TEXT = "Plan mode is on. Outline what you would do without making any changes. Do not edit files, run shell commands, or otherwise execute side effects; produce a plan only.";
|
|
@@ -6856,35 +7214,28 @@ init_config();
|
|
|
6856
7214
|
import * as fs2 from "fs/promises";
|
|
6857
7215
|
async function runInit(flags) {
|
|
6858
7216
|
await fs2.mkdir(paths.home(), { recursive: true });
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
existing = void 0;
|
|
6864
|
-
}
|
|
6865
|
-
if (!existing) {
|
|
6866
|
-
const config = await writeMinimalInitConfig();
|
|
7217
|
+
const existingToken = await loadAuthToken();
|
|
7218
|
+
if (!existingToken) {
|
|
7219
|
+
const token = generateAuthToken();
|
|
7220
|
+
await writeAuthToken(token);
|
|
6867
7221
|
process.stdout.write(
|
|
6868
|
-
`Initialized ${paths.
|
|
6869
|
-
Auth token: ${
|
|
7222
|
+
`Initialized ${paths.authToken()}
|
|
7223
|
+
Auth token: ${token}
|
|
6870
7224
|
`
|
|
6871
7225
|
);
|
|
6872
7226
|
return;
|
|
6873
7227
|
}
|
|
6874
7228
|
if (flagBool(flags, "rotate-token")) {
|
|
6875
7229
|
const newToken = generateAuthToken();
|
|
6876
|
-
await
|
|
6877
|
-
const daemon = raw.daemon ??= {};
|
|
6878
|
-
daemon.authToken = newToken;
|
|
6879
|
-
});
|
|
7230
|
+
await writeAuthToken(newToken);
|
|
6880
7231
|
process.stdout.write(
|
|
6881
|
-
`Rotated token in ${paths.
|
|
7232
|
+
`Rotated token in ${paths.authToken()}
|
|
6882
7233
|
New token: ${newToken}
|
|
6883
7234
|
`
|
|
6884
7235
|
);
|
|
6885
7236
|
return;
|
|
6886
7237
|
}
|
|
6887
|
-
process.stdout.write(`
|
|
7238
|
+
process.stdout.write(`Auth token already exists at ${paths.authToken()}.
|
|
6888
7239
|
`);
|
|
6889
7240
|
process.stdout.write("Pass --rotate-token to generate a new auth token.\n");
|
|
6890
7241
|
}
|
|
@@ -7275,13 +7626,13 @@ function npxPackageBasename(agent) {
|
|
|
7275
7626
|
const atIdx = afterSlash.lastIndexOf("@");
|
|
7276
7627
|
return atIdx <= 0 ? afterSlash : afterSlash.slice(0, atIdx);
|
|
7277
7628
|
}
|
|
7278
|
-
async function planSpawn(agent,
|
|
7629
|
+
async function planSpawn(agent, callerArgs = []) {
|
|
7279
7630
|
if (agent.distribution.npx) {
|
|
7280
7631
|
const npx = agent.distribution.npx;
|
|
7281
|
-
const
|
|
7632
|
+
const tail = callerArgs.length > 0 ? callerArgs : npx.args ?? [];
|
|
7282
7633
|
return {
|
|
7283
7634
|
command: "npx",
|
|
7284
|
-
args,
|
|
7635
|
+
args: ["-y", npx.package, ...tail],
|
|
7285
7636
|
env: npx.env ?? {}
|
|
7286
7637
|
};
|
|
7287
7638
|
}
|
|
@@ -7297,18 +7648,19 @@ async function planSpawn(agent, extraArgs = []) {
|
|
|
7297
7648
|
version: agent.version ?? "current",
|
|
7298
7649
|
target
|
|
7299
7650
|
});
|
|
7651
|
+
const tail = callerArgs.length > 0 ? callerArgs : target.args ?? [];
|
|
7300
7652
|
return {
|
|
7301
7653
|
command: cmdPath,
|
|
7302
|
-
args:
|
|
7654
|
+
args: tail,
|
|
7303
7655
|
env: target.env ?? {}
|
|
7304
7656
|
};
|
|
7305
7657
|
}
|
|
7306
7658
|
if (agent.distribution.uvx) {
|
|
7307
7659
|
const uvx = agent.distribution.uvx;
|
|
7308
|
-
const
|
|
7660
|
+
const tail = callerArgs.length > 0 ? callerArgs : uvx.args ?? [];
|
|
7309
7661
|
return {
|
|
7310
7662
|
command: "uvx",
|
|
7311
|
-
args,
|
|
7663
|
+
args: [uvx.package, ...tail],
|
|
7312
7664
|
env: uvx.env ?? {}
|
|
7313
7665
|
};
|
|
7314
7666
|
}
|
|
@@ -7816,7 +8168,8 @@ var SessionManager = class {
|
|
|
7816
8168
|
agentId: params.agentId,
|
|
7817
8169
|
cwd: params.cwd,
|
|
7818
8170
|
agentArgs: params.agentArgs,
|
|
7819
|
-
mcpServers: params.mcpServers
|
|
8171
|
+
mcpServers: params.mcpServers,
|
|
8172
|
+
model: params.model
|
|
7820
8173
|
});
|
|
7821
8174
|
const session = new Session({
|
|
7822
8175
|
cwd: params.cwd,
|
|
@@ -8008,7 +8361,7 @@ var SessionManager = class {
|
|
|
8008
8361
|
);
|
|
8009
8362
|
}
|
|
8010
8363
|
let initialModel = extractInitialModel(newResult);
|
|
8011
|
-
const desired = this.defaultModels[params.agentId];
|
|
8364
|
+
const desired = params.model ?? this.defaultModels[params.agentId];
|
|
8012
8365
|
if (desired && desired !== initialModel) {
|
|
8013
8366
|
try {
|
|
8014
8367
|
await agent.connection.request("session/set_model", {
|
|
@@ -9459,7 +9812,8 @@ function registerAcpWsEndpoint(app, deps) {
|
|
|
9459
9812
|
agentId: params.agentId ?? deps.defaultAgent,
|
|
9460
9813
|
mcpServers: params.mcpServers,
|
|
9461
9814
|
title: hydraMeta.name,
|
|
9462
|
-
agentArgs: hydraMeta.agentArgs
|
|
9815
|
+
agentArgs: hydraMeta.agentArgs,
|
|
9816
|
+
model: hydraMeta.model
|
|
9463
9817
|
});
|
|
9464
9818
|
const client = bindClientToSession(connection, session, state);
|
|
9465
9819
|
await session.attach(client, "full");
|
|
@@ -10834,6 +11188,9 @@ function wireShim({
|
|
|
10834
11188
|
outgoing = injectHydraMeta(outgoing, { name: namingState.name });
|
|
10835
11189
|
namingState.used = true;
|
|
10836
11190
|
}
|
|
11191
|
+
if (opts.model) {
|
|
11192
|
+
outgoing = injectHydraMeta(outgoing, { model: opts.model });
|
|
11193
|
+
}
|
|
10837
11194
|
void upstream.send(outgoing);
|
|
10838
11195
|
return;
|
|
10839
11196
|
}
|
|
@@ -10976,10 +11333,10 @@ async function main() {
|
|
|
10976
11333
|
const positionalAgentId = afterLaunch[0];
|
|
10977
11334
|
const agentArgs = afterLaunch.slice(1);
|
|
10978
11335
|
const { flags: flags2 } = parseArgs(beforeLaunch);
|
|
10979
|
-
const agentId = positionalAgentId ?? resolveOption(flags2, "agent
|
|
11336
|
+
const agentId = positionalAgentId ?? resolveOption(flags2, "agent");
|
|
10980
11337
|
if (!agentId) {
|
|
10981
11338
|
process.stderr.write(
|
|
10982
|
-
"Usage: hydra-acp launch <agent
|
|
11339
|
+
"Usage: hydra-acp launch <agent> [agent-args...]\n"
|
|
10983
11340
|
);
|
|
10984
11341
|
process.exit(2);
|
|
10985
11342
|
return;
|
|
@@ -10987,7 +11344,8 @@ async function main() {
|
|
|
10987
11344
|
const launchResume = flags2.resume;
|
|
10988
11345
|
const sessionId2 = typeof launchResume === "string" ? launchResume : resolveOption(flags2, "session-id");
|
|
10989
11346
|
const name2 = resolveOption(flags2, "name");
|
|
10990
|
-
|
|
11347
|
+
const model2 = resolveOption(flags2, "model");
|
|
11348
|
+
await runShim({ sessionId: sessionId2, agentId, agentArgs, name: name2, model: model2 });
|
|
10991
11349
|
return;
|
|
10992
11350
|
}
|
|
10993
11351
|
const { positional, flags } = parseArgs(argv);
|
|
@@ -11004,22 +11362,24 @@ async function main() {
|
|
|
11004
11362
|
const resumeFlag = flags.resume;
|
|
11005
11363
|
const sessionId = typeof resumeFlag === "string" ? resumeFlag : resolveOption(flags, "session-id");
|
|
11006
11364
|
const name = resolveOption(flags, "name");
|
|
11007
|
-
const agentIdFromFlag = resolveOption(flags, "agent
|
|
11365
|
+
const agentIdFromFlag = resolveOption(flags, "agent");
|
|
11366
|
+
const model = resolveOption(flags, "model");
|
|
11008
11367
|
if (!subcommand) {
|
|
11009
11368
|
if (process.stdout.isTTY) {
|
|
11010
11369
|
await dispatchTui(flags, {
|
|
11011
11370
|
sessionId,
|
|
11012
11371
|
agentId: agentIdFromFlag,
|
|
11013
|
-
name
|
|
11372
|
+
name,
|
|
11373
|
+
model
|
|
11014
11374
|
});
|
|
11015
11375
|
return;
|
|
11016
11376
|
}
|
|
11017
|
-
await runShim({ sessionId, name, agentId: agentIdFromFlag });
|
|
11377
|
+
await runShim({ sessionId, name, agentId: agentIdFromFlag, model });
|
|
11018
11378
|
return;
|
|
11019
11379
|
}
|
|
11020
11380
|
switch (subcommand) {
|
|
11021
11381
|
case "shim":
|
|
11022
|
-
await runShim({ sessionId, name, agentId: agentIdFromFlag });
|
|
11382
|
+
await runShim({ sessionId, name, agentId: agentIdFromFlag, model });
|
|
11023
11383
|
return;
|
|
11024
11384
|
case "init":
|
|
11025
11385
|
await runInit(flags);
|
|
@@ -11141,7 +11501,8 @@ async function main() {
|
|
|
11141
11501
|
await dispatchTui(flags, {
|
|
11142
11502
|
sessionId,
|
|
11143
11503
|
agentId: agentIdFromFlag,
|
|
11144
|
-
name
|
|
11504
|
+
name,
|
|
11505
|
+
model
|
|
11145
11506
|
});
|
|
11146
11507
|
return;
|
|
11147
11508
|
default:
|
|
@@ -11169,6 +11530,9 @@ async function dispatchTui(flags, base) {
|
|
|
11169
11530
|
if (base.name !== void 0) {
|
|
11170
11531
|
tuiOpts.name = base.name;
|
|
11171
11532
|
}
|
|
11533
|
+
if (base.model !== void 0) {
|
|
11534
|
+
tuiOpts.model = base.model;
|
|
11535
|
+
}
|
|
11172
11536
|
await runTui(tuiOpts);
|
|
11173
11537
|
}
|
|
11174
11538
|
function readVersion() {
|
|
@@ -11191,9 +11555,9 @@ function printHelp() {
|
|
|
11191
11555
|
" hydra-acp Auto: TUI when stdout is a TTY, shim otherwise (the editor-spawned case)",
|
|
11192
11556
|
" hydra-acp shim Run as ACP shim explicitly (forces shim mode regardless of TTY)",
|
|
11193
11557
|
" hydra-acp tui [opts] Run the terminal UI explicitly (see below for opts)",
|
|
11194
|
-
" hydra-acp launch <agent
|
|
11195
|
-
" Shim mode, force daemon to spawn <agent
|
|
11196
|
-
" from the registry. Args after <agent
|
|
11558
|
+
" hydra-acp launch <agent> [agent-args...]",
|
|
11559
|
+
" Shim mode, force daemon to spawn <agent>",
|
|
11560
|
+
" from the registry. Args after <agent>",
|
|
11197
11561
|
" are forwarded to the agent's command.",
|
|
11198
11562
|
" hydra-acp --resume <id> Attach to an existing session (TUI when in a terminal, shim otherwise)",
|
|
11199
11563
|
" hydra-acp init [--rotate-token] Initialize ~/.hydra-acp/config.json",
|
|
@@ -11214,14 +11578,15 @@ function printHelp() {
|
|
|
11214
11578
|
" hydra-acp extensions logs <name> [-f] [-n N]Tail or follow an extension's log",
|
|
11215
11579
|
" hydra-acp agents [list] List agents in the cached registry",
|
|
11216
11580
|
" hydra-acp agents refresh Force a registry re-fetch",
|
|
11217
|
-
" hydra-acp tui flags: [--resume [<id>]] [--new] [--agent
|
|
11581
|
+
" hydra-acp tui flags: [--resume [<id>]] [--new] [--agent <id>] [--model <id>] [--cwd <path>] [--name <label>]",
|
|
11218
11582
|
" --resume <id> attaches to a specific session; bare --resume picks the most-recent",
|
|
11219
11583
|
" in cwd. Smart default (no flags): picks if any live sessions exist, else new.",
|
|
11220
11584
|
" hydra-acp --version Print version",
|
|
11221
11585
|
" hydra-acp --help Show this help",
|
|
11222
11586
|
"",
|
|
11223
11587
|
"Config knob flags accept env-var equivalents (flag wins):",
|
|
11224
|
-
" --agent
|
|
11588
|
+
" --agent HYDRA_ACP_AGENT",
|
|
11589
|
+
" --model HYDRA_ACP_MODEL (one-shot at session/new; ignored on --resume)",
|
|
11225
11590
|
" --resume / --session-id HYDRA_ACP_SESSION_ID",
|
|
11226
11591
|
" --name HYDRA_ACP_NAME",
|
|
11227
11592
|
""
|