@codeworksh/aikit 0.7.2 → 0.8.0-dev.20260905193244

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/index.mjs CHANGED
@@ -1,349 +1,15 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { c as FailureSchema, r as normalize, t as failure_exports } from "./failure-xra3YPdY.mjs";
3
- import { E as NamedError, _ as getProviders, c as APIMethodEnumSchema, d as KnownProviderEnumSchema, f as ProviderInfo, g as getModels, h as getModel, i as packageForModel, l as ActiveThinkingLevel, m as clampThinkingLevel, o as resolveLanguageModel, p as calculateCost, r as loadProviderFactory, u as KnownProviderEnum, v as model_exports, x as registry, y as modelsAreEqual } from "./registry-DHskX4NP.mjs";
4
- import { g as shortHash, n as createOpenAICodex, r as openaiCodex, v as openAICodexTools, x as resolveOpenAICodexToolConstraint } from "./openai-codex-_3XkJ4bs.mjs";
2
+ import { S as NamedError, _ as registry, a as KnownProviderEnumSchema, c as calculateCost, d as getModels, f as getProviders, g as optionsKey, i as KnownProviderEnum, l as clampThinkingLevel, m as modelsAreEqual, n as APIMethodEnumSchema, p as model_exports, r as ActiveThinkingLevel, s as ThinkingBudgets$1, u as getModel, v as thinkingTokenBudgetField } from "./model-CBnredXj.mjs";
3
+ import { C as ToolCallSchema, D as transformMessages, E as resolveJsonSchemaConstraint, S as AssistantMessageSchema, T as message_exports, g as shortHash, n as createOpenAICodex, r as openaiCodex, v as openAICodexTools, w as createAssistantMessage$1, x as resolveOpenAICodexToolConstraint } from "./openai-codex-t4B7g-gv.mjs";
4
+ import { r as normalize, t as failure_exports } from "./failure-SAPyG-w2.mjs";
5
+ import { i as packageForModel, o as resolveLanguageModel, r as loadProviderFactory } from "./registry-DYdYTwFt.mjs";
5
6
  import Type$2, { Type, Type as Type$1 } from "typebox";
6
- import { uuidv7 } from "uuidv7";
7
- import Value from "typebox/value";
8
7
  import { jsonSchema, streamText } from "ai";
9
8
  import { parse } from "partial-json";
10
9
  import Schema from "typebox/schema";
11
- //#region src/message/message.ts
12
- var message_exports = /* @__PURE__ */ __exportAll({
13
- AssistantMessageSchema: () => AssistantMessageSchema,
14
- ConstrainedSamplingSchema: () => ConstrainedSamplingSchema,
15
- ContextSchema: () => ContextSchema,
16
- GrammarVariantsSchema: () => GrammarVariantsSchema,
17
- ImageContentSchema: () => ImageContentSchema,
18
- MessageSchema: () => MessageSchema,
19
- StopReasonSchema: () => StopReasonSchema,
20
- TextContentSchema: () => TextContentSchema,
21
- ThinkingContentSchema: () => ThinkingContentSchema,
22
- ToolAbortedSchema: () => ToolAbortedSchema,
23
- ToolCallAbortedPartSchema: () => ToolCallAbortedPartSchema,
24
- ToolCallBaseSchema: () => ToolCallBaseSchema,
25
- ToolCallCompletedPartSchema: () => ToolCallCompletedPartSchema,
26
- ToolCallErrorPartSchema: () => ToolCallErrorPartSchema,
27
- ToolCallInFlightSchema: () => ToolCallInFlightSchema,
28
- ToolCallPendingPartSchema: () => ToolCallPendingPartSchema,
29
- ToolCallRunningPartSchema: () => ToolCallRunningPartSchema,
30
- ToolCallSchema: () => ToolCallSchema,
31
- ToolCallSkippedPartSchema: () => ToolCallSkippedPartSchema,
32
- ToolCallTerminalPartSchema: () => ToolCallTerminalPartSchema,
33
- ToolCompletedSchema: () => ToolCompletedSchema,
34
- ToolErrorResult: () => ToolErrorResult,
35
- ToolErrorSchema: () => ToolErrorSchema,
36
- ToolRunningPartial: () => ToolRunningPartial,
37
- ToolRunningSchema: () => ToolRunningSchema,
38
- ToolSchema: () => ToolSchema,
39
- ToolSkippedSchema: () => ToolSkippedSchema,
40
- ToolStatusEnum: () => ToolStatusEnum,
41
- ToolSuccessResult: () => ToolSuccessResult,
42
- UsageSchema: () => UsageSchema,
43
- UserMessageSchema: () => UserMessageSchema,
44
- createAssistantMessage: () => createAssistantMessage$1,
45
- createMessageId: () => createMessageId,
46
- createUserMessage: () => createUserMessage,
47
- defineTool: () => defineTool,
48
- transformMessages: () => transformMessages
49
- });
50
- const TextContentSchema = Type$2.Object({
51
- type: Type$2.Literal("text"),
52
- text: Type$2.String(),
53
- textSignature: Type$2.Optional(Type$2.String())
54
- });
55
- const ImageContentSchema = Type$2.Object({
56
- type: Type$2.Literal("image"),
57
- data: Type$2.String(),
58
- mimeType: Type$2.String()
59
- });
60
- const ThinkingContentSchema = Type$2.Object({
61
- type: Type$2.Literal("thinking"),
62
- thinking: Type$2.String(),
63
- thinkingSignature: Type$2.Optional(Type$2.String()),
64
- /** When true, the thinking content was redacted by safety filters. The opaque
65
- * encrypted payload is stored in `thinkingSignature` so it can be passed back
66
- * to the API for multi-turn continuity. */
67
- redacted: Type$2.Optional(Type$2.Boolean())
68
- });
69
- const ToolCallBaseSchema = Type$2.Object({
70
- type: Type$2.Literal("toolCall"),
71
- callID: Type$2.String(),
72
- name: Type$2.String(),
73
- arguments: Type$2.Record(Type$2.String(), Type$2.Any()),
74
- thoughtSignature: Type$2.Optional(Type$2.String()),
75
- namespace: Type$2.Optional(Type$2.String()),
76
- /** Names from Context.tools that became available after this tool result. */
77
- addedToolNames: Type$2.Optional(Type$2.Array(Type$2.String())),
78
- time: Type$2.Object({
79
- start: Type$2.Number(),
80
- end: Type$2.Number()
81
- })
82
- });
83
- const ToolSuccessResult = Type$2.Object({
84
- content: Type$2.Array(Type$2.Union([TextContentSchema, ImageContentSchema])),
85
- details: Type$2.Optional(Type$2.Any()),
86
- isError: Type$2.Literal(false)
87
- });
88
- const ToolErrorResult = Type$2.Object({
89
- content: Type$2.Array(Type$2.Union([TextContentSchema, ImageContentSchema])),
90
- details: Type$2.Optional(Type$2.Any()),
91
- isError: Type$2.Literal(true)
92
- });
93
- const ToolRunningPartial = Type$2.Object({
94
- content: Type$2.Optional(Type$2.Array(Type$2.Union([TextContentSchema, ImageContentSchema]))),
95
- details: Type$2.Optional(Type$2.Any())
96
- });
97
- const ToolStatusEnum = {
98
- pending: "pending",
99
- running: "running",
100
- completed: "completed",
101
- error: "error",
102
- skipped: "skipped",
103
- aborted: "aborted"
104
- };
105
- const ToolRunningSchema = Type$2.Object({
106
- status: Type$2.Literal(ToolStatusEnum.running),
107
- partial: Type$2.Optional(ToolRunningPartial)
108
- });
109
- const ToolCompletedSchema = Type$2.Object({
110
- status: Type$2.Literal(ToolStatusEnum.completed),
111
- result: ToolSuccessResult
112
- });
113
- const ToolErrorSchema = Type$2.Object({
114
- status: Type$2.Literal(ToolStatusEnum.error),
115
- result: ToolErrorResult
116
- });
117
- const ToolSkippedSchema = Type$2.Object({
118
- status: Type$2.Literal(ToolStatusEnum.skipped),
119
- result: ToolErrorResult
120
- });
121
- const ToolAbortedSchema = Type$2.Object({
122
- status: Type$2.Literal(ToolStatusEnum.aborted),
123
- result: ToolErrorResult
124
- });
125
- const ToolCallPendingPartSchema = Type$2.Intersect([ToolCallBaseSchema, Type$2.Object({ status: Type$2.Literal(ToolStatusEnum.pending) })]);
126
- const ToolCallRunningPartSchema = Type$2.Evaluate(Type$2.Intersect([ToolCallBaseSchema, ToolRunningSchema]));
127
- const ToolCallCompletedPartSchema = Type$2.Evaluate(Type$2.Intersect([ToolCallBaseSchema, ToolCompletedSchema]));
128
- const ToolCallErrorPartSchema = Type$2.Evaluate(Type$2.Intersect([ToolCallBaseSchema, ToolErrorSchema]));
129
- const ToolCallSkippedPartSchema = Type$2.Evaluate(Type$2.Intersect([ToolCallBaseSchema, ToolSkippedSchema]));
130
- const ToolCallAbortedPartSchema = Type$2.Evaluate(Type$2.Intersect([ToolCallBaseSchema, ToolAbortedSchema]));
131
- const ToolCallTerminalPartSchema = Type$2.Union([
132
- ToolCallCompletedPartSchema,
133
- ToolCallErrorPartSchema,
134
- ToolCallSkippedPartSchema,
135
- ToolCallAbortedPartSchema
136
- ]);
137
- const ToolCallInFlightSchema = Type$2.Object({
138
- callID: Type$2.String(),
139
- name: Type$2.String(),
140
- rawArgs: Type$2.Record(Type$2.String(), Type$2.Unknown()),
141
- args: Type$2.Optional(Type$2.Unknown())
142
- });
143
- const ToolCallSchema = Type$2.Union([
144
- ToolCallPendingPartSchema,
145
- ToolCallRunningPartSchema,
146
- ToolCallTerminalPartSchema
147
- ]);
148
- const UsageSchema = Type$2.Object({
149
- input: Type$2.Number(),
150
- output: Type$2.Number(),
151
- cacheRead: Type$2.Number(),
152
- cacheWrite: Type$2.Number(),
153
- reasoning: Type$2.Optional(Type$2.Number()),
154
- totalTokens: Type$2.Number(),
155
- cost: Type$2.Object({
156
- input: Type$2.Number(),
157
- output: Type$2.Number(),
158
- cacheRead: Type$2.Number(),
159
- cacheWrite: Type$2.Number(),
160
- total: Type$2.Number()
161
- })
162
- });
163
- const StopReasonSchema = Type$2.Union([
164
- Type$2.Literal("stop"),
165
- Type$2.Literal("length"),
166
- Type$2.Literal("toolUse"),
167
- Type$2.Literal("error"),
168
- Type$2.Literal("aborted")
169
- ]);
170
- const UserMessageSchema = Type$2.Object({
171
- messageId: Type$2.String(),
172
- role: Type$2.Literal("user"),
173
- time: Type$2.Object({ created: Type$2.Number() }),
174
- parts: Type$2.Array(Type$2.Union([TextContentSchema, ImageContentSchema]))
175
- });
176
- const AssistantMessageSchema = Type$2.Object({
177
- role: Type$2.Literal("assistant"),
178
- protocol: KnownProviderEnumSchema,
179
- provider: ProviderInfo,
180
- model: Type$2.String(),
181
- usage: UsageSchema,
182
- stopReason: StopReasonSchema,
183
- errorMessage: Type$2.Optional(Type$2.String()),
184
- /** Structured, provider-independent details for a failed request. */
185
- failure: Type$2.Optional(FailureSchema),
186
- time: Type$2.Object({
187
- created: Type$2.Number(),
188
- completed: Type$2.Number()
189
- }),
190
- parts: Type$2.Array(Type$2.Union([
191
- TextContentSchema,
192
- ImageContentSchema,
193
- ThinkingContentSchema,
194
- ToolCallSchema
195
- ])),
196
- responseId: Type$2.Optional(Type$2.String()),
197
- responseModel: Type$2.Optional(Type$2.String()),
198
- messageId: Type$2.String()
199
- });
200
- const MessageSchema = Type$2.Union([UserMessageSchema, AssistantMessageSchema]);
201
- function createMessageId() {
202
- return uuidv7();
203
- }
204
- function createUserMessage(message) {
205
- const { messageId = createMessageId(), ...rest } = message;
206
- return {
207
- messageId,
208
- ...rest
209
- };
210
- }
211
- function createAssistantMessage$1(message) {
212
- const { messageId = createMessageId(), ...rest } = message;
213
- return {
214
- messageId,
215
- ...rest
216
- };
217
- }
218
- const GrammarVariantsSchema = Type$2.Partial(Type$2.Object({
219
- openai_lark: Type$2.String(),
220
- openai_regex: Type$2.String()
221
- }));
222
- const ConstrainedSamplingSchema = Type$2.Union([Type$2.Object({
223
- type: Type$2.Literal("json_schema"),
224
- strict: Type$2.Union([Type$2.Literal("prefer"), Type$2.Literal("require")])
225
- }), Type$2.Object({
226
- type: Type$2.Literal("grammar"),
227
- variants: GrammarVariantsSchema
228
- })]);
229
- /**
230
- * Generic tool definition with typed parameter schema.
231
- * Usage:
232
- *
233
- * const search = Message.defineTool({
234
- * name: "search",
235
- * description: "Search documents",
236
- * parameters: Type.Object({
237
- * query: Type.String(),
238
- * limit: Type.Optional(Type.Number()),
239
- * }),
240
- * });
241
- *
242
- * type SearchParams = Static<typeof search.parameters>;
243
- */
244
- const ToolSchema = Type$2.Object({
245
- name: Type$2.String(),
246
- description: Type$2.String(),
247
- parameters: Type$2.Unsafe({}),
248
- constrainedSampling: Type$2.Optional(Type$2.Union([Type$2.Literal(false), ConstrainedSamplingSchema]))
249
- });
250
- function defineTool(tool) {
251
- return tool;
252
- }
253
- const ContextSchema = Type$2.Object({
254
- systemPrompt: Type$2.Optional(Type$2.String()),
255
- messages: Type$2.Array(MessageSchema),
256
- tools: Type$2.Optional(Type$2.Array(ToolSchema))
257
- });
258
- function isUnresolvedToolCall(toolCall) {
259
- return toolCall.status === ToolStatusEnum.pending || toolCall.status === ToolStatusEnum.running;
260
- }
261
- function syntheticSkippedToolCall(toolCall) {
262
- const { status: _status, partial: _partial, ...base } = toolCall;
263
- return {
264
- ...base,
265
- status: ToolStatusEnum.skipped,
266
- result: {
267
- content: [{
268
- type: "text",
269
- text: "No result provided"
270
- }],
271
- isError: true
272
- },
273
- time: {
274
- ...toolCall.time,
275
- end: Math.max(toolCall.time.end, Date.now())
276
- }
277
- };
278
- }
279
- function transformMessages(messages, model, normalizeToolCallId) {
280
- const toolCallIDMap = /* @__PURE__ */ new Map();
281
- const transformed = [];
282
- for (const msg of messages) {
283
- if (msg.role === "user") {
284
- transformed.push(msg);
285
- continue;
286
- }
287
- const assistantMsg = msg;
288
- const isSameModel = assistantMsg.provider.id === model.provider.id && assistantMsg.protocol === model.protocol && assistantMsg.model === model.id;
289
- const parts = assistantMsg.parts.flatMap((block) => {
290
- if (block.type === "thinking") {
291
- if (isSameModel && block.thinkingSignature) return block;
292
- if (!block.thinking || block.thinking.trim() === "") return [];
293
- if (isSameModel) return block;
294
- return {
295
- type: "text",
296
- text: block.thinking
297
- };
298
- }
299
- if (block.type === "text") {
300
- if (isSameModel) return block;
301
- return {
302
- type: "text",
303
- text: block.text
304
- };
305
- }
306
- if (block.type === "toolCall") {
307
- const toolCall = block;
308
- let normalizedToolCall = toolCall;
309
- if (!isSameModel && toolCall.thoughtSignature) {
310
- normalizedToolCall = { ...toolCall };
311
- delete normalizedToolCall.thoughtSignature;
312
- }
313
- const normalizedID = toolCallIDMap.get(block.callID);
314
- if (normalizedID && normalizedID !== block.callID) normalizedToolCall = {
315
- ...normalizedToolCall,
316
- callID: normalizedID
317
- };
318
- else if (!isSameModel && normalizeToolCallId) {
319
- const nextID = normalizeToolCallId(toolCall.callID, model, assistantMsg);
320
- if (nextID !== toolCall.callID) {
321
- toolCallIDMap.set(toolCall.callID, nextID);
322
- normalizedToolCall = {
323
- ...normalizedToolCall,
324
- callID: nextID
325
- };
326
- }
327
- }
328
- return normalizedToolCall;
329
- }
330
- return block;
331
- }).map((block) => {
332
- if (block.type === "toolCall" && isUnresolvedToolCall(block)) return syntheticSkippedToolCall(block);
333
- return block;
334
- });
335
- transformed.push({
336
- ...assistantMsg,
337
- parts
338
- });
339
- }
340
- return transformed;
341
- }
342
- //#endregion
10
+ import Value from "typebox/value";
343
11
  //#region src/event/event.ts
344
12
  var event_exports = /* @__PURE__ */ __exportAll({
345
- AgentEventSchema: () => AgentEventSchema,
346
- AgentEventType: () => AgentEventType,
347
13
  LLMMessageEventSchema: () => LLMMessageEventSchema,
348
14
  LLMMessageEventType: () => LLMMessageEventType
349
15
  });
@@ -447,122 +113,10 @@ const LLMMessageEventSchema = Type$2.Union([
447
113
  error: AssistantMessageSchema
448
114
  })
449
115
  ]);
450
- const AgentEventType = {
451
- agentStart: "agent.start",
452
- agentEnd: "agent.end",
453
- turnStart: "turn.start",
454
- turnEnd: "turn.end",
455
- messageStart: "message.start",
456
- messagePartStart: "message.part.start",
457
- messagePartUpdate: "message.part.update",
458
- messagePartEnd: "message.part.end",
459
- messageUpdate: "message.update",
460
- messageEnd: "message.end",
461
- toolExecutionStart: "tool.execution.start",
462
- toolExecutionUpdate: "tool.execution.update",
463
- toolExecutionEnd: "tool.execution.end"
464
- };
465
- const UserMessagePart = Type$2.Union([TextContentSchema, ImageContentSchema]);
466
- const AssistantMessagePart = Type$2.Union([
467
- TextContentSchema,
468
- ImageContentSchema,
469
- ThinkingContentSchema,
470
- ToolCallSchema
471
- ]);
472
- const UserMessagePartStart = Type$2.Object({
473
- type: Type$2.Literal(AgentEventType.messagePartStart),
474
- message: UserMessageSchema,
475
- partIndex: Type$2.Number(),
476
- part: UserMessagePart
477
- });
478
- const UserMessagePartEnd = Type$2.Object({
479
- type: Type$2.Literal(AgentEventType.messagePartEnd),
480
- message: UserMessageSchema,
481
- partIndex: Type$2.Number(),
482
- part: UserMessagePart
483
- });
484
- const AssistantMessagePartStart = Type$2.Object({
485
- type: Type$2.Literal(AgentEventType.messagePartStart),
486
- message: AssistantMessageSchema,
487
- partIndex: Type$2.Number(),
488
- part: AssistantMessagePart
489
- });
490
- const AssistantMessagePartLLMUpdate = Type$2.Object({
491
- type: Type$2.Literal(AgentEventType.messagePartUpdate),
492
- message: AssistantMessageSchema,
493
- partIndex: Type$2.Number(),
494
- part: AssistantMessagePart,
495
- source: Type$2.Literal("llm")
496
- });
497
- const AssistantMessagePartToolUpdate = Type$2.Object({
498
- type: Type$2.Literal(AgentEventType.messagePartUpdate),
499
- message: AssistantMessageSchema,
500
- partIndex: Type$2.Number(),
501
- part: AssistantMessagePart,
502
- source: Type$2.Literal("tool")
503
- });
504
- const AssistantMessagePartEnd = Type$2.Object({
505
- type: Type$2.Literal(AgentEventType.messagePartEnd),
506
- message: AssistantMessageSchema,
507
- partIndex: Type$2.Number(),
508
- part: AssistantMessagePart
509
- });
510
- const ToolExecutionStartSchema = Type$2.Object({
511
- type: Type$2.Literal(AgentEventType.toolExecutionStart),
512
- messageId: Type$2.String(),
513
- partIndex: Type$2.Number(),
514
- toolCall: ToolCallPendingPartSchema
515
- });
516
- const ToolExecutionUpdateSchema = Type$2.Object({
517
- type: Type$2.Literal(AgentEventType.toolExecutionUpdate),
518
- messageId: Type$2.String(),
519
- partIndex: Type$2.Number(),
520
- toolCall: ToolCallRunningPartSchema
521
- });
522
- const ToolExecutionEndSchema = Type$2.Object({
523
- type: Type$2.Literal(AgentEventType.toolExecutionEnd),
524
- messageId: Type$2.String(),
525
- partIndex: Type$2.Number(),
526
- toolCall: ToolCallTerminalPartSchema
527
- });
528
116
  /**
529
117
  * Events emitted by the Agent lifecycle
530
118
  * These events provide fine-grained lifecycle information for messages, turns, and tool execution.
531
119
  */
532
- const AgentEventSchema = Type$2.Union([
533
- Type$2.Object({ type: Type$2.Literal(AgentEventType.agentStart) }),
534
- Type$2.Object({
535
- type: Type$2.Literal(AgentEventType.agentEnd),
536
- messages: Type$2.Array(MessageSchema)
537
- }),
538
- Type$2.Object({ type: Type$2.Literal(AgentEventType.turnStart) }),
539
- Type$2.Object({
540
- type: Type$2.Literal(AgentEventType.turnEnd),
541
- message: AssistantMessageSchema
542
- }),
543
- Type$2.Object({
544
- type: Type$2.Literal(AgentEventType.messageStart),
545
- message: MessageSchema
546
- }),
547
- Type$2.Union([UserMessagePartStart, UserMessagePartEnd]),
548
- Type$2.Union([
549
- AssistantMessagePartStart,
550
- AssistantMessagePartLLMUpdate,
551
- AssistantMessagePartToolUpdate,
552
- AssistantMessagePartEnd
553
- ]),
554
- Type$2.Object({
555
- type: Type$2.Literal(AgentEventType.messageUpdate),
556
- message: MessageSchema
557
- }),
558
- Type$2.Object({
559
- type: Type$2.Literal(AgentEventType.messageEnd),
560
- message: MessageSchema
561
- }),
562
- ToolExecutionStartSchema,
563
- ToolExecutionUpdateSchema,
564
- ToolExecutionEndSchema
565
- ]);
566
120
  //#endregion
567
121
  //#region src/llm.ts
568
122
  const llmImpl = async (provider, model, overrides) => getModel(provider, model, overrides);
@@ -641,6 +195,137 @@ function clearProtocolProviders() {
641
195
  protocolRegistry.clear();
642
196
  }
643
197
  //#endregion
198
+ //#region src/utils/estimate.ts
199
+ /** Rough characters-per-token ratio across the tokenizers in use. */
200
+ const CHARS_PER_TOKEN = 4;
201
+ /** A base64 image costs far more than its own text length; this is a flat stand-in. */
202
+ const ESTIMATED_IMAGE_CHARS = 4800;
203
+ function calculateContextTokens(usage) {
204
+ return usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
205
+ }
206
+ function safeJsonStringify(value) {
207
+ try {
208
+ return JSON.stringify(value) ?? "undefined";
209
+ } catch {
210
+ return "[unserializable]";
211
+ }
212
+ }
213
+ function estimateTextTokens(text) {
214
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
215
+ }
216
+ function contentChars(content) {
217
+ let chars = 0;
218
+ for (const block of content) chars += block.type === "text" ? block.text.length : ESTIMATED_IMAGE_CHARS;
219
+ return chars;
220
+ }
221
+ function estimateContentTokens(content) {
222
+ return Math.ceil(contentChars(content) / CHARS_PER_TOKEN);
223
+ }
224
+ /**
225
+ * A tool call costs its name, its arguments, and -- unlike providers that carry
226
+ * results in their own messages -- the result it already holds.
227
+ */
228
+ function toolCallChars(part) {
229
+ return part.name.length + safeJsonStringify(part.arguments).length + toolResultChars(part);
230
+ }
231
+ function toolResultChars(part) {
232
+ return "result" in part && part.result ? contentChars(part.result.content) : 0;
233
+ }
234
+ function estimateMessageTokens(message) {
235
+ if (message.role === "user") return estimateContentTokens(message.parts);
236
+ let chars = 0;
237
+ for (const part of message.parts) switch (part.type) {
238
+ case "text":
239
+ chars += part.text.length;
240
+ break;
241
+ case "image":
242
+ chars += ESTIMATED_IMAGE_CHARS;
243
+ break;
244
+ case "thinking":
245
+ chars += part.thinking.length;
246
+ break;
247
+ case "toolCall": chars += toolCallChars(part);
248
+ }
249
+ return Math.ceil(chars / CHARS_PER_TOKEN);
250
+ }
251
+ function lastAssistantUsage(messages) {
252
+ let latestPrefixTime = Number.NEGATIVE_INFINITY;
253
+ let found;
254
+ for (let i = 0; i < messages.length; i++) {
255
+ const message = messages[i];
256
+ if (message.role === "assistant") {
257
+ if (message.time.created >= latestPrefixTime && message.stopReason !== "aborted" && message.stopReason !== "error" && calculateContextTokens(message.usage) > 0) found = {
258
+ usage: message.usage,
259
+ index: i
260
+ };
261
+ }
262
+ latestPrefixTime = Math.max(latestPrefixTime, message.time.created);
263
+ }
264
+ return found;
265
+ }
266
+ function estimateMessages(messages) {
267
+ const usage = lastAssistantUsage(messages);
268
+ if (usage) {
269
+ const usageTokens = calculateContextTokens(usage.usage);
270
+ const latest = messages[usage.index];
271
+ let resultChars = 0;
272
+ for (const part of latest.parts) if (part.type === "toolCall") resultChars += toolResultChars(part);
273
+ let trailingTokens = Math.ceil(resultChars / CHARS_PER_TOKEN);
274
+ for (let i = usage.index + 1; i < messages.length; i++) trailingTokens += estimateMessageTokens(messages[i]);
275
+ return {
276
+ tokens: usageTokens + trailingTokens,
277
+ usageTokens,
278
+ trailingTokens,
279
+ lastUsageIndex: usage.index
280
+ };
281
+ }
282
+ let tokens = 0;
283
+ for (const message of messages) tokens += estimateMessageTokens(message);
284
+ return {
285
+ tokens,
286
+ usageTokens: 0,
287
+ trailingTokens: tokens,
288
+ lastUsageIndex: null
289
+ };
290
+ }
291
+ function estimateToolsTokens(tools) {
292
+ if (!tools || tools.length === 0) return 0;
293
+ return estimateTextTokens(safeJsonStringify(tools));
294
+ }
295
+ /** Tools discovered by execution of the given assistant turn or later turns. */
296
+ function toolNamesAddedAfter(messages, index) {
297
+ const names = /* @__PURE__ */ new Set();
298
+ for (let i = index; i < messages.length; i++) {
299
+ const message = messages[i];
300
+ if (message.role !== "assistant") continue;
301
+ for (const part of message.parts) {
302
+ if (part.type !== "toolCall") continue;
303
+ for (const name of part.addedToolNames ?? []) names.add(name);
304
+ }
305
+ }
306
+ return names;
307
+ }
308
+ function estimateContextTokens(context) {
309
+ const estimate = estimateMessages(context.messages);
310
+ if (estimate.lastUsageIndex !== null) {
311
+ const added = toolNamesAddedAfter(context.messages, estimate.lastUsageIndex);
312
+ const addedTokens = estimateToolsTokens(context.tools?.filter((tool) => added.has(tool.name)));
313
+ return {
314
+ tokens: estimate.tokens + addedTokens,
315
+ usageTokens: estimate.usageTokens,
316
+ trailingTokens: estimate.trailingTokens + addedTokens,
317
+ lastUsageIndex: estimate.lastUsageIndex
318
+ };
319
+ }
320
+ const prefixTokens = (context.systemPrompt ? estimateTextTokens(context.systemPrompt) : 0) + estimateToolsTokens(context.tools);
321
+ return {
322
+ tokens: estimate.tokens + prefixTokens,
323
+ usageTokens: estimate.usageTokens,
324
+ trailingTokens: estimate.trailingTokens + prefixTokens,
325
+ lastUsageIndex: estimate.lastUsageIndex
326
+ };
327
+ }
328
+ //#endregion
644
329
  //#region src/llm/shared.ts
645
330
  const CacheRetention = Type$1.Union([
646
331
  Type$1.Literal("none"),
@@ -662,22 +347,76 @@ const HelperOptions = Type$1.Object({
662
347
  onPayload: Type$1.Optional(Type$1.Unsafe({})),
663
348
  metadata: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.Unknown()))
664
349
  });
665
- const ThinkingBudgets = Type$1.Object({
666
- minimal: Type$1.Optional(Type$1.Number()),
667
- low: Type$1.Optional(Type$1.Number()),
668
- medium: Type$1.Optional(Type$1.Number()),
669
- high: Type$1.Optional(Type$1.Number()),
670
- xhigh: Type$1.Optional(Type$1.Number()),
671
- max: Type$1.Optional(Type$1.Number())
672
- });
350
+ const ThinkingBudgets = ThinkingBudgets$1;
673
351
  const SharedOptions = Type$1.Evaluate(Type$1.Intersect([GenerationOptions, HelperOptions]));
674
- const DEFAULT_MAX_OUTPUT_TOKENS = 32e3;
675
- const CONTEXT_WINDOW_OUTPUT_TOLERANCE = 1024;
352
+ /** Headroom left for the prompt when sizing a response against the context window. */
353
+ const CONTEXT_SAFETY_TOKENS = 4096;
354
+ const MIN_MAX_TOKENS = 1;
355
+ /**
356
+ * The model's own ceiling, unless the caller named a smaller one.
357
+ *
358
+ * No heuristic cap here: a catalog whose `maxTokens` spans the whole context
359
+ * window is handled by {@link clampMaxTokensToContext}, which knows what the
360
+ * prompt actually costs and does not have to guess.
361
+ */
676
362
  function applyDefaultMaxTokens(model, options) {
677
- const defaultMaxTokens = model.maxTokens > 0 ? model.maxTokens >= model.contextWindow - CONTEXT_WINDOW_OUTPUT_TOLERANCE ? Math.min(model.maxTokens, DEFAULT_MAX_OUTPUT_TOKENS) : model.maxTokens : void 0;
678
363
  return {
679
364
  ...options,
680
- maxTokens: options?.maxTokens ?? defaultMaxTokens
365
+ maxTokens: options?.maxTokens ?? (model.maxTokens > 0 ? model.maxTokens : void 0)
366
+ };
367
+ }
368
+ /**
369
+ * Shrink a response ceiling to what is left of the context window.
370
+ *
371
+ * Without this a long session asks for an answer that cannot fit and takes a
372
+ * provider overflow error instead of a shorter answer.
373
+ */
374
+ function clampMaxTokensToContext(model, context, maxTokens) {
375
+ if (model.contextWindow <= 0) return Math.max(MIN_MAX_TOKENS, maxTokens);
376
+ const available = model.contextWindow - estimateContextTokens(context).tokens - CONTEXT_SAFETY_TOKENS;
377
+ return Math.min(maxTokens, Math.max(MIN_MAX_TOKENS, available));
378
+ }
379
+ /** Tokens always left for the answer when a thinking budget shares the response ceiling. */
380
+ const MIN_ANSWER_TOKENS = 1024;
381
+ /**
382
+ * Default thinking budget per level, in tokens.
383
+ *
384
+ * Absolute rather than a fraction of `model.maxTokens`: a fraction turns one
385
+ * "medium" setting into wildly different budgets across models, and a budget is
386
+ * a property of how hard the task is, not of how large the model's ceiling is.
387
+ */
388
+ const DEFAULT_THINKING_BUDGETS = {
389
+ minimal: 1024,
390
+ low: 2048,
391
+ medium: 8192,
392
+ high: 16384
393
+ };
394
+ /** `xhigh` and `max` fall back to `high`'s budget unless one is configured for them. */
395
+ function baseThinkingLevel(level) {
396
+ return level === "xhigh" || level === "max" ? "high" : level;
397
+ }
398
+ function thinkingBudgetForLevel(level, budgets) {
399
+ const base = baseThinkingLevel(level);
400
+ return budgets?.[level] ?? budgets?.[base] ?? DEFAULT_THINKING_BUDGETS[base];
401
+ }
402
+ /** Cap a thinking budget so at least {@link MIN_ANSWER_TOKENS} remain under a shared response ceiling. */
403
+ function clampThinkingBudgetToAnswerRoom(thinkingBudget, ceiling) {
404
+ return Math.min(thinkingBudget, Math.max(0, ceiling - MIN_ANSWER_TOKENS));
405
+ }
406
+ /**
407
+ * Fit a thinking budget and an answer inside one response ceiling.
408
+ *
409
+ * The caller's `maxTokens` is the room they want for the *answer*, so thinking is
410
+ * added on top and the total is clamped to the model's ceiling. When even that
411
+ * does not fit, the budget gives way -- never the answer.
412
+ */
413
+ function adjustMaxTokensForThinking(baseMaxTokens, modelMaxTokens, level, budgets) {
414
+ let thinkingBudget = thinkingBudgetForLevel(level, budgets);
415
+ const maxTokens = baseMaxTokens === void 0 ? modelMaxTokens : Math.min(baseMaxTokens + thinkingBudget, modelMaxTokens);
416
+ if (maxTokens <= thinkingBudget) thinkingBudget = clampThinkingBudgetToAnswerRoom(thinkingBudget, maxTokens);
417
+ return {
418
+ maxTokens,
419
+ thinkingBudget
681
420
  };
682
421
  }
683
422
  Type$1.Evaluate(Type$1.Intersect([SharedOptions, Type$1.Object({
@@ -747,7 +486,7 @@ var AssistantMessageEventStream = class extends EventStream {
747
486
  super((event) => event.type === "done" || event.type === "error", (event) => {
748
487
  if (event.type === "done") return event.message;
749
488
  else if (event.type === "error") return event.error;
750
- throw new Error("Unexpected event type for final result");
489
+ throw new Error("unexpected event type for final result");
751
490
  });
752
491
  }
753
492
  };
@@ -771,6 +510,53 @@ function compact(value) {
771
510
  return result;
772
511
  }
773
512
  //#endregion
513
+ //#region src/llm/pricing.ts
514
+ /** Protocols that price a request by the OpenAI service tier it was served at. */
515
+ const SERVICE_TIER_KEYS = /* @__PURE__ */ new Set(["openai", "openai-codex"]);
516
+ function asString(value) {
517
+ return typeof value === "string" ? value : void 0;
518
+ }
519
+ function bag(value, key) {
520
+ if (typeof value !== "object" || value === null) return void 0;
521
+ const nested = value[key];
522
+ return typeof nested === "object" && nested !== null ? nested : void 0;
523
+ }
524
+ /** The tier the request asked for, before the provider says which one it used. */
525
+ function requestedServiceTier(model, options, providerOptions) {
526
+ const key = optionsKey(model);
527
+ if (!SERVICE_TIER_KEYS.has(key)) return void 0;
528
+ return asString(providerOptions[key]?.serviceTier ?? options.factoryOptions?.serviceTier ?? model.options?.serviceTier);
529
+ }
530
+ /**
531
+ * The tier the response was actually served at, which is what gets billed.
532
+ *
533
+ * A `"default"` response to an explicit flex or priority request still bills at
534
+ * the requested tier: the Codex backend reports the fallback it used, not the
535
+ * rate it charged.
536
+ */
537
+ function servedServiceTier(model, requested, providerMetadata) {
538
+ const key = optionsKey(model);
539
+ if (!SERVICE_TIER_KEYS.has(key)) return void 0;
540
+ const served = asString(bag(providerMetadata, key)?.serviceTier);
541
+ if (key === "openai-codex" && served === "default" && (requested === "flex" || requested === "priority")) return requested;
542
+ return served ?? requested;
543
+ }
544
+ function serviceTierCostMultiplier(model, serviceTier) {
545
+ return serviceTier === void 0 ? 1 : model.cost.serviceTierMultipliers?.[serviceTier] ?? 1;
546
+ }
547
+ /**
548
+ * Tokens Anthropic wrote with 1h retention, out of the total cache write.
549
+ *
550
+ * The AI SDK does not model the split, but it passes Anthropic's raw usage object
551
+ * through untouched, so read it from there.
552
+ */
553
+ function cacheWrite1hTokens(providerMetadata) {
554
+ for (const key of ["anthropic", "google-vertex-anthropic"]) {
555
+ const tokens = bag(bag(bag(providerMetadata, key), "usage"), "cache_creation")?.ephemeral_1h_input_tokens;
556
+ if (typeof tokens === "number") return tokens;
557
+ }
558
+ }
559
+ //#endregion
774
560
  //#region src/llm/runtime.ts
775
561
  function mergeHeaders(...headerSources) {
776
562
  const merged = {};
@@ -804,7 +590,23 @@ function cleanHeaders(headers) {
804
590
  function resolveBaseURL(model, options) {
805
591
  return (options.baseURL ?? model.api?.url ?? model.baseUrl) || void 0;
806
592
  }
807
- async function resolveAISDKLanguageModel(model, options) {
593
+ /**
594
+ * Inject a thinking-token budget the AI SDK has no field for.
595
+ *
596
+ * `@ai-sdk/openai-compatible` validates provider options against a closed schema,
597
+ * so an endpoint-specific budget field cannot travel that way. `transformRequestBody`
598
+ * is the SDK's own hook for exactly this: the last edit before the body is sent.
599
+ */
600
+ function thinkingTokenBudgetTransform(field, budget, existing) {
601
+ const chain = typeof existing === "function" ? existing : void 0;
602
+ return (body) => {
603
+ return {
604
+ ...chain ? chain(body) : body,
605
+ [field]: budget
606
+ };
607
+ };
608
+ }
609
+ async function resolveAISDKLanguageModel(model, options, thinkingTokenBudget) {
808
610
  const npm = packageForModel(model);
809
611
  const createProvider = await loadProviderFactory(npm);
810
612
  const apiKey = options.apiKey ?? getEnvApiKey(model.provider);
@@ -824,19 +626,171 @@ async function resolveAISDKLanguageModel(model, options) {
824
626
  });
825
627
  factoryOptions.name ??= model.provider.id;
826
628
  factoryOptions.includeUsage ??= true;
629
+ const budgetField = thinkingTokenBudgetField(model);
630
+ if (budgetField && thinkingTokenBudget !== void 0 && thinkingTokenBudget > 0) factoryOptions.transformRequestBody = thinkingTokenBudgetTransform(budgetField, thinkingTokenBudget, factoryOptions.transformRequestBody);
827
631
  }
828
632
  if (npm === "@codeworksh/ai-sdk-openai-codex") {
829
633
  const cacheDisabled = options.cacheRetention === "none" || typeof process !== "undefined" && process.env.CODEWORK_CACHE_RETENTION === "none";
830
634
  if (options.sessionId && !cacheDisabled) factoryOptions.sessionId ??= options.sessionId;
831
- factoryOptions.compat ??= {
635
+ factoryOptions.compat = {
832
636
  ...model.compat,
833
- supportsImages: model.input.includes("image")
637
+ supportsImages: model.input.includes("image"),
638
+ ...typeof factoryOptions.compat === "object" && factoryOptions.compat !== null ? factoryOptions.compat : {}
834
639
  };
835
640
  }
836
641
  const provider = createProvider(factoryOptions);
837
642
  return resolveLanguageModel(provider, model, options.method);
838
643
  }
839
644
  //#endregion
645
+ //#region src/llm/thinking.ts
646
+ /** Anthropic rejects an enabled thinking block whose budget is under 1024 tokens. */
647
+ const ANTHROPIC_MIN_THINKING_BUDGET = 1024;
648
+ /**
649
+ * The smallest budget this model will accept, for providers that take one at all.
650
+ *
651
+ * Adaptive models are exempt: they are sent an effort level and no budget, so
652
+ * there is no floor to fall under.
653
+ */
654
+ function minimumThinkingBudget(model) {
655
+ const key = optionsKey(model);
656
+ if (key !== "anthropic" && key !== "google-vertex-anthropic") return 0;
657
+ return model.compat?.forceAdaptiveThinking ? 0 : ANTHROPIC_MIN_THINKING_BUDGET;
658
+ }
659
+ function resolvePlan(model, context, options) {
660
+ const clampToContext = (maxTokens) => maxTokens === void 0 ? void 0 : clampMaxTokensToContext(model, context, maxTokens);
661
+ const requested = options.reasoning;
662
+ const level = requested ? clampThinkingLevel(model, requested) : "off";
663
+ if (level === "off") return {
664
+ level,
665
+ budget: 0,
666
+ maxTokens: clampToContext(options.maxTokens),
667
+ explicit: false
668
+ };
669
+ const budgets = {
670
+ ...model.thinkingBudgets,
671
+ ...options.thinkingBudgets
672
+ };
673
+ const explicit = options.thinkingBudgets?.[level] !== void 0;
674
+ const key = optionsKey(model);
675
+ const usesGoogleBudget = (key === "google" || key === "google-vertex") && !model.compat?.supportsThinkingLevel;
676
+ const requestedBudget = thinkingBudgetForLevel(level, budgets);
677
+ if (model.maxTokens <= 0) return {
678
+ level,
679
+ budget: requestedBudget,
680
+ maxTokens: clampToContext(options.maxTokens),
681
+ explicit
682
+ };
683
+ const dynamic = usesGoogleBudget && requestedBudget === -1;
684
+ const adjusted = adjustMaxTokensForThinking(options.maxTokens, model.maxTokens, level, { [level]: dynamic ? 0 : requestedBudget });
685
+ const maxTokens = clampMaxTokensToContext(model, context, adjusted.maxTokens);
686
+ const budget = maxTokens < adjusted.maxTokens ? clampThinkingBudgetToAnswerRoom(adjusted.thinkingBudget, maxTokens) : adjusted.thinkingBudget;
687
+ if (budget < minimumThinkingBudget(model)) return {
688
+ level: "off",
689
+ budget: 0,
690
+ maxTokens,
691
+ explicit
692
+ };
693
+ return {
694
+ level,
695
+ budget: dynamic ? -1 : budget,
696
+ maxTokens,
697
+ explicit
698
+ };
699
+ }
700
+ function googleThinkingLevel(mapped) {
701
+ return mapped === "xhigh" || mapped === "max" ? "high" : mapped;
702
+ }
703
+ /**
704
+ * Turn thinking off, in the provider's own words.
705
+ *
706
+ * Several models reason by default, so declining to *ask* for thinking is not the
707
+ * same as asking for none: the caller ends up paying output tokens for reasoning
708
+ * they never requested.
709
+ *
710
+ * OpenRouter is deliberately left alone. It fronts hundreds of third-party
711
+ * endpoints whose support varies per endpoint -- several reject the request
712
+ * outright with "reasoning is mandatory for this endpoint and cannot be disabled"
713
+ * -- so its reasoning stays pass-through.
714
+ */
715
+ function disabledProviderOptions(model) {
716
+ if (!model.reasoning) return {};
717
+ const key = optionsKey(model);
718
+ if (key === "anthropic" || key === "google-vertex-anthropic") return { [key]: { thinking: { type: "disabled" } } };
719
+ if (key === "google" || key === "google-vertex") {
720
+ if (model.thinkingLevelMap?.off === null) return {};
721
+ return { [key]: { thinkingConfig: googleDisabledThinkingConfig(model) } };
722
+ }
723
+ if (key === "openai" || key === "xai" || key === "openai-codex") {
724
+ const off = model.thinkingLevelMap?.off;
725
+ if (off === null) return {};
726
+ return { [key]: { reasoningEffort: off ?? "none" } };
727
+ }
728
+ return {};
729
+ }
730
+ /**
731
+ * Gemini 3 Pro cannot disable thinking, and Gemini 3 Flash and Gemma 4 cannot
732
+ * fully disable it either. For those, ask for the lowest level and leave
733
+ * `includeThoughts` off so the thinking stays hidden. Gemini 2.x takes a zero budget.
734
+ */
735
+ function googleDisabledThinkingConfig(model) {
736
+ if (model.compat?.supportsThinkingLevel) return { thinkingLevel: model.thinkingLevelMap?.off ?? "minimal" };
737
+ return { thinkingBudget: 0 };
738
+ }
739
+ /**
740
+ * The effort level an adaptive Claude model reasons at.
741
+ *
742
+ * A model's own `thinkingLevelMap` wins where it has an opinion -- that is where
743
+ * per-model support for `xhigh` and `max` is recorded -- and the rest collapse onto
744
+ * the three levels every adaptive model accepts.
745
+ */
746
+ function anthropicEffort(model, level) {
747
+ const mapped = model.thinkingLevelMap?.[level];
748
+ if (typeof mapped === "string") return mapped;
749
+ switch (level) {
750
+ case "minimal":
751
+ case "low": return "low";
752
+ case "medium": return "medium";
753
+ case "xhigh": return "xhigh";
754
+ case "max": return "max";
755
+ default: return "high";
756
+ }
757
+ }
758
+ function reasoningProviderOptions(model, plan) {
759
+ const level = plan.level;
760
+ if (level === "off") return disabledProviderOptions(model);
761
+ const mapped = model.thinkingLevelMap?.[level] ?? level;
762
+ if (!mapped) return {};
763
+ const key = optionsKey(model);
764
+ const budget = plan.budget;
765
+ if (key === "openai" || key === "xai" || key === "openai-codex") return { [key]: { reasoningEffort: mapped } };
766
+ if (key === "openrouter") return { [key]: { reasoning: { effort: mapped === "off" ? "none" : mapped } } };
767
+ if (key === "google" || key === "google-vertex") {
768
+ const thinkingLevel = googleThinkingLevel(mapped);
769
+ if (model.compat?.supportsThinkingLevel) return { [key]: { thinkingConfig: {
770
+ thinkingLevel,
771
+ includeThoughts: true
772
+ } } };
773
+ return { [key]: { thinkingConfig: {
774
+ includeThoughts: true,
775
+ thinkingBudget: budget
776
+ } } };
777
+ }
778
+ if (key === "anthropic" || key === "google-vertex-anthropic") {
779
+ if (model.compat?.forceAdaptiveThinking) return { [key]: {
780
+ thinking: {
781
+ type: "adaptive",
782
+ display: "summarized"
783
+ },
784
+ effort: anthropicEffort(model, level)
785
+ } };
786
+ return { [key]: { thinking: {
787
+ type: "enabled",
788
+ budgetTokens: budget
789
+ } } };
790
+ }
791
+ return {};
792
+ }
793
+ //#endregion
840
794
  //#region src/utils/jsonparse.ts
841
795
  /**
842
796
  * @description Helper function to parse partial jsons during LLM streaming.
@@ -1011,7 +965,7 @@ function toolResultOutput(toolCall) {
1011
965
  const images = toolCall.result.content.filter((content) => content.type === "image");
1012
966
  if (toolCall.result.isError) return {
1013
967
  type: "error-text",
1014
- value: text || "Tool returned an error"
968
+ value: text || "tool returned an error"
1015
969
  };
1016
970
  if (images.length === 0) return {
1017
971
  type: "text",
@@ -1061,20 +1015,32 @@ function decodeOpenAIReasoningSignature(signature) {
1061
1015
  itemId: parsed.itemId,
1062
1016
  ...typeof parsed.reasoningEncryptedContent === "string" || parsed.reasoningEncryptedContent === null ? { reasoningEncryptedContent: parsed.reasoningEncryptedContent } : {}
1063
1017
  };
1064
- } catch {}
1065
- return { itemId: signature };
1018
+ } catch {
1019
+ return;
1020
+ }
1021
+ }
1022
+ function googleThoughtSignature(metadata) {
1023
+ if (!isRecord(metadata)) return;
1024
+ for (const key of ["google", "google-vertex"]) {
1025
+ const provider = metadata[key];
1026
+ if (isRecord(provider) && typeof provider.thoughtSignature === "string") return provider.thoughtSignature;
1027
+ }
1066
1028
  }
1067
1029
  function assistantMessages(message, model) {
1068
1030
  if (message.stopReason === "error" || message.stopReason === "aborted") return [];
1069
1031
  const assistantContent = [];
1070
1032
  const toolResults = [];
1033
+ const googleReplay = (model.protocol === "google" || model.protocol === "google-vertex") && message.protocol === model.protocol && message.provider.id === model.provider.id && message.model === model.id;
1034
+ const googleOptions = (signature) => googleReplay && signature ? { [optionsKey(model)]: { thoughtSignature: signature } } : void 0;
1071
1035
  for (const part of message.parts) {
1036
+ const google = googleOptions(part.type === "text" ? part.textSignature : part.type === "thinking" ? part.thinkingSignature : part.type === "toolCall" ? part.thoughtSignature : void 0);
1072
1037
  if (part.type === "text") {
1073
1038
  const text = sanitizeSurrogates(part.text);
1074
1039
  if (text.trim().length === 0) continue;
1075
1040
  assistantContent.push({
1076
1041
  type: "text",
1077
1042
  text,
1043
+ ...google ? { providerOptions: google } : {},
1078
1044
  ...part.textSignature && message.protocol === KnownProviderEnum.openaiCodex ? { providerOptions: { "openai-codex": { messageId: part.textSignature } } } : {}
1079
1045
  });
1080
1046
  continue;
@@ -1088,8 +1054,11 @@ function assistantMessages(message, model) {
1088
1054
  };
1089
1055
  if (message.protocol === KnownProviderEnum.openai) {
1090
1056
  if (!part.thinkingSignature) continue;
1091
- reasoning.providerOptions = { openai: decodeOpenAIReasoningSignature(part.thinkingSignature) };
1092
- } else if (part.thinkingSignature) reasoning.providerOptions = message.protocol === KnownProviderEnum.openaiCodex ? { "openai-codex": { reasoningItem: part.thinkingSignature } } : { anthropic: { signature: part.thinkingSignature } };
1057
+ const openai = decodeOpenAIReasoningSignature(part.thinkingSignature);
1058
+ if (!openai) continue;
1059
+ reasoning.providerOptions = { openai };
1060
+ } else if (google) reasoning.providerOptions = google;
1061
+ else if (part.thinkingSignature && message.protocol !== "google" && message.protocol !== "google-vertex") reasoning.providerOptions = message.protocol === KnownProviderEnum.openaiCodex ? { "openai-codex": { reasoningItem: part.thinkingSignature } } : { anthropic: { signature: part.thinkingSignature } };
1093
1062
  assistantContent.push(reasoning);
1094
1063
  continue;
1095
1064
  }
@@ -1102,6 +1071,7 @@ function assistantMessages(message, model) {
1102
1071
  toolCallId: part.callID,
1103
1072
  toolName: part.name,
1104
1073
  input: sanitizeRecord(part.arguments ?? {}),
1074
+ ...google ? { providerOptions: google } : {},
1105
1075
  ...Object.keys(codexToolMetadata).length > 0 ? { providerOptions: { "openai-codex": codexToolMetadata } } : {}
1106
1076
  });
1107
1077
  const terminal = part.status === "pending" || part.status === "running" ? void 0 : part;
@@ -1112,7 +1082,7 @@ function assistantMessages(message, model) {
1112
1082
  ...terminal?.addedToolNames?.length ? { providerOptions: { "openai-codex": { addedToolNames: terminal.addedToolNames } } } : {},
1113
1083
  output: terminal ? toolResultOutput(terminal) : {
1114
1084
  type: "error-text",
1115
- value: "No result provided"
1085
+ value: "no result provided"
1116
1086
  }
1117
1087
  });
1118
1088
  }
@@ -1143,12 +1113,13 @@ function convertTools(tools, model) {
1143
1113
  if (!tools || tools.length === 0) return;
1144
1114
  const result = {};
1145
1115
  for (const tool of tools) {
1146
- const constraint = model?.protocol === KnownProviderEnum.openaiCodex ? resolveOpenAICodexToolConstraint(tool, model.compat) : void 0;
1116
+ const jsonSchemaConstraint = resolveJsonSchemaConstraint(tool, model?.compat);
1117
+ const grammarConstraint = model?.protocol === KnownProviderEnum.openaiCodex ? resolveOpenAICodexToolConstraint(tool, model.compat) : void 0;
1147
1118
  result[tool.name] = {
1148
1119
  description: tool.description,
1149
1120
  inputSchema: jsonSchema(tool.parameters),
1150
- ...constraint?.type === "json_schema" ? { strict: true } : {},
1151
- ...constraint?.type === "grammar" ? { providerOptions: { "openai-codex": { grammar: constraint } } } : {}
1121
+ ...jsonSchemaConstraint ? { strict: true } : {},
1122
+ ...grammarConstraint?.type === "grammar" ? { providerOptions: { "openai-codex": { grammar: grammarConstraint } } } : {}
1152
1123
  };
1153
1124
  }
1154
1125
  return result;
@@ -1163,9 +1134,10 @@ function mapFinishReason(reason) {
1163
1134
  default: return "stop";
1164
1135
  }
1165
1136
  }
1166
- function mapUsage(usage, model, costMultiplier = 1) {
1137
+ function mapUsage(usage, model, costMultiplier = 1, providerMetadata) {
1167
1138
  const cacheRead = usage?.inputTokenDetails.cacheReadTokens ?? 0;
1168
1139
  const cacheWrite = usage?.inputTokenDetails?.cacheWriteTokens ?? 0;
1140
+ const cacheWrite1h = cacheWrite1hTokens(providerMetadata);
1169
1141
  const input = usage?.inputTokenDetails?.noCacheTokens ?? Math.max((usage?.inputTokens ?? 0) - cacheRead - cacheWrite, 0);
1170
1142
  const output = usage?.outputTokens ?? 0;
1171
1143
  const totalTokens = usage?.totalTokens ?? input + cacheRead + cacheWrite + output;
@@ -1174,6 +1146,7 @@ function mapUsage(usage, model, costMultiplier = 1) {
1174
1146
  output,
1175
1147
  cacheRead,
1176
1148
  cacheWrite,
1149
+ ...cacheWrite1h === void 0 ? {} : { cacheWrite1h },
1177
1150
  ...usage?.outputTokenDetails?.reasoningTokens !== void 0 ? { reasoning: usage.outputTokenDetails.reasoningTokens } : {},
1178
1151
  totalTokens,
1179
1152
  cost: {
@@ -1214,9 +1187,6 @@ function updateToolCallFromInput(block, partialJson) {
1214
1187
  }
1215
1188
  //#endregion
1216
1189
  //#region src/llm/stream.ts
1217
- function providerOptionsKey(model) {
1218
- return model.providerOptionsKey ?? model.protocol;
1219
- }
1220
1190
  function mergeProviderOptions(...sources) {
1221
1191
  const result = {};
1222
1192
  for (const source of sources) {
@@ -1238,7 +1208,7 @@ function configuredCacheRetention(options) {
1238
1208
  }
1239
1209
  function cacheProviderOptions(model, options) {
1240
1210
  const retention = configuredCacheRetention(options);
1241
- const key = providerOptionsKey(model);
1211
+ const key = optionsKey(model);
1242
1212
  if (retention === "none") return {};
1243
1213
  if (key === "openai") {
1244
1214
  const openai = {};
@@ -1247,58 +1217,14 @@ function cacheProviderOptions(model, options) {
1247
1217
  if (retention === "long") openai.promptCacheRetention = "24h";
1248
1218
  return Object.keys(openai).length > 0 ? { [key]: openai } : {};
1249
1219
  }
1250
- if (key === "openai-codex" && options.sessionId) return { [key]: { promptCacheKey: options.sessionId } };
1251
1220
  if ((key === "anthropic" || key === "google-vertex-anthropic") && retention) return { [key]: { cacheControl: {
1252
1221
  type: "ephemeral",
1253
1222
  ...retention === "long" ? { ttl: "1h" } : {}
1254
1223
  } } };
1255
1224
  return {};
1256
1225
  }
1257
- const DEFAULT_THINKING_BUDGET_FRACTIONS = {
1258
- minimal: .05,
1259
- low: .1,
1260
- medium: .25,
1261
- high: .5,
1262
- xhigh: .8,
1263
- max: .95
1264
- };
1265
- const MIN_THINKING_BUDGET = 1024;
1266
- function resolveThinkingBudget(model, options, level) {
1267
- const explicit = options.thinkingBudgets?.[level];
1268
- if (explicit !== void 0) return explicit;
1269
- const key = providerOptionsKey(model);
1270
- if (key === "anthropic" || key === "google-vertex-anthropic") {
1271
- const fraction = DEFAULT_THINKING_BUDGET_FRACTIONS[level];
1272
- return Math.max(Math.floor(model.maxTokens * fraction), MIN_THINKING_BUDGET);
1273
- }
1274
- }
1275
- function reasoningProviderOptions(model, options) {
1276
- const requestedLevel = options.reasoning;
1277
- if (!requestedLevel) return {};
1278
- const level = clampThinkingLevel(model, requestedLevel);
1279
- if (level === "off") return {};
1280
- const mapped = model.thinkingLevelMap?.[level] ?? level;
1281
- if (!mapped) return {};
1282
- const key = providerOptionsKey(model);
1283
- const budget = resolveThinkingBudget(model, options, level);
1284
- if (key === "openai" || key === "xai" || key === "openai-codex") return { [key]: { reasoningEffort: mapped } };
1285
- if (key === "openrouter") return { [key]: { reasoning: { effort: mapped === "off" ? "none" : mapped } } };
1286
- if (key === "google" || key === "google-vertex") {
1287
- const thinkingLevel = mapped === "xhigh" ? "high" : mapped;
1288
- return { [key]: { thinkingConfig: {
1289
- thinkingLevel,
1290
- includeThoughts: true,
1291
- ...budget !== void 0 ? { thinkingBudget: budget } : {}
1292
- } } };
1293
- }
1294
- if (key === "anthropic" || key === "google-vertex-anthropic") return { [key]: { thinking: {
1295
- type: "enabled",
1296
- budgetTokens: budget ?? MIN_THINKING_BUDGET
1297
- } } };
1298
- return {};
1299
- }
1300
- function resolveProviderOptions(model, options) {
1301
- return mergeProviderOptions(model.providerOptions, cacheProviderOptions(model, options), reasoningProviderOptions(model, options), options.providerOptions);
1226
+ function resolveProviderOptions(model, options, plan) {
1227
+ return mergeProviderOptions(model.providerOptions, cacheProviderOptions(model, options), reasoningProviderOptions(model, plan), options.providerOptions);
1302
1228
  }
1303
1229
  function partIndex(output, block) {
1304
1230
  return output.parts.indexOf(block);
@@ -1410,6 +1336,8 @@ function finalizeToolCall(output, part, stream) {
1410
1336
  block.name = part.toolName;
1411
1337
  block.arguments = typeof part.input === "object" && part.input !== null ? part.input : {};
1412
1338
  const namespace = openAICodexMetadata(part.providerMetadata)?.namespace;
1339
+ const thoughtSignature = googleThoughtSignature(part.providerMetadata);
1340
+ if (thoughtSignature) block.thoughtSignature = thoughtSignature;
1413
1341
  if (typeof namespace === "string") block.namespace = namespace;
1414
1342
  block.time.end = Date.now();
1415
1343
  delete block.partialJson;
@@ -1422,13 +1350,15 @@ function finalizeToolCall(output, part, stream) {
1422
1350
  partial: output
1423
1351
  });
1424
1352
  }
1425
- function handlePart(part, output, model, stream, costMultiplier) {
1353
+ function handlePart(part, output, model, stream, pricing) {
1426
1354
  switch (part.type) {
1427
1355
  case "text-start":
1428
1356
  ensureTextBlock(output, part.id, stream);
1429
1357
  break;
1430
1358
  case "text-delta": {
1431
1359
  const block = ensureTextBlock(output, part.id, stream);
1360
+ const signature = googleThoughtSignature(part.providerMetadata);
1361
+ if (signature) block.textSignature = signature;
1432
1362
  block.text += part.text;
1433
1363
  stream.push({
1434
1364
  type: "text.delta",
@@ -1449,7 +1379,7 @@ function handlePart(part, output, model, stream, costMultiplier) {
1449
1379
  case "reasoning-delta": {
1450
1380
  const block = ensureThinkingBlock(output, part.id, stream);
1451
1381
  block.thinking += part.text;
1452
- const sig = (part.providerMetadata?.anthropic)?.signature ?? (part.providerMetadata?.["google-vertex-anthropic"])?.signature;
1382
+ const sig = (part.providerMetadata?.anthropic)?.signature ?? (part.providerMetadata?.["google-vertex-anthropic"])?.signature ?? googleThoughtSignature(part.providerMetadata);
1453
1383
  if (typeof sig === "string") block.thinkingSignature = sig;
1454
1384
  stream.push({
1455
1385
  type: "thinking.delta",
@@ -1488,11 +1418,12 @@ function handlePart(part, output, model, stream, costMultiplier) {
1488
1418
  case "finish-step":
1489
1419
  output.responseId ||= part.response.id;
1490
1420
  if (part.response.modelId && part.response.modelId !== model.id) output.responseModel ||= part.response.modelId;
1491
- output.usage = mapUsage(part.usage, model, costMultiplier);
1421
+ pricing.providerMetadata = part.providerMetadata ?? pricing.providerMetadata;
1422
+ output.usage = mapUsage(part.usage, model, resolvePricing(model, pricing), pricing.providerMetadata);
1492
1423
  output.stopReason = mapFinishReason(part.finishReason);
1493
1424
  break;
1494
1425
  case "finish":
1495
- output.usage = mapUsage(part.totalUsage, model, costMultiplier);
1426
+ output.usage = mapUsage(part.totalUsage, model, resolvePricing(model, pricing), pricing.providerMetadata);
1496
1427
  output.stopReason = mapFinishReason(part.finishReason);
1497
1428
  break;
1498
1429
  case "abort":
@@ -1502,30 +1433,29 @@ function handlePart(part, output, model, stream, costMultiplier) {
1502
1433
  case "error": throw part.error instanceof Error ? part.error : new Error(formatThrownError(part.error));
1503
1434
  }
1504
1435
  }
1505
- function resolveCostMultiplier(model, options, providerOptions) {
1506
- if (providerOptionsKey(model) !== "openai-codex") return 1;
1507
- const providerTier = providerOptions["openai-codex"]?.serviceTier;
1508
- const factoryTier = options.factoryOptions?.serviceTier;
1509
- const modelTier = model.options?.serviceTier;
1510
- const serviceTier = providerTier ?? factoryTier ?? modelTier;
1511
- if (serviceTier === "flex") return .5;
1512
- if (serviceTier === "priority") return model.id === "gpt-5.5" ? 2.5 : 2;
1513
- return 1;
1436
+ function resolvePricing(model, pricing) {
1437
+ return serviceTierCostMultiplier(model, servedServiceTier(model, pricing.requestedServiceTier, pricing.providerMetadata));
1514
1438
  }
1515
1439
  /**
1516
- * Resolve maxOutputTokens so that (maxOutputTokens + thinkingBudget) does not exceed model.maxTokens.
1517
- * Without this adjustment @ai-sdk/anthropic logs a warning and silently caps the value.
1440
+ * The response ceiling to send, already fitted around the thinking budget by
1441
+ * {@link Thinking.resolvePlan}.
1442
+ *
1443
+ * `plan.maxTokens` is the whole response -- thinking and answer together, which is
1444
+ * what Anthropic's own `max_tokens` means. But `@ai-sdk/anthropic` treats
1445
+ * `maxOutputTokens` as the answer alone and adds the budget back on before sending
1446
+ * (`baseArgs.max_tokens = maxTokens + thinkingBudget`). Handing it the total there
1447
+ * would ask for the budget twice, so it gets the answer room and reconstructs the
1448
+ * total itself.
1449
+ *
1450
+ * That only applies when a budget is actually sent. An adaptive model is given an
1451
+ * effort level and no budget, so the SDK adds nothing and the total goes through
1452
+ * unchanged -- subtracting there would quietly under-ask by the whole budget.
1518
1453
  */
1519
- function resolveMaxOutputTokens(model, options, providerOptions) {
1520
- const maxTokens = options.maxTokens;
1521
- if (maxTokens === void 0) return void 0;
1522
- const key = providerOptionsKey(model);
1454
+ function resolveMaxOutputTokens(model, plan) {
1455
+ const key = optionsKey(model);
1523
1456
  if (key === "openai-codex") return void 0;
1524
- if (key === "anthropic" || key === "google-vertex-anthropic") {
1525
- const budgetTokens = ((providerOptions?.[key])?.thinking)?.budgetTokens ?? 0;
1526
- if (budgetTokens > 0 && maxTokens + budgetTokens > model.maxTokens) return Math.max(model.maxTokens - budgetTokens, 1);
1527
- }
1528
- return maxTokens;
1457
+ if (plan.maxTokens === void 0) return void 0;
1458
+ return (key === "anthropic" || key === "google-vertex-anthropic") && !model.compat?.forceAdaptiveThinking ? plan.maxTokens - plan.budget : plan.maxTokens;
1529
1459
  }
1530
1460
  const stream$1 = (model, context, options) => {
1531
1461
  const stream = new AssistantMessageEventStream();
@@ -1533,11 +1463,12 @@ const stream$1 = (model, context, options) => {
1533
1463
  (async () => {
1534
1464
  const output = createAssistantMessage(model);
1535
1465
  try {
1536
- const languageModel = await resolveAISDKLanguageModel(model, runtimeOptions);
1466
+ const plan = resolvePlan(model, context, runtimeOptions);
1467
+ const languageModel = await resolveAISDKLanguageModel(model, runtimeOptions, plan.budget);
1537
1468
  const tools = convertTools(context.tools, model);
1538
1469
  const messages = convertMessages(context, model);
1539
- const providerOptions = resolveProviderOptions(model, runtimeOptions);
1540
- const costMultiplier = resolveCostMultiplier(model, runtimeOptions, providerOptions);
1470
+ const providerOptions = resolveProviderOptions(model, runtimeOptions, plan);
1471
+ const pricing = { requestedServiceTier: requestedServiceTier(model, runtimeOptions, providerOptions) };
1541
1472
  const activeTools = runtimeOptions.activeTools?.filter((name) => !tools || name in tools);
1542
1473
  let params = compact({
1543
1474
  model: languageModel,
@@ -1546,7 +1477,7 @@ const stream$1 = (model, context, options) => {
1546
1477
  tools,
1547
1478
  toolChoice: runtimeOptions.toolChoice,
1548
1479
  activeTools,
1549
- maxOutputTokens: resolveMaxOutputTokens(model, runtimeOptions, providerOptions),
1480
+ maxOutputTokens: resolveMaxOutputTokens(model, plan),
1550
1481
  temperature: runtimeOptions.temperature,
1551
1482
  providerOptions,
1552
1483
  abortSignal: runtimeOptions.signal,
@@ -1562,9 +1493,9 @@ const stream$1 = (model, context, options) => {
1562
1493
  type: "start",
1563
1494
  partial: output
1564
1495
  });
1565
- for await (const part of result.fullStream) handlePart(part, output, model, stream, costMultiplier);
1566
- if (runtimeOptions.signal?.aborted || output.stopReason === "aborted") throw new Error(output.errorMessage || "Request was aborted");
1567
- if (output.stopReason === "error") throw new Error(output.errorMessage || "Provider returned an error stop reason");
1496
+ for await (const part of result.fullStream) handlePart(part, output, model, stream, pricing);
1497
+ if (runtimeOptions.signal?.aborted || output.stopReason === "aborted") throw new Error(output.errorMessage || "request was aborted");
1498
+ if (output.stopReason === "error") throw new Error(output.errorMessage || "provider returned an error stop reason");
1568
1499
  output.time.completed = Date.now();
1569
1500
  stream.push({
1570
1501
  type: "done",
@@ -1675,7 +1606,7 @@ function validateSchema(schema, value, label) {
1675
1606
  */
1676
1607
  function validateToolCall(tools, toolExecution) {
1677
1608
  const tool = tools.find((t) => t.name === toolExecution.name);
1678
- if (!tool) throw new Error(`Tool "${toolExecution.name}" not found`);
1609
+ if (!tool) throw new Error(`tool "${toolExecution.name}" not found`);
1679
1610
  return validateToolArguments(tool, toolExecution);
1680
1611
  }
1681
1612
  /**