theorum 0.1.2

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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +338 -0
  3. package/docs/AGENT_PROFILE_CONTRACT.md +161 -0
  4. package/docs/CLI_SPEC.md +183 -0
  5. package/docs/SECRETS.md +55 -0
  6. package/esm/_dnt.polyfills.d.ts +11 -0
  7. package/esm/_dnt.polyfills.js +15 -0
  8. package/esm/_dnt.shims.d.ts +5 -0
  9. package/esm/_dnt.shims.js +61 -0
  10. package/esm/mod.d.ts +37 -0
  11. package/esm/mod.js +35 -0
  12. package/esm/package.json +3 -0
  13. package/esm/src/guardrails/error.d.ts +35 -0
  14. package/esm/src/guardrails/error.js +116 -0
  15. package/esm/src/guardrails/injection.d.ts +12 -0
  16. package/esm/src/guardrails/injection.js +220 -0
  17. package/esm/src/guardrails/keys.d.ts +12 -0
  18. package/esm/src/guardrails/keys.js +132 -0
  19. package/esm/src/guardrails/mod.d.ts +14 -0
  20. package/esm/src/guardrails/mod.js +14 -0
  21. package/esm/src/guardrails/sanitize.d.ts +22 -0
  22. package/esm/src/guardrails/sanitize.js +133 -0
  23. package/esm/src/guardrails/sensitive.d.ts +12 -0
  24. package/esm/src/guardrails/sensitive.js +88 -0
  25. package/esm/src/kernel/engine/boundary.d.ts +10 -0
  26. package/esm/src/kernel/engine/boundary.js +55 -0
  27. package/esm/src/kernel/engine/delta.d.ts +8 -0
  28. package/esm/src/kernel/engine/delta.js +362 -0
  29. package/esm/src/kernel/engine/hash.d.ts +1 -0
  30. package/esm/src/kernel/engine/hash.js +9 -0
  31. package/esm/src/kernel/engine/record.d.ts +2 -0
  32. package/esm/src/kernel/engine/record.js +7 -0
  33. package/esm/src/kernel/engine/repair.d.ts +9 -0
  34. package/esm/src/kernel/engine/repair.js +38 -0
  35. package/esm/src/kernel/engine/runner.d.ts +14 -0
  36. package/esm/src/kernel/engine/runner.js +731 -0
  37. package/esm/src/kernel/engine/tree.d.ts +2 -0
  38. package/esm/src/kernel/engine/tree.js +17 -0
  39. package/esm/src/kernel/mod.d.ts +16 -0
  40. package/esm/src/kernel/mod.js +15 -0
  41. package/esm/src/kernel/registry/catalog.d.ts +24 -0
  42. package/esm/src/kernel/registry/catalog.js +213 -0
  43. package/esm/src/kernel/registry/profiles.d.ts +36 -0
  44. package/esm/src/kernel/registry/profiles.js +111 -0
  45. package/esm/src/kernel/registry/resolve.d.ts +20 -0
  46. package/esm/src/kernel/registry/resolve.js +235 -0
  47. package/esm/src/kernel/registry/schemas.d.ts +14 -0
  48. package/esm/src/kernel/registry/schemas.js +23 -0
  49. package/esm/src/kernel/registry/tools.d.ts +12 -0
  50. package/esm/src/kernel/registry/tools.js +36 -0
  51. package/esm/src/kernel/types.d.ts +497 -0
  52. package/esm/src/kernel/types.js +10 -0
  53. package/esm/src/observability/mod.d.ts +12 -0
  54. package/esm/src/observability/mod.js +10 -0
  55. package/esm/src/observability/spans.d.ts +16 -0
  56. package/esm/src/observability/spans.js +56 -0
  57. package/esm/src/observability/trace-attach.d.ts +16 -0
  58. package/esm/src/observability/trace-attach.js +81 -0
  59. package/esm/src/observability/trace-record.d.ts +112 -0
  60. package/esm/src/observability/trace-record.js +140 -0
  61. package/esm/src/observability/trace-usage.d.ts +3 -0
  62. package/esm/src/observability/trace-usage.js +32 -0
  63. package/esm/src/observability/trace.d.ts +23 -0
  64. package/esm/src/observability/trace.js +121 -0
  65. package/esm/src/providers/attachments.d.ts +17 -0
  66. package/esm/src/providers/attachments.js +156 -0
  67. package/esm/src/providers/gemini-tape.d.ts +3 -0
  68. package/esm/src/providers/gemini-tape.js +46 -0
  69. package/esm/src/providers/google-tap.d.ts +3 -0
  70. package/esm/src/providers/google-tap.js +48 -0
  71. package/esm/src/providers/interactions.d.ts +5 -0
  72. package/esm/src/providers/interactions.js +127 -0
  73. package/esm/src/providers/media.d.ts +5 -0
  74. package/esm/src/providers/media.js +125 -0
  75. package/esm/src/providers/mod.d.ts +15 -0
  76. package/esm/src/providers/mod.js +13 -0
  77. package/esm/src/providers/openrouter-payload.d.ts +24 -0
  78. package/esm/src/providers/openrouter-payload.js +177 -0
  79. package/esm/src/providers/openrouter.d.ts +17 -0
  80. package/esm/src/providers/openrouter.js +332 -0
  81. package/esm/src/providers/provider.d.ts +13 -0
  82. package/esm/src/providers/provider.js +123 -0
  83. package/esm/src/providers/sse.d.ts +7 -0
  84. package/esm/src/providers/sse.js +53 -0
  85. package/esm/src/providers/tts.d.ts +24 -0
  86. package/esm/src/providers/tts.js +144 -0
  87. package/package.json +48 -0
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Profile resolution for THEORUM turns.
3
+ *
4
+ * This module validates caller overrides, selects models and tools, normalizes
5
+ * input parts, and produces the provider request state consumed by `runTurn`.
6
+ *
7
+ * @module
8
+ */
9
+ import { TheorumError } from '../../guardrails/error.js';
10
+ import { resolveGeminiBucket } from '../../guardrails/keys.js';
11
+ import { sanitizeTurnRequest } from '../../guardrails/sanitize.js';
12
+ import { assertImageGrounding, resolveImageFormat, resolveInputParts, } from '../../providers/media.js';
13
+ import { mintCanary } from '../engine/boundary.js';
14
+ import { CATALOG, clampThinkingLevel, modelEntry } from './catalog.js';
15
+ import { getProfile } from './profiles.js';
16
+ const BUILTINS = ['googleSearch', 'googleMaps', 'urlContext'];
17
+ function applyBuiltinMutualExclusions(requested) {
18
+ const search = requested.includes('googleSearch');
19
+ const maps = requested.includes('googleMaps');
20
+ const urlContext = requested.includes('urlContext');
21
+ // Google Interactions API: google_maps cannot be combined with google_search or url_context
22
+ if (maps && (search || urlContext)) {
23
+ return requested.filter((id) => id !== 'googleMaps');
24
+ }
25
+ return requested;
26
+ }
27
+ function firstSelectKey(selectMap) {
28
+ const [key] = Object.keys(selectMap);
29
+ return key;
30
+ }
31
+ function lookupSelectId(profile, select) {
32
+ const { select: selectMap } = profile.model;
33
+ if (!selectMap) {
34
+ return undefined;
35
+ }
36
+ let key = select;
37
+ if (!key) {
38
+ key = firstSelectKey(selectMap);
39
+ }
40
+ if (!key) {
41
+ return undefined;
42
+ }
43
+ return selectMap[key];
44
+ }
45
+ function pickModel(profile, select) {
46
+ if (profile.model.select) {
47
+ const id = lookupSelectId(profile, select);
48
+ if (!(id && profile.model.allow.includes(id))) {
49
+ let label = '';
50
+ if (select) {
51
+ label = select;
52
+ }
53
+ throw new TheorumError(`Unknown model select '${label}' for ${profile.id}`);
54
+ }
55
+ return id;
56
+ }
57
+ const [only] = profile.model.allow;
58
+ if (!only) {
59
+ throw new TheorumError(`Profile ${profile.id} has no models`);
60
+ }
61
+ return only;
62
+ }
63
+ function thinkingFromControl(modelId, thinkingOn) {
64
+ const catalog = modelEntry(modelId);
65
+ if (thinkingOn) {
66
+ return catalog.thinking.on;
67
+ }
68
+ return catalog.thinking.off;
69
+ }
70
+ function pinnedLevel(pinned, key) {
71
+ if (!key) {
72
+ return undefined;
73
+ }
74
+ return pinned[key];
75
+ }
76
+ function thinkingFromPin(profile, select) {
77
+ const pinned = profile.model.thinking;
78
+ if (typeof pinned === 'string') {
79
+ return pinned;
80
+ }
81
+ if (!pinned) {
82
+ throw new TheorumError(`Profile ${profile.id} must pin thinking or list it in controls`);
83
+ }
84
+ const fromSelect = pinnedLevel(pinned, select);
85
+ if (fromSelect) {
86
+ return fromSelect;
87
+ }
88
+ const fromFirst = pinnedLevel(pinned, firstSelectKey(profile.model.select ?? {}));
89
+ if (fromFirst) {
90
+ return fromFirst;
91
+ }
92
+ throw new TheorumError(`Profile ${profile.id} must pin thinking or list it in controls`);
93
+ }
94
+ function resolveThinking(profile, modelId, thinkingOn, select) {
95
+ const raw = profile.model.controls?.includes('thinking')
96
+ ? thinkingFromControl(modelId, thinkingOn)
97
+ : thinkingFromPin(profile, select);
98
+ return clampThinkingLevel(modelId, raw);
99
+ }
100
+ function resolveSummaries(profile, modelId, thinkingOn) {
101
+ const override = profile.model.override?.[modelId]?.summaries;
102
+ if (override) {
103
+ return override;
104
+ }
105
+ const entry = modelEntry(modelId);
106
+ if (profile.model.controls?.includes('thinking')) {
107
+ if (thinkingOn) {
108
+ return entry.summaries.on;
109
+ }
110
+ return entry.summaries.off;
111
+ }
112
+ return entry.summaries.on;
113
+ }
114
+ function isGatedOn(requested, id) {
115
+ if (!requested) {
116
+ return false;
117
+ }
118
+ return requested[id] === true;
119
+ }
120
+ function resolveBuiltins(profile, requested) {
121
+ const allowed = profile.tools.allow.filter((id) => CATALOG.tools[id]?.kind === 'builtin');
122
+ const picked = BUILTINS.filter((id) => allowed.includes(id) && isGatedOn(requested, id));
123
+ return applyBuiltinMutualExclusions(picked);
124
+ }
125
+ function resolveCustom(profile, requested) {
126
+ return profile.tools.allow.filter((id) => CATALOG.tools[id]?.kind === 'custom' && isGatedOn(requested, id));
127
+ }
128
+ function assertToolAllowed(profile, name) {
129
+ if (!profile.tools.allow.includes(name)) {
130
+ throw new TheorumError(`Tool '${name}' is not allowed on ${profile.id}`);
131
+ }
132
+ }
133
+ function resolveStructured(profile, slots) {
134
+ const { structured } = profile.outputs;
135
+ if (!structured) {
136
+ return null;
137
+ }
138
+ if (typeof structured === 'string') {
139
+ return structured;
140
+ }
141
+ const value = slots?.[structured.by];
142
+ if (value) {
143
+ const mapped = structured.map[value];
144
+ if (mapped) {
145
+ return mapped;
146
+ }
147
+ }
148
+ return structured.fallback;
149
+ }
150
+ function generationLimits(profile, model) {
151
+ const catalog = modelEntry(model);
152
+ const ov = profile.model.override?.[model];
153
+ return {
154
+ maxOutputTokens: ov?.maxOutputTokens ?? catalog.maxOutputTokens,
155
+ temperature: ov?.temperature ?? catalog.temperature,
156
+ };
157
+ }
158
+ /** Resolve a host `TurnRequest` into provider-ready generation state. */
159
+ function resolveTurn(req) {
160
+ const safe = sanitizeTurnRequest(req);
161
+ const input = safe.input ?? {};
162
+ const profile = getProfile(safe.profile);
163
+ const model = pickModel(profile, safe.select);
164
+ const thinkingOn = safe.thinking === true;
165
+ const limits = generationLimits(profile, model);
166
+ const builtins = resolveBuiltins(profile, safe.tools);
167
+ assertImageGrounding(model, builtins);
168
+ return {
169
+ profile,
170
+ generation: {
171
+ model,
172
+ previousInteractionId: safe.previousInteractionId,
173
+ store: safe.store,
174
+ thinking: resolveThinking(profile, model, thinkingOn, safe.select),
175
+ summaries: resolveSummaries(profile, model, thinkingOn),
176
+ maxOutputTokens: limits.maxOutputTokens,
177
+ temperature: limits.temperature,
178
+ builtins,
179
+ custom: resolveCustom(profile, safe.tools),
180
+ dynamicTools: safe.dynamicTools,
181
+ dynamicToolLoader: safe.dynamicToolLoader,
182
+ sessionPermissions: safe.sessionPermissions,
183
+ history: input.history,
184
+ maxSteps: profile.model.maxSteps ?? 1,
185
+ structured: resolveStructured(profile, input.slots),
186
+ image: resolveImageFormat(profile, model, input.slots),
187
+ voice: profile.outputs.voice,
188
+ input: resolveInputParts(profile, model, safe),
189
+ geminiBucket: resolveGeminiBucket(profile.model.key ?? 'freeA', model, builtins),
190
+ canary: profile.guardrails.canary !== false ? mintCanary() : '',
191
+ },
192
+ };
193
+ }
194
+ function primaryImageSpec(allow) {
195
+ const [primary] = allow;
196
+ if (!primary) {
197
+ return undefined;
198
+ }
199
+ return modelEntry(primary).image;
200
+ }
201
+ /** UI projection: catalog ∩ profile. Swatches are not included. */
202
+ /** Project a registered profile into a safe host/UI inspection object. */
203
+ function projectProfile(id) {
204
+ const profile = getProfile(id);
205
+ const { model, identity, tools, inputs, outputs } = profile;
206
+ const { select, allow, maxSteps, controls } = model;
207
+ const { handle, chat } = identity;
208
+ const { slots } = inputs;
209
+ return {
210
+ id: profile.id,
211
+ handle,
212
+ chat: chat !== false,
213
+ maxSteps: maxSteps ?? 1,
214
+ models: allow,
215
+ select: select ?? null,
216
+ controls: controls ?? [],
217
+ tools: tools.allow.map((name) => ({
218
+ name,
219
+ ...(CATALOG.tools[name] ?? { kind: 'custom', ui: true }),
220
+ })),
221
+ inputs,
222
+ slots: slots ?? {},
223
+ outputs,
224
+ image: primaryImageSpec(allow),
225
+ };
226
+ }
227
+ function pickSystemRole(profile, requested) {
228
+ const { identity } = profile;
229
+ const { handle, systemByRole } = identity;
230
+ if (requested && systemByRole && Object.hasOwn(systemByRole, requested)) {
231
+ return requested;
232
+ }
233
+ return handle;
234
+ }
235
+ export { assertToolAllowed, pickSystemRole, projectProfile, resolveTurn };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Runtime structured-output schema registry.
3
+ *
4
+ * Host apps register schemas by id, then reference those ids from profile output
5
+ * declarations.
6
+ *
7
+ * @module
8
+ */
9
+ import type { StructuredSpec } from '../types.js';
10
+ /** Register a host-owned structured output schema. */
11
+ declare function registerStructured(id: string, spec: StructuredSpec): void;
12
+ /** Fetch a registered structured output schema or throw a `TheorumError`. */
13
+ declare function getStructured(id: string): StructuredSpec;
14
+ export { getStructured, registerStructured };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Runtime structured-output schema registry.
3
+ *
4
+ * Host apps register schemas by id, then reference those ids from profile output
5
+ * declarations.
6
+ *
7
+ * @module
8
+ */
9
+ import { TheorumError } from '../../guardrails/error.js';
10
+ const schemas = new Map();
11
+ /** Register a host-owned structured output schema. */
12
+ function registerStructured(id, spec) {
13
+ schemas.set(id, spec);
14
+ }
15
+ /** Fetch a registered structured output schema or throw a `TheorumError`. */
16
+ function getStructured(id) {
17
+ const spec = schemas.get(id);
18
+ if (!spec) {
19
+ throw new TheorumError(`Unknown structured schema '${id}'`);
20
+ }
21
+ return spec;
22
+ }
23
+ export { getStructured, registerStructured };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Generic built-in custom tool executor.
3
+ *
4
+ * Static custom tools are intentionally minimal. Application-specific tools
5
+ * should be supplied as dynamic declarations with host-owned handlers.
6
+ *
7
+ * @module
8
+ */
9
+ import type { CustomToolId, Profile, ToolEnvelope } from '../types.js';
10
+ /** Execute a static tool after enforcing the profile allowlist. */
11
+ declare function executeTool(profile: Profile, name: CustomToolId, args: Record<string, unknown>): ToolEnvelope;
12
+ export { executeTool };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Generic built-in custom tool executor.
3
+ *
4
+ * Static custom tools are intentionally minimal. Application-specific tools
5
+ * should be supplied as dynamic declarations with host-owned handlers.
6
+ *
7
+ * @module
8
+ */
9
+ import { CATALOG } from './catalog.js';
10
+ import { assertToolAllowed } from './resolve.js';
11
+ function executeAskUser(args) {
12
+ const { kind, prompt } = args;
13
+ if (kind !== 'confirm' && kind !== 'choice' && kind !== 'text') {
14
+ return { status: 'error', finding: 'askUser.kind must be confirm, choice, or text' };
15
+ }
16
+ if (typeof prompt !== 'string' || !prompt.trim()) {
17
+ return { status: 'error', finding: 'askUser.prompt is required' };
18
+ }
19
+ return {
20
+ status: 'pause',
21
+ finding: prompt,
22
+ data: { ...args, schema: CATALOG.tools.askUser.schema },
23
+ };
24
+ }
25
+ /** Execute a static tool after enforcing the profile allowlist. */
26
+ function executeTool(profile, name, args) {
27
+ assertToolAllowed(profile, name);
28
+ if (name === 'askUser') {
29
+ return executeAskUser(args);
30
+ }
31
+ return {
32
+ status: 'error',
33
+ finding: `Tool '${name}' has no kernel executor; pass a dynamic tool handler instead.`,
34
+ };
35
+ }
36
+ export { executeTool };