@theoremjs/agents 0.0.0-stage → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (588) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +545 -2
  3. package/esm/_dnt.polyfills.d.ts +66 -0
  4. package/esm/_dnt.polyfills.js +51 -0
  5. package/esm/mod.d.ts +79 -0
  6. package/esm/mod.js +49 -0
  7. package/esm/package.json +3 -0
  8. package/esm/src/cli/bin.d.ts +7 -0
  9. package/esm/src/cli/bin.js +9 -0
  10. package/esm/src/cli/commands/bench.d.ts +6 -0
  11. package/esm/src/cli/commands/bench.js +480 -0
  12. package/esm/src/cli/commands/detect-compile.d.ts +17 -0
  13. package/esm/src/cli/commands/detect-compile.js +22 -0
  14. package/esm/src/cli/commands/eval.d.ts +37 -0
  15. package/esm/src/cli/commands/eval.js +190 -0
  16. package/esm/src/cli/commands/fuzz-canary.d.ts +15 -0
  17. package/esm/src/cli/commands/fuzz-canary.js +241 -0
  18. package/esm/src/cli/commands/fuzz-guardrails.d.ts +2 -0
  19. package/esm/src/cli/commands/fuzz-guardrails.js +5 -0
  20. package/esm/src/cli/commands/profile.d.ts +2 -0
  21. package/esm/src/cli/commands/profile.js +80 -0
  22. package/esm/src/cli/commands/run.d.ts +13 -0
  23. package/esm/src/cli/commands/run.js +45 -0
  24. package/esm/src/cli/commands/test.d.ts +26 -0
  25. package/esm/src/cli/commands/test.js +143 -0
  26. package/esm/src/cli/event-log.d.ts +15 -0
  27. package/esm/src/cli/event-log.js +136 -0
  28. package/esm/src/cli/index.d.ts +10 -0
  29. package/esm/src/cli/index.js +267 -0
  30. package/esm/src/cli/matrix/fixtures.d.ts +10 -0
  31. package/esm/src/cli/matrix/fixtures.js +67 -0
  32. package/esm/src/cli/matrix/synthesizer.d.ts +18 -0
  33. package/esm/src/cli/matrix/synthesizer.js +103 -0
  34. package/esm/src/evals/attachments.d.ts +5 -0
  35. package/esm/src/evals/attachments.js +55 -0
  36. package/esm/src/evals/breakdown.d.ts +30 -0
  37. package/esm/src/evals/breakdown.js +69 -0
  38. package/esm/src/evals/graders/answer.d.ts +16 -0
  39. package/esm/src/evals/graders/answer.js +134 -0
  40. package/esm/src/evals/graders/code.d.ts +40 -0
  41. package/esm/src/evals/graders/code.js +184 -0
  42. package/esm/src/evals/graders/judge.d.ts +24 -0
  43. package/esm/src/evals/graders/judge.js +367 -0
  44. package/esm/src/evals/graders/latency.d.ts +6 -0
  45. package/esm/src/evals/graders/latency.js +82 -0
  46. package/esm/src/evals/graders/live.d.ts +14 -0
  47. package/esm/src/evals/graders/live.js +42 -0
  48. package/esm/src/evals/graders/media.d.ts +18 -0
  49. package/esm/src/evals/graders/media.js +91 -0
  50. package/esm/src/evals/graders/shared.d.ts +11 -0
  51. package/esm/src/evals/graders/shared.js +62 -0
  52. package/esm/src/evals/graders/transcript.d.ts +19 -0
  53. package/esm/src/evals/graders/transcript.js +202 -0
  54. package/esm/src/evals/mod.d.ts +34 -0
  55. package/esm/src/evals/mod.js +22 -0
  56. package/esm/src/evals/record.d.ts +41 -0
  57. package/esm/src/evals/record.js +107 -0
  58. package/esm/src/evals/rubrics/catalog.d.ts +148 -0
  59. package/esm/src/evals/rubrics/catalog.js +846 -0
  60. package/esm/src/evals/rubrics/mod.d.ts +24 -0
  61. package/esm/src/evals/rubrics/mod.js +55 -0
  62. package/esm/src/evals/rubrics/mustache.d.ts +23 -0
  63. package/esm/src/evals/rubrics/mustache.js +154 -0
  64. package/esm/src/evals/rubrics/types.d.ts +54 -0
  65. package/esm/src/evals/rubrics/types.js +74 -0
  66. package/esm/src/evals/run.d.ts +75 -0
  67. package/esm/src/evals/run.js +418 -0
  68. package/esm/src/evals/suite.d.ts +28 -0
  69. package/esm/src/evals/suite.js +97 -0
  70. package/esm/src/evals/summary.d.ts +35 -0
  71. package/esm/src/evals/summary.js +31 -0
  72. package/esm/src/evals/trial.d.ts +14 -0
  73. package/esm/src/evals/trial.js +135 -0
  74. package/esm/src/evals/types.d.ts +169 -0
  75. package/esm/src/evals/types.js +113 -0
  76. package/esm/src/evals/verdict.d.ts +23 -0
  77. package/esm/src/evals/verdict.js +48 -0
  78. package/esm/src/guardrails/boundaries.d.ts +41 -0
  79. package/esm/src/guardrails/boundaries.js +103 -0
  80. package/esm/src/guardrails/canary-gate.d.ts +22 -0
  81. package/esm/src/guardrails/canary-gate.js +40 -0
  82. package/esm/src/guardrails/canary.d.ts +146 -0
  83. package/esm/src/guardrails/canary.js +974 -0
  84. package/esm/src/guardrails/compile-egress.d.ts +12 -0
  85. package/esm/src/guardrails/compile-egress.js +12 -0
  86. package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +24 -0
  87. package/esm/src/guardrails/corpus/canary-egress-attacks.js +305 -0
  88. package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +5 -0
  89. package/esm/src/guardrails/corpus/fuzz-inbound.js +199 -0
  90. package/esm/src/guardrails/corpus/inbound-payloads.d.ts +5 -0
  91. package/esm/src/guardrails/corpus/inbound-payloads.js +120 -0
  92. package/esm/src/guardrails/corpus/live-attacks.d.ts +15 -0
  93. package/esm/src/guardrails/corpus/live-attacks.js +229 -0
  94. package/esm/src/guardrails/corpus/mod.d.ts +7 -0
  95. package/esm/src/guardrails/corpus/mod.js +4 -0
  96. package/esm/src/guardrails/corpus/secrets.d.ts +12 -0
  97. package/esm/src/guardrails/corpus/secrets.js +16 -0
  98. package/esm/src/guardrails/corpus/strings.d.ts +23 -0
  99. package/esm/src/guardrails/corpus/strings.js +29 -0
  100. package/esm/src/guardrails/corpus/types.d.ts +36 -0
  101. package/esm/src/guardrails/corpus/types.js +1 -0
  102. package/esm/src/guardrails/credential-rules.d.ts +36 -0
  103. package/esm/src/guardrails/credential-rules.js +3001 -0
  104. package/esm/src/guardrails/credential-scan.d.ts +45 -0
  105. package/esm/src/guardrails/credential-scan.js +172 -0
  106. package/esm/src/guardrails/destinations.d.ts +45 -0
  107. package/esm/src/guardrails/destinations.js +234 -0
  108. package/esm/src/guardrails/detect-at.d.ts +107 -0
  109. package/esm/src/guardrails/detect-at.js +434 -0
  110. package/esm/src/guardrails/detect-reply.d.ts +61 -0
  111. package/esm/src/guardrails/detect-reply.js +95 -0
  112. package/esm/src/guardrails/detect-stream.d.ts +33 -0
  113. package/esm/src/guardrails/detect-stream.js +77 -0
  114. package/esm/src/guardrails/detectors.d.ts +216 -0
  115. package/esm/src/guardrails/detectors.js +503 -0
  116. package/esm/src/guardrails/egress-automata.d.ts +12 -0
  117. package/esm/src/guardrails/egress-automata.js +25582 -0
  118. package/esm/src/guardrails/egress-compiler.d.ts +55 -0
  119. package/esm/src/guardrails/egress-compiler.js +431 -0
  120. package/esm/src/guardrails/egress-patterns.d.ts +26 -0
  121. package/esm/src/guardrails/egress-patterns.js +36 -0
  122. package/esm/src/guardrails/egress-stream.d.ts +123 -0
  123. package/esm/src/guardrails/egress-stream.js +774 -0
  124. package/esm/src/guardrails/egress-urls.d.ts +86 -0
  125. package/esm/src/guardrails/egress-urls.js +909 -0
  126. package/esm/src/guardrails/egress.d.ts +46 -0
  127. package/esm/src/guardrails/egress.js +124 -0
  128. package/esm/src/guardrails/error.d.ts +31 -0
  129. package/esm/src/guardrails/error.js +96 -0
  130. package/esm/src/guardrails/event-schemas.d.ts +100 -0
  131. package/esm/src/guardrails/event-schemas.js +48 -0
  132. package/esm/src/guardrails/events.d.ts +13 -0
  133. package/esm/src/guardrails/events.js +45 -0
  134. package/esm/src/guardrails/hints.d.ts +14 -0
  135. package/esm/src/guardrails/hints.js +57 -0
  136. package/esm/src/guardrails/hits.d.ts +14 -0
  137. package/esm/src/guardrails/hits.js +30 -0
  138. package/esm/src/guardrails/host-patterns.d.ts +85 -0
  139. package/esm/src/guardrails/host-patterns.js +173 -0
  140. package/esm/src/guardrails/injection-patterns.d.ts +20 -0
  141. package/esm/src/guardrails/injection-patterns.js +126 -0
  142. package/esm/src/guardrails/injection-reversed.d.ts +9 -0
  143. package/esm/src/guardrails/injection-reversed.js +49 -0
  144. package/esm/src/guardrails/injection.d.ts +30 -0
  145. package/esm/src/guardrails/injection.js +460 -0
  146. package/esm/src/guardrails/labels.d.ts +40 -0
  147. package/esm/src/guardrails/labels.js +874 -0
  148. package/esm/src/guardrails/live-outbound-gate.d.ts +55 -0
  149. package/esm/src/guardrails/live-outbound-gate.js +298 -0
  150. package/esm/src/guardrails/mod.d.ts +54 -0
  151. package/esm/src/guardrails/mod.js +34 -0
  152. package/esm/src/guardrails/network.d.ts +46 -0
  153. package/esm/src/guardrails/network.js +330 -0
  154. package/esm/src/guardrails/normalize.d.ts +20 -0
  155. package/esm/src/guardrails/normalize.js +165 -0
  156. package/esm/src/guardrails/policy.d.ts +4 -0
  157. package/esm/src/guardrails/policy.js +21 -0
  158. package/esm/src/guardrails/progressive-yield.d.ts +110 -0
  159. package/esm/src/guardrails/progressive-yield.js +281 -0
  160. package/esm/src/guardrails/prompt-echo.d.ts +40 -0
  161. package/esm/src/guardrails/prompt-echo.js +306 -0
  162. package/esm/src/guardrails/quota.d.ts +21 -0
  163. package/esm/src/guardrails/quota.js +80 -0
  164. package/esm/src/guardrails/rules.d.ts +47 -0
  165. package/esm/src/guardrails/rules.js +47 -0
  166. package/esm/src/guardrails/sanitize.d.ts +40 -0
  167. package/esm/src/guardrails/sanitize.js +160 -0
  168. package/esm/src/guardrails/sensitive-groups.d.ts +21 -0
  169. package/esm/src/guardrails/sensitive-groups.js +12 -0
  170. package/esm/src/guardrails/sensitive.d.ts +22 -0
  171. package/esm/src/guardrails/sensitive.js +234 -0
  172. package/esm/src/guardrails/serialize.d.ts +15 -0
  173. package/esm/src/guardrails/serialize.js +37 -0
  174. package/esm/src/guardrails/testing.d.ts +11 -0
  175. package/esm/src/guardrails/testing.js +10 -0
  176. package/esm/src/guardrails/theorem-error.d.ts +21 -0
  177. package/esm/src/guardrails/theorem-error.js +54 -0
  178. package/esm/src/guardrails/thought-guard.d.ts +76 -0
  179. package/esm/src/guardrails/thought-guard.js +664 -0
  180. package/esm/src/guardrails/tool-directives.d.ts +38 -0
  181. package/esm/src/guardrails/tool-directives.js +195 -0
  182. package/esm/src/guardrails/tool-leak.d.ts +26 -0
  183. package/esm/src/guardrails/tool-leak.js +45 -0
  184. package/esm/src/guardrails/tool-result.d.ts +92 -0
  185. package/esm/src/guardrails/tool-result.js +272 -0
  186. package/esm/src/guardrails/types.d.ts +250 -0
  187. package/esm/src/guardrails/types.js +58 -0
  188. package/esm/src/host/client-turn.d.ts +36 -0
  189. package/esm/src/host/client-turn.js +80 -0
  190. package/esm/src/host/mint-trace.d.ts +29 -0
  191. package/esm/src/host/mint-trace.js +60 -0
  192. package/esm/src/host/mod.d.ts +13 -0
  193. package/esm/src/host/mod.js +11 -0
  194. package/esm/src/host/readStreamingJsonStringField.d.ts +2 -0
  195. package/esm/src/host/readStreamingJsonStringField.js +27 -0
  196. package/esm/src/host/reply.d.ts +13 -0
  197. package/esm/src/host/reply.js +41 -0
  198. package/esm/src/interface/blocks.d.ts +11 -0
  199. package/esm/src/interface/blocks.js +283 -0
  200. package/esm/src/interface/composer-actions.d.ts +24 -0
  201. package/esm/src/interface/composer-actions.js +33 -0
  202. package/esm/src/interface/draft.d.ts +20 -0
  203. package/esm/src/interface/draft.js +39 -0
  204. package/esm/src/interface/from-profile.d.ts +24 -0
  205. package/esm/src/interface/from-profile.js +179 -0
  206. package/esm/src/interface/history.d.ts +46 -0
  207. package/esm/src/interface/history.js +278 -0
  208. package/esm/src/interface/inputs.d.ts +11 -0
  209. package/esm/src/interface/inputs.js +67 -0
  210. package/esm/src/interface/mod.d.ts +33 -0
  211. package/esm/src/interface/mod.js +22 -0
  212. package/esm/src/interface/models.d.ts +26 -0
  213. package/esm/src/interface/models.js +50 -0
  214. package/esm/src/interface/pending.d.ts +57 -0
  215. package/esm/src/interface/pending.js +152 -0
  216. package/esm/src/interface/profile-interface.d.ts +4 -0
  217. package/esm/src/interface/profile-interface.js +237 -0
  218. package/esm/src/interface/session.d.ts +71 -0
  219. package/esm/src/interface/session.js +128 -0
  220. package/esm/src/interface/stop.d.ts +13 -0
  221. package/esm/src/interface/stop.js +28 -0
  222. package/esm/src/interface/tool-calls.d.ts +32 -0
  223. package/esm/src/interface/tool-calls.js +127 -0
  224. package/esm/src/interface/tool-invoke.d.ts +6 -0
  225. package/esm/src/interface/tool-invoke.js +22 -0
  226. package/esm/src/interface/tool-media.d.ts +11 -0
  227. package/esm/src/interface/tool-media.js +113 -0
  228. package/esm/src/interface/types.d.ts +278 -0
  229. package/esm/src/interface/types.js +5 -0
  230. package/esm/src/kernel/api-catalog.d.ts +85 -0
  231. package/esm/src/kernel/api-catalog.js +229 -0
  232. package/esm/src/kernel/auth/credential-source.d.ts +23 -0
  233. package/esm/src/kernel/auth/credential-source.js +11 -0
  234. package/esm/src/kernel/auth/crypto.d.ts +56 -0
  235. package/esm/src/kernel/auth/crypto.js +163 -0
  236. package/esm/src/kernel/auth/mod.d.ts +5 -0
  237. package/esm/src/kernel/auth/mod.js +5 -0
  238. package/esm/src/kernel/auth/oauth.d.ts +18 -0
  239. package/esm/src/kernel/auth/oauth.js +439 -0
  240. package/esm/src/kernel/auth/scope-refusal.d.ts +9 -0
  241. package/esm/src/kernel/auth/scope-refusal.js +8 -0
  242. package/esm/src/kernel/auth/typed-secret.d.ts +13 -0
  243. package/esm/src/kernel/auth/typed-secret.js +23 -0
  244. package/esm/src/kernel/auth/types.d.ts +152 -0
  245. package/esm/src/kernel/auth/types.js +1 -0
  246. package/esm/src/kernel/checkpoint.d.ts +43 -0
  247. package/esm/src/kernel/checkpoint.js +58 -0
  248. package/esm/src/kernel/default-scope.d.ts +72 -0
  249. package/esm/src/kernel/default-scope.js +121 -0
  250. package/esm/src/kernel/engine/call-trace.d.ts +19 -0
  251. package/esm/src/kernel/engine/call-trace.js +29 -0
  252. package/esm/src/kernel/engine/compaction.d.ts +71 -0
  253. package/esm/src/kernel/engine/compaction.js +203 -0
  254. package/esm/src/kernel/engine/decision.d.ts +43 -0
  255. package/esm/src/kernel/engine/decision.js +439 -0
  256. package/esm/src/kernel/engine/embedding.d.ts +8 -0
  257. package/esm/src/kernel/engine/embedding.js +118 -0
  258. package/esm/src/kernel/engine/hash.d.ts +6 -0
  259. package/esm/src/kernel/engine/hash.js +22 -0
  260. package/esm/src/kernel/engine/live-inbound.d.ts +19 -0
  261. package/esm/src/kernel/engine/live-inbound.js +44 -0
  262. package/esm/src/kernel/engine/live-ingress.d.ts +15 -0
  263. package/esm/src/kernel/engine/live-ingress.js +42 -0
  264. package/esm/src/kernel/engine/record.d.ts +3 -0
  265. package/esm/src/kernel/engine/record.js +11 -0
  266. package/esm/src/kernel/engine/repair.d.ts +7 -0
  267. package/esm/src/kernel/engine/repair.js +36 -0
  268. package/esm/src/kernel/engine/runner/gates.d.ts +5 -0
  269. package/esm/src/kernel/engine/runner/gates.js +508 -0
  270. package/esm/src/kernel/engine/runner/mod.d.ts +29 -0
  271. package/esm/src/kernel/engine/runner/mod.js +625 -0
  272. package/esm/src/kernel/engine/runner/nested.d.ts +32 -0
  273. package/esm/src/kernel/engine/runner/nested.js +134 -0
  274. package/esm/src/kernel/engine/runner/schema-validation.d.ts +35 -0
  275. package/esm/src/kernel/engine/runner/schema-validation.js +179 -0
  276. package/esm/src/kernel/engine/runner/stages.d.ts +28 -0
  277. package/esm/src/kernel/engine/runner/stages.js +41 -0
  278. package/esm/src/kernel/engine/runner/state.d.ts +97 -0
  279. package/esm/src/kernel/engine/runner/state.js +50 -0
  280. package/esm/src/kernel/engine/runner/step-order.d.ts +27 -0
  281. package/esm/src/kernel/engine/runner/step-order.js +165 -0
  282. package/esm/src/kernel/engine/runner/steps.d.ts +14 -0
  283. package/esm/src/kernel/engine/runner/steps.js +519 -0
  284. package/esm/src/kernel/engine/runner/stream.d.ts +71 -0
  285. package/esm/src/kernel/engine/runner/stream.js +376 -0
  286. package/esm/src/kernel/engine/runner/usage.d.ts +45 -0
  287. package/esm/src/kernel/engine/runner/usage.js +148 -0
  288. package/esm/src/kernel/engine/session/mod.d.ts +33 -0
  289. package/esm/src/kernel/engine/session/mod.js +1182 -0
  290. package/esm/src/kernel/engine/session/session-trace.d.ts +115 -0
  291. package/esm/src/kernel/engine/session/session-trace.js +500 -0
  292. package/esm/src/kernel/engine/token-estimate.d.ts +23 -0
  293. package/esm/src/kernel/engine/token-estimate.js +38 -0
  294. package/esm/src/kernel/engine/tool-trace.d.ts +58 -0
  295. package/esm/src/kernel/engine/tool-trace.js +132 -0
  296. package/esm/src/kernel/engine/tree.d.ts +2 -0
  297. package/esm/src/kernel/engine/tree.js +17 -0
  298. package/esm/src/kernel/engine/turn-trace.d.ts +119 -0
  299. package/esm/src/kernel/engine/turn-trace.js +855 -0
  300. package/esm/src/kernel/engine/usage.d.ts +36 -0
  301. package/esm/src/kernel/engine/usage.js +151 -0
  302. package/esm/src/kernel/interaction-parts.d.ts +13 -0
  303. package/esm/src/kernel/interaction-parts.js +40 -0
  304. package/esm/src/kernel/json.d.ts +10 -0
  305. package/esm/src/kernel/json.js +3 -0
  306. package/esm/src/kernel/mod.d.ts +74 -0
  307. package/esm/src/kernel/mod.js +45 -0
  308. package/esm/src/kernel/profile-graph.d.ts +145 -0
  309. package/esm/src/kernel/profile-graph.js +158 -0
  310. package/esm/src/kernel/profile-presence.d.ts +18 -0
  311. package/esm/src/kernel/profile-presence.js +158 -0
  312. package/esm/src/kernel/profile-scope.d.ts +40 -0
  313. package/esm/src/kernel/profile-scope.js +274 -0
  314. package/esm/src/kernel/protocols/binding.d.ts +65 -0
  315. package/esm/src/kernel/protocols/binding.js +125 -0
  316. package/esm/src/kernel/protocols/limits.d.ts +15 -0
  317. package/esm/src/kernel/protocols/limits.js +28 -0
  318. package/esm/src/kernel/protocols/mod.d.ts +5 -0
  319. package/esm/src/kernel/protocols/mod.js +5 -0
  320. package/esm/src/kernel/protocols/prices.d.ts +36 -0
  321. package/esm/src/kernel/protocols/prices.js +37 -0
  322. package/esm/src/kernel/protocols/protocol.d.ts +253 -0
  323. package/esm/src/kernel/protocols/protocol.js +253 -0
  324. package/esm/src/kernel/protocols/provider.d.ts +108 -0
  325. package/esm/src/kernel/protocols/provider.js +143 -0
  326. package/esm/src/kernel/protocols/runtime.d.ts +55 -0
  327. package/esm/src/kernel/protocols/runtime.js +534 -0
  328. package/esm/src/kernel/provider-budget.d.ts +32 -0
  329. package/esm/src/kernel/provider-budget.js +262 -0
  330. package/esm/src/kernel/provider-dispatch.d.ts +13 -0
  331. package/esm/src/kernel/provider-dispatch.js +36 -0
  332. package/esm/src/kernel/provider-live.d.ts +7 -0
  333. package/esm/src/kernel/provider-live.js +189 -0
  334. package/esm/src/kernel/provider-runtime.d.ts +57 -0
  335. package/esm/src/kernel/provider-runtime.js +524 -0
  336. package/esm/src/kernel/registry/attachments.d.ts +46 -0
  337. package/esm/src/kernel/registry/attachments.js +220 -0
  338. package/esm/src/kernel/registry/catalog.d.ts +25 -0
  339. package/esm/src/kernel/registry/catalog.js +85 -0
  340. package/esm/src/kernel/registry/embedding-rules.d.ts +28 -0
  341. package/esm/src/kernel/registry/embedding-rules.js +66 -0
  342. package/esm/src/kernel/registry/ingress.d.ts +12 -0
  343. package/esm/src/kernel/registry/ingress.js +179 -0
  344. package/esm/src/kernel/registry/kernel-registry.d.ts +15 -0
  345. package/esm/src/kernel/registry/kernel-registry.js +15 -0
  346. package/esm/src/kernel/registry/profile-outputs.d.ts +3 -0
  347. package/esm/src/kernel/registry/profile-outputs.js +10 -0
  348. package/esm/src/kernel/registry/profiles.d.ts +116 -0
  349. package/esm/src/kernel/registry/profiles.js +773 -0
  350. package/esm/src/kernel/registry/provider-request.d.ts +6 -0
  351. package/esm/src/kernel/registry/provider-request.js +34 -0
  352. package/esm/src/kernel/registry/resolve.d.ts +17 -0
  353. package/esm/src/kernel/registry/resolve.js +238 -0
  354. package/esm/src/kernel/registry/schemas.d.ts +12 -0
  355. package/esm/src/kernel/registry/schemas.js +27 -0
  356. package/esm/src/kernel/registry/slot-fill.d.ts +7 -0
  357. package/esm/src/kernel/registry/slot-fill.js +22 -0
  358. package/esm/src/kernel/registry/sole-model.d.ts +2 -0
  359. package/esm/src/kernel/registry/sole-model.js +4 -0
  360. package/esm/src/kernel/registry/system-prompt.d.ts +8 -0
  361. package/esm/src/kernel/registry/system-prompt.js +32 -0
  362. package/esm/src/kernel/registry/system-role.d.ts +3 -0
  363. package/esm/src/kernel/registry/system-role.js +9 -0
  364. package/esm/src/kernel/registry/vault.d.ts +14 -0
  365. package/esm/src/kernel/registry/vault.js +1 -0
  366. package/esm/src/kernel/schema.d.ts +226 -0
  367. package/esm/src/kernel/schema.js +895 -0
  368. package/esm/src/kernel/scope.d.ts +29 -0
  369. package/esm/src/kernel/scope.js +45 -0
  370. package/esm/src/kernel/stages.d.ts +176 -0
  371. package/esm/src/kernel/stages.js +431 -0
  372. package/esm/src/kernel/stop.d.ts +116 -0
  373. package/esm/src/kernel/stop.js +137 -0
  374. package/esm/src/kernel/system-parts.d.ts +26 -0
  375. package/esm/src/kernel/system-parts.js +98 -0
  376. package/esm/src/kernel/tools/activity-label.d.ts +22 -0
  377. package/esm/src/kernel/tools/activity-label.js +435 -0
  378. package/esm/src/kernel/tools/agent.d.ts +66 -0
  379. package/esm/src/kernel/tools/agent.js +212 -0
  380. package/esm/src/kernel/tools/coordinate.d.ts +4 -0
  381. package/esm/src/kernel/tools/coordinate.js +165 -0
  382. package/esm/src/kernel/tools/events.d.ts +66 -0
  383. package/esm/src/kernel/tools/events.js +184 -0
  384. package/esm/src/kernel/tools/execute.d.ts +100 -0
  385. package/esm/src/kernel/tools/execute.js +1173 -0
  386. package/esm/src/kernel/tools/gate-answer.d.ts +60 -0
  387. package/esm/src/kernel/tools/gate-answer.js +65 -0
  388. package/esm/src/kernel/tools/handler-events.d.ts +45 -0
  389. package/esm/src/kernel/tools/handler-events.js +106 -0
  390. package/esm/src/kernel/tools/harness.d.ts +17 -0
  391. package/esm/src/kernel/tools/harness.js +43 -0
  392. package/esm/src/kernel/tools/http-limit.d.ts +27 -0
  393. package/esm/src/kernel/tools/http-limit.js +85 -0
  394. package/esm/src/kernel/tools/invoke.d.ts +32 -0
  395. package/esm/src/kernel/tools/invoke.js +282 -0
  396. package/esm/src/kernel/tools/mcp-sessions.d.ts +23 -0
  397. package/esm/src/kernel/tools/mcp-sessions.js +69 -0
  398. package/esm/src/kernel/tools/mod.d.ts +10 -0
  399. package/esm/src/kernel/tools/mod.js +7 -0
  400. package/esm/src/kernel/tools/model-text.d.ts +16 -0
  401. package/esm/src/kernel/tools/model-text.js +31 -0
  402. package/esm/src/kernel/tools/operation.d.ts +76 -0
  403. package/esm/src/kernel/tools/operation.js +100 -0
  404. package/esm/src/kernel/tools/page-answer.d.ts +8 -0
  405. package/esm/src/kernel/tools/page-answer.js +19 -0
  406. package/esm/src/kernel/tools/permission.d.ts +15 -0
  407. package/esm/src/kernel/tools/permission.js +38 -0
  408. package/esm/src/kernel/tools/project.d.ts +11 -0
  409. package/esm/src/kernel/tools/project.js +55 -0
  410. package/esm/src/kernel/tools/registry.d.ts +27 -0
  411. package/esm/src/kernel/tools/registry.js +143 -0
  412. package/esm/src/kernel/tools/remote.d.ts +144 -0
  413. package/esm/src/kernel/tools/remote.js +1125 -0
  414. package/esm/src/kernel/tools/resolve.d.ts +31 -0
  415. package/esm/src/kernel/tools/resolve.js +267 -0
  416. package/esm/src/kernel/tools/run.d.ts +28 -0
  417. package/esm/src/kernel/tools/run.js +86 -0
  418. package/esm/src/kernel/tools/schema.d.ts +10 -0
  419. package/esm/src/kernel/tools/schema.js +95 -0
  420. package/esm/src/kernel/tools/signed-in-fetch.d.ts +19 -0
  421. package/esm/src/kernel/tools/signed-in-fetch.js +45 -0
  422. package/esm/src/kernel/tools/stage-run.d.ts +116 -0
  423. package/esm/src/kernel/tools/stage-run.js +198 -0
  424. package/esm/src/kernel/tools/types.d.ts +497 -0
  425. package/esm/src/kernel/tools/types.js +1 -0
  426. package/esm/src/kernel/tools/unchecked-output.d.ts +14 -0
  427. package/esm/src/kernel/tools/unchecked-output.js +18 -0
  428. package/esm/src/kernel/turn-events.d.ts +776 -0
  429. package/esm/src/kernel/turn-events.js +543 -0
  430. package/esm/src/kernel/types.d.ts +1025 -0
  431. package/esm/src/kernel/types.js +1 -0
  432. package/esm/src/kernel/util/base64.d.ts +4 -0
  433. package/esm/src/kernel/util/base64.js +15 -0
  434. package/esm/src/kernel/util/exact-type.d.ts +31 -0
  435. package/esm/src/kernel/util/exact-type.js +1 -0
  436. package/esm/src/kernel/util/find-last.d.ts +3 -0
  437. package/esm/src/kernel/util/find-last.js +9 -0
  438. package/esm/src/kernel/util/mime.d.ts +2 -0
  439. package/esm/src/kernel/util/mime.js +4 -0
  440. package/esm/src/kernel/util/partial-json.d.ts +23 -0
  441. package/esm/src/kernel/util/partial-json.js +128 -0
  442. package/esm/src/kernel/util/record.d.ts +2 -0
  443. package/esm/src/kernel/util/record.js +4 -0
  444. package/esm/src/observability/destinations.d.ts +14 -0
  445. package/esm/src/observability/destinations.js +43 -0
  446. package/esm/src/observability/jsonl.d.ts +18 -0
  447. package/esm/src/observability/jsonl.js +110 -0
  448. package/esm/src/observability/mod.d.ts +22 -0
  449. package/esm/src/observability/mod.js +16 -0
  450. package/esm/src/observability/openinference.d.ts +11 -0
  451. package/esm/src/observability/openinference.js +205 -0
  452. package/esm/src/observability/otlp.d.ts +71 -0
  453. package/esm/src/observability/otlp.js +98 -0
  454. package/esm/src/observability/phoenix.d.ts +30 -0
  455. package/esm/src/observability/phoenix.js +71 -0
  456. package/esm/src/observability/policy.d.ts +17 -0
  457. package/esm/src/observability/policy.js +73 -0
  458. package/esm/src/observability/resolve-policy.d.ts +8 -0
  459. package/esm/src/observability/resolve-policy.js +64 -0
  460. package/esm/src/observability/spans.d.ts +22 -0
  461. package/esm/src/observability/spans.js +72 -0
  462. package/esm/src/observability/trace-catalog.d.ts +128 -0
  463. package/esm/src/observability/trace-catalog.js +906 -0
  464. package/esm/src/observability/trace-record.d.ts +23 -0
  465. package/esm/src/observability/trace-record.js +228 -0
  466. package/esm/src/observability/trace-schema.d.ts +57 -0
  467. package/esm/src/observability/trace-schema.js +59 -0
  468. package/esm/src/observability/trace-sink.d.ts +14 -0
  469. package/esm/src/observability/trace-sink.js +3 -0
  470. package/esm/src/observability/trace-span.d.ts +98 -0
  471. package/esm/src/observability/trace-span.js +201 -0
  472. package/esm/src/observability/trace.d.ts +13 -0
  473. package/esm/src/observability/trace.js +37 -0
  474. package/esm/src/observability/types.d.ts +109 -0
  475. package/esm/src/observability/types.js +3 -0
  476. package/esm/src/presets/google/speech-voices.d.ts +9 -0
  477. package/esm/src/presets/google/speech-voices.js +38 -0
  478. package/esm/src/presets/google-limits.d.ts +13 -0
  479. package/esm/src/presets/google-limits.js +18 -0
  480. package/esm/src/presets/google.d.ts +119 -0
  481. package/esm/src/presets/google.js +197 -0
  482. package/esm/src/presets/local.d.ts +26 -0
  483. package/esm/src/presets/local.js +30 -0
  484. package/esm/src/presets/mod.d.ts +11 -0
  485. package/esm/src/presets/mod.js +9 -0
  486. package/esm/src/presets/openrouter.d.ts +26 -0
  487. package/esm/src/presets/openrouter.js +31 -0
  488. package/esm/src/providers/embedding-limits.d.ts +7 -0
  489. package/esm/src/providers/embedding-limits.js +12 -0
  490. package/esm/src/providers/gemini/embedding-protocol.d.ts +8 -0
  491. package/esm/src/providers/gemini/embedding-protocol.js +125 -0
  492. package/esm/src/providers/gemini/interactions-protocol.d.ts +8 -0
  493. package/esm/src/providers/gemini/interactions-protocol.js +105 -0
  494. package/esm/src/providers/gemini/live-protocol.d.ts +3 -0
  495. package/esm/src/providers/gemini/live-protocol.js +191 -0
  496. package/esm/src/providers/gemini/prices.d.ts +3 -0
  497. package/esm/src/providers/gemini/prices.js +63 -0
  498. package/esm/src/providers/gemini/provider.d.ts +7 -0
  499. package/esm/src/providers/gemini/provider.js +39 -0
  500. package/esm/src/providers/google/api-error.d.ts +8 -0
  501. package/esm/src/providers/google/api-error.js +123 -0
  502. package/esm/src/providers/google/builtins.d.ts +14 -0
  503. package/esm/src/providers/google/builtins.js +57 -0
  504. package/esm/src/providers/google/grounding.d.ts +5 -0
  505. package/esm/src/providers/google/grounding.js +226 -0
  506. package/esm/src/providers/google/interactions/framing.d.ts +27 -0
  507. package/esm/src/providers/google/interactions/framing.js +304 -0
  508. package/esm/src/providers/google/interactions/steps.d.ts +12 -0
  509. package/esm/src/providers/google/interactions/steps.js +200 -0
  510. package/esm/src/providers/google/interactions/stream.d.ts +23 -0
  511. package/esm/src/providers/google/interactions/stream.js +230 -0
  512. package/esm/src/providers/google/live/framing.d.ts +61 -0
  513. package/esm/src/providers/google/live/framing.js +597 -0
  514. package/esm/src/providers/google/live/stream.d.ts +13 -0
  515. package/esm/src/providers/google/live/stream.js +57 -0
  516. package/esm/src/providers/google/thinking.d.ts +3 -0
  517. package/esm/src/providers/google/thinking.js +8 -0
  518. package/esm/src/providers/google/urls.d.ts +4 -0
  519. package/esm/src/providers/google/urls.js +4 -0
  520. package/esm/src/providers/google/usage.d.ts +7 -0
  521. package/esm/src/providers/google/usage.js +49 -0
  522. package/esm/src/providers/live.d.ts +6 -0
  523. package/esm/src/providers/live.js +6 -0
  524. package/esm/src/providers/local/embedding-protocol.d.ts +7 -0
  525. package/esm/src/providers/local/embedding-protocol.js +60 -0
  526. package/esm/src/providers/local/provider.d.ts +7 -0
  527. package/esm/src/providers/local/provider.js +28 -0
  528. package/esm/src/providers/mod.d.ts +32 -0
  529. package/esm/src/providers/mod.js +23 -0
  530. package/esm/src/providers/open-ai/protocol.d.ts +3 -0
  531. package/esm/src/providers/open-ai/protocol.js +189 -0
  532. package/esm/src/providers/openrouter/embedding-protocol.d.ts +6 -0
  533. package/esm/src/providers/openrouter/embedding-protocol.js +86 -0
  534. package/esm/src/providers/openrouter/image.d.ts +15 -0
  535. package/esm/src/providers/openrouter/image.js +34 -0
  536. package/esm/src/providers/openrouter/images-protocol.d.ts +3 -0
  537. package/esm/src/providers/openrouter/images-protocol.js +82 -0
  538. package/esm/src/providers/openrouter/openai/compat.d.ts +19 -0
  539. package/esm/src/providers/openrouter/openai/compat.js +162 -0
  540. package/esm/src/providers/openrouter/openai/image-payload.d.ts +9 -0
  541. package/esm/src/providers/openrouter/openai/image-payload.js +115 -0
  542. package/esm/src/providers/openrouter/openai/reasoning-state.d.ts +2 -0
  543. package/esm/src/providers/openrouter/openai/reasoning-state.js +31 -0
  544. package/esm/src/providers/openrouter/openai/usage.d.ts +8 -0
  545. package/esm/src/providers/openrouter/openai/usage.js +56 -0
  546. package/esm/src/providers/openrouter/protocol.d.ts +8 -0
  547. package/esm/src/providers/openrouter/protocol.js +171 -0
  548. package/esm/src/providers/openrouter/provider.d.ts +10 -0
  549. package/esm/src/providers/openrouter/provider.js +50 -0
  550. package/esm/src/providers/shared/carried-tool-files.d.ts +7 -0
  551. package/esm/src/providers/shared/carried-tool-files.js +43 -0
  552. package/esm/src/providers/shared/pcm.d.ts +14 -0
  553. package/esm/src/providers/shared/pcm.js +78 -0
  554. package/esm/src/providers/shared/protocol-events.d.ts +8 -0
  555. package/esm/src/providers/shared/protocol-events.js +20 -0
  556. package/esm/src/providers/shared/response-identity.d.ts +11 -0
  557. package/esm/src/providers/shared/response-identity.js +12 -0
  558. package/esm/src/providers/shared/retry.d.ts +17 -0
  559. package/esm/src/providers/shared/retry.js +90 -0
  560. package/esm/src/providers/shared/sse.d.ts +6 -0
  561. package/esm/src/providers/shared/sse.js +93 -0
  562. package/esm/src/providers/shared/structured-output.d.ts +4 -0
  563. package/esm/src/providers/shared/structured-output.js +11 -0
  564. package/esm/src/providers/shared/tool-args.d.ts +24 -0
  565. package/esm/src/providers/shared/tool-args.js +88 -0
  566. package/esm/src/providers/shared/tool-draft.d.ts +16 -0
  567. package/esm/src/providers/shared/tool-draft.js +14 -0
  568. package/esm/src/providers/shared/upstream-tap.d.ts +8 -0
  569. package/esm/src/providers/shared/upstream-tap.js +85 -0
  570. package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
  571. package/esm/src/providers/shared/upstream-tape.js +88 -0
  572. package/esm/src/providers/system-one/protocol.d.ts +8 -0
  573. package/esm/src/providers/system-one/protocol.js +93 -0
  574. package/esm/src/providers/types.d.ts +51 -0
  575. package/esm/src/providers/types.js +2 -0
  576. package/esm/src/providers/typesafe/provider.d.ts +7 -0
  577. package/esm/src/providers/typesafe/provider.js +24 -0
  578. package/esm/src/surface/formats.d.ts +46 -0
  579. package/esm/src/surface/formats.js +178 -0
  580. package/esm/src/surface/mod.d.ts +16 -0
  581. package/esm/src/surface/mod.js +12 -0
  582. package/esm/src/surface/runtime.d.ts +44 -0
  583. package/esm/src/surface/runtime.js +441 -0
  584. package/esm/src/surface/tools.d.ts +9 -0
  585. package/esm/src/surface/tools.js +62 -0
  586. package/esm/src/surface/types.d.ts +110 -0
  587. package/esm/src/surface/types.js +4 -0
  588. package/package.json +100 -4
@@ -0,0 +1,974 @@
1
+ import { sha256 } from '../kernel/engine/hash.js';
2
+ import { mapStrings } from '../kernel/engine/tree.js';
3
+ import { turnEventSchema, } from '../kernel/turn-events.js';
4
+ import { OMIT_CANARY } from '../observability/spans.js';
5
+ import { labelText } from './labels.js';
6
+ import { promptEchoScanFrom, scanTextForPromptEcho } from './prompt-echo.js';
7
+ import { scanTextOf, textForScan } from './serialize.js';
8
+ /** The tag that opens the fence around user content. */
9
+ const USER_OPEN = '<user_data>';
10
+ /** The tag that closes the fence around user content. */
11
+ const USER_CLOSE = '</user_data>';
12
+ /** The tag that closes the fence around context. */
13
+ const CONTEXT_CLOSE = '</page_context>';
14
+ const CANARY_BYTES = 16;
15
+ const HEX_RADIX = 16;
16
+ const HEX_PAD = 2;
17
+ /** What replaces canary text a reply or event would have carried. */
18
+ /** A fence tag as a model could read one: spacing, case and closing `>` aside. */
19
+ const FENCE = /<\s*(?:\/\s*)?(?:user[\s_-]*data|page[\s_-]*context)\b(?:[^<>\n]{0,40}>|\s*(?:\/\s*)?>)?/gi;
20
+ /** Creates a 128-bit, cryptographically random token for one turn's canary binding. */
21
+ function mintCanary() {
22
+ const bytes = new Uint8Array(CANARY_BYTES);
23
+ crypto.getRandomValues(bytes);
24
+ let hex = '';
25
+ for (const byte of bytes) {
26
+ hex += byte.toString(HEX_RADIX).padStart(HEX_PAD, '0');
27
+ }
28
+ return hex;
29
+ }
30
+ /**
31
+ * A profile's canary for a system prompt: the same on every turn that sends
32
+ * that prompt, so the prompt a provider caches stays the same.
33
+ */
34
+ async function profileCanary(profileId, system) {
35
+ const hash = await sha256(JSON.stringify(['theorem.canary', profileId, system]));
36
+ return hash.slice(0, CANARY_BYTES * HEX_PAD);
37
+ }
38
+ /**
39
+ * Whether a request gives the model `canary` outside its system prompt: in
40
+ * its input, or in a history message the model did not write.
41
+ * `scanned` skips what an earlier step of the turn already read.
42
+ */
43
+ function requestGivesCanary(request, canary, scanned) {
44
+ const given = [request.input, ...(request.history ?? [])];
45
+ for (const part of given) {
46
+ if (('role' in part && part.role === 'assistant') || scanned.has(part))
47
+ continue;
48
+ scanned.add(part);
49
+ if (scanTextForCanaryLeak(textForScan(part).text, canary))
50
+ return true;
51
+ }
52
+ return false;
53
+ }
54
+ function stripUserFences(text) {
55
+ // why: Until none is left: removing one can join the text around it into another.
56
+ let stripped = text;
57
+ for (let last = ''; stripped !== last;) {
58
+ last = stripped;
59
+ stripped = stripped.replaceAll(FENCE, '');
60
+ }
61
+ return stripped.trim();
62
+ }
63
+ /**
64
+ * Removes pre-existing user-data fences, trims the input, and encloses the result
65
+ * in the canonical user-data fence used by the guardrail prompt contract.
66
+ */
67
+ function wrapUserData(text) {
68
+ return `${USER_OPEN}\n${stripUserFences(text)}\n${USER_CLOSE}`;
69
+ }
70
+ /**
71
+ * Encloses context in the fence that names its sender, with any fence already in it removed, so
72
+ * neither the page nor the person can pass text off as the host's.
73
+ */
74
+ function wrapContext(sender, text) {
75
+ return `<page_context from="${sender}">\n${stripUserFences(text)}\n${CONTEXT_CLOSE}`;
76
+ }
77
+ /** The labels' `context.note`, which tells the model what `wrapContext`'s tags mean. */
78
+ function contextNote(labels) {
79
+ return labelText('context.note', {}, labels);
80
+ }
81
+ /**
82
+ * The note is the labels' `canary.bind_note`: the profile's `labels`, then
83
+ * `overrideLabels`, then the default. Every override is checked for the
84
+ * `{canary}` placeholder when it is set — a note without the token binds nothing.
85
+ */
86
+ function bindCanary(system, canary, labels) {
87
+ const note = canaryNote(canary, labels);
88
+ if (!note) {
89
+ return system;
90
+ }
91
+ if (!system) {
92
+ return note;
93
+ }
94
+ return `${system}\n\n${note}`;
95
+ }
96
+ /** The note `bindCanary` appends, or nothing without a canary. */
97
+ function canaryNote(canary, labels) {
98
+ return canary ? labelText('canary.bind_note', { canary }, labels) : '';
99
+ }
100
+ /**
101
+ * The words of the note `labels` binds, without the canary: its text before
102
+ * the canary, or after it when nothing comes before. A reply repeating them
103
+ * repeats the system prompt. Undefined when the note has no words of its own.
104
+ */
105
+ function canaryNoteMarker(labels) {
106
+ const pieces = canaryNote('\u0000', labels)
107
+ .split('\u0000')
108
+ .map((piece) => piece.trim());
109
+ return pieces.find((piece) => piece.length > 0);
110
+ }
111
+ /** The labels' `user_data.note`, which tells the model what `wrapUserData`'s tags mean. */
112
+ function userDataNote(labels) {
113
+ return labelText('user_data.note', {}, labels);
114
+ }
115
+ /**
116
+ * Lookalike letters the scan reads as the Latin letter they imitate, for the
117
+ * letters a hex token and its transforms are written with (a–f, n–s).
118
+ */
119
+ const CONFUSABLES = {
120
+ а: 'a',
121
+ ɑ: 'a',
122
+ α: 'a',
123
+ ь: 'b',
124
+ в: 'b',
125
+ β: 'b',
126
+ с: 'c',
127
+ ϲ: 'c',
128
+ ԁ: 'd',
129
+ е: 'e',
130
+ ε: 'e',
131
+ о: 'o',
132
+ ο: 'o',
133
+ օ: 'o',
134
+ р: 'p',
135
+ ρ: 'p',
136
+ ԛ: 'q',
137
+ ѕ: 's',
138
+ };
139
+ /** Spoken names the word reading maps to the character they spell. */
140
+ const SPELLED = {
141
+ zero: '0',
142
+ one: '1',
143
+ two: '2',
144
+ three: '3',
145
+ four: '4',
146
+ five: '5',
147
+ six: '6',
148
+ seven: '7',
149
+ eight: '8',
150
+ nine: '9',
151
+ alpha: 'a',
152
+ alfa: 'a',
153
+ bravo: 'b',
154
+ charlie: 'c',
155
+ delta: 'd',
156
+ echo: 'e',
157
+ foxtrot: 'f',
158
+ november: 'n',
159
+ oscar: 'o',
160
+ papa: 'p',
161
+ quebec: 'q',
162
+ romeo: 'r',
163
+ sierra: 's',
164
+ };
165
+ /** The longest name in `SPELLED`: the most text one character of a leak can cover. */
166
+ const LONGEST_SPELLED = Math.max(...Object.keys(SPELLED).map((word) => word.length));
167
+ const WORD_CHAR = /^[\p{L}\p{N}]$/u;
168
+ const ASCII_END = 0x80;
169
+ function isAsciiAlphanumeric(code) {
170
+ return ((code >= 0x30 && code <= 0x39) ||
171
+ (code >= 0x41 && code <= 0x5a) ||
172
+ (code >= 0x61 && code <= 0x7a));
173
+ }
174
+ function isWordChar(char) {
175
+ const code = char.charCodeAt(0);
176
+ return code < ASCII_END ? isAsciiAlphanumeric(code) : WORD_CHAR.test(char);
177
+ }
178
+ /** Standard base64 characters: letters, digits, `+`, `/`. */
179
+ function isBase64Char(char) {
180
+ const code = char.charCodeAt(0);
181
+ return char === '+' || char === '/' || (code < ASCII_END && isAsciiAlphanumeric(code));
182
+ }
183
+ /** The last text folded, per case mode: every form of one scan reads the same text. */
184
+ const foldCache = new Map();
185
+ function foldText(text, foldCase) {
186
+ const cached = foldCache.get(foldCase);
187
+ if (cached?.text === text) {
188
+ return cached.chars;
189
+ }
190
+ const chars = foldChars(text, foldCase);
191
+ foldCache.set(foldCase, { text, chars });
192
+ return chars;
193
+ }
194
+ /** The characters of `text` as the scan reads them; `offset` is where `text` starts in the stream. */
195
+ function foldChars(text, foldCase, offset = 0) {
196
+ const out = [];
197
+ let at = offset;
198
+ for (const point of text) {
199
+ const to = at + point.length;
200
+ const lower = foldCase ? point.toLowerCase() : point;
201
+ const folded = point < '\u0080' ? lower : (CONFUSABLES[lower] ?? lower.normalize('NFKC'));
202
+ for (const char of folded) {
203
+ const read = foldCase ? char.toLowerCase() : char;
204
+ out.push({ char: read, at, to, word: isWordChar(read) });
205
+ }
206
+ at = to;
207
+ }
208
+ return out;
209
+ }
210
+ /**
211
+ * The most text allowed between two characters of one leak. Further apart,
212
+ * they are not read as one token, so an opening stops being held once this
213
+ * much text follows it: the hold stays short however sparse the alphabet.
214
+ */
215
+ const LEAK_GAP = 32;
216
+ /** Marks a gap wider than `LEAK_GAP` in a projection; no form's value contains it. */
217
+ const GAP = ' ';
218
+ function newProjection() {
219
+ return { kept: '', at: [], to: [] };
220
+ }
221
+ function breakGap(projection, at) {
222
+ const last = projection.to.at(-1);
223
+ if (last !== undefined && at - last > LEAK_GAP && !projection.kept.endsWith(GAP)) {
224
+ projection.kept += GAP;
225
+ projection.at.push(last);
226
+ projection.to.push(last);
227
+ }
228
+ }
229
+ /** Close a projection: an opening followed by more than `LEAK_GAP` of text is spent. */
230
+ function finish(projection, length) {
231
+ breakGap(projection, length);
232
+ return projection;
233
+ }
234
+ function keep(projection, char, at, to) {
235
+ breakGap(projection, at);
236
+ projection.kept += char;
237
+ projection.at.push(at);
238
+ projection.to.push(to);
239
+ }
240
+ /**
241
+ * How many consecutive characters of a form count as a leak: 16 of the token
242
+ * (64 bits), so a truncated or partly split token is still caught. A form
243
+ * shorter than its run is matched whole.
244
+ */
245
+ const TOKEN_LEAK_RUN = 16;
246
+ /** The same 64+ bits written as base64 (6 bits a character) or byte codes (2–3 digits a byte). */
247
+ const BASE64_LEAK_RUN = 20;
248
+ const BYTE_CODE_LEAK_RUN = 32;
249
+ const HEX_RADIX_OUT = 16;
250
+ const BASE64_EDGE = /^[A-Za-z0-9+/=_-]$/;
251
+ const BASE64_GROUP = 3;
252
+ const BASE64_GROUP_CHARS = 4;
253
+ const ROT13_SHIFT = 13;
254
+ const LATIN_LETTERS = 26;
255
+ const LOWER_A = 'a'.charCodeAt(0);
256
+ function rot13(text) {
257
+ return text.replace(/[a-z]/g, (char) => String.fromCharCode(((char.charCodeAt(0) - LOWER_A + ROT13_SHIFT) % LATIN_LETTERS) + LOWER_A));
258
+ }
259
+ function trimProjection(projection, length) {
260
+ const dropped = Math.max(0, projection.kept.length - length);
261
+ if (dropped > 0) {
262
+ projection.kept = projection.kept.slice(dropped);
263
+ projection.at.splice(0, dropped);
264
+ projection.to.splice(0, dropped);
265
+ }
266
+ return dropped;
267
+ }
268
+ /** A reader whose every character settles as it is read. */
269
+ function settledReader(accept) {
270
+ const projection = newProjection();
271
+ return {
272
+ projection,
273
+ read(chars) {
274
+ for (const { char, at, to } of chars) {
275
+ const kept = accept(char);
276
+ if (kept !== undefined)
277
+ keep(projection, kept, at, to);
278
+ }
279
+ },
280
+ openKept: () => '',
281
+ close: (length) => finish(projection, length),
282
+ shrunkTo: () => projection.kept.length,
283
+ trim: (length) => trimProjection(projection, length),
284
+ };
285
+ }
286
+ /** Reads, case-folded, each character of `alphabet` wherever it stands. */
287
+ function byCharacter(alphabet) {
288
+ return {
289
+ key: `char:${[...alphabet].sort().join('')}`,
290
+ foldCase: true,
291
+ open: () => settledReader((char) => (alphabet.has(char) ? char : undefined)),
292
+ };
293
+ }
294
+ /** The last projection of each reading: forms with the same reading share it within a scan. */
295
+ const projectionCache = new Map();
296
+ /** `text` as `reading` reads it, whole. */
297
+ function project(reading, text) {
298
+ const cached = projectionCache.get(reading.key);
299
+ if (cached?.text === text) {
300
+ return cached.projection;
301
+ }
302
+ const reader = reading.open();
303
+ reader.read(foldText(text, reading.foldCase));
304
+ const projection = reader.close(text.length);
305
+ projectionCache.set(reading.key, { text, projection });
306
+ return projection;
307
+ }
308
+ /** The token characters a word cut off by the end of the text could still spell. */
309
+ function couldSpell(written, alphabet) {
310
+ return Object.entries(SPELLED)
311
+ .filter(([name, char]) => name.startsWith(written) && alphabet.has(char))
312
+ .map(([, char]) => char)
313
+ .join('');
314
+ }
315
+ /**
316
+ * The shortest opening of a leak the hold keeps back. Shorter openings go out,
317
+ * so ordinary text is not held on every letter a token could start with; the
318
+ * cost is that a blocked leak shows the host at most this many characters less
319
+ * one — 3 of a 16-character run, where any run under 16 already passes.
320
+ */
321
+ const CANARY_OPENING_MIN = 4;
322
+ /** `CANARY_OPENING_MIN`, scaled down for a form whose leak run is short. */
323
+ function openingMin(form) {
324
+ return Math.min(CANARY_OPENING_MIN, Math.ceil(form.min / CANARY_OPENING_MIN));
325
+ }
326
+ const ANY_OPENING = () => 1;
327
+ /**
328
+ * Where the opening of `form` at the end of `kept` starts, if one is at least
329
+ * `shortest` long: any piece of the token it goes on past, as a run can start
330
+ * anywhere in it.
331
+ */
332
+ function openingFrom(kept, at, form, shortest) {
333
+ const continued = form.value.slice(0, -1);
334
+ for (let size = Math.min(kept.length, continued.length); size >= shortest; size--) {
335
+ if (continued.includes(kept.slice(kept.length - size))) {
336
+ return at[kept.length - size];
337
+ }
338
+ }
339
+ return undefined;
340
+ }
341
+ /**
342
+ * A word longer than this can be neither a spoken name nor one behind `0x`:
343
+ * how it reads is settled but for whether it stays in the alphabet.
344
+ */
345
+ const LONGEST_WORD_READ = LONGEST_SPELLED + HEX_PAD;
346
+ /** Whether `chars`, a whole word, starts with a `0x` byte prefix; the byte is the word. */
347
+ function isHexByte(chars) {
348
+ return chars.length > HEX_PAD && chars[0]?.char === '0' && chars[1]?.char === 'x';
349
+ }
350
+ /**
351
+ * Reads one word into `projection`; `open` when the text ends inside it, so
352
+ * it may still grow into another word.
353
+ */
354
+ function readWord(projection, chars, alphabet, open) {
355
+ const word = isHexByte(chars) ? chars.slice(HEX_PAD) : chars;
356
+ const written = word.map((c) => c.char).join('');
357
+ if (open) {
358
+ breakGap(projection, word[0]?.at ?? 0);
359
+ projection.unfinished = {
360
+ at: chars[0]?.at ?? 0,
361
+ settled: projection.kept.length,
362
+ spells: couldSpell(written, alphabet),
363
+ };
364
+ }
365
+ const spelled = SPELLED[written];
366
+ if (spelled !== undefined && alphabet.has(spelled)) {
367
+ keep(projection, spelled, word[0]?.at ?? 0, word.at(-1)?.to ?? 0);
368
+ }
369
+ else if (word.every((c) => alphabet.has(c.char))) {
370
+ for (const { char, at, to } of word)
371
+ keep(projection, char, at, to);
372
+ }
373
+ }
374
+ /**
375
+ * Reads word by word: a word written only in `alphabet` counts in full, a
376
+ * spoken name (`SPELLED`) counts as the character it spells, and any other
377
+ * word is a separator. "3, then f" reads as "3f"; "zero one" as "01".
378
+ *
379
+ * A word longer than `LONGEST_WORD_READ` is read as it streams: its
380
+ * characters are kept as they come while every one is in the alphabet, and
381
+ * taken back out if one is not. However long the word, each character is
382
+ * read once.
383
+ */
384
+ function byWord(alphabet) {
385
+ return {
386
+ key: `word:${[...alphabet].sort().join('')}`,
387
+ foldCase: true,
388
+ open: () => wordReader(alphabet),
389
+ };
390
+ }
391
+ function wordReader(alphabet) {
392
+ const projection = newProjection();
393
+ /** The open word. */
394
+ let word = [];
395
+ /** A long open word: how long `kept` was before it, and after the gap in front of it. */
396
+ let long;
397
+ let shrunk = 0;
398
+ function truncate(length) {
399
+ projection.kept = projection.kept.slice(0, length);
400
+ projection.at.length = length;
401
+ projection.to.length = length;
402
+ shrunk = Math.min(shrunk, length);
403
+ }
404
+ /** The open word just grew past `LONGEST_WORD_READ`: keep it while it is in the alphabet. */
405
+ function lengthen() {
406
+ const body = isHexByte(word) ? word.slice(HEX_PAD) : word;
407
+ const before = projection.kept.length;
408
+ breakGap(projection, body[0]?.at ?? 0);
409
+ long = { before, after: projection.kept.length, broken: false };
410
+ if (body.every((c) => alphabet.has(c.char))) {
411
+ for (const { char, at, to } of body)
412
+ keep(projection, char, at, to);
413
+ }
414
+ else {
415
+ breakWord();
416
+ }
417
+ }
418
+ /** The long open word has a character outside the alphabet: it is a separator. */
419
+ function breakWord() {
420
+ if (long && !long.broken) {
421
+ truncate(Math.max(0, long.before));
422
+ long.broken = true;
423
+ }
424
+ }
425
+ function grow(char) {
426
+ word.push(char);
427
+ if (word.length === LONGEST_WORD_READ + 1) {
428
+ lengthen();
429
+ }
430
+ else if (long && !long.broken) {
431
+ if (alphabet.has(char.char))
432
+ keep(projection, char.char, char.at, char.to);
433
+ else
434
+ breakWord();
435
+ }
436
+ }
437
+ function settle() {
438
+ // why: A long word's characters are already kept, or already taken back out.
439
+ if (!long)
440
+ readWord(projection, word, alphabet, false);
441
+ word = [];
442
+ long = undefined;
443
+ }
444
+ return {
445
+ projection,
446
+ read(chars) {
447
+ for (const char of chars) {
448
+ if (char.word)
449
+ grow(char);
450
+ else if (word.length > 0)
451
+ settle();
452
+ }
453
+ },
454
+ openKept() {
455
+ if (word.length === 0 || long) {
456
+ return '';
457
+ }
458
+ // why: Only `kept` is read back: the gap check needs just the last offset.
459
+ const view = { kept: projection.kept, at: [], to: projection.to.slice(-1) };
460
+ readWord(view, word, alphabet, true);
461
+ return view.kept.slice(projection.kept.length);
462
+ },
463
+ close(length) {
464
+ if (word.length > 0 && !long) {
465
+ readWord(projection, word, alphabet, true);
466
+ }
467
+ else if (long) {
468
+ const body = isHexByte(word) ? word.slice(HEX_PAD) : word;
469
+ breakGap(projection, body[0]?.at ?? 0);
470
+ projection.unfinished = {
471
+ at: word[0]?.at ?? 0,
472
+ settled: long.broken ? projection.kept.length : long.after,
473
+ spells: '',
474
+ };
475
+ }
476
+ return finish(projection, length);
477
+ },
478
+ shrunkTo() {
479
+ const least = shrunk;
480
+ shrunk = projection.kept.length;
481
+ return least;
482
+ },
483
+ trim(length) {
484
+ // why: A long word still kept may yet be taken back out, leaving what came
485
+ // before it to continue a run: keep `length` characters in front of it.
486
+ // Once its text spans more than `LEAK_GAP`, nothing before it can join a
487
+ // run after it, so that can go too. Its text, not what it folds to: a
488
+ // ligature keeps two characters for one.
489
+ const kept = long && !long.broken ? long : undefined;
490
+ const span = (word.at(-1)?.to ?? 0) - (word[0]?.at ?? 0);
491
+ const shallow = kept && span <= LEAK_GAP;
492
+ const dropped = trimProjection(projection, shallow ? projection.kept.length - kept.before + length : length);
493
+ if (kept) {
494
+ kept.before -= dropped;
495
+ kept.after -= dropped;
496
+ }
497
+ shrunk -= dropped;
498
+ return dropped;
499
+ },
500
+ };
501
+ }
502
+ /** Reads base64 characters wherever they stand; every base64 form shares the reading. */
503
+ const readBase64 = {
504
+ key: 'base64',
505
+ foldCase: false,
506
+ // why: URL-safe base64 reads as standard; padding is not part of the token.
507
+ open: () => settledReader((char) => {
508
+ const standard = char === '-' ? '+' : char === '_' ? '/' : char;
509
+ return isBase64Char(standard) ? standard : undefined;
510
+ }),
511
+ };
512
+ /**
513
+ * The base64 of the token at each of the three byte offsets it can start at
514
+ * inside larger encoded text, trimmed to the groups made of token bytes only.
515
+ */
516
+ function base64Cores(bytes) {
517
+ const cores = [];
518
+ for (let offset = 0; offset < BASE64_GROUP; offset++) {
519
+ const encoded = btoa('\0'.repeat(offset) + bytes);
520
+ const first = Math.ceil(offset / BASE64_GROUP);
521
+ const last = Math.floor((offset + bytes.length) / BASE64_GROUP);
522
+ cores.push(encoded.slice(first * BASE64_GROUP_CHARS, last * BASE64_GROUP_CHARS));
523
+ }
524
+ return cores;
525
+ }
526
+ /** The bytes a hex token spells, as a binary string; `undefined` for any other token. */
527
+ function decodedHexBytes(literal) {
528
+ if (literal.length % HEX_PAD !== 0 || !/^[0-9a-f]+$/.test(literal)) {
529
+ return undefined;
530
+ }
531
+ let bytes = '';
532
+ for (let at = 0; at < literal.length; at += HEX_PAD) {
533
+ bytes += String.fromCharCode(Number.parseInt(literal.slice(at, at + HEX_PAD), HEX_RADIX_OUT));
534
+ }
535
+ return bytes;
536
+ }
537
+ function leakRun(value, run) {
538
+ return Math.min(value.length, run);
539
+ }
540
+ let formsCache;
541
+ /**
542
+ * Every form a leaked canary is detected in: the token itself, reversed, and
543
+ * in ROT13 — each read character by character and word by word — its
544
+ * characters as hex or decimal codes, and its base64 at every byte offset; a
545
+ * hex token also as the bytes it spells, in decimal or base64. The token carries no fixed prefix, so no form
546
+ * depends on a marker the model could drop or split off.
547
+ */
548
+ function canaryLeakForms(canary) {
549
+ if (formsCache?.canary === canary) {
550
+ return formsCache.forms;
551
+ }
552
+ const literal = canary.toLowerCase();
553
+ const forms = [];
554
+ const seen = new Set();
555
+ // why: A token that reads the same reversed, or has no letters to rotate, is already covered.
556
+ for (const value of [literal, [...literal].reverse().join(''), rot13(literal)]) {
557
+ if (seen.has(value))
558
+ continue;
559
+ seen.add(value);
560
+ const alphabet = new Set(value);
561
+ const min = leakRun(value, TOKEN_LEAK_RUN);
562
+ forms.push({ value, min, reading: byCharacter(alphabet) });
563
+ forms.push({ value, min, reading: byWord(alphabet) });
564
+ }
565
+ const codes = [...canary].map((char) => char.charCodeAt(0));
566
+ const decoded = decodedHexBytes(literal);
567
+ for (const value of [
568
+ codes.map((code) => code.toString(HEX_RADIX_OUT).padStart(HEX_PAD, '0')).join(''),
569
+ codes.join(''),
570
+ ...(decoded ? [[...decoded].map((char) => char.charCodeAt(0)).join('')] : []),
571
+ ]) {
572
+ if (seen.has(value))
573
+ continue;
574
+ seen.add(value);
575
+ forms.push({
576
+ value,
577
+ min: leakRun(value, BYTE_CODE_LEAK_RUN),
578
+ reading: byCharacter(new Set(value)),
579
+ });
580
+ }
581
+ try {
582
+ for (const value of [...base64Cores(canary), ...(decoded ? base64Cores(decoded) : [])]) {
583
+ if (value && !seen.has(value)) {
584
+ seen.add(value);
585
+ forms.push({
586
+ value,
587
+ min: leakRun(value, BASE64_LEAK_RUN),
588
+ reading: readBase64,
589
+ edge: BASE64_EDGE,
590
+ });
591
+ }
592
+ }
593
+ }
594
+ catch {
595
+ /* why: a host canary outside Latin-1 has no base64 form */
596
+ }
597
+ formsCache = { canary, forms };
598
+ return forms;
599
+ }
600
+ /** Every `[start, end)` in `kept` holding `min` or more consecutive characters of `value`. */
601
+ function leakRunsIn(kept, form) {
602
+ const runs = [];
603
+ for (let from = 0; from + form.min <= form.value.length; from++) {
604
+ const piece = form.value.slice(from, from + form.min);
605
+ for (let found = kept.indexOf(piece); found >= 0; found = kept.indexOf(piece, found + 1)) {
606
+ runs.push([found, found + form.min]);
607
+ }
608
+ }
609
+ return runs;
610
+ }
611
+ function widen(text, [start, end], edge) {
612
+ if (!edge) {
613
+ return [start, end];
614
+ }
615
+ let from = start;
616
+ while (from > 0 && start - from < BASE64_GROUP_CHARS && edge.test(text.charAt(from - 1)))
617
+ from--;
618
+ let to = end;
619
+ while (to < text.length && to - end < BASE64_GROUP_CHARS && edge.test(text.charAt(to)))
620
+ to++;
621
+ return [from, to];
622
+ }
623
+ /**
624
+ * Offsets `[start, end)` of `text` covering each leak, ordered by start. One
625
+ * stretch read as a leak under several forms is one leak: ranges that overlap
626
+ * are joined.
627
+ */
628
+ function canaryLeakRanges(text, canary) {
629
+ const ranges = [];
630
+ for (const form of canaryLeakForms(canary)) {
631
+ const { kept, at, to } = project(form.reading, text);
632
+ for (const [start, end] of leakRunsIn(kept, form)) {
633
+ ranges.push(widen(text, [at[start] ?? 0, to[end - 1] ?? 0], form.edge));
634
+ }
635
+ }
636
+ const joined = [];
637
+ for (const [start, end] of ranges.sort((a, b) => a[0] - b[0])) {
638
+ const last = joined.at(-1);
639
+ if (last && start <= last[1])
640
+ last[1] = Math.max(last[1], end);
641
+ else
642
+ joined.push([start, end]);
643
+ }
644
+ return joined;
645
+ }
646
+ /** Leak detection over `canaryLeakForms` only, not general-purpose encoded-data detection. */
647
+ function scanTextForCanaryLeak(text, canary) {
648
+ if (!text || !canary) {
649
+ return false;
650
+ }
651
+ return canaryLeakForms(canary).some((form) => leakRunsIn(project(form.reading, text).kept, form).length > 0);
652
+ }
653
+ /** Whether `point` starts (`first`) or ends (`last`) in a word, as the scan reads words. */
654
+ function foldsToWord(point, edge) {
655
+ const chars = foldChars(point, true);
656
+ return (edge === 'first' ? chars[0] : chars.at(-1))?.word ?? false;
657
+ }
658
+ /**
659
+ * Where the word `at` would cut in two starts, as the scan reads words; `at`
660
+ * itself when it cuts none. The start of a word reads as another word once
661
+ * the rest is omitted ("0This" as "0"), so a release never ends inside one.
662
+ */
663
+ function wordStartAcross(text, at) {
664
+ const following = text.codePointAt(at);
665
+ if (following === undefined || !foldsToWord(String.fromCodePoint(following), 'first')) {
666
+ return at;
667
+ }
668
+ let start = at;
669
+ while (start > 0) {
670
+ const pair = start > 1 ? text.slice(start - 2, start) : '';
671
+ const point = pair.length === 2 && [...pair].length === 1 ? pair : text.slice(start - 1, start);
672
+ if (!foldsToWord(point, 'last'))
673
+ break;
674
+ start -= point.length;
675
+ if (!foldChars(point, true).every((c) => c.word))
676
+ break;
677
+ }
678
+ return start;
679
+ }
680
+ /**
681
+ * How much released text the hold rereads: an opening it released holds at
682
+ * most `CANARY_OPENING_MIN - 1` characters, each at most `LONGEST_WORD_READ`
683
+ * long and `LEAK_GAP` from the next, and a word cut where this starts reads
684
+ * as no character.
685
+ */
686
+ const RELEASED_LOOKBACK = CANARY_OPENING_MIN * (LONGEST_WORD_READ + LEAK_GAP) + LONGEST_WORD_READ;
687
+ /**
688
+ * How far before a point a leak run reaching past it can start: the longest
689
+ * run less one character, each read as above.
690
+ */
691
+ const CANARY_LEAK_REACH = Math.max(TOKEN_LEAK_RUN, BASE64_LEAK_RUN, BYTE_CODE_LEAK_RUN) * (LONGEST_WORD_READ + LEAK_GAP) +
692
+ LONGEST_WORD_READ;
693
+ /**
694
+ * Offset from which `text` must stay held: the earliest point where what
695
+ * follows is an opening of a leak form at least `CANARY_OPENING_MIN` long,
696
+ * and so could still grow into a leak; 0 when one began in `released`, the
697
+ * text shown before it. A shorter opening is released; the scan still reads
698
+ * it with what follows, so the leak it grows into is caught.
699
+ */
700
+ function canaryHoldFrom(text, canary, released = '') {
701
+ const lead = released.slice(-RELEASED_LOOKBACK);
702
+ return Math.max(0, canaryOpeningFrom(lead + text, canary) - lead.length);
703
+ }
704
+ /** Where the earliest opening the hold keeps starts in `text`; its length when none does. */
705
+ function canaryOpeningFrom(text, canary) {
706
+ return leakOpeningFrom(text, canary, openingMin);
707
+ }
708
+ /** Where the earliest opening of any leak form at least `minimum(form)` long starts in `text`. */
709
+ function leakOpeningFrom(text, canary, minimum) {
710
+ let from = text.length;
711
+ if (!canary) {
712
+ return from;
713
+ }
714
+ for (const form of canaryLeakForms(canary)) {
715
+ const { kept, at, unfinished } = project(form.reading, text);
716
+ const shortest = minimum(form);
717
+ const openings = [openingFrom(kept, at, form, shortest)];
718
+ if (unfinished) {
719
+ // why: An opening before the last word stays held until that word ends, whatever it reads as now.
720
+ const settled = kept.slice(0, unfinished.settled);
721
+ openings.push(openingFrom(settled, at, form, shortest));
722
+ // why: The last word may still grow into a spelled character that makes one long enough.
723
+ const grown = [...at.slice(0, unfinished.settled), unfinished.at];
724
+ for (const char of unfinished.spells) {
725
+ openings.push(openingFrom(settled + char, grown, form, shortest));
726
+ }
727
+ }
728
+ for (const opening of openings) {
729
+ if (opening !== undefined)
730
+ from = Math.min(from, opening);
731
+ }
732
+ }
733
+ return from;
734
+ }
735
+ /** Settled characters of a reading the stream scan keeps: every leak run is shorter. */
736
+ const SCAN_KEPT = BYTE_CODE_LEAK_RUN;
737
+ /** How long a reading's settled characters grow before the stream scan drops all but `SCAN_KEPT`. */
738
+ const SCAN_TRIM_AT = 8 * SCAN_KEPT;
739
+ const HIGH_SURROGATE_FIRST = 0xd800;
740
+ const HIGH_SURROGATE_LAST = 0xdbff;
741
+ function createCanaryScanner(canary) {
742
+ const readings = new Map();
743
+ for (const form of canary ? canaryLeakForms(canary) : []) {
744
+ const known = readings.get(form.reading.key);
745
+ if (known)
746
+ known.forms.push(form);
747
+ else {
748
+ readings.set(form.reading.key, {
749
+ reader: form.reading.open(),
750
+ foldCase: form.reading.foldCase,
751
+ forms: [form],
752
+ checked: 0,
753
+ });
754
+ }
755
+ }
756
+ let offset = 0;
757
+ /** A high surrogate the last text ended on, read with the low one that follows. */
758
+ let split = '';
759
+ let leaked = false;
760
+ function check(reading) {
761
+ const { reader } = reading;
762
+ // why: Runs ending before `checked` were read clean; a broken word may have moved it back.
763
+ const checked = Math.min(reading.checked, reader.shrunkTo());
764
+ const kept = reader.projection.kept + reader.openKept();
765
+ const found = reading.forms.some((form) => leakRunsIn(kept.slice(Math.max(0, checked - form.min + 1)), form).length > 0);
766
+ reading.checked = reader.projection.kept.length;
767
+ if (reading.checked > SCAN_TRIM_AT)
768
+ reading.checked -= reader.trim(SCAN_KEPT);
769
+ return found;
770
+ }
771
+ return {
772
+ push(fragment) {
773
+ if (leaked || !fragment) {
774
+ return leaked;
775
+ }
776
+ let text = split + fragment;
777
+ const last = text.charCodeAt(text.length - 1);
778
+ split = last >= HIGH_SURROGATE_FIRST && last <= HIGH_SURROGATE_LAST ? text.slice(-1) : '';
779
+ text = text.slice(0, text.length - split.length);
780
+ const folded = new Map();
781
+ for (const reading of readings.values()) {
782
+ let chars = folded.get(reading.foldCase);
783
+ if (!chars) {
784
+ chars = foldChars(text, reading.foldCase, offset);
785
+ folded.set(reading.foldCase, chars);
786
+ }
787
+ reading.reader.read(chars);
788
+ if (check(reading))
789
+ leaked = true;
790
+ }
791
+ offset += text.length;
792
+ return leaked;
793
+ },
794
+ };
795
+ }
796
+ /**
797
+ * The tail of a finished window that could still open a leak: what the next
798
+ * window of the same canary scans in front of its own text. Carried forward
799
+ * as it grows, it bounds the carry to one token's length. Unlike the hold it
800
+ * keeps openings of any length: a leak split across windows is still one run.
801
+ */
802
+ function canaryCarry(text, canary) {
803
+ return text.slice(leakOpeningFrom(text, canary, ANY_OPENING));
804
+ }
805
+ /**
806
+ * What the next window of the same turn or session scans in front of its own:
807
+ * a possible canary opening, and the words a prompt echo could continue from.
808
+ */
809
+ function promptLeakCarry(text, canary, privateSystem) {
810
+ const canaryTail = canary ? text.length - canaryCarry(text, canary).length : text.length;
811
+ const from = privateSystem
812
+ ? Math.min(canaryTail, promptEchoScanFrom(text, text.length))
813
+ : canaryTail;
814
+ return text.slice(from);
815
+ }
816
+ function redactCanaryText(text, canary) {
817
+ if (!text || !canary) {
818
+ return text;
819
+ }
820
+ let out = '';
821
+ let from = 0;
822
+ for (const [start, end] of canaryLeakRanges(text, canary)) {
823
+ if (start >= from) {
824
+ out += `${text.slice(from, start)}${OMIT_CANARY}`;
825
+ }
826
+ from = Math.max(from, end);
827
+ }
828
+ return out + text.slice(from);
829
+ }
830
+ /**
831
+ * Nothing in a thought stops the turn: `thought-guard.ts` omits what in it
832
+ * leaks. Every outbound gate reads this before scanning.
833
+ */
834
+ function isGuardedOutput(event) {
835
+ return event.type !== 'thought';
836
+ }
837
+ /**
838
+ * What of an event reaches the host as content, field by field: the one list
839
+ * every canary and prompt-leak scan reads. Builder-only fields
840
+ * (`errorInternal`), counts, identities, and media bytes are not content.
841
+ */
842
+ function hostContentOf(event) {
843
+ switch (event.type) {
844
+ case 'text':
845
+ case 'thought':
846
+ return [event.text];
847
+ case 'structured':
848
+ return [event.structured];
849
+ case 'grounding':
850
+ return [event.grounding];
851
+ case 'citation':
852
+ return [event.sources];
853
+ case 'compaction':
854
+ return [event.summary];
855
+ case 'evidence':
856
+ return [event.evidence, event.text, event.sessionResumptionHandle];
857
+ case 'session':
858
+ return [event.session];
859
+ case 'tool':
860
+ return [event.tool];
861
+ case 'tool_draft':
862
+ case 'structured_draft':
863
+ return [event.text];
864
+ case 'error':
865
+ return [event.error, event.errorCopy];
866
+ case 'custom':
867
+ return [event.data];
868
+ case 'reasoning':
869
+ return [event.text];
870
+ case 'attempt':
871
+ case 'media':
872
+ case 'tokens':
873
+ case 'guardrail':
874
+ case 'stage':
875
+ case 'done':
876
+ case 'response':
877
+ case 'provider_checkpoint':
878
+ case 'provider_warning':
879
+ return [];
880
+ default: {
881
+ const unhandled = event;
882
+ return unhandled;
883
+ }
884
+ }
885
+ }
886
+ /**
887
+ * The text of each field of an event's host content (`hostContentOf`), one
888
+ * string per field. Unguarded output (`isGuardedOutput`) carries none.
889
+ */
890
+ function guardedEventTexts(event) {
891
+ if (!isGuardedOutput(event)) {
892
+ return [];
893
+ }
894
+ return hostContentOf(event)
895
+ .map((field) => scanTextOf(field))
896
+ .filter((text) => text !== '');
897
+ }
898
+ /**
899
+ * Whether any field of an event's host content (`guardedEventTexts`) carries
900
+ * a leak. Unguarded output (`isGuardedOutput`) never carries one.
901
+ */
902
+ function eventHasCanary(event, canary) {
903
+ if (!canary) {
904
+ return false;
905
+ }
906
+ return guardedEventTexts(event).some((text) => scanTextForCanaryLeak(text, canary));
907
+ }
908
+ /**
909
+ * Call `flush` at stream end to release the held tail. With `privateSystem`, a reply
910
+ * echoing the system prompt (`scanTextForPromptEcho`) is a leak too.
911
+ */
912
+ function createCanaryStreamGate(canary, privateSystem) {
913
+ const scanner = createCanaryScanner(canary);
914
+ let pending = '';
915
+ /** Released text an opening or a prompt echo could still continue from, read but never re-released. */
916
+ let released = '';
917
+ /**
918
+ * Whether the stream holds a leak once `fragment` arrives, `window` being
919
+ * the unreleased text it ends. The canary scan reads each character once;
920
+ * the echo check rereads its own short lookback.
921
+ */
922
+ function leaks(fragment, window) {
923
+ const text = released + window;
924
+ return (scanner.push(fragment) ||
925
+ (privateSystem !== undefined &&
926
+ scanTextForPromptEcho(text.slice(promptEchoScanFrom(text, released.length)), privateSystem, canary)));
927
+ }
928
+ function step(fragment) {
929
+ const window = pending + fragment;
930
+ if (leaks(fragment, window)) {
931
+ return { leak: true };
932
+ }
933
+ const safeEnd = canaryHoldFrom(window, canary, released);
934
+ const emit = window.slice(0, safeEnd);
935
+ pending = window.slice(safeEnd);
936
+ released += emit;
937
+ const echoFrom = privateSystem
938
+ ? promptEchoScanFrom(released, released.length)
939
+ : released.length;
940
+ released = released.slice(Math.min(echoFrom, Math.max(0, released.length - RELEASED_LOOKBACK)));
941
+ return { leak: false, emit };
942
+ }
943
+ return {
944
+ process(fragment) {
945
+ if (!fragment) {
946
+ return { leak: false, emit: '' };
947
+ }
948
+ return step(fragment);
949
+ },
950
+ flush() {
951
+ if (leaks('', pending)) {
952
+ return { leak: true };
953
+ }
954
+ const emit = pending;
955
+ pending = '';
956
+ return { leak: false, emit };
957
+ },
958
+ };
959
+ }
960
+ /**
961
+ * The reply as it streams: text deltas, and the transcript of spoken output
962
+ * (Live `output_transcription` evidence). The one stream the outbound gates
963
+ * scan progressively; everything else is scanned whole per event.
964
+ */
965
+ function isStreamedCanaryEvent(event) {
966
+ return (event.type === 'text' ||
967
+ (event.type === 'evidence' && event.evidence.kind === 'output_transcription'));
968
+ }
969
+ /** The event with the canary replaced by `OMIT_CANARY` in every string it holds. */
970
+ function redactCanary(event, canary) {
971
+ // why: Replacing strings keeps the event's shape; the parse re-types it.
972
+ return turnEventSchema.parse(mapStrings(event, (text) => redactCanaryText(text, canary)));
973
+ }
974
+ export { bindCanary, CANARY_LEAK_REACH, canaryHoldFrom, canaryLeakRanges, canaryNote, canaryNoteMarker, canaryOpeningFrom, contextNote, createCanaryScanner, createCanaryStreamGate, eventHasCanary, guardedEventTexts, isStreamedCanaryEvent, mintCanary, OMIT_CANARY, profileCanary, promptLeakCarry, RELEASED_LOOKBACK, redactCanary, redactCanaryText, requestGivesCanary, scanTextForCanaryLeak, USER_CLOSE, USER_OPEN, userDataNote, wordStartAcross, wrapContext, wrapUserData, };