@konneal/engine 0.2.153 → 0.2.155
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/dist/answerGuards.d.ts +4 -0
- package/dist/{ask-UIIE5LD2.js → ask-MRVXR7CY.js} +1 -1
- package/dist/{chunk-I6HU3EM6.js → chunk-TXF5VMNM.js} +3 -1
- package/dist/worker_public/src/index.js +7 -3
- package/package.json +1 -1
- package/workers/worker_public/src/admin.ts +14 -1
- package/workers/worker_public/src/answerGuards.ts +36 -0
- package/workers/worker_public/src/ask.ts +1 -30
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function toolNarration(t: string): boolean;
|
|
2
|
+
export declare const 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.";
|
|
3
|
+
export declare function intentPreamble(t: string): boolean;
|
|
4
|
+
export declare const INTENT_PREAMBLE_NOTICE = "Correction notice: your draft announced a plan ('I'll look up \u2026') instead of answering. Do not narrate intent \u2014 answer the question NOW from the retrieved passages: give the values, clause references and citations the question asks for.";
|
|
@@ -1637,7 +1637,7 @@ async function extractNameplate(ai, model, image) {
|
|
|
1637
1637
|
return null;
|
|
1638
1638
|
}
|
|
1639
1639
|
|
|
1640
|
-
// workers/worker_public/src/
|
|
1640
|
+
// workers/worker_public/src/answerGuards.ts
|
|
1641
1641
|
function toolNarration(t) {
|
|
1642
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
1643
|
}
|
|
@@ -1649,6 +1649,8 @@ function intentPreamble(t) {
|
|
|
1649
1649
|
return !/\[\[u:|\[\^?\d+\]|\b\d+\.\d+\b/.test(s);
|
|
1650
1650
|
}
|
|
1651
1651
|
var INTENT_PREAMBLE_NOTICE = "Correction notice: your draft announced a plan ('I'll look up \u2026') instead of answering. Do not narrate intent \u2014 answer the question NOW from the retrieved passages: give the values, clause references and citations the question asks for.";
|
|
1652
|
+
|
|
1653
|
+
// workers/worker_public/src/ask.ts
|
|
1652
1654
|
function userImageDataUrl(body) {
|
|
1653
1655
|
const img = body?.image;
|
|
1654
1656
|
if (img == null) return null;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
handleMemories,
|
|
5
5
|
scoreFaithfulness,
|
|
6
6
|
scoreJudge
|
|
7
|
-
} from "../../chunk-
|
|
7
|
+
} from "../../chunk-TXF5VMNM.js";
|
|
8
8
|
import "../../chunk-ECOYO7EX.js";
|
|
9
9
|
import {
|
|
10
10
|
TOOLS_REGISTRY,
|
|
@@ -411,7 +411,11 @@ ${c.text.slice(0, 1500)}` }
|
|
|
411
411
|
context = typeof raw === "string" ? raw.trim().replace(/^["\']|[\"']$/g, "").slice(0, 400) : "";
|
|
412
412
|
if (!context) {
|
|
413
413
|
console.log("enrich raw keys:", Object.keys(res ?? {}), "sample:", JSON.stringify(res).slice(0, 300));
|
|
414
|
-
return { id: c.id, ok: false, error: "empty enrichment" };
|
|
414
|
+
if (contextOnly || abMode) return { id: c.id, ok: false, error: "empty enrichment" };
|
|
415
|
+
const rawText = typeof c.metadata.chunk_text === "string" && c.metadata.chunk_text ? c.metadata.chunk_text : c.text;
|
|
416
|
+
const rawVector = await embed(portModelRunner(env), MODELS.embed, rawText.slice(0, 6e3));
|
|
417
|
+
await env.VECTORIZE.upsert([{ id: c.id, values: rawVector, metadata: { ...c.metadata, chunk_text: rawText } }]);
|
|
418
|
+
return { id: c.id, ok: true, raw: true, cached: false, context: "" };
|
|
415
419
|
}
|
|
416
420
|
if (res?.usage) {
|
|
417
421
|
usage.prompt_tokens += Number(res.usage.prompt_tokens ?? 0);
|
|
@@ -1107,7 +1111,7 @@ async function callToolOnce(env, ctx, _req, tier, key, name, args, t0) {
|
|
|
1107
1111
|
const r = await runTool({ ...env, DB: env.DB }, { name, args }, "mcp");
|
|
1108
1112
|
payload = r ? { tool: r.name, query: r.query, result: r.output } : { error: { message: `tool ${name} returned nothing for the given arguments` } };
|
|
1109
1113
|
} else {
|
|
1110
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
1114
|
+
const res = name === "ask" ? await (await import("../../ask-MRVXR7CY.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-GAU5SEZI.js")).handleSearch(env, ctx, inner, tier, key);
|
|
1111
1115
|
payload = await res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
1112
1116
|
}
|
|
1113
1117
|
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 });
|
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.
|
|
30
|
+
"version": "0.2.155",
|
|
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",
|
|
@@ -78,7 +78,20 @@ export async function handleEnrich(env: Env, ctx: ExecutionContext, req: Request
|
|
|
78
78
|
context = typeof raw === "string" ? raw.trim().replace(/^["\']|[\"']$/g, "").slice(0, 400) : "";
|
|
79
79
|
if (!context) {
|
|
80
80
|
console.log("enrich raw keys:", Object.keys(res ?? {}), "sample:", JSON.stringify(res).slice(0, 300));
|
|
81
|
-
|
|
81
|
+
// The deterministic-empty class (observed 2026-10-20 on B 18's
|
|
82
|
+
// thin terms sections and two R 134-1 clauses — empty under
|
|
83
|
+
// BOTH the default and the alternate enrichment model): the
|
|
84
|
+
// chunk's own text is real corpus content, so the production
|
|
85
|
+
// flow serves it RAW rather than leaving a hole in the index
|
|
86
|
+
// — embed the original text without the context band, upsert
|
|
87
|
+
// without the ctx marker; a later forced re-enrich overwrites
|
|
88
|
+
// the vector in place. mode:"context" and "ab" keep failing:
|
|
89
|
+
// their builders want the signal, not a side-effecting write.
|
|
90
|
+
if (contextOnly || abMode) return { id: c.id, ok: false, error: "empty enrichment" };
|
|
91
|
+
const rawText = typeof c.metadata.chunk_text === "string" && c.metadata.chunk_text ? c.metadata.chunk_text : c.text;
|
|
92
|
+
const rawVector = await embed(portModelRunner(env), MODELS.embed, rawText.slice(0, 6000));
|
|
93
|
+
await env.VECTORIZE.upsert([{ id: c.id, values: rawVector, metadata: { ...c.metadata, chunk_text: rawText } }]);
|
|
94
|
+
return { id: c.id, ok: true, raw: true, cached: false, context: "" };
|
|
82
95
|
}
|
|
83
96
|
if (res?.usage) {
|
|
84
97
|
usage.prompt_tokens += Number(res.usage.prompt_tokens ?? 0);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// The answer-contract's text guards, dependency-free so plain node can
|
|
2
|
+
// load them for unit tests (ask.ts imports .md prompts that only the
|
|
3
|
+
// bundler resolves — the guards were module-local there and rode e2e
|
|
4
|
+
// alone). Both lanes share them: the JSON lane's contract retry and the
|
|
5
|
+
// streamed lane's 80-char head ruling.
|
|
6
|
+
|
|
7
|
+
// The tool-narration leak (2026-10-14, the R 134-1 probe): the answer
|
|
8
|
+
// model sometimes NARRATES the agent loop's call in prose — 'The
|
|
9
|
+
// db.find tool returned, for the query "…": {…json…}' — instead of
|
|
10
|
+
// answering. The TOOL-line scrub cannot catch prose. The JSON lane
|
|
11
|
+
// regenerates through the contract retry; the streamed lane buffers
|
|
12
|
+
// the head and sends the retry sentinel (the blind-answer guard's
|
|
13
|
+
// client contract) before streaming the fresh answer. One bounded
|
|
14
|
+
// restart per lane.
|
|
15
|
+
export function toolNarration(t: string): boolean {
|
|
16
|
+
return /^(?:the\s+)?[a-z][a-z_.]*\.[a-z_]+ tool returned\b/i.test(t.slice(0, 200)) ||
|
|
17
|
+
/tool returned,? for the query\b/i.test(t.slice(0, 300));
|
|
18
|
+
}
|
|
19
|
+
export const TOOL_NARRATION_NOTICE =
|
|
20
|
+
"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.";
|
|
21
|
+
|
|
22
|
+
// The intent preamble (2026-10-19, the ~200-word technical-file
|
|
23
|
+
// question): the draft is a PLAN, not an answer — "I'll look up the R
|
|
24
|
+
// 76-1 MPE clause and classification details in the corpus." — shipped
|
|
25
|
+
// while ten citations rode the same response. Narrow by construction:
|
|
26
|
+
// it must OPEN with a first-person intent verb AND carry no substance —
|
|
27
|
+
// short, no clause citation, no normative numbers (a real answer's
|
|
28
|
+
// opening names values or clauses).
|
|
29
|
+
export function intentPreamble(t: string): boolean {
|
|
30
|
+
const s = (t ?? "").trim();
|
|
31
|
+
if (s.length > 240) return false;
|
|
32
|
+
if (!/^(?:i'll|i will|let me|let's|i'm going to|i am going to)\b/i.test(s)) return false;
|
|
33
|
+
return !/\[\[u:|\[\^?\d+\]|\b\d+\.\d+\b/.test(s);
|
|
34
|
+
}
|
|
35
|
+
export const INTENT_PREAMBLE_NOTICE =
|
|
36
|
+
"Correction notice: your draft announced a plan ('I'll look up …') instead of answering. Do not narrate intent — answer the question NOW from the retrieved passages: give the values, clause references and citations the question asks for.";
|
|
@@ -49,37 +49,8 @@ import { TOOL_DECLARATION, parseToolCall, runTool, toolNote } from "./tools";
|
|
|
49
49
|
import { operationsLane } from "./operations.ts";
|
|
50
50
|
import { extractNameplate, nameplateRegisterQuery } from "./nameplate.ts";
|
|
51
51
|
import { P } from "./profile.ts";
|
|
52
|
+
import { toolNarration, intentPreamble, TOOL_NARRATION_NOTICE, INTENT_PREAMBLE_NOTICE } from "./answerGuards.ts";
|
|
52
53
|
|
|
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
|
-
|
|
68
|
-
// The intent preamble (2026-10-19, the ~200-word technical-file
|
|
69
|
-
// question): the draft is a PLAN, not an answer — "I'll look up the R
|
|
70
|
-
// 76-1 MPE clause and classification details in the corpus." — shipped
|
|
71
|
-
// while ten citations rode the same response. Narrow by construction:
|
|
72
|
-
// it must OPEN with a first-person intent verb AND carry no substance —
|
|
73
|
-
// short, no clause citation, no normative numbers (a real answer's
|
|
74
|
-
// opening names values or clauses).
|
|
75
|
-
function intentPreamble(t: string): boolean {
|
|
76
|
-
const s = (t ?? "").trim();
|
|
77
|
-
if (s.length > 240) return false;
|
|
78
|
-
if (!/^(?:i'll|i will|let me|let's|i'm going to|i am going to)\b/i.test(s)) return false;
|
|
79
|
-
return !/\[\[u:|\[\^?\d+\]|\b\d+\.\d+\b/.test(s);
|
|
80
|
-
}
|
|
81
|
-
const INTENT_PREAMBLE_NOTICE =
|
|
82
|
-
"Correction notice: your draft announced a plan ('I'll look up …') instead of answering. Do not narrate intent — answer the question NOW from the retrieved passages: give the values, clause references and citations the question asks for.";
|
|
83
54
|
|
|
84
55
|
/** User-uploaded image for multimodal questions: a data URL
|
|
85
56
|
* (data:image/(png|jpeg|webp|gif);base64,…) up to 6 MB of payload. The
|