@jmtrin/kevin-core 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/Archiver.d.ts +42 -0
- package/dist/Archiver.js +83 -0
- package/dist/ArtifactWriter.d.ts +93 -0
- package/dist/ArtifactWriter.js +291 -0
- package/dist/CausalChain.d.ts +22 -0
- package/dist/CausalChain.js +197 -0
- package/dist/ChatBridge.d.ts +41 -0
- package/dist/ChatBridge.js +103 -0
- package/dist/ConflictDetector.d.ts +35 -0
- package/dist/ConflictDetector.js +260 -0
- package/dist/ContextInjector.d.ts +222 -0
- package/dist/ContextInjector.js +533 -0
- package/dist/ConventionMiner.d.ts +35 -0
- package/dist/ConventionMiner.js +242 -0
- package/dist/Curator.d.ts +118 -0
- package/dist/Curator.js +326 -0
- package/dist/DashboardHtml.d.ts +5 -0
- package/dist/DashboardHtml.js +180 -0
- package/dist/Feedback.d.ts +67 -0
- package/dist/Feedback.js +120 -0
- package/dist/HookLiveness.d.ts +88 -0
- package/dist/HookLiveness.js +322 -0
- package/dist/InjectionLedger.d.ts +98 -0
- package/dist/InjectionLedger.js +295 -0
- package/dist/LessonFixer.d.ts +44 -0
- package/dist/LessonFixer.js +45 -0
- package/dist/Materializer.d.ts +84 -0
- package/dist/Materializer.js +181 -0
- package/dist/MemoryService.d.ts +360 -0
- package/dist/MemoryService.js +1242 -0
- package/dist/Migrate.d.ts +24 -0
- package/dist/Migrate.js +285 -0
- package/dist/PatternMiner.d.ts +49 -0
- package/dist/PatternMiner.js +132 -0
- package/dist/QualityGate.d.ts +124 -0
- package/dist/QualityGate.js +119 -0
- package/dist/Reflector.d.ts +91 -0
- package/dist/Reflector.js +307 -0
- package/dist/RepoIdentity.d.ts +124 -0
- package/dist/RepoIdentity.js +300 -0
- package/dist/RepoTruth.d.ts +80 -0
- package/dist/RepoTruth.js +599 -0
- package/dist/Retrospective.d.ts +18 -0
- package/dist/Retrospective.js +216 -0
- package/dist/SharedLayer.d.ts +159 -0
- package/dist/SharedLayer.js +462 -0
- package/dist/Store.d.ts +29 -0
- package/dist/Store.js +50 -0
- package/dist/ToolCallObserver.d.ts +32 -0
- package/dist/ToolCallObserver.js +201 -0
- package/dist/TuiActions.d.ts +43 -0
- package/dist/TuiActions.js +181 -0
- package/dist/TuiSnapshots.d.ts +24 -0
- package/dist/TuiSnapshots.js +159 -0
- package/dist/capabilities.d.ts +6 -0
- package/dist/capabilities.js +1 -0
- package/dist/columns.d.ts +11 -0
- package/dist/columns.js +54 -0
- package/dist/confidence.d.ts +8 -0
- package/dist/confidence.js +34 -0
- package/dist/contract.d.ts +68 -0
- package/dist/contract.js +430 -0
- package/dist/diff.d.ts +8 -0
- package/dist/diff.js +182 -0
- package/dist/env.d.ts +5 -0
- package/dist/env.js +12 -0
- package/dist/escape.d.ts +39 -0
- package/dist/escape.js +60 -0
- package/dist/fingerprint.d.ts +27 -0
- package/dist/fingerprint.js +73 -0
- package/dist/host.d.ts +18 -0
- package/dist/host.js +7 -0
- package/dist/idle-pipeline.d.ts +9 -0
- package/dist/idle-pipeline.js +44 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +109 -0
- package/dist/inferability.d.ts +32 -0
- package/dist/inferability.js +88 -0
- package/dist/kevin_approve.d.ts +34 -0
- package/dist/kevin_approve.js +52 -0
- package/dist/kevin_audit.d.ts +230 -0
- package/dist/kevin_audit.js +631 -0
- package/dist/kevin_bench.d.ts +11 -0
- package/dist/kevin_bench.js +83 -0
- package/dist/kevin_conflicts.d.ts +9 -0
- package/dist/kevin_conflicts.js +50 -0
- package/dist/kevin_contract.d.ts +8 -0
- package/dist/kevin_contract.js +59 -0
- package/dist/kevin_doctor.d.ts +63 -0
- package/dist/kevin_doctor.js +197 -0
- package/dist/kevin_facts.d.ts +42 -0
- package/dist/kevin_facts.js +36 -0
- package/dist/kevin_forget.d.ts +33 -0
- package/dist/kevin_forget.js +260 -0
- package/dist/kevin_native.d.ts +29 -0
- package/dist/kevin_native.js +79 -0
- package/dist/kevin_propose.d.ts +23 -0
- package/dist/kevin_propose.js +14 -0
- package/dist/kevin_publish.d.ts +38 -0
- package/dist/kevin_publish.js +18 -0
- package/dist/kevin_why.d.ts +23 -0
- package/dist/kevin_why.js +111 -0
- package/dist/memory-format.d.ts +20 -0
- package/dist/memory-format.js +59 -0
- package/dist/metrics.d.ts +97 -0
- package/dist/metrics.js +288 -0
- package/dist/migrations/001_initial.sql +92 -0
- package/dist/migrations/002_indexes.sql +14 -0
- package/dist/migrations/003_v02_signal.sql +58 -0
- package/dist/migrations/004_v03_knowledge.sql +138 -0
- package/dist/migrations/005_v04_signal.sql +57 -0
- package/dist/migrations/006_v05_glassbox.sql +118 -0
- package/dist/migrations/007_v06_pull.sql +145 -0
- package/dist/migrations/008_v07_truth.sql +124 -0
- package/dist/migrations/009_v08_team.sql +100 -0
- package/dist/migrations/010_v09_native.sql +78 -0
- package/dist/migrations/011_v10_proven.sql +69 -0
- package/dist/migrations/012_v11_drift.sql +24 -0
- package/dist/native.d.ts +23 -0
- package/dist/native.js +1 -0
- package/dist/okf-export.d.ts +3 -0
- package/dist/okf-export.js +134 -0
- package/dist/okf-import.d.ts +76 -0
- package/dist/okf-import.js +271 -0
- package/dist/okf.d.ts +107 -0
- package/dist/okf.js +303 -0
- package/dist/perf.d.ts +35 -0
- package/dist/perf.js +174 -0
- package/dist/query-tokenizer.d.ts +13 -0
- package/dist/query-tokenizer.js +133 -0
- package/dist/redact.d.ts +2 -0
- package/dist/redact.js +18 -0
- package/dist/replay-types.d.ts +69 -0
- package/dist/replay-types.js +156 -0
- package/dist/replay.d.ts +44 -0
- package/dist/replay.js +215 -0
- package/dist/sqlite-adapter.d.ts +12 -0
- package/dist/sqlite-adapter.js +78 -0
- package/dist/time-ms.d.ts +1 -0
- package/dist/time-ms.js +16 -0
- package/dist/tui-types.d.ts +59 -0
- package/dist/tui-types.js +4 -0
- package/dist/uuid.d.ts +1 -0
- package/dist/uuid.js +50 -0
- package/package.json +28 -0
package/dist/replay.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { Archiver } from "./Archiver.js";
|
|
2
|
+
import { CausalChain } from "./CausalChain.js";
|
|
3
|
+
import { ContextInjector } from "./ContextInjector.js";
|
|
4
|
+
import { composeIdlePipeline } from "./idle-pipeline.js";
|
|
5
|
+
import { InjectionLedger } from "./InjectionLedger.js";
|
|
6
|
+
import { MemoryService } from "./MemoryService.js";
|
|
7
|
+
import { Migrate, exportMigrationsDir } from "./Migrate.js";
|
|
8
|
+
import { Reflector } from "./Reflector.js";
|
|
9
|
+
import { Store } from "./Store.js";
|
|
10
|
+
import { ToolCallObserver } from "./ToolCallObserver.js";
|
|
11
|
+
import { Metrics } from "./metrics.js";
|
|
12
|
+
export async function replay(transcript, opts) {
|
|
13
|
+
const store = new Store({ path: opts?.dbPath ?? ":memory:" });
|
|
14
|
+
await new Migrate(store, exportMigrationsDir()).run();
|
|
15
|
+
// Freeze retrieval BEFORE any query happens (K5-008): deterministic
|
|
16
|
+
// mode uses the DATE_NOW sentinel, ignores recency and never bumps.
|
|
17
|
+
store
|
|
18
|
+
.prepare("INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('deterministic_retrieval', '1')")
|
|
19
|
+
.run();
|
|
20
|
+
store
|
|
21
|
+
.prepare("UPDATE kevin_settings SET value = '1' WHERE key = 'deterministic_retrieval'")
|
|
22
|
+
.run();
|
|
23
|
+
// v0.6.0 (K6-022) — the release floor (0.6) blocks every
|
|
24
|
+
// single-observation memory the v0.5-era fixture exercises; the replay
|
|
25
|
+
// harness preserves the v0.5 push semantics it was recorded under, the
|
|
26
|
+
// same opt-out the legacy plugin harnesses apply.
|
|
27
|
+
store
|
|
28
|
+
.prepare("INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('injection_confidence_floor', '0')")
|
|
29
|
+
.run();
|
|
30
|
+
store
|
|
31
|
+
.prepare("UPDATE kevin_settings SET value = '0' WHERE key = 'injection_confidence_floor'")
|
|
32
|
+
.run();
|
|
33
|
+
const metrics = new Metrics(store);
|
|
34
|
+
const memoryService = new MemoryService(store, metrics);
|
|
35
|
+
const observer = new ToolCallObserver(store, metrics);
|
|
36
|
+
// v0.3.0 fix (mirrors index.ts) — the Reflector stamps
|
|
37
|
+
// tool_calls.error_fingerprint with the stderr-based fingerprint the
|
|
38
|
+
// error memory uses; without it the recurrence queries in settle and
|
|
39
|
+
// the feedback loop never match (tool_calls.fingerprint is keyed on
|
|
40
|
+
// `${tool}|${args}|${success}`, memories on a hash of stderr/stdout).
|
|
41
|
+
const linkErrorStmt = store.prepare("UPDATE tool_calls SET error_fingerprint = ? WHERE id = ?");
|
|
42
|
+
const reflector = new Reflector(memoryService, {
|
|
43
|
+
throttleMs: 0,
|
|
44
|
+
onLinkError: (callID, fp) => {
|
|
45
|
+
linkErrorStmt.run(fp, callID);
|
|
46
|
+
},
|
|
47
|
+
}, metrics);
|
|
48
|
+
const ledger = new InjectionLedger(store, metrics);
|
|
49
|
+
const injector = new ContextInjector(memoryService, metrics, ledger);
|
|
50
|
+
const causalChain = new CausalChain(store, memoryService, metrics);
|
|
51
|
+
// The Archiver reads the clock through this accessor, which is updated
|
|
52
|
+
// to the current event's `at` before each event is processed.
|
|
53
|
+
let currentAt = new Date(transcript.events[0]?.at ?? Date.now());
|
|
54
|
+
const archiver = new Archiver(store, memoryService, metrics, () => currentAt);
|
|
55
|
+
const lastUserQueryBySession = new Map();
|
|
56
|
+
const callCache = new Map();
|
|
57
|
+
function resolveQuery(sessionId) {
|
|
58
|
+
return lastUserQueryBySession.get(sessionId) ?? "";
|
|
59
|
+
}
|
|
60
|
+
for (const event of transcript.events) {
|
|
61
|
+
currentAt = new Date(event.at);
|
|
62
|
+
await handleEvent(event);
|
|
63
|
+
}
|
|
64
|
+
metrics.flush();
|
|
65
|
+
const outcomeRows = store
|
|
66
|
+
.prepare("SELECT outcome, COUNT(*) AS n FROM kevin_injections GROUP BY outcome")
|
|
67
|
+
.all();
|
|
68
|
+
const counts = {
|
|
69
|
+
unmeasured: 0,
|
|
70
|
+
effective: 0,
|
|
71
|
+
ineffective: 0,
|
|
72
|
+
inconclusive: 0,
|
|
73
|
+
};
|
|
74
|
+
for (const r of outcomeRows) {
|
|
75
|
+
counts[r.outcome] = r.n;
|
|
76
|
+
}
|
|
77
|
+
const total = outcomeRows.reduce((acc, r) => acc + r.n, 0);
|
|
78
|
+
const tokenValue = (key) => {
|
|
79
|
+
const row = store
|
|
80
|
+
.prepare("SELECT value FROM kevin_metrics WHERE key = ?")
|
|
81
|
+
.get(key);
|
|
82
|
+
return row?.value ?? 0;
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
transcript: transcript.name,
|
|
86
|
+
memoriesCreated: totalMemories(store),
|
|
87
|
+
injections: {
|
|
88
|
+
total,
|
|
89
|
+
effective: counts.effective,
|
|
90
|
+
ineffective: counts.ineffective,
|
|
91
|
+
inconclusive: counts.inconclusive,
|
|
92
|
+
unmeasured: counts.unmeasured,
|
|
93
|
+
},
|
|
94
|
+
precisionRate: metrics.precisionRate(),
|
|
95
|
+
coverageRate: metrics.coverageRate(),
|
|
96
|
+
tokensInjected: {
|
|
97
|
+
prePrompt: tokenValue("tokens_injected_pre_prompt"),
|
|
98
|
+
compacting: tokenValue("tokens_injected_compacting"),
|
|
99
|
+
},
|
|
100
|
+
blocked: metrics.blockedSnapshot(),
|
|
101
|
+
};
|
|
102
|
+
async function handleEvent(event) {
|
|
103
|
+
switch (event.kind) {
|
|
104
|
+
case "session.created":
|
|
105
|
+
// Mirror of the live wiring (index.ts): a fresh session must
|
|
106
|
+
// not inherit the previous session's injection seen-set
|
|
107
|
+
// (plan §5.1 rule 3). Without this, a multi-session
|
|
108
|
+
// transcript would block every memory in session 2 with
|
|
109
|
+
// `seen_this_session` while the live plugin would admit it.
|
|
110
|
+
injector.onSessionCreated(event.sessionId);
|
|
111
|
+
return;
|
|
112
|
+
case "chat.message": {
|
|
113
|
+
const derived = injector.deriveQuery([
|
|
114
|
+
{ role: "user", content: event.text },
|
|
115
|
+
]);
|
|
116
|
+
if (derived.length > 0) {
|
|
117
|
+
lastUserQueryBySession.set(event.sessionId, derived);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
case "tool.before": {
|
|
122
|
+
callCache.set(event.callId, {
|
|
123
|
+
tool: event.tool,
|
|
124
|
+
args: event.args,
|
|
125
|
+
});
|
|
126
|
+
observer.onBefore({
|
|
127
|
+
tool: event.tool,
|
|
128
|
+
args: event.args,
|
|
129
|
+
sessionId: event.sessionId,
|
|
130
|
+
callID: event.callId,
|
|
131
|
+
projectId: null,
|
|
132
|
+
}, {});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
case "tool.after": {
|
|
136
|
+
const cached = callCache.get(event.callId);
|
|
137
|
+
const tool = cached?.tool ?? "unknown";
|
|
138
|
+
const args = cached?.args ?? {};
|
|
139
|
+
const stderr = event.stderr ?? "";
|
|
140
|
+
const stdout = event.stdout ?? "";
|
|
141
|
+
observer.onAfter({
|
|
142
|
+
tool,
|
|
143
|
+
args,
|
|
144
|
+
sessionId: event.sessionId,
|
|
145
|
+
callID: event.callId,
|
|
146
|
+
projectId: null,
|
|
147
|
+
}, { success: event.success, stdout, stderr, exitCode: event.exitCode });
|
|
148
|
+
if (!event.success) {
|
|
149
|
+
await reflector.invoke({
|
|
150
|
+
toolName: tool,
|
|
151
|
+
argsSummary: observer.summarizeArgs(args),
|
|
152
|
+
stderr,
|
|
153
|
+
stdout,
|
|
154
|
+
exitCode: event.exitCode,
|
|
155
|
+
errorType: observer.inferErrorType(stderr, stdout, event.exitCode),
|
|
156
|
+
sessionId: event.sessionId,
|
|
157
|
+
callID: event.callId,
|
|
158
|
+
projectId: null,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
causalChain.onSuccess(tool, args, null, event.sessionId);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
case "system.transform": {
|
|
167
|
+
const query = resolveQuery(event.sessionId);
|
|
168
|
+
if (!query)
|
|
169
|
+
return;
|
|
170
|
+
const output = { system: [] };
|
|
171
|
+
injector.onSystemTransform({
|
|
172
|
+
sessionID: event.sessionId,
|
|
173
|
+
messages: [{ role: "user", content: query }],
|
|
174
|
+
}, output);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
case "compacting": {
|
|
178
|
+
const query = resolveQuery(event.sessionId);
|
|
179
|
+
if (!query)
|
|
180
|
+
return;
|
|
181
|
+
const output = { context: [] };
|
|
182
|
+
injector.onCompacting({
|
|
183
|
+
sessionID: event.sessionId,
|
|
184
|
+
messages: [{ role: "user", content: query }],
|
|
185
|
+
}, output);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
case "session.idle": {
|
|
189
|
+
// K13-010 (D13-07) — single ORDER via composeIdlePipeline. Replay
|
|
190
|
+
// provides core-native implementations; adapter provides closures
|
|
191
|
+
// over its live services. The ORDER array is the single source.
|
|
192
|
+
await composeIdlePipeline({
|
|
193
|
+
"ledger.settle": () => { ledger.settle(event.sessionId); },
|
|
194
|
+
"archiver.run": () => { archiver.run(); },
|
|
195
|
+
"metrics.flush": () => { metrics.flush(); },
|
|
196
|
+
// core-native stubs for steps that are no-ops in hermetic replay
|
|
197
|
+
// but keep ORDER parity with the adapter (e.g. pattern mining)
|
|
198
|
+
"patternMiner.mine": () => { },
|
|
199
|
+
"causalChain.onSessionIdle": () => { void causalChain.onSessionIdle(event.sessionId); },
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* K13-010 — alias required by plan §4: runReplaySession is the core-native mount
|
|
208
|
+
* entry used by the parity harness (K13-011) and old tests. Signature mirrors
|
|
209
|
+
* `replay` but accepts env for future threading.
|
|
210
|
+
*/
|
|
211
|
+
export const runReplaySession = replay;
|
|
212
|
+
function totalMemories(store) {
|
|
213
|
+
const row = store.prepare("SELECT COUNT(*) AS n FROM memories").get();
|
|
214
|
+
return row.n ?? 0;
|
|
215
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface SqliteStatement {
|
|
2
|
+
get(...params: unknown[]): unknown;
|
|
3
|
+
all(...params: unknown[]): unknown[];
|
|
4
|
+
run(...params: unknown[]): void;
|
|
5
|
+
}
|
|
6
|
+
export interface SqliteAdapter {
|
|
7
|
+
prepare(sql: string): SqliteStatement;
|
|
8
|
+
transaction<T>(fn: () => T): () => T;
|
|
9
|
+
exec(sql: string): void;
|
|
10
|
+
close(): void;
|
|
11
|
+
}
|
|
12
|
+
export declare function createDatabase(path: string): SqliteAdapter;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
const localRequire = createRequire(import.meta.url);
|
|
3
|
+
function detectRuntime() {
|
|
4
|
+
if (typeof globalThis.Bun !== "undefined") {
|
|
5
|
+
return "bun";
|
|
6
|
+
}
|
|
7
|
+
return "node";
|
|
8
|
+
}
|
|
9
|
+
function loadBunDatabase(path) {
|
|
10
|
+
const mod = localRequire("bun:sqlite");
|
|
11
|
+
return new mod.Database(path);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Wrapper sobre `node:sqlite` (DatabaseSync, estable en Node 24+).
|
|
15
|
+
* API equivalente a better-sqlite3 excepto `transaction`, que implementamos
|
|
16
|
+
* manualmente con BEGIN/COMMIT/ROLLBACK.
|
|
17
|
+
*/
|
|
18
|
+
class NodeSqliteAdapter {
|
|
19
|
+
db;
|
|
20
|
+
constructor(db) {
|
|
21
|
+
this.db = db;
|
|
22
|
+
}
|
|
23
|
+
prepare(sql) {
|
|
24
|
+
return this.db.prepare(sql);
|
|
25
|
+
}
|
|
26
|
+
exec(sql) {
|
|
27
|
+
this.db.exec(sql);
|
|
28
|
+
}
|
|
29
|
+
close() {
|
|
30
|
+
this.db.close();
|
|
31
|
+
}
|
|
32
|
+
transaction(fn) {
|
|
33
|
+
return () => {
|
|
34
|
+
this.db.exec("BEGIN");
|
|
35
|
+
try {
|
|
36
|
+
const result = fn();
|
|
37
|
+
this.db.exec("COMMIT");
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
try {
|
|
42
|
+
this.db.exec("ROLLBACK");
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// ignore rollback errors
|
|
46
|
+
}
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function loadNodeSqliteDatabase(path) {
|
|
53
|
+
const mod = localRequire("node:sqlite");
|
|
54
|
+
return new NodeSqliteAdapter(new mod.DatabaseSync(path));
|
|
55
|
+
}
|
|
56
|
+
function loadBetterSqliteDatabase(path) {
|
|
57
|
+
const D = localRequire("better-sqlite3");
|
|
58
|
+
const Ctor = "default" in D ? D.default : D;
|
|
59
|
+
return new Ctor(path);
|
|
60
|
+
}
|
|
61
|
+
function loadNodeDatabase(path) {
|
|
62
|
+
// 1) Prefer node:sqlite (built-in, sin binarios nativos que descargar).
|
|
63
|
+
// Estable en Node 24+ sin flag; en Node 22/23 requiere --experimental-sqlite.
|
|
64
|
+
// Si require falla (modulo ausente o flag deshabilitado), cae al fallback.
|
|
65
|
+
try {
|
|
66
|
+
return loadNodeSqliteDatabase(path);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// 2) Fallback a better-sqlite3 (Node 20 o Node 22/23 sin flag).
|
|
70
|
+
return loadBetterSqliteDatabase(path);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function createDatabase(path) {
|
|
74
|
+
if (detectRuntime() === "bun") {
|
|
75
|
+
return loadBunDatabase(path);
|
|
76
|
+
}
|
|
77
|
+
return loadNodeDatabase(path);
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function toMs(legacyValue: string | null | undefined, msValue: number | null | undefined): number | null;
|
package/dist/time-ms.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// v1.1.0 (K11-003/K11-004 / plan §5.2, D11-01) — millisecond helper.
|
|
2
|
+
// Readers prefer _ms and fall back to legacy second-granularity column.
|
|
3
|
+
// The helper is the single implementation imported by InjectionLedger and
|
|
4
|
+
// CausalChain (moved here in K11-004 to avoid duplication).
|
|
5
|
+
export function toMs(legacyValue, msValue) {
|
|
6
|
+
if (typeof msValue === "number" && !Number.isNaN(msValue))
|
|
7
|
+
return msValue;
|
|
8
|
+
if (!legacyValue)
|
|
9
|
+
return null;
|
|
10
|
+
// SQLite datetime('now') is 'YYYY-MM-DD HH:MM:SS' UTC
|
|
11
|
+
const iso = legacyValue.includes("T")
|
|
12
|
+
? legacyValue
|
|
13
|
+
: `${legacyValue.replace(" ", "T")}Z`;
|
|
14
|
+
const n = Date.parse(iso);
|
|
15
|
+
return Number.isNaN(n) ? null : n;
|
|
16
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface ProposalView {
|
|
2
|
+
readonly id: string;
|
|
3
|
+
readonly kind: string;
|
|
4
|
+
readonly target_path: string;
|
|
5
|
+
readonly diff: string;
|
|
6
|
+
readonly memory_ids: readonly string[];
|
|
7
|
+
readonly created_at: string;
|
|
8
|
+
readonly truncated?: boolean;
|
|
9
|
+
readonly token?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ConflictView {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly kind: string;
|
|
14
|
+
readonly a_summary: string;
|
|
15
|
+
readonly b_summary: string;
|
|
16
|
+
readonly opened_at: string;
|
|
17
|
+
}
|
|
18
|
+
export interface HealthView {
|
|
19
|
+
readonly verdict: string;
|
|
20
|
+
readonly reason: string;
|
|
21
|
+
readonly hooks: readonly {
|
|
22
|
+
readonly hook: string;
|
|
23
|
+
readonly state: string;
|
|
24
|
+
readonly fire_count: number;
|
|
25
|
+
readonly expected_count: number;
|
|
26
|
+
}[];
|
|
27
|
+
readonly perf: readonly {
|
|
28
|
+
readonly scope: string;
|
|
29
|
+
readonly p95: number;
|
|
30
|
+
readonly budget_p95: number;
|
|
31
|
+
readonly within_budget: boolean;
|
|
32
|
+
}[];
|
|
33
|
+
readonly contract_digest: string;
|
|
34
|
+
readonly counters: Record<string, number>;
|
|
35
|
+
}
|
|
36
|
+
export interface TuiSnapshotSet {
|
|
37
|
+
readonly generatedAt: string;
|
|
38
|
+
readonly proposals: readonly ProposalView[];
|
|
39
|
+
readonly conflicts: readonly ConflictView[];
|
|
40
|
+
readonly health: HealthView;
|
|
41
|
+
}
|
|
42
|
+
export type TuiAction = {
|
|
43
|
+
readonly type: "approve";
|
|
44
|
+
readonly proposalId: string;
|
|
45
|
+
readonly token: string;
|
|
46
|
+
} | {
|
|
47
|
+
readonly type: "reject";
|
|
48
|
+
readonly proposalId: string;
|
|
49
|
+
readonly token: string;
|
|
50
|
+
readonly note?: string;
|
|
51
|
+
} | {
|
|
52
|
+
readonly type: "acknowledge";
|
|
53
|
+
readonly conflictId: string;
|
|
54
|
+
};
|
|
55
|
+
export interface ActionResult {
|
|
56
|
+
readonly action: TuiAction;
|
|
57
|
+
readonly status: "applied" | "rejected" | "stale_skipped" | "error";
|
|
58
|
+
readonly detail?: string;
|
|
59
|
+
}
|
package/dist/uuid.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function uuidv7(): string;
|
package/dist/uuid.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
// UUID v7 generator — timestamp-ordered IDs.
|
|
3
|
+
// Format: xxxxxxxx-xxxx-7xxx-yxxx-xxxxxxxxxxxx
|
|
4
|
+
// First 48 bits: Unix timestamp (ms). Version nibble set to 7.
|
|
5
|
+
// Variant nibble starts with binary 10.
|
|
6
|
+
let lastTs = 0;
|
|
7
|
+
let lastRandom = Buffer.alloc(10);
|
|
8
|
+
function toHex(bytes) {
|
|
9
|
+
return bytes.toString("hex");
|
|
10
|
+
}
|
|
11
|
+
export function uuidv7() {
|
|
12
|
+
let ts = Date.now();
|
|
13
|
+
if (ts < lastTs)
|
|
14
|
+
ts = lastTs;
|
|
15
|
+
if (ts === lastTs) {
|
|
16
|
+
for (let i = 9; i >= 0; i--) {
|
|
17
|
+
if (lastRandom[i] === 0xff) {
|
|
18
|
+
lastRandom[i] = 0;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
lastRandom[i]++;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
lastRandom = randomBytes(10);
|
|
28
|
+
}
|
|
29
|
+
lastTs = ts;
|
|
30
|
+
const rand = lastRandom;
|
|
31
|
+
const bytes = Buffer.alloc(16);
|
|
32
|
+
bytes[0] = Math.floor(ts / 2 ** 40) & 0xff;
|
|
33
|
+
bytes[1] = Math.floor(ts / 2 ** 32) & 0xff;
|
|
34
|
+
bytes[2] = (ts >>> 24) & 0xff;
|
|
35
|
+
bytes[3] = (ts >>> 16) & 0xff;
|
|
36
|
+
bytes[4] = (ts >>> 8) & 0xff;
|
|
37
|
+
bytes[5] = ts & 0xff;
|
|
38
|
+
bytes[6] = 0x70 | (rand[0] & 0x0f);
|
|
39
|
+
bytes[7] = rand[1];
|
|
40
|
+
bytes[8] = 0x80 | (rand[2] & 0x3f);
|
|
41
|
+
bytes[9] = rand[3];
|
|
42
|
+
bytes[10] = rand[4];
|
|
43
|
+
bytes[11] = rand[5];
|
|
44
|
+
bytes[12] = rand[6];
|
|
45
|
+
bytes[13] = rand[7];
|
|
46
|
+
bytes[14] = rand[8];
|
|
47
|
+
bytes[15] = rand[9];
|
|
48
|
+
const hex = toHex(bytes);
|
|
49
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
50
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jmtrin/kevin-core",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Kevin core — host-agnostic deterministic brain (Bedrock)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.json && node ./scripts/copy-migrations.mjs",
|
|
19
|
+
"typecheck": "tsc --noEmit"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.5.0"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT"
|
|
28
|
+
}
|