@jmtrin/opencode-kevin 1.1.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.
Files changed (131) hide show
  1. package/dist/plugin/capabilities.d.ts +2 -0
  2. package/dist/plugin/capabilities.js +3 -0
  3. package/dist/plugin/index.d.ts +5 -4
  4. package/dist/plugin/index.js +401 -108
  5. package/dist/plugin/native.d.ts +2 -2
  6. package/dist/plugin/native.js +2 -2
  7. package/package.json +56 -63
  8. package/LICENSE +0 -21
  9. package/README.md +0 -576
  10. package/dist/migrations/001_initial.sql +0 -92
  11. package/dist/migrations/002_indexes.sql +0 -14
  12. package/dist/migrations/003_v02_signal.sql +0 -58
  13. package/dist/migrations/004_v03_knowledge.sql +0 -138
  14. package/dist/migrations/005_v04_signal.sql +0 -57
  15. package/dist/migrations/006_v05_glassbox.sql +0 -118
  16. package/dist/migrations/007_v06_pull.sql +0 -145
  17. package/dist/migrations/008_v07_truth.sql +0 -124
  18. package/dist/migrations/009_v08_team.sql +0 -100
  19. package/dist/migrations/010_v09_native.sql +0 -78
  20. package/dist/migrations/011_v10_proven.sql +0 -69
  21. package/dist/migrations/012_v11_drift.sql +0 -24
  22. package/dist/plugin/Archiver.d.ts +0 -42
  23. package/dist/plugin/Archiver.js +0 -83
  24. package/dist/plugin/ArtifactWriter.d.ts +0 -93
  25. package/dist/plugin/ArtifactWriter.js +0 -291
  26. package/dist/plugin/CausalChain.d.ts +0 -22
  27. package/dist/plugin/CausalChain.js +0 -197
  28. package/dist/plugin/ConflictDetector.d.ts +0 -35
  29. package/dist/plugin/ConflictDetector.js +0 -260
  30. package/dist/plugin/ContextInjector.d.ts +0 -222
  31. package/dist/plugin/ContextInjector.js +0 -533
  32. package/dist/plugin/ConventionMiner.d.ts +0 -35
  33. package/dist/plugin/ConventionMiner.js +0 -242
  34. package/dist/plugin/Curator.d.ts +0 -116
  35. package/dist/plugin/Curator.js +0 -324
  36. package/dist/plugin/Feedback.d.ts +0 -67
  37. package/dist/plugin/Feedback.js +0 -120
  38. package/dist/plugin/HookLiveness.d.ts +0 -88
  39. package/dist/plugin/HookLiveness.js +0 -322
  40. package/dist/plugin/InjectionLedger.d.ts +0 -98
  41. package/dist/plugin/InjectionLedger.js +0 -295
  42. package/dist/plugin/LessonFixer.d.ts +0 -44
  43. package/dist/plugin/LessonFixer.js +0 -45
  44. package/dist/plugin/Materializer.d.ts +0 -83
  45. package/dist/plugin/Materializer.js +0 -181
  46. package/dist/plugin/MemoryService.d.ts +0 -360
  47. package/dist/plugin/MemoryService.js +0 -1242
  48. package/dist/plugin/Migrate.d.ts +0 -17
  49. package/dist/plugin/Migrate.js +0 -271
  50. package/dist/plugin/PatternMiner.d.ts +0 -49
  51. package/dist/plugin/PatternMiner.js +0 -132
  52. package/dist/plugin/QualityGate.d.ts +0 -124
  53. package/dist/plugin/QualityGate.js +0 -119
  54. package/dist/plugin/Reflector.d.ts +0 -91
  55. package/dist/plugin/Reflector.js +0 -307
  56. package/dist/plugin/RepoIdentity.d.ts +0 -124
  57. package/dist/plugin/RepoIdentity.js +0 -300
  58. package/dist/plugin/RepoTruth.d.ts +0 -80
  59. package/dist/plugin/RepoTruth.js +0 -599
  60. package/dist/plugin/Retrospective.d.ts +0 -17
  61. package/dist/plugin/Retrospective.js +0 -213
  62. package/dist/plugin/SharedLayer.d.ts +0 -159
  63. package/dist/plugin/SharedLayer.js +0 -462
  64. package/dist/plugin/Store.d.ts +0 -29
  65. package/dist/plugin/Store.js +0 -50
  66. package/dist/plugin/ToolCallObserver.d.ts +0 -32
  67. package/dist/plugin/ToolCallObserver.js +0 -201
  68. package/dist/plugin/columns.d.ts +0 -11
  69. package/dist/plugin/columns.js +0 -54
  70. package/dist/plugin/confidence.d.ts +0 -8
  71. package/dist/plugin/confidence.js +0 -34
  72. package/dist/plugin/contract.d.ts +0 -54
  73. package/dist/plugin/contract.js +0 -383
  74. package/dist/plugin/diff.d.ts +0 -8
  75. package/dist/plugin/diff.js +0 -182
  76. package/dist/plugin/escape.d.ts +0 -39
  77. package/dist/plugin/escape.js +0 -60
  78. package/dist/plugin/fingerprint.d.ts +0 -27
  79. package/dist/plugin/fingerprint.js +0 -73
  80. package/dist/plugin/inferability.d.ts +0 -32
  81. package/dist/plugin/inferability.js +0 -88
  82. package/dist/plugin/kevin_approve.d.ts +0 -34
  83. package/dist/plugin/kevin_approve.js +0 -52
  84. package/dist/plugin/kevin_audit.d.ts +0 -213
  85. package/dist/plugin/kevin_audit.js +0 -563
  86. package/dist/plugin/kevin_bench.d.ts +0 -10
  87. package/dist/plugin/kevin_bench.js +0 -82
  88. package/dist/plugin/kevin_conflicts.d.ts +0 -9
  89. package/dist/plugin/kevin_conflicts.js +0 -50
  90. package/dist/plugin/kevin_contract.d.ts +0 -8
  91. package/dist/plugin/kevin_contract.js +0 -59
  92. package/dist/plugin/kevin_doctor.d.ts +0 -62
  93. package/dist/plugin/kevin_doctor.js +0 -196
  94. package/dist/plugin/kevin_facts.d.ts +0 -42
  95. package/dist/plugin/kevin_facts.js +0 -36
  96. package/dist/plugin/kevin_forget.d.ts +0 -33
  97. package/dist/plugin/kevin_forget.js +0 -260
  98. package/dist/plugin/kevin_native.d.ts +0 -29
  99. package/dist/plugin/kevin_native.js +0 -79
  100. package/dist/plugin/kevin_propose.d.ts +0 -23
  101. package/dist/plugin/kevin_propose.js +0 -14
  102. package/dist/plugin/kevin_publish.d.ts +0 -38
  103. package/dist/plugin/kevin_publish.js +0 -18
  104. package/dist/plugin/kevin_why.d.ts +0 -23
  105. package/dist/plugin/kevin_why.js +0 -111
  106. package/dist/plugin/memory-format.d.ts +0 -20
  107. package/dist/plugin/memory-format.js +0 -59
  108. package/dist/plugin/metrics.d.ts +0 -97
  109. package/dist/plugin/metrics.js +0 -284
  110. package/dist/plugin/okf-export.d.ts +0 -3
  111. package/dist/plugin/okf-export.js +0 -134
  112. package/dist/plugin/okf-import.d.ts +0 -76
  113. package/dist/plugin/okf-import.js +0 -271
  114. package/dist/plugin/okf.d.ts +0 -107
  115. package/dist/plugin/okf.js +0 -303
  116. package/dist/plugin/perf.d.ts +0 -35
  117. package/dist/plugin/perf.js +0 -174
  118. package/dist/plugin/query-tokenizer.d.ts +0 -13
  119. package/dist/plugin/query-tokenizer.js +0 -133
  120. package/dist/plugin/redact.d.ts +0 -2
  121. package/dist/plugin/redact.js +0 -18
  122. package/dist/plugin/replay-types.d.ts +0 -69
  123. package/dist/plugin/replay-types.js +0 -156
  124. package/dist/plugin/replay.d.ts +0 -36
  125. package/dist/plugin/replay.js +0 -202
  126. package/dist/plugin/sqlite-adapter.d.ts +0 -12
  127. package/dist/plugin/sqlite-adapter.js +0 -81
  128. package/dist/plugin/time-ms.d.ts +0 -1
  129. package/dist/plugin/time-ms.js +0 -16
  130. package/dist/plugin/uuid.d.ts +0 -1
  131. 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;
@@ -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, "&amp;")
4
- .replace(/</g, "&lt;")
5
- .replace(/>/g, "&gt;");
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
- }