@agentionai/agents 1.14.0 → 1.15.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.
- package/dist/agents/AgentConfig.d.ts +31 -2
- package/dist/agents/openai/CodexAgent.d.ts +80 -8
- package/dist/agents/openai/CodexAgent.js +35 -4
- package/dist/agents/openai/OpenAiAgent.d.ts +113 -10
- package/dist/agents/openai/OpenAiAgent.js +98 -12
- package/dist/history/History.d.ts +2 -2
- package/dist/history/History.js +24 -2
- package/dist/history/index.d.ts +2 -2
- package/dist/history/index.js +2 -1
- package/dist/history/transformers.d.ts +12 -3
- package/dist/history/transformers.js +119 -13
- package/dist/history/types.d.ts +31 -1
- package/dist/history/types.js +15 -5
- package/package.json +3 -3
|
@@ -144,10 +144,31 @@ export interface OpenAISpecificConfig {
|
|
|
144
144
|
*/
|
|
145
145
|
promptCacheKey?: string;
|
|
146
146
|
/**
|
|
147
|
-
* How long cached prefixes stay warm — `"
|
|
147
|
+
* How long cached prefixes stay warm — `"in_memory"` expires them within
|
|
148
|
+
* minutes, `"24h"` keeps them up to a day. Left unset the default follows
|
|
149
|
+
* your organization's data-retention policy (`"24h"` without ZDR,
|
|
150
|
+
* `"in_memory"` with it), so set it explicitly if you care either way.
|
|
148
151
|
* Ignored by the ChatGPT/Codex backend, which manages its own cache.
|
|
149
152
|
*/
|
|
150
|
-
promptCacheRetention?: "
|
|
153
|
+
promptCacheRetention?: "in_memory" | "24h";
|
|
154
|
+
/**
|
|
155
|
+
* Request each turn's reasoning as an encrypted blob and replay it on every
|
|
156
|
+
* later request of the conversation, so a reasoning model keeps its own
|
|
157
|
+
* thinking across tool hops and turns instead of re-deriving it.
|
|
158
|
+
*
|
|
159
|
+
* Setting it to `false` stops both halves — nothing is requested, and blobs
|
|
160
|
+
* already in the history are not sent either, which is what makes it a way
|
|
161
|
+
* out of the model-switch rejection below.
|
|
162
|
+
*
|
|
163
|
+
* Defaults to on for models known to reason on OpenAI's own API, off
|
|
164
|
+
* otherwise — including behind a custom `baseURL`, where the host may not
|
|
165
|
+
* support the parameter. `CodexAgent` defaults it to on for every model.
|
|
166
|
+
*
|
|
167
|
+
* Reasoning blobs are tied to the model that produced them: switching models
|
|
168
|
+
* mid-conversation with a history full of them is rejected. Clear the history
|
|
169
|
+
* or turn this off when doing that.
|
|
170
|
+
*/
|
|
171
|
+
includeEncryptedReasoning?: boolean;
|
|
151
172
|
/**
|
|
152
173
|
* Override the API base URL. Defaults to `api.openai.com/v1`; `CodexAgent`
|
|
153
174
|
* defaults it to `https://chatgpt.com/backend-api/codex`, and setting it
|
|
@@ -159,6 +180,14 @@ export interface OpenAISpecificConfig {
|
|
|
159
180
|
* `chatgpt-account-id` header.
|
|
160
181
|
*/
|
|
161
182
|
accountId?: string;
|
|
183
|
+
/**
|
|
184
|
+
* `CodexAgent` only: conversation id sent as the `session_id` header, which
|
|
185
|
+
* is what routes requests to a shared prompt cache on that backend — setting
|
|
186
|
+
* it is how you opt into caching there. Unset by default, so the header is
|
|
187
|
+
* omitted and the request stays byte-identical; `randomUUID()` per agent
|
|
188
|
+
* gives a per-run cache, a stable id shares one across runs.
|
|
189
|
+
*/
|
|
190
|
+
sessionId?: string;
|
|
162
191
|
/**
|
|
163
192
|
* `CodexAgent` only: client identifier sent as the `originator` header.
|
|
164
193
|
* OpenAI varies the model catalog by originator.
|
|
@@ -49,7 +49,57 @@ export type CodexAgentConfig = Omit<OpenAiAgentConfig, "model" | "reasoningEffor
|
|
|
49
49
|
* @default CODEX_CLIENT_VERSION
|
|
50
50
|
*/
|
|
51
51
|
clientVersion?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Conversation identifier sent as the `session_id` header, as the Codex CLI
|
|
54
|
+
* does — **this is what makes prompt caching work on this backend**, and
|
|
55
|
+
* setting it is how you opt into that caching.
|
|
56
|
+
*
|
|
57
|
+
* Requests carrying the same value are routed together and reuse each other's
|
|
58
|
+
* cached prefix; without it the backend caches essentially nothing, however
|
|
59
|
+
* identical the prefix. Measured on 2026-09-10 with a ~9K-token prefix
|
|
60
|
+
* repeated 8 times: 0/14 calls hit the cache with no header, 12/14 hit
|
|
61
|
+
* (~98% of the prefix) with one. `prompt_cache_key`, the platform API's
|
|
62
|
+
* lever, makes no difference here.
|
|
63
|
+
*
|
|
64
|
+
* **Unset by default**, so the header is omitted entirely and a request stays
|
|
65
|
+
* byte-identical to what earlier versions sent — the same opt-in rule as
|
|
66
|
+
* {@link AgentConfig.promptCacheKey} and
|
|
67
|
+
* {@link AgentConfig.promptCacheRetention}. Caching is not free of side
|
|
68
|
+
* effects: it groups your requests server-side under an id you chose, so it
|
|
69
|
+
* is yours to turn on rather than the agent's to assume.
|
|
70
|
+
*
|
|
71
|
+
* Any stable string works; the value is opaque and only its stability
|
|
72
|
+
* matters. One id per conversation is the usual grain — `randomUUID()` per
|
|
73
|
+
* agent instance reproduces the old default:
|
|
74
|
+
*
|
|
75
|
+
* ```typescript
|
|
76
|
+
* new CodexAgent({ …, sessionId: randomUUID() }) // cache within this run
|
|
77
|
+
* new CodexAgent({ …, sessionId: conversationId }) // cache across runs
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* Note it is a *header*: `session_id` in the request body is rejected with
|
|
81
|
+
* *"Unsupported parameter: session_id"*.
|
|
82
|
+
*/
|
|
83
|
+
sessionId?: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Configuration a factory hands to the constructor, before the credentials it
|
|
87
|
+
* resolves are merged in.
|
|
88
|
+
*/
|
|
89
|
+
type CodexFactoryConfig = Omit<CodexAgentConfig, "apiKey" | "accountId"> & {
|
|
90
|
+
tokenOptions?: CodexTokenProviderOptions;
|
|
52
91
|
};
|
|
92
|
+
/**
|
|
93
|
+
* What a static factory needs of the class it was called on: a constructor for
|
|
94
|
+
* the concrete subclass — which is where `T` is inferred from — intersected
|
|
95
|
+
* with the sibling factory it delegates to, so an override of that runs too.
|
|
96
|
+
*
|
|
97
|
+
* `Pick<typeof CodexAgent, …>` rather than a hand-written signature: the
|
|
98
|
+
* sibling's own `this` parameter is what carries `T` through the delegation,
|
|
99
|
+
* and restating it here would erase that and pin every subclass back to the
|
|
100
|
+
* base type.
|
|
101
|
+
*/
|
|
102
|
+
type CodexAgentClass<T extends CodexAgent> = (new (config: CodexAgentConfig, history?: History) => T) & Pick<typeof CodexAgent, "fromCredentials">;
|
|
53
103
|
/**
|
|
54
104
|
* Agent for OpenAI models reached through a **ChatGPT subscription** rather
|
|
55
105
|
* than a platform API key.
|
|
@@ -88,6 +138,15 @@ export declare class CodexAgent extends OpenAiAgent<OpenAIModel, CodexModelCard>
|
|
|
88
138
|
private readonly originator;
|
|
89
139
|
private readonly clientVersion;
|
|
90
140
|
private readonly codexBaseURL;
|
|
141
|
+
/**
|
|
142
|
+
* The `session_id` this agent sends on every request, or `undefined` when the
|
|
143
|
+
* header is not being sent — the key the backend's prompt cache is routed by.
|
|
144
|
+
*
|
|
145
|
+
* `undefined` means prompt caching is effectively off for this agent; set
|
|
146
|
+
* {@link CodexSpecificConfig.sessionId} to opt in. Read it back to pin a
|
|
147
|
+
* later agent to the same cache.
|
|
148
|
+
*/
|
|
149
|
+
readonly sessionId?: string;
|
|
91
150
|
/**
|
|
92
151
|
* Quota state written by the fetch wrapper installed in the constructor.
|
|
93
152
|
*
|
|
@@ -126,18 +185,30 @@ export declare class CodexAgent extends OpenAiAgent<OpenAIModel, CodexModelCard>
|
|
|
126
185
|
* Build an agent from the credentials `codex login` stored, wrapped in a
|
|
127
186
|
* provider that refreshes the access token as it ages out.
|
|
128
187
|
*
|
|
188
|
+
* Constructs `this`, so `MyCodexAgent.fromCodexCli(…)` returns a
|
|
189
|
+
* `MyCodexAgent` — see {@link CodexAgent.fromCredentials}.
|
|
190
|
+
*
|
|
129
191
|
* @throws if no credentials are present — run `codex login` first.
|
|
130
192
|
*/
|
|
131
|
-
static fromCodexCli(
|
|
193
|
+
static fromCodexCli<T extends CodexAgent>(this: CodexAgentClass<T>, config: CodexFactoryConfig & {
|
|
132
194
|
/** Read `auth.json` from somewhere other than `$CODEX_HOME`. */
|
|
133
195
|
codexHome?: string;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
196
|
+
}, history?: History): Promise<T>;
|
|
197
|
+
/**
|
|
198
|
+
* Build an agent from credentials obtained however you like.
|
|
199
|
+
*
|
|
200
|
+
* Instantiates `this` rather than `CodexAgent`, so a subclass gets its own
|
|
201
|
+
* type back and its overrides actually run. Hard-coding the class here made
|
|
202
|
+
* `class MyCodexAgent extends CodexAgent` silently produce a plain
|
|
203
|
+
* `CodexAgent` — no error, no override, and nothing to see until an
|
|
204
|
+
* experiment came back saying the change under test had no effect.
|
|
205
|
+
*/
|
|
206
|
+
static fromCredentials<T extends CodexAgent>(this: new (config: CodexAgentConfig, history?: History) => T, credentials: CodexCredentials, config: CodexFactoryConfig, history?: History): T;
|
|
207
|
+
/**
|
|
208
|
+
* Every model on this backend reasons, and this is what the Codex CLI itself
|
|
209
|
+
* does, so the encrypted-reasoning round trip is on unless turned off.
|
|
210
|
+
*/
|
|
211
|
+
protected defaultIncludeEncryptedReasoning(): boolean;
|
|
141
212
|
/** This backend refuses `stream: false` outright. */
|
|
142
213
|
protected get forceStreaming(): boolean;
|
|
143
214
|
/**
|
|
@@ -166,4 +237,5 @@ export declare class CodexAgent extends OpenAiAgent<OpenAIModel, CodexModelCard>
|
|
|
166
237
|
*/
|
|
167
238
|
listModels(): Promise<ModelInfo<CodexModelCard>[]>;
|
|
168
239
|
}
|
|
240
|
+
export {};
|
|
169
241
|
//# sourceMappingURL=CodexAgent.d.ts.map
|
|
@@ -45,6 +45,9 @@ class CodexAgent extends OpenAiAgent_1.OpenAiAgent {
|
|
|
45
45
|
const accountId = config.accountId ?? vendorConfig.accountId;
|
|
46
46
|
const originator = config.originator ?? vendorConfig.originator ?? codex_auth_1.CODEX_ORIGINATOR;
|
|
47
47
|
const baseURL = config.baseURL ?? vendorConfig.baseURL ?? codex_auth_1.CODEX_BASE_URL;
|
|
48
|
+
// No generated fallback: caching is opt-in, so an unset id means the header
|
|
49
|
+
// is not sent at all.
|
|
50
|
+
const sessionId = config.sessionId ?? vendorConfig.sessionId;
|
|
48
51
|
// Filled by the fetch wrapper below and adopted as `this.limits` once
|
|
49
52
|
// `super()` has run.
|
|
50
53
|
const limits = {};
|
|
@@ -60,6 +63,11 @@ class CodexAgent extends OpenAiAgent_1.OpenAiAgent {
|
|
|
60
63
|
...(accountId ? { "chatgpt-account-id": accountId } : {}),
|
|
61
64
|
"OpenAI-Beta": "responses=experimental",
|
|
62
65
|
originator,
|
|
66
|
+
// Opt-in. Stable for the life of the agent: the backend keys its
|
|
67
|
+
// prompt cache on this, and drops to ~0% hit rate without it. Omitted
|
|
68
|
+
// entirely when unset, so the request is byte-identical to one sent
|
|
69
|
+
// before this existed.
|
|
70
|
+
...(sessionId ? { session_id: sessionId } : {}),
|
|
63
71
|
// Every Codex request is a stream; the SDK would send
|
|
64
72
|
// `application/json`, which no reference client does.
|
|
65
73
|
Accept: "text/event-stream",
|
|
@@ -86,8 +94,11 @@ class CodexAgent extends OpenAiAgent_1.OpenAiAgent {
|
|
|
86
94
|
this.accountId = accountId;
|
|
87
95
|
this.originator = originator;
|
|
88
96
|
this.clientVersion =
|
|
89
|
-
config.clientVersion ??
|
|
97
|
+
config.clientVersion ??
|
|
98
|
+
vendorConfig.clientVersion ??
|
|
99
|
+
codex_auth_1.CODEX_CLIENT_VERSION;
|
|
90
100
|
this.codexBaseURL = baseURL;
|
|
101
|
+
this.sessionId = sessionId;
|
|
91
102
|
}
|
|
92
103
|
/**
|
|
93
104
|
* What the most recent response said about the subscription's remaining
|
|
@@ -119,16 +130,29 @@ class CodexAgent extends OpenAiAgent_1.OpenAiAgent {
|
|
|
119
130
|
* Build an agent from the credentials `codex login` stored, wrapped in a
|
|
120
131
|
* provider that refreshes the access token as it ages out.
|
|
121
132
|
*
|
|
133
|
+
* Constructs `this`, so `MyCodexAgent.fromCodexCli(…)` returns a
|
|
134
|
+
* `MyCodexAgent` — see {@link CodexAgent.fromCredentials}.
|
|
135
|
+
*
|
|
122
136
|
* @throws if no credentials are present — run `codex login` first.
|
|
123
137
|
*/
|
|
124
138
|
static async fromCodexCli(config, history) {
|
|
125
139
|
const credentials = await (0, codex_auth_1.loadCodexCredentials)(config.codexHome);
|
|
126
|
-
|
|
140
|
+
// `this`, not `CodexAgent`: routed through the subclass so an override of
|
|
141
|
+
// `fromCredentials` is not skipped either.
|
|
142
|
+
return this.fromCredentials(credentials, config, history);
|
|
127
143
|
}
|
|
128
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* Build an agent from credentials obtained however you like.
|
|
146
|
+
*
|
|
147
|
+
* Instantiates `this` rather than `CodexAgent`, so a subclass gets its own
|
|
148
|
+
* type back and its overrides actually run. Hard-coding the class here made
|
|
149
|
+
* `class MyCodexAgent extends CodexAgent` silently produce a plain
|
|
150
|
+
* `CodexAgent` — no error, no override, and nothing to see until an
|
|
151
|
+
* experiment came back saying the change under test had no effect.
|
|
152
|
+
*/
|
|
129
153
|
static fromCredentials(credentials, config, history) {
|
|
130
154
|
const tokens = (0, codex_auth_1.createCodexTokenProvider)(credentials, config.tokenOptions);
|
|
131
|
-
return new
|
|
155
|
+
return new this({
|
|
132
156
|
...config,
|
|
133
157
|
// The function form: the SDK re-invokes it before every request, so a
|
|
134
158
|
// long run outlives the ~1h token.
|
|
@@ -136,6 +160,13 @@ class CodexAgent extends OpenAiAgent_1.OpenAiAgent {
|
|
|
136
160
|
accountId: credentials.accountId,
|
|
137
161
|
}, history);
|
|
138
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Every model on this backend reasons, and this is what the Codex CLI itself
|
|
165
|
+
* does, so the encrypted-reasoning round trip is on unless turned off.
|
|
166
|
+
*/
|
|
167
|
+
defaultIncludeEncryptedReasoning() {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
139
170
|
/** This backend refuses `stream: false` outright. */
|
|
140
171
|
get forceStreaming() {
|
|
141
172
|
return true;
|
|
@@ -59,13 +59,56 @@ export type AgentConfig<M extends OpenAIModel = OpenAIModel> = Omit<BaseAgentCon
|
|
|
59
59
|
*/
|
|
60
60
|
promptCacheKey?: string;
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
62
|
+
* Ask for each turn's reasoning to come back as an encrypted, replayable
|
|
63
|
+
* blob (`include: ["reasoning.encrypted_content"]`), and send those blobs
|
|
64
|
+
* back on every later request of the conversation.
|
|
65
|
+
*
|
|
66
|
+
* Both halves matter, and both happen here: requesting the blobs without
|
|
67
|
+
* replaying them changes nothing, and replaying is only possible because the
|
|
68
|
+
* agent runs with `store: false`, which leaves the provider holding no
|
|
69
|
+
* reasoning of its own.
|
|
70
|
+
*
|
|
71
|
+
* Why it is worth the bytes on a reasoning model: the model's own thinking is
|
|
72
|
+
* part of what it saw when it decided to call a tool, so dropping it between
|
|
73
|
+
* hops makes the model re-derive it — and, because the replayed prefix no
|
|
74
|
+
* longer matches what was processed last turn, breaks the prompt cache from
|
|
75
|
+
* the first turn onward.
|
|
76
|
+
*
|
|
77
|
+
* Setting it to `false` stops both halves too: nothing is requested, and
|
|
78
|
+
* blobs already sitting in the history are not replayed either. That is what
|
|
79
|
+
* makes turning it off a way out of the model-switch rejection below, rather
|
|
80
|
+
* than a half-measure that keeps sending the old model's reasoning.
|
|
81
|
+
*
|
|
82
|
+
* Defaults to on for models known to reason (see
|
|
83
|
+
* {@link OPENAI_REASONING_SUPPORT}) *on OpenAI's own API*, and off otherwise
|
|
84
|
+
* — including behind a custom `baseURL`, since a model name says nothing
|
|
85
|
+
* about whether the host serving it accepts the parameter. So a non-reasoning
|
|
86
|
+
* model such as `gpt-4.1-mini`, and any gateway or local server, sends a
|
|
87
|
+
* byte-identical request. Set it explicitly for a model too new to be in that
|
|
88
|
+
* table, or for a compatible host.
|
|
89
|
+
*
|
|
90
|
+
* Reasoning blobs are tied to the model that produced them: switching models
|
|
91
|
+
* mid-conversation with a history full of them is rejected. Clear the history
|
|
92
|
+
* or turn this off when doing that.
|
|
93
|
+
*/
|
|
94
|
+
includeEncryptedReasoning?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* How long cached prefixes stay warm. `"in_memory"` expires a prefix after
|
|
97
|
+
* minutes of inactivity (an hour at the outside); `"24h"` keeps it up to a
|
|
98
|
+
* day.
|
|
99
|
+
*
|
|
100
|
+
* Left unset, the default is your organization's data-retention policy, not
|
|
101
|
+
* a fixed value: orgs *without* ZDR default to `"24h"`, orgs *with* ZDR to
|
|
102
|
+
* `"in_memory"`. Set it explicitly to stop prefixes being retained for a day
|
|
103
|
+
* without having to enable ZDR account-wide.
|
|
104
|
+
*
|
|
105
|
+
* `gpt-5.5` and later accept only `"24h"` here, and the field is deprecated
|
|
106
|
+
* upstream in favour of `prompt_cache_options.ttl` — the two are independent
|
|
107
|
+
* (this is a *maximum* retention policy, `ttl` a *minimum* lifetime).
|
|
65
108
|
*
|
|
66
109
|
* Ignored by the ChatGPT/Codex backend, which manages its own cache.
|
|
67
110
|
*/
|
|
68
|
-
promptCacheRetention?: "
|
|
111
|
+
promptCacheRetention?: "in_memory" | "24h";
|
|
69
112
|
};
|
|
70
113
|
/**
|
|
71
114
|
* Lowest `reasoning.effort` the given model accepts, used to resolve
|
|
@@ -86,16 +129,19 @@ export declare function lowestReasoningEffort(model: string | undefined): Reason
|
|
|
86
129
|
/**
|
|
87
130
|
* `usage.input_tokens_details` as the wire actually carries it.
|
|
88
131
|
*
|
|
89
|
-
* The SDK
|
|
90
|
-
*
|
|
91
|
-
* live on 2026-09-10).
|
|
92
|
-
*
|
|
93
|
-
*
|
|
132
|
+
* The SDK declares both counts on `ResponseUsage.InputTokensDetails` as of
|
|
133
|
+
* `openai` 7.x (6.x had `cached_tokens` alone, though the ChatGPT/Codex backend
|
|
134
|
+
* already reported `cache_write_tokens` — observed live on 2026-09-10). What it
|
|
135
|
+
* gets wrong for our purposes is that it types them as *required*: a
|
|
136
|
+
* non-OpenAI host behind this SDK — llama.cpp, vLLM, a gateway — may report
|
|
137
|
+
* one, the other, or neither. Restating them as optional keeps `parseUsage`
|
|
138
|
+
* from trusting a number that isn't there, and keeps `undefined` ("provider
|
|
139
|
+
* said nothing") distinct from `0` ("nothing was cached").
|
|
94
140
|
*/
|
|
95
141
|
export type OpenAIInputTokensDetails = {
|
|
96
142
|
/** Prompt tokens served from cache. */
|
|
97
143
|
cached_tokens?: number;
|
|
98
|
-
/** Prompt tokens written to cache.
|
|
144
|
+
/** Prompt tokens written to cache. On `gpt-5.6`+ these bill at 1.25x input. */
|
|
99
145
|
cache_write_tokens?: number;
|
|
100
146
|
};
|
|
101
147
|
/**
|
|
@@ -230,6 +276,63 @@ export declare class OpenAiAgent<M extends OpenAIModel = OpenAIModel, TModelCard
|
|
|
230
276
|
* emits `response.reasoning_summary_text.delta` events when it is set.
|
|
231
277
|
*/
|
|
232
278
|
private buildReasoningParams;
|
|
279
|
+
/**
|
|
280
|
+
* Whether {@link AgentConfig.includeEncryptedReasoning} defaults to on, when
|
|
281
|
+
* the caller has not said either way.
|
|
282
|
+
*
|
|
283
|
+
* On only for OpenAI's own API *and* a model the reasoning table knows
|
|
284
|
+
* about. Asking a non-reasoning model for reasoning blobs would add a
|
|
285
|
+
* parameter it has nothing to put in; asking a third-party host behind a
|
|
286
|
+
* custom `baseURL` — a gateway, vLLM, llama.cpp — would silently start
|
|
287
|
+
* sending it a parameter it never received before, which is the opposite of
|
|
288
|
+
* the byte-identical request this default exists to preserve. A model name
|
|
289
|
+
* says nothing about the host serving it, so an OpenAI-shaped name on a proxy
|
|
290
|
+
* must not be enough on its own. Set the flag explicitly for a host that does
|
|
291
|
+
* support the round trip.
|
|
292
|
+
*
|
|
293
|
+
* `CodexAgent` overrides this: it always runs against a custom `baseURL`, and
|
|
294
|
+
* every model on that backend reasons.
|
|
295
|
+
*
|
|
296
|
+
* Called from the base constructor, so an override must depend on nothing but
|
|
297
|
+
* its arguments: the subclass's own fields are not assigned yet.
|
|
298
|
+
*/
|
|
299
|
+
protected defaultIncludeEncryptedReasoning(model: string | undefined, baseURL: string | undefined): boolean;
|
|
300
|
+
/**
|
|
301
|
+
* The `include` field, asking for reasoning to come back in a form that can
|
|
302
|
+
* be replayed on the next request.
|
|
303
|
+
*
|
|
304
|
+
* Omitted entirely when off, so requests stay byte-identical to what earlier
|
|
305
|
+
* versions sent. The other half of this — putting the returned items back
|
|
306
|
+
* into `input` — is `openAiTransformer`'s, fed by
|
|
307
|
+
* {@link OpenAiAgent.replayableReasoning}.
|
|
308
|
+
*/
|
|
309
|
+
private buildIncludeParams;
|
|
310
|
+
/**
|
|
311
|
+
* The `reasoning` items of a response that are worth keeping.
|
|
312
|
+
*
|
|
313
|
+
* Only items carrying `encrypted_content` qualify: the agent always sends
|
|
314
|
+
* `store: false`, so the provider has retained nothing, and an item replayed
|
|
315
|
+
* without its payload cannot be resolved — the request fails rather than
|
|
316
|
+
* silently ignoring it. A summary-only item is therefore dropped, exactly as
|
|
317
|
+
* it was before this existed.
|
|
318
|
+
*
|
|
319
|
+
* A turn that also used a **built-in tool** keeps its reasoning too. Those
|
|
320
|
+
* items (`web_search_call` and friends) are not stored, so the replayed turn
|
|
321
|
+
* is `[reasoning, reasoning, message]` where the model emitted
|
|
322
|
+
* `[reasoning, web_search_call, reasoning, message]`. That was expected to be
|
|
323
|
+
* rejected — "reasoning item without its required following item" — but it is
|
|
324
|
+
* not: probed live on 2026-09-10 against `gpt-5-nano` and `gpt-5.4-mini`, the
|
|
325
|
+
* API accepted that shape, the same reasoning item twice in a row, a
|
|
326
|
+
* reasoning item followed only by the next *user* turn, and a dangling
|
|
327
|
+
* reasoning item last in `input` with nothing after it at all. That ordering
|
|
328
|
+
* rule appears to govern the `store: true` / `previous_response_id` flow, not
|
|
329
|
+
* this one, which sends `store: false` and an explicit `input`.
|
|
330
|
+
*
|
|
331
|
+
* So the reasoning is kept. Dropping it would cost every `builtInTools` user
|
|
332
|
+
* their prompt-cache continuity to avoid a rejection that does not happen.
|
|
333
|
+
* Revisit if the API tightens.
|
|
334
|
+
*/
|
|
335
|
+
protected replayableReasoning(response: Response): unknown[];
|
|
233
336
|
/**
|
|
234
337
|
* Prompt-caching parameters, omitted entirely when unconfigured so a request
|
|
235
338
|
* stays byte-identical to what earlier versions sent.
|
|
@@ -107,7 +107,7 @@ function describeOpenAIError(error) {
|
|
|
107
107
|
const body = err?.error;
|
|
108
108
|
const fromBody = typeof body === "string"
|
|
109
109
|
? body
|
|
110
|
-
:
|
|
110
|
+
: body?.message ?? body?.detail ?? undefined;
|
|
111
111
|
return {
|
|
112
112
|
message: fromBody ?? err?.detail ?? err?.message ?? "Unknown error",
|
|
113
113
|
code: typeof body === "object" ? body?.code : undefined,
|
|
@@ -173,6 +173,9 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
173
173
|
const builtInTools = config.builtInTools ?? vendorConfig.builtInTools;
|
|
174
174
|
const promptCacheKey = config.promptCacheKey ?? vendorConfig.promptCacheKey;
|
|
175
175
|
const promptCacheRetention = config.promptCacheRetention ?? vendorConfig.promptCacheRetention;
|
|
176
|
+
const includeEncryptedReasoning = config.includeEncryptedReasoning ??
|
|
177
|
+
vendorConfig.includeEncryptedReasoning ??
|
|
178
|
+
this.defaultIncludeEncryptedReasoning(config.model, baseURL);
|
|
176
179
|
this.config = {
|
|
177
180
|
model: config.model || "gpt-4.1-mini",
|
|
178
181
|
// No default. `max_output_tokens` is optional on the Responses API, and
|
|
@@ -189,6 +192,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
189
192
|
builtInTools,
|
|
190
193
|
promptCacheKey,
|
|
191
194
|
promptCacheRetention,
|
|
195
|
+
includeEncryptedReasoning,
|
|
192
196
|
apiKey: config.apiKey,
|
|
193
197
|
baseURL,
|
|
194
198
|
temperature: config.temperature,
|
|
@@ -227,7 +231,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
227
231
|
/** The configured key, resolving the function form if that is what was given. */
|
|
228
232
|
async resolveApiKey() {
|
|
229
233
|
const key = this.config.apiKey;
|
|
230
|
-
return typeof key === "function" ? await key() :
|
|
234
|
+
return typeof key === "function" ? await key() : key ?? "";
|
|
231
235
|
}
|
|
232
236
|
getToolDefinitions() {
|
|
233
237
|
return Array.from(this.tools.values()).map((tool) => {
|
|
@@ -282,7 +286,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
282
286
|
const output = streamedItems;
|
|
283
287
|
const outputText = output
|
|
284
288
|
.filter((item) => item.type === "message")
|
|
285
|
-
.flatMap((item) => ("content" in item ?
|
|
289
|
+
.flatMap((item) => ("content" in item ? item.content ?? [] : []))
|
|
286
290
|
.filter((part) => part?.type === "output_text")
|
|
287
291
|
.map((part) => ("text" in part ? part.text : ""))
|
|
288
292
|
.join("");
|
|
@@ -354,6 +358,77 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
354
358
|
},
|
|
355
359
|
};
|
|
356
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* Whether {@link AgentConfig.includeEncryptedReasoning} defaults to on, when
|
|
363
|
+
* the caller has not said either way.
|
|
364
|
+
*
|
|
365
|
+
* On only for OpenAI's own API *and* a model the reasoning table knows
|
|
366
|
+
* about. Asking a non-reasoning model for reasoning blobs would add a
|
|
367
|
+
* parameter it has nothing to put in; asking a third-party host behind a
|
|
368
|
+
* custom `baseURL` — a gateway, vLLM, llama.cpp — would silently start
|
|
369
|
+
* sending it a parameter it never received before, which is the opposite of
|
|
370
|
+
* the byte-identical request this default exists to preserve. A model name
|
|
371
|
+
* says nothing about the host serving it, so an OpenAI-shaped name on a proxy
|
|
372
|
+
* must not be enough on its own. Set the flag explicitly for a host that does
|
|
373
|
+
* support the round trip.
|
|
374
|
+
*
|
|
375
|
+
* `CodexAgent` overrides this: it always runs against a custom `baseURL`, and
|
|
376
|
+
* every model on that backend reasons.
|
|
377
|
+
*
|
|
378
|
+
* Called from the base constructor, so an override must depend on nothing but
|
|
379
|
+
* its arguments: the subclass's own fields are not assigned yet.
|
|
380
|
+
*/
|
|
381
|
+
defaultIncludeEncryptedReasoning(model, baseURL) {
|
|
382
|
+
return baseURL === undefined && lowestReasoningEffort(model) !== undefined;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* The `include` field, asking for reasoning to come back in a form that can
|
|
386
|
+
* be replayed on the next request.
|
|
387
|
+
*
|
|
388
|
+
* Omitted entirely when off, so requests stay byte-identical to what earlier
|
|
389
|
+
* versions sent. The other half of this — putting the returned items back
|
|
390
|
+
* into `input` — is `openAiTransformer`'s, fed by
|
|
391
|
+
* {@link OpenAiAgent.replayableReasoning}.
|
|
392
|
+
*/
|
|
393
|
+
buildIncludeParams() {
|
|
394
|
+
return this.config.includeEncryptedReasoning
|
|
395
|
+
? { include: ["reasoning.encrypted_content"] }
|
|
396
|
+
: {};
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* The `reasoning` items of a response that are worth keeping.
|
|
400
|
+
*
|
|
401
|
+
* Only items carrying `encrypted_content` qualify: the agent always sends
|
|
402
|
+
* `store: false`, so the provider has retained nothing, and an item replayed
|
|
403
|
+
* without its payload cannot be resolved — the request fails rather than
|
|
404
|
+
* silently ignoring it. A summary-only item is therefore dropped, exactly as
|
|
405
|
+
* it was before this existed.
|
|
406
|
+
*
|
|
407
|
+
* A turn that also used a **built-in tool** keeps its reasoning too. Those
|
|
408
|
+
* items (`web_search_call` and friends) are not stored, so the replayed turn
|
|
409
|
+
* is `[reasoning, reasoning, message]` where the model emitted
|
|
410
|
+
* `[reasoning, web_search_call, reasoning, message]`. That was expected to be
|
|
411
|
+
* rejected — "reasoning item without its required following item" — but it is
|
|
412
|
+
* not: probed live on 2026-09-10 against `gpt-5-nano` and `gpt-5.4-mini`, the
|
|
413
|
+
* API accepted that shape, the same reasoning item twice in a row, a
|
|
414
|
+
* reasoning item followed only by the next *user* turn, and a dangling
|
|
415
|
+
* reasoning item last in `input` with nothing after it at all. That ordering
|
|
416
|
+
* rule appears to govern the `store: true` / `previous_response_id` flow, not
|
|
417
|
+
* this one, which sends `store: false` and an explicit `input`.
|
|
418
|
+
*
|
|
419
|
+
* So the reasoning is kept. Dropping it would cost every `builtInTools` user
|
|
420
|
+
* their prompt-cache continuity to avoid a rejection that does not happen.
|
|
421
|
+
* Revisit if the API tightens.
|
|
422
|
+
*/
|
|
423
|
+
replayableReasoning(response) {
|
|
424
|
+
if (!this.config.includeEncryptedReasoning)
|
|
425
|
+
return [];
|
|
426
|
+
return (response.output ?? []).filter((item) => typeof item === "object" &&
|
|
427
|
+
item !== null &&
|
|
428
|
+
item.type === "reasoning" &&
|
|
429
|
+
typeof item.encrypted_content ===
|
|
430
|
+
"string");
|
|
431
|
+
}
|
|
357
432
|
/**
|
|
358
433
|
* Prompt-caching parameters, omitted entirely when unconfigured so a request
|
|
359
434
|
* stays byte-identical to what earlier versions sent.
|
|
@@ -404,7 +479,9 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
404
479
|
// mid-loop. endExecution() in the finally block enforces limits once.
|
|
405
480
|
this.history.beginExecution();
|
|
406
481
|
try {
|
|
407
|
-
const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries()
|
|
482
|
+
const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries(), {
|
|
483
|
+
replayReasoning: this.config.includeEncryptedReasoning,
|
|
484
|
+
});
|
|
408
485
|
this.startTurnTimer();
|
|
409
486
|
const response = await this.createResponse({
|
|
410
487
|
model: this.config.model,
|
|
@@ -418,6 +495,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
418
495
|
user: this.config.user,
|
|
419
496
|
...this.buildReasoningParams(),
|
|
420
497
|
...this.buildCacheParams(),
|
|
498
|
+
...this.buildIncludeParams(),
|
|
421
499
|
}, { signal: options?.signal });
|
|
422
500
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, response);
|
|
423
501
|
return await this.handleResponse(response, options);
|
|
@@ -494,7 +572,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
494
572
|
messageOutput.type === "message" &&
|
|
495
573
|
messageOutput.status === "completed") {
|
|
496
574
|
// Normal text response - add to history in normalized format
|
|
497
|
-
const entry = transformers_1.openAiTransformer.fromProviderMessage("assistant", response.output_text);
|
|
575
|
+
const entry = transformers_1.openAiTransformer.fromProviderMessage("assistant", response.output_text, undefined, this.replayableReasoning(response));
|
|
498
576
|
this.addToHistory(entry);
|
|
499
577
|
this.emit(AgentEvent_1.AgentEvent.DONE, response, this.lastTokenUsage);
|
|
500
578
|
// Report completion to viz
|
|
@@ -522,7 +600,11 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
522
600
|
name: tc.name,
|
|
523
601
|
arguments: tc.arguments,
|
|
524
602
|
}));
|
|
525
|
-
const assistantEntry = transformers_1.openAiTransformer.fromProviderMessage("assistant", response.output_text || "", functionCalls
|
|
603
|
+
const assistantEntry = transformers_1.openAiTransformer.fromProviderMessage("assistant", response.output_text || "", functionCalls,
|
|
604
|
+
// The thinking that led to these calls: replayed on the follow-up so
|
|
605
|
+
// the model does not have to re-derive it, and so the prefix the
|
|
606
|
+
// provider caches still matches what it processed.
|
|
607
|
+
this.replayableReasoning(response));
|
|
526
608
|
this.addToHistory(assistantEntry);
|
|
527
609
|
const toolResponses = await this.handleToolUse(toolCalls, options);
|
|
528
610
|
// Add tool results to history (normalized)
|
|
@@ -532,7 +614,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
532
614
|
}
|
|
533
615
|
// Continue conversation
|
|
534
616
|
try {
|
|
535
|
-
const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries());
|
|
617
|
+
const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries(), { replayReasoning: this.config.includeEncryptedReasoning });
|
|
536
618
|
this.startTurnTimer();
|
|
537
619
|
const newResponse = await this.createResponse({
|
|
538
620
|
model: this.config.model,
|
|
@@ -546,6 +628,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
546
628
|
user: this.config.user,
|
|
547
629
|
...this.buildReasoningParams(),
|
|
548
630
|
...this.buildCacheParams(),
|
|
631
|
+
...this.buildIncludeParams(),
|
|
549
632
|
}, { signal: options?.signal });
|
|
550
633
|
this.emit(AgentEvent_1.AgentEvent.AFTER_EXECUTE, newResponse);
|
|
551
634
|
return this.handleResponse(newResponse, options);
|
|
@@ -714,9 +797,11 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
714
797
|
}
|
|
715
798
|
}
|
|
716
799
|
async *streamTurn(options) {
|
|
717
|
-
const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries()
|
|
800
|
+
const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries(), {
|
|
801
|
+
replayReasoning: this.config.includeEncryptedReasoning,
|
|
802
|
+
});
|
|
718
803
|
this.startTurnTimer();
|
|
719
|
-
const stream = await this.client.responses.create(this.transformRequestParams({
|
|
804
|
+
const stream = (await this.client.responses.create(this.transformRequestParams({
|
|
720
805
|
model: this.config.model,
|
|
721
806
|
max_output_tokens: this.config.maxTokens,
|
|
722
807
|
input: inputMessages,
|
|
@@ -728,7 +813,8 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
728
813
|
user: this.config.user,
|
|
729
814
|
...this.buildReasoningParams("auto"),
|
|
730
815
|
...this.buildCacheParams(),
|
|
731
|
-
|
|
816
|
+
...this.buildIncludeParams(),
|
|
817
|
+
}), { signal: options?.signal }));
|
|
732
818
|
let completedEvent = null;
|
|
733
819
|
const streamedItems = [];
|
|
734
820
|
// The Responses API builds the committed turn out of `response.completed`,
|
|
@@ -809,7 +895,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
809
895
|
name: tc.name,
|
|
810
896
|
arguments: tc.arguments,
|
|
811
897
|
}));
|
|
812
|
-
const assistantEntry = transformers_1.openAiTransformer.fromProviderMessage("assistant", response.output_text || "", functionCalls);
|
|
898
|
+
const assistantEntry = transformers_1.openAiTransformer.fromProviderMessage("assistant", response.output_text || "", functionCalls, this.replayableReasoning(response));
|
|
813
899
|
this.addToHistory(assistantEntry);
|
|
814
900
|
committed = true;
|
|
815
901
|
const toolResults = await this.handleToolUse(toolCalls, options);
|
|
@@ -820,7 +906,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
820
906
|
}
|
|
821
907
|
else {
|
|
822
908
|
const textContent = response.output_text || "";
|
|
823
|
-
const entry = transformers_1.openAiTransformer.fromProviderMessage("assistant", textContent);
|
|
909
|
+
const entry = transformers_1.openAiTransformer.fromProviderMessage("assistant", textContent, undefined, this.replayableReasoning(response));
|
|
824
910
|
this.addToHistory(entry);
|
|
825
911
|
committed = true;
|
|
826
912
|
this.emit(AgentEvent_1.AgentEvent.DONE, response, this.lastTokenUsage);
|
|
@@ -3,8 +3,8 @@ import { HistoryEntry, MessageRole, MessageContent } from "./types";
|
|
|
3
3
|
import type { ReduceOptions } from "./types";
|
|
4
4
|
/** @internal — exposed for test teardown only */
|
|
5
5
|
export declare function resetTokenxCache(): void;
|
|
6
|
-
export type { HistoryEntry, MessageRole, MessageContent, ReduceOptions, ImageMimeType, ImageUrlContent, ImageBase64Content, ThinkingContent, } from "./types";
|
|
7
|
-
export { text, toolUse, toolResult, thinking, textMessage, imageUrl, imageBase64, isTextContent, isToolUseContent, isToolResultContent, isThinkingContent, isImageUrlContent, isImageBase64Content, isImageContent, } from "./types";
|
|
6
|
+
export type { HistoryEntry, MessageRole, MessageContent, ReduceOptions, ImageMimeType, ImageUrlContent, ImageBase64Content, ThinkingContent, ReasoningDetailsFormat, } from "./types";
|
|
7
|
+
export { text, toolUse, toolResult, thinking, textMessage, imageUrl, imageBase64, isTextContent, isToolUseContent, isToolResultContent, isThinkingContent, isImageUrlContent, isImageBase64Content, isImageContent, reasoningDetailsFormatOf, } from "./types";
|
|
8
8
|
/**
|
|
9
9
|
* Metadata stored alongside each history entry.
|
|
10
10
|
* Extended with summary tracking fields for the compression plugin.
|
package/dist/history/History.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.History = exports.isImageContent = exports.isImageBase64Content = exports.isImageUrlContent = exports.isThinkingContent = exports.isToolResultContent = exports.isToolUseContent = exports.isTextContent = exports.imageBase64 = exports.imageUrl = exports.textMessage = exports.thinking = exports.toolResult = exports.toolUse = exports.text = void 0;
|
|
39
|
+
exports.History = exports.reasoningDetailsFormatOf = exports.isImageContent = exports.isImageBase64Content = exports.isImageUrlContent = exports.isThinkingContent = exports.isToolResultContent = exports.isToolUseContent = exports.isTextContent = exports.imageBase64 = exports.imageUrl = exports.textMessage = exports.thinking = exports.toolResult = exports.toolUse = exports.text = void 0;
|
|
40
40
|
exports.resetTokenxCache = resetTokenxCache;
|
|
41
41
|
const events_1 = __importDefault(require("events"));
|
|
42
42
|
const types_1 = require("./types");
|
|
@@ -53,9 +53,22 @@ void Promise.resolve().then(() => __importStar(require("tokenx"))).then((mod) =>
|
|
|
53
53
|
function resetTokenxCache() {
|
|
54
54
|
_estimateTokenCount = (t) => Math.ceil(t.length / 4);
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Chars per token for an opaque reasoning payload.
|
|
58
|
+
*
|
|
59
|
+
* `ThinkingContent.reasoningDetails` holds ciphertext — OpenAI's
|
|
60
|
+
* `encrypted_content`, OpenRouter's `reasoning.encrypted` — which stands in for
|
|
61
|
+
* the reasoning tokens the provider decrypts it back into, and is far longer
|
|
62
|
+
* than the tokens it represents. Running it through the text estimator counted
|
|
63
|
+
* it at roughly three times its weight, which trimmed history early for no
|
|
64
|
+
* reason. Measured on a Codex turn: 1892 chars of blob carried 156 reasoning
|
|
65
|
+
* tokens, i.e. ~12 chars per token against the ~4 a plain string averages.
|
|
66
|
+
*/
|
|
67
|
+
const CHARS_PER_ENCRYPTED_REASONING_TOKEN = 12;
|
|
56
68
|
/**
|
|
57
69
|
* Estimate token count for a content block array.
|
|
58
70
|
* Image blocks use a flat 1000-token estimate (resolution-independent conservative value).
|
|
71
|
+
* Encrypted reasoning payloads get their own ratio, see above.
|
|
59
72
|
* Text and tool blocks fall through to the tokenx estimator.
|
|
60
73
|
*/
|
|
61
74
|
function estimateContentTokens(content) {
|
|
@@ -63,6 +76,13 @@ function estimateContentTokens(content) {
|
|
|
63
76
|
if ((0, types_1.isImageContent)(block)) {
|
|
64
77
|
return sum + 1000;
|
|
65
78
|
}
|
|
79
|
+
if ((0, types_1.isThinkingContent)(block) && block.reasoningDetails?.length) {
|
|
80
|
+
const { reasoningDetails, ...rest } = block;
|
|
81
|
+
return (sum +
|
|
82
|
+
_estimateTokenCount(JSON.stringify(rest)) +
|
|
83
|
+
Math.ceil(JSON.stringify(reasoningDetails).length /
|
|
84
|
+
CHARS_PER_ENCRYPTED_REASONING_TOKEN));
|
|
85
|
+
}
|
|
66
86
|
return sum + _estimateTokenCount(JSON.stringify(block));
|
|
67
87
|
}, 0);
|
|
68
88
|
}
|
|
@@ -81,6 +101,7 @@ Object.defineProperty(exports, "isThinkingContent", { enumerable: true, get: fun
|
|
|
81
101
|
Object.defineProperty(exports, "isImageUrlContent", { enumerable: true, get: function () { return types_2.isImageUrlContent; } });
|
|
82
102
|
Object.defineProperty(exports, "isImageBase64Content", { enumerable: true, get: function () { return types_2.isImageBase64Content; } });
|
|
83
103
|
Object.defineProperty(exports, "isImageContent", { enumerable: true, get: function () { return types_2.isImageContent; } });
|
|
104
|
+
Object.defineProperty(exports, "reasoningDetailsFormatOf", { enumerable: true, get: function () { return types_2.reasoningDetailsFormatOf; } });
|
|
84
105
|
/**
|
|
85
106
|
* Manages conversation history in a provider-agnostic format.
|
|
86
107
|
*
|
|
@@ -433,7 +454,8 @@ class History extends events_1.default {
|
|
|
433
454
|
applyTrimming() {
|
|
434
455
|
if (this._executing)
|
|
435
456
|
return;
|
|
436
|
-
if (this.options.maxLength &&
|
|
457
|
+
if (this.options.maxLength &&
|
|
458
|
+
this._entries.length > this.options.maxLength) {
|
|
437
459
|
this._entries = this._entries.slice(this._entries.length - this.options.maxLength);
|
|
438
460
|
this.sanitizeToolPairs();
|
|
439
461
|
}
|
package/dist/history/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { History, resetTokenxCache, type EntryMetadata, type ReducibleEntry, type HistoryPlugin, } from "./History";
|
|
2
2
|
export { RedisHistory } from "./RedisHistory";
|
|
3
|
-
export type { HistoryEntry, MessageRole, MessageContent, TextContent, ToolUseContent, ToolResultContent, ThinkingContent, ProviderMeta, ReduceOptions, } from "./types";
|
|
4
|
-
export { text, toolUse, toolResult, thinking, textMessage, isTextContent, isToolUseContent, isToolResultContent, isThinkingContent, } from "./types";
|
|
3
|
+
export type { HistoryEntry, MessageRole, MessageContent, TextContent, ToolUseContent, ToolResultContent, ThinkingContent, ReasoningDetailsFormat, ProviderMeta, ReduceOptions, } from "./types";
|
|
4
|
+
export { text, toolUse, toolResult, thinking, textMessage, isTextContent, isToolUseContent, isToolResultContent, isThinkingContent, reasoningDetailsFormatOf, } from "./types";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/history/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isThinkingContent = exports.isToolResultContent = exports.isToolUseContent = exports.isTextContent = exports.textMessage = exports.thinking = exports.toolResult = exports.toolUse = exports.text = exports.RedisHistory = exports.resetTokenxCache = exports.History = void 0;
|
|
3
|
+
exports.reasoningDetailsFormatOf = exports.isThinkingContent = exports.isToolResultContent = exports.isToolUseContent = exports.isTextContent = exports.textMessage = exports.thinking = exports.toolResult = exports.toolUse = exports.text = exports.RedisHistory = exports.resetTokenxCache = exports.History = void 0;
|
|
4
4
|
var History_1 = require("./History");
|
|
5
5
|
Object.defineProperty(exports, "History", { enumerable: true, get: function () { return History_1.History; } });
|
|
6
6
|
Object.defineProperty(exports, "resetTokenxCache", { enumerable: true, get: function () { return History_1.resetTokenxCache; } });
|
|
@@ -16,4 +16,5 @@ Object.defineProperty(exports, "isTextContent", { enumerable: true, get: functio
|
|
|
16
16
|
Object.defineProperty(exports, "isToolUseContent", { enumerable: true, get: function () { return types_1.isToolUseContent; } });
|
|
17
17
|
Object.defineProperty(exports, "isToolResultContent", { enumerable: true, get: function () { return types_1.isToolResultContent; } });
|
|
18
18
|
Object.defineProperty(exports, "isThinkingContent", { enumerable: true, get: function () { return types_1.isThinkingContent; } });
|
|
19
|
+
Object.defineProperty(exports, "reasoningDetailsFormatOf", { enumerable: true, get: function () { return types_1.reasoningDetailsFormatOf; } });
|
|
19
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -23,9 +23,18 @@ export declare const anthropicTransformer: {
|
|
|
23
23
|
};
|
|
24
24
|
export declare const openAiTransformer: {
|
|
25
25
|
/**
|
|
26
|
-
* Convert normalized entries to OpenAI ResponseInputItem format
|
|
26
|
+
* Convert normalized entries to OpenAI ResponseInputItem format.
|
|
27
|
+
*
|
|
28
|
+
* `replayReasoning` defaults to on and mirrors
|
|
29
|
+
* `AgentConfig.includeEncryptedReasoning`: requesting the blobs and sending
|
|
30
|
+
* them back are one feature, so switching it off has to stop *both*.
|
|
31
|
+
* Otherwise a history that already holds blobs keeps replaying them — which
|
|
32
|
+
* is exactly the situation the flag is turned off to escape, since reasoning
|
|
33
|
+
* is tied to the model that produced it and switching models is rejected.
|
|
27
34
|
*/
|
|
28
|
-
toProvider(entries: HistoryEntry[]
|
|
35
|
+
toProvider(entries: HistoryEntry[], options?: {
|
|
36
|
+
replayReasoning?: boolean;
|
|
37
|
+
}): ResponseInputItem[];
|
|
29
38
|
/**
|
|
30
39
|
* Convert OpenAI response to normalized HistoryEntry
|
|
31
40
|
*/
|
|
@@ -34,7 +43,7 @@ export declare const openAiTransformer: {
|
|
|
34
43
|
call_id: string;
|
|
35
44
|
name: string;
|
|
36
45
|
arguments: string;
|
|
37
|
-
}
|
|
46
|
+
}>, reasoningItems?: unknown[]): HistoryEntry;
|
|
38
47
|
/**
|
|
39
48
|
* Create a tool result entry from OpenAI function call output
|
|
40
49
|
*/
|
|
@@ -10,17 +10,33 @@ const types_1 = require("./types");
|
|
|
10
10
|
// =============================================================================
|
|
11
11
|
// Anthropic Transformer
|
|
12
12
|
// =============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Whether a thinking block is one Anthropic itself produced, and so can be sent
|
|
15
|
+
* back to it.
|
|
16
|
+
*
|
|
17
|
+
* Anthropic signs every thinking block it emits (or returns it redacted), and
|
|
18
|
+
* rejects one whose signature does not verify — an empty string included. Other
|
|
19
|
+
* providers' reasoning reaches this transformer whenever a `History` is shared
|
|
20
|
+
* between agents, which the docs recommend: OpenAI's carries a summary and an
|
|
21
|
+
* encrypted payload but no signature, so it has to be dropped rather than
|
|
22
|
+
* replayed under an empty one.
|
|
23
|
+
*/
|
|
24
|
+
function isAnthropicThinkingBlock(block) {
|
|
25
|
+
return block.redactedData !== undefined || block.signature !== undefined;
|
|
26
|
+
}
|
|
13
27
|
exports.anthropicTransformer = {
|
|
14
28
|
/**
|
|
15
29
|
* Convert normalized entries to Anthropic MessageParam format
|
|
16
30
|
*/
|
|
17
31
|
toProvider(entries) {
|
|
18
|
-
return entries
|
|
32
|
+
return (entries
|
|
19
33
|
.filter((entry) => entry.role !== "system") // Anthropic handles system separately
|
|
20
34
|
.map((entry) => {
|
|
21
35
|
const role = entry.role === "assistant" ? "assistant" : "user";
|
|
22
36
|
// Convert content blocks to Anthropic's ContentBlockParam
|
|
23
|
-
const content = entry.content
|
|
37
|
+
const content = entry.content
|
|
38
|
+
.filter((block) => !(0, types_1.isThinkingContent)(block) || isAnthropicThinkingBlock(block))
|
|
39
|
+
.map((block) => {
|
|
24
40
|
if ((0, types_1.isTextContent)(block)) {
|
|
25
41
|
return { type: "text", text: block.text };
|
|
26
42
|
}
|
|
@@ -72,7 +88,12 @@ exports.anthropicTransformer = {
|
|
|
72
88
|
throw new Error(`Unknown content type: ${block.type}`);
|
|
73
89
|
});
|
|
74
90
|
return { role, content };
|
|
75
|
-
})
|
|
91
|
+
})
|
|
92
|
+
// Dropping a foreign thinking block can empty a turn that held nothing
|
|
93
|
+
// else (a reasoning-only assistant entry). Anthropic rejects a message
|
|
94
|
+
// with no content, and there is nothing left to say, so drop the message
|
|
95
|
+
// too. Tool pairs are unaffected: a thinking-only entry has no tool_use.
|
|
96
|
+
.filter((message) => message.content.length > 0));
|
|
76
97
|
},
|
|
77
98
|
/**
|
|
78
99
|
* Convert Anthropic response content to normalized HistoryEntry
|
|
@@ -139,12 +160,48 @@ function toOpenAiId(originalId) {
|
|
|
139
160
|
idMappingToOpenAi.set(originalId, newId);
|
|
140
161
|
return newId;
|
|
141
162
|
}
|
|
163
|
+
function isOpenAiReasoningItem(value) {
|
|
164
|
+
return (typeof value === "object" &&
|
|
165
|
+
value !== null &&
|
|
166
|
+
value.type === "reasoning");
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The reasoning items stored on an assistant entry, in the order they were
|
|
170
|
+
* received.
|
|
171
|
+
*
|
|
172
|
+
* They ride on `ThinkingContent.reasoningDetails` — the same passthrough slot
|
|
173
|
+
* OpenRouter's `reasoning_details` uses — because they have to survive the round
|
|
174
|
+
* trip untouched and there is nothing to model.
|
|
175
|
+
*/
|
|
176
|
+
function openAiReasoningItems(entry) {
|
|
177
|
+
return entry.content
|
|
178
|
+
.filter(types_1.isThinkingContent)
|
|
179
|
+
.filter((block) => (0, types_1.reasoningDetailsFormatOf)(block) === "openai.responses")
|
|
180
|
+
.flatMap((block) => block.reasoningDetails ?? [])
|
|
181
|
+
.filter(isOpenAiReasoningItem);
|
|
182
|
+
}
|
|
183
|
+
/** Human-readable text of a reasoning item's summary, for display in history. */
|
|
184
|
+
function reasoningSummaryText(items) {
|
|
185
|
+
return items
|
|
186
|
+
.flatMap((item) => item.summary ?? [])
|
|
187
|
+
.map((part) => part?.text ?? "")
|
|
188
|
+
.filter(Boolean)
|
|
189
|
+
.join("\n\n");
|
|
190
|
+
}
|
|
142
191
|
exports.openAiTransformer = {
|
|
143
192
|
/**
|
|
144
|
-
* Convert normalized entries to OpenAI ResponseInputItem format
|
|
193
|
+
* Convert normalized entries to OpenAI ResponseInputItem format.
|
|
194
|
+
*
|
|
195
|
+
* `replayReasoning` defaults to on and mirrors
|
|
196
|
+
* `AgentConfig.includeEncryptedReasoning`: requesting the blobs and sending
|
|
197
|
+
* them back are one feature, so switching it off has to stop *both*.
|
|
198
|
+
* Otherwise a history that already holds blobs keeps replaying them — which
|
|
199
|
+
* is exactly the situation the flag is turned off to escape, since reasoning
|
|
200
|
+
* is tied to the model that produced it and switching models is rejected.
|
|
145
201
|
*/
|
|
146
|
-
toProvider(entries) {
|
|
202
|
+
toProvider(entries, options) {
|
|
147
203
|
const items = [];
|
|
204
|
+
const replayReasoning = options?.replayReasoning ?? true;
|
|
148
205
|
for (const entry of entries) {
|
|
149
206
|
if (entry.role === "system") {
|
|
150
207
|
items.push({
|
|
@@ -157,6 +214,18 @@ exports.openAiTransformer = {
|
|
|
157
214
|
});
|
|
158
215
|
continue;
|
|
159
216
|
}
|
|
217
|
+
// Reasoning first, before anything else this turn produced — the order
|
|
218
|
+
// the model emitted it in. The Responses API turns out not to enforce
|
|
219
|
+
// this on the `store: false` + explicit-`input` flow these agents use
|
|
220
|
+
// (probed live 2026-09-10; see `OpenAiAgent.replayableReasoning` for what
|
|
221
|
+
// was accepted), so this is fidelity to what the model produced rather
|
|
222
|
+
// than a constraint. Emitting it out of order is not known to fail, but
|
|
223
|
+
// there is no reason to.
|
|
224
|
+
if (replayReasoning) {
|
|
225
|
+
for (const item of openAiReasoningItems(entry)) {
|
|
226
|
+
items.push(item);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
160
229
|
// Separate content blocks by type for OpenAI format
|
|
161
230
|
const textBlocks = entry.content.filter(types_1.isTextContent);
|
|
162
231
|
const toolUseBlocks = entry.content.filter(types_1.isToolUseContent);
|
|
@@ -238,8 +307,21 @@ exports.openAiTransformer = {
|
|
|
238
307
|
/**
|
|
239
308
|
* Convert OpenAI response to normalized HistoryEntry
|
|
240
309
|
*/
|
|
241
|
-
fromProviderMessage(role, outputText, functionCalls
|
|
310
|
+
fromProviderMessage(role, outputText, functionCalls,
|
|
311
|
+
/**
|
|
312
|
+
* `reasoning` items from `response.output`, stored verbatim so the next
|
|
313
|
+
* request can replay them. Pass only items that carry `encrypted_content`:
|
|
314
|
+
* with `store: false` the provider has kept nothing of its own, so a
|
|
315
|
+
* replayed item without it cannot be resolved and the request is rejected.
|
|
316
|
+
*/
|
|
317
|
+
reasoningItems) {
|
|
242
318
|
const content = [];
|
|
319
|
+
const reasoning = (reasoningItems ?? []).filter(isOpenAiReasoningItem);
|
|
320
|
+
if (reasoning.length > 0) {
|
|
321
|
+
// Summary text is for humans reading the history; the payload that
|
|
322
|
+
// matters is the untouched items on `reasoningDetails`.
|
|
323
|
+
content.push((0, types_1.thinking)(reasoningSummaryText(reasoning), undefined, undefined, reasoning, "openai.responses"));
|
|
324
|
+
}
|
|
243
325
|
if (outputText) {
|
|
244
326
|
content.push((0, types_1.text)(outputText));
|
|
245
327
|
}
|
|
@@ -553,7 +635,10 @@ exports.ollamaTransformer = {
|
|
|
553
635
|
const toolUseBlocks = entry.content.filter(types_1.isToolUseContent);
|
|
554
636
|
const toolResultBlocks = entry.content.filter(types_1.isToolResultContent);
|
|
555
637
|
if (entry.role === "system") {
|
|
556
|
-
messages.push({
|
|
638
|
+
messages.push({
|
|
639
|
+
role: "system",
|
|
640
|
+
content: textBlocks.map((c) => c.text).join("\n"),
|
|
641
|
+
});
|
|
557
642
|
continue;
|
|
558
643
|
}
|
|
559
644
|
if (entry.role === "assistant") {
|
|
@@ -579,7 +664,10 @@ exports.ollamaTransformer = {
|
|
|
579
664
|
}
|
|
580
665
|
}
|
|
581
666
|
else if (textBlocks.length > 0) {
|
|
582
|
-
messages.push({
|
|
667
|
+
messages.push({
|
|
668
|
+
role: "user",
|
|
669
|
+
content: textBlocks.map((c) => c.text).join("\n"),
|
|
670
|
+
});
|
|
583
671
|
}
|
|
584
672
|
}
|
|
585
673
|
return messages;
|
|
@@ -650,7 +738,10 @@ exports.chatCompletionsTransformer = {
|
|
|
650
738
|
const imageBase64Blocks = entry.content.filter(types_1.isImageBase64Content);
|
|
651
739
|
const hasImages = imageUrlBlocks.length > 0 || imageBase64Blocks.length > 0;
|
|
652
740
|
if (entry.role === "system") {
|
|
653
|
-
messages.push({
|
|
741
|
+
messages.push({
|
|
742
|
+
role: "system",
|
|
743
|
+
content: textBlocks.map((c) => c.text).join("\n"),
|
|
744
|
+
});
|
|
654
745
|
continue;
|
|
655
746
|
}
|
|
656
747
|
if (entry.role === "assistant") {
|
|
@@ -720,7 +811,10 @@ exports.chatCompletionsTransformer = {
|
|
|
720
811
|
messages.push({ role: "user", content: parts });
|
|
721
812
|
}
|
|
722
813
|
else if (textBlocks.length > 0) {
|
|
723
|
-
messages.push({
|
|
814
|
+
messages.push({
|
|
815
|
+
role: "user",
|
|
816
|
+
content: textBlocks.map((c) => c.text).join("\n"),
|
|
817
|
+
});
|
|
724
818
|
}
|
|
725
819
|
}
|
|
726
820
|
return messages;
|
|
@@ -807,7 +901,11 @@ function markLatestCacheBreakpoint(messages) {
|
|
|
807
901
|
const message = messages[i];
|
|
808
902
|
if (typeof message.content === "string" && message.content.length > 0) {
|
|
809
903
|
message.content = [
|
|
810
|
-
{
|
|
904
|
+
{
|
|
905
|
+
type: "text",
|
|
906
|
+
text: message.content,
|
|
907
|
+
cacheControl: { type: "ephemeral" },
|
|
908
|
+
},
|
|
811
909
|
];
|
|
812
910
|
return;
|
|
813
911
|
}
|
|
@@ -861,7 +959,15 @@ exports.openRouterTransformer = {
|
|
|
861
959
|
// and Anthropic and OpenAI models reject a tool-using turn whose
|
|
862
960
|
// signature did not come back. Only set the key when there is one, so
|
|
863
961
|
// requests for non-reasoning models stay byte-identical.
|
|
864
|
-
|
|
962
|
+
// Only OpenRouter's own blocks. A history shared with an OpenAI agent
|
|
963
|
+
// also carries Responses-API `reasoning` items in this slot, and they
|
|
964
|
+
// are not OpenRouter's to interpret — it returned 200 rather than an
|
|
965
|
+
// error when one was sent (probed live 2026-09-10, upstream
|
|
966
|
+
// `anthropic/claude-sonnet-4.5`), but forwarding another provider's
|
|
967
|
+
// opaque payload can only confuse the model or the upstream route.
|
|
968
|
+
const reasoningDetails = thinkingBlocks
|
|
969
|
+
.filter((block) => (0, types_1.reasoningDetailsFormatOf)(block) === "openrouter")
|
|
970
|
+
.flatMap((block) => block.reasoningDetails ?? []);
|
|
865
971
|
if (reasoningDetails.length > 0) {
|
|
866
972
|
msg.reasoningDetails = reasoningDetails;
|
|
867
973
|
}
|
|
@@ -934,7 +1040,7 @@ exports.openRouterTransformer = {
|
|
|
934
1040
|
const reasoningText = typeof message.reasoning === "string" ? message.reasoning : "";
|
|
935
1041
|
const reasoningDetails = message.reasoningDetails ?? [];
|
|
936
1042
|
if (reasoningText || reasoningDetails.length > 0) {
|
|
937
|
-
content.push((0, types_1.thinking)(reasoningText, undefined, undefined, reasoningDetails));
|
|
1043
|
+
content.push((0, types_1.thinking)(reasoningText, undefined, undefined, reasoningDetails, "openrouter"));
|
|
938
1044
|
}
|
|
939
1045
|
if (typeof message.content === "string" && message.content) {
|
|
940
1046
|
content.push((0, types_1.text)(message.content));
|
package/dist/history/types.d.ts
CHANGED
|
@@ -62,9 +62,32 @@ export type ThinkingContent = {
|
|
|
62
62
|
*
|
|
63
63
|
* Nothing here reads the contents; they only have to survive the round trip,
|
|
64
64
|
* so they stay untyped rather than modelling every provider's block shapes.
|
|
65
|
+
*
|
|
66
|
+
* The shapes are *not* interchangeable — see
|
|
67
|
+
* {@link ThinkingContent.reasoningDetailsFormat}.
|
|
65
68
|
*/
|
|
66
69
|
reasoningDetails?: unknown[];
|
|
70
|
+
/**
|
|
71
|
+
* Which provider's format {@link ThinkingContent.reasoningDetails} is in.
|
|
72
|
+
*
|
|
73
|
+
* Two producers share that slot and their shapes are mutually invalid:
|
|
74
|
+
* OpenRouter's entries are tagged `reasoning.text` / `reasoning.summary` /
|
|
75
|
+
* `reasoning.encrypted`, while the OpenAI Responses API's are whole
|
|
76
|
+
* `reasoning` items. A history shared between agents therefore has to say
|
|
77
|
+
* which is which, or each transformer forwards the other's blocks and the
|
|
78
|
+
* provider rejects the request.
|
|
79
|
+
*
|
|
80
|
+
* Absent on blocks written before this field existed, which are OpenRouter's
|
|
81
|
+
* by definition — it was the only producer then. Read it through
|
|
82
|
+
* {@link reasoningDetailsFormatOf} rather than directly, so that default
|
|
83
|
+
* stays in one place.
|
|
84
|
+
*/
|
|
85
|
+
reasoningDetailsFormat?: ReasoningDetailsFormat;
|
|
67
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* Provider formats that {@link ThinkingContent.reasoningDetails} can hold.
|
|
89
|
+
*/
|
|
90
|
+
export type ReasoningDetailsFormat = "openrouter" | "openai.responses";
|
|
68
91
|
/**
|
|
69
92
|
* Supported image MIME types across all providers
|
|
70
93
|
*/
|
|
@@ -207,7 +230,14 @@ export declare function toolUse(id: string, name: string, input: Record<string,
|
|
|
207
230
|
/**
|
|
208
231
|
* Create a thinking content block. Pass `redactedData` for redacted thinking.
|
|
209
232
|
*/
|
|
210
|
-
export declare function thinking(thinkingText: string, signature?: string, redactedData?: string, reasoningDetails?: unknown[]): ThinkingContent;
|
|
233
|
+
export declare function thinking(thinkingText: string, signature?: string, redactedData?: string, reasoningDetails?: unknown[], reasoningDetailsFormat?: ReasoningDetailsFormat): ThinkingContent;
|
|
234
|
+
/**
|
|
235
|
+
* The format of a block's {@link ThinkingContent.reasoningDetails}.
|
|
236
|
+
*
|
|
237
|
+
* Untagged blocks are OpenRouter's: it was the only producer before the tag
|
|
238
|
+
* existed, so that is what an untagged block can only have come from.
|
|
239
|
+
*/
|
|
240
|
+
export declare function reasoningDetailsFormatOf(block: ThinkingContent): ReasoningDetailsFormat;
|
|
211
241
|
/**
|
|
212
242
|
* Create a tool result content block
|
|
213
243
|
*/
|
package/dist/history/types.js
CHANGED
|
@@ -16,6 +16,7 @@ exports.isImageContent = isImageContent;
|
|
|
16
16
|
exports.text = text;
|
|
17
17
|
exports.toolUse = toolUse;
|
|
18
18
|
exports.thinking = thinking;
|
|
19
|
+
exports.reasoningDetailsFormatOf = reasoningDetailsFormatOf;
|
|
19
20
|
exports.toolResult = toolResult;
|
|
20
21
|
exports.textMessage = textMessage;
|
|
21
22
|
exports.imageUrl = imageUrl;
|
|
@@ -70,20 +71,29 @@ function toolUse(id, name, input, thoughtSignature) {
|
|
|
70
71
|
/**
|
|
71
72
|
* Create a thinking content block. Pass `redactedData` for redacted thinking.
|
|
72
73
|
*/
|
|
73
|
-
function thinking(thinkingText, signature, redactedData, reasoningDetails) {
|
|
74
|
+
function thinking(thinkingText, signature, redactedData, reasoningDetails, reasoningDetailsFormat = "openrouter") {
|
|
74
75
|
// As in `toolUse()`, only set the passthrough key when there is something in
|
|
75
76
|
// it, so a block stored without details serializes exactly as it did before
|
|
76
|
-
// the field existed.
|
|
77
|
+
// the field existed. The format tag rides along with the details for the same
|
|
78
|
+
// reason: it says nothing on its own.
|
|
79
|
+
const hasDetails = reasoningDetails !== undefined && reasoningDetails.length > 0;
|
|
77
80
|
return {
|
|
78
81
|
type: "thinking",
|
|
79
82
|
thinking: thinkingText,
|
|
80
83
|
signature,
|
|
81
84
|
redactedData,
|
|
82
|
-
...(
|
|
83
|
-
? { reasoningDetails }
|
|
84
|
-
: {}),
|
|
85
|
+
...(hasDetails ? { reasoningDetails, reasoningDetailsFormat } : {}),
|
|
85
86
|
};
|
|
86
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* The format of a block's {@link ThinkingContent.reasoningDetails}.
|
|
90
|
+
*
|
|
91
|
+
* Untagged blocks are OpenRouter's: it was the only producer before the tag
|
|
92
|
+
* existed, so that is what an untagged block can only have come from.
|
|
93
|
+
*/
|
|
94
|
+
function reasoningDetailsFormatOf(block) {
|
|
95
|
+
return block.reasoningDetailsFormat ?? "openrouter";
|
|
96
|
+
}
|
|
87
97
|
/**
|
|
88
98
|
* Create a tool result content block
|
|
89
99
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentionai/agents",
|
|
3
3
|
"author": "Laurent Zuijdwijk",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.15.0",
|
|
5
5
|
"description": "Agent Library",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"jsdoc": "^4.0.4",
|
|
133
133
|
"jsdoc-to-markdown": "^9.1.1",
|
|
134
134
|
"nodemon": "^2.0.22",
|
|
135
|
-
"openai": "^
|
|
135
|
+
"openai": "^7.13.0",
|
|
136
136
|
"prettier": "^2.8.7",
|
|
137
137
|
"rimraf": "^4.4.1",
|
|
138
138
|
"ts-jest": "^29.2.6",
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
"@openrouter/sdk": "^1.2.106",
|
|
154
154
|
"apache-arrow": "^18.0.0",
|
|
155
155
|
"ollama": "^0.5.18",
|
|
156
|
-
"openai": "^
|
|
156
|
+
"openai": "^7.13.0",
|
|
157
157
|
"voyageai": "^0.0.3"
|
|
158
158
|
},
|
|
159
159
|
"peerDependenciesMeta": {
|