modelrig 0.8.0 → 0.9.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.
Files changed (248) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/README.md +27 -2
  3. package/dist/artifacts/artifact.d.ts +12 -13
  4. package/dist/artifacts/artifact.d.ts.map +1 -1
  5. package/dist/artifacts/artifact.js +17 -23
  6. package/dist/artifacts/index.d.ts +13 -5
  7. package/dist/artifacts/index.d.ts.map +1 -1
  8. package/dist/artifacts/index.js +11 -4
  9. package/dist/artifacts/run-context.d.ts +3 -24
  10. package/dist/artifacts/run-context.d.ts.map +1 -1
  11. package/dist/artifacts/run-context.js +3 -39
  12. package/dist/bakeoff/engine.d.ts +29 -0
  13. package/dist/bakeoff/engine.d.ts.map +1 -1
  14. package/dist/bakeoff/engine.js +47 -0
  15. package/dist/bakeoff/eval-cases-rig.d.ts +36 -0
  16. package/dist/bakeoff/eval-cases-rig.d.ts.map +1 -0
  17. package/dist/bakeoff/eval-cases-rig.js +27 -0
  18. package/dist/bakeoff/eval-cases.d.ts +19 -0
  19. package/dist/bakeoff/eval-cases.d.ts.map +1 -1
  20. package/dist/bakeoff/eval-cases.js +18 -1
  21. package/dist/bakeoff/golden-judge.d.ts +47 -0
  22. package/dist/bakeoff/golden-judge.d.ts.map +1 -0
  23. package/dist/bakeoff/golden-judge.js +49 -0
  24. package/dist/bakeoff/quality.d.ts +30 -0
  25. package/dist/bakeoff/quality.d.ts.map +1 -1
  26. package/dist/bakeoff/quality.js +21 -0
  27. package/dist/bakeoff/replay.d.ts +11 -0
  28. package/dist/bakeoff/replay.d.ts.map +1 -1
  29. package/dist/bakeoff/replay.js +8 -0
  30. package/dist/bakeoff/report.d.ts +7 -0
  31. package/dist/bakeoff/report.d.ts.map +1 -1
  32. package/dist/bakeoff/report.js +26 -0
  33. package/dist/cli-judge.d.ts +12 -0
  34. package/dist/cli-judge.d.ts.map +1 -0
  35. package/dist/cli-judge.js +48 -0
  36. package/dist/cli.d.ts.map +1 -1
  37. package/dist/cli.js +14 -42
  38. package/dist/config.d.ts +54 -29
  39. package/dist/config.d.ts.map +1 -1
  40. package/dist/config.js +86 -47
  41. package/dist/detect/detector.d.ts +4 -2
  42. package/dist/detect/detector.d.ts.map +1 -1
  43. package/dist/detect/safe-harbor.d.ts +57 -0
  44. package/dist/detect/safe-harbor.d.ts.map +1 -0
  45. package/dist/detect/safe-harbor.js +278 -0
  46. package/dist/feedback/coverage.d.ts +11 -8
  47. package/dist/feedback/coverage.d.ts.map +1 -1
  48. package/dist/feedback/coverage.js +29 -16
  49. package/dist/feedback/grade.d.ts +51 -0
  50. package/dist/feedback/grade.d.ts.map +1 -0
  51. package/dist/feedback/grade.js +63 -0
  52. package/dist/gateway/http-protocol.d.ts +1 -1
  53. package/dist/gateway/http-protocol.d.ts.map +1 -1
  54. package/dist/gateway/http-protocol.js +1 -1
  55. package/dist/index.d.ts +44 -27
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +110 -43
  58. package/dist/init/observe-cli.js +1 -1
  59. package/dist/init/observe.d.ts +2 -2
  60. package/dist/init/observe.d.ts.map +1 -1
  61. package/dist/init/observe.js +11 -7
  62. package/dist/mask/arm.d.ts +112 -0
  63. package/dist/mask/arm.d.ts.map +1 -0
  64. package/dist/mask/arm.js +179 -0
  65. package/dist/mask/index.d.ts +2 -0
  66. package/dist/mask/index.d.ts.map +1 -1
  67. package/dist/mask/index.js +7 -1
  68. package/dist/mask/mask-policy-cli.d.ts +12 -4
  69. package/dist/mask/mask-policy-cli.d.ts.map +1 -1
  70. package/dist/mask/mask-policy-cli.js +34 -9
  71. package/dist/query/grammar.d.ts +2 -2
  72. package/dist/query/grammar.d.ts.map +1 -1
  73. package/dist/query/grammar.js +3 -1
  74. package/dist/query/saved.d.ts +13 -0
  75. package/dist/query/saved.d.ts.map +1 -1
  76. package/dist/query/saved.js +67 -1
  77. package/dist/receipts/types.d.ts +1 -1
  78. package/dist/receipts/types.d.ts.map +1 -1
  79. package/dist/receipts/types.js +1 -0
  80. package/dist/registry/build.d.ts +43 -1
  81. package/dist/registry/build.d.ts.map +1 -1
  82. package/dist/registry/build.js +52 -0
  83. package/dist/registry/pricing-shape.d.ts +29 -0
  84. package/dist/registry/pricing-shape.d.ts.map +1 -1
  85. package/dist/registry/pricing-shape.js +10 -1
  86. package/dist/registry/rows.d.ts.map +1 -1
  87. package/dist/registry/rows.js +1 -0
  88. package/dist/registry/select.d.ts +6 -1
  89. package/dist/registry/select.d.ts.map +1 -1
  90. package/dist/registry/select.js +11 -0
  91. package/dist/routes/call-config.d.ts +199 -0
  92. package/dist/routes/call-config.d.ts.map +1 -0
  93. package/dist/routes/call-config.js +343 -0
  94. package/dist/routes/loader.js +5 -3
  95. package/dist/routes/objective-loader.d.ts +12 -0
  96. package/dist/routes/objective-loader.d.ts.map +1 -1
  97. package/dist/routes/objective-loader.js +82 -0
  98. package/dist/routes/resolved-bundle.d.ts +50 -0
  99. package/dist/routes/resolved-bundle.d.ts.map +1 -1
  100. package/dist/routes/resolved-bundle.js +52 -0
  101. package/dist/routes/screen-loader.d.ts +28 -0
  102. package/dist/routes/screen-loader.d.ts.map +1 -1
  103. package/dist/routes/screen-loader.js +51 -0
  104. package/dist/run/accept.d.ts +2 -0
  105. package/dist/run/accept.d.ts.map +1 -1
  106. package/dist/run/accept.js +1 -0
  107. package/dist/run/dispatch.d.ts +42 -13
  108. package/dist/run/dispatch.d.ts.map +1 -1
  109. package/dist/run/dispatch.js +107 -25
  110. package/dist/run/grounding.d.ts +28 -2
  111. package/dist/run/grounding.d.ts.map +1 -1
  112. package/dist/run/grounding.js +54 -10
  113. package/dist/run/record-attempt.d.ts +4 -0
  114. package/dist/run/record-attempt.d.ts.map +1 -1
  115. package/dist/run/record-attempt.js +4 -0
  116. package/dist/run/routing-docs.js +1 -1
  117. package/dist/run/run.d.ts.map +1 -1
  118. package/dist/run/run.js +21 -26
  119. package/dist/savings/odometer.d.ts +4 -0
  120. package/dist/savings/odometer.d.ts.map +1 -1
  121. package/dist/savings/odometer.js +20 -1
  122. package/dist/savings/swap-execute.d.ts +7 -0
  123. package/dist/savings/swap-execute.d.ts.map +1 -1
  124. package/dist/savings/swap-execute.js +30 -0
  125. package/dist/savings/swaps-client.d.ts +10 -0
  126. package/dist/savings/swaps-client.d.ts.map +1 -1
  127. package/dist/screen/fixtures/probe-corpora.d.ts +23 -0
  128. package/dist/screen/fixtures/probe-corpora.d.ts.map +1 -0
  129. package/dist/screen/fixtures/probe-corpora.js +69 -0
  130. package/dist/screen/index.d.ts +21 -0
  131. package/dist/screen/index.d.ts.map +1 -0
  132. package/dist/screen/index.js +64 -0
  133. package/dist/screen/model-screen.d.ts +111 -0
  134. package/dist/screen/model-screen.d.ts.map +1 -0
  135. package/dist/screen/model-screen.js +178 -0
  136. package/dist/screen/patterns.d.ts +51 -0
  137. package/dist/screen/patterns.d.ts.map +1 -0
  138. package/dist/screen/patterns.js +151 -0
  139. package/dist/screen/phi-gate.d.ts +52 -0
  140. package/dist/screen/phi-gate.d.ts.map +1 -0
  141. package/dist/screen/phi-gate.js +86 -0
  142. package/dist/screen/policy.d.ts +71 -0
  143. package/dist/screen/policy.d.ts.map +1 -0
  144. package/dist/screen/policy.js +334 -0
  145. package/dist/screen/probe.d.ts +118 -0
  146. package/dist/screen/probe.d.ts.map +1 -0
  147. package/dist/screen/probe.js +132 -0
  148. package/dist/screen/screen-cli.d.ts +16 -0
  149. package/dist/screen/screen-cli.d.ts.map +1 -0
  150. package/dist/screen/screen-cli.js +178 -0
  151. package/dist/screen/screen.d.ts +99 -0
  152. package/dist/screen/screen.d.ts.map +1 -0
  153. package/dist/screen/screen.js +0 -0
  154. package/dist/screen/sidecar.d.ts +69 -0
  155. package/dist/screen/sidecar.d.ts.map +1 -0
  156. package/dist/screen/sidecar.js +145 -0
  157. package/dist/screen/types.d.ts +43 -2
  158. package/dist/screen/types.d.ts.map +1 -1
  159. package/dist/settings/api-fetcher.d.ts +14 -3
  160. package/dist/settings/api-fetcher.d.ts.map +1 -1
  161. package/dist/settings/api-fetcher.js +61 -10
  162. package/dist/settings/grading-guide.d.ts +76 -0
  163. package/dist/settings/grading-guide.d.ts.map +1 -0
  164. package/dist/settings/grading-guide.js +23 -0
  165. package/dist/settings/index.d.ts +3 -2
  166. package/dist/settings/index.d.ts.map +1 -1
  167. package/dist/settings/index.js +2 -1
  168. package/dist/settings/task-settings.d.ts +44 -7
  169. package/dist/settings/task-settings.d.ts.map +1 -1
  170. package/dist/settings/task-settings.js +53 -12
  171. package/dist/status.d.ts +26 -20
  172. package/dist/status.d.ts.map +1 -1
  173. package/dist/status.js +47 -29
  174. package/dist/telemetry/artifact-store.d.ts +5 -6
  175. package/dist/telemetry/artifact-store.d.ts.map +1 -1
  176. package/dist/telemetry/artifact-store.js +10 -24
  177. package/dist/telemetry/attempt-meta.d.ts +21 -0
  178. package/dist/telemetry/attempt-meta.d.ts.map +1 -1
  179. package/dist/telemetry/attempt-meta.js +9 -0
  180. package/dist/telemetry/exporter.d.ts +2 -2
  181. package/dist/telemetry/exporter.d.ts.map +1 -1
  182. package/dist/telemetry/exporter.js +22 -41
  183. package/dist/telemetry/grade-mappers.d.ts +22 -0
  184. package/dist/telemetry/grade-mappers.d.ts.map +1 -0
  185. package/dist/telemetry/grade-mappers.js +58 -0
  186. package/dist/telemetry/grades-sql.d.ts +53 -0
  187. package/dist/telemetry/grades-sql.d.ts.map +1 -0
  188. package/dist/telemetry/grades-sql.js +133 -0
  189. package/dist/telemetry/ingest-protocol.d.ts +29 -46
  190. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  191. package/dist/telemetry/ingest-protocol.js +16 -54
  192. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  193. package/dist/telemetry/store-migrations.js +51 -0
  194. package/dist/telemetry/store.d.ts +9 -6
  195. package/dist/telemetry/store.d.ts.map +1 -1
  196. package/dist/telemetry/store.js +8 -50
  197. package/dist/tenancy/api-key-format.d.ts +30 -1
  198. package/dist/tenancy/api-key-format.d.ts.map +1 -1
  199. package/dist/tenancy/api-key-format.js +33 -1
  200. package/dist/types/provider.d.ts +3 -0
  201. package/dist/types/provider.d.ts.map +1 -1
  202. package/dist/types/provider.js +25 -0
  203. package/dist/types/route.d.ts +78 -2
  204. package/dist/types/route.d.ts.map +1 -1
  205. package/dist/types/telemetry.d.ts +43 -46
  206. package/dist/types/telemetry.d.ts.map +1 -1
  207. package/dist/validate-cli.d.ts.map +1 -1
  208. package/dist/validate-cli.js +32 -0
  209. package/dist/verify/verify.d.ts +5 -0
  210. package/dist/verify/verify.d.ts.map +1 -1
  211. package/dist/verify/verify.js +2 -0
  212. package/dist/watch/evaluate.d.ts +84 -1
  213. package/dist/watch/evaluate.d.ts.map +1 -1
  214. package/dist/watch/evaluate.js +28 -0
  215. package/dist/watch/monitor.d.ts +8 -2
  216. package/dist/watch/monitor.d.ts.map +1 -1
  217. package/dist/watch/monitor.js +13 -5
  218. package/dist/watch/regret-monitor-cli.d.ts +16 -0
  219. package/dist/watch/regret-monitor-cli.d.ts.map +1 -0
  220. package/dist/watch/regret-monitor-cli.js +137 -0
  221. package/dist/watch/regret-monitor.d.ts +168 -0
  222. package/dist/watch/regret-monitor.d.ts.map +1 -0
  223. package/dist/watch/regret-monitor.js +304 -0
  224. package/dist/watch/upgrade.d.ts +62 -0
  225. package/dist/watch/upgrade.d.ts.map +1 -0
  226. package/dist/watch/upgrade.js +57 -0
  227. package/docs/agent-skill/SKILL.md +109 -5
  228. package/docs/how-it-fits.md +3 -2
  229. package/docs/instrumentation.md +149 -21
  230. package/docs/llms.txt +64 -20
  231. package/docs/mcp-oracle.md +249 -0
  232. package/docs/quickstart.md +103 -9
  233. package/package.json +16 -3
  234. package/registry/leaderboard.json +74 -0
  235. package/registry/registry.json +150 -112
  236. package/screen.d.ts +16 -0
  237. package/templates/call-disposition-qa.json +77 -0
  238. package/templates/catalog-cleansing.json +77 -0
  239. package/templates/compliance-review.json +76 -0
  240. package/templates/cs-next-action.json +76 -0
  241. package/templates/document-extraction.json +78 -0
  242. package/templates/financial-classification.json +75 -0
  243. package/templates/lead-qualification.json +76 -0
  244. package/templates/medical-classification.json +71 -0
  245. package/templates/ticket-triage.json +76 -0
  246. package/dist/telemetry/supabase-exporter.d.ts +0 -64
  247. package/dist/telemetry/supabase-exporter.d.ts.map +0 -1
  248. package/dist/telemetry/supabase-exporter.js +0 -211
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ /**
3
+ * `modelrig screen …` (SC-1a, spec §9) — extracted from cli.ts to keep it under
4
+ * the 700-LOC cap (same discipline as mask-policy-cli.ts).
5
+ *
6
+ * modelrig screen check <file|-> dry-run: scan content with the T0 pattern
7
+ * pack and print COUNTS by type × source.
8
+ * NEVER prints a matched value (spec §1.4).
9
+ * modelrig screen pull <detector> resolve the LOCAL cache path for an optional
10
+ * classifier's weights and, with --url, fetch
11
+ * them there. ModelRig never bundles or
12
+ * re-hosts weights (A3).
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.runScreenCli = runScreenCli;
16
+ const node_fs_1 = require("node:fs");
17
+ const node_fs_2 = require("node:fs");
18
+ const node_path_1 = require("node:path");
19
+ const screen_1 = require("./screen");
20
+ const policy_1 = require("./policy");
21
+ const probe_1 = require("./probe");
22
+ const probe_corpora_1 = require("./fixtures/probe-corpora");
23
+ const KNOWN_SOURCES = new Set([
24
+ "template",
25
+ "variable",
26
+ "search",
27
+ "tool_result",
28
+ "proxy_user",
29
+ "judge_meta",
30
+ "unknown",
31
+ ]);
32
+ /** Read the target — a file path, or `-` for stdin. */
33
+ function readTarget(target) {
34
+ if (target === "-")
35
+ return (0, node_fs_2.readFileSync)(0, "utf8"); // fd 0 = stdin
36
+ return (0, node_fs_2.readFileSync)(target, "utf8");
37
+ }
38
+ /** `modelrig screen check <file|->` — dry-run over one segment, counts only. */
39
+ async function screenCheck(flags) {
40
+ const target = flags.positional[1];
41
+ if (target === undefined) {
42
+ console.error("usage: modelrig screen check <file|-> [--source search|variable|proxy_user|tool_result]");
43
+ return 1;
44
+ }
45
+ const source = (flags.named.get("source") ?? "search");
46
+ if (!KNOWN_SOURCES.has(source)) {
47
+ console.error(`unknown --source "${source}" — allowed: ${[...KNOWN_SOURCES].join(", ")}`);
48
+ return 1;
49
+ }
50
+ let text;
51
+ try {
52
+ text = readTarget(target);
53
+ }
54
+ catch (err) {
55
+ console.error(`cannot read ${target === "-" ? "stdin" : `"${target}"`}: ${err instanceof Error ? err.message : String(err)}`);
56
+ return 1;
57
+ }
58
+ // Dry-run policy: detect mode, T0 pattern pack, default threshold. The content
59
+ // is scanned as a single untrusted segment; only COUNTS are printed.
60
+ const seam = (0, screen_1.createScreenSeam)((0, policy_1.resolveScreenPolicy)((0, policy_1.parseScreenPolicy)({ mode: "detect" })));
61
+ await seam.screenRequest([{ source, tier: "untrusted", text }]);
62
+ const receipt = seam.receipt();
63
+ console.log(`screen check — dry run (patterns@v1, counts only; values are never shown)`);
64
+ console.log(` risk: ${receipt.risk_bucket}`);
65
+ if (receipt.findings.length === 0) {
66
+ console.log(" findings: (none)");
67
+ return 0;
68
+ }
69
+ console.log(" findings (type × source × count):");
70
+ for (const f of receipt.findings) {
71
+ console.log(` ${f.type.padEnd(34)} ${f.source.padEnd(12)} ${f.count}`);
72
+ }
73
+ return 0;
74
+ }
75
+ /** `modelrig screen pull <detector> [--url <url>]` — resolve the local cache
76
+ * path for a classifier's weights and, with --url, fetch them there. A3: weights
77
+ * are NEVER bundled or re-hosted — the URL is the model's own vendor/HF page. */
78
+ async function screenPull(flags) {
79
+ const id = flags.positional[1];
80
+ if (id === undefined || !id.startsWith("classifier:")) {
81
+ console.error("usage: modelrig screen pull <classifier:name@version> [--url <weights-url>]");
82
+ return 1;
83
+ }
84
+ const dest = (0, policy_1.classifierModelPath)(id);
85
+ const url = flags.named.get("url");
86
+ if (url === undefined) {
87
+ console.log(`screen pull ${id}`);
88
+ console.log(` local cache path: ${dest}`);
89
+ console.log(" ModelRig never bundles or re-hosts weights (A3). Provide the model's own");
90
+ console.log(" vendor/Hugging Face weights URL with --url to fetch them here, or place the");
91
+ console.log(" .onnx file at the path above, then install the optional runtime peer");
92
+ console.log(" (onnxruntime-node) and add the detector to your route's `screen.detectors`.");
93
+ return 0;
94
+ }
95
+ (0, node_fs_2.mkdirSync)((0, node_path_1.dirname)(dest), { recursive: true });
96
+ try {
97
+ const res = await fetch(url);
98
+ if (!res.ok || res.body === null) {
99
+ console.error(`fetch failed: ${res.status} ${res.statusText}`);
100
+ return 1;
101
+ }
102
+ const bytes = Buffer.from(await res.arrayBuffer());
103
+ await new Promise((resolveWrite, reject) => {
104
+ const out = (0, node_fs_1.createWriteStream)(dest);
105
+ out.on("error", reject);
106
+ out.on("finish", () => resolveWrite());
107
+ out.end(bytes);
108
+ });
109
+ console.log(`screen pull ${id} → ${dest} (${bytes.length} bytes)`);
110
+ return 0;
111
+ }
112
+ catch (err) {
113
+ console.error(`fetch failed: ${err instanceof Error ? err.message : String(err)}`);
114
+ return 1;
115
+ }
116
+ }
117
+ /**
118
+ * `modelrig screen probe [--detectors <ids>] [--corpus <ids>]` (SC-3, spec-sc3
119
+ * §3) — run each configured detector over the license-clean SYNTHETIC corpora and
120
+ * print the MEASURED TPR / FPR / latency per corpus (counts + rates only, never a
121
+ * fixture value). The org-benign FP rate ("FP on your traffic") is measured from
122
+ * the content-capture lane in the console/hosted probe; the local CLI reports the
123
+ * public-corpus numbers and the "no captures to sample" teaching state, so a
124
+ * customer's traffic never rides this local surface.
125
+ */
126
+ async function screenProbe(flags) {
127
+ const detectorArg = flags.named.get("detectors") ?? flags.named.get("detector");
128
+ const detectorIds = detectorArg !== undefined ? detectorArg.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
129
+ const corpusArg = flags.named.get("corpus");
130
+ let corpora = probe_corpora_1.PROBE_CORPORA;
131
+ if (corpusArg !== undefined) {
132
+ const ids = corpusArg.split(",").map((s) => s.trim()).filter(Boolean);
133
+ const picked = ids.map((id) => (0, probe_corpora_1.corpusById)(id));
134
+ const missing = ids.filter((_id, i) => picked[i] === undefined);
135
+ if (missing.length > 0) {
136
+ console.error(`unknown corpus id(s): ${missing.join(", ")} — available: ${probe_corpora_1.PROBE_CORPORA.map((c) => c.id).join(", ")}`);
137
+ return 1;
138
+ }
139
+ corpora = picked.filter((c) => c !== undefined);
140
+ }
141
+ const resolved = (0, policy_1.resolveScreenPolicy)((0, policy_1.parseScreenPolicy)({ mode: "detect", ...(detectorIds !== undefined ? { detectors: detectorIds } : {}) }));
142
+ console.log("screen probe — measured on license-clean synthetic corpora (counts + rates only; values are never shown)");
143
+ for (const detector of resolved.detectors) {
144
+ const meta = resolved.detectorMeta.find((m) => m.id === detector.id);
145
+ const measured = await (0, probe_1.probeDetector)({
146
+ detector,
147
+ detectorVersion: meta?.version ?? "unknown",
148
+ corpora,
149
+ // The local CLI never samples a customer's captures (F9/P6); org-benign is
150
+ // measured by the console/hosted probe against the content-capture lane.
151
+ benign: [],
152
+ });
153
+ console.log(`\n ${detector.id} (version ${measured.detectorVersion}, threshold ${measured.threshold})`);
154
+ for (const c of measured.perCorpus) {
155
+ const tpr = c.tpr === null ? "n/a" : `${(c.tpr * 100).toFixed(1)}%`;
156
+ const fpr = c.fpr === null ? "n/a" : `${(c.fpr * 100).toFixed(1)}%`;
157
+ console.log(` ${c.corpusId.padEnd(22)} n=${String(c.n).padEnd(4)} TPR ${tpr.padEnd(7)} FP ${fpr.padEnd(7)} ${c.latencyMs}ms [${c.license}] ${c.digest.slice(0, 14)}…`);
158
+ }
159
+ console.log(" FP on your traffic: no captures to sample locally — run the probe from the console to sample the content-capture lane.");
160
+ }
161
+ console.log("\nProbed, not declared. Publish a detector-probe receipt from the console to make a number verifiable (never a literal).");
162
+ return 0;
163
+ }
164
+ /** Dispatch `modelrig screen <subcommand>`. */
165
+ async function runScreenCli(flags) {
166
+ const sub = flags.positional[0];
167
+ switch (sub) {
168
+ case "check":
169
+ return screenCheck(flags);
170
+ case "pull":
171
+ return screenPull(flags);
172
+ case "probe":
173
+ return screenProbe(flags);
174
+ default:
175
+ console.error("usage: modelrig screen <check|pull|probe> …");
176
+ return 1;
177
+ }
178
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Screening — the screen engine + receipt producer (WIRED 2026-08-25, SC-1a).
3
+ * Spec §4 (actions), §4.3 (fail-closed matrix), §5 (receipt).
4
+ *
5
+ * Seam: {@link createScreenSeam} wraps the assembled request on the REQUEST side
6
+ * (src/run/dispatch.ts — the SAME helper CS-1's mask seam wraps; screening runs
7
+ * BEFORE masking so mask tokens never confuse a detector) and the AdapterResult
8
+ * on the RESPONSE side (canary / citation / Unicode output checks).
9
+ *
10
+ * Fail-closed (spec §4.3): a REQUIRED deterministic detector that THROWS fails
11
+ * closed under `enforce` (invariant_violation, no request sent); under `detect`
12
+ * it warns once, sets `degraded`, and sends. A MISSING OPTIONAL classifier is
13
+ * handled at resolve time (policy.ts drops it, `degraded: true`) — never a block.
14
+ *
15
+ * LEAK INVARIANT (spec §1.4): this module consumes span OFFSETS internally and
16
+ * emits COUNTS by (detector × type × source). No matched span, dictionary, raw
17
+ * untrusted content, or detector score reaches a receipt, log, or failure
18
+ * message. `summarizeScreen` is the SINGLE producer (same discipline as
19
+ * summarizeScrub / summarizeMask).
20
+ */
21
+ import type { ContentSegment } from "./provenance";
22
+ import type { ResolvedScreenPolicy, ScreenAction, ScreenFinding, ScreenPolicy, ScreenReceipt } from "./types";
23
+ /** The request-side decision the run loop acts on. `block` ⇒ fail closed
24
+ * pre-dispatch (spec §4.3); otherwise `keepSegments` is the (possibly stripped)
25
+ * segment list to assemble and `quarantine` gates tool calls. */
26
+ export interface ScreenRequestOutcome {
27
+ readonly kind: "ok" | "block";
28
+ readonly keepSegments: readonly ContentSegment[];
29
+ readonly quarantine: boolean;
30
+ }
31
+ /** Output-side hygiene result (pure). */
32
+ export interface ScreenResponseResult {
33
+ readonly text: string;
34
+ readonly citationsDropped: number;
35
+ readonly canaryLeaked: boolean;
36
+ }
37
+ /** The accumulated outcome the single producer turns into a {@link
38
+ * ScreenReceipt}. Counts + enums only — never a value (spec §1.4). */
39
+ export interface ScreenOutcome {
40
+ readonly policyHash: string;
41
+ readonly detectors: readonly {
42
+ readonly id: string;
43
+ readonly version: string;
44
+ }[];
45
+ readonly findings: readonly ScreenFinding[];
46
+ readonly maxConfidence: number;
47
+ readonly action: ScreenAction | "none";
48
+ readonly quarantinedToolCalls?: number;
49
+ readonly canaryLeaked?: boolean;
50
+ readonly citationsDropped?: number;
51
+ readonly degraded?: boolean;
52
+ }
53
+ /**
54
+ * The stateful per-run screen seam the dispatch helper drives. Runs the resolved
55
+ * detectors over the untrusted/semi segments, resolves the per-tier action,
56
+ * accumulates counts-only state, and produces the receipt via the single
57
+ * producer. A deterministic detector throw is the §4.3 fail-closed pivot: under
58
+ * `enforce` it surfaces as a `block` outcome (no request sent); under `detect` it
59
+ * warns once and degrades.
60
+ */
61
+ export interface ScreenSeam {
62
+ /** Canary token to seed into the trusted system slot (spec §2.3), or null when
63
+ * the policy does not enable the canary. Read once at assembly time. */
64
+ readonly canaryToken: string | null;
65
+ /** Detect + resolve the request-side action. Async to admit the classifier
66
+ * tier. Accumulates findings/action into the seam. */
67
+ screenRequest(segments: readonly ContentSegment[]): Promise<ScreenRequestOutcome>;
68
+ /** Output-side hygiene: strip Unicode tags, allowlist citations, detect canary
69
+ * leak. Accumulates the counts and returns the cleaned text. */
70
+ screenResponse(text: string, injectedUrls: readonly string[]): string;
71
+ /** Record tool calls dropped because the turn was quarantined on a provider
72
+ * without `tool_choice_none` parity (A7). */
73
+ noteQuarantineDrop(count: number): void;
74
+ /** Build the counts-only {@link ScreenReceipt} from accumulated state. */
75
+ receipt(): ScreenReceipt;
76
+ }
77
+ export declare function createScreenSeam(resolved: ResolvedScreenPolicy): ScreenSeam;
78
+ /**
79
+ * The SINGLE producer of {@link ScreenReceipt} (same single-producer discipline
80
+ * as summarizeScrub / summarizeMask). Its parameter type is already counts-only,
81
+ * making the leak invariant STRUCTURAL — it cannot receive a matched value, a
82
+ * span, or a detector's internal score. Derives `risk_bucket` from the max
83
+ * confidence (which is NOT persisted); omits optional fields when absent so the
84
+ * jsonb payload on `meta.screen` is minimal and stable.
85
+ */
86
+ export declare function summarizeScreen(outcome: ScreenOutcome): ScreenReceipt;
87
+ /**
88
+ * Pure output-side hygiene, decoupled from the seam for direct testing and for
89
+ * the CLI dry-run (spec §2.3): strip Unicode tag chars, allowlist citation URLs
90
+ * against the injected sources (INJ-6), detect a canary leak. Never mutates
91
+ * input; returns the cleaned text plus counts.
92
+ */
93
+ export declare function screenResponse(outputText: string, policy: ScreenPolicy, injectedUrls: readonly string[], canaryToken?: string | null): ScreenResponseResult;
94
+ /**
95
+ * Test-only: reset the once-per-process detect-throw warning latch (mirrors
96
+ * resetMaskWarningsForTest).
97
+ */
98
+ export declare function resetScreenWarningsForTest(): void;
99
+ //# sourceMappingURL=screen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../src/screen/screen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,EACV,oBAAoB,EAEpB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACd,MAAM,SAAS,CAAC;AAOjB;;iEAEiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,SAAS,cAAc,EAAE,CAAC;IACjD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED;sEACsE;AACtE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjF,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC;IACvC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAmCD;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB;4EACwE;IACxE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;0DACsD;IACtD,aAAa,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAClF;oEACgE;IAChE,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC;IACtE;iDAC6C;IAC7C,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,0EAA0E;IAC1E,OAAO,IAAI,aAAa,CAAC;CAC1B;AAID,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,UAAU,CAkH3E;AAuBD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,CAarE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAC1B,oBAAoB,CAMtB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}
Binary file
@@ -0,0 +1,69 @@
1
+ /**
2
+ * SC-2 — the sidecar `Detector` adapter (spec-sc2 §3; shared with CS-1 D6).
3
+ *
4
+ * A `Detector` of the reserved `kind: "sidecar"` that adapts a CUSTOMER-RUN de-id
5
+ * container (GLiNER Guard / GLiNER2-PII / Presidio) over a deliberately tiny HTTP
6
+ * contract. ModelRig ships the ADAPTER and the CONTRACT only — never a model,
7
+ * never re-hosted weights (research §6a.3). The customer stands up their own
8
+ * service and owns its weights/license.
9
+ *
10
+ * THE CONTRACT (content-out only to the customer's own service):
11
+ * POST <endpoint> { text, source, tier } → the segment to scan
12
+ * 200 { spans: [{ start, end, type, confidence }] } → spans ONLY, never
13
+ * the matched text back
14
+ * The receipt id is `sidecar:<digest>` — the customer's local image/config
15
+ * digest label; ModelRig stores the label, not the model.
16
+ *
17
+ * FAIL-CLOSED (spec-sc2 §3, inherits SC-1 §4.3 via the shipped screen seam):
18
+ * on unreachable / timeout / malformed response the adapter THROWS a typed
19
+ * {@link SidecarUnavailableError}. The shipped seam (screen.ts) catches a
20
+ * detector throw and — under `enforce` — fails the run PRE-DISPATCH
21
+ * (invariant_violation, no request sent), which is exactly the required posture
22
+ * on a `phi` route where the sidecar is a load requirement (§4); under `detect`
23
+ * it degrades to the deterministic tier with `degraded: true`. We compose over
24
+ * that engine — we do NOT fork it.
25
+ *
26
+ * LEAK INVARIANT (spec-sc2 §1.4, §8b): the sidecar's REQUEST body, its RESPONSE
27
+ * body, and the span offsets NEVER reach a receipt / log / telemetry / OTLP span
28
+ * / failure message. Only per-`type` COUNTS survive (the seam rolls spans into
29
+ * counts). The thrown error carries the detector id + a failure CLASS only —
30
+ * never the endpoint's response text or the scanned content.
31
+ */
32
+ import type { Detector } from "../detect/detector";
33
+ /** Config for {@link createSidecarDetector}. `id` is the customer's local truth
34
+ * (`sidecar:<image-or-config-digest>`); `endpoint` is the customer-run service;
35
+ * `types` optionally narrows which span types the adapter keeps. */
36
+ export interface SidecarDetectorConfig {
37
+ /** Receipt id — normalized to `sidecar:<digest>` if a bare digest is given. */
38
+ readonly id: string;
39
+ /** The customer's own de-id endpoint. Content is POSTed here and nowhere else. */
40
+ readonly endpoint: string;
41
+ /** Per-call timeout; a slow/hung sidecar fails closed rather than blocking a
42
+ * run indefinitely. Default 2000 ms. */
43
+ readonly timeoutMs?: number;
44
+ /** Optional allowlist of span `type` labels to keep; others are dropped. */
45
+ readonly types?: readonly string[];
46
+ /** Injectable fetch for tests (defaults to global fetch). Never reads env. */
47
+ readonly fetchImpl?: typeof fetch;
48
+ }
49
+ /** Thrown on any sidecar failure (unreachable / timeout / non-2xx / malformed).
50
+ * Its message is a failure CLASS + the detector id ONLY — never the response
51
+ * body or the scanned content (leak invariant §1.4). The shipped screen seam
52
+ * catches it to apply the fail-closed matrix (§4.3). */
53
+ export declare class SidecarUnavailableError extends Error {
54
+ readonly detectorId: string;
55
+ /** A coarse machine-readable reason — no content, no body. */
56
+ readonly reason: "unreachable" | "timeout" | "http_error" | "malformed_response";
57
+ constructor(detectorId: string, reason: SidecarUnavailableError["reason"]);
58
+ }
59
+ /** Normalize an id to the `sidecar:<digest>` receipt shape. A caller may pass
60
+ * either the full `sidecar:<digest>` or a bare digest. */
61
+ export declare function sidecarReceiptId(id: string): string;
62
+ /**
63
+ * Build the sidecar `Detector`. `detect(input)` POSTs `{text, source, tier}` to
64
+ * the customer endpoint and returns spans; any failure THROWS
65
+ * {@link SidecarUnavailableError} so the shipped seam applies the fail-closed
66
+ * matrix. Never logs the request/response body.
67
+ */
68
+ export declare function createSidecarDetector(config: SidecarDetectorConfig): Detector;
69
+ //# sourceMappingURL=sidecar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar.d.ts","sourceRoot":"","sources":["../../src/screen/sidecar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAIV,QAAQ,EAET,MAAM,oBAAoB,CAAC;AAE5B;;oEAEoE;AACpE,MAAM,WAAW,qBAAqB;IACpC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;4CACwC;IACxC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACnC;AAID;;;wDAGwD;AACxD,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,YAAY,GAAG,oBAAoB,CAAC;gBACrE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC;CAM1E;AAED;0DAC0D;AAC1D,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEnD;AAoCD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,QAAQ,CA+C7E"}
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ /**
3
+ * SC-2 — the sidecar `Detector` adapter (spec-sc2 §3; shared with CS-1 D6).
4
+ *
5
+ * A `Detector` of the reserved `kind: "sidecar"` that adapts a CUSTOMER-RUN de-id
6
+ * container (GLiNER Guard / GLiNER2-PII / Presidio) over a deliberately tiny HTTP
7
+ * contract. ModelRig ships the ADAPTER and the CONTRACT only — never a model,
8
+ * never re-hosted weights (research §6a.3). The customer stands up their own
9
+ * service and owns its weights/license.
10
+ *
11
+ * THE CONTRACT (content-out only to the customer's own service):
12
+ * POST <endpoint> { text, source, tier } → the segment to scan
13
+ * 200 { spans: [{ start, end, type, confidence }] } → spans ONLY, never
14
+ * the matched text back
15
+ * The receipt id is `sidecar:<digest>` — the customer's local image/config
16
+ * digest label; ModelRig stores the label, not the model.
17
+ *
18
+ * FAIL-CLOSED (spec-sc2 §3, inherits SC-1 §4.3 via the shipped screen seam):
19
+ * on unreachable / timeout / malformed response the adapter THROWS a typed
20
+ * {@link SidecarUnavailableError}. The shipped seam (screen.ts) catches a
21
+ * detector throw and — under `enforce` — fails the run PRE-DISPATCH
22
+ * (invariant_violation, no request sent), which is exactly the required posture
23
+ * on a `phi` route where the sidecar is a load requirement (§4); under `detect`
24
+ * it degrades to the deterministic tier with `degraded: true`. We compose over
25
+ * that engine — we do NOT fork it.
26
+ *
27
+ * LEAK INVARIANT (spec-sc2 §1.4, §8b): the sidecar's REQUEST body, its RESPONSE
28
+ * body, and the span offsets NEVER reach a receipt / log / telemetry / OTLP span
29
+ * / failure message. Only per-`type` COUNTS survive (the seam rolls spans into
30
+ * counts). The thrown error carries the detector id + a failure CLASS only —
31
+ * never the endpoint's response text or the scanned content.
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.SidecarUnavailableError = void 0;
35
+ exports.sidecarReceiptId = sidecarReceiptId;
36
+ exports.createSidecarDetector = createSidecarDetector;
37
+ const DEFAULT_TIMEOUT_MS = 2000;
38
+ /** Thrown on any sidecar failure (unreachable / timeout / non-2xx / malformed).
39
+ * Its message is a failure CLASS + the detector id ONLY — never the response
40
+ * body or the scanned content (leak invariant §1.4). The shipped screen seam
41
+ * catches it to apply the fail-closed matrix (§4.3). */
42
+ class SidecarUnavailableError extends Error {
43
+ constructor(detectorId, reason) {
44
+ super(`sidecar detector "${detectorId}" is unavailable (${reason})`);
45
+ this.name = "SidecarUnavailableError";
46
+ this.detectorId = detectorId;
47
+ this.reason = reason;
48
+ }
49
+ }
50
+ exports.SidecarUnavailableError = SidecarUnavailableError;
51
+ /** Normalize an id to the `sidecar:<digest>` receipt shape. A caller may pass
52
+ * either the full `sidecar:<digest>` or a bare digest. */
53
+ function sidecarReceiptId(id) {
54
+ return id.startsWith("sidecar:") ? id : `sidecar:${id}`;
55
+ }
56
+ function isRecord(v) {
57
+ return typeof v === "object" && v !== null && !Array.isArray(v);
58
+ }
59
+ /** Read the response body into spans — DEFENSIVELY. Only `{start,end,type,
60
+ * confidence}` from each element is kept; every other field (and any echoed
61
+ * text) is discarded. A misshapen body is a malformed-response failure, never a
62
+ * silent open. */
63
+ function parseSpans(body, allow) {
64
+ if (!isRecord(body) || !Array.isArray(body.spans))
65
+ return [];
66
+ const spans = [];
67
+ for (const raw of body.spans) {
68
+ if (!isRecord(raw))
69
+ continue;
70
+ const { start, end, type, confidence } = raw;
71
+ if (typeof start !== "number" ||
72
+ typeof end !== "number" ||
73
+ typeof type !== "string" ||
74
+ typeof confidence !== "number" ||
75
+ start < 0 ||
76
+ end < start ||
77
+ confidence < 0 ||
78
+ confidence > 1 ||
79
+ type.length > 64) {
80
+ continue; // drop a malformed span; never trust the sidecar's bytes (T7)
81
+ }
82
+ if (allow !== null && !allow.has(type))
83
+ continue;
84
+ // ONLY these four fields — never any echoed text the sidecar may have added.
85
+ spans.push({ start, end, type, confidence });
86
+ }
87
+ return spans;
88
+ }
89
+ /**
90
+ * Build the sidecar `Detector`. `detect(input)` POSTs `{text, source, tier}` to
91
+ * the customer endpoint and returns spans; any failure THROWS
92
+ * {@link SidecarUnavailableError} so the shipped seam applies the fail-closed
93
+ * matrix. Never logs the request/response body.
94
+ */
95
+ function createSidecarDetector(config) {
96
+ const id = sidecarReceiptId(config.id);
97
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
98
+ const allow = config.types && config.types.length > 0 ? new Set(config.types) : null;
99
+ const doFetch = config.fetchImpl ?? fetch;
100
+ return {
101
+ id,
102
+ kind: "sidecar",
103
+ async detect(input) {
104
+ const payload = {
105
+ text: input.text,
106
+ source: input.source,
107
+ tier: input.tier,
108
+ };
109
+ const controller = new AbortController();
110
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
111
+ let res;
112
+ try {
113
+ res = await doFetch(config.endpoint, {
114
+ method: "POST",
115
+ headers: { "content-type": "application/json" },
116
+ body: JSON.stringify(payload),
117
+ signal: controller.signal,
118
+ });
119
+ }
120
+ catch (err) {
121
+ // An abort is a timeout; anything else is a transport failure. NEVER let
122
+ // the caught error's message (which could echo the payload) escape —
123
+ // only the coarse reason travels.
124
+ const reason = err instanceof Error && err.name === "AbortError" ? "timeout" : "unreachable";
125
+ throw new SidecarUnavailableError(id, reason);
126
+ }
127
+ finally {
128
+ clearTimeout(timer);
129
+ }
130
+ if (!res.ok)
131
+ throw new SidecarUnavailableError(id, "http_error");
132
+ let body;
133
+ try {
134
+ body = await res.json();
135
+ }
136
+ catch {
137
+ throw new SidecarUnavailableError(id, "malformed_response");
138
+ }
139
+ if (!isRecord(body) || !Array.isArray(body.spans)) {
140
+ throw new SidecarUnavailableError(id, "malformed_response");
141
+ }
142
+ return parseSpans(body, allow);
143
+ },
144
+ };
145
+ }
@@ -5,7 +5,7 @@
5
5
  * (packages/modelrig/src/detect/detector.ts) — mask (CS-1) and scrub v1 are the other
6
6
  * two. Load-bearing invariant: counts by type × source, never a matched value.
7
7
  */
8
- import type { ContentSource, DetectionType, TrustTier } from "../detect/detector";
8
+ import type { ContentSource, Detector, DetectionType, TrustTier } from "../detect/detector";
9
9
  /** Per-source policy action when a detector's confidence ≥ threshold (spec §4).
10
10
  * flag — receipt only, dispatch unchanged
11
11
  * strip — remove the flagged segment, keep the rest, dispatch
@@ -14,6 +14,21 @@ import type { ContentSource, DetectionType, TrustTier } from "../detect/detector
14
14
  * providers without parity, A7) and mark the step quarantined */
15
15
  export type ScreenAction = "flag" | "strip" | "block" | "quarantine";
16
16
  export type ScreenMode = "off" | "detect" | "enforce";
17
+ /** SC-2 sidecar config (spec-sc2 §3). When present, the resolver builds a
18
+ * `sidecar:<digest>` detector that POSTs `{text,source,tier}` to the customer's
19
+ * own de-id endpoint and reads spans back — never a bundled model, never a
20
+ * re-hosted weight. The endpoint receives content; the receipt gets counts only.
21
+ * The `phi` posture gate (§4) reads the presence of this detector. */
22
+ export interface ScreenSidecarConfig {
23
+ /** The customer's image/config digest — the receipt id is `sidecar:<digest>`. */
24
+ readonly digest: string;
25
+ /** The customer-run endpoint. Content is POSTed here and nowhere else. */
26
+ readonly endpoint: string;
27
+ /** Per-call timeout in ms (default 2000); a hung sidecar fails closed. */
28
+ readonly timeout_ms?: number;
29
+ /** Optional allowlist of span `type` labels the adapter keeps. */
30
+ readonly types?: readonly string[];
31
+ }
17
32
  /** The `screen:` block of a route bundle (spec §4). Absent ⇒ screening off and
18
33
  * byte-identical behaviour. */
19
34
  export interface ScreenPolicy {
@@ -22,12 +37,16 @@ export interface ScreenPolicy {
22
37
  * decide slot placement (untrusted never occupies the system slot, spec §2). */
23
38
  readonly sources?: Partial<Record<ContentSource, TrustTier>>;
24
39
  /** Detector ids to run (registry keys). T0 patterns always; T1 classifier
25
- * only when the optional peer dep + model file are present (A3). */
40
+ * only when the optional peer dep + model file are present (A3). SC-2 adds the
41
+ * deterministic Safe Harbor ids (`pii:<slug>@v1` / the `safe-harbor@v1` pack)
42
+ * and the `sidecar:<digest>` id (configured via {@link sidecar}). */
26
43
  readonly detectors?: readonly string[];
27
44
  /** Action per tier when risk ≥ threshold. */
28
45
  readonly on?: Partial<Record<TrustTier, ScreenAction>>;
29
46
  readonly threshold?: number;
30
47
  readonly output?: ScreenOutputPolicy;
48
+ /** SC-2 (spec-sc2 §3) — the customer-run de-id sidecar, when configured. */
49
+ readonly sidecar?: ScreenSidecarConfig;
31
50
  }
32
51
  /** Deterministic egress/output hygiene (spec §2.3 controls). */
33
52
  export interface ScreenOutputPolicy {
@@ -68,4 +87,26 @@ export interface ScreenReceipt {
68
87
  * degraded to T0 (spec §5 fail-closed matrix) — additive. */
69
88
  readonly degraded?: boolean;
70
89
  }
90
+ /** The identity of a detector as it appears on a receipt — id + version, never
91
+ * a model file path or weights digest (those are the customer's local truth). */
92
+ export interface DetectorIdentity {
93
+ readonly id: string;
94
+ readonly version: string;
95
+ }
96
+ /** A `screen:` policy resolved at ROUTE LOAD (mirrors {@link
97
+ * import("../mask/types").ResolvedMaskPolicy}): the parsed policy, the BUILT
98
+ * detector instances (T0 always, T1 iff its optional peer + model file were
99
+ * present), the identities for the receipt, a stable `policyHash`, and a
100
+ * `degraded` flag set when a CONFIGURED classifier could not be built at load
101
+ * (spec §4.3 — degrade to T0, never a silent block). Absent/`mode: off` ⇒ no
102
+ * resolved policy ⇒ byte-identical (the golden test). */
103
+ export interface ResolvedScreenPolicy {
104
+ readonly policy: ScreenPolicy;
105
+ readonly detectors: readonly Detector[];
106
+ readonly detectorMeta: readonly DetectorIdentity[];
107
+ readonly policyHash: string;
108
+ /** A configured optional detector was unavailable at load; screening runs on
109
+ * T0 alone and every receipt carries `degraded: true` (spec §4.3). */
110
+ readonly degraded: boolean;
111
+ }
71
112
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/screen/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAElF;;;;;+EAK+E;AAC/E,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,CAAC;AAErE,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEtD;+BAC+B;AAC/B,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B;oFACgF;IAChF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;IAC7D;wEACoE;IACpE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;IACvD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;CACtC;AAED,gEAAgE;AAChE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;CAC1C;AAED,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;gDACgD;AAChD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE5D;;WAEW;AACX,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjF,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC;IACvC,gEAAgE;IAChE,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,gEAAgE;IAChE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC;2CACuC;IACvC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC;iEAC6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/screen/types.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,7 @@
11
11
  * so the resolver stays fail-open (a settings read must never take a run down).
12
12
  */
13
13
  import type { RigConfig } from "../config";
14
- import { type TaskSettingsFetcher, type TaskSettingsResolver } from "./task-settings";
14
+ import { type OrgPostureFetcher, type TaskSettingsFetcher, type TaskSettingsResolver } from "./task-settings";
15
15
  export interface ApiTaskSettingsFetcherConfig {
16
16
  /** Base URL of the hosted control plane, e.g. https://api.modelrig.ai — the
17
17
  * SAME `ingestUrl` the api sink uses (the settings path hangs off the host). */
@@ -25,10 +25,21 @@ export interface ApiTaskSettingsFetcherConfig {
25
25
  }
26
26
  /**
27
27
  * Build the api-mode fetcher. `fetch(task)` GETs `/v1/tasks/:task/settings` and
28
- * returns the parsed settings, or null on ANY non-200, network error, or
29
- * malformed body — the resolver reads null as "leave the cache as it is".
28
+ * returns the parsed result (settings + org id), or null on ANY non-200, network
29
+ * error, or malformed body — the resolver reads null as "leave the cache as it is".
30
30
  */
31
31
  export declare function createApiTaskSettingsFetcher(config: ApiTaskSettingsFetcherConfig): TaskSettingsFetcher;
32
+ /**
33
+ * Build the api-mode ORG-posture fetcher (P15 CP-3). `fetch(orgId)` GETs
34
+ * `GET /v1/orgs/:org/settings` (any `rig_sk_` key of the caller's own org — no
35
+ * scope required) and returns the `artifact_posture`, or null on ANY non-200,
36
+ * network error, or malformed body. The org id comes from the task channel's
37
+ * echoed `org_id` — one control-plane channel, one credential (T8). Mirrors the
38
+ * task fetcher's leak discipline: Bearer key in a header, never the URL/body in
39
+ * an error, never a throw for a network/HTTP failure (the resolver reads null as
40
+ * "leave the last-known posture" — fail-open; the server ingest gate backstops).
41
+ */
42
+ export declare function createApiOrgPostureFetcher(config: ApiTaskSettingsFetcherConfig): OrgPostureFetcher;
32
43
  /**
33
44
  * Construct AND start the api-mode settings resolver from a rig config, or null
34
45
  * when the rig is not in api mode (local-only/direct keep env/YAML). Folds the
@@ -1 +1 @@
1
- {"version":3,"file":"api-fetcher.d.ts","sourceRoot":"","sources":["../../src/settings/api-fetcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,WAAW,CAAC;AACtD,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EAC1B,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,4BAA4B;IAC3C;oFACgF;IAChF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;2BAEuB;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAwBD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,4BAA4B,GACnC,mBAAmB,CAsBrB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,iBAAiB,EAAE,MAAM,GACxB,oBAAoB,GAAG,IAAI,CAW7B"}
1
+ {"version":3,"file":"api-fetcher.d.ts","sourceRoot":"","sources":["../../src/settings/api-fetcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,WAAW,CAAC;AACtD,OAAO,EAGL,KAAK,iBAAiB,EAEtB,KAAK,mBAAmB,EAExB,KAAK,oBAAoB,EAC1B,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,4BAA4B;IAC3C;oFACgF;IAChF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;2BAEuB;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAyCD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,4BAA4B,GACnC,mBAAmB,CAsBrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,4BAA4B,GACnC,iBAAiB,CAoBnB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,iBAAiB,EAAE,MAAM,GACxB,oBAAoB,GAAG,IAAI,CAW7B"}