theorum 0.1.15 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/README.md +177 -90
  2. package/esm/mod.d.ts +21 -11
  3. package/esm/mod.js +15 -8
  4. package/esm/src/cli/commands/bench.js +6 -3
  5. package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
  6. package/esm/src/cli/commands/fuzz-canary.js +195 -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/profile.js +25 -14
  10. package/esm/src/cli/commands/run.d.ts +3 -0
  11. package/esm/src/cli/commands/run.js +22 -30
  12. package/esm/src/cli/commands/test.d.ts +10 -1
  13. package/esm/src/cli/commands/test.js +31 -31
  14. package/esm/src/cli/event-log.d.ts +19 -0
  15. package/esm/src/cli/event-log.js +147 -0
  16. package/esm/src/cli/index.js +45 -11
  17. package/esm/src/cli/matrix/synthesizer.d.ts +7 -9
  18. package/esm/src/cli/matrix/synthesizer.js +32 -106
  19. package/esm/src/guardrails/canary-gate.d.ts +20 -0
  20. package/esm/src/guardrails/canary-gate.js +31 -0
  21. package/esm/src/guardrails/canary.d.ts +26 -0
  22. package/esm/src/guardrails/canary.js +139 -0
  23. package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +16 -0
  24. package/esm/src/guardrails/corpus/canary-egress-attacks.js +151 -0
  25. package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +11 -0
  26. package/esm/src/guardrails/corpus/fuzz-inbound.js +216 -0
  27. package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
  28. package/esm/src/guardrails/corpus/inbound-payloads.js +124 -0
  29. package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
  30. package/esm/src/guardrails/corpus/live-attacks.js +232 -0
  31. package/esm/src/guardrails/corpus/mod.d.ts +13 -0
  32. package/esm/src/guardrails/corpus/mod.js +11 -0
  33. package/esm/src/guardrails/corpus/secrets.d.ts +16 -0
  34. package/esm/src/guardrails/corpus/secrets.js +16 -0
  35. package/esm/src/guardrails/corpus/strings.d.ts +27 -0
  36. package/esm/src/guardrails/corpus/strings.js +33 -0
  37. package/esm/src/guardrails/corpus/types.d.ts +37 -0
  38. package/esm/src/guardrails/corpus/types.js +6 -0
  39. package/esm/src/guardrails/egress.d.ts +9 -0
  40. package/esm/src/guardrails/egress.js +36 -0
  41. package/esm/src/guardrails/error.js +6 -4
  42. package/esm/src/guardrails/injection.js +28 -5
  43. package/esm/src/guardrails/live-outbound-gate.d.ts +39 -0
  44. package/esm/src/guardrails/live-outbound-gate.js +180 -0
  45. package/esm/src/guardrails/mod.d.ts +13 -4
  46. package/esm/src/guardrails/mod.js +10 -4
  47. package/esm/src/guardrails/quota.js +1 -1
  48. package/esm/src/guardrails/sanitize.d.ts +11 -15
  49. package/esm/src/guardrails/sanitize.js +40 -49
  50. package/esm/src/guardrails/sensitive.js +2 -1
  51. package/esm/src/guardrails/testing.d.ts +10 -0
  52. package/esm/src/guardrails/testing.js +9 -0
  53. package/esm/src/host/client-turn.d.ts +19 -0
  54. package/esm/src/host/client-turn.js +32 -0
  55. package/esm/src/host/mod.d.ts +5 -3
  56. package/esm/src/host/mod.js +4 -3
  57. package/esm/src/interface/blocks.d.ts +20 -0
  58. package/esm/src/interface/blocks.js +180 -0
  59. package/esm/src/interface/from-profile.d.ts +14 -0
  60. package/esm/src/interface/from-profile.js +104 -0
  61. package/esm/src/interface/inputs.d.ts +8 -0
  62. package/esm/src/interface/inputs.js +127 -0
  63. package/esm/src/interface/mod.d.ts +10 -0
  64. package/esm/src/interface/mod.js +9 -0
  65. package/esm/src/interface/types.d.ts +153 -0
  66. package/esm/src/interface/types.js +13 -0
  67. package/esm/src/kernel/engine/delta.d.ts +24 -2
  68. package/esm/src/kernel/engine/delta.js +437 -39
  69. package/esm/src/kernel/engine/live-inbound.d.ts +9 -0
  70. package/esm/src/kernel/engine/live-inbound.js +17 -0
  71. package/esm/src/kernel/engine/repair.js +1 -1
  72. package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
  73. package/esm/src/kernel/engine/runner/gates.js +7 -7
  74. package/esm/src/kernel/engine/runner/mod.js +13 -13
  75. package/esm/src/kernel/engine/runner/state.d.ts +7 -0
  76. package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
  77. package/esm/src/kernel/engine/runner/steps.js +171 -37
  78. package/esm/src/kernel/engine/runner/stream.d.ts +1 -2
  79. package/esm/src/kernel/engine/runner/stream.js +66 -29
  80. package/esm/src/kernel/engine/session/mod.d.ts +23 -0
  81. package/esm/src/kernel/engine/session/mod.js +205 -0
  82. package/esm/src/kernel/mod.d.ts +9 -10
  83. package/esm/src/kernel/mod.js +6 -8
  84. package/esm/src/kernel/registry/attachments.d.ts +4 -2
  85. package/esm/src/kernel/registry/attachments.js +6 -3
  86. package/esm/src/kernel/registry/catalog.d.ts +3 -16
  87. package/esm/src/kernel/registry/catalog.js +5 -81
  88. package/esm/src/kernel/registry/ingress.d.ts +9 -4
  89. package/esm/src/kernel/registry/ingress.js +73 -63
  90. package/esm/src/kernel/registry/profiles.d.ts +35 -12
  91. package/esm/src/kernel/registry/profiles.js +159 -66
  92. package/esm/src/kernel/registry/provider-request.js +11 -7
  93. package/esm/src/kernel/registry/resolve.d.ts +5 -6
  94. package/esm/src/kernel/registry/resolve.js +100 -70
  95. package/esm/src/kernel/registry/vault.d.ts +12 -7
  96. package/esm/src/kernel/registry/vault.js +30 -8
  97. package/esm/src/kernel/schema.d.ts +145 -0
  98. package/esm/src/kernel/schema.js +462 -0
  99. package/esm/src/kernel/stop.d.ts +15 -10
  100. package/esm/src/kernel/stop.js +5 -5
  101. package/esm/src/kernel/tools/execute.d.ts +45 -0
  102. package/esm/src/kernel/tools/execute.js +363 -0
  103. package/esm/src/kernel/tools/harness.d.ts +8 -0
  104. package/esm/src/kernel/tools/harness.js +44 -0
  105. package/esm/src/kernel/tools/invoke.d.ts +10 -0
  106. package/esm/src/kernel/tools/invoke.js +83 -0
  107. package/esm/src/kernel/tools/mod.d.ts +11 -0
  108. package/esm/src/kernel/tools/mod.js +10 -0
  109. package/esm/src/kernel/tools/project.d.ts +12 -0
  110. package/esm/src/kernel/tools/project.js +34 -0
  111. package/esm/src/kernel/tools/registry.d.ts +23 -0
  112. package/esm/src/kernel/tools/registry.js +71 -0
  113. package/esm/src/kernel/tools/resolve.d.ts +29 -0
  114. package/esm/src/kernel/tools/resolve.js +262 -0
  115. package/esm/src/kernel/tools/schema.d.ts +13 -0
  116. package/esm/src/kernel/tools/schema.js +159 -0
  117. package/esm/src/kernel/tools/types.d.ts +216 -0
  118. package/esm/src/kernel/tools/types.js +9 -0
  119. package/esm/src/kernel/types.d.ts +338 -193
  120. package/esm/src/observability/trace-attach.d.ts +6 -4
  121. package/esm/src/observability/trace-attach.js +39 -25
  122. package/esm/src/observability/trace-record.d.ts +11 -9
  123. package/esm/src/observability/trace-record.js +36 -23
  124. package/esm/src/observability/trace-usage.d.ts +10 -3
  125. package/esm/src/observability/trace-usage.js +70 -17
  126. package/esm/src/observability/trace.d.ts +9 -1
  127. package/esm/src/observability/trace.js +11 -3
  128. package/esm/src/presets/google/speech-voices.d.ts +11 -0
  129. package/esm/src/presets/google/speech-voices.js +41 -0
  130. package/esm/src/presets/google.d.ts +36 -24
  131. package/esm/src/presets/google.js +50 -63
  132. package/esm/src/presets/mod.d.ts +2 -2
  133. package/esm/src/presets/mod.js +1 -1
  134. package/esm/src/providers/create-provider.d.ts +14 -14
  135. package/esm/src/providers/create-provider.js +55 -23
  136. package/esm/src/providers/google/interactions/framing.d.ts +15 -0
  137. package/esm/src/providers/google/interactions/framing.js +208 -0
  138. package/esm/src/providers/google/interactions/mod.d.ts +7 -0
  139. package/esm/src/providers/google/interactions/mod.js +7 -0
  140. package/esm/src/providers/google/interactions/stream.d.ts +83 -0
  141. package/esm/src/providers/google/interactions/stream.js +588 -0
  142. package/esm/src/providers/google/keys.d.ts +26 -0
  143. package/esm/src/providers/{keys.js → google/keys.js} +19 -31
  144. package/esm/src/providers/google/live/framing.d.ts +46 -0
  145. package/esm/src/providers/google/live/framing.js +515 -0
  146. package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
  147. package/esm/src/providers/google/live/openapi-schema.js +45 -0
  148. package/esm/src/providers/google/live/session.d.ts +25 -0
  149. package/esm/src/providers/google/live/session.js +134 -0
  150. package/esm/src/providers/google/live/stream.d.ts +36 -0
  151. package/esm/src/providers/google/live/stream.js +201 -0
  152. package/esm/src/providers/google/urls.d.ts +6 -0
  153. package/esm/src/providers/google/urls.js +6 -0
  154. package/esm/src/providers/local/local.d.ts +30 -0
  155. package/esm/src/providers/{local.js → local/local.js} +66 -126
  156. package/esm/src/providers/local/mod.d.ts +9 -0
  157. package/esm/src/providers/local/mod.js +9 -0
  158. package/esm/src/providers/mod.d.ts +6 -3
  159. package/esm/src/providers/mod.js +3 -1
  160. package/esm/src/providers/openrouter/chat.d.ts +87 -0
  161. package/esm/src/providers/{openrouter.js → openrouter/chat.js} +70 -229
  162. package/esm/src/providers/openrouter/image.d.ts +34 -0
  163. package/esm/src/providers/openrouter/image.js +286 -0
  164. package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
  165. package/esm/src/providers/openrouter/openai/chat-payload.js +60 -0
  166. package/esm/src/providers/openrouter/openai/compat.d.ts +47 -0
  167. package/esm/src/providers/openrouter/openai/compat.js +194 -0
  168. package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
  169. package/esm/src/providers/openrouter/openai/image-payload.js +84 -0
  170. package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
  171. package/esm/src/providers/openrouter/openai/sdk-messages.js +95 -0
  172. package/esm/src/providers/openrouter/resolve-api-key.d.ts +9 -0
  173. package/esm/src/providers/openrouter/resolve-api-key.js +24 -0
  174. package/esm/src/providers/openrouter/speech.d.ts +23 -0
  175. package/esm/src/providers/{speech.js → openrouter/speech.js} +32 -55
  176. package/esm/src/providers/probe.d.ts +1 -0
  177. package/esm/src/providers/probe.js +22 -0
  178. package/esm/src/providers/shared/pcm.d.ts +12 -0
  179. package/esm/src/providers/{pcm.js → shared/pcm.js} +16 -3
  180. package/esm/src/providers/shared/sse.d.ts +18 -0
  181. package/esm/src/providers/shared/sse.js +87 -0
  182. package/esm/src/providers/shared/tool-args.d.ts +17 -0
  183. package/esm/src/providers/shared/tool-args.js +45 -0
  184. package/esm/src/providers/shared/upstream-tap.d.ts +5 -0
  185. package/esm/src/providers/{google-tap.js → shared/upstream-tap.js} +4 -7
  186. package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
  187. package/esm/src/providers/{gemini-tape.js → shared/upstream-tape.js} +12 -22
  188. package/esm/src/providers/types.d.ts +27 -0
  189. package/esm/src/providers/types.js +1 -0
  190. package/package.json +11 -7
  191. package/docs/cli.md +0 -97
  192. package/docs/guardrails.md +0 -178
  193. package/docs/host.md +0 -97
  194. package/docs/kernel.md +0 -404
  195. package/docs/observability.md +0 -105
  196. package/docs/openrouter.md +0 -125
  197. package/docs/presets-google.md +0 -91
  198. package/docs/presets.md +0 -88
  199. package/docs/providers.md +0 -202
  200. package/docs/streaming.md +0 -96
  201. package/esm/src/kernel/engine/boundary.d.ts +0 -10
  202. package/esm/src/kernel/engine/boundary.js +0 -55
  203. package/esm/src/kernel/engine/runner/tools.d.ts +0 -13
  204. package/esm/src/kernel/engine/runner/tools.js +0 -198
  205. package/esm/src/kernel/registry/tools.d.ts +0 -12
  206. package/esm/src/kernel/registry/tools.js +0 -36
  207. package/esm/src/providers/expose-for-tests.d.ts +0 -1
  208. package/esm/src/providers/expose-for-tests.js +0 -25
  209. package/esm/src/providers/gemini-tape.d.ts +0 -2
  210. package/esm/src/providers/google-tap.d.ts +0 -3
  211. package/esm/src/providers/interactions.d.ts +0 -5
  212. package/esm/src/providers/interactions.js +0 -169
  213. package/esm/src/providers/keys.d.ts +0 -19
  214. package/esm/src/providers/local.d.ts +0 -29
  215. package/esm/src/providers/openrouter-mod.d.ts +0 -13
  216. package/esm/src/providers/openrouter-mod.js +0 -12
  217. package/esm/src/providers/openrouter-payload.d.ts +0 -39
  218. package/esm/src/providers/openrouter-payload.js +0 -195
  219. package/esm/src/providers/openrouter.d.ts +0 -15
  220. package/esm/src/providers/pcm.d.ts +0 -7
  221. package/esm/src/providers/provider.d.ts +0 -15
  222. package/esm/src/providers/provider.js +0 -202
  223. package/esm/src/providers/speech.d.ts +0 -23
  224. package/esm/src/providers/sse.d.ts +0 -7
  225. package/esm/src/providers/sse.js +0 -55
  226. package/esm/src/streaming/mod.d.ts +0 -9
  227. package/esm/src/streaming/mod.js +0 -8
  228. /package/esm/src/{streaming → host}/readStreamingJsonStringField.d.ts +0 -0
  229. /package/esm/src/{streaming → host}/readStreamingJsonStringField.js +0 -0
@@ -1,13 +1,8 @@
1
1
  /**
2
2
  * Request sanitization utilities for THEORUM.
3
3
  *
4
- * Sanitization removes inbound prompt-injection spans and sensitive-data spans
5
- * according to the active profile guardrail flags. The host remains responsible
6
- * for domain policy.
7
- *
8
4
  * @module
9
5
  */
10
- import { mapStrings } from '../kernel/engine/tree.js';
11
6
  import { sanitizeTurnBlobsForProfile } from '../kernel/registry/attachments.js';
12
7
  import { getProfile } from '../kernel/registry/profiles.js';
13
8
  import { applySpans } from '../observability/spans.js';
@@ -26,8 +21,7 @@ function sanitizeText(text, options) {
26
21
  ];
27
22
  return applySpans(text, spans);
28
23
  }
29
- /** Redact only sensitive data (credentials, PII) — skip injection patterns.
30
- * Use for model output text that never contained user-authored injection attempts. */
24
+ /** Redact only sensitive data (credentials, PII) — skip injection patterns. */
31
25
  function redactSensitiveOnly(text) {
32
26
  const spans = sensitiveSpans(text);
33
27
  if (spans.length === 0)
@@ -44,17 +38,8 @@ function sanitizeSlots(slots, options) {
44
38
  }
45
39
  return out;
46
40
  }
47
- function sanitizeArgs(args, options) {
48
- const next = mapStrings(args, (t) => sanitizeText(t, options));
49
- if (next && typeof next === 'object' && !Array.isArray(next)) {
50
- return next;
51
- }
52
- return args;
53
- }
54
- /** Maximum retained length for trace-safe host project ids. */
55
41
  const PROJECT_ID_MAX = 128;
56
42
  const PROJECT_ID_OK = /^[A-Za-z0-9._-]+$/;
57
- /** Return a trace-safe project id or `undefined` when the input is unsafe. */
58
43
  function sanitizeProjectId(id) {
59
44
  if (!id) {
60
45
  return undefined;
@@ -93,43 +78,23 @@ function sanitizeHistory(history, options) {
93
78
  ...(m.metadata ? { metadata: m.metadata } : {}),
94
79
  }));
95
80
  }
96
- function sanitizeDynamicTool(decl, options) {
97
- const clean = { ...decl };
98
- if (clean.description !== undefined) {
99
- clean.description = sanitizeText(clean.description, options);
100
- }
101
- if (clean.parameters !== undefined) {
102
- clean.parameters = sanitizeArgs(clean.parameters, options);
103
- }
104
- return clean;
105
- }
106
- /** Sanitize description and parameter schema text in dynamic tool declarations. */
107
- function sanitizeDynamicTools(tools, options) {
108
- if (!tools || tools.length === 0) {
109
- return tools;
110
- }
111
- return tools.map((decl) => sanitizeDynamicTool(decl, options));
112
- }
113
- /** Sanitize all user-controlled text and blobs in a turn request. */
114
- function sanitizeTurnRequest(req) {
81
+ function guardrailTextOptions(profileId) {
115
82
  let profileGuardrails;
116
83
  try {
117
- profileGuardrails = getProfile(req.profile)?.guardrails;
84
+ profileGuardrails = getProfile(profileId)?.guardrails;
118
85
  }
119
86
  catch {
120
- // If profile not registered yet, default to full guardrails
87
+ // If profile not registered yet, default to full guardrails.
121
88
  }
122
- const options = {
89
+ return {
123
90
  sanitizeInput: profileGuardrails?.sanitizeInput ?? true,
124
91
  redactSensitive: profileGuardrails?.redactSensitive ?? true,
125
92
  };
93
+ }
94
+ /** Sanitize user-controlled text fields; leave attachments/voice untouched. */
95
+ function sanitizeTurnRequestText(req, options) {
126
96
  const input = req.input ?? {};
127
- const { toolInvoke } = req;
128
97
  const { text: rawText } = input;
129
- let invoke = toolInvoke;
130
- if (toolInvoke) {
131
- invoke = { ...toolInvoke, arguments: sanitizeArgs(toolInvoke.arguments, options) };
132
- }
133
98
  let text = rawText;
134
99
  if (rawText !== undefined) {
135
100
  text = sanitizeText(rawText, options);
@@ -138,22 +103,48 @@ function sanitizeTurnRequest(req) {
138
103
  if (system !== undefined) {
139
104
  system = sanitizeText(system, options);
140
105
  }
141
- const { attachments, voice } = sanitizeTurnBlobsForProfile(req.profile, input.attachments, input.voice);
142
106
  return {
143
107
  ...req,
144
108
  system,
145
109
  projectId: sanitizeProjectId(req.projectId),
146
- dynamicTools: sanitizeDynamicTools(req.dynamicTools, options),
147
110
  input: {
148
111
  ...input,
149
112
  text,
150
113
  slots: sanitizeSlots(input.slots, options),
151
- attachments,
152
- voice,
153
114
  repair: sanitizeRepair(input.repair, options),
154
115
  history: sanitizeHistory(input.history, options),
155
116
  },
156
- toolInvoke: invoke,
157
117
  };
158
118
  }
159
- export { PROJECT_ID_MAX, redactSensitiveOnly, sanitizeDynamicTools, sanitizeProjectId, sanitizeText, sanitizeTurnRequest, };
119
+ /** Sanitize all user-controlled text and blobs in a turn request. */
120
+ function sanitizeTurnRequest(req) {
121
+ const options = guardrailTextOptions(req.profile);
122
+ const textSafe = sanitizeTurnRequestText(req, options);
123
+ const input = textSafe.input ?? {};
124
+ const { attachments, voice } = sanitizeTurnBlobsForProfile(req.profile, input.attachments, input.voice);
125
+ return {
126
+ ...textSafe,
127
+ input: {
128
+ ...input,
129
+ attachments,
130
+ voice,
131
+ },
132
+ };
133
+ }
134
+ /**
135
+ * Trace-safe request sanitize. Prefers full `sanitizeTurnRequest`; if blob/policy
136
+ * checks throw, still redacts text and keeps attachments for hashing — never invents empty input.
137
+ */
138
+ function sanitizeTurnRequestForTrace(req) {
139
+ try {
140
+ return { request: sanitizeTurnRequest(req) };
141
+ }
142
+ catch (err) {
143
+ const message = err instanceof Error ? err.message : String(err);
144
+ return {
145
+ request: sanitizeTurnRequestText(req, guardrailTextOptions(req.profile)),
146
+ sanitizeError: message,
147
+ };
148
+ }
149
+ }
150
+ export { PROJECT_ID_MAX, redactSensitiveOnly, sanitizeProjectId, sanitizeText, sanitizeTurnRequest, sanitizeTurnRequestForTrace, };
@@ -23,7 +23,8 @@ const GITHUB_PAT = /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g;
23
23
  const GITHUB_TOKEN = /\bghp_[A-Za-z0-9]{36}\b/g;
24
24
  const SLACK_TOKEN = /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g;
25
25
  const BEARER = /\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi;
26
- const PEM_KEY = /-----BEGIN (?:RSA )?PRIVATE KEY-----[\s\S]+?-----END (?:RSA )?PRIVATE KEY-----/g;
26
+ /** Bounded payload so PEM redaction cannot ReDoS on repeated BEGIN markers. */
27
+ const PEM_KEY = /-----BEGIN (?:RSA )?PRIVATE KEY-----[\s\S]{0,16384}?-----END (?:RSA )?PRIVATE KEY-----/g;
27
28
  const CARD_CANDIDATE = /\b(?:\d[\s.-]*?){13,19}\b/g;
28
29
  const KEY_PATTERNS = [
29
30
  SSN,
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Guardrails testing surface — adversarial corpus, fuzz runners, live attack builders.
3
+ *
4
+ * Import via `theorum/guardrails/testing` (not published on the production guardrails entry).
5
+ *
6
+ * @module
7
+ */
8
+ import "../../_dnt.polyfills.js";
9
+ export type { CanaryEgressAttack, CanaryEgressCatalogEntry, InboundFuzzPayload, InboundFuzzResult, LiveAttack, } from './corpus/mod.js';
10
+ export { buildCanaryEgressAttacks, buildLiveAttacks, canaryEgressCatalog, FIXED_CANARY, filterLiveAttacks, inboundFuzzPayloads, inboundPayloadByName, runInboundGuardrailFuzz, summarizeAttackBank, } from './corpus/mod.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Guardrails testing surface — adversarial corpus, fuzz runners, live attack builders.
3
+ *
4
+ * Import via `theorum/guardrails/testing` (not published on the production guardrails entry).
5
+ *
6
+ * @module
7
+ */
8
+ import "../../_dnt.polyfills.js";
9
+ export { buildCanaryEgressAttacks, buildLiveAttacks, canaryEgressCatalog, FIXED_CANARY, filterLiveAttacks, inboundFuzzPayloads, inboundPayloadByName, runInboundGuardrailFuzz, summarizeAttackBank, } from './corpus/mod.js';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Strip host-only diagnostics from turn events before client-facing transports.
3
+ *
4
+ * @module
5
+ */
6
+ import type { TurnEvent } from '../kernel/types.js';
7
+ /** Options for {@link forClient} / {@link forClientEvents}. */
8
+ export interface ClientTurnOptions {
9
+ /**
10
+ * Keep provider-native step payloads on `evidence` events.
11
+ * Default `false` — parsed fields (`kind`, `code`, `result`, citations) remain.
12
+ */
13
+ includeEvidenceRaw?: boolean;
14
+ }
15
+ /** Return a copy of one turn event safe to forward to browsers or end-user SSE. */
16
+ declare function forClient(event: TurnEvent, options?: ClientTurnOptions): TurnEvent;
17
+ /** Map {@link forClient} over a batch (e.g. Live relay or HTTP stream flush). */
18
+ declare function forClientEvents(events: TurnEvent[], options?: ClientTurnOptions): TurnEvent[];
19
+ export { forClient, forClientEvents };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Strip host-only diagnostics from turn events before client-facing transports.
3
+ *
4
+ * @module
5
+ */
6
+ function stripErrorInternal(event) {
7
+ if (event.type !== 'error' || !event.errorInternal) {
8
+ return event;
9
+ }
10
+ const { errorInternal: _internal, ...rest } = event;
11
+ return rest;
12
+ }
13
+ function stripEvidenceRaw(event) {
14
+ if (event.type !== 'evidence' || !event.evidence?.raw) {
15
+ return event;
16
+ }
17
+ const { raw: _raw, ...evidence } = event.evidence;
18
+ return { ...event, evidence };
19
+ }
20
+ /** Return a copy of one turn event safe to forward to browsers or end-user SSE. */
21
+ function forClient(event, options) {
22
+ let out = stripErrorInternal(event);
23
+ if (!options?.includeEvidenceRaw) {
24
+ out = stripEvidenceRaw(out);
25
+ }
26
+ return out;
27
+ }
28
+ /** Map {@link forClient} over a batch (e.g. Live relay or HTTP stream flush). */
29
+ function forClientEvents(events, options) {
30
+ return events.map((event) => forClient(event, options));
31
+ }
32
+ export { forClient, forClientEvents };
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Optional host-application helpers.
3
3
  *
4
- * These are not part of the turn kernel. They exist for Deno HTTP hosts that
5
- * want shared reply status mapping and cutout-trace flushing without re-
6
- * implementing the same glue in every app route.
4
+ * Not part of the turn kernel. Shared glue for Deno HTTP hosts (reply status,
5
+ * cutout-trace flush) and live structured-output preview while tokens stream.
7
6
  *
8
7
  * @module
9
8
  */
10
9
  import "../../_dnt.polyfills.js";
10
+ export type { ClientTurnOptions } from './client-turn.js';
11
+ export { forClient, forClientEvents } from './client-turn.js';
11
12
  export type { CutoutTape } from './mint-trace.js';
12
13
  export { flushMintTrace } from './mint-trace.js';
14
+ export { readStreamingJsonStringField } from './readStreamingJsonStringField.js';
13
15
  export { caughtStatus, HTTP_BUSY, HTTP_METHOD, HTTP_NOT_FOUND, HTTP_OK, json, } from './reply.js';
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * Optional host-application helpers.
3
3
  *
4
- * These are not part of the turn kernel. They exist for Deno HTTP hosts that
5
- * want shared reply status mapping and cutout-trace flushing without re-
6
- * implementing the same glue in every app route.
4
+ * Not part of the turn kernel. Shared glue for Deno HTTP hosts (reply status,
5
+ * cutout-trace flush) and live structured-output preview while tokens stream.
7
6
  *
8
7
  * @module
9
8
  */
10
9
  import "../../_dnt.polyfills.js";
10
+ export { forClient, forClientEvents } from './client-turn.js';
11
11
  export { flushMintTrace } from './mint-trace.js';
12
+ export { readStreamingJsonStringField } from './readStreamingJsonStringField.js';
12
13
  export { caughtStatus, HTTP_BUSY, HTTP_METHOD, HTTP_NOT_FOUND, HTTP_OK, json, } from './reply.js';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Turn event folding — map kernel `TurnEvent` streams to transcript blocks.
3
+ *
4
+ * @module
5
+ */
6
+ import type { TurnEvent } from '../kernel/types.js';
7
+ import type { FoldTurnEventsOptions, TranscriptBlock, UserTurnDraft } from './types.js';
8
+ declare function resetBlockIds(): void;
9
+ /** Build transcript blocks for a user-authored turn. */
10
+ declare function buildUserTurnBlocks(draft: UserTurnDraft, idPrefix?: string): TranscriptBlock[];
11
+ /**
12
+ * Fold a single assistant turn's `TurnEvent` stream into ordered transcript blocks.
13
+ *
14
+ * Merges consecutive `text` and `thought` deltas, upserts tool calls by id, and
15
+ * skips kernel bookkeeping events (`tokens`, `session`) unless folded into `turn-done`.
16
+ */
17
+ declare function foldTurnEvents(events: readonly TurnEvent[], options?: FoldTurnEventsOptions): TranscriptBlock[];
18
+ /** User draft blocks followed by folded assistant turn events. */
19
+ declare function foldConversationTurn(draft: UserTurnDraft, assistantEvents: readonly TurnEvent[], options?: FoldTurnEventsOptions): TranscriptBlock[];
20
+ export { buildUserTurnBlocks, foldConversationTurn, foldTurnEvents, resetBlockIds };
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Turn event folding — map kernel `TurnEvent` streams to transcript blocks.
3
+ *
4
+ * @module
5
+ */
6
+ let blockCounter = 0;
7
+ function nextBlockId(prefix) {
8
+ blockCounter += 1;
9
+ return `${prefix}-${String(blockCounter)}`;
10
+ }
11
+ function resetBlockIds() {
12
+ blockCounter = 0;
13
+ }
14
+ function isAppendableTextBlock(block, kind) {
15
+ return block?.kind === kind;
16
+ }
17
+ function toolKey(event, index) {
18
+ const tool = event.tool;
19
+ if (!tool)
20
+ return `tool-${String(index)}`;
21
+ return tool.callId ?? tool.id ?? `${tool.name}-${String(index)}`;
22
+ }
23
+ function appendText(blocks, kind, text, idPrefix) {
24
+ const last = blocks.at(-1);
25
+ if (isAppendableTextBlock(last, kind)) {
26
+ last.text += text;
27
+ return;
28
+ }
29
+ blocks.push({
30
+ id: nextBlockId(idPrefix),
31
+ kind,
32
+ text,
33
+ });
34
+ }
35
+ function upsertToolBlock(blocks, event, key) {
36
+ const tool = event.tool;
37
+ if (!tool)
38
+ return;
39
+ const existing = blocks.find((block) => block.kind === 'tool' && block.id === `tool-${key}`);
40
+ const payload = { ...tool, id: tool.id ?? tool.callId };
41
+ if (existing && existing.kind === 'tool') {
42
+ existing.tool = { ...existing.tool, ...payload };
43
+ return;
44
+ }
45
+ blocks.push({
46
+ id: `tool-${key}`,
47
+ kind: 'tool',
48
+ tool: payload,
49
+ });
50
+ }
51
+ /** Build transcript blocks for a user-authored turn. */
52
+ function buildUserTurnBlocks(draft, idPrefix = 'user') {
53
+ resetBlockIds();
54
+ const blocks = [];
55
+ const text = draft.text?.trim();
56
+ if (text) {
57
+ blocks.push({
58
+ id: nextBlockId(idPrefix),
59
+ kind: 'user-text',
60
+ text,
61
+ });
62
+ }
63
+ for (const file of draft.attachments ?? []) {
64
+ blocks.push({
65
+ id: nextBlockId(idPrefix),
66
+ kind: 'user-attachment',
67
+ name: file.name,
68
+ mimeType: file.mimeType,
69
+ sizeBytes: file.sizeBytes,
70
+ });
71
+ }
72
+ for (const file of draft.voice ?? []) {
73
+ blocks.push({
74
+ id: nextBlockId(idPrefix),
75
+ kind: 'user-voice',
76
+ name: file.name,
77
+ mimeType: file.mimeType,
78
+ sizeBytes: file.sizeBytes,
79
+ });
80
+ }
81
+ return blocks;
82
+ }
83
+ /**
84
+ * Fold a single assistant turn's `TurnEvent` stream into ordered transcript blocks.
85
+ *
86
+ * Merges consecutive `text` and `thought` deltas, upserts tool calls by id, and
87
+ * skips kernel bookkeeping events (`tokens`, `session`) unless folded into `turn-done`.
88
+ */
89
+ function foldTurnEvents(events, options = {}) {
90
+ const idPrefix = options.idPrefix ?? 'turn';
91
+ const showThoughts = options.showThoughts ?? true;
92
+ resetBlockIds();
93
+ const blocks = [];
94
+ let toolIndex = 0;
95
+ for (const event of events) {
96
+ switch (event.type) {
97
+ case 'thought':
98
+ if (showThoughts && event.text) {
99
+ appendText(blocks, 'thought', event.text, idPrefix);
100
+ }
101
+ break;
102
+ case 'text':
103
+ if (event.text) {
104
+ appendText(blocks, 'text', event.text, idPrefix);
105
+ }
106
+ break;
107
+ case 'tool': {
108
+ const key = toolKey(event, toolIndex);
109
+ toolIndex += 1;
110
+ upsertToolBlock(blocks, event, key);
111
+ break;
112
+ }
113
+ case 'structured':
114
+ blocks.push({
115
+ id: nextBlockId(idPrefix),
116
+ kind: 'structured',
117
+ value: event.structured,
118
+ });
119
+ break;
120
+ case 'media':
121
+ if (event.media) {
122
+ blocks.push({
123
+ id: nextBlockId(idPrefix),
124
+ kind: 'media',
125
+ mimeType: event.media.mimeType,
126
+ data: event.media.data,
127
+ });
128
+ }
129
+ break;
130
+ case 'grounding':
131
+ if (event.grounding) {
132
+ blocks.push({
133
+ id: nextBlockId(idPrefix),
134
+ kind: 'grounding',
135
+ grounding: event.grounding,
136
+ });
137
+ }
138
+ break;
139
+ case 'evidence':
140
+ if (event.evidence) {
141
+ blocks.push({
142
+ id: nextBlockId(idPrefix),
143
+ kind: 'evidence',
144
+ evidence: event.evidence,
145
+ });
146
+ }
147
+ break;
148
+ case 'error':
149
+ if (event.error) {
150
+ blocks.push({
151
+ id: nextBlockId(idPrefix),
152
+ kind: 'error',
153
+ message: event.error,
154
+ });
155
+ }
156
+ break;
157
+ case 'done':
158
+ blocks.push({
159
+ id: nextBlockId(idPrefix),
160
+ kind: 'turn-done',
161
+ stop: event.stop,
162
+ tokens: event.tokens,
163
+ interactionId: event.interactionId,
164
+ compaction: event.compaction !== undefined,
165
+ });
166
+ break;
167
+ case 'tokens':
168
+ case 'session':
169
+ break;
170
+ default:
171
+ break;
172
+ }
173
+ }
174
+ return blocks;
175
+ }
176
+ /** User draft blocks followed by folded assistant turn events. */
177
+ function foldConversationTurn(draft, assistantEvents, options = {}) {
178
+ return [...buildUserTurnBlocks(draft, 'user'), ...foldTurnEvents(assistantEvents, options)];
179
+ }
180
+ export { buildUserTurnBlocks, foldConversationTurn, foldTurnEvents, resetBlockIds };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Profile → `ProfileInterface` projection.
3
+ *
4
+ * Kernel `projectProfileObject` is the single inspection projection; this module
5
+ * only enriches `inputs` (acceptAttr) and attaches serializable guardrails.
6
+ *
7
+ * @module
8
+ */
9
+ import type { Profile, ProjectedProfile } from '../kernel/types.js';
10
+ import type { ProfileInterface, ProfileInterfaceSource } from './types.js';
11
+ declare function interfaceFrom(source: ProfileInterfaceSource): ProfileInterface;
12
+ declare function interfaceFromProfile(profile: Profile): ProfileInterface;
13
+ declare function interfaceFromProjected(projected: ProjectedProfile): ProfileInterface;
14
+ export { interfaceFrom, interfaceFromProfile, interfaceFromProjected };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Profile → `ProfileInterface` projection.
3
+ *
4
+ * Kernel `projectProfileObject` is the single inspection projection; this module
5
+ * only enriches `inputs` (acceptAttr) and attaches serializable guardrails.
6
+ *
7
+ * @module
8
+ */
9
+ import { projectProfileObject } from '../kernel/registry/resolve.js';
10
+ import { inputsFromSpec } from './inputs.js';
11
+ function guardrailsView(guardrails) {
12
+ if (!guardrails)
13
+ return undefined;
14
+ return {
15
+ quota: guardrails.quota,
16
+ canary: guardrails.canary,
17
+ sanitizeInput: guardrails.sanitizeInput,
18
+ redactSensitive: guardrails.redactSensitive,
19
+ hasEgress: Boolean(guardrails.egress),
20
+ };
21
+ }
22
+ function normalizeModel(model) {
23
+ return {
24
+ ...model,
25
+ select: model.select ?? null,
26
+ controls: model.controls ?? [],
27
+ };
28
+ }
29
+ function toolsResolved(projected, profile) {
30
+ if (projected.type === 'speech') {
31
+ return { allow: [], resolved: [] };
32
+ }
33
+ if (profile && profile.type !== 'speech') {
34
+ return {
35
+ allow: profile.tools.allow,
36
+ t2Loader: profile.tools.t2Loader,
37
+ resolved: projected.tools,
38
+ };
39
+ }
40
+ const allow = projected.tools
41
+ .filter((tool) => !('type' in tool && tool.type === 'builtin'))
42
+ .map((tool) => tool.name);
43
+ return { allow, resolved: projected.tools };
44
+ }
45
+ function enrich(projected, profile) {
46
+ const inputs = inputsFromSpec(projected.type, projected.inputs);
47
+ const identity = profile?.identity ?? { handle: projected.handle };
48
+ const model = normalizeModel(projected.model);
49
+ const guardrails = profile ? guardrailsView(profile.guardrails) : undefined;
50
+ const outputs = projected.outputs ?? undefined;
51
+ const shared = { id: projected.id, identity, model, outputs, guardrails };
52
+ switch (projected.type) {
53
+ case 'text':
54
+ return {
55
+ ...shared,
56
+ type: 'text',
57
+ inputs,
58
+ tools: toolsResolved(projected, profile),
59
+ turnResumption: profile?.type === 'text' ? profile.turnResumption : undefined,
60
+ };
61
+ case 'image':
62
+ return {
63
+ ...shared,
64
+ type: 'image',
65
+ image: projected.image ?? {},
66
+ inputs,
67
+ tools: toolsResolved(projected, profile),
68
+ turnResumption: profile?.type === 'image' ? profile.turnResumption : undefined,
69
+ };
70
+ case 'speech':
71
+ return {
72
+ ...shared,
73
+ type: 'speech',
74
+ speech: projected.speech ?? {},
75
+ inputs,
76
+ turnResumption: profile?.type === 'speech' ? profile.turnResumption : undefined,
77
+ };
78
+ case 'live':
79
+ return {
80
+ ...shared,
81
+ type: 'live',
82
+ live: projected.live ?? {},
83
+ inputs,
84
+ tools: toolsResolved(projected, profile),
85
+ };
86
+ default: {
87
+ const exhaustive = projected.type;
88
+ return exhaustive;
89
+ }
90
+ }
91
+ }
92
+ function interfaceFrom(source) {
93
+ if ('identity' in source) {
94
+ return enrich(projectProfileObject(source), source);
95
+ }
96
+ return enrich(source);
97
+ }
98
+ function interfaceFromProfile(profile) {
99
+ return interfaceFrom(profile);
100
+ }
101
+ function interfaceFromProjected(projected) {
102
+ return interfaceFrom(projected);
103
+ }
104
+ export { interfaceFrom, interfaceFromProfile, interfaceFromProjected };
@@ -0,0 +1,8 @@
1
+ import type { ProfileInputsSpec, ProfileType } from '../kernel/types.js';
2
+ import type { AttachmentValidationResult, ProfileInputsInterface, UserTurnDraft } from './types.js';
3
+ declare function attachmentAcceptAttr(accept: string[]): string;
4
+ declare function inputsFromSpec(type: ProfileType, inputs: ProfileInputsSpec | null | undefined): ProfileInputsInterface;
5
+ /** Validate staged files against resolved profile `inputs`. */
6
+ declare function validateProfileInputs(inputs: ProfileInputsInterface, draft: Pick<UserTurnDraft, 'attachments' | 'voice'>): AttachmentValidationResult;
7
+ declare function pickMediaRecorderMime(accept?: string[]): string | undefined;
8
+ export { attachmentAcceptAttr, inputsFromSpec, pickMediaRecorderMime, validateProfileInputs };