very-happy-cli 0.2.156 → 0.2.157
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/{AcpBackend-BHe1_F1h.cjs → AcpBackend-BymWX-LI.cjs} +2 -2
- package/dist/{AcpBackend-C3nc1u-s.mjs → AcpBackend-Cy18U7yw.mjs} +2 -2
- package/dist/{AcpSessionManager-BVxkQnNf.cjs → AcpSessionManager-BF7zBGAi.cjs} +1 -1
- package/dist/{AcpSessionManager-dN3k1LbI.mjs → AcpSessionManager-Ds8rcp95.mjs} +1 -1
- package/dist/{agentGuidance-C604n0Zq.mjs → agentGuidance-CU6YD_Ms.mjs} +560 -29
- package/dist/{agentGuidance-C-CmV03f.cjs → agentGuidance-Db76O1kw.cjs} +579 -28
- package/dist/{agentHome-CmIJNV7f.cjs → agentHome-B0CLy0Qx.cjs} +2 -2
- package/dist/{agentHome-D2XGsoqs.mjs → agentHome-BaJ9xtxB.mjs} +2 -2
- package/dist/{auto-B1I6G_Nf.mjs → auto-B1YAwaeU.mjs} +6 -6
- package/dist/{auto-CMDq4oIu.cjs → auto-DqKGFq8U.cjs} +6 -6
- package/dist/codex/happyMcpStdioBridge.cjs +2 -2
- package/dist/codex/happyMcpStdioBridge.mjs +2 -2
- package/dist/{config-BYHSRY2E.cjs → config-CzkBjZnV.cjs} +4 -4
- package/dist/{config-BkTu25bH.mjs → config-aAbhJllb.mjs} +4 -4
- package/dist/{index-B5QbzEga.mjs → index-BVu8Cufk.mjs} +8 -8
- package/dist/{index-DAAreiRF.mjs → index-BYU1e81k.mjs} +288 -44
- package/dist/{index-BbtR-Qto.cjs → index-C9QzZsUQ.cjs} +291 -47
- package/dist/{index-B_-7NpGZ.cjs → index-CTbGqVFY.cjs} +7 -7
- package/dist/{index-CB8QnjvS.cjs → index-Cz-uqnJk.cjs} +4 -4
- package/dist/{index-PYO8Mdz4.mjs → index-DQcRkI9r.mjs} +4 -4
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{install-CO1ps6Wt.cjs → install-BMlV0Tnr.cjs} +1 -1
- package/dist/{install-CNtXzwT_.mjs → install-BuYnqtVa.mjs} +1 -1
- package/dist/{installTerminalHooks-BOAnQ1Qe.cjs → installTerminalHooks-BVVOEGhG.cjs} +4 -4
- package/dist/{installTerminalHooks-CMpOV3RU.mjs → installTerminalHooks-ELDoqh0A.mjs} +4 -4
- package/dist/lib.cjs +2 -2
- package/dist/lib.d.cts +77 -1
- package/dist/lib.d.mts +77 -1
- package/dist/lib.mjs +2 -2
- package/dist/{limits-DmMdchTz.mjs → limits-A2c-3M-_.mjs} +162 -11
- package/dist/{limits-o29KXBgj.cjs → limits-CyuIt-Vl.cjs} +169 -10
- package/dist/{mcp-C_uejotl.mjs → mcp-Bo934Kpy.mjs} +4 -4
- package/dist/{mcp-CwK1nI7Y.cjs → mcp-CXkvnztT.cjs} +4 -4
- package/dist/{piRuntime-Dj5WnqzJ.cjs → piRuntime-CJjA26oD.cjs} +2 -2
- package/dist/{piRuntime-Cu2-8nOT.mjs → piRuntime-DdEY2mE9.mjs} +2 -2
- package/dist/{piTerminal-D-BIbov9.cjs → piTerminal-CpU3hzrX.cjs} +6 -6
- package/dist/{piTerminal-DanZeBrO.mjs → piTerminal-DwWmgumi.mjs} +6 -6
- package/dist/{runGemini-CZ_lhMWY.mjs → runGemini-Cj6oJQ-I.mjs} +6 -6
- package/dist/{runGemini--QqK-Ap5.cjs → runGemini-DS-0qm0L.cjs} +6 -6
- package/dist/{runOpenClaw-C0xLhTvk.cjs → runOpenClaw-CvSm8oTc.cjs} +5 -5
- package/dist/{runOpenClaw-CxgLpm38.mjs → runOpenClaw-DUCQxkI-.mjs} +5 -5
- package/dist/{send-CTcGMVuG.cjs → send-CHxobaoI.cjs} +43 -31
- package/dist/{send-BaOkDgH0.mjs → send-D5CyRyZ_.mjs} +43 -31
- package/dist/{sessions-OmvyJb0n.cjs → sessions-BFlIrKCY.cjs} +133 -49
- package/dist/{sessions-Bn304xF1.mjs → sessions-DsE01xKc.mjs} +133 -49
- package/dist/{spawn-BKNabutV.mjs → spawn-CdihhwHs.mjs} +4 -4
- package/dist/{spawn-CC6V2KiA.cjs → spawn-HV345RN-.cjs} +4 -4
- package/dist/{teams-CLbS2_4Q.mjs → teams-BE8nMB6O.mjs} +5 -5
- package/dist/{teams-XtrH3_h3.cjs → teams-BahinMnH.cjs} +5 -5
- package/dist/{todo-CEDvKNXK.cjs → todo-CMMQ6yhU.cjs} +2 -2
- package/dist/{todo-BcPbNKt2.mjs → todo-CuQAkSp2.mjs} +2 -2
- package/dist/{types-CphWjqHc.cjs → types-B0sSi6eM.cjs} +77 -4
- package/dist/{types-BEVzYSoV.mjs → types-BrGJ7PJz.mjs} +77 -4
- package/package.json +7 -7
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
|
-
var agentGuidance = require('./agentGuidance-
|
|
5
|
-
var persistence = require('./limits-
|
|
4
|
+
var agentGuidance = require('./agentGuidance-Db76O1kw.cjs');
|
|
5
|
+
var persistence = require('./limits-CyuIt-Vl.cjs');
|
|
6
6
|
var path = require('node:path');
|
|
7
7
|
require('zod');
|
|
8
8
|
require('node:crypto');
|
|
@@ -15,6 +15,7 @@ require('@paralleldrive/cuid2');
|
|
|
15
15
|
require('tweetnacl');
|
|
16
16
|
require('node:util');
|
|
17
17
|
|
|
18
|
+
const MACHINE_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
18
19
|
const ACTIONS = ["list", "read", "stop", "archive", "approve", "deny", "peers", "message"];
|
|
19
20
|
const ACTIONS_NEEDING_ID = /* @__PURE__ */ new Set(["read", "stop", "archive", "approve", "deny", "message"]);
|
|
20
21
|
const ACTIONS_NEEDING_REQUEST_ID = /* @__PURE__ */ new Set(["approve", "deny"]);
|
|
@@ -67,6 +68,10 @@ function parseSessionsArgs(args) {
|
|
|
67
68
|
const value = args[++i];
|
|
68
69
|
if (value === void 0 || value.trim() === "") throw new Error("--reply-to requires a value");
|
|
69
70
|
options.replyTo = value;
|
|
71
|
+
} else if (arg === "--machine") {
|
|
72
|
+
const value = args[++i];
|
|
73
|
+
if (value === void 0 || !MACHINE_ID_RE.test(value)) throw new Error("--machine requires a machine id");
|
|
74
|
+
options.machine = value;
|
|
70
75
|
} else if (arg === "--tag") {
|
|
71
76
|
const value = args[++i];
|
|
72
77
|
if (value === void 0) throw new Error("--tag requires a value");
|
|
@@ -132,31 +137,45 @@ function parseSessionsArgs(args) {
|
|
|
132
137
|
if (options.cwd !== void 0) throw new Error("--cwd only applies to `sessions peers`");
|
|
133
138
|
}
|
|
134
139
|
if (options.replyTo !== void 0 && options.action !== "message") throw new Error("--reply-to only applies to `sessions message`");
|
|
140
|
+
if (options.machine !== void 0) {
|
|
141
|
+
if (!MACHINE_ACTIONS.has(options.action)) throw new Error("--machine only applies to `sessions list|read|peers|message`");
|
|
142
|
+
if (options.all) throw new Error("--machine cannot be combined with --all (--all already asks every online machine)");
|
|
143
|
+
}
|
|
135
144
|
return options;
|
|
136
145
|
}
|
|
146
|
+
const MACHINE_ACTIONS = /* @__PURE__ */ new Set(["list", "read", "peers", "message"]);
|
|
137
147
|
const SESSIONS_HELP = `
|
|
138
|
-
${chalk.bold("very-happy sessions")} - Inspect and control sessions on this machine (for automation)
|
|
148
|
+
${chalk.bold("very-happy sessions")} - Inspect and control sessions on this machine and, through their daemons, on the account's other machines (for automation)
|
|
139
149
|
|
|
140
150
|
${chalk.bold("Usage:")}
|
|
141
|
-
very-happy sessions list [--all [--include-archived]] [--tag <name>] [--limit <n>] [--json]
|
|
142
|
-
very-happy sessions read <id> [--limit <n>] [--full] [--answer]
|
|
151
|
+
very-happy sessions list [--all [--include-archived]] [--machine <id>] [--tag <name>] [--limit <n>] [--json]
|
|
152
|
+
very-happy sessions read <id> [--machine <id>] [--limit <n>] [--full] [--answer]
|
|
143
153
|
[--wait [--timeout <s>]] [--json]
|
|
144
154
|
very-happy sessions stop <id> [--json]
|
|
145
155
|
very-happy sessions archive <id> [--json]
|
|
146
156
|
very-happy sessions approve <id> <requestId> [--for-session] [--json]
|
|
147
157
|
very-happy sessions deny <id> <requestId> [--reason <text>] [--json]
|
|
148
|
-
very-happy sessions peers [--scope repo|cwd|machine] [--cwd <dir>] [--json]
|
|
149
|
-
very-happy sessions message <id> <text> [--reply-to <msgId>] [--json]
|
|
158
|
+
very-happy sessions peers [--scope repo|cwd|machine] [--cwd <dir>] [--machine <id>] [--json]
|
|
159
|
+
very-happy sessions message <id> <text> [--reply-to <msgId>] [--machine <id>] [--json]
|
|
150
160
|
|
|
151
161
|
${chalk.bold("Actions:")}
|
|
152
162
|
list Running sessions plus recently seen ones, newest first.
|
|
153
163
|
With --all: every session on the account (newest 150, server
|
|
154
|
-
REST), attention first.
|
|
155
|
-
|
|
164
|
+
REST), attention first. Rows this machine cannot decrypt are
|
|
165
|
+
filled in by the online machine that spawned them (\`readable\`,
|
|
166
|
+
\`via\`, \`machine\` in --json; \`decryptable\` stays "this
|
|
167
|
+
machine's own key"); what no online machine holds shows only
|
|
156
168
|
id / active / archived / timestamps / url.
|
|
169
|
+
With --machine <id>: that machine's own listing (its daemon's
|
|
170
|
+
children plus what it saw lately).
|
|
157
171
|
read The tail of a session as a role-tagged transcript, plus where
|
|
158
172
|
the latest turn stands (\`turn\` in --json: ended, status and
|
|
159
|
-
\`answer\` = the agent's reply to the latest prompt).
|
|
173
|
+
\`answer\` = the agent's reply to the latest prompt). A session
|
|
174
|
+
another machine spawned is read through that machine's daemon.
|
|
175
|
+
Both list and read carry \`backgroundTasks\` ({count, tasks[]}):
|
|
176
|
+
async sub-agents / backgrounded commands still running after
|
|
177
|
+
the turn ended \u2014 such a session is NOT idle (absent = the
|
|
178
|
+
daemon or CLI is too old to report it).
|
|
160
179
|
stop SIGTERM the session's process via the local daemon.
|
|
161
180
|
archive Mark the session inactive server-side (it stays resumable).
|
|
162
181
|
approve Answer a pending permission request \`requestId\` with approve \u2014
|
|
@@ -167,13 +186,17 @@ ${chalk.bold("Actions:")}
|
|
|
167
186
|
the last 30 minutes. Default scope \`repo\`: same git repository,
|
|
168
187
|
including its other worktrees (\`sameWorktree\` says which share
|
|
169
188
|
your checkout); \`cwd\`: same directory; \`machine\`: everything.
|
|
170
|
-
|
|
171
|
-
|
|
189
|
+
With --machine <id>: the live sessions on that machine (scope
|
|
190
|
+
\`machine\` unless --cwd names a directory there).
|
|
191
|
+
message Send <text> into a running session. It arrives tagged with the
|
|
192
|
+
sender (the session named by VH_PEER_SESSION_ID when run from a
|
|
172
193
|
managed session's shell, else this CLI) and tells the peer how to
|
|
173
|
-
reply.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
194
|
+
reply. A session another machine spawned is delivered through
|
|
195
|
+
that machine's daemon, with this host named in the header.
|
|
196
|
+
Refused for sessions that are not running, or terminal mirrors.
|
|
197
|
+
\`delivered\` follows \`very-happy send\`: true only when a
|
|
198
|
+
wrapper was attached before and after the POST (\`stored\` = it
|
|
199
|
+
is on the server anyway).
|
|
177
200
|
|
|
178
201
|
${chalk.bold("Options:")}
|
|
179
202
|
--all list only: account-wide over REST instead of this
|
|
@@ -199,26 +222,31 @@ ${chalk.bold("Options:")}
|
|
|
199
222
|
--cwd <dir> peers only: directory the scope is computed from
|
|
200
223
|
(default: the process cwd, or HAPPY_SESSION_ID's cwd).
|
|
201
224
|
--reply-to <id> message only: the peer message id being answered.
|
|
225
|
+
--machine <id> list / read / peers / message: go through this account
|
|
226
|
+
machine's daemon (its id as the web's machine list shows
|
|
227
|
+
it). read / message: skip the lookup across machines.
|
|
202
228
|
--json Machine-readable output.
|
|
203
229
|
-h, --help Show this help
|
|
204
230
|
|
|
205
231
|
${chalk.bold("Scope:")}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
the
|
|
232
|
+
Sessions this machine's daemon spawned are handled with the local key in
|
|
233
|
+
~/.happy/sessions.json (pruned after 14 days). Any other session of the
|
|
234
|
+
account is handled by the daemon of the machine that spawned it: that
|
|
235
|
+
machine must be online and on CLI \u2265 0.2.157, the operation runs there with
|
|
236
|
+
its keys, and only the result travels back \u2014 this CLI never holds the
|
|
237
|
+
account content key or another machine's session key. Without --machine
|
|
238
|
+
the online machines are asked in turn which one holds the session.
|
|
239
|
+
approve / deny / stop / archive remain local-only.
|
|
213
240
|
|
|
214
241
|
${chalk.bold("Exit codes:")}
|
|
215
242
|
2 read --wait: the turn had not ended when --timeout ran out (the
|
|
216
243
|
partial state is still printed)
|
|
217
244
|
0 success (approve/deny: the wrapper acknowledged the verdict; check
|
|
218
245
|
\`settled\` in --json to see whether the request has left the pending set)
|
|
219
|
-
1 bad arguments, unknown session, no
|
|
220
|
-
|
|
221
|
-
|
|
246
|
+
1 bad arguments, unknown session, no machine of the account holds it (or
|
|
247
|
+
its machine is offline / too old), request not pending, session not
|
|
248
|
+
online, or the operation failed (including \`stop\` on a session the
|
|
249
|
+
daemon is not running)
|
|
222
250
|
`;
|
|
223
251
|
function formatWait(ms) {
|
|
224
252
|
const minutes = Math.floor(ms / 6e4);
|
|
@@ -227,17 +255,19 @@ function formatWait(ms) {
|
|
|
227
255
|
return `${Math.floor(minutes / 60)}h${minutes % 60}m`;
|
|
228
256
|
}
|
|
229
257
|
function formatAccountSummaryLine(summary) {
|
|
230
|
-
const state = summary.attention ? "[attention]" : summary.live ? "[running here]" : summary.active ? "[active elsewhere]" : summary.archived ? "[archived]" : "[idle]";
|
|
258
|
+
const state = summary.attention ? "[attention]" : summary.live ? summary.machine ? `[running on ${summary.machine.host}]` : "[running here]" : summary.active ? "[active elsewhere]" : summary.archived ? "[archived]" : "[idle]";
|
|
231
259
|
const parts = [summary.id, state];
|
|
232
|
-
if (!summary.
|
|
233
|
-
parts.push("(not
|
|
260
|
+
if (!summary.readable) {
|
|
261
|
+
parts.push("(not readable: no online machine holds its key)");
|
|
234
262
|
} else {
|
|
263
|
+
if (summary.machine) parts.push(`via=${summary.machine.host}`);
|
|
235
264
|
if (summary.title) parts.push(`title="${summary.title}"`);
|
|
236
265
|
if (summary.flavor) parts.push(`agent=${summary.flavor}`);
|
|
237
266
|
if (summary.tags?.length) parts.push(`tags=${summary.tags.join(",")}`);
|
|
238
267
|
if (summary.variant) parts.push(`variant=${summary.variant}`);
|
|
239
268
|
if (summary.machineId) parts.push(`machine=${summary.machineId}`);
|
|
240
269
|
if (summary.cwd) parts.push(`cwd=${summary.cwd}`);
|
|
270
|
+
if (summary.backgroundTasks && summary.backgroundTasks.count > 0) parts.push(formatBackgroundTasks(summary.backgroundTasks));
|
|
241
271
|
for (const request of summary.pending ?? []) {
|
|
242
272
|
parts.push(`pending=${request.id}:${request.tool}${request.waitingMs !== void 0 ? `(${formatWait(request.waitingMs)})` : ""}`);
|
|
243
273
|
}
|
|
@@ -248,6 +278,10 @@ function formatAccountSummaryLine(summary) {
|
|
|
248
278
|
function permissionFailureRecord(sessionId, requestId, error) {
|
|
249
279
|
return { sessionId, requestId, error: error instanceof Error ? error.message : String(error) };
|
|
250
280
|
}
|
|
281
|
+
function formatBackgroundTasks(report) {
|
|
282
|
+
const types = report.tasks.map((task) => task.type).filter(Boolean);
|
|
283
|
+
return `background=${report.count}${types.length ? `(${types.join(",")})` : ""}`;
|
|
284
|
+
}
|
|
251
285
|
function formatSummaryLine(summary) {
|
|
252
286
|
const parts = [summary.id, summary.live ? "[running]" : "[not running]"];
|
|
253
287
|
if (summary.title) parts.push(`title="${summary.title}"`);
|
|
@@ -256,6 +290,7 @@ function formatSummaryLine(summary) {
|
|
|
256
290
|
if (summary.variant) parts.push(`variant=${summary.variant}`);
|
|
257
291
|
if (summary.cwd) parts.push(`cwd=${summary.cwd}`);
|
|
258
292
|
if (summary.pid !== void 0) parts.push(`pid=${summary.pid}`);
|
|
293
|
+
if (summary.backgroundTasks && summary.backgroundTasks.count > 0) parts.push(formatBackgroundTasks(summary.backgroundTasks));
|
|
259
294
|
parts.push(summary.url);
|
|
260
295
|
return parts.join(" ");
|
|
261
296
|
}
|
|
@@ -274,20 +309,48 @@ async function handleSessionsCommand(args) {
|
|
|
274
309
|
}
|
|
275
310
|
try {
|
|
276
311
|
if (options.action === "list" && options.all) {
|
|
277
|
-
const
|
|
312
|
+
const listed = await agentGuidance.listAccountSessions({ recentLimit: options.tag ? 150 : options.limit, includeArchived: options.includeArchived });
|
|
313
|
+
const fill = await agentGuidance.fillForeignAccountRows(listed);
|
|
314
|
+
let sessions = fill.rows;
|
|
315
|
+
if (options.tag) {
|
|
316
|
+
const wanted = options.tag;
|
|
317
|
+
sessions = sessions.filter((summary) => summary.tags?.includes(wanted) === true);
|
|
318
|
+
const recentLimit = Math.max(0, options.limit ?? agentGuidance.DEFAULT_RECENT_LIMIT);
|
|
319
|
+
let idleKept = 0;
|
|
320
|
+
sessions = sessions.filter((summary) => summary.attention || summary.live || idleKept++ < recentLimit);
|
|
321
|
+
}
|
|
278
322
|
if (options.json) {
|
|
279
|
-
console.log(JSON.stringify({ sessions, scope: "account" }));
|
|
323
|
+
console.log(JSON.stringify({ sessions, scope: "account", machines: { asked: fill.asked, skipped: fill.skipped } }));
|
|
280
324
|
} else if (sessions.length === 0) {
|
|
281
|
-
console.log(options.tag ? `No
|
|
325
|
+
console.log(options.tag ? `No readable sessions tagged "${options.tag}" on this account.` : "No sessions found on this account.");
|
|
282
326
|
} else {
|
|
283
327
|
for (const summary of sessions) console.log(formatAccountSummaryLine(summary));
|
|
284
|
-
const foreign = sessions.filter((summary) => !summary.
|
|
328
|
+
const foreign = sessions.filter((summary) => !summary.readable).length;
|
|
329
|
+
const filled = fill.asked.reduce((n, a) => n + a.filled, 0);
|
|
330
|
+
if (filled > 0) {
|
|
331
|
+
console.log(chalk.dim(`${filled} session(s) read through their own machine's daemon (${fill.asked.filter((a) => a.filled > 0).map((a) => a.host ?? a.machineId).join(", ")}).`));
|
|
332
|
+
}
|
|
333
|
+
for (const asked of fill.asked.filter((a) => a.error)) console.log(chalk.dim(`machine ${asked.machineId}: ${asked.error}`));
|
|
285
334
|
if (foreign > 0) {
|
|
286
|
-
|
|
335
|
+
const why = fill.skipped.length > 0 ? ` (${fill.skipped.map((s) => `${s.machineId}: ${s.reason}`).join("; ")})` : "";
|
|
336
|
+
console.log(chalk.dim(`${foreign} session(s) belong to a machine that is not reachable right now${why}.`));
|
|
287
337
|
}
|
|
288
338
|
}
|
|
289
339
|
process.exit(0);
|
|
290
340
|
}
|
|
341
|
+
if (options.action === "list" && options.machine) {
|
|
342
|
+
const listing = await agentGuidance.listRemoteSessions(options.machine, { tag: options.tag, limit: options.limit });
|
|
343
|
+
const sessions = listing.sessions;
|
|
344
|
+
if (options.json) {
|
|
345
|
+
console.log(JSON.stringify({ sessions, machine: { id: listing.machineId, host: listing.host } }));
|
|
346
|
+
} else if (sessions.length === 0) {
|
|
347
|
+
console.log(options.tag ? `No sessions tagged "${options.tag}" on ${listing.host}.` : `No sessions found on ${listing.host}.`);
|
|
348
|
+
} else {
|
|
349
|
+
console.log(chalk.dim(`machine ${listing.host} (${listing.machineId})`));
|
|
350
|
+
for (const summary of sessions) console.log(formatSummaryLine(summary));
|
|
351
|
+
}
|
|
352
|
+
process.exit(0);
|
|
353
|
+
}
|
|
291
354
|
if (options.action === "list") {
|
|
292
355
|
const sessions = await agentGuidance.listSessions({ tag: options.tag, recentLimit: options.limit });
|
|
293
356
|
if (options.json) {
|
|
@@ -304,12 +367,13 @@ async function handleSessionsCommand(args) {
|
|
|
304
367
|
const persisted = persistence.readPersistedSessions();
|
|
305
368
|
const context = { self: () => agentGuidance.cliPeerSelf(process.env, cwd, persisted), readPersisted: () => persisted };
|
|
306
369
|
if (options.action === "peers") {
|
|
307
|
-
const listing = await agentGuidance.listPeerSessions(context, options.scope ?? "repo");
|
|
370
|
+
const listing = options.machine ? await agentGuidance.listRemotePeers(options.machine, { ...options.scope ? { scope: options.scope } : {}, ...options.cwd ? { cwd: options.cwd } : {} }) : await agentGuidance.listPeerSessions(context, options.scope ?? "repo");
|
|
308
371
|
if (options.json) {
|
|
309
372
|
console.log(JSON.stringify(listing));
|
|
310
373
|
} else if (listing.peers.length === 0) {
|
|
311
|
-
console.log(`No other live sessions in scope "${listing.scope}"${listing.self.repoRoot ? ` (repo ${listing.self.repoRoot})` : ""}.`);
|
|
374
|
+
console.log(`No other live sessions in scope "${listing.scope}"${listing.self.repoRoot ? ` (repo ${listing.self.repoRoot})` : ""}${"host" in listing ? ` on ${listing.host}` : ""}.`);
|
|
312
375
|
} else {
|
|
376
|
+
if ("host" in listing) console.log(chalk.dim(`machine ${String(listing.host)} (${String(listing.machineId)})`));
|
|
313
377
|
for (const peer of listing.peers) {
|
|
314
378
|
const parts = [peer.sessionId, `[${peer.kind === "mirror" ? "terminal" : "running"}]`];
|
|
315
379
|
if (peer.title) parts.push(`title="${peer.title}"`);
|
|
@@ -324,9 +388,9 @@ async function handleSessionsCommand(args) {
|
|
|
324
388
|
}
|
|
325
389
|
const target = options.sessionId;
|
|
326
390
|
try {
|
|
327
|
-
const result = await agentGuidance.sendPeerMessage(context, { to: target, body: options.text, replyTo: options.replyTo });
|
|
391
|
+
const result = await agentGuidance.sendPeerMessage(context, { to: target, body: options.text, replyTo: options.replyTo, ...options.machine ? { machineId: options.machine } : {} });
|
|
328
392
|
if (options.json) console.log(JSON.stringify(result));
|
|
329
|
-
else if (result.delivered) console.log(`Message ${result.messageId} delivered to ${target}
|
|
393
|
+
else if (result.delivered) console.log(`Message ${result.messageId} delivered to ${target}${result.machine ? ` on ${result.machine.host}` : ""}
|
|
330
394
|
${result.url}`);
|
|
331
395
|
else console.error(chalk.yellow("Not delivered:"), result.error ?? `session is ${result.status}`, result.stored ? "(stored server-side, unread)" : "");
|
|
332
396
|
process.exit(result.delivered ? 0 : 1);
|
|
@@ -338,24 +402,44 @@ ${result.url}`);
|
|
|
338
402
|
const sessionId = options.sessionId;
|
|
339
403
|
if (options.action === "read") {
|
|
340
404
|
let timedOut = false;
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
405
|
+
const local = !options.machine && persistence.readPersistedSessions()[sessionId] !== void 0;
|
|
406
|
+
let result;
|
|
407
|
+
if (local) {
|
|
408
|
+
if (options.wait) {
|
|
409
|
+
try {
|
|
410
|
+
await agentGuidance.waitForTurnEnd(sessionId, { timeoutMs: (options.timeoutSec ?? 600) * 1e3 });
|
|
411
|
+
} catch (error) {
|
|
412
|
+
if (!(error instanceof agentGuidance.TurnWaitTimeoutError)) throw error;
|
|
413
|
+
timedOut = true;
|
|
414
|
+
console.error(chalk.yellow("Timeout:"), error.message);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
result = await agentGuidance.readSessionTranscript(sessionId, options.limit ?? 20, { full: options.full });
|
|
418
|
+
} else {
|
|
419
|
+
const readOptions = { limit: options.limit ?? 20, full: options.full, ...options.machine ? { machineId: options.machine } : {} };
|
|
420
|
+
let remote;
|
|
421
|
+
if (options.wait) {
|
|
422
|
+
const timeoutMs = (options.timeoutSec ?? 600) * 1e3;
|
|
423
|
+
const waited = await agentGuidance.waitForRemoteTurnEnd(sessionId, { ...readOptions, timeoutMs });
|
|
424
|
+
remote = waited.read;
|
|
425
|
+
if (waited.timedOut) {
|
|
426
|
+
timedOut = true;
|
|
427
|
+
console.error(chalk.yellow("Timeout:"), `Session ${sessionId} on ${remote.host}: the latest turn did not end within ${Math.round(timeoutMs / 1e3)}s`);
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
remote = await agentGuidance.readRemoteTranscript(sessionId, readOptions);
|
|
348
431
|
}
|
|
432
|
+
const { machineId, host, ...rest } = remote;
|
|
433
|
+
result = { ...rest, machine: { id: machineId, host } };
|
|
349
434
|
}
|
|
350
|
-
const result = await agentGuidance.readSessionTranscript(sessionId, options.limit ?? 20, { full: options.full });
|
|
351
435
|
if (options.json) {
|
|
352
|
-
console.log(JSON.stringify(options.answer ? { sessionId, turn: result.turn } : result));
|
|
436
|
+
console.log(JSON.stringify(options.answer ? { sessionId, turn: result.turn, ...result.machine ? { machine: result.machine } : {} } : result));
|
|
353
437
|
} else if (options.answer) {
|
|
354
438
|
if (result.turn.answer.length > 0) console.log(result.turn.answer);
|
|
355
439
|
else console.error(chalk.dim("(the agent has not replied to the latest prompt yet)"));
|
|
356
440
|
} else {
|
|
357
|
-
console.log(formatSummaryLine(result.summary));
|
|
358
|
-
console.log(`--- last ${result.messageCount} message(s) ---`);
|
|
441
|
+
console.log(formatSummaryLine(result.summary) + (result.machine ? chalk.dim(` machine=${result.machine.host}`) : ""));
|
|
442
|
+
console.log(`--- last ${result.messageCount} message(s)${result.truncated ? " (truncated to fit the RPC budget)" : ""} ---`);
|
|
359
443
|
console.log(result.transcript.length > 0 ? result.transcript : "(no readable conversation content in this range)");
|
|
360
444
|
const turn = result.turn;
|
|
361
445
|
console.log(chalk.dim(`--- latest turn: ${turn.ended ? `ended (${turn.status ?? "unknown"})` : "running"}${turn.error ? ` \u2014 ${turn.error}` : ""} ---`));
|