@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.
- package/dist/plugin/capabilities.d.ts +2 -0
- package/dist/plugin/capabilities.js +3 -0
- package/dist/plugin/index.d.ts +5 -4
- package/dist/plugin/index.js +401 -108
- package/dist/plugin/native.d.ts +2 -2
- package/dist/plugin/native.js +2 -2
- package/package.json +56 -63
- package/LICENSE +0 -21
- package/README.md +0 -576
- package/dist/migrations/001_initial.sql +0 -92
- package/dist/migrations/002_indexes.sql +0 -14
- package/dist/migrations/003_v02_signal.sql +0 -58
- package/dist/migrations/004_v03_knowledge.sql +0 -138
- package/dist/migrations/005_v04_signal.sql +0 -57
- package/dist/migrations/006_v05_glassbox.sql +0 -118
- package/dist/migrations/007_v06_pull.sql +0 -145
- package/dist/migrations/008_v07_truth.sql +0 -124
- package/dist/migrations/009_v08_team.sql +0 -100
- package/dist/migrations/010_v09_native.sql +0 -78
- package/dist/migrations/011_v10_proven.sql +0 -69
- package/dist/migrations/012_v11_drift.sql +0 -24
- package/dist/plugin/Archiver.d.ts +0 -42
- package/dist/plugin/Archiver.js +0 -83
- package/dist/plugin/ArtifactWriter.d.ts +0 -93
- package/dist/plugin/ArtifactWriter.js +0 -291
- package/dist/plugin/CausalChain.d.ts +0 -22
- package/dist/plugin/CausalChain.js +0 -197
- package/dist/plugin/ConflictDetector.d.ts +0 -35
- package/dist/plugin/ConflictDetector.js +0 -260
- package/dist/plugin/ContextInjector.d.ts +0 -222
- package/dist/plugin/ContextInjector.js +0 -533
- package/dist/plugin/ConventionMiner.d.ts +0 -35
- package/dist/plugin/ConventionMiner.js +0 -242
- package/dist/plugin/Curator.d.ts +0 -116
- package/dist/plugin/Curator.js +0 -324
- package/dist/plugin/Feedback.d.ts +0 -67
- package/dist/plugin/Feedback.js +0 -120
- package/dist/plugin/HookLiveness.d.ts +0 -88
- package/dist/plugin/HookLiveness.js +0 -322
- package/dist/plugin/InjectionLedger.d.ts +0 -98
- package/dist/plugin/InjectionLedger.js +0 -295
- package/dist/plugin/LessonFixer.d.ts +0 -44
- package/dist/plugin/LessonFixer.js +0 -45
- package/dist/plugin/Materializer.d.ts +0 -83
- package/dist/plugin/Materializer.js +0 -181
- package/dist/plugin/MemoryService.d.ts +0 -360
- package/dist/plugin/MemoryService.js +0 -1242
- package/dist/plugin/Migrate.d.ts +0 -17
- package/dist/plugin/Migrate.js +0 -271
- package/dist/plugin/PatternMiner.d.ts +0 -49
- package/dist/plugin/PatternMiner.js +0 -132
- package/dist/plugin/QualityGate.d.ts +0 -124
- package/dist/plugin/QualityGate.js +0 -119
- package/dist/plugin/Reflector.d.ts +0 -91
- package/dist/plugin/Reflector.js +0 -307
- package/dist/plugin/RepoIdentity.d.ts +0 -124
- package/dist/plugin/RepoIdentity.js +0 -300
- package/dist/plugin/RepoTruth.d.ts +0 -80
- package/dist/plugin/RepoTruth.js +0 -599
- package/dist/plugin/Retrospective.d.ts +0 -17
- package/dist/plugin/Retrospective.js +0 -213
- package/dist/plugin/SharedLayer.d.ts +0 -159
- package/dist/plugin/SharedLayer.js +0 -462
- package/dist/plugin/Store.d.ts +0 -29
- package/dist/plugin/Store.js +0 -50
- package/dist/plugin/ToolCallObserver.d.ts +0 -32
- package/dist/plugin/ToolCallObserver.js +0 -201
- package/dist/plugin/columns.d.ts +0 -11
- package/dist/plugin/columns.js +0 -54
- package/dist/plugin/confidence.d.ts +0 -8
- package/dist/plugin/confidence.js +0 -34
- package/dist/plugin/contract.d.ts +0 -54
- package/dist/plugin/contract.js +0 -383
- package/dist/plugin/diff.d.ts +0 -8
- package/dist/plugin/diff.js +0 -182
- package/dist/plugin/escape.d.ts +0 -39
- package/dist/plugin/escape.js +0 -60
- package/dist/plugin/fingerprint.d.ts +0 -27
- package/dist/plugin/fingerprint.js +0 -73
- package/dist/plugin/inferability.d.ts +0 -32
- package/dist/plugin/inferability.js +0 -88
- package/dist/plugin/kevin_approve.d.ts +0 -34
- package/dist/plugin/kevin_approve.js +0 -52
- package/dist/plugin/kevin_audit.d.ts +0 -213
- package/dist/plugin/kevin_audit.js +0 -563
- package/dist/plugin/kevin_bench.d.ts +0 -10
- package/dist/plugin/kevin_bench.js +0 -82
- package/dist/plugin/kevin_conflicts.d.ts +0 -9
- package/dist/plugin/kevin_conflicts.js +0 -50
- package/dist/plugin/kevin_contract.d.ts +0 -8
- package/dist/plugin/kevin_contract.js +0 -59
- package/dist/plugin/kevin_doctor.d.ts +0 -62
- package/dist/plugin/kevin_doctor.js +0 -196
- package/dist/plugin/kevin_facts.d.ts +0 -42
- package/dist/plugin/kevin_facts.js +0 -36
- package/dist/plugin/kevin_forget.d.ts +0 -33
- package/dist/plugin/kevin_forget.js +0 -260
- package/dist/plugin/kevin_native.d.ts +0 -29
- package/dist/plugin/kevin_native.js +0 -79
- package/dist/plugin/kevin_propose.d.ts +0 -23
- package/dist/plugin/kevin_propose.js +0 -14
- package/dist/plugin/kevin_publish.d.ts +0 -38
- package/dist/plugin/kevin_publish.js +0 -18
- package/dist/plugin/kevin_why.d.ts +0 -23
- package/dist/plugin/kevin_why.js +0 -111
- package/dist/plugin/memory-format.d.ts +0 -20
- package/dist/plugin/memory-format.js +0 -59
- package/dist/plugin/metrics.d.ts +0 -97
- package/dist/plugin/metrics.js +0 -284
- package/dist/plugin/okf-export.d.ts +0 -3
- package/dist/plugin/okf-export.js +0 -134
- package/dist/plugin/okf-import.d.ts +0 -76
- package/dist/plugin/okf-import.js +0 -271
- package/dist/plugin/okf.d.ts +0 -107
- package/dist/plugin/okf.js +0 -303
- package/dist/plugin/perf.d.ts +0 -35
- package/dist/plugin/perf.js +0 -174
- package/dist/plugin/query-tokenizer.d.ts +0 -13
- package/dist/plugin/query-tokenizer.js +0 -133
- package/dist/plugin/redact.d.ts +0 -2
- package/dist/plugin/redact.js +0 -18
- package/dist/plugin/replay-types.d.ts +0 -69
- package/dist/plugin/replay-types.js +0 -156
- package/dist/plugin/replay.d.ts +0 -36
- package/dist/plugin/replay.js +0 -202
- package/dist/plugin/sqlite-adapter.d.ts +0 -12
- package/dist/plugin/sqlite-adapter.js +0 -81
- package/dist/plugin/time-ms.d.ts +0 -1
- package/dist/plugin/time-ms.js +0 -16
- package/dist/plugin/uuid.d.ts +0 -1
- package/dist/plugin/uuid.js +0 -50
package/dist/plugin/index.js
CHANGED
|
@@ -1,50 +1,56 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { homedir } from "node:os";
|
|
3
4
|
import { dirname, join } from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { tool } from "@opencode-ai/plugin";
|
|
6
|
-
import { Archiver } from "
|
|
7
|
-
import { ArtifactWriter } from "
|
|
8
|
-
import { CausalChain } from "
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
23
|
-
import
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
7
|
+
import { Archiver } from "@jmtrin/kevin-core";
|
|
8
|
+
import { ArtifactWriter } from "@jmtrin/kevin-core";
|
|
9
|
+
import { CausalChain } from "@jmtrin/kevin-core";
|
|
10
|
+
import { handleBridgeCommand } from "@jmtrin/kevin-core";
|
|
11
|
+
import { ConflictDetector } from "@jmtrin/kevin-core";
|
|
12
|
+
import { ContextInjector } from "@jmtrin/kevin-core";
|
|
13
|
+
import { ConventionMiner } from "@jmtrin/kevin-core";
|
|
14
|
+
import { Curator } from "@jmtrin/kevin-core";
|
|
15
|
+
import { writeDashboard } from "@jmtrin/kevin-core";
|
|
16
|
+
import { Feedback } from "@jmtrin/kevin-core";
|
|
17
|
+
import { HookLiveness } from "@jmtrin/kevin-core";
|
|
18
|
+
import { InjectionLedger } from "@jmtrin/kevin-core";
|
|
19
|
+
import { Materializer, SKILL_TOPIC } from "@jmtrin/kevin-core";
|
|
20
|
+
import { MemoryService, hasRepoIdColumn, } from "@jmtrin/kevin-core";
|
|
21
|
+
import { Migrate } from "@jmtrin/kevin-core";
|
|
22
|
+
import { PatternMiner } from "@jmtrin/kevin-core";
|
|
23
|
+
import { ERROR_LINE_RE, Reflector, STRONG_ERROR_RE } from "@jmtrin/kevin-core";
|
|
24
|
+
import * as RepoIdentity from "@jmtrin/kevin-core";
|
|
25
|
+
import { RepoTruth } from "@jmtrin/kevin-core";
|
|
26
|
+
import { Retrospective } from "@jmtrin/kevin-core";
|
|
27
|
+
import { SharedLayer } from "@jmtrin/kevin-core";
|
|
28
|
+
import { Store } from "@jmtrin/kevin-core";
|
|
29
|
+
import { ToolCallObserver } from "@jmtrin/kevin-core";
|
|
30
|
+
import { deleteMailbox, processActions, readMailbox, writeResults, } from "@jmtrin/kevin-core";
|
|
31
|
+
import { flushSnapshots } from "@jmtrin/kevin-core";
|
|
27
32
|
import { probe } from "./capabilities.js";
|
|
28
|
-
import { computeConfidence } from "
|
|
33
|
+
import { computeConfidence, exportMigrationsDir, composeIdlePipeline, KEVIN_VERSION } from "@jmtrin/kevin-core";
|
|
34
|
+
import { contractDigest, describeContract } from "@jmtrin/kevin-core";
|
|
29
35
|
import { probeHost, summarize } from "./host.js";
|
|
30
|
-
import { kevinApprove } from "
|
|
31
|
-
import { buildAudit } from "
|
|
32
|
-
import { buildKevinBench } from "
|
|
33
|
-
import { executeKevinConflicts } from "
|
|
34
|
-
import { buildKevinContract } from "
|
|
35
|
-
import { buildDoctor } from "
|
|
36
|
-
import { buildKevinFacts } from "
|
|
37
|
-
import { handleForget } from "
|
|
38
|
-
import { handleNative } from "
|
|
39
|
-
import { kevinPropose } from "
|
|
40
|
-
import { kevinPublish } from "
|
|
41
|
-
import { kevinWhy } from "
|
|
42
|
-
import { Metrics } from "
|
|
36
|
+
import { kevinApprove } from "@jmtrin/kevin-core";
|
|
37
|
+
import { buildAudit } from "@jmtrin/kevin-core";
|
|
38
|
+
import { buildKevinBench } from "@jmtrin/kevin-core";
|
|
39
|
+
import { executeKevinConflicts } from "@jmtrin/kevin-core";
|
|
40
|
+
import { buildKevinContract } from "@jmtrin/kevin-core";
|
|
41
|
+
import { buildDoctor } from "@jmtrin/kevin-core";
|
|
42
|
+
import { buildKevinFacts } from "@jmtrin/kevin-core";
|
|
43
|
+
import { handleForget } from "@jmtrin/kevin-core";
|
|
44
|
+
import { handleNative } from "@jmtrin/kevin-core";
|
|
45
|
+
import { kevinPropose } from "@jmtrin/kevin-core";
|
|
46
|
+
import { kevinPublish } from "@jmtrin/kevin-core";
|
|
47
|
+
import { kevinWhy } from "@jmtrin/kevin-core";
|
|
48
|
+
import { Metrics } from "@jmtrin/kevin-core";
|
|
43
49
|
import { attachNative } from "./native.js";
|
|
44
|
-
import { exportMarkdown, exportOkf } from "
|
|
45
|
-
import { importOkf } from "
|
|
46
|
-
import { Perf } from "
|
|
47
|
-
import { uuidv7 } from "
|
|
50
|
+
import { exportMarkdown, exportOkf } from "@jmtrin/kevin-core";
|
|
51
|
+
import { importOkf } from "@jmtrin/kevin-core";
|
|
52
|
+
import { Perf } from "@jmtrin/kevin-core";
|
|
53
|
+
import { uuidv7 } from "@jmtrin/kevin-core";
|
|
48
54
|
// v0.6.0 (K6-019) — process-global set of reference topics already
|
|
49
55
|
// registered with the host this process. Keeps re-registration idempotent
|
|
50
56
|
// across sessions within one process: a topic registered once is never
|
|
@@ -112,6 +118,11 @@ export const KEVIN_CONFIG_KEYS = [
|
|
|
112
118
|
"perf_ring_capacity",
|
|
113
119
|
"perf_flush_on_idle",
|
|
114
120
|
"contract_report_enabled",
|
|
121
|
+
// v1.2.0 (K12-001 / plan §4) — the single setting seeded by runtime
|
|
122
|
+
// (no migration this release). Omitting makes `kevin_config set`
|
|
123
|
+
// return { error: "unknown_key" } while `kevin_config list` still
|
|
124
|
+
// shows it.
|
|
125
|
+
"tui_snapshots_enabled",
|
|
115
126
|
];
|
|
116
127
|
// v1.1.0 (K11-007 / D11-08) — no new settings in 1.1.0; thresholds are constants (D11-03)
|
|
117
128
|
// v0.7.0 (K7-003 / plan §5.6, D7-12) — the explicit VALUE domain for
|
|
@@ -121,11 +132,41 @@ export const KEVIN_CONFIG_KEYS = [
|
|
|
121
132
|
// at the surface rather than silently changing every installation's
|
|
122
133
|
// behaviour on the next reflection.
|
|
123
134
|
export const ERROR_LESSON_MODE_VALUES = ["all", "triage_only"];
|
|
124
|
-
/** Plugin release version —
|
|
125
|
-
export
|
|
135
|
+
/** Plugin release version — single source is @jmtrin/kevin-core (B-003 drift fix). */
|
|
136
|
+
export { KEVIN_VERSION };
|
|
126
137
|
function resolveMigrationsDir() {
|
|
138
|
+
// K13-008 (D13-04): migrations now owned by @jmtrin/kevin-core.
|
|
139
|
+
// Explicit option is handled at call site (opts.migrationsDir ?? resolve...),
|
|
140
|
+
// so this helper only resolves the core location.
|
|
141
|
+
// 1. Installed layout: walk-up via require.resolve (reuses host.ts strategy)
|
|
142
|
+
try {
|
|
143
|
+
const pkgJson = createRequire(import.meta.url).resolve("@jmtrin/kevin-core/package.json");
|
|
144
|
+
const cand = join(dirname(pkgJson), "dist", "migrations");
|
|
145
|
+
if (existsSync(cand))
|
|
146
|
+
return cand;
|
|
147
|
+
}
|
|
148
|
+
catch { }
|
|
149
|
+
// 2. Workspace sources: delegate to core's own resolver (handles both
|
|
150
|
+
// src via tsx and dist after build).
|
|
151
|
+
try {
|
|
152
|
+
const dir = exportMigrationsDir();
|
|
153
|
+
if (existsSync(dir))
|
|
154
|
+
return dir;
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
157
|
+
// 3. Monorepo fallback probes (dev without build)
|
|
127
158
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
128
|
-
|
|
159
|
+
const candidates = [
|
|
160
|
+
join(here, "..", "..", "core", "dist", "migrations"),
|
|
161
|
+
join(here, "..", "..", "core", "migrations"),
|
|
162
|
+
join(here, "..", "migrations"),
|
|
163
|
+
join(here, "..", "..", "migrations"),
|
|
164
|
+
];
|
|
165
|
+
for (const c of candidates) {
|
|
166
|
+
if (existsSync(c))
|
|
167
|
+
return c;
|
|
168
|
+
}
|
|
169
|
+
return join(here, "..", "..", "core", "migrations");
|
|
129
170
|
}
|
|
130
171
|
const REKEY_TABLES = ["memories", "shared_entries", "okf_imports"];
|
|
131
172
|
export function performRekey(store, toRepoId, opts) {
|
|
@@ -258,6 +299,17 @@ export const KevinPlugin = async (input, options) => {
|
|
|
258
299
|
const store = new Store({ path: dbPath });
|
|
259
300
|
const migrationsDir = opts.migrationsDir ?? resolveMigrationsDir();
|
|
260
301
|
await new Migrate(store, migrationsDir).run();
|
|
302
|
+
// v1.2.0 (K12-001 / D12-??) — no migration this release: ensure the
|
|
303
|
+
// new setting exists with its default so `kevin_config list` shows it
|
|
304
|
+
// on a database that was already at 012.
|
|
305
|
+
try {
|
|
306
|
+
store
|
|
307
|
+
.prepare("INSERT OR IGNORE INTO kevin_settings (key, value) VALUES (?, ?)")
|
|
308
|
+
.run("tui_snapshots_enabled", "1");
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
// pre-003 DB without kevin_settings — nothing to seed
|
|
312
|
+
}
|
|
261
313
|
const metrics = new Metrics(store);
|
|
262
314
|
// v0.4.0 (K4-019): the plugin hooks expose no project field, so the
|
|
263
315
|
// project id is derived once from the plugin host's working directory
|
|
@@ -316,6 +368,8 @@ export const KevinPlugin = async (input, options) => {
|
|
|
316
368
|
// v0.7.0 (K7-009 / plan §5.1, D7-13) — the repository truth scanner reads
|
|
317
369
|
// the JSON project files. Runs once at init, gated by repo_truth_enabled.
|
|
318
370
|
const projectRoot = opts.projectRoot ?? process.cwd();
|
|
371
|
+
const materializerRoot = opts.materializerRoot ?? join(homedir(), ".opencode-kevin");
|
|
372
|
+
const kevinEnv = { projectRoot, dataRoot: materializerRoot };
|
|
319
373
|
const repoTruth = new RepoTruth(store, projectId, projectRoot, metrics);
|
|
320
374
|
if (memoryService.getSetting("repo_truth_enabled", "0") === "1") {
|
|
321
375
|
try {
|
|
@@ -355,7 +409,7 @@ export const KevinPlugin = async (input, options) => {
|
|
|
355
409
|
const archiver = new Archiver(store, memoryService, metrics);
|
|
356
410
|
const retrospective = new Retrospective(store, memoryService, {
|
|
357
411
|
dir: opts.retrospectivesDir,
|
|
358
|
-
}, metrics);
|
|
412
|
+
}, metrics, kevinEnv);
|
|
359
413
|
const patternMiner = new PatternMiner(store, memoryService, metrics);
|
|
360
414
|
const conventionMiner = new ConventionMiner(store, memoryService, projectId, metrics);
|
|
361
415
|
const conflictDetector = new ConflictDetector(store, projectId, metrics, repoTruth, memoryService);
|
|
@@ -387,17 +441,20 @@ export const KevinPlugin = async (input, options) => {
|
|
|
387
441
|
metrics.incr("shared_entries_imported", report.imported);
|
|
388
442
|
return report;
|
|
389
443
|
}
|
|
390
|
-
let curator = new Curator(store, memoryService, projectId, metrics, repoId);
|
|
444
|
+
let curator = new Curator(store, memoryService, projectId, metrics, repoId, kevinEnv);
|
|
391
445
|
// v0.6.0 (K6-017/018 / plan §5.6-5.7, D6-13) — pull-channel bundles and
|
|
392
446
|
// the v2 domain probe. `probe()` runs ONCE at init and the result is
|
|
393
447
|
// held; probing per-event is a hot-path cost for a value that cannot
|
|
394
448
|
// change within a process (K6-016). The Materializer writes next to its
|
|
395
449
|
// targets, so the bundle directories are created here at init.
|
|
396
|
-
const materializerRoot = opts.materializerRoot ?? join(homedir(), ".opencode-kevin");
|
|
397
450
|
mkdirSync(join(materializerRoot, "skills"), { recursive: true });
|
|
398
451
|
mkdirSync(join(materializerRoot, "refs"), { recursive: true });
|
|
399
452
|
const capabilities = probe(input);
|
|
400
|
-
|
|
453
|
+
// v1.2.0 (K12-012 / D12-03) — permission.ask probe (best-effort, additive).
|
|
454
|
+
// When the host exposes permission.ask, the presence is noted; absence is silent no-op.
|
|
455
|
+
// No new setting — bounded to tui_snapshots_enabled host-support check per spec.
|
|
456
|
+
void capabilities.permissionAsk;
|
|
457
|
+
const materializer = new Materializer(store, { root: materializerRoot }, kevinEnv);
|
|
401
458
|
// v0.9.0 (K9-016 / plan §5.4, D9-10) — native registration replaces
|
|
402
459
|
// file emission. When attachNative returns a registration for a
|
|
403
460
|
// surface, the corresponding *_emission_enabled path is skipped for
|
|
@@ -977,7 +1034,7 @@ export const KevinPlugin = async (input, options) => {
|
|
|
977
1034
|
version: KEVIN_VERSION,
|
|
978
1035
|
writer,
|
|
979
1036
|
});
|
|
980
|
-
curator = new Curator(store, memoryService, projectId, metrics, live.repoId);
|
|
1037
|
+
curator = new Curator(store, memoryService, projectId, metrics, live.repoId, kevinEnv);
|
|
981
1038
|
// v0.8.0 (BUG-003) — heal the OKF file header.
|
|
982
1039
|
// Rekey changes the scope the file is written
|
|
983
1040
|
// under; a stale `#repo` first line would make
|
|
@@ -1052,7 +1109,7 @@ export const KevinPlugin = async (input, options) => {
|
|
|
1052
1109
|
// block; on a pre-008 DB the block is omitted, partial:true.
|
|
1053
1110
|
// v0.8.0 (K8-007 / plan §5.7) — the penalized-memory rollup
|
|
1054
1111
|
// scopes on repoId once the 009 column exists.
|
|
1055
|
-
const report = buildAudit(store, metrics, capabilities, projectId, sessionIdentity.repoId);
|
|
1112
|
+
const report = buildAudit(store, metrics, capabilities, projectId, sessionIdentity.repoId, undefined, kevinEnv);
|
|
1056
1113
|
const payload = args.verbose
|
|
1057
1114
|
? report
|
|
1058
1115
|
: { ...report, settings: undefined };
|
|
@@ -1071,7 +1128,7 @@ export const KevinPlugin = async (input, options) => {
|
|
|
1071
1128
|
description: "Doctor de Kevin (v0.9.0): salud del host, hooks, dependencias y registros nativos. Solo lectura, sin LLM ni writes, invocable en cualquier momento. Devuelve host (version, flavour, shell, v2), hooks ordenados dead primero, dependencies (zod_copies), native (enabled, registered/verified por surface), verdict (healthy|degraded|unknown) y reason. Output pensado para pegar en un issue: sin filesystem paths ni session ids.",
|
|
1072
1129
|
args: {},
|
|
1073
1130
|
async execute() {
|
|
1074
|
-
const report = buildDoctor(store, host, memoryService);
|
|
1131
|
+
const report = buildDoctor(store, host, memoryService, {}, kevinEnv);
|
|
1075
1132
|
return {
|
|
1076
1133
|
title: "Doctor de Kevin",
|
|
1077
1134
|
output: JSON.stringify(report),
|
|
@@ -1140,7 +1197,7 @@ export const KevinPlugin = async (input, options) => {
|
|
|
1140
1197
|
.describe("status | last"),
|
|
1141
1198
|
},
|
|
1142
1199
|
async execute(args) {
|
|
1143
|
-
const result = buildKevinBench({ store }, args);
|
|
1200
|
+
const result = buildKevinBench({ store }, args, kevinEnv);
|
|
1144
1201
|
return {
|
|
1145
1202
|
title: "Benchmark de Kevin",
|
|
1146
1203
|
output: JSON.stringify(result),
|
|
@@ -1675,15 +1732,80 @@ export const KevinPlugin = async (input, options) => {
|
|
|
1675
1732
|
});
|
|
1676
1733
|
},
|
|
1677
1734
|
"chat.message": async (hookInput, output) => {
|
|
1735
|
+
const rawText = output.parts
|
|
1736
|
+
.map((p) => p)
|
|
1737
|
+
.filter((p) => p.type === "text")
|
|
1738
|
+
.map((p) => p.text ?? "")
|
|
1739
|
+
.join(" ");
|
|
1740
|
+
const trimmed = rawText.trim();
|
|
1741
|
+
// v1.2.0 (K12-018 / D12-09) — chat-command bridge BEFORE deriveQuery.
|
|
1742
|
+
// Valid commands are SWALLOWED (parts cleared) and never reach the model.
|
|
1743
|
+
// Must run outside perf.measure so early return actually exits the hook.
|
|
1744
|
+
if (trimmed.length > 0) {
|
|
1745
|
+
try {
|
|
1746
|
+
const bridgeDeps = {
|
|
1747
|
+
getPending: () => {
|
|
1748
|
+
try {
|
|
1749
|
+
const maybe = curator.pending;
|
|
1750
|
+
if (typeof maybe === "function") {
|
|
1751
|
+
const rows = maybe.call(curator);
|
|
1752
|
+
return rows.map((p) => ({
|
|
1753
|
+
id: p.id,
|
|
1754
|
+
proposedText: String(p.proposed_text ?? p.proposedText ?? ""),
|
|
1755
|
+
}));
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
catch { }
|
|
1759
|
+
try {
|
|
1760
|
+
const rows = store
|
|
1761
|
+
.prepare("SELECT id, proposed_text FROM curation_proposals WHERE status = 'pending'")
|
|
1762
|
+
.all();
|
|
1763
|
+
return rows.map((r) => ({
|
|
1764
|
+
id: r.id,
|
|
1765
|
+
proposedText: r.proposed_text,
|
|
1766
|
+
}));
|
|
1767
|
+
}
|
|
1768
|
+
catch {
|
|
1769
|
+
return [];
|
|
1770
|
+
}
|
|
1771
|
+
},
|
|
1772
|
+
approve: (id) => kevinApprove(store, memoryService, curator, writer, metrics, {
|
|
1773
|
+
proposalId: id,
|
|
1774
|
+
decision: "approve",
|
|
1775
|
+
}),
|
|
1776
|
+
reject: (id, note) => kevinApprove(store, memoryService, curator, writer, metrics, {
|
|
1777
|
+
proposalId: id,
|
|
1778
|
+
decision: "reject",
|
|
1779
|
+
}) && void note,
|
|
1780
|
+
acknowledge: (conflictId) => {
|
|
1781
|
+
try {
|
|
1782
|
+
const cd = conflictDetector;
|
|
1783
|
+
if (typeof cd.acknowledge === "function")
|
|
1784
|
+
cd.acknowledge(conflictId);
|
|
1785
|
+
else if (typeof cd.resolve === "function")
|
|
1786
|
+
cd.resolve(conflictId, "a");
|
|
1787
|
+
}
|
|
1788
|
+
catch { }
|
|
1789
|
+
},
|
|
1790
|
+
metrics,
|
|
1791
|
+
};
|
|
1792
|
+
const br = handleBridgeCommand(trimmed, bridgeDeps);
|
|
1793
|
+
if (br.handled) {
|
|
1794
|
+
try {
|
|
1795
|
+
output.parts = [];
|
|
1796
|
+
}
|
|
1797
|
+
catch { }
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
catch {
|
|
1802
|
+
// best-effort — bridge failure must not break chat flow
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1678
1805
|
perf.measure("chat.message", () => {
|
|
1679
|
-
|
|
1680
|
-
.map((p) => p)
|
|
1681
|
-
.filter((p) => p.type === "text")
|
|
1682
|
-
.map((p) => p.text ?? "")
|
|
1683
|
-
.join(" ");
|
|
1684
|
-
if (text.trim()) {
|
|
1806
|
+
if (trimmed.length > 0) {
|
|
1685
1807
|
const derived = injector.deriveQuery([
|
|
1686
|
-
{ role: "user", content:
|
|
1808
|
+
{ role: "user", content: rawText },
|
|
1687
1809
|
]);
|
|
1688
1810
|
lastUserQuery = derived.length > 0 ? derived : null;
|
|
1689
1811
|
if (hookInput.sessionID && lastUserQuery) {
|
|
@@ -1753,62 +1875,94 @@ export const KevinPlugin = async (input, options) => {
|
|
|
1753
1875
|
// BUG-011 — the session is done: drop the global query
|
|
1754
1876
|
// so the next session cannot reuse it.
|
|
1755
1877
|
lastUserQuery = null;
|
|
1756
|
-
//
|
|
1757
|
-
//
|
|
1758
|
-
//
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
// A throwing miner must not reject or truncate the idle chain.
|
|
1793
|
-
}
|
|
1794
|
-
// v0.7.0 (K7-016 / plan §5.5, D7-06) — conflict detection is
|
|
1795
|
-
// surfacing-only on idle. It may create/open conflict rows, but
|
|
1796
|
-
// no idle path can acknowledge or resolve one.
|
|
1878
|
+
// K13-010 (D13-07) — single ORDER via composeIdlePipeline.
|
|
1879
|
+
// Ledger/archiver/reflectors/pattern are the hand-synced core;
|
|
1880
|
+
// adapter and replay both consume IDLE_STEP_ORDER so re-order is visible in both.
|
|
1881
|
+
await composeIdlePipeline({
|
|
1882
|
+
"ledger.settle": () => { ledger.settle(sid); },
|
|
1883
|
+
"archiver.run": () => { archiver.run(); },
|
|
1884
|
+
retrospective: () => {
|
|
1885
|
+
fireAndForget(retrospective.generate(sid));
|
|
1886
|
+
},
|
|
1887
|
+
"reflectors.boost": () => { memoryService.boostPositiveReflectors(sid); },
|
|
1888
|
+
"reflectors.penalize": () => {
|
|
1889
|
+
const recurred = memoryService.penalizeRecurringReflectors(sid);
|
|
1890
|
+
injector.setRecurrences(recurred, sid);
|
|
1891
|
+
},
|
|
1892
|
+
"patternMiner.mine": () => { patternMiner.mine(projectId); },
|
|
1893
|
+
});
|
|
1894
|
+
// v0.7.0 (K7-012/K7-016) + causalChain — folded into the single ORDER via composeIdlePipeline (K13-010)
|
|
1895
|
+
await composeIdlePipeline({
|
|
1896
|
+
"conventionMiner.mine": () => {
|
|
1897
|
+
if (memoryService.getSetting("convention_mining_enabled", "0") === "1") {
|
|
1898
|
+
const conventions = conventionMiner.mine();
|
|
1899
|
+
conventionMiner.emit(conventions);
|
|
1900
|
+
}
|
|
1901
|
+
},
|
|
1902
|
+
"conflictDetector.detect": () => {
|
|
1903
|
+
if (memoryService.getSetting("conflict_detection_enabled", "0") === "1") {
|
|
1904
|
+
conflictDetector.detect();
|
|
1905
|
+
}
|
|
1906
|
+
},
|
|
1907
|
+
"causalChain.onSessionIdle": () => {
|
|
1908
|
+
fireAndForget(Promise.resolve().then(() => causalChain.onSessionIdle(sid)).catch(() => { }));
|
|
1909
|
+
},
|
|
1910
|
+
});
|
|
1911
|
+
// v1.2.0 (K12-007/K12-011 / D12-05) — TUI mailbox: actions→curate ordering.
|
|
1912
|
+
// Process mailbox BEFORE curator.propose so a fresh proposal created this idle
|
|
1913
|
+
// is NOT visible to a stale token (D12-04). Best-effort, never breaks idle.
|
|
1797
1914
|
try {
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1915
|
+
const mb = readMailbox(materializerRoot);
|
|
1916
|
+
if (mb.actions.length) {
|
|
1917
|
+
const tuiDeps = {
|
|
1918
|
+
getPending: () => {
|
|
1919
|
+
try {
|
|
1920
|
+
const maybe = curator.pending;
|
|
1921
|
+
if (typeof maybe === "function") {
|
|
1922
|
+
const rows = maybe.call(curator);
|
|
1923
|
+
return rows.map((p) => ({
|
|
1924
|
+
id: p.id,
|
|
1925
|
+
proposedText: String(p.proposed_text ?? p.proposedText ?? ""),
|
|
1926
|
+
}));
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
catch { }
|
|
1930
|
+
try {
|
|
1931
|
+
const rows = store
|
|
1932
|
+
.prepare("SELECT id, proposed_text FROM curation_proposals WHERE status = 'pending'")
|
|
1933
|
+
.all();
|
|
1934
|
+
return rows.map((r) => ({
|
|
1935
|
+
id: r.id,
|
|
1936
|
+
proposedText: r.proposed_text,
|
|
1937
|
+
}));
|
|
1938
|
+
}
|
|
1939
|
+
catch {
|
|
1940
|
+
return [];
|
|
1941
|
+
}
|
|
1942
|
+
},
|
|
1943
|
+
approve: (id) => kevinApprove(store, memoryService, curator, writer, metrics, {
|
|
1944
|
+
proposalId: id,
|
|
1945
|
+
decision: "approve",
|
|
1946
|
+
}),
|
|
1947
|
+
reject: (id, _note) => kevinApprove(store, memoryService, curator, writer, metrics, {
|
|
1948
|
+
proposalId: id,
|
|
1949
|
+
decision: "reject",
|
|
1950
|
+
}),
|
|
1951
|
+
acknowledge: (conflictId) => {
|
|
1952
|
+
const cd = conflictDetector;
|
|
1953
|
+
if (typeof cd.acknowledge === "function")
|
|
1954
|
+
cd.acknowledge(conflictId);
|
|
1955
|
+
},
|
|
1956
|
+
metrics,
|
|
1957
|
+
};
|
|
1958
|
+
const tuiResults = processActions(mb.actions, tuiDeps);
|
|
1959
|
+
writeResults(materializerRoot, tuiResults);
|
|
1960
|
+
deleteMailbox(materializerRoot);
|
|
1801
1961
|
}
|
|
1802
1962
|
}
|
|
1803
1963
|
catch {
|
|
1804
|
-
//
|
|
1964
|
+
// best-effort
|
|
1805
1965
|
}
|
|
1806
|
-
fireAndForget(Promise.resolve()
|
|
1807
|
-
.then(() => causalChain.onSessionIdle(sid))
|
|
1808
|
-
// non-blocking — promote is a best-effort pass
|
|
1809
|
-
// (legacy DBs pre-005 lack the recurrence_count
|
|
1810
|
-
// column)
|
|
1811
|
-
.catch(() => { }));
|
|
1812
1966
|
// v0.6.0 (K6-015 / plan §8.14) — session-idle curation
|
|
1813
1967
|
// generation. Dry-run only: `propose()` calls `plan()`
|
|
1814
1968
|
// and never `apply()`, so nothing here can touch disk
|
|
@@ -1850,6 +2004,145 @@ export const KevinPlugin = async (input, options) => {
|
|
|
1850
2004
|
// best-effort, same pattern as ledger.settle — a
|
|
1851
2005
|
// sync failure must not break the idle path
|
|
1852
2006
|
}
|
|
2007
|
+
// v1.2.0 (K12-003/K12-011/K12-017 / D12-05) — snapshot flush gated by tui_snapshots_enabled.
|
|
2008
|
+
// Order: actions→curate→syncSharedLayer→snapshots (D12-05) — snapshots reflect post-action truth.
|
|
2009
|
+
try {
|
|
2010
|
+
if (memoryService.getSetting("tui_snapshots_enabled", "1") === "1") {
|
|
2011
|
+
// Proposals
|
|
2012
|
+
let proposals = [];
|
|
2013
|
+
try {
|
|
2014
|
+
const rawPending = (() => {
|
|
2015
|
+
try {
|
|
2016
|
+
const maybe = curator.pending;
|
|
2017
|
+
if (typeof maybe === "function")
|
|
2018
|
+
return maybe.call(curator);
|
|
2019
|
+
}
|
|
2020
|
+
catch { }
|
|
2021
|
+
return store
|
|
2022
|
+
.prepare("SELECT id, kind, target_path, proposed_text, diff, memory_id, created_at FROM curation_proposals WHERE status = 'pending' ORDER BY created_at")
|
|
2023
|
+
.all();
|
|
2024
|
+
})();
|
|
2025
|
+
const { proposalToken } = await import("@jmtrin/kevin-core");
|
|
2026
|
+
proposals = rawPending.map((r) => {
|
|
2027
|
+
const row = r;
|
|
2028
|
+
const id = String(row.id ?? "");
|
|
2029
|
+
const proposed = String(row.proposed_text ?? row.proposedText ?? "");
|
|
2030
|
+
return {
|
|
2031
|
+
id,
|
|
2032
|
+
kind: String(row.kind ?? "agents_md"),
|
|
2033
|
+
target_path: String(row.target_path ?? row.targetPath ?? "AGENTS.md"),
|
|
2034
|
+
diff: String(row.diff ?? ""),
|
|
2035
|
+
memory_ids: String(row.memory_id ?? row.memoryIds ?? "")
|
|
2036
|
+
.split(",")
|
|
2037
|
+
.filter((s) => s.length > 0),
|
|
2038
|
+
created_at: String(row.created_at ??
|
|
2039
|
+
row.createdAt ??
|
|
2040
|
+
new Date().toISOString()),
|
|
2041
|
+
token: proposalToken(id, proposed),
|
|
2042
|
+
};
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
catch { }
|
|
2046
|
+
// Conflicts
|
|
2047
|
+
let conflicts = [];
|
|
2048
|
+
try {
|
|
2049
|
+
const maybe = conflictDetector.openConflicts;
|
|
2050
|
+
if (typeof maybe === "function") {
|
|
2051
|
+
const raw = maybe.call(conflictDetector);
|
|
2052
|
+
conflicts = raw.map((c) => {
|
|
2053
|
+
const row = c;
|
|
2054
|
+
return {
|
|
2055
|
+
id: String(row.id ?? ""),
|
|
2056
|
+
kind: String(row.kind ?? ""),
|
|
2057
|
+
a_summary: String(row.a_summary ?? row.aSummary ?? ""),
|
|
2058
|
+
b_summary: String(row.b_summary ?? row.bSummary ?? ""),
|
|
2059
|
+
opened_at: String(row.opened_at ??
|
|
2060
|
+
row.openedAt ??
|
|
2061
|
+
new Date().toISOString()),
|
|
2062
|
+
};
|
|
2063
|
+
});
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
catch {
|
|
2067
|
+
conflicts = [];
|
|
2068
|
+
}
|
|
2069
|
+
// Health
|
|
2070
|
+
let health;
|
|
2071
|
+
try {
|
|
2072
|
+
const doc = buildDoctor(store, host, memoryService, {}, kevinEnv);
|
|
2073
|
+
let perfRows = [];
|
|
2074
|
+
try {
|
|
2075
|
+
const stats = perf.stats?.();
|
|
2076
|
+
if (Array.isArray(stats)) {
|
|
2077
|
+
perfRows = stats.map((p) => {
|
|
2078
|
+
const row = p;
|
|
2079
|
+
const budget = row.budget;
|
|
2080
|
+
return {
|
|
2081
|
+
scope: String(row.scope ?? ""),
|
|
2082
|
+
p95: Number(row.p95 ?? 0),
|
|
2083
|
+
budget_p95: Number(budget?.p95Ms ??
|
|
2084
|
+
row.budget_p95 ??
|
|
2085
|
+
row.budgetP95 ??
|
|
2086
|
+
0),
|
|
2087
|
+
within_budget: Boolean(row.withinBudget ?? row.within_budget ?? true),
|
|
2088
|
+
};
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
catch {
|
|
2093
|
+
perfRows = [];
|
|
2094
|
+
}
|
|
2095
|
+
let digest = "unknown";
|
|
2096
|
+
try {
|
|
2097
|
+
digest = contractDigest(describeContract());
|
|
2098
|
+
}
|
|
2099
|
+
catch { }
|
|
2100
|
+
health = {
|
|
2101
|
+
verdict: doc.verdict,
|
|
2102
|
+
reason: doc.reason,
|
|
2103
|
+
hooks: doc.hooks.map((h) => ({
|
|
2104
|
+
hook: h.hook,
|
|
2105
|
+
state: h.state,
|
|
2106
|
+
fire_count: h.fire_count,
|
|
2107
|
+
expected_count: h.expected_count,
|
|
2108
|
+
})),
|
|
2109
|
+
perf: perfRows,
|
|
2110
|
+
contract_digest: digest,
|
|
2111
|
+
counters: metrics.snapshot(),
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
catch {
|
|
2115
|
+
health = {
|
|
2116
|
+
verdict: "unknown",
|
|
2117
|
+
reason: "health unavailable",
|
|
2118
|
+
hooks: [],
|
|
2119
|
+
perf: [],
|
|
2120
|
+
contract_digest: "unknown",
|
|
2121
|
+
counters: {},
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
flushSnapshots({
|
|
2125
|
+
root: materializerRoot,
|
|
2126
|
+
proposals,
|
|
2127
|
+
conflicts,
|
|
2128
|
+
health,
|
|
2129
|
+
metrics,
|
|
2130
|
+
version: KEVIN_VERSION,
|
|
2131
|
+
});
|
|
2132
|
+
try {
|
|
2133
|
+
writeDashboard(materializerRoot, {
|
|
2134
|
+
generatedAt: new Date().toISOString(),
|
|
2135
|
+
proposals,
|
|
2136
|
+
conflicts,
|
|
2137
|
+
health,
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
catch { }
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
catch {
|
|
2144
|
+
// best-effort — snapshot failure must not break idle
|
|
2145
|
+
}
|
|
1853
2146
|
// v1.0.0 (K10-013 / D10-08) — the session recorded work:
|
|
1854
2147
|
// arm the deferred dispose settlement. The ISO timestamp
|
|
1855
2148
|
// lets the next session.start compare it against
|
package/dist/plugin/native.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Materializer } from "
|
|
2
|
-
import type { Store } from "
|
|
1
|
+
import { type Materializer } from "@jmtrin/kevin-core";
|
|
2
|
+
import type { Store } from "@jmtrin/kevin-core";
|
|
3
3
|
import type { HostSurface } from "./host.js";
|
|
4
4
|
/** The surface `attachNative()` returned. `verified` is filled by the
|
|
5
5
|
* read-back checks (K9-014/015) once the host ran `setup()`. */
|