@hydra-acp/cli 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +196 -19
- package/dist/index.d.ts +46 -0
- package/dist/index.js +106 -8
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -264,7 +264,13 @@ var init_config = __esm({
|
|
|
264
264
|
// Width cap on the cwd column in the `sessions list` output and the
|
|
265
265
|
// TUI picker. Set higher if you keep deeply-nested working directories
|
|
266
266
|
// and want them visible; the elastic title column shrinks to make room.
|
|
267
|
-
cwdColumnMaxWidth: z.number().int().positive().default(24)
|
|
267
|
+
cwdColumnMaxWidth: z.number().int().positive().default(24),
|
|
268
|
+
// When true (default), emit OSC 9;4 progress-bar control codes so the
|
|
269
|
+
// host terminal can show an indeterminate busy indicator (taskbar pulse
|
|
270
|
+
// on Windows Terminal, dock badge on KDE/Konsole, etc.) while a turn is
|
|
271
|
+
// running. Set false if your terminal renders this obnoxiously or you
|
|
272
|
+
// just don't want it.
|
|
273
|
+
progressIndicator: z.boolean().default(true)
|
|
268
274
|
});
|
|
269
275
|
ExtensionName = z.string().min(1).regex(/^[A-Za-z0-9._-]+$/, "extension name must be filename-safe");
|
|
270
276
|
ExtensionBody = z.object({
|
|
@@ -302,11 +308,12 @@ var init_config = __esm({
|
|
|
302
308
|
maxScrollbackLines: 1e4,
|
|
303
309
|
mouse: true,
|
|
304
310
|
logMaxBytes: 5 * 1024 * 1024,
|
|
305
|
-
cwdColumnMaxWidth: 24
|
|
311
|
+
cwdColumnMaxWidth: 24,
|
|
312
|
+
progressIndicator: true
|
|
306
313
|
})
|
|
307
314
|
});
|
|
308
315
|
HydraConfigReadOnly = HydraConfig.extend({
|
|
309
|
-
daemon: DaemonConfig.omit({ authToken: true })
|
|
316
|
+
daemon: DaemonConfig.omit({ authToken: true }).default({})
|
|
310
317
|
});
|
|
311
318
|
}
|
|
312
319
|
});
|
|
@@ -483,6 +490,11 @@ var init_types = __esm({
|
|
|
483
490
|
// Last-known usage snapshot so list views can show per-session cost
|
|
484
491
|
// (and tokens, in callers that care) without resurrecting cold sessions.
|
|
485
492
|
currentUsage: SessionListUsage.optional(),
|
|
493
|
+
// Origin host (and origin upstream id) for imported sessions. Picker
|
|
494
|
+
// uses the host to fill in the UPSTREAM cell pre-first-attach;
|
|
495
|
+
// future "connect back to origin" callers would dial both.
|
|
496
|
+
importedFromMachine: z3.string().optional(),
|
|
497
|
+
importedFromUpstreamSessionId: z3.string().optional(),
|
|
486
498
|
updatedAt: z3.string(),
|
|
487
499
|
attachedClients: z3.number().int().nonnegative(),
|
|
488
500
|
status: z3.enum(["live", "cold"]).default("live"),
|
|
@@ -563,6 +575,15 @@ var init_connection = __esm({
|
|
|
563
575
|
}
|
|
564
576
|
}
|
|
565
577
|
}
|
|
578
|
+
// Discard any notifications buffered for the given method without firing
|
|
579
|
+
// handlers. Used by the resurrect path to drop the agent's session/load
|
|
580
|
+
// replay: that replay is the agent re-emitting our own history back at
|
|
581
|
+
// us, and if we flushed it through wireAgent's session/update handler
|
|
582
|
+
// every entry would be re-appended to history.jsonl, doubling the log
|
|
583
|
+
// each time the session was woken up.
|
|
584
|
+
drainBuffered(method) {
|
|
585
|
+
this.bufferedNotifications.delete(method);
|
|
586
|
+
}
|
|
566
587
|
onClose(handler) {
|
|
567
588
|
this.closeHandlers.push(handler);
|
|
568
589
|
}
|
|
@@ -997,6 +1018,7 @@ var init_session = __esm({
|
|
|
997
1018
|
// and noisy state churn keep a quiet session alive forever.
|
|
998
1019
|
lastRecordedAt;
|
|
999
1020
|
spawnReplacementAgent;
|
|
1021
|
+
logger;
|
|
1000
1022
|
agentChangeHandlers = [];
|
|
1001
1023
|
// Last available_commands_update we observed from the agent. Stored
|
|
1002
1024
|
// so we can re-broadcast a merged (hydra ∪ agent) list whenever
|
|
@@ -1028,6 +1050,7 @@ var init_session = __esm({
|
|
|
1028
1050
|
}
|
|
1029
1051
|
this.idleTimeoutMs = init.idleTimeoutMs ?? 0;
|
|
1030
1052
|
this.spawnReplacementAgent = init.spawnReplacementAgent;
|
|
1053
|
+
this.logger = init.logger;
|
|
1031
1054
|
if (init.firstPromptSeeded) {
|
|
1032
1055
|
this.firstPromptSeeded = true;
|
|
1033
1056
|
}
|
|
@@ -1357,6 +1380,9 @@ var init_session = __esm({
|
|
|
1357
1380
|
if (this.closed) {
|
|
1358
1381
|
return;
|
|
1359
1382
|
}
|
|
1383
|
+
this.logger?.info(
|
|
1384
|
+
`session ${this.sessionId} closing deleteRecord=${opts.deleteRecord ?? false} regenTitle=${opts.regenTitle ?? false}`
|
|
1385
|
+
);
|
|
1360
1386
|
this.cancelIdleTimer();
|
|
1361
1387
|
if (opts.regenTitle && this.firstPromptSeeded) {
|
|
1362
1388
|
const timeoutMs = opts.regenTitleTimeoutMs ?? 5e3;
|
|
@@ -1911,6 +1937,10 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
|
|
|
1911
1937
|
return;
|
|
1912
1938
|
}
|
|
1913
1939
|
const opts = this.firstPromptSeeded ? { deleteRecord: false, regenTitle: true } : { deleteRecord: true };
|
|
1940
|
+
const idleSec = Math.round(idle / 1e3);
|
|
1941
|
+
this.logger?.info(
|
|
1942
|
+
`session ${this.sessionId} idle timeout fired after ${idleSec}s (window=${Math.round(this.idleTimeoutMs / 1e3)}s) \u2014 closing`
|
|
1943
|
+
);
|
|
1914
1944
|
void this.close(opts).catch(() => void 0);
|
|
1915
1945
|
}
|
|
1916
1946
|
cancelIdleTimer() {
|
|
@@ -2087,6 +2117,8 @@ function recordFromMemorySession(args) {
|
|
|
2087
2117
|
lineageId: args.lineageId,
|
|
2088
2118
|
upstreamSessionId: args.upstreamSessionId,
|
|
2089
2119
|
importedFromSessionId: args.importedFromSessionId,
|
|
2120
|
+
importedFromUpstreamSessionId: args.importedFromUpstreamSessionId,
|
|
2121
|
+
importedFromMachine: args.importedFromMachine,
|
|
2090
2122
|
agentId: args.agentId,
|
|
2091
2123
|
cwd: args.cwd,
|
|
2092
2124
|
title: args.title,
|
|
@@ -2134,6 +2166,16 @@ var init_session_store = __esm({
|
|
|
2134
2166
|
// origin's local id at export time, kept for debuggability and as a
|
|
2135
2167
|
// breadcrumb in `sessions list` (informational, not used for routing).
|
|
2136
2168
|
importedFromSessionId: z4.string().optional(),
|
|
2169
|
+
// Origin's agent-side session id at export time. Carried as a
|
|
2170
|
+
// breadcrumb and as the handle a future "connect back to origin"
|
|
2171
|
+
// feature would dial. Absent when the origin record had no upstream
|
|
2172
|
+
// bound (re-export of an imported, not-yet-attached session).
|
|
2173
|
+
importedFromUpstreamSessionId: z4.string().optional(),
|
|
2174
|
+
// Hostname of the machine that exported the bundle we imported
|
|
2175
|
+
// (i.e. the most recent hop, not necessarily the true multi-hop
|
|
2176
|
+
// origin). Surfaced in the picker so imported rows don't look like
|
|
2177
|
+
// they materialized from nowhere.
|
|
2178
|
+
importedFromMachine: z4.string().optional(),
|
|
2137
2179
|
agentId: z4.string(),
|
|
2138
2180
|
cwd: z4.string(),
|
|
2139
2181
|
title: z4.string().optional(),
|
|
@@ -2349,6 +2391,7 @@ function encodeBundle(params) {
|
|
|
2349
2391
|
session: {
|
|
2350
2392
|
sessionId: params.record.sessionId,
|
|
2351
2393
|
lineageId: params.record.lineageId,
|
|
2394
|
+
...params.record.upstreamSessionId ? { upstreamSessionId: params.record.upstreamSessionId } : {},
|
|
2352
2395
|
agentId: params.record.agentId,
|
|
2353
2396
|
cwd: params.record.cwd,
|
|
2354
2397
|
...params.record.title !== void 0 ? { title: params.record.title } : {},
|
|
@@ -2386,6 +2429,12 @@ var init_bundle = __esm({
|
|
|
2386
2429
|
// Required on bundles — the export path backfills if the source
|
|
2387
2430
|
// record was written before lineageId existed.
|
|
2388
2431
|
lineageId: z5.string(),
|
|
2432
|
+
// The exporter's agent-side session id at export time. Carried so
|
|
2433
|
+
// importers can persist it as a breadcrumb (and, eventually, as the
|
|
2434
|
+
// handle a "connect back to origin" feature would need). Omitted on
|
|
2435
|
+
// bundles whose source record never bound to an agent (e.g. a
|
|
2436
|
+
// re-export of an imported, not-yet-attached session).
|
|
2437
|
+
upstreamSessionId: z5.string().optional(),
|
|
2389
2438
|
agentId: z5.string(),
|
|
2390
2439
|
cwd: z5.string(),
|
|
2391
2440
|
title: z5.string().optional(),
|
|
@@ -2599,7 +2648,7 @@ var init_agent_display = __esm({
|
|
|
2599
2648
|
function toRow(s, now = Date.now()) {
|
|
2600
2649
|
return {
|
|
2601
2650
|
session: stripHydraSessionPrefix(s.sessionId),
|
|
2602
|
-
upstream: s.upstreamSessionId
|
|
2651
|
+
upstream: formatUpstreamCell(s.upstreamSessionId, s.importedFromMachine),
|
|
2603
2652
|
state: formatState(s.status, s.attachedClients),
|
|
2604
2653
|
agent: formatAgentCell(s.agentId, s.currentUsage),
|
|
2605
2654
|
age: formatRelativeAge(s.updatedAt, now),
|
|
@@ -2607,6 +2656,15 @@ function toRow(s, now = Date.now()) {
|
|
|
2607
2656
|
cwd: shortenHomePath(s.cwd)
|
|
2608
2657
|
};
|
|
2609
2658
|
}
|
|
2659
|
+
function formatUpstreamCell(upstreamSessionId, importedFromMachine) {
|
|
2660
|
+
if (upstreamSessionId && upstreamSessionId.length > 0) {
|
|
2661
|
+
return upstreamSessionId;
|
|
2662
|
+
}
|
|
2663
|
+
if (importedFromMachine && importedFromMachine.length > 0) {
|
|
2664
|
+
return `\u2190 ${importedFromMachine}`;
|
|
2665
|
+
}
|
|
2666
|
+
return "-";
|
|
2667
|
+
}
|
|
2610
2668
|
function formatState(status, clients) {
|
|
2611
2669
|
if (status === "cold") {
|
|
2612
2670
|
return "COLD";
|
|
@@ -2946,11 +3004,11 @@ async function runSessionsImport(file, opts = {}) {
|
|
|
2946
3004
|
function bundleToSummary(parsed) {
|
|
2947
3005
|
return {
|
|
2948
3006
|
sessionId: parsed.session.sessionId,
|
|
2949
|
-
upstreamSessionId: "-",
|
|
2950
3007
|
cwd: parsed.session.cwd,
|
|
2951
3008
|
agentId: parsed.session.agentId,
|
|
2952
3009
|
currentUsage: parsed.session.currentUsage,
|
|
2953
3010
|
title: parsed.session.title,
|
|
3011
|
+
importedFromMachine: parsed.exportedFrom.machine,
|
|
2954
3012
|
attachedClients: 0,
|
|
2955
3013
|
updatedAt: parsed.session.updatedAt,
|
|
2956
3014
|
status: "cold"
|
|
@@ -2971,10 +3029,13 @@ function printBundleInfo(raw, cwdColumnMaxWidth) {
|
|
|
2971
3029
|
const maxWidth = process.stdout.isTTY ? process.stdout.columns : void 0;
|
|
2972
3030
|
process.stdout.write(formatRow(HEADER, widths, maxWidth, cwdColumnMaxWidth) + "\n");
|
|
2973
3031
|
process.stdout.write(formatRow(row, widths, maxWidth, cwdColumnMaxWidth) + "\n");
|
|
3032
|
+
const originUpstream = parsed.session.upstreamSessionId ?? "-";
|
|
2974
3033
|
process.stdout.write(
|
|
2975
3034
|
`
|
|
2976
3035
|
lineage: ${parsed.session.lineageId}
|
|
2977
3036
|
exported: ${parsed.exportedAt} from ${parsed.exportedFrom.machine} (hydra ${parsed.exportedFrom.hydraVersion})
|
|
3037
|
+
origin session: ${parsed.session.sessionId}
|
|
3038
|
+
origin upstream: ${originUpstream}
|
|
2978
3039
|
history entries: ${parsed.history.length}` + (parsed.promptHistory ? `, prompt history: ${parsed.promptHistory.length}
|
|
2979
3040
|
` : "\n")
|
|
2980
3041
|
);
|
|
@@ -3275,7 +3336,9 @@ async function listSessions(config, opts = {}, fetchImpl = fetch) {
|
|
|
3275
3336
|
agentId: s.agentId,
|
|
3276
3337
|
currentModel: s.currentModel,
|
|
3277
3338
|
currentUsage: s.currentUsage,
|
|
3278
|
-
title: s.title
|
|
3339
|
+
title: s.title,
|
|
3340
|
+
importedFromMachine: s.importedFromMachine,
|
|
3341
|
+
importedFromUpstreamSessionId: s.importedFromUpstreamSessionId
|
|
3279
3342
|
}));
|
|
3280
3343
|
}
|
|
3281
3344
|
async function killSession(config, id, fetchImpl = fetch) {
|
|
@@ -4427,6 +4490,12 @@ var init_screen = __esm({
|
|
|
4427
4490
|
pasteBuffer = "";
|
|
4428
4491
|
rawStdinHandler;
|
|
4429
4492
|
mouseEnabled;
|
|
4493
|
+
progressIndicatorEnabled;
|
|
4494
|
+
// Last OSC 9;4 state we wrote (3 = indeterminate, 0 = remove). Used to
|
|
4495
|
+
// suppress redundant writes when setBanner runs but `status` didn't
|
|
4496
|
+
// actually change, and to re-emit on start() if a picker round-trip
|
|
4497
|
+
// cleared the host terminal's indicator.
|
|
4498
|
+
lastProgressState = 0;
|
|
4430
4499
|
constructor(opts) {
|
|
4431
4500
|
this.term = opts.term;
|
|
4432
4501
|
this.dispatcher = opts.dispatcher;
|
|
@@ -4434,6 +4503,7 @@ var init_screen = __esm({
|
|
|
4434
4503
|
this.contentRepaintThrottleMs = opts.repaintThrottleMs ?? DEFAULT_CONTENT_REPAINT_THROTTLE_MS;
|
|
4435
4504
|
this.maxScrollbackLines = opts.maxScrollbackLines ?? DEFAULT_MAX_SCROLLBACK_LINES;
|
|
4436
4505
|
this.mouseEnabled = opts.mouse ?? true;
|
|
4506
|
+
this.progressIndicatorEnabled = opts.progressIndicator ?? true;
|
|
4437
4507
|
this.resizeHandler = () => this.repaint();
|
|
4438
4508
|
this.keyHandler = (name, _matches, data) => this.handleKey(name, data);
|
|
4439
4509
|
this.mouseHandler = (name) => this.handleMouse(name);
|
|
@@ -4462,6 +4532,8 @@ var init_screen = __esm({
|
|
|
4462
4532
|
}
|
|
4463
4533
|
this.term.on("resize", this.resizeHandler);
|
|
4464
4534
|
this.installBracketedPaste();
|
|
4535
|
+
this.lastProgressState = 0;
|
|
4536
|
+
this.writeProgressIndicator(this.banner.status === "busy" ? 3 : 0);
|
|
4465
4537
|
this.repaint();
|
|
4466
4538
|
}
|
|
4467
4539
|
stop() {
|
|
@@ -4482,6 +4554,7 @@ var init_screen = __esm({
|
|
|
4482
4554
|
this.term.grabInput(false);
|
|
4483
4555
|
this.term.hideCursor(false);
|
|
4484
4556
|
process.stdout.write("\x1B[?7h");
|
|
4557
|
+
this.writeProgressIndicator(0);
|
|
4485
4558
|
this.term.fullscreen(false);
|
|
4486
4559
|
this.term("\n");
|
|
4487
4560
|
}
|
|
@@ -4770,9 +4843,26 @@ var init_screen = __esm({
|
|
|
4770
4843
|
}
|
|
4771
4844
|
setBanner(banner) {
|
|
4772
4845
|
this.banner = { ...this.banner, ...banner };
|
|
4846
|
+
this.writeProgressIndicator(this.banner.status === "busy" ? 3 : 0);
|
|
4773
4847
|
this.drawBanner();
|
|
4774
4848
|
this.placeCursor();
|
|
4775
4849
|
}
|
|
4850
|
+
// OSC 9;4 progress-bar control. State 3 = indeterminate (pulsing
|
|
4851
|
+
// taskbar / dock badge while a turn is running); state 0 = remove.
|
|
4852
|
+
// ConEmu-flavor sequence — supported by Windows Terminal, WezTerm,
|
|
4853
|
+
// Ghostty, Konsole, Black Box, Rio, and others; ignored harmlessly
|
|
4854
|
+
// by terminals that don't implement it. Disabled entirely when
|
|
4855
|
+
// tui.progressIndicator is false.
|
|
4856
|
+
writeProgressIndicator(state) {
|
|
4857
|
+
if (!this.progressIndicatorEnabled) {
|
|
4858
|
+
return;
|
|
4859
|
+
}
|
|
4860
|
+
if (state === this.lastProgressState) {
|
|
4861
|
+
return;
|
|
4862
|
+
}
|
|
4863
|
+
this.lastProgressState = state;
|
|
4864
|
+
process.stdout.write(`\x1B]9;4;${state}\x1B\\`);
|
|
4865
|
+
}
|
|
4776
4866
|
// Transient right-side banner message. Cleared automatically after
|
|
4777
4867
|
// durationMs (default 4s). Each call resets the timer, so rapid
|
|
4778
4868
|
// successive notifications coalesce on the latest text. Active
|
|
@@ -7502,6 +7592,7 @@ async function runSession(term, config, opts, exitHint) {
|
|
|
7502
7592
|
repaintThrottleMs: config.tui.repaintThrottleMs,
|
|
7503
7593
|
maxScrollbackLines: config.tui.maxScrollbackLines,
|
|
7504
7594
|
mouse: config.tui.mouse,
|
|
7595
|
+
progressIndicator: config.tui.progressIndicator,
|
|
7505
7596
|
onKey: (events) => {
|
|
7506
7597
|
for (const ev of events) {
|
|
7507
7598
|
if (pendingPermission && tryHandlePermissionKey(ev)) {
|
|
@@ -8642,6 +8733,17 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
8642
8733
|
import { dirname as dirname6, resolve as resolve4 } from "path";
|
|
8643
8734
|
|
|
8644
8735
|
// src/cli/parse-args.ts
|
|
8736
|
+
var KNOWN_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
8737
|
+
"all",
|
|
8738
|
+
"foreground",
|
|
8739
|
+
"help",
|
|
8740
|
+
"info",
|
|
8741
|
+
"new",
|
|
8742
|
+
"reattach",
|
|
8743
|
+
"replace",
|
|
8744
|
+
"rotate-token",
|
|
8745
|
+
"version"
|
|
8746
|
+
]);
|
|
8645
8747
|
function parseArgs(argv) {
|
|
8646
8748
|
const positional = [];
|
|
8647
8749
|
const flags = {};
|
|
@@ -8660,6 +8762,11 @@ function parseArgs(argv) {
|
|
|
8660
8762
|
continue;
|
|
8661
8763
|
}
|
|
8662
8764
|
const key = token.slice(2);
|
|
8765
|
+
if (KNOWN_BOOLEAN_FLAGS.has(key)) {
|
|
8766
|
+
flags[key] = true;
|
|
8767
|
+
i += 1;
|
|
8768
|
+
continue;
|
|
8769
|
+
}
|
|
8663
8770
|
const next = argv[i + 1];
|
|
8664
8771
|
if (next !== void 0 && !next.startsWith("--")) {
|
|
8665
8772
|
flags[key] = next;
|
|
@@ -9383,12 +9490,14 @@ var AgentInstance = class _AgentInstance {
|
|
|
9383
9490
|
killed = false;
|
|
9384
9491
|
stderrTail = "";
|
|
9385
9492
|
stderrTailBytes;
|
|
9493
|
+
logger;
|
|
9386
9494
|
exitHandlers = [];
|
|
9387
9495
|
constructor(opts, child) {
|
|
9388
9496
|
this.agentId = opts.agentId;
|
|
9389
9497
|
this.cwd = opts.cwd;
|
|
9390
9498
|
this.child = child;
|
|
9391
9499
|
this.stderrTailBytes = opts.stderrTailBytes ?? DEFAULT_STDERR_TAIL_BYTES;
|
|
9500
|
+
this.logger = opts.logger;
|
|
9392
9501
|
if (!child.stdout || !child.stdin) {
|
|
9393
9502
|
throw new Error("agent subprocess missing stdio");
|
|
9394
9503
|
}
|
|
@@ -9397,7 +9506,15 @@ var AgentInstance = class _AgentInstance {
|
|
|
9397
9506
|
child.stderr?.setEncoding("utf8");
|
|
9398
9507
|
child.stderr?.on("data", (chunk) => {
|
|
9399
9508
|
this.stderrTail = (this.stderrTail + chunk).slice(-this.stderrTailBytes);
|
|
9400
|
-
|
|
9509
|
+
if (this.logger) {
|
|
9510
|
+
for (const line of chunk.split(/\r?\n/)) {
|
|
9511
|
+
if (line.length > 0) {
|
|
9512
|
+
this.logger.info(`[${opts.agentId}] ${line}`);
|
|
9513
|
+
}
|
|
9514
|
+
}
|
|
9515
|
+
} else {
|
|
9516
|
+
process.stderr.write(`[${opts.agentId}] ${chunk}`);
|
|
9517
|
+
}
|
|
9401
9518
|
});
|
|
9402
9519
|
child.on("error", (err) => {
|
|
9403
9520
|
const msg = this.formatFailure(err.message);
|
|
@@ -9405,9 +9522,16 @@ var AgentInstance = class _AgentInstance {
|
|
|
9405
9522
|
});
|
|
9406
9523
|
child.on("exit", (code, signal) => {
|
|
9407
9524
|
this.exited = true;
|
|
9408
|
-
if (
|
|
9525
|
+
if (this.killed) {
|
|
9526
|
+
this.logger?.info(
|
|
9527
|
+
`agent ${opts.agentId} pid=${child.pid} exited after kill code=${code} signal=${signal}`
|
|
9528
|
+
);
|
|
9529
|
+
} else {
|
|
9409
9530
|
const reason = `agent ${opts.agentId} exited before responding (code=${code} signal=${signal})`;
|
|
9410
9531
|
this.connection.fail(new Error(this.formatFailure(reason)));
|
|
9532
|
+
this.logger?.warn(
|
|
9533
|
+
`agent ${opts.agentId} pid=${child.pid} exited unexpectedly code=${code} signal=${signal}`
|
|
9534
|
+
);
|
|
9411
9535
|
}
|
|
9412
9536
|
for (const handler of this.exitHandlers) {
|
|
9413
9537
|
handler(code, signal);
|
|
@@ -9428,7 +9552,15 @@ stderr: ${tail}` : reason;
|
|
|
9428
9552
|
const child = spawn3(opts.plan.command, opts.plan.args, {
|
|
9429
9553
|
cwd: opts.cwd,
|
|
9430
9554
|
env,
|
|
9431
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
9555
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
9556
|
+
// setsid the agent into its own session/process group. The daemon
|
|
9557
|
+
// already runs in its own setsid'd session, but macOS terminals
|
|
9558
|
+
// (iTerm2, Terminal.app) sometimes still reach inherited child
|
|
9559
|
+
// processes when the user closes a window — putting the agent
|
|
9560
|
+
// one more session-boundary away keeps it alive across terminal
|
|
9561
|
+
// restarts. The daemon still owns the pipes, so this.kill()
|
|
9562
|
+
// continues to terminate it cleanly on idle/close.
|
|
9563
|
+
detached: true
|
|
9432
9564
|
});
|
|
9433
9565
|
return new _AgentInstance(opts, child);
|
|
9434
9566
|
}
|
|
@@ -9443,6 +9575,9 @@ stderr: ${tail}` : reason;
|
|
|
9443
9575
|
return;
|
|
9444
9576
|
}
|
|
9445
9577
|
this.killed = true;
|
|
9578
|
+
this.logger?.info(
|
|
9579
|
+
`agent ${this.agentId} pid=${this.child.pid} kill requested signal=${signal}`
|
|
9580
|
+
);
|
|
9446
9581
|
await this.connection.close().catch(() => void 0);
|
|
9447
9582
|
this.child.kill(signal);
|
|
9448
9583
|
}
|
|
@@ -9628,6 +9763,7 @@ var SessionManager = class {
|
|
|
9628
9763
|
this.histories = new HistoryStore({ maxEntries: this.sessionHistoryMaxEntries });
|
|
9629
9764
|
this.idleTimeoutMs = options.idleTimeoutMs ?? 0;
|
|
9630
9765
|
this.defaultModels = options.defaultModels ?? {};
|
|
9766
|
+
this.logger = options.logger;
|
|
9631
9767
|
}
|
|
9632
9768
|
registry;
|
|
9633
9769
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -9642,6 +9778,7 @@ var SessionManager = class {
|
|
|
9642
9778
|
// concurrent snapshot updates (e.g. an agent emitting model + mode
|
|
9643
9779
|
// back-to-back) don't lose writes via interleaved reads.
|
|
9644
9780
|
metaWriteQueues = /* @__PURE__ */ new Map();
|
|
9781
|
+
logger;
|
|
9645
9782
|
async create(params) {
|
|
9646
9783
|
const fresh = await this.bootstrapAgent({
|
|
9647
9784
|
agentId: params.agentId,
|
|
@@ -9659,6 +9796,7 @@ var SessionManager = class {
|
|
|
9659
9796
|
title: params.title,
|
|
9660
9797
|
agentArgs: params.agentArgs,
|
|
9661
9798
|
idleTimeoutMs: this.idleTimeoutMs,
|
|
9799
|
+
logger: this.logger,
|
|
9662
9800
|
spawnReplacementAgent: (p) => this.bootstrapAgent({ ...p, mcpServers: [] }),
|
|
9663
9801
|
historyStore: this.histories,
|
|
9664
9802
|
historyMaxEntries: this.sessionHistoryMaxEntries,
|
|
@@ -9741,6 +9879,7 @@ var SessionManager = class {
|
|
|
9741
9879
|
await agent.kill().catch(() => void 0);
|
|
9742
9880
|
return this.doResurrectFromImport(params);
|
|
9743
9881
|
}
|
|
9882
|
+
agent.connection.drainBuffered("session/update");
|
|
9744
9883
|
const session = new Session({
|
|
9745
9884
|
sessionId: params.hydraSessionId,
|
|
9746
9885
|
cwd: params.cwd,
|
|
@@ -9751,6 +9890,7 @@ var SessionManager = class {
|
|
|
9751
9890
|
title: params.title,
|
|
9752
9891
|
agentArgs: params.agentArgs,
|
|
9753
9892
|
idleTimeoutMs: this.idleTimeoutMs,
|
|
9893
|
+
logger: this.logger,
|
|
9754
9894
|
spawnReplacementAgent: (p) => this.bootstrapAgent({ ...p, mcpServers: [] }),
|
|
9755
9895
|
historyStore: this.histories,
|
|
9756
9896
|
historyMaxEntries: this.sessionHistoryMaxEntries,
|
|
@@ -9797,6 +9937,7 @@ var SessionManager = class {
|
|
|
9797
9937
|
title: params.title,
|
|
9798
9938
|
agentArgs: params.agentArgs,
|
|
9799
9939
|
idleTimeoutMs: this.idleTimeoutMs,
|
|
9940
|
+
logger: this.logger,
|
|
9800
9941
|
spawnReplacementAgent: (p) => this.bootstrapAgent({ ...p, mcpServers: [] }),
|
|
9801
9942
|
historyStore: this.histories,
|
|
9802
9943
|
historyMaxEntries: this.sessionHistoryMaxEntries,
|
|
@@ -10061,6 +10202,8 @@ var SessionManager = class {
|
|
|
10061
10202
|
agentId: r.agentId,
|
|
10062
10203
|
currentModel: r.currentModel,
|
|
10063
10204
|
currentUsage: r.currentUsage,
|
|
10205
|
+
importedFromMachine: r.importedFromMachine,
|
|
10206
|
+
importedFromUpstreamSessionId: r.importedFromUpstreamSessionId,
|
|
10064
10207
|
updatedAt: used,
|
|
10065
10208
|
attachedClients: 0,
|
|
10066
10209
|
status: "cold"
|
|
@@ -10168,6 +10311,8 @@ var SessionManager = class {
|
|
|
10168
10311
|
lineageId: args.bundle.session.lineageId,
|
|
10169
10312
|
upstreamSessionId: "",
|
|
10170
10313
|
importedFromSessionId: args.bundle.session.sessionId,
|
|
10314
|
+
importedFromUpstreamSessionId: args.bundle.session.upstreamSessionId,
|
|
10315
|
+
importedFromMachine: args.bundle.exportedFrom.machine,
|
|
10171
10316
|
agentId: args.bundle.session.agentId,
|
|
10172
10317
|
cwd: args.cwd ?? args.bundle.session.cwd,
|
|
10173
10318
|
title: args.bundle.session.title,
|
|
@@ -10290,6 +10435,8 @@ function mergeForPersistence(session, existing) {
|
|
|
10290
10435
|
lineageId: existing?.lineageId ?? generateLineageId(),
|
|
10291
10436
|
upstreamSessionId: session.upstreamSessionId,
|
|
10292
10437
|
importedFromSessionId: existing?.importedFromSessionId,
|
|
10438
|
+
importedFromUpstreamSessionId: existing?.importedFromUpstreamSessionId,
|
|
10439
|
+
importedFromMachine: existing?.importedFromMachine,
|
|
10293
10440
|
agentId: session.agentId,
|
|
10294
10441
|
cwd: session.cwd,
|
|
10295
10442
|
title: session.title,
|
|
@@ -11569,11 +11716,20 @@ async function startDaemon(config) {
|
|
|
11569
11716
|
await auth(request, reply);
|
|
11570
11717
|
});
|
|
11571
11718
|
const registry = new Registry(config);
|
|
11572
|
-
const
|
|
11719
|
+
const agentLogger = {
|
|
11720
|
+
info: (msg) => app.log.info(msg),
|
|
11721
|
+
warn: (msg) => app.log.warn(msg)
|
|
11722
|
+
};
|
|
11723
|
+
const spawner = (opts) => AgentInstance.spawn({
|
|
11724
|
+
...opts,
|
|
11725
|
+
stderrTailBytes: config.daemon.agentStderrTailBytes,
|
|
11726
|
+
logger: agentLogger
|
|
11727
|
+
});
|
|
11573
11728
|
const manager = new SessionManager(registry, spawner, void 0, {
|
|
11574
11729
|
idleTimeoutMs: config.daemon.sessionIdleTimeoutSeconds * 1e3,
|
|
11575
11730
|
defaultModels: config.defaultModels,
|
|
11576
|
-
sessionHistoryMaxEntries: config.daemon.sessionHistoryMaxEntries
|
|
11731
|
+
sessionHistoryMaxEntries: config.daemon.sessionHistoryMaxEntries,
|
|
11732
|
+
logger: agentLogger
|
|
11577
11733
|
});
|
|
11578
11734
|
const extensions = new ExtensionManager(extensionList(config));
|
|
11579
11735
|
registerHealthRoutes(app, HYDRA_VERSION);
|
|
@@ -11810,6 +11966,7 @@ async function runDaemonStart(flags = {}) {
|
|
|
11810
11966
|
};
|
|
11811
11967
|
process.on("SIGINT", () => void shutdown());
|
|
11812
11968
|
process.on("SIGTERM", () => void shutdown());
|
|
11969
|
+
process.on("SIGHUP", () => void 0);
|
|
11813
11970
|
return;
|
|
11814
11971
|
}
|
|
11815
11972
|
spawnDaemonDetached();
|
|
@@ -12880,6 +13037,17 @@ async function main() {
|
|
|
12880
13037
|
const positionalAgentId = afterLaunch[0];
|
|
12881
13038
|
const agentArgs = afterLaunch.slice(1);
|
|
12882
13039
|
const { flags: flags2 } = parseArgs(beforeLaunch);
|
|
13040
|
+
if (flags2.resume === true) {
|
|
13041
|
+
bareResumeError();
|
|
13042
|
+
return;
|
|
13043
|
+
}
|
|
13044
|
+
if (flags2.reattach === true) {
|
|
13045
|
+
process.stderr.write(
|
|
13046
|
+
"hydra-acp launch: --reattach is not valid here. Pass --resume <id> to attach to a specific session.\n"
|
|
13047
|
+
);
|
|
13048
|
+
process.exit(2);
|
|
13049
|
+
return;
|
|
13050
|
+
}
|
|
12883
13051
|
const agentId = positionalAgentId ?? resolveOption(flags2, "agent");
|
|
12884
13052
|
if (!agentId) {
|
|
12885
13053
|
process.stderr.write(
|
|
@@ -12888,8 +13056,7 @@ async function main() {
|
|
|
12888
13056
|
process.exit(2);
|
|
12889
13057
|
return;
|
|
12890
13058
|
}
|
|
12891
|
-
const
|
|
12892
|
-
const sessionId2 = typeof launchResume === "string" ? launchResume : resolveOption(flags2, "session-id");
|
|
13059
|
+
const sessionId2 = typeof flags2.resume === "string" ? flags2.resume : resolveOption(flags2, "session-id");
|
|
12893
13060
|
const name2 = resolveOption(flags2, "name");
|
|
12894
13061
|
const model2 = resolveOption(flags2, "model");
|
|
12895
13062
|
await runShim({ sessionId: sessionId2, agentId, agentArgs, name: name2, model: model2 });
|
|
@@ -12906,8 +13073,11 @@ async function main() {
|
|
|
12906
13073
|
return;
|
|
12907
13074
|
}
|
|
12908
13075
|
const subcommand = positional[0];
|
|
12909
|
-
|
|
12910
|
-
|
|
13076
|
+
if (flags.resume === true) {
|
|
13077
|
+
bareResumeError();
|
|
13078
|
+
return;
|
|
13079
|
+
}
|
|
13080
|
+
const sessionId = typeof flags.resume === "string" ? flags.resume : resolveOption(flags, "session-id");
|
|
12911
13081
|
const name = resolveOption(flags, "name");
|
|
12912
13082
|
const agentIdFromFlag = resolveOption(flags, "agent");
|
|
12913
13083
|
const model = resolveOption(flags, "model");
|
|
@@ -13064,7 +13234,7 @@ async function main() {
|
|
|
13064
13234
|
}
|
|
13065
13235
|
async function dispatchTui(flags, base) {
|
|
13066
13236
|
const cwd = resolveOption(flags, "cwd");
|
|
13067
|
-
const resume = flags.
|
|
13237
|
+
const resume = flags.reattach === true;
|
|
13068
13238
|
const forceNew = flags.new === true;
|
|
13069
13239
|
const { runTui } = await Promise.resolve().then(() => (init_tui(), tui_exports));
|
|
13070
13240
|
const tuiOpts = { resume, forceNew };
|
|
@@ -13085,6 +13255,12 @@ async function dispatchTui(flags, base) {
|
|
|
13085
13255
|
}
|
|
13086
13256
|
await runTui(tuiOpts);
|
|
13087
13257
|
}
|
|
13258
|
+
function bareResumeError() {
|
|
13259
|
+
process.stderr.write(
|
|
13260
|
+
"hydra-acp: --resume requires a session id. Use --resume <id> to attach to a specific session, or --reattach to pick the most recent one in cwd.\n"
|
|
13261
|
+
);
|
|
13262
|
+
process.exit(2);
|
|
13263
|
+
}
|
|
13088
13264
|
function readVersion() {
|
|
13089
13265
|
try {
|
|
13090
13266
|
const here = dirname6(fileURLToPath2(import.meta.url));
|
|
@@ -13110,6 +13286,7 @@ function printHelp() {
|
|
|
13110
13286
|
" from the registry. Args after <agent>",
|
|
13111
13287
|
" are forwarded to the agent's command.",
|
|
13112
13288
|
" hydra-acp --resume <id> Attach to an existing session (TUI when in a terminal, shim otherwise)",
|
|
13289
|
+
" hydra-acp --reattach Attach to the most-recent session for the current cwd (TUI/shim auto-pick)",
|
|
13113
13290
|
" hydra-acp init [--rotate-token] Initialize ~/.hydra-acp/config.json",
|
|
13114
13291
|
" hydra-acp daemon start [--foreground] Start daemon (detached by default; --foreground to attach)",
|
|
13115
13292
|
" hydra-acp daemon stop|restart|status",
|
|
@@ -13128,9 +13305,9 @@ function printHelp() {
|
|
|
13128
13305
|
" hydra-acp extensions logs <name> [-f] [-n N]Tail or follow an extension's log",
|
|
13129
13306
|
" hydra-acp agents [list] List agents in the cached registry",
|
|
13130
13307
|
" hydra-acp agents refresh Force a registry re-fetch",
|
|
13131
|
-
" hydra-acp tui flags: [--resume
|
|
13132
|
-
" --resume <id> attaches to a specific session;
|
|
13133
|
-
"
|
|
13308
|
+
" hydra-acp tui flags: [--resume <id>] [--reattach] [--new] [--agent <id>] [--model <id>] [--cwd <path>] [--name <label>]",
|
|
13309
|
+
" --resume <id> attaches to a specific session; --reattach picks the most-recent in cwd.",
|
|
13310
|
+
" Smart default (no flags): shows a picker when sessions exist, else new.",
|
|
13134
13311
|
" hydra-acp --version Print version",
|
|
13135
13312
|
" hydra-acp --help Show this help",
|
|
13136
13313
|
"",
|
package/dist/index.d.ts
CHANGED
|
@@ -103,18 +103,21 @@ declare const HydraConfig: z.ZodObject<{
|
|
|
103
103
|
mouse: z.ZodDefault<z.ZodBoolean>;
|
|
104
104
|
logMaxBytes: z.ZodDefault<z.ZodNumber>;
|
|
105
105
|
cwdColumnMaxWidth: z.ZodDefault<z.ZodNumber>;
|
|
106
|
+
progressIndicator: z.ZodDefault<z.ZodBoolean>;
|
|
106
107
|
}, "strip", z.ZodTypeAny, {
|
|
107
108
|
repaintThrottleMs: number;
|
|
108
109
|
maxScrollbackLines: number;
|
|
109
110
|
mouse: boolean;
|
|
110
111
|
logMaxBytes: number;
|
|
111
112
|
cwdColumnMaxWidth: number;
|
|
113
|
+
progressIndicator: boolean;
|
|
112
114
|
}, {
|
|
113
115
|
repaintThrottleMs?: number | undefined;
|
|
114
116
|
maxScrollbackLines?: number | undefined;
|
|
115
117
|
mouse?: boolean | undefined;
|
|
116
118
|
logMaxBytes?: number | undefined;
|
|
117
119
|
cwdColumnMaxWidth?: number | undefined;
|
|
120
|
+
progressIndicator?: boolean | undefined;
|
|
118
121
|
}>>;
|
|
119
122
|
}, "strip", z.ZodTypeAny, {
|
|
120
123
|
daemon: {
|
|
@@ -142,6 +145,7 @@ declare const HydraConfig: z.ZodObject<{
|
|
|
142
145
|
mouse: boolean;
|
|
143
146
|
logMaxBytes: number;
|
|
144
147
|
cwdColumnMaxWidth: number;
|
|
148
|
+
progressIndicator: boolean;
|
|
145
149
|
};
|
|
146
150
|
registry: {
|
|
147
151
|
url: string;
|
|
@@ -177,6 +181,7 @@ declare const HydraConfig: z.ZodObject<{
|
|
|
177
181
|
mouse?: boolean | undefined;
|
|
178
182
|
logMaxBytes?: number | undefined;
|
|
179
183
|
cwdColumnMaxWidth?: number | undefined;
|
|
184
|
+
progressIndicator?: boolean | undefined;
|
|
180
185
|
} | undefined;
|
|
181
186
|
registry?: {
|
|
182
187
|
url?: string | undefined;
|
|
@@ -1330,6 +1335,8 @@ declare const SessionListEntry: z.ZodObject<{
|
|
|
1330
1335
|
costAmount?: number | undefined;
|
|
1331
1336
|
costCurrency?: string | undefined;
|
|
1332
1337
|
}>>;
|
|
1338
|
+
importedFromMachine: z.ZodOptional<z.ZodString>;
|
|
1339
|
+
importedFromUpstreamSessionId: z.ZodOptional<z.ZodString>;
|
|
1333
1340
|
updatedAt: z.ZodString;
|
|
1334
1341
|
attachedClients: z.ZodNumber;
|
|
1335
1342
|
status: z.ZodDefault<z.ZodEnum<["live", "cold"]>>;
|
|
@@ -1351,6 +1358,8 @@ declare const SessionListEntry: z.ZodObject<{
|
|
|
1351
1358
|
costAmount?: number | undefined;
|
|
1352
1359
|
costCurrency?: string | undefined;
|
|
1353
1360
|
} | undefined;
|
|
1361
|
+
importedFromMachine?: string | undefined;
|
|
1362
|
+
importedFromUpstreamSessionId?: string | undefined;
|
|
1354
1363
|
}, {
|
|
1355
1364
|
cwd: string;
|
|
1356
1365
|
sessionId: string;
|
|
@@ -1368,6 +1377,8 @@ declare const SessionListEntry: z.ZodObject<{
|
|
|
1368
1377
|
costAmount?: number | undefined;
|
|
1369
1378
|
costCurrency?: string | undefined;
|
|
1370
1379
|
} | undefined;
|
|
1380
|
+
importedFromMachine?: string | undefined;
|
|
1381
|
+
importedFromUpstreamSessionId?: string | undefined;
|
|
1371
1382
|
}>;
|
|
1372
1383
|
type SessionListEntry = z.infer<typeof SessionListEntry>;
|
|
1373
1384
|
declare const SessionListResult: z.ZodObject<{
|
|
@@ -1394,6 +1405,8 @@ declare const SessionListResult: z.ZodObject<{
|
|
|
1394
1405
|
costAmount?: number | undefined;
|
|
1395
1406
|
costCurrency?: string | undefined;
|
|
1396
1407
|
}>>;
|
|
1408
|
+
importedFromMachine: z.ZodOptional<z.ZodString>;
|
|
1409
|
+
importedFromUpstreamSessionId: z.ZodOptional<z.ZodString>;
|
|
1397
1410
|
updatedAt: z.ZodString;
|
|
1398
1411
|
attachedClients: z.ZodNumber;
|
|
1399
1412
|
status: z.ZodDefault<z.ZodEnum<["live", "cold"]>>;
|
|
@@ -1415,6 +1428,8 @@ declare const SessionListResult: z.ZodObject<{
|
|
|
1415
1428
|
costAmount?: number | undefined;
|
|
1416
1429
|
costCurrency?: string | undefined;
|
|
1417
1430
|
} | undefined;
|
|
1431
|
+
importedFromMachine?: string | undefined;
|
|
1432
|
+
importedFromUpstreamSessionId?: string | undefined;
|
|
1418
1433
|
}, {
|
|
1419
1434
|
cwd: string;
|
|
1420
1435
|
sessionId: string;
|
|
@@ -1432,6 +1447,8 @@ declare const SessionListResult: z.ZodObject<{
|
|
|
1432
1447
|
costAmount?: number | undefined;
|
|
1433
1448
|
costCurrency?: string | undefined;
|
|
1434
1449
|
} | undefined;
|
|
1450
|
+
importedFromMachine?: string | undefined;
|
|
1451
|
+
importedFromUpstreamSessionId?: string | undefined;
|
|
1435
1452
|
}>, "many">;
|
|
1436
1453
|
nextCursor: z.ZodOptional<z.ZodString>;
|
|
1437
1454
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -1452,6 +1469,8 @@ declare const SessionListResult: z.ZodObject<{
|
|
|
1452
1469
|
costAmount?: number | undefined;
|
|
1453
1470
|
costCurrency?: string | undefined;
|
|
1454
1471
|
} | undefined;
|
|
1472
|
+
importedFromMachine?: string | undefined;
|
|
1473
|
+
importedFromUpstreamSessionId?: string | undefined;
|
|
1455
1474
|
}[];
|
|
1456
1475
|
nextCursor?: string | undefined;
|
|
1457
1476
|
}, {
|
|
@@ -1472,6 +1491,8 @@ declare const SessionListResult: z.ZodObject<{
|
|
|
1472
1491
|
costAmount?: number | undefined;
|
|
1473
1492
|
costCurrency?: string | undefined;
|
|
1474
1493
|
} | undefined;
|
|
1494
|
+
importedFromMachine?: string | undefined;
|
|
1495
|
+
importedFromUpstreamSessionId?: string | undefined;
|
|
1475
1496
|
}[];
|
|
1476
1497
|
nextCursor?: string | undefined;
|
|
1477
1498
|
}>;
|
|
@@ -1532,6 +1553,7 @@ declare class JsonRpcConnection {
|
|
|
1532
1553
|
onRequest(method: string, handler: RequestHandler): void;
|
|
1533
1554
|
setDefaultHandler(handler: RequestHandler): void;
|
|
1534
1555
|
onNotification(method: string, handler: NotificationHandler): void;
|
|
1556
|
+
drainBuffered(method: string): void;
|
|
1535
1557
|
onClose(handler: (err?: Error) => void): void;
|
|
1536
1558
|
request<T = unknown>(method: string, params?: unknown): Promise<T>;
|
|
1537
1559
|
requestWithId<T = unknown>(method: string, params?: unknown): {
|
|
@@ -1555,6 +1577,11 @@ interface AgentInstanceOptions {
|
|
|
1555
1577
|
plan: SpawnPlan;
|
|
1556
1578
|
extraEnv?: Record<string, string>;
|
|
1557
1579
|
stderrTailBytes?: number;
|
|
1580
|
+
logger?: AgentLogger;
|
|
1581
|
+
}
|
|
1582
|
+
interface AgentLogger {
|
|
1583
|
+
info: (msg: string) => void;
|
|
1584
|
+
warn: (msg: string) => void;
|
|
1558
1585
|
}
|
|
1559
1586
|
declare class AgentInstance {
|
|
1560
1587
|
readonly agentId: string;
|
|
@@ -1565,6 +1592,7 @@ declare class AgentInstance {
|
|
|
1565
1592
|
private killed;
|
|
1566
1593
|
private stderrTail;
|
|
1567
1594
|
private stderrTailBytes;
|
|
1595
|
+
private logger?;
|
|
1568
1596
|
private exitHandlers;
|
|
1569
1597
|
private constructor();
|
|
1570
1598
|
private formatFailure;
|
|
@@ -1635,6 +1663,10 @@ interface SessionInit {
|
|
|
1635
1663
|
agentMeta?: Record<string, unknown>;
|
|
1636
1664
|
agentArgs?: string[];
|
|
1637
1665
|
idleTimeoutMs?: number;
|
|
1666
|
+
logger?: {
|
|
1667
|
+
info: (msg: string) => void;
|
|
1668
|
+
warn: (msg: string) => void;
|
|
1669
|
+
};
|
|
1638
1670
|
spawnReplacementAgent?: SpawnReplacementAgent;
|
|
1639
1671
|
historyStore?: HistoryStore;
|
|
1640
1672
|
historyMaxEntries?: number;
|
|
@@ -1683,6 +1715,7 @@ declare class Session {
|
|
|
1683
1715
|
private idleTimer;
|
|
1684
1716
|
private lastRecordedAt;
|
|
1685
1717
|
private spawnReplacementAgent;
|
|
1718
|
+
private logger;
|
|
1686
1719
|
private agentChangeHandlers;
|
|
1687
1720
|
private agentAdvertisedCommands;
|
|
1688
1721
|
private agentCommandsHandlers;
|
|
@@ -1766,6 +1799,8 @@ declare const SessionRecord: z.ZodObject<{
|
|
|
1766
1799
|
lineageId: z.ZodOptional<z.ZodString>;
|
|
1767
1800
|
upstreamSessionId: z.ZodString;
|
|
1768
1801
|
importedFromSessionId: z.ZodOptional<z.ZodString>;
|
|
1802
|
+
importedFromUpstreamSessionId: z.ZodOptional<z.ZodString>;
|
|
1803
|
+
importedFromMachine: z.ZodOptional<z.ZodString>;
|
|
1769
1804
|
agentId: z.ZodString;
|
|
1770
1805
|
cwd: z.ZodString;
|
|
1771
1806
|
title: z.ZodOptional<z.ZodString>;
|
|
@@ -1818,6 +1853,8 @@ declare const SessionRecord: z.ZodObject<{
|
|
|
1818
1853
|
costAmount?: number | undefined;
|
|
1819
1854
|
costCurrency?: string | undefined;
|
|
1820
1855
|
} | undefined;
|
|
1856
|
+
importedFromMachine?: string | undefined;
|
|
1857
|
+
importedFromUpstreamSessionId?: string | undefined;
|
|
1821
1858
|
lineageId?: string | undefined;
|
|
1822
1859
|
importedFromSessionId?: string | undefined;
|
|
1823
1860
|
agentCommands?: {
|
|
@@ -1842,6 +1879,8 @@ declare const SessionRecord: z.ZodObject<{
|
|
|
1842
1879
|
costAmount?: number | undefined;
|
|
1843
1880
|
costCurrency?: string | undefined;
|
|
1844
1881
|
} | undefined;
|
|
1882
|
+
importedFromMachine?: string | undefined;
|
|
1883
|
+
importedFromUpstreamSessionId?: string | undefined;
|
|
1845
1884
|
lineageId?: string | undefined;
|
|
1846
1885
|
importedFromSessionId?: string | undefined;
|
|
1847
1886
|
agentCommands?: {
|
|
@@ -1874,6 +1913,7 @@ declare const Bundle: z.ZodObject<{
|
|
|
1874
1913
|
session: z.ZodObject<{
|
|
1875
1914
|
sessionId: z.ZodString;
|
|
1876
1915
|
lineageId: z.ZodString;
|
|
1916
|
+
upstreamSessionId: z.ZodOptional<z.ZodString>;
|
|
1877
1917
|
agentId: z.ZodString;
|
|
1878
1918
|
cwd: z.ZodString;
|
|
1879
1919
|
title: z.ZodOptional<z.ZodString>;
|
|
@@ -1914,6 +1954,7 @@ declare const Bundle: z.ZodObject<{
|
|
|
1914
1954
|
updatedAt: string;
|
|
1915
1955
|
lineageId: string;
|
|
1916
1956
|
createdAt: string;
|
|
1957
|
+
upstreamSessionId?: string | undefined;
|
|
1917
1958
|
title?: string | undefined;
|
|
1918
1959
|
currentModel?: string | undefined;
|
|
1919
1960
|
currentMode?: string | undefined;
|
|
@@ -1934,6 +1975,7 @@ declare const Bundle: z.ZodObject<{
|
|
|
1934
1975
|
updatedAt: string;
|
|
1935
1976
|
lineageId: string;
|
|
1936
1977
|
createdAt: string;
|
|
1978
|
+
upstreamSessionId?: string | undefined;
|
|
1937
1979
|
title?: string | undefined;
|
|
1938
1980
|
currentModel?: string | undefined;
|
|
1939
1981
|
currentMode?: string | undefined;
|
|
@@ -1976,6 +2018,7 @@ declare const Bundle: z.ZodObject<{
|
|
|
1976
2018
|
updatedAt: string;
|
|
1977
2019
|
lineageId: string;
|
|
1978
2020
|
createdAt: string;
|
|
2021
|
+
upstreamSessionId?: string | undefined;
|
|
1979
2022
|
title?: string | undefined;
|
|
1980
2023
|
currentModel?: string | undefined;
|
|
1981
2024
|
currentMode?: string | undefined;
|
|
@@ -2010,6 +2053,7 @@ declare const Bundle: z.ZodObject<{
|
|
|
2010
2053
|
updatedAt: string;
|
|
2011
2054
|
lineageId: string;
|
|
2012
2055
|
createdAt: string;
|
|
2056
|
+
upstreamSessionId?: string | undefined;
|
|
2013
2057
|
title?: string | undefined;
|
|
2014
2058
|
currentModel?: string | undefined;
|
|
2015
2059
|
currentMode?: string | undefined;
|
|
@@ -2059,6 +2103,7 @@ interface SessionManagerOptions {
|
|
|
2059
2103
|
idleTimeoutMs?: number;
|
|
2060
2104
|
defaultModels?: Record<string, string>;
|
|
2061
2105
|
sessionHistoryMaxEntries?: number;
|
|
2106
|
+
logger?: AgentLogger;
|
|
2062
2107
|
}
|
|
2063
2108
|
declare class SessionManager {
|
|
2064
2109
|
private registry;
|
|
@@ -2071,6 +2116,7 @@ declare class SessionManager {
|
|
|
2071
2116
|
private defaultModels;
|
|
2072
2117
|
private sessionHistoryMaxEntries;
|
|
2073
2118
|
private metaWriteQueues;
|
|
2119
|
+
private logger?;
|
|
2074
2120
|
constructor(registry: Registry, spawner?: AgentSpawner, store?: SessionStore, options?: SessionManagerOptions);
|
|
2075
2121
|
create(params: CreateSessionParams): Promise<Session>;
|
|
2076
2122
|
resurrect(params: ResurrectParams): Promise<Session>;
|
package/dist/index.js
CHANGED
|
@@ -120,7 +120,13 @@ var TuiConfig = z.object({
|
|
|
120
120
|
// Width cap on the cwd column in the `sessions list` output and the
|
|
121
121
|
// TUI picker. Set higher if you keep deeply-nested working directories
|
|
122
122
|
// and want them visible; the elastic title column shrinks to make room.
|
|
123
|
-
cwdColumnMaxWidth: z.number().int().positive().default(24)
|
|
123
|
+
cwdColumnMaxWidth: z.number().int().positive().default(24),
|
|
124
|
+
// When true (default), emit OSC 9;4 progress-bar control codes so the
|
|
125
|
+
// host terminal can show an indeterminate busy indicator (taskbar pulse
|
|
126
|
+
// on Windows Terminal, dock badge on KDE/Konsole, etc.) while a turn is
|
|
127
|
+
// running. Set false if your terminal renders this obnoxiously or you
|
|
128
|
+
// just don't want it.
|
|
129
|
+
progressIndicator: z.boolean().default(true)
|
|
124
130
|
});
|
|
125
131
|
var ExtensionName = z.string().min(1).regex(/^[A-Za-z0-9._-]+$/, "extension name must be filename-safe");
|
|
126
132
|
var ExtensionBody = z.object({
|
|
@@ -158,11 +164,12 @@ var HydraConfig = z.object({
|
|
|
158
164
|
maxScrollbackLines: 1e4,
|
|
159
165
|
mouse: true,
|
|
160
166
|
logMaxBytes: 5 * 1024 * 1024,
|
|
161
|
-
cwdColumnMaxWidth: 24
|
|
167
|
+
cwdColumnMaxWidth: 24,
|
|
168
|
+
progressIndicator: true
|
|
162
169
|
})
|
|
163
170
|
});
|
|
164
171
|
var HydraConfigReadOnly = HydraConfig.extend({
|
|
165
|
-
daemon: DaemonConfig.omit({ authToken: true })
|
|
172
|
+
daemon: DaemonConfig.omit({ authToken: true }).default({})
|
|
166
173
|
});
|
|
167
174
|
function extensionList(config) {
|
|
168
175
|
return Object.entries(config.extensions).map(([name, body]) => ({
|
|
@@ -1013,6 +1020,11 @@ var SessionListEntry = z3.object({
|
|
|
1013
1020
|
// Last-known usage snapshot so list views can show per-session cost
|
|
1014
1021
|
// (and tokens, in callers that care) without resurrecting cold sessions.
|
|
1015
1022
|
currentUsage: SessionListUsage.optional(),
|
|
1023
|
+
// Origin host (and origin upstream id) for imported sessions. Picker
|
|
1024
|
+
// uses the host to fill in the UPSTREAM cell pre-first-attach;
|
|
1025
|
+
// future "connect back to origin" callers would dial both.
|
|
1026
|
+
importedFromMachine: z3.string().optional(),
|
|
1027
|
+
importedFromUpstreamSessionId: z3.string().optional(),
|
|
1016
1028
|
updatedAt: z3.string(),
|
|
1017
1029
|
attachedClients: z3.number().int().nonnegative(),
|
|
1018
1030
|
status: z3.enum(["live", "cold"]).default("live"),
|
|
@@ -1162,6 +1174,15 @@ var JsonRpcConnection = class _JsonRpcConnection {
|
|
|
1162
1174
|
}
|
|
1163
1175
|
}
|
|
1164
1176
|
}
|
|
1177
|
+
// Discard any notifications buffered for the given method without firing
|
|
1178
|
+
// handlers. Used by the resurrect path to drop the agent's session/load
|
|
1179
|
+
// replay: that replay is the agent re-emitting our own history back at
|
|
1180
|
+
// us, and if we flushed it through wireAgent's session/update handler
|
|
1181
|
+
// every entry would be re-appended to history.jsonl, doubling the log
|
|
1182
|
+
// each time the session was woken up.
|
|
1183
|
+
drainBuffered(method) {
|
|
1184
|
+
this.bufferedNotifications.delete(method);
|
|
1185
|
+
}
|
|
1165
1186
|
onClose(handler) {
|
|
1166
1187
|
this.closeHandlers.push(handler);
|
|
1167
1188
|
}
|
|
@@ -1314,12 +1335,14 @@ var AgentInstance = class _AgentInstance {
|
|
|
1314
1335
|
killed = false;
|
|
1315
1336
|
stderrTail = "";
|
|
1316
1337
|
stderrTailBytes;
|
|
1338
|
+
logger;
|
|
1317
1339
|
exitHandlers = [];
|
|
1318
1340
|
constructor(opts, child) {
|
|
1319
1341
|
this.agentId = opts.agentId;
|
|
1320
1342
|
this.cwd = opts.cwd;
|
|
1321
1343
|
this.child = child;
|
|
1322
1344
|
this.stderrTailBytes = opts.stderrTailBytes ?? DEFAULT_STDERR_TAIL_BYTES;
|
|
1345
|
+
this.logger = opts.logger;
|
|
1323
1346
|
if (!child.stdout || !child.stdin) {
|
|
1324
1347
|
throw new Error("agent subprocess missing stdio");
|
|
1325
1348
|
}
|
|
@@ -1328,7 +1351,15 @@ var AgentInstance = class _AgentInstance {
|
|
|
1328
1351
|
child.stderr?.setEncoding("utf8");
|
|
1329
1352
|
child.stderr?.on("data", (chunk) => {
|
|
1330
1353
|
this.stderrTail = (this.stderrTail + chunk).slice(-this.stderrTailBytes);
|
|
1331
|
-
|
|
1354
|
+
if (this.logger) {
|
|
1355
|
+
for (const line of chunk.split(/\r?\n/)) {
|
|
1356
|
+
if (line.length > 0) {
|
|
1357
|
+
this.logger.info(`[${opts.agentId}] ${line}`);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
} else {
|
|
1361
|
+
process.stderr.write(`[${opts.agentId}] ${chunk}`);
|
|
1362
|
+
}
|
|
1332
1363
|
});
|
|
1333
1364
|
child.on("error", (err) => {
|
|
1334
1365
|
const msg = this.formatFailure(err.message);
|
|
@@ -1336,9 +1367,16 @@ var AgentInstance = class _AgentInstance {
|
|
|
1336
1367
|
});
|
|
1337
1368
|
child.on("exit", (code, signal) => {
|
|
1338
1369
|
this.exited = true;
|
|
1339
|
-
if (
|
|
1370
|
+
if (this.killed) {
|
|
1371
|
+
this.logger?.info(
|
|
1372
|
+
`agent ${opts.agentId} pid=${child.pid} exited after kill code=${code} signal=${signal}`
|
|
1373
|
+
);
|
|
1374
|
+
} else {
|
|
1340
1375
|
const reason = `agent ${opts.agentId} exited before responding (code=${code} signal=${signal})`;
|
|
1341
1376
|
this.connection.fail(new Error(this.formatFailure(reason)));
|
|
1377
|
+
this.logger?.warn(
|
|
1378
|
+
`agent ${opts.agentId} pid=${child.pid} exited unexpectedly code=${code} signal=${signal}`
|
|
1379
|
+
);
|
|
1342
1380
|
}
|
|
1343
1381
|
for (const handler of this.exitHandlers) {
|
|
1344
1382
|
handler(code, signal);
|
|
@@ -1359,7 +1397,15 @@ stderr: ${tail}` : reason;
|
|
|
1359
1397
|
const child = spawn3(opts.plan.command, opts.plan.args, {
|
|
1360
1398
|
cwd: opts.cwd,
|
|
1361
1399
|
env,
|
|
1362
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1400
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1401
|
+
// setsid the agent into its own session/process group. The daemon
|
|
1402
|
+
// already runs in its own setsid'd session, but macOS terminals
|
|
1403
|
+
// (iTerm2, Terminal.app) sometimes still reach inherited child
|
|
1404
|
+
// processes when the user closes a window — putting the agent
|
|
1405
|
+
// one more session-boundary away keeps it alive across terminal
|
|
1406
|
+
// restarts. The daemon still owns the pipes, so this.kill()
|
|
1407
|
+
// continues to terminate it cleanly on idle/close.
|
|
1408
|
+
detached: true
|
|
1363
1409
|
});
|
|
1364
1410
|
return new _AgentInstance(opts, child);
|
|
1365
1411
|
}
|
|
@@ -1374,6 +1420,9 @@ stderr: ${tail}` : reason;
|
|
|
1374
1420
|
return;
|
|
1375
1421
|
}
|
|
1376
1422
|
this.killed = true;
|
|
1423
|
+
this.logger?.info(
|
|
1424
|
+
`agent ${this.agentId} pid=${this.child.pid} kill requested signal=${signal}`
|
|
1425
|
+
);
|
|
1377
1426
|
await this.connection.close().catch(() => void 0);
|
|
1378
1427
|
this.child.kill(signal);
|
|
1379
1428
|
}
|
|
@@ -1486,6 +1535,7 @@ var Session = class {
|
|
|
1486
1535
|
// and noisy state churn keep a quiet session alive forever.
|
|
1487
1536
|
lastRecordedAt;
|
|
1488
1537
|
spawnReplacementAgent;
|
|
1538
|
+
logger;
|
|
1489
1539
|
agentChangeHandlers = [];
|
|
1490
1540
|
// Last available_commands_update we observed from the agent. Stored
|
|
1491
1541
|
// so we can re-broadcast a merged (hydra ∪ agent) list whenever
|
|
@@ -1517,6 +1567,7 @@ var Session = class {
|
|
|
1517
1567
|
}
|
|
1518
1568
|
this.idleTimeoutMs = init.idleTimeoutMs ?? 0;
|
|
1519
1569
|
this.spawnReplacementAgent = init.spawnReplacementAgent;
|
|
1570
|
+
this.logger = init.logger;
|
|
1520
1571
|
if (init.firstPromptSeeded) {
|
|
1521
1572
|
this.firstPromptSeeded = true;
|
|
1522
1573
|
}
|
|
@@ -1846,6 +1897,9 @@ var Session = class {
|
|
|
1846
1897
|
if (this.closed) {
|
|
1847
1898
|
return;
|
|
1848
1899
|
}
|
|
1900
|
+
this.logger?.info(
|
|
1901
|
+
`session ${this.sessionId} closing deleteRecord=${opts.deleteRecord ?? false} regenTitle=${opts.regenTitle ?? false}`
|
|
1902
|
+
);
|
|
1849
1903
|
this.cancelIdleTimer();
|
|
1850
1904
|
if (opts.regenTitle && this.firstPromptSeeded) {
|
|
1851
1905
|
const timeoutMs = opts.regenTitleTimeoutMs ?? 5e3;
|
|
@@ -2400,6 +2454,10 @@ _(switched from \`${oldAgentId}\` to \`${newAgentId}\`)_
|
|
|
2400
2454
|
return;
|
|
2401
2455
|
}
|
|
2402
2456
|
const opts = this.firstPromptSeeded ? { deleteRecord: false, regenTitle: true } : { deleteRecord: true };
|
|
2457
|
+
const idleSec = Math.round(idle / 1e3);
|
|
2458
|
+
this.logger?.info(
|
|
2459
|
+
`session ${this.sessionId} idle timeout fired after ${idleSec}s (window=${Math.round(this.idleTimeoutMs / 1e3)}s) \u2014 closing`
|
|
2460
|
+
);
|
|
2403
2461
|
void this.close(opts).catch(() => void 0);
|
|
2404
2462
|
}
|
|
2405
2463
|
cancelIdleTimer() {
|
|
@@ -2768,6 +2826,16 @@ var SessionRecord = z4.object({
|
|
|
2768
2826
|
// origin's local id at export time, kept for debuggability and as a
|
|
2769
2827
|
// breadcrumb in `sessions list` (informational, not used for routing).
|
|
2770
2828
|
importedFromSessionId: z4.string().optional(),
|
|
2829
|
+
// Origin's agent-side session id at export time. Carried as a
|
|
2830
|
+
// breadcrumb and as the handle a future "connect back to origin"
|
|
2831
|
+
// feature would dial. Absent when the origin record had no upstream
|
|
2832
|
+
// bound (re-export of an imported, not-yet-attached session).
|
|
2833
|
+
importedFromUpstreamSessionId: z4.string().optional(),
|
|
2834
|
+
// Hostname of the machine that exported the bundle we imported
|
|
2835
|
+
// (i.e. the most recent hop, not necessarily the true multi-hop
|
|
2836
|
+
// origin). Surfaced in the picker so imported rows don't look like
|
|
2837
|
+
// they materialized from nowhere.
|
|
2838
|
+
importedFromMachine: z4.string().optional(),
|
|
2771
2839
|
agentId: z4.string(),
|
|
2772
2840
|
cwd: z4.string(),
|
|
2773
2841
|
title: z4.string().optional(),
|
|
@@ -2888,6 +2956,8 @@ function recordFromMemorySession(args) {
|
|
|
2888
2956
|
lineageId: args.lineageId,
|
|
2889
2957
|
upstreamSessionId: args.upstreamSessionId,
|
|
2890
2958
|
importedFromSessionId: args.importedFromSessionId,
|
|
2959
|
+
importedFromUpstreamSessionId: args.importedFromUpstreamSessionId,
|
|
2960
|
+
importedFromMachine: args.importedFromMachine,
|
|
2891
2961
|
agentId: args.agentId,
|
|
2892
2962
|
cwd: args.cwd,
|
|
2893
2963
|
title: args.title,
|
|
@@ -3105,6 +3175,7 @@ var SessionManager = class {
|
|
|
3105
3175
|
this.histories = new HistoryStore({ maxEntries: this.sessionHistoryMaxEntries });
|
|
3106
3176
|
this.idleTimeoutMs = options.idleTimeoutMs ?? 0;
|
|
3107
3177
|
this.defaultModels = options.defaultModels ?? {};
|
|
3178
|
+
this.logger = options.logger;
|
|
3108
3179
|
}
|
|
3109
3180
|
registry;
|
|
3110
3181
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -3119,6 +3190,7 @@ var SessionManager = class {
|
|
|
3119
3190
|
// concurrent snapshot updates (e.g. an agent emitting model + mode
|
|
3120
3191
|
// back-to-back) don't lose writes via interleaved reads.
|
|
3121
3192
|
metaWriteQueues = /* @__PURE__ */ new Map();
|
|
3193
|
+
logger;
|
|
3122
3194
|
async create(params) {
|
|
3123
3195
|
const fresh = await this.bootstrapAgent({
|
|
3124
3196
|
agentId: params.agentId,
|
|
@@ -3136,6 +3208,7 @@ var SessionManager = class {
|
|
|
3136
3208
|
title: params.title,
|
|
3137
3209
|
agentArgs: params.agentArgs,
|
|
3138
3210
|
idleTimeoutMs: this.idleTimeoutMs,
|
|
3211
|
+
logger: this.logger,
|
|
3139
3212
|
spawnReplacementAgent: (p) => this.bootstrapAgent({ ...p, mcpServers: [] }),
|
|
3140
3213
|
historyStore: this.histories,
|
|
3141
3214
|
historyMaxEntries: this.sessionHistoryMaxEntries,
|
|
@@ -3218,6 +3291,7 @@ var SessionManager = class {
|
|
|
3218
3291
|
await agent.kill().catch(() => void 0);
|
|
3219
3292
|
return this.doResurrectFromImport(params);
|
|
3220
3293
|
}
|
|
3294
|
+
agent.connection.drainBuffered("session/update");
|
|
3221
3295
|
const session = new Session({
|
|
3222
3296
|
sessionId: params.hydraSessionId,
|
|
3223
3297
|
cwd: params.cwd,
|
|
@@ -3228,6 +3302,7 @@ var SessionManager = class {
|
|
|
3228
3302
|
title: params.title,
|
|
3229
3303
|
agentArgs: params.agentArgs,
|
|
3230
3304
|
idleTimeoutMs: this.idleTimeoutMs,
|
|
3305
|
+
logger: this.logger,
|
|
3231
3306
|
spawnReplacementAgent: (p) => this.bootstrapAgent({ ...p, mcpServers: [] }),
|
|
3232
3307
|
historyStore: this.histories,
|
|
3233
3308
|
historyMaxEntries: this.sessionHistoryMaxEntries,
|
|
@@ -3274,6 +3349,7 @@ var SessionManager = class {
|
|
|
3274
3349
|
title: params.title,
|
|
3275
3350
|
agentArgs: params.agentArgs,
|
|
3276
3351
|
idleTimeoutMs: this.idleTimeoutMs,
|
|
3352
|
+
logger: this.logger,
|
|
3277
3353
|
spawnReplacementAgent: (p) => this.bootstrapAgent({ ...p, mcpServers: [] }),
|
|
3278
3354
|
historyStore: this.histories,
|
|
3279
3355
|
historyMaxEntries: this.sessionHistoryMaxEntries,
|
|
@@ -3538,6 +3614,8 @@ var SessionManager = class {
|
|
|
3538
3614
|
agentId: r.agentId,
|
|
3539
3615
|
currentModel: r.currentModel,
|
|
3540
3616
|
currentUsage: r.currentUsage,
|
|
3617
|
+
importedFromMachine: r.importedFromMachine,
|
|
3618
|
+
importedFromUpstreamSessionId: r.importedFromUpstreamSessionId,
|
|
3541
3619
|
updatedAt: used,
|
|
3542
3620
|
attachedClients: 0,
|
|
3543
3621
|
status: "cold"
|
|
@@ -3645,6 +3723,8 @@ var SessionManager = class {
|
|
|
3645
3723
|
lineageId: args.bundle.session.lineageId,
|
|
3646
3724
|
upstreamSessionId: "",
|
|
3647
3725
|
importedFromSessionId: args.bundle.session.sessionId,
|
|
3726
|
+
importedFromUpstreamSessionId: args.bundle.session.upstreamSessionId,
|
|
3727
|
+
importedFromMachine: args.bundle.exportedFrom.machine,
|
|
3648
3728
|
agentId: args.bundle.session.agentId,
|
|
3649
3729
|
cwd: args.cwd ?? args.bundle.session.cwd,
|
|
3650
3730
|
title: args.bundle.session.title,
|
|
@@ -3767,6 +3847,8 @@ function mergeForPersistence(session, existing) {
|
|
|
3767
3847
|
lineageId: existing?.lineageId ?? generateLineageId(),
|
|
3768
3848
|
upstreamSessionId: session.upstreamSessionId,
|
|
3769
3849
|
importedFromSessionId: existing?.importedFromSessionId,
|
|
3850
|
+
importedFromUpstreamSessionId: existing?.importedFromUpstreamSessionId,
|
|
3851
|
+
importedFromMachine: existing?.importedFromMachine,
|
|
3770
3852
|
agentId: session.agentId,
|
|
3771
3853
|
cwd: session.cwd,
|
|
3772
3854
|
title: session.title,
|
|
@@ -4352,6 +4434,12 @@ var BundleSession = z5.object({
|
|
|
4352
4434
|
// Required on bundles — the export path backfills if the source
|
|
4353
4435
|
// record was written before lineageId existed.
|
|
4354
4436
|
lineageId: z5.string(),
|
|
4437
|
+
// The exporter's agent-side session id at export time. Carried so
|
|
4438
|
+
// importers can persist it as a breadcrumb (and, eventually, as the
|
|
4439
|
+
// handle a "connect back to origin" feature would need). Omitted on
|
|
4440
|
+
// bundles whose source record never bound to an agent (e.g. a
|
|
4441
|
+
// re-export of an imported, not-yet-attached session).
|
|
4442
|
+
upstreamSessionId: z5.string().optional(),
|
|
4355
4443
|
agentId: z5.string(),
|
|
4356
4444
|
cwd: z5.string(),
|
|
4357
4445
|
title: z5.string().optional(),
|
|
@@ -4384,6 +4472,7 @@ function encodeBundle(params) {
|
|
|
4384
4472
|
session: {
|
|
4385
4473
|
sessionId: params.record.sessionId,
|
|
4386
4474
|
lineageId: params.record.lineageId,
|
|
4475
|
+
...params.record.upstreamSessionId ? { upstreamSessionId: params.record.upstreamSessionId } : {},
|
|
4387
4476
|
agentId: params.record.agentId,
|
|
4388
4477
|
cwd: params.record.cwd,
|
|
4389
4478
|
...params.record.title !== void 0 ? { title: params.record.title } : {},
|
|
@@ -5175,11 +5264,20 @@ async function startDaemon(config) {
|
|
|
5175
5264
|
await auth(request, reply);
|
|
5176
5265
|
});
|
|
5177
5266
|
const registry = new Registry(config);
|
|
5178
|
-
const
|
|
5267
|
+
const agentLogger = {
|
|
5268
|
+
info: (msg) => app.log.info(msg),
|
|
5269
|
+
warn: (msg) => app.log.warn(msg)
|
|
5270
|
+
};
|
|
5271
|
+
const spawner = (opts) => AgentInstance.spawn({
|
|
5272
|
+
...opts,
|
|
5273
|
+
stderrTailBytes: config.daemon.agentStderrTailBytes,
|
|
5274
|
+
logger: agentLogger
|
|
5275
|
+
});
|
|
5179
5276
|
const manager = new SessionManager(registry, spawner, void 0, {
|
|
5180
5277
|
idleTimeoutMs: config.daemon.sessionIdleTimeoutSeconds * 1e3,
|
|
5181
5278
|
defaultModels: config.defaultModels,
|
|
5182
|
-
sessionHistoryMaxEntries: config.daemon.sessionHistoryMaxEntries
|
|
5279
|
+
sessionHistoryMaxEntries: config.daemon.sessionHistoryMaxEntries,
|
|
5280
|
+
logger: agentLogger
|
|
5183
5281
|
});
|
|
5184
5282
|
const extensions = new ExtensionManager(extensionList(config));
|
|
5185
5283
|
registerHealthRoutes(app, HYDRA_VERSION);
|