@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
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import { computeEntryId } from "./okf.js";
|
|
2
|
-
export function handleForget(input, deps) {
|
|
3
|
-
// v1.1.0 — every invocation counts, including dry runs and refusals (K11-005)
|
|
4
|
-
try {
|
|
5
|
-
deps.metrics.incr("forget_requests_total", 1);
|
|
6
|
-
}
|
|
7
|
-
catch {
|
|
8
|
-
// best-effort
|
|
9
|
-
}
|
|
10
|
-
if (!input.ids || input.ids.length === 0) {
|
|
11
|
-
return {
|
|
12
|
-
action: "forget",
|
|
13
|
-
ok: false,
|
|
14
|
-
dry_run: input.confirm !== true,
|
|
15
|
-
per_id: [],
|
|
16
|
-
reason: "no_ids",
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
const isDry = input.confirm !== true;
|
|
20
|
-
const per_id = [];
|
|
21
|
-
let anyChange = false;
|
|
22
|
-
let anyTombstonePlanned = false;
|
|
23
|
-
// Helper to compute entry_id for a memory row
|
|
24
|
-
const getEntryId = (row) => {
|
|
25
|
-
if (row.shared_entry_id)
|
|
26
|
-
return row.shared_entry_id;
|
|
27
|
-
return computeEntryId(row.type, row.content, row.scope);
|
|
28
|
-
};
|
|
29
|
-
// For dry_run: just plan, mutate nothing
|
|
30
|
-
if (isDry) {
|
|
31
|
-
for (const id of input.ids) {
|
|
32
|
-
const row = deps.store
|
|
33
|
-
.prepare("SELECT id, type, content, scope, status, layer, shared_entry_id FROM memories WHERE id = ?")
|
|
34
|
-
.get(id);
|
|
35
|
-
if (!row) {
|
|
36
|
-
per_id.push({ id, archived: false, reason: "not_found" });
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (row.status === "archived") {
|
|
40
|
-
// Idempotence: already archived
|
|
41
|
-
const isShared = row.layer === "shared" || row.shared_entry_id !== null;
|
|
42
|
-
if (isShared) {
|
|
43
|
-
const entryId = getEntryId(row);
|
|
44
|
-
try {
|
|
45
|
-
const plan = deps.sharedLayer.planTombstone([entryId], deps.okfPath);
|
|
46
|
-
const planned = plan.write.outcome !== "refused" && plan.entriesAdded > 0;
|
|
47
|
-
// Already archived implies tombstone already applied, so planned should be false (noop)
|
|
48
|
-
per_id.push({
|
|
49
|
-
id,
|
|
50
|
-
archived: false,
|
|
51
|
-
reason: "already_archived",
|
|
52
|
-
tombstone: { entry_id: entryId, planned: false, applied: false },
|
|
53
|
-
});
|
|
54
|
-
void plan;
|
|
55
|
-
void planned;
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
per_id.push({
|
|
59
|
-
id,
|
|
60
|
-
archived: false,
|
|
61
|
-
reason: "already_archived",
|
|
62
|
-
tombstone: { entry_id: entryId, planned: false, applied: false },
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
per_id.push({ id, archived: false, reason: "already_archived" });
|
|
68
|
-
}
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
// Would archive
|
|
72
|
-
anyChange = true;
|
|
73
|
-
const isShared = row.layer === "shared" || row.shared_entry_id !== null;
|
|
74
|
-
if (isShared) {
|
|
75
|
-
const entryId = getEntryId(row);
|
|
76
|
-
let planned = false;
|
|
77
|
-
try {
|
|
78
|
-
const plan = deps.sharedLayer.planTombstone([entryId], deps.okfPath);
|
|
79
|
-
// planTombstone returns ExportPlan with write outcome; if refused, planned false
|
|
80
|
-
planned = plan.write.outcome !== "refused";
|
|
81
|
-
anyTombstonePlanned = anyTombstonePlanned || planned;
|
|
82
|
-
per_id.push({
|
|
83
|
-
id,
|
|
84
|
-
archived: true,
|
|
85
|
-
tombstone: { entry_id: entryId, planned, applied: false },
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
per_id.push({
|
|
90
|
-
id,
|
|
91
|
-
archived: true,
|
|
92
|
-
tombstone: { entry_id: entryId, planned: false, applied: false },
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
per_id.push({ id, archived: true });
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const noop = !anyChange;
|
|
101
|
-
return {
|
|
102
|
-
action: "forget",
|
|
103
|
-
ok: true,
|
|
104
|
-
dry_run: true,
|
|
105
|
-
per_id,
|
|
106
|
-
...(noop ? { noop: true } : {}),
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
// Apply mode: confirm === true
|
|
110
|
-
// We need to archive locally and publish tombstones through applyExport
|
|
111
|
-
let partial = false;
|
|
112
|
-
let appliedTombstones = 0;
|
|
113
|
-
for (const id of input.ids) {
|
|
114
|
-
const row = deps.store
|
|
115
|
-
.prepare("SELECT id, type, content, scope, status, layer, shared_entry_id FROM memories WHERE id = ?")
|
|
116
|
-
.get(id);
|
|
117
|
-
if (!row) {
|
|
118
|
-
per_id.push({ id, archived: false, reason: "not_found" });
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (row.status === "archived") {
|
|
122
|
-
const isShared = row.layer === "shared" || row.shared_entry_id !== null;
|
|
123
|
-
if (isShared) {
|
|
124
|
-
const entryId = getEntryId(row);
|
|
125
|
-
per_id.push({
|
|
126
|
-
id,
|
|
127
|
-
archived: false,
|
|
128
|
-
reason: "already_archived",
|
|
129
|
-
tombstone: { entry_id: entryId, planned: false, applied: false },
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
per_id.push({ id, archived: false, reason: "already_archived" });
|
|
134
|
-
}
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
// Archive locally in a transaction
|
|
138
|
-
let archived = false;
|
|
139
|
-
try {
|
|
140
|
-
deps.store.transaction(() => {
|
|
141
|
-
deps.store
|
|
142
|
-
.prepare(`UPDATE memories SET status = 'archived', archived_at = datetime('now'), updated_at = datetime('now') WHERE id = ? AND status != 'archived'`)
|
|
143
|
-
.run(id);
|
|
144
|
-
const changes = deps.store.prepare("SELECT changes() AS c").get();
|
|
145
|
-
if (changes.c > 0)
|
|
146
|
-
archived = true;
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
catch (e) {
|
|
150
|
-
per_id.push({ id, archived: false, reason: "db_error" });
|
|
151
|
-
partial = true;
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
anyChange = anyChange || archived;
|
|
155
|
-
const isShared = row.layer === "shared" || row.shared_entry_id !== null;
|
|
156
|
-
if (!isShared) {
|
|
157
|
-
per_id.push({ id, archived });
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
const entryId = getEntryId(row);
|
|
161
|
-
// Plan tombstone
|
|
162
|
-
let plan;
|
|
163
|
-
try {
|
|
164
|
-
plan = deps.sharedLayer.planTombstone([entryId], deps.okfPath);
|
|
165
|
-
}
|
|
166
|
-
catch (e) {
|
|
167
|
-
// plan failure: rollback DB archive for this id (best-effort)
|
|
168
|
-
try {
|
|
169
|
-
deps.store
|
|
170
|
-
.prepare(`UPDATE memories SET status = 'active', archived_at = NULL WHERE id = ?`)
|
|
171
|
-
.run(id);
|
|
172
|
-
}
|
|
173
|
-
catch { }
|
|
174
|
-
per_id.push({
|
|
175
|
-
id,
|
|
176
|
-
archived: false,
|
|
177
|
-
reason: "plan_failed",
|
|
178
|
-
tombstone: { entry_id: entryId, planned: false, applied: false },
|
|
179
|
-
});
|
|
180
|
-
partial = true;
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
if (plan.write.outcome === "refused") {
|
|
184
|
-
// Refusal reasons are reused verbatim (repo_mismatch, unknown_entry)
|
|
185
|
-
const reason = plan.write.reason ?? "refused";
|
|
186
|
-
per_id.push({
|
|
187
|
-
id,
|
|
188
|
-
archived,
|
|
189
|
-
reason,
|
|
190
|
-
tombstone: { entry_id: entryId, planned: false, applied: false },
|
|
191
|
-
});
|
|
192
|
-
// DB archive already done; keep it (local archive is independent of shared refusal)
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
// Apply through single write funnel
|
|
196
|
-
try {
|
|
197
|
-
const applied = deps.sharedLayer.applyExport(plan);
|
|
198
|
-
const wasWritten = applied.applied === "written";
|
|
199
|
-
const wasNoop = applied.applied === "noop";
|
|
200
|
-
if (wasWritten)
|
|
201
|
-
appliedTombstones++;
|
|
202
|
-
// per_id tombstone: planned true if not refused, applied true only if written
|
|
203
|
-
per_id.push({
|
|
204
|
-
id,
|
|
205
|
-
archived,
|
|
206
|
-
tombstone: {
|
|
207
|
-
entry_id: entryId,
|
|
208
|
-
planned: true,
|
|
209
|
-
applied: wasWritten,
|
|
210
|
-
},
|
|
211
|
-
});
|
|
212
|
-
void wasNoop;
|
|
213
|
-
}
|
|
214
|
-
catch (e) {
|
|
215
|
-
// v1.1.0 — failure mid-way: transaction rollback restores DB; already-applied OKF write is reported honestly
|
|
216
|
-
// For this id, DB was already archived, but file write failed. We attempt to rollback DB for this id.
|
|
217
|
-
try {
|
|
218
|
-
deps.store
|
|
219
|
-
.prepare(`UPDATE memories SET status = 'active', archived_at = NULL WHERE id = ?`)
|
|
220
|
-
.run(id);
|
|
221
|
-
archived = false;
|
|
222
|
-
}
|
|
223
|
-
catch { }
|
|
224
|
-
per_id.push({
|
|
225
|
-
id,
|
|
226
|
-
archived: false,
|
|
227
|
-
reason: "partial",
|
|
228
|
-
tombstone: { entry_id: entryId, planned: true, applied: false },
|
|
229
|
-
});
|
|
230
|
-
partial = true;
|
|
231
|
-
// Continue to next id? According spec, failure mid-way reports ok:false reason partial
|
|
232
|
-
// We keep processing remaining ids? For now we continue but mark partial.
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
// Metrics: increment forget_tombstones_published by applied count (only when written, not noop)
|
|
236
|
-
if (appliedTombstones > 0) {
|
|
237
|
-
try {
|
|
238
|
-
deps.metrics.incr("forget_tombstones_published", appliedTombstones);
|
|
239
|
-
}
|
|
240
|
-
catch { }
|
|
241
|
-
}
|
|
242
|
-
const noop = !anyChange && per_id.every((p) => p.archived === false);
|
|
243
|
-
if (partial) {
|
|
244
|
-
return {
|
|
245
|
-
action: "forget",
|
|
246
|
-
ok: false,
|
|
247
|
-
dry_run: false,
|
|
248
|
-
per_id,
|
|
249
|
-
reason: "partial",
|
|
250
|
-
...(noop ? { noop: true } : {}),
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
return {
|
|
254
|
-
action: "forget",
|
|
255
|
-
ok: true,
|
|
256
|
-
dry_run: false,
|
|
257
|
-
per_id,
|
|
258
|
-
...(noop ? { noop: true } : {}),
|
|
259
|
-
};
|
|
260
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { Store } from "./Store.js";
|
|
2
|
-
import type { HostSurface } from "./host.js";
|
|
3
|
-
import type { SettingsReader } from "./native.js";
|
|
4
|
-
export type NativeAction = "show" | "enable" | "disable";
|
|
5
|
-
export interface NativeRegistrationRow {
|
|
6
|
-
readonly surface: "skill" | "reference";
|
|
7
|
-
readonly registered: boolean;
|
|
8
|
-
readonly verified: boolean;
|
|
9
|
-
readonly attached_at: string | null;
|
|
10
|
-
}
|
|
11
|
-
export interface NativeReport {
|
|
12
|
-
readonly action: NativeAction;
|
|
13
|
-
/** The setting value: '1' or '0' as stored (TEXT). */
|
|
14
|
-
readonly value: "1" | "0";
|
|
15
|
-
/** Whether registration would be effective on THIS host (v2 subpath present). */
|
|
16
|
-
readonly effective: boolean;
|
|
17
|
-
/** Present when `effective` is false: why the intent is inert. */
|
|
18
|
-
readonly reason?: string;
|
|
19
|
-
/** The latest persisted outcome per surface (show only). */
|
|
20
|
-
readonly registrations?: NativeRegistrationRow[];
|
|
21
|
-
/** The restart note (enable/disable only). */
|
|
22
|
-
readonly note?: string;
|
|
23
|
-
}
|
|
24
|
-
export interface NativeDeps {
|
|
25
|
-
readonly host: HostSurface;
|
|
26
|
-
readonly store: Store;
|
|
27
|
-
readonly settings: SettingsReader;
|
|
28
|
-
}
|
|
29
|
-
export declare function handleNative(action: NativeAction, deps: NativeDeps): NativeReport;
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// v0.9.0 (K9-019 / plan §5.5, D9-12) — `kevin_native`: inspect and toggle
|
|
2
|
-
// `native_registration_enabled`.
|
|
3
|
-
//
|
|
4
|
-
// Pure in the sense that matters: `show` reads only; `enable`/`disable`
|
|
5
|
-
// write `kevin_settings` and nothing else. Neither action re-attaches —
|
|
6
|
-
// the probe is frozen for the process lifetime (D9-12) — and the response
|
|
7
|
-
// says so explicitly, naming a restart as the requirement. `enable` on a
|
|
8
|
-
// host without the v2 subpath succeeds and reports the registration as
|
|
9
|
-
// inert: the setting is a statement of intent that becomes effective when
|
|
10
|
-
// the host catches up, and refusing would make it untestable on the
|
|
11
|
-
// majority of installations.
|
|
12
|
-
//
|
|
13
|
-
// The stored value is TEXT `'1'` or `'0'` — never a boolean, never
|
|
14
|
-
// `'true'` (kevin_settings.value is TEXT; compare with `=== "1"`).
|
|
15
|
-
function lastRows(store) {
|
|
16
|
-
try {
|
|
17
|
-
return store
|
|
18
|
-
.prepare(`SELECT surface, registered, verified, attached_at
|
|
19
|
-
FROM native_registrations
|
|
20
|
-
ORDER BY attached_at DESC, id DESC
|
|
21
|
-
LIMIT 20`)
|
|
22
|
-
.all()
|
|
23
|
-
.map((row) => {
|
|
24
|
-
const r = row;
|
|
25
|
-
return {
|
|
26
|
-
surface: r.surface,
|
|
27
|
-
registered: r.registered === 1,
|
|
28
|
-
verified: r.verified === 1,
|
|
29
|
-
attached_at: r.attached_at,
|
|
30
|
-
};
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
// pre-010 database: the table does not exist yet — no rows to
|
|
35
|
-
// report, and that is the truth.
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/** v0.9.0 (K9-019 / plan §5.5, D9-12) — the pure decision: the probe is
|
|
40
|
-
* frozen for the process lifetime, so "effective" is derived from the
|
|
41
|
-
* resolved host surface, never from a fresh probe call. */
|
|
42
|
-
function effectiveOn(host) {
|
|
43
|
-
return host.v2.skill || host.v2.reference;
|
|
44
|
-
}
|
|
45
|
-
export function handleNative(action, deps) {
|
|
46
|
-
const current = deps.settings.getSetting("native_registration_enabled", "0");
|
|
47
|
-
const value = current === "1" ? "1" : "0";
|
|
48
|
-
if (action === "show") {
|
|
49
|
-
const report = {
|
|
50
|
-
action,
|
|
51
|
-
value,
|
|
52
|
-
effective: effectiveOn(deps.host),
|
|
53
|
-
registrations: lastRows(deps.store),
|
|
54
|
-
...(effectiveOn(deps.host)
|
|
55
|
-
? {}
|
|
56
|
-
: {
|
|
57
|
-
reason: "v2 subpath absent from the resolved host package — registration would be inert",
|
|
58
|
-
}),
|
|
59
|
-
};
|
|
60
|
-
return report;
|
|
61
|
-
}
|
|
62
|
-
const next = action === "enable" ? "1" : "0";
|
|
63
|
-
deps.store
|
|
64
|
-
.prepare(`INSERT INTO kevin_settings (key, value) VALUES (?, ?)
|
|
65
|
-
ON CONFLICT(key) DO UPDATE SET value = excluded.value`)
|
|
66
|
-
.run("native_registration_enabled", next);
|
|
67
|
-
const report = {
|
|
68
|
-
action,
|
|
69
|
-
value: next,
|
|
70
|
-
effective: effectiveOn(deps.host),
|
|
71
|
-
note: "the probe is frozen for the process lifetime — restart the host for the change to take effect",
|
|
72
|
-
...(effectiveOn(deps.host)
|
|
73
|
-
? {}
|
|
74
|
-
: {
|
|
75
|
-
reason: "v2 subpath absent from the resolved host package — registration would be inert",
|
|
76
|
-
}),
|
|
77
|
-
};
|
|
78
|
-
return report;
|
|
79
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { ArtifactWriter } from "./ArtifactWriter.js";
|
|
2
|
-
import type { Curator, ProposalKind } from "./Curator.js";
|
|
3
|
-
/**
|
|
4
|
-
* K6-014 — v0.6.0 pull — `kevin_propose` tool logic.
|
|
5
|
-
*
|
|
6
|
-
* Strict dry run in the exact sense of v0.5 `kevin_trace` (D5-08): creates
|
|
7
|
-
* `pending` rows and returns their unified diffs. No disk write, no `curated`
|
|
8
|
-
* flag, no metric beyond `proposals_created` (which `Curator.propose()`
|
|
9
|
-
* itself increments).
|
|
10
|
-
*/
|
|
11
|
-
export interface ProposedChange {
|
|
12
|
-
readonly id: string;
|
|
13
|
-
readonly kind: ProposalKind;
|
|
14
|
-
readonly targetPath: string;
|
|
15
|
-
readonly memoryIds: readonly string[];
|
|
16
|
-
readonly status: "pending";
|
|
17
|
-
readonly createdAt: string;
|
|
18
|
-
readonly diff: string;
|
|
19
|
-
}
|
|
20
|
-
export interface ProposeResult {
|
|
21
|
-
readonly proposals: readonly ProposedChange[];
|
|
22
|
-
}
|
|
23
|
-
export declare function kevinPropose(curator: Curator, writer: ArtifactWriter, kind: ProposalKind): ProposeResult;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export function kevinPropose(curator, writer, kind) {
|
|
2
|
-
const proposals = curator.propose(kind, writer);
|
|
3
|
-
return {
|
|
4
|
-
proposals: proposals.map((p) => ({
|
|
5
|
-
id: p.id,
|
|
6
|
-
kind: p.kind,
|
|
7
|
-
targetPath: p.targetPath,
|
|
8
|
-
memoryIds: [...p.memoryIds],
|
|
9
|
-
status: "pending",
|
|
10
|
-
createdAt: p.createdAt,
|
|
11
|
-
diff: p.diff,
|
|
12
|
-
})),
|
|
13
|
-
};
|
|
14
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { ArtifactWriter } from "./ArtifactWriter.js";
|
|
2
|
-
import type { MaterializedBundle, Materializer } from "./Materializer.js";
|
|
3
|
-
import type { MemoryService } from "./MemoryService.js";
|
|
4
|
-
import type { Capabilities } from "./capabilities.js";
|
|
5
|
-
/**
|
|
6
|
-
* K6-020 — v0.6.0 pull — `kevin_publish` tool logic.
|
|
7
|
-
*
|
|
8
|
-
* Regenerates the pull-channel bundles on demand. Writes only through the
|
|
9
|
-
* `ArtifactWriter` and only to the Materializer's targets (D6-01, D6-07):
|
|
10
|
-
* `~/.opencode-kevin/skills/project-knowledge.md` and
|
|
11
|
-
* `~/.opencode-kevin/refs/<topic>.md`. `agents_md_path` is unreachable
|
|
12
|
-
* here by construction — the paths come from `bundleTargets()`, never from
|
|
13
|
-
* a setting — and that is asserted in the integration test even when the
|
|
14
|
-
* setting points inside `~/.opencode-kevin/`.
|
|
15
|
-
*
|
|
16
|
-
* Registration is a session-start concern (K6-018/019), not this tool's:
|
|
17
|
-
* the output reports the emission state honestly (`"unavailable"` on a v1
|
|
18
|
-
* host, `"off"` for a '0' setting, `"on"` when session start registers)
|
|
19
|
-
* instead of pretending success.
|
|
20
|
-
*/
|
|
21
|
-
export type EmissionState = "on" | "off" | "unavailable";
|
|
22
|
-
export interface PublishResult {
|
|
23
|
-
readonly bundles: readonly {
|
|
24
|
-
readonly topic: string;
|
|
25
|
-
readonly outcome: MaterializedBundle["outcome"];
|
|
26
|
-
}[];
|
|
27
|
-
readonly emission: {
|
|
28
|
-
readonly skill: EmissionState;
|
|
29
|
-
readonly reference: EmissionState;
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
export interface PublishDeps {
|
|
33
|
-
readonly materializer: Materializer;
|
|
34
|
-
readonly writer: ArtifactWriter;
|
|
35
|
-
readonly memoryService: MemoryService;
|
|
36
|
-
readonly capabilities: Capabilities;
|
|
37
|
-
}
|
|
38
|
-
export declare function kevinPublish(deps: PublishDeps): PublishResult;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
function emissionState(capable, setting) {
|
|
2
|
-
if (!capable)
|
|
3
|
-
return "unavailable";
|
|
4
|
-
return setting === "1" ? "on" : "off";
|
|
5
|
-
}
|
|
6
|
-
export function kevinPublish(deps) {
|
|
7
|
-
const bundles = deps.materializer.materialize(deps.writer);
|
|
8
|
-
return {
|
|
9
|
-
bundles: bundles.map((b) => ({
|
|
10
|
-
topic: b.topic,
|
|
11
|
-
outcome: b.outcome,
|
|
12
|
-
})),
|
|
13
|
-
emission: {
|
|
14
|
-
skill: emissionState(deps.capabilities.skills, deps.memoryService.getSetting("skill_emission_enabled", "0")),
|
|
15
|
-
reference: emissionState(deps.capabilities.references, deps.memoryService.getSetting("reference_emission_enabled", "0")),
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { Store } from "./Store.js";
|
|
2
|
-
export interface WhyInput {
|
|
3
|
-
query: string;
|
|
4
|
-
}
|
|
5
|
-
export interface WhyTraceEvent {
|
|
6
|
-
event: "failure" | "fix";
|
|
7
|
-
fingerprint: string;
|
|
8
|
-
session: string;
|
|
9
|
-
ts: string;
|
|
10
|
-
}
|
|
11
|
-
export interface WhyResult {
|
|
12
|
-
summary: string;
|
|
13
|
-
confidence: number;
|
|
14
|
-
evidence_count: number;
|
|
15
|
-
/** v0.4.0 (K4-020) — negative evidence for the honest phrasing. */
|
|
16
|
-
recurrence_count: number;
|
|
17
|
-
/** v0.4.0 (K4-020) — deterministic "Fixed by:" raw material, if any. */
|
|
18
|
-
fix_args: string | null;
|
|
19
|
-
last_verified: string | null;
|
|
20
|
-
trace: WhyTraceEvent[];
|
|
21
|
-
related_rules: string[];
|
|
22
|
-
}
|
|
23
|
-
export declare function kevinWhy(store: Store, query: string): WhyResult | null;
|
package/dist/plugin/kevin_why.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { TS_CODE_RULES } from "./Reflector.js";
|
|
2
|
-
import { hasFeedbackColumns } from "./columns.js";
|
|
3
|
-
import { computeConfidence } from "./confidence.js";
|
|
4
|
-
import { toMatchClause, tokenizeQuery } from "./query-tokenizer.js";
|
|
5
|
-
export function kevinWhy(store, query) {
|
|
6
|
-
// v0.3.0 fix (bug #7) — the old code wrapped the WHOLE query in a
|
|
7
|
-
// single quoted phrase, so it only matched the exact full-string
|
|
8
|
-
// sequence. Tokenize like MemoryService.queryRelevant: quote each
|
|
9
|
-
// word (escaping inner quotes) and AND them together, so partial or
|
|
10
|
-
// multi-word queries still hit the FTS index.
|
|
11
|
-
const tokens = tokenizeQuery(query);
|
|
12
|
-
if (tokens.length === 0)
|
|
13
|
-
return null;
|
|
14
|
-
const match = toMatchClause(tokens, " AND ");
|
|
15
|
-
// Best-effort (v0.4.0): DBs pre-005 lack `recurrence_count`; degrade
|
|
16
|
-
// to "no explanation" instead of crashing the calling session.
|
|
17
|
-
let patternRows;
|
|
18
|
-
try {
|
|
19
|
-
patternRows = store
|
|
20
|
-
.prepare(`SELECT m.id, m.content, m.fingerprint, m.evidence_count,
|
|
21
|
-
m.recurrence_count, m.fix_args, m.last_verified_at, m.created_at${hasFeedbackColumns(store)
|
|
22
|
-
? ", m.feedback_positive, m.feedback_negative"
|
|
23
|
-
: ""}
|
|
24
|
-
FROM memories_fts
|
|
25
|
-
JOIN memories m ON m.rowid = memories_fts.rowid
|
|
26
|
-
WHERE memories_fts MATCH ?
|
|
27
|
-
AND m.type = 'pattern'
|
|
28
|
-
AND m.origin IN ('causal', 'pattern')
|
|
29
|
-
AND m.status = 'active'
|
|
30
|
-
ORDER BY m.evidence_count DESC, m.created_at DESC
|
|
31
|
-
LIMIT 1`)
|
|
32
|
-
.all(match);
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
if (patternRows.length === 0)
|
|
38
|
-
return null;
|
|
39
|
-
const pattern = patternRows[0];
|
|
40
|
-
const fp = pattern.fingerprint;
|
|
41
|
-
// v0.4.0 (K4-010) — two-sided confidence shared with promoteToPattern.
|
|
42
|
-
// v0.5.0 (K5-010) — human feedback terms nudge it (D5-02).
|
|
43
|
-
const confidence = computeConfidence(pattern.evidence_count ?? 0, pattern.recurrence_count ?? 0, pattern.feedback_positive ?? 0, pattern.feedback_negative ?? 0);
|
|
44
|
-
// BUG-007 — the trace is built from the error-memory sessions below;
|
|
45
|
-
// the legacy `traceRows` probe (executed but unused, with a
|
|
46
|
-
// never-matching `tc.fingerprint LIKE '%<8-hex>%'` branch against the
|
|
47
|
-
// tool|args|success hash) was deleted.
|
|
48
|
-
const trace = [];
|
|
49
|
-
const errorSessions = store
|
|
50
|
-
.prepare(`SELECT DISTINCT source_session, fingerprint, created_at
|
|
51
|
-
FROM memories
|
|
52
|
-
WHERE fingerprint = ? AND type = 'error' AND origin = 'reflector'
|
|
53
|
-
ORDER BY created_at ASC`)
|
|
54
|
-
.all(fp);
|
|
55
|
-
for (const err of errorSessions) {
|
|
56
|
-
trace.push({
|
|
57
|
-
event: "failure",
|
|
58
|
-
fingerprint: err.fingerprint,
|
|
59
|
-
session: err.source_session ?? "unknown",
|
|
60
|
-
ts: err.created_at,
|
|
61
|
-
});
|
|
62
|
-
const fixRow = store
|
|
63
|
-
.prepare(`SELECT session_id, ts FROM tool_calls
|
|
64
|
-
WHERE fix_for_fingerprint = ?
|
|
65
|
-
AND session_id = ?
|
|
66
|
-
ORDER BY ts ASC LIMIT 1`)
|
|
67
|
-
.get(fp, err.source_session);
|
|
68
|
-
if (fixRow) {
|
|
69
|
-
trace.push({
|
|
70
|
-
event: "fix",
|
|
71
|
-
fingerprint: fp,
|
|
72
|
-
session: fixRow.session_id,
|
|
73
|
-
ts: fixRow.ts,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
const relatedRules = [];
|
|
78
|
-
const tsCodeMatch = query.match(/TS(\d{4,5})/);
|
|
79
|
-
if (tsCodeMatch) {
|
|
80
|
-
const hint = TS_CODE_RULES[tsCodeMatch[1]] ?? `review TS${tsCodeMatch[1]}`;
|
|
81
|
-
relatedRules.push(hint);
|
|
82
|
-
}
|
|
83
|
-
// v0.4.0 (K4-020) — honest summary (plan §5.3 / D4-11): with
|
|
84
|
-
// recurrences the pattern is NOT consistently resolved, so the
|
|
85
|
-
// phrasing reports the real success ratio ("resolved in N of M
|
|
86
|
-
// attempts") instead of "consistently resolved".
|
|
87
|
-
const evidenceCount = pattern.evidence_count ?? 0;
|
|
88
|
-
const recurrence = pattern.recurrence_count ?? 0;
|
|
89
|
-
let summary;
|
|
90
|
-
if (recurrence > 0) {
|
|
91
|
-
const fix = pattern.fix_args
|
|
92
|
-
? `fixing ${pattern.fix_args}`
|
|
93
|
-
: `fixing ${relatedRules.length > 0
|
|
94
|
-
? relatedRules.join(", ")
|
|
95
|
-
: "the underlying issue"}`;
|
|
96
|
-
summary = `When tool fails with ${query}: resolved in ${evidenceCount} of ${evidenceCount + recurrence} attempts by ${fix}.`;
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
summary = `When tool fails with ${query}: ${confidence >= 0.7 ? "consistently" : "often"} resolved by fixing ${relatedRules.length > 0 ? relatedRules.join(", ") : "the underlying issue"}.`;
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
summary,
|
|
103
|
-
confidence,
|
|
104
|
-
evidence_count: evidenceCount,
|
|
105
|
-
recurrence_count: recurrence,
|
|
106
|
-
fix_args: pattern.fix_args ?? null,
|
|
107
|
-
last_verified: pattern.last_verified_at ?? null,
|
|
108
|
-
trace,
|
|
109
|
-
related_rules: relatedRules,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export interface MemoryBlockItem {
|
|
2
|
-
id?: string;
|
|
3
|
-
type: string;
|
|
4
|
-
content: string;
|
|
5
|
-
protect?: boolean;
|
|
6
|
-
/** v0.4.0 (K4-023) — weak lesson admitted in debug mode: the row is
|
|
7
|
-
* rendered with a `(low confidence)` marker line. */
|
|
8
|
-
weak?: boolean;
|
|
9
|
-
}
|
|
10
|
-
export declare function escapeInjectedText(text: string): string;
|
|
11
|
-
/**
|
|
12
|
-
* v0.4.0 (K4-012) — progressive disclosure for injection: a short
|
|
13
|
-
* snippet row instead of the full body. Row = `id:` line + `[type]`
|
|
14
|
-
* prefix + the first 2 non-empty lines of content, wrapped in
|
|
15
|
-
* `<protect>` unless the memory opted out (`protect: false`). The
|
|
16
|
-
* full body stays available via `kevin_get`.
|
|
17
|
-
*/
|
|
18
|
-
export declare function formatSnippet(m: MemoryBlockItem): string;
|
|
19
|
-
export declare function formatMemories(memories: MemoryBlockItem[], tag: "context" | "memory"): string;
|
|
20
|
-
export declare function formatMemorySnippets(memories: MemoryBlockItem[], tag: "context" | "memory"): string;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
export function escapeInjectedText(text) {
|
|
2
|
-
return text
|
|
3
|
-
.replace(/&/g, "&")
|
|
4
|
-
.replace(/</g, "<")
|
|
5
|
-
.replace(/>/g, ">");
|
|
6
|
-
}
|
|
7
|
-
function typePrefix(m) {
|
|
8
|
-
const t = escapeInjectedText(String(m.type));
|
|
9
|
-
return m.weak ? `[${t}] (low confidence)` : `[${t}]`;
|
|
10
|
-
}
|
|
11
|
-
function formatRow(m) {
|
|
12
|
-
const idLine = m.id ? `id: ${escapeInjectedText(m.id)}\n` : "";
|
|
13
|
-
const body = `${idLine}${typePrefix(m)} ${escapeInjectedText(m.content)}`;
|
|
14
|
-
return m.protect === false ? body : `<protect>\n${body}\n</protect>`;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* v0.4.0 (K4-012) — progressive disclosure for injection: a short
|
|
18
|
-
* snippet row instead of the full body. Row = `id:` line + `[type]`
|
|
19
|
-
* prefix + the first 2 non-empty lines of content, wrapped in
|
|
20
|
-
* `<protect>` unless the memory opted out (`protect: false`). The
|
|
21
|
-
* full body stays available via `kevin_get`.
|
|
22
|
-
*/
|
|
23
|
-
export function formatSnippet(m) {
|
|
24
|
-
const lines = m.content
|
|
25
|
-
.split(/\r?\n/)
|
|
26
|
-
.map((l) => l.trim())
|
|
27
|
-
.filter((l) => l.length > 0)
|
|
28
|
-
.slice(0, 2);
|
|
29
|
-
// v0.4.0 (K4-025) — a pattern's `Fixed by:` line is the highest-value
|
|
30
|
-
// payload (plan §5.4 / D4-07); surface it in the snippet even though
|
|
31
|
-
// it sits below the 2-line cut. Only patterns carry the line.
|
|
32
|
-
const fixLine = lines
|
|
33
|
-
.map((l) => l)
|
|
34
|
-
.concat(m.content
|
|
35
|
-
.split(/\r?\n/)
|
|
36
|
-
.map((l) => l.trim())
|
|
37
|
-
.filter((l) => l.startsWith("Fixed by:")))[2];
|
|
38
|
-
const idLine = m.id ? `id: ${escapeInjectedText(m.id)}\n` : "";
|
|
39
|
-
const bodyLines = fixLine ? [...lines, fixLine] : lines;
|
|
40
|
-
const body = bodyLines.length === 0
|
|
41
|
-
? ""
|
|
42
|
-
: `${idLine}${typePrefix(m)} ${escapeInjectedText(bodyLines.join("\n"))}`;
|
|
43
|
-
return m.protect === false ? body : `<protect>\n${body}\n</protect>`;
|
|
44
|
-
}
|
|
45
|
-
function wrapBlock(body, tag) {
|
|
46
|
-
return tag === "context"
|
|
47
|
-
? `<kevin-context>Lecciones relevantes:\n${body}\n</kevin-context>`
|
|
48
|
-
: `<kevin-memory>\n${body}\n</kevin-memory>`;
|
|
49
|
-
}
|
|
50
|
-
export function formatMemories(memories, tag) {
|
|
51
|
-
if (memories.length === 0)
|
|
52
|
-
return "";
|
|
53
|
-
return wrapBlock(memories.map(formatRow).join("\n"), tag);
|
|
54
|
-
}
|
|
55
|
-
export function formatMemorySnippets(memories, tag) {
|
|
56
|
-
if (memories.length === 0)
|
|
57
|
-
return "";
|
|
58
|
-
return wrapBlock(memories.map(formatSnippet).join("\n"), tag);
|
|
59
|
-
}
|