knodin 0.7.6 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +19 -7
  2. package/benchmarks/competitors/SYNTHESIS.md +66 -0
  3. package/dist/bin/cli.js +2164 -108
  4. package/dist/bin/launcher.js +25 -3
  5. package/dist/src/agent-integration.js +304 -0
  6. package/dist/src/artifact-refresh.js +82 -0
  7. package/dist/src/cli-args.js +292 -0
  8. package/dist/src/cli-model.js +384 -0
  9. package/dist/src/codeflow-replay.js +81 -0
  10. package/dist/src/compact-structural.js +96 -0
  11. package/dist/src/compare.js +39 -0
  12. package/dist/src/competitive-cold-mcp.js +40 -0
  13. package/dist/src/competitive-constraints.js +21 -0
  14. package/dist/src/competitive-manifest.js +411 -0
  15. package/dist/src/competitive-measurement.js +183 -0
  16. package/dist/src/competitive-runner.js +487 -0
  17. package/dist/src/competitive-sandbox.js +108 -0
  18. package/dist/src/context-export.js +423 -0
  19. package/dist/src/context.js +102 -0
  20. package/dist/src/deterministic-random.js +34 -0
  21. package/dist/src/diagnostics-write-helper.js +473 -0
  22. package/dist/src/diagnostics.js +1476 -0
  23. package/dist/src/docs-sections.js +141 -0
  24. package/dist/src/doctor.js +382 -0
  25. package/dist/src/engine/ann-hnsw.js +261 -0
  26. package/dist/src/engine/embeddings.js +193 -0
  27. package/dist/src/engine/file-walker.js +49 -0
  28. package/dist/src/engine/git-history.js +289 -0
  29. package/dist/src/engine/index.js +14238 -0
  30. package/dist/src/engine/perf.js +115 -0
  31. package/dist/src/engine/prune.js +112 -0
  32. package/dist/src/engine/sarif-import.js +341 -0
  33. package/dist/src/engine/scip-import.js +423 -0
  34. package/dist/src/engine/source-policy.js +85 -0
  35. package/dist/src/engine/sqlite.js +71 -0
  36. package/dist/src/engine/state-paths.js +175 -0
  37. package/dist/src/engine/symbol-delete.js +58 -0
  38. package/dist/src/execution-profile.js +208 -0
  39. package/dist/src/failure-diagnosis.js +655 -0
  40. package/dist/src/fleet.js +7 -0
  41. package/dist/src/git-executable.js +31 -0
  42. package/dist/src/graph-layout.js +173 -0
  43. package/dist/src/graph-query-health.js +115 -0
  44. package/dist/src/hook-manager-integration.js +156 -0
  45. package/dist/src/index-activity.js +126 -0
  46. package/dist/src/init-progress-worker.js +106 -2
  47. package/dist/src/init-progress.js +155 -0
  48. package/dist/src/init.js +1295 -0
  49. package/dist/src/lifecycle-health.js +282 -0
  50. package/dist/src/lsp-readonly.js +217 -0
  51. package/dist/src/mcp-graph-worker.js +69 -0
  52. package/dist/src/mcp-reliability.js +154 -0
  53. package/dist/src/mcp-worker-supervisor.js +350 -0
  54. package/dist/src/mirror.js +290 -0
  55. package/dist/src/node-runtime.js +157 -0
  56. package/dist/src/output-compression.js +630 -0
  57. package/dist/src/output-telemetry.js +368 -0
  58. package/dist/src/pr-triage.js +638 -0
  59. package/dist/src/progressive-evidence.js +477 -0
  60. package/dist/src/pure-compression-cli.js +102 -0
  61. package/dist/src/relationship-adapters.js +377 -0
  62. package/dist/src/release-attestation.js +533 -0
  63. package/dist/src/release-preflight.js +513 -0
  64. package/dist/src/repair-lease.js +85 -0
  65. package/dist/src/repair-progress-worker.js +120 -2
  66. package/dist/src/repair-progress.js +262 -0
  67. package/dist/src/repository-init-process.js +177 -0
  68. package/dist/src/repository-management.js +1261 -0
  69. package/dist/src/response-budget.js +196 -0
  70. package/dist/src/server.js +217 -0
  71. package/dist/src/structural-fast-path.js +344 -0
  72. package/dist/src/structural-snapshot.js +37 -0
  73. package/dist/src/system-config.js +638 -0
  74. package/dist/src/terminal-help.js +83 -0
  75. package/dist/src/tools/knodin-tools.js +1640 -0
  76. package/dist/src/update-ceremony.js +162 -0
  77. package/dist/src/update-policy.js +944 -0
  78. package/dist/src/update-trust.js +504 -0
  79. package/dist/src/version.js +13 -0
  80. package/dist/src/visualization.js +515 -0
  81. package/dist/src/wait-for-fresh.js +98 -0
  82. package/dist/src/worktree-lifecycle.js +234 -0
  83. package/docs/BEHAVIORAL-CONTRACT.md +72 -0
  84. package/docs/CLI.md +20 -1
  85. package/docs/COMPARISON.md +403 -0
  86. package/docs/COMPETITIVE-LANDSCAPE-2026-08.md +267 -0
  87. package/docs/CONTAINED-EXECUTION.md +77 -0
  88. package/docs/DIAGNOSTICS.md +80 -0
  89. package/docs/GIT-HISTORY-REVIEW.md +39 -0
  90. package/docs/HANDOFF.md +180 -0
  91. package/docs/INSTALLATION.md +21 -18
  92. package/docs/MCP.md +59 -8
  93. package/docs/PROGRESSIVE-EVIDENCE.md +37 -0
  94. package/docs/PT-ACCESS-RECOMMENDATION.md +89 -0
  95. package/docs/RELEASE-0.3-EVIDENCE.md +73 -0
  96. package/docs/REPOSITORIES-AND-WORKTREES.md +18 -6
  97. package/docs/SCIP-IMPORT.md +62 -0
  98. package/docs/SIGNED-UPDATES.md +151 -0
  99. package/docs/TELEMETRY.md +46 -0
  100. package/docs/TOKEN-OPTIMIZER-SCORECARD.md +79 -0
  101. package/docs/assets/knodin-favicon.svg +4 -0
  102. package/docs/releases/0.3.0.md +46 -0
  103. package/docs/releases/0.4.0.md +68 -0
  104. package/docs/releases/0.4.1.md +28 -0
  105. package/docs/releases/0.4.2.md +27 -0
  106. package/docs/releases/0.4.3.md +23 -0
  107. package/docs/releases/0.5.0.md +29 -0
  108. package/docs/releases/0.5.1.md +17 -0
  109. package/docs/releases/0.6.0.md +18 -0
  110. package/docs/releases/0.7.0.md +24 -0
  111. package/docs/releases/0.7.1.md +21 -0
  112. package/docs/releases/0.7.2.md +21 -0
  113. package/docs/releases/0.7.3.md +23 -0
  114. package/docs/releases/0.7.4.md +17 -0
  115. package/docs/releases/0.7.5.md +20 -0
  116. package/docs/releases/0.8.0.md +74 -0
  117. package/docs/releases/0.8.2.md +34 -0
  118. package/package.json +127 -4
  119. package/roadmap/competitive-roadmap.md +3801 -0
  120. package/schemas/release-attestation-v1.schema.json +210 -0
  121. package/schemas/support-bundle-v2.schema.json +212 -0
  122. package/dist/chunks/chunk-DMQAGX77.js +0 -654
  123. package/dist/chunks/chunk-F4Z3Z766.js +0 -4
  124. package/dist/chunks/chunk-SIJAQVSX.js +0 -3
  125. package/dist/chunks/chunk-X6M4HUUE.js +0 -2
  126. package/dist/chunks/chunk-YPRMY2LP.js +0 -8
  127. package/dist/chunks/pure-compression-cli-4TA2TQD5.js +0 -5
  128. package/dist/chunks/server-7EDF4CBY.js +0 -14
  129. package/dist/chunks/structural-fast-path-KD5KQSPX.js +0 -4
  130. package/docs/releases/0.7.6.md +0 -25
@@ -0,0 +1,162 @@
1
+ import { createHash, createPublicKey } from "node:crypto";
2
+ import { compareBytes } from "./compare.js";
3
+ import { canonicalizeUpdateMetadata, verifyUpdateRootChain, } from "./update-trust.js";
4
+ const SHA256 = /^[0-9a-f]{64}$/;
5
+ const ID = /^[A-Za-z0-9][A-Za-z0-9._-]{2,63}$/;
6
+ function exactKeys(value, expected, label) {
7
+ const actual = Object.keys(value).sort(compareBytes);
8
+ const wanted = [...expected].sort(compareBytes);
9
+ if (actual.join("\0") !== wanted.join("\0"))
10
+ throw new Error(`${label} contains missing or unknown fields`);
11
+ }
12
+ function rejectPrivateMaterial(value, path = "input") {
13
+ if (typeof value === "string" && /PRIVATE KEY|recovery secret|seed phrase/i.test(value))
14
+ throw new Error(`${path} contains private material`);
15
+ if (!value || typeof value !== "object")
16
+ return;
17
+ for (const [key, child] of Object.entries(value)) {
18
+ if (/private|secret|seed|mnemonic|password|token/i.test(key))
19
+ throw new Error(`${path}.${key} is forbidden`);
20
+ rejectPrivateMaterial(child, `${path}.${key}`);
21
+ }
22
+ }
23
+ export function validateRootCeremonyManifest(value) {
24
+ rejectPrivateMaterial(value);
25
+ if (!value || typeof value !== "object" || Array.isArray(value))
26
+ throw new Error("manifest must be an object");
27
+ const manifest = value;
28
+ exactKeys(manifest, [
29
+ "schemaVersion",
30
+ "ceremonyId",
31
+ "purpose",
32
+ "productionActivation",
33
+ "custodians",
34
+ "roleKeyIds",
35
+ "thresholds",
36
+ ], "manifest");
37
+ if (manifest.schemaVersion !== 1 ||
38
+ !ID.test(manifest.ceremonyId) ||
39
+ !["rehearsal", "production"].includes(manifest.purpose) ||
40
+ manifest.productionActivation !== false)
41
+ throw new Error("invalid fail-closed ceremony header");
42
+ if (!Array.isArray(manifest.custodians) ||
43
+ manifest.custodians.length < 3 ||
44
+ new Set(manifest.custodians.map((c) => c.id)).size !== manifest.custodians.length ||
45
+ manifest.custodians.some((c) => {
46
+ exactKeys(c, c.role === "root-signer"
47
+ ? ["id", "role", "publicKeyId"]
48
+ : ["id", "role", "recoveryKeyIds", "evidenceId"], "custodian");
49
+ return (!ID.test(c.id) ||
50
+ !["root-signer", "recovery"].includes(c.role) ||
51
+ (c.role === "root-signer" && !SHA256.test(c.publicKeyId ?? "")) ||
52
+ (c.role === "recovery" &&
53
+ (!ID.test(c.evidenceId ?? "") ||
54
+ !Array.isArray(c.recoveryKeyIds) ||
55
+ c.recoveryKeyIds.some((id) => !SHA256.test(id)))));
56
+ }))
57
+ throw new Error("at least three distinct named custodians are required");
58
+ const roles = ["root", "targets", "snapshot", "timestamp"];
59
+ exactKeys(manifest.roleKeyIds, roles, "roleKeyIds");
60
+ exactKeys(manifest.thresholds, roles, "thresholds");
61
+ const all = new Set();
62
+ for (const role of roles) {
63
+ const ids = manifest.roleKeyIds?.[role];
64
+ const threshold = manifest.thresholds?.[role];
65
+ if (!Array.isArray(ids) ||
66
+ ids.length === 0 ||
67
+ ids.some((id) => !SHA256.test(id) || all.has(id)))
68
+ throw new Error("role keys must be valid and disjoint");
69
+ for (const id of ids)
70
+ all.add(id);
71
+ if (!Number.isInteger(threshold) ||
72
+ threshold < (role === "root" || role === "targets" ? 2 : 1) ||
73
+ threshold > ids.length)
74
+ throw new Error(`invalid ${role} threshold`);
75
+ }
76
+ if (manifest.custodians.filter((c) => c.role === "root-signer").length < manifest.thresholds.root ||
77
+ !manifest.custodians.some((c) => c.role === "recovery"))
78
+ throw new Error("custodian roles do not satisfy threshold and recovery separation");
79
+ const assigned = manifest.custodians
80
+ .filter((c) => c.role === "root-signer")
81
+ .map((c) => c.publicKeyId)
82
+ // `publicKeyId` is optional on the type but validated present for every
83
+ // root-signer above; `sort` also never passes undefined to a comparator.
84
+ .sort((left, right) => compareBytes(left ?? "", right ?? ""));
85
+ if (assigned.join("\0") !== [...manifest.roleKeyIds.root].sort(compareBytes).join("\0"))
86
+ throw new Error("root signer assignments must exactly match root role keys");
87
+ const recovery = manifest.custodians.filter((c) => c.role === "recovery");
88
+ if (recovery.length < manifest.thresholds.root ||
89
+ new Set(recovery.map((c) => c.evidenceId)).size !== recovery.length ||
90
+ recovery.some((c) => !c.recoveryKeyIds?.length ||
91
+ new Set(c.recoveryKeyIds).size !== c.recoveryKeyIds.length ||
92
+ c.recoveryKeyIds.length >= manifest.thresholds.root))
93
+ throw new Error("recovery sets must preserve root threshold independence");
94
+ const recovered = new Set(recovery.flatMap((c) => c.recoveryKeyIds ?? []));
95
+ if (manifest.roleKeyIds.root.some((id) => !recovered.has(id)))
96
+ throw new Error("recovery assignments must cover every root role key");
97
+ return manifest;
98
+ }
99
+ export function validateRootPinReviewReceipt(value, manifest) {
100
+ rejectPrivateMaterial(value);
101
+ if (!value || typeof value !== "object" || Array.isArray(value))
102
+ throw new Error("receipt must be an object");
103
+ const receipt = value;
104
+ exactKeys(receipt, [
105
+ "schemaVersion",
106
+ "ceremonyId",
107
+ "rootEnvelopeSha256",
108
+ "reviewerId",
109
+ "reviewedAt",
110
+ "decision",
111
+ "authentication",
112
+ "productionActivation",
113
+ ], "receipt");
114
+ if (receipt.schemaVersion !== 1 ||
115
+ receipt.ceremonyId !== manifest.ceremonyId ||
116
+ !SHA256.test(receipt.rootEnvelopeSha256) ||
117
+ !ID.test(receipt.reviewerId) ||
118
+ manifest.custodians.some((c) => c.id === receipt.reviewerId) ||
119
+ receipt.decision !== "approved" ||
120
+ receipt.authentication !== "rehearsal-unsigned" ||
121
+ receipt.productionActivation !== false ||
122
+ !Number.isFinite(Date.parse(receipt.reviewedAt)) ||
123
+ new Date(receipt.reviewedAt).toISOString() !== receipt.reviewedAt)
124
+ throw new Error("invalid or non-independent pin-review receipt");
125
+ return receipt;
126
+ }
127
+ export function preparePublicRootImport(envelope, pin, manifestValue, receiptValue, now = new Date()) {
128
+ rejectPrivateMaterial(envelope, "rootEnvelope");
129
+ const manifest = validateRootCeremonyManifest(manifestValue);
130
+ if (manifest.purpose === "production")
131
+ throw new Error("production import requires externally authenticated review and remains blocked");
132
+ const receipt = validateRootPinReviewReceipt(receiptValue, manifest);
133
+ if (Date.parse(receipt.reviewedAt) > now.getTime())
134
+ throw new Error("pin-review receipt cannot be future-dated");
135
+ const roles = ["root", "targets", "snapshot", "timestamp"];
136
+ for (const role of roles) {
137
+ const expected = manifest.roleKeyIds[role];
138
+ const actual = envelope.signed.roles[role];
139
+ if (actual.threshold !== manifest.thresholds[role] ||
140
+ [...actual.keyids].sort(compareBytes).join("\0") !==
141
+ [...expected].sort(compareBytes).join("\0"))
142
+ throw new Error(`manifest ${role} role does not match root envelope`);
143
+ }
144
+ for (const key of Object.values(envelope.signed.keys)) {
145
+ let canonical = "";
146
+ try {
147
+ canonical = createPublicKey(key.keyval.public)
148
+ .export({ type: "spki", format: "pem" })
149
+ .toString();
150
+ }
151
+ catch {
152
+ /* fail below */
153
+ }
154
+ if (canonical !== key.keyval.public)
155
+ throw new Error("root envelope keys must use canonical public SPKI PEM only");
156
+ }
157
+ const digest = createHash("sha256").update(canonicalizeUpdateMetadata(envelope)).digest("hex");
158
+ if (pin !== digest || receipt.rootEnvelopeSha256 !== digest)
159
+ throw new Error("root envelope, pin, and independent review receipt disagree");
160
+ verifyUpdateRootChain(envelope, pin, [], now);
161
+ return { rootEnvelope: envelope, rootEnvelopeSha256: digest, productionActivation: false };
162
+ }