@jmtrin/opencode-kevin 1.2.0 → 1.3.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/dist/plugin/index.d.ts +4 -3
- package/dist/plugin/index.js +122 -113
- package/dist/plugin/native.d.ts +2 -2
- package/dist/plugin/native.js +2 -2
- package/package.json +56 -68
- package/LICENSE +0 -21
- package/README.md +0 -605
- package/dist/migrations/001_initial.sql +0 -92
- package/dist/migrations/002_indexes.sql +0 -14
- package/dist/migrations/003_v02_signal.sql +0 -58
- package/dist/migrations/004_v03_knowledge.sql +0 -138
- package/dist/migrations/005_v04_signal.sql +0 -57
- package/dist/migrations/006_v05_glassbox.sql +0 -118
- package/dist/migrations/007_v06_pull.sql +0 -145
- package/dist/migrations/008_v07_truth.sql +0 -124
- package/dist/migrations/009_v08_team.sql +0 -100
- package/dist/migrations/010_v09_native.sql +0 -78
- package/dist/migrations/011_v10_proven.sql +0 -69
- package/dist/migrations/012_v11_drift.sql +0 -24
- package/dist/plugin/Archiver.d.ts +0 -42
- package/dist/plugin/Archiver.js +0 -83
- package/dist/plugin/ArtifactWriter.d.ts +0 -93
- package/dist/plugin/ArtifactWriter.js +0 -291
- package/dist/plugin/CausalChain.d.ts +0 -22
- package/dist/plugin/CausalChain.js +0 -197
- package/dist/plugin/ChatBridge.d.ts +0 -41
- package/dist/plugin/ChatBridge.js +0 -103
- package/dist/plugin/ConflictDetector.d.ts +0 -35
- package/dist/plugin/ConflictDetector.js +0 -260
- package/dist/plugin/ContextInjector.d.ts +0 -222
- package/dist/plugin/ContextInjector.js +0 -533
- package/dist/plugin/ConventionMiner.d.ts +0 -35
- package/dist/plugin/ConventionMiner.js +0 -242
- package/dist/plugin/Curator.d.ts +0 -116
- package/dist/plugin/Curator.js +0 -324
- package/dist/plugin/DashboardHtml.d.ts +0 -5
- package/dist/plugin/DashboardHtml.js +0 -180
- package/dist/plugin/Feedback.d.ts +0 -67
- package/dist/plugin/Feedback.js +0 -120
- package/dist/plugin/HookLiveness.d.ts +0 -88
- package/dist/plugin/HookLiveness.js +0 -322
- package/dist/plugin/InjectionLedger.d.ts +0 -98
- package/dist/plugin/InjectionLedger.js +0 -295
- package/dist/plugin/LessonFixer.d.ts +0 -44
- package/dist/plugin/LessonFixer.js +0 -45
- package/dist/plugin/Materializer.d.ts +0 -83
- package/dist/plugin/Materializer.js +0 -181
- package/dist/plugin/MemoryService.d.ts +0 -360
- package/dist/plugin/MemoryService.js +0 -1242
- package/dist/plugin/Migrate.d.ts +0 -17
- package/dist/plugin/Migrate.js +0 -271
- package/dist/plugin/PatternMiner.d.ts +0 -49
- package/dist/plugin/PatternMiner.js +0 -132
- package/dist/plugin/QualityGate.d.ts +0 -124
- package/dist/plugin/QualityGate.js +0 -119
- package/dist/plugin/Reflector.d.ts +0 -91
- package/dist/plugin/Reflector.js +0 -307
- package/dist/plugin/RepoIdentity.d.ts +0 -124
- package/dist/plugin/RepoIdentity.js +0 -300
- package/dist/plugin/RepoTruth.d.ts +0 -80
- package/dist/plugin/RepoTruth.js +0 -599
- package/dist/plugin/Retrospective.d.ts +0 -17
- package/dist/plugin/Retrospective.js +0 -216
- package/dist/plugin/SharedLayer.d.ts +0 -159
- package/dist/plugin/SharedLayer.js +0 -462
- package/dist/plugin/Store.d.ts +0 -29
- package/dist/plugin/Store.js +0 -50
- package/dist/plugin/ToolCallObserver.d.ts +0 -32
- package/dist/plugin/ToolCallObserver.js +0 -201
- package/dist/plugin/TuiActions.d.ts +0 -43
- package/dist/plugin/TuiActions.js +0 -181
- package/dist/plugin/TuiSnapshots.d.ts +0 -24
- package/dist/plugin/TuiSnapshots.js +0 -158
- package/dist/plugin/columns.d.ts +0 -11
- package/dist/plugin/columns.js +0 -54
- package/dist/plugin/confidence.d.ts +0 -8
- package/dist/plugin/confidence.js +0 -34
- package/dist/plugin/contract.d.ts +0 -54
- package/dist/plugin/contract.js +0 -386
- package/dist/plugin/diff.d.ts +0 -8
- package/dist/plugin/diff.js +0 -182
- package/dist/plugin/escape.d.ts +0 -39
- package/dist/plugin/escape.js +0 -60
- package/dist/plugin/fingerprint.d.ts +0 -27
- package/dist/plugin/fingerprint.js +0 -73
- package/dist/plugin/inferability.d.ts +0 -32
- package/dist/plugin/inferability.js +0 -88
- package/dist/plugin/kevin_approve.d.ts +0 -34
- package/dist/plugin/kevin_approve.js +0 -52
- package/dist/plugin/kevin_audit.d.ts +0 -229
- package/dist/plugin/kevin_audit.js +0 -631
- package/dist/plugin/kevin_bench.d.ts +0 -10
- package/dist/plugin/kevin_bench.js +0 -82
- package/dist/plugin/kevin_conflicts.d.ts +0 -9
- package/dist/plugin/kevin_conflicts.js +0 -50
- package/dist/plugin/kevin_contract.d.ts +0 -8
- package/dist/plugin/kevin_contract.js +0 -59
- package/dist/plugin/kevin_doctor.d.ts +0 -62
- package/dist/plugin/kevin_doctor.js +0 -196
- package/dist/plugin/kevin_facts.d.ts +0 -42
- package/dist/plugin/kevin_facts.js +0 -36
- package/dist/plugin/kevin_forget.d.ts +0 -33
- package/dist/plugin/kevin_forget.js +0 -260
- package/dist/plugin/kevin_native.d.ts +0 -29
- package/dist/plugin/kevin_native.js +0 -79
- package/dist/plugin/kevin_propose.d.ts +0 -23
- package/dist/plugin/kevin_propose.js +0 -14
- package/dist/plugin/kevin_publish.d.ts +0 -38
- package/dist/plugin/kevin_publish.js +0 -18
- package/dist/plugin/kevin_why.d.ts +0 -23
- package/dist/plugin/kevin_why.js +0 -111
- package/dist/plugin/memory-format.d.ts +0 -20
- package/dist/plugin/memory-format.js +0 -59
- package/dist/plugin/metrics.d.ts +0 -97
- package/dist/plugin/metrics.js +0 -288
- package/dist/plugin/okf-export.d.ts +0 -3
- package/dist/plugin/okf-export.js +0 -134
- package/dist/plugin/okf-import.d.ts +0 -76
- package/dist/plugin/okf-import.js +0 -271
- package/dist/plugin/okf.d.ts +0 -107
- package/dist/plugin/okf.js +0 -303
- package/dist/plugin/perf.d.ts +0 -35
- package/dist/plugin/perf.js +0 -174
- package/dist/plugin/query-tokenizer.d.ts +0 -13
- package/dist/plugin/query-tokenizer.js +0 -133
- package/dist/plugin/redact.d.ts +0 -2
- package/dist/plugin/redact.js +0 -18
- package/dist/plugin/replay-types.d.ts +0 -69
- package/dist/plugin/replay-types.js +0 -156
- package/dist/plugin/replay.d.ts +0 -36
- package/dist/plugin/replay.js +0 -202
- package/dist/plugin/sqlite-adapter.d.ts +0 -12
- package/dist/plugin/sqlite-adapter.js +0 -81
- package/dist/plugin/time-ms.d.ts +0 -1
- package/dist/plugin/time-ms.js +0 -16
- package/dist/plugin/tui-types.d.ts +0 -59
- package/dist/plugin/tui-types.js +0 -4
- package/dist/plugin/tui.d.ts +0 -18
- package/dist/plugin/tui.js +0 -198
- package/dist/plugin/uuid.d.ts +0 -1
- package/dist/plugin/uuid.js +0 -50
package/dist/plugin/perf.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { Store } from "./Store.js";
|
|
2
|
-
export type PerfScope = "tool.execute.before" | "tool.execute.after" | "chat.message" | "chat.system.transform" | "session.compacting" | "event" | "session.idle" | "dispose";
|
|
3
|
-
export interface Budget {
|
|
4
|
-
readonly scope: PerfScope;
|
|
5
|
-
readonly p95Ms: number;
|
|
6
|
-
readonly maxMs: number;
|
|
7
|
-
}
|
|
8
|
-
export declare const BUDGETS: readonly Budget[];
|
|
9
|
-
export interface PerfStat {
|
|
10
|
-
readonly scope: PerfScope;
|
|
11
|
-
readonly count: number;
|
|
12
|
-
readonly p50: number;
|
|
13
|
-
readonly p95: number;
|
|
14
|
-
readonly max: number;
|
|
15
|
-
readonly budget: Budget;
|
|
16
|
-
readonly withinBudget: boolean;
|
|
17
|
-
}
|
|
18
|
-
export declare class Perf {
|
|
19
|
-
private readonly enabled;
|
|
20
|
-
private readonly capacity;
|
|
21
|
-
private readonly rings;
|
|
22
|
-
private readonly cursors;
|
|
23
|
-
private readonly counts;
|
|
24
|
-
constructor(opts: {
|
|
25
|
-
enabled: boolean;
|
|
26
|
-
capacity?: number | string;
|
|
27
|
-
});
|
|
28
|
-
static fromSettings(settings: Record<string, string | null | undefined>): Perf;
|
|
29
|
-
measure<T>(scope: PerfScope, fn: () => T): T;
|
|
30
|
-
measureAsync<T>(scope: PerfScope, fn: () => Promise<T>): Promise<T>;
|
|
31
|
-
private record;
|
|
32
|
-
stats(): readonly PerfStat[];
|
|
33
|
-
flush(store: Store): void;
|
|
34
|
-
reset(): void;
|
|
35
|
-
}
|
package/dist/plugin/perf.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
export const BUDGETS = [
|
|
2
|
-
{ scope: "tool.execute.before", p95Ms: 2, maxMs: 10 },
|
|
3
|
-
{ scope: "tool.execute.after", p95Ms: 5, maxMs: 25 },
|
|
4
|
-
{ scope: "chat.message", p95Ms: 2, maxMs: 10 },
|
|
5
|
-
{ scope: "chat.system.transform", p95Ms: 15, maxMs: 50 },
|
|
6
|
-
{ scope: "session.compacting", p95Ms: 15, maxMs: 50 },
|
|
7
|
-
{ scope: "event", p95Ms: 5, maxMs: 25 },
|
|
8
|
-
{ scope: "session.idle", p95Ms: 150, maxMs: 600 },
|
|
9
|
-
{ scope: "dispose", p95Ms: 50, maxMs: 250 },
|
|
10
|
-
];
|
|
11
|
-
function clampCapacity(raw) {
|
|
12
|
-
if (raw === null || raw === undefined || raw === "")
|
|
13
|
-
return 512;
|
|
14
|
-
const n = Number.parseInt(raw, 10);
|
|
15
|
-
if (Number.isNaN(n))
|
|
16
|
-
return 512;
|
|
17
|
-
return Math.min(8192, Math.max(64, n));
|
|
18
|
-
}
|
|
19
|
-
export class Perf {
|
|
20
|
-
enabled;
|
|
21
|
-
capacity;
|
|
22
|
-
rings;
|
|
23
|
-
cursors;
|
|
24
|
-
counts;
|
|
25
|
-
constructor(opts) {
|
|
26
|
-
this.enabled = opts.enabled;
|
|
27
|
-
if (typeof opts.capacity === "string") {
|
|
28
|
-
this.capacity = clampCapacity(opts.capacity);
|
|
29
|
-
}
|
|
30
|
-
else if (typeof opts.capacity === "number") {
|
|
31
|
-
this.capacity = Math.min(8192, Math.max(64, Math.floor(opts.capacity)));
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
this.capacity = 512;
|
|
35
|
-
}
|
|
36
|
-
this.rings = new Map();
|
|
37
|
-
this.cursors = new Map();
|
|
38
|
-
this.counts = new Map();
|
|
39
|
-
const scopes = BUDGETS.map((b) => b.scope);
|
|
40
|
-
for (const s of scopes) {
|
|
41
|
-
this.rings.set(s, new Float64Array(this.capacity));
|
|
42
|
-
this.cursors.set(s, 0);
|
|
43
|
-
this.counts.set(s, 0);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
static fromSettings(settings) {
|
|
47
|
-
const enabled = settings.perf_enabled === "1";
|
|
48
|
-
const cap = clampCapacity(settings.perf_ring_capacity);
|
|
49
|
-
return new Perf({ enabled, capacity: cap });
|
|
50
|
-
}
|
|
51
|
-
measure(scope, fn) {
|
|
52
|
-
if (!this.enabled)
|
|
53
|
-
return fn();
|
|
54
|
-
const start = performance.now();
|
|
55
|
-
try {
|
|
56
|
-
return fn();
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
const dur = performance.now() - start;
|
|
60
|
-
this.record(scope, dur);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
async measureAsync(scope, fn) {
|
|
64
|
-
if (!this.enabled)
|
|
65
|
-
return fn();
|
|
66
|
-
const start = performance.now();
|
|
67
|
-
try {
|
|
68
|
-
return await fn();
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
const dur = performance.now() - start;
|
|
72
|
-
this.record(scope, dur);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
record(scope, ms) {
|
|
76
|
-
const ring = this.rings.get(scope);
|
|
77
|
-
if (!ring)
|
|
78
|
-
return;
|
|
79
|
-
const cur = this.cursors.get(scope) ?? 0;
|
|
80
|
-
ring[cur % this.capacity] = ms;
|
|
81
|
-
this.cursors.set(scope, (cur + 1) % this.capacity);
|
|
82
|
-
this.counts.set(scope, (this.counts.get(scope) ?? 0) + 1);
|
|
83
|
-
}
|
|
84
|
-
stats() {
|
|
85
|
-
const out = [];
|
|
86
|
-
for (const b of BUDGETS) {
|
|
87
|
-
const ring = this.rings.get(b.scope);
|
|
88
|
-
if (!ring)
|
|
89
|
-
continue;
|
|
90
|
-
const count = this.counts.get(b.scope) ?? 0;
|
|
91
|
-
if (count === 0) {
|
|
92
|
-
out.push({
|
|
93
|
-
scope: b.scope,
|
|
94
|
-
count: 0,
|
|
95
|
-
p50: 0,
|
|
96
|
-
p95: 0,
|
|
97
|
-
max: 0,
|
|
98
|
-
budget: b,
|
|
99
|
-
withinBudget: true,
|
|
100
|
-
});
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
const n = Math.min(count, this.capacity);
|
|
104
|
-
// Copy live samples: need to reconstruct in insertion order but for percentiles order doesn't matter.
|
|
105
|
-
// Take last n entries from ring (wrap-aware).
|
|
106
|
-
const samples = [];
|
|
107
|
-
const cur = this.cursors.get(b.scope) ?? 0;
|
|
108
|
-
// If count < capacity, samples are ring[0..count-1]; else ring[0..capacity-1] all valid but order is cur..cur+cap
|
|
109
|
-
if (count < this.capacity) {
|
|
110
|
-
for (let i = 0; i < count; i++)
|
|
111
|
-
samples.push(ring[i]);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
for (let i = 0; i < this.capacity; i++)
|
|
115
|
-
samples.push(ring[i]);
|
|
116
|
-
}
|
|
117
|
-
const sorted = [...samples].sort((a, b) => a - b);
|
|
118
|
-
// Percentile method: nearest-rank — p is the value at index ceil(p/100 * n) - 1
|
|
119
|
-
// of the ascending-sorted copy. Stated here so an implementer comparing against
|
|
120
|
-
// another percentile definition can see which one was chosen.
|
|
121
|
-
const p50 = sorted[Math.max(0, Math.ceil(sorted.length * 0.5) - 1)];
|
|
122
|
-
const p95 = sorted[Math.max(0, Math.ceil(sorted.length * 0.95) - 1)];
|
|
123
|
-
const max = sorted[sorted.length - 1];
|
|
124
|
-
out.push({
|
|
125
|
-
scope: b.scope,
|
|
126
|
-
count,
|
|
127
|
-
p50,
|
|
128
|
-
p95,
|
|
129
|
-
max,
|
|
130
|
-
budget: b,
|
|
131
|
-
withinBudget: p95 <= b.p95Ms,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
return out;
|
|
135
|
-
}
|
|
136
|
-
flush(store) {
|
|
137
|
-
if (!this.enabled)
|
|
138
|
-
return;
|
|
139
|
-
const stats = this.stats();
|
|
140
|
-
let recorded = 0;
|
|
141
|
-
let breaches = 0;
|
|
142
|
-
for (const s of stats) {
|
|
143
|
-
if (s.count === 0)
|
|
144
|
-
continue;
|
|
145
|
-
store
|
|
146
|
-
.prepare("INSERT INTO perf_samples (scope, sample_count, p50_ms, p95_ms, max_ms, budget_p95_ms, within_budget) VALUES (?, ?, ?, ?, ?, ?, ?)")
|
|
147
|
-
.run(s.scope, s.count, s.p50, s.p95, s.max, s.budget.p95Ms, s.withinBudget ? 1 : 0);
|
|
148
|
-
recorded++;
|
|
149
|
-
if (!s.withinBudget)
|
|
150
|
-
breaches++;
|
|
151
|
-
}
|
|
152
|
-
if (recorded > 0) {
|
|
153
|
-
store
|
|
154
|
-
.prepare(`UPDATE kevin_metrics SET value = value + ? WHERE key = 'perf_samples_recorded'`)
|
|
155
|
-
.run(recorded);
|
|
156
|
-
}
|
|
157
|
-
if (breaches > 0) {
|
|
158
|
-
store
|
|
159
|
-
.prepare(`UPDATE kevin_metrics SET value = value + ? WHERE key = 'perf_budget_breaches'`)
|
|
160
|
-
.run(breaches);
|
|
161
|
-
}
|
|
162
|
-
this.reset();
|
|
163
|
-
}
|
|
164
|
-
reset() {
|
|
165
|
-
for (const b of BUDGETS) {
|
|
166
|
-
// Zero the ring too: cursors and counts alone would let the next
|
|
167
|
-
// period's stats() read stale pre-reset samples whenever
|
|
168
|
-
// count < capacity (review fix, v1.0.0).
|
|
169
|
-
this.rings.get(b.scope)?.fill(0);
|
|
170
|
-
this.cursors.set(b.scope, 0);
|
|
171
|
-
this.counts.set(b.scope, 0);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* K4-013 — shared FTS5 query tokenizer.
|
|
3
|
-
*
|
|
4
|
-
* Injection recall ORs the quoted tokens (`"t1" OR "t2"`) while
|
|
5
|
-
* `kevin_why` ANDs them (`"t1" AND "t2"`).
|
|
6
|
-
*/
|
|
7
|
-
export declare const STOP_WORDS: Set<string>;
|
|
8
|
-
/** Lowercase, split on whitespace, drop stopwords. Returns raw tokens. */
|
|
9
|
-
export declare function tokenizeQuery(query: string): string[];
|
|
10
|
-
/** Quote a token for an FTS5 MATCH clause, escaping embedded quotes. */
|
|
11
|
-
export declare function quoteToken(token: string): string;
|
|
12
|
-
/** Join quoted tokens into a MATCH clause with the given separator. */
|
|
13
|
-
export declare function toMatchClause(tokens: string[], separator: " OR " | " AND "): string;
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* K4-013 — shared FTS5 query tokenizer.
|
|
3
|
-
*
|
|
4
|
-
* Injection recall ORs the quoted tokens (`"t1" OR "t2"`) while
|
|
5
|
-
* `kevin_why` ANDs them (`"t1" AND "t2"`).
|
|
6
|
-
*/
|
|
7
|
-
// v1.1.0 (K11-012 / plan §5.5, D11-05) — single source for STOP_WORDS (union of three lists)
|
|
8
|
-
export const STOP_WORDS = new Set([
|
|
9
|
-
"a",
|
|
10
|
-
"about",
|
|
11
|
-
"after",
|
|
12
|
-
"again",
|
|
13
|
-
"all",
|
|
14
|
-
"also",
|
|
15
|
-
"an",
|
|
16
|
-
"and",
|
|
17
|
-
"any",
|
|
18
|
-
"are",
|
|
19
|
-
"as",
|
|
20
|
-
"at",
|
|
21
|
-
"be",
|
|
22
|
-
"been",
|
|
23
|
-
"before",
|
|
24
|
-
"being",
|
|
25
|
-
"but",
|
|
26
|
-
"by",
|
|
27
|
-
"can",
|
|
28
|
-
"como",
|
|
29
|
-
"con",
|
|
30
|
-
"could",
|
|
31
|
-
"de",
|
|
32
|
-
"did",
|
|
33
|
-
"do",
|
|
34
|
-
"does",
|
|
35
|
-
"el",
|
|
36
|
-
"en",
|
|
37
|
-
"eso",
|
|
38
|
-
"for",
|
|
39
|
-
"from",
|
|
40
|
-
"had",
|
|
41
|
-
"has",
|
|
42
|
-
"have",
|
|
43
|
-
"he",
|
|
44
|
-
"her",
|
|
45
|
-
"his",
|
|
46
|
-
"how",
|
|
47
|
-
"i",
|
|
48
|
-
"if",
|
|
49
|
-
"in",
|
|
50
|
-
"into",
|
|
51
|
-
"is",
|
|
52
|
-
"it",
|
|
53
|
-
"its",
|
|
54
|
-
"la",
|
|
55
|
-
"las",
|
|
56
|
-
"los",
|
|
57
|
-
"may",
|
|
58
|
-
"mi",
|
|
59
|
-
"might",
|
|
60
|
-
"more",
|
|
61
|
-
"most",
|
|
62
|
-
"must",
|
|
63
|
-
"my",
|
|
64
|
-
"not",
|
|
65
|
-
"o",
|
|
66
|
-
"of",
|
|
67
|
-
"on",
|
|
68
|
-
"one",
|
|
69
|
-
"or",
|
|
70
|
-
"our",
|
|
71
|
-
"para",
|
|
72
|
-
"per",
|
|
73
|
-
"por",
|
|
74
|
-
"que",
|
|
75
|
-
"shall",
|
|
76
|
-
"she",
|
|
77
|
-
"sin",
|
|
78
|
-
"so",
|
|
79
|
-
"su",
|
|
80
|
-
"than",
|
|
81
|
-
"that",
|
|
82
|
-
"the",
|
|
83
|
-
"their",
|
|
84
|
-
"them",
|
|
85
|
-
"then",
|
|
86
|
-
"there",
|
|
87
|
-
"these",
|
|
88
|
-
"they",
|
|
89
|
-
"this",
|
|
90
|
-
"those",
|
|
91
|
-
"through",
|
|
92
|
-
"to",
|
|
93
|
-
"too",
|
|
94
|
-
"tu",
|
|
95
|
-
"un",
|
|
96
|
-
"una",
|
|
97
|
-
"under",
|
|
98
|
-
"up",
|
|
99
|
-
"us",
|
|
100
|
-
"via",
|
|
101
|
-
"was",
|
|
102
|
-
"we",
|
|
103
|
-
"were",
|
|
104
|
-
"what",
|
|
105
|
-
"when",
|
|
106
|
-
"where",
|
|
107
|
-
"which",
|
|
108
|
-
"while",
|
|
109
|
-
"who",
|
|
110
|
-
"why",
|
|
111
|
-
"will",
|
|
112
|
-
"with",
|
|
113
|
-
"would",
|
|
114
|
-
"y",
|
|
115
|
-
"you",
|
|
116
|
-
"your",
|
|
117
|
-
]);
|
|
118
|
-
/** Lowercase, split on whitespace, drop stopwords. Returns raw tokens. */
|
|
119
|
-
export function tokenizeQuery(query) {
|
|
120
|
-
return query
|
|
121
|
-
.trim()
|
|
122
|
-
.toLowerCase()
|
|
123
|
-
.split(/\s+/)
|
|
124
|
-
.filter((t) => t.length > 0 && !STOP_WORDS.has(t));
|
|
125
|
-
}
|
|
126
|
-
/** Quote a token for an FTS5 MATCH clause, escaping embedded quotes. */
|
|
127
|
-
export function quoteToken(token) {
|
|
128
|
-
return `"${token.replace(/"/g, '""')}"`;
|
|
129
|
-
}
|
|
130
|
-
/** Join quoted tokens into a MATCH clause with the given separator. */
|
|
131
|
-
export function toMatchClause(tokens, separator) {
|
|
132
|
-
return tokens.map(quoteToken).join(separator);
|
|
133
|
-
}
|
package/dist/plugin/redact.d.ts
DELETED
package/dist/plugin/redact.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const PATH_PATTERNS = [
|
|
2
|
-
/[a-z]:\\[^\s"'<>|*?:]+/gi,
|
|
3
|
-
/\/(?:home|users|var|tmp|opt|etc|root|usr|app|work|workspace|code|repo|project|src|build|dist|packages|services|api|web|client|server|lib|node_modules)(?:\/[^\s"'<>|*?:]+)*/gi,
|
|
4
|
-
];
|
|
5
|
-
export function redactPaths(text) {
|
|
6
|
-
let out = text;
|
|
7
|
-
for (const pat of PATH_PATTERNS) {
|
|
8
|
-
out = out.replace(pat, "<path>");
|
|
9
|
-
}
|
|
10
|
-
return out;
|
|
11
|
-
}
|
|
12
|
-
const PRIVATE_BLOCK_RE = /<private\b[^>]*>([\s\S]*?)<\/private>/gi;
|
|
13
|
-
export function stripPrivate(text) {
|
|
14
|
-
return text.replace(PRIVATE_BLOCK_RE, (_match, inner) => {
|
|
15
|
-
const n = inner.length;
|
|
16
|
-
return `<private: redacted ${n} chars>`;
|
|
17
|
-
});
|
|
18
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* v0.5.0 (K5-018 / plan §5.8) — replay transcript format.
|
|
3
|
-
*
|
|
4
|
-
* A hermetic, deterministic way to run a recorded session through the
|
|
5
|
-
* plugin's components and read the outcome distribution. `at` is an
|
|
6
|
-
* ISO-8601 string and is the ONLY source of time during replay:
|
|
7
|
-
* `Date.now()` must not be called anywhere in the replay path.
|
|
8
|
-
*
|
|
9
|
-
* v0.7.0 — hand-rolled validation: the zod dependency was removed
|
|
10
|
-
* (docs/Kevin_v0.9.0_Plan.md D9-05). This module has no zod import.
|
|
11
|
-
*/
|
|
12
|
-
export type ReplayEvent = {
|
|
13
|
-
kind: "session.created";
|
|
14
|
-
at: string;
|
|
15
|
-
sessionId: string;
|
|
16
|
-
} | {
|
|
17
|
-
kind: "chat.message";
|
|
18
|
-
at: string;
|
|
19
|
-
sessionId: string;
|
|
20
|
-
text: string;
|
|
21
|
-
} | {
|
|
22
|
-
kind: "tool.before";
|
|
23
|
-
at: string;
|
|
24
|
-
sessionId: string;
|
|
25
|
-
callId: string;
|
|
26
|
-
tool: string;
|
|
27
|
-
args: Record<string, unknown>;
|
|
28
|
-
} | {
|
|
29
|
-
kind: "tool.after";
|
|
30
|
-
at: string;
|
|
31
|
-
sessionId: string;
|
|
32
|
-
callId: string;
|
|
33
|
-
success: boolean;
|
|
34
|
-
stdout?: string;
|
|
35
|
-
stderr?: string;
|
|
36
|
-
exitCode?: number;
|
|
37
|
-
} | {
|
|
38
|
-
kind: "system.transform";
|
|
39
|
-
at: string;
|
|
40
|
-
sessionId: string;
|
|
41
|
-
} | {
|
|
42
|
-
kind: "compacting";
|
|
43
|
-
at: string;
|
|
44
|
-
sessionId: string;
|
|
45
|
-
} | {
|
|
46
|
-
kind: "session.idle";
|
|
47
|
-
at: string;
|
|
48
|
-
sessionId: string;
|
|
49
|
-
};
|
|
50
|
-
export interface ReplayTranscript {
|
|
51
|
-
readonly version: 1;
|
|
52
|
-
readonly name: string;
|
|
53
|
-
readonly events: readonly ReplayEvent[];
|
|
54
|
-
}
|
|
55
|
-
/** Validates an unknown payload (e.g. a parsed JSON file) as a transcript. */
|
|
56
|
-
export declare function parseTranscript(json: unknown): ReplayTranscript;
|
|
57
|
-
interface SchemaLike<T> {
|
|
58
|
-
parse(value: unknown): T;
|
|
59
|
-
safeParse(value: unknown): {
|
|
60
|
-
success: true;
|
|
61
|
-
data: T;
|
|
62
|
-
} | {
|
|
63
|
-
success: false;
|
|
64
|
-
error: Error;
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
export declare const replayEventSchema: SchemaLike<ReplayEvent>;
|
|
68
|
-
export declare const replayTranscriptSchema: SchemaLike<ReplayTranscript>;
|
|
69
|
-
export {};
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* v0.5.0 (K5-018 / plan §5.8) — replay transcript format.
|
|
3
|
-
*
|
|
4
|
-
* A hermetic, deterministic way to run a recorded session through the
|
|
5
|
-
* plugin's components and read the outcome distribution. `at` is an
|
|
6
|
-
* ISO-8601 string and is the ONLY source of time during replay:
|
|
7
|
-
* `Date.now()` must not be called anywhere in the replay path.
|
|
8
|
-
*
|
|
9
|
-
* v0.7.0 — hand-rolled validation: the zod dependency was removed
|
|
10
|
-
* (docs/Kevin_v0.9.0_Plan.md D9-05). This module has no zod import.
|
|
11
|
-
*/
|
|
12
|
-
const EVENT_KINDS = [
|
|
13
|
-
"session.created",
|
|
14
|
-
"chat.message",
|
|
15
|
-
"tool.before",
|
|
16
|
-
"tool.after",
|
|
17
|
-
"system.transform",
|
|
18
|
-
"compacting",
|
|
19
|
-
"session.idle",
|
|
20
|
-
];
|
|
21
|
-
const ISO_DATETIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
|
|
22
|
-
function isObject(value) {
|
|
23
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24
|
-
}
|
|
25
|
-
function isIsoDatetime(value) {
|
|
26
|
-
return (typeof value === "string" &&
|
|
27
|
-
ISO_DATETIME_RE.test(value) &&
|
|
28
|
-
!Number.isNaN(Date.parse(value)));
|
|
29
|
-
}
|
|
30
|
-
function isNonEmptyString(value) {
|
|
31
|
-
return typeof value === "string" && value.length > 0;
|
|
32
|
-
}
|
|
33
|
-
function isInteger(value) {
|
|
34
|
-
return typeof value === "number" && Number.isInteger(value);
|
|
35
|
-
}
|
|
36
|
-
function parseEvent(value) {
|
|
37
|
-
if (!isObject(value)) {
|
|
38
|
-
throw new Error("replay event must be an object");
|
|
39
|
-
}
|
|
40
|
-
const kind = value.kind;
|
|
41
|
-
if (typeof kind !== "string" ||
|
|
42
|
-
!EVENT_KINDS.includes(kind)) {
|
|
43
|
-
throw new Error(`unknown replay event kind: ${String(kind)}`);
|
|
44
|
-
}
|
|
45
|
-
const at = value.at;
|
|
46
|
-
if (!isIsoDatetime(at)) {
|
|
47
|
-
throw new Error("replay event `at` must be an ISO-8601 datetime string");
|
|
48
|
-
}
|
|
49
|
-
const sessionId = value.sessionId;
|
|
50
|
-
if (!isNonEmptyString(sessionId)) {
|
|
51
|
-
throw new Error("replay event `sessionId` must be a non-empty string");
|
|
52
|
-
}
|
|
53
|
-
switch (kind) {
|
|
54
|
-
case "session.created":
|
|
55
|
-
case "system.transform":
|
|
56
|
-
case "compacting":
|
|
57
|
-
case "session.idle":
|
|
58
|
-
return { kind, at, sessionId };
|
|
59
|
-
case "chat.message": {
|
|
60
|
-
const text = value.text;
|
|
61
|
-
if (!isNonEmptyString(text)) {
|
|
62
|
-
throw new Error("chat.message `text` must be a non-empty string");
|
|
63
|
-
}
|
|
64
|
-
return { kind, at, sessionId, text };
|
|
65
|
-
}
|
|
66
|
-
case "tool.before": {
|
|
67
|
-
const callId = value.callId;
|
|
68
|
-
if (!isNonEmptyString(callId)) {
|
|
69
|
-
throw new Error("tool.before `callId` must be a non-empty string");
|
|
70
|
-
}
|
|
71
|
-
const tool = value.tool;
|
|
72
|
-
if (!isNonEmptyString(tool)) {
|
|
73
|
-
throw new Error("tool.before `tool` must be a non-empty string");
|
|
74
|
-
}
|
|
75
|
-
if (!isObject(value.args)) {
|
|
76
|
-
throw new Error("tool.before `args` must be an object");
|
|
77
|
-
}
|
|
78
|
-
return { kind, at, sessionId, callId, tool, args: value.args };
|
|
79
|
-
}
|
|
80
|
-
case "tool.after": {
|
|
81
|
-
const callId = value.callId;
|
|
82
|
-
if (!isNonEmptyString(callId)) {
|
|
83
|
-
throw new Error("tool.after `callId` must be a non-empty string");
|
|
84
|
-
}
|
|
85
|
-
if (typeof value.success !== "boolean") {
|
|
86
|
-
throw new Error("tool.after `success` must be a boolean");
|
|
87
|
-
}
|
|
88
|
-
const event = {
|
|
89
|
-
kind,
|
|
90
|
-
at,
|
|
91
|
-
sessionId,
|
|
92
|
-
callId,
|
|
93
|
-
success: value.success,
|
|
94
|
-
};
|
|
95
|
-
if (value.stdout !== undefined) {
|
|
96
|
-
if (!isNonEmptyString(value.stdout)) {
|
|
97
|
-
throw new Error("tool.after `stdout` must be a non-empty string");
|
|
98
|
-
}
|
|
99
|
-
event.stdout = value.stdout;
|
|
100
|
-
}
|
|
101
|
-
if (value.stderr !== undefined) {
|
|
102
|
-
if (!isNonEmptyString(value.stderr)) {
|
|
103
|
-
throw new Error("tool.after `stderr` must be a non-empty string");
|
|
104
|
-
}
|
|
105
|
-
event.stderr = value.stderr;
|
|
106
|
-
}
|
|
107
|
-
if (value.exitCode !== undefined) {
|
|
108
|
-
if (!isInteger(value.exitCode)) {
|
|
109
|
-
throw new Error("tool.after `exitCode` must be an integer");
|
|
110
|
-
}
|
|
111
|
-
event.exitCode = value.exitCode;
|
|
112
|
-
}
|
|
113
|
-
return event;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
throw new Error(`unhandled replay event kind: ${String(kind)}`);
|
|
117
|
-
}
|
|
118
|
-
/** Validates an unknown payload (e.g. a parsed JSON file) as a transcript. */
|
|
119
|
-
export function parseTranscript(json) {
|
|
120
|
-
if (!isObject(json)) {
|
|
121
|
-
throw new Error("replay transcript must be an object");
|
|
122
|
-
}
|
|
123
|
-
if (json.version !== 1) {
|
|
124
|
-
throw new Error("replay transcript `version` must be 1");
|
|
125
|
-
}
|
|
126
|
-
const name = json.name;
|
|
127
|
-
if (!isNonEmptyString(name)) {
|
|
128
|
-
throw new Error("replay transcript `name` must be a non-empty string");
|
|
129
|
-
}
|
|
130
|
-
if (!Array.isArray(json.events) || json.events.length === 0) {
|
|
131
|
-
throw new Error("replay transcript `events` must be a non-empty array");
|
|
132
|
-
}
|
|
133
|
-
return {
|
|
134
|
-
version: 1,
|
|
135
|
-
name,
|
|
136
|
-
events: json.events.map(parseEvent),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function toSchema(parse) {
|
|
140
|
-
return {
|
|
141
|
-
parse,
|
|
142
|
-
safeParse(value) {
|
|
143
|
-
try {
|
|
144
|
-
return { success: true, data: parse(value) };
|
|
145
|
-
}
|
|
146
|
-
catch (error) {
|
|
147
|
-
return {
|
|
148
|
-
success: false,
|
|
149
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
export const replayEventSchema = toSchema(parseEvent);
|
|
156
|
-
export const replayTranscriptSchema = toSchema(parseTranscript);
|
package/dist/plugin/replay.d.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { ReplayTranscript } from "./replay-types.js";
|
|
2
|
-
/**
|
|
3
|
-
* v0.5.0 (K5-019 / plan §5.8, D5-12) — replay harness.
|
|
4
|
-
*
|
|
5
|
-
* Hermetic, deterministic driver: runs a recorded transcript through the
|
|
6
|
-
* plugin's components against an in-memory database with a frozen clock,
|
|
7
|
-
* then reports the outcome distribution. This is an artifact, not a
|
|
8
|
-
* release gate.
|
|
9
|
-
*
|
|
10
|
-
* The components are instantiated directly — NOT through `KevinPlugin`,
|
|
11
|
-
* which resolves a home-directory database path and registers tools.
|
|
12
|
-
* `Date.now()` must never be called in this path; every clock need is
|
|
13
|
-
* satisfied by the event's `at` timestamp (retrieval is frozen by the
|
|
14
|
-
* `deterministic_retrieval` setting; the Archiver clock is injected).
|
|
15
|
-
*/
|
|
16
|
-
export interface ReplayResult {
|
|
17
|
-
readonly transcript: string;
|
|
18
|
-
readonly memoriesCreated: number;
|
|
19
|
-
readonly injections: {
|
|
20
|
-
total: number;
|
|
21
|
-
effective: number;
|
|
22
|
-
ineffective: number;
|
|
23
|
-
inconclusive: number;
|
|
24
|
-
unmeasured: number;
|
|
25
|
-
};
|
|
26
|
-
readonly precisionRate: number;
|
|
27
|
-
readonly coverageRate: number;
|
|
28
|
-
readonly tokensInjected: {
|
|
29
|
-
prePrompt: number;
|
|
30
|
-
compacting: number;
|
|
31
|
-
};
|
|
32
|
-
readonly blocked: Record<string, number>;
|
|
33
|
-
}
|
|
34
|
-
export declare function replay(transcript: ReplayTranscript, opts?: {
|
|
35
|
-
dbPath?: string;
|
|
36
|
-
}): Promise<ReplayResult>;
|