@vibe-commit/capture 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -5
- package/dist/agent_hooks.d.ts +8 -0
- package/dist/agent_hooks.js +31 -1
- package/dist/agent_hooks.js.map +1 -1
- package/dist/agents/claude_code.d.ts +34 -0
- package/dist/agents/claude_code.js +141 -8
- package/dist/agents/claude_code.js.map +1 -1
- package/dist/agents/codex_cli.d.ts +13 -0
- package/dist/agents/codex_cli.js +241 -2
- package/dist/agents/codex_cli.js.map +1 -1
- package/dist/agents/cursor.d.ts +34 -0
- package/dist/agents/cursor.js +115 -1
- package/dist/agents/cursor.js.map +1 -1
- package/dist/agents/session_listing.d.ts +29 -0
- package/dist/agents/session_listing.js +49 -0
- package/dist/agents/session_listing.js.map +1 -0
- package/dist/agents/types.d.ts +118 -8
- package/dist/agents/types.js +17 -3
- package/dist/agents/types.js.map +1 -1
- package/dist/commands/approval_pending.d.ts +9 -0
- package/dist/commands/approval_pending.js +23 -0
- package/dist/commands/approval_pending.js.map +1 -0
- package/dist/commands/auth.js +31 -11
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/backfill.d.ts +160 -0
- package/dist/commands/backfill.js +748 -0
- package/dist/commands/backfill.js.map +1 -0
- package/dist/commands/browser_credential.d.ts +2 -0
- package/dist/commands/browser_credential.js +31 -14
- package/dist/commands/browser_credential.js.map +1 -1
- package/dist/commands/connect.d.ts +137 -0
- package/dist/commands/connect.js +203 -109
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/connect_picker.d.ts +27 -0
- package/dist/commands/connect_picker.js +415 -0
- package/dist/commands/connect_picker.js.map +1 -0
- package/dist/commands/context.d.ts +16 -0
- package/dist/commands/context.js +27 -0
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/credential_problem.d.ts +15 -4
- package/dist/commands/credential_problem.js +29 -22
- package/dist/commands/credential_problem.js.map +1 -1
- package/dist/commands/device_credential.d.ts +2 -0
- package/dist/commands/device_credential.js +16 -14
- package/dist/commands/device_credential.js.map +1 -1
- package/dist/commands/finalize.d.ts +95 -0
- package/dist/commands/finalize.js +249 -74
- package/dist/commands/finalize.js.map +1 -1
- package/dist/commands/finish_mint.d.ts +2 -6
- package/dist/commands/finish_mint.js +10 -2
- package/dist/commands/finish_mint.js.map +1 -1
- package/dist/commands/report.js +38 -30
- package/dist/commands/report.js.map +1 -1
- package/dist/commands/scan.d.ts +162 -0
- package/dist/commands/scan.js +507 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/status.js +119 -22
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/status_hooks.d.ts +14 -0
- package/dist/commands/status_hooks.js +81 -0
- package/dist/commands/status_hooks.js.map +1 -0
- package/dist/commands/status_machine.d.ts +18 -0
- package/dist/commands/status_machine.js +149 -0
- package/dist/commands/status_machine.js.map +1 -0
- package/dist/commands/why.js +44 -31
- package/dist/commands/why.js.map +1 -1
- package/dist/consent.d.ts +111 -0
- package/dist/consent.js +203 -1
- package/dist/consent.js.map +1 -1
- package/dist/copy/error_codes.d.ts +222 -0
- package/dist/copy/error_codes.js +105 -0
- package/dist/copy/error_codes.js.map +1 -0
- package/dist/copy/index.d.ts +2 -2
- package/dist/copy/index.js +2 -2
- package/dist/copy/index.js.map +1 -1
- package/dist/copy/strings.d.ts +497 -29
- package/dist/copy/strings.js +601 -35
- package/dist/copy/strings.js.map +1 -1
- package/dist/copy/time.d.ts +11 -0
- package/dist/copy/time.js +18 -0
- package/dist/copy/time.js.map +1 -1
- package/dist/credential.d.ts +5 -1
- package/dist/credential.js +2 -2
- package/dist/credential.js.map +1 -1
- package/dist/git.d.ts +43 -0
- package/dist/git.js +95 -0
- package/dist/git.js.map +1 -1
- package/dist/hooks/entry.d.ts +18 -1
- package/dist/hooks/entry.js +110 -13
- package/dist/hooks/entry.js.map +1 -1
- package/dist/hooks/post_commit.js +3 -3
- package/dist/hooks/post_commit.js.map +1 -1
- package/dist/hooks/post_rewrite.js +6 -4
- package/dist/hooks/post_rewrite.js.map +1 -1
- package/dist/hooks/session_start.d.ts +93 -0
- package/dist/hooks/session_start.js +199 -0
- package/dist/hooks/session_start.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +56 -8
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +57 -0
- package/dist/install.js +54 -20
- package/dist/install.js.map +1 -1
- package/dist/json.d.ts +32 -9
- package/dist/json.js +35 -8
- package/dist/json.js.map +1 -1
- package/dist/machine_state.d.ts +52 -0
- package/dist/machine_state.js +63 -0
- package/dist/machine_state.js.map +1 -0
- package/dist/oauth/session.d.ts +1 -0
- package/dist/org_pending.d.ts +52 -0
- package/dist/org_pending.js +109 -0
- package/dist/org_pending.js.map +1 -0
- package/dist/paths.d.ts +50 -0
- package/dist/paths.js +64 -0
- package/dist/paths.js.map +1 -1
- package/dist/picker.d.ts +180 -0
- package/dist/picker.js +477 -0
- package/dist/picker.js.map +1 -0
- package/dist/picker_checklist.d.ts +125 -0
- package/dist/picker_checklist.js +375 -0
- package/dist/picker_checklist.js.map +1 -0
- package/dist/policy.d.ts +54 -0
- package/dist/policy.js +47 -0
- package/dist/policy.js.map +1 -1
- package/dist/post.d.ts +50 -16
- package/dist/post.js +265 -58
- package/dist/post.js.map +1 -1
- package/dist/state.d.ts +6 -0
- package/dist/state.js +38 -1
- package/dist/state.js.map +1 -1
- package/dist/system_message.d.ts +8 -1
- package/dist/system_message.js +19 -1
- package/dist/system_message.js.map +1 -1
- package/dist/term.d.ts +28 -2
- package/dist/term.js +23 -2
- package/dist/term.js.map +1 -1
- package/dist/verb_help.d.ts +13 -0
- package/dist/verb_help.js +40 -0
- package/dist/verb_help.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -9
package/dist/agents/codex_cli.js
CHANGED
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
* measured by this task.** They are recorded as the brief stated them so a later
|
|
12
12
|
* reader can check them against the product rather than against this file.
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { readdirSync, realpathSync } from "node:fs";
|
|
15
|
+
import { createRequire } from "node:module";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { CODEX_INDEX_FILE, codexHooksPath, codexIndexDir, codexTranscriptRoot, isInside, isTranscriptFile, } from "../paths.js";
|
|
18
|
+
import { readEntries, sessionFile } from "./session_listing.js";
|
|
15
19
|
/**
|
|
16
20
|
* `SessionEnd` is the tight one — 3 s registered, 2.2 s client.
|
|
17
21
|
*
|
|
@@ -24,6 +28,226 @@ const CODEX_SESSION_END_CLIENT_MS = 2_200;
|
|
|
24
28
|
/** `Stop` and `PreCompact` are registered far wider — 30 s, client 8 s. */
|
|
25
29
|
const CODEX_TURN_REGISTERED_MS = 30_000;
|
|
26
30
|
const CODEX_TURN_CLIENT_MS = 8_000;
|
|
31
|
+
/**
|
|
32
|
+
* `SessionStart` takes the turn clocks, with no fast path of its own.
|
|
33
|
+
*
|
|
34
|
+
* ⛔ **A logged shortcut** — gstack-shortcut(dec-e650afbf), eng review D7 · B.
|
|
35
|
+
* The ceiling: a hung `SessionStart` can hold a session's start up by as long as
|
|
36
|
+
* the REGISTERED timeout (30 s here). Upgrade when a measurement or a user report
|
|
37
|
+
* shows session-start delay, or before slice 2's grants fetch lands in this handler.
|
|
38
|
+
*/
|
|
39
|
+
const CODEX_SESSION_START_REGISTERED_MS = CODEX_TURN_REGISTERED_MS; // gstack-shortcut(dec-e650afbf)
|
|
40
|
+
const CODEX_SESSION_START_CLIENT_MS = CODEX_TURN_CLIENT_MS; // gstack-shortcut(dec-e650afbf)
|
|
41
|
+
/**
|
|
42
|
+
* ⛔ **THE FOUR COLUMNS, AND NO OTHER — `D11 · A`, Alexander's approved read.**
|
|
43
|
+
*
|
|
44
|
+
* The thread index also holds `first_user_message`, `title`, `preview` and more,
|
|
45
|
+
* which are CONTENT. Selecting one would turn a metadata read into a transcript
|
|
46
|
+
* read, so the statement names exactly these four and `test/scan-codex-index.test.ts`
|
|
47
|
+
* asserts the SQL carries nothing else. ⚠ **`archived` is not here either, on
|
|
48
|
+
* purpose** (VQ, 2026-10-24): an archived thread's `rollout_path` is decided by
|
|
49
|
+
* the admission below, and a fifth column would change the approved read surface.
|
|
50
|
+
*/
|
|
51
|
+
export const CODEX_INDEX_COLUMNS = ["cwd", "git_origin_url", "rollout_path", "created_at"];
|
|
52
|
+
/** The one statement the adapter runs against a thread row. */
|
|
53
|
+
export const CODEX_INDEX_SQL = `SELECT ${CODEX_INDEX_COLUMNS.join(", ")} FROM threads`;
|
|
54
|
+
/** How long a locked index is waited for before the adapter gives up (eng review D2). */
|
|
55
|
+
const CODEX_BUSY_TIMEOUT_MS = 1_000;
|
|
56
|
+
/**
|
|
57
|
+
* `node:sqlite`, loaded here and nowhere else, with ITS warning and only its
|
|
58
|
+
* warning dropped — eng review D2 · A.
|
|
59
|
+
*
|
|
60
|
+
* Node 22 prints `ExperimentalWarning: SQLite is an experimental feature…` to
|
|
61
|
+
* stderr the first time the module loads, which would land mid-screen in the
|
|
62
|
+
* picker and breaks DESIGN.md §13's output rules. `process.emitWarning` is
|
|
63
|
+
* wrapped for the duration of the one `require` and restored in a `finally`;
|
|
64
|
+
* any OTHER warning, in that window or after it, still prints. Measured on
|
|
65
|
+
* node 22.17.0.
|
|
66
|
+
*
|
|
67
|
+
* ⚠ **A `require` and not a dynamic `import()`**, so the whole adapter stays
|
|
68
|
+
* synchronous like every other lister (`scan` is a chain of `execFileSync`
|
|
69
|
+
* anyway) and the wrapped window is exactly the load, not a promise turn that
|
|
70
|
+
* something else could emit a warning inside. Lazy: a machine with no Codex
|
|
71
|
+
* never loads the module at all.
|
|
72
|
+
*/
|
|
73
|
+
function loadSqlite() {
|
|
74
|
+
const original = process.emitWarning;
|
|
75
|
+
process.emitWarning = ((warning, ...rest) => {
|
|
76
|
+
const type = typeof warning === "string" ? rest[0] : warning.name;
|
|
77
|
+
const message = typeof warning === "string" ? warning : warning.message;
|
|
78
|
+
if (type === "ExperimentalWarning" && message.startsWith("SQLite"))
|
|
79
|
+
return;
|
|
80
|
+
return original.call(process, warning, ...rest);
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
return createRequire(import.meta.url)("node:sqlite");
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
process.emitWarning = original;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Does the `threads` table carry all four columns? A bare name test, nothing deeper. */
|
|
90
|
+
function hasIndexColumns(db) {
|
|
91
|
+
const present = new Set(db.prepare("PRAGMA table_info(threads)").all().map((c) => String(c.name)));
|
|
92
|
+
return CODEX_INDEX_COLUMNS.every((column) => present.has(column));
|
|
93
|
+
}
|
|
94
|
+
/** A locked database is a different line from an unrecognised one. */
|
|
95
|
+
function readFailure(err) {
|
|
96
|
+
return /locked|busy/i.test(err instanceof Error ? err.message : "")
|
|
97
|
+
? "index locked"
|
|
98
|
+
: "unreadable";
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Rollout files under `sessions/` that NO index row names — counted, never opened.
|
|
102
|
+
*
|
|
103
|
+
* Open Question 6's text, as VQ ruled (PR 1 ruling 5c): an index which cannot see a
|
|
104
|
+
* session is a thing to REPORT, not to guess about. The walk lists directory entries — names
|
|
105
|
+
* and file types, nothing else — and counts each regular `.jsonl`; a symlink is
|
|
106
|
+
* not a file here (`Dirent.isFile` is false for it).
|
|
107
|
+
*
|
|
108
|
+
* Compared as REALPATHS on both sides, because the index stores the path Codex
|
|
109
|
+
* wrote and the walk builds its own from `home`; a symlinked home would otherwise
|
|
110
|
+
* make every rollout look unindexed. A row whose rollout has gone cannot be
|
|
111
|
+
* resolved and names nothing, which is correct: it is not on disk to be matched.
|
|
112
|
+
*/
|
|
113
|
+
function countUnindexed(root, rolloutPaths) {
|
|
114
|
+
const named = new Set();
|
|
115
|
+
for (const path of rolloutPaths) {
|
|
116
|
+
try {
|
|
117
|
+
named.add(realpathSync(path));
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Gone from disk: it names no file that the walk could find.
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
let base;
|
|
124
|
+
try {
|
|
125
|
+
base = realpathSync(root);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
let count = 0;
|
|
131
|
+
const visit = (dir) => {
|
|
132
|
+
let entries;
|
|
133
|
+
try {
|
|
134
|
+
entries = readEntries(dir);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
for (const entry of entries ?? []) {
|
|
140
|
+
const path = join(dir, entry.name);
|
|
141
|
+
if (entry.isDirectory())
|
|
142
|
+
visit(path);
|
|
143
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl") && !named.has(path))
|
|
144
|
+
count += 1;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
visit(base);
|
|
148
|
+
return count;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Codex's sessions, from its thread index — `D11 · A`, eng review D2, `scan`.
|
|
152
|
+
*
|
|
153
|
+
* Codex keeps rollouts DATE-keyed (`sessions/YYYY/MM/DD/`), so a folder walk
|
|
154
|
+
* cannot say which repo one belongs to; its `threads` table can, because it
|
|
155
|
+
* records the real `cwd`. That row is the whole reason this adapter exists and
|
|
156
|
+
* the reason a Codex row is never ambiguous.
|
|
157
|
+
*
|
|
158
|
+
* ## ⛔ What is read, and how
|
|
159
|
+
*
|
|
160
|
+
* - **exactly `state_5.sqlite`**, and any other `state_N` is `unrecognised
|
|
161
|
+
* index` (Open Question 6; see `codexIndexDir`).
|
|
162
|
+
* - **`readOnly`, never `immutable`.** Codex keeps everything since its last
|
|
163
|
+
* checkpoint in the `-wal` file (on the author's machine, every thread since
|
|
164
|
+
* Sep 30), and an `immutable` open ignores it.
|
|
165
|
+
* - **the four columns only** (`CODEX_INDEX_SQL`), after a `PRAGMA table_info`
|
|
166
|
+
* check, and a 1000 ms busy timeout. Any failure is this agent's
|
|
167
|
+
* `unreadable` answer, so the other two agents' rows are untouched.
|
|
168
|
+
*
|
|
169
|
+
* ## ⛔ The rollout file is `lstat`ed, never opened
|
|
170
|
+
*
|
|
171
|
+
* A row is admitted only if its `rollout_path` lies under the sessions root and
|
|
172
|
+
* is a regular file there. That is also what decides an ARCHIVED thread, whose
|
|
173
|
+
* rollout is not under `sessions/`. The row's `git_origin_url` is carried but is
|
|
174
|
+
* not the slug source: the scan names a repo from the CLONE (`resolveRepoSlug`),
|
|
175
|
+
* so there is one normalisation and not two.
|
|
176
|
+
*/
|
|
177
|
+
function codexSessionSource(home, env) {
|
|
178
|
+
const dir = codexIndexDir(home, env);
|
|
179
|
+
const file = join(dir, CODEX_INDEX_FILE);
|
|
180
|
+
let present;
|
|
181
|
+
try {
|
|
182
|
+
present = readdirSync(dir);
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
const code = err.code;
|
|
186
|
+
return code === "ENOENT" || code === "ENOTDIR"
|
|
187
|
+
? { kind: "rows", rows: [], unindexed: 0 }
|
|
188
|
+
: { kind: "unreadable", reason: "unreadable" };
|
|
189
|
+
}
|
|
190
|
+
if (!present.includes(CODEX_INDEX_FILE)) {
|
|
191
|
+
// A Codex that keeps its index under another generation's name is a store
|
|
192
|
+
// this client was not checked against. One with no index at all has simply
|
|
193
|
+
// never recorded a thread — and its rollouts, if any, are all unindexed.
|
|
194
|
+
return present.some((name) => /^state_\d+\.sqlite$/.test(name))
|
|
195
|
+
? { kind: "unreadable", reason: "unrecognised index" }
|
|
196
|
+
: { kind: "rows", rows: [], unindexed: countUnindexed(codexTranscriptRoot(home, env), []) };
|
|
197
|
+
}
|
|
198
|
+
let sqlite;
|
|
199
|
+
try {
|
|
200
|
+
sqlite = loadSqlite();
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return { kind: "unreadable", reason: "unreadable" };
|
|
204
|
+
}
|
|
205
|
+
let db = null;
|
|
206
|
+
try {
|
|
207
|
+
// ⚠ `readOnly` stops THIS CODE writing the index and does not stop SQLite creating its own
|
|
208
|
+
// `-wal`/`-shm` files beside it, which any reader of a WAL database whose writer has closed does
|
|
209
|
+
// (measured by VQ on node 22.17, PR 1 F1). Nothing is written to `state_5.sqlite` itself.
|
|
210
|
+
db = new sqlite.DatabaseSync(file, { readOnly: true });
|
|
211
|
+
db.exec(`PRAGMA busy_timeout = ${CODEX_BUSY_TIMEOUT_MS}`);
|
|
212
|
+
if (!hasIndexColumns(db))
|
|
213
|
+
return { kind: "unreadable", reason: "unrecognised index" };
|
|
214
|
+
const root = codexTranscriptRoot(home, env);
|
|
215
|
+
const rows = [];
|
|
216
|
+
const named = [];
|
|
217
|
+
for (const row of db.prepare(CODEX_INDEX_SQL).all()) {
|
|
218
|
+
const { cwd, git_origin_url: origin, rollout_path: rollout, created_at: created } = row;
|
|
219
|
+
if (typeof cwd !== "string" || typeof rollout !== "string")
|
|
220
|
+
continue;
|
|
221
|
+
// Every path an index row names counts as INDEXED, admitted or not: an archived thread's rollout
|
|
222
|
+
// is not "missing from the index", it is simply not capturable (`D11 · A`).
|
|
223
|
+
named.push(rollout);
|
|
224
|
+
if (!isInside(root, rollout) || !isTranscriptFile(root, rollout))
|
|
225
|
+
continue;
|
|
226
|
+
const session = sessionFile(rollout);
|
|
227
|
+
if (session === null)
|
|
228
|
+
continue;
|
|
229
|
+
rows.push({
|
|
230
|
+
cwd,
|
|
231
|
+
origin: typeof origin === "string" ? origin : null,
|
|
232
|
+
// `created_at` is whole SECONDS (measured), the rest of this client's
|
|
233
|
+
// times are milliseconds.
|
|
234
|
+
session: typeof created === "number" ? { ...session, startedMs: created * 1000 } : session,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
return { kind: "rows", rows, unindexed: countUnindexed(root, named) };
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
return { kind: "unreadable", reason: readFailure(err) };
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
try {
|
|
244
|
+
db?.close();
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Closing a read-only handle that failed to open is not a second finding.
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
27
251
|
export const CODEX_CLI = {
|
|
28
252
|
id: "codex-cli",
|
|
29
253
|
transport: "ndjson",
|
|
@@ -46,7 +270,17 @@ export const CODEX_CLI = {
|
|
|
46
270
|
// against three throwaway `CODEX_HOME`s settled it: `SessionEnd` is recognised
|
|
47
271
|
// and says so by clamping, while `session_end` and a bogus key produce NO
|
|
48
272
|
// diagnostic — a file that parses, registers nothing, and reports success.
|
|
49
|
-
|
|
273
|
+
// `SessionStart` is in that schema list too, spelled the same way, and MEASURED
|
|
274
|
+
// live (Open Question 1, Codex 0.148.0): it fires before the first prompt — in
|
|
275
|
+
// the TUI lazily, when the first prompt is submitted — and accepts a
|
|
276
|
+
// `systemMessage`. ⚠ Those runs were under cmux's `--dangerously-bypass-hook-trust`;
|
|
277
|
+
// stock Codex >= 0.131.0 runs a user-level hook only after the user trusts it.
|
|
278
|
+
eventNames: {
|
|
279
|
+
Stop: "Stop",
|
|
280
|
+
PreCompact: "PreCompact",
|
|
281
|
+
SessionEnd: "SessionEnd",
|
|
282
|
+
SessionStart: "SessionStart",
|
|
283
|
+
},
|
|
50
284
|
// ⛔ `~/.codex/hooks.json`, honouring `CODEX_HOME` — MEASURED, since every
|
|
51
285
|
// probe in this unit drove a real `codex` through a throwaway `CODEX_HOME`
|
|
52
286
|
// and it read the `hooks.json` there. The NESTED shape, also measured: a
|
|
@@ -66,6 +300,10 @@ export const CODEX_CLI = {
|
|
|
66
300
|
registeredTimeoutMs: CODEX_SESSION_END_REGISTERED_MS,
|
|
67
301
|
clientBudgetMs: CODEX_SESSION_END_CLIENT_MS,
|
|
68
302
|
},
|
|
303
|
+
SessionStart: {
|
|
304
|
+
registeredTimeoutMs: CODEX_SESSION_START_REGISTERED_MS,
|
|
305
|
+
clientBudgetMs: CODEX_SESSION_START_CLIENT_MS,
|
|
306
|
+
},
|
|
69
307
|
},
|
|
70
308
|
// The delegated path ARRIVES on stdin as `agent_transcript_path` — there is no
|
|
71
309
|
// directory to glob. Nothing parses it yet; the discriminator is here so the
|
|
@@ -79,6 +317,7 @@ export const CODEX_CLI = {
|
|
|
79
317
|
// of a measurement, not as a fact about the agent. Measuring it is a change to
|
|
80
318
|
// this line and nothing else.
|
|
81
319
|
sessionIdFromEnv: () => null,
|
|
320
|
+
sessionSource: codexSessionSource,
|
|
82
321
|
// ⛔ NO `locateCurrentTranscript`, and the omission is the decision. Finding
|
|
83
322
|
// the repository would mean reading a `cwd` out of the transcript's own
|
|
84
323
|
// records — ANALYSIS, which D60 §D6 forbids this client. `connect` therefore
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex_cli.js","sourceRoot":"","sources":["../../src/agents/codex_cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"codex_cli.js","sourceRoot":"","sources":["../../src/agents/codex_cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,QAAQ,EACR,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAQhE;;;;;;GAMG;AACH,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAC9C,MAAM,2BAA2B,GAAG,KAAK,CAAC;AAE1C,2EAA2E;AAC3E,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAEnC;;;;;;;GAOG;AACH,MAAM,iCAAiC,GAAG,wBAAwB,CAAC,CAAC,gCAAgC;AACpG,MAAM,6BAA6B,GAAG,oBAAoB,CAAC,CAAC,gCAAgC;AAE5F;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,CAAU,CAAC;AAEpG,+DAA+D;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;AAEvF,yFAAyF;AACzF,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAKpC;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,UAAU;IACjB,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC;IACrC,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC,OAAuB,EAAE,GAAG,IAAe,EAAE,EAAE;QACrE,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;QAClE,MAAM,OAAO,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACxE,IAAI,IAAI,KAAK,qBAAqB,IAAI,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO;QAC3E,OAAQ,QAAyC,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACpF,CAAC,CAA+B,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,aAAa,CAAiB,CAAC;IACvE,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,WAAW,GAAG,QAAQ,CAAC;IACjC,CAAC;AACH,CAAC;AAED,yFAAyF;AACzF,SAAS,eAAe,CAAC,EAA8C;IACrE,MAAM,OAAO,GAAG,IAAI,GAAG,CACpB,EAAE,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC,GAAG,EAA2B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACjF,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CACf,CACF,CAAC;IACF,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,sEAAsE;AACtE,SAAS,WAAW,CAAC,GAAY;IAC/B,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,YAAY,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,cAAc,CAAC,IAAY,EAAE,YAA+B;IACnE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;IACH,CAAC;IACD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,GAAG,CAAC,GAAW,EAAQ,EAAE;QAClC,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,IAAI,EAAE,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,KAAK,CAAC,WAAW,EAAE;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC;iBAChC,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,KAAK,IAAI,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,CAAC;IACZ,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,SAAS,kBAAkB,CAAC,IAAY,EAAE,GAAsB;IAC9D,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAEzC,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS;YAC5C,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE;YAC1C,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACnD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACxC,0EAA0E;QAC1E,2EAA2E;QAC3E,yEAAyE;QACzE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7D,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAE;YACtD,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,cAAc,CAAC,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAChG,CAAC;IAED,IAAI,MAAoB,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,UAAU,EAAE,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACtD,CAAC;IACD,IAAI,EAAE,GAAsD,IAAI,CAAC;IACjE,IAAI,CAAC;QACH,2FAA2F;QAC3F,iGAAiG;QACjG,0FAA0F;QAC1F,EAAE,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,EAAE,CAAC,IAAI,CAAC,yBAAyB,qBAAqB,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACtF,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAgB,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,GAAG,EAA+B,EAAE,CAAC;YACjF,MAAM,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;YACxF,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ;gBAAE,SAAS;YACrE,iGAAiG;YACjG,4EAA4E;YAC5E,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC;gBAAE,SAAS;YAC3E,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YACrC,IAAI,OAAO,KAAK,IAAI;gBAAE,SAAS;YAC/B,IAAI,CAAC,IAAI,CAAC;gBACR,GAAG;gBACH,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;gBAClD,sEAAsE;gBACtE,0BAA0B;gBAC1B,OAAO,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO;aAC3F,CAAC,CAAC;QACL,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;IACxE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;IAC1D,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,EAAE,EAAE,KAAK,EAAE,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;QAC5E,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAsB;IAC1C,EAAE,EAAE,WAAW;IACf,SAAS,EAAE,QAAQ;IACnB,cAAc,EAAE,mBAAmB;IACnC,yEAAyE;IACzE,4EAA4E;IAC5E,0EAA0E;IAC1E,+DAA+D;IAC/D,UAAU,EAAE,gBAAgB;IAC5B,4EAA4E;IAC5E,8EAA8E;IAC9E,6EAA6E;IAC7E,8EAA8E;IAC9E,+EAA+E;IAC/E,mEAAmE;IACnE,EAAE;IACF,kEAAkE;IAClE,4EAA4E;IAC5E,+EAA+E;IAC/E,+EAA+E;IAC/E,0EAA0E;IAC1E,2EAA2E;IAC3E,gFAAgF;IAChF,+EAA+E;IAC/E,qEAAqE;IACrE,qFAAqF;IACrF,+EAA+E;IAC/E,UAAU,EAAE;QACV,IAAI,EAAE,MAAM;QACZ,UAAU,EAAE,YAAY;QACxB,UAAU,EAAE,YAAY;QACxB,YAAY,EAAE,cAAc;KAC7B;IACD,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,yEAAyE;IACzE,gEAAgE;IAChE,UAAU,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE;IACrD,MAAM,EAAE;QACN,IAAI,EAAE;YACJ,mBAAmB,EAAE,wBAAwB;YAC7C,cAAc,EAAE,oBAAoB;SACrC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,wBAAwB;YAC7C,cAAc,EAAE,oBAAoB;SACrC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,+BAA+B;YACpD,cAAc,EAAE,2BAA2B;SAC5C;QACD,YAAY,EAAE;YACZ,mBAAmB,EAAE,iCAAiC;YACtD,cAAc,EAAE,6BAA6B;SAC9C;KACF;IACD,+EAA+E;IAC/E,6EAA6E;IAC7E,oEAAoE;IACpE,oBAAoB,EAAE,UAAU;IAChC,SAAS,EAAE,WAAW;IACtB,8EAA8E;IAC9E,0EAA0E;IAC1E,8EAA8E;IAC9E,6EAA6E;IAC7E,+EAA+E;IAC/E,8BAA8B;IAC9B,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI;IAC5B,aAAa,EAAE,kBAAkB;IACjC,4EAA4E;IAC5E,wEAAwE;IACxE,6EAA6E;IAC7E,oDAAoD;CACrD,CAAC"}
|
package/dist/agents/cursor.d.ts
CHANGED
|
@@ -18,4 +18,38 @@
|
|
|
18
18
|
* difference is marked on each member below rather than left to a reader.
|
|
19
19
|
*/
|
|
20
20
|
import type { TranscriptDialect } from "./types.js";
|
|
21
|
+
/**
|
|
22
|
+
* The longest folder name Cursor writes verbatim.
|
|
23
|
+
*
|
|
24
|
+
* ⛔ **MEASURED (Open Question 3, VS, 2026-10-24), with `cursor-agent`, which
|
|
25
|
+
* creates the project folder even when it is not logged in.** A name up to 61
|
|
26
|
+
* characters is kept; a longer one becomes its first 53 characters, `-`, and 7
|
|
27
|
+
* hex characters (the hash varied with the input, so it is not a constant).
|
|
28
|
+
*/
|
|
29
|
+
export declare const CURSOR_NAME_LIMIT = 61;
|
|
30
|
+
/**
|
|
31
|
+
* The folder name Cursor gives a workspace path — Open Question 3.
|
|
32
|
+
*
|
|
33
|
+
* ⛔ **THIS IS NOT CLAUDE CODE'S RULE, and the difference is the measurement.**
|
|
34
|
+
* Every RUN of non-alphanumerics, a literal `--` included, collapses to ONE `-`;
|
|
35
|
+
* leading and trailing separators are dropped (so `/tmp/q/trail.` is `…-trail`
|
|
36
|
+
* and there is no leading `-`); case is kept. An emoji and `é'` each gave a single
|
|
37
|
+
* dash where Claude Code gives two.
|
|
38
|
+
*
|
|
39
|
+
* ⚠ **Which makes Cursor's names collide far more:** `a-b`, `a.b`, `a--b` and
|
|
40
|
+
* `a.b.` are all one folder. The scan's walk encodes every directory entry with
|
|
41
|
+
* THIS function and matches on the result, so the collapsing applies to every
|
|
42
|
+
* candidate rather than being assumed away.
|
|
43
|
+
*/
|
|
44
|
+
export declare function encodeProjectPath(path: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Is this folder name one Cursor cut and hashed? See `CURSOR_NAME_LIMIT`.
|
|
47
|
+
*
|
|
48
|
+
* ⚠ **A hashed name is always exactly 61 long, and so is a verbatim name that
|
|
49
|
+
* happens to be 61 long** — length alone cannot tell them apart. The test is the
|
|
50
|
+
* hash's shape too (`-` and 7 hex). A verbatim 61-character name that ends the
|
|
51
|
+
* same way is read as hashed, which is the safe error: it is skipped and
|
|
52
|
+
* counted, never matched to a path it might not be.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isHashedProjectName(name: string): boolean;
|
|
21
55
|
export declare const CURSOR: TranscriptDialect;
|
package/dist/agents/cursor.js
CHANGED
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
* ⚠ **What is DATA here and what is MEASURED is not the same set**, and the
|
|
18
18
|
* difference is marked on each member below rather than left to a reader.
|
|
19
19
|
*/
|
|
20
|
+
import { join } from "node:path";
|
|
20
21
|
import { cursorHooksPath, cursorTranscriptRoot, isCursorTranscript } from "../paths.js";
|
|
22
|
+
import { readEntries, sessionFile, subdirectories } from "./session_listing.js";
|
|
21
23
|
/**
|
|
22
24
|
* ⭐ **MEASURED. `D5`'s SECOND HALF IS ANSWERED (`TODOS[130]`), AND THE ANSWER IS
|
|
23
25
|
* THAT CURSOR IMPOSES NO NUMBER OF ITS OWN.**
|
|
@@ -79,6 +81,97 @@ import { cursorHooksPath, cursorTranscriptRoot, isCursorTranscript } from "../pa
|
|
|
79
81
|
*/
|
|
80
82
|
const CURSOR_REGISTERED_MS = 3_000;
|
|
81
83
|
const CURSOR_CLIENT_MS = 2_200;
|
|
84
|
+
/**
|
|
85
|
+
* The longest folder name Cursor writes verbatim.
|
|
86
|
+
*
|
|
87
|
+
* ⛔ **MEASURED (Open Question 3, VS, 2026-10-24), with `cursor-agent`, which
|
|
88
|
+
* creates the project folder even when it is not logged in.** A name up to 61
|
|
89
|
+
* characters is kept; a longer one becomes its first 53 characters, `-`, and 7
|
|
90
|
+
* hex characters (the hash varied with the input, so it is not a constant).
|
|
91
|
+
*/
|
|
92
|
+
export const CURSOR_NAME_LIMIT = 61;
|
|
93
|
+
/**
|
|
94
|
+
* The folder name Cursor gives a workspace path — Open Question 3.
|
|
95
|
+
*
|
|
96
|
+
* ⛔ **THIS IS NOT CLAUDE CODE'S RULE, and the difference is the measurement.**
|
|
97
|
+
* Every RUN of non-alphanumerics, a literal `--` included, collapses to ONE `-`;
|
|
98
|
+
* leading and trailing separators are dropped (so `/tmp/q/trail.` is `…-trail`
|
|
99
|
+
* and there is no leading `-`); case is kept. An emoji and `é'` each gave a single
|
|
100
|
+
* dash where Claude Code gives two.
|
|
101
|
+
*
|
|
102
|
+
* ⚠ **Which makes Cursor's names collide far more:** `a-b`, `a.b`, `a--b` and
|
|
103
|
+
* `a.b.` are all one folder. The scan's walk encodes every directory entry with
|
|
104
|
+
* THIS function and matches on the result, so the collapsing applies to every
|
|
105
|
+
* candidate rather than being assumed away.
|
|
106
|
+
*/
|
|
107
|
+
export function encodeProjectPath(path) {
|
|
108
|
+
return path.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Is this folder name one Cursor cut and hashed? See `CURSOR_NAME_LIMIT`.
|
|
112
|
+
*
|
|
113
|
+
* ⚠ **A hashed name is always exactly 61 long, and so is a verbatim name that
|
|
114
|
+
* happens to be 61 long** — length alone cannot tell them apart. The test is the
|
|
115
|
+
* hash's shape too (`-` and 7 hex). A verbatim 61-character name that ends the
|
|
116
|
+
* same way is read as hashed, which is the safe error: it is skipped and
|
|
117
|
+
* counted, never matched to a path it might not be.
|
|
118
|
+
*/
|
|
119
|
+
export function isHashedProjectName(name) {
|
|
120
|
+
return name.length === CURSOR_NAME_LIMIT && /-[0-9a-f]{7}$/.test(name);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The folder Cursor uses for a window with no workspace open. It is not a path,
|
|
124
|
+
* so it can never resolve to one and is not worth a "folder no longer exists"
|
|
125
|
+
* count. Seen by NAME on the author's machine; nothing in it was read.
|
|
126
|
+
*/
|
|
127
|
+
const CURSOR_NO_WORKSPACE = "empty-window";
|
|
128
|
+
/**
|
|
129
|
+
* Cursor's sessions, by folder — `D11 · A`, `scan`.
|
|
130
|
+
*
|
|
131
|
+
* `<root>/<encoded workspace>/agent-transcripts/<cid>/<cid>.jsonl`. Only the
|
|
132
|
+
* conversation's own file counts: `<cid>/subagents/…` is delegated work that
|
|
133
|
+
* belongs to the parent (`delegatedTranscripts: "announce"`). A flat
|
|
134
|
+
* `agent-transcripts/<cid>.jsonl` is accepted too, because `isCursorTranscript`
|
|
135
|
+
* admits any `.jsonl` at that depth and a count that disagreed with the gate
|
|
136
|
+
* would be a count of nothing.
|
|
137
|
+
*
|
|
138
|
+
* ⛔ Names and times only; see `claudeSessionSource` for the rule and its reason.
|
|
139
|
+
*/
|
|
140
|
+
function cursorSessionSource(home) {
|
|
141
|
+
const root = cursorTranscriptRoot(home);
|
|
142
|
+
const folders = [];
|
|
143
|
+
try {
|
|
144
|
+
for (const folder of subdirectories(root)) {
|
|
145
|
+
if (folder.name === CURSOR_NO_WORKSPACE)
|
|
146
|
+
continue;
|
|
147
|
+
const transcripts = join(folder.path, "agent-transcripts");
|
|
148
|
+
const sessions = [];
|
|
149
|
+
for (const entry of readEntries(transcripts) ?? []) {
|
|
150
|
+
const file = entry.isDirectory()
|
|
151
|
+
? join(transcripts, entry.name, `${entry.name}.jsonl`)
|
|
152
|
+
: entry.name.endsWith(".jsonl")
|
|
153
|
+
? join(transcripts, entry.name)
|
|
154
|
+
: null;
|
|
155
|
+
const session = file === null ? null : sessionFile(file);
|
|
156
|
+
if (session !== null)
|
|
157
|
+
sessions.push(session);
|
|
158
|
+
}
|
|
159
|
+
if (sessions.length > 0)
|
|
160
|
+
folders.push({ name: folder.name, sessions });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return { kind: "unreadable", reason: "unreadable" };
|
|
165
|
+
}
|
|
166
|
+
// Cursor keeps no record of the folders it has run in, so there is no second signal.
|
|
167
|
+
return {
|
|
168
|
+
kind: "folders",
|
|
169
|
+
folders,
|
|
170
|
+
encode: encodeProjectPath,
|
|
171
|
+
isHashed: isHashedProjectName,
|
|
172
|
+
recorded: [],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
82
175
|
export const CURSOR = {
|
|
83
176
|
id: "cursor",
|
|
84
177
|
transport: "ndjson",
|
|
@@ -103,7 +196,22 @@ export const CURSOR = {
|
|
|
103
196
|
// ⛔ Against the client's `SessionEnd`, `sessionEnd` matched NOTHING — not an
|
|
104
197
|
// error, a `false` — so a Cursor session took no settle on its final turn and
|
|
105
198
|
// lost it permanently. That is the whole of the bug.
|
|
106
|
-
|
|
199
|
+
//
|
|
200
|
+
// ⛔ **`sessionStart` is STATIC, NOT OBSERVED** (Open Question 1, 2026-10-24).
|
|
201
|
+
// `cursor-agent` was not logged in, so no session ran; the spelling and the
|
|
202
|
+
// payload (`conversation_id`, `generation_id`, `model`, `is_background_agent`,
|
|
203
|
+
// `composer_mode`) come from the CLI bundle 2026.08.11-e8db854, which runs it
|
|
204
|
+
// for a NEW chat before the first turn. It reads `env` and `additional_context`
|
|
205
|
+
// from the output — and `additional_context` goes into the agent's context and
|
|
206
|
+
// is NOT shown to the user. There is no user-visible field, so this client
|
|
207
|
+
// never uses it to carry a notice: Cursor users learn the state from `status`,
|
|
208
|
+
// and the first-`Stop` install is kept as the fallback for exactly this reason.
|
|
209
|
+
eventNames: {
|
|
210
|
+
Stop: "stop",
|
|
211
|
+
PreCompact: "preCompact",
|
|
212
|
+
SessionEnd: "sessionEnd",
|
|
213
|
+
SessionStart: "sessionStart",
|
|
214
|
+
},
|
|
107
215
|
// ⛔ `~/.cursor/hooks.json`, and the `cursor` SHAPE — the two are a pair.
|
|
108
216
|
// Read out of the shipped bundle's validator: a top-level numeric `version`
|
|
109
217
|
// is REQUIRED, entries are a FLAT array of hook scripts each needing a
|
|
@@ -125,6 +233,11 @@ export const CURSOR = {
|
|
|
125
233
|
registeredTimeoutMs: CURSOR_REGISTERED_MS,
|
|
126
234
|
clientBudgetMs: CURSOR_CLIENT_MS,
|
|
127
235
|
},
|
|
236
|
+
// gstack-shortcut(dec-e650afbf): `Stop`'s clocks, no fast path (eng review D7 · B).
|
|
237
|
+
SessionStart: {
|
|
238
|
+
registeredTimeoutMs: CURSOR_REGISTERED_MS, // gstack-shortcut(dec-e650afbf)
|
|
239
|
+
clientBudgetMs: CURSOR_CLIENT_MS, // gstack-shortcut(dec-e650afbf)
|
|
240
|
+
},
|
|
128
241
|
},
|
|
129
242
|
// ⛔ DECLARED EXPLICITLY, and it is load-bearing now in a way it was not
|
|
130
243
|
// before (D177 §2, §9). `subagentsDir()` would compute
|
|
@@ -142,6 +255,7 @@ export const CURSOR = {
|
|
|
142
255
|
// measurement, not as a fact about the agent. Measuring it is a change to this
|
|
143
256
|
// line and nothing else.
|
|
144
257
|
sessionIdFromEnv: () => null,
|
|
258
|
+
sessionSource: (home) => cursorSessionSource(home),
|
|
145
259
|
// ⛔ NO `locateCurrentTranscript`, and the omission is the decision — the same
|
|
146
260
|
// one Codex made. Finding the conversation for THIS project would mean munging
|
|
147
261
|
// a workspace path this client is never told, or reading a `cwd` out of the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Listing a folder-keyed agent's sessions — `D11 · A`, `scan`.
|
|
3
|
+
*
|
|
4
|
+
* The two folder-keyed dialects (Claude Code, Cursor) differ in how a path is
|
|
5
|
+
* encoded and where a session sits under the folder, and agree on everything
|
|
6
|
+
* this file does: read directory ENTRIES, `lstat` a candidate for its time, and
|
|
7
|
+
* never open it.
|
|
8
|
+
*
|
|
9
|
+
* ⛔ **`lstat`, REGULAR FILES ONLY.** A symlink named `x.jsonl` is not a session
|
|
10
|
+
* (Cursor's gate resolves for exactly that reason), and a FIFO or socket under a
|
|
11
|
+
* root this client does not own is a thing a later `readFileSync` hangs on or
|
|
12
|
+
* throws on. A listing that only ever `lstat`s cannot be made to block, which is
|
|
13
|
+
* the property `scan` needs from the one command that walks the whole root.
|
|
14
|
+
*/
|
|
15
|
+
import { type Dirent } from "node:fs";
|
|
16
|
+
import type { SourceSession } from "./types.js";
|
|
17
|
+
/**
|
|
18
|
+
* The entries of a directory, `null` when it is simply not there, and a THROW
|
|
19
|
+
* for anything else (permissions, I/O) — the caller turns a throw into the
|
|
20
|
+
* agent's `unreadable` answer, so an absent store and a broken one stay apart.
|
|
21
|
+
*/
|
|
22
|
+
export declare function readEntries(dir: string): Dirent[] | null;
|
|
23
|
+
/** One regular file's times, or null for anything that is not one. Never opens it. */
|
|
24
|
+
export declare function sessionFile(path: string): SourceSession | null;
|
|
25
|
+
/** `<dir>/<name>` for each entry that is a real directory (a symlink is not one). */
|
|
26
|
+
export declare function subdirectories(dir: string): {
|
|
27
|
+
name: string;
|
|
28
|
+
path: string;
|
|
29
|
+
}[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Listing a folder-keyed agent's sessions — `D11 · A`, `scan`.
|
|
3
|
+
*
|
|
4
|
+
* The two folder-keyed dialects (Claude Code, Cursor) differ in how a path is
|
|
5
|
+
* encoded and where a session sits under the folder, and agree on everything
|
|
6
|
+
* this file does: read directory ENTRIES, `lstat` a candidate for its time, and
|
|
7
|
+
* never open it.
|
|
8
|
+
*
|
|
9
|
+
* ⛔ **`lstat`, REGULAR FILES ONLY.** A symlink named `x.jsonl` is not a session
|
|
10
|
+
* (Cursor's gate resolves for exactly that reason), and a FIFO or socket under a
|
|
11
|
+
* root this client does not own is a thing a later `readFileSync` hangs on or
|
|
12
|
+
* throws on. A listing that only ever `lstat`s cannot be made to block, which is
|
|
13
|
+
* the property `scan` needs from the one command that walks the whole root.
|
|
14
|
+
*/
|
|
15
|
+
import { lstatSync, readdirSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
/**
|
|
18
|
+
* The entries of a directory, `null` when it is simply not there, and a THROW
|
|
19
|
+
* for anything else (permissions, I/O) — the caller turns a throw into the
|
|
20
|
+
* agent's `unreadable` answer, so an absent store and a broken one stay apart.
|
|
21
|
+
*/
|
|
22
|
+
export function readEntries(dir) {
|
|
23
|
+
try {
|
|
24
|
+
return readdirSync(dir, { withFileTypes: true });
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
const code = err.code;
|
|
28
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
29
|
+
return null;
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** One regular file's times, or null for anything that is not one. Never opens it. */
|
|
34
|
+
export function sessionFile(path) {
|
|
35
|
+
try {
|
|
36
|
+
const stat = lstatSync(path);
|
|
37
|
+
return stat.isFile() ? { path, mtimeMs: stat.mtimeMs } : null;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** `<dir>/<name>` for each entry that is a real directory (a symlink is not one). */
|
|
44
|
+
export function subdirectories(dir) {
|
|
45
|
+
return (readEntries(dir) ?? [])
|
|
46
|
+
.filter((entry) => entry.isDirectory())
|
|
47
|
+
.map((entry) => ({ name: entry.name, path: join(dir, entry.name) }));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=session_listing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session_listing.js","sourceRoot":"","sources":["../../src/agents/session_listing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAe,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACzD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;SACtC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AACzE,CAAC"}
|