maestro-agent-sdk 0.1.26 → 0.1.28
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/core/agent.d.ts +28 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +2 -0
- package/dist/core/agent.js.map +1 -1
- package/dist/core/is-abort-error.d.ts +18 -0
- package/dist/core/is-abort-error.d.ts.map +1 -1
- package/dist/core/is-abort-error.js +34 -0
- package/dist/core/is-abort-error.js.map +1 -1
- package/dist/core/loop.d.ts.map +1 -1
- package/dist/core/loop.js +69 -14
- package/dist/core/loop.js.map +1 -1
- package/dist/core/tool-result-truncation.d.ts +34 -0
- package/dist/core/tool-result-truncation.d.ts.map +1 -0
- package/dist/core/tool-result-truncation.js +162 -0
- package/dist/core/tool-result-truncation.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/memory/active-task-template.d.ts +7 -4
- package/dist/memory/active-task-template.d.ts.map +1 -1
- package/dist/memory/active-task-template.js +19 -4
- package/dist/memory/active-task-template.js.map +1 -1
- package/dist/memory/aux-model-map.d.ts +49 -0
- package/dist/memory/aux-model-map.d.ts.map +1 -0
- package/dist/memory/aux-model-map.js +103 -0
- package/dist/memory/aux-model-map.js.map +1 -0
- package/dist/memory/compressor.d.ts +34 -59
- package/dist/memory/compressor.d.ts.map +1 -1
- package/dist/memory/compressor.js +222 -89
- package/dist/memory/compressor.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 +18 -6
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +71 -8
- 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 +332 -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 +145 -0
- package/dist/providers/codex.d.ts.map +1 -0
- package/dist/providers/codex.js +417 -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/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +17 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maestro ↔ Codex Responses API format translators.
|
|
3
|
+
*
|
|
4
|
+
* The Responses API is OpenAI's newer thread-style endpoint. It speaks a
|
|
5
|
+
* different shape than Chat Completions:
|
|
6
|
+
*
|
|
7
|
+
* - Messages aren't a `messages` array — they're "input items" with
|
|
8
|
+
* polymorphic types (`message`, `function_call`, `function_call_output`,
|
|
9
|
+
* `reasoning`).
|
|
10
|
+
* - Tools live as top-level items, not inside an assistant message.
|
|
11
|
+
* - Text parts split by direction: user → `input_text`, assistant →
|
|
12
|
+
* `output_text`. Mixing them yields 400.
|
|
13
|
+
* - There is no `system` role; the instructions ride in a top-level
|
|
14
|
+
* `instructions` field on the request body.
|
|
15
|
+
*
|
|
16
|
+
* This module owns those translations and nothing else — the provider class
|
|
17
|
+
* stays focused on HTTP / streaming concerns.
|
|
18
|
+
*
|
|
19
|
+
* Hermes reference: `agent/codex_responses_adapter.py::_chat_messages_to_responses_input`
|
|
20
|
+
* and `_responses_tools`. We diverge from hermes in two places:
|
|
21
|
+
*
|
|
22
|
+
* 1. We start from Maestro's Anthropic-shaped `ProviderContentBlock[]`, not
|
|
23
|
+
* from OpenAI Chat Completions messages. Hermes does this conversion in
|
|
24
|
+
* two hops (Anthropic → ChatCompletions → Responses); we do it in one to
|
|
25
|
+
* keep the data path shorter and the type signatures honest.
|
|
26
|
+
*
|
|
27
|
+
* 2. We don't replay `codex_reasoning_items` / `codex_message_items` from
|
|
28
|
+
* prior turns yet. Reasoning replay (with `encrypted_content`) is what
|
|
29
|
+
* lets the Responses API maintain a coherent reasoning chain across
|
|
30
|
+
* turns — but maestro's loop currently drops the encrypted blobs after
|
|
31
|
+
* each turn, so there's nothing to replay. A future PR can extend
|
|
32
|
+
* `ProviderContentBlock` with a `codex_encrypted_reasoning` variant and
|
|
33
|
+
* wire the replay here.
|
|
34
|
+
*/
|
|
35
|
+
export function translateToolsToResponses(tools) {
|
|
36
|
+
if (!tools || tools.length === 0)
|
|
37
|
+
return undefined;
|
|
38
|
+
const out = [];
|
|
39
|
+
for (const t of tools) {
|
|
40
|
+
if (!t?.name || t.name.length === 0)
|
|
41
|
+
continue;
|
|
42
|
+
out.push({
|
|
43
|
+
type: "function",
|
|
44
|
+
name: t.name,
|
|
45
|
+
description: t.description ?? "",
|
|
46
|
+
strict: false,
|
|
47
|
+
parameters: t.input_schema ?? {
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: {},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return out.length > 0 ? out : undefined;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Convert one Maestro message into one-or-more Responses input items.
|
|
57
|
+
*
|
|
58
|
+
* Why one message can produce multiple items:
|
|
59
|
+
* - An assistant message with `text` + `tool_use` emits a `message` item AND
|
|
60
|
+
* one `function_call` per tool_use, in that exact order.
|
|
61
|
+
* - A user message with `tool_result` blocks emits a separate
|
|
62
|
+
* `function_call_output` per result. Text/image blocks accompanying the
|
|
63
|
+
* tool_results are flushed as their own `message` item before each result
|
|
64
|
+
* so call-result ordering with surrounding text is preserved (matches
|
|
65
|
+
* DeepSeek/Anthropic intent).
|
|
66
|
+
*/
|
|
67
|
+
export function translateMessageToResponsesItems(msg) {
|
|
68
|
+
// String-content fast path covers DeepSeek-style plain text replays.
|
|
69
|
+
if (typeof msg.content === "string") {
|
|
70
|
+
return [
|
|
71
|
+
{
|
|
72
|
+
type: "message",
|
|
73
|
+
role: msg.role,
|
|
74
|
+
content: msg.content,
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
}
|
|
78
|
+
if (msg.role === "user") {
|
|
79
|
+
return translateUserBlocks(msg.content);
|
|
80
|
+
}
|
|
81
|
+
return translateAssistantBlocks(msg.content);
|
|
82
|
+
}
|
|
83
|
+
function translateUserBlocks(blocks) {
|
|
84
|
+
const out = [];
|
|
85
|
+
let buffered = [];
|
|
86
|
+
const flush = () => {
|
|
87
|
+
if (buffered.length === 0)
|
|
88
|
+
return;
|
|
89
|
+
out.push({
|
|
90
|
+
type: "message",
|
|
91
|
+
role: "user",
|
|
92
|
+
content: condenseUserParts(buffered),
|
|
93
|
+
});
|
|
94
|
+
buffered = [];
|
|
95
|
+
};
|
|
96
|
+
for (const block of blocks) {
|
|
97
|
+
if (block.type === "text") {
|
|
98
|
+
if (block.text.length > 0)
|
|
99
|
+
buffered.push({ type: "input_text", text: block.text });
|
|
100
|
+
}
|
|
101
|
+
else if (block.type === "image") {
|
|
102
|
+
const url = imageBlockToDataUrl(block.source);
|
|
103
|
+
if (url)
|
|
104
|
+
buffered.push({ type: "input_image", image_url: url });
|
|
105
|
+
}
|
|
106
|
+
else if (block.type === "document") {
|
|
107
|
+
// Responses API doesn't accept PDF blocks directly at user message
|
|
108
|
+
// level — placeholder so the model knows the attachment existed.
|
|
109
|
+
// Caller should pre-extract text (e.g. via Read tool) for real content.
|
|
110
|
+
buffered.push({
|
|
111
|
+
type: "input_text",
|
|
112
|
+
text: `[document ${block.source.media_type}; PDF not visible to GPT-5 directly — extract text first.]`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else if (block.type === "tool_result") {
|
|
116
|
+
// tool_result MUST be its own item; flush any accumulated user content
|
|
117
|
+
// first so ordering with surrounding text/image is preserved.
|
|
118
|
+
flush();
|
|
119
|
+
out.push({
|
|
120
|
+
type: "function_call_output",
|
|
121
|
+
call_id: block.tool_use_id,
|
|
122
|
+
output: toolResultToResponsesOutput(block.content),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// Other block types (tool_use, thinking) don't legally appear on user
|
|
126
|
+
// messages — silently skip if encountered.
|
|
127
|
+
}
|
|
128
|
+
flush();
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
function translateAssistantBlocks(blocks) {
|
|
132
|
+
const out = [];
|
|
133
|
+
const textParts = [];
|
|
134
|
+
// Collect text first so the assistant message lands once, before any
|
|
135
|
+
// function_calls. The Responses API doesn't require this order strictly
|
|
136
|
+
// (it tolerates [function_call, message] too) but the matching chat-side
|
|
137
|
+
// semantics are "the assistant said X, then called tool Y" — we keep that
|
|
138
|
+
// narrative order in the replay.
|
|
139
|
+
for (const block of blocks) {
|
|
140
|
+
if (block.type === "text" && block.text.length > 0) {
|
|
141
|
+
textParts.push({ type: "output_text", text: block.text });
|
|
142
|
+
}
|
|
143
|
+
// We could surface `thinking` blocks here as `reasoning` items, but doing
|
|
144
|
+
// so without the `encrypted_content` blob the model originally produced
|
|
145
|
+
// would just be plain-text reasoning the API treats as ordinary assistant
|
|
146
|
+
// output — that hurts more than it helps (reveals chain-of-thought to
|
|
147
|
+
// anything that later reads the rollout). Drop until we plumb encrypted
|
|
148
|
+
// replay end-to-end.
|
|
149
|
+
}
|
|
150
|
+
if (textParts.length > 0) {
|
|
151
|
+
out.push({ type: "message", role: "assistant", content: textParts });
|
|
152
|
+
}
|
|
153
|
+
for (const block of blocks) {
|
|
154
|
+
if (block.type === "tool_use") {
|
|
155
|
+
out.push({
|
|
156
|
+
type: "function_call",
|
|
157
|
+
call_id: block.id,
|
|
158
|
+
name: block.name,
|
|
159
|
+
arguments: JSON.stringify(block.input ?? {}),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* If a user message ends up as a single text part, collapse to a plain string
|
|
167
|
+
* — keeps the wire shape predictable and avoids tripping any validator that
|
|
168
|
+
* expects a string when no image is attached. Mixed / image-bearing messages
|
|
169
|
+
* keep the array form. Mirrors DeepSeek's `condenseUserParts`.
|
|
170
|
+
*/
|
|
171
|
+
function condenseUserParts(parts) {
|
|
172
|
+
if (parts.length === 1 && parts[0].type === "input_text")
|
|
173
|
+
return parts[0].text;
|
|
174
|
+
return parts;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Build a `data:<mime>;base64,<data>` URL from a Maestro image source. The
|
|
178
|
+
* Responses API accepts either a hosted URL or a data URL — we always emit
|
|
179
|
+
* the latter so the host doesn't need to publish a temporary URL.
|
|
180
|
+
*
|
|
181
|
+
* Returns `undefined` for malformed sources rather than throwing — the
|
|
182
|
+
* containing message still flows, just without the image part.
|
|
183
|
+
*/
|
|
184
|
+
function imageBlockToDataUrl(source) {
|
|
185
|
+
if (source.type === "url" && source.url)
|
|
186
|
+
return source.url;
|
|
187
|
+
if (source.type === "base64" && source.data) {
|
|
188
|
+
const mime = source.media_type ?? "image/png";
|
|
189
|
+
return `data:${mime};base64,${source.data}`;
|
|
190
|
+
}
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Convert a `tool_result.content` value into the shape the Responses API
|
|
195
|
+
* accepts inside `function_call_output.output`.
|
|
196
|
+
*
|
|
197
|
+
* - String → string (fast path; the majority of tools return plain text).
|
|
198
|
+
* - Block array of only text → joined string (smaller wire payload).
|
|
199
|
+
* - Mixed array (text + image) → structured array; image blocks become
|
|
200
|
+
* `input_image` parts. Document blocks become a text placeholder for the
|
|
201
|
+
* same reason as user-message documents.
|
|
202
|
+
*/
|
|
203
|
+
function toolResultToResponsesOutput(content) {
|
|
204
|
+
if (typeof content === "string")
|
|
205
|
+
return content;
|
|
206
|
+
const parts = [];
|
|
207
|
+
for (const b of content) {
|
|
208
|
+
if (b.type === "text") {
|
|
209
|
+
parts.push({ type: "input_text", text: b.text });
|
|
210
|
+
}
|
|
211
|
+
else if (b.type === "image") {
|
|
212
|
+
const url = imageBlockToDataUrl(b.source);
|
|
213
|
+
if (url)
|
|
214
|
+
parts.push({ type: "input_image", image_url: url });
|
|
215
|
+
}
|
|
216
|
+
else if (b.type === "document") {
|
|
217
|
+
const bytes = b.source.data ? Math.floor((b.source.data.length * 3) / 4) : 0;
|
|
218
|
+
parts.push({
|
|
219
|
+
type: "input_text",
|
|
220
|
+
text: `[document ${b.source.media_type} ${bytes} bytes — extract text first.]`,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// All-text → collapse to a single string for compactness.
|
|
225
|
+
if (parts.every((p) => p.type === "input_text")) {
|
|
226
|
+
return parts.map((p) => p.text).join("\n");
|
|
227
|
+
}
|
|
228
|
+
return parts;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Top-level: translate the full Maestro message history into a flat array of
|
|
232
|
+
* Responses input items. The `system` string is consumed by the provider into
|
|
233
|
+
* the request's `instructions` field — it does NOT appear here.
|
|
234
|
+
*/
|
|
235
|
+
export function translateMessagesToResponses(messages) {
|
|
236
|
+
const out = [];
|
|
237
|
+
for (const msg of messages) {
|
|
238
|
+
for (const item of translateMessageToResponsesItems(msg)) {
|
|
239
|
+
out.push(item);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=codex-translators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-translators.js","sourceRoot":"","sources":["../../src/providers/codex-translators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AA6BH,MAAM,UAAU,yBAAyB,CACvC,KAAgD;IAEhD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACnD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC9C,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE;YAChC,MAAM,EAAE,KAAK;YACb,UAAU,EAAG,CAAC,CAAC,YAAmD,IAAI;gBACpE,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE;aACf;SACF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1C,CAAC;AA6BD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gCAAgC,CAC9C,GAAoB;IAEpB,qEAAqE;IACrE,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO;YACL;gBACE,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB;SACF,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,wBAAwB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAuC;IAClE,MAAM,GAAG,GAAyB,EAAE,CAAC;IACrC,IAAI,QAAQ,GAA2B,EAAE,CAAC;IAE1C,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAClC,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,iBAAiB,CAAC,QAAQ,CAAC;SACrC,CAAC,CAAC;QACH,QAAQ,GAAG,EAAE,CAAC;IAChB,CAAC,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACrF,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAClC,MAAM,GAAG,GAAG,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC9C,IAAI,GAAG;gBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAClE,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACrC,mEAAmE;YACnE,iEAAiE;YACjE,wEAAwE;YACxE,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,aAAa,KAAK,CAAC,MAAM,CAAC,UAAU,4DAA4D;aACvG,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YACxC,uEAAuE;YACvE,8DAA8D;YAC9D,KAAK,EAAE,CAAC;YACR,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,sBAAsB;gBAC5B,OAAO,EAAE,KAAK,CAAC,WAAW;gBAC1B,MAAM,EAAE,2BAA2B,CAAC,KAAK,CAAC,OAAO,CAAC;aACnD,CAAC,CAAC;QACL,CAAC;QACD,sEAAsE;QACtE,2CAA2C;IAC7C,CAAC;IACD,KAAK,EAAE,CAAC;IACR,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,wBAAwB,CAAC,MAAuC;IACvE,MAAM,GAAG,GAAyB,EAAE,CAAC;IACrC,MAAM,SAAS,GAA2B,EAAE,CAAC;IAE7C,qEAAqE;IACrE,wEAAwE;IACxE,yEAAyE;IACzE,0EAA0E;IAC1E,iCAAiC;IACjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnD,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,0EAA0E;QAC1E,wEAAwE;QACxE,0EAA0E;QAC1E,sEAAsE;QACtE,wEAAwE;QACxE,qBAAqB;IACvB,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC9B,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;aAC7C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,KAA6B;IACtD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/E,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,mBAAmB,CAAC,MAK5B;IACC,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,CAAC,GAAG;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IAC3D,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,IAAI,WAAW,CAAC;QAC9C,OAAO,QAAQ,IAAI,WAAW,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,2BAA2B,CAClC,OAAmD;IAEnD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,MAAM,KAAK,GAA2B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,mBAAmB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC1C,IAAI,GAAG;gBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/D,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7E,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,aAAa,CAAC,CAAC,MAAM,CAAC,UAAU,IAAI,KAAK,+BAA+B;aAC/E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,0DAA0D;IAC1D,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAE,CAA0C,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAoC;IAEpC,MAAM,GAAG,GAAyB,EAAE,CAAC;IACrC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,KAAK,MAAM,IAAI,IAAI,gCAAgC,CAAC,GAAG,CAAC,EAAE,CAAC;YACzD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodexResponsesProvider — call OpenAI's Responses API through the ChatGPT
|
|
3
|
+
* Codex backend using a `codex login` OAuth token.
|
|
4
|
+
*
|
|
5
|
+
* High-level shape:
|
|
6
|
+
*
|
|
7
|
+
* ┌────────────────────────┐ ┌──────────────────────────────────────┐
|
|
8
|
+
* │ AIAgent loop │ │ CodexResponsesProvider │
|
|
9
|
+
* │ ProviderMessage[] ─┼───▶│ - resolveAccessToken (refresh-aware)│
|
|
10
|
+
* │ │ │ - translateMessagesToResponses │
|
|
11
|
+
* │ │ │ - translateToolsToResponses │
|
|
12
|
+
* │ │ │ - POST /responses (stream:true) │
|
|
13
|
+
* │ ProviderStreamChunk ◀┼────│ - parseCodexStream │
|
|
14
|
+
* └────────────────────────┘ └──────────────────────────────────────┘
|
|
15
|
+
*
|
|
16
|
+
* Pinned constraints (verified empirically against
|
|
17
|
+
* `https://chatgpt.com/backend-api/codex/responses` on 2026-05-23):
|
|
18
|
+
*
|
|
19
|
+
* - **Stream is required**: non-streaming requests return
|
|
20
|
+
* `400 {"detail":"Stream must be set to true"}`.
|
|
21
|
+
* - **Model whitelist**: ChatGPT-account requests are limited to a small
|
|
22
|
+
* set of codex-issued model slugs (`gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`,
|
|
23
|
+
* `gpt-5.3-codex`, `gpt-5.2`, ...). Passing `gpt-5` returns 400 with
|
|
24
|
+
* `not supported when using Codex with a ChatGPT account`.
|
|
25
|
+
* - **Cloudflare headers required**: `originator: codex_cli_rs` plus a
|
|
26
|
+
* codex-shaped `User-Agent` are mandatory on non-residential IPs.
|
|
27
|
+
* - **`store: false`**: required when the rollout lives on the client side.
|
|
28
|
+
* Without it, the server expects subsequent requests to look items up
|
|
29
|
+
* by `id` and 404s when they can't.
|
|
30
|
+
*
|
|
31
|
+
* What this provider does NOT cover (deliberate scope cuts):
|
|
32
|
+
*
|
|
33
|
+
* - Reasoning chain replay across turns (would need `encrypted_content`
|
|
34
|
+
* plumbed into the message history). Reasoning summaries flow through
|
|
35
|
+
* as `thinking` blocks for the current turn only.
|
|
36
|
+
* - Built-in hosted tools (`web_search`, `file_search`). Only
|
|
37
|
+
* user-supplied function tools are wired.
|
|
38
|
+
* - Token-bucket rate-limit handling. The backend rate-limits aggressively
|
|
39
|
+
* for free accounts; surfacing a structured retry hint can be a follow-up.
|
|
40
|
+
*
|
|
41
|
+
* Hermes reference: `agent/transports/codex.py` + `_run_codex_stream`. We
|
|
42
|
+
* implement the same wire contract using only `fetch` and the SSE parser in
|
|
43
|
+
* `codex-stream.ts`, with no httpx / OpenAI SDK dependency.
|
|
44
|
+
*/
|
|
45
|
+
import { type CodexAuthError } from "../providers/codex-auth.js";
|
|
46
|
+
import type { Provider, ProviderCompleteOptions, ProviderResponse, ProviderStreamChunk } from "../providers/base.js";
|
|
47
|
+
import type { EffortLevel } from "../types.js";
|
|
48
|
+
/**
|
|
49
|
+
* Map maestro's `EffortLevel` to the Responses API `reasoning.effort` string.
|
|
50
|
+
*
|
|
51
|
+
* Codex backend accepts `low | medium | high | xhigh`. The 5-tier maestro
|
|
52
|
+
* scale collapses as follows:
|
|
53
|
+
* - `low` / `medium` / `high` → direct passthrough.
|
|
54
|
+
* - `xhigh` → passthrough (Codex actually supports this tier on gpt-5.x).
|
|
55
|
+
* - `max` → `xhigh`. Codex has no `max`; we pick the deepest tier the
|
|
56
|
+
* backend exposes rather than silently dropping the user's intent.
|
|
57
|
+
*/
|
|
58
|
+
export declare function effortForCodex(e: EffortLevel | undefined): string | undefined;
|
|
59
|
+
export interface CodexProviderOptions {
|
|
60
|
+
/** Override the Codex backend URL. Defaults to the ChatGPT-hosted
|
|
61
|
+
* endpoint. The `/responses` suffix is appended automatically. */
|
|
62
|
+
baseUrl?: string;
|
|
63
|
+
/** Override the timeout for the OAuth refresh round-trip. The streaming
|
|
64
|
+
* request itself uses `fetchTimeoutMs` (see below) — the agent loop also
|
|
65
|
+
* owns abort via signal. */
|
|
66
|
+
refreshTimeoutMs?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Skew window (seconds) for the refresh decision. When the cached access
|
|
69
|
+
* token is within this many seconds of `exp`, the next provider call
|
|
70
|
+
* refreshes before making the API request. Defaults to 5 minutes — large
|
|
71
|
+
* enough to cover one long streaming turn without rolling over mid-stream.
|
|
72
|
+
*/
|
|
73
|
+
refreshSkewSeconds?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Wall-clock ceiling on a single `/responses` POST before we abort and
|
|
76
|
+
* surface a TimeoutError. Default 600_000 ms (10 minutes) — Bun's bundled
|
|
77
|
+
* undici otherwise enforces a 5-minute `headersTimeout` that fires on
|
|
78
|
+
* gpt-5.5 with ~1MB+ request bodies (v0.1.28 production repro). Raising
|
|
79
|
+
* the wall to 10 min gives the model breathing room while keeping a
|
|
80
|
+
* bounded worst case if the server hangs entirely. Combined with the
|
|
81
|
+
* caller's `abortSignal` via `AbortSignal.any`, so a user-initiated abort
|
|
82
|
+
* still wins immediately.
|
|
83
|
+
*/
|
|
84
|
+
fetchTimeoutMs?: number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Provider implementation. Construct once per process; safe to share across
|
|
88
|
+
* concurrent agent turns (token refresh is best-effort idempotent under the
|
|
89
|
+
* skew check + on-disk persistence).
|
|
90
|
+
*/
|
|
91
|
+
export declare class CodexResponsesProvider implements Provider {
|
|
92
|
+
private readonly baseUrl;
|
|
93
|
+
private readonly refreshTimeoutMs;
|
|
94
|
+
private readonly refreshSkewSeconds;
|
|
95
|
+
private readonly fetchTimeoutMs;
|
|
96
|
+
constructor(opts?: CodexProviderOptions);
|
|
97
|
+
/**
|
|
98
|
+
* Build the AbortSignal we pass to `fetch`. Combines an explicit
|
|
99
|
+
* wall-clock timeout (`this.fetchTimeoutMs`) with the caller's optional
|
|
100
|
+
* abort signal so either path can short-circuit the request:
|
|
101
|
+
* - User abort fires → request cancels immediately.
|
|
102
|
+
* - Timeout fires → DOMException(name="TimeoutError") surfaces, which
|
|
103
|
+
* the v0.1.28 `isTimeoutError` helper catches in `provider.ts`.
|
|
104
|
+
*
|
|
105
|
+
* Why explicit timeout: Bun's undici default `headersTimeout` is 300_000
|
|
106
|
+
* ms (5 min) and fires regardless of user signal. Long requests on the
|
|
107
|
+
* heavy gpt-5.* tiers with large bodies blew through that wall in
|
|
108
|
+
* production. We raise the ceiling to 10 minutes (configurable) and pin
|
|
109
|
+
* the same value across the initial POST and the 401-refresh retry below.
|
|
110
|
+
*/
|
|
111
|
+
private buildFetchSignal;
|
|
112
|
+
/**
|
|
113
|
+
* Factory parity with `DeepseekProvider.fromEnv()` — for Codex the "env"
|
|
114
|
+
* inputs are really the on-disk `~/.codex/auth.json` (no API key env var
|
|
115
|
+
* makes sense for OAuth). The factory still throws early if the auth file
|
|
116
|
+
* is missing or stale, so a host can fail fast at startup instead of on
|
|
117
|
+
* the first user message.
|
|
118
|
+
*/
|
|
119
|
+
static fromEnv(opts?: CodexProviderOptions): CodexResponsesProvider;
|
|
120
|
+
/**
|
|
121
|
+
* Streaming entry point. The Codex backend rejects non-streaming requests
|
|
122
|
+
* outright, so this is the load-bearing method — `complete()` is just a
|
|
123
|
+
* convenience wrapper around it.
|
|
124
|
+
*/
|
|
125
|
+
stream(opts: ProviderCompleteOptions): AsyncGenerator<ProviderStreamChunk>;
|
|
126
|
+
/**
|
|
127
|
+
* Non-streaming entry point. The Codex backend doesn't support
|
|
128
|
+
* `stream: false`, so we drain `stream()` into a single `ProviderResponse`.
|
|
129
|
+
* Callers that want progressive UI updates should use `stream()` directly.
|
|
130
|
+
*
|
|
131
|
+
* The reconstruction follows DeepseekProvider's convention:
|
|
132
|
+
* - text deltas collapse into one `text` content block per stream;
|
|
133
|
+
* - tool_use_start + tool_use_input_delta + tool_use_complete bundle
|
|
134
|
+
* into one `tool_use` block with parsed JSON args;
|
|
135
|
+
* - thinking_complete blocks are forwarded verbatim.
|
|
136
|
+
*
|
|
137
|
+
* Block order in the returned `content`: thinking → text → tool_use,
|
|
138
|
+
* matching the assistant-history convention the loop expects on replay.
|
|
139
|
+
*/
|
|
140
|
+
complete(opts: ProviderCompleteOptions): Promise<ProviderResponse>;
|
|
141
|
+
}
|
|
142
|
+
export { CodexAuthError } from "../providers/codex-auth.js";
|
|
143
|
+
export type { CodexProviderOptions as _CodexProviderOptions };
|
|
144
|
+
export type _CodexAuthError = CodexAuthError;
|
|
145
|
+
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/providers/codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAGH,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACV,QAAQ,EACR,uBAAuB,EAEvB,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAO1B,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,SAAS,CAAC;AAOvD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAe7E;AAED,MAAM,WAAW,oBAAoB;IACnC;uEACmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;iCAE6B;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,qBAAa,sBAAuB,YAAW,QAAQ;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,IAAI,GAAE,oBAAyB;IAO3C;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,IAAI,GAAE,oBAAyB,GAAG,sBAAsB;IAIvE;;;;OAIG;IACI,MAAM,CAAC,IAAI,EAAE,uBAAuB,GAAG,cAAc,CAAC,mBAAmB,CAAC;IA4IjF;;;;;;;;;;;;;OAaG;IACG,QAAQ,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAsDzE;AAgGD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EAAE,oBAAoB,IAAI,qBAAqB,EAAE,CAAC;AAE9D,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC"}
|