knodin 0.8.2 → 0.8.4
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 +36 -13
- package/dist/bin/cli.js +248 -62
- package/dist/src/agent-events.js +128 -0
- package/dist/src/agent-hooks.js +156 -0
- package/dist/src/cli-model.js +16 -1
- package/dist/src/docs-sections.js +1 -0
- package/dist/src/engine/index.js +911 -55
- package/dist/src/init-progress-worker.js +4 -40
- package/dist/src/output-telemetry.js +8 -3
- package/dist/src/progress-worker-runtime.js +46 -0
- package/dist/src/repair-progress-worker.js +3 -40
- package/dist/src/resource-reachability.js +456 -0
- package/dist/src/response-budget.js +69 -65
- package/dist/src/session-telemetry.js +163 -0
- package/dist/src/tools/knodin-tools.js +12 -7
- package/docs/BEHAVIORAL-CONTRACT.md +47 -5
- package/docs/CLI.md +27 -0
- package/docs/COMPARISON.md +10 -0
- package/docs/DEMO.md +49 -0
- package/docs/INSTALLATION.md +11 -0
- package/docs/MCP.md +5 -0
- package/docs/TELEMETRY.md +19 -1
- package/docs/releases/0.8.3.md +47 -0
- package/docs/releases/0.8.4.md +22 -0
- package/package.json +14 -1
- package/roadmap/competitive-roadmap.md +115 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const CONFIG = ".knodin/telemetry-config.json";
|
|
5
|
+
const EVENTS = ".knodin/session-events.jsonl";
|
|
6
|
+
const KEY = ".knodin/telemetry-key";
|
|
7
|
+
function target(repo, relative) {
|
|
8
|
+
const root = fs.realpathSync(repo);
|
|
9
|
+
const value = path.resolve(root, relative);
|
|
10
|
+
if (!value.startsWith(`${root}${path.sep}`))
|
|
11
|
+
throw new Error("knodin telemetry path escaped repository");
|
|
12
|
+
let candidate = root;
|
|
13
|
+
for (const segment of path.relative(root, value).split(path.sep)) {
|
|
14
|
+
candidate = path.join(candidate, segment);
|
|
15
|
+
try {
|
|
16
|
+
if (fs.lstatSync(candidate).isSymbolicLink())
|
|
17
|
+
throw new Error("knodin telemetry refuses symlinks");
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (error.code !== "ENOENT")
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function atomic(filePath, value) {
|
|
27
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
28
|
+
const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
29
|
+
fs.writeFileSync(temporary, value, { mode: 0o600, flag: "wx" });
|
|
30
|
+
fs.renameSync(temporary, filePath);
|
|
31
|
+
}
|
|
32
|
+
function enabled(repo) {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(fs.readFileSync(target(repo, CONFIG), "utf8")).enabled === true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function enableSessionTelemetry(repo) {
|
|
41
|
+
atomic(target(repo, CONFIG), `${JSON.stringify({ schemaVersion: 1, enabled: true })}\n`);
|
|
42
|
+
const key = target(repo, KEY);
|
|
43
|
+
if (!fs.existsSync(key))
|
|
44
|
+
atomic(key, crypto.randomBytes(32).toString("hex"));
|
|
45
|
+
return { schemaVersion: 1, enabled: true, localOnly: true };
|
|
46
|
+
}
|
|
47
|
+
export function disableSessionTelemetry(repo) {
|
|
48
|
+
atomic(target(repo, CONFIG), `${JSON.stringify({ schemaVersion: 1, enabled: false })}\n`);
|
|
49
|
+
return { schemaVersion: 1, enabled: false, retained: readSessionEvents(repo).length };
|
|
50
|
+
}
|
|
51
|
+
function sessionHash(repo, sessionId) {
|
|
52
|
+
const secret = fs.readFileSync(target(repo, KEY), "utf8").trim();
|
|
53
|
+
return `session_${crypto.createHmac("sha256", secret).update(sessionId).digest("hex").slice(0, 20)}`;
|
|
54
|
+
}
|
|
55
|
+
export function appendSessionEvent(repo, input) {
|
|
56
|
+
if (!enabled(repo))
|
|
57
|
+
return false;
|
|
58
|
+
if (!input.sessionId || input.sessionId.length > 256)
|
|
59
|
+
return false;
|
|
60
|
+
const record = {
|
|
61
|
+
schemaVersion: 1,
|
|
62
|
+
event: input.event,
|
|
63
|
+
session: sessionHash(repo, input.sessionId),
|
|
64
|
+
at: input.at ?? new Date().toISOString(),
|
|
65
|
+
client: input.client ?? "claude",
|
|
66
|
+
...(input.context ? { context: input.context } : {}),
|
|
67
|
+
...(input.toolCategory ? { toolCategory: input.toolCategory } : {}),
|
|
68
|
+
...(input.operation && /^[a-z][a-z0-9_-]{0,63}$/.test(input.operation)
|
|
69
|
+
? { operation: input.operation }
|
|
70
|
+
: {}),
|
|
71
|
+
...(input.freshness ? { freshness: input.freshness } : {}),
|
|
72
|
+
...(typeof input.latencyMs === "number" ? { latencyMs: input.latencyMs } : {}),
|
|
73
|
+
...(typeof input.cacheHit === "boolean" ? { cacheHit: input.cacheHit } : {}),
|
|
74
|
+
};
|
|
75
|
+
const filePath = target(repo, EVENTS);
|
|
76
|
+
fs.appendFileSync(filePath, `${JSON.stringify(record)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
export function readSessionEvents(repo, retentionDays, now = new Date()) {
|
|
80
|
+
const filePath = target(repo, EVENTS);
|
|
81
|
+
if (!fs.existsSync(filePath))
|
|
82
|
+
return [];
|
|
83
|
+
const cutoff = retentionDays === undefined ? undefined : now.getTime() - retentionDays * 24 * 60 * 60 * 1000;
|
|
84
|
+
return fs
|
|
85
|
+
.readFileSync(filePath, "utf8")
|
|
86
|
+
.split(/\r?\n/)
|
|
87
|
+
.filter(Boolean)
|
|
88
|
+
.flatMap((line) => {
|
|
89
|
+
try {
|
|
90
|
+
const value = JSON.parse(line);
|
|
91
|
+
if (value.schemaVersion !== 1 || typeof value.session !== "string")
|
|
92
|
+
return [];
|
|
93
|
+
if (cutoff !== undefined) {
|
|
94
|
+
const timestamp = typeof value.at === "string" ? Date.parse(value.at) : Number.NaN;
|
|
95
|
+
if (!Number.isFinite(timestamp) || timestamp < cutoff)
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
return [value];
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
export function summarizeAdoption(events) {
|
|
106
|
+
const sessions = new Set(events.map(({ session }) => session));
|
|
107
|
+
const evidenceOperations = new Set(["context", "explain", "review", "map", "search", "query"]);
|
|
108
|
+
const knodinSessions = new Set(events
|
|
109
|
+
.filter(({ event, toolCategory, operation }) => event === "tool_success" &&
|
|
110
|
+
toolCategory === "knodin" &&
|
|
111
|
+
operation !== undefined &&
|
|
112
|
+
evidenceOperations.has(operation))
|
|
113
|
+
.map(({ session }) => session));
|
|
114
|
+
let fallbackTraversalAfterKnodin = 0;
|
|
115
|
+
let evidenceBeforeEdit = 0;
|
|
116
|
+
const seenKnodin = new Set();
|
|
117
|
+
for (const event of events) {
|
|
118
|
+
if (event.event === "turn_start")
|
|
119
|
+
seenKnodin.delete(event.session);
|
|
120
|
+
if (event.toolCategory === "knodin" &&
|
|
121
|
+
event.event === "tool_success" &&
|
|
122
|
+
event.operation !== undefined &&
|
|
123
|
+
evidenceOperations.has(event.operation))
|
|
124
|
+
seenKnodin.add(event.session);
|
|
125
|
+
if (event.event === "tool_success" &&
|
|
126
|
+
event.toolCategory === "manual_traversal" &&
|
|
127
|
+
seenKnodin.has(event.session))
|
|
128
|
+
fallbackTraversalAfterKnodin += 1;
|
|
129
|
+
if (event.event === "tool_success" &&
|
|
130
|
+
event.toolCategory === "edit" &&
|
|
131
|
+
seenKnodin.has(event.session))
|
|
132
|
+
evidenceBeforeEdit += 1;
|
|
133
|
+
if (event.event === "turn_end")
|
|
134
|
+
seenKnodin.delete(event.session);
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
sessions: sessions.size,
|
|
138
|
+
contextDelivered: events.filter(({ event, context }) => event === "session_start" && context === "delivered").length,
|
|
139
|
+
contextDegraded: events.filter(({ event, context }) => event === "session_start" && context === "degraded").length,
|
|
140
|
+
knodinSessions: knodinSessions.size,
|
|
141
|
+
turns: events.filter(({ event }) => event === "turn_start").length,
|
|
142
|
+
fallbackTraversalAfterKnodin,
|
|
143
|
+
evidenceBeforeEdit,
|
|
144
|
+
correctiveRoundTrips: null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
export function sessionTelemetryStatus(repo, retentionDays) {
|
|
148
|
+
const records = readSessionEvents(repo, retentionDays);
|
|
149
|
+
return {
|
|
150
|
+
schemaVersion: 1,
|
|
151
|
+
enabled: enabled(repo),
|
|
152
|
+
localOnly: true,
|
|
153
|
+
records: records.length,
|
|
154
|
+
adoption: summarizeAdoption(records),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export function clearSessionTelemetry(repo) {
|
|
158
|
+
const filePath = target(repo, EVENTS);
|
|
159
|
+
const records = readSessionEvents(repo).length;
|
|
160
|
+
const bytes = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0;
|
|
161
|
+
fs.rmSync(filePath, { force: true });
|
|
162
|
+
return { removed: bytes > 0, records, bytes };
|
|
163
|
+
}
|
|
@@ -116,6 +116,7 @@ const QUERY_PATTERNS = [
|
|
|
116
116
|
"batch_outline",
|
|
117
117
|
"project_overview",
|
|
118
118
|
"shortest_path",
|
|
119
|
+
"cross_substrate_path",
|
|
119
120
|
"impact",
|
|
120
121
|
"dead_code",
|
|
121
122
|
"large_functions",
|
|
@@ -127,6 +128,7 @@ const QUERY_PATTERNS = [
|
|
|
127
128
|
"traverse",
|
|
128
129
|
"feature_path",
|
|
129
130
|
"flow_analysis",
|
|
131
|
+
"resource_reachability",
|
|
130
132
|
"knowledge_gaps",
|
|
131
133
|
"surprising_connections",
|
|
132
134
|
"suggested_questions",
|
|
@@ -227,15 +229,15 @@ function buildDocumentedKnodinTools() {
|
|
|
227
229
|
},
|
|
228
230
|
toIdentity: {
|
|
229
231
|
type: "string",
|
|
230
|
-
description: "shortest_path destination identity selector.",
|
|
232
|
+
description: "shortest_path/cross_substrate_path destination identity selector.",
|
|
231
233
|
},
|
|
232
234
|
toFile: {
|
|
233
235
|
type: "string",
|
|
234
|
-
description: "shortest_path destination file selector.",
|
|
236
|
+
description: "shortest_path/cross_substrate_path destination file selector.",
|
|
235
237
|
},
|
|
236
238
|
toKind: {
|
|
237
239
|
type: "string",
|
|
238
|
-
description: "shortest_path destination kind selector.",
|
|
240
|
+
description: "shortest_path/cross_substrate_path destination kind selector.",
|
|
239
241
|
},
|
|
240
242
|
pattern: {
|
|
241
243
|
type: "string",
|
|
@@ -243,7 +245,7 @@ function buildDocumentedKnodinTools() {
|
|
|
243
245
|
description: "feature_path — deterministic downstream DFS over resolved source references. It is bounded by depth and item limits, source-evidenced per hop, cycle-guarded, and never claims runtime execution; dynamic dispatch and unresolved calls are omitted. " +
|
|
244
246
|
"lsp_diagnostics accepts a repo-relative TypeScript/JavaScript file; lsp_definitions, lsp_declarations, and lsp_implementations accept file:line:column. These use an optional local TypeScript language-service adapter, never start a daemon or write files, and return an explicit unavailable result when no local adapter supports the file type. " +
|
|
245
247
|
"api_contract_mismatches — bounded, source-evidenced static mismatch findings for literal Express/Fastify routes and literal fetch/Axios clients. Matching requires method and normalized path, and requires exact origin equality when a client uses an absolute origin; dynamic routes remain explicitly unresolved. mcp_tools — list indexed TypeScript MCP SDK tool registrations, or pass symbol to look up one exact tool name; returns description/schema/handler/file and exact versus heuristic confidence. import_cycles — repo-wide canonical directed file-import cycles; no symbol required, type-only imports included, non-import lineage/ORM edges excluded, and limit/truncated bounds output. " +
|
|
246
|
-
"query: the graph pattern to run (callers_of, callees_of, imports_of, importers_of, inheritors_of, structural_implementations_of, tests_for, file_summary, shortest_path, impact, dead_code, large_functions, large_files, rename_preview, flows, flow_of, stats, traverse, knowledge_gaps, community, federated_repos). structural_implementations_of returns TypeScript structurally typed object implementations separately from nominal extends/implements results. federated_repos — discover and list all registered/configured repository paths that this engine federates queries across. community — fetch a single community's full detail (name, size, cohesion, files, and symbols) by name or substring filter; empty `symbol` = all communities. large_files — whole-file line counts (min 200), sorted descending — the file-level counterpart to large_functions, for spotting god files rather than god functions; same optional path-substring filter, empty `symbol` = all files. stats — repo-level index size + health in one call (per-repo symbol/reference/dependency/embedding/file counts, per-kind and per-language breakdowns, orphaned-embedding count, schema version, last-indexed HEAD, and cached community/hub/bridge/flow totals); federated, empty `symbol`. traverse — typed BFS from `symbol` within `depth` hops (default 3, 1–6), selectable upstream/downstream/both and explicit edge kinds; every discovered edge includes direction, kind, provenance, confidence, files, and source line, with optional Tree-sitter-grounded argument expressions honestly marked heuristic. Sets `truncated` when the `limit` cap binds; single-repo. knowledge_gaps — repo-health weaknesses in one call: thin communities (<3 symbols), single-file communities, isolated (zero-degree) symbols, and untested hub/bridge hotspots; sourced from the cached map(), federated, empty `symbol`, one row carrying a `knowledgeGaps` block. surprising_connections — resolved edges scored by a composite surprise formula (cross-community, cross-language, peripheral-to-hub, cross-test-boundary, unusual edge kind) to surface unexpected coupling; sorted highest-first, top 15 by default, federated, empty `symbol`, each row carrying a `surprise` block. suggested_questions — prioritized, human-readable review prompts synthesized from knowledge_gaps + surprising_connections (untested hotspots first, then surprising edges, then thin/single-file communities); federated, empty `symbol`, each row a `question` string citing the real symbol/file, top 10 by default. architecture_overview — a federated architecture view with scoped community coupling and independently selectable packages, layers, boundaries, hotspots, entry points, and language facets; `path` is segment-safe and applies consistently.
|
|
248
|
+
"query: the graph pattern to run (callers_of, callees_of, imports_of, importers_of, inheritors_of, structural_implementations_of, tests_for, file_summary, shortest_path, cross_substrate_path, impact, dead_code, large_functions, large_files, rename_preview, flows, flow_of, flow_analysis, resource_reachability, stats, traverse, knowledge_gaps, community, federated_repos). cross_substrate_path proves only one static Salesforce Flow action to one uniquely resolved Apex @InvocableMethod and returns exact endpoint identities/evidence, freshness, budgets, and explicit unsupported crossings; it does not resolve Terraform, dbt, or arbitrary substrate paths. resource_reachability is a repo-wide, bounded, cached, on-demand TS/JS static heuristic for literal process.env/fs.readFileSync sources reaching console.log/fetch/db.query sinks; it reports evidence, coverage, omissions, freshness, truncation, and continuation and is never runtime reachability or exploitability proof. structural_implementations_of returns TypeScript structurally typed object implementations separately from nominal extends/implements results. federated_repos — discover and list all registered/configured repository paths that this engine federates queries across. community — fetch a single community's full detail (name, size, cohesion, files, and symbols) by name or substring filter; empty `symbol` = all communities. large_files — whole-file line counts (min 200), sorted descending — the file-level counterpart to large_functions, for spotting god files rather than god functions; same optional path-substring filter, empty `symbol` = all files. stats — repo-level index size + health in one call (per-repo symbol/reference/dependency/embedding/file counts, per-kind and per-language breakdowns, orphaned-embedding count, schema version, last-indexed HEAD, and cached community/hub/bridge/flow totals); federated, empty `symbol`. traverse — typed BFS from `symbol` within `depth` hops (default 3, 1–6), selectable upstream/downstream/both and explicit edge kinds; every discovered edge includes direction, kind, provenance, confidence, files, and source line, with optional Tree-sitter-grounded argument expressions honestly marked heuristic. Sets `truncated` when the `limit` cap binds; single-repo. knowledge_gaps — repo-health weaknesses in one call: thin communities (<3 symbols), single-file communities, isolated (zero-degree) symbols, and untested hub/bridge hotspots; sourced from the cached map(), federated, empty `symbol`, one row carrying a `knowledgeGaps` block. surprising_connections — resolved edges scored by a composite surprise formula (cross-community, cross-language, peripheral-to-hub, cross-test-boundary, unusual edge kind) to surface unexpected coupling; sorted highest-first, top 15 by default, federated, empty `symbol`, each row carrying a `surprise` block. suggested_questions — prioritized, human-readable review prompts synthesized from knowledge_gaps + surprising_connections (untested hotspots first, then surprising edges, then thin/single-file communities); federated, empty `symbol`, each row a `question` string citing the real symbol/file, top 10 by default. architecture_overview — a federated architecture view with scoped community coupling and independently selectable packages, layers, boundaries, hotspots, entry points, and language facets; `path` is segment-safe and applies consistently. Spring/event patterns (Java): triggers_of — the schedule edge between a @Scheduled method and its synthetic scheduler (pass the method to get its scheduler, or the scheduler to list scheduled methods). publishers_of — methods that publish a given event type (ApplicationEventPublisher.publishEvent). listeners_of — @EventListener methods that listen for a given event type. handlers_of — methods that handle a given HTTP endpoint path (Spring @*Mapping + the existing JS/Python endpoints). endpoints_for — the inverse: endpoint path(s) a given method handles. consumers_of — classes/methods consuming a given @Value config property. children_of — symbols contained in a file path (same as file_summary) or, for a class name, its member symbols by line-range containment. All seven require `symbol` (the target); single-repo, not federated.",
|
|
247
249
|
},
|
|
248
250
|
depth: {
|
|
249
251
|
type: "number",
|
|
@@ -292,7 +294,7 @@ function buildDocumentedKnodinTools() {
|
|
|
292
294
|
},
|
|
293
295
|
to: {
|
|
294
296
|
type: "string",
|
|
295
|
-
description: "query shortest_path: destination symbol (start symbol goes in `symbol`). query rename_preview: the new name (old name goes in `symbol`). Read-only by default (returns edit sites + a unified diff); pass `apply: true` to write the rename to disk.",
|
|
297
|
+
description: "query shortest_path/cross_substrate_path: destination symbol (start symbol goes in `symbol`). query rename_preview: the new name (old name goes in `symbol`). Read-only by default (returns edit sites + a unified diff); pass `apply: true` to write the rename to disk.",
|
|
296
298
|
},
|
|
297
299
|
apply: {
|
|
298
300
|
type: "boolean",
|
|
@@ -1569,8 +1571,8 @@ async function dispatchKnodinTool(args) {
|
|
|
1569
1571
|
const repoWide = REPO_WIDE_QUERY_PATTERNS.includes(pattern);
|
|
1570
1572
|
if (!symbol && !repoWide)
|
|
1571
1573
|
throw new Error(`knodin query ${pattern} requires \`symbol\``);
|
|
1572
|
-
if (pattern === "shortest_path" && !to)
|
|
1573
|
-
throw new Error(
|
|
1574
|
+
if ((pattern === "shortest_path" || pattern === "cross_substrate_path") && !to)
|
|
1575
|
+
throw new Error(`knodin query ${pattern} requires \`to\``);
|
|
1574
1576
|
if (pattern === "rename_preview" && !to)
|
|
1575
1577
|
throw new Error("knodin query rename_preview requires `symbol` and `to`");
|
|
1576
1578
|
const queryHealth = GRAPH_INDEPENDENT_QUERY_PATTERNS.has(pattern)
|
|
@@ -1602,6 +1604,9 @@ async function dispatchKnodinTool(args) {
|
|
|
1602
1604
|
direction: pattern === "traverse" ? direction : undefined,
|
|
1603
1605
|
includeDataFlow: pattern === "traverse" ? includeDataFlow : undefined,
|
|
1604
1606
|
flowVariable: pattern === "flow_analysis" ? flowVariable : undefined,
|
|
1607
|
+
resourceOffset: pattern === "resource_reachability" ? offset : undefined,
|
|
1608
|
+
resourceMaxBytes: pattern === "resource_reachability" ? byteBudget : undefined,
|
|
1609
|
+
resourceMaxTokens: pattern === "resource_reachability" ? tokenBudget : undefined,
|
|
1605
1610
|
architectureFacets,
|
|
1606
1611
|
topN,
|
|
1607
1612
|
sort,
|
|
@@ -16,10 +16,11 @@ action-specific response semantics fail closed when a required signal is absent.
|
|
|
16
16
|
- **Value:** less manual context assembly and fewer missed dependencies. The
|
|
17
17
|
paired task method and its bounded results are checked in under
|
|
18
18
|
[`benchmarks/evaluations/c93-engineering-outcomes/`](../benchmarks/evaluations/c93-engineering-outcomes/).
|
|
19
|
-
- **ROI:** fewer corrective agent round trips without
|
|
20
|
-
authentication, or source-egress overhead. C93
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
- **ROI:** the intended mechanism is fewer corrective agent round trips without
|
|
20
|
+
hosted-service, account, authentication, or source-egress overhead. C93's
|
|
21
|
+
accepted two-task replay measured no round-trip benefit; both arms needed one
|
|
22
|
+
correction. It therefore authorizes no productivity claim and leaves billed
|
|
23
|
+
cost and unobserved token counts unavailable.
|
|
23
24
|
- **Tomorrow:** run `knodin init`, inspect `knodin status`, then use the existing
|
|
24
25
|
checkout through the CLI or the single `knodin.knodin` MCP gateway. The
|
|
25
26
|
installation and client readiness evidence remains bounded by C91 below.
|
|
@@ -35,6 +36,13 @@ preserving its stable identity and evidence provenance. Stale, unavailable, or
|
|
|
35
36
|
ambiguous graph evidence must remain explicit and cannot be promoted to a
|
|
36
37
|
confident failure diagnosis, impact claim, or runtime-causality proof.
|
|
37
38
|
|
|
39
|
+
`ExplainResult.callers[].kind` and `ExplainResult.omissions[]` are governed by
|
|
40
|
+
the existing explain mapping: relationship kinds require source provenance and
|
|
41
|
+
stable resolved endpoints, while omissions require an exact source location and
|
|
42
|
+
must never be promoted into a graph edge. C99 exercises these added fields
|
|
43
|
+
without adding a gateway operation or discriminator, so the C95 applicability
|
|
44
|
+
matrix is unchanged.
|
|
45
|
+
|
|
38
46
|
The C95 fixtures run in a linked non-default worktree. A child harness installs
|
|
39
47
|
guards before importing the production dispatcher and observes global `fetch`,
|
|
40
48
|
HTTP/HTTPS request and get, TCP/TLS connection, callback and promise DNS
|
|
@@ -46,6 +54,39 @@ credential forwarding, source bytes at network/subprocess boundaries, and
|
|
|
46
54
|
native-addon loads must remain zero. Local subprocess execution is counted and
|
|
47
55
|
permitted only when those observed payload checks remain clean.
|
|
48
56
|
|
|
57
|
+
## C101 composition closure
|
|
58
|
+
|
|
59
|
+
C101 composes four independently bounded decisions without adding an MCP tool
|
|
60
|
+
or operation:
|
|
61
|
+
|
|
62
|
+
- C97 retained lazy semantic residency in one local process. It does **not**
|
|
63
|
+
authorize an idle-RSS-reclamation claim.
|
|
64
|
+
- C98 reports literal TS/JS source-to-sink paths as static heuristics with
|
|
65
|
+
coverage, omissions, freshness, budgets, and continuation. Truncation cannot
|
|
66
|
+
become a complete-result claim.
|
|
67
|
+
- C99 persists only uniquely source-resolved Inversify/tsyringe relationships;
|
|
68
|
+
ambiguous, dynamic, factory, alias, conditional, and reflective wiring stays
|
|
69
|
+
omission evidence rather than an edge.
|
|
70
|
+
- C100 proves one Salesforce Flow action to one uniquely resolved Apex
|
|
71
|
+
`@InvocableMethod`. It does not prove Terraform, dbt, arbitrary Salesforce
|
|
72
|
+
metadata, or general cross-substrate traversal.
|
|
73
|
+
|
|
74
|
+
`npm run verify:c101` source-binds those decisions, checks the CLI and one-tool
|
|
75
|
+
MCP query declarations, and rejects eight adversarial mutations of actual
|
|
76
|
+
production responses. Stale resource/path evidence and heuristic-to-exact
|
|
77
|
+
promotion fail; a truncated result cannot be presented as complete; and a DI
|
|
78
|
+
ambiguity, missing source witness, or path omission cannot become a supported
|
|
79
|
+
edge.
|
|
80
|
+
|
|
81
|
+
## Repository stewardship
|
|
82
|
+
|
|
83
|
+
An authoritative pull request starts the full test matrix and Sonar analysis,
|
|
84
|
+
so it is a long, resource-intensive operation. Assistants must not automatically
|
|
85
|
+
open one PR per item. Dependency-compatible, coherently scoped commits should
|
|
86
|
+
normally be stacked into one reasonably sized shared PR; unrelated work must
|
|
87
|
+
not be bundled merely to make the PR larger. The user or approved batch decides
|
|
88
|
+
when that review unit is ready.
|
|
89
|
+
|
|
49
90
|
## Known bounds
|
|
50
91
|
|
|
51
92
|
This replay proves the checked operation/action fixtures, not all repositories,
|
|
@@ -53,7 +94,8 @@ languages, frameworks, dynamic runtime behavior, clients, or deployment
|
|
|
53
94
|
environments. Static relationships and failure correlations are candidates,
|
|
54
95
|
not runtime-causality proof. Budgets can truncate results; indexes can be stale
|
|
55
96
|
or unavailable; names can be ambiguous; generated code, dynamic dispatch,
|
|
56
|
-
framework wiring
|
|
97
|
+
framework wiring outside C99's declared Inversify/tsyringe patterns, and
|
|
98
|
+
language coverage can leave relationships unresolved.
|
|
57
99
|
|
|
58
100
|
C91 remains parked pending native client, runtime-manager, Homebrew, and
|
|
59
101
|
Artifactory certification evidence. C94 remains blocked on C91 and therefore
|
package/docs/CLI.md
CHANGED
|
@@ -32,6 +32,33 @@ example repository existence, mutually exclusive configuration modes, and
|
|
|
32
32
|
graph-query target rules), but syntax cannot reach a handler unless the shared
|
|
33
33
|
declarative model accepts it first.
|
|
34
34
|
|
|
35
|
+
Optional Claude lifecycle integration is explicit and user-global:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
knodin agent-hooks install --client claude --dry-run
|
|
39
|
+
knodin agent-hooks install --client claude
|
|
40
|
+
knodin agent-hooks status --client claude
|
|
41
|
+
knodin agent-hooks uninstall --client claude
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Installation atomically merges knodin-owned `SessionStart`, turn, tool, and
|
|
45
|
+
session-end handlers into the user's Claude settings. It preserves unrelated
|
|
46
|
+
settings and hooks, including aidev-track, and uninstall removes only exact
|
|
47
|
+
knodin entries. SessionStart emits at most 600 tokens, 8 KiB, and 12 bounded
|
|
48
|
+
orientation items. Hooks fail open and no-op outside initialized repositories.
|
|
49
|
+
The internal `agent-event` command is machine-facing and consumes bounded JSON
|
|
50
|
+
from stdin; it is not a general event-ingestion API.
|
|
51
|
+
|
|
52
|
+
`knodin query resource_reachability --limit 100` runs cached, on-demand static
|
|
53
|
+
TS/JS source-to-sink analysis. It covers literal `process.env` and
|
|
54
|
+
`fs.readFileSync` reads reaching `console.log`, `fetch`, or `db.query` through
|
|
55
|
+
bounded assignment, argument, and return handoff. Results include ordered source
|
|
56
|
+
evidence, stable path identities, registry coverage, omissions, freshness, and
|
|
57
|
+
hard truncation/continuation metadata. Depth is fixed at 6 and paths at 100;
|
|
58
|
+
dynamic names, reflection, computed aliases, and unsupported languages are
|
|
59
|
+
reported rather than guessed. This is a static heuristic, not runtime reachability
|
|
60
|
+
or an exploitability verdict.
|
|
61
|
+
|
|
35
62
|
For support evidence, `knodin diagnostics enable|status|preview|archive|inspect|clear|disable`
|
|
36
63
|
manages an explicit local failure journal and redacted gzip JSON bundles.
|
|
37
64
|
Collection does not upload data. See `docs/DIAGNOSTICS.md` for retention,
|
package/docs/COMPARISON.md
CHANGED
|
@@ -51,6 +51,16 @@ the evaluation's competitor limitations. The active
|
|
|
51
51
|
[competitive roadmap](../roadmap/competitive-roadmap.md) links each claim to
|
|
52
52
|
its acceptance criteria and replay state.
|
|
53
53
|
|
|
54
|
+
The current positioning lead is the checked behavioral contract, not the raw
|
|
55
|
+
capability count. C98 adds a bounded TS/JS resource-reachability heuristic; C99
|
|
56
|
+
adds only uniquely source-resolved Inversify/tsyringe relationships; C100 proves
|
|
57
|
+
one Salesforce Flow action to one Apex `@InvocableMethod`; and C97 retains the
|
|
58
|
+
one-process semantic design without claiming idle RSS reclamation. C101 binds
|
|
59
|
+
those decisions and rejects stale, truncated, ambiguous, or omission-only
|
|
60
|
+
results when promoted into complete or exact claims. None of these fixtures
|
|
61
|
+
supports general runtime causality, arbitrary cross-substrate paths, broad DI
|
|
62
|
+
framework coverage, or universal competitor superiority.
|
|
63
|
+
|
|
54
64
|
## The quartet: knodin vs its three direct progenitors
|
|
55
65
|
|
|
56
66
|
knodin exists to fuse three tools this project already used side by side
|
package/docs/DEMO.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Bounded product walkthrough
|
|
2
|
+
|
|
3
|
+
This is the maintained positioning walkthrough, not C94's pending certified
|
|
4
|
+
five-minute client demonstration. C94 remains parked behind native installation
|
|
5
|
+
and client evidence; this document must not be cited as completing it.
|
|
6
|
+
|
|
7
|
+
## Value
|
|
8
|
+
|
|
9
|
+
On an existing checkout, initialize and orient before assembling context by
|
|
10
|
+
hand:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
knodin init
|
|
14
|
+
knodin status --deep
|
|
15
|
+
knodin context "change the review submission path"
|
|
16
|
+
knodin query impact submit --direction upstream
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The intended value is less manual context assembly and fewer missed
|
|
20
|
+
dependencies. C100 demonstrates one narrow improvement: a Salesforce Flow
|
|
21
|
+
action can resolve to the exact local Apex `@InvocableMethod`, rather than only
|
|
22
|
+
its class file. It does not demonstrate arbitrary cross-substrate analysis.
|
|
23
|
+
|
|
24
|
+
## ROI
|
|
25
|
+
|
|
26
|
+
Use the same local CLI or one MCP gateway against the checkout already on disk;
|
|
27
|
+
no hosted index, account, authentication, or source-code egress is required.
|
|
28
|
+
Fewer corrective round trips are an intended mechanism, not a measured product
|
|
29
|
+
claim: C93's accepted two-task replay found no round-trip difference and both
|
|
30
|
+
arms required one correction.
|
|
31
|
+
|
|
32
|
+
## Tomorrow
|
|
33
|
+
|
|
34
|
+
After `knodin init`, use `context`, `explain`, structured `query`, and `review`
|
|
35
|
+
through the CLI or the single operation-routed MCP tool. If status reports stale
|
|
36
|
+
or damaged state, repair it before relying on graph evidence.
|
|
37
|
+
|
|
38
|
+
## Secret sauce and bounds
|
|
39
|
+
|
|
40
|
+
Stable identity, exact source evidence, explicit freshness, and truthful
|
|
41
|
+
budgets compose behind the compact local surface. C101 mutates actual production
|
|
42
|
+
responses for resource reachability, TypeScript DI, and one Flow-to-Apex path;
|
|
43
|
+
stale, truncated, ambiguous, or evidence-free variants must fail the contract
|
|
44
|
+
oracle. C97 separately retains one-process semantic residency and authorizes no
|
|
45
|
+
idle-RSS-reclamation claim.
|
|
46
|
+
|
|
47
|
+
These checked fixtures do not prove runtime causality, broad DI framework
|
|
48
|
+
coverage, arbitrary Salesforce/Terraform/dbt paths, every client or platform,
|
|
49
|
+
or superiority over competitors.
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -205,6 +205,17 @@ idempotent. Local fixtures use zero hosted spend, no credentials, and no source
|
|
|
205
205
|
egress. Diagnostic archives remain governed by the privacy-safe preview and
|
|
206
206
|
archive contract in [`DIAGNOSTICS.md`](DIAGNOSTICS.md).
|
|
207
207
|
|
|
208
|
+
Claude users may explicitly install one user-global lifecycle adapter after
|
|
209
|
+
previewing its settings merge:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
knodin agent-hooks install --client claude --dry-run
|
|
213
|
+
knodin agent-hooks install --client claude
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
This is independent of `knodin init` and telemetry persistence. Remove only
|
|
217
|
+
knodin-owned entries with `knodin agent-hooks uninstall --client claude`.
|
|
218
|
+
|
|
208
219
|
Upgrade with the manager that owns the executable:
|
|
209
220
|
|
|
210
221
|
```bash
|
package/docs/MCP.md
CHANGED
|
@@ -4,6 +4,11 @@ knodin exposes exactly one MCP tool named `knodin`. Capabilities such as
|
|
|
4
4
|
context, explain, review, search, docs, doctor, repositories, and systems are
|
|
5
5
|
operations of that gateway, not separate top-level tools.
|
|
6
6
|
|
|
7
|
+
Use `operation: "query", pattern: "resource_reachability"` for the same bounded,
|
|
8
|
+
cached, repo-wide TS/JS analysis as the CLI. It requires no symbol and retains
|
|
9
|
+
the gateway's freshness refusal and response budgets; returned paths remain
|
|
10
|
+
source-evidenced static heuristics with explicit coverage and omissions.
|
|
11
|
+
|
|
7
12
|
Use `operation: "evidence"` for deterministic `locate`, `outline`, `evidence`,
|
|
8
13
|
and `expand` source delivery with a verified complete-file hash handshake and
|
|
9
14
|
recoverable hard-budget continuations. See `docs/PROGRESSIVE-EVIDENCE.md`.
|
package/docs/TELEMETRY.md
CHANGED
|
@@ -25,13 +25,31 @@ knodin telemetry export
|
|
|
25
25
|
knodin telemetry clear
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
Claude adoption evidence is an independent repository-local opt-in:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
knodin telemetry enable
|
|
32
|
+
knodin telemetry disable
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
When enabled and the optional Claude hooks are installed, knodin records HMAC'd
|
|
36
|
+
session identity, lifecycle event kind, turn boundaries, coarse tool category,
|
|
37
|
+
supported knodin operation, freshness, context delivery, cache state, and
|
|
38
|
+
latency. It never persists prompts, transcript paths, source, raw tool input or
|
|
39
|
+
output, shell command text, usernames, or absolute paths. Disable stops new
|
|
40
|
+
session events without deleting retained history; `clear` removes both
|
|
41
|
+
operation and session event stores.
|
|
42
|
+
|
|
28
43
|
`--retention-days 1..3650` changes the read/persistence window. `--input` and
|
|
29
44
|
`--output` accept only repository-contained, non-symlink paths. `clear` is the
|
|
30
45
|
only command that deletes the private JSONL file; report and export never do.
|
|
31
46
|
|
|
32
47
|
The default report is `.knodin/telemetry-report.html`. It includes summary,
|
|
33
48
|
operation, time-series, private repository, indexing-cost, latency, freshness,
|
|
34
|
-
|
|
49
|
+
fidelity, session adoption, evidence-before-edit, and observed fallback-traversal
|
|
50
|
+
views with light/dark styling. Ordinary hooks cannot establish why a follow-up
|
|
51
|
+
occurred, so corrective round trips and task success remain unavailable unless
|
|
52
|
+
supplied by a controlled outcome replay. The default machine-readable export
|
|
35
53
|
is `.knodin/telemetry-export.json` and labels measured, modeled, and unknown
|
|
36
54
|
baselines explicitly. Neither artifact needs a daemon, hosted service,
|
|
37
55
|
authentication, or source egress.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# knodin 0.8.3
|
|
2
|
+
|
|
3
|
+
This release strengthens Knodin's source-evidenced graph across repository
|
|
4
|
+
lifecycle, cross-substrate analysis, and bounded organization-scale workflows.
|
|
5
|
+
|
|
6
|
+
- Adds bounded repository-wide resource reachability for source-proven
|
|
7
|
+
environment, file, fetch, console, and database paths. Results retain stable
|
|
8
|
+
identities, exact evidence, explicit unsupported cases, freshness, response
|
|
9
|
+
budgets, and deterministic continuation.
|
|
10
|
+
- Resolves source-proven TypeScript dependency-injection wiring for supported
|
|
11
|
+
Inversify and tsyringe forms. Framework and registry scoping, ambiguity,
|
|
12
|
+
conditional wiring, incremental refresh, and removal are represented without
|
|
13
|
+
guessing across containers or frameworks.
|
|
14
|
+
- Proves exact Salesforce Flow-to-Apex paths from Flow action metadata to unique
|
|
15
|
+
`@InvocableMethod` targets, including targeted Apex refresh, source evidence,
|
|
16
|
+
omissions, freshness, and bounded responses.
|
|
17
|
+
- Makes repair converge truthfully across schema migration, empty repositories,
|
|
18
|
+
final verification failure, and future-schema refusal. Scoped repairs avoid
|
|
19
|
+
unrelated TypeScript DI reads while still rebuilding affected DI evidence.
|
|
20
|
+
- Strengthens the public behavioral contract and positioning with replayed
|
|
21
|
+
production CLI, MCP, and engine responses. C97-C101 evidence is independently
|
|
22
|
+
replayable and source-bound; failed performance experiments remain recorded
|
|
23
|
+
as limitations rather than superiority claims.
|
|
24
|
+
- Prevents stale package and barrel-resolution caches, avoids caching transient
|
|
25
|
+
filesystem failures, and keeps independent engine instances from closing
|
|
26
|
+
resources still owned by another instance.
|
|
27
|
+
- Reuses a bounded, generation-scoped resource corpus so warm reachability
|
|
28
|
+
queries do not reread every indexed source file while still failing closed on
|
|
29
|
+
disk drift.
|
|
30
|
+
- Bounds cross-substrate evidence consistently with the outer response budget,
|
|
31
|
+
fixes member-call reachability false positives, and makes evidence verifiers
|
|
32
|
+
recompute their claim-bearing gates instead of trusting stored booleans.
|
|
33
|
+
- Removes duplicated progress-worker and response-budget machinery, updates the
|
|
34
|
+
audited development dependency set, and fixes lifecycle evaluation owners so
|
|
35
|
+
the complete test process terminates.
|
|
36
|
+
- Records that pull requests are intentionally expensive because Sonar and the
|
|
37
|
+
complete repository gates run on each review stream; compatible work should
|
|
38
|
+
be stacked into one coherent PR rather than opened automatically as many small
|
|
39
|
+
PRs.
|
|
40
|
+
|
|
41
|
+
The one-process semantic-residency evaluation remains a retained no-go: neither
|
|
42
|
+
in-process ONNX disposal, worker threads, quantized weights, nor vector/cache
|
|
43
|
+
eviction met the repeatable memory gate without violating the local one-process
|
|
44
|
+
product boundary. Knodin retains lazy loading and bounded caches rather than
|
|
45
|
+
claiming unsupported reclamation.
|
|
46
|
+
|
|
47
|
+
This remains an ordinary 0.x release, not a dogfood-only build and not GA.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# knodin 0.8.4
|
|
2
|
+
|
|
3
|
+
This release adds optional, bounded coding-session orientation and local
|
|
4
|
+
adoption evidence while preserving knodin's freshness and privacy boundaries.
|
|
5
|
+
|
|
6
|
+
- Adds an explicit, idempotent Claude hook installer for compact SessionStart
|
|
7
|
+
context. Injection is limited to 600 tokens, 8 KiB, and 12 items, reports its
|
|
8
|
+
bounds, rejects stale cached evidence, and fails open outside initialized
|
|
9
|
+
repositories.
|
|
10
|
+
- Adds opt-in, repository-local session telemetry and an adoption dashboard for
|
|
11
|
+
delivered context, successful graph-evidence operations, fallback traversal,
|
|
12
|
+
and evidence-before-edit observations.
|
|
13
|
+
- Persists only coarse allowlisted metadata and locally keyed session hashes;
|
|
14
|
+
prompts, source, raw tool input, commands, paths, and unhashed session IDs are
|
|
15
|
+
not retained.
|
|
16
|
+
- Keeps corrective round trips explicitly unavailable because ordinary hook
|
|
17
|
+
events cannot establish why an agent made a follow-up call or prove a causal
|
|
18
|
+
productivity improvement.
|
|
19
|
+
- Adds C102's preregistered paired-evaluation design so future outcome claims
|
|
20
|
+
require replay evidence rather than being inferred from adoption counters.
|
|
21
|
+
|
|
22
|
+
This remains an ordinary 0.x release, not a dogfood-only build and not GA.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knodin",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "knodin — source-evidenced local code intelligence with known bounds. Stable identity, fresh evidence, truthful budgets, and recoverable bounded views.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"docs/DOCTOR-AND-UPDATES.md",
|
|
26
26
|
"docs/DIAGNOSTICS.md",
|
|
27
27
|
"docs/BEHAVIORAL-CONTRACT.md",
|
|
28
|
+
"docs/DEMO.md",
|
|
28
29
|
"docs/COMPARISON.md",
|
|
29
30
|
"docs/COMPETITIVE-LANDSCAPE-2026-08.md",
|
|
30
31
|
"docs/INDEXING-POLICY-AND-PROVENANCE.md",
|
|
@@ -57,6 +58,8 @@
|
|
|
57
58
|
"docs/releases/0.7.5.md",
|
|
58
59
|
"docs/releases/0.8.0.md",
|
|
59
60
|
"docs/releases/0.8.2.md",
|
|
61
|
+
"docs/releases/0.8.3.md",
|
|
62
|
+
"docs/releases/0.8.4.md",
|
|
60
63
|
"docs/assets/knodin-favicon.svg",
|
|
61
64
|
"docs/SYSTEMS-AND-RELATIONSHIPS.md",
|
|
62
65
|
"docs/TELEMETRY.md",
|
|
@@ -114,6 +117,16 @@
|
|
|
114
117
|
"bench:c93": "tsx benchmarks/evaluations/c93-engineering-outcomes/runner.ts",
|
|
115
118
|
"verify:c93": "tsx scripts/verify-c93.ts",
|
|
116
119
|
"verify:c95": "tsx scripts/verify-c95.ts && vitest run src/__tests__/unit/behavior-contract.spec.ts src/__tests__/unit/failure-diagnosis.spec.ts src/__tests__/unit/impact.spec.ts src/__tests__/unit/freshness.spec.ts",
|
|
120
|
+
"bench:c97": "tsx benchmarks/evaluations/c97-one-process-semantic-residency/runner.ts",
|
|
121
|
+
"verify:c97": "tsx scripts/verify-c97.ts",
|
|
122
|
+
"bench:c98": "tsx benchmarks/evaluations/c98-resource-reachability/runner.ts",
|
|
123
|
+
"verify:c98": "tsx scripts/verify-c98.ts && vitest run src/__tests__/unit/resource-reachability.spec.ts",
|
|
124
|
+
"bench:c99": "tsx benchmarks/evaluations/c99-typescript-di/runner.ts",
|
|
125
|
+
"verify:c99": "tsx scripts/verify-c99.ts && vitest run src/__tests__/unit/typescript-di.spec.ts",
|
|
126
|
+
"bench:c100": "tsx benchmarks/evaluations/c100-flow-apex-path/runner.ts",
|
|
127
|
+
"verify:c100": "tsx scripts/verify-c100.ts && vitest run src/__tests__/unit/salesforce-metadata.spec.ts",
|
|
128
|
+
"bench:c101": "tsx benchmarks/evaluations/c101-contract-positioning/runner.ts",
|
|
129
|
+
"verify:c101": "tsx scripts/verify-c101.ts",
|
|
117
130
|
"bench:ann": "bun scripts/ann-bench.ts",
|
|
118
131
|
"bench:perf": "tsx scripts/perf-bench.ts",
|
|
119
132
|
"bench:competitive": "tsx scripts/competitive-bakeoff.ts",
|