kibi-cli 0.17.0 → 0.19.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.
Files changed (241) hide show
  1. package/dist/cli-json-command.d.ts +1 -2
  2. package/dist/cli-json-command.d.ts.map +1 -1
  3. package/dist/cli-json-command.js +88 -44
  4. package/dist/cli-operation-loader.d.ts +3 -0
  5. package/dist/cli-operation-loader.d.ts.map +1 -0
  6. package/dist/cli-operation-loader.js +58 -0
  7. package/dist/cli-operation-metadata.d.ts +82 -0
  8. package/dist/cli-operation-metadata.d.ts.map +1 -0
  9. package/dist/cli-operation-metadata.js +106 -0
  10. package/dist/cli-protocol.d.ts +1 -1
  11. package/dist/cli-protocol.d.ts.map +1 -1
  12. package/dist/cli-protocol.js +4 -3
  13. package/dist/cli-register-foundation.d.ts.map +1 -1
  14. package/dist/cli-register-foundation.js +14 -17
  15. package/dist/cli-register-json.d.ts +3 -0
  16. package/dist/cli-register-json.d.ts.map +1 -0
  17. package/dist/cli-register-json.js +35 -0
  18. package/dist/cli-register-maintenance.d.ts.map +1 -1
  19. package/dist/cli-register-maintenance.js +41 -8
  20. package/dist/cli-register-reporting.d.ts.map +1 -1
  21. package/dist/cli-register-reporting.js +18 -14
  22. package/dist/cli-register-skills.d.ts.map +1 -1
  23. package/dist/cli-register-skills.js +8 -6
  24. package/dist/cli.d.ts.map +1 -1
  25. package/dist/cli.js +3 -2
  26. package/dist/commands/aggregated-checks.d.ts.map +1 -1
  27. package/dist/commands/aggregated-checks.js +1 -0
  28. package/dist/commands/check.d.ts.map +1 -1
  29. package/dist/commands/check.js +30 -12
  30. package/dist/commands/coverage.d.ts +5 -0
  31. package/dist/commands/coverage.d.ts.map +1 -1
  32. package/dist/commands/coverage.js +6 -1
  33. package/dist/commands/discovery-shared.d.ts +2 -1
  34. package/dist/commands/discovery-shared.d.ts.map +1 -1
  35. package/dist/commands/discovery-shared.js +35 -13
  36. package/dist/commands/discovery-table.d.ts.map +1 -1
  37. package/dist/commands/discovery-table.js +96 -5
  38. package/dist/commands/engine.d.ts +8 -0
  39. package/dist/commands/engine.d.ts.map +1 -0
  40. package/dist/commands/engine.js +100 -0
  41. package/dist/commands/gaps.js +1 -1
  42. package/dist/commands/graph.js +1 -1
  43. package/dist/commands/query.d.ts.map +1 -1
  44. package/dist/commands/query.js +3 -1
  45. package/dist/commands/search.d.ts.map +1 -1
  46. package/dist/commands/search.js +3 -1
  47. package/dist/commands/status.d.ts.map +1 -1
  48. package/dist/commands/status.js +2 -1
  49. package/dist/commands/sync/cache.d.ts +12 -0
  50. package/dist/commands/sync/cache.d.ts.map +1 -1
  51. package/dist/commands/sync/cache.js +38 -1
  52. package/dist/commands/sync/persistence.d.ts +10 -1
  53. package/dist/commands/sync/persistence.d.ts.map +1 -1
  54. package/dist/commands/sync/persistence.js +276 -43
  55. package/dist/commands/sync/staging.d.ts +10 -1
  56. package/dist/commands/sync/staging.d.ts.map +1 -1
  57. package/dist/commands/sync/staging.js +84 -1
  58. package/dist/commands/sync.d.ts.map +1 -1
  59. package/dist/commands/sync.js +521 -27
  60. package/dist/commands/usage-metrics.d.ts +66 -2
  61. package/dist/commands/usage-metrics.d.ts.map +1 -1
  62. package/dist/commands/usage-metrics.js +34 -24
  63. package/dist/commands/usage-remediation.d.ts +9 -0
  64. package/dist/commands/usage-remediation.d.ts.map +1 -0
  65. package/dist/commands/usage-remediation.js +52 -0
  66. package/dist/engine-daemon.d.ts +3 -0
  67. package/dist/engine-daemon.d.ts.map +1 -0
  68. package/dist/engine-daemon.js +34 -0
  69. package/dist/engine.d.ts +63 -0
  70. package/dist/engine.d.ts.map +1 -0
  71. package/dist/engine.js +1256 -0
  72. package/dist/extractors/markdown.d.ts +8 -0
  73. package/dist/extractors/markdown.d.ts.map +1 -1
  74. package/dist/extractors/markdown.js +69 -2
  75. package/dist/extractors/relationships.d.ts +2 -0
  76. package/dist/extractors/relationships.d.ts.map +1 -1
  77. package/dist/extractors/relationships.js +15 -17
  78. package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
  79. package/dist/operations/modeling/logic-modeling.js +14 -5
  80. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  81. package/dist/operations/modeling/model-requirement.js +27 -2
  82. package/dist/operations/modeling/predicate-applyplan.d.ts +1 -1
  83. package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
  84. package/dist/operations/modeling/predicate-applyplan.js +18 -6
  85. package/dist/operations/modeling/predicate-inference.d.ts.map +1 -1
  86. package/dist/operations/modeling/predicate-inference.js +5 -17
  87. package/dist/operations/modeling/predicate-loader.js +1 -1
  88. package/dist/operations/modeling/predicate-types.d.ts +9 -1
  89. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  90. package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
  91. package/dist/operations/modeling/suggest-predicates.js +35 -11
  92. package/dist/operations/mutation/audit.js +1 -1
  93. package/dist/operations/mutation/contradictions.d.ts +1 -1
  94. package/dist/operations/mutation/contradictions.d.ts.map +1 -1
  95. package/dist/operations/mutation/contradictions.js +23 -18
  96. package/dist/operations/mutation/serialization.d.ts.map +1 -1
  97. package/dist/operations/mutation/serialization.js +6 -3
  98. package/dist/operations/mutation/upsert.d.ts +3 -1
  99. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  100. package/dist/operations/mutation/upsert.js +51 -12
  101. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  102. package/dist/operations/mutation/validate-upsert.js +18 -2
  103. package/dist/operations/mutation/validation.d.ts.map +1 -1
  104. package/dist/operations/mutation/validation.js +8 -0
  105. package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
  106. package/dist/operations/semantic-advisor/analysis-receipt.js +7 -1
  107. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  108. package/dist/operations/semantic-advisor/analyze-prose.js +76 -61
  109. package/dist/operations/semantic-advisor/clauses.d.ts +1 -0
  110. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  111. package/dist/operations/semantic-advisor/clauses.js +7 -7
  112. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +18 -0
  113. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -0
  114. package/dist/operations/semantic-advisor/ingestion-boundary.js +167 -0
  115. package/dist/operations/semantic-advisor/shared.d.ts +8 -0
  116. package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
  117. package/dist/operations/semantic-advisor/shared.js +37 -3
  118. package/dist/operations/semantic-advisor/types.d.ts +5 -0
  119. package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
  120. package/dist/prolog/codec.d.ts.map +1 -1
  121. package/dist/prolog/codec.js +9 -2
  122. package/dist/prolog.d.ts +14 -0
  123. package/dist/prolog.d.ts.map +1 -1
  124. package/dist/prolog.js +279 -69
  125. package/dist/public/diagnostic-usage.d.ts +17 -0
  126. package/dist/public/diagnostic-usage.d.ts.map +1 -0
  127. package/dist/public/diagnostic-usage.js +152 -0
  128. package/dist/public/distribution-parity.d.ts +90 -0
  129. package/dist/public/distribution-parity.d.ts.map +1 -0
  130. package/dist/public/distribution-parity.js +367 -0
  131. package/dist/public/impact/full-kb-quality.d.ts +2 -0
  132. package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
  133. package/dist/public/impact/full-kb-quality.js +53 -1
  134. package/dist/public/impact-diagnostics.d.ts +2 -0
  135. package/dist/public/impact-diagnostics.d.ts.map +1 -1
  136. package/dist/public/impact-diagnostics.js +1 -0
  137. package/dist/public/operations/catalog.d.ts +151 -6
  138. package/dist/public/operations/catalog.d.ts.map +1 -1
  139. package/dist/public/operations/check-executor.d.ts.map +1 -1
  140. package/dist/public/operations/check-executor.js +4 -0
  141. package/dist/public/operations/discovery-entities.d.ts.map +1 -1
  142. package/dist/public/operations/discovery-entities.js +3 -2
  143. package/dist/public/operations/discovery-executors.d.ts +6 -0
  144. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  145. package/dist/public/operations/discovery-executors.js +58 -5
  146. package/dist/public/operations/index.d.ts +4 -0
  147. package/dist/public/operations/index.d.ts.map +1 -1
  148. package/dist/public/operations/index.js +4 -0
  149. package/dist/public/operations/legacy-migration-plan.d.ts +113 -0
  150. package/dist/public/operations/legacy-migration-plan.d.ts.map +1 -0
  151. package/dist/public/operations/legacy-migration-plan.js +409 -0
  152. package/dist/public/operations/node-ports.d.ts.map +1 -1
  153. package/dist/public/operations/node-ports.js +90 -0
  154. package/dist/public/operations/prolog-json.d.ts.map +1 -1
  155. package/dist/public/operations/prolog-json.js +14 -4
  156. package/dist/public/operations/repair-plan.d.ts +184 -0
  157. package/dist/public/operations/repair-plan.d.ts.map +1 -0
  158. package/dist/public/operations/repair-plan.js +397 -0
  159. package/dist/public/operations/runtime-types.d.ts +38 -0
  160. package/dist/public/operations/runtime-types.d.ts.map +1 -1
  161. package/dist/public/operations/specs/check.d.ts +2 -2
  162. package/dist/public/operations/specs/check.js +2 -2
  163. package/dist/public/operations/specs/discovery.d.ts +1 -1
  164. package/dist/public/operations/specs/discovery.js +1 -1
  165. package/dist/public/operations/specs/modeling.d.ts +19 -1
  166. package/dist/public/operations/specs/modeling.d.ts.map +1 -1
  167. package/dist/public/operations/specs/modeling.js +16 -1
  168. package/dist/public/operations/specs/mutation-schemas.d.ts +96 -1
  169. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  170. package/dist/public/operations/specs/mutation-schemas.js +40 -1
  171. package/dist/public/operations/specs/reporting.d.ts +42 -1
  172. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  173. package/dist/public/operations/specs/reporting.js +70 -6
  174. package/dist/public/operations/specs/semantic.js +1 -1
  175. package/dist/public/operations/specs/upsert.d.ts +96 -1
  176. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  177. package/dist/public/operations/workspace-snapshot.d.ts +10 -0
  178. package/dist/public/operations/workspace-snapshot.d.ts.map +1 -0
  179. package/dist/public/operations/workspace-snapshot.js +20 -0
  180. package/dist/public/schemas/entity.d.ts.map +1 -1
  181. package/dist/public/schemas/entity.js +43 -1
  182. package/dist/public/skills/init-kibi/SKILL.md +2 -2
  183. package/dist/public/skills/kibi-freshness/SKILL.md +2 -2
  184. package/dist/public/skills/kibi-traceability/SKILL.md +8 -2
  185. package/dist/public/skills/kibi-usage/SKILL.md +12 -6
  186. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +1 -1
  187. package/dist/public/skills/kibi-usage/resources/logic-ir.md +1 -1
  188. package/dist/public/skills/kibi-usage/resources/operation-access.md +1 -1
  189. package/dist/public/skills/kibi-usage/resources/workflows.md +19 -2
  190. package/dist/public/telemetry-acceptance.d.ts +89 -0
  191. package/dist/public/telemetry-acceptance.d.ts.map +1 -0
  192. package/dist/public/telemetry-acceptance.js +597 -0
  193. package/dist/public/telemetry-remediation.d.ts +33 -0
  194. package/dist/public/telemetry-remediation.d.ts.map +1 -0
  195. package/dist/public/telemetry-remediation.js +257 -0
  196. package/dist/public/verification-receipt.d.ts +75 -0
  197. package/dist/public/verification-receipt.d.ts.map +1 -0
  198. package/dist/public/verification-receipt.js +129 -0
  199. package/dist/runtime/cli-runtime.d.ts.map +1 -1
  200. package/dist/runtime/cli-runtime.js +33 -37
  201. package/dist/schemas/entity.schema.json +96 -2
  202. package/dist/traceability/temp-kb.d.ts.map +1 -1
  203. package/dist/traceability/temp-kb.js +36 -3
  204. package/dist/types/entities.d.ts +1 -0
  205. package/dist/types/entities.d.ts.map +1 -1
  206. package/dist/utils/rule-registry.d.ts +2 -0
  207. package/dist/utils/rule-registry.d.ts.map +1 -1
  208. package/package.json +36 -4
  209. package/src/public/diagnostic-usage.ts +206 -0
  210. package/src/public/distribution-parity.ts +580 -0
  211. package/src/public/impact/full-kb-quality.ts +74 -1
  212. package/src/public/impact-diagnostics.ts +16 -0
  213. package/src/public/operations/check-executor.ts +4 -0
  214. package/src/public/operations/discovery-entities.ts +3 -2
  215. package/src/public/operations/discovery-executors.ts +68 -5
  216. package/src/public/operations/index.ts +4 -0
  217. package/src/public/operations/legacy-migration-plan.ts +688 -0
  218. package/src/public/operations/node-ports.ts +109 -0
  219. package/src/public/operations/prolog-json.ts +15 -3
  220. package/src/public/operations/repair-plan.ts +562 -0
  221. package/src/public/operations/runtime-types.ts +47 -0
  222. package/src/public/operations/specs/check.ts +2 -2
  223. package/src/public/operations/specs/discovery.ts +1 -1
  224. package/src/public/operations/specs/modeling.ts +19 -1
  225. package/src/public/operations/specs/mutation-schemas.ts +47 -1
  226. package/src/public/operations/specs/reporting.ts +91 -6
  227. package/src/public/operations/specs/semantic.ts +1 -1
  228. package/src/public/operations/workspace-snapshot.ts +28 -0
  229. package/src/public/schemas/entity.ts +44 -1
  230. package/src/public/skills/init-kibi/SKILL.md +2 -2
  231. package/src/public/skills/kibi-freshness/SKILL.md +2 -2
  232. package/src/public/skills/kibi-traceability/SKILL.md +8 -2
  233. package/src/public/skills/kibi-usage/SKILL.md +12 -6
  234. package/src/public/skills/kibi-usage/resources/fact-lanes.md +1 -1
  235. package/src/public/skills/kibi-usage/resources/logic-ir.md +1 -1
  236. package/src/public/skills/kibi-usage/resources/operation-access.md +1 -1
  237. package/src/public/skills/kibi-usage/resources/workflows.md +19 -2
  238. package/src/public/telemetry-acceptance.ts +859 -0
  239. package/src/public/telemetry-remediation.ts +462 -0
  240. package/src/public/verification-receipt.ts +170 -0
  241. package/src/schemas/entity.schema.json +96 -2
package/dist/prolog.js CHANGED
@@ -15,7 +15,7 @@
15
15
  You should have received a copy of the GNU Affero General Public License
16
16
  along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
  */
18
- import { spawn, spawnSync } from "node:child_process";
18
+ import { spawn } from "node:child_process";
19
19
  import { existsSync } from "node:fs";
20
20
  import { createRequire } from "node:module";
21
21
  import path from "node:path";
@@ -26,6 +26,22 @@ const PROLOG_OUTPUT_MAX_BUFFER_BYTES = 8 * 1024 * 1024;
26
26
  const PROLOG_OUTPUT_OVERFLOW_ERROR = "Query exceeded bounded Prolog output capacity (ENOBUFS); narrow the operation or reduce stored entity size";
27
27
  const INTERACTIVE_QUERY_FRAME_END = "__KIBI_QUERY_FRAME_END__";
28
28
  const require = createRequire(import.meta.url);
29
+ function packageVersion(packagePath) {
30
+ try {
31
+ const packageJson = require(packagePath);
32
+ return typeof packageJson.version === "string"
33
+ ? packageJson.version
34
+ : "unknown";
35
+ }
36
+ catch {
37
+ return "unknown";
38
+ }
39
+ }
40
+ const KIBI_PACKAGE_VERSIONS = [
41
+ `kibi-cli@${packageVersion(path.join(importMetaDir, "..", "package.json"))}`,
42
+ `kibi-core@${packageVersion(path.join(importMetaDir, "..", "..", "core", "package.json"))}`,
43
+ `kibi-mcp@${packageVersion(path.join(importMetaDir, "..", "..", "mcp", "package.json"))}`,
44
+ ].join(",");
29
45
  export function resolveKbPlPath() {
30
46
  // implements REQ-009
31
47
  const overrideKbPath = getKbPlPathOverride();
@@ -74,12 +90,17 @@ export class PrologProcess {
74
90
  cache = new Map();
75
91
  interactiveQueryTail = Promise.resolve();
76
92
  terminationPromise = null;
77
- useOneShotMode = typeof globalThis.Bun !== "undefined";
93
+ oneShotProcesses = new Set();
94
+ useOneShotMode;
78
95
  attachedKbPath = null;
79
96
  onProcessExit = null;
80
97
  constructor(options = {}) {
81
98
  this.swiplPath = options.swiplPath || "swipl";
82
99
  this.timeout = options.timeout || 30000;
100
+ this.useOneShotMode =
101
+ options.oneShot ??
102
+ (process.env.NODE_ENV === "test" &&
103
+ typeof globalThis.Bun !== "undefined");
83
104
  }
84
105
  async start() {
85
106
  if (!existsSync(this.swiplPath) && this.swiplPath !== "swipl") {
@@ -90,7 +111,15 @@ export class PrologProcess {
90
111
  "-g",
91
112
  `use_module('${kbPath}'), use_module(library(semweb/rdf_db)), set_prolog_flag(answer_write_options, [max_depth(0), quoted(true)])`,
92
113
  "--quiet",
93
- ]);
114
+ ], {
115
+ detached: process.platform !== "win32",
116
+ env: {
117
+ ...process.env,
118
+ KIBI_RUNTIME_NAME: process.versions.bun !== undefined ? "bun" : "node",
119
+ KIBI_RUNTIME_VERSION: process.versions.bun ?? process.versions.node ?? "unknown",
120
+ KIBI_PACKAGE_VERSIONS: process.env.KIBI_PACKAGE_VERSIONS ?? KIBI_PACKAGE_VERSIONS,
121
+ },
122
+ });
94
123
  this.clearQueryBuffers();
95
124
  if (!this.process.stdout || !this.process.stderr || !this.process.stdin) {
96
125
  throw new Error("Failed to spawn Prolog process");
@@ -164,34 +193,53 @@ export class PrologProcess {
164
193
  return oneShotResult;
165
194
  }
166
195
  if (!isSingleGoal) {
167
- const batchGoal = `(${goal.map((item) => this.normalizeGoal(item)).join(", ")})`;
196
+ // A batch is one logical write. Wrapping it here keeps RDF mutations
197
+ // atomic in the long-lived process; persistence is appended by the
198
+ // interactive wrapper once the transaction succeeds.
199
+ const batchGoal = `rdf_transaction((${goal.map((item) => this.normalizeGoal(item)).join(", ")}))`;
168
200
  return this.query(batchGoal);
169
201
  }
170
202
  if (!this.isProcessUsable()) {
203
+ // Keep the low-level PrologPort useful for isolated callers that only
204
+ // need a single read (and for legacy unit fixtures). Normal CLI/MCP
205
+ // runtimes provide an EngineClient, while explicitly attached callers
206
+ // call start() and stay interactive; this fallback never participates
207
+ // in the engine-backed workflow.
208
+ if (this.process === null) {
209
+ return this.execOneShot(goal, this.attachedKbPath);
210
+ }
171
211
  throw new Error("Prolog process not started");
172
212
  }
173
213
  const runInteractiveQuery = async () => {
174
214
  this.clearQueryBuffers();
175
215
  const debug = isPrologDebugEnabled();
176
216
  const normalizedGoal = this.normalizeGoal(goal);
177
- const wrappedGoal = /^once\s*\(/.test(normalizedGoal)
178
- ? `catch(${normalizedGoal}, _E, (print_message(error, _E), fail))`
179
- : `catch(once((${normalizedGoal})), _E, (print_message(error, _E), fail))`;
217
+ const goalLabel = this.goalLabel(normalizedGoal);
218
+ const shouldPersist = this.isMutatingGoal(normalizedGoal) &&
219
+ !normalizedGoal.includes("kb_save") &&
220
+ !normalizedGoal.includes("kb_commit_upsert");
221
+ const goalWithPersistence = shouldPersist
222
+ ? `(${normalizedGoal}, kb_save)`
223
+ : normalizedGoal;
224
+ const wrappedGoal = /^once\s*\(/.test(goalWithPersistence)
225
+ ? `catch(${goalWithPersistence}, _E, (print_message(error, _E), fail))`
226
+ : `catch(once((${goalWithPersistence})), _E, (print_message(error, _E), fail))`;
180
227
  const start = Date.now();
181
228
  if (debug) {
182
- console.error(`[prolog debug] start query: ${normalizedGoal}`);
229
+ console.error(`[prolog debug] start query: ${goalLabel}`);
183
230
  }
184
231
  this.process?.stdin?.write(`${wrappedGoal}.\nwriteln('${INTERACTIVE_QUERY_FRAME_END}').\n`);
185
232
  return new Promise((resolve, reject) => {
186
233
  let settled = false;
187
234
  const timeoutId = setTimeout(() => {
188
- const msg = `Query timeout after ${this.timeout / 1000}s (pid=${this.process?.pid ?? 0}, killed=${this.process?.killed ? "yes" : "no"}, exitCode=${this.process?.exitCode ?? "null"}, goal=${JSON.stringify(normalizedGoal.slice(0, 120))})`;
235
+ const stage = this.lastDiagnosticStage(this.errorBuffer) ?? "unknown";
236
+ const msg = `Query timeout after ${this.timeout / 1000}s (stage=${stage}, pid=${this.process?.pid ?? 0}, killed=${this.process?.killed ? "yes" : "no"}, exitCode=${this.process?.exitCode ?? "null"}, goal=${goalLabel})`;
189
237
  if (debug) {
190
- const tailOut = this.outputBuffer.slice(-2048);
191
- const tailErr = this.errorBuffer.slice(-2048);
192
238
  console.error(`[prolog debug] timeout: ${msg}`);
193
- console.error(`[prolog debug] last stdout: ---\n${tailOut}\n---`);
194
- console.error(`[prolog debug] last stderr: ---\n${tailErr}\n---`);
239
+ const runtime = this.errorBuffer
240
+ .split("\n")
241
+ .find((line) => line.includes("__KIBI_RUNTIME__"));
242
+ console.error(`[prolog debug] runtime=${runtime ?? "unknown"} packages=${process.env.KIBI_PACKAGE_VERSIONS ?? KIBI_PACKAGE_VERSIONS}`);
195
243
  }
196
244
  settled = true;
197
245
  void this.terminate().finally(() => {
@@ -229,12 +277,12 @@ export class PrologProcess {
229
277
  clearTimeout(timeoutId);
230
278
  settled = true;
231
279
  if (debug) {
232
- console.error(`[prolog debug] query error: ${normalizedGoal} error=${this.errorBuffer.split("\n")[0]}`);
280
+ console.error(`[prolog debug] query error: ${goalLabel} error=${this.errorBuffer.split("\n")[0]}`);
233
281
  }
234
282
  resolve({
235
283
  success: false,
236
284
  bindings: {},
237
- error: this.translateError(this.errorBuffer),
285
+ error: this.addDiagnosticStage(this.translateError(this.errorBuffer), normalizedGoal, this.errorBuffer),
238
286
  });
239
287
  return;
240
288
  }
@@ -268,7 +316,7 @@ export class PrologProcess {
268
316
  this.cache.set(goalKey, result);
269
317
  }
270
318
  if (debug) {
271
- console.error(`[prolog debug] query success: ${normalizedGoal} elapsed=${(Date.now() - start) / 1000}s`);
319
+ console.error(`[prolog debug] query success: ${goalLabel} elapsed=${(Date.now() - start) / 1000}s`);
272
320
  }
273
321
  resolve(result);
274
322
  return;
@@ -294,23 +342,34 @@ export class PrologProcess {
294
342
  releaseQuery();
295
343
  }
296
344
  }
345
+ /** Execute a group of goals as one transaction and one durable save. */
346
+ async queryBatch(goals) {
347
+ return this.query([...goals]);
348
+ }
297
349
  invalidateCache() {
298
350
  this.cache.clear();
299
351
  }
300
352
  isCacheableGoal(goal) {
301
353
  const trimmed = goal.trim();
354
+ if (trimmed.startsWith("rdf_transaction(") ||
355
+ this.isMutatingGoal(trimmed)) {
356
+ return false;
357
+ }
302
358
  return !(trimmed.startsWith("kb_attach(") ||
303
359
  trimmed.startsWith("kb_detach") ||
304
360
  trimmed.startsWith("kb_save") ||
361
+ trimmed.startsWith("kb_storage_status") ||
362
+ trimmed.startsWith("kb_storage_compact") ||
363
+ trimmed.startsWith("kb_storage_export") ||
364
+ trimmed.startsWith("status:") ||
365
+ trimmed.startsWith("kb_commit_upsert(") ||
305
366
  trimmed.startsWith("kb_assert_") ||
306
367
  trimmed.startsWith("kb_delete_") ||
307
368
  trimmed.startsWith("kb_retract_"));
308
369
  }
309
370
  // implements REQ-009
310
371
  isMutatingGoal(goal) {
311
- return (goal.includes("kb_assert_") ||
312
- goal.includes("kb_delete_") ||
313
- goal.includes("kb_retract_"));
372
+ return /(?:kb_(?:assert|delete|retract|commit|save|log)_|\bkb_save\b|\brdf_(?:assert|retract))/u.test(goal);
314
373
  }
315
374
  // implements REQ-009
316
375
  isExplicitSaveGoal(goal) {
@@ -344,7 +403,7 @@ export class PrologProcess {
344
403
  error: "KB already attached",
345
404
  };
346
405
  }
347
- const attachResult = this.execOneShot(trimmedGoal, null);
406
+ const attachResult = await this.execOneShot(trimmedGoal, null);
348
407
  if (attachResult.success) {
349
408
  this.attachedKbPath = attachPath;
350
409
  }
@@ -352,7 +411,7 @@ export class PrologProcess {
352
411
  }
353
412
  return this.execOneShot(trimmedGoal, this.attachedKbPath);
354
413
  }
355
- execOneShot(goal, kbPath) {
414
+ async execOneShot(goal, kbPath) {
356
415
  const originalGoalList = Array.isArray(goal)
357
416
  ? goal.map((item) => this.normalizeGoal(item))
358
417
  : [this.normalizeGoal(goal)];
@@ -386,37 +445,121 @@ export class PrologProcess {
386
445
  kbPath ? "kb_detach" : "true",
387
446
  "(QuerySucceeded == true -> (forall(member(Name=Value, Vars), (write(Name), write('='), write_term(Value, [quoted(true), max_depth(0)]), writeln('.'))), writeln('__KIBI_TRUE__.')) ; writeln('__KIBI_FALSE__.'))",
388
447
  ].join(", ");
389
- const result = spawnSync(this.swiplPath, ["-q", "-g", prologGoal, "-t", "halt"], {
390
- encoding: "utf8",
391
- timeout: this.timeout,
392
- maxBuffer: PROLOG_OUTPUT_MAX_BUFFER_BYTES,
393
- env: {
394
- ...process.env,
395
- KIBI_GOAL: combinedGoal,
396
- ...(kbPath ? { KIBI_KB_PATH: kbPath } : {}),
397
- },
398
- stdio: ["ignore", "pipe", "pipe"],
448
+ const runtimeName = process.versions.bun !== undefined ? "bun" : "node";
449
+ const runtimeVersion = process.versions.bun ?? process.versions.node ?? "unknown";
450
+ const packageVersions = process.env.KIBI_PACKAGE_VERSIONS ?? KIBI_PACKAGE_VERSIONS;
451
+ let child;
452
+ try {
453
+ child = spawn(this.swiplPath, ["-q", "-g", prologGoal, "-t", "halt"], {
454
+ detached: process.platform !== "win32",
455
+ env: {
456
+ ...process.env,
457
+ KIBI_GOAL: combinedGoal,
458
+ KIBI_RUNTIME_NAME: runtimeName,
459
+ KIBI_RUNTIME_VERSION: runtimeVersion,
460
+ KIBI_PACKAGE_VERSIONS: packageVersions,
461
+ ...(kbPath ? { KIBI_KB_PATH: kbPath } : {}),
462
+ },
463
+ stdio: ["ignore", "pipe", "pipe"],
464
+ });
465
+ }
466
+ catch (error) {
467
+ return {
468
+ success: false,
469
+ bindings: {},
470
+ error: error instanceof Error ? error.message : String(error),
471
+ };
472
+ }
473
+ this.oneShotProcesses.add(child);
474
+ let stdout = "";
475
+ let stderr = "";
476
+ let outputBytes = 0;
477
+ let errorBytes = 0;
478
+ let outputOverflowed = false;
479
+ let timedOut = false;
480
+ let settled = false;
481
+ let closed = false;
482
+ let termination = null;
483
+ const appendChunk = (chunk, target) => {
484
+ if (outputOverflowed)
485
+ return;
486
+ const bytes = target === "stdout" ? outputBytes : errorBytes;
487
+ const remaining = PROLOG_OUTPUT_MAX_BUFFER_BYTES - bytes;
488
+ if (chunk.byteLength > remaining) {
489
+ const clipped = chunk.subarray(0, Math.max(0, remaining)).toString();
490
+ if (target === "stdout")
491
+ stdout += clipped;
492
+ else
493
+ stderr += clipped;
494
+ outputOverflowed = true;
495
+ termination ??= this.terminateProcessTree(child);
496
+ return;
497
+ }
498
+ if (target === "stdout") {
499
+ stdout += chunk.toString();
500
+ outputBytes += chunk.byteLength;
501
+ }
502
+ else {
503
+ stderr += chunk.toString();
504
+ errorBytes += chunk.byteLength;
505
+ }
506
+ };
507
+ child.stdout?.on("data", (chunk) => appendChunk(chunk, "stdout"));
508
+ child.stderr?.on("data", (chunk) => appendChunk(chunk, "stderr"));
509
+ const completion = new Promise((resolve) => {
510
+ child.once("error", (error) => {
511
+ closed = true;
512
+ resolve({ code: null, signal: null, error });
513
+ });
514
+ child.once("close", (code, signal) => {
515
+ closed = true;
516
+ resolve({ code, signal });
517
+ });
399
518
  });
400
- if (result.error &&
401
- (result.error.message.includes("timed out") ||
402
- // Bun/Node differ here; keep a conservative timeout detection.
403
- result.error.message.includes("ETIMEDOUT"))) {
404
- throw new Error(`Query timeout after ${this.timeout / 1000}s`);
519
+ const timeoutId = setTimeout(() => {
520
+ if (settled || closed)
521
+ return;
522
+ timedOut = true;
523
+ termination ??= this.terminateProcessTree(child);
524
+ }, this.timeout);
525
+ const result = await completion;
526
+ settled = true;
527
+ clearTimeout(timeoutId);
528
+ if (termination !== null)
529
+ await termination;
530
+ this.oneShotProcesses.delete(child);
531
+ const stage = this.lastDiagnosticStage(stderr) ?? "unknown";
532
+ if (isPrologDebugEnabled()) {
533
+ const runtime = stderr
534
+ .split("\n")
535
+ .find((line) => line.includes("__KIBI_RUNTIME__"));
536
+ if (runtime !== undefined) {
537
+ console.error(`[prolog debug] ${runtime}`);
538
+ }
539
+ console.error(`[prolog debug] child pid=${child.pid ?? 0} runtime=${runtimeName}@${runtimeVersion} packages=${packageVersions} stage=${stage}`);
540
+ }
541
+ if (timedOut) {
542
+ throw new Error(`Query timeout after ${this.timeout / 1000}s (stage=${stage}, pid=${child.pid ?? 0}, goal=${this.goalLabel(combinedGoal ?? "true")})`);
543
+ }
544
+ if (outputOverflowed) {
545
+ return {
546
+ success: false,
547
+ bindings: {},
548
+ error: this.addDiagnosticStage(PROLOG_OUTPUT_OVERFLOW_ERROR, combinedGoal ?? "true", stderr),
549
+ };
405
550
  }
406
- if (result.error?.message.includes("ENOBUFS")) {
551
+ if (result.error !== undefined) {
407
552
  return {
408
553
  success: false,
409
554
  bindings: {},
410
- error: PROLOG_OUTPUT_OVERFLOW_ERROR,
555
+ error: this.addDiagnosticStage(result.error.message, combinedGoal ?? "true", stderr),
411
556
  };
412
557
  }
413
- const stdout = result.stdout ?? "";
414
- const stderr = result.stderr ?? "";
415
558
  if (stderr.includes("ERROR")) {
416
559
  return {
417
560
  success: false,
418
561
  bindings: {},
419
- error: this.translateError(stderr),
562
+ error: this.addDiagnosticStage(this.translateError(stderr), combinedGoal ?? "true", stderr),
420
563
  };
421
564
  }
422
565
  if (stdout.includes("__KIBI_TRUE__")) {
@@ -439,7 +582,7 @@ export class PrologProcess {
439
582
  return {
440
583
  success: false,
441
584
  bindings: {},
442
- error: `Query failed - stdout: ${stdout.substring(0, 200)}, stderr: ${stderr.substring(0, 200)}`,
585
+ error: this.addDiagnosticStage(`Query failed - stdout: ${stdout.substring(0, 200)}, stderr: ${stderr.substring(0, 200)}`, combinedGoal ?? "true", stderr),
443
586
  };
444
587
  }
445
588
  normalizeGoal(goal) {
@@ -478,6 +621,66 @@ export class PrologProcess {
478
621
  this.errorBuffer += chunk.toString();
479
622
  this.errorBufferBytes += chunk.byteLength;
480
623
  }
624
+ goalLabel(goal) {
625
+ const match = goal
626
+ .trim()
627
+ .match(/^([a-z][A-Za-z0-9_]*(?::[a-z][A-Za-z0-9_]*)?)/);
628
+ return match?.[1] ?? "anonymous";
629
+ }
630
+ addDiagnosticStage(message, goal, diagnostics) {
631
+ if (this.goalLabel(goal) !== "kb_commit_upsert")
632
+ return message;
633
+ const stage = this.lastDiagnosticStage(diagnostics);
634
+ return stage === null ? message : `${message} (stage=${stage})`;
635
+ }
636
+ lastDiagnosticStage(text) {
637
+ const matches = [...text.matchAll(/__KIBI_STAGE__:([^\s\r\n]+)/g)];
638
+ return matches.at(-1)?.[1] ?? null;
639
+ }
640
+ signalProcessTree(child, signal) {
641
+ const pid = child.pid;
642
+ if (pid === undefined || pid <= 0)
643
+ return;
644
+ if (process.platform !== "win32") {
645
+ try {
646
+ process.kill(-pid, signal);
647
+ return;
648
+ }
649
+ catch {
650
+ // Fall back to the direct child when a process group is unavailable.
651
+ }
652
+ }
653
+ try {
654
+ child.kill(signal);
655
+ }
656
+ catch {
657
+ // The child may have exited between the group and direct kill attempts.
658
+ }
659
+ }
660
+ async terminateProcessTree(child) {
661
+ let closed = false;
662
+ const exited = new Promise((resolve) => {
663
+ const markExited = () => {
664
+ closed = true;
665
+ resolve();
666
+ };
667
+ child.once("close", markExited);
668
+ child.once("error", markExited);
669
+ });
670
+ if (child.exitCode === null)
671
+ this.signalProcessTree(child, "SIGTERM");
672
+ await Promise.race([
673
+ exited,
674
+ new Promise((resolve) => setTimeout(resolve, 1000)),
675
+ ]);
676
+ if (!closed) {
677
+ this.signalProcessTree(child, "SIGKILL");
678
+ await Promise.race([
679
+ exited,
680
+ new Promise((resolve) => setTimeout(resolve, 1000)),
681
+ ]);
682
+ }
683
+ }
481
684
  // implements REQ-009
482
685
  isProcessUsable() {
483
686
  return Boolean(this.process?.stdin &&
@@ -500,26 +703,39 @@ export class PrologProcess {
500
703
  }
501
704
  // implements REQ-core-prolog-process-management
502
705
  translateError(errorText) {
706
+ // Diagnostic markers intentionally contain words such as `lock` and the
707
+ // audit path. Remove them before classifying the actual Prolog error so a
708
+ // contradiction at the check stage is not mistaken for an audit lock.
709
+ const cleanError = errorText.replace(/^__KIBI_(?:STAGE|RUNTIME)__:[^\r\n]*\r?\n?/gm, "");
710
+ if (cleanError.includes("stale_snapshot")) {
711
+ return "KB snapshot is stale; reattach or refresh the runtime before retrying (stale_snapshot)";
712
+ }
713
+ if ((cleanError.includes("audit_log") ||
714
+ cleanError.includes("audit.log") ||
715
+ cleanError.includes("Resource temporarily unavailable")) &&
716
+ (cleanError.includes("lock") || cleanError.includes("permission"))) {
717
+ return "Audit journal is locked by another Kibi runtime; restart the stale MCP/CLI session before retrying";
718
+ }
503
719
  // SWI-Prolog print_message/2 formats errors as human-readable messages,
504
720
  // not raw Prolog terms. Match the actual output format.
505
- if (errorText.includes("does not exist") &&
506
- /entity [`'"].+?[`'"]/.test(errorText)) {
721
+ if (cleanError.includes("does not exist") &&
722
+ /entity [`'"].+?[`'"]/.test(cleanError)) {
507
723
  // SWI-Prolog doubles single quotes in formatted messages: ''REQ-TEST''
508
- const entityIdMatch = errorText.match(/entity [`'"]+(.+?)[`'"]+ does not exist/);
724
+ const entityIdMatch = cleanError.match(/entity [`'"]+(.+?)[`'"]+ does not exist/);
509
725
  const matchedEntityId = entityIdMatch?.[1];
510
726
  const entityId = matchedEntityId !== undefined
511
727
  ? matchedEntityId.replace(/^`?'+|'+`?$/g, "")
512
728
  : "unknown";
513
- if (errorText.includes("Target entity does not exist")) {
729
+ if (cleanError.includes("Target entity does not exist")) {
514
730
  return `Target entity does not exist: ${entityId}`;
515
731
  }
516
- if (errorText.includes("Source entity does not exist")) {
732
+ if (cleanError.includes("Source entity does not exist")) {
517
733
  return `Source entity does not exist: ${entityId}`;
518
734
  }
519
735
  return `Entity does not exist: ${entityId}`;
520
736
  }
521
- if (errorText.includes("Type error: `relationship' expected")) {
522
- const relMatch = errorText.match(/\(Invalid relationship: ([^)]+)\)/);
737
+ if (cleanError.includes("Type error: `relationship' expected")) {
738
+ const relMatch = cleanError.match(/\(Invalid relationship: ([^)]+)\)/);
523
739
  const invalidRelationship = relMatch?.[1];
524
740
  if (invalidRelationship !== undefined) {
525
741
  return `Invalid relationship: ${invalidRelationship.trim()}`;
@@ -527,21 +743,21 @@ export class PrologProcess {
527
743
  return "Invalid relationship type or direction";
528
744
  }
529
745
  // Fallback: check for raw Prolog error terms (used by other tools)
530
- if (errorText.includes("existence_error") ||
531
- errorText.includes("Unknown procedure")) {
746
+ if (cleanError.includes("existence_error") ||
747
+ cleanError.includes("Unknown procedure")) {
532
748
  return "Predicate or file not found";
533
749
  }
534
- if (errorText.includes("permission_error")) {
750
+ if (cleanError.includes("permission_error")) {
535
751
  return "Access denied or KB locked";
536
752
  }
537
- if (errorText.includes("syntax_error") ||
538
- errorText.includes("Operator expected")) {
753
+ if (cleanError.includes("syntax_error") ||
754
+ cleanError.includes("Operator expected")) {
539
755
  return "Invalid query syntax";
540
756
  }
541
- if (errorText.includes("timeout_error")) {
757
+ if (cleanError.includes("timeout_error")) {
542
758
  return `Operation exceeded ${this.timeout / 1000}s timeout`;
543
759
  }
544
- const simpleError = (errorText
760
+ const simpleError = (cleanError
545
761
  .replace(/ERROR:\s*/g, "")
546
762
  .replace(/^\*\*.*\*\*$/gm, "")
547
763
  .replace(/^\s+/gm, "")
@@ -564,22 +780,16 @@ export class PrologProcess {
564
780
  this.onProcessExit = null;
565
781
  }
566
782
  const current = this.process;
783
+ const oneShots = [...this.oneShotProcesses];
567
784
  this.process = null;
568
785
  this.clearQueryBuffers();
569
- if (current) {
786
+ if (current || oneShots.length > 0) {
570
787
  this.terminationPromise = (async () => {
571
- current.stdin?.end();
572
- current.kill("SIGTERM");
573
- await new Promise((resolve) => {
574
- const timeout = setTimeout(() => {
575
- current.kill("SIGKILL");
576
- resolve(undefined);
577
- }, 1000);
578
- current.on("exit", () => {
579
- clearTimeout(timeout);
580
- resolve(undefined);
581
- });
582
- });
788
+ if (current) {
789
+ current.stdin?.end();
790
+ await this.terminateProcessTree(current);
791
+ }
792
+ await Promise.all(oneShots.map((child) => this.terminateProcessTree(child)));
583
793
  })();
584
794
  try {
585
795
  await this.terminationPromise;
@@ -0,0 +1,17 @@
1
+ export interface DiagnosticUsageInput {
2
+ readonly workspaceRoot: string;
3
+ readonly tool: string;
4
+ readonly businessArgs: Readonly<Record<string, unknown>>;
5
+ readonly telemetry: Readonly<Record<string, unknown>> | null;
6
+ readonly startedAt: Date;
7
+ readonly finishedAt?: Date;
8
+ readonly status: "success" | "error";
9
+ readonly result?: unknown;
10
+ readonly error?: string;
11
+ readonly requestId?: string;
12
+ readonly logPath?: string;
13
+ }
14
+ export declare function diagnosticMutationFingerprint(args: Readonly<Record<string, unknown>>): string;
15
+ export declare function deriveDiagnosticUsageFields(tool: string, args: Readonly<Record<string, unknown>>, telemetry: Readonly<Record<string, unknown>> | null, result: unknown): Record<string, unknown>;
16
+ export declare function appendCliDiagnosticUsage(input: DiagnosticUsageInput): void;
17
+ //# sourceMappingURL=diagnostic-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostic-usage.d.ts","sourceRoot":"","sources":["../../src/public/diagnostic-usage.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7D,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AA4BD,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACtC,MAAM,CAIR;AAiED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACvC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,EACnD,MAAM,EAAE,OAAO,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmDzB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAgC1E"}