agent-yes 1.265.3 → 1.265.4
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/{SUPPORTED_CLIS-X3jH37iY.js → SUPPORTED_CLIS-C3tcRetU.js} +2 -2
- package/dist/{SUPPORTED_CLIS-DnYyo_km.js → SUPPORTED_CLIS-CY3SaV0D.js} +3 -3
- package/dist/{agentShare-D8Qx06xK.js → agentShare-g6N1A6A_.js} +3 -3
- package/dist/{callback-CByNygd_.js → callback-L4k-v2GG.js} +3 -3
- package/dist/{callback-C5A5zDzb.js → callback-z-w0ND5z.js} +3 -3
- package/dist/cli.js +8 -8
- package/dist/hist-CoXBBjJt.js +442 -0
- package/dist/index.js +2 -2
- package/dist/{nodeRuntime-vhOBffJ3.js → nodeRuntime-CT0w6zsB.js} +1 -1
- package/dist/{notifyDaemon-DuKvOHJJ.js → notifyDaemon-BL1tmxUK.js} +3 -3
- package/dist/{openBrowser-D-aQjh3y.js → openBrowser-Cw8hg7ok.js} +1 -1
- package/dist/{remotes-lOhHSoSS.js → remotes-DWVw-Q7V.js} +1 -1
- package/dist/{remotes-Cn-wszbK.js → remotes-iIvqiFWK.js} +2 -2
- package/dist/{rustBinary-CkTiZVJ4.js → rustBinary-DSVRhDzO.js} +2 -2
- package/dist/{schedule-R6C567wG.js → schedule-i4o5SJ_h.js} +6 -6
- package/dist/{serve-bXCIgnq_.js → serve-CpGJD2Kt.js} +23 -23
- package/dist/{setup-CtUFv2Se.js → setup-B-iQKsQW.js} +4 -4
- package/dist/{spawnGate-D0E1uRP7.js → spawnGate-BmsTv340.js} +2 -2
- package/dist/{spawnGate-DqKBA07k.js → spawnGate-S6EoFdzX.js} +1 -1
- package/dist/{subcommands-Cbhtldub.js → subcommands-CQPnT5qu.js} +22 -18
- package/dist/{subcommands-CnARtyVR.js → subcommands-Cgt2-kjg.js} +2 -2
- package/dist/{systemPathLink-DTAhFXFx.js → systemPathLink-CjLmiPbB.js} +1 -1
- package/dist/{terminal-B8jbz_HG.js → terminal-CybRgMZu.js} +2 -2
- package/dist/{todoCli-CngDeaT2.js → todoCli-DZmOmcvd.js} +1 -1
- package/dist/{tray-wWMvGWeY.js → tray-Cjm3a8JH.js} +1 -1
- package/dist/trayApp-CmwwBg8v.js +5 -0
- package/dist/{trayApp-CZR7Thm6.js → trayApp-UglHbk_m.js} +2 -2
- package/dist/{ts-ZKWh7obB.js → ts-nuUnrVcf.js} +2 -2
- package/dist/{versionChecker-C6RmYXss.js → versionChecker-CMOLbEEi.js} +2 -2
- package/dist/{webrtcRemote-BkZcA5yq.js → webrtcRemote-zQybN9IR.js} +1 -1
- package/dist/{widget-BOcpTCTQ.js → widget-y5Bpvwsa.js} +3 -3
- package/dist/{workspaceConfig-Bxuk_MOq.js → workspaceConfig-BDqp7fYl.js} +1 -1
- package/dist/{ws-CPJxObp_.js → ws-BrLV-AlH.js} +3 -3
- package/dist/{ws-C9NmPMjN.js → ws-CH8GzcQn.js} +3 -3
- package/package.json +1 -1
- package/ts/hist.spec.ts +18 -3
- package/ts/hist.ts +13 -4
- package/ts/subcommands.ts +8 -0
- package/dist/trayApp-Bx0qlt3A.js +0 -5
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as CLIS_CONFIG } from "./ts-
|
|
1
|
+
import { t as CLIS_CONFIG } from "./ts-nuUnrVcf.js";
|
|
2
2
|
|
|
3
3
|
//#region ts/SUPPORTED_CLIS.ts
|
|
4
4
|
const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
|
|
5
5
|
|
|
6
6
|
//#endregion
|
|
7
7
|
export { SUPPORTED_CLIS as t };
|
|
8
|
-
//# sourceMappingURL=SUPPORTED_CLIS-
|
|
8
|
+
//# sourceMappingURL=SUPPORTED_CLIS-C3tcRetU.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "./logger-CDIsZ-Pp.js";
|
|
2
|
-
import "./versionChecker-
|
|
3
|
-
import "./ts-
|
|
2
|
+
import "./versionChecker-CMOLbEEi.js";
|
|
3
|
+
import "./ts-nuUnrVcf.js";
|
|
4
4
|
import "./todoAutomation-BQRW614S.js";
|
|
5
5
|
import "./JsonlStore-CPGIR0Wy.js";
|
|
6
6
|
import "./globalPidIndex-Dq4KY2IV.js";
|
|
7
7
|
import "./pidStore-DJqSZPJi.js";
|
|
8
8
|
import "./messageLog-BYFwioFY.js";
|
|
9
|
-
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
9
|
+
import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C3tcRetU.js";
|
|
10
10
|
|
|
11
11
|
export { SUPPORTED_CLIS };
|
|
@@ -3,9 +3,9 @@ import "./globalPidIndex-Dq4KY2IV.js";
|
|
|
3
3
|
import "./messageLog-BYFwioFY.js";
|
|
4
4
|
import "./e2e-CUMZC53I.js";
|
|
5
5
|
import "./configShared-DtHMBCl7.js";
|
|
6
|
-
import { R as resolveOne } from "./subcommands-
|
|
6
|
+
import { R as resolveOne } from "./subcommands-CQPnT5qu.js";
|
|
7
7
|
import "./webrtcLink-CS4D-3G1.js";
|
|
8
|
-
import "./remotes-
|
|
8
|
+
import "./remotes-iIvqiFWK.js";
|
|
9
9
|
import { a as startShare } from "./share-B-e_9WBx.js";
|
|
10
10
|
|
|
11
11
|
//#region ts/agentShare.ts
|
|
@@ -229,4 +229,4 @@ function transformEvent(rawEvent, agentId, forwarded) {
|
|
|
229
229
|
|
|
230
230
|
//#endregion
|
|
231
231
|
export { createScopedShare, listShares, revokeAllShares, revokeShare };
|
|
232
|
-
//# sourceMappingURL=agentShare-
|
|
232
|
+
//# sourceMappingURL=agentShare-g6N1A6A_.js.map
|
|
@@ -3,10 +3,10 @@ import "./globalPidIndex-Dq4KY2IV.js";
|
|
|
3
3
|
import "./messageLog-BYFwioFY.js";
|
|
4
4
|
import "./e2e-CUMZC53I.js";
|
|
5
5
|
import "./configShared-DtHMBCl7.js";
|
|
6
|
-
import "./subcommands-
|
|
6
|
+
import "./subcommands-CQPnT5qu.js";
|
|
7
7
|
import "./webrtcLink-CS4D-3G1.js";
|
|
8
|
-
import "./remotes-
|
|
8
|
+
import "./remotes-iIvqiFWK.js";
|
|
9
9
|
import "./callbackCore-Cg3Ye0ZS.js";
|
|
10
|
-
import { i as loadOrCreateCallbackSecret, n as isCallbackRevoked, r as loadCallbackSecretReadOnly, t as cmdCallback } from "./callback-
|
|
10
|
+
import { i as loadOrCreateCallbackSecret, n as isCallbackRevoked, r as loadCallbackSecretReadOnly, t as cmdCallback } from "./callback-z-w0ND5z.js";
|
|
11
11
|
|
|
12
12
|
export { cmdCallback };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as agentYesHome } from "./agentYesHome-CtmsY6I-.js";
|
|
2
|
-
import { R as resolveOne } from "./subcommands-
|
|
2
|
+
import { R as resolveOne } from "./subcommands-CQPnT5qu.js";
|
|
3
3
|
import { a as mintCapability, o as parseExpires } from "./callbackCore-Cg3Ye0ZS.js";
|
|
4
4
|
import { randomBytes } from "node:crypto";
|
|
5
5
|
import { chmodSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
@@ -72,7 +72,7 @@ async function resolveBase(flag) {
|
|
|
72
72
|
if (flag) return flag;
|
|
73
73
|
if (process.env.AGENT_YES_CALLBACK_BASE) return process.env.AGENT_YES_CALLBACK_BASE;
|
|
74
74
|
try {
|
|
75
|
-
const { resolveLocalServeUrl } = await import("./serve-
|
|
75
|
+
const { resolveLocalServeUrl } = await import("./serve-CpGJD2Kt.js");
|
|
76
76
|
const url = await resolveLocalServeUrl();
|
|
77
77
|
if (url) return url;
|
|
78
78
|
} catch {}
|
|
@@ -187,4 +187,4 @@ async function cmdCallback(rest) {
|
|
|
187
187
|
|
|
188
188
|
//#endregion
|
|
189
189
|
export { loadOrCreateCallbackSecret as i, isCallbackRevoked as n, loadCallbackSecretReadOnly as r, cmdCallback as t };
|
|
190
|
-
//# sourceMappingURL=callback-
|
|
190
|
+
//# sourceMappingURL=callback-z-w0ND5z.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { t as invokedCliName } from "./invokedCli-uqM2YYA7.js";
|
|
3
3
|
import { n as logger } from "./logger-CDIsZ-Pp.js";
|
|
4
|
-
import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-
|
|
5
|
-
import { r as getRustBinary } from "./rustBinary-
|
|
4
|
+
import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-CMOLbEEi.js";
|
|
5
|
+
import { r as getRustBinary } from "./rustBinary-DSVRhDzO.js";
|
|
6
6
|
import { argv } from "process";
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
8
|
import ms from "ms";
|
|
@@ -295,7 +295,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
295
295
|
const rawArg = process.argv[2];
|
|
296
296
|
const managerCommands = !invokedCliName(process.argv);
|
|
297
297
|
const isHelpFlag = rawArg === "-h" || rawArg === "--help";
|
|
298
|
-
const { isSubcommand, runSubcommand, cmdHelp, isUnknownManagerToken, isBareManagerInvocation } = await import("./subcommands-
|
|
298
|
+
const { isSubcommand, runSubcommand, cmdHelp, isUnknownManagerToken, isBareManagerInvocation } = await import("./subcommands-Cgt2-kjg.js");
|
|
299
299
|
if (isHelpFlag && process.argv.length === 3) {
|
|
300
300
|
await cmdHelp(managerCommands);
|
|
301
301
|
process.exit(0);
|
|
@@ -309,7 +309,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
|
|
|
309
309
|
process.exit(code ?? 0);
|
|
310
310
|
}
|
|
311
311
|
{
|
|
312
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
312
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-CY3SaV0D.js");
|
|
313
313
|
if (isUnknownManagerToken(rawArg, managerCommands, SUPPORTED_CLIS)) {
|
|
314
314
|
process.stderr.write(`ay: unknown subcommand or CLI '${rawArg}'.\n See 'ay help' for subcommands. To run an agent, name a CLI:\n 'ay <cli> …' (e.g. 'ay claude …') or 'ay --cli <cli> …'.\n`);
|
|
315
315
|
process.exit(1);
|
|
@@ -328,16 +328,16 @@ await checkAndAutoUpdate();
|
|
|
328
328
|
logger.info(versionString());
|
|
329
329
|
const config = parseCliArgs(process.argv);
|
|
330
330
|
if (config.tray) {
|
|
331
|
-
const { startTray } = await import("./tray-
|
|
331
|
+
const { startTray } = await import("./tray-Cjm3a8JH.js");
|
|
332
332
|
await startTray();
|
|
333
333
|
await new Promise(() => {});
|
|
334
334
|
}
|
|
335
335
|
{
|
|
336
|
-
const { ensureTray } = await import("./tray-
|
|
336
|
+
const { ensureTray } = await import("./tray-Cjm3a8JH.js");
|
|
337
337
|
ensureTray();
|
|
338
338
|
}
|
|
339
339
|
if (!config.showVersion && !config.appendPrompt && !config.tray) {
|
|
340
|
-
const { waitForSpawnCapacity } = await import("./spawnGate-
|
|
340
|
+
const { waitForSpawnCapacity } = await import("./spawnGate-S6EoFdzX.js");
|
|
341
341
|
await waitForSpawnCapacity({
|
|
342
342
|
onWait: (reason, waitedMs) => {
|
|
343
343
|
if (config.verbose || waitedMs === 0) console.error(`[agent-yes] spawn gate: ${reason} — waiting…`);
|
|
@@ -360,7 +360,7 @@ if (config.useRust) {
|
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
362
|
if (rustBinary) {
|
|
363
|
-
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-
|
|
363
|
+
const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-CY3SaV0D.js");
|
|
364
364
|
const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
|
|
365
365
|
if (config.verbose) {
|
|
366
366
|
console.log(`[rust] Using binary: ${rustBinary}`);
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import yargs from "yargs";
|
|
2
|
+
import { open, readdir, stat } from "fs/promises";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
//#region ts/histStore.ts
|
|
7
|
+
/**
|
|
8
|
+
* `ay hist` — read the tail of *coding-agent conversation transcripts* that
|
|
9
|
+
* already exist on this machine (Claude Code, Codex), newest-last.
|
|
10
|
+
*
|
|
11
|
+
* This is deliberately NOT `ay tail`. `ay tail` follows the live PTY log of an
|
|
12
|
+
* agent process agent-yes spawned; `ay hist` reads the durable JSONL transcript
|
|
13
|
+
* the CLI itself writes, including sessions from before agent-yes was involved
|
|
14
|
+
* and sessions whose process already exited.
|
|
15
|
+
*
|
|
16
|
+
* Design notes (the two that actually matter):
|
|
17
|
+
*
|
|
18
|
+
* 1. **Read backwards.** Transcript lines are whole JSON records and routinely
|
|
19
|
+
* reach 1.3MB (tool outputs, base64 images). Reading a file forward — or
|
|
20
|
+
* `readFile().split("\n")` — costs megabytes to show six records. We seek to
|
|
21
|
+
* EOF and walk back in chunks, stopping as soon as the caller has enough, so
|
|
22
|
+
* cost is proportional to what's displayed, not to session length.
|
|
23
|
+
*
|
|
24
|
+
* 2. **No index.** These files are append-only, so a sidecar index storing
|
|
25
|
+
* `indexed_up_to_bytes` would let a future `ay hist search` re-scan only the
|
|
26
|
+
* tail delta. Tailing needs none of that, and the 90% case is tailing — so
|
|
27
|
+
* v1 ships with zero index, zero daemon, zero cache invalidation.
|
|
28
|
+
*/
|
|
29
|
+
const NEWLINE = 10;
|
|
30
|
+
/**
|
|
31
|
+
* Claude Code names each project directory after its cwd with every
|
|
32
|
+
* non-alphanumeric byte replaced by `-`:
|
|
33
|
+
* `/code/snomiao/cv.snomiao.com` → `-code-snomiao-cv-snomiao-com`.
|
|
34
|
+
*
|
|
35
|
+
* Encoding the cwd and comparing is exact and costs no file reads — decoding
|
|
36
|
+
* the slug back to a path is not possible, since `-` is ambiguous.
|
|
37
|
+
*/
|
|
38
|
+
function encodeProjectSlug(cwd) {
|
|
39
|
+
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
40
|
+
}
|
|
41
|
+
function claudeProjectsDir(home = homedir()) {
|
|
42
|
+
return path.join(home, ".claude", "projects");
|
|
43
|
+
}
|
|
44
|
+
function codexSessionsDir(home = homedir()) {
|
|
45
|
+
return path.join(home, ".codex", "sessions");
|
|
46
|
+
}
|
|
47
|
+
async function listJsonlDeep(dir, out = [], depth = 0) {
|
|
48
|
+
if (depth > 4) return out;
|
|
49
|
+
let entries;
|
|
50
|
+
try {
|
|
51
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
52
|
+
} catch {
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
for (const e of entries) {
|
|
56
|
+
const full = path.join(dir, e.name);
|
|
57
|
+
if (e.isDirectory()) await listJsonlDeep(full, out, depth + 1);
|
|
58
|
+
else if (e.isFile() && e.name.endsWith(".jsonl")) out.push(full);
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Locate transcripts, newest first. Only stats files — never opens them — so
|
|
64
|
+
* this stays cheap even with thousands of sessions on disk.
|
|
65
|
+
*/
|
|
66
|
+
async function discoverTranscripts(opts = {}) {
|
|
67
|
+
const home = opts.home ?? homedir();
|
|
68
|
+
const sources = opts.sources ?? ["claude", "codex"];
|
|
69
|
+
const found = [];
|
|
70
|
+
if (sources.includes("claude")) {
|
|
71
|
+
const root = claudeProjectsDir(home);
|
|
72
|
+
const wanted = opts.cwd ? encodeProjectSlug(opts.cwd) : null;
|
|
73
|
+
let projects = [];
|
|
74
|
+
try {
|
|
75
|
+
projects = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
76
|
+
} catch {
|
|
77
|
+
projects = [];
|
|
78
|
+
}
|
|
79
|
+
for (const slug of projects) {
|
|
80
|
+
if (wanted && slug !== wanted) continue;
|
|
81
|
+
for (const file of await listJsonlDeep(path.join(root, slug))) {
|
|
82
|
+
const st = await stat(file).catch(() => null);
|
|
83
|
+
if (!st?.isFile() || st.size === 0) continue;
|
|
84
|
+
found.push({
|
|
85
|
+
path: file,
|
|
86
|
+
source: "claude",
|
|
87
|
+
sessionId: path.basename(file, ".jsonl"),
|
|
88
|
+
mtimeMs: st.mtimeMs,
|
|
89
|
+
size: st.size
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (sources.includes("codex")) for (const file of await listJsonlDeep(codexSessionsDir(home))) {
|
|
95
|
+
if (!path.basename(file).startsWith("rollout-")) continue;
|
|
96
|
+
const st = await stat(file).catch(() => null);
|
|
97
|
+
if (!st?.isFile() || st.size === 0) continue;
|
|
98
|
+
found.push({
|
|
99
|
+
path: file,
|
|
100
|
+
source: "codex",
|
|
101
|
+
sessionId: path.basename(file, ".jsonl"),
|
|
102
|
+
mtimeMs: st.mtimeMs,
|
|
103
|
+
size: st.size
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
found.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
107
|
+
if (opts.cwd && sources.includes("codex")) {
|
|
108
|
+
const kept = [];
|
|
109
|
+
for (const f of found) {
|
|
110
|
+
if (f.source !== "codex") {
|
|
111
|
+
kept.push(f);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const cwd = await readCodexCwd(f.path);
|
|
115
|
+
if (cwd === opts.cwd) kept.push({
|
|
116
|
+
...f,
|
|
117
|
+
cwd
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return kept;
|
|
121
|
+
}
|
|
122
|
+
return found;
|
|
123
|
+
}
|
|
124
|
+
/** Read the leading `session_meta` record of a Codex rollout to learn its cwd. */
|
|
125
|
+
async function readCodexCwd(file) {
|
|
126
|
+
let fh;
|
|
127
|
+
try {
|
|
128
|
+
fh = await open(file, "r");
|
|
129
|
+
const buf = Buffer.alloc(64 * 1024);
|
|
130
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
|
|
131
|
+
const nl = buf.indexOf(NEWLINE);
|
|
132
|
+
const end = nl === -1 ? bytesRead : nl;
|
|
133
|
+
return JSON.parse(buf.subarray(0, end).toString("utf8")).payload?.cwd;
|
|
134
|
+
} catch {
|
|
135
|
+
return;
|
|
136
|
+
} finally {
|
|
137
|
+
await fh?.close();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Yield whole lines from EOF toward the start, newest first, as
|
|
142
|
+
* `{ offset, text }`. Lazy: the generator only reads another chunk when the
|
|
143
|
+
* consumer asks for another line, so `take(6)` reads ~one chunk regardless of
|
|
144
|
+
* whether the file is 4KB or 400MB.
|
|
145
|
+
*
|
|
146
|
+
* A trailing partial line (a session being appended to right now) is dropped —
|
|
147
|
+
* we stop at the last complete newline rather than hand back half a record.
|
|
148
|
+
*/
|
|
149
|
+
async function* readLinesBackward(file, opts = {}) {
|
|
150
|
+
const chunkSize = opts.chunkSize ?? 64 * 1024;
|
|
151
|
+
const maxRecordBytes = opts.maxRecordBytes ?? 16 * 1024 * 1024;
|
|
152
|
+
const fh = await open(file, "r");
|
|
153
|
+
try {
|
|
154
|
+
let pos = (await fh.stat()).size;
|
|
155
|
+
if (pos === 0) return;
|
|
156
|
+
let pending = Buffer.alloc(0);
|
|
157
|
+
let base = pos;
|
|
158
|
+
let first = true;
|
|
159
|
+
while (pos > 0) {
|
|
160
|
+
const len = Math.min(chunkSize, pos);
|
|
161
|
+
pos -= len;
|
|
162
|
+
const buf = Buffer.alloc(len);
|
|
163
|
+
await fh.read(buf, 0, len, pos);
|
|
164
|
+
const combined = pending.length ? Buffer.concat([buf, pending]) : buf;
|
|
165
|
+
base = pos;
|
|
166
|
+
let end = combined.length;
|
|
167
|
+
if (first) {
|
|
168
|
+
first = false;
|
|
169
|
+
const last = combined.lastIndexOf(NEWLINE);
|
|
170
|
+
end = last === -1 ? 0 : last;
|
|
171
|
+
}
|
|
172
|
+
while (end > 0) {
|
|
173
|
+
const nl = combined.lastIndexOf(NEWLINE, end - 1);
|
|
174
|
+
if (nl === -1) break;
|
|
175
|
+
const text = combined.subarray(nl + 1, end).toString("utf8");
|
|
176
|
+
if (text.trim()) yield {
|
|
177
|
+
offset: base + nl + 1,
|
|
178
|
+
text
|
|
179
|
+
};
|
|
180
|
+
end = nl;
|
|
181
|
+
}
|
|
182
|
+
pending = combined.subarray(0, end);
|
|
183
|
+
if (pending.length > maxRecordBytes) pending = Buffer.alloc(0);
|
|
184
|
+
}
|
|
185
|
+
const head = pending.toString("utf8");
|
|
186
|
+
if (head.trim()) yield {
|
|
187
|
+
offset: 0,
|
|
188
|
+
text: head
|
|
189
|
+
};
|
|
190
|
+
} finally {
|
|
191
|
+
await fh.close();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function claudeText(content, includeTools) {
|
|
195
|
+
if (typeof content === "string") return content;
|
|
196
|
+
if (!Array.isArray(content)) return "";
|
|
197
|
+
const parts = [];
|
|
198
|
+
for (const raw of content) {
|
|
199
|
+
if (!raw || typeof raw !== "object") continue;
|
|
200
|
+
if (raw.type === "text" && typeof raw.text === "string") parts.push(raw.text);
|
|
201
|
+
else if (includeTools && raw.type === "thinking" && typeof raw.thinking === "string") parts.push(`(thinking) ${raw.thinking}`);
|
|
202
|
+
else if (includeTools && raw.type === "tool_use") parts.push(`[tool: ${raw.name ?? "?"}]`);
|
|
203
|
+
else if (includeTools && raw.type === "tool_result") parts.push(`[tool result]`);
|
|
204
|
+
}
|
|
205
|
+
return parts.join("\n").trim();
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Reduce one raw transcript line to a displayable turn, or null when the record
|
|
209
|
+
* is not conversational (metadata, token counts, tool plumbing, …).
|
|
210
|
+
*
|
|
211
|
+
* Returning null for the great majority of records is the point: it is what
|
|
212
|
+
* makes six records of *conversation* cost six records of output rather than
|
|
213
|
+
* six records of JSON noise.
|
|
214
|
+
*/
|
|
215
|
+
function projectRecord(line, source, opts = {}) {
|
|
216
|
+
let rec;
|
|
217
|
+
try {
|
|
218
|
+
rec = JSON.parse(line);
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
if (!rec || typeof rec !== "object") return null;
|
|
223
|
+
if (source === "claude") {
|
|
224
|
+
if (rec.type !== "user" && rec.type !== "assistant") return null;
|
|
225
|
+
if (rec.isSidechain && !opts.includeSidechains) return null;
|
|
226
|
+
const role = rec.message?.role === "assistant" ? "assistant" : "user";
|
|
227
|
+
const text = claudeText(rec.message?.content, opts.includeTools ?? false);
|
|
228
|
+
if (!text) return null;
|
|
229
|
+
return {
|
|
230
|
+
ts: typeof rec.timestamp === "string" ? rec.timestamp : null,
|
|
231
|
+
role,
|
|
232
|
+
text
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (rec.type !== "event_msg") return null;
|
|
236
|
+
const kind = rec.payload?.type;
|
|
237
|
+
if (kind !== "user_message" && kind !== "agent_message") return null;
|
|
238
|
+
const text = typeof rec.payload?.message === "string" ? rec.payload.message.trim() : "";
|
|
239
|
+
if (!text) return null;
|
|
240
|
+
return {
|
|
241
|
+
ts: typeof rec.timestamp === "string" ? rec.timestamp : null,
|
|
242
|
+
role: kind === "user_message" ? "user" : "assistant",
|
|
243
|
+
text
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function encodeCursor(r) {
|
|
247
|
+
return `${r.ts ?? ""}|${r.sessionId}|${r.offset}`;
|
|
248
|
+
}
|
|
249
|
+
/** Parse an encoded cursor; returns null for a bare timestamp or garbage. */
|
|
250
|
+
function decodeCursor(raw) {
|
|
251
|
+
const parts = raw.split("|");
|
|
252
|
+
if (parts.length !== 3) return null;
|
|
253
|
+
const offset = Number(parts[2]);
|
|
254
|
+
if (!Number.isFinite(offset)) return null;
|
|
255
|
+
return {
|
|
256
|
+
ts: parts[0],
|
|
257
|
+
sessionId: parts[1],
|
|
258
|
+
offset
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/** Total order over records: timestamp, then session, then byte offset. */
|
|
262
|
+
function compareRecords(a, b) {
|
|
263
|
+
return (a.ts ?? "").localeCompare(b.ts ?? "") || a.sessionId.localeCompare(b.sessionId) || a.offset - b.offset;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Newest `limit` conversational records of one transcript, oldest-first.
|
|
267
|
+
*
|
|
268
|
+
* Stops reading as soon as `limit` records are collected — the whole reason the
|
|
269
|
+
* backward reader is a generator.
|
|
270
|
+
*/
|
|
271
|
+
async function tailTranscript(file, opts) {
|
|
272
|
+
const out = [];
|
|
273
|
+
for await (const { offset, text } of readLinesBackward(file.path, opts)) {
|
|
274
|
+
const projected = projectRecord(text, file.source, opts);
|
|
275
|
+
if (!projected) continue;
|
|
276
|
+
if (opts.role && projected.role !== opts.role) continue;
|
|
277
|
+
const record = {
|
|
278
|
+
...projected,
|
|
279
|
+
source: file.source,
|
|
280
|
+
sessionId: file.sessionId,
|
|
281
|
+
file: file.path,
|
|
282
|
+
offset
|
|
283
|
+
};
|
|
284
|
+
if (opts.cursor) {
|
|
285
|
+
if (compareRecords(record, opts.cursor) >= 0) continue;
|
|
286
|
+
} else if (opts.before && projected.ts && projected.ts >= opts.before) continue;
|
|
287
|
+
out.push(record);
|
|
288
|
+
if (out.length >= opts.limit) break;
|
|
289
|
+
}
|
|
290
|
+
return out.reverse();
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Merge the tails of the most recently touched transcripts into one
|
|
294
|
+
* chronological page — "what have my agents been saying", across sessions.
|
|
295
|
+
*/
|
|
296
|
+
async function histPage(q) {
|
|
297
|
+
const considered = (await discoverTranscripts({
|
|
298
|
+
home: q.home,
|
|
299
|
+
cwd: q.cwd,
|
|
300
|
+
sources: q.sources
|
|
301
|
+
})).slice(0, q.maxFiles ?? 40);
|
|
302
|
+
const perFile = q.limit > 0 ? q.limit + 1 : Number.MAX_SAFE_INTEGER;
|
|
303
|
+
const cursor = q.cursor ?? (q.before ? decodeCursor(q.before) : null);
|
|
304
|
+
const merged = (await Promise.all(considered.map((f) => tailTranscript(f, {
|
|
305
|
+
...q,
|
|
306
|
+
limit: perFile,
|
|
307
|
+
cursor: cursor ?? void 0
|
|
308
|
+
})))).flat();
|
|
309
|
+
merged.sort(compareRecords);
|
|
310
|
+
const records = q.limit > 0 ? merged.slice(-q.limit) : merged;
|
|
311
|
+
const oldest = records[0];
|
|
312
|
+
return {
|
|
313
|
+
records,
|
|
314
|
+
cursor: merged.length > records.length && oldest ? encodeCursor(oldest) : null,
|
|
315
|
+
scanned: considered.length
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region ts/hist.ts
|
|
321
|
+
/**
|
|
322
|
+
* `ay hist` — CLI surface over {@link ./histStore.ts}.
|
|
323
|
+
*
|
|
324
|
+
* Reads past coding-agent conversations (Claude Code, Codex) that already exist
|
|
325
|
+
* on disk. Distinct from `ay tail`, which follows the live PTY log of a process
|
|
326
|
+
* agent-yes spawned; `ay hist` reads the CLI's own durable transcript, so it
|
|
327
|
+
* still works for sessions that already exited.
|
|
328
|
+
*/
|
|
329
|
+
const DEFAULT_LIMIT = 6;
|
|
330
|
+
const DEFAULT_SNIPPET = 600;
|
|
331
|
+
const DIM = "\x1B[2m";
|
|
332
|
+
const CYAN = "\x1B[36m";
|
|
333
|
+
const GREEN = "\x1B[32m";
|
|
334
|
+
const RESET = "\x1B[0m";
|
|
335
|
+
function shortTime(ts) {
|
|
336
|
+
if (!ts) return "??:??";
|
|
337
|
+
const d = new Date(ts);
|
|
338
|
+
if (Number.isNaN(d.getTime())) return "??:??";
|
|
339
|
+
const now = Date.now();
|
|
340
|
+
const sameDay = new Date(now).toDateString() === d.toDateString();
|
|
341
|
+
const hhmm = d.toTimeString().slice(0, 5);
|
|
342
|
+
return sameDay ? hhmm : `${d.toISOString().slice(5, 10)} ${hhmm}`;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Collapse a turn to `max` chars, noting how much was withheld.
|
|
346
|
+
*
|
|
347
|
+
* `color` gates the dim tint on that note. Everything else in the renderer
|
|
348
|
+
* already honours it, but this note hardcoded the escapes — so `ay hist | cat`
|
|
349
|
+
* stayed clean only until a turn was long enough to truncate, i.e. the common
|
|
350
|
+
* case. Defaults true so a direct `snippet()` call keeps its old look.
|
|
351
|
+
*/
|
|
352
|
+
function snippet(text, max, color = true) {
|
|
353
|
+
const trimmed = text.trim();
|
|
354
|
+
if (max <= 0 || trimmed.length <= max) return trimmed;
|
|
355
|
+
const head = trimmed.slice(0, max);
|
|
356
|
+
const hiddenLines = trimmed.slice(max).split("\n").length;
|
|
357
|
+
const dim = color ? DIM : "";
|
|
358
|
+
const off = color ? RESET : "";
|
|
359
|
+
return `${head}… ${dim}(+${trimmed.length - max} chars, ${hiddenLines} more lines)${off}`;
|
|
360
|
+
}
|
|
361
|
+
function renderRecord(r, opts) {
|
|
362
|
+
const who = r.role === "user" ? "user" : r.source;
|
|
363
|
+
const tint = opts.color ? r.role === "user" ? CYAN : GREEN : "";
|
|
364
|
+
const dim = opts.color ? DIM : "";
|
|
365
|
+
const off = opts.color ? RESET : "";
|
|
366
|
+
const body = snippet(r.text, opts.max, opts.color).split("\n").map((l) => ` ${l}`).join("\n");
|
|
367
|
+
return `${`${tint}${who}${off} ${dim}${shortTime(r.ts)} ${r.sessionId.slice(0, 8)}${off}`}\n${body}\n`;
|
|
368
|
+
}
|
|
369
|
+
async function cmdHist(rest) {
|
|
370
|
+
const argv = await yargs(rest).usage("Usage: ay hist [options]\n\nTail past coding-agent conversations (Claude Code, Codex) from this machine.\nDefaults to sessions for the current directory, newest last.\n\nPagination: rerun with --before <cursor> using the cursor printed at the end.").option("n", {
|
|
371
|
+
type: "number",
|
|
372
|
+
default: DEFAULT_LIMIT,
|
|
373
|
+
description: `Number of turns (default ${DEFAULT_LIMIT}; 0 = no cap)`
|
|
374
|
+
}).option("all", {
|
|
375
|
+
type: "boolean",
|
|
376
|
+
default: false,
|
|
377
|
+
description: "All projects, not just the current directory"
|
|
378
|
+
}).option("cwd", {
|
|
379
|
+
type: "string",
|
|
380
|
+
description: "Scope to this directory instead of $PWD"
|
|
381
|
+
}).option("source", {
|
|
382
|
+
choices: ["claude", "codex"],
|
|
383
|
+
description: "Only one agent's transcripts"
|
|
384
|
+
}).option("role", {
|
|
385
|
+
choices: ["user", "assistant"],
|
|
386
|
+
description: "Only my prompts, or only the agent's replies"
|
|
387
|
+
}).option("before", {
|
|
388
|
+
type: "string",
|
|
389
|
+
description: "Cursor: only turns older than this ISO timestamp"
|
|
390
|
+
}).option("tools", {
|
|
391
|
+
type: "boolean",
|
|
392
|
+
default: false,
|
|
393
|
+
description: "Include tool calls/results and thinking as markers"
|
|
394
|
+
}).option("sidechains", {
|
|
395
|
+
type: "boolean",
|
|
396
|
+
default: false,
|
|
397
|
+
description: "Include Claude sub-agent turns"
|
|
398
|
+
}).option("full", {
|
|
399
|
+
type: "boolean",
|
|
400
|
+
default: false,
|
|
401
|
+
description: "Do not truncate turns"
|
|
402
|
+
}).option("json", {
|
|
403
|
+
type: "boolean",
|
|
404
|
+
default: false,
|
|
405
|
+
description: "JSONL for scripts/agents"
|
|
406
|
+
}).help().parse();
|
|
407
|
+
const cwd = argv.all ? void 0 : argv.cwd ?? process.cwd();
|
|
408
|
+
const page = await histPage({
|
|
409
|
+
cwd,
|
|
410
|
+
limit: Number(argv.n),
|
|
411
|
+
before: argv.before,
|
|
412
|
+
role: argv.role,
|
|
413
|
+
sources: argv.source ? [argv.source] : void 0,
|
|
414
|
+
includeTools: Boolean(argv.tools),
|
|
415
|
+
includeSidechains: Boolean(argv.sidechains)
|
|
416
|
+
});
|
|
417
|
+
if (argv.json) {
|
|
418
|
+
for (const r of page.records) process.stdout.write(JSON.stringify(r) + "\n");
|
|
419
|
+
return page.records.length ? 0 : 1;
|
|
420
|
+
}
|
|
421
|
+
if (!page.records.length) {
|
|
422
|
+
const where = cwd ? `for ${cwd}` : "on this machine";
|
|
423
|
+
process.stderr.write(`no agent conversation history ${where} (scanned ${page.scanned} transcript${page.scanned === 1 ? "" : "s"}).\n` + (cwd ? `try: ay hist --all\n` : ""));
|
|
424
|
+
return 1;
|
|
425
|
+
}
|
|
426
|
+
const color = process.stdout.isTTY === true;
|
|
427
|
+
const max = argv.full ? 0 : DEFAULT_SNIPPET;
|
|
428
|
+
for (const r of page.records) process.stdout.write(renderRecord(r, {
|
|
429
|
+
color,
|
|
430
|
+
max
|
|
431
|
+
}) + "\n");
|
|
432
|
+
if (page.cursor) {
|
|
433
|
+
const dim = color ? DIM : "";
|
|
434
|
+
const off = color ? RESET : "";
|
|
435
|
+
process.stderr.write(`${dim}older: ay hist --before ${page.cursor}${off}\n`);
|
|
436
|
+
}
|
|
437
|
+
return 0;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
//#endregion
|
|
441
|
+
export { cmdHist };
|
|
442
|
+
//# sourceMappingURL=hist-CoXBBjJt.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./logger-CDIsZ-Pp.js";
|
|
2
|
-
import "./versionChecker-
|
|
3
|
-
import { a as hasTodoStore, i as answerAsk, l as AgentContext, n as agentYes, o as listAsks, r as config, s as listAsksForProject, t as CLIS_CONFIG } from "./ts-
|
|
2
|
+
import "./versionChecker-CMOLbEEi.js";
|
|
3
|
+
import { a as hasTodoStore, i as answerAsk, l as AgentContext, n as agentYes, o as listAsks, r as config, s as listAsksForProject, t as CLIS_CONFIG } from "./ts-nuUnrVcf.js";
|
|
4
4
|
import { _ as isKnownKind, a as renderTree, b as statesOf, c as monitorHint, d as openStore, f as DONE_STATE, g as initialState, h as canTransition, i as renderDigest, l as CycleError, m as ORPHANED_STATE, o as unblockedTasks, p as LIFECYCLES, r as openBlockers, s as describeBlock, t as reconcileTodos, u as TodoStore, v as nextStates, x as removeControlCharacters, y as requiredGate } from "./todoAutomation-BQRW614S.js";
|
|
5
5
|
import "./JsonlStore-CPGIR0Wy.js";
|
|
6
6
|
import "./globalPidIndex-Dq4KY2IV.js";
|
|
@@ -3,9 +3,9 @@ import "./globalPidIndex-Dq4KY2IV.js";
|
|
|
3
3
|
import "./messageLog-BYFwioFY.js";
|
|
4
4
|
import "./e2e-CUMZC53I.js";
|
|
5
5
|
import "./configShared-DtHMBCl7.js";
|
|
6
|
-
import { E as listRecords, F as renderLogTailLines, Q as hostId, X as appendEvent, Z as gcInboxes, at as daemonLockOwnerPath, b as isPidAlive, et as listInboxParents, it as daemonLockDir, nt as readInbox, ot as notifyDir, rt as shouldStealLock, tt as liveWatchers, u as deriveLiveState } from "./subcommands-
|
|
6
|
+
import { E as listRecords, F as renderLogTailLines, Q as hostId, X as appendEvent, Z as gcInboxes, at as daemonLockOwnerPath, b as isPidAlive, et as listInboxParents, it as daemonLockDir, nt as readInbox, ot as notifyDir, rt as shouldStealLock, tt as liveWatchers, u as deriveLiveState } from "./subcommands-CQPnT5qu.js";
|
|
7
7
|
import "./webrtcLink-CS4D-3G1.js";
|
|
8
|
-
import "./remotes-
|
|
8
|
+
import "./remotes-iIvqiFWK.js";
|
|
9
9
|
import { mkdir, readFile, rename, rm, stat, writeFile } from "fs/promises";
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
11
|
import { execFile, execFileSync } from "node:child_process";
|
|
@@ -836,4 +836,4 @@ async function ensureDaemon() {
|
|
|
836
836
|
|
|
837
837
|
//#endregion
|
|
838
838
|
export { daemonStatus, ensureDaemon, requestDaemonStop, runDaemon };
|
|
839
|
-
//# sourceMappingURL=notifyDaemon-
|
|
839
|
+
//# sourceMappingURL=notifyDaemon-BL1tmxUK.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./e2e-CUMZC53I.js";
|
|
2
2
|
import "./webrtcLink-CS4D-3G1.js";
|
|
3
|
-
import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-
|
|
3
|
+
import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-iIvqiFWK.js";
|
|
4
4
|
|
|
5
5
|
export { cmdRemote };
|
|
@@ -86,7 +86,7 @@ async function resolveRemoteSpec(spec) {
|
|
|
86
86
|
* The bridge lives for the rest of the process (torn down on `process.exit`).
|
|
87
87
|
*/
|
|
88
88
|
async function resolveWebrtc(link, keyword) {
|
|
89
|
-
const { startWebrtcBridge } = await import("./webrtcRemote-
|
|
89
|
+
const { startWebrtcBridge } = await import("./webrtcRemote-zQybN9IR.js");
|
|
90
90
|
const bridge = await startWebrtcBridge(link);
|
|
91
91
|
return {
|
|
92
92
|
url: bridge.baseUrl,
|
|
@@ -173,4 +173,4 @@ async function cmdRemote(rest) {
|
|
|
173
173
|
|
|
174
174
|
//#endregion
|
|
175
175
|
export { resolveRemoteSpec as a, readRemotes as i, deleteRemoteAlias as n, writeRemoteAlias as o, parseDirectRemoteSpec as r, cmdRemote as t };
|
|
176
|
-
//# sourceMappingURL=remotes-
|
|
176
|
+
//# sourceMappingURL=remotes-iIvqiFWK.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as getInstalledPackage } from "./versionChecker-
|
|
1
|
+
import { r as getInstalledPackage } from "./versionChecker-CMOLbEEi.js";
|
|
2
2
|
import { execFileSync } from "child_process";
|
|
3
3
|
import { existsSync, mkdirSync, unlinkSync } from "fs";
|
|
4
4
|
import { chmod, copyFile } from "fs/promises";
|
|
@@ -243,4 +243,4 @@ async function getRustBinary(options = {}) {
|
|
|
243
243
|
|
|
244
244
|
//#endregion
|
|
245
245
|
export { findTrayLauncher as n, getRustBinary as r, findSpawnHiddenLauncher as t };
|
|
246
|
-
//# sourceMappingURL=rustBinary-
|
|
246
|
+
//# sourceMappingURL=rustBinary-DSVRhDzO.js.map
|