@konneal/engine 0.2.74 → 0.2.76
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/{ask-FHA56VIN.js → ask-PK75QJ66.js} +2 -2
- package/dist/{certificates-EMHFAUWC.js → certificates-DNRUOYRF.js} +1 -1
- package/dist/{chunk-CNSTPYX3.js → chunk-64G44J7U.js} +57 -30
- package/dist/{chunk-3IRX4B7Q.js → chunk-VGVTIPFW.js} +1 -1
- package/dist/operations.d.ts +18 -1
- package/dist/worker_public/src/index.js +3 -3
- package/package.json +1 -1
- package/workers/worker_public/src/ask.ts +25 -4
- package/workers/worker_public/src/certificates.ts +1 -1
- package/workers/worker_public/src/nameplate.ts +29 -16
- package/workers/worker_public/src/operations.ts +23 -10
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleAsk
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-64G44J7U.js";
|
|
4
4
|
import "./chunk-7HWJOFTQ.js";
|
|
5
5
|
import "./chunk-SPKPH54J.js";
|
|
6
6
|
import "./chunk-A3QHHUN5.js";
|
|
7
7
|
import "./chunk-LVZTWUVJ.js";
|
|
8
8
|
import "./chunk-2PCUAJJO.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-VGVTIPFW.js";
|
|
10
10
|
import "./chunk-3FYJM7LH.js";
|
|
11
11
|
export {
|
|
12
12
|
handleAsk
|
|
@@ -72,7 +72,7 @@ import {
|
|
|
72
72
|
certificateLinks,
|
|
73
73
|
registerNote,
|
|
74
74
|
searchRegister
|
|
75
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-VGVTIPFW.js";
|
|
76
76
|
import {
|
|
77
77
|
P
|
|
78
78
|
} from "./chunk-3FYJM7LH.js";
|
|
@@ -1896,7 +1896,7 @@ async function runTool(db, call) {
|
|
|
1896
1896
|
if (call.name !== "register_search") return null;
|
|
1897
1897
|
const query = String(call.args?.query ?? "").trim().slice(0, 160);
|
|
1898
1898
|
if (!query) return null;
|
|
1899
|
-
const { searchRegister: searchRegister2, registerNote: registerNote2 } = await import("./certificates-
|
|
1899
|
+
const { searchRegister: searchRegister2, registerNote: registerNote2 } = await import("./certificates-DNRUOYRF.js");
|
|
1900
1900
|
const reg = await searchRegister2(db, query, true);
|
|
1901
1901
|
const output = reg?.rows?.length ? registerNote2(reg.rows) : `No certificate was found for "${query}" in the certificates database (the register snapshot). State this as the search's result, with the searched string visible.`;
|
|
1902
1902
|
return { name: call.name, query, output };
|
|
@@ -1992,18 +1992,18 @@ function catalogNote(anchors) {
|
|
|
1992
1992
|
return `The COMPLETE name index of the platform's operations:
|
|
1993
1993
|
${anchors.map((a) => `- ${a}`).join("\n")}`;
|
|
1994
1994
|
}
|
|
1995
|
-
async function
|
|
1996
|
-
if (!isOperationIntent(query)) return
|
|
1995
|
+
async function operationsLane(db, query) {
|
|
1996
|
+
if (!isOperationIntent(query)) return { matched: [] };
|
|
1997
1997
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1998
|
-
const
|
|
1999
|
-
if (
|
|
1998
|
+
const lane = await operationsLaneOnce(db, query, attempt > 0);
|
|
1999
|
+
if (lane !== void 0) return lane;
|
|
2000
2000
|
}
|
|
2001
|
-
return
|
|
2001
|
+
return { matched: [] };
|
|
2002
2002
|
}
|
|
2003
|
-
async function
|
|
2003
|
+
async function operationsLaneOnce(db, query, retried) {
|
|
2004
2004
|
try {
|
|
2005
|
-
const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
|
|
2006
|
-
const opRows = rows.map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? "") })).filter((r) => r.anchor);
|
|
2005
|
+
const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text, docidentifier FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
|
|
2006
|
+
const opRows = rows.map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? ""), docidentifier: String(r.docidentifier ?? "Platform API") })).filter((r) => r.anchor);
|
|
2007
2007
|
const matched = matchOperations(query, opRows);
|
|
2008
2008
|
const parts = [];
|
|
2009
2009
|
if (matched.length) {
|
|
@@ -2011,7 +2011,8 @@ async function operationsCatalogNoteOnce(db, query, retried) {
|
|
|
2011
2011
|
${matched.map((m) => `- ${m.text}`).join("\n")}`);
|
|
2012
2012
|
}
|
|
2013
2013
|
parts.push(catalogNote(opRows.map((r) => r.anchor)));
|
|
2014
|
-
|
|
2014
|
+
const note = parts.filter(Boolean).join("\n\n") || void 0;
|
|
2015
|
+
return { note, matched };
|
|
2015
2016
|
} catch (e) {
|
|
2016
2017
|
console.error(`operations-catalog: D1 read failed${retried ? " twice" : ""} \u2014 the note is dropped`, String(e).slice(0, 120));
|
|
2017
2018
|
return void 0;
|
|
@@ -2049,23 +2050,31 @@ function nameplateRegisterQuery(np, question) {
|
|
|
2049
2050
|
|
|
2050
2051
|
// workers/worker_public/src/nameplate.ts
|
|
2051
2052
|
async function extractNameplate(ai, model, image) {
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2053
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2054
|
+
try {
|
|
2055
|
+
const res = await ai.run(model, {
|
|
2056
|
+
messages: [
|
|
2057
|
+
{ role: "system", content: nameplate_default.trimEnd() },
|
|
2058
|
+
{ role: "user", content: [{ type: "image_url", image_url: { url: image } }] }
|
|
2059
|
+
],
|
|
2060
|
+
// the budget rises with the retry's effort — elevated effort under
|
|
2061
|
+
// a flat budget starves the answer on this model (measured,
|
|
2062
|
+
// 2026-09-18: 5/38 at medium with the flat budget)
|
|
2063
|
+
max_tokens: attempt === 0 ? 512 : 1536,
|
|
2064
|
+
reasoning_effort: attempt === 0 ? "low" : "medium"
|
|
2065
|
+
});
|
|
2066
|
+
const text = typeof res?.response === "string" ? res.response : res?.choices?.[0]?.message?.content;
|
|
2067
|
+
const np = parseNameplate(text ?? "");
|
|
2068
|
+
if (np) {
|
|
2069
|
+
console.log(`nameplate-extract: ${np.manufacturer} / ${np.model}${attempt ? " (on retry)" : ""}`);
|
|
2070
|
+
return np;
|
|
2071
|
+
}
|
|
2072
|
+
console.log(`nameplate-extract: no parse on attempt ${attempt + 1} (${(text ?? "").slice(0, 120)})`);
|
|
2073
|
+
} catch (e) {
|
|
2074
|
+
console.log(`nameplate-extract: FAILED attempt ${attempt + 1} ${String(e).slice(0, 160)}`);
|
|
2075
|
+
}
|
|
2068
2076
|
}
|
|
2077
|
+
return null;
|
|
2069
2078
|
}
|
|
2070
2079
|
|
|
2071
2080
|
// workers/worker_public/src/ask.ts
|
|
@@ -2461,7 +2470,7 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
2461
2470
|
}
|
|
2462
2471
|
console.log("register-search:", JSON.stringify({ shaped: !!register, tokens: register?.tokens ?? null, rows: register?.rows?.length ?? null, first: register?.rows?.[0]?.num ?? null }));
|
|
2463
2472
|
const regNote = register ? [registerNote(register.rows), certificateLinks(register.rows)].filter(Boolean).join("\n") : void 0;
|
|
2464
|
-
const
|
|
2473
|
+
const ops = await operationsLane(env.DB, q.query);
|
|
2465
2474
|
if (understanding?.intent === "conversational") {
|
|
2466
2475
|
const queryHash2 = await sha256Hex(q.query);
|
|
2467
2476
|
const messages2 = [
|
|
@@ -2807,13 +2816,31 @@ Answer account questions from these records ONLY: name the record when you use i
|
|
|
2807
2816
|
return matched.length ? matched : g;
|
|
2808
2817
|
})();
|
|
2809
2818
|
const vocabNote = glossaryForNote.length ? "Vocabulary binding \u2014 defined terms in the indexed corpus that may name this question's subject:\n" + glossaryForNote.map((g) => `- ${g.term} (${g.docidentifier}): ${g.definition}`).join("\n") + "\nIf the question describes a symptom or behavior in everyday words, OPEN the answer by naming the matching defined term, quote its definition, and cite its defining publication; keep using that term throughout. Match TIME SCALE carefully: change under a constant load over minutes/hours is creep; change over months/years of use is span stability or durability \u2014 do not call long-term drift creep." : void 0;
|
|
2819
|
+
const opsHits = ops.matched.map((m) => ({
|
|
2820
|
+
id: `ops:${m.anchor}`,
|
|
2821
|
+
score: 0.999,
|
|
2822
|
+
metadata: {
|
|
2823
|
+
doc_id: "smart-ops:catalog",
|
|
2824
|
+
docidentifier: m.docidentifier ?? "Platform API",
|
|
2825
|
+
doctype: "api-operation",
|
|
2826
|
+
doc_number: "",
|
|
2827
|
+
edition: "",
|
|
2828
|
+
language: "en",
|
|
2829
|
+
clause_anchor: m.anchor,
|
|
2830
|
+
clause_title: `Platform operation ${m.anchor}`,
|
|
2831
|
+
tier: "curated",
|
|
2832
|
+
corpus: "smart-ops",
|
|
2833
|
+
text_ref: ""
|
|
2834
|
+
},
|
|
2835
|
+
text: `${m.anchor} \u2014 ${m.text}`
|
|
2836
|
+
}));
|
|
2810
2837
|
const { messages, usedHits } = buildMessages(
|
|
2811
2838
|
q.query,
|
|
2812
|
-
hits,
|
|
2839
|
+
[...opsHits, ...hits],
|
|
2813
2840
|
q.lang,
|
|
2814
2841
|
keptHistory,
|
|
2815
2842
|
// stage-extracted graph facts (GraphRAG) ride the same note channel
|
|
2816
|
-
[
|
|
2843
|
+
[ops.note, processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...retrieved.notes ?? []].filter(Boolean).join("\n") || void 0,
|
|
2817
2844
|
summary,
|
|
2818
2845
|
budget
|
|
2819
2846
|
);
|
|
@@ -121,7 +121,7 @@ function registerNote(rows) {
|
|
|
121
121
|
].join("\n");
|
|
122
122
|
}
|
|
123
123
|
function certificateLinks(rows) {
|
|
124
|
-
const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.
|
|
124
|
+
const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.oimlsmart.org/cert-pdf/${r.pdf_key}`);
|
|
125
125
|
return links.length ? `The certificates' documents:
|
|
126
126
|
${links.join("\n")}` : "";
|
|
127
127
|
}
|
package/dist/operations.d.ts
CHANGED
|
@@ -6,9 +6,26 @@ export declare function anchorWords(anchor: string): string[];
|
|
|
6
6
|
export declare function matchOperations(query: string, rows: {
|
|
7
7
|
anchor: string;
|
|
8
8
|
text: string;
|
|
9
|
+
docidentifier?: string;
|
|
9
10
|
}[]): {
|
|
10
11
|
anchor: string;
|
|
11
12
|
text: string;
|
|
13
|
+
docidentifier?: string;
|
|
12
14
|
}[];
|
|
13
15
|
export declare function catalogNote(anchors: string[]): string;
|
|
14
|
-
|
|
16
|
+
/** The lane's whole result: the authoritative note (matched lines +
|
|
17
|
+
* name index) AND the matched rows themselves — the ask path rides the
|
|
18
|
+
* matched operations as CITABLE PASSAGES, because the refusal logic
|
|
19
|
+
* keys on passages and a side-note cannot outrank the passages'
|
|
20
|
+
* silence (observed live 2026-09-29: the note was present and the
|
|
21
|
+
* model still refused "the only API operation in the current context
|
|
22
|
+
* is the blob download"). */
|
|
23
|
+
export interface OperationsLane {
|
|
24
|
+
note?: string;
|
|
25
|
+
matched: {
|
|
26
|
+
anchor: string;
|
|
27
|
+
text: string;
|
|
28
|
+
docidentifier?: string;
|
|
29
|
+
}[];
|
|
30
|
+
}
|
|
31
|
+
export declare function operationsLane(db: any, query: string): Promise<OperationsLane>;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
modelGroundingBlock,
|
|
7
7
|
scoreFaithfulness,
|
|
8
8
|
scoreJudge
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-64G44J7U.js";
|
|
10
10
|
import {
|
|
11
11
|
handleSearch
|
|
12
12
|
} from "../../chunk-KO6DRI7C.js";
|
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
sha256Hex,
|
|
64
64
|
today
|
|
65
65
|
} from "../../chunk-2PCUAJJO.js";
|
|
66
|
-
import "../../chunk-
|
|
66
|
+
import "../../chunk-VGVTIPFW.js";
|
|
67
67
|
import {
|
|
68
68
|
P,
|
|
69
69
|
setProfile
|
|
@@ -959,7 +959,7 @@ async function handleMcp(env, ctx, req, tier, key) {
|
|
|
959
959
|
// stream:false forces the JSON lane (anon defaults to SSE)
|
|
960
960
|
body: JSON.stringify({ ...args, stream: false })
|
|
961
961
|
});
|
|
962
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
962
|
+
const res = name === "ask" ? await (await import("../../ask-PK75QJ66.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-LVUQLA3C.js")).handleSearch(env, ctx, inner, tier, key);
|
|
963
963
|
return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
964
964
|
});
|
|
965
965
|
if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
|
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.76",
|
|
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",
|
|
@@ -41,7 +41,7 @@ import { chargeTokens, checkQuota, clientIp, estimateTokens, telemetry, tokenBud
|
|
|
41
41
|
import { graphExpand, editionNote } from "./graph";
|
|
42
42
|
import { registerNote, searchRegister, certificateLinks } from "./certificates";
|
|
43
43
|
import { TOOL_DECLARATION, parseToolCall, runTool, toolNote } from "./tools";
|
|
44
|
-
import {
|
|
44
|
+
import { operationsLane } from "./operations.ts";
|
|
45
45
|
import { extractNameplate, nameplateRegisterQuery } from "./nameplate.ts";
|
|
46
46
|
import { P } from "./profile.ts";
|
|
47
47
|
|
|
@@ -677,7 +677,7 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
677
677
|
}
|
|
678
678
|
console.log("register-search:", JSON.stringify({ shaped: !!register, tokens: register?.tokens ?? null, rows: register?.rows?.length ?? null, first: register?.rows?.[0]?.num ?? null }));
|
|
679
679
|
const regNote = register ? [registerNote(register.rows), certificateLinks(register.rows)].filter(Boolean).join("\n") : undefined;
|
|
680
|
-
const
|
|
680
|
+
const ops = await operationsLane(env.DB, q.query);
|
|
681
681
|
|
|
682
682
|
// Conversational route, decided by query UNDERSTANDING (any language, any
|
|
683
683
|
// phrasing) — not string matching. No retrieval: nothing in the corpus
|
|
@@ -1182,13 +1182,34 @@ let convEntities: Array<{ entity: string; kind: string }> = [];
|
|
|
1182
1182
|
glossaryForNote.map((g) => `- ${g.term} (${g.docidentifier}): ${g.definition}`).join("\n") +
|
|
1183
1183
|
"\nIf the question describes a symptom or behavior in everyday words, OPEN the answer by naming the matching defined term, quote its definition, and cite its defining publication; keep using that term throughout. Match TIME SCALE carefully: change under a constant load over minutes/hours is creep; change over months/years of use is span stability or durability — do not call long-term drift creep."
|
|
1184
1184
|
: undefined;
|
|
1185
|
+
// the matched operations ride as CITABLE PASSAGES ahead of the
|
|
1186
|
+
// retrieved hits — the refusal logic and the citation machinery key on
|
|
1187
|
+
// passages, and a side-note cannot outrank the passages' silence
|
|
1188
|
+
const opsHits: Hit[] = ops.matched.map((m) => ({
|
|
1189
|
+
id: `ops:${m.anchor}`,
|
|
1190
|
+
score: 0.999,
|
|
1191
|
+
metadata: {
|
|
1192
|
+
doc_id: "smart-ops:catalog",
|
|
1193
|
+
docidentifier: m.docidentifier ?? "Platform API",
|
|
1194
|
+
doctype: "api-operation",
|
|
1195
|
+
doc_number: "",
|
|
1196
|
+
edition: "",
|
|
1197
|
+
language: "en",
|
|
1198
|
+
clause_anchor: m.anchor,
|
|
1199
|
+
clause_title: `Platform operation ${m.anchor}`,
|
|
1200
|
+
tier: "curated",
|
|
1201
|
+
corpus: "smart-ops",
|
|
1202
|
+
text_ref: "",
|
|
1203
|
+
},
|
|
1204
|
+
text: `${m.anchor} — ${m.text}`,
|
|
1205
|
+
}));
|
|
1185
1206
|
const { messages, usedHits } = buildMessages(
|
|
1186
1207
|
q.query,
|
|
1187
|
-
hits,
|
|
1208
|
+
[...opsHits, ...hits],
|
|
1188
1209
|
q.lang,
|
|
1189
1210
|
keptHistory,
|
|
1190
1211
|
// stage-extracted graph facts (GraphRAG) ride the same note channel
|
|
1191
|
-
[
|
|
1212
|
+
[ops.note, processNote, eNote, regNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...(retrieved.notes ?? [])].filter(Boolean).join("\n") || undefined,
|
|
1192
1213
|
summary,
|
|
1193
1214
|
budget,
|
|
1194
1215
|
);
|
|
@@ -134,7 +134,7 @@ export function registerNote(rows: RegisterRow[]): string {
|
|
|
134
134
|
* is in the R2 plane (TODO 7) — the user gets the document, not just
|
|
135
135
|
* the fact. */
|
|
136
136
|
export function certificateLinks(rows: RegisterRow[]): string {
|
|
137
|
-
const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.
|
|
137
|
+
const links = rows.filter((r) => r.pdf_key).map((r) => `- ${r.num}: https://www.oimlsmart.org/cert-pdf/${r.pdf_key}`);
|
|
138
138
|
return links.length ? `The certificates' documents:\n${links.join("\n")}` : "";
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -14,23 +14,36 @@ export type { Nameplate };
|
|
|
14
14
|
export { nameplateRegisterQuery };
|
|
15
15
|
|
|
16
16
|
export async function extractNameplate(ai: any, model: string, image: string): Promise<Nameplate | null> {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
// one retry, decided on the RESULT not the error: the observed live
|
|
18
|
+
// failure (2026-10-03) is a structurally valid response carrying null
|
|
19
|
+
// fields — the same image the answer model read perfectly a moment
|
|
20
|
+
// later. A transient vision-call miss must not strand a certificate
|
|
21
|
+
// question with no register rows.
|
|
22
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
23
|
+
try {
|
|
24
|
+
const res: any = await ai.run(model, {
|
|
25
|
+
messages: [
|
|
26
|
+
{ role: "system", content: nameplatePrompt.trimEnd() },
|
|
27
|
+
{ role: "user", content: [{ type: "image_url", image_url: { url: image } }] },
|
|
28
|
+
],
|
|
29
|
+
// the budget rises with the retry's effort — elevated effort under
|
|
30
|
+
// a flat budget starves the answer on this model (measured,
|
|
31
|
+
// 2026-09-18: 5/38 at medium with the flat budget)
|
|
32
|
+
max_tokens: attempt === 0 ? 512 : 1536,
|
|
33
|
+
reasoning_effort: attempt === 0 ? "low" : "medium",
|
|
34
|
+
});
|
|
35
|
+
const text = typeof res?.response === "string" ? res.response : res?.choices?.[0]?.message?.content;
|
|
36
|
+
const np = parseNameplate(text ?? "");
|
|
37
|
+
if (np) {
|
|
38
|
+
console.log(`nameplate-extract: ${np.manufacturer} / ${np.model}${attempt ? " (on retry)" : ""}`);
|
|
39
|
+
return np;
|
|
40
|
+
}
|
|
41
|
+
console.log(`nameplate-extract: no parse on attempt ${attempt + 1} (${(text ?? "").slice(0, 120)})`);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.log(`nameplate-extract: FAILED attempt ${attempt + 1} ${String(e).slice(0, 160)}`);
|
|
44
|
+
}
|
|
33
45
|
}
|
|
46
|
+
return null; // the bridge is optional — the ask never depends on it
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
export const nameplateModel = () => MODELS.member;
|
|
@@ -38,7 +38,7 @@ export function anchorWords(anchor: string): string[] {
|
|
|
38
38
|
|
|
39
39
|
const singular = (w: string) => (w.length > 3 && w.endsWith("s") ? w.slice(0, -1) : w);
|
|
40
40
|
|
|
41
|
-
export function matchOperations(query: string, rows: { anchor: string; text: string }[]): { anchor: string; text: string }[] {
|
|
41
|
+
export function matchOperations(query: string, rows: { anchor: string; text: string; docidentifier?: string }[]): { anchor: string; text: string; docidentifier?: string }[] {
|
|
42
42
|
const q = query.toLowerCase();
|
|
43
43
|
const stems = ACTION_STEMS.filter((s) => q.includes(s));
|
|
44
44
|
const objects = query
|
|
@@ -68,29 +68,42 @@ export function catalogNote(anchors: string[]): string {
|
|
|
68
68
|
return `The COMPLETE name index of the platform's operations:\n${anchors.map((a) => `- ${a}`).join("\n")}`;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
/** The lane's whole result: the authoritative note (matched lines +
|
|
72
|
+
* name index) AND the matched rows themselves — the ask path rides the
|
|
73
|
+
* matched operations as CITABLE PASSAGES, because the refusal logic
|
|
74
|
+
* keys on passages and a side-note cannot outrank the passages'
|
|
75
|
+
* silence (observed live 2026-09-29: the note was present and the
|
|
76
|
+
* model still refused "the only API operation in the current context
|
|
77
|
+
* is the blob download"). */
|
|
78
|
+
export interface OperationsLane {
|
|
79
|
+
note?: string;
|
|
80
|
+
matched: { anchor: string; text: string; docidentifier?: string }[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function operationsLane(db: any, query: string): Promise<OperationsLane> {
|
|
84
|
+
if (!isOperationIntent(query)) return { matched: [] };
|
|
73
85
|
// one retry, and the failure is LOUD: a silently dropped note answers
|
|
74
86
|
// an operation question with the OIML refusal (observed live 2026-09-29
|
|
75
87
|
// — a transient D1 read failed into the catch and the lane vanished)
|
|
76
88
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
89
|
+
const lane = await operationsLaneOnce(db, query, attempt > 0);
|
|
90
|
+
if (lane !== undefined) return lane;
|
|
79
91
|
}
|
|
80
|
-
return
|
|
92
|
+
return { matched: [] };
|
|
81
93
|
}
|
|
82
94
|
|
|
83
|
-
async function
|
|
95
|
+
async function operationsLaneOnce(db: any, query: string, retried: boolean): Promise<OperationsLane | undefined> {
|
|
84
96
|
try {
|
|
85
|
-
const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
|
|
86
|
-
const opRows = (rows as any[]).map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? "") })).filter((r) => r.anchor);
|
|
97
|
+
const rows = (await db.prepare("SELECT clause_anchor AS anchor, substr(text, 1, 300) AS text, docidentifier FROM chunks WHERE corpus = 'smart-ops' AND clause_anchor IS NOT NULL ORDER BY clause_anchor").all()).results ?? [];
|
|
98
|
+
const opRows = (rows as any[]).map((r) => ({ anchor: String(r.anchor ?? ""), text: String(r.text ?? ""), docidentifier: String(r.docidentifier ?? "Platform API") })).filter((r) => r.anchor);
|
|
87
99
|
const matched = matchOperations(query, opRows);
|
|
88
100
|
const parts: string[] = [];
|
|
89
101
|
if (matched.length) {
|
|
90
102
|
parts.push(`Operations whose name or description matches the question (AUTHORITATIVE — if one matches, the answer MUST name it and cite its line; never answer that the information is missing):\n${matched.map((m) => `- ${m.text}`).join("\n")}`);
|
|
91
103
|
}
|
|
92
104
|
parts.push(catalogNote(opRows.map((r) => r.anchor)));
|
|
93
|
-
|
|
105
|
+
const note = parts.filter(Boolean).join("\n\n") || undefined;
|
|
106
|
+
return { note, matched };
|
|
94
107
|
} catch (e) {
|
|
95
108
|
// the lane is optional — but never silent (the blind-failure lesson)
|
|
96
109
|
console.error(`operations-catalog: D1 read failed${retried ? " twice" : ""} — the note is dropped`, String(e).slice(0, 120));
|