@jmtrin/opencode-kevin 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/plugin/capabilities.d.ts +2 -0
  2. package/dist/plugin/capabilities.js +3 -0
  3. package/dist/plugin/index.d.ts +5 -4
  4. package/dist/plugin/index.js +401 -108
  5. package/dist/plugin/native.d.ts +2 -2
  6. package/dist/plugin/native.js +2 -2
  7. package/package.json +56 -63
  8. package/LICENSE +0 -21
  9. package/README.md +0 -576
  10. package/dist/migrations/001_initial.sql +0 -92
  11. package/dist/migrations/002_indexes.sql +0 -14
  12. package/dist/migrations/003_v02_signal.sql +0 -58
  13. package/dist/migrations/004_v03_knowledge.sql +0 -138
  14. package/dist/migrations/005_v04_signal.sql +0 -57
  15. package/dist/migrations/006_v05_glassbox.sql +0 -118
  16. package/dist/migrations/007_v06_pull.sql +0 -145
  17. package/dist/migrations/008_v07_truth.sql +0 -124
  18. package/dist/migrations/009_v08_team.sql +0 -100
  19. package/dist/migrations/010_v09_native.sql +0 -78
  20. package/dist/migrations/011_v10_proven.sql +0 -69
  21. package/dist/migrations/012_v11_drift.sql +0 -24
  22. package/dist/plugin/Archiver.d.ts +0 -42
  23. package/dist/plugin/Archiver.js +0 -83
  24. package/dist/plugin/ArtifactWriter.d.ts +0 -93
  25. package/dist/plugin/ArtifactWriter.js +0 -291
  26. package/dist/plugin/CausalChain.d.ts +0 -22
  27. package/dist/plugin/CausalChain.js +0 -197
  28. package/dist/plugin/ConflictDetector.d.ts +0 -35
  29. package/dist/plugin/ConflictDetector.js +0 -260
  30. package/dist/plugin/ContextInjector.d.ts +0 -222
  31. package/dist/plugin/ContextInjector.js +0 -533
  32. package/dist/plugin/ConventionMiner.d.ts +0 -35
  33. package/dist/plugin/ConventionMiner.js +0 -242
  34. package/dist/plugin/Curator.d.ts +0 -116
  35. package/dist/plugin/Curator.js +0 -324
  36. package/dist/plugin/Feedback.d.ts +0 -67
  37. package/dist/plugin/Feedback.js +0 -120
  38. package/dist/plugin/HookLiveness.d.ts +0 -88
  39. package/dist/plugin/HookLiveness.js +0 -322
  40. package/dist/plugin/InjectionLedger.d.ts +0 -98
  41. package/dist/plugin/InjectionLedger.js +0 -295
  42. package/dist/plugin/LessonFixer.d.ts +0 -44
  43. package/dist/plugin/LessonFixer.js +0 -45
  44. package/dist/plugin/Materializer.d.ts +0 -83
  45. package/dist/plugin/Materializer.js +0 -181
  46. package/dist/plugin/MemoryService.d.ts +0 -360
  47. package/dist/plugin/MemoryService.js +0 -1242
  48. package/dist/plugin/Migrate.d.ts +0 -17
  49. package/dist/plugin/Migrate.js +0 -271
  50. package/dist/plugin/PatternMiner.d.ts +0 -49
  51. package/dist/plugin/PatternMiner.js +0 -132
  52. package/dist/plugin/QualityGate.d.ts +0 -124
  53. package/dist/plugin/QualityGate.js +0 -119
  54. package/dist/plugin/Reflector.d.ts +0 -91
  55. package/dist/plugin/Reflector.js +0 -307
  56. package/dist/plugin/RepoIdentity.d.ts +0 -124
  57. package/dist/plugin/RepoIdentity.js +0 -300
  58. package/dist/plugin/RepoTruth.d.ts +0 -80
  59. package/dist/plugin/RepoTruth.js +0 -599
  60. package/dist/plugin/Retrospective.d.ts +0 -17
  61. package/dist/plugin/Retrospective.js +0 -213
  62. package/dist/plugin/SharedLayer.d.ts +0 -159
  63. package/dist/plugin/SharedLayer.js +0 -462
  64. package/dist/plugin/Store.d.ts +0 -29
  65. package/dist/plugin/Store.js +0 -50
  66. package/dist/plugin/ToolCallObserver.d.ts +0 -32
  67. package/dist/plugin/ToolCallObserver.js +0 -201
  68. package/dist/plugin/columns.d.ts +0 -11
  69. package/dist/plugin/columns.js +0 -54
  70. package/dist/plugin/confidence.d.ts +0 -8
  71. package/dist/plugin/confidence.js +0 -34
  72. package/dist/plugin/contract.d.ts +0 -54
  73. package/dist/plugin/contract.js +0 -383
  74. package/dist/plugin/diff.d.ts +0 -8
  75. package/dist/plugin/diff.js +0 -182
  76. package/dist/plugin/escape.d.ts +0 -39
  77. package/dist/plugin/escape.js +0 -60
  78. package/dist/plugin/fingerprint.d.ts +0 -27
  79. package/dist/plugin/fingerprint.js +0 -73
  80. package/dist/plugin/inferability.d.ts +0 -32
  81. package/dist/plugin/inferability.js +0 -88
  82. package/dist/plugin/kevin_approve.d.ts +0 -34
  83. package/dist/plugin/kevin_approve.js +0 -52
  84. package/dist/plugin/kevin_audit.d.ts +0 -213
  85. package/dist/plugin/kevin_audit.js +0 -563
  86. package/dist/plugin/kevin_bench.d.ts +0 -10
  87. package/dist/plugin/kevin_bench.js +0 -82
  88. package/dist/plugin/kevin_conflicts.d.ts +0 -9
  89. package/dist/plugin/kevin_conflicts.js +0 -50
  90. package/dist/plugin/kevin_contract.d.ts +0 -8
  91. package/dist/plugin/kevin_contract.js +0 -59
  92. package/dist/plugin/kevin_doctor.d.ts +0 -62
  93. package/dist/plugin/kevin_doctor.js +0 -196
  94. package/dist/plugin/kevin_facts.d.ts +0 -42
  95. package/dist/plugin/kevin_facts.js +0 -36
  96. package/dist/plugin/kevin_forget.d.ts +0 -33
  97. package/dist/plugin/kevin_forget.js +0 -260
  98. package/dist/plugin/kevin_native.d.ts +0 -29
  99. package/dist/plugin/kevin_native.js +0 -79
  100. package/dist/plugin/kevin_propose.d.ts +0 -23
  101. package/dist/plugin/kevin_propose.js +0 -14
  102. package/dist/plugin/kevin_publish.d.ts +0 -38
  103. package/dist/plugin/kevin_publish.js +0 -18
  104. package/dist/plugin/kevin_why.d.ts +0 -23
  105. package/dist/plugin/kevin_why.js +0 -111
  106. package/dist/plugin/memory-format.d.ts +0 -20
  107. package/dist/plugin/memory-format.js +0 -59
  108. package/dist/plugin/metrics.d.ts +0 -97
  109. package/dist/plugin/metrics.js +0 -284
  110. package/dist/plugin/okf-export.d.ts +0 -3
  111. package/dist/plugin/okf-export.js +0 -134
  112. package/dist/plugin/okf-import.d.ts +0 -76
  113. package/dist/plugin/okf-import.js +0 -271
  114. package/dist/plugin/okf.d.ts +0 -107
  115. package/dist/plugin/okf.js +0 -303
  116. package/dist/plugin/perf.d.ts +0 -35
  117. package/dist/plugin/perf.js +0 -174
  118. package/dist/plugin/query-tokenizer.d.ts +0 -13
  119. package/dist/plugin/query-tokenizer.js +0 -133
  120. package/dist/plugin/redact.d.ts +0 -2
  121. package/dist/plugin/redact.js +0 -18
  122. package/dist/plugin/replay-types.d.ts +0 -69
  123. package/dist/plugin/replay-types.js +0 -156
  124. package/dist/plugin/replay.d.ts +0 -36
  125. package/dist/plugin/replay.js +0 -202
  126. package/dist/plugin/sqlite-adapter.d.ts +0 -12
  127. package/dist/plugin/sqlite-adapter.js +0 -81
  128. package/dist/plugin/time-ms.d.ts +0 -1
  129. package/dist/plugin/time-ms.js +0 -16
  130. package/dist/plugin/uuid.d.ts +0 -1
  131. package/dist/plugin/uuid.js +0 -50
@@ -1,100 +0,0 @@
1
- -- ============================================================
2
- -- Kevin 0.8.0 - Migration 009: Team (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
- -- Scope note: this migration introduces `repo_id`, a SECOND
9
- -- scoping dimension. `project_id` is retained on every table,
10
- -- unchanged, as local-path provenance (D8-02). Nothing that
11
- -- reads `project_id` today stops working.
12
- -- ============================================================
13
-
14
- -- 1. shared_entries: the local projection of the committed OKF file.
15
- -- One row per (repo_id, entry_id). Rewritten by SharedLayer.import(),
16
- -- never edited by hand, never the source of truth - the file is.
17
- -- No REFERENCES to memories: an entry may arrive from a teammate
18
- -- before any local memory corresponds to it (D8-12).
19
- CREATE TABLE IF NOT EXISTS shared_entries (
20
- id TEXT PRIMARY KEY,
21
- repo_id TEXT NOT NULL,
22
- entry_id TEXT NOT NULL,
23
- type TEXT NOT NULL CHECK (type IN ('decision', 'rule', 'pattern', 'solution')),
24
- statement TEXT NOT NULL,
25
- scope TEXT,
26
- confidence REAL NOT NULL DEFAULT 0.0,
27
- evidence INTEGER NOT NULL DEFAULT 0,
28
- origin TEXT NOT NULL DEFAULT 'shared',
29
- author_hash TEXT,
30
- op TEXT NOT NULL CHECK (op IN ('assert', 'tombstone')) DEFAULT 'assert',
31
- supersedes TEXT,
32
- created_at TEXT NOT NULL,
33
- imported_at TEXT NOT NULL DEFAULT (datetime('now'))
34
- );
35
-
36
- -- 1b. Identity is (repo_id, entry_id). The UNIQUE index is what makes
37
- -- import() an idempotent upsert instead of an append.
38
- CREATE UNIQUE INDEX IF NOT EXISTS uq_shared_entries
39
- ON shared_entries(repo_id, entry_id);
40
- CREATE INDEX IF NOT EXISTS idx_shared_entries_op
41
- ON shared_entries(op);
42
- CREATE INDEX IF NOT EXISTS idx_shared_entries_type
43
- ON shared_entries(type);
44
-
45
- -- 2. okf_imports: append-only audit of every read of the shared file,
46
- -- including no-op reads and refusals. `file_hash` drives the skip
47
- -- path in SharedLayer.import() (D8-14).
48
- CREATE TABLE IF NOT EXISTS okf_imports (
49
- id TEXT PRIMARY KEY,
50
- repo_id TEXT NOT NULL,
51
- path TEXT NOT NULL,
52
- file_hash TEXT,
53
- entries_parsed INTEGER NOT NULL DEFAULT 0,
54
- entries_folded INTEGER NOT NULL DEFAULT 0,
55
- entries_rejected INTEGER NOT NULL DEFAULT 0,
56
- skipped INTEGER NOT NULL DEFAULT 0,
57
- imported_at TEXT NOT NULL DEFAULT (datetime('now'))
58
- );
59
-
60
- CREATE INDEX IF NOT EXISTS idx_okf_imports_repo
61
- ON okf_imports(repo_id, imported_at);
62
-
63
- -- 3. memories: the second scoping dimension and the layer marker.
64
- -- repo_id is NULLABLE and back-filled by the post-apply hook, not by
65
- -- a DEFAULT: the value depends on the row's existing project_id and
66
- -- SQLite cannot express that in a column default.
67
- -- layer carries NO CHECK constraint - widening it later would force
68
- -- the migration-004 rebuild path (D8-07). Enforced in TypeScript.
69
- ALTER TABLE memories ADD COLUMN repo_id TEXT;
70
- ALTER TABLE memories ADD COLUMN layer TEXT NOT NULL DEFAULT 'local';
71
- ALTER TABLE memories ADD COLUMN shared_entry_id TEXT;
72
-
73
- CREATE INDEX IF NOT EXISTS idx_memories_repo_id
74
- ON memories(repo_id, status);
75
- CREATE INDEX IF NOT EXISTS idx_memories_layer
76
- ON memories(layer);
77
-
78
- -- 4. kevin_metrics: seed the six v0.8 counters (33 -> 39).
79
- INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
80
- ('shared_entries_total', 0),
81
- ('shared_entries_imported', 0),
82
- ('shared_entries_exported', 0),
83
- ('okf_merge_folds', 0),
84
- ('rekey_events', 0),
85
- ('injections_from_shared', 0);
86
-
87
- -- 5. kevin_settings: seed the five v0.8 flags (18 -> 23).
88
- -- shared_layer_enabled defaults OFF: this release must be opted into,
89
- -- because its first side effect is a new file in the user's repository.
90
- -- shared_confidence_floor (0.7) is deliberately STRICTER than
91
- -- injection_confidence_floor (0.6) - see 5.7.
92
- INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
93
- ('shared_layer_enabled', '0'),
94
- ('okf_path', '.kevin/knowledge.okf'),
95
- ('share_requires_approval', '1'),
96
- ('author_identity_mode', 'hashed'),
97
- ('shared_confidence_floor', '0.7');
98
-
99
- -- 6. Seed version 009.
100
- INSERT OR IGNORE INTO schema_version (version) VALUES ('009');
@@ -1,78 +0,0 @@
1
- -- ============================================================
2
- -- Kevin v0.9.0 "Native" — host surface and hook liveness
3
- -- Migration 010. Additive only. No table rebuild.
4
- -- ============================================================
5
-
6
- -- 1. Hook liveness. Deliberately NOT project-scoped: a hook is a
7
- -- property of the host binary, not of a checkout. One row per
8
- -- hook name, updated in place. See D9-08.
9
- CREATE TABLE IF NOT EXISTS hook_liveness (
10
- hook TEXT PRIMARY KEY,
11
- experimental INTEGER NOT NULL DEFAULT 0,
12
- fire_count INTEGER NOT NULL DEFAULT 0,
13
- error_count INTEGER NOT NULL DEFAULT 0,
14
- expected_count INTEGER NOT NULL DEFAULT 0,
15
- first_seen_at TEXT,
16
- last_seen_at TEXT,
17
- dead_since TEXT,
18
- plugin_version TEXT
19
- );
20
-
21
- CREATE INDEX IF NOT EXISTS idx_hook_liveness_dead
22
- ON hook_liveness(dead_since);
23
-
24
- -- 2. Host probe history. Append-only. Off by default; exists so a
25
- -- user chasing an intermittent fault can turn it on and get a
26
- -- timeline instead of a single current value.
27
- CREATE TABLE IF NOT EXISTS host_probes (
28
- id TEXT PRIMARY KEY,
29
- probed_at TEXT NOT NULL DEFAULT (datetime('now')),
30
- plugin_version TEXT,
31
- flavour TEXT NOT NULL,
32
- has_shell INTEGER NOT NULL DEFAULT 0,
33
- v2_skill INTEGER NOT NULL DEFAULT 0,
34
- v2_reference INTEGER NOT NULL DEFAULT 0,
35
- notes TEXT
36
- );
37
-
38
- CREATE INDEX IF NOT EXISTS idx_host_probes_at
39
- ON host_probes(probed_at);
40
-
41
- -- 3. Native registration outcomes. One row per attach attempt, so
42
- -- "registered but unverified" is a queryable state rather than a
43
- -- log line.
44
- CREATE TABLE IF NOT EXISTS native_registrations (
45
- id TEXT PRIMARY KEY,
46
- attached_at TEXT NOT NULL DEFAULT (datetime('now')),
47
- surface TEXT NOT NULL CHECK (surface IN ('skill', 'reference')),
48
- registered INTEGER NOT NULL DEFAULT 0,
49
- verified INTEGER NOT NULL DEFAULT 0,
50
- note TEXT
51
- );
52
-
53
- CREATE INDEX IF NOT EXISTS idx_native_registrations_surface
54
- ON native_registrations(surface, attached_at);
55
-
56
- -- 4. Metric seeds (39 -> 45).
57
- INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
58
- ('hook_fires_total', 0),
59
- ('hook_errors_total', 0),
60
- ('hooks_dead_total', 0),
61
- ('injections_suppressed_dead_hook', 0),
62
- ('native_registrations_total', 0),
63
- ('native_registration_failures', 0);
64
-
65
- -- 5. Setting seeds (23 -> 27).
66
- -- hook_liveness_enabled defaults ON: it is a read-only instrument
67
- -- on the success path, and an instrument nobody switches on is an
68
- -- instrument nobody has.
69
- -- native_registration_enabled defaults OFF: it changes where a
70
- -- curated skill comes from, and that is a change a user opts into.
71
- INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
72
- ('hook_liveness_enabled', '1'),
73
- ('native_registration_enabled', '0'),
74
- ('host_probe_history_enabled', '0'),
75
- ('dead_hook_report_threshold', '3');
76
-
77
- -- 6. Version marker.
78
- INSERT OR IGNORE INTO schema_version (version) VALUES ('010');
@@ -1,69 +0,0 @@
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');
@@ -1,24 +0,0 @@
1
- -- millisecond companions for the two tables whose ORDERING decides outcomes.
2
- ALTER TABLE tool_calls ADD COLUMN ts_ms INTEGER;
3
- ALTER TABLE kevin_injections ADD COLUMN injected_at_ms INTEGER;
4
-
5
- -- conservative backfill: seconds -> ms (x1000). Rows keep their original
6
- -- second-granularity value in the old column; nothing is rewritten there.
7
- UPDATE tool_calls SET ts_ms =
8
- CAST(strftime('%s', ts) AS INTEGER) * 1000
9
- WHERE ts_ms IS NULL AND ts IS NOT NULL;
10
-
11
- UPDATE kevin_injections SET injected_at_ms =
12
- CAST(strftime('%s', injected_at) AS INTEGER) * 1000
13
- WHERE injected_at_ms IS NULL AND injected_at IS NOT NULL;
14
-
15
- CREATE INDEX idx_tool_calls_ts_ms ON tool_calls(ts_ms);
16
- CREATE INDEX idx_injections_injected_ms ON kevin_injections(injected_at_ms);
17
-
18
- -- metric seeds (C-05 additions carry since=1.1.0 in contract)
19
- INSERT INTO kevin_metrics (key, value, updated_at) VALUES
20
- ('bench_regression_failures', 0, datetime('now')),
21
- ('forget_requests_total', 0, datetime('now')),
22
- ('forget_tombstones_published', 0, datetime('now'));
23
-
24
- INSERT INTO schema_version (version) VALUES ('012');
@@ -1,42 +0,0 @@
1
- import type { MemoryService } from "./MemoryService.js";
2
- import type { Store } from "./Store.js";
3
- import type { Metrics } from "./metrics.js";
4
- /**
5
- * v0.5.0 Archiver (K5-012 / plan §5.4, D5-05).
6
- *
7
- * Lifecycle tail of the memory loop: reflectors mark a lesson `stale` when
8
- * its fingerprint recurred enough times (penalizeRecurringReflectors,
9
- * migration 003), but nothing ever retires those rows — they keep
10
- * circulating through status filters and inflating counts. The Archiver
11
- * runs on `session.idle` and retires stale memories whose last activity
12
- * (`updated_at`, falling back to `last_verified_at`) is older than
13
- * `archive_after_days` (kevin_settings, seeded '30' in migration 006).
14
- *
15
- * Rules:
16
- * - Only `status = 'stale'` rows with `archived_at IS NULL`.
17
- * - `type != 'pattern'`: a pattern is the FIXED form of a fingerprint
18
- * (K4-025); archiving it would break kevin_why and the recurrence
19
- * re-admission path (QualityGate rule 4).
20
- * - Clock: the injection clock (K5-008) — tests freeze it; production
21
- * defaults to `new Date()`. Never `Date.now()` inside.
22
- * - `memories_archived` is bumped by the batch size, once per run.
23
- * - Pre-006 DBs (no `archived_at`) degrade to a no-op.
24
- *
25
- * Archived rows remain queryable with `includeSuperseded: true`-style
26
- * reads (status filter is exclusive, not destructive).
27
- */
28
- export declare class Archiver {
29
- private readonly store;
30
- private readonly memoryService;
31
- private readonly metrics;
32
- private readonly now;
33
- constructor(store: Store, memoryService: MemoryService, metrics?: Metrics | null, now?: () => Date);
34
- /**
35
- * Archives every eligible stale memory. Returns the number of rows
36
- * retired. Idempotent: archived rows are excluded by `archived_at IS
37
- * NULL`, so a second run archives nothing.
38
- */
39
- run(): number;
40
- private archiveAfterDays;
41
- private hasArchivedColumn;
42
- }
@@ -1,83 +0,0 @@
1
- import { hasArchivedColumn } from "./columns.js";
2
- /**
3
- * v0.5.0 Archiver (K5-012 / plan §5.4, D5-05).
4
- *
5
- * Lifecycle tail of the memory loop: reflectors mark a lesson `stale` when
6
- * its fingerprint recurred enough times (penalizeRecurringReflectors,
7
- * migration 003), but nothing ever retires those rows — they keep
8
- * circulating through status filters and inflating counts. The Archiver
9
- * runs on `session.idle` and retires stale memories whose last activity
10
- * (`updated_at`, falling back to `last_verified_at`) is older than
11
- * `archive_after_days` (kevin_settings, seeded '30' in migration 006).
12
- *
13
- * Rules:
14
- * - Only `status = 'stale'` rows with `archived_at IS NULL`.
15
- * - `type != 'pattern'`: a pattern is the FIXED form of a fingerprint
16
- * (K4-025); archiving it would break kevin_why and the recurrence
17
- * re-admission path (QualityGate rule 4).
18
- * - Clock: the injection clock (K5-008) — tests freeze it; production
19
- * defaults to `new Date()`. Never `Date.now()` inside.
20
- * - `memories_archived` is bumped by the batch size, once per run.
21
- * - Pre-006 DBs (no `archived_at`) degrade to a no-op.
22
- *
23
- * Archived rows remain queryable with `includeSuperseded: true`-style
24
- * reads (status filter is exclusive, not destructive).
25
- */
26
- export class Archiver {
27
- store;
28
- memoryService;
29
- metrics;
30
- now;
31
- constructor(store, memoryService, metrics, now = () => new Date()) {
32
- this.store = store;
33
- this.memoryService = memoryService;
34
- this.metrics = metrics ?? null;
35
- this.now = now;
36
- }
37
- /**
38
- * Archives every eligible stale memory. Returns the number of rows
39
- * retired. Idempotent: archived rows are excluded by `archived_at IS
40
- * NULL`, so a second run archives nothing.
41
- */
42
- run() {
43
- if (!this.hasArchivedColumn())
44
- return 0;
45
- const days = this.archiveAfterDays();
46
- if (days <= 0)
47
- return 0;
48
- const now = this.now();
49
- const cutoff = formatSqliteUtc(new Date(now.getTime() - days * 86_400_000));
50
- this.store
51
- .prepare(`UPDATE memories
52
- SET status = 'archived', archived_at = ?, updated_at = ?
53
- WHERE status = 'stale'
54
- AND type != 'pattern'
55
- AND archived_at IS NULL
56
- AND updated_at < ?`)
57
- .run(formatSqliteUtc(now), formatSqliteUtc(now), cutoff);
58
- const changed = this.store.prepare("SELECT changes() AS n").get();
59
- const n = changed.n;
60
- if (n > 0)
61
- this.metrics?.incr("memories_archived", n);
62
- return n;
63
- }
64
- archiveAfterDays() {
65
- try {
66
- const raw = this.memoryService.getSetting("archive_after_days", "30");
67
- const n = Number(raw);
68
- return Number.isFinite(n) ? n : 30;
69
- }
70
- catch {
71
- return 30;
72
- }
73
- }
74
- hasArchivedColumn() {
75
- return hasArchivedColumn(this.store);
76
- }
77
- }
78
- /** 'YYYY-MM-DD HH:MM:SS' UTC — the format SQLite `datetime('now')` uses,
79
- * so lexicographic comparisons against `updated_at` are valid. */
80
- function formatSqliteUtc(d) {
81
- const pad = (n) => String(n).padStart(2, "0");
82
- return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
83
- }
@@ -1,93 +0,0 @@
1
- import type { Store } from "./Store.js";
2
- import type { Metrics } from "./metrics.js";
3
- export declare const MARKER_BEGIN = "<!-- kevin:begin \u2014 curated by opencode-kevin, safe to edit -->";
4
- export declare const MARKER_END = "<!-- kevin:end -->";
5
- export type WriteOutcome = "written" | "noop" | "refused";
6
- /**
7
- * v0.8.0 (K8-019 / D8-08) — the two write modes. `markers` is the
8
- * v0.6.0 behaviour, byte for byte: a splice between the two marker
9
- * comments, used for `AGENTS.md`, a file humans edit. `whole` replaces
10
- * the entire file and is used only for Kevin-owned paths such as
11
- * `.kevin/knowledge.okf` — a file humans do not hand-edit.
12
- */
13
- export type WriteMode = "markers" | "whole";
14
- export interface WriteRequest {
15
- readonly path: string;
16
- readonly mode: WriteMode;
17
- /** marker block body ("markers"), or whole-file content ("whole"). */
18
- readonly content: string;
19
- /**
20
- * Caller-side refusal reason (K8-020 / D6-03): when present, the plan
21
- * is refused — nothing is written, and the refusal is audited with
22
- * both hashes. The refusal conditions belong to the caller; the
23
- * writer only records them.
24
- */
25
- readonly refusal?: string;
26
- }
27
- export interface WritePlan {
28
- readonly path: string;
29
- readonly before: string;
30
- readonly after: string;
31
- readonly diff: string;
32
- readonly outcome: WriteOutcome;
33
- readonly reason?: string;
34
- readonly hashBefore: string;
35
- readonly hashAfter: string;
36
- }
37
- /**
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.
54
- */
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;
65
- /**
66
- * v0.6.0 (K6-005 / plan §5.1) — the single write path to disk (D6-01).
67
- *
68
- * `plan()` is pure: it reads the target file, locates the marker pair, splices
69
- * the body between the markers and returns a {@link WritePlan}. It performs no
70
- * writes — rule 1. `apply()` is implemented by K6-007; until then it is a stub
71
- * that throws, so no caller can accidentally write before the audit trail
72
- * exists.
73
- *
74
- * `projectId` is a constructor argument rather than a per-call argument so that
75
- * every audit row is attributed without the call site having to remember.
76
- */
77
- export declare class ArtifactWriter {
78
- private readonly store;
79
- private readonly projectId;
80
- private readonly metrics;
81
- constructor(store: Store, projectId: string, metrics?: Metrics | null);
82
- plan(path: string, body: string): WritePlan;
83
- plan(request: WriteRequest): WritePlan;
84
- /**
85
- * K8-019 (D8-08) — the single write funnel: every file Kevin writes
86
- * goes through this method, which is the ONLY call site of `apply()`
87
- * in the plugin (asserted by tests/unit/single_write_path.test.ts).
88
- */
89
- write(request: WriteRequest, proposalId?: string): WriteOutcome;
90
- apply(plan: WritePlan, proposalId?: string): WriteOutcome;
91
- private renameTemp;
92
- private audit;
93
- }