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
package/README.md CHANGED
@@ -10,11 +10,18 @@
10
10
 
11
11
  # THEORUM: The Flat Agent Kernel
12
12
 
13
- **Current release: `0.1.15`** (`jsr:@theorum/core` / npm `theorum`).
13
+ **Current release: `1.1.3`** (`jsr:@theorum/core` / npm `theorum`).
14
+
15
+ > ⚠️ **Package Migration Notice:**
16
+ > Starting with version 2.0, the package and scope are officially rebranded from `theorum` / `@theorum/core` to **`@theoremai/agents`** on both **npm** and **JSR**.
17
+ > - **npm**: `npm install @theoremai/agents` (deprecating `theorum`)
18
+ > - **JSR**: `deno add jsr:@theoremai/agents` (deprecating `jsr:@theorum/core`)
19
+ >
20
+ > All legacy `theorum` / `jsr:@theorum/core` v1.x releases are deprecated in favor of `@theoremai/agents`.
14
21
 
15
22
  > **"Profiles describe the contract. Providers move bytes. The runner enforces the turn."**
16
23
 
17
- THEORUM is a compact TypeScript agent kernel for apps that need deterministic agent execution without embedding product logic inside the runtime. It gives a host application one runner, typed profiles, multimodal input normalization, dynamic tool dispatch, provider adapters, trace sinks, and guardrail hooks.
24
+ THEORUM is a compact TypeScript agent kernel for apps that need deterministic agent execution without embedding product logic inside the runtime. It gives a host application one runner, typed profiles, multimodal input normalization, a registered tool system with per-turn gating, provider adapters, trace sinks, and guardrail hooks.
18
25
 
19
26
  The package is intentionally **not** an agent product. It ships no app profiles, no prompts, no secrets, no database policy, no business rules, and no channel-specific UX. Those belong in the host application.
20
27
 
@@ -29,9 +36,9 @@ OpenRouter chat transport is powered by Vercel AI SDK Core under the adapter. TH
29
36
  profiles = "Host-owned declarations for model, inputs, outputs, tools, and guardrails"
30
37
  runner = "Single deterministic execution path for one agent turn"
31
38
  providers = "createProvider routes protocol/provider; adapters stay internal"
32
- tools = "Profile allowlist ceiling plus per-turn dynamic declarations"
39
+ tools = "Profile allowlist ceiling plus per-turn opt-in gates"
33
40
  egress = "Typed host hook for outbound disclosure checks and repair loops"
34
- traces = "Host-injected sinks; no environment variables or bundled destinations"
41
+ traces = "Profile observability + host-registered destinations; no env vars or bundled DB"
35
42
 
36
43
  [non_goals]
37
44
  app_profiles = "No bundled assistants, demos, product personas, or business tasks"
@@ -40,6 +47,10 @@ realtime_voice = "Not included yet; persistent duplex sessions stay host-owned"
40
47
  product_copy = "No channel wording, refusal copy, iMessage/Alexa/Web policy, or UX defaults"
41
48
  ```
42
49
 
50
+ React UI and the headless interface projection remain repo-private under [`react/`](./react/)
51
+ and `src/interface/` while their public contracts are being designed. They are excluded from
52
+ the JSR and npm packages.
53
+
43
54
  ---
44
55
 
45
56
  ## Architecture
@@ -61,7 +72,7 @@ flowchart TD
61
72
  Resolve["resolveTurn"]
62
73
  Guard["sanitize + canary + egress"]
63
74
  Runner["runTurn"]
64
- ToolLoop["dynamic tool loop"]
75
+ ToolLoop["registered tool loop"]
65
76
  Repair["repair attempts"]
66
77
  end
67
78
 
@@ -84,27 +95,66 @@ flowchart TD
84
95
  Runner --> TraceSink
85
96
  ```
86
97
 
87
- Hosts bind transports with `createProvider(profile, { gemini, openRouter })`. One door; protocol/provider (and speech role) pick the adapter.
98
+ Hosts bind transports with `createProvider(profile, { gemini, openAiGateway })`. One door; protocol/provider (and speech role) pick the adapter.
88
99
 
89
- ### Turn Lifecycle
100
+ ### Turn execution and tools
101
+
102
+ One turn is a single pipeline. Tools share `executeRegisteredTool` with `invokeTool`; compaction,
103
+ guardrails, and streaming attach at different layers.
90
104
 
91
105
  ```mermaid
92
- stateDiagram-v2
93
- [*] --> ResolveProfile: host sends TurnRequest
94
- ResolveProfile --> NormalizeInput: profile input rules
95
- NormalizeInput --> BindBoundary: canary + user data fencing
96
- BindBoundary --> ProviderStream: ModelProvider.complete
97
- ProviderStream --> ToolDispatch: tool event
98
- ToolDispatch --> ProviderStream: autonomous loop continues
99
- ProviderStream --> EgressGate: final candidate
100
- EgressGate --> RepairTurn: blocked + retry budget
101
- RepairTurn --> ProviderStream
102
- EgressGate --> ValidateOutput: clear
103
- ValidateOutput --> EmitEvents: text/media/structured/tokens/done
104
- EmitEvents --> Trace: host sink receives audit record
105
- Trace --> [*]
106
+ flowchart TD
107
+ subgraph Host["Host application"]
108
+ REG["registerTool at startup"]
109
+ REQ["TurnRequest<br/>(tools gate · continueFrom · …)"]
110
+ UI["Pause UI"]
111
+ INV["invokeTool(resume)"]
112
+ end
113
+
114
+ subgraph Ingress["runTurn ingress"]
115
+ SAN["sanitizeTurnRequest"]
116
+ RES["resolveTurn TurnToolSnapshot"]
117
+ CB{"timing: before<br/>compact history?"}
118
+ SYS["system + canary<br/>(+ CONTINUE_INSTRUCTION if continueFrom)"]
119
+ end
120
+
121
+ subgraph Attempt["Attempt (egress / validation retries)"]
122
+ subgraph Steps["maxSteps loop"]
123
+ PL["provider.complete<br/>(wire schemas + history)"]
124
+ TE["executeRegisteredTool"]
125
+ HK["formatToolResult → history<br/>or Interactions continuation"]
126
+ end
127
+ EG["egress + validation<br/>(assistant text in attempt)"]
128
+ end
129
+
130
+ OUT["done<br/>(stop · tokens · compaction signal?)"]
131
+ TR["trace record"]
132
+
133
+ REG -.-> TE
134
+ REQ --> SAN --> RES --> CB --> SYS --> Steps
135
+ PL -->|model tool calls| TE
136
+ TE -->|complete| HK --> PL
137
+ TE -->|pause · exit step loop| EG
138
+ UI --> INV --> TE
139
+ Steps -->|loop ends| EG
140
+ EG -->|repair retry| SAN
141
+ EG --> OUT --> TR
142
+
143
+ INV -.->|separate entry · no provider| TE
106
144
  ```
107
145
 
146
+ **How the verticals meet tools:**
147
+
148
+ | Vertical | Where it runs | Tool interaction |
149
+ | --- | --- | --- |
150
+ | **Compaction** | Before turn (`timing: 'before'`) or signal on `done` (`timing: 'after'`) | Summarizes `TurnHistoryMessage` history — including `tool_calls` and `role: 'tool'` rows — not the live registry or mid-turn wire snapshot |
151
+ | **Guardrails** | Ingress sanitize; egress/validation after the step loop | Sanitizes user text and history content; tool catalog descriptions and model-emitted arguments are host/registration concerns. Egress inspects assistant **text** in the attempt, not tool progress events |
152
+ | **Streaming** | Provider stream + tool handler generators | Provider tool-call events buffer until execution; handler `progress` / `trace` / `artifact` / `warning` phases stream during `executeRegisteredTool`. `streamThoughts: false` filters thoughts only |
153
+ | **Resumption** | Two paths — do not mix | **`stop.kind: 'tool'`** → host UI → `invokeTool` with `resume` (skips turn gate). **`length` / `stream_incomplete` / …** → new `runTurn` with `continueFrom` (+ `CONTINUE_INSTRUCTION` in system); host must re-gate tools |
154
+
155
+ On tool pause the `maxSteps` loop exits (`stop.kind: 'tool'`), egress may still evaluate
156
+ buffered assistant text from that attempt, then the turn emits terminal `done`.
157
+
108
158
  ---
109
159
 
110
160
  ## Install
@@ -145,6 +195,7 @@ import {
145
195
  } from "jsr:@theorum/core";
146
196
 
147
197
  const profile = defineProfile({
198
+ type: "text",
148
199
  id: "assistant.basic",
149
200
  identity: {
150
201
  handle: "assistant",
@@ -157,18 +208,19 @@ const profile = defineProfile({
157
208
  config: {
158
209
  hostFastModel: {
159
210
  apiId: "perplexity/sonar",
160
- openRouterId: "perplexity/sonar",
161
211
  thinking: { on: "high", off: "minimal" },
162
212
  thinkingLevels: ["minimal", "low", "medium", "high"],
163
213
  summaries: { on: "auto", off: "none" },
164
214
  maxOutputTokens: 8192,
165
215
  temperature: 1,
166
- keyBuiltins: [],
216
+ builtInTools: [],
167
217
  },
168
218
  },
169
219
  thinking: "minimal",
170
220
  maxSteps: 1,
171
221
  },
222
+ tools: { allow: [] },
223
+ inputs: { text: true },
172
224
  outputs: {
173
225
  streaming: { streamThoughts: false },
174
226
  },
@@ -197,38 +249,52 @@ for await (const event of runTurn(
197
249
 
198
250
  ---
199
251
 
200
- ## Dynamic Tools
252
+ ## Registered Tools
201
253
 
202
- THEORUM separates tool concerns into three layers.
254
+ THEORUM separates tool concerns into four layers.
203
255
 
204
256
  | Layer | Owner | Purpose |
205
257
  | :--- | :--- | :--- |
206
- | **Access** | Profile | Hard ceiling: the agent cannot use a tool outside `profile.tools.allow`. |
207
- | **Visibility** | Turn request | Per-turn declarations: T0/T1/T2 schemas can be passed or loaded dynamically. |
208
- | **Permission** | Host app | `auto`, `session_consent`, and `always_confirm` determine whether execution pauses. |
258
+ | **Catalog** | Host (startup) | `registerTool` schema, handler, access, loadTier, permission |
259
+ | **Allow** | Profile | Custom: `tools.allow`. Builtins: `models.*.builtInTools` |
260
+ | **Visibility** | Registry + profile | `loadTier` on tool; T1 via `tools.t1Policy`; T2 via `tools.t2Loader`. Live (`runSession`) wires every allowed tool at setup; `host` profiles execute every allowed tool via `invokeTool`. |
261
+ | **Permission** | Host app | `auto`, `session_consent`, and `always_confirm` determine whether execution pauses |
209
262
 
210
263
  ```ts
211
- const dynamicTools = [
212
- {
213
- name: "lookup_order",
214
- description: "Fetch order state from the host application.",
215
- loadTier: "T1",
216
- permissionTier: "session_consent",
217
- parameters: {
218
- type: "object",
219
- properties: { orderId: { type: "string" } },
220
- required: ["orderId"],
221
- },
222
- handler: async (args) => ({
223
- status: "ok",
224
- finding: "Order is in transit.",
225
- data: { orderId: args.orderId, state: "in_transit" },
226
- }),
227
- },
228
- ] as const;
264
+ import { z } from 'zod';
265
+ import { registerTool, invokeTool, runTurn } from 'theorum';
266
+
267
+ registerTool({
268
+ type: 'function',
269
+ name: 'lookup_order',
270
+ description: 'Fetch order state from the host application.',
271
+ category: 'operations',
272
+ access: 'read-only',
273
+ paths: ['*'],
274
+ loadTier: 'T0',
275
+ permission: 'session_consent',
276
+ input: z.object({ orderId: z.string() }),
277
+ output: z.object({ finding: z.string() }),
278
+ handler: async (input) => ({
279
+ finding: `Order ${input.orderId} is in transit.`,
280
+ }),
281
+ });
282
+
283
+ // Profile allow
284
+ tools: { allow: ['lookup_order', 'load_tools'] }
285
+
286
+ runTurn({ profile, input: { text: '…' } }, provider);
287
+
288
+ // Gate resume (permission / confirm / auth) — ask_user completes awaiting; answer is a new user turn
289
+ invokeTool({ profile, name: 'risky_tool', input: {…}, resume: { granted: true }, snapshot, turnInput });
229
290
  ```
230
291
 
231
- The host owns the handler and authorization state. The kernel only enforces the declared contract.
292
+ The host owns handlers and authorization state. The kernel enforces the declared contract
293
+ via shared `executeRegisteredTool` for model tool calls and `invokeTool` for host resumes.
294
+
295
+ Function tools require **Zod** input/output schemas at registration time.
296
+
297
+ **Migration:** [`docs/MIGRATION-tool-system.md`](docs/MIGRATION-tool-system.md) (breaking changes from `dynamicTools` / `ToolEnvelope`).
232
298
 
233
299
  ---
234
300
 
@@ -238,22 +304,28 @@ Inbound and outbound safety are generic kernel hooks.
238
304
 
239
305
  ```ts
240
306
  const guardedProfile = defineProfile({
307
+ type: "text",
241
308
  id: "assistant.guarded",
309
+ identity: { handle: "guarded", system: "You are a careful assistant." },
242
310
  model: {
311
+ protocol: "openAi",
312
+ provider: "openrouter",
243
313
  allow: ["hostFastModel"],
244
314
  config: {
245
315
  hostFastModel: {
246
316
  apiId: "perplexity/sonar",
247
- openRouterId: "perplexity/sonar",
248
317
  thinking: { on: "high", off: "minimal" },
249
318
  thinkingLevels: ["minimal", "low", "medium", "high"],
250
319
  summaries: { on: "auto", off: "none" },
251
320
  maxOutputTokens: 8192,
252
321
  temperature: 1,
253
- keyBuiltins: [],
322
+ builtInTools: [],
254
323
  },
255
324
  },
325
+ thinking: "minimal",
256
326
  },
327
+ tools: { allow: [] },
328
+ inputs: { text: true },
257
329
  guardrails: {
258
330
  egress: {
259
331
  onBlock: "reject_to_agent",
@@ -289,7 +361,7 @@ import { createProvider, runTurn } from "jsr:@theorum/core";
289
361
 
290
362
  const provider = createProvider(profile, {
291
363
  gemini: { vault: hostGeminiKeyVault, fetch },
292
- openRouter: { apiKey: hostSecrets.openRouterApiKey },
364
+ openAiGateway: { apiKey: hostSecrets.openRouterApiKey },
293
365
  // openAi + local — optional; default baseUrl http://127.0.0.1:11434
294
366
  local: { baseUrl: hostResolvedLocalBaseUrl },
295
367
  });
@@ -299,7 +371,7 @@ for await (const event of runTurn({ profile: profile.id, input: { text: "…" }
299
371
  }
300
372
  ```
301
373
 
302
- `createProvider` routes from `profile.model.protocol` / `provider`. Speech roles use the same call — Interactions when Google, `/audio/speech` when openAi/openrouter (same `openRouter` credentials).
374
+ `createProvider` routes from `profile.model.protocol` / `provider`. Speech roles use the same call — Interactions when Google, `/audio/speech` when openAi/openrouter (same `openAiGateway` credentials).
303
375
 
304
376
  | Profile | Transport |
305
377
  | :--- | :--- |
@@ -310,17 +382,14 @@ for await (const event of runTurn({ profile: profile.id, input: { text: "…" }
310
382
 
311
383
  Local adapters take an optional `baseUrl` (default `http://127.0.0.1:11434`). THEORUM does not read `OLLAMA_HOST`; hosts that honor that env should resolve it and pass `local.baseUrl`. History `parts` (including images) are mapped on the wire; `done` events include a normalized `stop` from the OpenAI `finish_reason`.
312
384
 
313
- OpenRouter uses Vercel AI SDK Core inside THEORUM's provider adapter. That stack
314
- loads **lazily on the first `complete` call** for `openAi` + `openrouter` chat
315
- not when importing THEORUM, and not for Google or local providers. The adapter
316
- still emits THEORUM `TurnEvent` values and preserves raw provider evidence for
317
- citations/provenance where the normalized SDK stream does not expose enough detail.
318
-
319
- Advanced OpenRouter exports live under `theorum/openrouter` (`createOpenRouterProvider`,
320
- `toOpenRouterPayload`, …). Prefer `createProvider` for turns unless the host needs
321
- to wire the OpenRouter adapter directly. Direct local construction is also available
322
- as `createLocalProvider` from the main / providers entrypoints. Importing
323
- `theorum/openrouter` loads the Vercel SDK immediately.
385
+ OpenRouter uses Vercel AI SDK Core inside THEORUM's provider adapter. Provider
386
+ adapters load **lazily on the first `complete` call** for the selected transport
387
+ not when importing THEORUM. Importing `createProvider` alone does not pull in
388
+ Google Interactions, OpenRouter/AI SDK, speech, or local adapter graphs.
389
+ The OpenRouter adapter still emits THEORUM `TurnEvent` values and preserves raw
390
+ provider evidence for citations/provenance where the normalized SDK stream does
391
+ not expose enough detail. Use `createProvider` for all turns; adapter modules
392
+ stay internal to the providers package.
324
393
 
325
394
  ---
326
395
 
@@ -328,16 +397,21 @@ as `createLocalProvider` from the main / providers entrypoints. Importing
328
397
 
329
398
  | Entrypoint | Purpose |
330
399
  | :--- | :--- |
331
- | `jsr:@theorum/core` / `theorum` | Main kernel API: profiles, schemas, runner, core types, provider constructors. |
332
- | `jsr:@theorum/core/kernel` / `theorum/kernel` | Profile/turn types, tool catalog, `requireModelSpec`, thinking clamps over host model maps. |
333
- | `jsr:@theorum/core/providers` / `theorum/providers` | `createProvider` + Gemini vault types. |
334
- | `jsr:@theorum/core/openrouter` / `theorum/openrouter` | Direct OpenRouter provider adapter and payload helpers (advanced). |
335
- | `jsr:@theorum/core/guardrails` / `theorum/guardrails` | Sanitization, public error mapping, inbound injection/sensitive-data primitives. |
400
+ | `jsr:@theorum/core` / `theorum` | Main kernel API: profiles, schemas, runner, core types, provider constructors, declarative HTTP/MCP tool execution. |
401
+ | `jsr:@theorum/core/kernel` / `theorum/kernel` | Profile/turn types, tool catalog, `requireModelBinding`, thinking clamps over host model maps. |
402
+ | `jsr:@theorum/core/providers` / `theorum/providers` | `createProvider` + Gemini vault types + host option bags. |
403
+ | `jsr:@theorum/core/providers/local` / `theorum/providers/local` | Direct local OpenAI-compat adapter (`createLocalProvider`, `DEFAULT_LOCAL_BASE_URL`). |
404
+ | `jsr:@theorum/core/guardrails` / `theorum/guardrails` | Sanitization, canary/egress gates, public error mapping, inbound injection/sensitive-data primitives. |
405
+ | `jsr:@theorum/core/guardrails/testing` / `theorum/guardrails/testing` | Adversarial corpus + fuzz helpers (test/harness only). |
336
406
  | `jsr:@theorum/core/observability` / `theorum/observability` | Trace sinks and trace record helpers. |
337
407
  | `jsr:@theorum/core/host` / `theorum/host` | Optional Deno HTTP helpers (`json`, status mapping, cutout mint flush). |
338
408
  | `jsr:@theorum/core/cli` / `theorum/cli` | Profile inspection and stress-test CLI (`theorum` binary on npm). |
339
409
  | `jsr:@theorum/core/presets` / `theorum/presets` | Optional convenience packs (`registerGooglePreset`, …). |
340
- | `jsr:@theorum/core/presets/google` / `theorum/presets/google` | Google builtins (search/maps/urlContext) + Interactions/OpenRouter wire metadata. |
410
+ | `jsr:@theorum/core/presets/google` / `theorum/presets/google` | Google builtins (search/maps/urlContext/codeExecution) + Interactions/OpenRouter wire metadata. |
411
+
412
+ Demo fixtures (travel concierge seeds, local handlers) live in the **repo-private**
413
+ `@theorum/playground` package under `playground/` — never published with the kernel.
414
+ Hosts that need them link `file:../theorum/playground`.
341
415
 
342
416
  Internal files remain present in source for maintainability, but package consumers should use the public entrypoints above.
343
417
 
@@ -348,39 +422,63 @@ Named exports from the root barrel (same symbols hosts get from `theorum` /
348
422
 
349
423
  | Group | Symbols |
350
424
  | --- | --- |
351
- | Guardrails errors | `describeError`, `isAbortError`, `publicError`, `TheorumError`, `throwIfAborted`, `toErrorEvent` |
352
- | Quota | `QuotaSlotStatus`, `clientIp`, `quotaMessage`, `releaseSlot`, `resetSlots`, `skipQuota`, `takeSlot` |
353
- | Sanitize | `PROJECT_ID_MAX`, `sanitizeProjectId`, `sanitizeText`, `sanitizeTurnRequest` |
425
+ | Guardrails errors | `describeError`, `isAbortError`, `publicError`, `TheorumError`, `throwIfAborted`, `toErrorEvent`, `PUBLIC_CANARY` |
426
+ | Network guardrails | `assertSafeUrl`, `isLocalhostName`, `isPrivateOrLocalAddress` |
427
+ | Guardrail vocabulary | `AdvisoryLevel`, `TrustLevel`, `GuardrailStage`, `Severity`, `GuardrailHit`, `Verdict`, `GuardrailAction`, `GuardrailContext`, `GuardrailEvent`, `OutboundPayload`, `Provenance`, `ToolOrigin`, `ScanText`, `EgressEnforcer`, `EgressOnBlock`, `ProfileEgressSpec`, `ProfileGuardrailsSpec`, `HostGuardrailsSpec`, `NetworkGuardrailSpec`, `CanaryGuardrailSpec`, `QuotaGuardrailSpec`, `ResolvedGuardrailPolicy`, `DetectionOptions`, `GuardedToolText`, `TurnTaint`, `TaintGate`, `TaintGuardrailSpec`, `TRUST_LEVELS`, `GUARDRAIL_STAGES`, `SEVERITIES`, `TOOL_ORIGINS`, `EGRESS_ON_BLOCK` |
428
+ | Guardrail policy | `resolveGuardrailPolicy`, `detectionForTrust`, `detectionForProfile`, `collectEgressHits`, `hitRules`, `EGRESS_RULES`, `runEnforcer` |
429
+ | Tool boundary | `guardToolResult`, `guardToolFailureText`, `inspectToolArguments`, `toolCallEvent`, `wrapToolData`, `isRemoteOrigin`, `composeToolText`, `checkTaintGate`, `recordTaint`, `isTainted`, `isSuspicious`, `directiveHits`, `looksDirective`, `advisoryLevel`, `DIRECTIVE_RULES`, `ADVISORY_LEVELS`, `TOOL_CLOSE`, `TOOL_ORIGINS`, `TAINT_GATES`, `textForScan`, `scanTextOf` |
430
+ | Quota | `QuotaSlotStatus`, `QuotaExhausted`, `clientIp`, `quotaExhausted`, `releaseSlot`, `resetSlots`, `skipQuota`, `takeSlot` |
431
+ | Lexicon | `LEXICON_KEYS`, `LexiconKey`, `LexiconOverrides`, `LexiconParams`, `lexiconDefault`, `lexiconText`, `overrideLexicon`, `resetLexicon` |
432
+ | Sanitize | `PROJECT_ID_MAX`, `sanitizeProjectId`, `sanitizeText`, `detectText`, `sanitizeTurnRequest`, `sanitizeTurnRequestWithEvents`, `sanitizeTurnRequestForTrace`, `redactSensitiveOnly`, `guardrailFromHits`, `guardrailFromVerdict`, `guardrailTurnEvent`, `projectGuardrailTurnEvent`, `hitFromSpan`, `matchPreview`, `projectGuardrailEvent`, `GUARDRAIL_MATCH_PREVIEW_MAX` |
433
+ | Canary / egress | `mintCanary`, `bindCanary`, `wrapUserData`, `scanTextForCanaryLeak`, `redactCanary`, `OMIT_CANARY`, `createCanaryStreamGate`, `eventHasCanary`, `createCanaryGateSession`, `filterCanaryGatedEvents`, `CanaryGateResult`, `CanaryGateSession`, `CanaryStreamGate`, `standardEgressEnforce`, `createOutboundProgressiveGate`, `createProgressiveYieldGate`, `DEFAULT_HOLDBACK`, `createLiveOutboundGateSession`, `processLiveOutboundBatch`, `finalizeLiveOutboundTurn`, `LiveOutboundBatchResult`, `LiveOutboundGateSession`, `ProgressiveYieldGate`, `ProgressiveYieldGateOptions`, `ProgressiveYieldResult` |
354
434
  | Compaction | `CompactionSplit`, `CompactionTokens`, `compactionMeter`, `compactionNeeded`, `estimateHistoryTokens`, `HISTORY_MEDIA_TOKENS`, `HISTORY_TEXT_ENCODING`, `resolveCompactionTokens`, `resolveHistoryTokens`, `shouldCompact`, `splitForCompaction` |
355
- | Runner | `runTurn` |
356
- | Catalog | `CATALOG`, `clampThinkingLevel`, `clampThinkingLevelForApiId`, `mediaKindForMime`, `getTool`, `listBuiltinIds`, `mimeAllowed`, `mimeEssence`, `modelEntryByApiId`, `registerTools`, `requireModelSpec`, `resetTools` |
357
- | Profiles | `ProfileDefinition`, `clearProfiles`, `defineProfile`, `getProfile`, `hasProfile`, `listProfiles`, `registerProfile`, `registerProfiles`, `projectProfile`, `resolveTurn` |
358
- | Structured | `getStructured`, `registerStructured`, `executeTool` |
359
- | Stop / resume | `ProfileResumeSpec`, `TurnContinueFrom`, `TurnStop`, `TurnStopKind`, `AUTO_CONTINUE_DELAY_MS`, `CONTINUE_INSTRUCTION`, `DEFAULT_AUTO_CONTINUE`, `GenerationStopError`, `isGenerationStopError`, `isResumeableStop`, `isUserCancelledStop`, `shouldAutoContinue`, `turnStopFromClientStreamEnd`, `turnStopFromInteractionStatus`, `turnStopFromOpenRouter` |
360
- | Observability | `jsonlSink`, `memorySink`, `noopSink`, `resolveTraceDir`, `sinkFromDir`, `writeTrace`, `TraceRecord` |
361
- | Providers | `CreateProviderOptions`, `GeminiTransport`, `GeminiVault`, `LocalProviderConfig`, `createLocalProvider`, `createProvider`, `DEFAULT_LOCAL_BASE_URL` |
435
+ | Runner | `runTurn`, `runSession`, `RunSessionOptions`, `prepareLiveInboundText`, `liveIngressEnabled`, `liveIngressEnabledFromSpec`, `liveIngressChannelDefault`, `hasAnyLiveIngress`, `assertLiveIngress`, `assertLiveIngressConfigured`, `LiveIngressChannel` |
436
+ | Attachments | `assertAttachmentLimits`, `maxBytesForMime`, `requireMediaLimits`, `resolveMediaLimits`, `sanitizeCsvText`, `sanitizeTurnBlobs`, `sanitizeTurnBlobsForProfile` |
437
+ | Catalog | `clampThinkingLevel`, `clampThinkingLevelForApiId`, `mediaChannelForMime`, `MediaInputChannel`, `mediaKindForMime`, `mimeAllowed`, `mimeEssence`, `modelEntryByApiId`, `requireModelBinding` |
438
+ | Schema | `PROFILE_FIELDS`, `PROFILE_GRAPH`, `PROFILE_TYPES`, `PROFILE_TYPE_PROTOCOLS`, `protocolsForProfileType`, `isValidProfileProtocol`, `EXTRA_FIELDS`, `fieldMeta`, `catalogPathFor`, `DYNAMIC_FIELD_PARENTS`, `spineFacetsForProfileType`, `profileGraphFacet`, `ProfileGraphFacet`, `ProfileGraphFacetId`, `ProfileGraphEditor`, `ProfileGraphRole`, `PROTOCOLS`, `PROVIDERS`, `PROTOCOL_PROVIDERS`, `providersFor`, `protocolsFor`, `isValidPair`, `coerceProvider`, `coerceProtocol`, `coerceSpeechFormat`, `isSpeechFormatAllowedForProtocol`, `speechFormatsForProtocol`, `THINKING_LEVELS`, `KEY_SLOTS`, `OVERFLOW_KEY_SLOTS`, `MEDIA_INPUT_KINDS`, `MEDIA_INPUT_KIND_VALUES`, `MEDIA_WILDCARDS`, `ATTACHMENT_ACCEPT_MIMES`, `VOICE_ACCEPT_MIMES`, `SUMMARY_MODES`, `STREAM_MODES`, `SPEECH_AUDIO_FORMATS`, `SCHEMA_ENFORCEMENTS`, `COMPACTION_METERS`, `COMPACTION_TIMINGS`, `CACHE_MODES`, `CACHE_TTLS`, `TURN_STAGES`, `TURN_INJECT_STAGES`, `TURN_STOP_KINDS`, `CONTINUE_STOP_KINDS`, `TOOL_GATE_KINDS`, `AWAITING_USER_INPUT_KINDS`, `AWAITING_USER_INPUT_STATUS`, `TOOL_LOAD_TIERS`, `TOOL_ACCESS`, `TOOL_PERMISSION`, `TOOL_TYPES`, `AUTH_UNAUTHENTICATED_POLICIES`, `HTTP_METHODS`, `PLAYGROUND_AUTH_TYPES`, `TOOL_AUTH_TYPES`, `AuthUnauthenticatedPolicy`, `CustomToolType`, `HttpMethod`, `PlaygroundAuthType`, `ToolAccess`, `ToolAuthType`, `ToolPermission`, `ToolType`, `LIVE_ACTIVITY_HANDLINGS`, `LIVE_CONTEXT_COMPRESSIONS`, `LIVE_SPEECH_SENSITIVITIES`, `EGRESS_ON_BLOCK`, `EgressOnBlock` |
439
+ | Profiles | `ProfileDefinition`, `ProfileDefinitionBase`, `TextProfileDefinition`, `ImageProfileDefinition`, `SpeechProfileDefinition`, `LiveProfileDefinition`, `HostProfileDefinition`, `clearProfiles`, `defineProfile`, `getProfile`, `hasProfile`, `listProfiles`, `registerProfile`, `registerProfiles`, `projectProfile`, `resolveTurn`, `pickModel` |
440
+ | Tools | `registerTool`, `registerTools`, `invokeTool`, `registerHarnessTools`, `getTool`, `hasTool`, `requireTool`, `listTools`, `listBuiltinIds`, `listFunctionIds`, `resetTools`, `formatToolResult`, `prepareTurnToolSnapshot`, `buildHttpToolTarget`, `executeHttpTool`, `executeMcpTool`, `parseMcpRpcResponse`, `isUnsupportedMcpProtocolError`, `MCP_PROTOCOL_VERSIONS`, `McpProtocolVersion`, `resolveToolAuth` |
441
+ | Structured | `getStructured`, `registerStructured` |
442
+ | Stop / resume | `ProfileTurnBehaviourSpec`, `ProfileTurnResumptionSpec`, `TurnContinueFrom`, `TurnStop`, `TurnStopKind`, `ContinueStopKind`, `CONTINUE_STOP_KINDS`, `AUTO_CONTINUE_DELAY_MS`, `CONTINUE_INSTRUCTION`, `DEFAULT_ALLOW_CONTINUE`, `DEFAULT_AUTO_CONTINUE`, `GenerationStopError`, `isContinueStopKind`, `isGenerationStopError`, `isResumeableStop`, `isUserCancelledStop`, `profileAllowsSteering`, `profileAllowsInject`, `profileTurnResumption`, `shouldAutoContinue`, `turnStopFromClientStreamEnd`, `turnStopFromInteractionStatus`, `turnStopFromOpenAiFinishReason` |
443
+ | Stages (target foundation) | `TURN_STAGES`, `TURN_INJECT_STAGES`, `STAGE_AFFORDANCES`, `STAGE_AFFORDANCE_MATRIX`, `TOOL_GATE_KINDS`, `AWAITING_USER_INPUT_KINDS`, `AWAITING_USER_INPUT_STATUS`, `applyStageResult`, `parseAwaitingUserInput`, `parseToolGate`, `isTurnStage`, `isTurnInjectStage`, `isToolGateKind`, `isAwaitingUserInput`, `stageAllowsAffordance`, `stageEventFields`, `profileAllowsInject`, `StageAffordance`, `StageContext`, `StageResult`, `StageMutate`, `StageHandler`, `StageApplyInput`, `StageApplyOutput`, `StageApplyWarning`, `StageApplyWarningCode`, `StageEventExtra`, `AwaitingUserInput`, `ToolGate` — contract [`docs/contracts/stages.md`](docs/contracts/stages.md) |
444
+ | Observability | `jsonlSink`, `memorySink`, `noopSink`, `resolveTraceDir`, `sinkFromDir`, `writeTrace`, `registerTraceDestination`, `jsonlDestination`, `requireTraceDestination`, `getTraceDestination`, `listTraceDestinationIds`, `clearTraceDestinations`, `isJsonlTraceDestination`, `isTraceSink`, `resolveTraceWriter`, `resolveObservabilityPolicy`, `TraceRecord`, `TraceSink`, `JsonlSinkOptions`, `JsonlTraceDestination`, `TraceDestination`, `ProfileObservabilitySpec`, `ResolvedObservabilityPolicy`, `ResolvedTraceInclude`, `ResolvedTraceScrub`, `TraceIncludeSpec`, `TraceScrubSpec` |
445
+ | Providers | `CreateProviderOptions`, `GeminiTransport`, `KeyVault`, `LocalProviderConfig`, `OpenAiGatewayConfig`, `createProvider` (local: `theorum/providers/local` → `createLocalProvider`, `DEFAULT_LOCAL_BASE_URL`) |
362
446
 
363
447
  Kernel types re-exported through this barrel follow `export type *` from
364
- `src/kernel/types.ts` (see `src/kernel/CONTRACT.md`).
448
+ `src/kernel/types.ts` (behavioral detail for contributors: repo
449
+ `docs/contracts/kernel.md`).
365
450
 
366
451
  ---
367
452
 
368
453
  ## Documentation
369
454
 
370
- Package docs are co-located with each public export (plus this README for `.`):
455
+ THEORUM keeps **package docs** and **repo contracts** separate.
456
+
457
+ | Surface | What it is | In the published package? |
458
+ | --- | --- | --- |
459
+ | **This README** | How hosts use THEORUM (API, boundaries, examples) | Yes |
460
+ | **Repo contracts** (`docs/contracts/*.md`) | Maintainer ownership + behavioral specs for docs-truth | **No** — GitHub / clone only |
461
+ | **Docs-truth** (`docs/DOCS_TRUTH.md`, `docs/_map.mjs`) | Lint graph that enforces those contracts | **No** |
371
462
 
372
- | Doc | Export |
463
+ On GitHub, module contracts:
464
+
465
+ | Doc (repo only) | Export |
373
466
  | :--- | :--- |
374
- | [`src/kernel/CONTRACT.md`](src/kernel/CONTRACT.md) | `theorum/kernel` — profiles, runner, compaction, stop/resume |
375
- | [`src/providers/CONTRACT.md`](src/providers/CONTRACT.md) | `theorum/providers``createProvider`, secrets boundary |
376
- | [`src/providers/OPENROUTER.md`](src/providers/OPENROUTER.md) | `theorum/openrouter` |
377
- | [`src/guardrails/CONTRACT.md`](src/guardrails/CONTRACT.md) | `theorum/guardrails` |
378
- | [`src/observability/CONTRACT.md`](src/observability/CONTRACT.md) | `theorum/observability` |
379
- | [`src/host/CONTRACT.md`](src/host/CONTRACT.md) | `theorum/host` |
380
- | [`src/cli/CONTRACT.md`](src/cli/CONTRACT.md) | `theorum/cli` |
381
- | [`src/presets/CONTRACT.md`](src/presets/CONTRACT.md) | `theorum/presets` |
382
- | [`src/presets/GOOGLE.md`](src/presets/GOOGLE.md) | `theorum/presets/google` |
383
- | [`src/streaming/CONTRACT.md`](src/streaming/CONTRACT.md) | `theorum/streaming` |
467
+ | [`docs/contracts/kernel.md`](docs/contracts/kernel.md) | `theorum/kernel` |
468
+ | [`docs/contracts/stages.md`](docs/contracts/stages.md) | Turn stages slices 1–3 landed on branch; release cut when docs match product |
469
+ | [`docs/contracts/providers.md`](docs/contracts/providers.md) | `theorum/providers` |
470
+ | [`docs/contracts/guardrails.md`](docs/contracts/guardrails.md) | `theorum/guardrails` |
471
+ | [`docs/contracts/observability.md`](docs/contracts/observability.md) | `theorum/observability` |
472
+ | [`docs/contracts/host.md`](docs/contracts/host.md) | `theorum/host` |
473
+ | [`docs/contracts/kernel.md`](docs/contracts/kernel.md) (repo-private headless interface) | `src/interface/` |
474
+ | [`docs/contracts/cli.md`](docs/contracts/cli.md) | `theorum/cli` |
475
+ | [`docs/contracts/presets.md`](docs/contracts/presets.md) | `theorum/presets` |
476
+ | [`docs/contracts/presets-google.md`](docs/contracts/presets-google.md) | `theorum/presets/google` |
477
+
478
+ Migrating from per-turn `dynamicTools`? See
479
+ [`docs/MIGRATION-tool-system.md`](docs/MIGRATION-tool-system.md).
480
+ Ownership-boundary cut (playground out of package, quota/lexicon/composer)? See
481
+ [`docs/MIGRATION-boundary.md`](docs/MIGRATION-boundary.md).
384
482
 
385
483
  Document health is enforced by `npm run lint:docs` — the **first** step of
386
484
  `npm run lint` / `deno task lint` (`docs/_map.mjs`):
@@ -389,6 +487,9 @@ Document health is enforced by `npm run lint:docs` — the **first** step of
389
487
  - Export parity with `package.json` and export-drift vs entry `mod.ts` files
390
488
  - Doc + **section** freshness on every code change (no Export-only gaming)
391
489
  - Behavioral sections require `contract_test` evidence (≥2 supports each)
490
+ - Publish gates keep `docs/` and `src/**/*.md` out of npm/JSR (`verify-publish-bundle`)
491
+ - Freshness diffs use a 32 MiB `git` buffer so large `origin/main...HEAD` patches
492
+ are not silently dropped (`ENOBUFS`)
392
493
  - Pre-commit runs `lint:docs` automatically (`prepare` installs the hook on `npm install`)
393
494
 
394
495
  ---
@@ -399,9 +500,22 @@ Document health is enforced by `npm run lint:docs` — the **first** step of
399
500
  npm install
400
501
  npm run test
401
502
  npm run lint
402
- deno publish --dry-run --allow-dirty
503
+ deno install
504
+ deno publish --dry-run
505
+ npm run build:npm
506
+ cd npm && npm pack
403
507
  ```
404
508
 
509
+ To dry-run npm publish when the current version is already on the registry, bump to an ephemeral prerelease first (CI does this automatically):
510
+
511
+ ```bash
512
+ cd npm
513
+ npm version 0.0.0-pr.local --no-git-tag-version
514
+ npm publish --dry-run --access public --tag ci-validate
515
+ ```
516
+
517
+ PR CI runs JSR and npm dry-run checks in the required `publish-dry-run` job.
518
+
405
519
  Run the packaged CLI locally:
406
520
 
407
521
  ```bash
@@ -418,16 +532,16 @@ cd npm
418
532
  npm pack
419
533
  ```
420
534
 
421
- Run a live OpenRouter smoke test with a host-resolved key. The key is passed as an argument and is never read from a Theorum `.env` file.
535
+ Run an OpenRouter provider smoke test with a host-resolved key. The key is passed as an argument and is never read from a Theorum `.env` file.
422
536
 
423
537
  ```bash
424
- deno run --allow-net scripts/verify-live.ts --api-key "$OPENROUTER_API_KEY"
538
+ deno run --allow-net scripts/verify-provider-smoke.ts --api-key "$OPENROUTER_API_KEY"
425
539
  ```
426
540
 
427
- The default live verifier uses `perplexity/sonar` because it is broadly available on OpenRouter. Hosts can override both the profile-facing model id and provider-native id:
541
+ The default smoke uses `perplexity/sonar` because it is broadly available on OpenRouter. Hosts can override both the profile-facing model id and provider-native id:
428
542
 
429
543
  ```bash
430
- deno run --allow-net scripts/verify-live.ts \
544
+ deno run --allow-net scripts/verify-provider-smoke.ts \
431
545
  --api-key "$OPENROUTER_API_KEY" \
432
546
  --model hostFastModel \
433
547
  --api-id perplexity/sonar
@@ -441,15 +555,42 @@ THEORUM is ready for host applications when these statements stay true:
441
555
 
442
556
  ```toml
443
557
  [boundary]
558
+ # Rule: "Host decides, Theorum runs."
444
559
  profiles_in_package = false
560
+ demos_in_package = false
445
561
  env_files_in_package = false
446
562
  ambient_secret_reads = false
447
563
  business_logic_in_kernel = false
564
+ unownable_user_or_model_copy = false
448
565
  provider_keys_host_owned = true
566
+ provider_adapters_lazy = true
449
567
  trace_sinks_host_injected = true
450
568
  realtime_duplex_voice = "out of scope"
451
569
  ```
452
570
 
571
+ **Facts vs policy.** Provider facts may ship (model capabilities, wire shapes,
572
+ protocol metadata — e.g. `theorum/presets/google`). Product policy may not
573
+ (prompts, personas, end-user copy, demo apps, channel behavior). Every
574
+ user- or model-visible string is either host-supplied or an overridable
575
+ registered default in the kernel lexicon (`overrideLexicon`). Behavioral
576
+ defaults live as typed profile-schema fields. Optional packages
577
+ (`playground/`) are inert extras: deleting them changes no kernel behavior.
578
+
579
+ Invariant properties (machine-checked where noted):
580
+
581
+ | Id | Property | Check |
582
+ | --- | --- | --- |
583
+ | P1 | No ambient authority — construct with every Deno permission denied | `tests/kernel/zero-permission-import.test.ts` |
584
+ | P2 | No unownable words — user/model-visible strings are host-suppliable or lexicon defaults | lexicon + full-tree `scripts/docs-truth/copy-lint.mjs` + two-hosts test |
585
+ | P3 | No buried policy — behavioral defaults are declared profile-schema fields | `PROFILE_FIELDS` / schema |
586
+ | P4 | Inert extras — optional entrypoints removable without behavior change | publish-bundle gate excludes `playground/` |
587
+
588
+ Provider adapters load **lazily** on the first `complete` for that transport —
589
+ `createProvider` and `theorum/providers` stay a thin barrel (`src/providers/mod.ts`);
590
+ implementation modules (e.g. `google/interactions/`, `openrouter/`, `local/`) are
591
+ not pulled in at import time. `trace-attach` lazy-loads Interactions wire helpers
592
+ only for `geminiInteractions` traces.
593
+
453
594
  If an app needs domain rules, platform delivery policy, product copy, database access, or session memory, that belongs outside THEORUM.
454
595
 
455
596
  ---
@@ -487,8 +628,10 @@ MIT License. Copyright (c) ORCHID AI LLC.
487
628
  },
488
629
  "Package Boundary": {
489
630
  "supports": [
490
- { "kind": "source", "path": "src/providers/create-provider.ts" },
491
- { "kind": "contract_test", "path": "tests/providers/create-provider.test.ts" }
631
+ { "kind": "source", "path": "src/providers/mod.ts" },
632
+ { "kind": "source", "path": "src/guardrails/lexicon.ts" },
633
+ { "kind": "contract_test", "path": "tests/kernel/two-hosts-boundary.test.ts" },
634
+ { "kind": "contract_test", "path": "tests/kernel/zero-permission-import.test.ts" }
492
635
  ]
493
636
  }
494
637
  }