@konneal/engine 0.2.18 → 0.2.20

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.
Files changed (35) hide show
  1. package/dist/ask-36JS3555.js +12 -0
  2. package/dist/boundary.d.ts +17 -0
  3. package/dist/{chunk-EFQALN2Z.js → chunk-BV7IKH3N.js} +2 -2
  4. package/dist/{chunk-5MBWE7WD.js → chunk-COKARI7O.js} +2 -2
  5. package/dist/{chunk-7UT76RQO.js → chunk-DGFJUA2C.js} +8 -7
  6. package/dist/{chunk-PBXE2LFD.js → chunk-EE6V6X6D.js} +82 -18
  7. package/dist/{chunk-DBBGOOMZ.js → chunk-FNJ457SE.js} +1 -1
  8. package/dist/{chunk-ADXV2DPK.js → chunk-RESCBSX6.js} +1 -1
  9. package/dist/{chunk-YFDWKXEB.js → chunk-SLZ4TZNO.js} +4 -4
  10. package/dist/{chunk-TJRTVJW5.js → chunk-YTWPZE5O.js} +11 -5
  11. package/dist/drafts.d.ts +1 -1
  12. package/dist/profile.gen.d.ts +7 -5
  13. package/dist/profile2.gen.d.ts +2 -0
  14. package/dist/search-YA6TD6QJ.js +12 -0
  15. package/dist/worker_mcp/src/index.js +3 -3
  16. package/dist/worker_public/src/config.js +2 -2
  17. package/dist/worker_public/src/index.js +10 -10
  18. package/dist/worker_public/src/profile.js +1 -1
  19. package/dist/worker_public/src/refusal.js +2 -2
  20. package/dist/worker_public/src/requestScope.js +3 -3
  21. package/package.json +1 -1
  22. package/profile/publisher.yaml +4 -0
  23. package/profile/sources.yaml +1 -1
  24. package/workers/worker_public/src/aggregation.ts +6 -5
  25. package/workers/worker_public/src/ask.ts +25 -4
  26. package/workers/worker_public/src/auth.ts +1 -1
  27. package/workers/worker_public/src/boundary.ts +73 -0
  28. package/workers/worker_public/src/drafts.ts +8 -8
  29. package/workers/worker_public/src/index.ts +1 -1
  30. package/workers/worker_public/src/livedata.ts +2 -2
  31. package/workers/worker_public/src/pipeline.ts +2 -1
  32. package/workers/worker_public/src/profile.gen.ts +11 -5
  33. package/workers/worker_public/src/profile2.gen.ts +4 -0
  34. package/dist/ask-4O753Y4T.js +0 -12
  35. package/dist/search-RFKT7ZZY.js +0 -12
@@ -0,0 +1,12 @@
1
+ import {
2
+ handleAsk
3
+ } from "./chunk-EE6V6X6D.js";
4
+ import "./chunk-DGFJUA2C.js";
5
+ import "./chunk-BV7IKH3N.js";
6
+ import "./chunk-FNJ457SE.js";
7
+ import "./chunk-COKARI7O.js";
8
+ import "./chunk-RESCBSX6.js";
9
+ import "./chunk-YTWPZE5O.js";
10
+ export {
11
+ handleAsk
12
+ };
@@ -0,0 +1,17 @@
1
+ export interface LicensedEntry {
2
+ key: string;
3
+ package?: string;
4
+ doc_number?: string;
5
+ title?: string;
6
+ }
7
+ export interface LicensedMatch {
8
+ entry: LicensedEntry;
9
+ matched: string[];
10
+ }
11
+ export declare function distinctiveTokens(title: string): string[];
12
+ /** The licensed entry whose distinctive title tokens best match the
13
+ * question. Needs TWO token hits (or one hyphenated-compound hit) —
14
+ * a single shared word is not a topic match. */
15
+ export declare function matchLicensedTopic(query: string, licensed: LicensedEntry[]): LicensedMatch | null;
16
+ /** The note text: the posture instruction for an unentitled match. */
17
+ export declare function boundaryNoteText(match: LicensedMatch, citing: string[]): string;
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  LIMITS,
3
3
  sha256Hex
4
- } from "./chunk-ADXV2DPK.js";
4
+ } from "./chunk-RESCBSX6.js";
5
5
  import {
6
6
  P
7
- } from "./chunk-TJRTVJW5.js";
7
+ } from "./chunk-YTWPZE5O.js";
8
8
 
9
9
  // workers/worker_public/src/bubble.ts
10
10
  function isAllowedBubbleOrigin(origin) {
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  DATASETS,
3
3
  datasetAllowed
4
- } from "./chunk-ADXV2DPK.js";
4
+ } from "./chunk-RESCBSX6.js";
5
5
  import {
6
6
  P
7
- } from "./chunk-TJRTVJW5.js";
7
+ } from "./chunk-YTWPZE5O.js";
8
8
 
9
9
  // workers/worker_public/src/requestScope.ts
10
10
  function licenseDeclared() {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  bubbleConfirmPage,
3
3
  isAllowedBubbleOrigin
4
- } from "./chunk-EFQALN2Z.js";
4
+ } from "./chunk-BV7IKH3N.js";
5
5
  import {
6
6
  DATASETS,
7
7
  LIMITS,
@@ -12,12 +12,12 @@ import {
12
12
  processExpansion,
13
13
  sha256Hex,
14
14
  today
15
- } from "./chunk-ADXV2DPK.js";
15
+ } from "./chunk-RESCBSX6.js";
16
16
  import {
17
17
  P,
18
18
  __commonJS,
19
19
  __toESM
20
- } from "./chunk-TJRTVJW5.js";
20
+ } from "./chunk-YTWPZE5O.js";
21
21
 
22
22
  // node_modules/@oimlsmart/oiml-pubid/dist/index.js
23
23
  var require_dist = __commonJS({
@@ -1939,7 +1939,8 @@ ${context}` }
1939
1939
  };
1940
1940
  }
1941
1941
  function publicationUrl(meta) {
1942
- if (meta.corpus && !["oiml", "dirty", "clean"].includes(meta.corpus)) return void 0;
1942
+ const ownCorpora = P().publisher.catalog_corpora;
1943
+ if (meta.corpus && ownCorpora && !ownCorpora.includes(meta.corpus)) return void 0;
1943
1944
  const tpl = P().publisher.catalog_url_template;
1944
1945
  if (!tpl || !meta.doctype || !meta.doc_number) return void 0;
1945
1946
  return tpl.replace("{type}", meta.doctype.toLowerCase()) + meta.doc_number;
@@ -2032,9 +2033,9 @@ async function sha256Hex2(s) {
2032
2033
  function liveDataConfig(env) {
2033
2034
  const platformApi = (env.SMART_PLATFORM_API ?? "").trim().replace(/\/$/, "");
2034
2035
  const platformClientId = (env.SMART_PLATFORM_CLIENT_ID ?? "").trim();
2035
- const issuer = (env.OIDC_ISSUER ?? "https://id.oimlsmart.org").trim().replace(/\/$/, "");
2036
+ const issuer = (env.OIDC_ISSUER ?? "").trim().replace(/\/$/, "");
2036
2037
  const clientId = (env.OIDC_CLIENT_ID ?? "").trim();
2037
- if (!platformApi || !platformClientId || !clientId) return null;
2038
+ if (!platformApi || !platformClientId || !clientId || !issuer) return null;
2038
2039
  return { platformApi, platformClientId, issuer, clientId, clientSecret: env.OIDC_CLIENT_SECRET };
2039
2040
  }
2040
2041
  var SUBJECT_KEY = (sessionHash) => `opat:${sessionHash}`;
@@ -2444,7 +2445,7 @@ function authErrorText(reason) {
2444
2445
  return PLAIN_LANGUAGE[reason] ?? "Sign-in failed. Please try again.";
2445
2446
  }
2446
2447
  function authConfig(env) {
2447
- const issuer = env.OIDC_ISSUER ?? "https://id.oimlsmart.org";
2448
+ const issuer = (env.OIDC_ISSUER ?? "").trim().replace(/\/$/, "");
2448
2449
  const clientId = env.OIDC_CLIENT_ID;
2449
2450
  const redirectUri = env.OIDC_REDIRECT_URI ?? "https://ai.oimlsmart.org/auth/callback";
2450
2451
  const sessionSecret = env.SESSION_SECRET;
@@ -27,23 +27,23 @@ import {
27
27
  syntheticUnderstanding,
28
28
  telemetry,
29
29
  understandQuery
30
- } from "./chunk-7UT76RQO.js";
30
+ } from "./chunk-DGFJUA2C.js";
31
31
  import {
32
32
  corsHeaders,
33
33
  err,
34
34
  json,
35
35
  readJson,
36
36
  validateQuery
37
- } from "./chunk-EFQALN2Z.js";
37
+ } from "./chunk-BV7IKH3N.js";
38
38
  import {
39
39
  canonicalRefusal,
40
40
  refusalAnswer
41
- } from "./chunk-DBBGOOMZ.js";
41
+ } from "./chunk-FNJ457SE.js";
42
42
  import {
43
43
  entitlementScope,
44
44
  requestSalt,
45
45
  resolveRequestScope
46
- } from "./chunk-5MBWE7WD.js";
46
+ } from "./chunk-COKARI7O.js";
47
47
  import {
48
48
  LIMITS,
49
49
  MODELS,
@@ -53,10 +53,10 @@ import {
53
53
  requestEffort,
54
54
  roleModel,
55
55
  sha256Hex
56
- } from "./chunk-ADXV2DPK.js";
56
+ } from "./chunk-RESCBSX6.js";
57
57
  import {
58
58
  P
59
- } from "./chunk-TJRTVJW5.js";
59
+ } from "./chunk-YTWPZE5O.js";
60
60
 
61
61
  // workers/worker_public/src/internal_gateway.ts
62
62
  async function retrieveInternal(service, auth, query) {
@@ -883,9 +883,10 @@ function classToken(query) {
883
883
  return m ? m[1].toLowerCase() : null;
884
884
  }
885
885
  function classAsColumn(cols, token) {
886
- const exact = cols.find((c) => /^class_[a-z0-9.]+$/.test(c.name) && c.name.slice(6) === token);
886
+ const classCols = cols.filter((c) => /^class_[a-z0-9.]+$/.test(c.name));
887
+ const exact = classCols.find((c) => c.name.slice(6) === token);
887
888
  if (exact) return exact;
888
- return cols.find((c) => /^class_[a-z0-9.]+$/.test(c.name) && c.name.slice(6) === "cd" && (token === "c" || token === "d"));
889
+ return classCols.find((c) => c.name.slice(6).startsWith(token));
889
890
  }
890
891
  function numericColumns(cols) {
891
892
  return cols.filter((c) => c.type === "number" || c.type === "integer" || /^class_[a-z0-9.]+$/.test(c.name));
@@ -1074,9 +1075,57 @@ function evaluateAggregation(nodes, query) {
1074
1075
  return null;
1075
1076
  }
1076
1077
 
1078
+ // workers/worker_public/src/boundary.ts
1079
+ var TITLE_STOPWORDS = /* @__PURE__ */ new Set([
1080
+ "environmental",
1081
+ "testing",
1082
+ "test",
1083
+ "tests",
1084
+ "guidance",
1085
+ "generic",
1086
+ "standards",
1087
+ "standard",
1088
+ "electromagnetic",
1089
+ "compatibility",
1090
+ "environment",
1091
+ "description",
1092
+ "measurement",
1093
+ "techniques",
1094
+ "immunity",
1095
+ "residential",
1096
+ "commercial",
1097
+ "industrial",
1098
+ "environments",
1099
+ "and",
1100
+ "for",
1101
+ "the",
1102
+ "iec",
1103
+ "iso"
1104
+ ]);
1105
+ function distinctiveTokens(title) {
1106
+ return title.toLowerCase().split(/[^a-z0-9.]+/).filter((w) => w.length >= 3 && !TITLE_STOPWORDS.has(w) && !/^[0-9:-]+$/.test(w));
1107
+ }
1108
+ function matchLicensedTopic(query, licensed) {
1109
+ const words = new Set(query.toLowerCase().split(/[^a-z0-9.]+/).filter(Boolean));
1110
+ let best = null;
1111
+ for (const entry of licensed) {
1112
+ if (!entry.title) continue;
1113
+ const matched = distinctiveTokens(entry.title).filter((w) => words.has(w));
1114
+ if (matched.length < 2) continue;
1115
+ if (!best || matched.length > best.matched.length) best = { entry, matched };
1116
+ }
1117
+ return best;
1118
+ }
1119
+ function boundaryNoteText(match, citing) {
1120
+ const doc = match.entry.doc_number ?? match.entry.key;
1121
+ const title = match.entry.title ?? doc;
1122
+ const refs = citing.length ? `The public corpus references it from ${citing.join(", ")}.` : "";
1123
+ return `LICENSED BOUNDARY \u2014 ${title} (IEC ${doc}) is licensed content in this deployment; its procedure is NOT part of the public corpus you are grounded in. ${refs} When answering: name the licensed document as the authoritative source of the procedure and say it is available to entitled callers. Do NOT state its conditioning or severity parameters in ANY form \u2014 no temperatures, humidity levels, durations or cycle counts, not even as a single bound or as a value another publication repeats or cites; keep the description structural (expose, condition, recover) and let the licensed document own the numbers. If the grounding passages state parameter values, say only that the publications describe their own requirements and that the procedure's parameters are in the licensed document.`;
1124
+ }
1125
+
1077
1126
  // workers/worker_public/src/drafts.ts
1078
1127
  var ACT_VERB = "(?:draft|prepare|pre-?fill|fill\\s+(?:in|out)|start|submit|file|lodge)";
1079
- var ACT_TARGET = "(?:new\\s+)?(?:certification\\s+|type[ -]evaluation\\s+|OIML[- ]CS\\s+)?application";
1128
+ var ACT_TARGET = "(?:new\\s+)?(?:certification\\s+|type[ -]evaluation\\s+)?application";
1080
1129
  var INTENT_RES = [
1081
1130
  new RegExp(`\\b${ACT_VERB}\\b[\\s\\S]{0,60}?\\b${ACT_TARGET}\\b`, "i"),
1082
1131
  new RegExp(`\\b${ACT_TARGET}\\b[\\s\\S]{0,30}?\\b(?:draft|prepare|pre-?fill|for me)\\b`, "i")
@@ -1101,18 +1150,18 @@ function roleLabel(role) {
1101
1150
  if (["cs_admin", "admin"].includes(role)) return "a scheme administrator";
1102
1151
  return `the "${role}" role`;
1103
1152
  }
1104
- var EXTRACTION_SYSTEM = `You extract the fields of a new OIML certification application from the user's own messages.
1153
+ var EXTRACTION_SYSTEM = `You extract the fields of a new certification application from the user's own messages.
1105
1154
 
1106
1155
  Rules:
1107
1156
  - Output ONLY a JSON object \u2014 no prose, no code fence.
1108
1157
  - Copy every value from the user's own words, and for each field give "source": the exact contiguous span of the user's message you copied it from.
1109
1158
  - NEVER infer, complete, normalize away, or guess a value. If the user did not state it, omit the field entirely.
1110
- - "standard": the Recommendation the user named (e.g. "R 60" or "OIML R 60:2021") \u2014 a plain string, or omit when none was named.
1159
+ - "standard": the publication the user named (e.g. "AB 99" or "ACME AB 99:2019") \u2014 a plain string, or omit when none was named.
1111
1160
  - "scheme": only when the user named scheme A or scheme B explicitly.
1112
1161
 
1113
1162
  Schema (every field optional):
1114
1163
  {
1115
- "standard": "R 60",
1164
+ "standard": "AB 99",
1116
1165
  "family_designation": { "value": "\u2026", "source": "\u2026" },
1117
1166
  "group_label": { "value": "\u2026", "source": "\u2026" },
1118
1167
  "model_designation": { "value": "\u2026", "source": "\u2026" },
@@ -1275,14 +1324,14 @@ async function prepareDraft(env, opts) {
1275
1324
  const untraced = dropped.find((d) => d.field === "standard");
1276
1325
  return refusal(
1277
1326
  "standard_unresolved",
1278
- untraced ? `I can't anchor the draft: you haven't named the Recommendation in your own words (the ${untraced.value} reading isn't yours). Name it plainly \u2014 for example OIML R 60 \u2014 and I'll prepare the draft.` : "I can't anchor the draft: you haven't named the Recommendation. Name it plainly \u2014 for example OIML R 60 \u2014 and I'll prepare it."
1327
+ untraced ? `I can't anchor the draft: you haven't named the Recommendation in your own words (the ${untraced.value} reading isn't yours). Name it plainly \u2014 for example ACME AB 99 \u2014 and I'll prepare the draft.` : "I can't anchor the draft: you haven't named the publication. Name it plainly \u2014 for example ACME AB 99 \u2014 and I'll prepare it."
1279
1328
  );
1280
1329
  }
1281
1330
  const standard = await resolveStandard(env, kept.standard);
1282
1331
  if (!standard) {
1283
1332
  return refusal(
1284
1333
  "standard_unresolved",
1285
- `I couldn't resolve ${kept.standard} as a publication in the corpus, so I can't anchor the draft. Name the Recommendation plainly \u2014 for example OIML R 60 \u2014 and I'll prepare it.`
1334
+ `I couldn't resolve ${kept.standard} as a publication in the corpus, so I can't anchor the draft. Name the publication plainly \u2014 for example ACME AB 99 \u2014 and I'll prepare it.`
1286
1335
  );
1287
1336
  }
1288
1337
  const fields = {
@@ -1904,7 +1953,7 @@ ${summary}` }] : [],
1904
1953
  const verdictBlock = machineVerdict ? {
1905
1954
  unit_id: boundModel.node_id,
1906
1955
  type: "verdict",
1907
- docidentifier: `${P().publisher.name} SMART model (${boundModel.standard})`,
1956
+ docidentifier: `${P().publisher.name} model (${boundModel.standard})`,
1908
1957
  payload: {
1909
1958
  verdict: machineVerdict.verdict,
1910
1959
  on_violation: machineVerdict.on_violation,
@@ -1917,7 +1966,7 @@ ${summary}` }] : [],
1917
1966
  const conditionBlock = conditionVerdict ? {
1918
1967
  unit_id: conditionVerdict.matched[0] ?? conditionVerdict.nearest.node_id,
1919
1968
  type: "verdict",
1920
- docidentifier: `IEC SMART model (${conditionStandard})`,
1969
+ docidentifier: `${P().publisher.name} model (${conditionStandard})`,
1921
1970
  payload: {
1922
1971
  verdict: conditionVerdict.verdict,
1923
1972
  missing: [],
@@ -1952,7 +2001,7 @@ ${summary}` }] : [],
1952
2001
  const aggregationBlock = aggregationVerdict ? {
1953
2002
  unit_id: aggregationVerdict.table,
1954
2003
  type: "verdict",
1955
- docidentifier: `SMART model table${aggregationStandard ? ` (${aggregationStandard})` : ""}`,
2004
+ docidentifier: `${P().publisher.name} model table${aggregationStandard ? ` (${aggregationStandard})` : ""}`,
1956
2005
  payload: {
1957
2006
  check: `${aggregationVerdict.operation}: ${aggregationVerdict.column ?? aggregationVerdict.table_title ?? aggregationVerdict.table} = ${aggregationVerdict.value}${aggregationVerdict.unit ? ` ${aggregationVerdict.unit}` : ""}`,
1958
2007
  meaning: aggregationVerdict.table_title,
@@ -1963,6 +2012,21 @@ ${summary}` }] : [],
1963
2012
  }
1964
2013
  } : null;
1965
2014
  if (aggregationVerdict) console.log("aggregation engine:", aggregationVerdict.operation, aggregationVerdict.table, "\u2192", aggregationVerdict.value);
2015
+ let boundaryNote = null;
2016
+ if (P().sources?.licensed?.length) {
2017
+ const match = matchLicensedTopic(q.query, P().sources.licensed);
2018
+ if (match && !(standardKeys?.has(match.entry.key) ?? false)) {
2019
+ const docNum = match.entry.doc_number ?? "";
2020
+ let citing = [];
2021
+ if (docNum) {
2022
+ const rows = await env.DB.prepare(
2023
+ "SELECT n.label AS label FROM graph_edges e JOIN graph_nodes n ON e.src = n.id WHERE e.kind = 'cites' AND e.dst LIKE ?1 LIMIT 4"
2024
+ ).bind(`%${docNum}%`).all().catch(() => ({ results: [] }));
2025
+ citing = (rows.results ?? []).map((r) => String(r.label ?? "").replace(/^doc:/, ""));
2026
+ }
2027
+ boundaryNote = boundaryNoteText(match, citing);
2028
+ }
2029
+ }
1966
2030
  try {
1967
2031
  const tR = Date.now();
1968
2032
  if (declaredCtx?.kind === "account") {
@@ -2056,7 +2120,7 @@ Answer account questions from these records ONLY: name the record when you use i
2056
2120
  q.lang,
2057
2121
  keptHistory,
2058
2122
  // stage-extracted graph facts (GraphRAG) ride the same note channel
2059
- [processNote, eNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, licenseNote, ...retrieved.notes ?? []].filter(Boolean).join("\n") || void 0,
2123
+ [processNote, eNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...retrieved.notes ?? []].filter(Boolean).join("\n") || void 0,
2060
2124
  summary,
2061
2125
  budget
2062
2126
  );
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  P
3
- } from "./chunk-TJRTVJW5.js";
3
+ } from "./chunk-YTWPZE5O.js";
4
4
 
5
5
  // workers/worker_public/src/refusal.ts
6
6
  function refusalAnswer() {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  P
3
- } from "./chunk-TJRTVJW5.js";
3
+ } from "./chunk-YTWPZE5O.js";
4
4
 
5
5
  // workers/worker_public/src/config.ts
6
6
  var MODELS = {
@@ -7,24 +7,24 @@ import {
7
7
  sessionFrom,
8
8
  telemetry,
9
9
  understandQuery
10
- } from "./chunk-7UT76RQO.js";
10
+ } from "./chunk-DGFJUA2C.js";
11
11
  import {
12
12
  corsHeaders,
13
13
  err,
14
14
  json,
15
15
  readJson,
16
16
  validateQuery
17
- } from "./chunk-EFQALN2Z.js";
17
+ } from "./chunk-BV7IKH3N.js";
18
18
  import {
19
19
  entitlementScope,
20
20
  standardKeysFrom
21
- } from "./chunk-5MBWE7WD.js";
21
+ } from "./chunk-COKARI7O.js";
22
22
  import {
23
23
  LIMITS,
24
24
  MODELS,
25
25
  num,
26
26
  sha256Hex
27
- } from "./chunk-ADXV2DPK.js";
27
+ } from "./chunk-RESCBSX6.js";
28
28
 
29
29
  // workers/worker_public/src/search.ts
30
30
  async function handleSearch(env, ctx, req, tier, key) {
@@ -48,6 +48,12 @@ var PROFILE = {
48
48
  "references": {
49
49
  "label_prefix": ""
50
50
  },
51
+ "catalog_url_template": "https://catalog.fixture.example.org/{type}",
52
+ "catalog_corpora": [
53
+ "pub",
54
+ "dirty",
55
+ "clean"
56
+ ],
51
57
  "features": {
52
58
  "drafts": false,
53
59
  "model_plane": false
@@ -109,11 +115,11 @@ var PROFILE = {
109
115
  "models": {},
110
116
  "licensed": [
111
117
  {
112
- "key": "std:fixture-60068-2-30",
113
- "package": "fixture-60068-2-30",
114
- "doc_number": "60068-2-30",
115
- "title": "FIXTURE environmental testing \u2014 damp heat, cyclic",
116
- "edition": "2005"
118
+ "key": "std:fixture-ab-99",
119
+ "package": "fixture-ab-99",
120
+ "doc_number": "ab-99",
121
+ "title": "FIXTURE climatic conditioning, cyclic humidity",
122
+ "edition": "2019"
117
123
  }
118
124
  ]
119
125
  },
package/dist/drafts.d.ts CHANGED
@@ -5,7 +5,7 @@ export interface DraftSample {
5
5
  export interface ApplicationPrefillFields {
6
6
  /** the Recommendation, as the estate URN (urn:oiml:pub:r:60:2021) */
7
7
  standard_doc: string;
8
- /** display label ("OIML R 60:2021") */
8
+ /** display label ("ACME AB 99:2019") */
9
9
  standard_label?: string;
10
10
  family_designation?: string;
11
11
  group_label?: string;
@@ -17,6 +17,8 @@ export declare const PROFILE: {
17
17
  readonly references: {
18
18
  readonly label_prefix: "";
19
19
  };
20
+ readonly catalog_url_template: "https://catalog.fixture.example.org/{type}";
21
+ readonly catalog_corpora: readonly ["pub", "dirty", "clean"];
20
22
  readonly features: {
21
23
  readonly drafts: false;
22
24
  readonly model_plane: false;
@@ -55,11 +57,11 @@ export declare const PROFILE: {
55
57
  readonly terminology: {};
56
58
  readonly models: {};
57
59
  readonly licensed: readonly [{
58
- readonly key: "std:fixture-60068-2-30";
59
- readonly package: "fixture-60068-2-30";
60
- readonly doc_number: "60068-2-30";
61
- readonly title: "FIXTURE environmental testing — damp heat, cyclic";
62
- readonly edition: "2005";
60
+ readonly key: "std:fixture-ab-99";
61
+ readonly package: "fixture-ab-99";
62
+ readonly doc_number: "ab-99";
63
+ readonly title: "FIXTURE climatic conditioning, cyclic humidity";
64
+ readonly edition: "2019";
63
65
  }];
64
66
  };
65
67
  readonly ui: {
@@ -22,6 +22,8 @@ export declare const PROFILE: {
22
22
  readonly review: readonly ["review"];
23
23
  readonly glossary: readonly ["glossary"];
24
24
  };
25
+ readonly catalog_url_template: "https://catalog.atlas.example/{type}";
26
+ readonly catalog_corpora: readonly ["spec"];
25
27
  readonly features: {
26
28
  readonly drafts: false;
27
29
  readonly model_plane: false;
@@ -0,0 +1,12 @@
1
+ import {
2
+ handleSearch
3
+ } from "./chunk-SLZ4TZNO.js";
4
+ import "./chunk-DGFJUA2C.js";
5
+ import "./chunk-BV7IKH3N.js";
6
+ import "./chunk-FNJ457SE.js";
7
+ import "./chunk-COKARI7O.js";
8
+ import "./chunk-RESCBSX6.js";
9
+ import "./chunk-YTWPZE5O.js";
10
+ export {
11
+ handleSearch
12
+ };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  authenticate
3
- } from "../../chunk-EFQALN2Z.js";
4
- import "../../chunk-ADXV2DPK.js";
3
+ } from "../../chunk-BV7IKH3N.js";
4
+ import "../../chunk-RESCBSX6.js";
5
5
  import {
6
6
  P,
7
7
  setProfile
8
- } from "../../chunk-TJRTVJW5.js";
8
+ } from "../../chunk-YTWPZE5O.js";
9
9
 
10
10
  // workers/worker_mcp/src/index.ts
11
11
  var PROTOCOL_VERSION = "2025-06-18";
@@ -16,8 +16,8 @@ import {
16
16
  roleModel,
17
17
  sha256Hex,
18
18
  today
19
- } from "../../chunk-ADXV2DPK.js";
20
- import "../../chunk-TJRTVJW5.js";
19
+ } from "../../chunk-RESCBSX6.js";
20
+ import "../../chunk-YTWPZE5O.js";
21
21
  export {
22
22
  DATASETS,
23
23
  LIMITS,
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  handleSearch
3
- } from "../../chunk-YFDWKXEB.js";
3
+ } from "../../chunk-SLZ4TZNO.js";
4
4
  import {
5
5
  checkQuoteAnchors,
6
6
  handleAsk,
7
7
  handleMemories,
8
8
  scoreJudge,
9
9
  standardForDocNumber
10
- } from "../../chunk-PBXE2LFD.js";
10
+ } from "../../chunk-EE6V6X6D.js";
11
11
  import {
12
12
  buildMessages,
13
13
  citations,
@@ -29,7 +29,7 @@ import {
29
29
  sessionFrom,
30
30
  telemetry,
31
31
  understandQuery
32
- } from "../../chunk-7UT76RQO.js";
32
+ } from "../../chunk-DGFJUA2C.js";
33
33
  import {
34
34
  authenticate,
35
35
  corsHeaders,
@@ -38,14 +38,14 @@ import {
38
38
  readJson,
39
39
  validateQuery,
40
40
  withCors
41
- } from "../../chunk-EFQALN2Z.js";
41
+ } from "../../chunk-BV7IKH3N.js";
42
42
  import {
43
43
  canonicalRefusal
44
- } from "../../chunk-DBBGOOMZ.js";
44
+ } from "../../chunk-FNJ457SE.js";
45
45
  import {
46
46
  entitlementScope,
47
47
  standardKeysFrom
48
- } from "../../chunk-5MBWE7WD.js";
48
+ } from "../../chunk-COKARI7O.js";
49
49
  import {
50
50
  LIMITS,
51
51
  MODELS,
@@ -56,11 +56,11 @@ import {
56
56
  roleModel,
57
57
  sha256Hex,
58
58
  today
59
- } from "../../chunk-ADXV2DPK.js";
59
+ } from "../../chunk-RESCBSX6.js";
60
60
  import {
61
61
  P,
62
62
  setProfile
63
- } from "../../chunk-TJRTVJW5.js";
63
+ } from "../../chunk-YTWPZE5O.js";
64
64
 
65
65
  // workers/worker_public/src/conversations.ts
66
66
  var ID_RE = /^[a-zA-Z0-9_-]{8,64}$/;
@@ -979,7 +979,7 @@ async function handleMcp(env, ctx, req, tier, key) {
979
979
  // stream:false forces the JSON lane (anon defaults to SSE)
980
980
  body: JSON.stringify({ ...args, stream: false })
981
981
  });
982
- const res = name === "ask" ? await (await import("../../ask-4O753Y4T.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-RFKT7ZZY.js")).handleSearch(env, ctx, inner, tier, key);
982
+ const res = name === "ask" ? await (await import("../../ask-36JS3555.js")).handleAsk(env, ctx, inner, tier, key) : await (await import("../../search-YA6TD6QJ.js")).handleSearch(env, ctx, inner, tier, key);
983
983
  return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
984
984
  });
985
985
  if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
@@ -1404,7 +1404,7 @@ async function absenceRoute(c) {
1404
1404
  const namedStd = namedDocumentIn(String(body?.standard ?? ""));
1405
1405
  const standard = standardForDocNumber(namedStd?.doc_number ?? String(body?.standard ?? "").trim());
1406
1406
  const topic = String(body?.topic ?? "").trim().toLowerCase();
1407
- if (!standard || !topic) return err(400, "invalid_input", 'standard (e.g. "R 60") and topic are required');
1407
+ if (!standard || !topic) return err(400, "invalid_input", "standard and topic are required");
1408
1408
  try {
1409
1409
  const nodes = (await env.DB.prepare("SELECT node_id, kind, name, content FROM model_nodes WHERE standard = ?1").bind(standard).all()).results ?? [];
1410
1410
  const tokens = topic.split(/\s+/).filter((t2) => t2.length > 2);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  P,
3
3
  setProfile
4
- } from "../../chunk-TJRTVJW5.js";
4
+ } from "../../chunk-YTWPZE5O.js";
5
5
  export {
6
6
  P,
7
7
  setProfile
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  canonicalRefusal,
3
3
  refusalAnswer
4
- } from "../../chunk-DBBGOOMZ.js";
5
- import "../../chunk-TJRTVJW5.js";
4
+ } from "../../chunk-FNJ457SE.js";
5
+ import "../../chunk-YTWPZE5O.js";
6
6
  export {
7
7
  canonicalRefusal,
8
8
  refusalAnswer
@@ -4,9 +4,9 @@ import {
4
4
  requestSalt,
5
5
  resolveRequestScope,
6
6
  standardKeysFrom
7
- } from "../../chunk-5MBWE7WD.js";
8
- import "../../chunk-ADXV2DPK.js";
9
- import "../../chunk-TJRTVJW5.js";
7
+ } from "../../chunk-COKARI7O.js";
8
+ import "../../chunk-RESCBSX6.js";
9
+ import "../../chunk-YTWPZE5O.js";
10
10
  export {
11
11
  entitlementScope,
12
12
  licenseDeclared,
package/package.json CHANGED
@@ -34,7 +34,7 @@
34
34
  "@oimlsmart/oiml-pubid": "^1.2.1"
35
35
  },
36
36
  "dependencies": {},
37
- "version": "0.2.18",
37
+ "version": "0.2.20",
38
38
  "description": "The Konneal engine: the publisher-agnostic build pipeline and API plane for standards intelligence (retrieval, answer contract, verdicts, evaluation).",
39
39
  "license": "BSD-3-Clause",
40
40
  "type": "module",
@@ -19,6 +19,10 @@ codec: plain-slug
19
19
  session_cookie: fixture-session
20
20
  references:
21
21
  label_prefix: ""
22
+ # the publisher's catalog page ({type} = lowercase doctype) and the
23
+ # corpora the catalog covers (foreign-corpus chunks get no catalog link)
24
+ catalog_url_template: "https://catalog.fixture.example.org/{type}"
25
+ catalog_corpora: [pub, dirty, clean]
22
26
  # engine features this deployment switches on (default: off — the
23
27
  # engine serves every publisher without them)
24
28
  features:
@@ -11,4 +11,4 @@ models: {}
11
11
  # Absent or empty = the deployment serves public content only. The
12
12
  # fixture declares one so the license scope rides every engine test.
13
13
  licensed:
14
- - { key: "std:fixture-60068-2-30", package: "fixture-60068-2-30", doc_number: "60068-2-30", title: "FIXTURE environmental testing — damp heat, cyclic", edition: "2005" }
14
+ - { key: "std:fixture-ab-99", package: "fixture-ab-99", doc_number: "ab-99", title: "FIXTURE climatic conditioning, cyclic humidity", edition: "2019" }
@@ -73,13 +73,14 @@ function classToken(query: string): string | null {
73
73
  return m ? m[1]!.toLowerCase() : null;
74
74
  }
75
75
 
76
- /** The class columns shaped `class_a` / `class_cd` — classes as COLUMNS
77
- * (R 60's stabilisation table) rather than as row values. */
76
+ /** The class columns shaped `class_<id>` — classes as COLUMNS rather
77
+ * than as row values. Exact id first; a column whose suffix STARTS WITH
78
+ * the token serves compound class ids sharing one column (c → cd). */
78
79
  function classAsColumn(cols: Column[], token: string): Column | undefined {
79
- const exact = cols.find((c) => /^class_[a-z0-9.]+$/.test(c.name) && c.name.slice(6) === token);
80
+ const classCols = cols.filter((c) => /^class_[a-z0-9.]+$/.test(c.name));
81
+ const exact = classCols.find((c) => c.name.slice(6) === token);
80
82
  if (exact) return exact;
81
- // classes C and D share one column (class_cd) in R 60-2 Table 1
82
- return cols.find((c) => /^class_[a-z0-9.]+$/.test(c.name) && c.name.slice(6) === "cd" && (token === "c" || token === "d"));
83
+ return classCols.find((c) => c.name.slice(6).startsWith(token));
83
84
  }
84
85
 
85
86
  function numericColumns(cols: Column[]): Column[] {
@@ -23,6 +23,7 @@ import { bindModelNode, licenseBoundaryNote, licenseBoundaryRefusal, licensedEnt
23
23
  import { evaluate as machineEvaluate, verdictNote } from "./verdict";
24
24
  import { evaluateConditionSets, quantitiesIn, type ConditionVerdict } from "./conditions";
25
25
  import { evaluateAggregation, type AggregationVerdict } from "./aggregation";
26
+ import { matchLicensedTopic, boundaryNoteText } from "./boundary";
26
27
  import { detectDraftIntent, prepareDraft } from "./drafts";
27
28
  import { memoryNote } from "./memories";
28
29
  import { entitlementScope, resolveRequestScope, requestSalt } from "./requestScope";
@@ -773,7 +774,7 @@ async function handleAsk(
773
774
  ? {
774
775
  unit_id: boundModel!.node_id,
775
776
  type: "verdict",
776
- docidentifier: `${P().publisher.name} SMART model (${boundModel!.standard})`,
777
+ docidentifier: `${P().publisher.name} model (${boundModel!.standard})`,
777
778
  payload: {
778
779
  verdict: machineVerdict.verdict,
779
780
  on_violation: machineVerdict.on_violation,
@@ -788,7 +789,7 @@ async function handleAsk(
788
789
  ? {
789
790
  unit_id: conditionVerdict.matched[0] ?? conditionVerdict.nearest!.node_id,
790
791
  type: "verdict",
791
- docidentifier: `IEC SMART model (${conditionStandard})`,
792
+ docidentifier: `${P().publisher.name} model (${conditionStandard})`,
792
793
  payload: {
793
794
  verdict: conditionVerdict.verdict,
794
795
  missing: [],
@@ -834,7 +835,7 @@ async function handleAsk(
834
835
  ? {
835
836
  unit_id: aggregationVerdict.table,
836
837
  type: "verdict",
837
- docidentifier: `SMART model table${aggregationStandard ? ` (${aggregationStandard})` : ""}`,
838
+ docidentifier: `${P().publisher.name} model table${aggregationStandard ? ` (${aggregationStandard})` : ""}`,
838
839
  payload: {
839
840
  check: `${aggregationVerdict.operation}: ${aggregationVerdict.column ?? aggregationVerdict.table_title ?? aggregationVerdict.table} = ${aggregationVerdict.value}${aggregationVerdict.unit ? ` ${aggregationVerdict.unit}` : ""}`,
840
841
  meaning: aggregationVerdict.table_title,
@@ -846,6 +847,26 @@ async function handleAsk(
846
847
  }
847
848
  : null;
848
849
  if (aggregationVerdict) console.log("aggregation engine:", aggregationVerdict.operation, aggregationVerdict.table, "→", aggregationVerdict.value);
850
+ // ── the licensed boundary note (TODO.rag/12): an unentitled question
851
+ // that is topically ABOUT a licensed standard gets the boundary
852
+ // posture — the citation graph names the public publications that
853
+ // reference the licensed document, and the note instructs the model
854
+ // to attribute, never to recite the licensed parameters.
855
+ let boundaryNote: string | null = null;
856
+ if (P().sources?.licensed?.length) {
857
+ const match = matchLicensedTopic(q.query, P().sources.licensed);
858
+ if (match && !(standardKeys?.has(match.entry.key) ?? false)) {
859
+ const docNum = match.entry.doc_number ?? "";
860
+ let citing: string[] = [];
861
+ if (docNum) {
862
+ const rows = await env.DB.prepare(
863
+ "SELECT n.label AS label FROM graph_edges e JOIN graph_nodes n ON e.src = n.id WHERE e.kind = 'cites' AND e.dst LIKE ?1 LIMIT 4",
864
+ ).bind(`%${docNum}%`).all().catch(() => ({ results: [] }));
865
+ citing = (rows.results ?? []).map((r: any) => String(r.label ?? "").replace(/^doc:/, ""));
866
+ }
867
+ boundaryNote = boundaryNoteText(match, citing);
868
+ }
869
+ }
849
870
  try {
850
871
  const tR = Date.now();
851
872
  // ── The "my account" live read (TODO.ai-platform/03) — resolved
@@ -995,7 +1016,7 @@ async function handleAsk(
995
1016
  q.lang,
996
1017
  keptHistory,
997
1018
  // stage-extracted graph facts (GraphRAG) ride the same note channel
998
- [processNote, eNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, licenseNote, ...(retrieved.notes ?? [])].filter(Boolean).join("\n") || undefined,
1019
+ [processNote, eNote, contextNote(declaredCtx, docScope), accountNote, modelNote, vocabNote, memNote, machineNote, conditionNote, aggregationNote, boundaryNote, licenseNote, ...(retrieved.notes ?? [])].filter(Boolean).join("\n") || undefined,
999
1020
  summary,
1000
1021
  budget,
1001
1022
  );
@@ -48,7 +48,7 @@ export interface AuthConfig {
48
48
  }
49
49
 
50
50
  function authConfig(env: any): AuthConfig | null {
51
- const issuer = env.OIDC_ISSUER ?? "https://id.oimlsmart.org";
51
+ const issuer = (env.OIDC_ISSUER ?? "").trim().replace(/\/$/, "");
52
52
  const clientId = env.OIDC_CLIENT_ID;
53
53
  const redirectUri = env.OIDC_REDIRECT_URI ?? "https://ai.oimlsmart.org/auth/callback";
54
54
  const sessionSecret = env.SESSION_SECRET;
@@ -0,0 +1,73 @@
1
+ // The licensed boundary note (TODO.rag/12): when a question is about a
2
+ // topic that matches a LICENSED standard the caller is not entitled to,
3
+ // the note gives the answer model the boundary posture — name the
4
+ // licensed document as the authoritative procedure, name the public
5
+ // publications that reference it, and never recite the licensed
6
+ // parameters as if from the source. Pure matching/text lives here; the
7
+ // inverse-cites D1 read lives at the call site.
8
+
9
+ export interface LicensedEntry {
10
+ key: string;
11
+ package?: string;
12
+ doc_number?: string;
13
+ title?: string;
14
+ }
15
+
16
+ export interface LicensedMatch {
17
+ entry: LicensedEntry;
18
+ matched: string[];
19
+ }
20
+
21
+ // title words that carry no topical identity — a question about "tests"
22
+ // or "immunity" must not alone light the boundary
23
+ const TITLE_STOPWORDS = new Set([
24
+ "environmental", "testing", "test", "tests", "guidance", "generic",
25
+ "standards", "standard", "electromagnetic", "compatibility",
26
+ "environment", "description", "measurement", "techniques", "immunity",
27
+ "residential", "commercial", "industrial", "environments", "and", "for", "the",
28
+ "iec", "iso",
29
+ ]);
30
+
31
+ export function distinctiveTokens(title: string): string[] {
32
+ return title
33
+ .toLowerCase()
34
+ .split(/[^a-z0-9.]+/)
35
+ .filter((w) => w.length >= 3 && !TITLE_STOPWORDS.has(w) && !/^[0-9:-]+$/.test(w));
36
+ }
37
+
38
+ /** The licensed entry whose distinctive title tokens best match the
39
+ * question. Needs TWO token hits (or one hyphenated-compound hit) —
40
+ * a single shared word is not a topic match. */
41
+ export function matchLicensedTopic(query: string, licensed: LicensedEntry[]): LicensedMatch | null {
42
+ const words = new Set(query.toLowerCase().split(/[^a-z0-9.]+/).filter(Boolean));
43
+ let best: { entry: LicensedEntry; matched: string[] } | null = null;
44
+ for (const entry of licensed) {
45
+ if (!entry.title) continue;
46
+ const matched = distinctiveTokens(entry.title).filter((w) => words.has(w));
47
+ if (matched.length < 2) continue;
48
+ if (!best || matched.length > best.matched.length) best = { entry, matched };
49
+ }
50
+ return best;
51
+ }
52
+
53
+ /** The note text: the posture instruction for an unentitled match. */
54
+ export function boundaryNoteText(match: LicensedMatch, citing: string[]): string {
55
+ const doc = match.entry.doc_number ?? match.entry.key;
56
+ const title = match.entry.title ?? doc;
57
+ const refs = citing.length
58
+ ? `The public corpus references it from ${citing.join(", ")}.`
59
+ : "";
60
+ return (
61
+ `LICENSED BOUNDARY — ${title} (IEC ${doc}) is licensed content in this ` +
62
+ `deployment; its procedure is NOT part of the public corpus you are grounded in. ` +
63
+ `${refs} When answering: name the licensed document as the authoritative source of ` +
64
+ `the procedure and say it is available to entitled callers. Do NOT state its ` +
65
+ `conditioning or severity parameters in ANY form — no temperatures, humidity ` +
66
+ `levels, durations or cycle counts, not even as a single bound or as a value ` +
67
+ `another publication repeats or cites; keep the description structural (expose, ` +
68
+ `condition, recover) and let the licensed document own the numbers. If the ` +
69
+ `grounding passages state parameter values, say only that the publications ` +
70
+ `describe their own requirements and that the procedure's parameters are in the ` +
71
+ `licensed document.`
72
+ );
73
+ }
@@ -48,7 +48,7 @@ export interface DraftSample {
48
48
  export interface ApplicationPrefillFields {
49
49
  /** the Recommendation, as the estate URN (urn:oiml:pub:r:60:2021) */
50
50
  standard_doc: string;
51
- /** display label ("OIML R 60:2021") */
51
+ /** display label ("ACME AB 99:2019") */
52
52
  standard_label?: string;
53
53
  family_designation?: string;
54
54
  group_label?: string;
@@ -124,7 +124,7 @@ export interface PrepareOpts {
124
124
  // performs).
125
125
 
126
126
  const ACT_VERB = "(?:draft|prepare|pre-?fill|fill\\s+(?:in|out)|start|submit|file|lodge)";
127
- const ACT_TARGET = "(?:new\\s+)?(?:certification\\s+|type[ -]evaluation\\s+|OIML[- ]CS\\s+)?application";
127
+ const ACT_TARGET = "(?:new\\s+)?(?:certification\\s+|type[ -]evaluation\\s+)?application";
128
128
  const INTENT_RES = [
129
129
  new RegExp(`\\b${ACT_VERB}\\b[\\s\\S]{0,60}?\\b${ACT_TARGET}\\b`, "i"),
130
130
  new RegExp(`\\b${ACT_TARGET}\\b[\\s\\S]{0,30}?\\b(?:draft|prepare|pre-?fill|for me)\\b`, "i"),
@@ -165,18 +165,18 @@ function roleLabel(role: string): string {
165
165
 
166
166
  // ── the extraction (the LLM proposes) ────────────────────────────────
167
167
 
168
- const EXTRACTION_SYSTEM = `You extract the fields of a new OIML certification application from the user's own messages.
168
+ const EXTRACTION_SYSTEM = `You extract the fields of a new certification application from the user's own messages.
169
169
 
170
170
  Rules:
171
171
  - Output ONLY a JSON object — no prose, no code fence.
172
172
  - Copy every value from the user's own words, and for each field give "source": the exact contiguous span of the user's message you copied it from.
173
173
  - NEVER infer, complete, normalize away, or guess a value. If the user did not state it, omit the field entirely.
174
- - "standard": the Recommendation the user named (e.g. "R 60" or "OIML R 60:2021") — a plain string, or omit when none was named.
174
+ - "standard": the publication the user named (e.g. "AB 99" or "ACME AB 99:2019") — a plain string, or omit when none was named.
175
175
  - "scheme": only when the user named scheme A or scheme B explicitly.
176
176
 
177
177
  Schema (every field optional):
178
178
  {
179
- "standard": "R 60",
179
+ "standard": "AB 99",
180
180
  "family_designation": { "value": "…", "source": "…" },
181
181
  "group_label": { "value": "…", "source": "…" },
182
182
  "model_designation": { "value": "…", "source": "…" },
@@ -428,8 +428,8 @@ export async function prepareDraft(env: any, opts: PrepareOpts): Promise<DraftVe
428
428
  "standard_unresolved",
429
429
  untraced
430
430
  ? `I can't anchor the draft: you haven't named the Recommendation in your own words (the ${untraced.value} reading isn't yours). ` +
431
- "Name it plainly — for example OIML R 60 — and I'll prepare the draft."
432
- : "I can't anchor the draft: you haven't named the Recommendation. Name it plainly — for example OIML R 60 — and I'll prepare it.",
431
+ "Name it plainly — for example ACME AB 99 — and I'll prepare the draft."
432
+ : "I can't anchor the draft: you haven't named the publication. Name it plainly — for example ACME AB 99 — and I'll prepare it.",
433
433
  );
434
434
  }
435
435
  const standard = await resolveStandard(env, kept.standard);
@@ -437,7 +437,7 @@ export async function prepareDraft(env: any, opts: PrepareOpts): Promise<DraftVe
437
437
  return refusal(
438
438
  "standard_unresolved",
439
439
  `I couldn't resolve ${kept.standard} as a publication in the corpus, so I can't anchor the draft. ` +
440
- "Name the Recommendation plainly — for example OIML R 60 — and I'll prepare it.",
440
+ "Name the publication plainly — for example ACME AB 99 — and I'll prepare it.",
441
441
  );
442
442
  }
443
443
 
@@ -192,7 +192,7 @@ async function absenceRoute(c: RouteContext): Promise<Response> {
192
192
  const namedStd = namedDocumentIn(String(body?.standard ?? ""));
193
193
  const standard = standardForDocNumber(namedStd?.doc_number ?? String(body?.standard ?? "").trim());
194
194
  const topic = String(body?.topic ?? "").trim().toLowerCase();
195
- if (!standard || !topic) return err(400, "invalid_input", "standard (e.g. \"R 60\") and topic are required");
195
+ if (!standard || !topic) return err(400, "invalid_input", "standard and topic are required");
196
196
  try {
197
197
  const nodes = (await env.DB.prepare("SELECT node_id, kind, name, content FROM model_nodes WHERE standard = ?1").bind(standard).all()).results ?? [];
198
198
  const tokens = topic.split(/\s+/).filter((t: string) => t.length > 2);
@@ -50,9 +50,9 @@ export interface LiveDataConfig {
50
50
  export function liveDataConfig(env: any): LiveDataConfig | null {
51
51
  const platformApi = (env.SMART_PLATFORM_API ?? "").trim().replace(/\/$/, "");
52
52
  const platformClientId = (env.SMART_PLATFORM_CLIENT_ID ?? "").trim();
53
- const issuer = (env.OIDC_ISSUER ?? "https://id.oimlsmart.org").trim().replace(/\/$/, "");
53
+ const issuer = (env.OIDC_ISSUER ?? "").trim().replace(/\/$/, "");
54
54
  const clientId = (env.OIDC_CLIENT_ID ?? "").trim();
55
- if (!platformApi || !platformClientId || !clientId) return null;
55
+ if (!platformApi || !platformClientId || !clientId || !issuer) return null;
56
56
  return { platformApi, platformClientId, issuer, clientId, clientSecret: env.OIDC_CLIENT_SECRET };
57
57
  }
58
58
 
@@ -384,7 +384,8 @@ function publicationUrl(meta: ChunkMeta): string | undefined {
384
384
  // foreign-corpus chunk has no entry in it, and a fabricated url is
385
385
  // worse than none (the door for internal renderings is the internal
386
386
  // origin, wired by the site's doc-base configuration)
387
- if (meta.corpus && !["oiml", "dirty", "clean"].includes(meta.corpus)) return undefined;
387
+ const ownCorpora = P().publisher.catalog_corpora;
388
+ if (meta.corpus && ownCorpora && !ownCorpora.includes(meta.corpus)) return undefined;
388
389
  const tpl = P().publisher.catalog_url_template;
389
390
  if (!tpl || !meta.doctype || !meta.doc_number) return undefined;
390
391
  return tpl.replace("{type}", meta.doctype.toLowerCase()) + meta.doc_number;
@@ -19,6 +19,12 @@ export const PROFILE = {
19
19
  "references": {
20
20
  "label_prefix": ""
21
21
  },
22
+ "catalog_url_template": "https://catalog.fixture.example.org/{type}",
23
+ "catalog_corpora": [
24
+ "pub",
25
+ "dirty",
26
+ "clean"
27
+ ],
22
28
  "features": {
23
29
  "drafts": false,
24
30
  "model_plane": false
@@ -80,11 +86,11 @@ export const PROFILE = {
80
86
  "models": {},
81
87
  "licensed": [
82
88
  {
83
- "key": "std:fixture-60068-2-30",
84
- "package": "fixture-60068-2-30",
85
- "doc_number": "60068-2-30",
86
- "title": "FIXTURE environmental testing — damp heat, cyclic",
87
- "edition": "2005"
89
+ "key": "std:fixture-ab-99",
90
+ "package": "fixture-ab-99",
91
+ "doc_number": "ab-99",
92
+ "title": "FIXTURE climatic conditioning, cyclic humidity",
93
+ "edition": "2019"
88
94
  }
89
95
  ]
90
96
  },
@@ -32,6 +32,10 @@ export const PROFILE = {
32
32
  "glossary"
33
33
  ]
34
34
  },
35
+ "catalog_url_template": "https://catalog.atlas.example/{type}",
36
+ "catalog_corpora": [
37
+ "spec"
38
+ ],
35
39
  "features": {
36
40
  "drafts": false,
37
41
  "model_plane": false
@@ -1,12 +0,0 @@
1
- import {
2
- handleAsk
3
- } from "./chunk-PBXE2LFD.js";
4
- import "./chunk-7UT76RQO.js";
5
- import "./chunk-EFQALN2Z.js";
6
- import "./chunk-DBBGOOMZ.js";
7
- import "./chunk-5MBWE7WD.js";
8
- import "./chunk-ADXV2DPK.js";
9
- import "./chunk-TJRTVJW5.js";
10
- export {
11
- handleAsk
12
- };
@@ -1,12 +0,0 @@
1
- import {
2
- handleSearch
3
- } from "./chunk-YFDWKXEB.js";
4
- import "./chunk-7UT76RQO.js";
5
- import "./chunk-EFQALN2Z.js";
6
- import "./chunk-DBBGOOMZ.js";
7
- import "./chunk-5MBWE7WD.js";
8
- import "./chunk-ADXV2DPK.js";
9
- import "./chunk-TJRTVJW5.js";
10
- export {
11
- handleSearch
12
- };