@konneal/engine 0.1.4 → 0.2.1

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 (58) hide show
  1. package/dist/admin.d.ts +1 -0
  2. package/dist/ask-47RNGK2R.js +12 -0
  3. package/dist/chunk-3OXSQH7Y.js +1852 -0
  4. package/dist/chunk-6GOSMLRH.js +2781 -0
  5. package/dist/{chunk-EHJEELVB.js → chunk-LNSDBEKS.js} +1 -1
  6. package/dist/{chunk-35ODH64W.js → chunk-Q327B27J.js} +33 -0
  7. package/dist/{chunk-OCNLV7Q7.js → chunk-Q6LI4T7M.js} +6 -1
  8. package/dist/{chunk-ROF3Q7UC.js → chunk-SN3ANQ3Y.js} +2 -2
  9. package/dist/chunk-VJZLVU3S.js +64 -0
  10. package/dist/{chunk-CAEHIVG5.js → chunk-WGXATDXY.js} +1 -1
  11. package/dist/codecs.d.ts +3 -3
  12. package/dist/completion.d.ts +1 -1
  13. package/dist/config.d.ts +9 -0
  14. package/dist/faithfulness.d.ts +1 -0
  15. package/dist/mcp-proto.d.ts +25 -0
  16. package/dist/mcp.d.ts +4 -0
  17. package/dist/openapi-surface.gen.d.ts +9 -0
  18. package/dist/openapi-types.d.ts +2141 -0
  19. package/dist/profile.gen.d.ts +1 -0
  20. package/dist/prompts/system.md +1 -0
  21. package/dist/quota.d.ts +4 -1
  22. package/dist/search-OMPBMZT4.js +11 -0
  23. package/dist/tablecontext.d.ts +7 -0
  24. package/dist/verdict-parse.d.ts +5 -0
  25. package/dist/worker_mcp/src/index.js +3 -3
  26. package/dist/worker_public/src/config.js +4 -2
  27. package/dist/worker_public/src/index.js +1193 -4932
  28. package/dist/worker_public/src/profile.js +1 -1
  29. package/dist/worker_public/src/refusal.js +2 -2
  30. package/dist/worker_public/src/requestScope.js +3 -3
  31. package/docs/spec-api.md +27 -13
  32. package/package.json +12 -3
  33. package/profile/prompts.yaml +3 -0
  34. package/workers/shared/router.ts +23 -16
  35. package/workers/worker_public/migrations/0014_usage_cache.sql +5 -0
  36. package/workers/worker_public/openapi.yaml +1169 -0
  37. package/workers/worker_public/prompts/system.md +1 -0
  38. package/workers/worker_public/schema.sql +3 -1
  39. package/workers/worker_public/src/admin.ts +51 -7
  40. package/workers/worker_public/src/ai.ts +10 -2
  41. package/workers/worker_public/src/ask.ts +94 -22
  42. package/workers/worker_public/src/codecs.ts +35 -10
  43. package/workers/worker_public/src/completion.ts +24 -1
  44. package/workers/worker_public/src/config.ts +10 -0
  45. package/workers/worker_public/src/faithfulness.ts +10 -17
  46. package/workers/worker_public/src/grader.ts +2 -2
  47. package/workers/worker_public/src/index.ts +106 -58
  48. package/workers/worker_public/src/lib/router.ts +1 -1
  49. package/workers/worker_public/src/mcp-proto.ts +71 -0
  50. package/workers/worker_public/src/mcp.ts +47 -0
  51. package/workers/worker_public/src/openapi-surface.gen.ts +318 -0
  52. package/workers/worker_public/src/pipeline.ts +4 -2
  53. package/workers/worker_public/src/profile.gen.ts +1 -0
  54. package/workers/worker_public/src/projects.ts +4 -2
  55. package/workers/worker_public/src/quota.ts +4 -2
  56. package/workers/worker_public/src/research.ts +55 -3
  57. package/workers/worker_public/src/tablecontext.ts +13 -2
  58. package/workers/worker_public/src/verdict-parse.ts +60 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  P,
3
3
  setProfile
4
- } from "../../chunk-35ODH64W.js";
4
+ } from "../../chunk-Q327B27J.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-CAEHIVG5.js";
5
- import "../../chunk-35ODH64W.js";
4
+ } from "../../chunk-WGXATDXY.js";
5
+ import "../../chunk-Q327B27J.js";
6
6
  export {
7
7
  canonicalRefusal,
8
8
  refusalAnswer
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  requestSalt,
3
3
  resolveRequestScope
4
- } from "../../chunk-EHJEELVB.js";
5
- import "../../chunk-OCNLV7Q7.js";
6
- import "../../chunk-35ODH64W.js";
4
+ } from "../../chunk-LNSDBEKS.js";
5
+ import "../../chunk-Q6LI4T7M.js";
6
+ import "../../chunk-Q327B27J.js";
7
7
  export {
8
8
  requestSalt,
9
9
  resolveRequestScope
package/docs/spec-api.md CHANGED
@@ -1,9 +1,13 @@
1
1
  # API surface spec
2
2
 
3
- The worker's complete HTTP inventory, mirrored 1:1 by the `ROUTES`
4
- registry in `workers/worker_public/src/index.ts` (dispatched by
5
- `lib/router.ts`'s `matchRoute`). Adding a route = one registry entry +
6
- its handler; this document changes with it. Request/response details
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` | * | session | `conversationsRoute` → `handleConversations` | List (GET) / create (POST) / delete (DELETE) the signed-in user's conversations. |
31
- | `/api/conversations/:id` | * | session (owner) | `conversationsRoute` | One conversation (GET) / delete (DELETE). |
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` | * | session | `memoriesRoute` → `handleMemories` | Personalized memory files (#171): GET list / POST create-update (≤10 files × 8k chars) / DELETE by id — 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). |
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 }` → 404. Patterns are
67
- segment-exact with `:param` capture and a trailing `*` wildcard
68
- (`/assets/*`); entry order is irrelevant because no pattern overlaps
69
- another. The `/api` and `/v1` twins deliberately share one handler so
70
- the tier split can never diverge between the two publications.
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/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
- "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/",
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.1.4",
39
+ "version": "0.2.1",
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": [
@@ -1,4 +1,7 @@
1
1
  vars:
2
+ publisher_identity: >-
3
+ the Fixture Organization — a worldwide organization that publishes
4
+ the fixture corpus
2
5
  assistant_identity: >-
3
6
  the fixture assistant — a public service answering questions about
4
7
  the fixture publisher's documents
@@ -21,25 +21,32 @@ export interface Route {
21
21
  handler: RouteHandler;
22
22
  }
23
23
 
24
- export function matchRoute(routes: Route[], method: string, path: string): { route: Route; params: Record<string, string> } | null {
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 patternSegs = route.pattern.split("/").filter(Boolean);
29
- if (patternSegs.length !== segments.length && !patternSegs[patternSegs.length - 1]?.startsWith("*")) continue;
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;