@jmtrin/opencode-kevin 0.8.0 → 0.9.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/README.md +63 -10
- package/dist/migrations/010_v09_native.sql +78 -0
- package/dist/plugin/HookLiveness.d.ts +81 -0
- package/dist/plugin/HookLiveness.js +324 -0
- package/dist/plugin/HookLiveness.js.map +1 -0
- package/dist/plugin/Materializer.d.ts +25 -0
- package/dist/plugin/Materializer.js +39 -10
- package/dist/plugin/Materializer.js.map +1 -1
- package/dist/plugin/Migrate.d.ts +1 -0
- package/dist/plugin/Migrate.js +46 -5
- package/dist/plugin/Migrate.js.map +1 -1
- package/dist/plugin/RepoIdentity.d.ts +20 -9
- package/dist/plugin/RepoIdentity.js +44 -9
- package/dist/plugin/RepoIdentity.js.map +1 -1
- package/dist/plugin/Retrospective.js +9 -0
- package/dist/plugin/Retrospective.js.map +1 -1
- package/dist/plugin/host.d.ts +70 -0
- package/dist/plugin/host.js +251 -0
- package/dist/plugin/host.js.map +1 -0
- package/dist/plugin/index.d.ts +2 -2
- package/dist/plugin/index.js +129 -7
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/kevin_audit.d.ts +29 -0
- package/dist/plugin/kevin_audit.js +84 -0
- package/dist/plugin/kevin_audit.js.map +1 -1
- package/dist/plugin/kevin_doctor.d.ts +57 -0
- package/dist/plugin/kevin_doctor.js +168 -0
- package/dist/plugin/kevin_doctor.js.map +1 -0
- package/dist/plugin/kevin_native.d.ts +29 -0
- package/dist/plugin/kevin_native.js +80 -0
- package/dist/plugin/kevin_native.js.map +1 -0
- package/dist/plugin/native.d.ts +92 -0
- package/dist/plugin/native.js +191 -0
- package/dist/plugin/native.js.map +1 -0
- package/migrations/010_v09_native.sql +78 -0
- package/package.json +2 -2
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* K9-004 — v0.9.0 native — host probe (plan §5.1, D9-12).
|
|
3
|
+
*
|
|
4
|
+
* Kevin stops assuming the host and asks it instead. `probeHost()` inspects the
|
|
5
|
+
* plugin input and the resolved `@opencode-ai/plugin` package and answers four
|
|
6
|
+
* questions once per process: what plugin version is actually installed, which
|
|
7
|
+
* v1+v2 surface exists, what project context the host already knows, and
|
|
8
|
+
* whether a shell was handed over.
|
|
9
|
+
*
|
|
10
|
+
* Contract (plan §5.1 / §4 traps 24-26):
|
|
11
|
+
* - Never throws: every read is guarded, every failure appends to `notes` and
|
|
12
|
+
* yields a conservative `false`. A throw during construction takes down the
|
|
13
|
+
* host's plugin load — strictly worse than any missing feature.
|
|
14
|
+
* - Duck-typed: only `typeof` and `in` checks, never `instanceof`, never a cast
|
|
15
|
+
* to a host class.
|
|
16
|
+
* - The v2 probe is a dynamic `await import()` of the v2/promise subpath inside
|
|
17
|
+
* a try. Rejection is the answer (a pre-v2 package). When it resolves we record
|
|
18
|
+
* which domains are actually exposed rather than assuming both — the promise
|
|
19
|
+
* subpath exports `define` at runtime and its `skill`/`reference` domains are
|
|
20
|
+
* compile-time types, so domain availability is verified against the resolved
|
|
21
|
+
* package's own declaration files.
|
|
22
|
+
* - Runs once: the result is cached and frozen. A capability that appears
|
|
23
|
+
* mid-session is indistinguishable from a bug (D9-12).
|
|
24
|
+
* - `pluginVersion` comes from the resolved package's `package.json` — never
|
|
25
|
+
* from the declared range, which plan §3.4 showed to be untrustworthy.
|
|
26
|
+
* - The v2 subpath string itself lives in native.ts (K9-013 containment): this
|
|
27
|
+
* module only ever names it through the imported symbol.
|
|
28
|
+
*/
|
|
29
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
30
|
+
import { createRequire } from "node:module";
|
|
31
|
+
import { dirname, join } from "node:path";
|
|
32
|
+
import { fileURLToPath } from "node:url";
|
|
33
|
+
// v0.9.0 (K9-013 / plan §5.4, D9-11) — the only file allowed to name the v2
|
|
34
|
+
// subpath is plugin/native.ts; this module uses the exported symbol.
|
|
35
|
+
import { V2_SPECIFIER } from "./native.js";
|
|
36
|
+
let cachedSurface = null;
|
|
37
|
+
/**
|
|
38
|
+
* v0.9.0 (K9-004 / plan §5.1, D9-12)
|
|
39
|
+
* `importV2` is a test seam; production callers omit it and the real
|
|
40
|
+
* `await import()` runs inside the try.
|
|
41
|
+
*/
|
|
42
|
+
export async function probeHost(input, options) {
|
|
43
|
+
if (cachedSurface !== null) {
|
|
44
|
+
return cachedSurface;
|
|
45
|
+
}
|
|
46
|
+
const notes = [];
|
|
47
|
+
const project = readProject(input, notes);
|
|
48
|
+
const hasShell = readShell(input, notes);
|
|
49
|
+
const v2 = await probeV2(notes, options?.importV2);
|
|
50
|
+
const pluginVersion = readPluginVersion(notes);
|
|
51
|
+
const surface = Object.freeze({
|
|
52
|
+
pluginVersion,
|
|
53
|
+
flavour: v2 === null ? "v1-only" : "v1+v2",
|
|
54
|
+
project: Object.freeze(project),
|
|
55
|
+
hasShell,
|
|
56
|
+
v2: Object.freeze({
|
|
57
|
+
skill: v2?.skill ?? false,
|
|
58
|
+
reference: v2?.reference ?? false,
|
|
59
|
+
}),
|
|
60
|
+
notes: Object.freeze(notes),
|
|
61
|
+
});
|
|
62
|
+
cachedSurface = surface;
|
|
63
|
+
return surface;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* v0.9.0 (K9-004) — test hook. probeHost caches its result (probe once, freeze,
|
|
67
|
+
* restart to re-probe); tests reset between cases.
|
|
68
|
+
*/
|
|
69
|
+
export function resetHostProbeCache() {
|
|
70
|
+
cachedSurface = null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* v0.9.0 (K9-004 / plan §5.1) — one stable paragraph: version, flavour and the
|
|
74
|
+
* domain flags. No paths, no identifiers; safe to paste into an issue report.
|
|
75
|
+
*/
|
|
76
|
+
export function summarize(s) {
|
|
77
|
+
const version = s.pluginVersion ?? "unknown";
|
|
78
|
+
const shell = s.hasShell ? "yes" : "no";
|
|
79
|
+
const skill = s.v2.skill ? "yes" : "no";
|
|
80
|
+
const reference = s.v2.reference ? "yes" : "no";
|
|
81
|
+
return `host plugin ${version}, flavour ${s.flavour}, shell ${shell}, v2 skill ${skill}, v2 reference ${reference}`;
|
|
82
|
+
}
|
|
83
|
+
function readProject(input, notes) {
|
|
84
|
+
let id = null;
|
|
85
|
+
let worktree = null;
|
|
86
|
+
let directory = null;
|
|
87
|
+
if (typeof input === "object" && input !== null) {
|
|
88
|
+
const record = input;
|
|
89
|
+
const project = record.project;
|
|
90
|
+
if (typeof project === "object" && project !== null) {
|
|
91
|
+
const p = project;
|
|
92
|
+
id = typeof p.id === "string" ? p.id : null;
|
|
93
|
+
worktree = typeof p.worktree === "string" ? p.worktree : null;
|
|
94
|
+
directory =
|
|
95
|
+
typeof p.directory === "string" ? p.directory : null;
|
|
96
|
+
}
|
|
97
|
+
// PluginInput also carries top-level worktree/directory; fall back to
|
|
98
|
+
// them when the project object does not.
|
|
99
|
+
if (worktree === null && typeof record.worktree === "string") {
|
|
100
|
+
worktree = record.worktree;
|
|
101
|
+
}
|
|
102
|
+
if (directory === null && typeof record.directory === "string") {
|
|
103
|
+
directory = record.directory;
|
|
104
|
+
}
|
|
105
|
+
if (id === null && worktree === null && directory === null) {
|
|
106
|
+
notes.push("no project context on the plugin input (D9-12)");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
notes.push("plugin input is not an object — host fields unavailable (D9-12)");
|
|
111
|
+
}
|
|
112
|
+
return { id, worktree, directory };
|
|
113
|
+
}
|
|
114
|
+
function readShell(input, notes) {
|
|
115
|
+
if (typeof input !== "object" || input === null) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const shell = input.$;
|
|
119
|
+
const present = (typeof shell === "object" && shell !== null) ||
|
|
120
|
+
typeof shell === "function";
|
|
121
|
+
if (!present) {
|
|
122
|
+
notes.push("no shell ($) handed over — zero process-launching stays a decision (D9-12)");
|
|
123
|
+
}
|
|
124
|
+
return present;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* v0.9.0 (K9-004 / plan §5.1, D9-11/D9-12)
|
|
128
|
+
* Returns null when the v2 surface is absent; otherwise the domain flags
|
|
129
|
+
* verified against the resolved package's own declaration files.
|
|
130
|
+
*/
|
|
131
|
+
async function probeV2(notes, importV2) {
|
|
132
|
+
let mod;
|
|
133
|
+
try {
|
|
134
|
+
mod = importV2
|
|
135
|
+
? await importV2()
|
|
136
|
+
: await import(/* @vite-ignore */ V2_SPECIFIER);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
notes.push(`${V2_SPECIFIER} import rejected — this host package predates the v2 subpath (D9-11)`);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
if (typeof mod !== "object" || mod === null) {
|
|
143
|
+
notes.push("v2/promise resolved to a non-object module — treated as v1-only (D9-12)");
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
if (typeof mod.define !== "function") {
|
|
147
|
+
notes.push("v2/promise resolved but exposes no define — treated as v1-only (D9-12)");
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const root = resolvePluginRoot(notes);
|
|
151
|
+
if (root === null) {
|
|
152
|
+
notes.push("v2/promise resolved but the package root is unreachable — v2 domains unverified (D9-12)");
|
|
153
|
+
return { skill: false, reference: false };
|
|
154
|
+
}
|
|
155
|
+
// The promise subpath ships skill/reference as declaration files; record
|
|
156
|
+
// what is actually there rather than assuming both (plan §5.1).
|
|
157
|
+
return {
|
|
158
|
+
skill: existsSync(join(root, "dist", "v2", "promise", "skill.d.ts")),
|
|
159
|
+
reference: existsSync(join(root, "dist", "v2", "promise", "reference.d.ts")),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function readPluginVersion(notes) {
|
|
163
|
+
const root = resolvePluginRoot(notes);
|
|
164
|
+
if (root === null) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
169
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
170
|
+
return pkg.version;
|
|
171
|
+
}
|
|
172
|
+
notes.push("resolved plugin package.json declares no version (K9-004)");
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
notes.push("resolved plugin package.json unreadable (K9-004)");
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* v0.9.0 (K9-004 / plan §5.1) — the installed package's directory, found by
|
|
182
|
+
* the most reliable strategy that works in this runtime. Never throws.
|
|
183
|
+
*/
|
|
184
|
+
function resolvePluginRoot(notes) {
|
|
185
|
+
// 1. import.meta.resolve: the true resolved copy (Node >= 20.6, Bun).
|
|
186
|
+
const resolveMeta = import.meta.resolve;
|
|
187
|
+
if (typeof resolveMeta === "function") {
|
|
188
|
+
try {
|
|
189
|
+
const url = resolveMeta("@opencode-ai/plugin");
|
|
190
|
+
const root = packageRootOf(url, notes);
|
|
191
|
+
if (root !== null) {
|
|
192
|
+
return root;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// unsupported in this runtime — next strategy
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// 2. createRequire: hosts that ship a "require" condition (this package
|
|
200
|
+
// exports only "import", so this is a forward-compat strategy).
|
|
201
|
+
try {
|
|
202
|
+
const resolve = createRequire(import.meta.url).resolve(`${V2_SPECIFIER}/index.js`);
|
|
203
|
+
const root = packageRootOf(resolve, notes);
|
|
204
|
+
if (root !== null) {
|
|
205
|
+
return root;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// not resolvable via require — next strategy
|
|
210
|
+
}
|
|
211
|
+
// 3. Walk up from cwd looking for a node_modules copy (works in the repo
|
|
212
|
+
// and in typical host layouts; best effort, guarded).
|
|
213
|
+
let dir = process.cwd();
|
|
214
|
+
for (let depth = 0; depth < 10; depth += 1) {
|
|
215
|
+
const candidate = join(dir, "node_modules", "@opencode-ai", "plugin");
|
|
216
|
+
if (existsSync(join(candidate, "package.json"))) {
|
|
217
|
+
return candidate;
|
|
218
|
+
}
|
|
219
|
+
const parent = dirname(dir);
|
|
220
|
+
if (parent === dir) {
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
dir = parent;
|
|
224
|
+
}
|
|
225
|
+
notes.push("resolved plugin package root not reachable (K9-004)");
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
/** Accepts a file URL or a plain path to a file inside the package. */
|
|
229
|
+
function packageRootOf(file, notes) {
|
|
230
|
+
try {
|
|
231
|
+
const path = file.startsWith("file:")
|
|
232
|
+
? fileURLToPath(file)
|
|
233
|
+
: file.replace(/\\/g, "/");
|
|
234
|
+
// dist/index.js → dist → root; dist/v2/promise/index.js → three levels.
|
|
235
|
+
const candidates = [
|
|
236
|
+
dirname(path),
|
|
237
|
+
dirname(dirname(path)),
|
|
238
|
+
dirname(dirname(dirname(path))),
|
|
239
|
+
];
|
|
240
|
+
for (const candidate of candidates) {
|
|
241
|
+
if (existsSync(join(candidate, "package.json"))) {
|
|
242
|
+
return candidate;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
notes.push("could not map the resolved plugin file to its package root (K9-004)");
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../plugin/host.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -16,10 +16,10 @@ export interface KevinPluginOptions {
|
|
|
16
16
|
* v0.4.0 (K4-021) — settings surfaced by `kevin_config` (plan §8.8).
|
|
17
17
|
* Unknown keys are rejected on `set` unless `strict: false`.
|
|
18
18
|
*/
|
|
19
|
-
export declare const KEVIN_CONFIG_KEYS: readonly ["quality_gate_enabled", "lesson_snippet_injection", "patternminer_enabled", "cross_project_enabled", "llm_reflection_enabled", "tool_calls_dedup_enabled", "deterministic_retrieval", "pre_prompt_budget_tokens", "archive_after_days", "curation_enabled", "agents_md_path", "skill_emission_enabled", "reference_emission_enabled", "injection_confidence_floor", "repo_truth_enabled", "convention_mining_enabled", "conflict_detection_enabled", "error_lesson_mode", "shared_layer_enabled", "okf_path", "share_requires_approval", "author_identity_mode", "shared_confidence_floor"];
|
|
19
|
+
export declare const KEVIN_CONFIG_KEYS: readonly ["quality_gate_enabled", "lesson_snippet_injection", "patternminer_enabled", "cross_project_enabled", "llm_reflection_enabled", "tool_calls_dedup_enabled", "deterministic_retrieval", "pre_prompt_budget_tokens", "archive_after_days", "curation_enabled", "agents_md_path", "skill_emission_enabled", "reference_emission_enabled", "injection_confidence_floor", "repo_truth_enabled", "convention_mining_enabled", "conflict_detection_enabled", "error_lesson_mode", "shared_layer_enabled", "okf_path", "share_requires_approval", "author_identity_mode", "shared_confidence_floor", "hook_liveness_enabled", "native_registration_enabled", "host_probe_history_enabled", "dead_hook_report_threshold"];
|
|
20
20
|
export declare const ERROR_LESSON_MODE_VALUES: readonly ["all", "triage_only"];
|
|
21
21
|
/** Plugin release version — stamped into generated files (K8-021/027). */
|
|
22
|
-
export declare const KEVIN_VERSION = "0.
|
|
22
|
+
export declare const KEVIN_VERSION = "0.9.0";
|
|
23
23
|
export interface RekeyCounts {
|
|
24
24
|
memories: number;
|
|
25
25
|
shared_entries: number;
|
package/dist/plugin/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { ContextInjector } from "./ContextInjector.js";
|
|
|
11
11
|
import { ConventionMiner } from "./ConventionMiner.js";
|
|
12
12
|
import { Curator } from "./Curator.js";
|
|
13
13
|
import { Feedback } from "./Feedback.js";
|
|
14
|
+
import { HookLiveness } from "./HookLiveness.js";
|
|
14
15
|
import { InjectionLedger } from "./InjectionLedger.js";
|
|
15
16
|
import { Materializer, SKILL_TOPIC } from "./Materializer.js";
|
|
16
17
|
import { MemoryService, hasRepoIdColumn, } from "./MemoryService.js";
|
|
@@ -25,16 +26,21 @@ import { Store } from "./Store.js";
|
|
|
25
26
|
import { ToolCallObserver } from "./ToolCallObserver.js";
|
|
26
27
|
import { probe } from "./capabilities.js";
|
|
27
28
|
import { computeConfidence } from "./confidence.js";
|
|
29
|
+
import { probeHost, summarize } from "./host.js";
|
|
28
30
|
import { kevinApprove } from "./kevin_approve.js";
|
|
29
31
|
import { buildAudit } from "./kevin_audit.js";
|
|
30
32
|
import { executeKevinConflicts } from "./kevin_conflicts.js";
|
|
33
|
+
import { buildDoctor } from "./kevin_doctor.js";
|
|
31
34
|
import { buildKevinFacts } from "./kevin_facts.js";
|
|
35
|
+
import { handleNative } from "./kevin_native.js";
|
|
32
36
|
import { kevinPropose } from "./kevin_propose.js";
|
|
33
37
|
import { kevinPublish } from "./kevin_publish.js";
|
|
34
38
|
import { kevinWhy } from "./kevin_why.js";
|
|
35
39
|
import { Metrics } from "./metrics.js";
|
|
40
|
+
import { attachNative } from "./native.js";
|
|
36
41
|
import { exportMarkdown, exportOkf } from "./okf-export.js";
|
|
37
42
|
import { importOkf } from "./okf-import.js";
|
|
43
|
+
import { uuidv7 } from "./uuid.js";
|
|
38
44
|
// v0.6.0 (K6-019) — process-global set of reference topics already
|
|
39
45
|
// registered with the host this process. Keeps re-registration idempotent
|
|
40
46
|
// across sessions within one process: a topic registered once is never
|
|
@@ -84,6 +90,17 @@ export const KEVIN_CONFIG_KEYS = [
|
|
|
84
90
|
"share_requires_approval",
|
|
85
91
|
"author_identity_mode",
|
|
86
92
|
"shared_confidence_floor",
|
|
93
|
+
// v0.9.0 (K9-003 / plan §8.10) — the four keys seeded by migration 010
|
|
94
|
+
// section 5. Omitting these makes `kevin_config set` return
|
|
95
|
+
// { error: "unknown_key" } while `kevin_config list` still shows them.
|
|
96
|
+
// hook_liveness_enabled and the two registration/history flags must be
|
|
97
|
+
// compared with === "1" (they are TEXT, and '0' is truthy);
|
|
98
|
+
// dead_hook_report_threshold is a string read with Number.parseInt and
|
|
99
|
+
// clamped to [1, 1000], NaN defaulting to 3 (conventions, §2; D9-09).
|
|
100
|
+
"hook_liveness_enabled",
|
|
101
|
+
"native_registration_enabled",
|
|
102
|
+
"host_probe_history_enabled",
|
|
103
|
+
"dead_hook_report_threshold",
|
|
87
104
|
];
|
|
88
105
|
// v0.7.0 (K7-003 / plan §5.6, D7-12) — the explicit VALUE domain for
|
|
89
106
|
// `error_lesson_mode`. The setting is TEXT and must be compared with
|
|
@@ -93,7 +110,7 @@ export const KEVIN_CONFIG_KEYS = [
|
|
|
93
110
|
// behaviour on the next reflection.
|
|
94
111
|
export const ERROR_LESSON_MODE_VALUES = ["all", "triage_only"];
|
|
95
112
|
/** Plugin release version — stamped into generated files (K8-021/027). */
|
|
96
|
-
export const KEVIN_VERSION = "0.
|
|
113
|
+
export const KEVIN_VERSION = "0.9.0";
|
|
97
114
|
function resolveMigrationsDir() {
|
|
98
115
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
99
116
|
return join(here, "..", "migrations");
|
|
@@ -235,7 +252,12 @@ export const KevinPlugin = async (input, options) => {
|
|
|
235
252
|
// (plan §5.7 fallback; D2-11 project scoping wired into the live path).
|
|
236
253
|
// v0.8.0 (K8-006 / plan §5.1): the repository identity resolves once at
|
|
237
254
|
// init, next to the project id, and never on a hot path.
|
|
238
|
-
|
|
255
|
+
// v0.9.0 (K9-004/K9-006 / plan §5.1-5.2, D9-12/D9-13): the host surface
|
|
256
|
+
// is probed once at construction — frozen, never re-probed — and feeds
|
|
257
|
+
// the identity chain as the third source, above `path` and below
|
|
258
|
+
// `declared`/`remote`.
|
|
259
|
+
const host = await probeHost(input);
|
|
260
|
+
const identity = RepoIdentity.resolve(process.cwd(), host);
|
|
239
261
|
const projectId = identity.projectId;
|
|
240
262
|
const repoId = identity.repoId;
|
|
241
263
|
const identitySource = identity.source;
|
|
@@ -249,6 +271,27 @@ export const KevinPlugin = async (input, options) => {
|
|
|
249
271
|
// not what the session is scoped on.
|
|
250
272
|
let sessionIdentity = identity;
|
|
251
273
|
const memoryService = new MemoryService(store, metrics, repoId);
|
|
274
|
+
// v0.9.0 (K9-008 / plan §5.1, D9-08) — host probe history: one
|
|
275
|
+
// host_probes row per construction when host_probe_history_enabled
|
|
276
|
+
// is explicitly '1' (TEXT comparison — truthiness would fire on
|
|
277
|
+
// '0'). Append-only and unbounded by design: off by default, one
|
|
278
|
+
// row per process start, no retention policy.
|
|
279
|
+
if (memoryService.getSetting("host_probe_history_enabled", "0") === "1") {
|
|
280
|
+
store
|
|
281
|
+
.prepare(`INSERT INTO host_probes
|
|
282
|
+
(id, plugin_version, flavour, has_shell, v2_skill, v2_reference, notes)
|
|
283
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
284
|
+
.run(uuidv7(), host.pluginVersion, host.flavour, host.hasShell ? 1 : 0, host.v2.skill ? 1 : 0, host.v2.reference ? 1 : 0, host.notes.join("\n"));
|
|
285
|
+
}
|
|
286
|
+
// v0.9.0 (K9-009 / plan §5.3, D9-07/D9-08) — hook liveness. Wraps every
|
|
287
|
+
// hook with a success-path recorder; counters persist on the
|
|
288
|
+
// metrics.flush() cadence. Explicit === "1" TEXT comparison
|
|
289
|
+
// (kevin_settings.value is TEXT).
|
|
290
|
+
const liveness = new HookLiveness(store, {
|
|
291
|
+
enabled: memoryService.getSetting("hook_liveness_enabled", "1") === "1",
|
|
292
|
+
thresholdText: memoryService.getSetting("dead_hook_report_threshold", "3"),
|
|
293
|
+
pluginVersion: host.pluginVersion,
|
|
294
|
+
});
|
|
252
295
|
// v0.7.0 (K7-009 / plan §5.1, D7-13) — the repository truth scanner reads
|
|
253
296
|
// the JSON project files. Runs once at init, gated by repo_truth_enabled.
|
|
254
297
|
const projectRoot = opts.projectRoot ?? process.cwd();
|
|
@@ -334,6 +377,24 @@ export const KevinPlugin = async (input, options) => {
|
|
|
334
377
|
mkdirSync(join(materializerRoot, "refs"), { recursive: true });
|
|
335
378
|
const capabilities = probe(input);
|
|
336
379
|
const materializer = new Materializer(store, { root: materializerRoot });
|
|
380
|
+
// v0.9.0 (K9-016 / plan §5.4, D9-10) — native registration replaces
|
|
381
|
+
// file emission. When attachNative returns a registration for a
|
|
382
|
+
// surface, the corresponding *_emission_enabled path is skipped for
|
|
383
|
+
// that surface only. The guard lives in Materializer.
|
|
384
|
+
try {
|
|
385
|
+
const nativeReg = await attachNative(host, {
|
|
386
|
+
materializer,
|
|
387
|
+
settings: memoryService,
|
|
388
|
+
store,
|
|
389
|
+
});
|
|
390
|
+
if (nativeReg) {
|
|
391
|
+
materializer.markNativeRegistered("skill", nativeReg.registered.skill);
|
|
392
|
+
materializer.markNativeRegistered("reference", nativeReg.registered.reference);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
// attachNative never throws (D9-12) — guard against unexpected rejection.
|
|
397
|
+
}
|
|
337
398
|
// v0.6.0 (K6-018 / plan §8.14, D6-13) — skill emission at session
|
|
338
399
|
// start, behind the capability probe AND `skill_emission_enabled`
|
|
339
400
|
// (default '0'). Both no-op paths are silent: "unavailable" (v1 host)
|
|
@@ -445,7 +506,11 @@ export const KevinPlugin = async (input, options) => {
|
|
|
445
506
|
projectId,
|
|
446
507
|
}));
|
|
447
508
|
}
|
|
448
|
-
|
|
509
|
+
// v0.9.0 (K9-009 / plan §5.3) — the hooks object is wrapped here at the
|
|
510
|
+
// point of return: transparent (identical keys/arity/returns/errors),
|
|
511
|
+
// recording fires on the success path only. With hook_liveness_enabled
|
|
512
|
+
// '0' the wrapper returns this same object untouched.
|
|
513
|
+
return liveness.wrap({
|
|
449
514
|
tool: {
|
|
450
515
|
kevin_save: tool({
|
|
451
516
|
description: "Guarda una memoria en el conocimiento persistente de Kevin (error, pattern, decision o context).",
|
|
@@ -783,8 +848,8 @@ export const KevinPlugin = async (input, options) => {
|
|
|
783
848
|
// monotone across releases; K6-024 extends this
|
|
784
849
|
// block with the remaining v0.6 fields. v0.8.0
|
|
785
850
|
// (K8-025) — 18 v0.7 + kevin_project +
|
|
786
|
-
//
|
|
787
|
-
tool_count:
|
|
851
|
+
// kevin_native = 23.
|
|
852
|
+
tool_count: 23,
|
|
788
853
|
v07,
|
|
789
854
|
memories_reflector: memoriesReflector,
|
|
790
855
|
memories_agent: memoriesAgent,
|
|
@@ -819,6 +884,10 @@ export const KevinPlugin = async (input, options) => {
|
|
|
819
884
|
v06,
|
|
820
885
|
// v0.8.0 (K8-025) — omitted on pre-009 DBs.
|
|
821
886
|
v08,
|
|
887
|
+
// v0.9.0 (K9-008 / plan §5.1) — one-line host
|
|
888
|
+
// summary: version, flavour, shell, v2 flags;
|
|
889
|
+
// no paths, no ids (charset-safe for log lines).
|
|
890
|
+
host_summary: summarize(host),
|
|
822
891
|
}),
|
|
823
892
|
};
|
|
824
893
|
},
|
|
@@ -846,7 +915,7 @@ export const KevinPlugin = async (input, options) => {
|
|
|
846
915
|
path: res.path,
|
|
847
916
|
id: res.id,
|
|
848
917
|
created_at: res.createdAt,
|
|
849
|
-
generator: "opencode-kevin/0.
|
|
918
|
+
generator: "opencode-kevin/0.9.0",
|
|
850
919
|
}
|
|
851
920
|
: {
|
|
852
921
|
action: "init",
|
|
@@ -968,6 +1037,48 @@ export const KevinPlugin = async (input, options) => {
|
|
|
968
1037
|
};
|
|
969
1038
|
},
|
|
970
1039
|
}),
|
|
1040
|
+
// v0.9.0 (K9-018 / plan §5.5, D9-09) — kevin_doctor: pure
|
|
1041
|
+
// reads, no writes, no probe re-run, no model call. The host
|
|
1042
|
+
// block comes from the frozen init-time probe, hooks from the
|
|
1043
|
+
// persisted hook_liveness table, native from the last
|
|
1044
|
+
// registration per surface, verdict from reduceVerdict.
|
|
1045
|
+
kevin_doctor: tool({
|
|
1046
|
+
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.",
|
|
1047
|
+
args: {},
|
|
1048
|
+
async execute() {
|
|
1049
|
+
const report = buildDoctor(store, host, memoryService);
|
|
1050
|
+
return {
|
|
1051
|
+
title: "Doctor de Kevin",
|
|
1052
|
+
output: JSON.stringify(report),
|
|
1053
|
+
};
|
|
1054
|
+
},
|
|
1055
|
+
}),
|
|
1056
|
+
// v0.9.0 (K9-019 / plan §5.5, D9-12) — kevin_native: inspect
|
|
1057
|
+
// and toggle native_registration_enabled. enable/disable write
|
|
1058
|
+
// kevin_settings only and never re-attach — the probe is
|
|
1059
|
+
// frozen for the process lifetime, so a restart is the
|
|
1060
|
+
// requirement for the change to take effect. `enable` on a
|
|
1061
|
+
// host without the v2 subpath succeeds and reports the
|
|
1062
|
+
// registration as inert: the setting is a statement of intent.
|
|
1063
|
+
kevin_native: tool({
|
|
1064
|
+
description: "Registros nativos v2 (v0.9.0): show reporta el setting native_registration_enabled, si el host resuelto expone el subpath v2 (effective) y las ultimas filas de native_registrations por surface; enable/disable escriben el setting ('1'/'0' TEXT) y NO re-adjuntan — el probe es frozen para la vida del proceso, un restart del host es el requisito para que el cambio tenga efecto; enable en un host sin subpath v2 exito con registration inert.",
|
|
1065
|
+
args: {
|
|
1066
|
+
action: tool.schema
|
|
1067
|
+
.enum(["show", "enable", "disable"])
|
|
1068
|
+
.default("show"),
|
|
1069
|
+
},
|
|
1070
|
+
async execute(args) {
|
|
1071
|
+
const report = handleNative(args.action, {
|
|
1072
|
+
host,
|
|
1073
|
+
store,
|
|
1074
|
+
settings: memoryService,
|
|
1075
|
+
});
|
|
1076
|
+
return {
|
|
1077
|
+
title: "Kevin native",
|
|
1078
|
+
output: JSON.stringify(report),
|
|
1079
|
+
};
|
|
1080
|
+
},
|
|
1081
|
+
}),
|
|
971
1082
|
kevin_retrospective: tool({
|
|
972
1083
|
description: "Genera un retrospective markdown para una sesion (resume tools que fallaron y lecciones aprendidas).",
|
|
973
1084
|
args: {
|
|
@@ -1418,6 +1529,13 @@ export const KevinPlugin = async (input, options) => {
|
|
|
1418
1529
|
}, {});
|
|
1419
1530
|
},
|
|
1420
1531
|
"tool.execute.after": async (hookInput, output) => {
|
|
1532
|
+
// v0.9.0 (K9-010 / plan §5.3) — checkpoint: the session reached a
|
|
1533
|
+
// model turn, so the system prompt was assembled and
|
|
1534
|
+
// `experimental.chat.system.transform` MUST have been offered.
|
|
1535
|
+
// Deduped per session inside HookLiveness.expect.
|
|
1536
|
+
if (hookInput.sessionID) {
|
|
1537
|
+
liveness.expect("experimental.chat.system.transform", hookInput.sessionID);
|
|
1538
|
+
}
|
|
1421
1539
|
const meta = (output.metadata ?? {});
|
|
1422
1540
|
const outputText = String(output.output ?? "");
|
|
1423
1541
|
const stderr = String(meta.stderr ?? "");
|
|
@@ -1637,6 +1755,9 @@ export const KevinPlugin = async (input, options) => {
|
|
|
1637
1755
|
}
|
|
1638
1756
|
}
|
|
1639
1757
|
metrics.flush();
|
|
1758
|
+
// v0.9.0 (K9-009 / plan §5.3) — liveness counters persist on
|
|
1759
|
+
// the same cadence as the metrics.
|
|
1760
|
+
liveness.flush();
|
|
1640
1761
|
}
|
|
1641
1762
|
else if (type === "session.next.tool.failed") {
|
|
1642
1763
|
const callID = props.callID;
|
|
@@ -1654,10 +1775,11 @@ export const KevinPlugin = async (input, options) => {
|
|
|
1654
1775
|
},
|
|
1655
1776
|
dispose: async () => {
|
|
1656
1777
|
await Promise.allSettled([...pending]);
|
|
1778
|
+
liveness.flush();
|
|
1657
1779
|
metrics.close();
|
|
1658
1780
|
store.close();
|
|
1659
1781
|
},
|
|
1660
|
-
};
|
|
1782
|
+
});
|
|
1661
1783
|
};
|
|
1662
1784
|
export default KevinPlugin;
|
|
1663
1785
|
//# sourceMappingURL=index.js.map
|