@tyroneross/bookmark 0.1.0 → 0.3.2
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/.claude-plugin/marketplace.json +29 -12
- package/.claude-plugin/plugin.json +12 -8
- package/.codex-plugin/plugin.json +31 -0
- package/.mcp.json +8 -0
- package/CLAUDE.md +83 -49
- package/LICENSE +202 -21
- package/README.md +150 -57
- package/agents/snapshot-analyst.md +1 -1
- package/commands/bookmark.md +29 -0
- package/commands/feedback.md +37 -0
- package/commands/restore.md +14 -4
- package/commands/snapshot.md +19 -7
- package/commands/status.md +1 -1
- package/dist/cli/index.js +627 -87
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/snapshot-resolution.d.ts +15 -0
- package/dist/cli/snapshot-resolution.d.ts.map +1 -0
- package/dist/cli/snapshot-resolution.js +21 -0
- package/dist/cli/snapshot-resolution.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +149 -23
- package/dist/config.js.map +1 -1
- package/dist/context/freshness.d.ts +3 -0
- package/dist/context/freshness.d.ts.map +1 -0
- package/dist/context/freshness.js +29 -0
- package/dist/context/freshness.js.map +1 -0
- package/dist/context/handoff-prompt.d.ts +12 -0
- package/dist/context/handoff-prompt.d.ts.map +1 -0
- package/dist/context/handoff-prompt.js +29 -0
- package/dist/context/handoff-prompt.js.map +1 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +9 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +85 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +97 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +304 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/registry.d.ts +40 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +134 -0
- package/dist/registry.js.map +1 -0
- package/dist/restore/index.d.ts +13 -1
- package/dist/restore/index.d.ts.map +1 -1
- package/dist/restore/index.js +163 -43
- package/dist/restore/index.js.map +1 -1
- package/dist/setup/auto-setup.d.ts +8 -2
- package/dist/setup/auto-setup.d.ts.map +1 -1
- package/dist/setup/auto-setup.js +287 -67
- package/dist/setup/auto-setup.js.map +1 -1
- package/dist/setup/configure-hooks.d.ts +14 -3
- package/dist/setup/configure-hooks.d.ts.map +1 -1
- package/dist/setup/configure-hooks.js +156 -24
- package/dist/setup/configure-hooks.js.map +1 -1
- package/dist/snapshot/capture.d.ts +13 -9
- package/dist/snapshot/capture.d.ts.map +1 -1
- package/dist/snapshot/capture.js +42 -105
- package/dist/snapshot/capture.js.map +1 -1
- package/dist/snapshot/compress.d.ts +3 -3
- package/dist/snapshot/compress.d.ts.map +1 -1
- package/dist/snapshot/compress.js +35 -67
- package/dist/snapshot/compress.js.map +1 -1
- package/dist/snapshot/storage.d.ts +7 -0
- package/dist/snapshot/storage.d.ts.map +1 -1
- package/dist/snapshot/storage.js +31 -6
- package/dist/snapshot/storage.js.map +1 -1
- package/dist/threshold/state.d.ts +5 -0
- package/dist/threshold/state.d.ts.map +1 -1
- package/dist/threshold/state.js +28 -2
- package/dist/threshold/state.js.map +1 -1
- package/dist/threshold/token-usage.d.ts +29 -0
- package/dist/threshold/token-usage.d.ts.map +1 -0
- package/dist/threshold/token-usage.js +128 -0
- package/dist/threshold/token-usage.js.map +1 -0
- package/dist/trails/identity.d.ts +76 -0
- package/dist/trails/identity.d.ts.map +1 -0
- package/dist/trails/identity.js +117 -0
- package/dist/trails/identity.js.map +1 -0
- package/dist/trails/reader.d.ts +7 -0
- package/dist/trails/reader.d.ts.map +1 -0
- package/dist/trails/reader.js +22 -0
- package/dist/trails/reader.js.map +1 -0
- package/dist/trails/writer.d.ts +9 -0
- package/dist/trails/writer.d.ts.map +1 -0
- package/dist/trails/writer.js +110 -0
- package/dist/trails/writer.js.map +1 -0
- package/dist/transcript/extractor.d.ts +17 -4
- package/dist/transcript/extractor.d.ts.map +1 -1
- package/dist/transcript/extractor.js +141 -174
- package/dist/transcript/extractor.js.map +1 -1
- package/dist/transcript/parser.d.ts.map +1 -1
- package/dist/transcript/parser.js +69 -7
- package/dist/transcript/parser.js.map +1 -1
- package/dist/types.d.ts +37 -38
- package/dist/types.d.ts.map +1 -1
- package/hooks/hooks.json +12 -14
- package/package.json +13 -10
- package/scripts/install-plugin.sh +48 -0
- package/skills/context-continuity/SKILL.md +43 -24
- package/commands/activate.md +0 -20
- package/dist/transcript/estimator.d.ts +0 -20
- package/dist/transcript/estimator.d.ts.map +0 -1
- package/dist/transcript/estimator.js +0 -95
- package/dist/transcript/estimator.js.map +0 -1
|
@@ -1,93 +1,61 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
1
2
|
/**
|
|
2
|
-
* Compress a
|
|
3
|
-
*
|
|
4
|
-
* This becomes LATEST.md —
|
|
3
|
+
* Compress a snapshot into markdown — now focused on file tracking data only.
|
|
4
|
+
* Intent/decisions/progress come from Claude-written bookmark.context.md, not this.
|
|
5
|
+
* This becomes LATEST.md — supplementary file change data.
|
|
5
6
|
*/
|
|
6
7
|
export function compressToMarkdown(snapshot) {
|
|
7
8
|
const lines = [];
|
|
8
|
-
// Header
|
|
9
9
|
const date = new Date(snapshot.timestamp).toISOString();
|
|
10
|
-
lines.push(`#
|
|
10
|
+
lines.push(`# File Changes - ${date}`);
|
|
11
11
|
lines.push('');
|
|
12
|
-
lines.push(`>
|
|
13
|
-
lines.push(`> Context remaining: ${Math.round(snapshot.context_remaining_pct * 100)}% (~${snapshot.token_estimate.toLocaleString()} tokens used)`);
|
|
12
|
+
lines.push(`> ${snapshot.snapshot_id} | cycle ${snapshot.compaction_cycle} | ${snapshot.trigger}`);
|
|
14
13
|
lines.push('');
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
lines.push('
|
|
18
|
-
lines.push(truncate(snapshot.current_status, 300));
|
|
14
|
+
if (snapshot.context_used_pct !== undefined && snapshot.context_limit_tokens) {
|
|
15
|
+
const usedPct = Math.round(snapshot.context_used_pct * 100);
|
|
16
|
+
lines.push(`> Context: ${usedPct}% used | ${snapshot.model ?? 'unknown model'} | limit ${formatTokens(snapshot.context_limit_tokens)}`);
|
|
19
17
|
lines.push('');
|
|
20
18
|
}
|
|
21
|
-
//
|
|
22
|
-
if (snapshot.decisions.length > 0) {
|
|
23
|
-
lines.push('## Decisions Made');
|
|
24
|
-
for (const d of snapshot.decisions.slice(0, 8)) {
|
|
25
|
-
const rationale = d.rationale ? ` (${truncate(d.rationale, 80)})` : '';
|
|
26
|
-
lines.push(`- ${truncate(d.description, 150)}${rationale}`);
|
|
27
|
-
}
|
|
28
|
-
if (snapshot.decisions.length > 8) {
|
|
29
|
-
lines.push(`- ...and ${snapshot.decisions.length - 8} more`);
|
|
30
|
-
}
|
|
31
|
-
lines.push('');
|
|
32
|
-
}
|
|
33
|
-
// Files Changed
|
|
19
|
+
// Files Changed — the core value of this snapshot
|
|
34
20
|
if (snapshot.files_changed.length > 0) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
lines.push(`-
|
|
42
|
-
}
|
|
43
|
-
lines.push('');
|
|
44
|
-
}
|
|
45
|
-
// Open Items
|
|
46
|
-
if (snapshot.open_items.length > 0) {
|
|
47
|
-
lines.push('## Open Items');
|
|
48
|
-
for (const item of snapshot.open_items.slice(0, 8)) {
|
|
49
|
-
const priority = item.priority === 'high' ? ' [HIGH]' : item.priority === 'medium' ? ' [MED]' : '';
|
|
50
|
-
lines.push(`- [ ] ${truncate(item.description, 150)}${priority}`);
|
|
21
|
+
const totalLines = snapshot.files_changed.reduce((sum, f) => sum + (f.lines_changed ?? 0), 0);
|
|
22
|
+
lines.push(`## Files Changed (${snapshot.files_changed.length} files, ~${totalLines} lines)`);
|
|
23
|
+
for (const f of snapshot.files_changed.slice(0, 10)) {
|
|
24
|
+
const shortPath = shortenPath(f.path, snapshot.project_path);
|
|
25
|
+
const ops = f.operations.join('/');
|
|
26
|
+
const lc = f.lines_changed ? ` ~${f.lines_changed}L` : '';
|
|
27
|
+
lines.push(`- \`${shortPath}\` (${ops}${lc})`);
|
|
51
28
|
}
|
|
52
|
-
if (snapshot.
|
|
53
|
-
lines.push(`-
|
|
29
|
+
if (snapshot.files_changed.length > 10) {
|
|
30
|
+
lines.push(`- ...+${snapshot.files_changed.length - 10} more`);
|
|
54
31
|
}
|
|
55
32
|
lines.push('');
|
|
56
33
|
}
|
|
57
|
-
//
|
|
58
|
-
if (snapshot.unknowns.length > 0) {
|
|
59
|
-
lines.push('## Unknowns / Blockers');
|
|
60
|
-
for (const u of snapshot.unknowns.slice(0, 5)) {
|
|
61
|
-
lines.push(`- ${truncate(u, 150)}`);
|
|
62
|
-
}
|
|
63
|
-
lines.push('');
|
|
64
|
-
}
|
|
65
|
-
// Errors (only unresolved)
|
|
66
|
-
const unresolvedErrors = snapshot.errors_encountered.filter(e => !e.resolved);
|
|
67
|
-
if (unresolvedErrors.length > 0) {
|
|
68
|
-
lines.push('## Unresolved Errors');
|
|
69
|
-
for (const e of unresolvedErrors.slice(0, 3)) {
|
|
70
|
-
const tool = e.tool ? ` (${e.tool})` : '';
|
|
71
|
-
lines.push(`- ${truncate(e.message, 150)}${tool}`);
|
|
72
|
-
}
|
|
73
|
-
lines.push('');
|
|
74
|
-
}
|
|
75
|
-
// Tool Usage Summary (compact)
|
|
34
|
+
// Tool usage — single line summary
|
|
76
35
|
if (Object.keys(snapshot.tools_summary).length > 0) {
|
|
77
36
|
const topTools = Object.entries(snapshot.tools_summary)
|
|
78
37
|
.sort((a, b) => b[1] - a[1])
|
|
79
38
|
.slice(0, 5)
|
|
80
39
|
.map(([tool, count]) => `${tool}: ${count}`)
|
|
81
40
|
.join(', ');
|
|
82
|
-
lines.push(`>
|
|
41
|
+
lines.push(`> Tools: ${topTools}`);
|
|
83
42
|
lines.push('');
|
|
84
43
|
}
|
|
85
|
-
lines.push('*bookmark —
|
|
44
|
+
lines.push('*bookmark — file tracking snapshot*');
|
|
86
45
|
return lines.join('\n');
|
|
87
46
|
}
|
|
88
|
-
function
|
|
89
|
-
if (
|
|
90
|
-
return
|
|
91
|
-
|
|
47
|
+
function formatTokens(value) {
|
|
48
|
+
if (value >= 1_000_000)
|
|
49
|
+
return `${value / 1_000_000}M`;
|
|
50
|
+
if (value >= 1_000)
|
|
51
|
+
return `${Math.round(value / 1_000)}K`;
|
|
52
|
+
return String(value);
|
|
53
|
+
}
|
|
54
|
+
/** Shorten a file path relative to project root */
|
|
55
|
+
function shortenPath(filePath, projectPath) {
|
|
56
|
+
if (projectPath && filePath.startsWith(projectPath)) {
|
|
57
|
+
return filePath.slice(projectPath.length).replace(/^\//, '');
|
|
58
|
+
}
|
|
59
|
+
return basename(filePath);
|
|
92
60
|
}
|
|
93
61
|
//# sourceMappingURL=compress.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compress.js","sourceRoot":"","sources":["../../src/snapshot/compress.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compress.js","sourceRoot":"","sources":["../../src/snapshot/compress.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Snapshot, SnapshotEntry } from '../types.js';
|
|
2
|
+
export declare function generateSnapshotId(): string;
|
|
2
3
|
export declare function getSnapshotsDir(storagePath: string): string;
|
|
3
4
|
export declare function getLatestPath(storagePath: string): string;
|
|
4
5
|
export declare function getIndexPath(storagePath: string): string;
|
|
@@ -10,4 +11,10 @@ export declare function listSnapshots(storagePath: string, limit?: number): Snap
|
|
|
10
11
|
export declare function writeLatestMd(storagePath: string, content: string): void;
|
|
11
12
|
export declare function readLatestMd(storagePath: string): string | null;
|
|
12
13
|
export declare function getSnapshotCount(storagePath: string): number;
|
|
14
|
+
/**
|
|
15
|
+
* Walk the snapshot chain via prior_snapshot_id links.
|
|
16
|
+
* Returns snapshots in chronological order (oldest first).
|
|
17
|
+
* Caps at maxDepth to prevent runaway chains.
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadSnapshotChain(storagePath: string, maxDepth?: number): Snapshot[];
|
|
13
20
|
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/snapshot/storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/snapshot/storage.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAiB,aAAa,EAAE,MAAM,aAAa,CAAC;AAI1E,wBAAgB,kBAAkB,IAAI,MAAM,CAQ3C;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAW3D;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAW7E;AAED,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAcrF;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAMvE;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,aAAa,EAAE,CAI9E;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAGxE;AAED,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ/D;AA+DD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQ5D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,QAAQ,EAAE,CAkB/E"}
|
package/dist/snapshot/storage.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
const INDEX_VERSION = '1.0.0';
|
|
4
|
-
function generateSnapshotId() {
|
|
5
|
+
export function generateSnapshotId() {
|
|
5
6
|
const now = new Date();
|
|
6
7
|
const pad = (n, len = 2) => String(n).padStart(len, '0');
|
|
7
8
|
const date = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`;
|
|
8
9
|
const time = `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
|
9
|
-
|
|
10
|
+
const milliseconds = pad(now.getMilliseconds(), 3);
|
|
11
|
+
const entropy = randomBytes(2).toString('hex');
|
|
12
|
+
return `SNAP_${date}_${time}_${milliseconds}_${entropy}`;
|
|
10
13
|
}
|
|
11
14
|
export function getSnapshotsDir(storagePath) {
|
|
12
15
|
return join(storagePath, 'snapshots');
|
|
@@ -40,7 +43,7 @@ export function storeSnapshot(storagePath, snapshot) {
|
|
|
40
43
|
}
|
|
41
44
|
export function loadSnapshot(storagePath, snapshotId) {
|
|
42
45
|
// Validate snapshot ID format to prevent path traversal
|
|
43
|
-
if (!/^SNAP_\d{8}_\d{6}
|
|
46
|
+
if (!/^SNAP_\d{8}_\d{6}(?:_\d{3}_[a-f0-9]{4})?$/.test(snapshotId)) {
|
|
44
47
|
return null;
|
|
45
48
|
}
|
|
46
49
|
const snapshotPath = join(getSnapshotsDir(storagePath), `${snapshotId}.json`);
|
|
@@ -112,11 +115,9 @@ function updateIndex(storagePath, snapshot) {
|
|
|
112
115
|
timestamp: snapshot.timestamp,
|
|
113
116
|
trigger: snapshot.trigger,
|
|
114
117
|
compaction_cycle: snapshot.compaction_cycle,
|
|
118
|
+
files_changed_count: snapshot.files_changed.length,
|
|
115
119
|
context_remaining_pct: snapshot.context_remaining_pct,
|
|
116
120
|
token_estimate: snapshot.token_estimate,
|
|
117
|
-
decisions_count: snapshot.decisions.length,
|
|
118
|
-
files_changed_count: snapshot.files_changed.length,
|
|
119
|
-
open_items_count: snapshot.open_items.length,
|
|
120
121
|
};
|
|
121
122
|
// Add to front (most recent first)
|
|
122
123
|
index.snapshots.unshift(entry);
|
|
@@ -148,4 +149,28 @@ export function getSnapshotCount(storagePath) {
|
|
|
148
149
|
return 0;
|
|
149
150
|
}
|
|
150
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Walk the snapshot chain via prior_snapshot_id links.
|
|
154
|
+
* Returns snapshots in chronological order (oldest first).
|
|
155
|
+
* Caps at maxDepth to prevent runaway chains.
|
|
156
|
+
*/
|
|
157
|
+
export function loadSnapshotChain(storagePath, maxDepth = 5) {
|
|
158
|
+
const latest = loadLatestSnapshot(storagePath);
|
|
159
|
+
if (!latest)
|
|
160
|
+
return [];
|
|
161
|
+
const chain = [latest];
|
|
162
|
+
const seen = new Set([latest.snapshot_id]);
|
|
163
|
+
let current = latest;
|
|
164
|
+
while (current.prior_snapshot_id && chain.length < maxDepth) {
|
|
165
|
+
if (seen.has(current.prior_snapshot_id))
|
|
166
|
+
break; // Prevent cycles
|
|
167
|
+
const prior = loadSnapshot(storagePath, current.prior_snapshot_id);
|
|
168
|
+
if (!prior)
|
|
169
|
+
break;
|
|
170
|
+
seen.add(prior.snapshot_id);
|
|
171
|
+
chain.push(prior);
|
|
172
|
+
current = prior;
|
|
173
|
+
}
|
|
174
|
+
return chain.reverse(); // Chronological: oldest first
|
|
175
|
+
}
|
|
151
176
|
//# sourceMappingURL=storage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/snapshot/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC1F,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,aAAa,GAAG,OAAO,CAAC;AAE9B,
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/snapshot/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,4 +5,9 @@ export declare function saveState(storagePath: string, state: BookmarkState): vo
|
|
|
5
5
|
export declare function incrementCompaction(state: BookmarkState, thresholds: number[]): BookmarkState;
|
|
6
6
|
export declare function resetForNewSession(state: BookmarkState, sessionId: string, thresholds: number[]): BookmarkState;
|
|
7
7
|
export declare function updateSnapshotTime(state: BookmarkState): BookmarkState;
|
|
8
|
+
/**
|
|
9
|
+
* Increment snapshots_taken for the current session in session_history.
|
|
10
|
+
* Previously this was never called — snapshots_taken was always 0.
|
|
11
|
+
*/
|
|
12
|
+
export declare function incrementSnapshotCount(state: BookmarkState): BookmarkState;
|
|
8
13
|
//# sourceMappingURL=state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/threshold/state.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;AAkB/D,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAY5D;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAOzE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/threshold/state.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;AAkB/D,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAY5D;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAOzE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,aAAa,CAY7F;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,aAAa,CAgC/G;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAMtE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAW1E"}
|
package/dist/threshold/state.js
CHANGED
|
@@ -10,7 +10,7 @@ function defaultState() {
|
|
|
10
10
|
current_threshold: 0.20,
|
|
11
11
|
last_snapshot_time: 0,
|
|
12
12
|
last_event_time: 0,
|
|
13
|
-
snapshot_interval_minutes:
|
|
13
|
+
snapshot_interval_minutes: 5,
|
|
14
14
|
session_history: [],
|
|
15
15
|
};
|
|
16
16
|
}
|
|
@@ -46,6 +46,10 @@ export function incrementCompaction(state, thresholds) {
|
|
|
46
46
|
...state,
|
|
47
47
|
compaction_count: newCount,
|
|
48
48
|
current_threshold: thresholds[thresholdIndex],
|
|
49
|
+
// The first prompt after compaction can still see the final pre-compaction
|
|
50
|
+
// usage record. Keep handled thresholds until a lower usage record proves
|
|
51
|
+
// the new context cycle is active.
|
|
52
|
+
token_thresholds_triggered: state.token_thresholds_triggered,
|
|
49
53
|
};
|
|
50
54
|
}
|
|
51
55
|
export function resetForNewSession(state, sessionId, thresholds) {
|
|
@@ -57,7 +61,7 @@ export function resetForNewSession(state, sessionId, thresholds) {
|
|
|
57
61
|
started: state.session_history.find(s => s.session_id === state.session_id)?.started ?? state.last_event_time,
|
|
58
62
|
ended: Date.now(),
|
|
59
63
|
compaction_count: state.compaction_count,
|
|
60
|
-
snapshots_taken: 0, //
|
|
64
|
+
snapshots_taken: 0, // Incremented by incrementSnapshotCount on each capture
|
|
61
65
|
};
|
|
62
66
|
history.unshift(currentEntry);
|
|
63
67
|
if (history.length > MAX_SESSION_HISTORY) {
|
|
@@ -71,6 +75,13 @@ export function resetForNewSession(state, sessionId, thresholds) {
|
|
|
71
75
|
current_threshold: thresholds[0],
|
|
72
76
|
last_event_time: Date.now(),
|
|
73
77
|
session_history: history,
|
|
78
|
+
token_thresholds_triggered: [],
|
|
79
|
+
latest_model: undefined,
|
|
80
|
+
latest_context_tokens: undefined,
|
|
81
|
+
latest_context_limit_tokens: undefined,
|
|
82
|
+
latest_context_used_pct: undefined,
|
|
83
|
+
latest_context_observed_at: undefined,
|
|
84
|
+
unknown_context_limit_notified_model: undefined,
|
|
74
85
|
};
|
|
75
86
|
}
|
|
76
87
|
export function updateSnapshotTime(state) {
|
|
@@ -80,4 +91,19 @@ export function updateSnapshotTime(state) {
|
|
|
80
91
|
last_event_time: Date.now(),
|
|
81
92
|
};
|
|
82
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Increment snapshots_taken for the current session in session_history.
|
|
96
|
+
* Previously this was never called — snapshots_taken was always 0.
|
|
97
|
+
*/
|
|
98
|
+
export function incrementSnapshotCount(state) {
|
|
99
|
+
if (!state.session_id)
|
|
100
|
+
return state;
|
|
101
|
+
const history = state.session_history.map(entry => {
|
|
102
|
+
if (entry.session_id === state.session_id) {
|
|
103
|
+
return { ...entry, snapshots_taken: entry.snapshots_taken + 1 };
|
|
104
|
+
}
|
|
105
|
+
return entry;
|
|
106
|
+
});
|
|
107
|
+
return { ...state, session_history: history };
|
|
108
|
+
}
|
|
83
109
|
//# sourceMappingURL=state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/threshold/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG1C,MAAM,aAAa,GAAG,OAAO,CAAC;AAC9B,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,SAAS,YAAY;IACnB,OAAO;QACL,OAAO,EAAE,aAAa;QACtB,UAAU,EAAE,EAAE;QACd,gBAAgB,EAAE,CAAC;QACnB,iBAAiB,EAAE,IAAI;QACvB,kBAAkB,EAAE,CAAC;QACrB,eAAe,EAAE,CAAC;QAClB,yBAAyB,EAAE,
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/threshold/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG1C,MAAM,aAAa,GAAG,OAAO,CAAC;AAC9B,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,SAAS,YAAY;IACnB,OAAO;QACL,OAAO,EAAE,aAAa;QACtB,UAAU,EAAE,EAAE;QACd,gBAAgB,EAAE,CAAC;QACnB,iBAAiB,EAAE,IAAI;QACvB,kBAAkB,EAAE,CAAC;QACrB,eAAe,EAAE,CAAC;QAClB,yBAAyB,EAAE,CAAC;QAC5B,eAAe,EAAE,EAAE;KACpB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,WAAmB;IAC9C,OAAO,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,WAAmB;IAC3C,MAAM,SAAS,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,OAAO,YAAY,EAAE,CAAC;IACxB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;QAChD,OAAO,EAAE,GAAG,YAAY,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,YAAY,EAAE,CAAC;IACxB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,WAAmB,EAAE,KAAoB;IACjE,MAAM,SAAS,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAoB,EAAE,UAAoB;IAC5E,MAAM,QAAQ,GAAG,KAAK,CAAC,gBAAgB,GAAG,CAAC,CAAC;IAC5C,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjE,OAAO;QACL,GAAG,KAAK;QACR,gBAAgB,EAAE,QAAQ;QAC1B,iBAAiB,EAAE,UAAU,CAAC,cAAc,CAAC;QAC7C,2EAA2E;QAC3E,0EAA0E;QAC1E,mCAAmC;QACnC,0BAA0B,EAAE,KAAK,CAAC,0BAA0B;KAC7D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAoB,EAAE,SAAiB,EAAE,UAAoB;IAC9F,yCAAyC;IACzC,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC;IAC3C,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,MAAM,YAAY,GAAiB;YACjC,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,OAAO,EAAE,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,CAAC,EAAE,OAAO,IAAI,KAAK,CAAC,eAAe;YAC7G,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE;YACjB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;YACxC,eAAe,EAAE,CAAC,EAAE,wDAAwD;SAC7E,CAAC;QACF,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC9B,IAAI,OAAO,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;YACzC,OAAO,CAAC,MAAM,GAAG,mBAAmB,CAAC;QACvC,CAAC;IACH,CAAC;IAED,OAAO;QACL,GAAG,KAAK;QACR,UAAU,EAAE,SAAS;QACrB,gBAAgB,EAAE,CAAC;QACnB,iBAAiB,EAAE,UAAU,CAAC,CAAC,CAAC;QAChC,eAAe,EAAE,IAAI,CAAC,GAAG,EAAE;QAC3B,eAAe,EAAE,OAAO;QACxB,0BAA0B,EAAE,EAAE;QAC9B,YAAY,EAAE,SAAS;QACvB,qBAAqB,EAAE,SAAS;QAChC,2BAA2B,EAAE,SAAS;QACtC,uBAAuB,EAAE,SAAS;QAClC,0BAA0B,EAAE,SAAS;QACrC,oCAAoC,EAAE,SAAS;KAChD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAoB;IACrD,OAAO;QACL,GAAG,KAAK;QACR,kBAAkB,EAAE,IAAI,CAAC,GAAG,EAAE;QAC9B,eAAe,EAAE,IAAI,CAAC,GAAG,EAAE;KAC5B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAoB;IACzD,IAAI,CAAC,KAAK,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAEpC,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QAChD,IAAI,KAAK,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;YAC1C,OAAO,EAAE,GAAG,KAAK,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;QAClE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface ContextUsage {
|
|
2
|
+
status: 'measured';
|
|
3
|
+
model: string;
|
|
4
|
+
usedTokens: number;
|
|
5
|
+
contextLimitTokens: number;
|
|
6
|
+
usedFraction: number;
|
|
7
|
+
remainingFraction: number;
|
|
8
|
+
source: 'transcript_usage';
|
|
9
|
+
}
|
|
10
|
+
export interface UnknownContextLimitUsage {
|
|
11
|
+
status: 'unknown_context_limit';
|
|
12
|
+
model: string;
|
|
13
|
+
usedTokens: number;
|
|
14
|
+
source: 'transcript_usage';
|
|
15
|
+
}
|
|
16
|
+
export type ContextUsageObservation = ContextUsage | UnknownContextLimitUsage;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the context limit for models observed in Claude Code transcripts.
|
|
19
|
+
* An explicit override wins so users can handle provider/model changes without
|
|
20
|
+
* waiting for a Bookmark release. Unknown models return null so Bookmark can
|
|
21
|
+
* ask for a verified limit instead of guessing.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveContextLimit(model: string, override?: number): number | null;
|
|
24
|
+
/** Read only the transcript tail and return the latest assistant usage record. */
|
|
25
|
+
export declare function readLatestContextUsage(transcriptPath: string, contextLimitOverride?: number, pendingPrompt?: string): ContextUsageObservation | null;
|
|
26
|
+
export declare function newlyCrossedThresholds(usedFraction: number, configuredThresholds: number[], alreadyTriggered?: number[]): number[];
|
|
27
|
+
/** Re-arm alerts only after a lower usage record proves a new context cycle. */
|
|
28
|
+
export declare function handledThresholdsForUsage(usedFraction: number, configuredThresholds: number[], alreadyTriggered?: number[]): number[];
|
|
29
|
+
//# sourceMappingURL=token-usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-usage.d.ts","sourceRoot":"","sources":["../../src/threshold/token-usage.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,uBAAuB,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED,MAAM,MAAM,uBAAuB,GAAG,YAAY,GAAG,wBAAwB,CAAC;AAE9E;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBnF;AAED,kFAAkF;AAClF,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,oBAAoB,CAAC,EAAE,MAAM,EAC7B,aAAa,SAAK,GACjB,uBAAuB,GAAG,IAAI,CA0EhC;AAED,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,oBAAoB,EAAE,MAAM,EAAE,EAC9B,gBAAgB,GAAE,MAAM,EAAO,GAC9B,MAAM,EAAE,CAMV;AAED,gFAAgF;AAChF,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,oBAAoB,EAAE,MAAM,EAAE,EAC9B,gBAAgB,GAAE,MAAM,EAAO,GAC9B,MAAM,EAAE,CAGV"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { closeSync, existsSync, fstatSync, openSync, readSync } from 'node:fs';
|
|
2
|
+
const MAX_TAIL_BYTES = 4 * 1024 * 1024;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the context limit for models observed in Claude Code transcripts.
|
|
5
|
+
* An explicit override wins so users can handle provider/model changes without
|
|
6
|
+
* waiting for a Bookmark release. Unknown models return null so Bookmark can
|
|
7
|
+
* ask for a verified limit instead of guessing.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveContextLimit(model, override) {
|
|
10
|
+
if (override && Number.isFinite(override) && override > 0)
|
|
11
|
+
return override;
|
|
12
|
+
const normalized = normalizeModelId(model);
|
|
13
|
+
const hasOneMillionContext = [
|
|
14
|
+
/^claude-(?:opus|sonnet|fable|mythos)-5(?:-|$)/,
|
|
15
|
+
/^claude-opus-4-(?:6|7|8)(?:-|$)/,
|
|
16
|
+
/^claude-sonnet-4-6(?:-|$)/,
|
|
17
|
+
/^claude-mythos-preview(?:-|$)/,
|
|
18
|
+
].some(pattern => pattern.test(normalized));
|
|
19
|
+
if (hasOneMillionContext)
|
|
20
|
+
return 1_000_000;
|
|
21
|
+
const hasTwoHundredThousandContext = [
|
|
22
|
+
/^claude-(?:opus|sonnet|haiku)-4-5(?:-|$)/,
|
|
23
|
+
].some(pattern => pattern.test(normalized));
|
|
24
|
+
return hasTwoHundredThousandContext ? 200_000 : null;
|
|
25
|
+
}
|
|
26
|
+
/** Read only the transcript tail and return the latest assistant usage record. */
|
|
27
|
+
export function readLatestContextUsage(transcriptPath, contextLimitOverride, pendingPrompt = '') {
|
|
28
|
+
if (!existsSync(transcriptPath))
|
|
29
|
+
return null;
|
|
30
|
+
let fd;
|
|
31
|
+
try {
|
|
32
|
+
fd = openSync(transcriptPath, 'r');
|
|
33
|
+
const size = fstatSync(fd).size;
|
|
34
|
+
if (size === 0)
|
|
35
|
+
return null;
|
|
36
|
+
const bytesToRead = Math.min(size, MAX_TAIL_BYTES);
|
|
37
|
+
const start = size - bytesToRead;
|
|
38
|
+
const buffer = Buffer.alloc(bytesToRead);
|
|
39
|
+
readSync(fd, buffer, 0, bytesToRead, start);
|
|
40
|
+
let text = buffer.toString('utf8');
|
|
41
|
+
if (start > 0) {
|
|
42
|
+
const firstNewline = text.indexOf('\n');
|
|
43
|
+
text = firstNewline >= 0 ? text.slice(firstNewline + 1) : '';
|
|
44
|
+
}
|
|
45
|
+
const lines = text.split('\n');
|
|
46
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
47
|
+
const line = lines[index].trim();
|
|
48
|
+
if (!line)
|
|
49
|
+
continue;
|
|
50
|
+
try {
|
|
51
|
+
const record = JSON.parse(line);
|
|
52
|
+
if (typeof record.type === 'string' && record.type !== 'assistant')
|
|
53
|
+
continue;
|
|
54
|
+
const message = isRecord(record.message) ? record.message : record;
|
|
55
|
+
const usage = isRecord(message.usage) ? message.usage : null;
|
|
56
|
+
const model = typeof message.model === 'string' ? message.model : null;
|
|
57
|
+
if (!usage || !model || model === '<synthetic>')
|
|
58
|
+
continue;
|
|
59
|
+
const inputTokens = tokenCount(usage.input_tokens);
|
|
60
|
+
const cacheCreationTokens = tokenCount(usage.cache_creation_input_tokens);
|
|
61
|
+
const cacheReadTokens = tokenCount(usage.cache_read_input_tokens);
|
|
62
|
+
const outputTokens = tokenCount(usage.output_tokens);
|
|
63
|
+
const promptEstimate = pendingPrompt
|
|
64
|
+
? Math.ceil(Buffer.byteLength(pendingPrompt, 'utf8') / 4)
|
|
65
|
+
: 0;
|
|
66
|
+
const usedTokens = inputTokens + cacheCreationTokens + cacheReadTokens + outputTokens + promptEstimate;
|
|
67
|
+
if (usedTokens <= 0)
|
|
68
|
+
continue;
|
|
69
|
+
const contextLimitTokens = resolveContextLimit(model, contextLimitOverride);
|
|
70
|
+
if (contextLimitTokens === null) {
|
|
71
|
+
return {
|
|
72
|
+
status: 'unknown_context_limit',
|
|
73
|
+
model,
|
|
74
|
+
usedTokens,
|
|
75
|
+
source: 'transcript_usage',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const usedFraction = Math.min(usedTokens / contextLimitTokens, 1);
|
|
79
|
+
return {
|
|
80
|
+
status: 'measured',
|
|
81
|
+
model,
|
|
82
|
+
usedTokens,
|
|
83
|
+
contextLimitTokens,
|
|
84
|
+
usedFraction,
|
|
85
|
+
remainingFraction: Math.max(0, 1 - usedFraction),
|
|
86
|
+
source: 'transcript_usage',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Skip malformed or partial JSONL lines.
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
if (fd !== undefined)
|
|
99
|
+
closeSync(fd);
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
export function newlyCrossedThresholds(usedFraction, configuredThresholds, alreadyTriggered = []) {
|
|
104
|
+
const handled = new Set(alreadyTriggered.map(value => Number(value.toFixed(4))));
|
|
105
|
+
return [...configuredThresholds]
|
|
106
|
+
.filter(value => Number.isFinite(value) && value > 0 && value < 1)
|
|
107
|
+
.sort((a, b) => a - b)
|
|
108
|
+
.filter(value => usedFraction >= value && !handled.has(Number(value.toFixed(4))));
|
|
109
|
+
}
|
|
110
|
+
/** Re-arm alerts only after a lower usage record proves a new context cycle. */
|
|
111
|
+
export function handledThresholdsForUsage(usedFraction, configuredThresholds, alreadyTriggered = []) {
|
|
112
|
+
const lowestThreshold = Math.min(...configuredThresholds);
|
|
113
|
+
return usedFraction < lowestThreshold ? [] : alreadyTriggered;
|
|
114
|
+
}
|
|
115
|
+
function tokenCount(value) {
|
|
116
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
|
|
117
|
+
}
|
|
118
|
+
function normalizeModelId(model) {
|
|
119
|
+
return model
|
|
120
|
+
.toLowerCase()
|
|
121
|
+
.replace(/^anthropic\./, '')
|
|
122
|
+
.replace(/-v\d+:\d+$/, '')
|
|
123
|
+
.replace(/@(\d{8})$/, '-$1');
|
|
124
|
+
}
|
|
125
|
+
function isRecord(value) {
|
|
126
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=token-usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-usage.js","sourceRoot":"","sources":["../../src/threshold/token-usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOOKMARK_IDENTITY block parser + validator.
|
|
3
|
+
*
|
|
4
|
+
* The identity block is an HTML comment at the top of bookmark.context.md
|
|
5
|
+
* that declares which project/repo/branch the summary belongs to. It makes
|
|
6
|
+
* cross-session restoration unambiguous — you can always verify the
|
|
7
|
+
* bookmark you're restoring from matches the CWD you're restoring into.
|
|
8
|
+
*
|
|
9
|
+
* Example:
|
|
10
|
+
* <!-- BOOKMARK_IDENTITY
|
|
11
|
+
* scope: repo
|
|
12
|
+
* project: travel-planner
|
|
13
|
+
* repo_path: /Users/me/Desktop/git-folder/Travel Planner
|
|
14
|
+
* branch: feature/summer-camps
|
|
15
|
+
* head: 4988383
|
|
16
|
+
* written: 2026-04-11
|
|
17
|
+
* -->
|
|
18
|
+
*
|
|
19
|
+
* Two scopes are supported:
|
|
20
|
+
* - `repo` — bookmark belongs to a specific git repo (or directory)
|
|
21
|
+
* - `home` — bookmark is a POINTER, no active context itself; delegates
|
|
22
|
+
* via `points_to_canonical` to a repo-scoped bookmark
|
|
23
|
+
*/
|
|
24
|
+
export type BookmarkScope = 'repo' | 'home';
|
|
25
|
+
export interface BookmarkIdentity {
|
|
26
|
+
scope: BookmarkScope;
|
|
27
|
+
project?: string;
|
|
28
|
+
repo_path?: string;
|
|
29
|
+
repo_name?: string;
|
|
30
|
+
branch?: string;
|
|
31
|
+
head?: string;
|
|
32
|
+
base?: string;
|
|
33
|
+
written?: string;
|
|
34
|
+
written_by?: string;
|
|
35
|
+
points_to_project?: string;
|
|
36
|
+
points_to_repo?: string;
|
|
37
|
+
points_to_canonical?: string;
|
|
38
|
+
/** All raw key:value pairs, for forward-compat keys we don't model */
|
|
39
|
+
raw: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
export interface IdentityParseResult {
|
|
42
|
+
identity: BookmarkIdentity | null;
|
|
43
|
+
/** Content with the identity block stripped, ready to present */
|
|
44
|
+
bodyWithoutIdentity: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parse a bookmark.context.md file's content and extract the identity block.
|
|
48
|
+
* Returns `identity: null` if no block is present — callers should treat that
|
|
49
|
+
* as a legacy/untagged file and fall back to current behavior.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseIdentity(content: string): IdentityParseResult;
|
|
52
|
+
/**
|
|
53
|
+
* Validate a repo-scoped identity against the storage path that was loaded.
|
|
54
|
+
*
|
|
55
|
+
* Returns a warning string if the identity's `repo_path` disagrees with the
|
|
56
|
+
* working directory, suggesting the bookmark file was moved or the CWD is
|
|
57
|
+
* not the repo it describes. Returns null when they match or when we can't
|
|
58
|
+
* determine (missing field).
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateRepoIdentity(identity: BookmarkIdentity, cwd: string): string | null;
|
|
61
|
+
/**
|
|
62
|
+
* Follow a home-scope pointer to its canonical file.
|
|
63
|
+
*
|
|
64
|
+
* If the identity is home-scoped and has `points_to_canonical`, return the
|
|
65
|
+
* content of that file (plus its own parsed identity). Returns null if the
|
|
66
|
+
* pointer target doesn't exist or isn't readable — caller should fall back
|
|
67
|
+
* to presenting the pointer body itself.
|
|
68
|
+
*/
|
|
69
|
+
export interface PointerTarget {
|
|
70
|
+
canonical_path: string;
|
|
71
|
+
content: string;
|
|
72
|
+
identity: BookmarkIdentity | null;
|
|
73
|
+
staleness_hours: number | null;
|
|
74
|
+
}
|
|
75
|
+
export declare function followPointer(identity: BookmarkIdentity): PointerTarget | null;
|
|
76
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/trails/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,iEAAiE;IACjE,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAID;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAwClE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,gBAAgB,EAC1B,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,IAAI,CAiBf;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,gBAAgB,GAAG,aAAa,GAAG,IAAI,CAqB9E"}
|