@konneal/engine 0.2.2 → 0.2.4
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-YCDXMIZ4.js +12 -0
- package/dist/{chunk-LNSDBEKS.js → chunk-5MBWE7WD.js} +29 -4
- package/dist/{chunk-Q6LI4T7M.js → chunk-ADXV2DPK.js} +1 -1
- package/dist/{chunk-WGXATDXY.js → chunk-DBBGOOMZ.js} +1 -1
- package/dist/{chunk-SN3ANQ3Y.js → chunk-EFQALN2Z.js} +2 -2
- package/dist/{chunk-PVHSBXZD.js → chunk-OGFH3RDM.js} +56 -21
- package/dist/{chunk-6GOSMLRH.js → chunk-RLT4W2VX.js} +28 -5
- package/dist/{chunk-VJZLVU3S.js → chunk-THSHLUOS.js} +9 -4
- package/dist/{chunk-Q327B27J.js → chunk-TJRTVJW5.js} +13 -2
- package/dist/modelplane.d.ts +44 -1
- package/dist/profile.gen.d.ts +9 -0
- package/dist/prompts/system.md +1 -0
- package/dist/requestScope.d.ts +25 -4
- package/dist/search-7TO2RWQE.js +12 -0
- package/dist/selfquery.d.ts +6 -0
- package/dist/worker_mcp/src/index.js +3 -3
- package/dist/worker_public/src/config.js +2 -2
- package/dist/worker_public/src/index.js +13 -10
- 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 +11 -5
- package/docs/spec-pipeline.md +1 -0
- package/package.json +4 -6
- package/profile/prompts.yaml +14 -0
- package/profile/sources.yaml +9 -0
- package/workers/shared/chunk.ts +7 -0
- package/workers/worker_internal/src/index.ts +3 -3
- package/workers/worker_public/prompts/system.md +1 -0
- package/workers/worker_public/src/ask.ts +33 -10
- package/workers/worker_public/src/index.ts +2 -1
- package/workers/worker_public/src/modelplane.ts +94 -4
- package/workers/worker_public/src/pipeline.ts +13 -5
- package/workers/worker_public/src/profile.gen.ts +13 -2
- package/workers/worker_public/src/requestScope.ts +49 -7
- package/workers/worker_public/src/search.ts +7 -1
- package/workers/worker_public/src/selfquery.ts +26 -0
- package/workers/worker_public/src/stages/citationProbe.ts +6 -1
- package/workers/worker_public/src/stages/index.ts +2 -0
- package/workers/worker_public/src/stages/licenseScope.ts +23 -0
- package/workers/worker_public/src/stages/types.ts +11 -0
- package/dist/ask-VFSAF5WG.js +0 -12
- package/dist/search-OMPBMZT4.js +0 -11
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// The per-request scope model (MECE: ask.ts orchestrates, this module
|
|
2
2
|
// owns the derivation): which DATASETS the request searches (the sidebar
|
|
3
|
-
// toggles, server-intersected with session permissions)
|
|
4
|
-
//
|
|
3
|
+
// toggles, server-intersected with session permissions), which memory
|
|
4
|
+
// files ride it, and which LICENSED standards the caller's organization
|
|
5
|
+
// is entitled to — plus the answer-cache SALT all three selections
|
|
5
6
|
// produce. Pure: D1 access stays with the caller (memoryNote); this
|
|
6
7
|
// module only derives.
|
|
7
8
|
import { DATASETS, datasetAllowed } from "./config.ts";
|
|
9
|
+
import { P } from "./profile.ts";
|
|
8
10
|
|
|
9
11
|
export interface RequestScope {
|
|
10
12
|
/** dataset ids the request may search */
|
|
@@ -18,6 +20,37 @@ export interface RequestScope {
|
|
|
18
20
|
isoOn: boolean;
|
|
19
21
|
/** raw (validated) memory ids from the body — empty for anon */
|
|
20
22
|
memoryIds: string[];
|
|
23
|
+
/** raw (validated) license entitlement keys from the body — empty for
|
|
24
|
+
* anon and for requests that carry none (TODO.external-refs/08) */
|
|
25
|
+
standardKeys: Set<string>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The deployment's declared licensed standards (profile sources.yaml
|
|
29
|
+
* `licensed:` — key/package/doc_number rows). Empty = the deployment
|
|
30
|
+
* serves public content only and the entitlement scope is inert. */
|
|
31
|
+
export function licenseDeclared(): boolean {
|
|
32
|
+
return (P().sources?.licensed?.length ?? 0) > 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The request's entitlement set, VALIDATED against the declared
|
|
36
|
+
* whitelist: unknown keys drop (a forged key can never widen scope past
|
|
37
|
+
* the standards the deployment actually keys). Absent field = empty set
|
|
38
|
+
* — the fail-closed default for a deployment that declares licensed
|
|
39
|
+
* content. */
|
|
40
|
+
export function standardKeysFrom(body: any): Set<string> {
|
|
41
|
+
const declared = new Set<string>((P().sources?.licensed ?? []).map((l: any) => String(l.key)));
|
|
42
|
+
const raw = Array.isArray(body?.licensed_standards) ? body.licensed_standards : [];
|
|
43
|
+
return new Set(
|
|
44
|
+
raw.filter((x: unknown): x is string => typeof x === "string" && declared.has(x)),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The RetrieveOptions value for the hard scope: null when the
|
|
49
|
+
* deployment declares no licensed content (inert — zero behavior
|
|
50
|
+
* change); otherwise the caller's validated set, EMPTY INCLUDED (the
|
|
51
|
+
* unentitled caller: licensed chunks hidden, citation metadata stays). */
|
|
52
|
+
export function entitlementScope(keys: Set<string>): Set<string> | null {
|
|
53
|
+
return licenseDeclared() ? keys : null;
|
|
21
54
|
}
|
|
22
55
|
|
|
23
56
|
/** Validate + intersect. Returns { error } when the request explicitly
|
|
@@ -54,18 +87,27 @@ export function resolveRequestScope(body: any, member: unknown): RequestScope |
|
|
|
54
87
|
// federation flag: any session-gated (federated) dataset in scope
|
|
55
88
|
isoOn: scopeIds.some((id) => DATASETS().find((x) => x.id === id)?.session === true),
|
|
56
89
|
memoryIds,
|
|
90
|
+
standardKeys: standardKeysFrom(body),
|
|
57
91
|
};
|
|
58
92
|
}
|
|
59
93
|
|
|
60
94
|
/** The answer-cache salt: request-scoped context that materially changes
|
|
61
|
-
* the answer (dataset scope, memory selection
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
95
|
+
* the answer (dataset scope, memory selection, license entitlements —
|
|
96
|
+
* the licensed tier changes the grounding, so two callers asking the
|
|
97
|
+
* same question must never share an entry). Requests differing only in
|
|
98
|
+
* salt share query text — an unsalted key would serve a scoped (or
|
|
99
|
+
* memory-flavored, or licensed-tier) answer to a plain ask. Null =
|
|
100
|
+
* default scope, no memory, no entitlement effect: keys stay
|
|
101
|
+
* byte-identical to the pre-salt era. */
|
|
65
102
|
export function requestSalt(scope: RequestScope, memoryUsed: string[]): string | null {
|
|
66
|
-
|
|
103
|
+
const licensed = licenseDeclared();
|
|
104
|
+
if (!scope.narrowed && !memoryUsed.length && !licensed) return null;
|
|
67
105
|
return JSON.stringify({
|
|
68
106
|
...(scope.narrowed ? { d: [...scope.corpora].sort() } : {}),
|
|
69
107
|
...(memoryUsed.length ? { m: [...memoryUsed].sort() } : {}),
|
|
108
|
+
// the entitlement set rides whenever the deployment keys content at
|
|
109
|
+
// all — an unentitled ask and an entitled ask of the same text are
|
|
110
|
+
// different answers even when the set is empty
|
|
111
|
+
...(licensed ? { s: [...scope.standardKeys].sort() } : {}),
|
|
70
112
|
});
|
|
71
113
|
}
|
|
@@ -4,6 +4,7 @@ import { portModelRunner } from "./env.ts";
|
|
|
4
4
|
import type { Background } from "./ports/runtime.ts";
|
|
5
5
|
import { retrieve } from "./pipeline";
|
|
6
6
|
import { understandQuery } from "./understand";
|
|
7
|
+
import { entitlementScope, standardKeysFrom } from "./requestScope";
|
|
7
8
|
import { sessionFrom } from "./auth";
|
|
8
9
|
import { err, json, corsHeaders, readJson, validateQuery, type ApiKey } from "./lib/http";
|
|
9
10
|
import { checkQuota, clientIp, telemetry } from "./quota";
|
|
@@ -32,9 +33,14 @@ export async function handleSearch(
|
|
|
32
33
|
|
|
33
34
|
const understanding = await understandQuery(portModelRunner(env), MODELS.understand, q.query, []);
|
|
34
35
|
const graphDocNumbers = await graphExpand(env, understanding);
|
|
36
|
+
// the license entitlement set rides the body exactly as it does on the
|
|
37
|
+
// ask path (TODO.external-refs/08): request-scoped, whitelist-validated,
|
|
38
|
+
// fail-closed — a retrieval with an empty set returns zero licensed
|
|
39
|
+
// chunks at the transport level
|
|
40
|
+
const standardKeys = entitlementScope(standardKeysFrom(body));
|
|
35
41
|
let retrieved;
|
|
36
42
|
try {
|
|
37
|
-
retrieved = await retrieve(env, q.query, { understanding, graphDocNumbers });
|
|
43
|
+
retrieved = await retrieve(env, q.query, { understanding, graphDocNumbers, standardKeys });
|
|
38
44
|
} catch {
|
|
39
45
|
return err(503, "retrieval_unavailable", "Search is briefly busy — please retry in a moment.");
|
|
40
46
|
}
|
|
@@ -23,3 +23,29 @@ export function toVectorizeFilter(f: QueryFilters): Record<string, string> | und
|
|
|
23
23
|
}
|
|
24
24
|
return undefined;
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
// ── the license entitlement scope (TODO.external-refs/08) ────────────────
|
|
28
|
+
// A chunk's `standard_key` metadata carries the licensed package's
|
|
29
|
+
// entitlement key; public content carries none. The scope is a HARD
|
|
30
|
+
// FILTER (a hit never reaches ranking when its key is outside the
|
|
31
|
+
// caller's set) — but it deliberately does NOT translate into a
|
|
32
|
+
// Vectorize metadata predicate: the wire has no "field missing OR in-set"
|
|
33
|
+
// operator, so a `standard_key $in […]` push-down would exclude every
|
|
34
|
+
// public chunk (they predate the field). The scope binds pool-level
|
|
35
|
+
// (stages/licenseScope.ts, before rerank) and at the lexical lane's
|
|
36
|
+
// source (pipeline.ts, the sealScope posture) instead; the single-doc
|
|
37
|
+
// post-seal fetches (section-descent, typed-pin parent, edition-cover)
|
|
38
|
+
// are doc-scoped and a document's key is a per-document constant, so
|
|
39
|
+
// they cannot re-admit a dropped key.
|
|
40
|
+
|
|
41
|
+
/** The one entitlement predicate: no key = public = always allowed; a key
|
|
42
|
+
* outside the caller's set = never allowed. `null`/undefined keys (the
|
|
43
|
+
* deployment declares no licensed content) disable the scope entirely. */
|
|
44
|
+
export function standardKeyAllowed(
|
|
45
|
+
meta: { standard_key?: string },
|
|
46
|
+
keys: ReadonlySet<string> | null | undefined,
|
|
47
|
+
): boolean {
|
|
48
|
+
if (!keys) return true;
|
|
49
|
+
const k = meta.standard_key;
|
|
50
|
+
return !k || keys.has(k);
|
|
51
|
+
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import type { Stage } from "./types.ts";
|
|
15
15
|
import { namedDocumentIn } from "../context.ts";
|
|
16
16
|
import { refCodec } from "../codecs.ts";
|
|
17
|
+
import { standardKeyAllowed } from "../selfquery.ts";
|
|
17
18
|
import type { Hit } from "../../../shared/chunk.ts";
|
|
18
19
|
|
|
19
20
|
const CITE_PATTERN = /\b(?:cite[sd]?|citing|referenc(?:e|es|ed|ing)|list[s]?|quote[sd]?)\b/i;
|
|
@@ -123,9 +124,13 @@ export const citationProbe: Stage = {
|
|
|
123
124
|
const [probes, citeRows] = (await c.lane["citation-probe"]) as [Hit[], CiteRow[]];
|
|
124
125
|
const seen = new Set(c.hits.map((m: any) => m.id));
|
|
125
126
|
let added = 0;
|
|
126
|
-
// only bibliography-shaped chunks (clause title or text mentions it)
|
|
127
|
+
// only bibliography-shaped chunks (clause title or text mentions it);
|
|
128
|
+
// the push rides the entitlement predicate — the probe runs past the
|
|
129
|
+
// pool-level license scope, and a licensed family's bibliography
|
|
130
|
+
// passages must obey the same hard scope as everything else
|
|
127
131
|
for (const h of probes) {
|
|
128
132
|
if (seen.has(h.id as any)) continue;
|
|
133
|
+
if (!standardKeyAllowed(h.metadata as any, c.opts.standardKeys)) continue;
|
|
129
134
|
const title = String((h.metadata as any)?.clause_title ?? "");
|
|
130
135
|
const text = String(h.text ?? "");
|
|
131
136
|
if (/bibliograph|normative reference/i.test(title + " " + text.slice(0, 300))) {
|
|
@@ -23,6 +23,7 @@ import { poolOpen } from "./poolOpen.ts";
|
|
|
23
23
|
import { lexicalUnion } from "./lexicalUnion.ts";
|
|
24
24
|
import { federate } from "./federate.ts";
|
|
25
25
|
import { seal } from "./seal.ts";
|
|
26
|
+
import { licenseScope } from "./licenseScope.ts";
|
|
26
27
|
import { corpusScope } from "./corpusScope.ts";
|
|
27
28
|
import { editionCover } from "./editionCover.ts";
|
|
28
29
|
import { stdRefNudge } from "./stdRefNudge.ts";
|
|
@@ -51,6 +52,7 @@ export const STAGES: Stage[] = [
|
|
|
51
52
|
lexicalUnion,
|
|
52
53
|
federate,
|
|
53
54
|
seal,
|
|
55
|
+
licenseScope,
|
|
54
56
|
overviewDemote,
|
|
55
57
|
familyBoost,
|
|
56
58
|
rerankStage,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The license entitlement hard scope (TODO.external-refs/08) — pool-level,
|
|
2
|
+
// after every candidate lane has merged, BEFORE rerank + the top-N cut
|
|
3
|
+
// (the `seal` posture, never the post-rerank corpus-scope drop). A chunk
|
|
4
|
+
// whose `standard_key` lies outside the caller's entitlement set never
|
|
5
|
+
// competes for the window: the model never sees licensed text the caller
|
|
6
|
+
// cannot be shown, so no prompt-level leakage either. The predicate is
|
|
7
|
+
// selfquery.standardKeyAllowed; the lane audit that binds every query
|
|
8
|
+
// shape lives in its doc comment.
|
|
9
|
+
import { standardKeyAllowed } from "../selfquery.ts";
|
|
10
|
+
import type { Stage } from "./types.ts";
|
|
11
|
+
|
|
12
|
+
export const licenseScope: Stage = {
|
|
13
|
+
name: "license-scope",
|
|
14
|
+
when: (c) => !!c.opts.standardKeys,
|
|
15
|
+
run: (c) => {
|
|
16
|
+
const keys = c.opts.standardKeys!;
|
|
17
|
+
const before = c.hits.length;
|
|
18
|
+
c.hits = c.hits.filter((h) => standardKeyAllowed(h.metadata, keys));
|
|
19
|
+
if (c.hits.length !== before) {
|
|
20
|
+
console.log("license scope:", before, "→", c.hits.length, "candidates within the caller's entitlement set");
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -36,6 +36,17 @@ export interface RetrieveOptions {
|
|
|
36
36
|
* intersects the requested ids with session permissions before
|
|
37
37
|
* building this set. */
|
|
38
38
|
datasetScope?: Set<string> | null;
|
|
39
|
+
/** The license entitlement set (TODO.external-refs/08): the standard
|
|
40
|
+
* keys the caller's organization is entitled to, resolved
|
|
41
|
+
* request-scoped by the deployment (never a client-tunable filter —
|
|
42
|
+
* it arrives through the same trusted request context as the session,
|
|
43
|
+
* and the profile's declared licensed list is the validation
|
|
44
|
+
* whitelist). NON-NULL activates the hard scope: chunks carrying a
|
|
45
|
+
* `standard_key` outside the set never reach ranking (an EMPTY set is
|
|
46
|
+
* the unentitled caller — licensed content hidden, citation-level
|
|
47
|
+
* metadata stays). Null = the deployment declares no licensed
|
|
48
|
+
* content, the scope is inert. */
|
|
49
|
+
standardKeys?: Set<string> | null;
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
export interface GlossaryEntry {
|
package/dist/ask-VFSAF5WG.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
handleAsk
|
|
3
|
-
} from "./chunk-PVHSBXZD.js";
|
|
4
|
-
import "./chunk-LNSDBEKS.js";
|
|
5
|
-
import "./chunk-6GOSMLRH.js";
|
|
6
|
-
import "./chunk-SN3ANQ3Y.js";
|
|
7
|
-
import "./chunk-WGXATDXY.js";
|
|
8
|
-
import "./chunk-Q6LI4T7M.js";
|
|
9
|
-
import "./chunk-Q327B27J.js";
|
|
10
|
-
export {
|
|
11
|
-
handleAsk
|
|
12
|
-
};
|
package/dist/search-OMPBMZT4.js
DELETED