@konneal/engine 0.2.90 → 0.2.92
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-D6JV2R2R.js → ask-UG5UYRJV.js} +2 -2
- package/dist/{chunk-KZV4I7ET.js → chunk-EHABW23I.js} +17 -504
- package/dist/chunk-QBWZ6HVP.js +962 -0
- package/dist/{tools-26IJZX5M.js → tools-PI7IXAL4.js} +2 -1
- package/dist/tools-content.d.ts +3 -0
- package/dist/worker_public/src/index.js +7 -7
- package/package.json +1 -1
- package/workers/worker_public/src/tools-content.ts +202 -0
- package/workers/worker_public/src/tools.ts +2 -2
- package/dist/chunk-A7QS2X3J.js +0 -279
package/dist/tools-content.d.ts
CHANGED
|
@@ -3,4 +3,7 @@ export declare const unitsGet: ToolSpec;
|
|
|
3
3
|
export declare const graphCites: ToolSpec;
|
|
4
4
|
export declare const docsSection: ToolSpec;
|
|
5
5
|
export declare const documentsFamily: ToolSpec;
|
|
6
|
+
export declare const licensedSection: ToolSpec;
|
|
7
|
+
export declare const verdictEvaluate: ToolSpec;
|
|
8
|
+
export declare const conditionsCheck: ToolSpec;
|
|
6
9
|
export declare const glossaryLookup: ToolSpec;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
bindModelNode,
|
|
3
2
|
checkQuoteAnchors,
|
|
4
3
|
handleAsk,
|
|
5
4
|
handleMemories,
|
|
6
|
-
modelGroundingBlock,
|
|
7
5
|
scoreFaithfulness,
|
|
8
6
|
scoreJudge
|
|
9
|
-
} from "../../chunk-
|
|
7
|
+
} from "../../chunk-EHABW23I.js";
|
|
10
8
|
import {
|
|
11
|
-
TOOLS_REGISTRY
|
|
12
|
-
|
|
9
|
+
TOOLS_REGISTRY,
|
|
10
|
+
bindModelNode,
|
|
11
|
+
modelGroundingBlock
|
|
12
|
+
} from "../../chunk-QBWZ6HVP.js";
|
|
13
13
|
import {
|
|
14
14
|
handleSearch
|
|
15
15
|
} from "../../chunk-TJTEBS4S.js";
|
|
@@ -973,12 +973,12 @@ async function handleMcp(env, ctx, req, tier, key) {
|
|
|
973
973
|
// stream:false forces the JSON lane (anon defaults to SSE)
|
|
974
974
|
body: JSON.stringify({ ...args, stream: false })
|
|
975
975
|
});
|
|
976
|
-
const { runTool, TOOLS_REGISTRY: TOOLS_REGISTRY2 } = await import("../../tools-
|
|
976
|
+
const { runTool, TOOLS_REGISTRY: TOOLS_REGISTRY2 } = await import("../../tools-PI7IXAL4.js");
|
|
977
977
|
if (TOOLS_REGISTRY2.some((t) => t.name === name && t.audiences.includes("mcp"))) {
|
|
978
978
|
const r = await runTool({ ...env, DB: env.DB }, { name, args }, "mcp");
|
|
979
979
|
return r ? { tool: r.name, query: r.query, result: r.output } : { error: { message: `tool ${name} returned nothing for the given arguments` } };
|
|
980
980
|
}
|
|
981
|
-
const res = name === "ask" ? await (await import("../../ask-
|
|
981
|
+
const res = name === "ask" ? await (await import("../../ask-UG5UYRJV.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-G76DK4AF.js")).handleSearch(env, ctx, inner, tier, key);
|
|
982
982
|
return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
983
983
|
});
|
|
984
984
|
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.92",
|
|
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",
|
|
@@ -10,6 +10,10 @@ import { resolveBlocks } from "./refs.ts";
|
|
|
10
10
|
import { portIndex, portModelRunner, hasLane } from "./env.ts";
|
|
11
11
|
import { embed } from "./ai.ts";
|
|
12
12
|
import { THRESHOLDS } from "./config.ts";
|
|
13
|
+
import { standardKeysFrom } from "./requestScope.ts";
|
|
14
|
+
import { licenseBoundaryRefusal, licensedEntryForDocNumber, licensedEntryForPackage } from "./modelplane.ts";
|
|
15
|
+
import { evaluate as evaluateVerdict } from "./verdict.ts";
|
|
16
|
+
import { evaluateConditionSets } from "./conditions.ts";
|
|
13
17
|
|
|
14
18
|
const slug = (docidentifier: string) =>
|
|
15
19
|
docidentifier
|
|
@@ -137,6 +141,204 @@ export const documentsFamily: ToolSpec = {
|
|
|
137
141
|
},
|
|
138
142
|
};
|
|
139
143
|
|
|
144
|
+
|
|
145
|
+
// The licensed library (the owner's 2026-10-04 direction): "return
|
|
146
|
+
// clause X of document Y" for licensed standards, behind the SAME
|
|
147
|
+
// entitlement predicate the ask path applies — the caller declares
|
|
148
|
+
// keys, the deployment's declared whitelist validates them (a forged
|
|
149
|
+
// key can never widen scope), and the unentitled caller receives the
|
|
150
|
+
// boundary refusal: the standard's title and edition, the declare
|
|
151
|
+
// pointer, never a word of the text. The entitled caller gets the
|
|
152
|
+
// model plane's typed nodes for the clause — the computing form.
|
|
153
|
+
export const licensedSection: ToolSpec = {
|
|
154
|
+
name: "licensed.section",
|
|
155
|
+
description:
|
|
156
|
+
"Fetch one clause of a licensed standard's typed model content — requirements, condition sets and parameters as structured data — behind the caller's license entitlement keys. An unentitled caller receives the license boundary: the standard's title and edition and the declare flow, never its text.",
|
|
157
|
+
params: [
|
|
158
|
+
{ key: "doc", required: true, description: "the standard's document number, e.g. 60068-2-30 or 61000-4-2" },
|
|
159
|
+
{ key: "clause", required: true, description: "the clause number inside the standard, e.g. 5 or 9" },
|
|
160
|
+
{ key: "licensed_standards", required: true, description: 'the caller\'s license entitlement keys, e.g. ["std:iec-60068-2-30"] — validated against the licenses this deployment declares; keys it does not know drop' },
|
|
161
|
+
],
|
|
162
|
+
audiences: ["mcp"],
|
|
163
|
+
handler: async (env, args) => {
|
|
164
|
+
const doc = String(args?.doc ?? "").trim();
|
|
165
|
+
const clause = String(args?.clause ?? "").trim();
|
|
166
|
+
if (doc.length < 3 || !clause) return null;
|
|
167
|
+
// "IEC 60068-2-30" / "ISO/IEC 17025" → the bare doc number the
|
|
168
|
+
// licensed registry keys on
|
|
169
|
+
const docNumber = doc.replace(/^(?:ISO\/IEC|ISO|IEC)\s+/i, "").trim();
|
|
170
|
+
const entry = licensedEntryForDocNumber(docNumber);
|
|
171
|
+
if (!entry) {
|
|
172
|
+
return {
|
|
173
|
+
name: "licensed.section",
|
|
174
|
+
query: `${doc} §${clause}`,
|
|
175
|
+
output: `"${doc}" is not among the licensed standards this deployment keys. For clauses of the public corpus use the docs.section tool instead. State this plainly.`,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const keys = standardKeysFrom({ licensed_standards: args?.licensed_standards });
|
|
179
|
+
if (!keys.has(entry.key)) {
|
|
180
|
+
const refusal = licenseBoundaryRefusal(docNumber, keys);
|
|
181
|
+
return {
|
|
182
|
+
name: "licensed.section",
|
|
183
|
+
query: `${doc} §${clause}`,
|
|
184
|
+
output: refusal ?? "License boundary — the caller's entitlement set does not cover this standard's text.",
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const rows = await env.DB.prepare(
|
|
189
|
+
"SELECT node_id, kind, name, clause_ref, content FROM model_nodes WHERE standard = ?1 AND clause_ref = ?2 ORDER BY node_id LIMIT 20",
|
|
190
|
+
)
|
|
191
|
+
.bind(entry.package, clause)
|
|
192
|
+
.all();
|
|
193
|
+
const nodes = ((rows.results ?? []) as { node_id: string; kind: string; name: string | null; clause_ref: string; content: string }[]);
|
|
194
|
+
if (!nodes.length) {
|
|
195
|
+
const c = await env.DB.prepare(
|
|
196
|
+
"SELECT DISTINCT clause_ref FROM model_nodes WHERE standard = ?1 ORDER BY clause_ref",
|
|
197
|
+
)
|
|
198
|
+
.bind(entry.package)
|
|
199
|
+
.all();
|
|
200
|
+
const clauses = ((c.results ?? []) as { clause_ref: string }[]).map((r) => r.clause_ref).filter(Boolean).join(", ");
|
|
201
|
+
return {
|
|
202
|
+
name: "licensed.section",
|
|
203
|
+
query: `${doc} §${clause}`,
|
|
204
|
+
output: `No typed nodes are indexed for ${doc} §${clause}.${clauses ? ` The clauses with typed nodes: ${clauses}.` : ""} State this plainly.`,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
name: "licensed.section",
|
|
209
|
+
query: `${doc} §${clause}`,
|
|
210
|
+
output: JSON.stringify(
|
|
211
|
+
nodes.map((n) => {
|
|
212
|
+
let content: unknown = n.content;
|
|
213
|
+
try { content = JSON.parse(n.content); } catch { /* verbatim when not JSON */ }
|
|
214
|
+
return { node_id: n.node_id, kind: n.kind, name: n.name, clause: n.clause_ref, content };
|
|
215
|
+
}),
|
|
216
|
+
),
|
|
217
|
+
};
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
// The deterministic engines as tools (the owner's 2026-10-04
|
|
226
|
+
// direction): computing with the standard, not reading it. Both wrap
|
|
227
|
+
// the engines the ask path already runs — no new evaluation logic
|
|
228
|
+
// lives here; the tool is the door, the engine is the authority.
|
|
229
|
+
// Void-when-parameters-miss is the honest contract: the tool states
|
|
230
|
+
// WHICH parameters the question did not state, never guesses.
|
|
231
|
+
|
|
232
|
+
export const verdictEvaluate: ToolSpec = {
|
|
233
|
+
name: "verdict.evaluate",
|
|
234
|
+
description:
|
|
235
|
+
"Deterministically evaluate a model-plane node's machine checks (OCL boolean expressions, threshold limits) against the quantities a question states. Returns pass/fail with every check's expression and values, or void with the missing parameter names when the question does not state enough. The machine computes; cite the node's clause.",
|
|
236
|
+
params: [
|
|
237
|
+
{ key: "node_id", required: true, description: "the model-plane node id, e.g. /req/metrological/repeatability (node ids appear in answers' verdict blocks)" },
|
|
238
|
+
{ key: "question", required: true, description: "the statement carrying the quantities, e.g. 'is mpe 0.02 with n_lc 3000 within the limit?'" },
|
|
239
|
+
],
|
|
240
|
+
audiences: ["mcp"],
|
|
241
|
+
handler: async (env, args) => {
|
|
242
|
+
const node_id = String(args?.node_id ?? "").trim();
|
|
243
|
+
const question = String(args?.question ?? "").trim();
|
|
244
|
+
if (!node_id.startsWith("/") || question.length < 4) return null;
|
|
245
|
+
try {
|
|
246
|
+
// the same zero-or-ambiguous rule bindModelNode applies: a node
|
|
247
|
+
// id that indexes under exactly one standard resolves; several
|
|
248
|
+
// or none resolve to nothing — no silent pick
|
|
249
|
+
const rows = await env.DB.prepare(
|
|
250
|
+
"SELECT standard, kind, name, content FROM model_nodes WHERE node_id = ?1 LIMIT 2",
|
|
251
|
+
)
|
|
252
|
+
.bind(node_id)
|
|
253
|
+
.all();
|
|
254
|
+
const found = (rows.results ?? []) as { standard: string; kind: string; name: string | null; content: string }[];
|
|
255
|
+
if (found.length !== 1) {
|
|
256
|
+
return {
|
|
257
|
+
name: "verdict.evaluate",
|
|
258
|
+
query: node_id,
|
|
259
|
+
output: found.length
|
|
260
|
+
? `The node id ${node_id} is indexed under several standards — name the standard to disambiguate. State this plainly.`
|
|
261
|
+
: `No model-plane node ${node_id} is indexed. State this plainly.`,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const node = found[0]!;
|
|
265
|
+
const entry = licensedEntryForPackage(node.standard);
|
|
266
|
+
if (entry) {
|
|
267
|
+
return {
|
|
268
|
+
name: "verdict.evaluate",
|
|
269
|
+
query: node_id,
|
|
270
|
+
output: licenseBoundaryRefusal(entry.doc_number, null) ?? "License boundary — the node belongs to a licensed standard the caller's entitlement set does not cover.",
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
let content: unknown;
|
|
274
|
+
try { content = JSON.parse(node.content); } catch { content = null; }
|
|
275
|
+
const v = evaluateVerdict(content, question);
|
|
276
|
+
if (!v) {
|
|
277
|
+
return {
|
|
278
|
+
name: "verdict.evaluate",
|
|
279
|
+
query: node_id,
|
|
280
|
+
output: `The node ${node_id} (${node.kind}) carries no machine-checkable expressions — it is not evaluatable by the verdict engine. State this plainly.`,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
name: "verdict.evaluate",
|
|
285
|
+
query: node_id,
|
|
286
|
+
output: JSON.stringify({ standard: node.standard, node_id, kind: node.kind, name: node.name, verdict: v.verdict, on_violation: v.on_violation, missing: v.missing, checks: v.checks }),
|
|
287
|
+
};
|
|
288
|
+
} catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export const conditionsCheck: ToolSpec = {
|
|
295
|
+
name: "conditions.check",
|
|
296
|
+
description:
|
|
297
|
+
"Check a stated combination of environmental quantities (temperature, humidity, duration, cycles) against the indexed severity/condition sets — the pass answer names the matched set and every band, the fail names the nearest set and its distance. Machine evaluation over the condition-set nodes; cite the matched set's clause.",
|
|
298
|
+
params: [
|
|
299
|
+
{ key: "question", required: true, description: "the stated combination, e.g. 'damp heat cyclic test at 55 °C for 2 cycles of 24 h'" },
|
|
300
|
+
{ key: "standard", required: false, description: "optional scope, the package id, e.g. iec-60068-2-30 (defaults to every indexed condition set the caller may see)" },
|
|
301
|
+
{ key: "licensed_standards", required: false, description: 'license entitlement keys for licensed condition sets, validated against the deployment\'s declared licenses' },
|
|
302
|
+
],
|
|
303
|
+
audiences: ["mcp"],
|
|
304
|
+
handler: async (env, args) => {
|
|
305
|
+
const question = String(args?.question ?? "").trim();
|
|
306
|
+
if (question.length < 4) return null;
|
|
307
|
+
const standard = args?.standard ? String(args.standard).trim() : null;
|
|
308
|
+
const keys = standardKeysFrom({ licensed_standards: args?.licensed_standards });
|
|
309
|
+
try {
|
|
310
|
+
const rows = standard
|
|
311
|
+
? await env.DB.prepare("SELECT node_id, standard, content FROM model_nodes WHERE kind = 'condition_set' AND standard = ?1").bind(standard).all()
|
|
312
|
+
: await env.DB.prepare("SELECT node_id, standard, content FROM model_nodes WHERE kind = 'condition_set'").all();
|
|
313
|
+
const all = ((rows.results ?? []) as { node_id: string; standard: string; content: string }[]).map((r) => {
|
|
314
|
+
let content: unknown = r.content;
|
|
315
|
+
try { content = JSON.parse(r.content); } catch { /* verbatim */ }
|
|
316
|
+
return { node_id: r.node_id, standard: r.standard, content };
|
|
317
|
+
});
|
|
318
|
+
// the ask path's own gate: licensed condition sets compete only
|
|
319
|
+
// for entitled callers — the same licensedEntryForPackage filter
|
|
320
|
+
const visible = all.filter((n) => {
|
|
321
|
+
const entry = licensedEntryForPackage(n.standard);
|
|
322
|
+
return !entry || keys.has(entry.key);
|
|
323
|
+
});
|
|
324
|
+
if (!visible.length) {
|
|
325
|
+
return { name: "conditions.check", query: question, output: "No condition sets are indexed (or visible to this caller's entitlements). State this plainly." };
|
|
326
|
+
}
|
|
327
|
+
const v = evaluateConditionSets(visible, question);
|
|
328
|
+
if (!v) {
|
|
329
|
+
return { name: "conditions.check", query: question, output: "No quantities were recognized in the statement — state the values (temperature, humidity, duration, cycles). The engine checks stated numbers, never guesses." };
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
name: "conditions.check",
|
|
333
|
+
query: question,
|
|
334
|
+
output: JSON.stringify({ verdict: v.verdict, matched: v.matched, checks: v.checks }),
|
|
335
|
+
};
|
|
336
|
+
} catch {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
|
|
140
342
|
export const glossaryLookup: ToolSpec = {
|
|
141
343
|
name: "glossary.lookup",
|
|
142
344
|
description:
|
|
@@ -64,9 +64,9 @@ const certificatesSearch: ToolSpec = {
|
|
|
64
64
|
},
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
import { unitsGet, graphCites, docsSection, glossaryLookup, documentsFamily } from "./tools-content.ts";
|
|
67
|
+
import { unitsGet, graphCites, docsSection, glossaryLookup, documentsFamily, licensedSection, verdictEvaluate, conditionsCheck } from "./tools-content.ts";
|
|
68
68
|
|
|
69
|
-
export const TOOLS_REGISTRY: ToolSpec[] = [certificatesSearch, unitsGet, graphCites, docsSection, glossaryLookup, documentsFamily];
|
|
69
|
+
export const TOOLS_REGISTRY: ToolSpec[] = [certificatesSearch, unitsGet, graphCites, docsSection, glossaryLookup, documentsFamily, licensedSection, verdictEvaluate, conditionsCheck];
|
|
70
70
|
|
|
71
71
|
/** The agent loop's protocol declaration, GENERATED from the registry —
|
|
72
72
|
* the model's tool vocabulary is always the registry's, never a
|
package/dist/chunk-A7QS2X3J.js
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
embed,
|
|
3
|
-
hasLane,
|
|
4
|
-
portIndex,
|
|
5
|
-
portModelRunner
|
|
6
|
-
} from "./chunk-LOBYXEVD.js";
|
|
7
|
-
import {
|
|
8
|
-
THRESHOLDS
|
|
9
|
-
} from "./chunk-2PCUAJJO.js";
|
|
10
|
-
|
|
11
|
-
// workers/worker_public/src/refs.ts
|
|
12
|
-
var REF = /\[\[(u:[A-Za-z0-9_-]+)\]\]/g;
|
|
13
|
-
function availableUnitIds(hits) {
|
|
14
|
-
const ids = /* @__PURE__ */ new Set();
|
|
15
|
-
for (const h of hits) {
|
|
16
|
-
const u = h.metadata?.unit_id;
|
|
17
|
-
if (u) ids.add(u);
|
|
18
|
-
}
|
|
19
|
-
return ids;
|
|
20
|
-
}
|
|
21
|
-
function parseRefs(text) {
|
|
22
|
-
return [...text.matchAll(REF)].map((m) => m[1]);
|
|
23
|
-
}
|
|
24
|
-
function sanitizeRefs(text, available) {
|
|
25
|
-
const dropped = [];
|
|
26
|
-
const out = text.replace(REF, (full, id) => {
|
|
27
|
-
if (available.has(id)) return full;
|
|
28
|
-
dropped.push(id);
|
|
29
|
-
return "";
|
|
30
|
-
});
|
|
31
|
-
return { text: out, dropped };
|
|
32
|
-
}
|
|
33
|
-
async function resolveBlocks(db, refs) {
|
|
34
|
-
if (!refs.length) return [];
|
|
35
|
-
const uniq = [...new Set(refs)].slice(0, 12);
|
|
36
|
-
const blocks = [];
|
|
37
|
-
for (let i = 0; i < uniq.length; i += 20) {
|
|
38
|
-
const batch = uniq.slice(i, i + 20);
|
|
39
|
-
const placeholders = batch.map((_, n) => `?${n + 1}`).join(",");
|
|
40
|
-
try {
|
|
41
|
-
const res = await db.prepare(`SELECT unit_id, type, docidentifier, edition, payload FROM unit_payloads WHERE unit_id IN (${placeholders})`).bind(...batch).all();
|
|
42
|
-
for (const r of res.results) {
|
|
43
|
-
let payload = {};
|
|
44
|
-
try {
|
|
45
|
-
payload = JSON.parse(String(r.payload));
|
|
46
|
-
} catch {
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
blocks.push({
|
|
50
|
-
unit_id: String(r.unit_id),
|
|
51
|
-
type: String(r.type),
|
|
52
|
-
docidentifier: String(r.docidentifier ?? ""),
|
|
53
|
-
edition: r.edition ? String(r.edition) : void 0,
|
|
54
|
-
payload
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
} catch (e) {
|
|
58
|
-
console.log("resolveBlocks failed:", String(e).slice(0, 150));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return blocks;
|
|
62
|
-
}
|
|
63
|
-
async function contractV2(db, answer, usedHits) {
|
|
64
|
-
const available = availableUnitIds(usedHits);
|
|
65
|
-
const { text, dropped } = sanitizeRefs(answer, available);
|
|
66
|
-
if (dropped.length) console.log("refs: dropped", dropped.length, "invalid (not in passages)");
|
|
67
|
-
const refs = parseRefs(text);
|
|
68
|
-
const blocks = await resolveBlocks(db, refs);
|
|
69
|
-
return { text, blocks, dropped };
|
|
70
|
-
}
|
|
71
|
-
function tableRetyped(text, availableTable) {
|
|
72
|
-
if (!availableTable) return false;
|
|
73
|
-
return /(^|\n)\s*\|[^\n]+\|\s*(\n\s*\|[-: |]+\|\s*)?(\n|$)/.test(text) && (text.match(/\|/g) ?? []).length >= 6;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// workers/worker_public/src/tools-content.ts
|
|
77
|
-
var slug = (docidentifier) => docidentifier.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
78
|
-
var unitsGet = {
|
|
79
|
-
name: "units.get",
|
|
80
|
-
description: "Fetch typed Recommendation content \u2014 a table, formula or figure as structured data (columns, rows, MathML, caption), by its unit id. The machine-consumable form of a Recommendation's content, for computing with rather than reading.",
|
|
81
|
-
params: [{ key: "unit_id", required: true, description: "the unit id exactly as an answer's [[u:\u2026]] reference spells it" }],
|
|
82
|
-
audiences: ["mcp"],
|
|
83
|
-
handler: async (env, args) => {
|
|
84
|
-
const unit_id = String(args?.unit_id ?? "").trim().replace(/^\[?\[?u:/, "u:").replace(/]?\]?$/, "");
|
|
85
|
-
if (!/^u:[\w.-]+$/.test(unit_id)) return null;
|
|
86
|
-
const blocks = await resolveBlocks(env.DB, [unit_id]);
|
|
87
|
-
if (!blocks.length) {
|
|
88
|
-
return { name: "units.get", query: unit_id, output: `No typed unit "${unit_id}" exists in the indexed payloads. State this plainly.` };
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
name: "units.get",
|
|
92
|
-
query: unit_id,
|
|
93
|
-
output: JSON.stringify(
|
|
94
|
-
blocks.map((b) => ({ unit_id: b.unit_id, type: b.type, docidentifier: b.docidentifier, payload: b.payload }))
|
|
95
|
-
)
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
var graphCites = {
|
|
100
|
-
name: "graph.cites",
|
|
101
|
-
description: "The bibliography edges: what a publication's own bibliography actually cites, extracted from its indexed bibliography sections. Returns the cited works' labels for a document (optionally scoped to an edition).",
|
|
102
|
-
params: [
|
|
103
|
-
{ key: "doc", required: true, description: "the publication identifier, e.g. R 60-1:2021" },
|
|
104
|
-
{ key: "edition", required: false, description: "an edition year to scope, e.g. 2021" }
|
|
105
|
-
],
|
|
106
|
-
audiences: ["mcp"],
|
|
107
|
-
handler: async (env, args) => {
|
|
108
|
-
const doc = String(args?.doc ?? "").trim();
|
|
109
|
-
if (doc.length < 3) return null;
|
|
110
|
-
const edition = args?.edition ? String(args.edition).trim() : null;
|
|
111
|
-
const pat = `%${doc.replace(/[:\s]+/g, "%")}%${edition ? edition : ""}%`;
|
|
112
|
-
try {
|
|
113
|
-
const rows = await env.DB.prepare(
|
|
114
|
-
"SELECT DISTINCT n.label AS label FROM graph_edges e JOIN documents d ON e.src = d.canonical_id JOIN graph_nodes n ON e.dst = n.id WHERE d.docidentifier LIKE ?1 AND e.kind = 'cites' ORDER BY label LIMIT 60"
|
|
115
|
-
).bind(pat).all();
|
|
116
|
-
const labels = (rows.results ?? []).map((r) => r.label).filter(Boolean);
|
|
117
|
-
return {
|
|
118
|
-
name: "graph.cites",
|
|
119
|
-
query: `${doc}${edition ? ` (${edition})` : ""}`,
|
|
120
|
-
output: labels.length ? `The publication's bibliography cites:
|
|
121
|
-
${labels.map((l) => `- ${l}`).join("\n")}` : `No bibliography citations are indexed for "${doc}"${edition ? ` (${edition})` : ""}. State this plainly.`
|
|
122
|
-
};
|
|
123
|
-
} catch {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
var docsSection = {
|
|
129
|
-
name: "docs.section",
|
|
130
|
-
description: "Fetch one clause of a publication's rendered document \u2014 its text and its permanent deep-link URL \u2014 for agents that need the human-formatted passage with its anchor.",
|
|
131
|
-
params: [
|
|
132
|
-
{ key: "doc", required: true, description: "the publication identifier, e.g. R 60-1:2021" },
|
|
133
|
-
{ key: "clause", required: true, description: "the clause anchor, e.g. 5.1.1 or annex-b" }
|
|
134
|
-
],
|
|
135
|
-
audiences: ["mcp"],
|
|
136
|
-
handler: async (env, args) => {
|
|
137
|
-
const doc = String(args?.doc ?? "").trim();
|
|
138
|
-
const clause = String(args?.clause ?? "").trim();
|
|
139
|
-
if (doc.length < 3 || !clause) return null;
|
|
140
|
-
try {
|
|
141
|
-
const rows = await env.DB.prepare(
|
|
142
|
-
"SELECT docidentifier, clause_anchor, clause_title, text FROM chunks WHERE docidentifier LIKE ?1 AND REPLACE(clause_anchor, ' ', '') = ?2 LIMIT 1"
|
|
143
|
-
).bind(`%${doc.replace(/[:\s]+/g, "%")}%`, clause).all();
|
|
144
|
-
const r = (rows.results ?? [])[0];
|
|
145
|
-
if (!r) {
|
|
146
|
-
return { name: "docs.section", query: `${doc} \xA7${clause}`, output: `No clause "${clause}" is indexed for "${doc}". State this plainly.` };
|
|
147
|
-
}
|
|
148
|
-
const url = `https://www.ommisa.org/docs/${slug(r.docidentifier)}.html#${r.clause_anchor}`;
|
|
149
|
-
return {
|
|
150
|
-
name: "docs.section",
|
|
151
|
-
query: `${r.docidentifier} \xA7${r.clause_anchor}`,
|
|
152
|
-
output: JSON.stringify({ docidentifier: r.docidentifier, clause: r.clause_anchor, title: r.clause_title, url, text: (r.text ?? "").slice(0, 4e3) })
|
|
153
|
-
};
|
|
154
|
-
} catch {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
var documentsFamily = {
|
|
160
|
-
name: "documents.family",
|
|
161
|
-
description: "Look up the publication registry for a family: every edition with its derived status (in-force/superseded), which edition is ACTIVE (terminal of the successor chain), and supersession links. Use for current/latest-edition and edition-history questions.",
|
|
162
|
-
params: [{ key: "family", required: true, description: "the family key, e.g. R-60 (series letter and number)" }],
|
|
163
|
-
audiences: ["mcp"],
|
|
164
|
-
handler: async (env, args) => {
|
|
165
|
-
const family = String(args?.family ?? "").trim().toUpperCase();
|
|
166
|
-
if (!/^[A-Z]-\d{1,3}$/.test(family)) return null;
|
|
167
|
-
try {
|
|
168
|
-
const rows = await env.DB.prepare(
|
|
169
|
-
"SELECT d.docidentifier, d.derived_status, d.active, s.docidentifier AS succ FROM documents d LEFT JOIN documents s ON d.superseded_by = s.canonical_id WHERE d.family = ?1 ORDER BY d.part, d.edition"
|
|
170
|
-
).bind(family).all();
|
|
171
|
-
const lines = (rows.results ?? []).map(
|
|
172
|
-
(r) => `${r.docidentifier} \u2014 ${r.derived_status}${r.active ? " [ACTIVE]" : ""}${r.succ ? ` \u2192 superseded by ${r.succ}` : ""}`
|
|
173
|
-
);
|
|
174
|
-
if (!lines.length) {
|
|
175
|
-
return { name: "documents.family", query: family, output: `No editions are registered for the family ${family}. State this plainly.` };
|
|
176
|
-
}
|
|
177
|
-
return { name: "documents.family", query: family, output: lines.join("\n") };
|
|
178
|
-
} catch {
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
var glossaryLookup = {
|
|
184
|
-
name: "glossary.lookup",
|
|
185
|
-
description: "Look up a defined term in the terminology datasets \u2014 the concept's definition and its defining publication. Bind everyday words to the defined term before interpreting a question.",
|
|
186
|
-
params: [{ key: "term", required: true, description: "the term or everyday phrase to bind, e.g. creep or load cell" }],
|
|
187
|
-
audiences: ["mcp"],
|
|
188
|
-
handler: async (env, args) => {
|
|
189
|
-
const term = String(args?.term ?? "").trim();
|
|
190
|
-
if (term.length < 2) return null;
|
|
191
|
-
if (!hasLane(env, "glossary")) return null;
|
|
192
|
-
try {
|
|
193
|
-
const vec = await embed(portModelRunner(env), "", term);
|
|
194
|
-
const cands = await portIndex(env, "glossary").query({ vector: vec, topK: 5 });
|
|
195
|
-
const hits = cands.filter((m) => m.score >= THRESHOLDS.glossaryCosineFloor).map((m) => ({
|
|
196
|
-
term: String(m.metadata?.clause_title ?? "").trim(),
|
|
197
|
-
definition: String(m.metadata?.chunk_text ?? "").split(" \u2014 ").slice(1).join(" \u2014 ").slice(0, 400),
|
|
198
|
-
docidentifier: String(m.metadata?.docidentifier ?? "")
|
|
199
|
-
})).filter((x) => x.term && x.definition);
|
|
200
|
-
if (!hits.length) {
|
|
201
|
-
return { name: "glossary.lookup", query: term, output: `No defined term matches "${term}" in the terminology datasets. State this plainly.` };
|
|
202
|
-
}
|
|
203
|
-
const seen = /* @__PURE__ */ new Set();
|
|
204
|
-
const uniq = hits.filter((h) => {
|
|
205
|
-
const k = h.term.toLowerCase().replace(/[^a-z0-9 ]/g, "").replace(/labeler\b/g, "labeller").replace(/\s+/g, " ").trim();
|
|
206
|
-
if (seen.has(k)) return false;
|
|
207
|
-
seen.add(k);
|
|
208
|
-
return true;
|
|
209
|
-
}).slice(0, 3);
|
|
210
|
-
return {
|
|
211
|
-
name: "glossary.lookup",
|
|
212
|
-
query: term,
|
|
213
|
-
output: uniq.map((h) => `- ${h.term} (${h.docidentifier}): ${h.definition}`).join("\n")
|
|
214
|
-
};
|
|
215
|
-
} catch {
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
// workers/worker_public/src/tools.ts
|
|
222
|
-
function parseToolCall(text) {
|
|
223
|
-
const m = /^TOOL\s+([a-z_.]+)\s*(\{[^\n}]*\})?/m.exec(String(text ?? ""));
|
|
224
|
-
if (!m) return null;
|
|
225
|
-
let args = {};
|
|
226
|
-
if (m[2]) {
|
|
227
|
-
try {
|
|
228
|
-
args = JSON.parse(m[2]);
|
|
229
|
-
} catch {
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
} else if (/^TOOL\s+[a-z_.]+\s*\{/m.test(String(text ?? ""))) {
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
return { name: m[1].toLowerCase(), args };
|
|
236
|
-
}
|
|
237
|
-
var certificatesSearch = {
|
|
238
|
-
name: "certificates.search",
|
|
239
|
-
description: "Search the certificate register (a snapshot) by holder name, model designation, or a printed certificate number. Returns the matching rows \u2014 number, holder, model, issue year, status, and document links where on file \u2014 or the exact no-match statement for the string asked.",
|
|
240
|
-
params: [{ key: "query", required: true, description: "the holder, model, or printed certificate number to look up" }],
|
|
241
|
-
audiences: ["agent", "mcp"],
|
|
242
|
-
handler: async (env, args) => {
|
|
243
|
-
const query = String(args?.query ?? "").trim().slice(0, 160);
|
|
244
|
-
if (!query) return null;
|
|
245
|
-
const { searchRegister, registerNote, certificateLinks } = await import("./certificates-CO7KX2S2.js");
|
|
246
|
-
const reg = await searchRegister(env.DB, query, true);
|
|
247
|
-
const output = reg?.rows?.length ? [registerNote(reg.rows), certificateLinks(reg.rows)].filter(Boolean).join("\n") : `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.`;
|
|
248
|
-
return { name: "certificates.search", query, output };
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
var TOOLS_REGISTRY = [certificatesSearch, unitsGet, graphCites, docsSection, glossaryLookup, documentsFamily];
|
|
252
|
-
var TOOL_DECLARATION = [
|
|
253
|
-
"You may use one tool before answering, by writing a single line:",
|
|
254
|
-
...TOOLS_REGISTRY.filter((t) => t.audiences.includes("agent")).map((t) => {
|
|
255
|
-
const shape = `{${t.params.map((p) => `"${p.key}": "<${p.description}>"`).join(", ")}}`;
|
|
256
|
-
return `TOOL ${t.name} ${shape}`;
|
|
257
|
-
}),
|
|
258
|
-
"The worker runs it and returns the result attributed \u2014 phrase the tool's result as what it returned, with the searched string visible in your answer. Use a tool when the question turns on what it answers (including from a photograph). If you do not need it, answer directly without the line."
|
|
259
|
-
].join("\n");
|
|
260
|
-
async function runTool(env, call, audience = "agent") {
|
|
261
|
-
const spec = TOOLS_REGISTRY.find((t) => t.name === call.name && t.audiences.includes(audience));
|
|
262
|
-
if (!spec) return null;
|
|
263
|
-
return spec.handler(env, call.args ?? {});
|
|
264
|
-
}
|
|
265
|
-
function toolNote(r) {
|
|
266
|
-
return `The ${r.name} tool returned, for the query "${r.query}":
|
|
267
|
-
${r.output}`;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export {
|
|
271
|
-
resolveBlocks,
|
|
272
|
-
contractV2,
|
|
273
|
-
tableRetyped,
|
|
274
|
-
parseToolCall,
|
|
275
|
-
TOOLS_REGISTRY,
|
|
276
|
-
TOOL_DECLARATION,
|
|
277
|
-
runTool,
|
|
278
|
-
toolNote
|
|
279
|
-
};
|