theorum 0.1.13 → 0.1.14
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.
- package/README.md +81 -4
- package/docs/cli.md +97 -0
- package/docs/guardrails.md +178 -0
- package/docs/host.md +97 -0
- package/docs/kernel.md +404 -0
- package/docs/observability.md +105 -0
- package/docs/openrouter.md +125 -0
- package/docs/presets-google.md +91 -0
- package/docs/presets.md +88 -0
- package/docs/providers.md +201 -0
- package/docs/streaming.md +96 -0
- package/esm/mod.d.ts +1 -1
- package/esm/mod.js +1 -1
- package/esm/src/kernel/mod.d.ts +1 -1
- package/esm/src/kernel/mod.js +1 -1
- package/esm/src/kernel/registry/catalog.d.ts +3 -3
- package/esm/src/kernel/registry/catalog.js +5 -5
- package/esm/src/kernel/registry/ingress.js +6 -6
- package/esm/src/kernel/types.d.ts +3 -3
- package/esm/src/providers/local.d.ts +1 -1
- package/esm/src/providers/local.js +1 -1
- package/package.json +1 -1
- package/docs/COMPACTION.md +0 -227
- package/docs/SECRETS.md +0 -65
- package/docs/STOP.md +0 -85
package/docs/kernel.md
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# Kernel (`theorum/kernel`)
|
|
2
|
+
|
|
3
|
+
Type-first contracts for profiles, turns, tools, compaction, stop/resume, and
|
|
4
|
+
`runTurn`. Import here when a host needs the kernel surface without pulling
|
|
5
|
+
provider adapters.
|
|
6
|
+
|
|
7
|
+
## Export
|
|
8
|
+
|
|
9
|
+
| Field | Value |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Import | `theorum/kernel` / `jsr:@theorum/core/kernel` |
|
|
12
|
+
| Module | `src/kernel/mod.ts` |
|
|
13
|
+
| Also on | Root `theorum` / `mod.ts` re-exports the same runner and many helpers |
|
|
14
|
+
|
|
15
|
+
## Ownership
|
|
16
|
+
|
|
17
|
+
| Scope | Path |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| Tree | `src/kernel/` (engine, registry, `stop.ts`, `types.ts`) |
|
|
20
|
+
| Re-exports | `theorum/streaming` re-exports stop helpers; source of truth stays here |
|
|
21
|
+
|
|
22
|
+
## Profiles
|
|
23
|
+
|
|
24
|
+
Hosts declare agents with `defineProfile` / `registerProfile` (or
|
|
25
|
+
`registerProfiles`). `getProfile` / `hasProfile` / `listProfiles` / `clearProfiles`
|
|
26
|
+
manage the in-memory registry.
|
|
27
|
+
|
|
28
|
+
A `Profile` binds:
|
|
29
|
+
|
|
30
|
+
| Block | Role |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `identity` | `handle`, optional `chat`, `system` / `systemByRole` |
|
|
33
|
+
| `model` | `protocol`, `provider`, `allow`, `config`, optional `select` / `thinking` / `controls` / `maxSteps` / `key` |
|
|
34
|
+
| `tools` | Allowlist ceiling (`allow: ToolId[]`) |
|
|
35
|
+
| `inputs` | Text / attachments / voice / slots / per-mime limits |
|
|
36
|
+
| `outputs` | Structured, image, speech, streaming, validation, `resume` |
|
|
37
|
+
| `guardrails` | Quota, canary, sanitize, redact, egress |
|
|
38
|
+
|
|
39
|
+
Multimodal ingress uses provider-neutral `InteractionPart` values;
|
|
40
|
+
`InteractionMediaPart.type` is `MediaInputKind` (`image` | `audio` | `video` |
|
|
41
|
+
`document`). MIME → kind mapping lives in `mediaKindForMime` (`catalog.ts`).
|
|
42
|
+
|
|
43
|
+
`model.protocol` is `'geminiInteractions' | 'openAi'`.
|
|
44
|
+
`model.provider` is `'google' | 'openrouter' | 'local'`.
|
|
45
|
+
Every id in `allow` must exist in `config`. Each `ModelSpec` carries wire ids
|
|
46
|
+
(`apiId`, optional `openRouterId`), `thinking` / `summaries` maps,
|
|
47
|
+
`thinkingLevels`, `maxOutputTokens`, `temperature`, `keyBuiltins`, optional
|
|
48
|
+
vault `key`, optional `compaction`.
|
|
49
|
+
|
|
50
|
+
Defaults applied at registration (`profiles.ts`):
|
|
51
|
+
|
|
52
|
+
| Field | Default when omitted |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `model.maxSteps` | `1` |
|
|
55
|
+
| `guardrails.canary` | `true` |
|
|
56
|
+
| `guardrails.sanitizeInput` | profile-dependent |
|
|
57
|
+
| `guardrails.redactSensitive` | profile-dependent |
|
|
58
|
+
|
|
59
|
+
`projectProfile` / `resolveTurn` project a registered profile + `TurnRequest`
|
|
60
|
+
into a `ProjectedProfile` / `ResolvedGeneration` the runner and providers consume.
|
|
61
|
+
|
|
62
|
+
## Turn lifecycle
|
|
63
|
+
|
|
64
|
+
`runTurn(request, provider, sink?)` is the single deterministic execution path
|
|
65
|
+
for one agent turn. Pipeline (see `engine/runner/mod.ts`):
|
|
66
|
+
|
|
67
|
+
1. **Resolve** — `resolveTurn` picks model, thinking, tools, structured schema,
|
|
68
|
+
streaming flags, canary token.
|
|
69
|
+
2. **Sanitize** — `sanitizeTurnRequest` strips injection/sensitive spans per
|
|
70
|
+
profile guardrails (unless disabled).
|
|
71
|
+
3. **Compaction (before)** — when `timing: 'before'` and threshold fires, kernel
|
|
72
|
+
runs the compaction profile turn synchronously, then continues with trimmed
|
|
73
|
+
history.
|
|
74
|
+
4. **Canary bind** — `bindCanary` embeds the per-turn canary in system text when
|
|
75
|
+
`guardrails.canary` is enabled.
|
|
76
|
+
5. **Provider stream** — `provider.complete` yields partial events; runner may
|
|
77
|
+
gate thoughts/media per `outputs.streaming`.
|
|
78
|
+
6. **Tool loop** — while under `maxSteps`, tool calls execute via `executeTool`
|
|
79
|
+
/ `invokeFromUi`; results feed the next step.
|
|
80
|
+
7. **Validation / repair** — structured output validators (`outputs.validation`)
|
|
81
|
+
may trigger repair turns with `input.repair`.
|
|
82
|
+
8. **Egress** — `guardrails.egress.enforce` may block, refuse, or retry with
|
|
83
|
+
repair guidance before releasing user-visible text.
|
|
84
|
+
9. **Trace** — optional sink receives a `TraceRecord`; failures are swallowed.
|
|
85
|
+
10. **Terminal `done`** — one `done` event with tokens, optional `stop`,
|
|
86
|
+
optional `compaction` signal (`timing: 'after'`).
|
|
87
|
+
|
|
88
|
+
`continueFrom` on `TurnRequest` prepends `CONTINUE_INSTRUCTION` and carries
|
|
89
|
+
partial assistant text/artifact from a resumeable stop.
|
|
90
|
+
|
|
91
|
+
Optional `compactionProvider` on `TurnRequest` when the compactor profile uses a
|
|
92
|
+
different transport than the primary turn.
|
|
93
|
+
|
|
94
|
+
## Stream events
|
|
95
|
+
|
|
96
|
+
`runTurn` and adapters yield `TurnEvent`:
|
|
97
|
+
|
|
98
|
+
| `type` | Payload highlights |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `thought` | Model reasoning stream (may be gated) |
|
|
101
|
+
| `text` | User-visible assistant text |
|
|
102
|
+
| `tool` | Tool call envelope (`ok` / `error` / `pause`) |
|
|
103
|
+
| `structured` | Parsed JSON object when schema enforced |
|
|
104
|
+
| `media` | Generated image/audio bytes + mime |
|
|
105
|
+
| `grounding` | Search/maps grounding metadata |
|
|
106
|
+
| `evidence` | Provider-native evidence attachments |
|
|
107
|
+
| `tokens` | `input` / `output` / `total` usage (billing; may gate `meter: 'input'`) |
|
|
108
|
+
| `done` | Terminal: `stop`, `tokens`, `compaction`, final text pointer |
|
|
109
|
+
| `error` | Public-safe failure (`toErrorEvent`) |
|
|
110
|
+
|
|
111
|
+
`TurnHistoryMessage` preserves `role`, `content`, `parts`, `tool_calls`,
|
|
112
|
+
`tool_call_id`, and opaque `metadata` across turns.
|
|
113
|
+
|
|
114
|
+
## Dynamic tools
|
|
115
|
+
|
|
116
|
+
Per-turn tools sit under profile `tools.allow`:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
TurnRequest: {
|
|
120
|
+
dynamicTools?: DynamicToolDeclaration[];
|
|
121
|
+
dynamicToolLoader?: DynamicToolLoader; // T2 expansion
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
| Field | Role |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `loadTier` | `T0` / `T1` / `T2` visibility strategy (host-owned) |
|
|
128
|
+
| `permissionTier` | `auto` / `session_consent` / `always_confirm` |
|
|
129
|
+
| `parameters` | JSON Schema fragment sent to the provider |
|
|
130
|
+
|
|
131
|
+
`executeTool` runs catalogued builtins/custom tools. Conflicts (e.g. Google maps vs
|
|
132
|
+
search) are enforced via catalog `conflictsWith`.
|
|
133
|
+
|
|
134
|
+
## Outputs and guardrails
|
|
135
|
+
|
|
136
|
+
Profile `outputs` pins behavior the kernel enforces before adapters run:
|
|
137
|
+
|
|
138
|
+
| Pin | Effect |
|
|
139
|
+
| --- | --- |
|
|
140
|
+
| `structured` | Schema id or slot-mapped ids; `responseFormat` vs prompt enforcement |
|
|
141
|
+
| `image` | Aspect ratio, size, mime, grounding allowance, max input images |
|
|
142
|
+
| `speech` | TTS voice + `format` (`pcm` → WAV; `mp3` OpenAI-only) |
|
|
143
|
+
| `streaming` | `mode`, `streamThoughts`, `gateMedia` |
|
|
144
|
+
| `validation` | Field validators + `maxRetries` + `repairGuidance` |
|
|
145
|
+
| `resume` | `allowContinue` / `autoContinue` stop kinds |
|
|
146
|
+
|
|
147
|
+
Profile `guardrails`:
|
|
148
|
+
|
|
149
|
+
| Flag | Effect |
|
|
150
|
+
| --- | --- |
|
|
151
|
+
| `quota` | Host HTTP helper only (`theorum/guardrails`); not enforced inside `runTurn` |
|
|
152
|
+
| `canary` | Per-turn canary token; egress checks leakage |
|
|
153
|
+
| `sanitizeInput` / `redactSensitive` | Pre-provider text/blob scrub |
|
|
154
|
+
| `egress` | Host `enforce` hook; `onBlock`: `reject_to_agent` or `refuse_to_user` |
|
|
155
|
+
|
|
156
|
+
## Compaction
|
|
157
|
+
|
|
158
|
+
Optional per-model policy on `ModelSpec.compaction`. Kernel owns trigger, split,
|
|
159
|
+
and timing; host owns persistence/reassembly unless `timing: 'before'` runs the
|
|
160
|
+
compactor inline.
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
compaction: {
|
|
164
|
+
maxTokens: 2000,
|
|
165
|
+
compactAt: 0.75,
|
|
166
|
+
previousExchanges: 8,
|
|
167
|
+
profile: "my.compactor",
|
|
168
|
+
timing: "after",
|
|
169
|
+
meter: "history",
|
|
170
|
+
trigger: (ctx) =>
|
|
171
|
+
ctx.tokens > ctx.compactAt * ctx.maxTokens || hostRamPressure(),
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Meter
|
|
176
|
+
|
|
177
|
+
| Value | Counts |
|
|
178
|
+
| --- | --- |
|
|
179
|
+
| `history` (default) | `input.historyTokens` or local estimate of `history` only |
|
|
180
|
+
| `input` | Full prompt: `input.inputTokens` (before) or `tokens.input` (after) |
|
|
181
|
+
|
|
182
|
+
Provider `tokens` events always stream; they gate compaction only when
|
|
183
|
+
`meter: 'input'`.
|
|
184
|
+
|
|
185
|
+
### History estimate (`meter: 'history'`)
|
|
186
|
+
|
|
187
|
+
1. Host `historyTokens` wins when set.
|
|
188
|
+
2. Else estimate from `input.history`:
|
|
189
|
+
- **Text** — tiktoken `o200k_base` (`HISTORY_TEXT_ENCODING`) over content,
|
|
190
|
+
text parts, tool-call arguments. Loads **lazily** on first text estimate.
|
|
191
|
+
- **Media** — stubs when size unknown (`HISTORY_MEDIA_TOKENS`: image/document
|
|
192
|
+
258, audio 32, video 263).
|
|
193
|
+
- Current-turn attachments/voice are **not** history.
|
|
194
|
+
|
|
195
|
+
### `previousExchanges`
|
|
196
|
+
|
|
197
|
+
| Value | Retain |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| `≥ 1` integer | That many recent user-started exchanges |
|
|
200
|
+
| `(0, 1)` fraction | Tail fitting in `fraction * maxTokens` (must be `< compactAt`) |
|
|
201
|
+
| `0` | Compact everything |
|
|
202
|
+
|
|
203
|
+
### Compaction profile
|
|
204
|
+
|
|
205
|
+
A compaction profile is a normal registered profile. Minimal summarizer:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
registerProfile(defineProfile({
|
|
209
|
+
id: "my.compactor",
|
|
210
|
+
identity: {
|
|
211
|
+
handle: "Compactor",
|
|
212
|
+
system: "Summarize this conversation concisely. Preserve unresolved issues, "
|
|
213
|
+
+ "decisions, and key facts.",
|
|
214
|
+
},
|
|
215
|
+
model: { /* allow + config */, maxSteps: 1 },
|
|
216
|
+
tools: { allow: [] },
|
|
217
|
+
inputs: { text: true },
|
|
218
|
+
outputs: { structured: "my.summary.schema" },
|
|
219
|
+
guardrails: { canary: false, sanitizeInput: false, redactSensitive: false },
|
|
220
|
+
}));
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### After-turn signal
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
for await (const event of runTurn(req, provider)) {
|
|
227
|
+
if (event.type === "done" && event.compaction?.needed) {
|
|
228
|
+
const { history, tokens, meter, promptTokens } = event.compaction;
|
|
229
|
+
// host runs compactor async, rewrites persisted history
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Compaction exports
|
|
235
|
+
|
|
236
|
+
| Export | Role |
|
|
237
|
+
| --- | --- |
|
|
238
|
+
| `CompactionSpec` / `CompactionMeter` / `CompactionTriggerContext` | Config types |
|
|
239
|
+
| `CompactionSignal` | `done.compaction` payload |
|
|
240
|
+
| `CompactionSplit` / `CompactionTokens` | Split + resolved counts |
|
|
241
|
+
| `resolveHistoryTokens` / `resolveCompactionTokens` | Meter resolution |
|
|
242
|
+
| `estimateHistoryTokens` | Local BPE + media stubs |
|
|
243
|
+
| `compactionNeeded` / `shouldCompact` | Threshold / custom trigger |
|
|
244
|
+
| `splitForCompaction` | `{ toCompact, toRetain }` |
|
|
245
|
+
|
|
246
|
+
Register-time validation: `maxTokens > 0`, `compactAt ∈ (0,1)`, integer
|
|
247
|
+
`previousExchanges ≥ 1`, fractional `< compactAt`, meter ∈ `{history,input}`,
|
|
248
|
+
compaction profile registered first.
|
|
249
|
+
|
|
250
|
+
## Stop and resume
|
|
251
|
+
|
|
252
|
+
Providers map native finish reasons into `TurnStop` on terminal `done` events.
|
|
253
|
+
|
|
254
|
+
| `kind` | Meaning |
|
|
255
|
+
| --- | --- |
|
|
256
|
+
| `completed` | Normal completion |
|
|
257
|
+
| `length` | Output / budget cut off |
|
|
258
|
+
| `tool` | Model requested tool use |
|
|
259
|
+
| `filtered` | Content filter |
|
|
260
|
+
| `provider_error` | Upstream failure |
|
|
261
|
+
| `cancelled` | User / host abort |
|
|
262
|
+
| `stream_incomplete` | Stream ended without terminal reason |
|
|
263
|
+
|
|
264
|
+
Mappers: `turnStopFromOpenRouter`, `turnStopFromInteractionStatus`,
|
|
265
|
+
`turnStopFromClientStreamEnd` (host SSE drop).
|
|
266
|
+
|
|
267
|
+
### Resume policy
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
outputs: {
|
|
271
|
+
resume: {
|
|
272
|
+
allowContinue: ['length', 'stream_incomplete', 'provider_error'],
|
|
273
|
+
autoContinue: ['length', 'stream_incomplete'],
|
|
274
|
+
},
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
| Constant / helper | Value / role |
|
|
279
|
+
| --- | --- |
|
|
280
|
+
| `DEFAULT_ALLOW_CONTINUE` | length, stream_incomplete, provider_error |
|
|
281
|
+
| `DEFAULT_AUTO_CONTINUE` | length, stream_incomplete |
|
|
282
|
+
| `AUTO_CONTINUE_DELAY_MS` | `1500` — suggested pause before one-shot auto-continue |
|
|
283
|
+
| `CONTINUE_INSTRUCTION` | Fixed continue system text (do not replace per app) |
|
|
284
|
+
| `isResumeableStop` | Profile `allowContinue` or default |
|
|
285
|
+
| `shouldAutoContinue` | One silent resume; never for `cancelled` |
|
|
286
|
+
| `isUserCancelledStop` | `kind === 'cancelled'` |
|
|
287
|
+
|
|
288
|
+
### Continue turn
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
for await (const event of runTurn({
|
|
292
|
+
profile: "my.agent",
|
|
293
|
+
input: { text: "" },
|
|
294
|
+
continueFrom: {
|
|
295
|
+
stop: previousDone.stop,
|
|
296
|
+
partialText: bufferedAssistantText,
|
|
297
|
+
},
|
|
298
|
+
}, provider)) { /* … */ }
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
`GenerationStopError` / `isGenerationStopError` optional throw path for hosts
|
|
302
|
+
that prefer exceptions over stream `done.stop`.
|
|
303
|
+
|
|
304
|
+
## Validation
|
|
305
|
+
|
|
306
|
+
Beyond compaction rules (above), `registerProfile` asserts:
|
|
307
|
+
|
|
308
|
+
- Each `tools.allow` / `model.allow` id resolves to catalog / config entries.
|
|
309
|
+
- Profiles with attachments or voice set `maxFiles`, `maxBytes`, `maxTurnBytes`.
|
|
310
|
+
|
|
311
|
+
Runtime structured validation uses `outputs.validation.fields` keyed by dotted
|
|
312
|
+
paths; failures can trigger repair turns via `input.repair`.
|
|
313
|
+
|
|
314
|
+
## Exported API
|
|
315
|
+
|
|
316
|
+
Live barrel: `src/kernel/mod.ts`. Type surface: `export type *` from
|
|
317
|
+
`types.ts` (all public kernel types).
|
|
318
|
+
|
|
319
|
+
| Group | Symbols |
|
|
320
|
+
| --- | --- |
|
|
321
|
+
| Compaction | `CompactionSplit`, `CompactionTokens`, `compactionMeter`, `compactionNeeded`, `estimateHistoryTokens`, `HISTORY_MEDIA_TOKENS`, `HISTORY_TEXT_ENCODING`, `resolveCompactionTokens`, `resolveHistoryTokens`, `shouldCompact`, `splitForCompaction` |
|
|
322
|
+
| Runner | `runTurn` |
|
|
323
|
+
| Catalog | `CATALOG`, `clampThinkingLevel`, `clampThinkingLevelForApiId`, `mediaKindForMime`, `getTool`, `listBuiltinIds`, `mimeAllowed`, `mimeEssence`, `modelEntryByApiId`, `registerTools`, `requireModelSpec`, `resetTools` |
|
|
324
|
+
| Profiles | `ProfileDefinition`, `clearProfiles`, `defineProfile`, `getProfile`, `hasProfile`, `listProfiles`, `registerProfile`, `registerProfiles`, `projectProfile`, `resolveTurn` |
|
|
325
|
+
| Structured + tools | `getStructured`, `registerStructured`, `executeTool` |
|
|
326
|
+
| Stop / resume | `ProfileResumeSpec`, `TurnContinueFrom`, `TurnStop`, `TurnStopKind`, `AUTO_CONTINUE_DELAY_MS`, `CONTINUE_INSTRUCTION`, `DEFAULT_AUTO_CONTINUE`, `GenerationStopError`, `isGenerationStopError`, `isResumeableStop`, `isUserCancelledStop`, `shouldAutoContinue`, `turnStopFromClientStreamEnd`, `turnStopFromInteractionStatus`, `turnStopFromOpenRouter` |
|
|
327
|
+
|
|
328
|
+
```theorum-evidence
|
|
329
|
+
{
|
|
330
|
+
"sections": {
|
|
331
|
+
"Export": {
|
|
332
|
+
"supports": [
|
|
333
|
+
{ "kind": "source", "path": "src/kernel/mod.ts" },
|
|
334
|
+
{ "kind": "config", "path": "package.json" }
|
|
335
|
+
]
|
|
336
|
+
},
|
|
337
|
+
"Ownership": {
|
|
338
|
+
"supports": [
|
|
339
|
+
{ "kind": "source", "path": "src/kernel/mod.ts" },
|
|
340
|
+
{ "kind": "graph", "path": "docs/_map.mjs" }
|
|
341
|
+
]
|
|
342
|
+
},
|
|
343
|
+
"Profiles": {
|
|
344
|
+
"supports": [
|
|
345
|
+
{ "kind": "source", "path": "src/kernel/registry/profiles.ts" },
|
|
346
|
+
{ "kind": "source", "path": "src/kernel/types.ts" },
|
|
347
|
+
{ "kind": "contract_test", "path": "tests/kernel/profiles.test.ts" }
|
|
348
|
+
]
|
|
349
|
+
},
|
|
350
|
+
"Turn lifecycle": {
|
|
351
|
+
"supports": [
|
|
352
|
+
{ "kind": "source", "path": "src/kernel/engine/runner/mod.ts" },
|
|
353
|
+
{ "kind": "source", "path": "src/kernel/registry/resolve.ts" },
|
|
354
|
+
{ "kind": "contract_test", "path": "tests/kernel/theorum.test.ts" }
|
|
355
|
+
]
|
|
356
|
+
},
|
|
357
|
+
"Stream events": {
|
|
358
|
+
"supports": [
|
|
359
|
+
{ "kind": "source", "path": "src/kernel/types.ts" },
|
|
360
|
+
{ "kind": "contract_test", "path": "tests/kernel/theorum.test.ts" }
|
|
361
|
+
]
|
|
362
|
+
},
|
|
363
|
+
"Dynamic tools": {
|
|
364
|
+
"supports": [
|
|
365
|
+
{ "kind": "source", "path": "src/kernel/registry/tools.ts" },
|
|
366
|
+
{ "kind": "source", "path": "src/kernel/engine/runner/tools.ts" },
|
|
367
|
+
{ "kind": "contract_test", "path": "tests/kernel/theorum.test.ts" }
|
|
368
|
+
]
|
|
369
|
+
},
|
|
370
|
+
"Outputs and guardrails": {
|
|
371
|
+
"supports": [
|
|
372
|
+
{ "kind": "source", "path": "src/kernel/engine/runner/gates.ts" },
|
|
373
|
+
{ "kind": "contract_test", "path": "tests/kernel/theorum.test.ts" }
|
|
374
|
+
]
|
|
375
|
+
},
|
|
376
|
+
"Compaction": {
|
|
377
|
+
"supports": [
|
|
378
|
+
{ "kind": "source", "path": "src/kernel/engine/compaction.ts" },
|
|
379
|
+
{ "kind": "source", "path": "src/kernel/engine/history-tokens.ts" },
|
|
380
|
+
{ "kind": "contract_test", "path": "tests/kernel/compaction.test.ts" }
|
|
381
|
+
]
|
|
382
|
+
},
|
|
383
|
+
"Stop and resume": {
|
|
384
|
+
"supports": [
|
|
385
|
+
{ "kind": "source", "path": "src/kernel/stop.ts" },
|
|
386
|
+
{ "kind": "contract_test", "path": "tests/streaming/turnStop.test.ts" }
|
|
387
|
+
]
|
|
388
|
+
},
|
|
389
|
+
"Validation": {
|
|
390
|
+
"supports": [
|
|
391
|
+
{ "kind": "source", "path": "src/kernel/registry/profiles.ts" },
|
|
392
|
+
{ "kind": "source", "path": "src/kernel/engine/runner/schema-validation.ts" },
|
|
393
|
+
{ "kind": "contract_test", "path": "tests/kernel/profiles.test.ts" }
|
|
394
|
+
]
|
|
395
|
+
},
|
|
396
|
+
"Exported API": {
|
|
397
|
+
"supports": [
|
|
398
|
+
{ "kind": "source", "path": "src/kernel/mod.ts" },
|
|
399
|
+
{ "kind": "contract_test", "path": "tests/kernel/theorum.test.ts" }
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
```
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Observability (`theorum/observability`)
|
|
2
|
+
|
|
3
|
+
Trace sinks and record helpers. THEORUM does not own a database, does not read
|
|
4
|
+
trace-related environment variables for destinations, and never lets tracing
|
|
5
|
+
fail a turn.
|
|
6
|
+
|
|
7
|
+
## Export
|
|
8
|
+
|
|
9
|
+
| Field | Value |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Import | `theorum/observability` / `jsr:@theorum/core/observability` |
|
|
12
|
+
| Module | `src/observability/mod.ts` |
|
|
13
|
+
|
|
14
|
+
## Ownership
|
|
15
|
+
|
|
16
|
+
| Path | Role |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| `src/observability/trace.ts` | Sink implementations + `writeTrace` |
|
|
19
|
+
| `src/observability/trace-record.ts` | `TraceRecord` shape |
|
|
20
|
+
| `src/observability/trace-usage.ts` | Token usage attachment |
|
|
21
|
+
| `src/observability/trace-attach.ts` | Attachment helpers |
|
|
22
|
+
| `src/observability/spans.ts` | Span redaction (`applySpans`) |
|
|
23
|
+
|
|
24
|
+
## Trace sinks
|
|
25
|
+
|
|
26
|
+
Pass a `TraceSink` as the optional third argument to `runTurn`:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
for await (const event of runTurn(request, provider, jsonlSink(hostTraceDir))) {
|
|
30
|
+
// …
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Sink | Behavior |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `noopSink()` | Drop records (default when omitted) |
|
|
37
|
+
| `memorySink(into)` | Append `TraceRecord`s to a caller-owned array |
|
|
38
|
+
| `jsonlSink(dir)` | Daily rotating JSONL under a host-chosen directory |
|
|
39
|
+
| `sinkFromDir(dir)` | Resolve a directory sink helper |
|
|
40
|
+
| `resolveTraceDir(...)` | Path helper for hosts assembling a trace root |
|
|
41
|
+
|
|
42
|
+
`jsonlSink` writes `turns-YYYY-MM-DD.jsonl`, rotates around 32 MiB, and prunes
|
|
43
|
+
files older than 14 days. Directory creation is recursive.
|
|
44
|
+
|
|
45
|
+
`writeTrace(sink, recordPromise)` awaits the record and writes it; errors from
|
|
46
|
+
the sink are swallowed so observability cannot abort execution.
|
|
47
|
+
|
|
48
|
+
## Trace records
|
|
49
|
+
|
|
50
|
+
`TraceRecord` captures turn identity, timing, model selection, token usage, and
|
|
51
|
+
related fields for host analytics. Built by `buildRecord` in the runner path and
|
|
52
|
+
consumed by sinks.
|
|
53
|
+
|
|
54
|
+
| Module | Role |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `trace-record.ts` | Record type + builder inputs |
|
|
57
|
+
| `trace-usage.ts` | Attach provider token events |
|
|
58
|
+
| `trace-attach.ts` | Correlate attachments / metadata |
|
|
59
|
+
|
|
60
|
+
## Exported API
|
|
61
|
+
|
|
62
|
+
| Export | Kind |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `TraceSink` | type |
|
|
65
|
+
| `TraceRecord` | type |
|
|
66
|
+
| `jsonlSink`, `memorySink`, `noopSink` | function |
|
|
67
|
+
| `resolveTraceDir`, `sinkFromDir` | function |
|
|
68
|
+
| `writeTrace` | function |
|
|
69
|
+
|
|
70
|
+
```theorum-evidence
|
|
71
|
+
{
|
|
72
|
+
"sections": {
|
|
73
|
+
"Export": {
|
|
74
|
+
"supports": [
|
|
75
|
+
{ "kind": "source", "path": "src/observability/mod.ts" },
|
|
76
|
+
{ "kind": "config", "path": "package.json" }
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
"Ownership": {
|
|
80
|
+
"supports": [
|
|
81
|
+
{ "kind": "source", "path": "src/observability/mod.ts" },
|
|
82
|
+
{ "kind": "graph", "path": "docs/_map.mjs" }
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"Trace sinks": {
|
|
86
|
+
"supports": [
|
|
87
|
+
{ "kind": "source", "path": "src/observability/trace.ts" },
|
|
88
|
+
{ "kind": "contract_test", "path": "tests/observability/trace.test.ts" }
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
"Trace records": {
|
|
92
|
+
"supports": [
|
|
93
|
+
{ "kind": "source", "path": "src/observability/trace-record.ts" },
|
|
94
|
+
{ "kind": "contract_test", "path": "tests/observability/trace.test.ts" }
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
"Exported API": {
|
|
98
|
+
"supports": [
|
|
99
|
+
{ "kind": "source", "path": "src/observability/mod.ts" },
|
|
100
|
+
{ "kind": "contract_test", "path": "tests/observability/trace.test.ts" }
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# OpenRouter (`theorum/openrouter`)
|
|
2
|
+
|
|
3
|
+
Direct OpenRouter provider adapter and OpenAI-compatible payload helpers.
|
|
4
|
+
Prefer `createProvider(profile, { openRouter })` from `theorum` /
|
|
5
|
+
`theorum/providers` for turn execution. Use this entry when building payloads
|
|
6
|
+
or constructing the adapter outside the runner.
|
|
7
|
+
|
|
8
|
+
## Export
|
|
9
|
+
|
|
10
|
+
| Field | Value |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| Import | `theorum/openrouter` / `jsr:@theorum/core/openrouter` |
|
|
13
|
+
| Module | `src/providers/openrouter-mod.ts` |
|
|
14
|
+
|
|
15
|
+
## Ownership
|
|
16
|
+
|
|
17
|
+
| Path | Role |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `src/providers/openrouter.ts` | Chat adapter (`createOpenRouterProvider`) |
|
|
20
|
+
| `src/providers/openrouter-mod.ts` | Public barrel |
|
|
21
|
+
| `src/providers/openrouter-payload.ts` | Payload + model resolution |
|
|
22
|
+
|
|
23
|
+
Importing this entry loads `@openrouter/ai-sdk-provider` / `ai` **immediately**.
|
|
24
|
+
The lazy path through `createProvider` defers that until first `complete`.
|
|
25
|
+
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
`OpenRouterConfig` (host-supplied):
|
|
29
|
+
|
|
30
|
+
| Field | Role |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `apiKey` | Bearer credential |
|
|
33
|
+
| `baseUrl` | Optional API base override |
|
|
34
|
+
| `siteUrl` / `siteName` | Optional HTTP-Referer / X-Title style metadata |
|
|
35
|
+
| `fetch` | Optional custom fetch |
|
|
36
|
+
| `modelMap` | Optional map from THEORUM model id → OpenRouter model string |
|
|
37
|
+
|
|
38
|
+
## Model resolution
|
|
39
|
+
|
|
40
|
+
`resolveOpenRouterModel(modelId, customMap?, wire?)` precedence:
|
|
41
|
+
|
|
42
|
+
| Step | Source |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| 1 | `customMap[modelId]` |
|
|
45
|
+
| 2 | `wire.openRouterId` |
|
|
46
|
+
| 3 | `wire.apiId` when it contains `/` |
|
|
47
|
+
| 4 | `google/${wire.apiId}` when `apiId` set |
|
|
48
|
+
| 5 | Pass-through model id string |
|
|
49
|
+
|
|
50
|
+
## Payloads
|
|
51
|
+
|
|
52
|
+
`toOpenRouterPayload` maps `ProviderCompleteRequest` → OpenAI chat-completions body:
|
|
53
|
+
|
|
54
|
+
| Area | Mapped from |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| Messages | History + multimodal parts |
|
|
57
|
+
| Tools | Catalog builtins + dynamic tools |
|
|
58
|
+
| Structured output | `responseFormat` / JSON schema |
|
|
59
|
+
| Thinking | `reasoning.effort` |
|
|
60
|
+
|
|
61
|
+
## Adapter behavior
|
|
62
|
+
|
|
63
|
+
| Concern | Behavior |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| Entry | `createOpenRouterProvider(config)` → `ModelProvider` |
|
|
66
|
+
| Stream | Yields normalized `TurnEvent`s |
|
|
67
|
+
| Stop | `done.stop` via `turnStopFromOpenRouter` |
|
|
68
|
+
|
|
69
|
+
## Exported API
|
|
70
|
+
|
|
71
|
+
| Export | Kind |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `createOpenRouterProvider` | function |
|
|
74
|
+
| `OpenRouterConfig` | type |
|
|
75
|
+
| `resolveOpenRouterModel` | function |
|
|
76
|
+
| `toOpenRouterPayload` | function |
|
|
77
|
+
|
|
78
|
+
```theorum-evidence
|
|
79
|
+
{
|
|
80
|
+
"sections": {
|
|
81
|
+
"Export": {
|
|
82
|
+
"supports": [
|
|
83
|
+
{ "kind": "source", "path": "src/providers/openrouter-mod.ts" },
|
|
84
|
+
{ "kind": "config", "path": "package.json" }
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
"Ownership": {
|
|
88
|
+
"supports": [
|
|
89
|
+
{ "kind": "source", "path": "src/providers/openrouter.ts" },
|
|
90
|
+
{ "kind": "graph", "path": "docs/_map.mjs" }
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
"Configuration": {
|
|
94
|
+
"supports": [
|
|
95
|
+
{ "kind": "source", "path": "src/providers/openrouter-payload.ts" },
|
|
96
|
+
{ "kind": "contract_test", "path": "tests/providers/openrouter-payload.test.ts" }
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"Model resolution": {
|
|
100
|
+
"supports": [
|
|
101
|
+
{ "kind": "source", "path": "src/providers/openrouter-payload.ts" },
|
|
102
|
+
{ "kind": "contract_test", "path": "tests/providers/openrouter-payload.test.ts" }
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
"Payloads": {
|
|
106
|
+
"supports": [
|
|
107
|
+
{ "kind": "source", "path": "src/providers/openrouter-payload.ts" },
|
|
108
|
+
{ "kind": "contract_test", "path": "tests/providers/openrouter-payload.test.ts" }
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"Adapter behavior": {
|
|
112
|
+
"supports": [
|
|
113
|
+
{ "kind": "source", "path": "src/providers/openrouter.ts" },
|
|
114
|
+
{ "kind": "contract_test", "path": "tests/providers/openrouter.test.ts" }
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
"Exported API": {
|
|
118
|
+
"supports": [
|
|
119
|
+
{ "kind": "source", "path": "src/providers/openrouter-mod.ts" },
|
|
120
|
+
{ "kind": "contract_test", "path": "tests/providers/openrouter.test.ts" }
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|