specweave 3.0.5 → 3.0.6

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 (36) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/README.md +2 -2
  3. package/bin/specweave.js +5 -5
  4. package/bin/startup-check.js +1 -1
  5. package/dist/src/cli/commands/auto-handoff.d.ts +4 -26
  6. package/dist/src/cli/commands/auto-handoff.d.ts.map +1 -1
  7. package/dist/src/cli/commands/auto-handoff.js +23 -52
  8. package/dist/src/cli/commands/auto-handoff.js.map +1 -1
  9. package/dist/src/core/session/checkpoint-worker.d.ts +2 -0
  10. package/dist/src/core/session/checkpoint-worker.d.ts.map +1 -0
  11. package/dist/src/core/session/checkpoint-worker.js +43 -0
  12. package/dist/src/core/session/checkpoint-worker.js.map +1 -0
  13. package/dist/src/core/session/handoff-doc-format.d.ts +2 -0
  14. package/dist/src/core/session/handoff-doc-format.d.ts.map +1 -1
  15. package/dist/src/core/session/handoff-doc-format.js +16 -4
  16. package/dist/src/core/session/handoff-doc-format.js.map +1 -1
  17. package/dist/src/core/session/handoff-git-state.d.ts +3 -1
  18. package/dist/src/core/session/handoff-git-state.d.ts.map +1 -1
  19. package/dist/src/core/session/handoff-git-state.js +32 -12
  20. package/dist/src/core/session/handoff-git-state.js.map +1 -1
  21. package/dist/src/core/session/session-checkpoint.d.ts +46 -0
  22. package/dist/src/core/session/session-checkpoint.d.ts.map +1 -0
  23. package/dist/src/core/session/session-checkpoint.js +262 -0
  24. package/dist/src/core/session/session-checkpoint.js.map +1 -0
  25. package/dist/src/core/session/usage-guard.d.ts +6 -69
  26. package/dist/src/core/session/usage-guard.d.ts.map +1 -1
  27. package/dist/src/core/session/usage-guard.js +5 -136
  28. package/dist/src/core/session/usage-guard.js.map +1 -1
  29. package/dist/src/core/session/work-handoff.d.ts +4 -0
  30. package/dist/src/core/session/work-handoff.d.ts.map +1 -1
  31. package/dist/src/core/session/work-handoff.js +31 -10
  32. package/dist/src/core/session/work-handoff.js.map +1 -1
  33. package/package.json +1 -1
  34. package/plugins/specweave/.claude-plugin/plugin.json +1 -1
  35. package/plugins/specweave/skills/handoff/SKILL.md +21 -13
  36. package/src/templates/AGENTS.md.template +1 -1
@@ -37,17 +37,20 @@ export const BOOKKEEPING_PATHSPECS = [
37
37
  /** Total Git budget leaves headroom below PreCompact's five-second budget. */
38
38
  export const GIT_CAPTURE_BUDGET_MS = 3500;
39
39
  /** Capture through a private index; an interrupted hook never stages user files. */
40
- export function captureGitState(repoRoot, diffOutputPath) {
40
+ export function captureGitState(repoRoot, diffOutputPath, opts = {}) {
41
41
  const empty = {
42
42
  isGitRepo: false, branch: '', shortSha: '', statusPorcelain: '',
43
43
  diffStat: '', hasUncommittedChanges: false,
44
44
  };
45
45
  const deadline = Date.now() + GIT_CAPTURE_BUDGET_MS;
46
46
  const env = { ...process.env, GIT_OPTIONAL_LOCKS: '0' };
47
- function git(args, trim = true) {
47
+ function git(args, trim = true, required = true) {
48
48
  const remaining = deadline - Date.now();
49
- if (remaining <= 0)
49
+ if (remaining <= 0) {
50
+ if (opts.strict)
51
+ throw new Error('checkpoint Git capture budget exceeded');
50
52
  return null;
53
+ }
51
54
  try {
52
55
  const result = execFileSync('git', args, {
53
56
  cwd: repoRoot, env, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
@@ -55,19 +58,32 @@ export function captureGitState(repoRoot, diffOutputPath) {
55
58
  });
56
59
  return trim ? result.trim() : result;
57
60
  }
58
- catch {
61
+ catch (error) {
62
+ // Missing HEAD/upstream are normal. Timeouts and required commands are not.
63
+ if (opts.strict && (required || error.status !== 128 ||
64
+ error.signal || typeof error.code === 'string')) {
65
+ throw new Error('checkpoint Git capture failed');
66
+ }
59
67
  return null;
60
68
  }
61
69
  }
70
+ function writeDiff(content) {
71
+ if (!opts.strict) {
72
+ safeWriteDiff(diffOutputPath, content);
73
+ return;
74
+ }
75
+ fs.mkdirSync(path.dirname(diffOutputPath), { recursive: true });
76
+ fs.writeFileSync(diffOutputPath, content, 'utf8');
77
+ }
62
78
  let temporary;
63
79
  try {
64
- if (git(['rev-parse', '--is-inside-work-tree']) !== 'true') {
65
- safeWriteDiff(diffOutputPath, '');
80
+ if (git(['rev-parse', '--is-inside-work-tree'], true, fs.existsSync(path.join(repoRoot, '.git'))) !== 'true') {
81
+ writeDiff('');
66
82
  return empty;
67
83
  }
68
84
  const indexPath = git(['rev-parse', '--path-format=absolute', '--git-path', 'index']);
69
85
  if (!indexPath) {
70
- safeWriteDiff(diffOutputPath, '');
86
+ writeDiff('');
71
87
  return empty;
72
88
  }
73
89
  temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'specweave-handoff-index-'));
@@ -75,8 +91,8 @@ export function captureGitState(repoRoot, diffOutputPath) {
75
91
  if (fs.existsSync(indexPath))
76
92
  fs.copyFileSync(indexPath, privateIndex);
77
93
  Object.assign(env, { GIT_INDEX_FILE: privateIndex });
78
- const branch = git(['rev-parse', '--abbrev-ref', 'HEAD']) ?? '';
79
- const shortSha = git(['rev-parse', '--short', 'HEAD']) ?? '';
94
+ const branch = git(['rev-parse', '--abbrev-ref', 'HEAD'], true, false) ?? '';
95
+ const shortSha = git(['rev-parse', '--short', 'HEAD'], true, false) ?? '';
80
96
  const scope = ['--', '.', ...BOOKKEEPING_PATHSPECS];
81
97
  const statusPorcelain = git(['status', '--porcelain', ...scope]) ?? '';
82
98
  // NUL-delimited paths handle spaces, quotes, Unicode and embedded newlines.
@@ -91,9 +107,11 @@ export function captureGitState(repoRoot, diffOutputPath) {
91
107
  const stagedDiff = (hasHead ? git([...staged, ...scope]) : '') ?? '';
92
108
  const workingStat = git([...working, '--stat', ...scope]) ?? '';
93
109
  const stagedStat = (hasHead ? git([...staged, '--stat', ...scope]) : '') ?? '';
94
- safeWriteDiff(diffOutputPath, [workingDiff, stagedDiff].filter(Boolean).join('\n'));
95
- const upstream = git(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']) ?? '';
110
+ writeDiff([workingDiff, stagedDiff].filter(Boolean).join('\n'));
111
+ const upstream = git(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'], true, false) ?? '';
96
112
  const aheadRaw = upstream ? git(['rev-list', '--count', `${upstream}..HEAD`]) : null;
113
+ if (opts.strict && Date.now() > deadline)
114
+ throw new Error('checkpoint Git capture budget exceeded');
97
115
  return {
98
116
  isGitRepo: true, branch, shortSha, statusPorcelain,
99
117
  diffStat: [workingStat, stagedStat].filter(Boolean).join('\n'),
@@ -102,7 +120,9 @@ export function captureGitState(repoRoot, diffOutputPath) {
102
120
  ahead: aheadRaw !== null && aheadRaw !== '' ? Number(aheadRaw) : undefined,
103
121
  };
104
122
  }
105
- catch {
123
+ catch (error) {
124
+ if (opts.strict)
125
+ throw error;
106
126
  safeWriteDiff(diffOutputPath, '');
107
127
  return empty;
108
128
  }
@@ -1 +1 @@
1
- {"version":3,"file":"handoff-git-state.js","sourceRoot":"","sources":["../../../../src/core/session/handoff-git-state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handoff-git-state.js","sourceRoot":"","sources":["../../../../src/core/session/handoff-git-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ export declare const CHECKPOINT_INTERVAL_MS: number;
2
+ export declare const CHECKPOINT_WORKER_TIMEOUT_MS = 8000;
3
+ export declare const CHECKPOINT_LOCK_STALE_MS = 15000;
4
+ export declare const CHECKPOINT_MAX_WORKERS = 4;
5
+ export interface CheckpointInput {
6
+ session_id?: string;
7
+ sessionId?: string;
8
+ cwd?: string;
9
+ workspaceRoot?: string;
10
+ last_assistant_message?: string;
11
+ }
12
+ export interface CheckpointOptions {
13
+ home?: string;
14
+ now?: number;
15
+ }
16
+ export interface CheckpointReceipt {
17
+ version: 1;
18
+ sessionId: string;
19
+ cwd: string;
20
+ savedAt: string;
21
+ docPath: string;
22
+ diffPath: string;
23
+ incrementId?: string;
24
+ }
25
+ export interface CheckpointRequest {
26
+ version: 1;
27
+ sessionId: string;
28
+ cwd: string;
29
+ directory: string;
30
+ generation: string;
31
+ token: string;
32
+ slot: string;
33
+ summary?: string;
34
+ }
35
+ export declare function checkpointsDirectory(home?: string): string;
36
+ export declare function checkpointDirectory(input: CheckpointInput, opts?: CheckpointOptions): string | undefined;
37
+ export declare function readSessionCheckpoint(input: CheckpointInput, opts?: CheckpointOptions): CheckpointReceipt | undefined;
38
+ /** @internal Reserve a bounded worker slot and persist only scrubbed worker input. */
39
+ export declare function prepareSessionCheckpoint(input: CheckpointInput, opts?: CheckpointOptions): string | undefined;
40
+ /** Queue at most one capture per session/worktree and four per user. All errors fail open. */
41
+ export declare function queueSessionCheckpoint(input: CheckpointInput, opts?: CheckpointOptions): void;
42
+ /** Called by the supervisor on success, failure, or its hard deadline. */
43
+ export declare function cleanupCheckpointRequest(requestPath: string): void;
44
+ /** Worker entry: publish the receipt last, so interruption leaves the previous snapshot usable. */
45
+ export declare function runSessionCheckpoint(requestPath: string): Promise<void>;
46
+ //# sourceMappingURL=session-checkpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-checkpoint.d.ts","sourceRoot":"","sources":["../../../../src/core/session/session-checkpoint.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,sBAAsB,QAAa,CAAC;AACjD,eAAO,MAAM,4BAA4B,OAAQ,CAAC;AAClD,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAAG,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE;AAElE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAuBD,wBAAgB,oBAAoB,CAAC,IAAI,SAAe,GAAG,MAAM,CAEhE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,GAAE,iBAAsB,GAAG,MAAM,GAAG,SAAS,CAK5G;AAMD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,GAAE,iBAAsB,GAAG,iBAAiB,GAAG,SAAS,CAMzH;AAoDD,sFAAsF;AACtF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,GAAE,iBAAsB,GAAG,MAAM,GAAG,SAAS,CA0CjH;AAED,8FAA8F;AAC9F,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,GAAE,iBAAsB,GAAG,IAAI,CAUjG;AAED,0EAA0E;AAC1E,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAWlE;AAED,mGAAmG;AACnG,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC7E"}
@@ -0,0 +1,262 @@
1
+ /** Local, session-owned recovery snapshots. Scheduling never waits for Git or a model. */
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { createHash, randomUUID } from 'node:crypto';
6
+ import { spawn } from 'node:child_process';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { scrubSecrets } from './handoff-secret-scrub.js';
9
+ export const CHECKPOINT_INTERVAL_MS = 5 * 60000;
10
+ export const CHECKPOINT_WORKER_TIMEOUT_MS = 8000;
11
+ export const CHECKPOINT_LOCK_STALE_MS = 15000;
12
+ export const CHECKPOINT_MAX_WORKERS = 4;
13
+ /** Resolve symlinks and the nearest Git worktree without invoking Git in the hook. */
14
+ function checkpointIdentity(input) {
15
+ const sessionId = input.session_id ?? input.sessionId;
16
+ if (typeof sessionId !== 'string' || !/^[\w.-]{1,128}$/.test(sessionId) || sessionId.includes('..'))
17
+ return undefined;
18
+ const start = input.cwd ?? input.workspaceRoot ?? process.cwd();
19
+ if (typeof start !== 'string' || !path.isAbsolute(start))
20
+ return undefined;
21
+ try {
22
+ const original = fs.realpathSync(start);
23
+ if (!fs.statSync(original).isDirectory())
24
+ return undefined;
25
+ let current = original;
26
+ while (true) {
27
+ if (fs.existsSync(path.join(current, '.git')))
28
+ return { sessionId, cwd: current };
29
+ const parent = path.dirname(current);
30
+ if (parent === current)
31
+ break;
32
+ current = parent;
33
+ }
34
+ // Plain folders can still preserve a summary; no recursive project scan.
35
+ return { sessionId, cwd: original };
36
+ }
37
+ catch {
38
+ return undefined;
39
+ }
40
+ }
41
+ export function checkpointsDirectory(home = os.homedir()) {
42
+ return path.join(home, '.specweave', 'checkpoints');
43
+ }
44
+ export function checkpointDirectory(input, opts = {}) {
45
+ const identity = checkpointIdentity(input);
46
+ if (!identity)
47
+ return undefined;
48
+ const key = createHash('sha256').update(identity.cwd).update('\0').update(identity.sessionId).digest('hex');
49
+ return path.join(checkpointsDirectory(opts.home), key);
50
+ }
51
+ function readJson(file) {
52
+ try {
53
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
54
+ }
55
+ catch {
56
+ return undefined;
57
+ }
58
+ }
59
+ export function readSessionCheckpoint(input, opts = {}) {
60
+ const directory = checkpointDirectory(input, opts);
61
+ if (!directory)
62
+ return undefined;
63
+ const receipt = readJson(path.join(directory, 'current.json'));
64
+ return receipt?.version === 1 && typeof receipt.savedAt === 'string' && typeof receipt.docPath === 'string'
65
+ && typeof receipt.diffPath === 'string' && fs.existsSync(receipt.docPath) && fs.existsSync(receipt.diffPath) ? receipt : undefined;
66
+ }
67
+ function leaseMatches(file, token) {
68
+ const lease = readJson(path.join(file, `${token}.json`));
69
+ return lease?.token === token && typeof lease.ticket === 'number';
70
+ }
71
+ function releaseLease(file, token) {
72
+ // Every contender owns a distinct filename, including after stale recovery.
73
+ for (const suffix of ['json', 'ready']) {
74
+ try {
75
+ fs.unlinkSync(path.join(file, `${token}.${suffix}`));
76
+ }
77
+ catch { /* already gone */ }
78
+ }
79
+ try {
80
+ fs.rmdirSync(file);
81
+ }
82
+ catch { /* another contender still owns this directory */ }
83
+ }
84
+ function acquireLease(file, token, now) {
85
+ try {
86
+ // A short bakery election: announce choosing first, then select one greater
87
+ // than every existing ticket. Concurrent choosers cause a safe retry; the
88
+ // stable lowest (ticket, token) wins. No shared filename is ever unlinked.
89
+ fs.mkdirSync(file, { recursive: true, mode: 0o700 });
90
+ const own = path.join(file, `${token}.json`);
91
+ fs.writeFileSync(own, JSON.stringify({ token, at: now }), { flag: 'wx', mode: 0o600 });
92
+ const candidates = () => {
93
+ const leases = [];
94
+ for (const name of fs.readdirSync(file)) {
95
+ if (!name.endsWith('.json'))
96
+ continue;
97
+ const candidate = path.join(file, name);
98
+ try {
99
+ if (Date.now() - fs.statSync(candidate).mtimeMs > CHECKPOINT_LOCK_STALE_MS) {
100
+ fs.unlinkSync(candidate);
101
+ continue;
102
+ }
103
+ // A half-written contender must also prevent concurrent ownership.
104
+ leases.push(readJson(candidate) ?? { token: name, at: now });
105
+ }
106
+ catch { /* a contender finished between readdir and read */ }
107
+ }
108
+ return leases;
109
+ };
110
+ const ticket = 1 + Math.max(0, ...candidates().map((lease) => lease.ticket ?? 0));
111
+ const ready = path.join(file, `${token}.ready`);
112
+ fs.writeFileSync(ready, JSON.stringify({ token, at: now, ticket }), { flag: 'wx', mode: 0o600 });
113
+ fs.renameSync(ready, own);
114
+ if (candidates().some((lease) => lease.token !== token &&
115
+ (lease.ticket === undefined || lease.ticket < ticket || (lease.ticket === ticket && lease.token < token)))) {
116
+ releaseLease(file, token);
117
+ return false;
118
+ }
119
+ return true;
120
+ }
121
+ catch {
122
+ releaseLease(file, token);
123
+ return false;
124
+ }
125
+ }
126
+ /** @internal Reserve a bounded worker slot and persist only scrubbed worker input. */
127
+ export function prepareSessionCheckpoint(input, opts = {}) {
128
+ const identity = checkpointIdentity(input);
129
+ const directory = checkpointDirectory(input, opts);
130
+ if (!identity || !directory)
131
+ return;
132
+ const now = opts.now ?? Date.now();
133
+ const previous = readSessionCheckpoint(input, opts);
134
+ const savedAt = previous ? Date.parse(previous.savedAt) : NaN;
135
+ if (Number.isFinite(savedAt) && now >= savedAt && now - savedAt < CHECKPOINT_INTERVAL_MS)
136
+ return;
137
+ const token = randomUUID();
138
+ const lock = path.join(directory, 'pending.lock');
139
+ if (!acquireLease(lock, token, now))
140
+ return;
141
+ let slot;
142
+ let requestPath;
143
+ try {
144
+ // A worker can have finished between our initial receipt read and election.
145
+ const latest = readSessionCheckpoint(input, opts);
146
+ const latestAt = latest ? Date.parse(latest.savedAt) : NaN;
147
+ const afterLease = opts.now ?? Date.now();
148
+ if (Number.isFinite(latestAt) && afterLease >= latestAt && afterLease - latestAt < CHECKPOINT_INTERVAL_MS) {
149
+ releaseLease(lock, token);
150
+ return;
151
+ }
152
+ const slots = path.join(checkpointsDirectory(opts.home), '.workers');
153
+ for (let i = 0; i < CHECKPOINT_MAX_WORKERS; i++) {
154
+ const candidate = path.join(slots, `${i}.lock`);
155
+ if (acquireLease(candidate, token, now)) {
156
+ slot = candidate;
157
+ break;
158
+ }
159
+ }
160
+ if (!slot) {
161
+ releaseLease(lock, token);
162
+ return;
163
+ }
164
+ const generation = `snapshot-${now}-${token}`;
165
+ const summary = typeof input.last_assistant_message === 'string'
166
+ ? scrubSecrets(input.last_assistant_message.slice(0, 32000)).scrubbed : undefined;
167
+ const request = { version: 1, ...identity, directory, generation, token, slot, ...(summary ? { summary } : {}) };
168
+ requestPath = path.join(directory, `request-${token}.json`);
169
+ fs.writeFileSync(requestPath, JSON.stringify(request), { flag: 'wx', mode: 0o600 });
170
+ return requestPath;
171
+ }
172
+ catch {
173
+ if (requestPath)
174
+ cleanupCheckpointRequest(requestPath);
175
+ else {
176
+ releaseLease(lock, token);
177
+ if (slot)
178
+ releaseLease(slot, token);
179
+ }
180
+ }
181
+ }
182
+ /** Queue at most one capture per session/worktree and four per user. All errors fail open. */
183
+ export function queueSessionCheckpoint(input, opts = {}) {
184
+ const requestPath = prepareSessionCheckpoint(input, opts);
185
+ if (!requestPath)
186
+ return;
187
+ try {
188
+ const worker = fileURLToPath(new URL('./checkpoint-worker.js', import.meta.url));
189
+ if (!fs.existsSync(worker))
190
+ throw new Error('checkpoint worker is not built');
191
+ const child = spawn(process.execPath, [worker, requestPath], { detached: true, stdio: 'ignore', windowsHide: true });
192
+ child.once('error', () => cleanupCheckpointRequest(requestPath));
193
+ child.unref();
194
+ }
195
+ catch {
196
+ cleanupCheckpointRequest(requestPath);
197
+ }
198
+ }
199
+ /** Called by the supervisor on success, failure, or its hard deadline. */
200
+ export function cleanupCheckpointRequest(requestPath) {
201
+ const request = readJson(requestPath);
202
+ if (!request || request.version !== 1)
203
+ return;
204
+ releaseLease(path.join(request.directory, 'pending.lock'), request.token);
205
+ releaseLease(request.slot, request.token);
206
+ const generationDir = path.join(request.directory, request.generation);
207
+ const receipt = readJson(path.join(request.directory, 'current.json'));
208
+ if (!receipt || path.dirname(receipt.docPath) !== generationDir) {
209
+ try {
210
+ fs.rmSync(generationDir, { recursive: true, force: true });
211
+ }
212
+ catch { /* best effort */ }
213
+ }
214
+ try {
215
+ fs.unlinkSync(requestPath);
216
+ }
217
+ catch { /* already removed */ }
218
+ }
219
+ /** Worker entry: publish the receipt last, so interruption leaves the previous snapshot usable. */
220
+ export async function runSessionCheckpoint(requestPath) {
221
+ const request = readJson(requestPath);
222
+ if (!request || request.version !== 1 || !leaseMatches(path.join(request.directory, 'pending.lock'), request.token))
223
+ return;
224
+ const generationDir = path.join(request.directory, request.generation);
225
+ const previous = readJson(path.join(request.directory, 'current.json'));
226
+ let published = false;
227
+ try {
228
+ fs.mkdirSync(generationDir, { mode: 0o700 });
229
+ const { buildWorkHandoff } = await import('./work-handoff.js');
230
+ const result = await buildWorkHandoff(request.cwd, {
231
+ checkpoint: true, checkpointRoot: request.cwd, push: false, keepClaims: true,
232
+ out: path.join(generationDir, 'handoff.md'), reason: 'automatic local session checkpoint', summary: request.summary,
233
+ });
234
+ if (!fs.statSync(result.docPath).size || !fs.existsSync(result.diffPath))
235
+ throw new Error('incomplete checkpoint');
236
+ if (!leaseMatches(path.join(request.directory, 'pending.lock'), request.token))
237
+ return;
238
+ const receipt = {
239
+ version: 1, sessionId: request.sessionId, cwd: request.cwd, savedAt: new Date().toISOString(),
240
+ docPath: result.docPath, diffPath: result.diffPath, ...(result.incrementId ? { incrementId: result.incrementId } : {}),
241
+ };
242
+ const temporary = path.join(request.directory, `current-${request.token}.json`);
243
+ fs.writeFileSync(temporary, JSON.stringify(receipt, null, 2) + '\n', { flag: 'wx', mode: 0o600 });
244
+ fs.renameSync(temporary, path.join(request.directory, 'current.json'));
245
+ published = true;
246
+ // Retain the preceding generation for recovery; prune only our old snapshot directories.
247
+ const keep = new Set([generationDir, previous && path.dirname(previous.docPath)]);
248
+ for (const name of fs.readdirSync(request.directory)) {
249
+ const file = path.join(request.directory, name);
250
+ if (name.startsWith('snapshot-') && !keep.has(file))
251
+ fs.rmSync(file, { recursive: true, force: true });
252
+ }
253
+ }
254
+ catch {
255
+ if (!published)
256
+ try {
257
+ fs.rmSync(generationDir, { recursive: true, force: true });
258
+ }
259
+ catch { /* preserve existing receipt */ }
260
+ }
261
+ }
262
+ //# sourceMappingURL=session-checkpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-checkpoint.js","sourceRoot":"","sources":["../../../../src/core/session/session-checkpoint.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,GAAG,KAAM,CAAC;AACjD,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAK,CAAC;AAClD,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAM,CAAC;AAC/C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAkCxC,sFAAsF;AACtF,SAAS,kBAAkB,CAAC,KAAsB;IAChD,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,CAAC;IACtD,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACtH,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAChE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3E,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE;YAAE,OAAO,SAAS,CAAC;QAC3D,IAAI,OAAO,GAAG,QAAQ,CAAC;QACvB,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;YAClF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACrC,IAAI,MAAM,KAAK,OAAO;gBAAE,MAAM;YAC9B,OAAO,GAAG,MAAM,CAAC;QACnB,CAAC;QACD,yEAAyE;QACzE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,SAAS,CAAC;IAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE;IACtD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAsB,EAAE,OAA0B,EAAE;IACtF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5G,OAAO,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,QAAQ,CAAI,IAAY;IAC/B,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,SAAS,CAAC;IAAC,CAAC;AAC5F,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAsB,EAAE,OAA0B,EAAE;IACxF,MAAM,SAAS,GAAG,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,OAAO,GAAG,QAAQ,CAAoB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;IAClF,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ;WACtG,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACvI,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,KAAa;IAC/C,MAAM,KAAK,GAAG,QAAQ,CAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC;IAChE,OAAO,KAAK,EAAE,KAAK,KAAK,KAAK,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC;AACpE,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,KAAa;IAC/C,4EAA4E;IAC5E,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC;YAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,CAAC;QAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iDAAiD,CAAC,CAAC;AACzF,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,KAAa,EAAE,GAAW;IAC5D,IAAI,CAAC;QACH,4EAA4E;QAC5E,0EAA0E;QAC1E,2EAA2E;QAC3E,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC;QAC7C,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACvF,MAAM,UAAU,GAAG,GAAY,EAAE;YAC/B,MAAM,MAAM,GAAY,EAAE,CAAC;YAC3B,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;oBAAE,SAAS;gBACtC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACxC,IAAI,CAAC;oBACH,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,wBAAwB,EAAE,CAAC;wBAC3E,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;wBACzB,SAAS;oBACX,CAAC;oBACD,mEAAmE;oBACnE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAQ,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;gBACtE,CAAC;gBAAC,MAAM,CAAC,CAAC,mDAAmD,CAAC,CAAC;YACjE,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC;QACF,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;QAClF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC;QAChD,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACjG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC1B,IAAI,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,KAAK;YACpD,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7G,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC1B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AACtD,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,wBAAwB,CAAC,KAAsB,EAAE,OAA0B,EAAE;IAC3F,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS;QAAE,OAAO;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9D,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,OAAO,IAAI,GAAG,GAAG,OAAO,GAAG,sBAAsB;QAAE,OAAO;IACjG,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAClD,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC;QAAE,OAAO;IAC5C,IAAI,IAAwB,CAAC;IAC7B,IAAI,WAA+B,CAAC;IACpC,IAAI,CAAC;QACH,4EAA4E;QAC5E,MAAM,MAAM,GAAG,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1C,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,UAAU,IAAI,QAAQ,IAAI,UAAU,GAAG,QAAQ,GAAG,sBAAsB,EAAE,CAAC;YAC1G,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC1B,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC;QACrE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,sBAAsB,EAAE,CAAC,EAAE,EAAE,CAAC;YAChD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAChD,IAAI,YAAY,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;gBAAC,IAAI,GAAG,SAAS,CAAC;gBAAC,MAAM;YAAC,CAAC;QACvE,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAAC,OAAO;QAAC,CAAC;QACjD,MAAM,UAAU,GAAG,YAAY,GAAG,IAAI,KAAK,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC,sBAAsB,KAAK,QAAQ;YAC9D,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,EAAE,KAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,MAAM,OAAO,GAAsB,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QACpI,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC;QAC5D,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACpF,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,WAAW;YAAE,wBAAwB,CAAC,WAAW,CAAC,CAAC;aAClD,CAAC;YACJ,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC1B,IAAI,IAAI;gBAAE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;AACH,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,sBAAsB,CAAC,KAAsB,EAAE,OAA0B,EAAE;IACzF,MAAM,WAAW,GAAG,wBAAwB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,WAAW;QAAE,OAAO;IACzB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,wBAAwB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACjF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC9E,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACrH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,wBAAwB,CAAC,WAAW,CAAC,CAAC,CAAC;QACjE,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QAAC,wBAAwB,CAAC,WAAW,CAAC,CAAC;IAAC,CAAC;AACpD,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,wBAAwB,CAAC,WAAmB;IAC1D,MAAM,OAAO,GAAG,QAAQ,CAAoB,WAAW,CAAC,CAAC;IACzD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,KAAK,CAAC;QAAE,OAAO;IAC9C,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1E,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG,QAAQ,CAAoB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;IAC1F,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,aAAa,EAAE,CAAC;QAChE,IAAI,CAAC;YAAC,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACjG,CAAC;IACD,IAAI,CAAC;QAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC;AACrE,CAAC;AAED,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,WAAmB;IAC5D,MAAM,OAAO,GAAG,QAAQ,CAAoB,WAAW,CAAC,CAAC;IACzD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO;IAC5H,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,QAAQ,CAAoB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;IAC3F,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7C,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE;YACjD,UAAU,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI;YAC5E,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,oCAAoC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO;SACpH,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACnH,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QACvF,MAAM,OAAO,GAAsB;YACjC,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC7F,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvH,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC;QAChF,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClG,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;QACvE,SAAS,GAAG,IAAI,CAAC;QACjB,yFAAyF;QACzF,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAClF,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAChD,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACzG,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC;gBAAC,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,+BAA+B,CAAC,CAAC;IAC/H,CAAC;AACH,CAAC"}
@@ -1,27 +1,8 @@
1
- /**
2
- * Auto-handoff near the usage limit.
3
- *
4
- * Neither Claude Code nor Codex tells a hook how much of the plan's limit is
5
- * left, so the guard reads it where each tool does expose it:
6
- *
7
- * Claude Code the status line command receives `rate_limits.five_hour` /
8
- * `seven_day` / `spend_limit` `.used_percentage` (Pro and Max,
9
- * after the first reply). `specweave statusline` saves them per
10
- * session under ~/.specweave/usage/.
11
- * Codex every turn appends a `token_count` event with
12
- * `rate_limits.primary` / `secondary` `.used_percent` to the
13
- * rollout file the hook's `transcript_path` points at.
14
- *
15
- * The Stop hook (`specweave usage-guard`) then blocks the stop once per
16
- * session and usage window when any window is at or past the threshold, and
17
- * the agent runs `specweave handoff`. Under the threshold it prints `{}`: no
18
- * tokens, no files.
19
- *
20
- * @module core/session/usage-guard
1
+ /** Usage readings for display only. Automatic saving is quota-independent.
2
+ * A plan allowance may be exhausted while paid credits or a proxy still work.
3
+ * Never turn these advisory readings into a stop/continue instruction.
21
4
  */
22
5
  export declare const DEFAULT_THRESHOLD = 90;
23
- /** The shortest usage window; a session that hit the limit may hit it again after this. */
24
- export declare const LIMIT_HIT_REARM_MS: number;
25
6
  export interface UsageWindow {
26
7
  /** "5-hour", "weekly", "spend" or "<n>-minute". */
27
8
  name: string;
@@ -97,32 +78,14 @@ export declare function latestClaudeReading(home?: string): UsageReading | undef
97
78
  export declare function latestCodexReading(home?: string): UsageReading | undefined;
98
79
  /** The fullest window that has not reset yet. */
99
80
  export declare function fullest(windows: UsageWindow[], now?: number): UsageWindow | undefined;
100
- export declare function handoffInstruction(w: UsageWindow): string;
81
+ /** Retained for integrations that imported the old guard. Never steer the model. */
101
82
  export interface GuardInput {
102
83
  session_id?: string;
103
84
  transcript_path?: string;
104
85
  stop_hook_active?: boolean;
105
86
  }
106
- export interface GuardOutput {
107
- /** Codex: block the stop; `reason` goes to the model. */
108
- decision?: 'block';
109
- reason?: string;
110
- /**
111
- * Claude Code: non-error context for the model; the turn continues so it can
112
- * act on it. A `decision: "block"` would show the user "Stop hook error".
113
- */
114
- hookSpecificOutput?: {
115
- hookEventName: 'Stop';
116
- additionalContext: string;
117
- };
118
- /** Claude Code: a line shown to the user. */
119
- systemMessage?: string;
120
- }
121
- /**
122
- * Stop-hook output. The first time a session is at or past the threshold in a
123
- * usage window, it keeps the agent going so it hands off; otherwise `{}`.
124
- */
125
- export declare function usageGuard(input: GuardInput, opts?: {
87
+ export type GuardOutput = Record<string, never>;
88
+ export declare function usageGuard(_input: GuardInput, _opts?: {
126
89
  home?: string;
127
90
  now?: number;
128
91
  env?: NodeJS.ProcessEnv;
@@ -130,30 +93,4 @@ export declare function usageGuard(input: GuardInput, opts?: {
130
93
  }): GuardOutput;
131
94
  /** One short status line: the fullest windows, e.g. "5-hour 42% · weekly 12%". */
132
95
  export declare function usageSummary(windows: UsageWindow[]): string;
133
- /**
134
- * When a turn fails on the rate limit, Claude Code and Grok Build fire
135
- * `StopFailure` with `error: "rate_limit"`. It is the backstop for a session
136
- * that ran out before the Stop hook could ask for a handoff, and the only
137
- * signal Grok Build gives (it shows no usage percentage). Grok's hook input is
138
- * camelCase (`sessionId`, `workspaceRoot`), Claude Code's snake_case.
139
- */
140
- export interface LimitHitInput {
141
- sessionId?: string;
142
- session_id?: string;
143
- cwd?: string;
144
- workspaceRoot?: string;
145
- error?: string;
146
- }
147
- /**
148
- * Where a rate-limited turn should hand off, or undefined when it should not:
149
- * auto-handoff is off, the failure is not a rate limit, the session already
150
- * already wrote one in the last five hours, or the directory is not a
151
- * SpecWeave project. Writes its marker before returning, so a retry storm
152
- * hands off once. The marker is separate from the Stop hook's: a session that
153
- * was asked to hand off at 90% and still ran out writes a fresh handoff.
154
- */
155
- export declare function limitHitTarget(input: LimitHitInput, opts?: {
156
- home?: string;
157
- now?: number;
158
- }): string | undefined;
159
96
  //# sourceMappingURL=usage-guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usage-guard.d.ts","sourceRoot":"","sources":["../../../../src/core/session/usage-guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usage-guard.d.ts","sourceRoot":"","sources":["../../../../src/core/session/usage-guard.ts"],"names":[],"mappings":"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"}