@konneal/engine 0.2.147 → 0.2.148

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-EJVRNPDB.js";
3
+ } from "./chunk-2SRQDX7Y.js";
4
4
  import "./chunk-ECOYO7EX.js";
5
5
  import "./chunk-VYEMFSU5.js";
6
6
  import "./chunk-XH5QGX2O.js";
@@ -1638,6 +1638,10 @@ async function extractNameplate(ai, model, image) {
1638
1638
  }
1639
1639
 
1640
1640
  // workers/worker_public/src/ask.ts
1641
+ function toolNarration(t) {
1642
+ return /^(?: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));
1643
+ }
1644
+ var TOOL_NARRATION_NOTICE = "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.";
1641
1645
  function userImageDataUrl(body) {
1642
1646
  const img = body?.image;
1643
1647
  if (img == null) return null;
@@ -2607,9 +2611,38 @@ Answer account questions from these records ONLY: name the record when you use i
2607
2611
  const stats = {};
2608
2612
  let full = "";
2609
2613
  try {
2614
+ let headSettled = false;
2610
2615
  for await (const tok of sseTokens(stream, stats)) {
2611
2616
  full += tok;
2612
- send({ type: "token", v: tok });
2617
+ if (headSettled) {
2618
+ send({ type: "token", v: tok });
2619
+ continue;
2620
+ }
2621
+ if (full.length < 80) continue;
2622
+ headSettled = true;
2623
+ if (toolNarration(full)) {
2624
+ console.log("tool-narration(stream): regenerating once");
2625
+ send({ type: "retry" });
2626
+ const regen = await generateStream(env, model, [
2627
+ ...messages,
2628
+ { role: "system", content: TOOL_NARRATION_NOTICE }
2629
+ ], routeEffort(routed.route));
2630
+ if (regen) {
2631
+ full = "";
2632
+ for await (const tok2 of sseTokens(regen, stats)) {
2633
+ full += tok2;
2634
+ send({ type: "token", v: tok2 });
2635
+ }
2636
+ } else {
2637
+ send({ type: "token", v: full });
2638
+ }
2639
+ break;
2640
+ }
2641
+ send({ type: "token", v: full });
2642
+ }
2643
+ if (!headSettled) {
2644
+ headSettled = true;
2645
+ if (!toolNarration(full)) send({ type: "token", v: full });
2613
2646
  }
2614
2647
  } catch {
2615
2648
  }
@@ -2724,7 +2757,6 @@ Answer account questions from these records ONLY: name the record when you use i
2724
2757
  if (answer) answer = canonicalRefusal(answer, refusalScope(answerCites(answer)));
2725
2758
  if (answer) answer = scrubToolLines(answer);
2726
2759
  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));
2728
2760
  stageTiming.generate = Date.now() - tGen;
2729
2761
  let used = usedHits;
2730
2762
  if (answer && !answer.includes(refusalAnswer())) {
@@ -2744,7 +2776,7 @@ Answer account questions from these records ONLY: name the record when you use i
2744
2776
  if (anchors.violations.length > 0 || retyped || unreferenced || toolNarration(answer)) {
2745
2777
  console.log("contract check:", anchors.violations.length, "anchor violations; tableRetyped:", retyped, "; tableDataUnreferenced:", unreferenced, "; toolNarration:", toolNarration(answer), "\u2014 regenerating");
2746
2778
  const tableUnitId = unreferenced ? used.find((h) => h.metadata.unit_id && h.metadata.block === "table")?.metadata.unit_id : void 0;
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;
2779
+ const note = toolNarration(answer) ? TOOL_NARRATION_NOTICE : 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;
2748
2780
  generateRetries += 1;
2749
2781
  const corrected = await generateOnce(env, model, [...messages, { role: "system", content: note }], routeEffort(routed.route));
2750
2782
  if (corrected) {
@@ -4,7 +4,7 @@ import {
4
4
  handleMemories,
5
5
  scoreFaithfulness,
6
6
  scoreJudge
7
- } from "../../chunk-EJVRNPDB.js";
7
+ } from "../../chunk-2SRQDX7Y.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-ILW2EV7X.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-MBKT7D7M.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 });
@@ -84,6 +84,22 @@ All **37** compiled sample documents (R/D/B/G/E + OIML-CS admin + parts
84
84
  342 cited documents (native Relaton + pubid), ~11k graph rows
85
85
  ```
86
86
 
87
+ ## Full-corpus run (2026-10-13, live; the lutaml-0.8.106 + ± re-baseline)
88
+
89
+ **46** documents export and ingest at the current stack (lutaml-model
90
+ 0.8.106 float, the ± glyph mapping in plain-text extraction, the six
91
+ series-less editions carrying `:docidentifier:`):
92
+
93
+ ```
94
+ 46/46 bundles → 5,336 chunks, 1,423 typed units, 293 glossary terms,
95
+ 425 cited documents; D1: 15,390-statement graph projection, 33,480 FTS rows
96
+ ```
97
+
98
+ Uncertainty ranges are first-class end to end on this baseline: display
99
+ text carries ± (AsciiMath `pm` maps at the glyph layer), tolerance cells
100
+ parse into `payload.uncertainties[]` records, and the verdict engine
101
+ evaluates stated bands with guarded acceptance.
102
+
87
103
  Includes the amendment cases: R 60/A1 keeps its 17 terms in an annex
88
104
  (recovered into the glossary), and the OIML-CS admin documents keep
89
105
  their whole body in `<preface>` (walked as clauses). Clause text now
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.147",
30
+ "version": "0.2.148",
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",
@@ -50,6 +50,21 @@ import { operationsLane } from "./operations.ts";
50
50
  import { extractNameplate, nameplateRegisterQuery } from "./nameplate.ts";
51
51
  import { P } from "./profile.ts";
52
52
 
53
+ // the tool-narration leak (2026-10-14, the R 134-1 probe): the answer
54
+ // model sometimes NARRATES the agent loop's call in prose — 'The
55
+ // db.find tool returned, for the query "…": {…json…}' — instead of
56
+ // answering. The TOOL-line scrub cannot catch prose. The JSON lane
57
+ // regenerates through the contract retry; the streamed lane buffers
58
+ // the head and sends the retry sentinel (the blind-answer guard's
59
+ // client contract) before streaming the fresh answer. One bounded
60
+ // restart per lane.
61
+ function toolNarration(t: string): boolean {
62
+ return /^(?:the\s+)?[a-z][a-z_.]*\.[a-z_]+ tool returned\b/i.test(t.slice(0, 200)) ||
63
+ /tool returned,? for the query\b/i.test(t.slice(0, 300));
64
+ }
65
+ const TOOL_NARRATION_NOTICE =
66
+ "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.";
67
+
53
68
  /** User-uploaded image for multimodal questions: a data URL
54
69
  * (data:image/(png|jpeg|webp|gif);base64,…) up to 6 MB of payload. The
55
70
  * question text still drives retrieval; the image is CONTEXT for the
@@ -1536,9 +1551,43 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
1536
1551
  const stats: { usage?: unknown } = {};
1537
1552
  let full = "";
1538
1553
  try {
1554
+ // the head buffers until it can rule out the tool-narration
1555
+ // leak (the JSON lane's contract retry cannot run here — the
1556
+ // tokens are already on the wire): a narrating head sends the
1557
+ // retry sentinel — the blind-answer guard's client contract,
1558
+ // which clears the buffer — and the fresh answer streams.
1559
+ // One bounded restart; a narrating retry ships as-is.
1560
+ let headSettled = false;
1539
1561
  for await (const tok of sseTokens(stream, stats)) {
1540
1562
  full += tok;
1541
- send({ type: "token", v: tok });
1563
+ if (headSettled) { send({ type: "token", v: tok }); continue; }
1564
+ if (full.length < 80) continue;
1565
+ headSettled = true;
1566
+ if (toolNarration(full)) {
1567
+ console.log("tool-narration(stream): regenerating once");
1568
+ send({ type: "retry" });
1569
+ const regen = await generateStream(env, model, [
1570
+ ...messages,
1571
+ { role: "system", content: TOOL_NARRATION_NOTICE },
1572
+ ], routeEffort(routed.route));
1573
+ if (regen) {
1574
+ full = "";
1575
+ for await (const tok2 of sseTokens(regen, stats)) {
1576
+ full += tok2;
1577
+ send({ type: "token", v: tok2 });
1578
+ }
1579
+ } else {
1580
+ // the restart failed — ship the buffered head rather
1581
+ // than an empty answer
1582
+ send({ type: "token", v: full });
1583
+ }
1584
+ break;
1585
+ }
1586
+ send({ type: "token", v: full });
1587
+ }
1588
+ if (!headSettled) {
1589
+ headSettled = true;
1590
+ if (!toolNarration(full)) send({ type: "token", v: full });
1542
1591
  }
1543
1592
  } catch {
1544
1593
  // stream ended prematurely — deliver what we have
@@ -1696,9 +1745,6 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
1696
1745
  // answering. The line scrub cannot catch prose; the contract retry
1697
1746
  // below regenerates with an explicit notice, and the detector also
1698
1747
  // 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));
1702
1748
  stageTiming.generate = Date.now() - tGen;
1703
1749
 
1704
1750
  // ── Deterministic quote-anchor + table-retyping check ──
@@ -1731,7 +1777,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
1731
1777
  ? used.find((h: Hit) => h.metadata.unit_id && h.metadata.block === "table")?.metadata.unit_id
1732
1778
  : undefined;
1733
1779
  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."
1780
+ ? TOOL_NARRATION_NOTICE
1735
1781
  : retyped || unreferenced
1736
1782
  ? `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.`
1737
1783
  : ANCHOR_CORRECTION_NOTE;