@jigyasudham/veto 2.9.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -4
- package/dist/cli/statusline.d.ts +9 -0
- package/dist/cli/statusline.d.ts.map +1 -1
- package/dist/cli/statusline.js +59 -3
- package/dist/cli/statusline.js.map +1 -1
- package/dist/cli.js +166 -5
- package/dist/cli.js.map +1 -1
- package/dist/memory/config.d.ts +10 -0
- package/dist/memory/config.d.ts.map +1 -1
- package/dist/memory/config.js +48 -8
- package/dist/memory/config.js.map +1 -1
- package/dist/memory/local.d.ts.map +1 -1
- package/dist/memory/local.js +32 -10
- package/dist/memory/local.js.map +1 -1
- package/dist/server/handlers/core.d.ts.map +1 -1
- package/dist/server/handlers/core.js +14 -1
- package/dist/server/handlers/core.js.map +1 -1
- package/dist/server/handlers/session.d.ts.map +1 -1
- package/dist/server/handlers/session.js +51 -2
- package/dist/server/handlers/session.js.map +1 -1
- package/dist/tools/definitions.d.ts +23 -3
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +11 -3
- package/dist/tools/definitions.js.map +1 -1
- package/dist/transcripts/adapters/claude.d.ts +27 -0
- package/dist/transcripts/adapters/claude.d.ts.map +1 -0
- package/dist/transcripts/adapters/claude.js +194 -0
- package/dist/transcripts/adapters/claude.js.map +1 -0
- package/dist/transcripts/archive.d.ts +23 -0
- package/dist/transcripts/archive.d.ts.map +1 -0
- package/dist/transcripts/archive.js +101 -0
- package/dist/transcripts/archive.js.map +1 -0
- package/dist/transcripts/cache.d.ts +66 -0
- package/dist/transcripts/cache.d.ts.map +1 -0
- package/dist/transcripts/cache.js +0 -0
- package/dist/transcripts/cache.js.map +1 -0
- package/dist/transcripts/chunk.d.ts +26 -0
- package/dist/transcripts/chunk.d.ts.map +1 -0
- package/dist/transcripts/chunk.js +54 -0
- package/dist/transcripts/chunk.js.map +1 -0
- package/dist/transcripts/config.d.ts +52 -0
- package/dist/transcripts/config.d.ts.map +1 -0
- package/dist/transcripts/config.js +135 -0
- package/dist/transcripts/config.js.map +1 -0
- package/dist/transcripts/embed.d.ts +58 -0
- package/dist/transcripts/embed.d.ts.map +1 -0
- package/dist/transcripts/embed.js +303 -0
- package/dist/transcripts/embed.js.map +1 -0
- package/dist/transcripts/expand.d.ts +20 -0
- package/dist/transcripts/expand.d.ts.map +1 -0
- package/dist/transcripts/expand.js +97 -0
- package/dist/transcripts/expand.js.map +1 -0
- package/dist/transcripts/ingest.d.ts +17 -0
- package/dist/transcripts/ingest.d.ts.map +1 -0
- package/dist/transcripts/ingest.js +120 -0
- package/dist/transcripts/ingest.js.map +1 -0
- package/dist/transcripts/manage.d.ts +42 -0
- package/dist/transcripts/manage.d.ts.map +1 -0
- package/dist/transcripts/manage.js +123 -0
- package/dist/transcripts/manage.js.map +1 -0
- package/dist/transcripts/mapping.d.ts +23 -0
- package/dist/transcripts/mapping.d.ts.map +1 -0
- package/dist/transcripts/mapping.js +42 -0
- package/dist/transcripts/mapping.js.map +1 -0
- package/dist/transcripts/mask.d.ts +26 -0
- package/dist/transcripts/mask.d.ts.map +1 -0
- package/dist/transcripts/mask.js +121 -0
- package/dist/transcripts/mask.js.map +1 -0
- package/dist/transcripts/on-save.d.ts +12 -0
- package/dist/transcripts/on-save.d.ts.map +1 -0
- package/dist/transcripts/on-save.js +37 -0
- package/dist/transcripts/on-save.js.map +1 -0
- package/dist/transcripts/pyramid.d.ts +38 -0
- package/dist/transcripts/pyramid.d.ts.map +1 -0
- package/dist/transcripts/pyramid.js +113 -0
- package/dist/transcripts/pyramid.js.map +1 -0
- package/dist/transcripts/recall.d.ts +46 -0
- package/dist/transcripts/recall.d.ts.map +1 -0
- package/dist/transcripts/recall.js +107 -0
- package/dist/transcripts/recall.js.map +1 -0
- package/dist/transcripts/schema.d.ts +79 -0
- package/dist/transcripts/schema.d.ts.map +1 -0
- package/dist/transcripts/schema.js +178 -0
- package/dist/transcripts/schema.js.map +1 -0
- package/dist/transcripts/search.d.ts +57 -0
- package/dist/transcripts/search.d.ts.map +1 -0
- package/dist/transcripts/search.js +332 -0
- package/dist/transcripts/search.js.map +1 -0
- package/dist/transcripts/store.d.ts +21 -0
- package/dist/transcripts/store.d.ts.map +1 -0
- package/dist/transcripts/store.js +105 -0
- package/dist/transcripts/store.js.map +1 -0
- package/dist/transcripts/toc.d.ts +18 -0
- package/dist/transcripts/toc.d.ts.map +1 -0
- package/dist/transcripts/toc.js +96 -0
- package/dist/transcripts/toc.js.map +1 -0
- package/dist/transcripts/tokenize.d.ts +14 -0
- package/dist/transcripts/tokenize.d.ts.map +1 -0
- package/dist/transcripts/tokenize.js +70 -0
- package/dist/transcripts/tokenize.js.map +1 -0
- package/dist/transcripts/vectors.d.ts +40 -0
- package/dist/transcripts/vectors.d.ts.map +1 -0
- package/dist/transcripts/vectors.js +203 -0
- package/dist/transcripts/vectors.js.map +1 -0
- package/package.json +3 -2
- package/server.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manage.d.ts","sourceRoot":"","sources":["../../src/transcripts/manage.ts"],"names":[],"mappings":"AAYA,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAG7D,MAAM,MAAM,cAAc,GAAG;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAUF,wBAAgB,YAAY,CAAC,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,cAAc,EAAE,CAOjG;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,GAAG,EAAE,OAAO,EAAE,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,GAAG,IAAI,CAAC;AAEpG,wBAAgB,WAAW,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG,aAAa,CAKrF;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAkDnH,wBAAgB,YAAY,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG,WAAW,CAUpF;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAI5D;AAED,wBAAgB,QAAQ,IAAI,WAAW,CAQtC;AAED,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAExG,wBAAgB,oBAAoB,IAAI,SAAS,CAMhD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI1C"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Management: list / show / purge + disk usage (VERSION-3 item 6, Step 13).
|
|
2
|
+
//
|
|
3
|
+
// Purge is a TRUE cascading delete — for each archive it removes the events, the
|
|
4
|
+
// search index rows (docs + postings), the archive row, the session mapping, and
|
|
5
|
+
// the L0 file, leaving no orphan rows (Step 14 asserts zero orphans). Row deletes
|
|
6
|
+
// run in one transaction; the L0 file is unlinked best-effort afterwards (a
|
|
7
|
+
// leftover file is harmless disk, a dangling row is not).
|
|
8
|
+
import { statSync, existsSync, rmSync } from 'node:fs';
|
|
9
|
+
import { getTranscriptsDb, transcriptsDbPath } from './store.js';
|
|
10
|
+
import { resetCaches } from './cache.js';
|
|
11
|
+
import { normalizeProjectDir } from '../memory/local.js';
|
|
12
|
+
import { buildTOC } from './toc.js';
|
|
13
|
+
import { buildFacts } from './pyramid.js';
|
|
14
|
+
function toSummary(r) {
|
|
15
|
+
return {
|
|
16
|
+
sourceSessionId: r.source_session_id, source: r.source, projectDir: r.project_dir,
|
|
17
|
+
events: r.indexed_through_seq, sourceBytes: r.source_bytes, archiveBytes: r.archive_bytes,
|
|
18
|
+
capturedAt: r.captured_at, updatedAt: r.updated_at, indexed: r.parser_version > 0,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function listArchives(opts = {}) {
|
|
22
|
+
const db = getTranscriptsDb();
|
|
23
|
+
const limit = Math.min(Math.max(opts.limit ?? 50, 1), 500);
|
|
24
|
+
const rows = opts.projectDir
|
|
25
|
+
? db.prepare(`SELECT * FROM archives WHERE project_dir = ? ORDER BY updated_at DESC LIMIT ?`).all(normalizeProjectDir(opts.projectDir), limit)
|
|
26
|
+
: db.prepare(`SELECT * FROM archives ORDER BY updated_at DESC LIMIT ?`).all(limit);
|
|
27
|
+
return rows.map(toSummary);
|
|
28
|
+
}
|
|
29
|
+
export function showArchive(sourceSessionId, source = 'claude') {
|
|
30
|
+
const db = getTranscriptsDb();
|
|
31
|
+
const r = db.prepare(`SELECT * FROM archives WHERE source = ? AND source_session_id = ?`).get(source, sourceSessionId);
|
|
32
|
+
if (!r)
|
|
33
|
+
return null;
|
|
34
|
+
return { summary: toSummary(r), toc: buildTOC(r.id), facts: buildFacts(r.id) };
|
|
35
|
+
}
|
|
36
|
+
function purgeArchiveRows(archives) {
|
|
37
|
+
const db = getTranscriptsDb();
|
|
38
|
+
const res = { archives: 0, events: 0, indexRows: 0, files: 0, mappings: 0 };
|
|
39
|
+
const paths = [];
|
|
40
|
+
db.exec('BEGIN');
|
|
41
|
+
try {
|
|
42
|
+
for (const a of archives) {
|
|
43
|
+
res.events += db.prepare(`SELECT COUNT(*) n FROM events WHERE archive_id = ?`).get(a.id).n;
|
|
44
|
+
// Two plain queries, not one with a reused ?1 — node 22.13's sqlite
|
|
45
|
+
// binding doesn't dedupe numbered parameters (SQLITE_RANGE on CI).
|
|
46
|
+
res.indexRows += db.prepare(`SELECT COUNT(*) n FROM search_docs WHERE archive_id = ?`).get(a.id).n;
|
|
47
|
+
res.indexRows += db.prepare(`SELECT COUNT(*) n FROM search_postings WHERE doc_id IN (SELECT id FROM search_docs WHERE archive_id = ?)`).get(a.id).n;
|
|
48
|
+
// Chunk vectors hang off the same doc ids, so they orphan the same way
|
|
49
|
+
// postings would if this were forgotten (C3 asserts zero orphans).
|
|
50
|
+
res.indexRows += db.prepare(`SELECT COUNT(*) n FROM search_vectors WHERE doc_id IN (SELECT id FROM search_docs WHERE archive_id = ?)`).get(a.id).n;
|
|
51
|
+
db.prepare(`DELETE FROM events WHERE archive_id = ?`).run(a.id);
|
|
52
|
+
db.prepare(`DELETE FROM search_postings WHERE doc_id IN (SELECT id FROM search_docs WHERE archive_id = ?)`).run(a.id);
|
|
53
|
+
db.prepare(`DELETE FROM search_vectors WHERE doc_id IN (SELECT id FROM search_docs WHERE archive_id = ?)`).run(a.id);
|
|
54
|
+
db.prepare(`DELETE FROM search_docs WHERE archive_id = ?`).run(a.id);
|
|
55
|
+
db.prepare(`DELETE FROM archives WHERE id = ?`).run(a.id);
|
|
56
|
+
const m = db.prepare(`DELETE FROM session_map WHERE source = ? AND source_session_id = ?`).run(a.source, a.source_session_id);
|
|
57
|
+
res.mappings += Number(m.changes ?? 0);
|
|
58
|
+
res.archives += 1;
|
|
59
|
+
if (a.archive_path)
|
|
60
|
+
paths.push(a.archive_path);
|
|
61
|
+
}
|
|
62
|
+
db.exec('COMMIT');
|
|
63
|
+
resetCaches();
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
db.exec('ROLLBACK');
|
|
67
|
+
throw e;
|
|
68
|
+
}
|
|
69
|
+
// Rows are gone and committed; remove the L0 files best-effort.
|
|
70
|
+
for (const p of paths) {
|
|
71
|
+
if (existsSync(p)) {
|
|
72
|
+
try {
|
|
73
|
+
rmSync(p);
|
|
74
|
+
res.files += 1;
|
|
75
|
+
}
|
|
76
|
+
catch { /* leftover file is harmless */ }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return res;
|
|
80
|
+
}
|
|
81
|
+
export function purgeSession(sourceSessionId, source = 'claude') {
|
|
82
|
+
const db = getTranscriptsDb();
|
|
83
|
+
const arch = db.prepare(`SELECT * FROM archives WHERE source = ? AND source_session_id = ?`).get(source, sourceSessionId);
|
|
84
|
+
const result = purgeArchiveRows(arch ? [arch] : []);
|
|
85
|
+
// Purge the mapping even if there was no archive yet (capture never ran).
|
|
86
|
+
if (!arch) {
|
|
87
|
+
const m = db.prepare(`DELETE FROM session_map WHERE source = ? AND source_session_id = ?`).run(source, sourceSessionId);
|
|
88
|
+
result.mappings += Number(m.changes ?? 0);
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
export function purgeProject(projectDir) {
|
|
93
|
+
const db = getTranscriptsDb();
|
|
94
|
+
const archives = db.prepare(`SELECT * FROM archives WHERE project_dir = ?`).all(normalizeProjectDir(projectDir));
|
|
95
|
+
return purgeArchiveRows(archives);
|
|
96
|
+
}
|
|
97
|
+
export function purgeAll() {
|
|
98
|
+
const db = getTranscriptsDb();
|
|
99
|
+
const archives = db.prepare(`SELECT * FROM archives`).all();
|
|
100
|
+
const res = purgeArchiveRows(archives);
|
|
101
|
+
// Sweep any orphan mappings left with no archive.
|
|
102
|
+
const m = db.prepare(`DELETE FROM session_map`).run();
|
|
103
|
+
res.mappings = Math.max(res.mappings, Number(m.changes ?? 0));
|
|
104
|
+
return res;
|
|
105
|
+
}
|
|
106
|
+
export function transcriptsDiskUsage() {
|
|
107
|
+
const db = getTranscriptsDb();
|
|
108
|
+
const agg = db.prepare(`SELECT COUNT(*) n, COALESCE(SUM(archive_bytes), 0) b FROM archives`).get();
|
|
109
|
+
let dbBytes = 0;
|
|
110
|
+
try {
|
|
111
|
+
dbBytes = statSync(transcriptsDbPath()).size;
|
|
112
|
+
}
|
|
113
|
+
catch { /* db not on disk (memory) */ }
|
|
114
|
+
return { archives: agg.n, archiveBytes: agg.b, dbBytes, totalBytes: dbBytes + agg.b };
|
|
115
|
+
}
|
|
116
|
+
export function fmtBytes(n) {
|
|
117
|
+
if (n < 1024)
|
|
118
|
+
return `${n} B`;
|
|
119
|
+
if (n < 1024 * 1024)
|
|
120
|
+
return `${(n / 1024).toFixed(1)} KB`;
|
|
121
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=manage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manage.js","sourceRoot":"","sources":["../../src/transcripts/manage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SessionMapRow } from './schema.js';
|
|
2
|
+
export type RecordMappingInput = {
|
|
3
|
+
source?: string;
|
|
4
|
+
sourceSessionId: string;
|
|
5
|
+
transcriptPath: string;
|
|
6
|
+
projectDir?: string | null;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* UPSERT a session's transcript path. Idempotent on (source, source_session_id):
|
|
10
|
+
* a later render just refreshes transcript_path/project_dir/last_seen_at. The
|
|
11
|
+
* caller treats this as fire-and-forget; it must not throw on the hot path, so
|
|
12
|
+
* hot-path callers wrap it — but keep it cheap regardless.
|
|
13
|
+
*/
|
|
14
|
+
export declare function recordSessionMapping(m: RecordMappingInput): void;
|
|
15
|
+
/** Look up the mapping for an exact session id. */
|
|
16
|
+
export declare function getSessionMapping(sourceSessionId: string, source?: string): SessionMapRow | null;
|
|
17
|
+
/**
|
|
18
|
+
* Newest ACTIVE mapping for a project from a given source. Save-time capture
|
|
19
|
+
* uses this when it knows the project but not the exact source_session_id
|
|
20
|
+
* (S5 concurrency rule: bind the newest active mapping, ties broken by recency).
|
|
21
|
+
*/
|
|
22
|
+
export declare function latestMappingForProject(projectDir: string, source?: string): SessionMapRow | null;
|
|
23
|
+
//# sourceMappingURL=mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../src/transcripts/mapping.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAYhE;AAED,mDAAmD;AACnD,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG,aAAa,GAAG,IAAI,CAKlG;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG,aAAa,GAAG,IAAI,CAMnG"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Live host-session -> transcript-file mapping (VERSION-3 item 6, Step 4).
|
|
2
|
+
//
|
|
3
|
+
// The statusline (which is the one place that reliably sees Claude Code's
|
|
4
|
+
// session_id + transcript_path on every render) UPSERTs into session_map
|
|
5
|
+
// fire-and-forget. Save-time capture (Step 5) then looks up which file to
|
|
6
|
+
// archive for the session being saved. All writes land in the sidecar
|
|
7
|
+
// transcripts.db — veto.db is never touched.
|
|
8
|
+
import { getTranscriptsDb } from './store.js';
|
|
9
|
+
import { normalizeProjectDir } from '../memory/local.js';
|
|
10
|
+
/**
|
|
11
|
+
* UPSERT a session's transcript path. Idempotent on (source, source_session_id):
|
|
12
|
+
* a later render just refreshes transcript_path/project_dir/last_seen_at. The
|
|
13
|
+
* caller treats this as fire-and-forget; it must not throw on the hot path, so
|
|
14
|
+
* hot-path callers wrap it — but keep it cheap regardless.
|
|
15
|
+
*/
|
|
16
|
+
export function recordSessionMapping(m) {
|
|
17
|
+
const db = getTranscriptsDb();
|
|
18
|
+
const now = new Date().toISOString();
|
|
19
|
+
const proj = m.projectDir ? normalizeProjectDir(m.projectDir) : null;
|
|
20
|
+
db.prepare(`INSERT INTO session_map (source, source_session_id, transcript_path, project_dir, last_seen_at)
|
|
21
|
+
VALUES (?, ?, ?, ?, ?)
|
|
22
|
+
ON CONFLICT(source, source_session_id) DO UPDATE SET
|
|
23
|
+
transcript_path = excluded.transcript_path,
|
|
24
|
+
project_dir = excluded.project_dir,
|
|
25
|
+
last_seen_at = excluded.last_seen_at`).run(m.source ?? 'claude', m.sourceSessionId, m.transcriptPath, proj, now);
|
|
26
|
+
}
|
|
27
|
+
/** Look up the mapping for an exact session id. */
|
|
28
|
+
export function getSessionMapping(sourceSessionId, source = 'claude') {
|
|
29
|
+
const db = getTranscriptsDb();
|
|
30
|
+
return db.prepare(`SELECT * FROM session_map WHERE source = ? AND source_session_id = ?`).get(source, sourceSessionId) ?? null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Newest ACTIVE mapping for a project from a given source. Save-time capture
|
|
34
|
+
* uses this when it knows the project but not the exact source_session_id
|
|
35
|
+
* (S5 concurrency rule: bind the newest active mapping, ties broken by recency).
|
|
36
|
+
*/
|
|
37
|
+
export function latestMappingForProject(projectDir, source = 'claude') {
|
|
38
|
+
const db = getTranscriptsDb();
|
|
39
|
+
const proj = normalizeProjectDir(projectDir);
|
|
40
|
+
return db.prepare(`SELECT * FROM session_map WHERE source = ? AND project_dir = ? ORDER BY last_seen_at DESC LIMIT 1`).get(source, proj) ?? null;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../src/transcripts/mapping.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,0EAA0E;AAC1E,sEAAsE;AACtE,6CAA6C;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAUzD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,CAAqB;IACxD,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,EAAE,CAAC,OAAO,CACR;;;;;+CAK2C,CAC5C,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,cAAc,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,iBAAiB,CAAC,eAAuB,EAAE,MAAM,GAAG,QAAQ;IAC1E,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAC9B,OAAQ,EAAE,CAAC,OAAO,CAChB,sEAAsE,CACvE,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAA+B,IAAI,IAAI,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAkB,EAAE,MAAM,GAAG,QAAQ;IAC3E,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAC7C,OAAQ,EAAE,CAAC,OAAO,CAChB,mGAAmG,CACpG,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAA+B,IAAI,IAAI,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type MaskPattern = {
|
|
2
|
+
type: string;
|
|
3
|
+
regex: RegExp;
|
|
4
|
+
/** Capture group that is the secret VALUE; 0 = redact the whole match. */
|
|
5
|
+
group: number;
|
|
6
|
+
};
|
|
7
|
+
/** Matches an emitted mask token — lets callers detect already-masked content. */
|
|
8
|
+
export declare const REDACTED_RE: RegExp;
|
|
9
|
+
export declare function fingerprint(secret: string): string;
|
|
10
|
+
export declare function maskToken(secret: string): string;
|
|
11
|
+
export type MaskResult = {
|
|
12
|
+
/** The input with every detected secret value replaced by a fingerprint token. */
|
|
13
|
+
text: string;
|
|
14
|
+
/** Number of secrets redacted (deduped by span). */
|
|
15
|
+
count: number;
|
|
16
|
+
/** Distinct fingerprints seen, in order of first appearance. */
|
|
17
|
+
fingerprints: string[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Detect and redact secrets. Operates on the WHOLE string (so multiline keys and
|
|
21
|
+
* global offsets work). Overlapping matches resolve to a single redaction.
|
|
22
|
+
*/
|
|
23
|
+
export declare function mask(input: string): MaskResult;
|
|
24
|
+
/** Convenience: true when the input contains at least one detectable secret. */
|
|
25
|
+
export declare function hasSecrets(input: string): boolean;
|
|
26
|
+
//# sourceMappingURL=mask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../../src/transcripts/mask.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AA2BF,kFAAkF;AAClF,eAAO,MAAM,WAAW,QAAoC,CAAC;AAM7D,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAUF;;;GAGG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CA+C9C;AAED,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Secret detection + masking for transcript capture (VERSION-3 item 6, Step 3).
|
|
2
|
+
//
|
|
3
|
+
// The single shared primitive both write-side (ingest into derived layers) and
|
|
4
|
+
// read-side (mask-on-expansion of L0 bytes) call, so a secret can never transit
|
|
5
|
+
// into an AI context through Veto. A detected secret is replaced with a STABLE
|
|
6
|
+
// fingerprint token — REDACTED[sha256:xxxxxxxx] — of the secret value, so:
|
|
7
|
+
// • the value itself is never stored in an indexable/servable layer, and
|
|
8
|
+
// • recurrences of the same secret share a token, so recall can still say
|
|
9
|
+
// "a secret was here" and correlate without ever revealing it.
|
|
10
|
+
//
|
|
11
|
+
// Patterns extend src/agents/security/secrets.ts (that scanner is code-oriented
|
|
12
|
+
// and display-only). Here we redact the VALUE span (keeping surrounding prose
|
|
13
|
+
// readable) and add a multiline private-key block + a few creds people paste
|
|
14
|
+
// into chat. Detection is best-effort defense-in-depth — the non-synced archive
|
|
15
|
+
// dir and `veto transcripts redact` are the backstops for what it misses.
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
17
|
+
// group 1 (where present) is the sensitive value; the label/quotes/host around
|
|
18
|
+
// it are preserved so a recalled line stays readable.
|
|
19
|
+
const MASK_PATTERNS = [
|
|
20
|
+
// Multiline block first — redact the ENTIRE key, not just the header line.
|
|
21
|
+
{ type: 'Private Key Block', regex: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP |ENCRYPTED )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA |PGP |ENCRYPTED )?PRIVATE KEY-----/, group: 0 },
|
|
22
|
+
{ type: 'AWS Access Key', regex: /AKIA[0-9A-Z]{16}/, group: 0 },
|
|
23
|
+
{ type: 'AWS Secret Access Key', regex: /aws_secret_access_key\s*=\s*['"]?([A-Za-z0-9/+]{40})['"]?/i, group: 1 },
|
|
24
|
+
{ type: 'GitHub Token', regex: /gh[posru]_[A-Za-z0-9]{36,}/, group: 0 },
|
|
25
|
+
{ type: 'GitHub Token (env)', regex: /github_token\s*[:=]\s*['"]([A-Za-z0-9_]{20,})['"]/i, group: 1 },
|
|
26
|
+
{ type: 'Slack Token', regex: /xox[baprs]-[0-9A-Za-z-]{10,}/, group: 0 },
|
|
27
|
+
{ type: 'Google API Key', regex: /AIza[0-9A-Za-z_-]{35}/, group: 0 },
|
|
28
|
+
{ type: 'Provider Key (sk-)', regex: /sk-[A-Za-z0-9_-]{20,}/, group: 0 },
|
|
29
|
+
{ type: 'Bearer Token', regex: /bearer\s+([A-Za-z0-9\-._~+/]{20,}=*)/i, group: 1 },
|
|
30
|
+
{ type: 'MongoDB URI Password', regex: /mongodb(?:\+srv)?:\/\/[^\s:@/]+:([^\s@/]+)@/i, group: 1 },
|
|
31
|
+
{ type: 'Postgres URI Password', regex: /postgres(?:ql)?:\/\/[^\s:@/]+:([^\s@/]+)@/i, group: 1 },
|
|
32
|
+
{ type: 'MySQL URI Password', regex: /mysql:\/\/[^\s:@/]+:([^\s@/]+)@/i, group: 1 },
|
|
33
|
+
// JSON-style "key":"value" (tool inputs are JSON) — the quote sits before the
|
|
34
|
+
// colon, so the key[:=]value patterns above don't catch it.
|
|
35
|
+
{ type: 'JSON secret field', regex: /"(?:password|passwd|secret|token|api[_-]?key|apikey|access[_-]?token|refresh[_-]?token|client[_-]?secret|jwt[_-]?secret|auth[_-]?token|private[_-]?key|passphrase)"\s*:\s*"([^"]{4,})"/i, group: 1 },
|
|
36
|
+
{ type: 'API Key (generic)', regex: /(?:api[_-]?key)\s*[:=]\s*['"]([A-Za-z0-9_-]{20,})['"]/i, group: 1 },
|
|
37
|
+
{ type: 'JWT Secret', regex: /jwt_secret\s*[:=]\s*['"]([^'"]{8,})['"]/i, group: 1 },
|
|
38
|
+
{ type: 'Password', regex: /password\s*[:=]\s*['"]([^'"]{4,})['"]/i, group: 1 },
|
|
39
|
+
{ type: 'Generic High-Entropy Secret', regex: /(?:secret|token|passwd)\s*[:=]\s*['"]([A-Za-z0-9+/=_-]{24,})['"]/i, group: 1 },
|
|
40
|
+
];
|
|
41
|
+
/** Matches an emitted mask token — lets callers detect already-masked content. */
|
|
42
|
+
export const REDACTED_RE = /REDACTED\[sha256:[0-9a-f]{8}\]/g;
|
|
43
|
+
// Anchored form: true when a string IS exactly a mask token (used to keep mask()
|
|
44
|
+
// idempotent — a token sitting inside quotes must not be re-matched as a value).
|
|
45
|
+
const IS_TOKEN_RE = /^REDACTED\[sha256:[0-9a-f]{8}\]$/;
|
|
46
|
+
export function fingerprint(secret) {
|
|
47
|
+
return createHash('sha256').update(secret).digest('hex').slice(0, 8);
|
|
48
|
+
}
|
|
49
|
+
export function maskToken(secret) {
|
|
50
|
+
return `REDACTED[sha256:${fingerprint(secret)}]`;
|
|
51
|
+
}
|
|
52
|
+
/** Recompile a pattern with the global + indices flags (idempotent on flags). */
|
|
53
|
+
function withScanFlags(re) {
|
|
54
|
+
const base = re.flags.replace(/[gd]/g, '');
|
|
55
|
+
return new RegExp(re.source, base + 'gd');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Detect and redact secrets. Operates on the WHOLE string (so multiline keys and
|
|
59
|
+
* global offsets work). Overlapping matches resolve to a single redaction.
|
|
60
|
+
*/
|
|
61
|
+
export function mask(input) {
|
|
62
|
+
if (!input)
|
|
63
|
+
return { text: input, count: 0, fingerprints: [] };
|
|
64
|
+
const spans = [];
|
|
65
|
+
for (const p of MASK_PATTERNS) {
|
|
66
|
+
const re = withScanFlags(p.regex);
|
|
67
|
+
let m;
|
|
68
|
+
while ((m = re.exec(input)) !== null) {
|
|
69
|
+
if (m[0].length === 0) {
|
|
70
|
+
re.lastIndex++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
let start, end, secret;
|
|
74
|
+
const gi = p.group;
|
|
75
|
+
const idx = m.indices;
|
|
76
|
+
if (gi > 0 && idx && idx[gi]) {
|
|
77
|
+
[start, end] = idx[gi];
|
|
78
|
+
secret = input.slice(start, end);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
start = m.index;
|
|
82
|
+
end = m.index + m[0].length;
|
|
83
|
+
secret = m[0];
|
|
84
|
+
}
|
|
85
|
+
// Idempotency: never re-redact an already-emitted mask token (it can sit
|
|
86
|
+
// inside quotes and re-match a value pattern).
|
|
87
|
+
if (IS_TOKEN_RE.test(secret))
|
|
88
|
+
continue;
|
|
89
|
+
spans.push({ start, end, secret });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (spans.length === 0)
|
|
93
|
+
return { text: input, count: 0, fingerprints: [] };
|
|
94
|
+
// Earliest-start, then widest, then drop anything overlapping an accepted span.
|
|
95
|
+
spans.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
96
|
+
const merged = [];
|
|
97
|
+
let lastEnd = -1;
|
|
98
|
+
for (const s of spans) {
|
|
99
|
+
if (s.start >= lastEnd) {
|
|
100
|
+
merged.push(s);
|
|
101
|
+
lastEnd = s.end;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
let out = '';
|
|
105
|
+
let cursor = 0;
|
|
106
|
+
const fingerprints = [];
|
|
107
|
+
for (const s of merged) {
|
|
108
|
+
out += input.slice(cursor, s.start) + maskToken(s.secret);
|
|
109
|
+
const fp = fingerprint(s.secret);
|
|
110
|
+
if (!fingerprints.includes(fp))
|
|
111
|
+
fingerprints.push(fp);
|
|
112
|
+
cursor = s.end;
|
|
113
|
+
}
|
|
114
|
+
out += input.slice(cursor);
|
|
115
|
+
return { text: out, count: merged.length, fingerprints };
|
|
116
|
+
}
|
|
117
|
+
/** Convenience: true when the input contains at least one detectable secret. */
|
|
118
|
+
export function hasSecrets(input) {
|
|
119
|
+
return mask(input).count > 0;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=mask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask.js","sourceRoot":"","sources":["../../src/transcripts/mask.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type OnSaveTranscript = {
|
|
2
|
+
status: string;
|
|
3
|
+
events?: number;
|
|
4
|
+
secrets_redacted?: number;
|
|
5
|
+
archive_dir?: string;
|
|
6
|
+
note?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function captureOnSave(opts: {
|
|
9
|
+
projectDir?: string | null;
|
|
10
|
+
vetoSessionId?: string | null;
|
|
11
|
+
}): Promise<OnSaveTranscript | null>;
|
|
12
|
+
//# sourceMappingURL=on-save.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"on-save.d.ts","sourceRoot":"","sources":["../../src/transcripts/on-save.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAsB,aAAa,CAAC,IAAI,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAyBzI"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Save-time capture orchestration (VERSION-3 item 6, Step 7).
|
|
2
|
+
//
|
|
3
|
+
// Runs capture, then (bounded) ingest so the save response can report the leak
|
|
4
|
+
// count and the one-time first-capture note. Never throws — the caller wraps it
|
|
5
|
+
// too, but this returns null on any problem so a save is never affected.
|
|
6
|
+
import { isCaptureEnabled, firstCaptureNote, effectiveTranscriptsDir } from './config.js';
|
|
7
|
+
import { captureSession } from './archive.js';
|
|
8
|
+
import { ingestArchive } from './ingest.js';
|
|
9
|
+
// Don't parse+index a monster transcript inline on the save path (architect:
|
|
10
|
+
// keep save-time work bounded). Above this, capture still archives; indexing is
|
|
11
|
+
// deferred to first recall (Step 11's ensureIndexed).
|
|
12
|
+
const INLINE_INGEST_MAX_BYTES = 16 * 1024 * 1024;
|
|
13
|
+
export async function captureOnSave(opts) {
|
|
14
|
+
if (!isCaptureEnabled())
|
|
15
|
+
return null;
|
|
16
|
+
const cap = await captureSession({ source: 'claude', projectDir: opts.projectDir, vetoSessionId: opts.vetoSessionId });
|
|
17
|
+
if (!cap.ok)
|
|
18
|
+
return null; // skipped (no mapping / missing / too_large) or error — stay silent
|
|
19
|
+
const out = { status: cap.status };
|
|
20
|
+
// Index inline for small transcripts so we can report the leak count now.
|
|
21
|
+
if (cap.archiveId && cap.status === 'archived' && (cap.sourceBytes ?? 0) <= INLINE_INGEST_MAX_BYTES) {
|
|
22
|
+
const ing = ingestArchive(cap.archiveId);
|
|
23
|
+
if (ing.status === 'indexed') {
|
|
24
|
+
out.events = ing.events;
|
|
25
|
+
out.secrets_redacted = ing.secretsRedacted;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const note = firstCaptureNote();
|
|
29
|
+
if (note) {
|
|
30
|
+
out.archive_dir = effectiveTranscriptsDir();
|
|
31
|
+
out.note = out.secrets_redacted
|
|
32
|
+
? `${note} (${out.secrets_redacted} secret-like string(s) were redacted from the index.)`
|
|
33
|
+
: note;
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=on-save.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"on-save.js","sourceRoot":"","sources":["../../src/transcripts/on-save.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,yEAAyE;AAEzE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,6EAA6E;AAC7E,gFAAgF;AAChF,sDAAsD;AACtD,MAAM,uBAAuB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAUjD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAmE;IACrG,IAAI,CAAC,gBAAgB,EAAE;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IACvH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC,CAAC,oEAAoE;IAE9F,MAAM,GAAG,GAAqB,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;IAErD,0EAA0E;IAC1E,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,uBAAuB,EAAE,CAAC;QACpG,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;YACxB,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC,eAAe,CAAC;QAC7C,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,gBAAgB,EAAE,CAAC;IAChC,IAAI,IAAI,EAAE,CAAC;QACT,GAAG,CAAC,WAAW,GAAG,uBAAuB,EAAE,CAAC;QAC5C,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,gBAAgB;YAC7B,CAAC,CAAC,GAAG,IAAI,KAAK,GAAG,CAAC,gBAAgB,uDAAuD;YACzF,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const EDIT_TOOLS: Set<string>;
|
|
2
|
+
export declare const FILE_RE: RegExp;
|
|
3
|
+
export declare const COMMAND_RE: RegExp;
|
|
4
|
+
export declare const ERROR_RE: RegExp;
|
|
5
|
+
export type SpineEntry = {
|
|
6
|
+
seq: number;
|
|
7
|
+
role: string;
|
|
8
|
+
kind: string;
|
|
9
|
+
ts: string | null;
|
|
10
|
+
text: string;
|
|
11
|
+
};
|
|
12
|
+
export type SessionFacts = {
|
|
13
|
+
archiveId: string;
|
|
14
|
+
firstTs: string | null;
|
|
15
|
+
lastTs: string | null;
|
|
16
|
+
counts: {
|
|
17
|
+
user: number;
|
|
18
|
+
assistant: number;
|
|
19
|
+
tool_calls: number;
|
|
20
|
+
tool_results: number;
|
|
21
|
+
reasoning: number;
|
|
22
|
+
events: number;
|
|
23
|
+
};
|
|
24
|
+
tools: {
|
|
25
|
+
name: string;
|
|
26
|
+
count: number;
|
|
27
|
+
}[];
|
|
28
|
+
files: string[];
|
|
29
|
+
commands: string[];
|
|
30
|
+
errorCount: number;
|
|
31
|
+
};
|
|
32
|
+
/** L2 — the conversation spine (user + assistant text, in order). */
|
|
33
|
+
export declare function buildSpine(archiveId: string): SpineEntry[];
|
|
34
|
+
/** L1 — deterministic facts about the session. */
|
|
35
|
+
export declare function buildFacts(archiveId: string): SessionFacts;
|
|
36
|
+
/** Compact human/AI-readable rendering of L1 for resume injection. */
|
|
37
|
+
export declare function renderFacts(f: SessionFacts): string;
|
|
38
|
+
//# sourceMappingURL=pyramid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pyramid.d.ts","sourceRoot":"","sources":["../../src/transcripts/pyramid.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,UAAU,aAA0D,CAAC;AAClF,eAAO,MAAM,OAAO,QAAqE,CAAC;AAC1F,eAAO,MAAM,UAAU,QAA8B,CAAC;AACtD,eAAO,MAAM,QAAQ,QAAmH,CAAC;AAEzI,MAAM,MAAM,UAAU,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtG,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzH,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,EAAE,CAc1D;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CA4D1D;AAED,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CASnD"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Memory pyramid L1 (facts) + L2 (spine) (VERSION-3 item 6, Step 8).
|
|
2
|
+
//
|
|
3
|
+
// Both are DERIVED from the already-masked events table — no new storage. L3 (the
|
|
4
|
+
// abstract summary) is what veto_session_save already stores.
|
|
5
|
+
// • L1 facts: deterministic aggregates — files touched, commands, tool usage,
|
|
6
|
+
// error count, message counts, timespan. Cheap, no LLM. Injected on resume.
|
|
7
|
+
// • L2 spine: user messages verbatim + assistant conclusions, tool chatter
|
|
8
|
+
// stripped. The searchable conversation backbone (the portable index indexes it in Step 10).
|
|
9
|
+
import { getTranscriptsDb } from './store.js';
|
|
10
|
+
// Shared deterministic extractors (reused by the TOC in Step 9).
|
|
11
|
+
export const EDIT_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);
|
|
12
|
+
export const FILE_RE = /"(?:file_path|filePath|notebook_path|file|path)"\s*:\s*"([^"]+)"/;
|
|
13
|
+
export const COMMAND_RE = /"command"\s*:\s*"([^"]+)"/;
|
|
14
|
+
export const ERROR_RE = /\b(error|errno|failed|failure|exception|traceback|not found|cannot|denied|refused|non-zero|exit code [1-9])\b/i;
|
|
15
|
+
/** L2 — the conversation spine (user + assistant text, in order). */
|
|
16
|
+
export function buildSpine(archiveId) {
|
|
17
|
+
const db = getTranscriptsDb();
|
|
18
|
+
const rows = db.prepare(`SELECT seq, role, kind, ts_utc, text FROM events
|
|
19
|
+
WHERE archive_id = ? AND kind IN ('user_message','assistant_message')
|
|
20
|
+
ORDER BY seq`).all(archiveId);
|
|
21
|
+
return rows.map(r => ({
|
|
22
|
+
seq: r.seq,
|
|
23
|
+
role: r.role ?? (r.kind === 'user_message' ? 'user' : 'assistant'),
|
|
24
|
+
kind: r.kind,
|
|
25
|
+
ts: r.ts_utc,
|
|
26
|
+
text: r.text ?? '',
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
/** L1 — deterministic facts about the session. */
|
|
30
|
+
export function buildFacts(archiveId) {
|
|
31
|
+
const db = getTranscriptsDb();
|
|
32
|
+
const rows = db.prepare(`SELECT kind, tool_name, text, ts_utc FROM events WHERE archive_id = ? ORDER BY seq`).all(archiveId);
|
|
33
|
+
const counts = { user: 0, assistant: 0, tool_calls: 0, tool_results: 0, reasoning: 0, events: rows.length };
|
|
34
|
+
const toolCounts = new Map();
|
|
35
|
+
const files = new Set();
|
|
36
|
+
const commands = [];
|
|
37
|
+
let errorCount = 0;
|
|
38
|
+
let firstTs = null;
|
|
39
|
+
let lastTs = null;
|
|
40
|
+
for (const r of rows) {
|
|
41
|
+
if (r.ts_utc) {
|
|
42
|
+
if (!firstTs || r.ts_utc < firstTs)
|
|
43
|
+
firstTs = r.ts_utc;
|
|
44
|
+
if (!lastTs || r.ts_utc > lastTs)
|
|
45
|
+
lastTs = r.ts_utc;
|
|
46
|
+
}
|
|
47
|
+
switch (r.kind) {
|
|
48
|
+
case 'user_message':
|
|
49
|
+
counts.user++;
|
|
50
|
+
break;
|
|
51
|
+
case 'assistant_message':
|
|
52
|
+
counts.assistant++;
|
|
53
|
+
break;
|
|
54
|
+
case 'reasoning':
|
|
55
|
+
counts.reasoning++;
|
|
56
|
+
break;
|
|
57
|
+
case 'tool_result':
|
|
58
|
+
counts.tool_results++;
|
|
59
|
+
if (r.text && ERROR_RE.test(r.text))
|
|
60
|
+
errorCount++;
|
|
61
|
+
break;
|
|
62
|
+
case 'tool_call': {
|
|
63
|
+
counts.tool_calls++;
|
|
64
|
+
const name = r.tool_name ?? 'tool';
|
|
65
|
+
toolCounts.set(name, (toolCounts.get(name) ?? 0) + 1);
|
|
66
|
+
if (r.text) {
|
|
67
|
+
if (EDIT_TOOLS.has(name)) {
|
|
68
|
+
const m = r.text.match(FILE_RE);
|
|
69
|
+
if (m)
|
|
70
|
+
files.add(m[1]);
|
|
71
|
+
}
|
|
72
|
+
else if (name === 'Bash') {
|
|
73
|
+
const m = r.text.match(COMMAND_RE);
|
|
74
|
+
if (m)
|
|
75
|
+
commands.push(m[1].slice(0, 200));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
default: break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const tools = [...toolCounts.entries()]
|
|
84
|
+
.map(([name, count]) => ({ name, count }))
|
|
85
|
+
.sort((a, b) => b.count - a.count);
|
|
86
|
+
return {
|
|
87
|
+
archiveId,
|
|
88
|
+
firstTs,
|
|
89
|
+
lastTs,
|
|
90
|
+
counts,
|
|
91
|
+
tools,
|
|
92
|
+
files: [...files],
|
|
93
|
+
commands: [...new Set(commands)],
|
|
94
|
+
errorCount,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Compact human/AI-readable rendering of L1 for resume injection. */
|
|
98
|
+
export function renderFacts(f) {
|
|
99
|
+
const lines = [];
|
|
100
|
+
lines.push(`events=${f.counts.events} (user ${f.counts.user} · assistant ${f.counts.assistant} · tools ${f.counts.tool_calls})`);
|
|
101
|
+
if (f.firstTs)
|
|
102
|
+
lines.push(`span=${f.firstTs} → ${f.lastTs}`);
|
|
103
|
+
if (f.tools.length)
|
|
104
|
+
lines.push(`tools: ${f.tools.slice(0, 6).map(t => `${t.name}×${t.count}`).join(', ')}`);
|
|
105
|
+
if (f.files.length)
|
|
106
|
+
lines.push(`files: ${f.files.slice(0, 20).join(', ')}`);
|
|
107
|
+
if (f.commands.length)
|
|
108
|
+
lines.push(`commands: ${f.commands.slice(0, 10).join(' | ')}`);
|
|
109
|
+
if (f.errorCount)
|
|
110
|
+
lines.push(`errors observed: ${f.errorCount}`);
|
|
111
|
+
return lines.join('\n');
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=pyramid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pyramid.js","sourceRoot":"","sources":["../../src/transcripts/pyramid.ts"],"names":[],"mappings":"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"}
|