@jmtrin/opencode-kevin 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/plugin/index.d.ts +4 -3
  2. package/dist/plugin/index.js +122 -113
  3. package/dist/plugin/native.d.ts +2 -2
  4. package/dist/plugin/native.js +2 -2
  5. package/package.json +56 -68
  6. package/LICENSE +0 -21
  7. package/README.md +0 -605
  8. package/dist/migrations/001_initial.sql +0 -92
  9. package/dist/migrations/002_indexes.sql +0 -14
  10. package/dist/migrations/003_v02_signal.sql +0 -58
  11. package/dist/migrations/004_v03_knowledge.sql +0 -138
  12. package/dist/migrations/005_v04_signal.sql +0 -57
  13. package/dist/migrations/006_v05_glassbox.sql +0 -118
  14. package/dist/migrations/007_v06_pull.sql +0 -145
  15. package/dist/migrations/008_v07_truth.sql +0 -124
  16. package/dist/migrations/009_v08_team.sql +0 -100
  17. package/dist/migrations/010_v09_native.sql +0 -78
  18. package/dist/migrations/011_v10_proven.sql +0 -69
  19. package/dist/migrations/012_v11_drift.sql +0 -24
  20. package/dist/plugin/Archiver.d.ts +0 -42
  21. package/dist/plugin/Archiver.js +0 -83
  22. package/dist/plugin/ArtifactWriter.d.ts +0 -93
  23. package/dist/plugin/ArtifactWriter.js +0 -291
  24. package/dist/plugin/CausalChain.d.ts +0 -22
  25. package/dist/plugin/CausalChain.js +0 -197
  26. package/dist/plugin/ChatBridge.d.ts +0 -41
  27. package/dist/plugin/ChatBridge.js +0 -103
  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/DashboardHtml.d.ts +0 -5
  37. package/dist/plugin/DashboardHtml.js +0 -180
  38. package/dist/plugin/Feedback.d.ts +0 -67
  39. package/dist/plugin/Feedback.js +0 -120
  40. package/dist/plugin/HookLiveness.d.ts +0 -88
  41. package/dist/plugin/HookLiveness.js +0 -322
  42. package/dist/plugin/InjectionLedger.d.ts +0 -98
  43. package/dist/plugin/InjectionLedger.js +0 -295
  44. package/dist/plugin/LessonFixer.d.ts +0 -44
  45. package/dist/plugin/LessonFixer.js +0 -45
  46. package/dist/plugin/Materializer.d.ts +0 -83
  47. package/dist/plugin/Materializer.js +0 -181
  48. package/dist/plugin/MemoryService.d.ts +0 -360
  49. package/dist/plugin/MemoryService.js +0 -1242
  50. package/dist/plugin/Migrate.d.ts +0 -17
  51. package/dist/plugin/Migrate.js +0 -271
  52. package/dist/plugin/PatternMiner.d.ts +0 -49
  53. package/dist/plugin/PatternMiner.js +0 -132
  54. package/dist/plugin/QualityGate.d.ts +0 -124
  55. package/dist/plugin/QualityGate.js +0 -119
  56. package/dist/plugin/Reflector.d.ts +0 -91
  57. package/dist/plugin/Reflector.js +0 -307
  58. package/dist/plugin/RepoIdentity.d.ts +0 -124
  59. package/dist/plugin/RepoIdentity.js +0 -300
  60. package/dist/plugin/RepoTruth.d.ts +0 -80
  61. package/dist/plugin/RepoTruth.js +0 -599
  62. package/dist/plugin/Retrospective.d.ts +0 -17
  63. package/dist/plugin/Retrospective.js +0 -216
  64. package/dist/plugin/SharedLayer.d.ts +0 -159
  65. package/dist/plugin/SharedLayer.js +0 -462
  66. package/dist/plugin/Store.d.ts +0 -29
  67. package/dist/plugin/Store.js +0 -50
  68. package/dist/plugin/ToolCallObserver.d.ts +0 -32
  69. package/dist/plugin/ToolCallObserver.js +0 -201
  70. package/dist/plugin/TuiActions.d.ts +0 -43
  71. package/dist/plugin/TuiActions.js +0 -181
  72. package/dist/plugin/TuiSnapshots.d.ts +0 -24
  73. package/dist/plugin/TuiSnapshots.js +0 -158
  74. package/dist/plugin/columns.d.ts +0 -11
  75. package/dist/plugin/columns.js +0 -54
  76. package/dist/plugin/confidence.d.ts +0 -8
  77. package/dist/plugin/confidence.js +0 -34
  78. package/dist/plugin/contract.d.ts +0 -54
  79. package/dist/plugin/contract.js +0 -386
  80. package/dist/plugin/diff.d.ts +0 -8
  81. package/dist/plugin/diff.js +0 -182
  82. package/dist/plugin/escape.d.ts +0 -39
  83. package/dist/plugin/escape.js +0 -60
  84. package/dist/plugin/fingerprint.d.ts +0 -27
  85. package/dist/plugin/fingerprint.js +0 -73
  86. package/dist/plugin/inferability.d.ts +0 -32
  87. package/dist/plugin/inferability.js +0 -88
  88. package/dist/plugin/kevin_approve.d.ts +0 -34
  89. package/dist/plugin/kevin_approve.js +0 -52
  90. package/dist/plugin/kevin_audit.d.ts +0 -229
  91. package/dist/plugin/kevin_audit.js +0 -631
  92. package/dist/plugin/kevin_bench.d.ts +0 -10
  93. package/dist/plugin/kevin_bench.js +0 -82
  94. package/dist/plugin/kevin_conflicts.d.ts +0 -9
  95. package/dist/plugin/kevin_conflicts.js +0 -50
  96. package/dist/plugin/kevin_contract.d.ts +0 -8
  97. package/dist/plugin/kevin_contract.js +0 -59
  98. package/dist/plugin/kevin_doctor.d.ts +0 -62
  99. package/dist/plugin/kevin_doctor.js +0 -196
  100. package/dist/plugin/kevin_facts.d.ts +0 -42
  101. package/dist/plugin/kevin_facts.js +0 -36
  102. package/dist/plugin/kevin_forget.d.ts +0 -33
  103. package/dist/plugin/kevin_forget.js +0 -260
  104. package/dist/plugin/kevin_native.d.ts +0 -29
  105. package/dist/plugin/kevin_native.js +0 -79
  106. package/dist/plugin/kevin_propose.d.ts +0 -23
  107. package/dist/plugin/kevin_propose.js +0 -14
  108. package/dist/plugin/kevin_publish.d.ts +0 -38
  109. package/dist/plugin/kevin_publish.js +0 -18
  110. package/dist/plugin/kevin_why.d.ts +0 -23
  111. package/dist/plugin/kevin_why.js +0 -111
  112. package/dist/plugin/memory-format.d.ts +0 -20
  113. package/dist/plugin/memory-format.js +0 -59
  114. package/dist/plugin/metrics.d.ts +0 -97
  115. package/dist/plugin/metrics.js +0 -288
  116. package/dist/plugin/okf-export.d.ts +0 -3
  117. package/dist/plugin/okf-export.js +0 -134
  118. package/dist/plugin/okf-import.d.ts +0 -76
  119. package/dist/plugin/okf-import.js +0 -271
  120. package/dist/plugin/okf.d.ts +0 -107
  121. package/dist/plugin/okf.js +0 -303
  122. package/dist/plugin/perf.d.ts +0 -35
  123. package/dist/plugin/perf.js +0 -174
  124. package/dist/plugin/query-tokenizer.d.ts +0 -13
  125. package/dist/plugin/query-tokenizer.js +0 -133
  126. package/dist/plugin/redact.d.ts +0 -2
  127. package/dist/plugin/redact.js +0 -18
  128. package/dist/plugin/replay-types.d.ts +0 -69
  129. package/dist/plugin/replay-types.js +0 -156
  130. package/dist/plugin/replay.d.ts +0 -36
  131. package/dist/plugin/replay.js +0 -202
  132. package/dist/plugin/sqlite-adapter.d.ts +0 -12
  133. package/dist/plugin/sqlite-adapter.js +0 -81
  134. package/dist/plugin/time-ms.d.ts +0 -1
  135. package/dist/plugin/time-ms.js +0 -16
  136. package/dist/plugin/tui-types.d.ts +0 -59
  137. package/dist/plugin/tui-types.js +0 -4
  138. package/dist/plugin/tui.d.ts +0 -18
  139. package/dist/plugin/tui.js +0 -198
  140. package/dist/plugin/uuid.d.ts +0 -1
  141. package/dist/plugin/uuid.js +0 -50
@@ -1,39 +0,0 @@
1
- /**
2
- * v1.0.0 (K10-027 / plan §5.7) — the untrusted-input boundary.
3
- *
4
- * One pure, total, idempotent escaping function per container Kevin
5
- * writes stored text into. Every function here is a fixed point after
6
- * one application — applying it twice equals applying it once — because
7
- * a re-curated memory passes through the boundary on every regeneration
8
- * and must not accumulate escaping.
9
- *
10
- * This module imports nothing and touches no filesystem. It is the
11
- * boundary; the single enforcement point is ArtifactWriter (D6-01).
12
- */
13
- /**
14
- * Neutralise text destined for the AGENTS.md marker block: HTML-escape
15
- * (the memory-format discipline, made idempotent) so the literal
16
- * `<!-- kevin:end -->` marker sequence and every comment terminator
17
- * lose their meaning inside the block. The line-level strip of anything
18
- * containing `kevin:begin`/`kevin:end` stays in
19
- * ArtifactWriter.sanitizeArtifactBody — it is a filter, not an escape.
20
- */
21
- export declare function escapeForMarkerBlock(text: string): string;
22
- /**
23
- * Neutralise fenced-code delimiters: any run of three or more backticks
24
- * or tildes would close (or open) a markdown fence around the rendered
25
- * block and let stored text masquerade as un-fenced document content.
26
- * Each byte of the run becomes its numeric HTML entity, which renders
27
- * identically but can never form a delimiter again.
28
- */
29
- export declare function escapeForFence(text: string): string;
30
- /**
31
- * Neutralise bytes that would terminate an OKF v2 line: newline,
32
- * carriage return and every other C0 control character become their
33
- * JSON-style escapes, so a statement can never split into a second
34
- * line even if a future writer forgets JSON.stringify. Quotation marks
35
- * and backslashes are deliberately NOT touched here — the OKF line is
36
- * canonical JSON and re-escaping them would corrupt parsing; JSON
37
- * already guarantees they stay inside the string.
38
- */
39
- export declare function escapeForOkfLine(text: string): string;
@@ -1,60 +0,0 @@
1
- /**
2
- * v1.0.0 (K10-027 / plan §5.7) — the untrusted-input boundary.
3
- *
4
- * One pure, total, idempotent escaping function per container Kevin
5
- * writes stored text into. Every function here is a fixed point after
6
- * one application — applying it twice equals applying it once — because
7
- * a re-curated memory passes through the boundary on every regeneration
8
- * and must not accumulate escaping.
9
- *
10
- * This module imports nothing and touches no filesystem. It is the
11
- * boundary; the single enforcement point is ArtifactWriter (D6-01).
12
- */
13
- const ENTITY_HEADS = /&(?!(amp|lt|gt|#\d+);)/g;
14
- /**
15
- * Neutralise text destined for the AGENTS.md marker block: HTML-escape
16
- * (the memory-format discipline, made idempotent) so the literal
17
- * `<!-- kevin:end -->` marker sequence and every comment terminator
18
- * lose their meaning inside the block. The line-level strip of anything
19
- * containing `kevin:begin`/`kevin:end` stays in
20
- * ArtifactWriter.sanitizeArtifactBody — it is a filter, not an escape.
21
- */
22
- export function escapeForMarkerBlock(text) {
23
- return text
24
- .replace(ENTITY_HEADS, "&amp;")
25
- .replace(/</g, "&lt;")
26
- .replace(/>/g, "&gt;");
27
- }
28
- /**
29
- * Neutralise fenced-code delimiters: any run of three or more backticks
30
- * or tildes would close (or open) a markdown fence around the rendered
31
- * block and let stored text masquerade as un-fenced document content.
32
- * Each byte of the run becomes its numeric HTML entity, which renders
33
- * identically but can never form a delimiter again.
34
- */
35
- export function escapeForFence(text) {
36
- return text
37
- .replace(/`{3,}/g, (run) => "&#96;".repeat(run.length))
38
- .replace(/~{3,}/g, (run) => "&#126;".repeat(run.length));
39
- }
40
- /**
41
- * Neutralise bytes that would terminate an OKF v2 line: newline,
42
- * carriage return and every other C0 control character become their
43
- * JSON-style escapes, so a statement can never split into a second
44
- * line even if a future writer forgets JSON.stringify. Quotation marks
45
- * and backslashes are deliberately NOT touched here — the OKF line is
46
- * canonical JSON and re-escaping them would corrupt parsing; JSON
47
- * already guarantees they stay inside the string.
48
- */
49
- export function escapeForOkfLine(text) {
50
- // biome-ignore lint/suspicious/noControlCharactersInRegex: neutralising control bytes is this function's entire job
51
- return text.replace(/[\u0000-\u001f]/g, (c) => {
52
- if (c === "\n")
53
- return "\\n";
54
- if (c === "\r")
55
- return "\\r";
56
- if (c === "\t")
57
- return "\\t";
58
- return `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`;
59
- });
60
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * Normalize an error/output string before fingerprinting.
3
- *
4
- * Steps:
5
- * 1. Strip ANSI CSI sequences.
6
- * 2. Lowercase (for case-insensitive matching of paths/extensions).
7
- * 3. Replace `<path>.<ext>:<line>[:<col>` with `.<ext>` so the same
8
- * error thrown from different files hashes the same.
9
- * 4. Collapse all whitespace runs to a single space and trim.
10
- */
11
- export declare function normalize(content: string): string;
12
- /**
13
- * Compute the FNV-1a 64-bit hash of `input`, returned as a 16-char
14
- * lowercase hex string (zero-padded to 64 bits).
15
- *
16
- * FNV-1a: hash = offset_basis; for each byte: hash ^= byte; hash *= prime (mod 2^64).
17
- */
18
- export declare function fnv1a64(input: string): string;
19
- /**
20
- * Fingerprint an error/output string. Optionally salt the hash with
21
- * `project_id` so the same error text in two different projects
22
- * produces two distinct fingerprints (per decision D2-11/D2-14).
23
- *
24
- * Salt is prepended and separated by a NUL byte to avoid accidental
25
- * collisions where one project's name is a prefix of another's.
26
- */
27
- export declare function fingerprint(content: string, project_id?: string): string;
@@ -1,73 +0,0 @@
1
- // ============================================================
2
- // Kevin 0.2.0 — fingerprint (K2-003)
3
- // ============================================================
4
- // Deterministic, zero-dependency content hash used to dedup
5
- // error memories and to throttle per-fingerprint reflection.
6
- //
7
- // We deliberately avoid node:crypto so the module is portable
8
- // across plugin hosts and stays cheap. FNV-1a 64-bit gives a
9
- // decent spread for short-to-medium error strings and is
10
- // reproducible across runs and platforms (BigInt arithmetic).
11
- // ============================================================
12
- const FNV_OFFSET_BASIS = 0xcbf29ce484222325n;
13
- const FNV_PRIME = 0x100000001b3n;
14
- const FNV_MOD = 1n << 64n;
15
- // ANSI CSI sequences (color codes, cursor moves, etc.).
16
- // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI stripping requires matching the ESC (\x1b) control char by definition.
17
- const ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]/g;
18
- // Path-with-line-and-column references: `src/foo.ts:42:7` -> `.ts`.
19
- // Matches any non-whitespace path prefix followed by a dotted
20
- // extension and `:NN` or `:NN:NN` line/column markers. Useful for
21
- // stripping absolute or per-repo paths before fingerprinting so
22
- // the same error text in two repos/copy-paste edits hashes alike.
23
- const PATH_LINE_RE = /[^\s:]*\.([a-z0-9]+):\d+(?::\d+)?/g;
24
- // Whitespace collapse — preserves token order but ignores
25
- // line-break and indentation differences.
26
- const WS_RE = /\s+/g;
27
- /**
28
- * Normalize an error/output string before fingerprinting.
29
- *
30
- * Steps:
31
- * 1. Strip ANSI CSI sequences.
32
- * 2. Lowercase (for case-insensitive matching of paths/extensions).
33
- * 3. Replace `<path>.<ext>:<line>[:<col>` with `.<ext>` so the same
34
- * error thrown from different files hashes the same.
35
- * 4. Collapse all whitespace runs to a single space and trim.
36
- */
37
- export function normalize(content) {
38
- return content
39
- .replace(ANSI_RE, "")
40
- .toLowerCase()
41
- .replace(PATH_LINE_RE, ".$1")
42
- .replace(WS_RE, " ")
43
- .trim();
44
- }
45
- /**
46
- * Compute the FNV-1a 64-bit hash of `input`, returned as a 16-char
47
- * lowercase hex string (zero-padded to 64 bits).
48
- *
49
- * FNV-1a: hash = offset_basis; for each byte: hash ^= byte; hash *= prime (mod 2^64).
50
- */
51
- export function fnv1a64(input) {
52
- let hash = FNV_OFFSET_BASIS;
53
- for (let i = 0; i < input.length; i++) {
54
- const byte = input.charCodeAt(i) & 0xff;
55
- hash ^= BigInt(byte);
56
- hash = (hash * FNV_PRIME) % FNV_MOD;
57
- }
58
- // Encode as 16-char lowercase hex.
59
- return hash.toString(16).padStart(16, "0");
60
- }
61
- /**
62
- * Fingerprint an error/output string. Optionally salt the hash with
63
- * `project_id` so the same error text in two different projects
64
- * produces two distinct fingerprints (per decision D2-11/D2-14).
65
- *
66
- * Salt is prepended and separated by a NUL byte to avoid accidental
67
- * collisions where one project's name is a prefix of another's.
68
- */
69
- export function fingerprint(content, project_id) {
70
- const normalized = normalize(content);
71
- const salted = project_id ? `${project_id}\u0000${normalized}` : normalized;
72
- return fnv1a64(salted);
73
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * K6-010 — v0.6.0 pull — deterministic error-knowledge classifier (plan §5.3).
3
- *
4
- * Pure module: no DB, no clock, no filesystem, no RNG. Fully unit-testable in
5
- * isolation, and therefore fully unit-tested.
6
- */
7
- export type Inferability = "inferable" | "non_inferable" | "unknown";
8
- /**
9
- * The `Reflector` dispatch surface of §3.4, restated as data: error codes whose
10
- * payload IS the knowledge — no project-specific interpretation required.
11
- */
12
- export declare const SELF_DESCRIBING_CODES: ReadonlySet<string>;
13
- /**
14
- * Rules from plan §5.3, evaluated in order — the ordering is the specification:
15
- *
16
- * 1. `type` ∈ decision / rule / solution → `non_inferable`
17
- * 2. `type` = pattern → `non_inferable`
18
- * 3. `type` = error and `metadata.dispatch.code` ∈ SELF_DESCRIBING_CODES → `inferable`
19
- * 4. `type` = error and content names a project-specific path, script or flag → `non_inferable`
20
- * 5. otherwise → `unknown`
21
- *
22
- * Rule 4 beats rule 3: a TS2304 is inferable, but "TS2304 on
23
- * `./scripts/gen-routes.ts` because the generator must run before tsc" is not —
24
- * the project-specific script name is the payload, not the code. Rule 3's
25
- * condition (code membership) is evaluated first for the bare case; rule 4
26
- * catches what rule 3 does not.
27
- */
28
- export declare function classify(memory: {
29
- type: string;
30
- content: string;
31
- metadata?: unknown;
32
- }): Inferability;
@@ -1,88 +0,0 @@
1
- /**
2
- * K6-010 — v0.6.0 pull — deterministic error-knowledge classifier (plan §5.3).
3
- *
4
- * Pure module: no DB, no clock, no filesystem, no RNG. Fully unit-testable in
5
- * isolation, and therefore fully unit-tested.
6
- */
7
- /**
8
- * The `Reflector` dispatch surface of §3.4, restated as data: error codes whose
9
- * payload IS the knowledge — no project-specific interpretation required.
10
- */
11
- export const SELF_DESCRIBING_CODES = new Set([
12
- "TS2304",
13
- "TS2307",
14
- "TS2322",
15
- "TS2339",
16
- "TS2305",
17
- "TS2552",
18
- "TS2740",
19
- "TS6133",
20
- "TS18047",
21
- "E0433",
22
- "E0432",
23
- "command_not_found",
24
- ]);
25
- /**
26
- * Rule 4 detector — deliberately conservative. Errs toward `non_inferable`,
27
- * because a false `inferable` silently withholds real knowledge from curation
28
- * forever, while a false `non_inferable` costs one line a human rejects.
29
- */
30
- const NPM_RUN_RE = /\b(?:npm|pnpm|yarn|bun)(?:\s+(?:run|exec))?\s+[\w@:/-]+/;
31
- const RELATIVE_PATH_RE = /\.{1,2}\/[\w./@-]+/;
32
- const FLAG_RE = /(?:^|\s)--[\w-]+/;
33
- const FILE_EXT_RE = /\b[\w./@-]+\.[a-zA-Z][a-zA-Z0-9]{0,7}\b/;
34
- function namesProjectSpecific(content) {
35
- return (NPM_RUN_RE.test(content) ||
36
- RELATIVE_PATH_RE.test(content) ||
37
- FLAG_RE.test(content) ||
38
- FILE_EXT_RE.test(content));
39
- }
40
- function dispatchCode(metadata) {
41
- if (metadata === null || typeof metadata !== "object")
42
- return null;
43
- const dispatch = metadata.dispatch;
44
- if (dispatch === null || typeof dispatch !== "object")
45
- return null;
46
- const code = dispatch.code;
47
- return typeof code === "string" && code.length > 0 ? code : null;
48
- }
49
- /**
50
- * Rules from plan §5.3, evaluated in order — the ordering is the specification:
51
- *
52
- * 1. `type` ∈ decision / rule / solution → `non_inferable`
53
- * 2. `type` = pattern → `non_inferable`
54
- * 3. `type` = error and `metadata.dispatch.code` ∈ SELF_DESCRIBING_CODES → `inferable`
55
- * 4. `type` = error and content names a project-specific path, script or flag → `non_inferable`
56
- * 5. otherwise → `unknown`
57
- *
58
- * Rule 4 beats rule 3: a TS2304 is inferable, but "TS2304 on
59
- * `./scripts/gen-routes.ts` because the generator must run before tsc" is not —
60
- * the project-specific script name is the payload, not the code. Rule 3's
61
- * condition (code membership) is evaluated first for the bare case; rule 4
62
- * catches what rule 3 does not.
63
- */
64
- export function classify(memory) {
65
- // Rule 1 — authored knowledge is never re-derived.
66
- if (memory.type === "decision" ||
67
- memory.type === "rule" ||
68
- memory.type === "solution") {
69
- return "non_inferable";
70
- }
71
- // Rule 2 — a mined sequence is project-specific by construction.
72
- if (memory.type === "pattern") {
73
- return "non_inferable";
74
- }
75
- if (memory.type === "error") {
76
- const code = dispatchCode(memory.metadata);
77
- // Rule 4 — content naming a project-specific path, script or flag.
78
- if (namesProjectSpecific(memory.content)) {
79
- return "non_inferable";
80
- }
81
- // Rule 3 — a bare self-describing code is inferable.
82
- if (code !== null && SELF_DESCRIBING_CODES.has(code)) {
83
- return "inferable";
84
- }
85
- }
86
- // Rule 5.
87
- return "unknown";
88
- }
@@ -1,34 +0,0 @@
1
- import type { ArtifactWriter } from "./ArtifactWriter.js";
2
- import type { Curator, ProposalStatus } from "./Curator.js";
3
- import type { MemoryService } from "./MemoryService.js";
4
- import type { Store } from "./Store.js";
5
- import type { Metrics } from "./metrics.js";
6
- /**
7
- * K6-014 — v0.6.0 pull — `kevin_approve` tool logic.
8
- *
9
- * The ONLY code path in the entire plugin that may call
10
- * `ArtifactWriter`'s apply method (D6-01; enforced by
11
- * tests/unit/single_write_path.test.ts).
12
- *
13
- * There is deliberately no "trusted mode" and no flag that skips the human:
14
- * the approval gate is the release's entire safety model.
15
- */
16
- export interface ApproveArgs {
17
- readonly proposalId: string;
18
- readonly decision: "approve" | "reject";
19
- }
20
- export type ApproveResult = {
21
- readonly proposalId: string;
22
- readonly status: "rejected";
23
- } | {
24
- readonly proposalId: string;
25
- readonly status: "applied";
26
- readonly outcome: string;
27
- readonly curated: number;
28
- } | {
29
- readonly error: "not_found" | "not_pending";
30
- readonly proposalId: string;
31
- readonly status?: ProposalStatus;
32
- readonly message: string;
33
- };
34
- export declare function kevinApprove(store: Store, memoryService: MemoryService, curator: Curator, writer: ArtifactWriter, metrics: Metrics | null, args: ApproveArgs): ApproveResult;
@@ -1,52 +0,0 @@
1
- export function kevinApprove(store, memoryService, curator, writer, metrics, args) {
2
- const row = store
3
- .prepare(`SELECT id, kind, target_path, proposed_text, memory_id, status
4
- FROM curation_proposals WHERE id = ?`)
5
- .get(args.proposalId);
6
- if (!row) {
7
- return {
8
- error: "not_found",
9
- proposalId: args.proposalId,
10
- message: `No proposal with id "${args.proposalId}".`,
11
- };
12
- }
13
- if (row.status !== "pending") {
14
- return {
15
- error: "not_pending",
16
- proposalId: args.proposalId,
17
- status: row.status,
18
- message: `Proposal "${args.proposalId}" is ${row.status}, not pending.`,
19
- };
20
- }
21
- if (args.decision === "reject") {
22
- curator.transition(args.proposalId, "reject");
23
- metrics?.incr("proposals_rejected", 1);
24
- return { proposalId: args.proposalId, status: "rejected" };
25
- }
26
- // approve → apply → applied (plan §5.5). The proposed_text is re-planned
27
- // at approval time against the file as it is now; write() is atomic and
28
- // audits every outcome including noop. The disk write happens BEFORE the
29
- // state transitions: if write() throws (e.g. a filesystem error), the
30
- // proposal is still 'pending' and the human can retry or reject — a
31
- // row stuck in 'approved' with no file written would be a dead end,
32
- // since kevin_approve only accepts pending rows.
33
- const outcome = writer.write({
34
- path: row.target_path,
35
- mode: "markers",
36
- content: row.proposed_text,
37
- }, args.proposalId);
38
- curator.transition(args.proposalId, "approve");
39
- curator.transition(args.proposalId, "apply");
40
- const memoryIds = row.memory_id.split(",").filter((s) => s.length > 0);
41
- const curated = memoryService.markCurated(memoryIds, sqliteNow());
42
- metrics?.incr("proposals_approved", 1);
43
- return {
44
- proposalId: args.proposalId,
45
- status: "applied",
46
- outcome,
47
- curated,
48
- };
49
- }
50
- function sqliteNow() {
51
- return new Date().toISOString().slice(0, 19).replace("T", " ");
52
- }
@@ -1,229 +0,0 @@
1
- import type { Store } from "./Store.js";
2
- import type { Capabilities } from "./capabilities.js";
3
- import type { Metrics } from "./metrics.js";
4
- /**
5
- * v0.5.0 (K5-016 / plan §5.7) — `kevin_audit`.
6
- *
7
- * Pure SQL over `memories`, `kevin_injections`, `kevin_metrics`,
8
- * `memory_feedback`. No writes, no LLM, no filesystem access.
9
- *
10
- * There is deliberately **no `kevin_context_ratio`** (D5-09): Kevin cannot
11
- * observe total session input tokens, so any such figure would be fabricated.
12
- *
13
- * Pre-006 databases degrade gracefully: any block that fails (missing column
14
- * or table) falls back to its zero value and the report is flagged
15
- * `"partial": true` instead of throwing.
16
- *
17
- * v0.6.0 (K6-023 / plan §5.8) — the `channels` and `curation` blocks are the
18
- * release's own scoreboard. On a pre-007 database they are **omitted** (not
19
- * zero-valued) and `"partial": true` is set: a report that cannot answer
20
- * "do the pull channels beat push?" must say so rather than suggest it can.
21
- *
22
- * v0.7.0 (K7-019 / plan §5.3, K7-006) — the `truth` block is project-scoped:
23
- * facts scanned, penalized memory count and the truncation flag for the
24
- * current project. It is omitted (and `"partial": true` set) on a pre-008
25
- * database, exactly like the conflicts block. `facts_scanned` counts every
26
- * `repo_facts` row for the project — the same COUNT(*) semantics as the
27
- * `repo_facts_scanned` metric — while `truncated` reports the scan cap
28
- * separately, mirroring `kevin_facts`.
29
- */
30
- export interface AuditReport {
31
- memories: {
32
- total: number;
33
- by_status: Record<string, number>;
34
- by_origin: Record<string, number>;
35
- by_type: Record<string, number>;
36
- ignored: number;
37
- archived: number;
38
- with_feedback: number;
39
- superseded_with_target: number;
40
- };
41
- injections: {
42
- total: number;
43
- effective: number;
44
- ineffective: number;
45
- inconclusive: number;
46
- unmeasured: number;
47
- precision_rate: number;
48
- coverage_rate: number;
49
- };
50
- blocked: Record<string, number>;
51
- feedback: {
52
- positive: number;
53
- negative: number;
54
- by_verdict: Record<string, number>;
55
- };
56
- tokens: {
57
- pre_prompt: number;
58
- compacting: number;
59
- };
60
- settings: Record<string, string>;
61
- conflicts?: {
62
- by_kind: Record<string, number>;
63
- by_status: Record<string, number>;
64
- };
65
- truth?: TruthReport;
66
- mix?: {
67
- injected_by_type: Record<string, number>;
68
- injected_total: number;
69
- non_error_injected: number;
70
- non_error_share: number;
71
- precision_error: number;
72
- precision_non_error: number;
73
- meets_exit_criterion: boolean;
74
- reason?: "immature_db";
75
- };
76
- channels?: ChannelReport;
77
- curation?: CurationReport;
78
- /**
79
- * v0.8.0 (K8-018/023 / plan §5.2, §5.7) — the shared-layer block.
80
- * K8-018 kept it minimal (write_refusals); K8-023 extends it with
81
- * the full team rollup, computed in pure SQL. On a database below
82
- * the v0.7.0 maturity floor the precision numbers are omitted and
83
- * `reason: "immature_db"` reported instead.
84
- */
85
- team?: {
86
- write_refusals: number;
87
- shared_total?: number;
88
- tombstones?: number;
89
- distinct_authors?: number;
90
- last_import_at?: string | null;
91
- last_import_rejected?: number;
92
- precision_shared?: number;
93
- precision_local?: number;
94
- reason?: "immature_db";
95
- };
96
- /**
97
- * v0.9.0 (K9-020 / plan §5.5) — the host block, alongside the v0.7.0
98
- * `mix` and v0.8.0 `team` blocks. Pure SQL: hook states in aggregate
99
- * (live/dead/unknown counts, fires and errors totals), the resolved
100
- * plugin version, native registration outcomes per surface, and the
101
- * verdict derivable from the database alone. On a pre-010 database
102
- * the block is omitted (not zero-valued) and `"partial": true` set,
103
- * following the channels/curation precedent.
104
- */
105
- host?: {
106
- plugin_version: string | null;
107
- hooks: {
108
- live: number;
109
- dead: number;
110
- unknown: number;
111
- fires_total: number;
112
- errors_total: number;
113
- };
114
- native: {
115
- total: number;
116
- verified: number;
117
- failures: number;
118
- by_surface: Record<string, {
119
- registered: number;
120
- verified: number;
121
- }>;
122
- };
123
- verdict: "healthy" | "degraded" | "unknown";
124
- };
125
- /**
126
- * v1.0.0 (K10-020 / plan §5.6) — the perf block: per scope, the most
127
- * recent `perf_samples` aggregate row (count/p50/p95/max) plus the
128
- * stored budget and verdict. The stored aggregates are used DIRECTLY —
129
- * re-aggregating aggregates would produce a number with no meaning
130
- * (plan anti-pattern 42), and using the most recent row per scope is
131
- * exactly what `bench:check` does, so `within_budget` agrees with it
132
- * on the same data. Omitted (not zero-valued) on a pre-011 database,
133
- * following the host precedent.
134
- */
135
- perf?: {
136
- scopes: Record<string, {
137
- count: number;
138
- p50: number;
139
- p95: number;
140
- max: number;
141
- budget_p95: number;
142
- within_budget: boolean;
143
- }>;
144
- };
145
- /**
146
- * v1.0.0 (K10-020 / plan §5.6) — the contract block: the live contract's
147
- * identity, computed from source rather than the database, so it is
148
- * present even on a pre-011 database.
149
- */
150
- contract?: {
151
- contract_version: number;
152
- digest: string;
153
- clause_count: number;
154
- deprecated_count: number;
155
- };
156
- /**
157
- * v1.2.0 (K12-014 / D12-??) — TUI surface block. Read-only introspection:
158
- * enabled_setting is the kevin_settings value, last_flush_age_s is seconds
159
- * since meta.json generatedAt (null when absent/corrupt), mailbox_depth is
160
- * queue length (null when file missing), last_results is the parsed
161
- * results.json array (null when absent). Dashboard age and bridge counters
162
- * added in K12-019.
163
- */
164
- tui?: {
165
- enabled_setting: string;
166
- last_flush_age_s: number | null;
167
- mailbox_depth: number | null;
168
- last_results: unknown | null;
169
- dashboard_last_write_age_s: number | null;
170
- bridge_interceptions: number;
171
- };
172
- partial: boolean;
173
- }
174
- /**
175
- * v0.6.0 (K6-023 / plan §5.8) — three-channel comparison on the same axes.
176
- * `skill_emission` / `reference_emission` distinguish the three states a
177
- * channel can be in: "unavailable" (v1 host, no domain on the plugin input),
178
- * "off" (capable host, setting '0'), "on" (capable host, setting '1').
179
- * Collapsing "unavailable" and "off" would make "my host is too old"
180
- * indistinguishable from "I turned it off".
181
- */
182
- export type EmissionState = "on" | "off" | "unavailable";
183
- export interface ChannelReport {
184
- push: {
185
- tokens_pre_prompt: number;
186
- tokens_compacting: number;
187
- injections_total: number;
188
- precision_rate: number;
189
- coverage_rate: number;
190
- budget_tokens: number;
191
- };
192
- pull: {
193
- proposals_created: number;
194
- proposals_approved: number;
195
- proposals_rejected: number;
196
- artifact_writes_total: number;
197
- artifact_writes_noop: number;
198
- references_registered: number;
199
- skills_registered: number;
200
- skill_emission: EmissionState;
201
- reference_emission: EmissionState;
202
- };
203
- }
204
- export interface CurationReport {
205
- eligible: number;
206
- curated: number;
207
- inferable: number;
208
- non_inferable: number;
209
- unknown: number;
210
- proposals_by_status: Record<string, number>;
211
- }
212
- /**
213
- * v0.7.0 (K7-019 / plan §5.3) — the Project Truth block. The same shape
214
- * `kevin_facts` exposes: `facts_scanned` counts every repo_facts row for the
215
- * project (COUNT(*) semantics, matching the `repo_facts_scanned` metric),
216
- * `penalized_memories` counts memories with `truth_penalty > 0`, and
217
- * `truncated` reports the 500-fact scan cap from the `_truncated` marker row.
218
- */
219
- export interface TruthReport {
220
- facts_scanned: number;
221
- penalized_memories: number;
222
- truncated: {
223
- is_truncated: true;
224
- count: number;
225
- } | {
226
- is_truncated: false;
227
- };
228
- }
229
- export declare function buildAudit(store: Store, metrics: Metrics, capabilities?: Capabilities, projectId?: string, repoId?: string | null, tuiRoot?: string): AuditReport;