@jmtrin/opencode-kevin 0.2.0 → 0.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/README.md +76 -35
- package/dist/migrations/004_v03_knowledge.sql +138 -0
- package/dist/plugin/CausalChain.d.ts +11 -0
- package/dist/plugin/CausalChain.js +109 -0
- package/dist/plugin/CausalChain.js.map +1 -0
- package/dist/plugin/ContextInjector.d.ts +12 -0
- package/dist/plugin/ContextInjector.js +31 -0
- package/dist/plugin/ContextInjector.js.map +1 -1
- package/dist/plugin/MemoryService.d.ts +62 -2
- package/dist/plugin/MemoryService.js +260 -15
- package/dist/plugin/MemoryService.js.map +1 -1
- package/dist/plugin/Migrate.js +12 -0
- package/dist/plugin/Migrate.js.map +1 -1
- package/dist/plugin/Reflector.d.ts +16 -0
- package/dist/plugin/Reflector.js +58 -19
- package/dist/plugin/Reflector.js.map +1 -1
- package/dist/plugin/ToolCallObserver.js +1 -1
- package/dist/plugin/ToolCallObserver.js.map +1 -1
- package/dist/plugin/index.js +130 -10
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/kevin_why.d.ts +19 -0
- package/dist/plugin/kevin_why.js +92 -0
- package/dist/plugin/kevin_why.js.map +1 -0
- package/dist/plugin/metrics.d.ts +1 -1
- package/dist/plugin/metrics.js +3 -0
- package/dist/plugin/metrics.js.map +1 -1
- package/dist/plugin/okf-export.d.ts +3 -0
- package/dist/plugin/okf-export.js +86 -0
- package/dist/plugin/okf-export.js.map +1 -0
- package/dist/plugin/okf-import.d.ts +73 -0
- package/dist/plugin/okf-import.js +239 -0
- package/dist/plugin/okf-import.js.map +1 -0
- package/migrations/004_v03_knowledge.sql +138 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ Kevin is an [OpenCode](https://opencode.ai) plugin that **observes** every agent
|
|
|
6
6
|
|
|
7
7
|
- **Local-first**: SQLite + FTS5, no external services, no network calls.
|
|
8
8
|
- **Global memory**: a single `~/.opencode-kevin/kevin.db` shared across all your projects (WAL mode → safe for concurrent sessions). No per-project folders.
|
|
9
|
+
- **Knowledge + Causality (v0.3.0)**: causal failure→fix chains, `kevin_why` explanations, OKF export/import, a supersede model, and human-in-the-loop AGENTS.md suggestions.
|
|
9
10
|
- **Standalone**: works without any other plugin. With the ecosystem, it learns more richly.
|
|
10
11
|
|
|
11
12
|
---
|
|
@@ -30,7 +31,7 @@ For a **single project**, put the same `plugin` array in `./opencode.json` or `.
|
|
|
30
31
|
|
|
31
32
|
### 2. Restart OpenCode
|
|
32
33
|
|
|
33
|
-
Config is loaded once at startup and is **not hot-reloaded** — quit and reopen OpenCode after editing. On start, OpenCode resolves the npm spec, caches the plugin in `~/.cache/opencode/packages/@jmtrin/opencode-kevin/`, and exposes
|
|
34
|
+
Config is loaded once at startup and is **not hot-reloaded** — quit and reopen OpenCode after editing. On start, OpenCode resolves the npm spec, caches the plugin in `~/.cache/opencode/packages/@jmtrin/opencode-kevin/`, and exposes nine tools: `kevin_save`, `kevin_query`, `kevin_get`, `kevin_recall`, `kevin_status`, `kevin_retrospective`, `kevin_why`, `kevin_export`, `kevin_import`.
|
|
34
35
|
|
|
35
36
|
### 3. Where data lives
|
|
36
37
|
|
|
@@ -89,32 +90,43 @@ Use `:memory:` for `dbPath` in tests.
|
|
|
89
90
|
▼
|
|
90
91
|
┌─────────────────┐
|
|
91
92
|
│ OBSERVE │ ToolCallObserver records every call
|
|
92
|
-
│ ToolCallObserver│ (tool, args redacted, success, duration, error_type
|
|
93
|
-
│ │
|
|
93
|
+
│ ToolCallObserver│ (tool, args redacted, success, duration, error_type,
|
|
94
|
+
│ │ id = callID) + stripPrivate + opt-in dedup (v0.2.0)
|
|
94
95
|
└────────┬────────┘
|
|
95
|
-
│ on failure
|
|
96
|
-
▼
|
|
97
|
-
┌─────────────────┐
|
|
98
|
-
│ LEARN │
|
|
99
|
-
│ Reflector │
|
|
100
|
-
│
|
|
101
|
-
│
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
│
|
|
107
|
-
│
|
|
108
|
-
│
|
|
96
|
+
│ on failure │ on success
|
|
97
|
+
▼ ▼
|
|
98
|
+
┌─────────────────┐ ┌─────────────────────────┐
|
|
99
|
+
│ LEARN │ │ CAUSAL CHAIN (v0.3.0) │
|
|
100
|
+
│ Reflector │ │ CausalChain.onSuccess │
|
|
101
|
+
│ heuristic lesson │ │ links the fix to the │
|
|
102
|
+
│ per-error-code │ │ failure within 10 tool │
|
|
103
|
+
│ rule table │ │ calls (error_fingerprint)
|
|
104
|
+
│ (v0.2.0 v2), │ └───────────┬─────────────┘
|
|
105
|
+
│ per-fingerprint │ │
|
|
106
|
+
│ throttle BEFORE │ │ session.idle
|
|
107
|
+
│ LLM enrich │ ▼
|
|
108
|
+
│ (opt-in v0.3.0),│ ┌─────────────────────────┐
|
|
109
|
+
│ stamps │ │ CausalChain.onSessionIdle│
|
|
110
|
+
│ error_fingerprint│ │ promotes recurring errors│
|
|
111
|
+
└────────┬────────┘ │ → causal patterns (idempotent,
|
|
112
|
+
│ │ cumulative evidence) │
|
|
113
|
+
▼ └───────────┬───────────────┘
|
|
114
|
+
┌─────────────────┐ │
|
|
115
|
+
│ SHARE │◄──────────────┘
|
|
116
|
+
│ ContextInjector │ injects relevant lessons pre-prompt
|
|
117
|
+
│ │ (1500 tokens) + on compacting (2000)
|
|
118
|
+
│ │ + <protect> + id: line (v0.2.0)
|
|
109
119
|
│ │ + origin-aware rank (v0.2.0)
|
|
110
|
-
│ │ +
|
|
120
|
+
│ │ + <kevin-suggestion> after negative
|
|
121
|
+
│ │ feedback half (HITL, v0.3.0)
|
|
111
122
|
└────────┬────────┘
|
|
112
123
|
│ session.idle
|
|
113
124
|
▼
|
|
114
125
|
┌─────────────────┐
|
|
115
|
-
│ RETROSPECTIVE │
|
|
116
|
-
│ │ with origin labels,
|
|
126
|
+
│ RETROSPECTIVE │ generates ~/.opencode-kevin/retrospectives/<session>.md
|
|
127
|
+
│ │ with origin labels, FP recap, metrics snapshot (v0.2.0)
|
|
117
128
|
│ │ + boostPositiveReflectors (v0.2.0)
|
|
129
|
+
│ │ + penalizeRecurringReflectors (v0.3.0)
|
|
118
130
|
│ │ + PatternMiner.mine (opt-in, v0.2.0)
|
|
119
131
|
└─────────────────┘
|
|
120
132
|
```
|
|
@@ -123,7 +135,7 @@ Use `:memory:` for `dbPath` in tests.
|
|
|
123
135
|
|
|
124
136
|
## Tools
|
|
125
137
|
|
|
126
|
-
Kevin exposes
|
|
138
|
+
Kevin exposes 9 tools callable by the agent:
|
|
127
139
|
|
|
128
140
|
### `kevin_save`
|
|
129
141
|
|
|
@@ -134,11 +146,13 @@ kevin_save({ type: "decision", content: "We use vitest for tests", scope: "proje
|
|
|
134
146
|
// → { "id": "0195a3b2-..." }
|
|
135
147
|
```
|
|
136
148
|
|
|
137
|
-
`type`: `error` | `pattern` | `decision` | `context
|
|
149
|
+
`type`: `error` | `pattern` | `decision` | `context` | `rule` | `solution` (v0.3.0). `scope`: `project` (persists) | `session` (TTL 24h).
|
|
150
|
+
|
|
151
|
+
Saving a `decision` or `rule` with the same `fingerprint` as an existing active row supersedes the old one (v0.3.0 — `status='superseded'`, hidden from default queries).
|
|
138
152
|
|
|
139
153
|
### `kevin_query`
|
|
140
154
|
|
|
141
|
-
Searches memories by text (FTS5 + bm25). Returns a **slim** payload by default (v0.2.0). Pass `full: true` for the v0.1.x full content body.
|
|
155
|
+
Searches memories by text (FTS5 + bm25). Returns a **slim** payload by default (v0.2.0). Pass `full: true` for the v0.1.x full content body, or `evidence: true` (v0.3.0) to include `confidence`, `evidence_count`, `last_verified_at` in the slim payload.
|
|
142
156
|
|
|
143
157
|
```
|
|
144
158
|
kevin_query({ query: "typecheck", type: "error", limit: 5 })
|
|
@@ -156,12 +170,13 @@ Fetches a **single full memory** by id (v0.2.0 — progressive disclosure). Use
|
|
|
156
170
|
kevin_get({ id: "0195a3b2-..." })
|
|
157
171
|
// → { "id": "...", "type": "error", "content": "...", "scope": "project",
|
|
158
172
|
// "relevanceScore": 0.55, "origin": "reflector", "fingerprint": "cbf29ce484222325",
|
|
159
|
-
// "projectId": null, "metadata": null
|
|
173
|
+
// "projectId": null, "metadata": null,
|
|
174
|
+
// "evidenceCount": 0, "lastVerifiedAt": null, "status": "active" }
|
|
160
175
|
```
|
|
161
176
|
|
|
162
177
|
### `kevin_recall`
|
|
163
178
|
|
|
164
|
-
Retrieves relevant memories (greedy fill by relevance). Without `query`, returns all memories in scope.
|
|
179
|
+
Retrieves relevant memories (greedy fill by relevance). Without `query`, returns all memories in scope. Pass `includeSuperseded: true` to include superseded rows (v0.3.0).
|
|
165
180
|
|
|
166
181
|
```
|
|
167
182
|
kevin_recall({ query: "auth", limit: 3 })
|
|
@@ -170,15 +185,16 @@ kevin_recall({ query: "auth", limit: 3 })
|
|
|
170
185
|
|
|
171
186
|
### `kevin_status`
|
|
172
187
|
|
|
173
|
-
Global counts and metrics
|
|
188
|
+
Global counts and metrics. v0.2.0 adds `memories_reflector`, `memories_agent`, `memories_pattern` and a `metrics` object; v0.3.0 adds `memories_causal` and 3 more seeded counters (`patterns_causal`, `causal_links`, `memories_superseded`).
|
|
174
189
|
|
|
175
190
|
```
|
|
176
191
|
kevin_status({})
|
|
177
192
|
// → { "memories": 42, "memories_reflector": 12, "memories_agent": 30, "memories_pattern": 0,
|
|
178
|
-
// "tool_calls": 318, "retrospectives": 7,
|
|
193
|
+
// "memories_causal": 1, "tool_calls": 318, "retrospectives": 7,
|
|
179
194
|
// "metrics": { "tokens_injected_pre_prompt": 51, "tokens_injected_compacting": 0,
|
|
180
195
|
// "reflections_throttled": 3, "duplicate_suppressions": 2,
|
|
181
|
-
// "tool_calls_deduped": 0, "patterns_mined": 0
|
|
196
|
+
// "tool_calls_deduped": 0, "patterns_mined": 0,
|
|
197
|
+
// "patterns_causal": 1, "causal_links": 2, "memories_superseded": 0 } }
|
|
182
198
|
```
|
|
183
199
|
|
|
184
200
|
### `kevin_retrospective`
|
|
@@ -191,6 +207,26 @@ kevin_retrospective({ session_id: "sess-abc" })
|
|
|
191
207
|
// or → { "message": "No failures in session sess-abc." }
|
|
192
208
|
```
|
|
193
209
|
|
|
210
|
+
### `kevin_why` (v0.3.0)
|
|
211
|
+
|
|
212
|
+
Explains *why* a failure keeps happening: looks up causal patterns for the query and builds a failure → fix trace from memories + tool_calls, including related TypeScript error-code rules.
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
kevin_why({ query: "TS2304 cannot find name" })
|
|
216
|
+
// → { "summary": "TS2304 recurs because ... Confirmed by 2 fixes.",
|
|
217
|
+
// "confidence": 0.7, "evidence_count": 2, "last_verified": "2026-08-01 10:00:00",
|
|
218
|
+
// "trace": [ { "type": "error", "summary": "..." }, { "type": "fix", "tool": "bash" } ],
|
|
219
|
+
// "related_rules": [ { "code": "TS2304", "suggestion": "import or typo" } ] }
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### `kevin_export` (v0.3.0)
|
|
223
|
+
|
|
224
|
+
Exports knowledge for sharing: `decision`/`rule`/`pattern` memories (active only, no raw errors) as YAML-frontmatter blocks (`format: "okf"`) or markdown (`format: "markdown"`). Includes `id`, `type`, `confidence`, `evidence_count`, `last_verified_at`, `fingerprint`.
|
|
225
|
+
|
|
226
|
+
### `kevin_import` (v0.3.0)
|
|
227
|
+
|
|
228
|
+
Ingests an exported bundle. Each entry becomes a `context` memory with `origin='imported'`; a fingerprint collision with an existing `decision`/`rule` supersedes the old row. Returns `{ imported, superseded }`.
|
|
229
|
+
|
|
194
230
|
---
|
|
195
231
|
|
|
196
232
|
## Hooks
|
|
@@ -200,11 +236,11 @@ Kevin subscribes to 6 OpenCode hooks:
|
|
|
200
236
|
| Hook | What Kevin does |
|
|
201
237
|
|---|---|
|
|
202
238
|
| `tool.execute.before` | Records tool call start (callID + redacted args) |
|
|
203
|
-
| `tool.execute.after` | Records result; on failure → Reflector.invoke async (throttled) |
|
|
204
|
-
| `experimental.chat.system.transform` | Injects relevant lessons in `<kevin-context>` (1500 tokens) |
|
|
205
|
-
| `experimental.session.compacting` | Re-injects lessons in `<kevin-memory>` after compacting (2000 tokens) |
|
|
239
|
+
| `tool.execute.after` | Records result (id = callID); on failure → Reflector.invoke async (throttled); on success → CausalChain.onSuccess links the fix (v0.3.0) |
|
|
240
|
+
| `experimental.chat.system.transform` | Injects relevant lessons in `<kevin-context>` (1500 tokens) + optional `<kevin-suggestion>` (v0.3.0) |
|
|
241
|
+
| `experimental.session.compacting` | Re-injects lessons in `<kevin-memory>` after compacting (2000 tokens) + optional `<kevin-suggestion>` |
|
|
206
242
|
| `event` (`session.created`) | Captures current `sessionID` |
|
|
207
|
-
| `event` (`session.idle`) | Generates retrospective.md
|
|
243
|
+
| `event` (`session.idle`) | Generates retrospective.md; boosts positive lessons (v0.2.0); penalizes recurring failures (v0.3.0); promotes causal patterns + mines patterns (opt-in); flushes metrics |
|
|
208
244
|
|
|
209
245
|
**Redaction**: absolute paths (`C:\Users\...`, `/home/...`) → `<path>` and secrets (`API_KEY=`, `Bearer`, `token`) → `<redacted>` before persisting anything. v0.2.0 adds `<private>…</private>` block redaction: sweeps tool call args and output before persistence, replaces with `<private: redacted N chars>`.
|
|
210
246
|
|
|
@@ -260,14 +296,18 @@ plugin/
|
|
|
260
296
|
index.ts # Entry point: KevinPlugin
|
|
261
297
|
Store.ts # Wrapper SQLite (node:sqlite / bun:sqlite / better-sqlite3 fallback)
|
|
262
298
|
Migrate.ts # Idempotent migrations + post-apply hooks
|
|
263
|
-
MemoryService.ts # save/query/getRelevant (FTS5 + bm25 + origin-aware rank)
|
|
299
|
+
MemoryService.ts # save/query/getRelevant (FTS5 + bm25 + origin-aware rank + supersede)
|
|
264
300
|
ToolCallObserver.ts # onBefore/onAfter + redact + inferErrorType + dedup (opt-in)
|
|
265
|
-
Reflector.ts # Heuristic lessons + per-fingerprint throttle + lesson v2
|
|
266
|
-
|
|
301
|
+
Reflector.ts # Heuristic lessons + per-fingerprint throttle + lesson v2 + LLM enrich
|
|
302
|
+
CausalChain.ts # v0.3.0 — links fixes to failures + promotes causal patterns
|
|
303
|
+
ContextInjector.ts # deriveQuery + pre-prompt/compacting injection + <kevin-suggestion>
|
|
267
304
|
Retrospective.ts # Generates retrospective.md + FP recap + metrics snapshot
|
|
268
305
|
fingerprint.ts # FNV-1a 64-bit (in-house, no node:crypto)
|
|
269
306
|
metrics.ts # In-memory counters + debounced flush to kevin_metrics
|
|
270
307
|
PatternMiner.ts # Opt-in deterministic 2-gram/3-gram miner
|
|
308
|
+
kevin_why.ts # v0.3.0 — kevin_why tool: failure→fix traces + related rules
|
|
309
|
+
okf-export.ts # v0.3.0 — kevin_export: OKF/markdown export
|
|
310
|
+
okf-import.ts # v0.3.0 — kevin_import: bundle parser + import
|
|
271
311
|
memory-format.ts # escapeInjectedText, formatMemories, <protect> + id: line wrappers
|
|
272
312
|
redact.ts # redactPaths + stripPrivate
|
|
273
313
|
uuid.ts # UUIDv7
|
|
@@ -275,6 +315,7 @@ migrations/
|
|
|
275
315
|
001_initial.sql # schema: memories, tool_calls, retrospectives
|
|
276
316
|
002_indexes.sql # FTS5 + indexes
|
|
277
317
|
003_v02_signal.sql # v0.2.0 Signal Quality: fingerprint, origin, metrics, dedup indexes
|
|
318
|
+
004_v03_knowledge.sql # v0.3.0 Knowledge + Causality: evidence/status/supersede, error_fingerprint
|
|
278
319
|
tests/{unit,integration,e2e}/
|
|
279
320
|
scripts/
|
|
280
321
|
copy-migrations.mjs # build step: copies *.sql to dist/migrations
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- Kevin 0.3.0 — Migration 004: Knowledge + Causality (additive)
|
|
3
|
+
-- ============================================================
|
|
4
|
+
-- Backward-compatible, additive only. All new columns are
|
|
5
|
+
-- nullable or carry a NOT NULL DEFAULT so legacy rows keep
|
|
6
|
+
-- working without a destructive rebuild.
|
|
7
|
+
-- ============================================================
|
|
8
|
+
|
|
9
|
+
-- 1. memories: evidence + lifecycle columns.
|
|
10
|
+
-- evidence_count — how many times this fingerprint was confirmed as fixed.
|
|
11
|
+
-- last_verified_at — timestamp of the most recent causal confirmation.
|
|
12
|
+
-- status — lifecycle state; default 'active'. Superseded rows are hidden.
|
|
13
|
+
ALTER TABLE memories ADD COLUMN evidence_count INTEGER NOT NULL DEFAULT 0;
|
|
14
|
+
ALTER TABLE memories ADD COLUMN last_verified_at TEXT;
|
|
15
|
+
ALTER TABLE memories ADD COLUMN status TEXT NOT NULL DEFAULT 'active'
|
|
16
|
+
CHECK (status IN ('active', 'superseded', 'stale', 'archived'));
|
|
17
|
+
|
|
18
|
+
-- 2. tool_calls: causal link + feedback-loop link columns.
|
|
19
|
+
-- fix_for_fingerprint — set when this successful call resolved a prior failure
|
|
20
|
+
-- with the given fingerprint. NULL for tool calls that were not fixes.
|
|
21
|
+
-- error_fingerprint — set by Reflector (via onLinkError callback) when a call
|
|
22
|
+
-- FAILS, to the stderr-based fingerprint the matching error memory uses.
|
|
23
|
+
-- This fixes the v0.2.0/v0.3.0 feedback-loop fingerprint mismatch bug:
|
|
24
|
+
-- tool_calls.fingerprint is hashed from "tool|args|success" by ToolCallObserver,
|
|
25
|
+
-- while memories.fingerprint is hashed from stderr text by Reflector — they
|
|
26
|
+
-- never agreed, so boost/penalize queries silently mismatched. The new column
|
|
27
|
+
-- stores the SAME identity dimension the error memory uses.
|
|
28
|
+
ALTER TABLE tool_calls ADD COLUMN fix_for_fingerprint TEXT;
|
|
29
|
+
ALTER TABLE tool_calls ADD COLUMN error_fingerprint TEXT;
|
|
30
|
+
|
|
31
|
+
-- 3. Index: causal linkage by fingerprint. Used by CausalChain.onSuccess
|
|
32
|
+
-- and kevin_why to materialize traces.
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_tool_calls_fix_fp
|
|
34
|
+
ON tool_calls(fix_for_fingerprint)
|
|
35
|
+
WHERE fix_for_fingerprint IS NOT NULL;
|
|
36
|
+
|
|
37
|
+
-- 3b. Index: feedback-loop linkage by error_fingerprint. Used by
|
|
38
|
+
-- boostPositiveReflectors / penalizeRecurringReflectors to count
|
|
39
|
+
-- recurrences by the same identity dimension the error memory uses.
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_tool_calls_error_fp
|
|
41
|
+
ON tool_calls(error_fingerprint)
|
|
42
|
+
WHERE error_fingerprint IS NOT NULL;
|
|
43
|
+
|
|
44
|
+
-- 4. Index: memories by fingerprint for promotion + supersede queries.
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_memories_fp
|
|
46
|
+
ON memories(fingerprint)
|
|
47
|
+
WHERE fingerprint IS NOT NULL;
|
|
48
|
+
|
|
49
|
+
-- 5. kevin_metrics: seed new v0.3 counters.
|
|
50
|
+
INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
|
|
51
|
+
('patterns_causal', 0),
|
|
52
|
+
('causal_links', 0),
|
|
53
|
+
('memories_superseded', 0);
|
|
54
|
+
|
|
55
|
+
-- 6. kevin_settings: seed new opt-in flags.
|
|
56
|
+
INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
|
|
57
|
+
('llm_reflection_enabled', '0'),
|
|
58
|
+
('cross_project_enabled', '0');
|
|
59
|
+
|
|
60
|
+
-- 7. Seed version 004.
|
|
61
|
+
INSERT OR IGNORE INTO schema_version (version) VALUES ('004');
|
|
62
|
+
|
|
63
|
+
-- ============================================================
|
|
64
|
+
-- 8. Rebuild memories table with expanded CHECK constraints.
|
|
65
|
+
-- v0.3.0 introduces new types (rule, solution) and origins
|
|
66
|
+
-- (causal, imported). SQLite cannot ALTER a CHECK constraint,
|
|
67
|
+
-- so we rebuild via a temporary table. FTS5 external-content
|
|
68
|
+
-- table references the content table by name, so we drop+recreate
|
|
69
|
+
-- the FTS5 triggers after the rebuild.
|
|
70
|
+
-- ============================================================
|
|
71
|
+
|
|
72
|
+
-- Step 1: Create new table with correct constraints
|
|
73
|
+
CREATE TABLE memories_v04 (
|
|
74
|
+
id TEXT PRIMARY KEY,
|
|
75
|
+
type TEXT NOT NULL CHECK(type IN ('error','pattern','decision','context','rule','solution')),
|
|
76
|
+
content TEXT NOT NULL,
|
|
77
|
+
scope TEXT NOT NULL DEFAULT 'project' CHECK(scope IN ('project','session')),
|
|
78
|
+
relevance_score REAL DEFAULT 0.5,
|
|
79
|
+
source_tool TEXT,
|
|
80
|
+
source_session TEXT,
|
|
81
|
+
metadata TEXT,
|
|
82
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
83
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
84
|
+
expires_at TEXT,
|
|
85
|
+
project_id TEXT,
|
|
86
|
+
fingerprint TEXT,
|
|
87
|
+
origin TEXT NOT NULL DEFAULT 'agent'
|
|
88
|
+
CHECK(origin IN ('reflector','agent','pattern','retrospective','causal','imported')),
|
|
89
|
+
evidence_count INTEGER NOT NULL DEFAULT 0,
|
|
90
|
+
last_verified_at TEXT,
|
|
91
|
+
status TEXT NOT NULL DEFAULT 'active'
|
|
92
|
+
CHECK(status IN ('active','superseded','stale','archived'))
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
-- Step 2: Copy data preserving rowids (FTS5 content sync relies on rowid)
|
|
96
|
+
INSERT INTO memories_v04
|
|
97
|
+
(rowid, id, type, content, scope, relevance_score, source_tool,
|
|
98
|
+
source_session, metadata, created_at, updated_at, expires_at,
|
|
99
|
+
project_id, fingerprint, origin, evidence_count, last_verified_at, status)
|
|
100
|
+
SELECT rowid, id, type, content, scope, relevance_score, source_tool,
|
|
101
|
+
source_session, metadata, created_at, updated_at, expires_at,
|
|
102
|
+
project_id, fingerprint, origin, evidence_count, last_verified_at, status
|
|
103
|
+
FROM memories;
|
|
104
|
+
|
|
105
|
+
-- Step 3: Drop old table and FTS triggers
|
|
106
|
+
DROP TRIGGER IF EXISTS memories_ai;
|
|
107
|
+
DROP TRIGGER IF EXISTS memories_ad;
|
|
108
|
+
DROP TRIGGER IF EXISTS memories_au;
|
|
109
|
+
DROP TABLE memories;
|
|
110
|
+
|
|
111
|
+
-- Step 4: Rename new table
|
|
112
|
+
ALTER TABLE memories_v04 RENAME TO memories;
|
|
113
|
+
|
|
114
|
+
-- Step 5: Recreate FTS triggers (memories_fts is content=external, survives DROP of content table)
|
|
115
|
+
CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
|
|
116
|
+
INSERT INTO memories_fts(rowid, content) VALUES (new.rowid, new.content);
|
|
117
|
+
END;
|
|
118
|
+
|
|
119
|
+
CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
|
|
120
|
+
INSERT INTO memories_fts(memories_fts, rowid, content) VALUES ('delete', old.rowid, old.content);
|
|
121
|
+
END;
|
|
122
|
+
|
|
123
|
+
CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
|
|
124
|
+
INSERT INTO memories_fts(memories_fts, rowid, content) VALUES ('delete', old.rowid, old.content);
|
|
125
|
+
INSERT INTO memories_fts(rowid, content) VALUES (new.rowid, new.content);
|
|
126
|
+
END;
|
|
127
|
+
|
|
128
|
+
-- Step 6: Recreate indexes
|
|
129
|
+
CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
|
|
130
|
+
CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(scope);
|
|
131
|
+
CREATE INDEX IF NOT EXISTS idx_memories_relevance ON memories(relevance_score DESC);
|
|
132
|
+
CREATE INDEX IF NOT EXISTS idx_memories_created ON memories(created_at);
|
|
133
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_memories_error_fp
|
|
134
|
+
ON memories(project_id, fingerprint)
|
|
135
|
+
WHERE type = 'error' AND fingerprint IS NOT NULL AND origin = 'reflector';
|
|
136
|
+
CREATE INDEX IF NOT EXISTS idx_memories_fp
|
|
137
|
+
ON memories(fingerprint)
|
|
138
|
+
WHERE fingerprint IS NOT NULL;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MemoryService } from "./MemoryService.js";
|
|
2
|
+
import type { Store } from "./Store.js";
|
|
3
|
+
import type { Metrics } from "./metrics.js";
|
|
4
|
+
export declare class CausalChain {
|
|
5
|
+
private store;
|
|
6
|
+
private memoryService;
|
|
7
|
+
private metrics;
|
|
8
|
+
constructor(store: Store, memoryService: MemoryService, metrics: Metrics | null);
|
|
9
|
+
onSuccess(_tool: string, _args: Record<string, unknown>, _projectId: string | null, sessionId: string): void;
|
|
10
|
+
onSessionIdle(sessionId: string): number;
|
|
11
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/** K3-007 — a success only links to a failure within this many calls. */
|
|
2
|
+
const MAX_LINK_DISTANCE = 10;
|
|
3
|
+
export class CausalChain {
|
|
4
|
+
store;
|
|
5
|
+
memoryService;
|
|
6
|
+
metrics;
|
|
7
|
+
constructor(store, memoryService, metrics) {
|
|
8
|
+
this.store = store;
|
|
9
|
+
this.memoryService = memoryService;
|
|
10
|
+
this.metrics = metrics;
|
|
11
|
+
}
|
|
12
|
+
// K3-007: link a success to the failing fingerprint only when it
|
|
13
|
+
// occurred within MAX_LINK_DISTANCE tool calls of the failure (plan
|
|
14
|
+
// §K3-007 acceptance: "within 24h and within 10 tool calls").
|
|
15
|
+
// v0.3.0 fix (bug #3) — the old code linked the most recent session
|
|
16
|
+
// success to the most recent unlinked error memory regardless of
|
|
17
|
+
// distance, so an unrelated success (e.g. an `ls` run after a
|
|
18
|
+
// typecheck failure) was stamped as the fix for that error.
|
|
19
|
+
onSuccess(_tool, _args, _projectId, sessionId) {
|
|
20
|
+
const successRow = this.store
|
|
21
|
+
.prepare(`SELECT rowid FROM tool_calls
|
|
22
|
+
WHERE session_id = ? AND success = 1
|
|
23
|
+
ORDER BY rowid DESC LIMIT 1`)
|
|
24
|
+
.get(sessionId);
|
|
25
|
+
if (!successRow)
|
|
26
|
+
return;
|
|
27
|
+
const linkedFps = new Set(this.store
|
|
28
|
+
.prepare(`SELECT DISTINCT fix_for_fingerprint FROM tool_calls
|
|
29
|
+
WHERE session_id = ? AND fix_for_fingerprint IS NOT NULL`)
|
|
30
|
+
.all(sessionId).map((r) => r.fix_for_fingerprint));
|
|
31
|
+
// Most recent failing calls in this session, newest first. The
|
|
32
|
+
// failing call's `error_fingerprint` (stamped by Reflector via
|
|
33
|
+
// onLinkError) is the SAME identity dimension the error memory
|
|
34
|
+
// uses; `fingerprint` is the legacy tool|args|success hash and
|
|
35
|
+
// simply never matches a reflector error memory.
|
|
36
|
+
const failRows = this.store
|
|
37
|
+
.prepare(`SELECT rowid, COALESCE(error_fingerprint, fingerprint) AS fp
|
|
38
|
+
FROM tool_calls
|
|
39
|
+
WHERE session_id = ?
|
|
40
|
+
AND success = 0
|
|
41
|
+
AND (error_fingerprint IS NOT NULL OR fingerprint IS NOT NULL)
|
|
42
|
+
ORDER BY rowid DESC LIMIT ?`)
|
|
43
|
+
.all(sessionId, MAX_LINK_DISTANCE);
|
|
44
|
+
for (const fail of failRows) {
|
|
45
|
+
if (!fail.fp || linkedFps.has(fail.fp))
|
|
46
|
+
continue;
|
|
47
|
+
const dist = successRow.rowid - fail.rowid;
|
|
48
|
+
if (dist <= 0 || dist > MAX_LINK_DISTANCE)
|
|
49
|
+
continue;
|
|
50
|
+
const mem = this.store
|
|
51
|
+
.prepare(`SELECT 1 FROM memories
|
|
52
|
+
WHERE fingerprint = ? AND type = 'error'
|
|
53
|
+
AND origin = 'reflector' AND status = 'active'
|
|
54
|
+
AND created_at > datetime('now', '-24 hours')
|
|
55
|
+
LIMIT 1`)
|
|
56
|
+
.get(fail.fp);
|
|
57
|
+
if (!mem)
|
|
58
|
+
continue;
|
|
59
|
+
this.store
|
|
60
|
+
.prepare("UPDATE tool_calls SET fix_for_fingerprint = ? WHERE rowid = ?")
|
|
61
|
+
.run(fail.fp, successRow.rowid);
|
|
62
|
+
this.metrics?.incr("causal_links", 1);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
onSessionIdle(sessionId) {
|
|
67
|
+
const linkedErrors = this.store
|
|
68
|
+
.prepare(`SELECT m.id, m.fingerprint,
|
|
69
|
+
(SELECT COUNT(*)
|
|
70
|
+
FROM tool_calls tc_all
|
|
71
|
+
WHERE tc_all.fix_for_fingerprint = m.fingerprint) as evidence_count
|
|
72
|
+
FROM memories m
|
|
73
|
+
WHERE m.type = 'error'
|
|
74
|
+
AND m.origin = 'reflector'
|
|
75
|
+
AND m.fingerprint IN (
|
|
76
|
+
SELECT DISTINCT fix_for_fingerprint
|
|
77
|
+
FROM tool_calls
|
|
78
|
+
WHERE session_id = ? AND fix_for_fingerprint IS NOT NULL
|
|
79
|
+
)
|
|
80
|
+
AND m.status = 'active'
|
|
81
|
+
GROUP BY m.fingerprint
|
|
82
|
+
HAVING (
|
|
83
|
+
SELECT MAX(tc.rowid) FROM tool_calls tc
|
|
84
|
+
WHERE tc.fix_for_fingerprint = m.fingerprint
|
|
85
|
+
) > COALESCE(
|
|
86
|
+
(SELECT MAX(m2.rowid) FROM memories m2
|
|
87
|
+
WHERE m2.fingerprint = m.fingerprint
|
|
88
|
+
AND m2.type = 'pattern'
|
|
89
|
+
AND m2.origin = 'causal'),
|
|
90
|
+
0
|
|
91
|
+
)`)
|
|
92
|
+
.all(sessionId);
|
|
93
|
+
let promoted = 0;
|
|
94
|
+
for (const err of linkedErrors) {
|
|
95
|
+
try {
|
|
96
|
+
const id = this.memoryService.promoteToPattern(err.id, err.evidence_count);
|
|
97
|
+
if (id) {
|
|
98
|
+
promoted++;
|
|
99
|
+
this.metrics?.incr("patterns_causal", 1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// promoteToPattern may fail if the error memory was removed
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return promoted;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=CausalChain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CausalChain.js","sourceRoot":"","sources":["../../plugin/CausalChain.ts"],"names":[],"mappings":"AAIA,yEAAyE;AACzE,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,MAAM,OAAO,WAAW;IAEd;IACA;IACA;IAHT,YACS,KAAY,EACZ,aAA4B,EAC5B,OAAuB;QAFvB,UAAK,GAAL,KAAK,CAAO;QACZ,kBAAa,GAAb,aAAa,CAAe;QAC5B,YAAO,GAAP,OAAO,CAAgB;IAC7B,CAAC;IAEJ,iEAAiE;IACjE,oEAAoE;IACpE,8DAA8D;IAC9D,oEAAoE;IACpE,iEAAiE;IACjE,8DAA8D;IAC9D,4DAA4D;IAC5D,SAAS,CACR,KAAa,EACb,KAA8B,EAC9B,UAAyB,EACzB,SAAiB;QAEjB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK;aAC3B,OAAO,CACP;;iCAE6B,CAC7B;aACA,GAAG,CAAC,SAAS,CAAkC,CAAC;QAClD,IAAI,CAAC,UAAU;YAAE,OAAO;QAExB,MAAM,SAAS,GAAG,IAAI,GAAG,CAEvB,IAAI,CAAC,KAAK;aACR,OAAO,CACP;gEAC0D,CAC1D;aACA,GAAG,CAAC,SAAS,CACf,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,CACnC,CAAC;QAEF,+DAA+D;QAC/D,+DAA+D;QAC/D,+DAA+D;QAC/D,+DAA+D;QAC/D,iDAAiD;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK;aACzB,OAAO,CACP;;;;;iCAK6B,CAC7B;aACA,GAAG,CAAC,SAAS,EAAE,iBAAiB,CAG/B,CAAC;QAEJ,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAE,SAAS;YACjD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAC3C,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,iBAAiB;gBAAE,SAAS;YAEpD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK;iBACpB,OAAO,CACP;;;;cAIS,CACT;iBACA,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,IAAI,CAAC,GAAG;gBAAE,SAAS;YAEnB,IAAI,CAAC,KAAK;iBACR,OAAO,CACP,+DAA+D,CAC/D;iBACA,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;YACtC,OAAO;QACR,CAAC;IACF,CAAC;IAED,aAAa,CAAC,SAAiB;QAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK;aAC7B,OAAO,CACP;;;;;;;;;;;;;;;;;;;;;;;OAuBG,CACH;aACA,GAAG,CAAC,SAAS,CAIZ,CAAC;QAEJ,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;YAChC,IAAI,CAAC;gBACJ,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAC7C,GAAG,CAAC,EAAE,EACN,GAAG,CAAC,cAAc,CAClB,CAAC;gBACF,IAAI,EAAE,EAAE,CAAC;oBACR,QAAQ,EAAE,CAAC;oBACX,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC;gBAC1C,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,4DAA4D;YAC7D,CAAC;QACF,CAAC;QAED,OAAO,QAAQ,CAAC;IACjB,CAAC;CACD"}
|
|
@@ -21,7 +21,19 @@ export interface CompactingOutput {
|
|
|
21
21
|
export declare class ContextInjector {
|
|
22
22
|
private memoryService;
|
|
23
23
|
private metrics;
|
|
24
|
+
private lastRecurrenceCount;
|
|
24
25
|
constructor(memoryService: MemoryService, metrics?: Metrics | null);
|
|
26
|
+
/**
|
|
27
|
+
* v0.3.0 (K3-020) — notify the injector that the negative feedback half
|
|
28
|
+
* fired N times in the last session.idle. The next system.transform or
|
|
29
|
+
* compacting hook will prepend a HITL suggestion block.
|
|
30
|
+
*/
|
|
31
|
+
setRecurrences(count: number): void;
|
|
32
|
+
/**
|
|
33
|
+
* v0.3.0 (K3-020) — generate a HITL suggestion block when recurrences
|
|
34
|
+
* occurred. Returns the block or empty string. Resets the counter.
|
|
35
|
+
*/
|
|
36
|
+
generateSuggestion(): string;
|
|
25
37
|
/**
|
|
26
38
|
* v0.2.0 (K2-024): origin-aware ranking at injection time is delegated
|
|
27
39
|
* to `MemoryService.getRelevant()` (K2-023, D2-13). The injector does
|
|
@@ -71,10 +71,41 @@ function isWordChar(ch) {
|
|
|
71
71
|
export class ContextInjector {
|
|
72
72
|
memoryService;
|
|
73
73
|
metrics;
|
|
74
|
+
lastRecurrenceCount = 0;
|
|
74
75
|
constructor(memoryService, metrics = null) {
|
|
75
76
|
this.memoryService = memoryService;
|
|
76
77
|
this.metrics = metrics;
|
|
77
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* v0.3.0 (K3-020) — notify the injector that the negative feedback half
|
|
81
|
+
* fired N times in the last session.idle. The next system.transform or
|
|
82
|
+
* compacting hook will prepend a HITL suggestion block.
|
|
83
|
+
*/
|
|
84
|
+
setRecurrences(count) {
|
|
85
|
+
this.lastRecurrenceCount = count;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* v0.3.0 (K3-020) — generate a HITL suggestion block when recurrences
|
|
89
|
+
* occurred. Returns the block or empty string. Resets the counter.
|
|
90
|
+
*/
|
|
91
|
+
generateSuggestion() {
|
|
92
|
+
if (this.lastRecurrenceCount === 0)
|
|
93
|
+
return "";
|
|
94
|
+
const block = `<kevin-suggestion>
|
|
95
|
+
The same error pattern recurred ${this.lastRecurrenceCount} time(s) this session.
|
|
96
|
+
Consider adding a skill or convention to AGENTS.md to prevent this.
|
|
97
|
+
|
|
98
|
+
Draft:
|
|
99
|
+
\`\`\`markdown
|
|
100
|
+
## Recurring pattern
|
|
101
|
+
- ${this.lastRecurrenceCount} recurrence(s) this session
|
|
102
|
+
- Kevin's confidence in this pattern is low
|
|
103
|
+
- Consider documenting the fix in AGENTS.md
|
|
104
|
+
\`\`\`
|
|
105
|
+
</kevin-suggestion>`;
|
|
106
|
+
this.lastRecurrenceCount = 0;
|
|
107
|
+
return block;
|
|
108
|
+
}
|
|
78
109
|
/**
|
|
79
110
|
* v0.2.0 (K2-024): origin-aware ranking at injection time is delegated
|
|
80
111
|
* to `MemoryService.getRelevant()` (K2-023, D2-13). The injector does
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextInjector.js","sourceRoot":"","sources":["../../plugin/ContextInjector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAgB,cAAc,EAAE,MAAM,cAAc,CAAC;AAyB5D,MAAM,uBAAuB,GAAG,IAAI,CAAC;AACrC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,MAAM,UAAU,GAAG,IAAI,GAAG,CAAS;IAClC,GAAG;IACH,IAAI;IACJ,KAAK;IACL,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,KAAK;IACL,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,MAAM;IACN,IAAI;IACJ,KAAK;IACL,KAAK;IACL,KAAK;IACL,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IACL,IAAI;IACJ,MAAM;IACN,KAAK;IACL,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,KAAK;IACL,KAAK;IACL,MAAM;IACN,GAAG;IACH,KAAK;IACL,MAAM;IACN,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,KAAK;IACL,KAAK;CACL,CAAC,CAAC;AAEH,SAAS,UAAU,CAAC,EAAU;IAC7B,OAAO,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,OAAO,eAAe;
|
|
1
|
+
{"version":3,"file":"ContextInjector.js","sourceRoot":"","sources":["../../plugin/ContextInjector.ts"],"names":[],"mappings":"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"}
|