@konneal/engine 0.1.3 → 0.2.0
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/admin.d.ts +1 -0
- package/dist/ask-NAWBPZPU.js +12 -0
- package/dist/chunk-6HCFW5PM.js +2780 -0
- package/dist/{chunk-EHJEELVB.js → chunk-LNSDBEKS.js} +1 -1
- package/dist/chunk-LNUSF3UK.js +1852 -0
- package/dist/{chunk-35ODH64W.js → chunk-Q327B27J.js} +33 -0
- package/dist/{chunk-OCNLV7Q7.js → chunk-Q6LI4T7M.js} +6 -1
- package/dist/chunk-RFG3QKVY.js +64 -0
- package/dist/{chunk-ROF3Q7UC.js → chunk-SN3ANQ3Y.js} +2 -2
- package/dist/{chunk-CAEHIVG5.js → chunk-WGXATDXY.js} +1 -1
- package/dist/codecs.d.ts +3 -3
- package/dist/completion.d.ts +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/faithfulness.d.ts +1 -0
- package/dist/mcp-proto.d.ts +25 -0
- package/dist/mcp.d.ts +4 -0
- package/dist/openapi-surface.gen.d.ts +9 -0
- package/dist/openapi-types.d.ts +2141 -0
- package/dist/pipeline.d.ts +3 -0
- package/dist/profile.gen.d.ts +1 -0
- package/dist/prompts/system.md +1 -0
- package/dist/prompts/understanding.md +1 -0
- package/dist/quota.d.ts +4 -1
- package/dist/search-7XMYAV34.js +11 -0
- package/dist/tablecontext.d.ts +7 -0
- package/dist/verdict-parse.d.ts +5 -0
- package/dist/worker_mcp/src/index.js +3 -3
- package/dist/worker_public/src/config.js +4 -2
- package/dist/worker_public/src/index.js +1190 -4834
- package/dist/worker_public/src/profile.js +1 -1
- package/dist/worker_public/src/refusal.js +2 -2
- package/dist/worker_public/src/requestScope.js +3 -3
- package/docs/INGEST-ARCHITECTURE.md +1 -0
- package/docs/projects-design.md +7 -0
- package/docs/sota-mechanisms.md +15 -0
- package/docs/spec-api.md +27 -13
- package/docs/spec-pipeline.md +20 -13
- package/package.json +12 -3
- package/profile/prompts.yaml +3 -0
- package/workers/shared/router.ts +23 -16
- package/workers/worker_public/migrations/0014_usage_cache.sql +5 -0
- package/workers/worker_public/openapi.yaml +1169 -0
- package/workers/worker_public/prompts/system.md +1 -0
- package/workers/worker_public/prompts/understanding.md +1 -0
- package/workers/worker_public/schema.sql +3 -1
- package/workers/worker_public/src/admin.ts +51 -7
- package/workers/worker_public/src/ask.ts +97 -24
- package/workers/worker_public/src/codecs.ts +35 -10
- package/workers/worker_public/src/completion.ts +24 -1
- package/workers/worker_public/src/config.ts +10 -0
- package/workers/worker_public/src/conversations.ts +1 -1
- package/workers/worker_public/src/faithfulness.ts +10 -17
- package/workers/worker_public/src/grader.ts +2 -2
- package/workers/worker_public/src/index.ts +106 -58
- package/workers/worker_public/src/lib/router.ts +1 -1
- package/workers/worker_public/src/mcp-proto.ts +71 -0
- package/workers/worker_public/src/mcp.ts +47 -0
- package/workers/worker_public/src/openapi-surface.gen.ts +318 -0
- package/workers/worker_public/src/pipeline.ts +9 -3
- package/workers/worker_public/src/profile.gen.ts +1 -0
- package/workers/worker_public/src/projects.ts +4 -2
- package/workers/worker_public/src/quota.ts +4 -2
- package/workers/worker_public/src/research.ts +55 -3
- package/workers/worker_public/src/stages/citationProbe.ts +145 -0
- package/workers/worker_public/src/stages/index.ts +2 -0
- package/workers/worker_public/src/stages/types.ts +4 -0
- package/workers/worker_public/src/tablecontext.ts +13 -2
- package/workers/worker_public/src/verdict-parse.ts +60 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
requestSalt,
|
|
3
3
|
resolveRequestScope
|
|
4
|
-
} from "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
6
|
-
import "../../chunk-
|
|
4
|
+
} from "../../chunk-LNSDBEKS.js";
|
|
5
|
+
import "../../chunk-Q6LI4T7M.js";
|
|
6
|
+
import "../../chunk-Q327B27J.js";
|
|
7
7
|
export {
|
|
8
8
|
requestSalt,
|
|
9
9
|
resolveRequestScope
|
|
@@ -54,6 +54,7 @@ hand.
|
|
|
54
54
|
│ (derived status, active flags — the SSOT) │
|
|
55
55
|
│ structure typed nodes → ChunkRecordV2 (per block type) │
|
|
56
56
|
│ derivation registry + graph projection (relaton edges + │
|
|
57
|
+
│ citation edges from the indexed bibliographies │
|
|
57
58
|
│ glossarist defines edges) │
|
|
58
59
|
│ enrichment contextual contexts (quality-first lane, KV- │
|
|
59
60
|
│ cached, content-hash invalidated) │
|
package/docs/projects-design.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
> The question: should chats group into **projects** that share a
|
|
4
4
|
> per-project contextual memory? Yes — and the parts already exist.
|
|
5
|
+
>
|
|
6
|
+
> **Shipped (2026-09-15)**: membership is drag-and-drop — a chat drags
|
|
7
|
+
> onto a project row to file, onto the conversations list to unfile,
|
|
8
|
+
> with drop-target highlights and a live hint; a per-row picker covers
|
|
9
|
+
> touch/keyboard; chats carry a project badge. The conversations list
|
|
10
|
+
> carries `project_id` from the server (membership stays server-truth)
|
|
11
|
+
> and the move rides the server conversation id.
|
|
5
12
|
> This is the design; nothing is implemented yet.
|
|
6
13
|
|
|
7
14
|
## 1. Prior art — what the best got right and wrong
|
package/docs/sota-mechanisms.md
CHANGED
|
@@ -97,6 +97,21 @@ unit selection, so the pin lands the object the question is about
|
|
|
97
97
|
ranking answers from stale editions, guesses at doc scope, and never
|
|
98
98
|
surfaces a typed object.
|
|
99
99
|
|
|
100
|
+
### 7b. The citation graph (structured questions get graph answers)
|
|
101
|
+
|
|
102
|
+
Bibliographic citations become graph edges: what each edition's
|
|
103
|
+
bibliography cites is extracted at index time (the identifier grammar
|
|
104
|
+
lives in the publisher codec — ISO/IEC floor generic, publisher series
|
|
105
|
+
on top; ~300 cites edges across the OIML corpus), stored beside the
|
|
106
|
+
publication registry, and keyed by the same normalized pubids
|
|
107
|
+
everywhere ("OIML R 60", never a bare number). When a question asks
|
|
108
|
+
what a publication cites or references, the answer receives the
|
|
109
|
+
cited-standards list per edition as authoritative structured data
|
|
110
|
+
(the `notes` channel), while the bibliography section itself is
|
|
111
|
+
retrieved as the passage that grounds the answer verbatim. Structured
|
|
112
|
+
questions get graph answers; semantic questions get vector answers;
|
|
113
|
+
the hybrid class gets both, fused by the same rerank.
|
|
114
|
+
|
|
100
115
|
### 8. The answer contract (claims are checkable)
|
|
101
116
|
Inline citations on every claim; normative values quoted verbatim from
|
|
102
117
|
the cited passage; tables/formulas/figures rendered as typed objects
|
package/docs/spec-api.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# API surface spec
|
|
2
2
|
|
|
3
|
-
The worker's complete HTTP inventory
|
|
4
|
-
|
|
5
|
-
`
|
|
6
|
-
|
|
3
|
+
The worker's complete HTTP inventory. The API routes are generated from
|
|
4
|
+
the OpenAPI document `workers/worker_public/openapi.yaml`
|
|
5
|
+
(`scripts/gen-openapi-routes.mjs` → `src/openapi-surface.gen.ts`), and
|
|
6
|
+
`index.ts`'s `OPENAPI_HANDLERS` binds every operation to its handler;
|
|
7
|
+
`tests/openapi-surface.test.ts` pins the bijection, and the non-API
|
|
8
|
+
routes (pages, unit assets, rendered documents) are declared in
|
|
9
|
+
`INFRA_ROUTES`. Adding a route = the yaml entry + one handler binding +
|
|
10
|
+
regenerate; this document changes with it. Request/response details
|
|
7
11
|
beyond the inventory: [API.md](API.md).
|
|
8
12
|
|
|
9
13
|
## Conventions
|
|
@@ -27,13 +31,19 @@ beyond the inventory: [API.md](API.md).
|
|
|
27
31
|
| `/auth/callback` | GET | none | `handleCallback` | OIDC callback; mints the session cookie; bubble mode postMessages the token to the validated origin. |
|
|
28
32
|
| `/auth/me` | GET | session | `handleMe` | `{ authenticated, name, email, roles, tier }`. |
|
|
29
33
|
| `/auth/logout` | GET, POST | none | `handleLogout` | Clears the session. |
|
|
30
|
-
| `/api/conversations` |
|
|
31
|
-
| `/api/conversations/:id` |
|
|
34
|
+
| `/api/conversations` | GET, POST | session | `conversationsRoute` → `handleConversations` | List / create the signed-in member's conversations. |
|
|
35
|
+
| `/api/conversations/:id` | GET, PATCH, DELETE | session (owner) | `conversationsRoute` | One conversation: read / rename / delete. |
|
|
32
36
|
| `/api/conversations/:id/messages` | POST | session (owner) | `appendMessageRoute` → `handleAppendMessage` | Append a message turn. |
|
|
33
37
|
| `/api/conversations/:id/share` | POST | session (owner) | `shareRoute` → `handleShareConversation` | Publish a conversation to an unlisted share slug. (Was shadowed by the compound conversations branch pre-route-table — the pattern inventory made the collision visible and fixed it.) |
|
|
34
38
|
| `/api/shared/:slug` | GET | none | `getSharedRoute` → `handleGetShared` | Read a shared conversation. |
|
|
39
|
+
| `/api/projects` | GET, POST | session | `projectsRoute` → `handleProjects` | Contexts (#187): list / create / rename by id / file a conversation by `conversation_id` + `project_id` (null unfiles — membership-as-move). |
|
|
40
|
+
| `/api/projects/:id` | DELETE | session (owner) | `projectsRoute` | Delete a context: files detached, conversations returned to the general list. |
|
|
41
|
+
| `/api/projects/:id/files` | GET, POST | session (owner) | `projectFilesRoute` → `handleProjectFiles` | List / attach (or replace by id) a context's files; POST names the context by `project_id` in the body. |
|
|
42
|
+
| `/api/project-files/:id` | DELETE | session (owner) | `projectFilesRoute` | Detach one file. |
|
|
35
43
|
| `/api/datasets` | GET | none (session enriches) | `datasetsRoute` | The corpus catalog + starter questions (the UI's empty state — content from the API, never hardcoded in the client). Session-gated datasets carry `requires` (the estate permission, e.g. `the ai-preview permission (id.oimlsmart.org)`) and `enabled` reflects the permission, not just login. |
|
|
36
|
-
| `/api/memories` |
|
|
44
|
+
| `/api/memories` | GET, POST | session | `memoriesRoute` → `handleMemories` | Personalized memory files (#171): GET list / POST create-or-replace by id (≤10 files × 8k chars) — every query owner-filtered by the session sub. Selected ids ride `/api/ask` as `memories: [id…]` (≤4 per ask, injected as one bounded trusted-user-facts note; the selection salts the answer cache). |
|
|
45
|
+
| `/api/memories/:id` | DELETE | session (owner) | `memoriesRoute` | Delete one memory file. |
|
|
46
|
+
| `/mcp` | POST | tier | `mcpRoute` → `handleMcp` | The Model Context Protocol server (JSON-RPC 2025-06-18, streamable HTTP); tools: `ask`, `retrieve`. |
|
|
37
47
|
| `/health` | GET | none | `healthRoute` | Liveness + deployed `index_version` (the deploy-drift guard reads this). |
|
|
38
48
|
| `/api/ask`, `/v1/ask` | POST | tier | `askRoute` → `handleAsk` | The answer contract: streamed or JSON answer, citations, typed blocks, context echo. Quotas per tier. Optional `datasets: [id…]` narrows the corpora searched (server-intersected with session permissions; an explicitly-empty list is a 400); optional `memories: [id…]` selects the member's memory files to inject. Both selections salt the answer cache — a scoped or memory-flavored answer never serves a plain ask. |
|
|
39
49
|
| `/api/absence`, `/v1/absence` | POST | tier | `absenceRoute` | Provable absence: exhaustive enumeration over a standard's model plane; `{ verdict: absent \| present, enumerated, matches }`. |
|
|
@@ -50,10 +60,11 @@ beyond the inventory: [API.md](API.md).
|
|
|
50
60
|
| `/admin/judge`, `/v1/admin/judge` | POST | ADMIN_TOKEN | `handleJudge` | LLM-as-judge scoring (promotion gates). |
|
|
51
61
|
| `/v1/admin/keys` | POST | ADMIN_TOKEN | `handleCreateKey` | Issue an API key (`oiml_<hex>`, shown once). |
|
|
52
62
|
| `/v1/admin/keys` | GET | ADMIN_TOKEN | `handleListKeys` | List keys (no secrets). |
|
|
63
|
+
| `/v1/admin/keys/:id` | DELETE | ADMIN_TOKEN | `handleRevokeKey` | Revoke a key. |
|
|
53
64
|
| `/v1/admin/stats` | GET | ADMIN_TOKEN | `adminStatsRoute` | 7-day telemetry: queries/day/tier, spend by model, feedback, error rate; prunes >90d rows. |
|
|
54
65
|
| `/docs/:slug.html`, `/docs/:slug.anchors.json` | GET | public | `docsRoute` | Rendered publication documents (metanorma-mirror layer 1) served from R2 under `docs/`, immutable cache; the anchors map (clause number → heading anchor id) powers citation deep links. Public OIML content only. |
|
|
55
66
|
| `/admin/enrich` | POST | ADMIN_TOKEN | `handleEnrich` | modes: default (context+embed+upsert in place), `context` (generate only, KV-cached), `ab` (generate at an explicit effort with NO side effects — the experiment lane; accepts an admin-gated prompt override for judged comparisons). |
|
|
56
|
-
| anything else | any | — | — | `404 not_found`. |
|
|
67
|
+
| anything else | any | — | — | `404 not_found`; a known path under an undeclared method answers `405 method_not_allowed`. |
|
|
57
68
|
|
|
58
69
|
## Request semantics worth naming
|
|
59
70
|
|
|
@@ -63,11 +74,14 @@ beyond the inventory: [API.md](API.md).
|
|
|
63
74
|
## Dispatch semantics
|
|
64
75
|
|
|
65
76
|
`fetch` = OPTIONS preflight (204) → `matchRoute(ROUTES, method, path)` →
|
|
66
|
-
handler with `{ env, req, ctx, url, path, params }` →
|
|
67
|
-
|
|
68
|
-
(
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
handler with `{ env, req, ctx, url, path, params }` → 405 when the path
|
|
78
|
+
is served under another method → 404. `ROUTES` is `INFRA_ROUTES` +
|
|
79
|
+
`OPENAPI_SURFACE` (generated from the yaml) mapped through
|
|
80
|
+
`OPENAPI_HANDLERS`. Patterns are segment-exact with `:param` capture and
|
|
81
|
+
a trailing `*` wildcard (`/assets/*`); entry order is irrelevant because
|
|
82
|
+
no pattern overlaps another. The `/api` and `/v1` twins deliberately
|
|
83
|
+
share one handler so the tier split can never diverge between the two
|
|
84
|
+
publications.
|
|
71
85
|
|
|
72
86
|
## Isolation invariants (binding lint, `npm run lint:wrangler`)
|
|
73
87
|
|
package/docs/spec-pipeline.md
CHANGED
|
@@ -51,18 +51,25 @@ candidate lanes → pool open → pool-level merges → refinement → window as
|
|
|
51
51
|
| 13 | `family-boost` | boost only under `filter.doc_number`; sort always | blocking | mutates `hits[].score`, sorts | Family chunks decisively boosted for doc-scoped queries; the sort establishes the rerank-failure fallback order. |
|
|
52
52
|
| 14 | `rerank` | `hits.length > 1` | additive | `hits[].rerank_score`, sorts, family pin | Cross-encoder scores; vector order is the designed fallback. Post-rerank family pin for doc-scoped queries. |
|
|
53
53
|
| 15 | `lexical-rrf` | `hits.length > 1 && lexicalHits.length` | blocking | REPLACES `hits` order | RRF fusion with the full-corpus lexical ranking. Runs even when rerank failed (additive semantics preserve this). |
|
|
54
|
-
| 16 | `
|
|
55
|
-
| 17 | `
|
|
56
|
-
| 18 | `
|
|
57
|
-
| 19 | `
|
|
58
|
-
| 20 | `
|
|
59
|
-
| 21 | `
|
|
60
|
-
| 22 | `
|
|
61
|
-
| 23 | `
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
54
|
+
| 16 | `citation-probe` | citation-question shape + a document named in the TEXT | additive | appends `hits`, appends `notes` | GraphRAG: bibliography-shaped questions get (a) the family's bibliography sections as passages — FTS over the chunk store, deterministic, text fetched from D1 (getByIds returns no text), pushed to `hits` at score 10 — and (b) the graph's `cites` edges for the family as an authoritative per-edition note on the `notes` channel, identifiers codec-normalized. The graph answers the STRUCTURE; the passages ground it verbatim. |
|
|
55
|
+
| 17 | `corpus-scope` | `opts.datasetScope` | blocking | filters `hits` | Dataset scope (the sidebar toggles): drops hits whose corpus the request excludes. LAST of the pool-assembly stages — the lexical union above refills the pool after rerank, so filtering earlier let excluded corpora back in. Corpora the toggle model doesn't name pass untouched. |
|
|
56
|
+
| 18 | `edition-cover` | `!filters.edition && hits.length > 1` | additive | appends `hits` | Registry-driven cover: when a pool holds only stale editions of a document whose ACTIVE edition the documents registry knows, fetch the current edition's chunks and add at `editionCoverDiscount` — steering needs the successor present to demote. |
|
|
57
|
+
| 19 | `std-ref-nudge` | query names ISO/IEC/ASTM/EN | blocking | `hits[].rerank_score`, sorts | Standard-reference nudge: chunks CARRYING such a citation get `stdRefNudgeSpread × spread` — the citing clause is the answer to "which standard does X invoke", and generic family prose otherwise fills the window (l5a measured). |
|
|
58
|
+
| 20 | `term-nudge` | `u.term` | blocking | `hits[].rerank_score`, sorts | Clause whose head IS the asked term gets `termNudgeSpread × spread` (decisive). |
|
|
59
|
+
| 21 | `concept-steer` | `glossary.length && hits.length > 1` | blocking | `hits[].rerank_score`, sorts | Vocabulary-link families boosted (`conceptSteerSpread`). |
|
|
60
|
+
| 22 | `edition-steer` | `!filters.edition && hits.length > 1` | blocking | `hits[].rerank_score`, sorts | Cross-pub recency boost + family-relative superseded-edition demotion (spread-scaled). |
|
|
61
|
+
| 23 | `structural-propagate` | — | blocking | REPLACES `hits` | FABLE TreeExpansion: score blends along the clause tree. |
|
|
62
|
+
| 24 | `diversity` | — | blocking | `finalHits` (from `hits`) | Per-publication caps (1 overview / 2–3 clauses; global overview cap 2/6); window cut to `rerankKeep`. FIRST writer of `finalHits`. |
|
|
63
|
+
| 25 | `typed-pin` | pin families resolvable | blocking (inner parent-fetch additive) | `finalHits` | Answer-contract v2: one typed unit guaranteed a slot (+ small-to-big parent fetch at `smallToBigDiscount`). |
|
|
64
|
+
| 26 | `section-descent` | a ranked depth-1 summary has children | additive | `finalHits` | Summary node → top child clauses at `sectionDescentDiscount`; the summary retires when children answer. |
|
|
65
|
+
| 27 | `dedup` | — | blocking | `finalHits` | FABLE ancestor-descendant same-chain collapse (≥0.5 text overlap). |
|
|
66
|
+
| 28 | `window-floor` | — | blocking | filters `finalHits` | Evidence-budget cut at `windowFloorFraction` of top; typed/family/unscored exempt; never fewer than two. |
|
|
67
|
+
|
|
68
|
+
The `notes` channel (the GraphRAG seam): stages may append structured
|
|
69
|
+
facts to `PipelineContext.notes`; `retrieve()` returns them and the ask
|
|
70
|
+
path merges them into the answer prompt's retrieval note — the same
|
|
71
|
+
channel the vocabulary link rides. `citation-probe` is the first
|
|
72
|
+
writer; any future graph-derived fact uses the same seam.
|
|
66
73
|
|
|
67
74
|
## Ordering dependencies (why the order is what it is)
|
|
68
75
|
|
|
@@ -77,7 +84,7 @@ candidate lanes → pool open → pool-level merges → refinement → window as
|
|
|
77
84
|
- **`seal` before `overview-demote`/`rerank`**: the declared context is
|
|
78
85
|
a hard scope, not a preference — steering and reranking happen WITHIN
|
|
79
86
|
it.
|
|
80
|
-
- **`rerank` before every steering stage** (
|
|
87
|
+
- **`rerank` before every steering stage** (19–22): steering is
|
|
81
88
|
spread-scaled over rerank scores; steering before rerank would be
|
|
82
89
|
erased by the re-sort.
|
|
83
90
|
- **`structural-propagate` after steering, before `diversity`**:
|
package/package.json
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"test:ui": "node tests/ui.mjs",
|
|
15
15
|
"test:bridge": "node --test tests/bridge.test.mjs",
|
|
16
16
|
"test:units": "node --test --experimental-strip-types tests/*.test.ts",
|
|
17
|
-
"
|
|
17
|
+
"gen:openapi-types": "node scripts/gen-openapi-types.mjs",
|
|
18
|
+
"build": "rm -rf dist dist-types && esbuild workers/worker_mcp/src/index.ts workers/worker_public/src/index.ts workers/worker_public/src/config.ts workers/worker_public/src/refusal.ts workers/worker_public/src/profile.ts workers/worker_public/src/requestScope.ts --bundle --format=esm --splitting --platform=neutral --outdir=dist --loader:.md=text && tsc -p tsconfig.build.json && cp dist-types/worker_public/src/*.d.ts dist/ && mkdir -p dist/worker_mcp/src && cp dist-types/worker_mcp/src/*.d.ts dist/worker_mcp/src/ && mkdir -p dist/prompts && cp workers/worker_public/prompts/*.md dist/prompts/ && node scripts/gen-openapi-types.mjs",
|
|
18
19
|
"prepare": "npm run build",
|
|
19
20
|
"lint:ports": "node scripts/lint-ports.mjs",
|
|
20
21
|
"lint:publisher": "node scripts/lint-publisher-leak.mjs",
|
|
@@ -24,14 +25,18 @@
|
|
|
24
25
|
"@cloudflare/workers-types": "^5.20260911.1",
|
|
25
26
|
"esbuild": "^0.28.2",
|
|
26
27
|
"jsdom": "^29.1.1",
|
|
28
|
+
"openapi-typescript": "^7.13.0",
|
|
27
29
|
"playwright": "^1.62.1",
|
|
28
30
|
"yaml": "^2.9.1"
|
|
29
31
|
},
|
|
30
32
|
"dependencies": {
|
|
31
33
|
"@astrojs/markdown-satteri": "^0.4.1",
|
|
32
|
-
"@astrojs/mdx": "^4.3.14"
|
|
34
|
+
"@astrojs/mdx": "^4.3.14",
|
|
35
|
+
"@konneal/client": "github:konneal/client#22ce599",
|
|
36
|
+
"@konneal/engine": "github:konneal/engine#2d6c39b",
|
|
37
|
+
"@oimlsmart/oiml-pubid": "^1.2.1"
|
|
33
38
|
},
|
|
34
|
-
"version": "0.
|
|
39
|
+
"version": "0.2.0",
|
|
35
40
|
"description": "The Konneal engine: the publisher-agnostic build pipeline and API plane for standards intelligence (retrieval, answer contract, verdicts, evaluation).",
|
|
36
41
|
"license": "BSD-3-Clause",
|
|
37
42
|
"type": "module",
|
|
@@ -57,6 +62,10 @@
|
|
|
57
62
|
"types": "./dist/requestScope.d.ts",
|
|
58
63
|
"default": "./dist/worker_public/src/requestScope.js"
|
|
59
64
|
},
|
|
65
|
+
"./openapi": "./workers/worker_public/openapi.yaml",
|
|
66
|
+
"./openapi-types": {
|
|
67
|
+
"types": "./dist/openapi-types.d.ts"
|
|
68
|
+
},
|
|
60
69
|
"./mcp": "./dist/worker_mcp/src/index.js"
|
|
61
70
|
},
|
|
62
71
|
"files": [
|
package/profile/prompts.yaml
CHANGED
package/workers/shared/router.ts
CHANGED
|
@@ -21,25 +21,32 @@ export interface Route {
|
|
|
21
21
|
handler: RouteHandler;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
/** The path half of the match, method-blind: segment-exact with :param
|
|
25
|
+
* capture and a trailing * wildcard. Returns the captured params when
|
|
26
|
+
* the pattern fits, null otherwise. The 405 probe uses it to tell a
|
|
27
|
+
* known path under a wrong method from an unknown path. */
|
|
28
|
+
export function routeMatchesPath(pattern: string, path: string): Record<string, string> | null {
|
|
25
29
|
const segments = path.split("/").filter(Boolean);
|
|
30
|
+
const patternSegs = pattern.split("/").filter(Boolean);
|
|
31
|
+
if (patternSegs.length !== segments.length && !patternSegs[patternSegs.length - 1]?.startsWith("*")) return null;
|
|
32
|
+
const params: Record<string, string> = {};
|
|
33
|
+
for (let i = 0; i < patternSegs.length; i++) {
|
|
34
|
+
const ps = patternSegs[i];
|
|
35
|
+
if (ps.startsWith("*")) return params; // wildcard matches rest
|
|
36
|
+
if (ps.startsWith(":")) {
|
|
37
|
+
params[ps.slice(1)] = segments[i];
|
|
38
|
+
} else if (ps !== segments[i]) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return params;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function matchRoute(routes: Route[], method: string, path: string): { route: Route; params: Record<string, string> } | null {
|
|
26
46
|
for (const route of routes) {
|
|
27
47
|
if (route.method !== method && route.method !== "*") continue;
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
const params: Record<string, string> = {};
|
|
31
|
-
let matched = true;
|
|
32
|
-
for (let i = 0; i < patternSegs.length; i++) {
|
|
33
|
-
const ps = patternSegs[i];
|
|
34
|
-
if (ps.startsWith("*")) break; // wildcard matches rest
|
|
35
|
-
if (ps.startsWith(":")) {
|
|
36
|
-
params[ps.slice(1)] = segments[i];
|
|
37
|
-
} else if (ps !== segments[i]) {
|
|
38
|
-
matched = false;
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (matched) return { route, params };
|
|
48
|
+
const params = routeMatchesPath(route.pattern, path);
|
|
49
|
+
if (params) return { route, params };
|
|
43
50
|
}
|
|
44
51
|
return null;
|
|
45
52
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- Usage telemetry (2026-09-17): the cache mix on the queries ledger.
|
|
2
|
+
-- 'exact' | 'semantic' for served-from-cache answers, NULL for a live
|
|
3
|
+
-- generation — the answer-cache story (hit rates per tier) becomes
|
|
4
|
+
-- measurable instead of inferred.
|
|
5
|
+
ALTER TABLE queries ADD COLUMN cache TEXT;
|