@a-dray/aglib 0.1.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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/dist/agent.d.ts +158 -0
  4. package/dist/agent.js +2 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/content.d.ts +45 -0
  7. package/dist/content.js +11 -0
  8. package/dist/content.js.map +1 -0
  9. package/dist/harness/adapters/acp/index.d.ts +92 -0
  10. package/dist/harness/adapters/acp/index.js +479 -0
  11. package/dist/harness/adapters/acp/index.js.map +1 -0
  12. package/dist/harness/adapters/acp/rpc.d.ts +23 -0
  13. package/dist/harness/adapters/acp/rpc.js +103 -0
  14. package/dist/harness/adapters/acp/rpc.js.map +1 -0
  15. package/dist/harness/adapters/native/compaction.d.ts +16 -0
  16. package/dist/harness/adapters/native/compaction.js +55 -0
  17. package/dist/harness/adapters/native/compaction.js.map +1 -0
  18. package/dist/harness/adapters/native/index.d.ts +2 -0
  19. package/dist/harness/adapters/native/index.js +2 -0
  20. package/dist/harness/adapters/native/index.js.map +1 -0
  21. package/dist/harness/adapters/native/loop.d.ts +25 -0
  22. package/dist/harness/adapters/native/loop.js +153 -0
  23. package/dist/harness/adapters/native/loop.js.map +1 -0
  24. package/dist/harness/harness.d.ts +129 -0
  25. package/dist/harness/harness.js +2 -0
  26. package/dist/harness/harness.js.map +1 -0
  27. package/dist/harness/index.d.ts +3 -0
  28. package/dist/harness/index.js +2 -0
  29. package/dist/harness/index.js.map +1 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +5 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/json.d.ts +4 -0
  34. package/dist/json.js +2 -0
  35. package/dist/json.js.map +1 -0
  36. package/dist/model/adapters/anthropic/index.d.ts +34 -0
  37. package/dist/model/adapters/anthropic/index.js +271 -0
  38. package/dist/model/adapters/anthropic/index.js.map +1 -0
  39. package/dist/model/adapters/fake/index.d.ts +27 -0
  40. package/dist/model/adapters/fake/index.js +37 -0
  41. package/dist/model/adapters/fake/index.js.map +1 -0
  42. package/dist/model/adapters/openai-compatible/index.d.ts +29 -0
  43. package/dist/model/adapters/openai-compatible/index.js +226 -0
  44. package/dist/model/adapters/openai-compatible/index.js.map +1 -0
  45. package/dist/model/conformance.d.ts +210 -0
  46. package/dist/model/conformance.js +427 -0
  47. package/dist/model/conformance.js.map +1 -0
  48. package/dist/model/index.d.ts +2 -0
  49. package/dist/model/index.js +2 -0
  50. package/dist/model/index.js.map +1 -0
  51. package/dist/model/model.d.ts +85 -0
  52. package/dist/model/model.js +8 -0
  53. package/dist/model/model.js.map +1 -0
  54. package/dist/result.d.ts +23 -0
  55. package/dist/result.js +3 -0
  56. package/dist/result.js.map +1 -0
  57. package/dist/run.d.ts +10 -0
  58. package/dist/run.js +477 -0
  59. package/dist/run.js.map +1 -0
  60. package/dist/sandbox/adapters/docker.d.ts +22 -0
  61. package/dist/sandbox/adapters/docker.js +443 -0
  62. package/dist/sandbox/adapters/docker.js.map +1 -0
  63. package/dist/sandbox/adapters/local.d.ts +11 -0
  64. package/dist/sandbox/adapters/local.js +284 -0
  65. package/dist/sandbox/adapters/local.js.map +1 -0
  66. package/dist/sandbox/conformance.d.ts +73 -0
  67. package/dist/sandbox/conformance.js +309 -0
  68. package/dist/sandbox/conformance.js.map +1 -0
  69. package/dist/sandbox/index.d.ts +1 -0
  70. package/dist/sandbox/index.js +2 -0
  71. package/dist/sandbox/index.js.map +1 -0
  72. package/dist/sandbox/sandbox.d.ts +172 -0
  73. package/dist/sandbox/sandbox.js +2 -0
  74. package/dist/sandbox/sandbox.js.map +1 -0
  75. package/dist/session/entry.d.ts +196 -0
  76. package/dist/session/entry.js +2 -0
  77. package/dist/session/entry.js.map +1 -0
  78. package/dist/session/index.d.ts +2 -0
  79. package/dist/session/index.js +2 -0
  80. package/dist/session/index.js.map +1 -0
  81. package/dist/session/log.d.ts +20 -0
  82. package/dist/session/log.js +24 -0
  83. package/dist/session/log.js.map +1 -0
  84. package/dist/session/messages.d.ts +42 -0
  85. package/dist/session/messages.js +99 -0
  86. package/dist/session/messages.js.map +1 -0
  87. package/dist/store/adapters/sqlite.d.ts +20 -0
  88. package/dist/store/adapters/sqlite.js +365 -0
  89. package/dist/store/adapters/sqlite.js.map +1 -0
  90. package/dist/store/conformance.d.ts +66 -0
  91. package/dist/store/conformance.js +580 -0
  92. package/dist/store/conformance.js.map +1 -0
  93. package/dist/store/index.d.ts +1 -0
  94. package/dist/store/index.js +2 -0
  95. package/dist/store/index.js.map +1 -0
  96. package/dist/store/store.d.ts +244 -0
  97. package/dist/store/store.js +2 -0
  98. package/dist/store/store.js.map +1 -0
  99. package/dist/tools/execute.d.ts +24 -0
  100. package/dist/tools/execute.js +105 -0
  101. package/dist/tools/execute.js.map +1 -0
  102. package/dist/tools/index.d.ts +2 -0
  103. package/dist/tools/index.js +2 -0
  104. package/dist/tools/index.js.map +1 -0
  105. package/dist/tools/tool.d.ts +106 -0
  106. package/dist/tools/tool.js +34 -0
  107. package/dist/tools/tool.js.map +1 -0
  108. package/package.json +119 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"","sourcesContent":["/** Anything that survives a round-trip through storage or a provider wire. */\nexport type JsonValue =\n | null\n | boolean\n | number\n | string\n | readonly JsonValue[]\n | { readonly [key: string]: JsonValue };\n"]}
@@ -0,0 +1,34 @@
1
+ import type { Model } from "../../model.js";
2
+ export interface AnthropicOptions {
3
+ apiKey: string;
4
+ model: string;
5
+ baseUrl?: string;
6
+ version?: string;
7
+ fetch?: typeof fetch;
8
+ }
9
+ /**
10
+ * The Anthropic Messages wire.
11
+ *
12
+ * A genuinely different shape from OpenAI's — the leading system prompt hoisted
13
+ * out of the message list, tool results carried as user content blocks, cache
14
+ * breakpoints marked inline — which is the point of it being here: a port
15
+ * proved against one wire format has not been proved.
16
+ *
17
+ * Three things this wire will refuse, and what we send instead:
18
+ *
19
+ * - **Thinking budgets.** `thinking: { type: "enabled", budget_tokens }` is
20
+ * removed on the current models and returns 400. Depth is `output_config.effort`
21
+ * over adaptive thinking, so `effort` maps there and nothing computes a budget.
22
+ * - **Sampling controls.** `temperature`, `top_p` and `top_k` are removed on the
23
+ * same models. `ModelRequest.temperature` is therefore dropped here rather
24
+ * than forwarded into a request the provider rejects.
25
+ * - **A system message in the middle of a turn.** Only the *leading* run of
26
+ * system messages becomes the `system` field. One arriving after the
27
+ * conversation has started is turn-scoped context, which the projection
28
+ * deliberately places after the cache boundary, so it stays in `messages` as
29
+ * a mid-conversation system message. Hoisting it would put the one thing that
30
+ * changes every turn at the front of the prefix that must not change.
31
+ * Supported on Opus 5, Opus 4.8 and Fable 5; a model without it fails the
32
+ * request rather than silently having its cache invalidated every turn.
33
+ */
34
+ export declare function createAnthropicModel(options: AnthropicOptions): Model;
@@ -0,0 +1,271 @@
1
+ import { err, ok } from "../../../result.js";
2
+ import { textOf } from "../../../content.js";
3
+ /**
4
+ * The Anthropic Messages wire.
5
+ *
6
+ * A genuinely different shape from OpenAI's — the leading system prompt hoisted
7
+ * out of the message list, tool results carried as user content blocks, cache
8
+ * breakpoints marked inline — which is the point of it being here: a port
9
+ * proved against one wire format has not been proved.
10
+ *
11
+ * Three things this wire will refuse, and what we send instead:
12
+ *
13
+ * - **Thinking budgets.** `thinking: { type: "enabled", budget_tokens }` is
14
+ * removed on the current models and returns 400. Depth is `output_config.effort`
15
+ * over adaptive thinking, so `effort` maps there and nothing computes a budget.
16
+ * - **Sampling controls.** `temperature`, `top_p` and `top_k` are removed on the
17
+ * same models. `ModelRequest.temperature` is therefore dropped here rather
18
+ * than forwarded into a request the provider rejects.
19
+ * - **A system message in the middle of a turn.** Only the *leading* run of
20
+ * system messages becomes the `system` field. One arriving after the
21
+ * conversation has started is turn-scoped context, which the projection
22
+ * deliberately places after the cache boundary, so it stays in `messages` as
23
+ * a mid-conversation system message. Hoisting it would put the one thing that
24
+ * changes every turn at the front of the prefix that must not change.
25
+ * Supported on Opus 5, Opus 4.8 and Fable 5; a model without it fails the
26
+ * request rather than silently having its cache invalidated every turn.
27
+ */
28
+ export function createAnthropicModel(options) {
29
+ const call = options.fetch ?? fetch;
30
+ const baseUrl = options.baseUrl ?? "https://api.anthropic.com/v1";
31
+ return {
32
+ id: `anthropic:${options.model}`,
33
+ async *generate(request) {
34
+ // Only the leading run of system messages is the system prompt. Anything
35
+ // after it belongs where the projection put it — see the header.
36
+ let lead = 0;
37
+ while (lead < request.messages.length && request.messages[lead].role === "system")
38
+ lead += 1;
39
+ const system = request.messages.slice(0, lead).map((message) => textOf(message.content));
40
+ const conversation = request.messages.slice(lead);
41
+ // The prefix ends where the caller says it does. In practice that is the
42
+ // end of the system prompt, which is the one span fixed for the whole
43
+ // run; a mark past it is honoured too rather than quietly ignored.
44
+ const boundary = request.cacheAfter;
45
+ const cacheSystem = boundary !== undefined && boundary >= lead && system.length > 0;
46
+ const cacheAt = boundary !== undefined && boundary > lead ? boundary - lead - 1 : -1;
47
+ const maxOutputTokens = request.maxOutputTokens ?? 32_000;
48
+ let response;
49
+ try {
50
+ response = await call(`${baseUrl}/messages`, {
51
+ method: "POST",
52
+ headers: {
53
+ "content-type": "application/json",
54
+ "x-api-key": options.apiKey,
55
+ "anthropic-version": options.version ?? "2023-06-01",
56
+ },
57
+ body: JSON.stringify({
58
+ model: options.model,
59
+ max_tokens: maxOutputTokens,
60
+ ...(system.length ? { system: encodeSystem(system, cacheSystem) } : {}),
61
+ messages: encodeConversation(conversation, cacheAt),
62
+ ...(request.tools?.length ? { tools: request.tools.map(encodeTool) } : {}),
63
+ ...(request.effort
64
+ ? { thinking: { type: "adaptive" }, output_config: { effort: request.effort } }
65
+ : {}),
66
+ stream: true,
67
+ }),
68
+ ...(request.signal ? { signal: request.signal } : {}),
69
+ });
70
+ }
71
+ catch (error) {
72
+ return err(transportError(error, request.signal));
73
+ }
74
+ if (!response.ok)
75
+ return err(await httpError(response));
76
+ if (!response.body)
77
+ return err({ code: "provider", message: "No response body", retryable: true });
78
+ const text = [];
79
+ const calls = [];
80
+ let partial = "";
81
+ let stop;
82
+ let usage = {};
83
+ let model;
84
+ // A stream that dies mid-body — cancelled, dropped, truncated — must come
85
+ // back as a typed failure like any other. Without this the generator throws,
86
+ // `run.result` rejects instead of resolving `cancelled`, no `run.finished`
87
+ // is ever committed, and the activation stays open until its claim expires.
88
+ try {
89
+ for await (const event of sseData(response.body)) {
90
+ let frame;
91
+ try {
92
+ frame = JSON.parse(event);
93
+ }
94
+ catch {
95
+ continue;
96
+ }
97
+ if (frame.type === "message_start" && frame.message) {
98
+ model = frame.message.model;
99
+ usage = { ...usage, ...decodeUsage(frame.message.usage) };
100
+ }
101
+ if (frame.type === "content_block_start" && frame.content_block?.type === "tool_use") {
102
+ calls.push({ callId: frame.content_block.id, name: frame.content_block.name, arguments: "" });
103
+ partial = "";
104
+ }
105
+ if (frame.type === "content_block_delta") {
106
+ const delta = frame.delta;
107
+ if (delta.type === "text_delta" && delta.text) {
108
+ text.push(delta.text);
109
+ yield { type: "text.delta", text: delta.text };
110
+ }
111
+ if (delta.type === "thinking_delta" && delta.thinking)
112
+ yield { type: "reasoning.delta", text: delta.thinking };
113
+ if (delta.type === "input_json_delta" && delta.partial_json !== undefined) {
114
+ partial += delta.partial_json;
115
+ const current = calls.at(-1);
116
+ if (current)
117
+ yield { type: "tool-call.delta", callId: current.callId, arguments: delta.partial_json };
118
+ }
119
+ }
120
+ if (frame.type === "content_block_stop" && calls.length) {
121
+ const current = calls.at(-1);
122
+ if (!current.arguments)
123
+ current.arguments = partial || "{}";
124
+ }
125
+ if (frame.type === "message_delta") {
126
+ stop = frame.delta?.stop_reason ?? stop;
127
+ usage = { ...usage, ...decodeUsage(frame.usage) };
128
+ }
129
+ if (frame.type === "error") {
130
+ return err({ code: "provider", message: frame.error?.message ?? "Provider error", retryable: true });
131
+ }
132
+ }
133
+ }
134
+ catch (error) {
135
+ return err(transportError(error, request.signal));
136
+ }
137
+ return ok({
138
+ message: { content: text.join(""), ...(calls.length ? { calls } : {}) },
139
+ finishReason: stop === "max_tokens" ? "length" : stop === "refusal" ? "refusal" : calls.length ? "tool-calls" : "stop",
140
+ usage,
141
+ ...(model ? { model } : {}),
142
+ });
143
+ },
144
+ };
145
+ }
146
+ /**
147
+ * Text blocks rather than one joined string, so the end of the prefix can carry
148
+ * a breakpoint. Only the last block is marked: a breakpoint caches everything
149
+ * before it, so marking each one would buy nothing and spend the budget of four.
150
+ */
151
+ function encodeSystem(blocks, cache) {
152
+ return blocks.map((text, index) => ({
153
+ type: "text",
154
+ text,
155
+ ...(cache && index === blocks.length - 1 ? { cache_control: { type: "ephemeral" } } : {}),
156
+ }));
157
+ }
158
+ /**
159
+ * Tool results are user-role content blocks here, not a role of their own.
160
+ *
161
+ * `cacheAt` is an index into `messages`; the breakpoint lands on the last
162
+ * content block that message produced. Tool results merge into a preceding user
163
+ * block, so the mapping from message to block is not one to one and the mark is
164
+ * placed as each message is encoded rather than computed afterwards.
165
+ */
166
+ function encodeConversation(messages, cacheAt = -1) {
167
+ const out = [];
168
+ let mark;
169
+ messages.forEach((message, index) => {
170
+ if (message.role === "tool") {
171
+ const block = {
172
+ type: "tool_result", tool_use_id: message.callId, content: textOf(message.content),
173
+ ...(message.isError ? { is_error: true } : {}),
174
+ };
175
+ const last = out.at(-1);
176
+ if (last?.role === "user")
177
+ last.content.push(block);
178
+ else
179
+ out.push({ role: "user", content: [block] });
180
+ }
181
+ else if (message.role === "assistant") {
182
+ const content = [];
183
+ const spoken = textOf(message.content);
184
+ if (spoken)
185
+ content.push({ type: "text", text: spoken });
186
+ for (const call of message.calls ?? []) {
187
+ content.push({ type: "tool_use", id: call.callId, name: call.name, input: safeJson(call.arguments) });
188
+ }
189
+ out.push({ role: "assistant", content });
190
+ }
191
+ else if (message.role === "system") {
192
+ // Turn-scoped context, kept where the projection put it: an operator
193
+ // instruction inside the conversation leaves the cached prefix intact,
194
+ // where rewriting the front of it would invalidate every following turn.
195
+ out.push({ role: "system", content: encodeContent(message.content) });
196
+ }
197
+ else {
198
+ out.push({ role: "user", content: encodeContent(message.content) });
199
+ }
200
+ if (index === cacheAt)
201
+ mark = out.at(-1)?.content;
202
+ });
203
+ const block = mark?.at(-1);
204
+ if (block)
205
+ block["cache_control"] = { type: "ephemeral" };
206
+ return out;
207
+ }
208
+ function encodeContent(content) {
209
+ if (typeof content === "string")
210
+ return [{ type: "text", text: content }];
211
+ const parts = [];
212
+ for (const part of content) {
213
+ if (part.type === "text")
214
+ parts.push({ type: "text", text: part.text });
215
+ if (part.type === "image") {
216
+ parts.push(part.source.kind === "url"
217
+ ? { type: "image", source: { type: "url", url: part.source.url } }
218
+ : { type: "image", source: { type: "base64", media_type: part.mediaType, data: part.source.data } });
219
+ }
220
+ }
221
+ return parts;
222
+ }
223
+ const encodeTool = (tool) => ({
224
+ name: tool.name, description: tool.description, input_schema: tool.parameters,
225
+ });
226
+ const safeJson = (raw) => { try {
227
+ return JSON.parse(raw);
228
+ }
229
+ catch {
230
+ return {};
231
+ } };
232
+ const decodeUsage = (usage) => usage ? {
233
+ ...(usage.input_tokens !== undefined ? { inputTokens: usage.input_tokens } : {}),
234
+ ...(usage.output_tokens !== undefined ? { outputTokens: usage.output_tokens } : {}),
235
+ ...(usage.cache_read_input_tokens !== undefined ? { cacheReadTokens: usage.cache_read_input_tokens } : {}),
236
+ ...(usage.cache_creation_input_tokens !== undefined ? { cacheWriteTokens: usage.cache_creation_input_tokens } : {}),
237
+ } : {};
238
+ async function httpError(response) {
239
+ const body = await response.text().catch(() => "");
240
+ const code = response.status === 401 || response.status === 403 ? "auth"
241
+ : response.status === 429 ? "rate-limit"
242
+ : response.status === 400 && /prompt is too long|context/i.test(body) ? "context-length"
243
+ : response.status >= 500 ? "provider"
244
+ : "failed";
245
+ return {
246
+ code,
247
+ message: `${response.status} ${response.statusText}${body ? `: ${body.slice(0, 400)}` : ""}`,
248
+ retryable: code === "rate-limit" || code === "provider",
249
+ };
250
+ }
251
+ function transportError(error, signal) {
252
+ if (signal?.aborted)
253
+ return { code: "cancelled", message: "Generation cancelled", retryable: false };
254
+ return { code: "provider", message: error instanceof Error ? error.message : String(error), retryable: true };
255
+ }
256
+ async function* sseData(body) {
257
+ const decoder = new TextDecoder();
258
+ let buffer = "";
259
+ for await (const chunk of body) {
260
+ buffer += decoder.decode(chunk, { stream: true });
261
+ let newline = buffer.indexOf("\n");
262
+ while (newline >= 0) {
263
+ const line = buffer.slice(0, newline).trim();
264
+ buffer = buffer.slice(newline + 1);
265
+ if (line.startsWith("data:"))
266
+ yield line.slice(5).trim();
267
+ newline = buffer.indexOf("\n");
268
+ }
269
+ }
270
+ }
271
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/model/adapters/anthropic/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ContentPart, Message, Model, ModelDelta, ModelError, ModelRequest, ModelResponse, ToolCall, ToolSpec, Usage,\n} from \"../../model.js\";\nimport { err, ok, type Result } from \"../../../result.js\";\nimport { textOf } from \"../../../content.js\";\n\nexport interface AnthropicOptions {\n apiKey: string;\n model: string;\n baseUrl?: string;\n version?: string;\n fetch?: typeof fetch;\n}\n\n/**\n * The Anthropic Messages wire.\n *\n * A genuinely different shape from OpenAI's — the leading system prompt hoisted\n * out of the message list, tool results carried as user content blocks, cache\n * breakpoints marked inline — which is the point of it being here: a port\n * proved against one wire format has not been proved.\n *\n * Three things this wire will refuse, and what we send instead:\n *\n * - **Thinking budgets.** `thinking: { type: \"enabled\", budget_tokens }` is\n * removed on the current models and returns 400. Depth is `output_config.effort`\n * over adaptive thinking, so `effort` maps there and nothing computes a budget.\n * - **Sampling controls.** `temperature`, `top_p` and `top_k` are removed on the\n * same models. `ModelRequest.temperature` is therefore dropped here rather\n * than forwarded into a request the provider rejects.\n * - **A system message in the middle of a turn.** Only the *leading* run of\n * system messages becomes the `system` field. One arriving after the\n * conversation has started is turn-scoped context, which the projection\n * deliberately places after the cache boundary, so it stays in `messages` as\n * a mid-conversation system message. Hoisting it would put the one thing that\n * changes every turn at the front of the prefix that must not change.\n * Supported on Opus 5, Opus 4.8 and Fable 5; a model without it fails the\n * request rather than silently having its cache invalidated every turn.\n */\nexport function createAnthropicModel(options: AnthropicOptions): Model {\n const call = options.fetch ?? fetch;\n const baseUrl = options.baseUrl ?? \"https://api.anthropic.com/v1\";\n\n return {\n id: `anthropic:${options.model}`,\n\n async *generate(request: ModelRequest): AsyncGenerator<ModelDelta, Result<ModelResponse, ModelError>> {\n // Only the leading run of system messages is the system prompt. Anything\n // after it belongs where the projection put it — see the header.\n let lead = 0;\n while (lead < request.messages.length && request.messages[lead]!.role === \"system\") lead += 1;\n const system = request.messages.slice(0, lead).map((message) => textOf(message.content));\n const conversation = request.messages.slice(lead);\n\n // The prefix ends where the caller says it does. In practice that is the\n // end of the system prompt, which is the one span fixed for the whole\n // run; a mark past it is honoured too rather than quietly ignored.\n const boundary = request.cacheAfter;\n const cacheSystem = boundary !== undefined && boundary >= lead && system.length > 0;\n const cacheAt = boundary !== undefined && boundary > lead ? boundary - lead - 1 : -1;\n\n const maxOutputTokens = request.maxOutputTokens ?? 32_000;\n\n let response: Response;\n try {\n response = await call(`${baseUrl}/messages`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-api-key\": options.apiKey,\n \"anthropic-version\": options.version ?? \"2023-06-01\",\n },\n body: JSON.stringify({\n model: options.model,\n max_tokens: maxOutputTokens,\n ...(system.length ? { system: encodeSystem(system, cacheSystem) } : {}),\n messages: encodeConversation(conversation, cacheAt),\n ...(request.tools?.length ? { tools: request.tools.map(encodeTool) } : {}),\n ...(request.effort\n ? { thinking: { type: \"adaptive\" }, output_config: { effort: request.effort } }\n : {}),\n stream: true,\n }),\n ...(request.signal ? { signal: request.signal } : {}),\n });\n } catch (error) {\n return err(transportError(error, request.signal));\n }\n if (!response.ok) return err(await httpError(response));\n if (!response.body) return err({ code: \"provider\", message: \"No response body\", retryable: true });\n\n const text: string[] = [];\n const calls: ToolCall[] = [];\n let partial = \"\";\n let stop: string | undefined;\n let usage: Usage = {};\n let model: string | undefined;\n\n // A stream that dies mid-body — cancelled, dropped, truncated — must come\n // back as a typed failure like any other. Without this the generator throws,\n // `run.result` rejects instead of resolving `cancelled`, no `run.finished`\n // is ever committed, and the activation stays open until its claim expires.\n try {\n for await (const event of sseData(response.body)) {\n let frame: AnthropicFrame;\n try { frame = JSON.parse(event) as AnthropicFrame; } catch { continue; }\n\n if (frame.type === \"message_start\" && frame.message) {\n model = frame.message.model;\n usage = { ...usage, ...decodeUsage(frame.message.usage) };\n }\n if (frame.type === \"content_block_start\" && frame.content_block?.type === \"tool_use\") {\n calls.push({ callId: frame.content_block.id!, name: frame.content_block.name!, arguments: \"\" });\n partial = \"\";\n }\n if (frame.type === \"content_block_delta\") {\n const delta = frame.delta!;\n if (delta.type === \"text_delta\" && delta.text) { text.push(delta.text); yield { type: \"text.delta\", text: delta.text }; }\n if (delta.type === \"thinking_delta\" && delta.thinking) yield { type: \"reasoning.delta\", text: delta.thinking };\n if (delta.type === \"input_json_delta\" && delta.partial_json !== undefined) {\n partial += delta.partial_json;\n const current = calls.at(-1);\n if (current) yield { type: \"tool-call.delta\", callId: current.callId, arguments: delta.partial_json };\n }\n }\n if (frame.type === \"content_block_stop\" && calls.length) {\n const current = calls.at(-1)!;\n if (!current.arguments) current.arguments = partial || \"{}\";\n }\n if (frame.type === \"message_delta\") {\n stop = frame.delta?.stop_reason ?? stop;\n usage = { ...usage, ...decodeUsage(frame.usage) };\n }\n if (frame.type === \"error\") {\n return err({ code: \"provider\", message: frame.error?.message ?? \"Provider error\", retryable: true });\n }\n }\n } catch (error) {\n return err(transportError(error, request.signal));\n }\n\n return ok({\n message: { content: text.join(\"\"), ...(calls.length ? { calls } : {}) },\n finishReason: stop === \"max_tokens\" ? \"length\" : stop === \"refusal\" ? \"refusal\" : calls.length ? \"tool-calls\" : \"stop\",\n usage,\n ...(model ? { model } : {}),\n });\n },\n };\n}\n\n/**\n * Text blocks rather than one joined string, so the end of the prefix can carry\n * a breakpoint. Only the last block is marked: a breakpoint caches everything\n * before it, so marking each one would buy nothing and spend the budget of four.\n */\nfunction encodeSystem(blocks: readonly string[], cache: boolean): unknown[] {\n return blocks.map((text, index) => ({\n type: \"text\",\n text,\n ...(cache && index === blocks.length - 1 ? { cache_control: { type: \"ephemeral\" } } : {}),\n }));\n}\n\n/**\n * Tool results are user-role content blocks here, not a role of their own.\n *\n * `cacheAt` is an index into `messages`; the breakpoint lands on the last\n * content block that message produced. Tool results merge into a preceding user\n * block, so the mapping from message to block is not one to one and the mark is\n * placed as each message is encoded rather than computed afterwards.\n */\nfunction encodeConversation(messages: readonly Message[], cacheAt = -1): unknown[] {\n const out: { role: string; content: unknown[] }[] = [];\n let mark: unknown[] | undefined;\n\n messages.forEach((message, index) => {\n if (message.role === \"tool\") {\n const block = {\n type: \"tool_result\", tool_use_id: message.callId, content: textOf(message.content),\n ...(message.isError ? { is_error: true } : {}),\n };\n const last = out.at(-1);\n if (last?.role === \"user\") last.content.push(block);\n else out.push({ role: \"user\", content: [block] });\n } else if (message.role === \"assistant\") {\n const content: unknown[] = [];\n const spoken = textOf(message.content);\n if (spoken) content.push({ type: \"text\", text: spoken });\n for (const call of message.calls ?? []) {\n content.push({ type: \"tool_use\", id: call.callId, name: call.name, input: safeJson(call.arguments) });\n }\n out.push({ role: \"assistant\", content });\n } else if (message.role === \"system\") {\n // Turn-scoped context, kept where the projection put it: an operator\n // instruction inside the conversation leaves the cached prefix intact,\n // where rewriting the front of it would invalidate every following turn.\n out.push({ role: \"system\", content: encodeContent(message.content) });\n } else {\n out.push({ role: \"user\", content: encodeContent(message.content) });\n }\n if (index === cacheAt) mark = out.at(-1)?.content;\n });\n\n const block = mark?.at(-1) as Record<string, unknown> | undefined;\n if (block) block[\"cache_control\"] = { type: \"ephemeral\" };\n return out;\n}\n\nfunction encodeContent(content: Message[\"content\"]): unknown[] {\n if (typeof content === \"string\") return [{ type: \"text\", text: content }];\n const parts: unknown[] = [];\n for (const part of content as readonly ContentPart[]) {\n if (part.type === \"text\") parts.push({ type: \"text\", text: part.text });\n if (part.type === \"image\") {\n parts.push(part.source.kind === \"url\"\n ? { type: \"image\", source: { type: \"url\", url: part.source.url } }\n : { type: \"image\", source: { type: \"base64\", media_type: part.mediaType, data: part.source.data } });\n }\n }\n return parts;\n}\n\nconst encodeTool = (tool: ToolSpec) => ({\n name: tool.name, description: tool.description, input_schema: tool.parameters,\n});\n\nconst safeJson = (raw: string): unknown => { try { return JSON.parse(raw); } catch { return {}; } };\n\nconst decodeUsage = (usage: AnthropicUsage | undefined): Usage => usage ? {\n ...(usage.input_tokens !== undefined ? { inputTokens: usage.input_tokens } : {}),\n ...(usage.output_tokens !== undefined ? { outputTokens: usage.output_tokens } : {}),\n ...(usage.cache_read_input_tokens !== undefined ? { cacheReadTokens: usage.cache_read_input_tokens } : {}),\n ...(usage.cache_creation_input_tokens !== undefined ? { cacheWriteTokens: usage.cache_creation_input_tokens } : {}),\n} : {};\n\nasync function httpError(response: Response): Promise<ModelError> {\n const body = await response.text().catch(() => \"\");\n const code: ModelError[\"code\"] =\n response.status === 401 || response.status === 403 ? \"auth\"\n : response.status === 429 ? \"rate-limit\"\n : response.status === 400 && /prompt is too long|context/i.test(body) ? \"context-length\"\n : response.status >= 500 ? \"provider\"\n : \"failed\";\n return {\n code,\n message: `${response.status} ${response.statusText}${body ? `: ${body.slice(0, 400)}` : \"\"}`,\n retryable: code === \"rate-limit\" || code === \"provider\",\n };\n}\n\nfunction transportError(error: unknown, signal?: AbortSignal): ModelError {\n if (signal?.aborted) return { code: \"cancelled\", message: \"Generation cancelled\", retryable: false };\n return { code: \"provider\", message: error instanceof Error ? error.message : String(error), retryable: true };\n}\n\nasync function* sseData(body: ReadableStream<Uint8Array>): AsyncGenerator<string> {\n const decoder = new TextDecoder();\n let buffer = \"\";\n for await (const chunk of body as unknown as AsyncIterable<Uint8Array>) {\n buffer += decoder.decode(chunk, { stream: true });\n let newline = buffer.indexOf(\"\\n\");\n while (newline >= 0) {\n const line = buffer.slice(0, newline).trim();\n buffer = buffer.slice(newline + 1);\n if (line.startsWith(\"data:\")) yield line.slice(5).trim();\n newline = buffer.indexOf(\"\\n\");\n }\n }\n}\n\ninterface AnthropicUsage {\n input_tokens?: number; output_tokens?: number;\n cache_read_input_tokens?: number; cache_creation_input_tokens?: number;\n}\ninterface AnthropicFrame {\n type: string;\n message?: { model?: string; usage?: AnthropicUsage };\n content_block?: { type: string; id?: string; name?: string };\n delta?: { type?: string; text?: string; thinking?: string; partial_json?: string; stop_reason?: string };\n usage?: AnthropicUsage;\n error?: { message?: string };\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import type { Model, ToolCall, Usage } from "../../model.js";
2
+ export interface FakeResponse {
3
+ text?: string;
4
+ calls?: readonly ToolCall[];
5
+ usage?: Usage;
6
+ /** Which model the provider says answered. Absent, like a provider that does not say. */
7
+ model?: string;
8
+ /**
9
+ * Why the turn ended, where the response's own shape cannot say.
10
+ *
11
+ * Only these two: `stop` and `tool-calls` are already determined by whether
12
+ * there are calls, and a second way to say them would be a second answer to
13
+ * one question. Being cut off at the ceiling or declined by the provider is a
14
+ * fact nothing else here expresses — and without it the loop's two branches
15
+ * for exactly those endings are unreachable from any test.
16
+ */
17
+ finishReason?: "length" | "refusal";
18
+ }
19
+ /**
20
+ * Returns the given responses in order.
21
+ *
22
+ * Deliberately dumb. It is a real implementation of the port — so it runs the
23
+ * same conformance the providers do — and it has exactly one way to express
24
+ * each fact. Running out of responses is an error, never a repeat: a fake more
25
+ * forgiving than reality quietly makes tests pass that should not.
26
+ */
27
+ export declare function createFakeModel(responses: readonly FakeResponse[]): Model;
@@ -0,0 +1,37 @@
1
+ import { err, ok } from "../../../result.js";
2
+ /**
3
+ * Returns the given responses in order.
4
+ *
5
+ * Deliberately dumb. It is a real implementation of the port — so it runs the
6
+ * same conformance the providers do — and it has exactly one way to express
7
+ * each fact. Running out of responses is an error, never a repeat: a fake more
8
+ * forgiving than reality quietly makes tests pass that should not.
9
+ */
10
+ export function createFakeModel(responses) {
11
+ let cursor = 0;
12
+ return {
13
+ id: "fake",
14
+ async *generate({ signal }) {
15
+ if (signal?.aborted)
16
+ return err({ code: "cancelled", message: "Generation cancelled", retryable: false });
17
+ const response = responses[cursor];
18
+ cursor += 1;
19
+ if (!response) {
20
+ return err({
21
+ code: "provider",
22
+ message: `Fake model has no response for call ${cursor}; it was given ${responses.length}`,
23
+ retryable: false,
24
+ });
25
+ }
26
+ if (response.text)
27
+ yield { type: "text.delta", text: response.text };
28
+ return ok({
29
+ message: { content: response.text ?? "", ...(response.calls?.length ? { calls: response.calls } : {}) },
30
+ finishReason: response.finishReason ?? (response.calls?.length ? "tool-calls" : "stop"),
31
+ usage: response.usage ?? {},
32
+ ...(response.model ? { model: response.model } : {}),
33
+ });
34
+ },
35
+ };
36
+ }
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/model/adapters/fake/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,oBAAoB,CAAC;AAoB1D;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,SAAkC;IAChE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO;QACL,EAAE,EAAE,MAAM;QACV,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE;YACxB,IAAI,MAAM,EAAE,OAAO;gBAAE,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1G,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;YACnC,MAAM,IAAI,CAAC,CAAC;YACZ,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,GAAG,CAAC;oBACT,IAAI,EAAE,UAAU;oBAChB,OAAO,EAAE,uCAAuC,MAAM,kBAAkB,SAAS,CAAC,MAAM,EAAE;oBAC1F,SAAS,EAAE,KAAK;iBACjB,CAAC,CAAC;YACL,CAAC;YACD,IAAI,QAAQ,CAAC,IAAI;gBAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;YACrE,OAAO,EAAE,CAAC;gBACR,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAI,IAAI,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;gBACvG,YAAY,EAAE,QAAQ,CAAC,YAAY,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC;gBACvF,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,EAAE;gBAC3B,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACrD,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { Model, ModelDelta, ModelError, ModelResponse, ToolCall, Usage } from \"../../model.js\";\nimport { err, ok, type Result } from \"../../../result.js\";\n\nexport interface FakeResponse {\n text?: string;\n calls?: readonly ToolCall[];\n usage?: Usage;\n /** Which model the provider says answered. Absent, like a provider that does not say. */\n model?: string;\n /**\n * Why the turn ended, where the response's own shape cannot say.\n *\n * Only these two: `stop` and `tool-calls` are already determined by whether\n * there are calls, and a second way to say them would be a second answer to\n * one question. Being cut off at the ceiling or declined by the provider is a\n * fact nothing else here expresses — and without it the loop's two branches\n * for exactly those endings are unreachable from any test.\n */\n finishReason?: \"length\" | \"refusal\";\n}\n\n/**\n * Returns the given responses in order.\n *\n * Deliberately dumb. It is a real implementation of the port — so it runs the\n * same conformance the providers do — and it has exactly one way to express\n * each fact. Running out of responses is an error, never a repeat: a fake more\n * forgiving than reality quietly makes tests pass that should not.\n */\nexport function createFakeModel(responses: readonly FakeResponse[]): Model {\n let cursor = 0;\n return {\n id: \"fake\",\n async *generate({ signal }): AsyncGenerator<ModelDelta, Result<ModelResponse, ModelError>> {\n if (signal?.aborted) return err({ code: \"cancelled\", message: \"Generation cancelled\", retryable: false });\n const response = responses[cursor];\n cursor += 1;\n if (!response) {\n return err({\n code: \"provider\",\n message: `Fake model has no response for call ${cursor}; it was given ${responses.length}`,\n retryable: false,\n });\n }\n if (response.text) yield { type: \"text.delta\", text: response.text };\n return ok({\n message: { content: response.text ?? \"\", ...(response.calls?.length ? { calls: response.calls } : {}) },\n finishReason: response.finishReason ?? (response.calls?.length ? \"tool-calls\" : \"stop\"),\n usage: response.usage ?? {},\n ...(response.model ? { model: response.model } : {}),\n });\n },\n };\n}\n"]}
@@ -0,0 +1,29 @@
1
+ import type { Model } from "../../model.js";
2
+ export interface OpenAiCompatibleOptions {
3
+ apiKey: string;
4
+ baseUrl: string;
5
+ model: string;
6
+ headers?: Readonly<Record<string, string>>;
7
+ /**
8
+ * How this endpoint spells reasoning effort. OpenAI takes `reasoning_effort`;
9
+ * OpenRouter takes `reasoning: { effort }` and forwards it to whoever is
10
+ * serving the model. Sending both is not compatibility — OpenAI rejects the
11
+ * request outright — so the dialect is declared rather than guessed.
12
+ */
13
+ effortParameter?: "reasoning_effort" | "reasoning" | "none";
14
+ /** Injectable for tests; defaults to global fetch. */
15
+ fetch?: typeof fetch;
16
+ }
17
+ /**
18
+ * The OpenAI chat-completions wire, which almost every provider now speaks —
19
+ * OpenRouter, Together, Groq, vLLM, Ollama. One adapter, one base URL.
20
+ */
21
+ export declare function createOpenAiCompatibleModel(options: OpenAiCompatibleOptions): Model;
22
+ /** OpenRouter is this wire with a fixed base URL and attribution headers. */
23
+ export declare function createOpenRouterModel(input: {
24
+ apiKey: string;
25
+ model: string;
26
+ appUrl?: string;
27
+ appName?: string;
28
+ fetch?: typeof fetch;
29
+ }): Model;