@theoremjs/agents 0.3.4-next.480.ge8c8d47 → 0.3.4-next.484.g20d8b9d

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.
@@ -1,4 +1,6 @@
1
1
  import { type Protocol } from '../../kernel/protocols/mod.js';
2
- import { type SystemOneOptions } from '../system-one/protocol.js';
3
- /** OpenRouter's decision API: System One's questions and answers, at OpenRouter's own endpoint. */
4
- export declare const openRouterDecisions: Protocol<SystemOneOptions>;
2
+ /**
3
+ * OpenRouter's decision API: questions about a state, answered in one call. It takes no options:
4
+ * images and box questions need no opt-in, and a request with an image runs in long context.
5
+ */
6
+ export declare const openRouterDecisions: Protocol<Record<string, never>>;
@@ -1,9 +1,57 @@
1
- import { extendProtocol } from '../../kernel/protocols/mod.js';
2
- import { systemOne } from '../system-one/protocol.js';
3
- /** OpenRouter's decision API: System One's questions and answers, at OpenRouter's own endpoint. */
4
- export const openRouterDecisions = extendProtocol(systemOne, {
1
+ import { z } from 'zod';
2
+ import { jsonObjectSchema } from '../../kernel/json.js';
3
+ import { defineProtocol } from '../../kernel/protocols/mod.js';
4
+ import { decisionAnswered } from '../system-one/protocol.js';
5
+ /**
6
+ * OpenRouter's decision API: questions about a state, answered in one call. It takes no options:
7
+ * images and box questions need no opt-in, and a request with an image runs in long context.
8
+ */
9
+ export const openRouterDecisions = defineProtocol({
5
10
  id: 'openRouterDecisions',
6
11
  name: 'OpenRouter Decisions',
7
12
  description: "OpenRouter's decision API: questions about a state, answered in one call.", // labels-exempt: builder field description, read by the studio and docs
8
13
  endpoints: { decision: '/alpha/decisions' },
14
+ options: z.strictObject({}),
15
+ create(context) {
16
+ return {
17
+ decide(request) {
18
+ const assets = request.assets ?? {};
19
+ const body = jsonObjectSchema.parse({
20
+ model: context.apiId,
21
+ state: withImages(request.state, assets),
22
+ questions: Object.fromEntries(Object.entries(request.questions).map(([id, question]) => [
23
+ id,
24
+ {
25
+ ...question,
26
+ instructions: withImages(question.instructions, assets),
27
+ ...('criteria' in question && question.criteria !== undefined
28
+ ? { criteria: withImages(question.criteria, assets) }
29
+ : {}),
30
+ },
31
+ ])),
32
+ });
33
+ return decisionAnswered('openRouterDecisions', context.send(body, { stream: false }));
34
+ },
35
+ };
36
+ },
9
37
  });
38
+ /**
39
+ * Each reference becomes the reference followed by its image, where the reference was.
40
+ * why: OpenRouter ignores an `assets` map; it reads image parts in place.
41
+ */
42
+ function withImages(value, assets) {
43
+ if (typeof value === 'string') {
44
+ const asset = assets[value];
45
+ return asset
46
+ ? [
47
+ value,
48
+ { type: 'image_url', image_url: { url: `data:${asset.mimeType};base64,${asset.data}` } },
49
+ ]
50
+ : value;
51
+ }
52
+ if (Array.isArray(value))
53
+ return value.map((item) => withImages(item, assets));
54
+ if (value !== null && typeof value === 'object')
55
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, withImages(item, assets)]));
56
+ return value;
57
+ }
@@ -1,4 +1,6 @@
1
+ import { type JsonObject } from '../../kernel/json.js';
1
2
  import { type Protocol } from '../../kernel/protocols/mod.js';
3
+ import type { DecisionResult } from '../../kernel/types.js';
2
4
  /** What a System One binding may set. Both are TypeSafe's opt-in "danger zone" headers. */
3
5
  export interface SystemOneOptions {
4
6
  images?: boolean;
@@ -6,3 +8,5 @@ export interface SystemOneOptions {
6
8
  }
7
9
  /** TypeSafe's decision API: questions about a state, answered in one call. */
8
10
  export declare const systemOne: Protocol<SystemOneOptions>;
11
+ /** The first decision reply, read into the kernel's result. Shared by every decision API that answers as System One does. */
12
+ export declare function decisionAnswered(protocol: string, rows: AsyncIterable<JsonObject>): Promise<DecisionResult>;
@@ -56,34 +56,35 @@ export const systemOne = defineProtocol({
56
56
  ? { assets: assetsOnWire(request.assets) }
57
57
  : {}),
58
58
  });
59
- for await (const row of context.send(body, { stream: false, headers })) {
60
- const parsed = reply.safeParse(row);
61
- if (!parsed.success)
62
- throw new TheoremError('bad_response', labelText('protocol.reply_shape', {
63
- protocol: 'systemOne',
64
- issues: z.prettifyError(parsed.error),
65
- }));
66
- const { model, answers, usage } = parsed.data;
67
- // invariant: The kernel validates every answer against its question before exposing it.
68
- return {
69
- model,
70
- answers: answers,
71
- ...(usage
72
- ? {
73
- usage: {
74
- inputTokens: usage.input_tokens,
75
- outputTokens: usage.output_tokens,
76
- ...(usage.cost === undefined ? {} : { costUsd: usage.cost }),
77
- },
78
- }
79
- : {}),
80
- };
81
- }
82
- throw new TheoremError('bad_response', labelText('provider.decision_failed'));
59
+ return await decisionAnswered('systemOne', context.send(body, { stream: false, headers }));
83
60
  },
84
61
  };
85
62
  },
86
63
  });
64
+ /** The first decision reply, read into the kernel's result. Shared by every decision API that answers as System One does. */
65
+ export async function decisionAnswered(protocol, rows) {
66
+ for await (const row of rows) {
67
+ const parsed = reply.safeParse(row);
68
+ if (!parsed.success)
69
+ throw new TheoremError('bad_response', labelText('protocol.reply_shape', { protocol, issues: z.prettifyError(parsed.error) }));
70
+ const { model, answers, usage } = parsed.data;
71
+ // invariant: The kernel validates every answer against its question before exposing it.
72
+ return {
73
+ model,
74
+ answers: answers,
75
+ ...(usage
76
+ ? {
77
+ usage: {
78
+ inputTokens: usage.input_tokens,
79
+ outputTokens: usage.output_tokens,
80
+ ...(usage.cost === undefined ? {} : { costUsd: usage.cost }),
81
+ },
82
+ }
83
+ : {}),
84
+ };
85
+ }
86
+ throw new TheoremError('bad_response', labelText('provider.decision_failed'));
87
+ }
87
88
  const FORMATS = { 'image/png': 'png', 'image/jpeg': 'jpeg' };
88
89
  function assetsOnWire(assets) {
89
90
  return Object.fromEntries(Object.entries(assets).map(([reference, { mimeType, data }]) => [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theoremjs/agents",
3
- "version": "0.3.4-next.480.ge8c8d47",
3
+ "version": "0.3.4-next.484.g20d8b9d",
4
4
  "description": "A flat TypeScript agent kernel for typed profiles, deterministic turn execution, registered tools, provider adapters, guardrails, and host-injected traces.",
5
5
  "keywords": [
6
6
  "agent",