theorum 0.1.15 → 1.1.3

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 (305) hide show
  1. package/README.md +241 -98
  2. package/esm/mod.d.ts +57 -28
  3. package/esm/mod.js +43 -23
  4. package/esm/src/cli/commands/bench.js +18 -18
  5. package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
  6. package/esm/src/cli/commands/fuzz-canary.js +191 -0
  7. package/esm/src/cli/commands/fuzz-guardrails.d.ts +3 -5
  8. package/esm/src/cli/commands/fuzz-guardrails.js +4 -581
  9. package/esm/src/cli/commands/guardrails-eval.d.ts +14 -0
  10. package/esm/src/cli/commands/guardrails-eval.js +15 -0
  11. package/esm/src/cli/commands/profile.js +35 -15
  12. package/esm/src/cli/commands/run.d.ts +3 -0
  13. package/esm/src/cli/commands/run.js +23 -32
  14. package/esm/src/cli/commands/test.d.ts +10 -1
  15. package/esm/src/cli/commands/test.js +34 -34
  16. package/esm/src/cli/event-log.d.ts +19 -0
  17. package/esm/src/cli/event-log.js +147 -0
  18. package/esm/src/cli/index.js +57 -11
  19. package/esm/src/cli/matrix/synthesizer.d.ts +10 -12
  20. package/esm/src/cli/matrix/synthesizer.js +45 -118
  21. package/esm/src/guardrails/canary-gate.d.ts +21 -0
  22. package/esm/src/guardrails/canary-gate.js +32 -0
  23. package/esm/src/guardrails/canary.d.ts +34 -0
  24. package/esm/src/guardrails/canary.js +150 -0
  25. package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +17 -0
  26. package/esm/src/guardrails/corpus/canary-egress-attacks.js +151 -0
  27. package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +11 -0
  28. package/esm/src/guardrails/corpus/fuzz-inbound.js +213 -0
  29. package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
  30. package/esm/src/guardrails/corpus/inbound-payloads.js +125 -0
  31. package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
  32. package/esm/src/guardrails/corpus/live-attacks.js +231 -0
  33. package/esm/src/guardrails/corpus/mod.d.ts +14 -0
  34. package/esm/src/guardrails/corpus/mod.js +11 -0
  35. package/esm/src/guardrails/corpus/secrets.d.ts +17 -0
  36. package/esm/src/guardrails/corpus/secrets.js +17 -0
  37. package/esm/src/guardrails/corpus/strings.d.ts +28 -0
  38. package/esm/src/guardrails/corpus/strings.js +34 -0
  39. package/esm/src/guardrails/corpus/types.d.ts +38 -0
  40. package/esm/src/guardrails/corpus/types.js +6 -0
  41. package/esm/src/guardrails/egress.d.ts +32 -0
  42. package/esm/src/guardrails/egress.js +87 -0
  43. package/esm/src/guardrails/error.d.ts +14 -23
  44. package/esm/src/guardrails/error.js +87 -76
  45. package/esm/src/guardrails/eval/corpus.d.ts +108 -0
  46. package/esm/src/guardrails/eval/corpus.js +978 -0
  47. package/esm/src/guardrails/eval/mod.d.ts +51 -0
  48. package/esm/src/guardrails/eval/mod.js +133 -0
  49. package/esm/src/guardrails/eval/score.d.ts +66 -0
  50. package/esm/src/guardrails/eval/score.js +114 -0
  51. package/esm/src/guardrails/events.d.ts +25 -0
  52. package/esm/src/guardrails/events.js +56 -0
  53. package/esm/src/guardrails/hits.d.ts +24 -0
  54. package/esm/src/guardrails/hits.js +45 -0
  55. package/esm/src/guardrails/injection.js +28 -5
  56. package/esm/src/guardrails/lexicon.d.ts +39 -0
  57. package/esm/src/guardrails/lexicon.js +200 -0
  58. package/esm/src/guardrails/live-outbound-gate.d.ts +41 -0
  59. package/esm/src/guardrails/live-outbound-gate.js +222 -0
  60. package/esm/src/guardrails/mod.d.ts +30 -6
  61. package/esm/src/guardrails/mod.js +20 -5
  62. package/esm/src/guardrails/network.d.ts +19 -0
  63. package/esm/src/guardrails/network.js +234 -0
  64. package/esm/src/guardrails/policy.d.ts +35 -0
  65. package/esm/src/guardrails/policy.js +50 -0
  66. package/esm/src/guardrails/progressive-yield.d.ts +51 -0
  67. package/esm/src/guardrails/progressive-yield.js +98 -0
  68. package/esm/src/guardrails/quota.d.ts +17 -3
  69. package/esm/src/guardrails/quota.js +18 -4
  70. package/esm/src/guardrails/sanitize.d.ts +45 -19
  71. package/esm/src/guardrails/sanitize.js +177 -94
  72. package/esm/src/guardrails/sensitive.js +2 -1
  73. package/esm/src/guardrails/serialize.d.ts +35 -0
  74. package/esm/src/guardrails/serialize.js +58 -0
  75. package/esm/src/guardrails/testing.d.ts +17 -0
  76. package/esm/src/guardrails/testing.js +13 -0
  77. package/esm/src/guardrails/theorum-error.d.ts +12 -0
  78. package/esm/src/guardrails/theorum-error.js +15 -0
  79. package/esm/src/guardrails/tool-directives.d.ts +48 -0
  80. package/esm/src/guardrails/tool-directives.js +124 -0
  81. package/esm/src/guardrails/tool-result.d.ts +93 -0
  82. package/esm/src/guardrails/tool-result.js +276 -0
  83. package/esm/src/guardrails/types.d.ts +291 -0
  84. package/esm/src/guardrails/types.js +72 -0
  85. package/esm/src/host/client-turn.d.ts +19 -0
  86. package/esm/src/host/client-turn.js +36 -0
  87. package/esm/src/host/mint-trace.d.ts +1 -1
  88. package/esm/src/host/mod.d.ts +5 -3
  89. package/esm/src/host/mod.js +4 -3
  90. package/esm/src/kernel/auth/crypto.d.ts +42 -0
  91. package/esm/src/kernel/auth/crypto.js +106 -0
  92. package/esm/src/kernel/auth/mod.d.ts +11 -0
  93. package/esm/src/kernel/auth/mod.js +11 -0
  94. package/esm/src/kernel/auth/oauth.d.ts +47 -0
  95. package/esm/src/kernel/auth/oauth.js +278 -0
  96. package/esm/src/kernel/auth/types.d.ts +133 -0
  97. package/esm/src/kernel/auth/types.js +13 -0
  98. package/esm/src/kernel/engine/delta.d.ts +24 -2
  99. package/esm/src/kernel/engine/delta.js +478 -39
  100. package/esm/src/kernel/engine/live-inbound.d.ts +21 -0
  101. package/esm/src/kernel/engine/live-inbound.js +31 -0
  102. package/esm/src/kernel/engine/live-ingress.d.ts +19 -0
  103. package/esm/src/kernel/engine/live-ingress.js +47 -0
  104. package/esm/src/kernel/engine/repair.js +13 -12
  105. package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
  106. package/esm/src/kernel/engine/runner/gates.js +130 -43
  107. package/esm/src/kernel/engine/runner/mod.d.ts +6 -4
  108. package/esm/src/kernel/engine/runner/mod.js +192 -53
  109. package/esm/src/kernel/engine/runner/schema-validation.js +3 -3
  110. package/esm/src/kernel/engine/runner/stages.d.ts +39 -0
  111. package/esm/src/kernel/engine/runner/stages.js +89 -0
  112. package/esm/src/kernel/engine/runner/state.d.ts +31 -0
  113. package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
  114. package/esm/src/kernel/engine/runner/steps.js +244 -43
  115. package/esm/src/kernel/engine/runner/stream.d.ts +9 -3
  116. package/esm/src/kernel/engine/runner/stream.js +140 -44
  117. package/esm/src/kernel/engine/session/mod.d.ts +25 -0
  118. package/esm/src/kernel/engine/session/mod.js +557 -0
  119. package/esm/src/kernel/interaction-parts.d.ts +14 -0
  120. package/esm/src/kernel/interaction-parts.js +23 -0
  121. package/esm/src/kernel/mod.d.ts +21 -10
  122. package/esm/src/kernel/mod.js +11 -8
  123. package/esm/src/kernel/profile-graph.d.ts +159 -0
  124. package/esm/src/kernel/profile-graph.js +156 -0
  125. package/esm/src/kernel/registry/attachments.d.ts +12 -10
  126. package/esm/src/kernel/registry/attachments.js +33 -27
  127. package/esm/src/kernel/registry/catalog.d.ts +25 -24
  128. package/esm/src/kernel/registry/catalog.js +60 -101
  129. package/esm/src/kernel/registry/ingress.d.ts +9 -4
  130. package/esm/src/kernel/registry/ingress.js +97 -75
  131. package/esm/src/kernel/registry/profile-outputs.d.ts +4 -0
  132. package/esm/src/kernel/registry/profile-outputs.js +8 -0
  133. package/esm/src/kernel/registry/profiles.d.ts +55 -12
  134. package/esm/src/kernel/registry/profiles.js +413 -73
  135. package/esm/src/kernel/registry/provider-request.js +13 -7
  136. package/esm/src/kernel/registry/resolve.d.ts +8 -8
  137. package/esm/src/kernel/registry/resolve.js +169 -154
  138. package/esm/src/kernel/registry/schemas.js +1 -1
  139. package/esm/src/kernel/registry/sole-model.d.ts +8 -0
  140. package/esm/src/kernel/registry/sole-model.js +10 -0
  141. package/esm/src/kernel/registry/system-prompt.d.ts +10 -0
  142. package/esm/src/kernel/registry/system-prompt.js +40 -0
  143. package/esm/src/kernel/registry/system-role.d.ts +8 -0
  144. package/esm/src/kernel/registry/system-role.js +14 -0
  145. package/esm/src/kernel/registry/vault.d.ts +12 -7
  146. package/esm/src/kernel/registry/vault.js +32 -10
  147. package/esm/src/kernel/schema.d.ts +231 -0
  148. package/esm/src/kernel/schema.js +607 -0
  149. package/esm/src/kernel/stages.d.ts +175 -0
  150. package/esm/src/kernel/stages.js +476 -0
  151. package/esm/src/kernel/stop.d.ts +78 -19
  152. package/esm/src/kernel/stop.js +51 -16
  153. package/esm/src/kernel/tools/events.d.ts +41 -0
  154. package/esm/src/kernel/tools/events.js +71 -0
  155. package/esm/src/kernel/tools/execute.d.ts +84 -0
  156. package/esm/src/kernel/tools/execute.js +614 -0
  157. package/esm/src/kernel/tools/harness.d.ts +8 -0
  158. package/esm/src/kernel/tools/harness.js +46 -0
  159. package/esm/src/kernel/tools/invoke.d.ts +10 -0
  160. package/esm/src/kernel/tools/invoke.js +101 -0
  161. package/esm/src/kernel/tools/mod.d.ts +13 -0
  162. package/esm/src/kernel/tools/mod.js +11 -0
  163. package/esm/src/kernel/tools/permission.d.ts +15 -0
  164. package/esm/src/kernel/tools/permission.js +47 -0
  165. package/esm/src/kernel/tools/project.d.ts +12 -0
  166. package/esm/src/kernel/tools/project.js +36 -0
  167. package/esm/src/kernel/tools/registry.d.ts +23 -0
  168. package/esm/src/kernel/tools/registry.js +81 -0
  169. package/esm/src/kernel/tools/remote.d.ts +94 -0
  170. package/esm/src/kernel/tools/remote.js +577 -0
  171. package/esm/src/kernel/tools/resolve.d.ts +39 -0
  172. package/esm/src/kernel/tools/resolve.js +283 -0
  173. package/esm/src/kernel/tools/schema.d.ts +15 -0
  174. package/esm/src/kernel/tools/schema.js +176 -0
  175. package/esm/src/kernel/tools/stage-run.d.ts +105 -0
  176. package/esm/src/kernel/tools/stage-run.js +155 -0
  177. package/esm/src/kernel/tools/types.d.ts +394 -0
  178. package/esm/src/kernel/tools/types.js +9 -0
  179. package/esm/src/kernel/types.d.ts +540 -256
  180. package/esm/src/kernel/util/find-last.d.ts +2 -0
  181. package/esm/src/kernel/util/find-last.js +10 -0
  182. package/esm/src/observability/destinations.d.ts +31 -0
  183. package/esm/src/observability/destinations.js +67 -0
  184. package/esm/src/observability/mod.d.ts +10 -3
  185. package/esm/src/observability/mod.js +6 -2
  186. package/esm/src/observability/policy.d.ts +27 -0
  187. package/esm/src/observability/policy.js +80 -0
  188. package/esm/src/observability/resolve-policy.d.ts +16 -0
  189. package/esm/src/observability/resolve-policy.js +64 -0
  190. package/esm/src/observability/trace-attach.d.ts +8 -4
  191. package/esm/src/observability/trace-attach.js +50 -29
  192. package/esm/src/observability/trace-record.d.ts +23 -13
  193. package/esm/src/observability/trace-record.js +96 -39
  194. package/esm/src/observability/trace-sink.d.ts +19 -0
  195. package/esm/src/observability/trace-sink.js +10 -0
  196. package/esm/src/observability/trace-usage.d.ts +10 -3
  197. package/esm/src/observability/trace-usage.js +70 -17
  198. package/esm/src/observability/trace.d.ts +18 -7
  199. package/esm/src/observability/trace.js +34 -17
  200. package/esm/src/observability/types.d.ts +113 -0
  201. package/esm/src/observability/types.js +11 -0
  202. package/esm/src/presets/google/speech-voices.d.ts +11 -0
  203. package/esm/src/presets/google/speech-voices.js +41 -0
  204. package/esm/src/presets/google.d.ts +36 -24
  205. package/esm/src/presets/google.js +50 -63
  206. package/esm/src/presets/mod.d.ts +2 -2
  207. package/esm/src/presets/mod.js +1 -1
  208. package/esm/src/providers/create-provider.d.ts +20 -17
  209. package/esm/src/providers/create-provider.js +72 -26
  210. package/esm/src/providers/google/interactions/framing.d.ts +23 -0
  211. package/esm/src/providers/google/interactions/framing.js +269 -0
  212. package/esm/src/providers/google/interactions/mod.d.ts +7 -0
  213. package/esm/src/providers/google/interactions/mod.js +7 -0
  214. package/esm/src/providers/google/interactions/stream.d.ts +83 -0
  215. package/esm/src/providers/google/interactions/stream.js +588 -0
  216. package/esm/src/providers/google/keys.d.ts +26 -0
  217. package/esm/src/providers/{keys.js → google/keys.js} +19 -31
  218. package/esm/src/providers/google/live/framing.d.ts +49 -0
  219. package/esm/src/providers/google/live/framing.js +552 -0
  220. package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
  221. package/esm/src/providers/google/live/openapi-schema.js +46 -0
  222. package/esm/src/providers/google/live/session.d.ts +25 -0
  223. package/esm/src/providers/google/live/session.js +134 -0
  224. package/esm/src/providers/google/live/stream.d.ts +45 -0
  225. package/esm/src/providers/google/live/stream.js +214 -0
  226. package/esm/src/providers/google/urls.d.ts +6 -0
  227. package/esm/src/providers/google/urls.js +6 -0
  228. package/esm/src/providers/local/local.d.ts +30 -0
  229. package/esm/src/providers/{local.js → local/local.js} +66 -126
  230. package/esm/src/providers/local/mod.d.ts +9 -0
  231. package/esm/src/providers/local/mod.js +9 -0
  232. package/esm/src/providers/mod.d.ts +6 -3
  233. package/esm/src/providers/mod.js +3 -1
  234. package/esm/src/providers/openrouter/cache-control.d.ts +24 -0
  235. package/esm/src/providers/openrouter/cache-control.js +23 -0
  236. package/esm/src/providers/openrouter/chat.d.ts +107 -0
  237. package/esm/src/providers/{openrouter.js → openrouter/chat.js} +117 -231
  238. package/esm/src/providers/openrouter/image.d.ts +34 -0
  239. package/esm/src/providers/openrouter/image.js +275 -0
  240. package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
  241. package/esm/src/providers/openrouter/openai/chat-payload.js +82 -0
  242. package/esm/src/providers/openrouter/openai/compat.d.ts +53 -0
  243. package/esm/src/providers/openrouter/openai/compat.js +213 -0
  244. package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
  245. package/esm/src/providers/openrouter/openai/image-payload.js +90 -0
  246. package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
  247. package/esm/src/providers/openrouter/openai/sdk-messages.js +122 -0
  248. package/esm/src/providers/openrouter/resolve-api-key.d.ts +9 -0
  249. package/esm/src/providers/openrouter/resolve-api-key.js +24 -0
  250. package/esm/src/providers/openrouter/speech.d.ts +23 -0
  251. package/esm/src/providers/{speech.js → openrouter/speech.js} +32 -55
  252. package/esm/src/providers/probe.d.ts +1 -0
  253. package/esm/src/providers/probe.js +22 -0
  254. package/esm/src/providers/shared/pcm.d.ts +12 -0
  255. package/esm/src/providers/{pcm.js → shared/pcm.js} +16 -3
  256. package/esm/src/providers/shared/sse.d.ts +18 -0
  257. package/esm/src/providers/shared/sse.js +87 -0
  258. package/esm/src/providers/shared/tool-args.d.ts +17 -0
  259. package/esm/src/providers/shared/tool-args.js +45 -0
  260. package/esm/src/providers/shared/upstream-tap.d.ts +5 -0
  261. package/esm/src/providers/{google-tap.js → shared/upstream-tap.js} +4 -7
  262. package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
  263. package/esm/src/providers/{gemini-tape.js → shared/upstream-tape.js} +12 -22
  264. package/esm/src/providers/types.d.ts +27 -0
  265. package/esm/src/providers/types.js +1 -0
  266. package/package.json +11 -7
  267. package/docs/cli.md +0 -97
  268. package/docs/guardrails.md +0 -178
  269. package/docs/host.md +0 -97
  270. package/docs/kernel.md +0 -404
  271. package/docs/observability.md +0 -105
  272. package/docs/openrouter.md +0 -125
  273. package/docs/presets-google.md +0 -91
  274. package/docs/presets.md +0 -88
  275. package/docs/providers.md +0 -202
  276. package/docs/streaming.md +0 -96
  277. package/esm/src/kernel/engine/boundary.d.ts +0 -10
  278. package/esm/src/kernel/engine/boundary.js +0 -55
  279. package/esm/src/kernel/engine/runner/tools.d.ts +0 -13
  280. package/esm/src/kernel/engine/runner/tools.js +0 -198
  281. package/esm/src/kernel/registry/tools.d.ts +0 -12
  282. package/esm/src/kernel/registry/tools.js +0 -36
  283. package/esm/src/providers/expose-for-tests.d.ts +0 -1
  284. package/esm/src/providers/expose-for-tests.js +0 -25
  285. package/esm/src/providers/gemini-tape.d.ts +0 -2
  286. package/esm/src/providers/google-tap.d.ts +0 -3
  287. package/esm/src/providers/interactions.d.ts +0 -5
  288. package/esm/src/providers/interactions.js +0 -169
  289. package/esm/src/providers/keys.d.ts +0 -19
  290. package/esm/src/providers/local.d.ts +0 -29
  291. package/esm/src/providers/openrouter-mod.d.ts +0 -13
  292. package/esm/src/providers/openrouter-mod.js +0 -12
  293. package/esm/src/providers/openrouter-payload.d.ts +0 -39
  294. package/esm/src/providers/openrouter-payload.js +0 -195
  295. package/esm/src/providers/openrouter.d.ts +0 -15
  296. package/esm/src/providers/pcm.d.ts +0 -7
  297. package/esm/src/providers/provider.d.ts +0 -15
  298. package/esm/src/providers/provider.js +0 -202
  299. package/esm/src/providers/speech.d.ts +0 -23
  300. package/esm/src/providers/sse.d.ts +0 -7
  301. package/esm/src/providers/sse.js +0 -55
  302. package/esm/src/streaming/mod.d.ts +0 -9
  303. package/esm/src/streaming/mod.js +0 -8
  304. /package/esm/src/{streaming → host}/readStreamingJsonStringField.d.ts +0 -0
  305. /package/esm/src/{streaming → host}/readStreamingJsonStringField.js +0 -0
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Guardrail vocabulary — trust levels, stages, verdicts, and profile policy shape.
3
+ *
4
+ * This module is the single source of truth for guardrail types. It must not import
5
+ * from `src/kernel/`: the kernel type-imports `ProfileGuardrailsSpec` for
6
+ * `ProfileCommon.guardrails`, and that edge stays one-directional. Implementation
7
+ * modules under `src/guardrails/` may import kernel types freely.
8
+ *
9
+ * @module
10
+ */
11
+ /**
12
+ * Origin trust for text entering the model's context.
13
+ *
14
+ * - `trusted` — author-time profile text (`identity.system`). Sensitive redaction
15
+ * only; injection redaction would mangle the host's own instructions.
16
+ * - `assembled` — host-built per turn (`req.system`). Interpolates retrieval and
17
+ * user data, so it is permeable and takes full detection.
18
+ * - `untrusted` — user input, tool results, attachments, delegated agents.
19
+ */
20
+ export declare const TRUST_LEVELS: readonly ["trusted", "assembled", "untrusted"];
21
+ export type TrustLevel = (typeof TRUST_LEVELS)[number];
22
+ /** Boundary a guardrail check runs at. */
23
+ export declare const GUARDRAIL_STAGES: readonly ["input", "history", "system", "attachment", "tool_call", "tool_result", "output_delta", "output_final", "network", "live_inbound", "live_outbound", "trace"];
24
+ export type GuardrailStage = (typeof GUARDRAIL_STAGES)[number];
25
+ /** How serious a hit is. Does not decide what happens next — that is `onBlock`. */
26
+ export declare const SEVERITIES: readonly ["info", "low", "medium", "high"];
27
+ export type Severity = (typeof SEVERITIES)[number];
28
+ /** Egress block handling. */
29
+ export declare const EGRESS_ON_BLOCK: readonly ["reject_to_agent", "refuse_to_user"];
30
+ export type EgressOnBlock = (typeof EGRESS_ON_BLOCK)[number];
31
+ /** One detector match. */
32
+ export interface GuardrailHit {
33
+ /** Stable rule id, e.g. `injection.instruction-override`. */
34
+ rule: string;
35
+ severity: Severity;
36
+ /** Offsets into the inspected text; absent for whole-payload checks. */
37
+ span?: {
38
+ start: number;
39
+ end: number;
40
+ };
41
+ /**
42
+ * Exact matched substring (capped). Present when detectors had the source text.
43
+ * Stripped from host/trace unless `observability.include.guardrailMatchPreview`.
44
+ */
45
+ match?: string;
46
+ }
47
+ /**
48
+ * Outcome of one guardrail evaluation.
49
+ *
50
+ * A discriminated union so a new variant fails every unhandled `switch` at
51
+ * compile time rather than falling through at runtime.
52
+ */
53
+ export type Verdict = {
54
+ action: 'allow';
55
+ } | {
56
+ action: 'redact';
57
+ text: string;
58
+ hits: GuardrailHit[];
59
+ } | {
60
+ action: 'flag';
61
+ hits: GuardrailHit[];
62
+ } | {
63
+ action: 'block';
64
+ hits: GuardrailHit[];
65
+ /** Sent to the model on a repair turn when `onBlock` is `reject_to_agent`. */
66
+ rejection: string;
67
+ /** Shown to the user when `onBlock` is `refuse_to_user`. Host-owned copy. */
68
+ refusal?: string;
69
+ };
70
+ export type GuardrailAction = Verdict['action'];
71
+ /**
72
+ * Where a tool result came from.
73
+ *
74
+ * `local` is host TypeScript the profile registered; `http` and `mcp` are remote
75
+ * services whose bytes the host does not control. `delegated` is another agent
76
+ * answering through the tool boundary — its output is model-generated prose that
77
+ * reads as authoritative, which is why depth is tracked separately.
78
+ */
79
+ export declare const TOOL_ORIGINS: readonly ["local", "builtin", "http", "mcp", "delegated"];
80
+ export type ToolOrigin = (typeof TOOL_ORIGINS)[number];
81
+ /** Where a piece of content entered the turn from. */
82
+ export interface Provenance {
83
+ origin: ToolOrigin;
84
+ /** Registered tool name. */
85
+ tool: string;
86
+ /**
87
+ * Hops from the user's turn. A direct tool call is 1; a tool result produced by
88
+ * a delegated agent that itself called tools is deeper. Depth matters because a
89
+ * two-hop delegation can otherwise launder remote content into trusted-looking
90
+ * output.
91
+ */
92
+ depth: number;
93
+ }
94
+ /**
95
+ * Untrusted content a turn has already taken into its context.
96
+ *
97
+ * Once a turn has read attacker-influenceable bytes, a later tool call is a
98
+ * confused-deputy risk: the content can ask the agent to act, and the agent has
99
+ * authority the content does not. Sources are kept in order so a policy can reason
100
+ * about depth as well as presence.
101
+ */
102
+ export interface TurnTaint {
103
+ sources: Provenance[];
104
+ /**
105
+ * Directive hits found in remote content this turn read.
106
+ *
107
+ * Separates "read something remote" from "read something that tried to steer
108
+ * me". The second is far rarer, so a gate keyed on it refuses far less
109
+ * legitimate work.
110
+ */
111
+ suspicious: GuardrailHit[];
112
+ }
113
+ /**
114
+ * What a turn may still do after it has ingested untrusted remote content.
115
+ *
116
+ * Enforcement is opt-in. Tracking and reporting are on by default — every
117
+ * remote read is observable — but refusing tool calls changes what working agents
118
+ * are allowed to do, so a host declares which access levels to gate rather than
119
+ * having the kernel guess.
120
+ */
121
+ /**
122
+ * How strongly the tool-ingress signals fired, derived from the hits themselves.
123
+ *
124
+ * Not a probability: there is no calibrated model behind it. `elevated` means one
125
+ * directive signal alongside an external destination; `high` means the content
126
+ * named a tool the model can call, or several signals agreed.
127
+ */
128
+ export declare const ADVISORY_LEVELS: readonly ["none", "elevated", "high"];
129
+ export type AdvisoryLevel = (typeof ADVISORY_LEVELS)[number];
130
+ export declare const TAINT_GATES: readonly ["off", "destructive", "write"];
131
+ export type TaintGate = (typeof TAINT_GATES)[number];
132
+ /**
133
+ * Only structural facts gate tool calls.
134
+ *
135
+ * Content signals from `tool-directives.ts` deliberately have no gate here. They
136
+ * are pattern matches with no measured precision, and refusing a tool call on an
137
+ * unpredictable signal makes an agent unreliable rather than safe — the failure is
138
+ * invisible to the user and looks like the agent being stupid. Those signals
139
+ * annotate the fence and raise telemetry; the model still gets to decide, and the
140
+ * host still gets to see.
141
+ */
142
+ export interface TaintGuardrailSpec {
143
+ /**
144
+ * Host copy appended to the fence when tool content looks directive.
145
+ *
146
+ * The kernel states what it observed; what the agent should *do* about it —
147
+ * ask the user, refuse, proceed carefully — is product behaviour and stays
148
+ * host-owned. Omitted means the observation is stated without guidance.
149
+ */
150
+ advisoryGuidance?: string;
151
+ /**
152
+ * Least-severe tool capability refused once the turn has read remote content.
153
+ *
154
+ * - `off` (default) — report only.
155
+ * - `destructive` — refuse hard-to-undo calls.
156
+ * - `write` — refuse those and any state-changing call.
157
+ *
158
+ * Stated as a capability threshold rather than a list of tool `access` values so
159
+ * the guardrail vocabulary stays independent of the tool registry; the kernel
160
+ * maps a tool's declared access onto it.
161
+ */
162
+ afterRemoteRead?: TaintGate;
163
+ }
164
+ /** Facts a check may read. Deliberately excludes the full profile. */
165
+ export interface GuardrailContext {
166
+ stage: GuardrailStage;
167
+ trust: TrustLevel;
168
+ profileId: string;
169
+ canary?: string;
170
+ role?: string;
171
+ slots?: Record<string, string>;
172
+ /** Set on tool-shaped stages; absent for user and system text. */
173
+ provenance?: Provenance;
174
+ }
175
+ /**
176
+ * One guardrail decision, as it reaches the host and the trace.
177
+ *
178
+ * Carries rule identity and offsets, never the matched content, so a trace sink
179
+ * can count and locate hits without becoming a second copy of the secret.
180
+ */
181
+ export interface GuardrailEvent {
182
+ stage: GuardrailStage;
183
+ trust: TrustLevel;
184
+ action: GuardrailAction;
185
+ hits: GuardrailHit[];
186
+ provenance?: Provenance;
187
+ }
188
+ /**
189
+ * User-visible output projected out of the turn's events.
190
+ *
191
+ * Structured output travels alongside text so a profile with `outputs.structured`
192
+ * is not invisible to its own egress policy.
193
+ */
194
+ export interface OutboundPayload {
195
+ /** Concatenated user-visible text for this attempt. */
196
+ text: string;
197
+ /** Structured output, when the profile emits it. */
198
+ structured?: unknown;
199
+ }
200
+ /** Evaluates candidate user-visible output before release. */
201
+ export type EgressEnforcer = (payload: OutboundPayload, context: GuardrailContext) => Verdict | Promise<Verdict>;
202
+ /** Profile egress policy for rejection, retry, or refusal behavior. */
203
+ export interface ProfileEgressSpec {
204
+ enforce: EgressEnforcer;
205
+ onBlock?: EgressOnBlock;
206
+ maxRetries?: number;
207
+ repairGuidance?: string;
208
+ }
209
+ /** SSRF and network access policy for HTTP and remote MCP tools. */
210
+ export interface NetworkGuardrailSpec {
211
+ /**
212
+ * When true, allows connections to localhost / loopback and private subnets
213
+ * (e.g. for local dev/testing). Default: false.
214
+ */
215
+ allowPrivateNetworks?: boolean;
216
+ /** Hostnames or IP addresses permitted regardless of private subnet status. */
217
+ allowedHosts?: string[];
218
+ /**
219
+ * Allowed URL schemes. Defaults to `['https']`, or `['http', 'https']` when
220
+ * `allowPrivateNetworks` is set.
221
+ */
222
+ allowedSchemes?: string[];
223
+ }
224
+ /** Optional daily turn quota consumed by host HTTP middleware. */
225
+ export interface QuotaGuardrailSpec {
226
+ perDay: number;
227
+ /**
228
+ * Host copy surfaced when the quota trips. The kernel never authors this:
229
+ * `quotaExhausted` returns structured data (`code`, `perDay`) and includes
230
+ * this string only when the host set it.
231
+ */
232
+ message?: string;
233
+ }
234
+ /**
235
+ * Per-turn canary switches. `true` / `false` toggles minting with the
236
+ * registered default bind note; the object form supplies host copy.
237
+ */
238
+ export interface CanaryGuardrailSpec {
239
+ /**
240
+ * Host template appended to the system prompt binding the canary. Must
241
+ * contain the `{canary}` placeholder; `bindCanary` refuses a note that lost
242
+ * the token. Omitted means the lexicon default (`canary.bind_note`).
243
+ */
244
+ bindNote?: string;
245
+ }
246
+ /** Profile guardrail switches enforced by the kernel. */
247
+ export interface ProfileGuardrailsSpec {
248
+ /** Optional daily turn quota; omitted means quota enforcement is not configured. */
249
+ quota?: QuotaGuardrailSpec;
250
+ canary?: boolean | CanaryGuardrailSpec;
251
+ sanitizeInput?: boolean;
252
+ redactSensitive?: boolean;
253
+ egress?: ProfileEgressSpec;
254
+ /** SSRF and network access policies for HTTP and MCP tools. */
255
+ network?: NetworkGuardrailSpec;
256
+ /** What the turn may still do after reading untrusted remote content. */
257
+ taint?: TaintGuardrailSpec;
258
+ }
259
+ /** The guardrail field names a `host` profile may set. */
260
+ export declare const HOST_GUARDRAIL_FIELDS: readonly ["sanitizeInput", "redactSensitive", "network", "taint"];
261
+ /**
262
+ * The guardrail switches a `host` profile may set.
263
+ *
264
+ * A host profile runs no model, so only the guards that fire on the `invokeTool`
265
+ * path exist for it: the detectors applied to model-supplied arguments and to
266
+ * tool result and failure text (`sanitizeInput`, `redactSensitive`), SSRF
267
+ * clearance for declarative HTTP and MCP targets (`network`), and the
268
+ * confused-deputy gate on a tainted turn (`taint`). Everything else in
269
+ * {@link ProfileGuardrailsSpec} — quota, canary, egress — guards a model turn
270
+ * and is refused by `defineProfile` on `type: 'host'`.
271
+ *
272
+ * This is a view of the one guardrail vocabulary, not a second hierarchy.
273
+ */
274
+ export type HostGuardrailsSpec = Pick<ProfileGuardrailsSpec, (typeof HOST_GUARDRAIL_FIELDS)[number]>;
275
+ /**
276
+ * A profile's guardrail switches with defaults applied.
277
+ *
278
+ * Every path resolves through `resolveGuardrailPolicy` so turn and Live ingress
279
+ * cannot drift apart on defaults.
280
+ */
281
+ export interface ResolvedGuardrailPolicy {
282
+ sanitizeInput: boolean;
283
+ redactSensitive: boolean;
284
+ canary: boolean;
285
+ /** Host bind-note template from `guardrails.canary.bindNote`, when set. */
286
+ canaryBindNote?: string;
287
+ egress?: ProfileEgressSpec;
288
+ network?: NetworkGuardrailSpec;
289
+ quota?: QuotaGuardrailSpec;
290
+ taint?: TaintGuardrailSpec;
291
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Guardrail vocabulary — trust levels, stages, verdicts, and profile policy shape.
3
+ *
4
+ * This module is the single source of truth for guardrail types. It must not import
5
+ * from `src/kernel/`: the kernel type-imports `ProfileGuardrailsSpec` for
6
+ * `ProfileCommon.guardrails`, and that edge stays one-directional. Implementation
7
+ * modules under `src/guardrails/` may import kernel types freely.
8
+ *
9
+ * @module
10
+ */
11
+ /**
12
+ * Origin trust for text entering the model's context.
13
+ *
14
+ * - `trusted` — author-time profile text (`identity.system`). Sensitive redaction
15
+ * only; injection redaction would mangle the host's own instructions.
16
+ * - `assembled` — host-built per turn (`req.system`). Interpolates retrieval and
17
+ * user data, so it is permeable and takes full detection.
18
+ * - `untrusted` — user input, tool results, attachments, delegated agents.
19
+ */
20
+ export const TRUST_LEVELS = ['trusted', 'assembled', 'untrusted'];
21
+ /** Boundary a guardrail check runs at. */
22
+ export const GUARDRAIL_STAGES = [
23
+ 'input',
24
+ 'history',
25
+ 'system',
26
+ 'attachment',
27
+ 'tool_call',
28
+ 'tool_result',
29
+ 'output_delta',
30
+ 'output_final',
31
+ 'network',
32
+ 'live_inbound',
33
+ 'live_outbound',
34
+ 'trace',
35
+ ];
36
+ /** How serious a hit is. Does not decide what happens next — that is `onBlock`. */
37
+ export const SEVERITIES = ['info', 'low', 'medium', 'high'];
38
+ /** Egress block handling. */
39
+ export const EGRESS_ON_BLOCK = ['reject_to_agent', 'refuse_to_user'];
40
+ /**
41
+ * Where a tool result came from.
42
+ *
43
+ * `local` is host TypeScript the profile registered; `http` and `mcp` are remote
44
+ * services whose bytes the host does not control. `delegated` is another agent
45
+ * answering through the tool boundary — its output is model-generated prose that
46
+ * reads as authoritative, which is why depth is tracked separately.
47
+ */
48
+ export const TOOL_ORIGINS = ['local', 'builtin', 'http', 'mcp', 'delegated'];
49
+ /**
50
+ * What a turn may still do after it has ingested untrusted remote content.
51
+ *
52
+ * Enforcement is opt-in. Tracking and reporting are on by default — every
53
+ * remote read is observable — but refusing tool calls changes what working agents
54
+ * are allowed to do, so a host declares which access levels to gate rather than
55
+ * having the kernel guess.
56
+ */
57
+ /**
58
+ * How strongly the tool-ingress signals fired, derived from the hits themselves.
59
+ *
60
+ * Not a probability: there is no calibrated model behind it. `elevated` means one
61
+ * directive signal alongside an external destination; `high` means the content
62
+ * named a tool the model can call, or several signals agreed.
63
+ */
64
+ export const ADVISORY_LEVELS = ['none', 'elevated', 'high'];
65
+ export const TAINT_GATES = ['off', 'destructive', 'write'];
66
+ /** The guardrail field names a `host` profile may set. */
67
+ export const HOST_GUARDRAIL_FIELDS = [
68
+ 'sanitizeInput',
69
+ 'redactSensitive',
70
+ 'network',
71
+ 'taint',
72
+ ];
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Strip host-only diagnostics from turn events before client-facing transports.
3
+ *
4
+ * @module
5
+ */
6
+ import type { TurnEvent } from '../kernel/types.js';
7
+ /** Options for {@link forClient} / {@link forClientEvents}. */
8
+ export interface ClientTurnOptions {
9
+ /**
10
+ * Keep provider-native step payloads on `evidence` events.
11
+ * Default `false` — parsed fields (`kind`, `code`, `result`, citations) remain.
12
+ */
13
+ includeEvidenceRaw?: boolean;
14
+ }
15
+ /** Return a copy of one turn event safe to forward to browsers or end-user SSE. */
16
+ declare function forClient(event: TurnEvent, options?: ClientTurnOptions): TurnEvent;
17
+ /** Map {@link forClient} over a batch (e.g. Live relay or HTTP stream flush). */
18
+ declare function forClientEvents(events: TurnEvent[], options?: ClientTurnOptions): TurnEvent[];
19
+ export { forClient, forClientEvents };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Strip host-only diagnostics from turn events before client-facing transports.
3
+ *
4
+ * @module
5
+ */
6
+ import { projectGuardrailTurnEvent } from '../guardrails/events.js';
7
+ function stripErrorInternal(event) {
8
+ if (event.type !== 'error' || !event.errorInternal) {
9
+ return event;
10
+ }
11
+ const { errorInternal: _internal, ...rest } = event;
12
+ return rest;
13
+ }
14
+ function stripEvidenceRaw(event) {
15
+ if (event.type !== 'evidence' || !event.evidence?.raw) {
16
+ return event;
17
+ }
18
+ const { raw: _raw, ...evidence } = event.evidence;
19
+ return { ...event, evidence };
20
+ }
21
+ /** Return a copy of one turn event safe to forward to browsers or end-user SSE. */
22
+ function forClient(event, options) {
23
+ let out = stripErrorInternal(event);
24
+ if (!options?.includeEvidenceRaw) {
25
+ out = stripEvidenceRaw(out);
26
+ }
27
+ // Clients never receive matched substrings — even if the host opted into
28
+ // guardrailMatchPreview for server logs / JSONL.
29
+ out = projectGuardrailTurnEvent(out, false);
30
+ return out;
31
+ }
32
+ /** Map {@link forClient} over a batch (e.g. Live relay or HTTP stream flush). */
33
+ function forClientEvents(events, options) {
34
+ return events.map((event) => forClient(event, options));
35
+ }
36
+ export { forClient, forClientEvents };
@@ -6,8 +6,8 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- import { type TraceSink } from '../observability/trace.js';
10
9
  import type { TraceRecord } from '../observability/trace-record.js';
10
+ import type { TraceSink } from '../observability/trace-sink.js';
11
11
  interface CutoutTape {
12
12
  ok: boolean;
13
13
  ms: number;
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Optional host-application helpers.
3
3
  *
4
- * These are not part of the turn kernel. They exist for Deno HTTP hosts that
5
- * want shared reply status mapping and cutout-trace flushing without re-
6
- * implementing the same glue in every app route.
4
+ * Not part of the turn kernel. Shared glue for Deno HTTP hosts (reply status,
5
+ * cutout-trace flush) and live structured-output preview while tokens stream.
7
6
  *
8
7
  * @module
9
8
  */
10
9
  import "../../_dnt.polyfills.js";
10
+ export type { ClientTurnOptions } from './client-turn.js';
11
+ export { forClient, forClientEvents } from './client-turn.js';
11
12
  export type { CutoutTape } from './mint-trace.js';
12
13
  export { flushMintTrace } from './mint-trace.js';
14
+ export { readStreamingJsonStringField } from './readStreamingJsonStringField.js';
13
15
  export { caughtStatus, HTTP_BUSY, HTTP_METHOD, HTTP_NOT_FOUND, HTTP_OK, json, } from './reply.js';
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * Optional host-application helpers.
3
3
  *
4
- * These are not part of the turn kernel. They exist for Deno HTTP hosts that
5
- * want shared reply status mapping and cutout-trace flushing without re-
6
- * implementing the same glue in every app route.
4
+ * Not part of the turn kernel. Shared glue for Deno HTTP hosts (reply status,
5
+ * cutout-trace flush) and live structured-output preview while tokens stream.
7
6
  *
8
7
  * @module
9
8
  */
10
9
  import "../../_dnt.polyfills.js";
10
+ export { forClient, forClientEvents } from './client-turn.js';
11
11
  export { flushMintTrace } from './mint-trace.js';
12
+ export { readStreamingJsonStringField } from './readStreamingJsonStringField.js';
12
13
  export { caughtStatus, HTTP_BUSY, HTTP_METHOD, HTTP_NOT_FOUND, HTTP_OK, json, } from './reply.js';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Web Crypto utilities for OAuth 2.1 PKCE and stateless sealed state envelopes.
3
+ *
4
+ * All operations use standard `crypto.subtle` and `crypto.getRandomValues`,
5
+ * ensuring 100% portability across Node, Deno, Bun, Cloudflare Workers, and browsers.
6
+ *
7
+ * @module
8
+ */
9
+ /** Encode Uint8Array to RFC 4648 base64url string without padding. */
10
+ export declare function toBase64Url(bytes: Uint8Array): string;
11
+ /** Decode RFC 4648 base64url string to Uint8Array. */
12
+ export declare function fromBase64Url(base64url: string): Uint8Array;
13
+ /**
14
+ * Generate a cryptographically secure PKCE code verifier (RFC 7636 Section 4.1).
15
+ * Length must be between 43 and 128 characters without modulo bias.
16
+ */
17
+ export declare function generateCodeVerifier(length?: number): string;
18
+ /**
19
+ * Compute the PKCE code challenge using S256 (RFC 7636 Section 4.2):
20
+ * `BASE64URL(SHA256(ASCII(code_verifier)))`
21
+ */
22
+ export declare function computeCodeChallenge(verifier: string): Promise<string>;
23
+ export interface SealedStatePayload {
24
+ codeVerifier: string;
25
+ expectedIssuer: string;
26
+ resource?: string;
27
+ redirectUri: string;
28
+ expiresAt: number;
29
+ clientId: string;
30
+ extra?: Record<string, unknown>;
31
+ }
32
+ /**
33
+ * Create a stateless HMAC-SHA256 signed envelope for OAuth `state`.
34
+ * This allows a stateless backend to recover the code_verifier and expected issuer
35
+ * upon receiving the OAuth callback, without any database or session cache.
36
+ */
37
+ export declare function sealStatePayload(payload: SealedStatePayload, secret: string): Promise<string>;
38
+ /**
39
+ * Unpack and verify an HMAC-SHA256 signed `state` envelope.
40
+ * Validates cryptographic signature and expiration timestamp.
41
+ */
42
+ export declare function unsealStatePayload(sealed: string, secret: string): Promise<SealedStatePayload>;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Web Crypto utilities for OAuth 2.1 PKCE and stateless sealed state envelopes.
3
+ *
4
+ * All operations use standard `crypto.subtle` and `crypto.getRandomValues`,
5
+ * ensuring 100% portability across Node, Deno, Bun, Cloudflare Workers, and browsers.
6
+ *
7
+ * @module
8
+ */
9
+ /** Encode Uint8Array to RFC 4648 base64url string without padding. */
10
+ export function toBase64Url(bytes) {
11
+ let binary = '';
12
+ for (let i = 0; i < bytes.byteLength; i++) {
13
+ binary += String.fromCharCode(bytes[i]);
14
+ }
15
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
16
+ }
17
+ /** Decode RFC 4648 base64url string to Uint8Array. */
18
+ export function fromBase64Url(base64url) {
19
+ let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
20
+ while (base64.length % 4 !== 0) {
21
+ base64 += '=';
22
+ }
23
+ try {
24
+ const binary = atob(base64);
25
+ const bytes = new Uint8Array(binary.length);
26
+ for (let i = 0; i < binary.length; i++) {
27
+ bytes[i] = binary.charCodeAt(i);
28
+ }
29
+ return bytes;
30
+ }
31
+ catch (err) {
32
+ throw new Error(`Invalid base64url encoding: ${err instanceof Error ? err.message : String(err)}`);
33
+ }
34
+ }
35
+ /**
36
+ * Generate a cryptographically secure PKCE code verifier (RFC 7636 Section 4.1).
37
+ * Length must be between 43 and 128 characters without modulo bias.
38
+ */
39
+ export function generateCodeVerifier(length = 64) {
40
+ if (length < 43 || length > 128) {
41
+ throw new RangeError(`Invalid PKCE code_verifier length: ${length}. RFC 7636 Section 4.1 requires length between 43 and 128 characters.`);
42
+ }
43
+ const validChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
44
+ const maxValid = 256 - (256 % validChars.length); // 198 (66 * 3) eliminates modulo bias
45
+ let verifier = '';
46
+ const buffer = new Uint8Array(length * 2);
47
+ while (verifier.length < length) {
48
+ crypto.getRandomValues(buffer);
49
+ for (let i = 0; i < buffer.length && verifier.length < length; i++) {
50
+ const val = buffer[i];
51
+ if (val !== undefined && val < maxValid) {
52
+ verifier += validChars[val % validChars.length];
53
+ }
54
+ }
55
+ }
56
+ return verifier;
57
+ }
58
+ /**
59
+ * Compute the PKCE code challenge using S256 (RFC 7636 Section 4.2):
60
+ * `BASE64URL(SHA256(ASCII(code_verifier)))`
61
+ */
62
+ export async function computeCodeChallenge(verifier) {
63
+ const encoder = new TextEncoder();
64
+ const data = encoder.encode(verifier);
65
+ const digest = await crypto.subtle.digest('SHA-256', data);
66
+ return toBase64Url(new Uint8Array(digest));
67
+ }
68
+ /**
69
+ * Create a stateless HMAC-SHA256 signed envelope for OAuth `state`.
70
+ * This allows a stateless backend to recover the code_verifier and expected issuer
71
+ * upon receiving the OAuth callback, without any database or session cache.
72
+ */
73
+ export async function sealStatePayload(payload, secret) {
74
+ const encoder = new TextEncoder();
75
+ const jsonStr = JSON.stringify(payload);
76
+ const payloadBytes = encoder.encode(jsonStr);
77
+ const payloadB64 = toBase64Url(payloadBytes);
78
+ const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
79
+ const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(payloadB64));
80
+ const signatureB64 = toBase64Url(new Uint8Array(signature));
81
+ return `${payloadB64}.${signatureB64}`;
82
+ }
83
+ /**
84
+ * Unpack and verify an HMAC-SHA256 signed `state` envelope.
85
+ * Validates cryptographic signature and expiration timestamp.
86
+ */
87
+ export async function unsealStatePayload(sealed, secret) {
88
+ const parts = sealed.split('.');
89
+ if (parts.length !== 2) {
90
+ throw new Error('Invalid sealed state format'); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
91
+ }
92
+ const [payloadB64, signatureB64] = parts;
93
+ const encoder = new TextEncoder();
94
+ const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']);
95
+ const signatureBytes = fromBase64Url(signatureB64);
96
+ const isValid = await crypto.subtle.verify('HMAC', key, signatureBytes, encoder.encode(payloadB64));
97
+ if (!isValid) {
98
+ throw new Error('OAuth state HMAC signature verification failed: state has been tampered with or corrupted');
99
+ }
100
+ const payloadJson = new TextDecoder().decode(fromBase64Url(payloadB64));
101
+ const payload = JSON.parse(payloadJson);
102
+ if (Date.now() > payload.expiresAt) {
103
+ throw new Error('OAuth state has expired'); // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
104
+ }
105
+ return payload;
106
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Authentication and authorization primitives for Theorum.
3
+ *
4
+ * Implements stateless OAuth 2.1 PKCE, RFC 9728 discovery, RFC 8414 AS metadata,
5
+ * RFC 9207 issuer validation, and RFC 8707 resource indicators.
6
+ *
7
+ * @module
8
+ */
9
+ export * from './crypto.js';
10
+ export * from './oauth.js';
11
+ export * from './types.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Authentication and authorization primitives for Theorum.
3
+ *
4
+ * Implements stateless OAuth 2.1 PKCE, RFC 9728 discovery, RFC 8414 AS metadata,
5
+ * RFC 9207 issuer validation, and RFC 8707 resource indicators.
6
+ *
7
+ * @module
8
+ */
9
+ export * from './crypto.js';
10
+ export * from './oauth.js';
11
+ export * from './types.js';