@konneal/engine 0.2.146 → 0.2.147

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleAsk
3
- } from "./chunk-VISPFCQP.js";
3
+ } from "./chunk-EJVRNPDB.js";
4
4
  import "./chunk-ECOYO7EX.js";
5
5
  import "./chunk-VYEMFSU5.js";
6
6
  import "./chunk-XH5QGX2O.js";
@@ -2724,6 +2724,7 @@ Answer account questions from these records ONLY: name the record when you use i
2724
2724
  if (answer) answer = canonicalRefusal(answer, refusalScope(answerCites(answer)));
2725
2725
  if (answer) answer = scrubToolLines(answer);
2726
2726
  if (answer) answer = exciseMisplacedPin(answer, answerCites(answer));
2727
+ const toolNarration = (t) => /^(?:the\s+)?[a-z][a-z_.]*\.[a-z_]+ tool returned\b/i.test(t.slice(0, 200)) || /tool returned,? for the query\b/i.test(t.slice(0, 300));
2727
2728
  stageTiming.generate = Date.now() - tGen;
2728
2729
  let used = usedHits;
2729
2730
  if (answer && !answer.includes(refusalAnswer())) {
@@ -2740,17 +2741,17 @@ Answer account questions from these records ONLY: name the record when you use i
2740
2741
  );
2741
2742
  return nums.filter((n) => tableNums.has(n)).length >= 2;
2742
2743
  })();
2743
- if (anchors.violations.length > 0 || retyped || unreferenced) {
2744
- console.log("contract check:", anchors.violations.length, "anchor violations; tableRetyped:", retyped, "; tableDataUnreferenced:", unreferenced, "\u2014 regenerating");
2744
+ if (anchors.violations.length > 0 || retyped || unreferenced || toolNarration(answer)) {
2745
+ console.log("contract check:", anchors.violations.length, "anchor violations; tableRetyped:", retyped, "; tableDataUnreferenced:", unreferenced, "; toolNarration:", toolNarration(answer), "\u2014 regenerating");
2745
2746
  const tableUnitId = unreferenced ? used.find((h) => h.metadata.unit_id && h.metadata.block === "table")?.metadata.unit_id : void 0;
2746
- const note = retyped || unreferenced ? `Correction notice: your draft reproduced a table as markdown or presented a served table's data without its reference. Rewrite the answer: describe the table in prose, cite the clause, and write the reference token [[u:${tableUnitId ?? "<unit id>"}]] exactly where the table belongs. Do not render any table as markdown.` : ANCHOR_CORRECTION_NOTE;
2747
+ const note = toolNarration(answer) ? "Correction notice: your draft narrated an internal tool call ('the \u2026 tool returned \u2026') instead of answering. Rewrite: answer the question directly from the retrieved passages, cite the clauses, and never mention tools, queries, wire formats, or JSON." : retyped || unreferenced ? `Correction notice: your draft reproduced a table as markdown or presented a served table's data without its reference. Rewrite the answer: describe the table in prose, cite the clause, and write the reference token [[u:${tableUnitId ?? "<unit id>"}]] exactly where the table belongs. Do not render any table as markdown.` : ANCHOR_CORRECTION_NOTE;
2747
2748
  generateRetries += 1;
2748
2749
  const corrected = await generateOnce(env, model, [...messages, { role: "system", content: note }], routeEffort(routed.route));
2749
2750
  if (corrected) {
2750
2751
  const correctedAnswer = canonicalRefusal(corrected, refusalScope(answerCites(corrected)));
2751
2752
  const retryAnchors = checkQuoteAnchors(correctedAnswer, used.map((h) => h.text));
2752
2753
  const retryRetyped = tableRetyped(correctedAnswer, hasTableUnit);
2753
- if (retryAnchors.violations.length < anchors.violations.length || !retryRetyped && retyped || unreferenced && correctedAnswer.includes("[[u:")) {
2754
+ if (retryAnchors.violations.length < anchors.violations.length || !retryRetyped && retyped || unreferenced && correctedAnswer.includes("[[u:") || !toolNarration(correctedAnswer)) {
2754
2755
  answer = correctedAnswer;
2755
2756
  }
2756
2757
  }
@@ -4,7 +4,7 @@ import {
4
4
  handleMemories,
5
5
  scoreFaithfulness,
6
6
  scoreJudge
7
- } from "../../chunk-VISPFCQP.js";
7
+ } from "../../chunk-EJVRNPDB.js";
8
8
  import "../../chunk-ECOYO7EX.js";
9
9
  import {
10
10
  TOOLS_REGISTRY,
@@ -1079,7 +1079,7 @@ async function callToolOnce(env, ctx, _req, tier, key, name, args, t0) {
1079
1079
  const r = await runTool({ ...env, DB: env.DB }, { name, args }, "mcp");
1080
1080
  payload = r ? { tool: r.name, query: r.query, result: r.output } : { error: { message: `tool ${name} returned nothing for the given arguments` } };
1081
1081
  } else {
1082
- const res = name === "ask" ? await (await import("../../ask-RIWVOI3D.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-2PKZJMUV.js")).handleSearch(env, ctx, inner, tier, key);
1082
+ const res = name === "ask" ? await (await import("../../ask-ILW2EV7X.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-2PKZJMUV.js")).handleSearch(env, ctx, inner, tier, key);
1083
1083
  payload = await res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
1084
1084
  }
1085
1085
  telemetry(env, ctx, tier, `mcp:${name}`, null, !payload?.error, JSON.stringify(payload).length, await sha256Hex(`${name}:${JSON.stringify(args ?? {})}`), void 0, void 0, { durationMs: Date.now() - t0 });
@@ -80,9 +80,9 @@ hand.
80
80
  |---|---|
81
81
  | OII | de-facto: parse.py (HTML-first) + graph.py (relaton/glossarist). Target: consume the four native object serializations; the AI-serialization proposal (metanorma#592) is the upstream contract |
82
82
  | Identity SSOT | ✅ documents registry in D1 (derived status, active flags, supersession chains) |
83
- | Registry serving | ✅ edition notes injected into doc-scoped asks; `GET /api/documents?family=` public API |
83
+ | Registry serving | ✅ edition notes injected into doc-scoped asks; the registry serves through the MCP `oiml_documents` tool and the `/docs/*` renderings |
84
84
  | Graph projection | ✅ 7,128 nodes / 6,486 edges in D1; query lane live (defined_terms → defines → candidates) |
85
- | Chunking | prose-only today; ChunkRecordV2 typed blocks = next build (tables/equations/requirements) |
85
+ | Chunking | typed units live on the MKO plane (tables/formulas/terms/requirements as typed payloads, clause-anchored citations); the parse plane stays clause-prose |
86
86
  | Enrichment | ✅ 98.7% corpus (the residual ~400 are persistent empty enrichments), KV-cached, replayed from the durable record after full restores |
87
87
  | Verification | unit suites + contract tests in CI (`npm run test:units`, pydantic↔TS wire contract, schema-union drift); the data-quality report loop = next build |
88
88
  | Orchestration | manual commands today; one-command pipeline (`ingest run --doc X`) when the above land |
@@ -262,10 +262,14 @@ Beyond the ladder, the machine objects are EXECUTED, not retrieved:
262
262
  machine limit gets the check evaluated against its stated values —
263
263
  pass / the standard's own violation word / void naming what is
264
264
  missing — attached as data the answer must present faithfully.
265
- Counterfactuals are free (values are values).
266
- - **Provable absence** (`/v1/absence`): an enumeration certificate
267
- over the standard's model plane — absent means "N nodes enumerated,
268
- 0 matches", never a bare refusal.
265
+ Stated tolerances bind beside their values and the check re-runs at
266
+ every band corner; a band spanning both outcomes voids with its
267
+ reason (guarded acceptance). Counterfactuals are free (values are
268
+ values).
269
+ - **Honest absence**: the question names a standard, the model plane
270
+ is enumerated, and a fruitless search answers as a plain sentence
271
+ with the scope named — never a bare refusal, never a certificate
272
+ document (the `/v1/absence` endpoint retired at engine 0.2.54).
269
273
  - **Answer verification** (`/v1/verify`): the deterministic contract
270
274
  battery plus a judged faithfulness score, exposed for any answer.
271
275
 
package/package.json CHANGED
@@ -27,7 +27,7 @@
27
27
  "playwright": "^1.62.1",
28
28
  "yaml": "^2.9.1"
29
29
  },
30
- "version": "0.2.146",
30
+ "version": "0.2.147",
31
31
  "description": "The Konneal engine: the publisher-agnostic build pipeline and API plane for standards intelligence (retrieval, answer contract, verdicts, evaluation).",
32
32
  "license": "BSD-3-Clause",
33
33
  "type": "module",
@@ -1690,6 +1690,15 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
1690
1690
  // (measured on chip-scoped asks: correct cited answers carrying the
1691
1691
  // pin mid-text, read by users and graders as total refusals)
1692
1692
  if (answer) answer = exciseMisplacedPin(answer, answerCites(answer));
1693
+ // the tool-narration leak (2026-10-14, the R 134-1 probe): the answer
1694
+ // model sometimes NARRATES the agent loop's call in prose — 'The
1695
+ // db.find tool returned, for the query "…": {…json…}' — instead of
1696
+ // answering. The line scrub cannot catch prose; the contract retry
1697
+ // below regenerates with an explicit notice, and the detector also
1698
+ // gates the retry's acceptance so a narrating retry cannot ship.
1699
+ const toolNarration = (t: string) =>
1700
+ /^(?:the\s+)?[a-z][a-z_.]*\.[a-z_]+ tool returned\b/i.test(t.slice(0, 200)) ||
1701
+ /tool returned,? for the query\b/i.test(t.slice(0, 300));
1693
1702
  stageTiming.generate = Date.now() - tGen;
1694
1703
 
1695
1704
  // ── Deterministic quote-anchor + table-retyping check ──
@@ -1714,14 +1723,16 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
1714
1723
  );
1715
1724
  return nums.filter((n) => tableNums.has(n)).length >= 2;
1716
1725
  })();
1717
- if (anchors.violations.length > 0 || retyped || unreferenced) {
1718
- console.log("contract check:", anchors.violations.length, "anchor violations; tableRetyped:", retyped, "; tableDataUnreferenced:", unreferenced, "— regenerating");
1726
+ if (anchors.violations.length > 0 || retyped || unreferenced || toolNarration(answer)) {
1727
+ console.log("contract check:", anchors.violations.length, "anchor violations; tableRetyped:", retyped, "; tableDataUnreferenced:", unreferenced, "; toolNarration:", toolNarration(answer), "— regenerating");
1719
1728
  // name the EXACT unit the token must reference — a generic note
1720
1729
  // leaves the model guessing which id to write
1721
1730
  const tableUnitId = unreferenced
1722
1731
  ? used.find((h: Hit) => h.metadata.unit_id && h.metadata.block === "table")?.metadata.unit_id
1723
1732
  : undefined;
1724
- const note = retyped || unreferenced
1733
+ const note = toolNarration(answer)
1734
+ ? "Correction notice: your draft narrated an internal tool call ('the … tool returned …') instead of answering. Rewrite: answer the question directly from the retrieved passages, cite the clauses, and never mention tools, queries, wire formats, or JSON."
1735
+ : retyped || unreferenced
1725
1736
  ? `Correction notice: your draft reproduced a table as markdown or presented a served table's data without its reference. Rewrite the answer: describe the table in prose, cite the clause, and write the reference token [[u:${tableUnitId ?? "<unit id>"}]] exactly where the table belongs. Do not render any table as markdown.`
1726
1737
  : ANCHOR_CORRECTION_NOTE;
1727
1738
  generateRetries += 1;
@@ -1730,7 +1741,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
1730
1741
  const correctedAnswer = canonicalRefusal(corrected, refusalScope(answerCites(corrected)));
1731
1742
  const retryAnchors = checkQuoteAnchors(correctedAnswer, used.map((h: Hit) => h.text));
1732
1743
  const retryRetyped = tableRetyped(correctedAnswer, hasTableUnit);
1733
- if (retryAnchors.violations.length < anchors.violations.length || (!retryRetyped && retyped) || (unreferenced && correctedAnswer.includes("[[u:"))) {
1744
+ if (retryAnchors.violations.length < anchors.violations.length || (!retryRetyped && retyped) || (unreferenced && correctedAnswer.includes("[[u:")) || !toolNarration(correctedAnswer)) {
1734
1745
  answer = correctedAnswer;
1735
1746
  }
1736
1747
  }