@vibe-commit/capture 0.9.0 → 0.11.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.
Files changed (88) hide show
  1. package/README.md +49 -1
  2. package/dist/agent_hooks.js +2 -2
  3. package/dist/cli.d.ts +35 -0
  4. package/dist/cli.js +412 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/backfill.js +19 -2
  7. package/dist/commands/backfill.js.map +1 -1
  8. package/dist/commands/finalize.d.ts +37 -1
  9. package/dist/commands/finalize.js +131 -29
  10. package/dist/commands/finalize.js.map +1 -1
  11. package/dist/commands/status.js +7 -0
  12. package/dist/commands/status.js.map +1 -1
  13. package/dist/commands/status_machine.js +5 -0
  14. package/dist/commands/status_machine.js.map +1 -1
  15. package/dist/commands/upload_worker.d.ts +63 -0
  16. package/dist/commands/upload_worker.js +336 -0
  17. package/dist/commands/upload_worker.js.map +1 -0
  18. package/dist/commands/why.d.ts +19 -1
  19. package/dist/commands/why.js +35 -3
  20. package/dist/commands/why.js.map +1 -1
  21. package/dist/commit_files.d.ts +64 -0
  22. package/dist/commit_files.js +83 -0
  23. package/dist/commit_files.js.map +1 -0
  24. package/dist/copy/strings.d.ts +34 -14
  25. package/dist/copy/strings.js +51 -14
  26. package/dist/copy/strings.js.map +1 -1
  27. package/dist/drain.d.ts +68 -0
  28. package/dist/drain.js +234 -0
  29. package/dist/drain.js.map +1 -0
  30. package/dist/firing.d.ts +169 -0
  31. package/dist/firing.js +334 -0
  32. package/dist/firing.js.map +1 -0
  33. package/dist/hooks/entry.d.ts +171 -26
  34. package/dist/hooks/entry.js +335 -507
  35. package/dist/hooks/entry.js.map +1 -1
  36. package/dist/hooks/fire_lock.d.ts +36 -87
  37. package/dist/hooks/fire_lock.js +40 -99
  38. package/dist/hooks/fire_lock.js.map +1 -1
  39. package/dist/hooks/post_commit.d.ts +11 -0
  40. package/dist/hooks/post_commit.js +16 -8
  41. package/dist/hooks/post_commit.js.map +1 -1
  42. package/dist/identity.d.ts +32 -0
  43. package/dist/identity.js +38 -0
  44. package/dist/identity.js.map +1 -0
  45. package/dist/index.d.ts +3 -17
  46. package/dist/index.js +59 -450
  47. package/dist/index.js.map +1 -1
  48. package/dist/paths.d.ts +18 -0
  49. package/dist/paths.js +31 -0
  50. package/dist/paths.js.map +1 -1
  51. package/dist/pending_prune.d.ts +2 -2
  52. package/dist/pending_prune.js +2 -2
  53. package/dist/policy.d.ts +17 -0
  54. package/dist/policy.js +7 -0
  55. package/dist/policy.js.map +1 -1
  56. package/dist/post.d.ts +63 -4
  57. package/dist/post.js +76 -24
  58. package/dist/post.js.map +1 -1
  59. package/dist/replay.d.ts +167 -0
  60. package/dist/replay.js +301 -0
  61. package/dist/replay.js.map +1 -0
  62. package/dist/session_targets.d.ts +12 -0
  63. package/dist/session_targets.js +6 -1
  64. package/dist/session_targets.js.map +1 -1
  65. package/dist/spool.d.ts +55 -21
  66. package/dist/spool.js +182 -38
  67. package/dist/spool.js.map +1 -1
  68. package/dist/spool_union.d.ts +18 -2
  69. package/dist/spool_union.js +23 -2
  70. package/dist/spool_union.js.map +1 -1
  71. package/dist/state.js +21 -14
  72. package/dist/state.js.map +1 -1
  73. package/dist/stdin.d.ts +2 -0
  74. package/dist/stdin.js +13 -0
  75. package/dist/stdin.js.map +1 -0
  76. package/dist/uploader_status.d.ts +34 -0
  77. package/dist/uploader_status.js +25 -0
  78. package/dist/uploader_status.js.map +1 -0
  79. package/dist/uploader_view.d.ts +39 -0
  80. package/dist/uploader_view.js +106 -0
  81. package/dist/uploader_view.js.map +1 -0
  82. package/dist/verbs.d.ts +11 -0
  83. package/dist/verbs.js +13 -0
  84. package/dist/verbs.js.map +1 -0
  85. package/dist/version.d.ts +1 -1
  86. package/dist/version.js +1 -1
  87. package/dist/version.js.map +1 -1
  88. package/package.json +2 -2
package/dist/firing.js ADDED
@@ -0,0 +1,334 @@
1
+ /**
2
+ * THE FIRING LOG AND THE HOOK'S LIVENESS STAMP — U3 of the detached uploader.
3
+ *
4
+ * A *firing* is one hook that ended a turn: today's hook body, deferred. The hook appends one line saying "this turn ended here, at this
5
+ * size, in this folder, with these repos" and returns; the upload worker (U5) replays the lines in order, each ALONE, with the values
6
+ * recorded at the moment the hook fired (plan §3.2-§3.4, D15). This module is the storage half and nothing in the hook calls it yet:
7
+ * it is inert until the hook is flipped (U6).
8
+ *
9
+ * ## What it owns
10
+ * - `<sid>.firings.jsonl` — append-only, one `O_APPEND` write per line, no lock (hooks never wait).
11
+ * - `<sid>.firings.cursor` — the worker's progress: a byte offset, and how many times the firing at that offset made the worker THROW.
12
+ * Written atomically, and only by a holder of the session's writer lock.
13
+ * - `<repo digest>/<sid>.live` — the hook-only liveness stamp, and the ONE function that reads liveness (`liveAtOf`).
14
+ *
15
+ * ## What a firing holds: paths and numbers only
16
+ * No credential, no transcript content. The worker re-confines `transcript` and `announced` with `admitsTranscript` before every read.
17
+ *
18
+ * ## ⛔ A LEAF MODULE. It imports `paths` and `atomic_write` and nothing else of this package's, because `spool.ts` and `state.ts` both
19
+ * read liveness through it and neither may import the other.
20
+ */
21
+ import { appendFileSync, chmodSync, closeSync, fstatSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, rmSync, statSync } from "node:fs";
22
+ import { dirname } from "node:path";
23
+ import { writeFileAtomic } from "./atomic_write.js";
24
+ import { sessionFileStem, sessionFiringsCursorPath, sessionFiringsPath, sessionLivePath, targetsDir } from "./paths.js";
25
+ /** The line format this build writes and reads. A reader sees a NEWER `v` as a stop sign, never as garbage (§3.14). */
26
+ export const FIRING_VERSION = 1;
27
+ /** A line longer than this is not written: a firing is ~300 bytes, and one `O_APPEND` write of a small buffer is what keeps hooks lock-free. */
28
+ export const MAX_FIRING_BYTES = 32 * 1024;
29
+ /** After this many uncaught throws on one firing, the worker skips it. A firing only sets an EOF, so the next one still covers its bytes. */
30
+ export const MAX_FIRING_THROWS = 3;
31
+ /** More sub-agent files than this in one firing: the map is left out (uncapped) rather than growing the line past `MAX_FIRING_BYTES`. */
32
+ export const MAX_FIRING_SUB_FILES = 200;
33
+ // --- the log -------------------------------------------------------------------------------------------------------------------
34
+ /**
35
+ * Append one firing. Returns false on any failure — a hook must never throw, and a missed firing self-heals: the NEXT firing's `eof`
36
+ * covers the same bytes.
37
+ *
38
+ * ⛔ ONE `O_APPEND` WRITE. Two hooks (Cursor double-fires) interleave whole lines, never halves, so no lock is needed. A line over
39
+ * `MAX_FIRING_BYTES` is refused rather than split.
40
+ */
41
+ export function appendFiring(home, sessionId, firing) {
42
+ try {
43
+ const line = `${JSON.stringify(firing)}\n`;
44
+ if (Buffer.byteLength(line) > MAX_FIRING_BYTES)
45
+ return false;
46
+ const path = sessionFiringsPath(home, sessionId);
47
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
48
+ appendFileSync(path, line, { mode: 0o600 });
49
+ chmodSync(path, 0o600);
50
+ return true;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ /**
57
+ * What the hook calls: `appendFiring`, and if the line is refused while it carries a `sub` map, once more WITHOUT it (uncapped, the behaviour before
58
+ * the cap existed). The map is the one part of a line that grows with the session; the cap must never cost a firing. Never throws.
59
+ */
60
+ export function recordFiring(home, sessionId, firing) {
61
+ if (appendFiring(home, sessionId, firing))
62
+ return true;
63
+ if (firing.sub === undefined)
64
+ return false;
65
+ const { sub: _dropped, ...withoutSub } = firing;
66
+ return appendFiring(home, sessionId, withoutSub);
67
+ }
68
+ /**
69
+ * The firings from `from` (a cursor offset) on. Never throws: an unreadable log reads as empty.
70
+ *
71
+ * A malformed COMPLETE line is skipped and counted in `through`. A trailing line with no newline is a hook mid-append and is left alone.
72
+ * A line of a newer `v` stops the read and is not consumed (`blockedByNewer`).
73
+ */
74
+ export function readFirings(home, sessionId, from = 0) {
75
+ const empty = { firings: [], through: from, blockedByNewer: false };
76
+ let fd = null;
77
+ try {
78
+ fd = openSync(sessionFiringsPath(home, sessionId), "r");
79
+ const size = fstatSync(fd).size;
80
+ if (!Number.isSafeInteger(from) || from < 0 || from >= size)
81
+ return { ...empty, through: Math.min(Math.max(from, 0), size) };
82
+ const buf = Buffer.alloc(size - from);
83
+ let got = 0;
84
+ while (got < buf.length) {
85
+ const n = readSync(fd, buf, got, buf.length - got, from + got);
86
+ if (n <= 0)
87
+ break;
88
+ got += n;
89
+ }
90
+ const firings = [];
91
+ let through = from;
92
+ let start = 0;
93
+ for (let nl = buf.indexOf(0x0a, 0); nl !== -1 && nl < got; nl = buf.indexOf(0x0a, start)) {
94
+ const parsed = parseFiring(buf.subarray(start, nl).toString("utf8"), sessionFileStem(sessionId));
95
+ const lineStart = from + start;
96
+ const lineEnd = from + nl + 1;
97
+ if (parsed === "newer")
98
+ return { firings, through, blockedByNewer: true };
99
+ if (parsed !== null)
100
+ firings.push({ firing: parsed, start: lineStart, end: lineEnd });
101
+ through = lineEnd;
102
+ start = nl + 1;
103
+ }
104
+ return { firings, through, blockedByNewer: false };
105
+ }
106
+ catch {
107
+ return empty;
108
+ }
109
+ finally {
110
+ if (fd !== null) {
111
+ try {
112
+ closeSync(fd);
113
+ }
114
+ catch {
115
+ /* nothing to do */
116
+ }
117
+ }
118
+ }
119
+ }
120
+ /** A valid firing, `"newer"` for a line of a version this build does not know, or null for anything else. */
121
+ function parseFiring(text, stem) {
122
+ let raw;
123
+ try {
124
+ raw = JSON.parse(text);
125
+ }
126
+ catch {
127
+ return null;
128
+ }
129
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
130
+ return null;
131
+ const o = raw;
132
+ if (typeof o.v !== "number" || !Number.isInteger(o.v) || o.v < 1)
133
+ return null;
134
+ if (o.v > FIRING_VERSION)
135
+ return "newer";
136
+ const str = (x) => typeof x === "string" && x !== "";
137
+ if (typeof o.at !== "number" || !Number.isFinite(o.at) || o.at <= 0)
138
+ return null;
139
+ if (typeof o.eof !== "number" || !Number.isSafeInteger(o.eof) || o.eof < 0)
140
+ return null;
141
+ // `sid` must be THIS log's session: a line whose id sanitises to another file name could otherwise be replayed under another session's cursor,
142
+ // lock and ledger. It is deliberately NOT required to be header-safe HERE: `sid` goes on the wire as `x-session-id` and delivery refuses an id the
143
+ // wire cannot carry (`sessionIdSafe`, post.ts) as a LOUD write-off (`never`, a gap `status` counts). Dropping such a line at the parse would be a
144
+ // new, differently-silent no-op (the 182 ruling); the id never reaches a header, a file name or a lock path except through `sanitise`.
145
+ if (!str(o.sid) || o.sid.length > 512 || sessionFileStem(o.sid) !== stem)
146
+ return null;
147
+ if (!str(o.event) || !str(o.transcript) || !str(o.agent) || !str(o.id) || typeof o.cwd !== "string")
148
+ return null;
149
+ const prior = {};
150
+ if (o.prior !== undefined) {
151
+ if (o.prior === null || typeof o.prior !== "object" || Array.isArray(o.prior))
152
+ return null;
153
+ for (const [key, value] of Object.entries(o.prior)) {
154
+ if (value === null)
155
+ prior[key] = null;
156
+ else if (typeof value === "number" && Number.isFinite(value) && value > 0)
157
+ prior[key] = value;
158
+ else
159
+ return null;
160
+ }
161
+ }
162
+ let keys;
163
+ if (o.keys !== undefined) {
164
+ const k = o.keys;
165
+ if (k === null || typeof k !== "object" || !str(k.worktree) || !str(k.consent))
166
+ return null;
167
+ keys = { worktree: k.worktree, consent: k.consent };
168
+ }
169
+ if (o.slug !== undefined && !str(o.slug))
170
+ return null;
171
+ if (o.announced !== undefined && !str(o.announced))
172
+ return null;
173
+ // A `sub` map that is not exactly {key: non-negative integer} is IGNORED (the firing is uncapped, today's behaviour), never a reason to drop the firing.
174
+ let sub;
175
+ if (o.sub !== null && typeof o.sub === "object" && !Array.isArray(o.sub)) {
176
+ const entries = Object.entries(o.sub);
177
+ if (entries.length <= MAX_FIRING_SUB_FILES && entries.every(([, size]) => typeof size === "number" && Number.isSafeInteger(size) && size >= 0)) {
178
+ sub = Object.fromEntries(entries);
179
+ }
180
+ }
181
+ return {
182
+ v: o.v,
183
+ at: o.at,
184
+ sid: o.sid,
185
+ event: o.event,
186
+ eof: o.eof,
187
+ transcript: o.transcript,
188
+ cwd: o.cwd,
189
+ ...(keys === undefined ? {} : { keys }),
190
+ ...(o.slug === undefined ? {} : { slug: o.slug }),
191
+ agent: o.agent,
192
+ id: o.id,
193
+ prior,
194
+ ...(o.announced === undefined ? {} : { announced: o.announced }),
195
+ ...(sub === undefined ? {} : { sub }),
196
+ };
197
+ }
198
+ const LOG_SUFFIX = ".firings.jsonl";
199
+ /**
200
+ * Every session that has a firing log: the file stems, oldest-modified first (so a session that has waited longest is served first). A
201
+ * stem is `sanitise(sid)`; it names the log and cursor files and nothing else, because the id to SEND is `Firing.sid`. Never throws.
202
+ */
203
+ export function firingSessions(home) {
204
+ try {
205
+ const dir = targetsDir(home);
206
+ return readdirSync(dir)
207
+ .filter((name) => name.endsWith(LOG_SUFFIX) && name.length > LOG_SUFFIX.length)
208
+ .map((name) => ({ stem: name.slice(0, -LOG_SUFFIX.length), at: modifiedAt(`${dir}/${name}`) }))
209
+ .sort((a, b) => a.at - b.at || (a.stem < b.stem ? -1 : 1))
210
+ .map((entry) => entry.stem);
211
+ }
212
+ catch {
213
+ return [];
214
+ }
215
+ }
216
+ function modifiedAt(path) {
217
+ try {
218
+ return statSync(path).mtimeMs;
219
+ }
220
+ catch {
221
+ return 0;
222
+ }
223
+ }
224
+ /** Forget a session's firing log and cursor (with its target record, on the existing 30-day schedule). Never throws. */
225
+ export function forgetFirings(home, sessionId) {
226
+ for (const path of [sessionFiringsPath(home, sessionId), sessionFiringsCursorPath(home, sessionId)]) {
227
+ try {
228
+ rmSync(path, { force: true });
229
+ }
230
+ catch {
231
+ /* an undeletable file is retried next schedule */
232
+ }
233
+ }
234
+ }
235
+ export const START_OF_LOG = { offset: 0, throws: 0 };
236
+ /**
237
+ * The cursor, or the start of the log when it is missing or unreadable. Starting over is SAFE: replaying a consumed firing is a no-op
238
+ * (`firing.eof <= sentOffset`, trap 1), just slower.
239
+ */
240
+ export function readCursor(home, sessionId) {
241
+ try {
242
+ const raw = JSON.parse(readFileSync(sessionFiringsCursorPath(home, sessionId), "utf8"));
243
+ const offset = raw.offset;
244
+ const throws = raw.throws;
245
+ if (typeof offset !== "number" || !Number.isSafeInteger(offset) || offset < 0)
246
+ return START_OF_LOG;
247
+ return { offset, throws: typeof throws === "number" && Number.isSafeInteger(throws) && throws > 0 ? throws : 0 };
248
+ }
249
+ catch {
250
+ return START_OF_LOG;
251
+ }
252
+ }
253
+ /**
254
+ * Persist the cursor atomically. ⛔ ONLY A HOLDER OF THE SESSION'S WRITER LOCK may call this, and only after a heartbeat that said the
255
+ * lock is still its own (§3.3: "every cursor write beats first"); a worker that lost its lock writes nothing. Returns false on failure.
256
+ */
257
+ export function writeCursor(home, sessionId, cursor) {
258
+ try {
259
+ const path = sessionFiringsCursorPath(home, sessionId);
260
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
261
+ writeFileAtomic(path, `${JSON.stringify({ v: 1, offset: cursor.offset, throws: cursor.throws })}\n`, 0o600);
262
+ return true;
263
+ }
264
+ catch {
265
+ return false;
266
+ }
267
+ }
268
+ /** Move the cursor to `offset`; the throw count belongs to the firing it left, so it resets. */
269
+ export const advanced = (cursor, offset) => ({ offset: Math.max(cursor.offset, offset), throws: 0 });
270
+ /** The firing at the cursor made the worker throw once more. */
271
+ export const threw = (cursor) => ({ offset: cursor.offset, throws: cursor.throws + 1 });
272
+ /** Skip the firing at the cursor: it has thrown `MAX_FIRING_THROWS` times. */
273
+ export const shouldSkip = (cursor) => cursor.throws >= MAX_FIRING_THROWS;
274
+ // --- liveness ------------------------------------------------------------------------------------------------------------------
275
+ /**
276
+ * Stamp "this session's hook ran at `atMs`" for one repo key. HOOK-ONLY: the worker runs `deliver` with `byHook: false` and never calls
277
+ * this. A JSON object (`{at}`) rather than a bare number so P2's T7 can add `transcript` without a format change; the reader ignores
278
+ * unknown fields. Atomic replace. Never throws; false on failure (the commit then falls to the cold rung or goes unrecorded, as today
279
+ * before a first hook).
280
+ */
281
+ export function stampLive(home, key, atMs) {
282
+ const path = sessionLivePath(home, key);
283
+ if (path === null || !Number.isFinite(atMs) || atMs <= 0)
284
+ return false;
285
+ try {
286
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
287
+ writeFileAtomic(path, `${JSON.stringify({ at: atMs })}\n`, 0o600);
288
+ return true;
289
+ }
290
+ catch {
291
+ return false;
292
+ }
293
+ }
294
+ /** The `.live` stamp at `path`, or null when absent, unreadable or not a positive number. Unknown fields are ignored. */
295
+ export function readLiveFile(path) {
296
+ try {
297
+ const at = JSON.parse(readFileSync(path, "utf8")).at;
298
+ return typeof at === "number" && Number.isFinite(at) && at > 0 ? at : null;
299
+ }
300
+ catch {
301
+ return null;
302
+ }
303
+ }
304
+ /**
305
+ * ⛔ THE ONE LIVENESS READER (§3.5). A session is live as of the LATER of its state file's `liveAt` (stamped by `deliver()` for a hook
306
+ * delivery) and its `.live` file (stamped by the hook itself, before the worker has delivered anything). All three readers go through
307
+ * this: `liveSessions` and `pendingModeFor` (`spool.ts`) and the two `status` classifiers (`state.ts`). Read straight from the files,
308
+ * never from the mtime: a `finalize` that rewrites a dead session's state must not make it live (VG review 2, D2).
309
+ */
310
+ export function liveAtOf(stateLiveAt, livePath) {
311
+ const stamped = livePath === null ? null : readLiveFile(livePath);
312
+ if (stamped === null)
313
+ return stateLiveAt;
314
+ return stateLiveAt === null ? stamped : Math.max(stateLiveAt, stamped);
315
+ }
316
+ /**
317
+ * Which repo keys a hook stamps `.live` for (§3.5).
318
+ * - the turn-end repo, when allowed;
319
+ * - for a dialect that ISOLATES its content records (Claude Code, Codex): also every ledger key in the session's target record;
320
+ * - for one that does not (Cursor): the turn-end repo ONLY. Sticky targets stay in Cursor's record but are not delivered, and only
321
+ * delivered targets stamp today. Stamping them would keep a repo Cursor has left live for 30 minutes past every turn, a human's commit
322
+ * there would become `sole_live_session`, and the row is permanent.
323
+ */
324
+ export function liveStampKeys(input) {
325
+ const keys = new Set();
326
+ if (input.turnEnd !== null && input.turnEnd !== "")
327
+ keys.add(input.turnEnd);
328
+ if (input.isolates)
329
+ for (const key of input.recordLedgerKeys)
330
+ if (key !== "")
331
+ keys.add(key);
332
+ return [...keys];
333
+ }
334
+ //# sourceMappingURL=firing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firing.js","sourceRoot":"","sources":["../src/firing.ts"],"names":[],"mappings":"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"}
@@ -36,11 +36,12 @@
36
36
  import { type IngestCredential } from "../credential.js";
37
37
  import { type Delivery, type FileKey } from "../post.js";
38
38
  import { type RecordCutter } from "../policy.js";
39
- import { type Isolation } from "../session_targets.js";
39
+ import { type Isolation, type Resolution, type Target } from "../session_targets.js";
40
40
  import type { HookEventName } from "../agents/types.js";
41
+ import { type PriorLiveness } from "../firing.js";
41
42
  import { type SessionKey } from "../paths.js";
42
43
  export { confinementRoots } from "../project.js";
43
- import { turnStart, type TurnStart } from "../pending_bound.js";
44
+ import { turnStart, type PendingMode, type TurnStart } from "../pending_bound.js";
44
45
  /**
45
46
  * Default wall-clock budget. `CR-028` sets the real per-event `timeout` in
46
47
  * `claude-plugin/hooks/hooks.json`; this is the client-side backstop, which must
@@ -62,30 +63,6 @@ export declare const DEFAULT_HOOK_BUDGET_MS = 5000;
62
63
  export declare function sendTimeoutMs(budgetMs: number, consumedMs: number): number;
63
64
  /** A promise that resolves after `ms`. The one clock the settle path needs. */
64
65
  export declare function wait(ms: number): Promise<void>;
65
- /**
66
- * The budget the SENDS may spend — the whole budget, less the stamp's share.
67
- *
68
- * A function, and exported, for the reason `sendTimeoutMs` is: the carve-out is
69
- * the thing this task changes, and a wall-clock test cannot pin it. Every
70
- * discriminator that distinguishes reserve from no-reserve through a real
71
- * process turned out to slide with per-send overhead — a 149 ms window that
72
- * moves by 25 ms for every 10 ms of connect-and-compress cost — so pinning it
73
- * there would be tuning a constant to one machine. Here it is arithmetic.
74
- *
75
- * ⛔ `budgetMs` itself still governs the SETTLE and the WATCHDOG. Only the sends
76
- * are carved.
77
- */
78
- /**
79
- * ⛔ **TAKES THE RESOLVED EVENT, NEVER THE RAW `hook_event_name` (`CR-195`).**
80
- *
81
- * It used to take a `string`, and that is precisely how Cursor's `sessionEnd`
82
- * bought a full-budget send with no reserve: the comparison was against
83
- * `"SessionEnd"`, so an unrecognised spelling was `false` rather than an error.
84
- * `HookEventName | null` makes the resolution a thing the CALLER must already
85
- * have done — `null` is the honest "none of the three", and an unresolved wire
86
- * string is no longer expressible here at all.
87
- */
88
- export declare function sendBudgetMs(budgetMs: number, event: HookEventName | null, sinceHookStartMs?: number): number;
89
66
  export interface HookInput {
90
67
  readonly sessionId: string;
91
68
  readonly transcriptPath: string;
@@ -205,6 +182,139 @@ export declare function runHook(ctx: HookEnvironment): Promise<never>;
205
182
  * `VIBECOMMIT_HOOK_TIMEOUT_MS` still wins outright, as it always has.
206
183
  */
207
184
  export declare function hookBudgetMs(env: NodeJS.ProcessEnv, agentId: string): number;
185
+ /**
186
+ * U4 — HOW A DELIVERY IS PACED. A HOOK has a wall clock: one shared deadline, every request's timeout trimmed to what is left, a floor
187
+ * under the budget below which a further file is not started. The UPLOAD WORKER has none, and ⛔ must not be given an infinite one:
188
+ * `setTimeout(Infinity)` fires at 1 ms (trap 4), and any deadline at all brings back the abandonment this build removes (`withinBudget`
189
+ * trims the timeout near the end of a round, the client aborts, the server applies). So it passes a FIXED request timeout and no deadline.
190
+ */
191
+ export type Pace = {
192
+ readonly kind: "hook";
193
+ readonly settleStart: number;
194
+ readonly sendBudget: number;
195
+ readonly consumedMs: number;
196
+ } | {
197
+ readonly kind: "unbounded";
198
+ readonly timeoutMs: number;
199
+ };
200
+ /**
201
+ * U4 — THE INPUTS A REPLAY FIXES AT THE MOMENT THE HOOK FIRED (plan §3.4). Absent for the inline hook, `finalize`, `backfill` and the drain,
202
+ * which are "now" and read the whole spool.
203
+ */
204
+ export interface ReplayAsOf {
205
+ /** `firing.at`: the clock for promotion and pending mode, and the commit pin. */
206
+ readonly at: number;
207
+ /** `firing.prior`: liveness as the hook read it BEFORE it stamped (`firing.ts`), keyed by ledger key. */
208
+ readonly prior: PriorLiveness;
209
+ /** U6b (F5): `firing.sub`, the ceiling per sub-agent file. Left out when the firing has none or is a settled `SessionEnd` (real sizes). */
210
+ readonly subCap?: Readonly<Record<string, number>>;
211
+ }
212
+ /**
213
+ * U4 — WHAT HAPPENED TO ONE TARGET, in the terms the worker's cursor needs. `Delivery.kind` alone cannot say: `nothing-to-send` is also a lost
214
+ * lock, a failed read and an unterminated record, and `ok` can be one delta of several. Derived from the delivery AND the ledger (plan §3.3).
215
+ */
216
+ export type TargetReason =
217
+ /** `sentOffset >= eof`: this firing's main bytes are delivered (or were already). */
218
+ {
219
+ readonly kind: "caught-up";
220
+ }
221
+ /** A RECORDED state keeps the bytes owed; they ride this target's next delivery, and it never holds back another target. */
222
+ | {
223
+ readonly kind: "held";
224
+ readonly why: "capture-not-approved" | "stopped" | "unterminated" | "folder-forbidden";
225
+ }
226
+ /** The server refused these bytes for good; they were advanced past and counted. */
227
+ | {
228
+ readonly kind: "never";
229
+ }
230
+ /** A `later` with NO recorded state (a timeout, a 5xx): the bytes are still owed and the worker backs off. */
231
+ | {
232
+ readonly kind: "later";
233
+ }
234
+ /** Credential-level: the worker stops. */
235
+ | {
236
+ readonly kind: "fatal";
237
+ }
238
+ /** The caller's lock heartbeat failed: nothing was written, and nothing may be called done. */
239
+ | {
240
+ readonly kind: "lock-lost";
241
+ }
242
+ /** The span could not be read, or the ledger could not be written: retried, and counted toward the throw limit. */
243
+ | {
244
+ readonly kind: "not-progressed";
245
+ readonly why: "read-failed" | "unwritable";
246
+ }
247
+ /** Sent, but `sentOffset < eof` still: more owed, call again. */
248
+ | {
249
+ readonly kind: "more-owed";
250
+ };
251
+ /** What delivering one target produced. */
252
+ export interface TargetResult {
253
+ readonly target: Target;
254
+ readonly delivery: Delivery;
255
+ readonly subagents: DelegatedDeliveries;
256
+ /** The server refused THIS target for good (C3): nothing else about the firing changes. */
257
+ readonly stopped: boolean;
258
+ /** U4: see `TargetReason`. */
259
+ readonly reason: TargetReason;
260
+ }
261
+ /** Pure. The reason for one target from its delivery and where its main ledger stands against the `eof` it was asked to reach. */
262
+ export declare function targetReason(delivery: Delivery, sentOffset: number, eof: number, stopped: boolean): TargetReason;
263
+ /**
264
+ * U4 — DELIVER ONE FIRING TO ITS RESOLVED TARGETS. The loop `hookBody` always ran inline, callable by the upload worker with a replay's
265
+ * as-of inputs and no wall clock. Behaviour for the inline hook is unchanged: `pace.kind === "hook"` and no `asOf` is the old code.
266
+ *
267
+ * Order and fairness are as before: least-recently-sent target first, so a 20 MB backlog on one cannot starve the rest.
268
+ */
269
+ export declare function deliverFiring(opts: {
270
+ ctx: HookEnvironment;
271
+ input: HookInput;
272
+ url: string;
273
+ credential: IngestCredential;
274
+ resolution: Resolution;
275
+ eof: number;
276
+ event: HookEventName | null;
277
+ isSessionEnd: boolean;
278
+ pace: Pace;
279
+ asOf?: ReplayAsOf;
280
+ /**
281
+ * U5: the caller's session-lock heartbeat. Called before a target is touched and before every request and write beneath it (`beforeRequest`,
282
+ * U1). `false` means the lock is no longer ours: nothing more is written, and that target reports `lock-lost`. The inline hook passes none.
283
+ */
284
+ heartbeat?: () => boolean;
285
+ }): Promise<TargetResult[]>;
286
+ /**
287
+ * U4 — `pendingModeFor`, answered from `firing.prior` (plan §3.4, P2 point 4). `prior` keeps `pendingModeFor`'s three outcomes:
288
+ * - a NUMBER is the stamp the hook read before it stamped: live within the window of `firing.at` is `first-turn`, older is `stale-own`;
289
+ * - an explicit `null` is a state file with NO `liveAt` (what `finalize` and `backfill` leave): `stale-own`;
290
+ * - an ABSENT key is no state file at hook time: `first-turn`. A key the hook did not record at all (a second work tree, a parent-folder
291
+ * repo on its first firing) falls back to `pendingModeFor` judged at `firing.at`, never `Date.now()`, which is safe because stamps arrive in
292
+ * firing order.
293
+ * A plain number-or-omitted map would read a resumed session as `first-turn` and keep a lingering-id line.
294
+ */
295
+ export declare function modeFromPrior(home: string, key: SessionKey, asOf: ReplayAsOf): PendingMode;
296
+ /**
297
+ * U6b (F2d): THE WORKER'S PACE BETWEEN REQUESTS (D9): sleep `max(250 ms, the request's own elapsed)`, capped at 30 s. It lives here, beside the delivery loops
298
+ * that must honour it, because ONE worker step can put several requests on the wire (the main delta, then one per sub-agent file) and pacing only
299
+ * after the whole step let a fast server take thirteen of them back to back.
300
+ */
301
+ export declare const UNBOUNDED_PACE_FLOOR_MS = 250;
302
+ export declare const UNBOUNDED_PACE_CAP_MS = 30000;
303
+ export declare function unboundedPaceMs(elapsedMs: number): number;
304
+ /** A session with no firing of its own for this long is IDLE: another session's firing may finish its owed bytes (R19 · D22). */
305
+ export declare const DRAIN_IDLE_MS: number;
306
+ /** Session records one firing looks at. Examined ones move to the back of the line (their mtime moves), so none starves. */
307
+ export declare const DRAIN_EXAMINE_MAX = 40;
308
+ /** A record whose transcript is gone and that owes nothing is forgotten after this long. */
309
+ export declare const DRAIN_FORGET_MS: number;
310
+ /**
311
+ * An owed target whose transcript is GONE (Claude Code deletes transcripts ~30 days on) can never be sent: its hold becomes the
312
+ * honest gap now (R19 · D22), and the hold is cleared. Whatever was already delivered is untouched.
313
+ */
314
+ export declare function writeOffOwed(home: string, record: {
315
+ readonly sessionId: string;
316
+ readonly targets: readonly Target[];
317
+ }, nowMs: number): void;
208
318
  /** One sub-agent transcript that passed confinement and the key bound. */
209
319
  interface SubagentFile {
210
320
  readonly path: string;
@@ -276,8 +386,23 @@ export declare function deliverSubagents(opts: {
276
386
  * bytes this target was never allowed to send. Absent for a repo target: it sends to each file's end, as it always did.
277
387
  */
278
388
  sizeCap?: Readonly<Record<string, number>>;
389
+ /**
390
+ * U6b (F5): a REPLAYED firing's ceiling per sub-agent file, `Firing.sub` (what existed when the hook ran). Combined with `sizeCap` by `min`; a file
391
+ * with no key sends nothing. Absent for a hook, the drain, `finalize` and `backfill` (they read the real size), and for a settled `SessionEnd`.
392
+ */
393
+ firingCap?: Readonly<Record<string, number>>;
279
394
  /** A-T4: what the delegated streams may contain (R17 / R6). Default: a repo target's own `redactionRoots`. */
280
395
  isolation?: Isolation;
396
+ /** U4: the upload worker's stamp (`DeliveryContext.liveAtMs`); requires `byHook: false`. */
397
+ liveAtMs?: number;
398
+ /**
399
+ * U4 — **THE WORKER'S PACE**: a fixed request timeout and NO budget. When set, `startedAt`/`sendBudget` are ignored: no remainder arithmetic,
400
+ * no floor under which a further file is skipped, no `deadlineAtMs`, and no cap of `MAX_SUBAGENT_FILES` per firing (it loops until none is
401
+ * owed). Never `Infinity`: `setTimeout(Infinity)` fires at 1 ms (trap 4).
402
+ */
403
+ unboundedTimeoutMs?: number;
404
+ /** U6b (F2d): in an unbounded step, wait this long before the FIRST request (the main delta just went); each later one waits `unboundedPaceMs` of the previous request. */
405
+ pauseFirstMs?: number;
281
406
  /**
282
407
  * C25: the caller's session-lock heartbeat (`finalize`, `backfill`). Called before each delegated file and before every request
283
408
  * inside it; `false` means the lock is gone, and the loop stops with nothing more written. A hook passes none.
@@ -302,6 +427,26 @@ export interface DelegatedDeliveries {
302
427
  */
303
428
  readonly deliveries: readonly Delivery[];
304
429
  }
430
+ /**
431
+ * Every `agent-*.jsonl` under this session's `subagents/`, oldest first.
432
+ *
433
+ * **`agent-*.jsonl`, never `agent-*`.** The directory carries an
434
+ * `agent-<id>.meta.json` beside every transcript — 636 of each on this machine,
435
+ * exactly 1:1 — so the looser glob would double every upload and send metadata
436
+ * as transcript content.
437
+ *
438
+ * **Every path is confined.** `isInsideAny(transcriptRoots(...))` gates each
439
+ * entry before it is opened, exactly as the main transcript is gated: this adds
440
+ * a new SET of readers to the surface `/cso` finding 1 rated HIGH, and a
441
+ * symlinked entry in `subagents/` must not walk out of the transcript roots.
442
+ *
443
+ * Oldest first, because it cannot starve. Ordering by pending bytes would let a
444
+ * small file lose every round forever; by mtime, every file reaches the front.
445
+ *
446
+ * A missing or unreadable directory is simply "no sub-agents" — silent, per the
447
+ * hook contract.
448
+ */
449
+ export declare function findSubagentFiles(ctx: HookEnvironment, transcriptPath: string): SubagentFile[];
305
450
  /** Size in bytes, or null if it cannot be read. */
306
451
  export declare function fileSize(path: string): number | null;
307
452
  /**