@jigyasudham/veto 3.1.2 → 3.2.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 +26 -1
- package/dist/cli.js +51 -1
- package/dist/cli.js.map +1 -1
- package/dist/host.d.ts +23 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +81 -0
- package/dist/host.js.map +1 -0
- package/dist/server/handlers/observability.d.ts.map +1 -1
- package/dist/server/handlers/observability.js +16 -1
- package/dist/server/handlers/observability.js.map +1 -1
- package/dist/server/handlers/session.d.ts.map +1 -1
- package/dist/server/handlers/session.js +18 -5
- package/dist/server/handlers/session.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +17 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/definitions.d.ts +3 -3
- package/dist/tools/definitions.js +3 -3
- package/dist/tools/definitions.js.map +1 -1
- package/dist/transcripts/adapters/claude.d.ts +2 -24
- package/dist/transcripts/adapters/claude.d.ts.map +1 -1
- package/dist/transcripts/adapters/claude.js +31 -129
- package/dist/transcripts/adapters/claude.js.map +1 -1
- package/dist/transcripts/adapters/codex.d.ts +4 -0
- package/dist/transcripts/adapters/codex.d.ts.map +1 -0
- package/dist/transcripts/adapters/codex.js +120 -0
- package/dist/transcripts/adapters/codex.js.map +1 -0
- package/dist/transcripts/adapters/gemini.d.ts +4 -0
- package/dist/transcripts/adapters/gemini.d.ts.map +1 -0
- package/dist/transcripts/adapters/gemini.js +124 -0
- package/dist/transcripts/adapters/gemini.js.map +1 -0
- package/dist/transcripts/adapters/index.d.ts +16 -0
- package/dist/transcripts/adapters/index.d.ts.map +1 -0
- package/dist/transcripts/adapters/index.js +36 -0
- package/dist/transcripts/adapters/index.js.map +1 -0
- package/dist/transcripts/adapters/jsonl.d.ts +67 -0
- package/dist/transcripts/adapters/jsonl.d.ts.map +1 -0
- package/dist/transcripts/adapters/jsonl.js +138 -0
- package/dist/transcripts/adapters/jsonl.js.map +1 -0
- package/dist/transcripts/archive.d.ts.map +1 -1
- package/dist/transcripts/archive.js +2 -1
- package/dist/transcripts/archive.js.map +1 -1
- package/dist/transcripts/discover.d.ts +50 -0
- package/dist/transcripts/discover.d.ts.map +1 -0
- package/dist/transcripts/discover.js +247 -0
- package/dist/transcripts/discover.js.map +1 -0
- package/dist/transcripts/expand.d.ts +33 -2
- package/dist/transcripts/expand.d.ts.map +1 -1
- package/dist/transcripts/expand.js +95 -9
- package/dist/transcripts/expand.js.map +1 -1
- package/dist/transcripts/ingest.js +2 -2
- package/dist/transcripts/ingest.js.map +1 -1
- package/dist/transcripts/mapping.d.ts +8 -0
- package/dist/transcripts/mapping.d.ts.map +1 -1
- package/dist/transcripts/mapping.js +1 -1
- package/dist/transcripts/mapping.js.map +1 -1
- package/dist/transcripts/metric.d.ts +80 -0
- package/dist/transcripts/metric.d.ts.map +1 -0
- package/dist/transcripts/metric.js +306 -0
- package/dist/transcripts/metric.js.map +1 -0
- package/dist/transcripts/on-save.d.ts +18 -0
- package/dist/transcripts/on-save.d.ts.map +1 -1
- package/dist/transcripts/on-save.js +41 -2
- package/dist/transcripts/on-save.js.map +1 -1
- package/dist/transcripts/pyramid.d.ts +8 -0
- package/dist/transcripts/pyramid.d.ts.map +1 -1
- package/dist/transcripts/pyramid.js +40 -6
- package/dist/transcripts/pyramid.js.map +1 -1
- package/dist/transcripts/recall.d.ts +2 -0
- package/dist/transcripts/recall.d.ts.map +1 -1
- package/dist/transcripts/recall.js +36 -12
- package/dist/transcripts/recall.js.map +1 -1
- package/dist/transcripts/search.d.ts.map +1 -1
- package/dist/transcripts/search.js +20 -11
- package/dist/transcripts/search.js.map +1 -1
- package/dist/transcripts/toc.js +3 -4
- package/dist/transcripts/toc.js.map +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Below this many eligible resumes the result is noise, and reports as such. */
|
|
2
|
+
export declare const MIN_ELIGIBLE_RESUMES = 30;
|
|
3
|
+
/** A window shorter than this cannot show a habit, only a mood. */
|
|
4
|
+
export declare const MIN_WINDOW_DAYS = 30;
|
|
5
|
+
/** Eligible resumes that reach for recall. */
|
|
6
|
+
export declare const ATTACH_TARGET = 0.3;
|
|
7
|
+
/**
|
|
8
|
+
* Ceiling on queries the AI had to REFORMULATE.
|
|
9
|
+
*
|
|
10
|
+
* This deliberately replaced an "expansion rate >= 50%" target, which was
|
|
11
|
+
* self-defeating: it assumed opening an excerpt is the desired outcome, so
|
|
12
|
+
* improving phase 1 until its snippets answer the question outright would have
|
|
13
|
+
* DRIVEN THE NUMBER DOWN and scored the improvement as a failure. Expansion is
|
|
14
|
+
* still reported, but it is not a target.
|
|
15
|
+
*
|
|
16
|
+
* Reformulation is gated instead because it is the one unambiguous signal in the
|
|
17
|
+
* data: a query followed by another query means phase 1 did not serve the first
|
|
18
|
+
* one. A query followed by nothing cannot be read — satisfied and gave-up look
|
|
19
|
+
* identical from here — so it is reported as `silent` and never scored.
|
|
20
|
+
*/
|
|
21
|
+
export declare const REFORMULATION_CEILING = 0.4;
|
|
22
|
+
/**
|
|
23
|
+
* How long after a resume a recall query still counts as belonging to it (and
|
|
24
|
+
* likewise a query → its expansion). Long enough for a real working session,
|
|
25
|
+
* short enough that tomorrow's query is not credited to yesterday's resume.
|
|
26
|
+
*/
|
|
27
|
+
export declare const ATTRIBUTION_WINDOW_HOURS = 6;
|
|
28
|
+
export type MetricVerdict = 'insufficient_data' | 'healthy' | 'not_reached_for' | 'retrieval_not_landing';
|
|
29
|
+
export type RecallMetric = {
|
|
30
|
+
window: {
|
|
31
|
+
from: string | null;
|
|
32
|
+
to: string | null;
|
|
33
|
+
days: number;
|
|
34
|
+
};
|
|
35
|
+
coverage: {
|
|
36
|
+
captureEnabled: boolean;
|
|
37
|
+
captureEnabledAt: string | null;
|
|
38
|
+
archives: number;
|
|
39
|
+
events: number;
|
|
40
|
+
projectsWithArchives: string[];
|
|
41
|
+
};
|
|
42
|
+
resumes: {
|
|
43
|
+
total: number;
|
|
44
|
+
inWindow: number;
|
|
45
|
+
eligible: number;
|
|
46
|
+
ineligible: number;
|
|
47
|
+
unscoped: number;
|
|
48
|
+
};
|
|
49
|
+
attach: {
|
|
50
|
+
attached: number;
|
|
51
|
+
rate: number | null;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Every query lands in exactly one bucket. `silent` is deliberately NOT scored:
|
|
55
|
+
* a query followed by nothing is indistinguishable between "phase 1 answered
|
|
56
|
+
* it" and "the AI gave up", and pretending otherwise would invent a number.
|
|
57
|
+
*/
|
|
58
|
+
queries: {
|
|
59
|
+
total: number;
|
|
60
|
+
expanded: number;
|
|
61
|
+
reformulated: number;
|
|
62
|
+
silent: number;
|
|
63
|
+
expansionRate: number | null;
|
|
64
|
+
reformulationRate: number | null;
|
|
65
|
+
};
|
|
66
|
+
depth: {
|
|
67
|
+
expands: number;
|
|
68
|
+
perExpandingQuery: number | null;
|
|
69
|
+
distinctSessionsExpanded: number;
|
|
70
|
+
};
|
|
71
|
+
verdict: {
|
|
72
|
+
code: MetricVerdict;
|
|
73
|
+
headline: string;
|
|
74
|
+
detail: string;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
export declare function recallMetric(): RecallMetric;
|
|
78
|
+
/** Human-readable report for `veto transcripts metric`. */
|
|
79
|
+
export declare function renderRecallMetric(m: RecallMetric): string;
|
|
80
|
+
//# sourceMappingURL=metric.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metric.d.ts","sourceRoot":"","sources":["../../src/transcripts/metric.ts"],"names":[],"mappings":"AAsCA,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,mEAAmE;AACnE,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,8CAA8C;AAC9C,eAAO,MAAM,aAAa,MAAO,CAAC;AAClC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,MAAO,CAAC;AAC1C;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAI1C,MAAM,MAAM,aAAa,GACrB,mBAAmB,GACnB,SAAS,GACT,iBAAiB,GACjB,uBAAuB,CAAC;AAE5B,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,QAAQ,EAAE;QACR,cAAc,EAAE,OAAO,CAAC;QACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,oBAAoB,EAAE,MAAM,EAAE,CAAC;KAChC,CAAC;IACF,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAClD;;;;OAIG;IACH,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,CAAC;IACF,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,wBAAwB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/F,OAAO,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACpE,CAAC;AAyCF,wBAAgB,YAAY,IAAI,YAAY,CA2J3C;AAID,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAkC1D"}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
// The v3.0 success metric — "does deep recall actually get used?" (VERSION-3).
|
|
2
|
+
//
|
|
3
|
+
// v3.0 shipped without a confirmed success metric, so three releases later there
|
|
4
|
+
// was no measure of whether transcript recall is reached for at all. This
|
|
5
|
+
// computes that measure.
|
|
6
|
+
//
|
|
7
|
+
// IT IS A HEALTH CHECK, NOT THE v3.2 GATE. v3.2 (lesson mining) was written as
|
|
8
|
+
// gated on "once recall proves demand", and it is tempting to point this number
|
|
9
|
+
// at that gate. Do not: it reads ONE local database, and no threshold on a
|
|
10
|
+
// single user — who is also the person who built the feature — can become market
|
|
11
|
+
// evidence. Council 06b7b55c (YELLOW, 2026-08-22) was explicit that binding v3.2
|
|
12
|
+
// to this number would leave v3.2 blocked on something that can never be
|
|
13
|
+
// representative. Demand evidence lives outside the product.
|
|
14
|
+
//
|
|
15
|
+
// NOTHING NEW IS INSTRUMENTED. Every tool call is already traced into veto.db's
|
|
16
|
+
// tool_call_trace_log, and archives.captured_at already says when recall became
|
|
17
|
+
// possible, so the metric is derived from data that exists rather than from new
|
|
18
|
+
// telemetry. Only the SHAPE of a recall call is read (which keys the args had),
|
|
19
|
+
// never the query text — the metric must not become a second copy of what the
|
|
20
|
+
// user searched for.
|
|
21
|
+
//
|
|
22
|
+
// WHAT THIS CAN AND CANNOT ANSWER. It reads one machine's local database and
|
|
23
|
+
// nothing is ever uploaded, by design. So it answers "is recall being used HERE"
|
|
24
|
+
// — dogfooding, and whether the two-call loop completes at all. It cannot
|
|
25
|
+
// measure market demand; that evidence lives outside the product (downloads,
|
|
26
|
+
// issues, user reports) and the v3.2 gate should not be argued from this number
|
|
27
|
+
// alone. Any user can run it against their own data.
|
|
28
|
+
import { getDb } from '../memory/local.js';
|
|
29
|
+
import { normalizeProjectDir } from '../memory/local.js';
|
|
30
|
+
import { getTranscriptsDb, transcriptsAvailable } from './store.js';
|
|
31
|
+
import { getConfig } from '../memory/config.js';
|
|
32
|
+
// ─── Pre-registered thresholds ───────────────────────────────────────────────
|
|
33
|
+
// Fixed BEFORE the numbers are read so the gate cannot be argued backwards from
|
|
34
|
+
// whatever the data happens to say. They are a judgement call, not a discovery;
|
|
35
|
+
// change them deliberately and in the open, not to make a release pass.
|
|
36
|
+
/** Below this many eligible resumes the result is noise, and reports as such. */
|
|
37
|
+
export const MIN_ELIGIBLE_RESUMES = 30;
|
|
38
|
+
/** A window shorter than this cannot show a habit, only a mood. */
|
|
39
|
+
export const MIN_WINDOW_DAYS = 30;
|
|
40
|
+
/** Eligible resumes that reach for recall. */
|
|
41
|
+
export const ATTACH_TARGET = 0.30;
|
|
42
|
+
/**
|
|
43
|
+
* Ceiling on queries the AI had to REFORMULATE.
|
|
44
|
+
*
|
|
45
|
+
* This deliberately replaced an "expansion rate >= 50%" target, which was
|
|
46
|
+
* self-defeating: it assumed opening an excerpt is the desired outcome, so
|
|
47
|
+
* improving phase 1 until its snippets answer the question outright would have
|
|
48
|
+
* DRIVEN THE NUMBER DOWN and scored the improvement as a failure. Expansion is
|
|
49
|
+
* still reported, but it is not a target.
|
|
50
|
+
*
|
|
51
|
+
* Reformulation is gated instead because it is the one unambiguous signal in the
|
|
52
|
+
* data: a query followed by another query means phase 1 did not serve the first
|
|
53
|
+
* one. A query followed by nothing cannot be read — satisfied and gave-up look
|
|
54
|
+
* identical from here — so it is reported as `silent` and never scored.
|
|
55
|
+
*/
|
|
56
|
+
export const REFORMULATION_CEILING = 0.40;
|
|
57
|
+
/**
|
|
58
|
+
* How long after a resume a recall query still counts as belonging to it (and
|
|
59
|
+
* likewise a query → its expansion). Long enough for a real working session,
|
|
60
|
+
* short enough that tomorrow's query is not credited to yesterday's resume.
|
|
61
|
+
*/
|
|
62
|
+
export const ATTRIBUTION_WINDOW_HOURS = 6;
|
|
63
|
+
const RESUME_TOOLS = ['veto_continue', 'veto_session_restore'];
|
|
64
|
+
/** SQLite `datetime('now')` text and ISO timestamps compared on one scale (both UTC). */
|
|
65
|
+
function toSqlTime(iso) {
|
|
66
|
+
return iso.replace('T', ' ').slice(0, 19);
|
|
67
|
+
}
|
|
68
|
+
function hoursBetween(a, b) {
|
|
69
|
+
const ta = Date.parse(a.replace(' ', 'T') + 'Z');
|
|
70
|
+
const tb = Date.parse(b.replace(' ', 'T') + 'Z');
|
|
71
|
+
if (Number.isNaN(ta) || Number.isNaN(tb))
|
|
72
|
+
return Number.POSITIVE_INFINITY;
|
|
73
|
+
return (tb - ta) / 3_600_000;
|
|
74
|
+
}
|
|
75
|
+
/** Recall calls split by which phase of the two-call loop they are. */
|
|
76
|
+
function classifyReplay(argsJson) {
|
|
77
|
+
if (!argsJson)
|
|
78
|
+
return 'legacy';
|
|
79
|
+
try {
|
|
80
|
+
const a = JSON.parse(argsJson);
|
|
81
|
+
if (a.expand && typeof a.expand === 'object')
|
|
82
|
+
return 'expand';
|
|
83
|
+
if (a.query)
|
|
84
|
+
return 'query';
|
|
85
|
+
}
|
|
86
|
+
catch { /* unparseable args are not recall */ }
|
|
87
|
+
return 'legacy'; // the unrelated session_id tool-call trace mode
|
|
88
|
+
}
|
|
89
|
+
/** Which archived session an expand opened, when it named one (for depth). */
|
|
90
|
+
function expandTarget(argsJson) {
|
|
91
|
+
if (!argsJson)
|
|
92
|
+
return null;
|
|
93
|
+
try {
|
|
94
|
+
const a = JSON.parse(argsJson);
|
|
95
|
+
const e = a.expand;
|
|
96
|
+
if (!e)
|
|
97
|
+
return null;
|
|
98
|
+
if (typeof e.source_session_id === 'string')
|
|
99
|
+
return e.source_session_id;
|
|
100
|
+
if (typeof e.archive_id === 'string')
|
|
101
|
+
return e.archive_id;
|
|
102
|
+
if (typeof e.event_id === 'string')
|
|
103
|
+
return `event:${e.event_id}`;
|
|
104
|
+
}
|
|
105
|
+
catch { /* ignore */ }
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
export function recallMetric() {
|
|
109
|
+
const db = getDb();
|
|
110
|
+
const cfg = getConfig().transcripts;
|
|
111
|
+
// ── Coverage: when did recall become possible, and over how much?
|
|
112
|
+
let archives = [];
|
|
113
|
+
let events = 0;
|
|
114
|
+
if (transcriptsAvailable()) {
|
|
115
|
+
try {
|
|
116
|
+
const tdb = getTranscriptsDb();
|
|
117
|
+
archives = tdb.prepare('SELECT project_dir, captured_at FROM archives').all();
|
|
118
|
+
events = tdb.prepare('SELECT COUNT(*) AS n FROM events').get().n;
|
|
119
|
+
}
|
|
120
|
+
catch { /* sidecar unreadable — reported as zero coverage below */ }
|
|
121
|
+
}
|
|
122
|
+
// Recall is only possible for a project from the moment it first has an archive.
|
|
123
|
+
const firstArchiveByProject = new Map();
|
|
124
|
+
for (const a of archives) {
|
|
125
|
+
if (!a.project_dir)
|
|
126
|
+
continue;
|
|
127
|
+
const p = normalizeProjectDir(a.project_dir);
|
|
128
|
+
const t = toSqlTime(a.captured_at);
|
|
129
|
+
const prev = firstArchiveByProject.get(p);
|
|
130
|
+
if (!prev || t < prev)
|
|
131
|
+
firstArchiveByProject.set(p, t);
|
|
132
|
+
}
|
|
133
|
+
const earliestArchive = [...firstArchiveByProject.values()].sort()[0] ?? null;
|
|
134
|
+
// ── Traces. Resumes carry the veto session id, which resolves the project.
|
|
135
|
+
const resumeRows = db.prepare(`SELECT t.recorded_at, t.tool_name, t.args_json, s.project_dir
|
|
136
|
+
FROM tool_call_trace_log t
|
|
137
|
+
LEFT JOIN sessions s ON s.id = t.session_id
|
|
138
|
+
WHERE t.tool_name IN (${RESUME_TOOLS.map(() => '?').join(',')})
|
|
139
|
+
AND t.result_status = 'success'
|
|
140
|
+
ORDER BY t.recorded_at`).all(...RESUME_TOOLS);
|
|
141
|
+
const replayRows = db.prepare(`SELECT recorded_at, tool_name, args_json, NULL AS project_dir
|
|
142
|
+
FROM tool_call_trace_log
|
|
143
|
+
WHERE tool_name = 'veto_session_replay' AND result_status = 'success'
|
|
144
|
+
ORDER BY recorded_at`).all();
|
|
145
|
+
const queries = replayRows.filter(r => classifyReplay(r.args_json) === 'query');
|
|
146
|
+
const expands = replayRows.filter(r => classifyReplay(r.args_json) === 'expand');
|
|
147
|
+
// ── Eligibility. A resume counts only if recall could have helped it: it
|
|
148
|
+
// happened after that project's first archive existed. Resumes whose project
|
|
149
|
+
// cannot be resolved are excluded from BOTH sides rather than guessed at.
|
|
150
|
+
let inWindow = 0, eligible = 0, ineligible = 0, unscoped = 0;
|
|
151
|
+
const eligibleTimes = [];
|
|
152
|
+
for (const r of resumeRows) {
|
|
153
|
+
if (!earliestArchive || r.recorded_at < earliestArchive)
|
|
154
|
+
continue;
|
|
155
|
+
inWindow++;
|
|
156
|
+
if (!r.project_dir) {
|
|
157
|
+
unscoped++;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const first = firstArchiveByProject.get(normalizeProjectDir(r.project_dir));
|
|
161
|
+
if (first && r.recorded_at >= first) {
|
|
162
|
+
eligible++;
|
|
163
|
+
eligibleTimes.push(r.recorded_at);
|
|
164
|
+
}
|
|
165
|
+
else
|
|
166
|
+
ineligible++;
|
|
167
|
+
}
|
|
168
|
+
// ── Attach: an eligible resume followed by a recall query, before the next
|
|
169
|
+
// resume and inside the attribution window.
|
|
170
|
+
const resumeTimes = resumeRows.map(r => r.recorded_at);
|
|
171
|
+
let attached = 0;
|
|
172
|
+
for (const t of eligibleTimes) {
|
|
173
|
+
const nextResume = resumeTimes.find(x => x > t) ?? null;
|
|
174
|
+
const hit = queries.some(q => q.recorded_at > t
|
|
175
|
+
&& (!nextResume || q.recorded_at < nextResume)
|
|
176
|
+
&& hoursBetween(t, q.recorded_at) <= ATTRIBUTION_WINDOW_HOURS);
|
|
177
|
+
if (hit)
|
|
178
|
+
attached++;
|
|
179
|
+
}
|
|
180
|
+
// ── What became of each query. Exactly one bucket each:
|
|
181
|
+
// expanded — led to opening an exact excerpt
|
|
182
|
+
// reformulated — followed by ANOTHER query in the window: phase 1 failed it
|
|
183
|
+
// silent — neither; unreadable, so never scored (see REFORMULATION_CEILING)
|
|
184
|
+
const queryTimes = queries.map(q => q.recorded_at);
|
|
185
|
+
let expandedQueries = 0, reformulated = 0;
|
|
186
|
+
for (const t of queryTimes) {
|
|
187
|
+
const nextQuery = queryTimes.find(x => x > t) ?? null;
|
|
188
|
+
const expandedHere = expands.some(e => e.recorded_at > t
|
|
189
|
+
&& (!nextQuery || e.recorded_at < nextQuery)
|
|
190
|
+
&& hoursBetween(t, e.recorded_at) <= ATTRIBUTION_WINDOW_HOURS);
|
|
191
|
+
if (expandedHere) {
|
|
192
|
+
expandedQueries++;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (nextQuery && hoursBetween(t, nextQuery) <= ATTRIBUTION_WINDOW_HOURS)
|
|
196
|
+
reformulated++;
|
|
197
|
+
}
|
|
198
|
+
const silent = queries.length - expandedQueries - reformulated;
|
|
199
|
+
const distinctExpanded = new Set(expands.map(e => expandTarget(e.args_json)).filter(Boolean)).size;
|
|
200
|
+
const from = earliestArchive;
|
|
201
|
+
const to = resumeRows.length || replayRows.length
|
|
202
|
+
? [...resumeRows, ...replayRows].map(r => r.recorded_at).sort().slice(-1)[0]
|
|
203
|
+
: null;
|
|
204
|
+
const days = from && to ? Math.max(0, Math.round(hoursBetween(from, to) / 24)) : 0;
|
|
205
|
+
const attachRate = eligible > 0 ? attached / eligible : null;
|
|
206
|
+
const expansionRate = queries.length > 0 ? expandedQueries / queries.length : null;
|
|
207
|
+
const reformulationRate = queries.length > 0 ? reformulated / queries.length : null;
|
|
208
|
+
// ── Verdict. Sample size is checked FIRST: a rate over three resumes is a
|
|
209
|
+
// number, not evidence, and reporting it as a verdict is how a metric starts
|
|
210
|
+
// lying.
|
|
211
|
+
let code;
|
|
212
|
+
let headline;
|
|
213
|
+
let detail;
|
|
214
|
+
if (eligible < MIN_ELIGIBLE_RESUMES || days < MIN_WINDOW_DAYS) {
|
|
215
|
+
code = 'insufficient_data';
|
|
216
|
+
headline = 'Not enough data to judge yet';
|
|
217
|
+
detail = `Needs ${MIN_ELIGIBLE_RESUMES} eligible resumes over ${MIN_WINDOW_DAYS} days; have ${eligible} over ${days}.`
|
|
218
|
+
+ (expands.length === 0 && queries.length > 0
|
|
219
|
+
? ` Observation, not a verdict: ${queries.length} recall quer${queries.length === 1 ? 'y has' : 'ies have'} run and none opened an excerpt.`
|
|
220
|
+
+ ` That is only a defect if phase 1 was not already enough — which this data cannot tell you.`
|
|
221
|
+
: '');
|
|
222
|
+
}
|
|
223
|
+
else if ((attachRate ?? 0) < ATTACH_TARGET) {
|
|
224
|
+
code = 'not_reached_for';
|
|
225
|
+
headline = 'Recall is available but rarely reached for';
|
|
226
|
+
detail = `Attach rate ${(100 * (attachRate ?? 0)).toFixed(0)}% is below the ${(100 * ATTACH_TARGET).toFixed(0)}% target.`;
|
|
227
|
+
}
|
|
228
|
+
else if ((reformulationRate ?? 0) > REFORMULATION_CEILING) {
|
|
229
|
+
code = 'retrieval_not_landing';
|
|
230
|
+
headline = 'Recall is used, but queries keep having to be re-asked';
|
|
231
|
+
detail = `Attach rate ${(100 * (attachRate ?? 0)).toFixed(0)}% meets the target, but `
|
|
232
|
+
+ `${(100 * (reformulationRate ?? 0)).toFixed(0)}% of queries were reformulated `
|
|
233
|
+
+ `(ceiling ${(100 * REFORMULATION_CEILING).toFixed(0)}%) — phase 1 is not serving them.`;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
code = 'healthy';
|
|
237
|
+
headline = 'Deep recall is reached for, and phase 1 serves the query';
|
|
238
|
+
detail = `Attach ${(100 * (attachRate ?? 0)).toFixed(0)}% and reformulation `
|
|
239
|
+
+ `${(100 * (reformulationRate ?? 0)).toFixed(0)}% are both within target.`;
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
window: { from, to, days },
|
|
243
|
+
coverage: {
|
|
244
|
+
captureEnabled: cfg.enabled,
|
|
245
|
+
captureEnabledAt: cfg.consent_at,
|
|
246
|
+
archives: archives.length,
|
|
247
|
+
events,
|
|
248
|
+
projectsWithArchives: [...firstArchiveByProject.keys()],
|
|
249
|
+
},
|
|
250
|
+
resumes: { total: resumeRows.length, inWindow, eligible, ineligible, unscoped },
|
|
251
|
+
attach: { attached, rate: attachRate },
|
|
252
|
+
queries: {
|
|
253
|
+
total: queries.length, expanded: expandedQueries, reformulated, silent,
|
|
254
|
+
expansionRate, reformulationRate,
|
|
255
|
+
},
|
|
256
|
+
depth: {
|
|
257
|
+
expands: expands.length,
|
|
258
|
+
perExpandingQuery: expandedQueries > 0 ? expands.length / expandedQueries : null,
|
|
259
|
+
distinctSessionsExpanded: distinctExpanded,
|
|
260
|
+
},
|
|
261
|
+
verdict: { code, headline, detail },
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const pct = (r) => (r === null ? '—' : `${(100 * r).toFixed(0)}%`);
|
|
265
|
+
/** Human-readable report for `veto transcripts metric`. */
|
|
266
|
+
export function renderRecallMetric(m) {
|
|
267
|
+
const lines = [];
|
|
268
|
+
lines.push(' Deep-recall adoption — the v3.0 success metric');
|
|
269
|
+
lines.push(' ─────────────────────────────────────────────────────');
|
|
270
|
+
if (!m.coverage.captureEnabled) {
|
|
271
|
+
lines.push(' Transcript capture is OFF, so recall was never possible here.');
|
|
272
|
+
lines.push(' Nothing to measure until: veto transcripts enable');
|
|
273
|
+
return lines.join('\n');
|
|
274
|
+
}
|
|
275
|
+
lines.push(` Window: ${m.window.from ?? '(no archives yet)'} → ${m.window.to ?? '—'} (${m.window.days}d)`);
|
|
276
|
+
lines.push(` Coverage: ${m.coverage.archives} archive(s), ${m.coverage.events} events, `
|
|
277
|
+
+ `${m.coverage.projectsWithArchives.length} project(s)`);
|
|
278
|
+
lines.push('');
|
|
279
|
+
lines.push(` Resumes: ${m.resumes.eligible} eligible `
|
|
280
|
+
+ `(of ${m.resumes.inWindow} since recall became possible; `
|
|
281
|
+
+ `${m.resumes.ineligible} in projects with no archive, ${m.resumes.unscoped} unattributable)`);
|
|
282
|
+
lines.push(` Attach rate: ${pct(m.attach.rate)} ${m.attach.attached}/${m.resumes.eligible} eligible resumes reached for recall`
|
|
283
|
+
+ ` ${c(m.attach.rate, ATTACH_TARGET)} target ${pct(ATTACH_TARGET)}`);
|
|
284
|
+
lines.push(` Re-asked: ${pct(m.queries.reformulationRate)} ${m.queries.reformulated}/${m.queries.total} queries had to be reformulated`
|
|
285
|
+
+ ` ${c(m.queries.reformulationRate === null ? null : 1 - m.queries.reformulationRate, 1 - REFORMULATION_CEILING)} ceiling ${pct(REFORMULATION_CEILING)}`);
|
|
286
|
+
lines.push(` Of ${m.queries.total} quer${m.queries.total === 1 ? 'y' : 'ies'}: `
|
|
287
|
+
+ `${m.queries.expanded} opened an excerpt · ${m.queries.reformulated} re-asked · ${m.queries.silent} neither`);
|
|
288
|
+
lines.push(` Depth: ${m.depth.expands} expansion(s), `
|
|
289
|
+
+ `${m.depth.perExpandingQuery === null ? '—' : m.depth.perExpandingQuery.toFixed(1)} per expanding query, `
|
|
290
|
+
+ `${m.depth.distinctSessionsExpanded} distinct session(s) opened`);
|
|
291
|
+
lines.push('');
|
|
292
|
+
lines.push(` Verdict: ${m.verdict.headline}`);
|
|
293
|
+
lines.push(` ${m.verdict.detail}`);
|
|
294
|
+
lines.push('');
|
|
295
|
+
lines.push(' Reads THIS machine only — nothing is uploaded. It is a health check on');
|
|
296
|
+
lines.push(' whether recall works for you. It is NOT evidence of demand across users,');
|
|
297
|
+
lines.push(' and must not be used on its own to justify building on top of recall.');
|
|
298
|
+
lines.push(' "Neither" is not scored: satisfied and gave-up look identical from here.');
|
|
299
|
+
return lines.join('\n');
|
|
300
|
+
}
|
|
301
|
+
function c(rate, target) {
|
|
302
|
+
if (rate === null)
|
|
303
|
+
return ' ';
|
|
304
|
+
return rate >= target ? '✓' : '✗';
|
|
305
|
+
}
|
|
306
|
+
//# sourceMappingURL=metric.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metric.js","sourceRoot":"","sources":["../../src/transcripts/metric.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,30 @@
|
|
|
1
|
+
import { type TranscriptSource } from './adapters/index.js';
|
|
1
2
|
export type OnSaveTranscript = {
|
|
2
3
|
status: string;
|
|
4
|
+
source?: string;
|
|
3
5
|
events?: number;
|
|
4
6
|
secrets_redacted?: number;
|
|
5
7
|
archive_dir?: string;
|
|
6
8
|
note?: string;
|
|
7
9
|
};
|
|
10
|
+
/** Map a declared save platform onto a capture source; unknown platforms → claude. */
|
|
11
|
+
export declare function sourceForPlatform(platform?: string | null): TranscriptSource;
|
|
12
|
+
/**
|
|
13
|
+
* Which host's transcript to archive.
|
|
14
|
+
*
|
|
15
|
+
* The MCP handshake wins whenever it resolves, because capture is a question
|
|
16
|
+
* about WHICH PROCESS is hosting Veto — not about what the model believes it is.
|
|
17
|
+
* A model in Codex that leaves `platform` at its default would otherwise make
|
|
18
|
+
* Veto look for a Claude transcript and silently archive nothing.
|
|
19
|
+
*
|
|
20
|
+
* When the host is unrecognized (a client Veto has no marker for) the declared
|
|
21
|
+
* platform is the only signal left, so it is used; and if that is not a
|
|
22
|
+
* supported source either, capture is skipped rather than guessed.
|
|
23
|
+
*/
|
|
24
|
+
export declare function captureSourceFor(host: TranscriptSource | null, declaredPlatform?: string | null): TranscriptSource | null;
|
|
8
25
|
export declare function captureOnSave(opts: {
|
|
9
26
|
projectDir?: string | null;
|
|
10
27
|
vetoSessionId?: string | null;
|
|
28
|
+
platform?: string | null;
|
|
11
29
|
}): Promise<OnSaveTranscript | null>;
|
|
12
30
|
//# sourceMappingURL=on-save.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-save.d.ts","sourceRoot":"","sources":["../../src/transcripts/on-save.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"on-save.d.ts","sourceRoot":"","sources":["../../src/transcripts/on-save.ts"],"names":[],"mappings":"AAcA,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAOhF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,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,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,gBAAgB,CAG5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,gBAAgB,GAAG,IAAI,EAC7B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,GAC/B,gBAAgB,GAAG,IAAI,CAIzB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoCnC"}
|
|
@@ -3,20 +3,59 @@
|
|
|
3
3
|
// Runs capture, then (bounded) ingest so the save response can report the leak
|
|
4
4
|
// count and the one-time first-capture note. Never throws — the caller wraps it
|
|
5
5
|
// too, but this returns null on any problem so a save is never affected.
|
|
6
|
+
//
|
|
7
|
+
// The source is the platform the save declares (`veto_session_save`'s `platform`
|
|
8
|
+
// arg), because that is the CLI whose transcript the user is actually in. Claude
|
|
9
|
+
// Code publishes its own mapping from the statusline; Codex and Gemini have no
|
|
10
|
+
// such hook, so their mapping is discovered from disk first (see discover.ts).
|
|
6
11
|
import { isCaptureEnabled, firstCaptureNote, effectiveTranscriptsDir } from './config.js';
|
|
7
12
|
import { captureSession } from './archive.js';
|
|
8
13
|
import { ingestArchive } from './ingest.js';
|
|
14
|
+
import { isTranscriptSource } from './adapters/index.js';
|
|
9
15
|
// Don't parse+index a monster transcript inline on the save path (architect:
|
|
10
16
|
// keep save-time work bounded). Above this, capture still archives; indexing is
|
|
11
17
|
// deferred to first recall (Step 11's ensureIndexed).
|
|
12
18
|
const INLINE_INGEST_MAX_BYTES = 16 * 1024 * 1024;
|
|
19
|
+
/** Map a declared save platform onto a capture source; unknown platforms → claude. */
|
|
20
|
+
export function sourceForPlatform(platform) {
|
|
21
|
+
const p = (platform ?? '').trim().toLowerCase();
|
|
22
|
+
return isTranscriptSource(p) ? p : 'claude';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Which host's transcript to archive.
|
|
26
|
+
*
|
|
27
|
+
* The MCP handshake wins whenever it resolves, because capture is a question
|
|
28
|
+
* about WHICH PROCESS is hosting Veto — not about what the model believes it is.
|
|
29
|
+
* A model in Codex that leaves `platform` at its default would otherwise make
|
|
30
|
+
* Veto look for a Claude transcript and silently archive nothing.
|
|
31
|
+
*
|
|
32
|
+
* When the host is unrecognized (a client Veto has no marker for) the declared
|
|
33
|
+
* platform is the only signal left, so it is used; and if that is not a
|
|
34
|
+
* supported source either, capture is skipped rather than guessed.
|
|
35
|
+
*/
|
|
36
|
+
export function captureSourceFor(host, declaredPlatform) {
|
|
37
|
+
if (host)
|
|
38
|
+
return host;
|
|
39
|
+
const p = (declaredPlatform ?? '').trim().toLowerCase();
|
|
40
|
+
return isTranscriptSource(p) ? p : null;
|
|
41
|
+
}
|
|
13
42
|
export async function captureOnSave(opts) {
|
|
14
43
|
if (!isCaptureEnabled())
|
|
15
44
|
return null;
|
|
16
|
-
const
|
|
45
|
+
const source = sourceForPlatform(opts.platform);
|
|
46
|
+
// Codex/Gemini publish no session mapping of their own — find theirs on disk
|
|
47
|
+
// before capture looks one up. Bounded and best-effort.
|
|
48
|
+
if (source !== 'claude') {
|
|
49
|
+
try {
|
|
50
|
+
const { discoverSessions } = await import('./discover.js');
|
|
51
|
+
discoverSessions(source);
|
|
52
|
+
}
|
|
53
|
+
catch { /* discovery is best-effort; capture will just find no mapping */ }
|
|
54
|
+
}
|
|
55
|
+
const cap = await captureSession({ source, projectDir: opts.projectDir, vetoSessionId: opts.vetoSessionId });
|
|
17
56
|
if (!cap.ok)
|
|
18
57
|
return null; // skipped (no mapping / missing / too_large) or error — stay silent
|
|
19
|
-
const out = { status: cap.status };
|
|
58
|
+
const out = { status: cap.status, source };
|
|
20
59
|
// Index inline for small transcripts so we can report the leak count now.
|
|
21
60
|
if (cap.archiveId && cap.status === 'archived' && (cap.sourceBytes ?? 0) <= INLINE_INGEST_MAX_BYTES) {
|
|
22
61
|
const ing = ingestArchive(cap.archiveId);
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"on-save.js","sourceRoot":"","sources":["../../src/transcripts/on-save.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
export declare const EDIT_TOOLS: Set<string>;
|
|
2
|
+
export declare const SHELL_TOOLS: Set<string>;
|
|
2
3
|
export declare const FILE_RE: RegExp;
|
|
3
4
|
export declare const COMMAND_RE: RegExp;
|
|
5
|
+
export declare const PATCH_FILE_RE: RegExp;
|
|
4
6
|
export declare const ERROR_RE: RegExp;
|
|
7
|
+
/**
|
|
8
|
+
* File paths a tool-call digest touched. Handles both shapes an edit tool can
|
|
9
|
+
* take: JSON args with a path key (Claude, Gemini) and a patch body whose
|
|
10
|
+
* envelope names the files (Codex apply_patch, which can touch several at once).
|
|
11
|
+
*/
|
|
12
|
+
export declare function extractFiles(text: string): string[];
|
|
5
13
|
export type SpineEntry = {
|
|
6
14
|
seq: number;
|
|
7
15
|
role: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pyramid.d.ts","sourceRoot":"","sources":["../../src/transcripts/pyramid.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pyramid.d.ts","sourceRoot":"","sources":["../../src/transcripts/pyramid.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,UAAU,aAIrB,CAAC;AACH,eAAO,MAAM,WAAW,aAItB,CAAC;AACH,eAAO,MAAM,OAAO,QAAqE,CAAC;AAC1F,eAAO,MAAM,UAAU,QAA8B,CAAC;AAGtD,eAAO,MAAM,aAAa,QAA6C,CAAC;AACxE,eAAO,MAAM,QAAQ,QAAmH,CAAC;AAEzI;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAWnD;AAED,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,CA2D1D;AAED,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CASnD"}
|
|
@@ -7,11 +7,46 @@
|
|
|
7
7
|
// • L2 spine: user messages verbatim + assistant conclusions, tool chatter
|
|
8
8
|
// stripped. The searchable conversation backbone (the portable index indexes it in Step 10).
|
|
9
9
|
import { getTranscriptsDb } from './store.js';
|
|
10
|
-
// Shared deterministic extractors (reused by the TOC in Step 9).
|
|
11
|
-
|
|
10
|
+
// Shared deterministic extractors (reused by the TOC in Step 9). The tool names
|
|
11
|
+
// span all three hosts, because facts are derived from the normalized events and
|
|
12
|
+
// a Codex or Gemini session must yield the same file/command facts a Claude one
|
|
13
|
+
// does: Claude edits with Edit/Write, Codex with apply_patch, Gemini with
|
|
14
|
+
// replace/write_file — and each shells out under its own name.
|
|
15
|
+
export const EDIT_TOOLS = new Set([
|
|
16
|
+
'Edit', 'Write', 'MultiEdit', 'NotebookEdit', // claude
|
|
17
|
+
'apply_patch', // codex
|
|
18
|
+
'replace', 'write_file', // gemini
|
|
19
|
+
]);
|
|
20
|
+
export const SHELL_TOOLS = new Set([
|
|
21
|
+
'Bash', // claude
|
|
22
|
+
'shell_command', 'local_shell_call', // codex
|
|
23
|
+
'run_shell_command', // gemini
|
|
24
|
+
]);
|
|
12
25
|
export const FILE_RE = /"(?:file_path|filePath|notebook_path|file|path)"\s*:\s*"([^"]+)"/;
|
|
13
26
|
export const COMMAND_RE = /"command"\s*:\s*"([^"]+)"/;
|
|
27
|
+
// Codex's apply_patch takes a patch body rather than JSON args, so its file names
|
|
28
|
+
// only appear in the patch envelope.
|
|
29
|
+
export const PATCH_FILE_RE = /\*\*\* (?:Add|Update|Delete) File: (.+)/g;
|
|
14
30
|
export const ERROR_RE = /\b(error|errno|failed|failure|exception|traceback|not found|cannot|denied|refused|non-zero|exit code [1-9])\b/i;
|
|
31
|
+
/**
|
|
32
|
+
* File paths a tool-call digest touched. Handles both shapes an edit tool can
|
|
33
|
+
* take: JSON args with a path key (Claude, Gemini) and a patch body whose
|
|
34
|
+
* envelope names the files (Codex apply_patch, which can touch several at once).
|
|
35
|
+
*/
|
|
36
|
+
export function extractFiles(text) {
|
|
37
|
+
const out = [];
|
|
38
|
+
const m = text.match(FILE_RE);
|
|
39
|
+
if (m)
|
|
40
|
+
out.push(m[1]);
|
|
41
|
+
PATCH_FILE_RE.lastIndex = 0;
|
|
42
|
+
let p;
|
|
43
|
+
while ((p = PATCH_FILE_RE.exec(text)) !== null) {
|
|
44
|
+
const f = p[1].trim();
|
|
45
|
+
if (f)
|
|
46
|
+
out.push(f);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
15
50
|
/** L2 — the conversation spine (user + assistant text, in order). */
|
|
16
51
|
export function buildSpine(archiveId) {
|
|
17
52
|
const db = getTranscriptsDb();
|
|
@@ -65,11 +100,10 @@ export function buildFacts(archiveId) {
|
|
|
65
100
|
toolCounts.set(name, (toolCounts.get(name) ?? 0) + 1);
|
|
66
101
|
if (r.text) {
|
|
67
102
|
if (EDIT_TOOLS.has(name)) {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
files.add(m[1]);
|
|
103
|
+
for (const f of extractFiles(r.text))
|
|
104
|
+
files.add(f);
|
|
71
105
|
}
|
|
72
|
-
else if (name
|
|
106
|
+
else if (SHELL_TOOLS.has(name)) {
|
|
73
107
|
const m = r.text.match(COMMAND_RE);
|
|
74
108
|
if (m)
|
|
75
109
|
commands.push(m[1].slice(0, 200));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pyramid.js","sourceRoot":"","sources":["../../src/transcripts/pyramid.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,kFAAkF;AAClF,8DAA8D;AAC9D,gFAAgF;AAChF,gFAAgF;AAChF,6EAA6E;AAC7E,iGAAiG;AAEjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,
|
|
1
|
+
{"version":3,"file":"pyramid.js","sourceRoot":"","sources":["../../src/transcripts/pyramid.ts"],"names":[],"mappings":"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"}
|