maestro-agent-sdk 0.1.26 → 0.1.27
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/README.md +20 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/platform/config.d.ts +5 -0
- package/dist/platform/config.d.ts.map +1 -1
- package/dist/platform/config.js +9 -0
- package/dist/platform/config.js.map +1 -1
- package/dist/platform/version.d.ts +1 -1
- package/dist/platform/version.js +1 -1
- package/dist/provider.d.ts +16 -4
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +39 -4
- package/dist/provider.js.map +1 -1
- package/dist/providers/codex-auth.d.ts +149 -0
- package/dist/providers/codex-auth.d.ts.map +1 -0
- package/dist/providers/codex-auth.js +312 -0
- package/dist/providers/codex-auth.js.map +1 -0
- package/dist/providers/codex-stream.d.ts +42 -0
- package/dist/providers/codex-stream.d.ts.map +1 -0
- package/dist/providers/codex-stream.js +330 -0
- package/dist/providers/codex-stream.js.map +1 -0
- package/dist/providers/codex-translators.d.ts +105 -0
- package/dist/providers/codex-translators.d.ts.map +1 -0
- package/dist/providers/codex-translators.js +244 -0
- package/dist/providers/codex-translators.js.map +1 -0
- package/dist/providers/codex.d.ts +117 -0
- package/dist/providers/codex.d.ts.map +1 -0
- package/dist/providers/codex.js +334 -0
- package/dist/providers/codex.js.map +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +25 -1
- package/dist/registry.js.map +1 -1
- package/package.json +17 -1
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex OAuth loader + refresh.
|
|
3
|
+
*
|
|
4
|
+
* Reads `~/.codex/auth.json` (the file the upstream `codex` CLI maintains) and
|
|
5
|
+
* surfaces the access token in the shape the Codex Responses provider needs.
|
|
6
|
+
* Handles JWT-claim extraction for the `ChatGPT-Account-ID` Cloudflare header
|
|
7
|
+
* and refreshes the access token against `https://auth.openai.com/oauth/token`
|
|
8
|
+
* when it is past the configured skew threshold.
|
|
9
|
+
*
|
|
10
|
+
* Why we read codex's own file rather than running our own OAuth flow:
|
|
11
|
+
* - The user already authenticated via `codex login`. Forcing a second
|
|
12
|
+
* OAuth dance just to use the same backend would be hostile UX.
|
|
13
|
+
* - codex CLI rotates refresh tokens periodically and writes them back to
|
|
14
|
+
* `auth.json`. We write the rotated refresh token back to the same file
|
|
15
|
+
* so both clients stay in sync — single source of truth.
|
|
16
|
+
*
|
|
17
|
+
* Hermes reference: `hermes_cli/auth.py::refresh_codex_oauth_pure` and
|
|
18
|
+
* `agent/auxiliary_client.py::_codex_cloudflare_headers`. Behavioral parity
|
|
19
|
+
* with those two functions is the bar — diverge only when a TS-specific
|
|
20
|
+
* concern (no httpx, no JWT lib) forces it.
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
23
|
+
import { homedir } from "node:os";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
/**
|
|
26
|
+
* OAuth `client_id` the upstream codex CLI registered with OpenAI's OAuth
|
|
27
|
+
* server. Pinned because the refresh endpoint validates it; rotating without
|
|
28
|
+
* coordinating with codex CLI would break tokens for both clients.
|
|
29
|
+
*/
|
|
30
|
+
const CODEX_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
31
|
+
const CODEX_OAUTH_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
32
|
+
/** Refresh when the access token has < this many seconds left. The hermes
|
|
33
|
+
* default is 5 minutes; we match so a long-running maestro turn doesn't
|
|
34
|
+
* pass through the expiry boundary mid-stream. */
|
|
35
|
+
const DEFAULT_REFRESH_SKEW_SECONDS = 300;
|
|
36
|
+
/** Path to `auth.json`. Honors `CODEX_HOME` env var for parity with
|
|
37
|
+
* upstream codex CLI; falls back to `~/.codex/auth.json`. */
|
|
38
|
+
export function codexAuthPath() {
|
|
39
|
+
const home = process.env.CODEX_HOME?.trim();
|
|
40
|
+
const root = home && home.length > 0 ? home : join(homedir(), ".codex");
|
|
41
|
+
return join(root, "auth.json");
|
|
42
|
+
}
|
|
43
|
+
/** Read the auth file. Throws a structured error if the file is missing or
|
|
44
|
+
* malformed — callers can map this to a "run `codex login`" message. */
|
|
45
|
+
export function readCodexAuth(path) {
|
|
46
|
+
const p = path ?? codexAuthPath();
|
|
47
|
+
if (!existsSync(p)) {
|
|
48
|
+
throw new CodexAuthError(`Codex auth file not found at ${p}. Run \`codex login\` first.`, "codex_auth_missing", true);
|
|
49
|
+
}
|
|
50
|
+
let parsed;
|
|
51
|
+
try {
|
|
52
|
+
parsed = JSON.parse(readFileSync(p, "utf8"));
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
throw new CodexAuthError(`Codex auth file at ${p} is not valid JSON: ${e instanceof Error ? e.message : String(e)}`, "codex_auth_invalid_json", true);
|
|
56
|
+
}
|
|
57
|
+
if (parsed.auth_mode !== "chatgpt") {
|
|
58
|
+
throw new CodexAuthError(`Codex auth_mode is "${parsed.auth_mode}", expected "chatgpt". Use a regular OpenAI provider for API-key auth.`, "codex_auth_wrong_mode", false);
|
|
59
|
+
}
|
|
60
|
+
if (!parsed.tokens?.access_token || !parsed.tokens?.refresh_token) {
|
|
61
|
+
throw new CodexAuthError(`Codex auth file at ${p} is missing access_token or refresh_token. Run \`codex login\` again.`, "codex_auth_missing_tokens", true);
|
|
62
|
+
}
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Decode a JWT access token (no signature verification — we only need the
|
|
67
|
+
* `chatgpt_account_id` claim for an HTTP header). Returns `undefined` if the
|
|
68
|
+
* token isn't a parseable JWT; callers must tolerate that case and fall back
|
|
69
|
+
* to omitting the `ChatGPT-Account-ID` header (the request still succeeds for
|
|
70
|
+
* most account types — only Cloudflare-edge mitigation rules require it).
|
|
71
|
+
*/
|
|
72
|
+
export function decodeJwtClaims(accessToken) {
|
|
73
|
+
try {
|
|
74
|
+
const parts = accessToken.split(".");
|
|
75
|
+
if (parts.length < 2)
|
|
76
|
+
return undefined;
|
|
77
|
+
const payload = parts[1];
|
|
78
|
+
// base64url → base64 + padding
|
|
79
|
+
const b64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
80
|
+
const padded = b64.padEnd(b64.length + ((4 - (b64.length % 4)) % 4), "=");
|
|
81
|
+
const json = Buffer.from(padded, "base64").toString("utf8");
|
|
82
|
+
return JSON.parse(json);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Pull the `chatgpt_account_id` value out of a Codex JWT. The claim path
|
|
90
|
+
* (`https://api.openai.com/auth.chatgpt_account_id`) is what hermes pins
|
|
91
|
+
* against codex-rs `auth.rs`; we copy it verbatim.
|
|
92
|
+
*/
|
|
93
|
+
export function extractAccountId(accessToken) {
|
|
94
|
+
const claims = decodeJwtClaims(accessToken);
|
|
95
|
+
if (!claims)
|
|
96
|
+
return undefined;
|
|
97
|
+
const authClaim = claims["https://api.openai.com/auth"];
|
|
98
|
+
if (!authClaim || typeof authClaim !== "object")
|
|
99
|
+
return undefined;
|
|
100
|
+
const acct = authClaim.chatgpt_account_id;
|
|
101
|
+
return typeof acct === "string" && acct.length > 0 ? acct : undefined;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Return the access-token `exp` claim as an absolute epoch-seconds value, or
|
|
105
|
+
* `undefined` if not present / unparseable. Used by `accessTokenIsExpiring`.
|
|
106
|
+
*/
|
|
107
|
+
export function accessTokenExpiresAt(accessToken) {
|
|
108
|
+
const claims = decodeJwtClaims(accessToken);
|
|
109
|
+
if (!claims)
|
|
110
|
+
return undefined;
|
|
111
|
+
const exp = claims.exp;
|
|
112
|
+
return typeof exp === "number" && Number.isFinite(exp) ? exp : undefined;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* `true` when the access token is within `skewSeconds` of its `exp` claim
|
|
116
|
+
* (or already expired). Tokens with no `exp` claim are conservatively treated
|
|
117
|
+
* as "always refresh" — better to round-trip the refresh endpoint than to
|
|
118
|
+
* leak a stale token into a long-running stream.
|
|
119
|
+
*/
|
|
120
|
+
export function accessTokenIsExpiring(accessToken, skewSeconds = DEFAULT_REFRESH_SKEW_SECONDS) {
|
|
121
|
+
const exp = accessTokenExpiresAt(accessToken);
|
|
122
|
+
if (exp === undefined)
|
|
123
|
+
return true;
|
|
124
|
+
const now = Math.floor(Date.now() / 1000);
|
|
125
|
+
return exp - now <= skewSeconds;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Required Cloudflare-bypass headers for `chatgpt.com/backend-api/codex`.
|
|
129
|
+
*
|
|
130
|
+
* The edge in front of the Codex endpoint allowlists requests whose
|
|
131
|
+
* `originator` is one of `codex_cli_rs`, `codex_vscode`, `codex_sdk_ts`, or
|
|
132
|
+
* anything starting with `Codex`. Non-residential IPs without an allowlisted
|
|
133
|
+
* originator get a 403 with `cf-mitigated: challenge` regardless of auth
|
|
134
|
+
* correctness — meaning the request never reaches the application layer and
|
|
135
|
+
* the bug looks like an auth problem when it isn't.
|
|
136
|
+
*
|
|
137
|
+
* We pin `originator: codex_cli_rs` (the upstream codex-rs CLI value) and
|
|
138
|
+
* shape the User-Agent to match codex-rs's fingerprint. The
|
|
139
|
+
* `ChatGPT-Account-ID` header is conditional — most accounts don't need it,
|
|
140
|
+
* but Enterprise / Team accounts will 403 without it.
|
|
141
|
+
*/
|
|
142
|
+
export function cloudflareHeaders(accessToken) {
|
|
143
|
+
const h = {
|
|
144
|
+
Authorization: `Bearer ${accessToken}`,
|
|
145
|
+
"User-Agent": "codex_cli_rs/0.0.0 (maestro-agent-sdk)",
|
|
146
|
+
originator: "codex_cli_rs",
|
|
147
|
+
};
|
|
148
|
+
const acct = extractAccountId(accessToken);
|
|
149
|
+
if (acct)
|
|
150
|
+
h["ChatGPT-Account-ID"] = acct;
|
|
151
|
+
return h;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Error type for all OAuth-related failures. `reloginRequired` indicates the
|
|
155
|
+
* user must run `codex login` again — the refresh-token chain is unrecoverable.
|
|
156
|
+
* Other failures (network, 5xx) are retryable in-process.
|
|
157
|
+
*/
|
|
158
|
+
export class CodexAuthError extends Error {
|
|
159
|
+
code;
|
|
160
|
+
reloginRequired;
|
|
161
|
+
constructor(message, code, reloginRequired) {
|
|
162
|
+
super(message);
|
|
163
|
+
this.code = code;
|
|
164
|
+
this.reloginRequired = reloginRequired;
|
|
165
|
+
this.name = "CodexAuthError";
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Refresh the access token using the stored refresh token. Returns the new
|
|
170
|
+
* tokens (access + possibly rotated refresh) without touching the on-disk
|
|
171
|
+
* file. Callers that want to persist call `writeRefreshedTokens()` below.
|
|
172
|
+
*
|
|
173
|
+
* Behavior mirrors hermes's `refresh_codex_oauth_pure` — same client_id,
|
|
174
|
+
* same form-encoded body, same error-mapping rules. The notable difference is
|
|
175
|
+
* that we leave timeouts to the platform `fetch` (Node 22+ supports
|
|
176
|
+
* `signal: AbortSignal.timeout(ms)`); we don't pull in a `httpx` analog.
|
|
177
|
+
*/
|
|
178
|
+
export async function refreshAccessToken(refreshToken, timeoutMs = 20_000) {
|
|
179
|
+
if (!refreshToken || refreshToken.length === 0) {
|
|
180
|
+
throw new CodexAuthError("Cannot refresh — refresh_token is empty. Run `codex login` again.", "codex_auth_missing_refresh_token", true);
|
|
181
|
+
}
|
|
182
|
+
const body = new URLSearchParams({
|
|
183
|
+
grant_type: "refresh_token",
|
|
184
|
+
refresh_token: refreshToken,
|
|
185
|
+
client_id: CODEX_OAUTH_CLIENT_ID,
|
|
186
|
+
});
|
|
187
|
+
let response;
|
|
188
|
+
try {
|
|
189
|
+
response = await fetch(CODEX_OAUTH_TOKEN_URL, {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: {
|
|
192
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
193
|
+
Accept: "application/json",
|
|
194
|
+
},
|
|
195
|
+
body,
|
|
196
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
throw new CodexAuthError(`Codex token refresh network error: ${e instanceof Error ? e.message : String(e)}`, "codex_refresh_network", false);
|
|
201
|
+
}
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
// Parse the error body to decide whether the user needs to re-login.
|
|
204
|
+
// OpenAI returns two distinct shapes here — OAuth spec ({error,error_description})
|
|
205
|
+
// and OpenAI's own ({error:{code,message,type}}) — we accept either.
|
|
206
|
+
let code = "codex_refresh_failed";
|
|
207
|
+
let message = `Codex token refresh failed (HTTP ${response.status})`;
|
|
208
|
+
try {
|
|
209
|
+
const err = (await response.json());
|
|
210
|
+
const errObj = err.error;
|
|
211
|
+
if (typeof errObj === "string" && errObj.length > 0) {
|
|
212
|
+
code = errObj;
|
|
213
|
+
const desc = err.error_description ?? err.message;
|
|
214
|
+
if (typeof desc === "string" && desc.length > 0)
|
|
215
|
+
message = `Codex token refresh: ${desc}`;
|
|
216
|
+
}
|
|
217
|
+
else if (errObj && typeof errObj === "object") {
|
|
218
|
+
const nested = errObj;
|
|
219
|
+
const nestedCode = nested.code ?? nested.type;
|
|
220
|
+
if (typeof nestedCode === "string" && nestedCode.length > 0)
|
|
221
|
+
code = nestedCode;
|
|
222
|
+
const nestedMsg = nested.message;
|
|
223
|
+
if (typeof nestedMsg === "string" && nestedMsg.length > 0)
|
|
224
|
+
message = `Codex token refresh: ${nestedMsg}`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Non-JSON body — keep the default message.
|
|
229
|
+
}
|
|
230
|
+
const reloginRequired = code === "invalid_grant" ||
|
|
231
|
+
code === "invalid_token" ||
|
|
232
|
+
code === "invalid_request" ||
|
|
233
|
+
code === "refresh_token_reused" ||
|
|
234
|
+
response.status === 401 ||
|
|
235
|
+
response.status === 403;
|
|
236
|
+
throw new CodexAuthError(message, code, reloginRequired);
|
|
237
|
+
}
|
|
238
|
+
let payload;
|
|
239
|
+
try {
|
|
240
|
+
payload = (await response.json());
|
|
241
|
+
}
|
|
242
|
+
catch (e) {
|
|
243
|
+
throw new CodexAuthError(`Codex token refresh returned non-JSON body: ${e instanceof Error ? e.message : String(e)}`, "codex_refresh_invalid_json", true);
|
|
244
|
+
}
|
|
245
|
+
const newAccess = payload.access_token;
|
|
246
|
+
if (typeof newAccess !== "string" || newAccess.length === 0) {
|
|
247
|
+
throw new CodexAuthError("Codex token refresh response was missing access_token.", "codex_refresh_missing_access_token", true);
|
|
248
|
+
}
|
|
249
|
+
// Rotate the refresh token only when the server returned a fresh one.
|
|
250
|
+
// OAuth servers vary — some always rotate, some only on certain conditions.
|
|
251
|
+
// Keeping the old one when the server omits it matches OAuth 2.0 spec §6.
|
|
252
|
+
const newRefresh = typeof payload.refresh_token === "string" && payload.refresh_token.length > 0
|
|
253
|
+
? payload.refresh_token
|
|
254
|
+
: refreshToken;
|
|
255
|
+
return { access_token: newAccess, refresh_token: newRefresh };
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Persist a freshly refreshed token pair back to `~/.codex/auth.json` while
|
|
259
|
+
* preserving the other fields (auth_mode, OPENAI_API_KEY, account_id,
|
|
260
|
+
* id_token). Updates `last_refresh` to "now". Best-effort — if the write
|
|
261
|
+
* fails the in-process token is still usable for the lifetime of the
|
|
262
|
+
* process; we just won't share it with codex CLI.
|
|
263
|
+
*
|
|
264
|
+
* Note: this is racy with codex CLI running in parallel. The upstream
|
|
265
|
+
* codex-rs implementation uses an advisory file lock; we don't bother
|
|
266
|
+
* because typical maestro usage doesn't overlap with `codex` invocations
|
|
267
|
+
* on the same machine. If lock contention becomes a real problem, lift
|
|
268
|
+
* the lockfile pattern from hermes's `_auth_store_lock`.
|
|
269
|
+
*/
|
|
270
|
+
export function writeRefreshedTokens(newTokens, path) {
|
|
271
|
+
const p = path ?? codexAuthPath();
|
|
272
|
+
const current = readCodexAuth(p);
|
|
273
|
+
const updated = {
|
|
274
|
+
...current,
|
|
275
|
+
last_refresh: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
276
|
+
tokens: {
|
|
277
|
+
...current.tokens,
|
|
278
|
+
access_token: newTokens.access_token,
|
|
279
|
+
refresh_token: newTokens.refresh_token,
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
writeFileSync(p, `${JSON.stringify(updated, null, 2)}\n`, { mode: 0o600 });
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* The top-level helper a Provider uses on every turn. Reads the on-disk
|
|
286
|
+
* tokens; refreshes if expiring; writes the refreshed pair back to disk;
|
|
287
|
+
* returns the live access token. Idempotent under concurrent calls within
|
|
288
|
+
* one process — repeated calls in the same expiry window just hit the
|
|
289
|
+
* `isExpiring` short-circuit. Cross-process concurrency is best-effort
|
|
290
|
+
* (see `writeRefreshedTokens`).
|
|
291
|
+
*
|
|
292
|
+
* `forceRefresh` is exposed for tests and for hosts that want to recover
|
|
293
|
+
* from a 401 mid-stream by minting a fresh token and retrying once.
|
|
294
|
+
*/
|
|
295
|
+
export async function resolveAccessToken(opts) {
|
|
296
|
+
const skew = opts?.skewSeconds ?? DEFAULT_REFRESH_SKEW_SECONDS;
|
|
297
|
+
const auth = readCodexAuth();
|
|
298
|
+
if (!opts?.forceRefresh && !accessTokenIsExpiring(auth.tokens.access_token, skew)) {
|
|
299
|
+
return auth.tokens.access_token;
|
|
300
|
+
}
|
|
301
|
+
const refreshed = await refreshAccessToken(auth.tokens.refresh_token, opts?.timeoutMs);
|
|
302
|
+
try {
|
|
303
|
+
writeRefreshedTokens(refreshed);
|
|
304
|
+
}
|
|
305
|
+
catch (e) {
|
|
306
|
+
// Disk write failed but the in-memory token is still good — log via
|
|
307
|
+
// console.warn rather than crashing the agent.
|
|
308
|
+
console.warn(`[codex-auth] Could not persist refreshed tokens: ${e instanceof Error ? e.message : String(e)}`);
|
|
309
|
+
}
|
|
310
|
+
return refreshed.access_token;
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=codex-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-auth.js","sourceRoot":"","sources":["../../src/providers/codex-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex Responses API SSE stream → Maestro `ProviderStreamChunk` adapter.
|
|
3
|
+
*
|
|
4
|
+
* The Responses API SSE protocol is event-typed (each event has a `type`
|
|
5
|
+
* field plus a `data` JSON payload). The events of interest:
|
|
6
|
+
*
|
|
7
|
+
* - `response.output_item.added` — a new top-level output item began
|
|
8
|
+
* (message | function_call | reasoning). Use to learn the item's
|
|
9
|
+
* `id` / `name` / kind before deltas start arriving.
|
|
10
|
+
* - `response.output_text.delta` — text chunk for the current message item.
|
|
11
|
+
* - `response.function_call_arguments.delta` — JSON-args chunk for the
|
|
12
|
+
* current function_call item.
|
|
13
|
+
* - `response.function_call_arguments.done` — args finalized.
|
|
14
|
+
* - `response.output_item.done` — the item is closed; carries the full
|
|
15
|
+
* completed item (with final args, content, status).
|
|
16
|
+
* - `response.reasoning_summary_text.delta` / `.done` — reasoning summary
|
|
17
|
+
* stream (only when `reasoning.summary` was enabled).
|
|
18
|
+
* - `response.completed` — terminal. Carries `response.usage` and the
|
|
19
|
+
* full output array.
|
|
20
|
+
* - `response.incomplete` / `response.failed` — terminal failures.
|
|
21
|
+
*
|
|
22
|
+
* The codex backend has a known bug where `get_final_response()` returns an
|
|
23
|
+
* empty `output` even after valid items streamed — hermes patches this with
|
|
24
|
+
* "synthesize from deltas" backfill. We do the same here: keep a parallel
|
|
25
|
+
* accumulator and use it as the source of truth for `message_complete`,
|
|
26
|
+
* ignoring the `response.completed.response.output` array.
|
|
27
|
+
*
|
|
28
|
+
* The output is the same `ProviderStreamChunk` vocabulary the agent loop
|
|
29
|
+
* already consumes for Anthropic / DeepSeek, so adding Codex requires no
|
|
30
|
+
* changes to `core/loop.ts`.
|
|
31
|
+
*/
|
|
32
|
+
import type { ProviderStreamChunk } from "../providers/base.js";
|
|
33
|
+
/**
|
|
34
|
+
* Convert a Responses API SSE stream (the raw `Response.body` from `fetch`)
|
|
35
|
+
* into the maestro `ProviderStreamChunk` async iterable.
|
|
36
|
+
*
|
|
37
|
+
* Aborts when `abortSignal` fires by cancelling the underlying reader; the
|
|
38
|
+
* generator yields nothing further and any in-progress items become stale
|
|
39
|
+
* (the agent loop will reconcile on its end via the abort flag).
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseCodexStream(body: ReadableStream<Uint8Array>, abortSignal?: AbortSignal): AsyncGenerator<ProviderStreamChunk>;
|
|
42
|
+
//# sourceMappingURL=codex-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-stream.d.ts","sourceRoot":"","sources":["../../src/providers/codex-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAwB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AA2ClF;;;;;;;GAOG;AACH,wBAAuB,gBAAgB,CACrC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,WAAW,CAAC,EAAE,WAAW,GACxB,cAAc,CAAC,mBAAmB,CAAC,CA6MrC"}
|