@jmtrin/opencode-kevin 0.9.0 → 1.0.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/README.md +558 -823
  2. package/dist/migrations/011_v10_proven.sql +69 -0
  3. package/dist/plugin/Archiver.js +0 -1
  4. package/dist/plugin/ArtifactWriter.d.ts +25 -10
  5. package/dist/plugin/ArtifactWriter.js +43 -25
  6. package/dist/plugin/CausalChain.js +0 -1
  7. package/dist/plugin/ConflictDetector.js +0 -1
  8. package/dist/plugin/ContextInjector.js +0 -1
  9. package/dist/plugin/ConventionMiner.js +0 -1
  10. package/dist/plugin/Curator.js +0 -1
  11. package/dist/plugin/Feedback.js +0 -1
  12. package/dist/plugin/HookLiveness.d.ts +6 -0
  13. package/dist/plugin/HookLiveness.js +15 -1
  14. package/dist/plugin/InjectionLedger.js +0 -1
  15. package/dist/plugin/LessonFixer.js +0 -1
  16. package/dist/plugin/Materializer.js +0 -1
  17. package/dist/plugin/MemoryService.js +0 -1
  18. package/dist/plugin/Migrate.d.ts +1 -1
  19. package/dist/plugin/Migrate.js +25 -3
  20. package/dist/plugin/PatternMiner.js +0 -1
  21. package/dist/plugin/QualityGate.js +0 -1
  22. package/dist/plugin/Reflector.js +0 -1
  23. package/dist/plugin/RepoIdentity.js +0 -1
  24. package/dist/plugin/RepoTruth.js +0 -1
  25. package/dist/plugin/Retrospective.js +7 -1
  26. package/dist/plugin/SharedLayer.js +0 -1
  27. package/dist/plugin/Store.js +0 -1
  28. package/dist/plugin/ToolCallObserver.js +0 -1
  29. package/dist/plugin/capabilities.js +0 -1
  30. package/dist/plugin/confidence.js +0 -1
  31. package/dist/plugin/contract.d.ts +46 -0
  32. package/dist/plugin/contract.js +368 -0
  33. package/dist/plugin/diff.js +0 -1
  34. package/dist/plugin/escape.d.ts +39 -0
  35. package/dist/plugin/escape.js +60 -0
  36. package/dist/plugin/fingerprint.js +0 -1
  37. package/dist/plugin/host.js +0 -1
  38. package/dist/plugin/index.d.ts +2 -2
  39. package/dist/plugin/index.js +425 -227
  40. package/dist/plugin/inferability.js +0 -1
  41. package/dist/plugin/kevin_approve.js +0 -1
  42. package/dist/plugin/kevin_audit.d.ts +31 -0
  43. package/dist/plugin/kevin_audit.js +60 -1
  44. package/dist/plugin/kevin_bench.d.ts +10 -0
  45. package/dist/plugin/kevin_bench.js +82 -0
  46. package/dist/plugin/kevin_conflicts.js +0 -1
  47. package/dist/plugin/kevin_contract.d.ts +8 -0
  48. package/dist/plugin/kevin_contract.js +59 -0
  49. package/dist/plugin/kevin_doctor.d.ts +5 -0
  50. package/dist/plugin/kevin_doctor.js +30 -2
  51. package/dist/plugin/kevin_facts.js +0 -1
  52. package/dist/plugin/kevin_native.js +0 -1
  53. package/dist/plugin/kevin_propose.js +0 -1
  54. package/dist/plugin/kevin_publish.js +0 -1
  55. package/dist/plugin/kevin_why.js +0 -1
  56. package/dist/plugin/memory-format.js +0 -1
  57. package/dist/plugin/metrics.js +0 -1
  58. package/dist/plugin/native.js +0 -1
  59. package/dist/plugin/okf-export.js +0 -1
  60. package/dist/plugin/okf-import.js +0 -1
  61. package/dist/plugin/okf.js +0 -1
  62. package/dist/plugin/perf.d.ts +35 -0
  63. package/dist/plugin/perf.js +174 -0
  64. package/dist/plugin/query-tokenizer.js +0 -1
  65. package/dist/plugin/redact.js +0 -1
  66. package/dist/plugin/replay-types.js +0 -1
  67. package/dist/plugin/replay.js +0 -1
  68. package/dist/plugin/sqlite-adapter.js +0 -1
  69. package/dist/plugin/uuid.js +0 -1
  70. package/package.json +21 -6
  71. package/dist/plugin/Archiver.js.map +0 -1
  72. package/dist/plugin/ArtifactWriter.js.map +0 -1
  73. package/dist/plugin/CausalChain.js.map +0 -1
  74. package/dist/plugin/ConflictDetector.js.map +0 -1
  75. package/dist/plugin/ContextInjector.js.map +0 -1
  76. package/dist/plugin/ConventionMiner.js.map +0 -1
  77. package/dist/plugin/Curator.js.map +0 -1
  78. package/dist/plugin/Feedback.js.map +0 -1
  79. package/dist/plugin/HookLiveness.js.map +0 -1
  80. package/dist/plugin/InjectionLedger.js.map +0 -1
  81. package/dist/plugin/LessonFixer.js.map +0 -1
  82. package/dist/plugin/Materializer.js.map +0 -1
  83. package/dist/plugin/MemoryService.js.map +0 -1
  84. package/dist/plugin/Migrate.js.map +0 -1
  85. package/dist/plugin/PatternMiner.js.map +0 -1
  86. package/dist/plugin/QualityGate.js.map +0 -1
  87. package/dist/plugin/Reflector.js.map +0 -1
  88. package/dist/plugin/RepoIdentity.js.map +0 -1
  89. package/dist/plugin/RepoTruth.js.map +0 -1
  90. package/dist/plugin/Retrospective.js.map +0 -1
  91. package/dist/plugin/SharedLayer.js.map +0 -1
  92. package/dist/plugin/Store.js.map +0 -1
  93. package/dist/plugin/ToolCallObserver.js.map +0 -1
  94. package/dist/plugin/capabilities.js.map +0 -1
  95. package/dist/plugin/confidence.js.map +0 -1
  96. package/dist/plugin/diff.js.map +0 -1
  97. package/dist/plugin/fingerprint.js.map +0 -1
  98. package/dist/plugin/host.js.map +0 -1
  99. package/dist/plugin/index.js.map +0 -1
  100. package/dist/plugin/inferability.js.map +0 -1
  101. package/dist/plugin/kevin_approve.js.map +0 -1
  102. package/dist/plugin/kevin_audit.js.map +0 -1
  103. package/dist/plugin/kevin_conflicts.js.map +0 -1
  104. package/dist/plugin/kevin_doctor.js.map +0 -1
  105. package/dist/plugin/kevin_facts.js.map +0 -1
  106. package/dist/plugin/kevin_native.js.map +0 -1
  107. package/dist/plugin/kevin_propose.js.map +0 -1
  108. package/dist/plugin/kevin_publish.js.map +0 -1
  109. package/dist/plugin/kevin_why.js.map +0 -1
  110. package/dist/plugin/memory-format.js.map +0 -1
  111. package/dist/plugin/metrics.js.map +0 -1
  112. package/dist/plugin/native.js.map +0 -1
  113. package/dist/plugin/okf-export.js.map +0 -1
  114. package/dist/plugin/okf-import.js.map +0 -1
  115. package/dist/plugin/okf.js.map +0 -1
  116. package/dist/plugin/query-tokenizer.js.map +0 -1
  117. package/dist/plugin/redact.js.map +0 -1
  118. package/dist/plugin/replay-types.js.map +0 -1
  119. package/dist/plugin/replay.js.map +0 -1
  120. package/dist/plugin/sqlite-adapter.js.map +0 -1
  121. package/dist/plugin/uuid.js.map +0 -1
  122. package/migrations/001_initial.sql +0 -92
  123. package/migrations/002_indexes.sql +0 -14
  124. package/migrations/003_v02_signal.sql +0 -58
  125. package/migrations/004_v03_knowledge.sql +0 -138
  126. package/migrations/005_v04_signal.sql +0 -57
  127. package/migrations/006_v05_glassbox.sql +0 -118
  128. package/migrations/007_v06_pull.sql +0 -145
  129. package/migrations/008_v07_truth.sql +0 -124
  130. package/migrations/009_v08_team.sql +0 -100
  131. package/migrations/010_v09_native.sql +0 -78
@@ -0,0 +1,69 @@
1
+ -- =============================================================
2
+ -- Kevin v1.0.0 "Proven" — migration 011
3
+ -- Adds: perf sampling, benchmark results.
4
+ -- No ALTER TABLE (second release running); no column is retyped.
5
+ -- =============================================================
6
+
7
+ -- 1. Per-scope latency aggregates, flushed at session.idle.
8
+ -- Machine-scoped: no project_id, for the same reason hook_liveness
9
+ -- has none (v0.9.0 D9-08) — latency is a property of the install.
10
+ CREATE TABLE IF NOT EXISTS perf_samples (
11
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
12
+ scope TEXT NOT NULL,
13
+ sample_count INTEGER NOT NULL,
14
+ p50_ms REAL NOT NULL,
15
+ p95_ms REAL NOT NULL,
16
+ max_ms REAL NOT NULL,
17
+ budget_p95_ms REAL NOT NULL,
18
+ within_budget INTEGER NOT NULL DEFAULT 1,
19
+ recorded_at TEXT NOT NULL DEFAULT (datetime('now'))
20
+ );
21
+
22
+ CREATE INDEX IF NOT EXISTS idx_perf_samples_scope
23
+ ON perf_samples(scope, recorded_at DESC);
24
+ CREATE INDEX IF NOT EXISTS idx_perf_samples_breach
25
+ ON perf_samples(within_budget) WHERE within_budget = 0;
26
+
27
+ -- 2. Benchmark results. Append-only. One row per arm per run.
28
+ -- arm CHECK is closed by the plan; widening requires a table rebuild (intentional friction).
29
+ -- scope CHECK is intentionally absent: the scope union may gain members in a 1.x minor.
30
+ CREATE TABLE IF NOT EXISTS bench_runs (
31
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ corpus_digest TEXT NOT NULL,
33
+ contract_digest TEXT NOT NULL,
34
+ package_version TEXT NOT NULL,
35
+ runtime TEXT NOT NULL,
36
+ arm TEXT NOT NULL,
37
+ k INTEGER NOT NULL,
38
+ precision_at_k REAL NOT NULL,
39
+ recall_at_k REAL NOT NULL,
40
+ mrr REAL NOT NULL,
41
+ ran_at TEXT NOT NULL DEFAULT (datetime('now')),
42
+ CHECK (arm IN ('none', 'recent-k', 'random-k', 'kevin'))
43
+ );
44
+
45
+ CREATE INDEX IF NOT EXISTS idx_bench_runs_corpus
46
+ ON bench_runs(corpus_digest, ran_at DESC);
47
+ CREATE INDEX IF NOT EXISTS idx_bench_runs_arm
48
+ ON bench_runs(arm, ran_at DESC);
49
+
50
+ -- 3. Metric seeds (45 -> 51).
51
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('perf_samples_recorded', 0);
52
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('perf_budget_breaches', 0);
53
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('dispose_fires_total', 0);
54
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('dispose_misses_total', 0);
55
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('contract_digest_changes', 0);
56
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('bench_runs_total', 0);
57
+
58
+ -- 4. Setting seeds (27 -> 31). All TEXT; '0'/'1' are strings.
59
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('perf_enabled', '1');
60
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('perf_ring_capacity', '512');
61
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('perf_flush_on_idle', '1');
62
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('contract_report_enabled', '1');
63
+
64
+ -- 5. `dispose` becomes the seventh instrumented hook (v0.9.0 migration 010
65
+ -- seeded six rows; this is a data seed, not a schema change).
66
+ INSERT OR IGNORE INTO hook_liveness (hook) VALUES ('dispose');
67
+
68
+ -- 6. Schema version.
69
+ INSERT OR IGNORE INTO schema_version (version) VALUES ('011');
@@ -96,4 +96,3 @@ function formatSqliteUtc(d) {
96
96
  const pad = (n) => String(n).padStart(2, "0");
97
97
  return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
98
98
  }
99
- //# sourceMappingURL=Archiver.js.map
@@ -35,18 +35,33 @@ export interface WritePlan {
35
35
  readonly hashAfter: string;
36
36
  }
37
37
  /**
38
- * Rule 9 (K6-009 / plan §5.1, D6-02) — three layers applied to the body
39
- * before splicing:
40
- * (a) the escaping discipline of `plugin/memory-format.ts`;
41
- * (b) strip any line containing `kevin:begin` or `kevin:end`, in any
42
- * casing, anywhere in the line;
43
- * (c) strip HTML comment terminators (`-->`).
44
- * Without this, a memory containing a literal `<!-- kevin:end -->` line would
45
- * close the marker comment early and let subsequent content escape the
46
- * curated region on the next regeneration a marker-injection variant of the
47
- * v0.1.5 prompt-injection defect (plan §3.5).
38
+ * Rule 9, layer (a) — superseded in v1.0.0 by `escapeForMarkerBlock`
39
+ * in `plugin/escape.ts` (K10-027), which carries the same idempotent
40
+ * entity discipline.
41
+ */
42
+ /**
43
+ * v1.0.0 (K10-027 / plan §5.7) the boundary functions now live in
44
+ * `plugin/escape.ts`; this composer applies them in fence-then-marker
45
+ * order. Layer (b) strip any line containing `kevin:begin` or
46
+ * `kevin:end`, in any casing is a filter rather than an escape and
47
+ * stays here. Without layer (b), a memory containing a literal
48
+ * `<!-- kevin:end -->` line would close the marker comment early and
49
+ * let subsequent content escape the curated region on the next
50
+ * regeneration — a marker-injection variant of the v0.1.5
51
+ * prompt-injection defect (plan §3.5). The trailing `-->` strip is kept
52
+ * as defence in depth although `escapeForMarkerBlock` already escapes
53
+ * every `>` that could form one.
48
54
  */
49
55
  export declare function sanitizeArtifactBody(body: string): string;
56
+ /**
57
+ * v1.0.0 (K10-027 / plan §5.7, rule 2) — whole-file writes escape by
58
+ * container. Only `.okf` files are line-oriented JSON: each line gets
59
+ * `escapeForOkfLine`, which is the identity on a well-formed OKF line
60
+ * (canonical JSON never contains raw control characters) and therefore
61
+ * preserves the re-render `noop`. Kevin-owned markdown paths keep their
62
+ * bytes — fences there are legitimate content.
63
+ */
64
+ export declare function escapeForContainer(path: string, content: string): string;
50
65
  /**
51
66
  * v0.6.0 (K6-005 / plan §5.1) — the single write path to disk (D6-01).
52
67
  *
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { closeSync, fsyncSync, openSync, readFileSync, renameSync, unlinkSync, writeSync, } from "node:fs";
3
3
  import { unifiedDiff } from "./diff.js";
4
+ import { escapeForFence, escapeForMarkerBlock, escapeForOkfLine, } from "./escape.js";
4
5
  import { uuidv7 } from "./uuid.js";
5
6
  // v0.6.0 (K6-005 / plan §5.1, D6-02) — the frozen marker contract. The exact
6
7
  // byte sequences are load-bearing: README, v1.0.0 plan C-01 and the round-trip
@@ -30,39 +31,55 @@ function normalizeEol(body, eol) {
30
31
  return body.replace(/\r\n/g, "\n").replace(/\n/g, eol);
31
32
  }
32
33
  /**
33
- * Rule 9, layer (a) — the escaping discipline of `plugin/memory-format.ts`
34
- * (`&`, `<`, `>`) made idempotent: an ampersand that already heads a known
35
- * entity (`&amp;`, `&lt;`, `&gt;`, `&#NNN;`) is left alone, so sanitizing
36
- * sanitized output is a fixed point. The round-trip property of the marker
37
- * block depends on this: a non-idempotent escape would grow `&amp;lt;` on
38
- * every regeneration (K6-009 / plan §5.1 rule 9).
34
+ * Rule 9, layer (a) — superseded in v1.0.0 by `escapeForMarkerBlock`
35
+ * in `plugin/escape.ts` (K10-027), which carries the same idempotent
36
+ * entity discipline.
39
37
  */
40
- function escapeIdempotent(text) {
41
- return text
42
- .replace(/&(?!(amp|lt|gt|#\d+);)/g, "&amp;")
43
- .replace(/</g, "&lt;")
44
- .replace(/>/g, "&gt;");
45
- }
46
38
  /**
47
- * Rule 9 (K6-009 / plan §5.1, D6-02) — three layers applied to the body
48
- * before splicing:
49
- * (a) the escaping discipline of `plugin/memory-format.ts`;
50
- * (b) strip any line containing `kevin:begin` or `kevin:end`, in any
51
- * casing, anywhere in the line;
52
- * (c) strip HTML comment terminators (`-->`).
53
- * Without this, a memory containing a literal `<!-- kevin:end -->` line would
54
- * close the marker comment early and let subsequent content escape the
55
- * curated region on the next regeneration a marker-injection variant of the
56
- * v0.1.5 prompt-injection defect (plan §3.5).
39
+ * v1.0.0 (K10-027 / plan §5.7) — the boundary functions now live in
40
+ * `plugin/escape.ts`; this composer applies them in fence-then-marker
41
+ * order. Layer (b) strip any line containing `kevin:begin` or
42
+ * `kevin:end`, in any casing is a filter rather than an escape and
43
+ * stays here. Without layer (b), a memory containing a literal
44
+ * `<!-- kevin:end -->` line would close the marker comment early and
45
+ * let subsequent content escape the curated region on the next
46
+ * regeneration a marker-injection variant of the v0.1.5
47
+ * prompt-injection defect (plan §3.5). The trailing `-->` strip is kept
48
+ * as defence in depth although `escapeForMarkerBlock` already escapes
49
+ * every `>` that could form one.
57
50
  */
58
51
  export function sanitizeArtifactBody(body) {
59
- const escaped = escapeIdempotent(body);
52
+ const escaped = escapeForMarkerBlock(escapeForFence(body));
60
53
  const kept = escaped
61
54
  .split("\n")
62
55
  .filter((line) => !/kevin:begin|kevin:end/i.test(line))
63
56
  .join("\n");
64
57
  return kept.replace(/-->/g, "");
65
58
  }
59
+ /**
60
+ * v1.0.0 (K10-027 / plan §5.7, rule 2) — whole-file writes escape by
61
+ * container. Only `.okf` files are line-oriented JSON: each line gets
62
+ * `escapeForOkfLine`, which is the identity on a well-formed OKF line
63
+ * (canonical JSON never contains raw control characters) and therefore
64
+ * preserves the re-render `noop`. Kevin-owned markdown paths keep their
65
+ * bytes — fences there are legitimate content.
66
+ */
67
+ export function escapeForContainer(path, content) {
68
+ if (!path.endsWith(".okf"))
69
+ return content;
70
+ return content
71
+ .split("\n")
72
+ .map((line) => {
73
+ // A CRLF file (healHeader preserves the original EOL) leaves a
74
+ // trailing \r on each line after the split — it is line-ending
75
+ // bytes, not statement content, so it must survive untouched.
76
+ if (line.endsWith("\r")) {
77
+ return `${escapeForOkfLine(line.slice(0, -1))}\r`;
78
+ }
79
+ return escapeForOkfLine(line);
80
+ })
81
+ .join("\n");
82
+ }
66
83
  /**
67
84
  * v0.6.0 (K6-005 / plan §5.1) — the single write path to disk (D6-01).
68
85
  *
@@ -113,7 +130,9 @@ export class ArtifactWriter {
113
130
  // like any other refusal: after = before, both hashes recorded.
114
131
  const refusal = request.refusal;
115
132
  const refused = refusal !== undefined;
116
- const after = refused ? before : content;
133
+ // K10-027 the container boundary: OKF lines are escaped at the
134
+ // single write path, never by callers.
135
+ const after = refused ? before : escapeForContainer(path, content);
117
136
  return {
118
137
  path,
119
138
  before,
@@ -270,4 +289,3 @@ export class ArtifactWriter {
270
289
  .run(uuidv7(), proposalId ?? null, this.projectId, plan.path, Buffer.byteLength(plan.before, "utf8"), Buffer.byteLength(plan.after, "utf8"), plan.hashBefore, plan.hashAfter, plan.outcome, plan.reason ?? null);
271
290
  }
272
291
  }
273
- //# sourceMappingURL=ArtifactWriter.js.map
@@ -176,4 +176,3 @@ export class CausalChain {
176
176
  return row?.value === "1";
177
177
  }
178
178
  }
179
- //# sourceMappingURL=CausalChain.js.map
@@ -280,4 +280,3 @@ function oppositePair(a, b) {
280
280
  }
281
281
  return null;
282
282
  }
283
- //# sourceMappingURL=ConflictDetector.js.map
@@ -531,4 +531,3 @@ Consider adding this convention to AGENTS.md:
531
531
  output.context.push(block);
532
532
  }
533
533
  }
534
- //# sourceMappingURL=ContextInjector.js.map
@@ -240,4 +240,3 @@ function statementForSequence(a, b) {
240
240
  function statementForSequence3(a, b, c) {
241
241
  return `every ${a} is followed by ${b} and then ${c}`;
242
242
  }
243
- //# sourceMappingURL=ConventionMiner.js.map
@@ -322,4 +322,3 @@ function normalizeBullet(line) {
322
322
  .replace(/\s+\((?:verified|feedback)[^)]*\)\s*$/i, "");
323
323
  return normalize(statement);
324
324
  }
325
- //# sourceMappingURL=Curator.js.map
@@ -135,4 +135,3 @@ export class Feedback {
135
135
  .run(memoryId, memoryId, memoryId);
136
136
  }
137
137
  }
138
- //# sourceMappingURL=Feedback.js.map
@@ -68,6 +68,12 @@ export declare class HookLiveness {
68
68
  /** Persists every counter row. Called on the metrics.flush() cadence. */
69
69
  flush(): void;
70
70
  private makeWrapper;
71
+ /**
72
+ * v1.0.0 (K10-013 / plan §5.3) — records the `dispose` fire and
73
+ * flushes immediately, from inside the dispose delegate: this is the
74
+ * last write of the process, and nothing after it can persist.
75
+ */
76
+ recordDispose(): void;
71
77
  private recordSuccess;
72
78
  private recordError;
73
79
  private loadFromDb;
@@ -212,6 +212,12 @@ export class HookLiveness {
212
212
  }
213
213
  makeWrapper(key, delegate) {
214
214
  const record = () => {
215
+ // v1.0.0 (K10-013) — dispose is recorded inside its own delegate
216
+ // (via recordDispose()) rather than here: the wrapper's post-return
217
+ // record would land after the store is closed and could never be
218
+ // persisted — the event being detected is the process ending.
219
+ if (key === "dispose")
220
+ return;
215
221
  if (HOOK_NAMES.includes(key)) {
216
222
  this.recordSuccess(key);
217
223
  }
@@ -258,6 +264,15 @@ export class HookLiveness {
258
264
  }
259
265
  };
260
266
  }
267
+ /**
268
+ * v1.0.0 (K10-013 / plan §5.3) — records the `dispose` fire and
269
+ * flushes immediately, from inside the dispose delegate: this is the
270
+ * last write of the process, and nothing after it can persist.
271
+ */
272
+ recordDispose() {
273
+ this.recordSuccess("dispose");
274
+ this.flush();
275
+ }
261
276
  recordSuccess(hook) {
262
277
  const c = this.counters.get(hook);
263
278
  if (!c)
@@ -321,4 +336,3 @@ export function reduceVerdict(reports) {
321
336
  reason: `${pending} hook(s) without checkpoint`,
322
337
  };
323
338
  }
324
- //# sourceMappingURL=HookLiveness.js.map
@@ -246,4 +246,3 @@ function readOriginCallId(store, memoryId) {
246
246
  return null;
247
247
  }
248
248
  }
249
- //# sourceMappingURL=InjectionLedger.js.map
@@ -43,4 +43,3 @@ export async function enrichAtPromotion(pattern, enrichFn) {
43
43
  }
44
44
  return deterministicFixLine(pattern);
45
45
  }
46
- //# sourceMappingURL=LessonFixer.js.map
@@ -266,4 +266,3 @@ export class Materializer {
266
266
  }));
267
267
  }
268
268
  }
269
- //# sourceMappingURL=Materializer.js.map
@@ -1333,4 +1333,3 @@ function rankCompare(a, b, nowMs, deterministic) {
1333
1333
  }
1334
1334
  return sqliteUtcToMs(b.createdAt) - sqliteUtcToMs(a.createdAt); // newer first
1335
1335
  }
1336
- //# sourceMappingURL=MemoryService.js.map
@@ -5,7 +5,7 @@ export interface MigrateResult {
5
5
  applied: string[];
6
6
  }
7
7
  export type PostApplyHook = (store: Store) => void;
8
- export declare const HOOK_NAMES: readonly ["tool.execute.before", "tool.execute.after", "chat.message", "experimental.chat.system.transform", "experimental.session.compacting", "event"];
8
+ export declare const HOOK_NAMES: readonly ["tool.execute.before", "tool.execute.after", "chat.message", "experimental.chat.system.transform", "experimental.session.compacting", "event", "dispose"];
9
9
  export declare class Migrate {
10
10
  private store;
11
11
  private migrationsDir;
@@ -1,7 +1,8 @@
1
1
  import { readFileSync, readdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  // v0.9.0 (K9-001 / plan §5.3, D9-08) — the six hooks Kevin registers, in
4
- // hook-object order. Canonical list: the "010" post-apply hook seeds
4
+ // hook-object order, plus `dispose` (v1.0.0 K10-013): the seventh
5
+ // instrumented hook. Canonical list: the "010" post-apply hook seeds
5
6
  // hook_liveness from it, and HookLiveness (K9-009) wraps exactly these
6
7
  // names, so a future hook added here is visible in the table on the next
7
8
  // migration run.
@@ -12,6 +13,7 @@ export const HOOK_NAMES = [
12
13
  "experimental.chat.system.transform",
13
14
  "experimental.session.compacting",
14
15
  "event",
16
+ "dispose",
15
17
  ];
16
18
  // Built-in post-apply hooks, keyed by migration version. Each hook runs inside
17
19
  // the same transaction as the migration's DDL, so a hook failure rolls back the
@@ -163,6 +165,24 @@ const DEFAULT_POST_APPLY_HOOKS = {
163
165
  .prepare("UPDATE hook_liveness SET experimental = CASE WHEN hook LIKE 'experimental.%' THEN 1 ELSE 0 END")
164
166
  .run();
165
167
  },
168
+ // v1.0.0 (K10-005 / plan §6.1) — Proven: four operations, all idempotent.
169
+ // 1. Seed dispose row defensively.
170
+ // 2-3. Re-derive perf_budget_breaches and bench_runs_total.
171
+ // 4. Normalise NULL within_budget to 1.
172
+ "011": (store) => {
173
+ store
174
+ .prepare("INSERT OR IGNORE INTO hook_liveness (hook) VALUES ('dispose')")
175
+ .run();
176
+ store
177
+ .prepare("UPDATE kevin_metrics SET value = (SELECT COUNT(*) FROM perf_samples WHERE within_budget = 0) WHERE key = 'perf_budget_breaches'")
178
+ .run();
179
+ store
180
+ .prepare("UPDATE kevin_metrics SET value = (SELECT COUNT(*) FROM bench_runs) WHERE key = 'bench_runs_total'")
181
+ .run();
182
+ store
183
+ .prepare("UPDATE perf_samples SET within_budget = 1 WHERE within_budget IS NULL")
184
+ .run();
185
+ },
166
186
  };
167
187
  export class Migrate {
168
188
  store;
@@ -200,7 +220,10 @@ export class Migrate {
200
220
  // updates and re-derivation, so a no-op startup can heal a
201
221
  // crash that landed between the DDL and the hook without
202
222
  // re-applying DDL.
203
- if (from === "008" || from === "009" || from === "010") {
223
+ if (from === "008" ||
224
+ from === "009" ||
225
+ from === "010" ||
226
+ from === "011") {
204
227
  const repairHook = this.postApplyHooks.get(from);
205
228
  if (repairHook)
206
229
  this.store.transaction(() => repairHook(this.store));
@@ -241,4 +264,3 @@ export class Migrate {
241
264
  .filter((m) => m !== null && m.version > current);
242
265
  }
243
266
  }
244
- //# sourceMappingURL=Migrate.js.map
@@ -130,4 +130,3 @@ export class PatternMiner {
130
130
  return Array.from(map.values());
131
131
  }
132
132
  }
133
- //# sourceMappingURL=PatternMiner.js.map
@@ -117,4 +117,3 @@ export const QualityGate = {
117
117
  return { allowed: true, reason: "ok" };
118
118
  },
119
119
  };
120
- //# sourceMappingURL=QualityGate.js.map
@@ -305,4 +305,3 @@ export class Reflector {
305
305
  return "";
306
306
  }
307
307
  }
308
- //# sourceMappingURL=Reflector.js.map
@@ -298,4 +298,3 @@ export function initProjectFile(cwd, writer) {
298
298
  };
299
299
  }
300
300
  }
301
- //# sourceMappingURL=RepoIdentity.js.map
@@ -597,4 +597,3 @@ function normalizeBool(v) {
597
597
  return null;
598
598
  }
599
599
  export { MAX_FACTS_PER_PROJECT };
600
- //# sourceMappingURL=RepoTruth.js.map
@@ -67,6 +67,13 @@ export const METRIC_KEY_LABELS = {
67
67
  injections_suppressed_dead_hook: "Inyecciones suprimidas (hook muerto)",
68
68
  native_registrations_total: "Registros nativos (totales)",
69
69
  native_registration_failures: "Fallos de registro nativo",
70
+ // v1.0.0 (K10-005 / plan §6) — the six keys seeded by migration 011 section 3.
71
+ perf_samples_recorded: "Muestras de rendimiento registradas",
72
+ perf_budget_breaches: "Brechas de presupuesto de rendimiento",
73
+ dispose_fires_total: "Disparos de dispose (totales)",
74
+ dispose_misses_total: "Misses de dispose",
75
+ contract_digest_changes: "Cambios de digest de contrato",
76
+ bench_runs_total: "Ejecuciones de benchmark (totales)",
70
77
  };
71
78
  function originLabel(origin) {
72
79
  if (origin === "reflector")
@@ -200,4 +207,3 @@ export class Retrospective {
200
207
  return lines.join("\n");
201
208
  }
202
209
  }
203
- //# sourceMappingURL=Retrospective.js.map
@@ -460,4 +460,3 @@ export class SharedLayer {
460
460
  return n;
461
461
  }
462
462
  }
463
- //# sourceMappingURL=SharedLayer.js.map
@@ -48,4 +48,3 @@ export class Store {
48
48
  return this.db;
49
49
  }
50
50
  }
51
- //# sourceMappingURL=Store.js.map
@@ -186,4 +186,3 @@ export class ToolCallObserver {
186
186
  return v;
187
187
  }
188
188
  }
189
- //# sourceMappingURL=ToolCallObserver.js.map
@@ -39,4 +39,3 @@ export function probe(input) {
39
39
  return ALL_FALSE;
40
40
  }
41
41
  }
42
- //# sourceMappingURL=capabilities.js.map
@@ -32,4 +32,3 @@ export function computeConfidence(evidenceCount, recurrenceCount, positiveFeedba
32
32
  FEEDBACK_NEGATIVE_STEP * negativeFeedback;
33
33
  return Math.min(CONFIDENCE_MAX, Math.max(CONFIDENCE_MIN, raw));
34
34
  }
35
- //# sourceMappingURL=confidence.js.map
@@ -0,0 +1,46 @@
1
+ export declare const CONTRACT_VERSION = 1;
2
+ export type Stability = "frozen" | "forward-only";
3
+ export interface ContractClause {
4
+ readonly id: string;
5
+ readonly title: string;
6
+ readonly stability: Stability;
7
+ readonly since: string;
8
+ readonly deprecated?: string;
9
+ readonly replacement?: string;
10
+ readonly value: unknown;
11
+ }
12
+ export interface PublicContract {
13
+ readonly contractVersion: number;
14
+ readonly clauses: readonly ContractClause[];
15
+ }
16
+ export interface ContractInput {
17
+ readonly packageName?: string;
18
+ readonly packageVersion?: string;
19
+ }
20
+ export declare const CONTRACT_TOOL_NAMES: readonly string[];
21
+ /**
22
+ * v1.0.0 (K10-018/K10-019 / plan §5.6) — tools added AFTER the initial
23
+ * freeze, each carrying the `since` the deprecation policy requires. A
24
+ * 1.x addition without an entry here fails the contract test as
25
+ * added_bare.
26
+ */
27
+ export declare const CONTRACT_TOOL_ADDITIONS: readonly {
28
+ name: string;
29
+ since: string;
30
+ }[];
31
+ /**
32
+ * v1.0.0 (K10-027 / plan §5.7) — the C-09 boundary addition. Stored is
33
+ * not trusted: anything reaching an artifact or a prompt is escaped at
34
+ * the single write path, according to its container.
35
+ */
36
+ export declare const BOUNDARY_INVARIANT = "untrusted-input escaping at the single write path";
37
+ export declare function describeContract(_input?: ContractInput): PublicContract;
38
+ export declare function contractDigest(c: PublicContract): string;
39
+ export type ContractDiffKind = "removed" | "changed" | "added_ok" | "added_bare";
40
+ export interface ContractDiff {
41
+ readonly clauseId: string;
42
+ readonly path: string;
43
+ readonly kind: ContractDiffKind;
44
+ readonly remedy: string;
45
+ }
46
+ export declare function diffContract(golden: PublicContract, live: PublicContract): readonly ContractDiff[];