replicas-engine 0.1.610 → 0.1.611
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/src/index.js +379 -157
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -60,6 +60,9 @@ var VALID_CODING_AGENT_PROVIDERS = VALID_AGENT_PROVIDERS.filter(
|
|
|
60
60
|
function isValidAgentProvider(value) {
|
|
61
61
|
return VALID_AGENT_PROVIDERS.some((p) => p === value);
|
|
62
62
|
}
|
|
63
|
+
function isValidCodingAgentProvider(value) {
|
|
64
|
+
return VALID_CODING_AGENT_PROVIDERS.some((provider) => provider === value);
|
|
65
|
+
}
|
|
63
66
|
var VALID_THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max", "ultra", "ultracode"];
|
|
64
67
|
var STANDARD_THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
65
68
|
var THINKING_LEVELS_BY_AGENT = {
|
|
@@ -86,6 +89,117 @@ function codexReasoningEffortForThinkingLevel(thinkingLevel) {
|
|
|
86
89
|
return thinkingLevel ? CODEX_REASONING_EFFORT_BY_THINKING_LEVEL[thinkingLevel] : void 0;
|
|
87
90
|
}
|
|
88
91
|
|
|
92
|
+
// ../shared/src/agent-quota.ts
|
|
93
|
+
var OUT_OF_CREDITS_PATTERNS = [
|
|
94
|
+
/credit balance is too low/,
|
|
95
|
+
/out.of.credits/,
|
|
96
|
+
/credits?.?(?:depleted|exhausted|remaining)/,
|
|
97
|
+
/insufficient credits/,
|
|
98
|
+
/insufficient_quota/
|
|
99
|
+
];
|
|
100
|
+
var RATE_LIMIT_PATTERNS = [
|
|
101
|
+
// Loose on purpose: providers word this as "rate limit", "rate_limit",
|
|
102
|
+
// "usageLimitExceeded" and so on, and these only ever run against error text.
|
|
103
|
+
/rate.?limit/,
|
|
104
|
+
/usage.?limit/,
|
|
105
|
+
/too many requests/,
|
|
106
|
+
/\bquota\b/
|
|
107
|
+
];
|
|
108
|
+
var HTTP_429_PATTERN = /(?:^|[^0-9])429(?:$|[^0-9])/;
|
|
109
|
+
var HTTP_CONTEXT_PATTERN = /(api error|request|response|status|http)/;
|
|
110
|
+
function detectAgentQuotaLimit(text) {
|
|
111
|
+
const normalized = text.toLowerCase();
|
|
112
|
+
if (!normalized) return null;
|
|
113
|
+
if (OUT_OF_CREDITS_PATTERNS.some((pattern) => pattern.test(normalized))) return "out_of_credits";
|
|
114
|
+
if (RATE_LIMIT_PATTERNS.some((pattern) => pattern.test(normalized))) return "rate_limit";
|
|
115
|
+
return HTTP_429_PATTERN.test(normalized) && HTTP_CONTEXT_PATTERN.test(normalized) ? "rate_limit" : null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ../shared/src/credentials/types.ts
|
|
119
|
+
var CREDENTIAL_SCOPE = {
|
|
120
|
+
USER: "user",
|
|
121
|
+
ORG: "org"
|
|
122
|
+
};
|
|
123
|
+
var CREDENTIAL_SCOPES = Object.values(CREDENTIAL_SCOPE);
|
|
124
|
+
var CREDENTIAL_SCOPE_PRIORITY = [CREDENTIAL_SCOPE.USER, CREDENTIAL_SCOPE.ORG];
|
|
125
|
+
|
|
126
|
+
// ../shared/src/credentials/agents.ts
|
|
127
|
+
var CREDENTIAL_METHOD = {
|
|
128
|
+
OAUTH: "oauth",
|
|
129
|
+
API_KEY: "api_key",
|
|
130
|
+
FOUNDRY: "foundry",
|
|
131
|
+
BEDROCK: "bedrock",
|
|
132
|
+
OPENCODE_GO: "opencode-go",
|
|
133
|
+
ASTER: "aster",
|
|
134
|
+
OPENROUTER: "openrouter"
|
|
135
|
+
};
|
|
136
|
+
var CREDENTIAL_TYPE = {
|
|
137
|
+
CLAUDE_OAUTH: "claude",
|
|
138
|
+
ANTHROPIC_API_KEY: "claude_anthropic_api_key",
|
|
139
|
+
CLAUDE_FOUNDRY: "claude_foundry",
|
|
140
|
+
CLAUDE_BEDROCK: "claude_bedrock",
|
|
141
|
+
CODEX_OAUTH: "codex",
|
|
142
|
+
OPENAI_API_KEY: "openai_api_key",
|
|
143
|
+
CODEX_FOUNDRY: "codex_foundry",
|
|
144
|
+
CURSOR_API_KEY: "cursor_api_key",
|
|
145
|
+
OPENCODE_GO_API_KEY: "opencode_go_api_key",
|
|
146
|
+
ASTER_API_KEY: "aster_api_key",
|
|
147
|
+
OPENROUTER_API_KEY: "openrouter_api_key"
|
|
148
|
+
};
|
|
149
|
+
var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
|
|
150
|
+
[AGENT.CLAUDE]: [
|
|
151
|
+
{ method: CREDENTIAL_METHOD.OAUTH, credentialType: CREDENTIAL_TYPE.CLAUDE_OAUTH },
|
|
152
|
+
{ method: CREDENTIAL_METHOD.API_KEY, credentialType: CREDENTIAL_TYPE.ANTHROPIC_API_KEY },
|
|
153
|
+
{ method: CREDENTIAL_METHOD.FOUNDRY, credentialType: CREDENTIAL_TYPE.CLAUDE_FOUNDRY },
|
|
154
|
+
{ method: CREDENTIAL_METHOD.BEDROCK, credentialType: CREDENTIAL_TYPE.CLAUDE_BEDROCK }
|
|
155
|
+
],
|
|
156
|
+
[AGENT.CODEX]: [
|
|
157
|
+
{ method: CREDENTIAL_METHOD.OAUTH, credentialType: CREDENTIAL_TYPE.CODEX_OAUTH },
|
|
158
|
+
{ method: CREDENTIAL_METHOD.API_KEY, credentialType: CREDENTIAL_TYPE.OPENAI_API_KEY },
|
|
159
|
+
{ method: CREDENTIAL_METHOD.FOUNDRY, credentialType: CREDENTIAL_TYPE.CODEX_FOUNDRY }
|
|
160
|
+
],
|
|
161
|
+
[AGENT.CURSOR]: [
|
|
162
|
+
{ method: CREDENTIAL_METHOD.API_KEY, credentialType: CREDENTIAL_TYPE.CURSOR_API_KEY }
|
|
163
|
+
],
|
|
164
|
+
[AGENT.OPENCODE]: [
|
|
165
|
+
{ method: CREDENTIAL_METHOD.OPENCODE_GO, credentialType: CREDENTIAL_TYPE.OPENCODE_GO_API_KEY },
|
|
166
|
+
{ method: CREDENTIAL_METHOD.ASTER, credentialType: CREDENTIAL_TYPE.ASTER_API_KEY },
|
|
167
|
+
{ method: CREDENTIAL_METHOD.OPENROUTER, credentialType: CREDENTIAL_TYPE.OPENROUTER_API_KEY }
|
|
168
|
+
],
|
|
169
|
+
[AGENT.PI]: [
|
|
170
|
+
{ method: CREDENTIAL_METHOD.ASTER, credentialType: CREDENTIAL_TYPE.ASTER_API_KEY },
|
|
171
|
+
{ method: CREDENTIAL_METHOD.OPENROUTER, credentialType: CREDENTIAL_TYPE.OPENROUTER_API_KEY }
|
|
172
|
+
]
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// ../shared/src/analytics/types.ts
|
|
176
|
+
function isAgentChatActivityRecord(value) {
|
|
177
|
+
return isRecord(value) && typeof value.chatId === "string" && typeof value.provider === "string" && isValidAgentProvider(value.provider) && typeof value.model === "string" && (value.credentialMethod === void 0 || isAuthMethod(value.credentialMethod)) && (value.credentialScope === void 0 || isCredentialScope(value.credentialScope)) && value.credentialMethod === void 0 === (value.credentialScope === void 0) && (value.senderUserId === void 0 || typeof value.senderUserId === "string") && typeof value.occurredAt === "string";
|
|
178
|
+
}
|
|
179
|
+
function isAuthMethod(value) {
|
|
180
|
+
return typeof value === "string" && Object.values(CREDENTIAL_METHOD).some((method) => method === value);
|
|
181
|
+
}
|
|
182
|
+
function isCredentialScope(value) {
|
|
183
|
+
return typeof value === "string" && CREDENTIAL_SCOPES.some((scope) => scope === value);
|
|
184
|
+
}
|
|
185
|
+
function isAgentChatTurnActivityRecord(value) {
|
|
186
|
+
return isAgentChatActivityRecord(value) && typeof value.turnId === "string" && typeof value.seconds === "number" && Number.isFinite(value.seconds) && value.seconds >= 0;
|
|
187
|
+
}
|
|
188
|
+
function isAgentChatActivityCallRecord(value) {
|
|
189
|
+
return isAgentChatActivityRecord(value) && typeof value.callId === "string" && value.callId.length > 0;
|
|
190
|
+
}
|
|
191
|
+
function isNamedAgentChatActivityRecord(value, field) {
|
|
192
|
+
if (!isAgentChatActivityCallRecord(value)) return false;
|
|
193
|
+
const name = value[field];
|
|
194
|
+
return typeof name === "string" && name.length > 0;
|
|
195
|
+
}
|
|
196
|
+
function isAgentChatSkillActivityRecord(value) {
|
|
197
|
+
return isNamedAgentChatActivityRecord(value, "skillName");
|
|
198
|
+
}
|
|
199
|
+
function isAgentChatMcpActivityRecord(value) {
|
|
200
|
+
return isNamedAgentChatActivityRecord(value, "mcpName");
|
|
201
|
+
}
|
|
202
|
+
|
|
89
203
|
// ../shared/src/event.ts
|
|
90
204
|
var CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE = "claude-partial-message";
|
|
91
205
|
function coerceClaudePartialMessagePayload(payload) {
|
|
@@ -112,6 +226,23 @@ var COMPACTION_STATUS_EVENT_TYPE = "compaction-status";
|
|
|
112
226
|
var CHAT_INTERRUPTED_EVENT_TYPE = "replicas-interrupted";
|
|
113
227
|
var CHAT_GOAL_EVENT_TYPE = "chat-goal";
|
|
114
228
|
var AUTH_RETRY_STATUS_EVENT_TYPE = "auth-retry-status";
|
|
229
|
+
var AUTH_FALLBACK_EVENT_TYPE = "auth-fallback";
|
|
230
|
+
function coerceAuthFallbackPayload(payload) {
|
|
231
|
+
const { provider, reason, exhaustedMethod, exhaustedScope, candidateMethod, candidateScope, status, detail } = payload;
|
|
232
|
+
if (typeof provider !== "string" || !isValidCodingAgentProvider(provider) || reason !== "rate_limit" && reason !== "out_of_credits" || !isAuthMethod(exhaustedMethod) || !isAuthMethod(candidateMethod) || !isCredentialScope(candidateScope) || status !== "switched" && status !== "failed") {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
provider,
|
|
237
|
+
reason,
|
|
238
|
+
exhaustedMethod,
|
|
239
|
+
...isCredentialScope(exhaustedScope) ? { exhaustedScope } : {},
|
|
240
|
+
candidateMethod,
|
|
241
|
+
candidateScope,
|
|
242
|
+
status,
|
|
243
|
+
...typeof detail === "string" && detail ? { detail } : {}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
115
246
|
var CONTEXT_USAGE_EVENT_TYPE = "context-usage";
|
|
116
247
|
|
|
117
248
|
// ../shared/src/languages.ts
|
|
@@ -205,91 +336,6 @@ function detectLanguageByPath(filePath) {
|
|
|
205
336
|
return EXT_TO_LANGUAGE[ext] ?? null;
|
|
206
337
|
}
|
|
207
338
|
|
|
208
|
-
// ../shared/src/credentials/types.ts
|
|
209
|
-
var CREDENTIAL_SCOPE = {
|
|
210
|
-
USER: "user",
|
|
211
|
-
ORG: "org"
|
|
212
|
-
};
|
|
213
|
-
var CREDENTIAL_SCOPES = Object.values(CREDENTIAL_SCOPE);
|
|
214
|
-
var CREDENTIAL_SCOPE_PRIORITY = [CREDENTIAL_SCOPE.USER, CREDENTIAL_SCOPE.ORG];
|
|
215
|
-
|
|
216
|
-
// ../shared/src/credentials/agents.ts
|
|
217
|
-
var CREDENTIAL_METHOD = {
|
|
218
|
-
OAUTH: "oauth",
|
|
219
|
-
API_KEY: "api_key",
|
|
220
|
-
FOUNDRY: "foundry",
|
|
221
|
-
BEDROCK: "bedrock",
|
|
222
|
-
OPENCODE_GO: "opencode-go",
|
|
223
|
-
ASTER: "aster",
|
|
224
|
-
OPENROUTER: "openrouter"
|
|
225
|
-
};
|
|
226
|
-
var CREDENTIAL_TYPE = {
|
|
227
|
-
CLAUDE_OAUTH: "claude",
|
|
228
|
-
ANTHROPIC_API_KEY: "claude_anthropic_api_key",
|
|
229
|
-
CLAUDE_FOUNDRY: "claude_foundry",
|
|
230
|
-
CLAUDE_BEDROCK: "claude_bedrock",
|
|
231
|
-
CODEX_OAUTH: "codex",
|
|
232
|
-
OPENAI_API_KEY: "openai_api_key",
|
|
233
|
-
CODEX_FOUNDRY: "codex_foundry",
|
|
234
|
-
CURSOR_API_KEY: "cursor_api_key",
|
|
235
|
-
OPENCODE_GO_API_KEY: "opencode_go_api_key",
|
|
236
|
-
ASTER_API_KEY: "aster_api_key",
|
|
237
|
-
OPENROUTER_API_KEY: "openrouter_api_key"
|
|
238
|
-
};
|
|
239
|
-
var AGENT_CREDENTIAL_METHODS_IN_ORDER = {
|
|
240
|
-
[AGENT.CLAUDE]: [
|
|
241
|
-
{ method: CREDENTIAL_METHOD.OAUTH, credentialType: CREDENTIAL_TYPE.CLAUDE_OAUTH },
|
|
242
|
-
{ method: CREDENTIAL_METHOD.API_KEY, credentialType: CREDENTIAL_TYPE.ANTHROPIC_API_KEY },
|
|
243
|
-
{ method: CREDENTIAL_METHOD.FOUNDRY, credentialType: CREDENTIAL_TYPE.CLAUDE_FOUNDRY },
|
|
244
|
-
{ method: CREDENTIAL_METHOD.BEDROCK, credentialType: CREDENTIAL_TYPE.CLAUDE_BEDROCK }
|
|
245
|
-
],
|
|
246
|
-
[AGENT.CODEX]: [
|
|
247
|
-
{ method: CREDENTIAL_METHOD.OAUTH, credentialType: CREDENTIAL_TYPE.CODEX_OAUTH },
|
|
248
|
-
{ method: CREDENTIAL_METHOD.API_KEY, credentialType: CREDENTIAL_TYPE.OPENAI_API_KEY },
|
|
249
|
-
{ method: CREDENTIAL_METHOD.FOUNDRY, credentialType: CREDENTIAL_TYPE.CODEX_FOUNDRY }
|
|
250
|
-
],
|
|
251
|
-
[AGENT.CURSOR]: [
|
|
252
|
-
{ method: CREDENTIAL_METHOD.API_KEY, credentialType: CREDENTIAL_TYPE.CURSOR_API_KEY }
|
|
253
|
-
],
|
|
254
|
-
[AGENT.OPENCODE]: [
|
|
255
|
-
{ method: CREDENTIAL_METHOD.OPENCODE_GO, credentialType: CREDENTIAL_TYPE.OPENCODE_GO_API_KEY },
|
|
256
|
-
{ method: CREDENTIAL_METHOD.ASTER, credentialType: CREDENTIAL_TYPE.ASTER_API_KEY },
|
|
257
|
-
{ method: CREDENTIAL_METHOD.OPENROUTER, credentialType: CREDENTIAL_TYPE.OPENROUTER_API_KEY }
|
|
258
|
-
],
|
|
259
|
-
[AGENT.PI]: [
|
|
260
|
-
{ method: CREDENTIAL_METHOD.ASTER, credentialType: CREDENTIAL_TYPE.ASTER_API_KEY },
|
|
261
|
-
{ method: CREDENTIAL_METHOD.OPENROUTER, credentialType: CREDENTIAL_TYPE.OPENROUTER_API_KEY }
|
|
262
|
-
]
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
// ../shared/src/analytics/types.ts
|
|
266
|
-
function isAgentChatActivityRecord(value) {
|
|
267
|
-
return isRecord(value) && typeof value.chatId === "string" && typeof value.provider === "string" && isValidAgentProvider(value.provider) && typeof value.model === "string" && (value.credentialMethod === void 0 || isAuthMethod(value.credentialMethod)) && (value.credentialScope === void 0 || isCredentialScope(value.credentialScope)) && value.credentialMethod === void 0 === (value.credentialScope === void 0) && (value.senderUserId === void 0 || typeof value.senderUserId === "string") && typeof value.occurredAt === "string";
|
|
268
|
-
}
|
|
269
|
-
function isAuthMethod(value) {
|
|
270
|
-
return typeof value === "string" && Object.values(CREDENTIAL_METHOD).some((method) => method === value);
|
|
271
|
-
}
|
|
272
|
-
function isCredentialScope(value) {
|
|
273
|
-
return typeof value === "string" && CREDENTIAL_SCOPES.some((scope) => scope === value);
|
|
274
|
-
}
|
|
275
|
-
function isAgentChatTurnActivityRecord(value) {
|
|
276
|
-
return isAgentChatActivityRecord(value) && typeof value.turnId === "string" && typeof value.seconds === "number" && Number.isFinite(value.seconds) && value.seconds >= 0;
|
|
277
|
-
}
|
|
278
|
-
function isAgentChatActivityCallRecord(value) {
|
|
279
|
-
return isAgentChatActivityRecord(value) && typeof value.callId === "string" && value.callId.length > 0;
|
|
280
|
-
}
|
|
281
|
-
function isNamedAgentChatActivityRecord(value, field) {
|
|
282
|
-
if (!isAgentChatActivityCallRecord(value)) return false;
|
|
283
|
-
const name = value[field];
|
|
284
|
-
return typeof name === "string" && name.length > 0;
|
|
285
|
-
}
|
|
286
|
-
function isAgentChatSkillActivityRecord(value) {
|
|
287
|
-
return isNamedAgentChatActivityRecord(value, "skillName");
|
|
288
|
-
}
|
|
289
|
-
function isAgentChatMcpActivityRecord(value) {
|
|
290
|
-
return isNamedAgentChatActivityRecord(value, "mcpName");
|
|
291
|
-
}
|
|
292
|
-
|
|
293
339
|
// ../shared/src/aster.ts
|
|
294
340
|
var ASTER_PROVIDER = "aster";
|
|
295
341
|
var ASTER_BASE_URL = "https://api.asterlab.ai/v1";
|
|
@@ -2501,7 +2547,8 @@ var DEFAULT_USER_PREFERENCES = {
|
|
|
2501
2547
|
auto_draft_prs: false,
|
|
2502
2548
|
open_prs_in_graphite: false,
|
|
2503
2549
|
default_fast_mode: false,
|
|
2504
|
-
agent_defaults: { ...EMPTY_AGENT_DEFAULT_SETTINGS }
|
|
2550
|
+
agent_defaults: { ...EMPTY_AGENT_DEFAULT_SETTINGS },
|
|
2551
|
+
credential_priority: null
|
|
2505
2552
|
};
|
|
2506
2553
|
function defaultReplicasAgentAbilities() {
|
|
2507
2554
|
const abilities = {};
|
|
@@ -2686,7 +2733,7 @@ var TRANSIENT_NETWORK_ERROR_PATTERNS = [
|
|
|
2686
2733
|
/connection closed/,
|
|
2687
2734
|
/closed unexpectedly/
|
|
2688
2735
|
];
|
|
2689
|
-
var
|
|
2736
|
+
var HTTP_CONTEXT_PATTERN2 = /(api error|request|response|status|http)/;
|
|
2690
2737
|
var HTTP_STATUS_PATTERN = /(^|[^0-9])([1-5][0-9]{2})(?=$|[^0-9])/g;
|
|
2691
2738
|
function extractErrorText(error) {
|
|
2692
2739
|
const parts = [];
|
|
@@ -2728,7 +2775,7 @@ function isTransientErrorText(text, options = {}) {
|
|
|
2728
2775
|
const httpStatuses = new Set(options.httpStatuses ?? []);
|
|
2729
2776
|
const httpStatusClasses = new Set(options.httpStatusClasses ?? []);
|
|
2730
2777
|
if (httpStatuses.size === 0 && httpStatusClasses.size === 0) return false;
|
|
2731
|
-
if (options.requireHttpContext !== false && !
|
|
2778
|
+
if (options.requireHttpContext !== false && !HTTP_CONTEXT_PATTERN2.test(normalized)) return false;
|
|
2732
2779
|
for (const match of normalized.matchAll(HTTP_STATUS_PATTERN)) {
|
|
2733
2780
|
const status = Number(match[2]);
|
|
2734
2781
|
if (httpStatuses.has(status) || httpStatusClasses.has(Math.floor(status / 100))) return true;
|
|
@@ -2739,7 +2786,7 @@ function isTransientErrorText(text, options = {}) {
|
|
|
2739
2786
|
// ../shared/src/claude-auth.ts
|
|
2740
2787
|
function isClaudeAuthErrorText(text) {
|
|
2741
2788
|
const lower = text.toLowerCase();
|
|
2742
|
-
return lower.includes("failed to authenticate") || lower.includes("authentication_error") || lower.includes("authentication_failed") || lower.includes("authentication failed") || lower.includes("invalid authentication credentials") || lower.includes("not logged in") || lower.includes("please run /login") || lower.includes("
|
|
2789
|
+
return lower.includes("failed to authenticate") || lower.includes("authentication_error") || lower.includes("authentication_failed") || lower.includes("authentication failed") || lower.includes("invalid authentication credentials") || lower.includes("not logged in") || lower.includes("please run /login") || lower.includes("401") && lower.includes("authentic");
|
|
2743
2790
|
}
|
|
2744
2791
|
|
|
2745
2792
|
// ../shared/src/codex-auth.ts
|
|
@@ -5122,21 +5169,24 @@ function parseDisplayMessages(events, agentType, codexAspTranscript, options = {
|
|
|
5122
5169
|
const shouldFilter = options.filter ?? true;
|
|
5123
5170
|
const parsedEvents = agentType === "claude" || agentType === "relay" ? parseClaudeEvents(events, options.parentToolUseId) : parseAgentEvents(events, agentType);
|
|
5124
5171
|
const legacyMessages = shouldFilter ? filterDisplayMessages(parsedEvents, agentType) : parsedEvents;
|
|
5172
|
+
const finalize = (messages) => shouldFilter ? applyAuthFallbackNotices(applyInterruptions(messages, events), events) : messages;
|
|
5125
5173
|
if (agentType !== "codex" || !codexAspTranscript) {
|
|
5126
|
-
return
|
|
5174
|
+
return finalize(legacyMessages);
|
|
5127
5175
|
}
|
|
5128
5176
|
const nativeCodexMessages = shouldFilter ? filterDisplayMessages(parseCodexAspTranscript(codexAspTranscript), agentType) : parseCodexAspTranscript(codexAspTranscript);
|
|
5129
|
-
|
|
5130
|
-
|
|
5177
|
+
return finalize(mergeCodexAspDisplayMessages(nativeCodexMessages, legacyMessages));
|
|
5178
|
+
}
|
|
5179
|
+
function insertByTimestamp(messages, message) {
|
|
5180
|
+
const messageMs = parseTimestampMs(message.timestamp);
|
|
5181
|
+
let index = messages.length;
|
|
5182
|
+
while (index > 0 && parseTimestampMs(messages[index - 1].timestamp) > messageMs) index--;
|
|
5183
|
+
messages.splice(index, 0, message);
|
|
5131
5184
|
}
|
|
5132
5185
|
function applyInterruptions(messages, events) {
|
|
5133
5186
|
const result = [...messages];
|
|
5134
5187
|
for (const event of events) {
|
|
5135
5188
|
if (event.type !== CHAT_INTERRUPTED_EVENT_TYPE) continue;
|
|
5136
|
-
|
|
5137
|
-
let index = result.length;
|
|
5138
|
-
while (index > 0 && parseTimestampMs(result[index - 1].timestamp) > eventMs) index--;
|
|
5139
|
-
result.splice(index, 0, {
|
|
5189
|
+
insertByTimestamp(result, {
|
|
5140
5190
|
id: `interruption-${event.timestamp}`,
|
|
5141
5191
|
type: "interruption",
|
|
5142
5192
|
timestamp: event.timestamp
|
|
@@ -5168,6 +5218,24 @@ function applyInterruptions(messages, events) {
|
|
|
5168
5218
|
}
|
|
5169
5219
|
return finalized;
|
|
5170
5220
|
}
|
|
5221
|
+
function applyAuthFallbackNotices(messages, events) {
|
|
5222
|
+
const notices = [];
|
|
5223
|
+
events.forEach((event, index) => {
|
|
5224
|
+
if (event.type !== AUTH_FALLBACK_EVENT_TYPE) return;
|
|
5225
|
+
const payload = coerceAuthFallbackPayload(event.payload);
|
|
5226
|
+
if (!payload) return;
|
|
5227
|
+
notices.push({
|
|
5228
|
+
...payload,
|
|
5229
|
+
id: `auth-fallback-${event.timestamp}-${index}`,
|
|
5230
|
+
type: "auth_fallback",
|
|
5231
|
+
timestamp: event.timestamp
|
|
5232
|
+
});
|
|
5233
|
+
});
|
|
5234
|
+
if (notices.length === 0) return messages;
|
|
5235
|
+
const result = [...messages];
|
|
5236
|
+
for (const notice of notices) insertByTimestamp(result, notice);
|
|
5237
|
+
return result;
|
|
5238
|
+
}
|
|
5171
5239
|
function isCodexInitializationPrompt(message) {
|
|
5172
5240
|
return message.type === "user" && removeReplicasInstructions(message.content).trim() === "Hello";
|
|
5173
5241
|
}
|
|
@@ -5392,6 +5460,28 @@ function setAgentCredentialSnapshot(provider, snapshot) {
|
|
|
5392
5460
|
process.env.REPLICAS_AGENT_CREDENTIALS = JSON.stringify(ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS);
|
|
5393
5461
|
}
|
|
5394
5462
|
|
|
5463
|
+
// src/services/credential-fallbacks.ts
|
|
5464
|
+
var fallbacksByAgent = /* @__PURE__ */ new Map();
|
|
5465
|
+
var exhaustedByAgent = /* @__PURE__ */ new Map();
|
|
5466
|
+
function recordCredentialFallback(notice) {
|
|
5467
|
+
fallbacksByAgent.set(notice.provider, notice);
|
|
5468
|
+
}
|
|
5469
|
+
function listCredentialFallbacks() {
|
|
5470
|
+
return [...fallbacksByAgent.values()].filter((notice) => {
|
|
5471
|
+
const live = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[notice.provider];
|
|
5472
|
+
if (!live) return false;
|
|
5473
|
+
return notice.status === "switched" ? live.method === notice.candidateMethod && live.scope === notice.candidateScope : live.method === notice.exhaustedMethod && live.scope === notice.exhaustedScope;
|
|
5474
|
+
});
|
|
5475
|
+
}
|
|
5476
|
+
function listExhaustedCredentials(provider) {
|
|
5477
|
+
return [...exhaustedByAgent.get(provider)?.values() ?? []];
|
|
5478
|
+
}
|
|
5479
|
+
function recordExhaustedCredential(provider, credential) {
|
|
5480
|
+
const spent = exhaustedByAgent.get(provider) ?? /* @__PURE__ */ new Map();
|
|
5481
|
+
spent.set(`${credential.method}|${credential.scope}`, credential);
|
|
5482
|
+
exhaustedByAgent.set(provider, spent);
|
|
5483
|
+
}
|
|
5484
|
+
|
|
5395
5485
|
// src/managers/base-refresh-manager.ts
|
|
5396
5486
|
var BaseRefreshManager = class {
|
|
5397
5487
|
constructor(managerName, intervalMs = 15 * 60 * 1e3) {
|
|
@@ -5446,6 +5536,29 @@ var BaseRefreshManager = class {
|
|
|
5446
5536
|
}
|
|
5447
5537
|
this.scheduleNextRefresh();
|
|
5448
5538
|
}
|
|
5539
|
+
async swapCredentials(params) {
|
|
5540
|
+
if (!this.getRuntimeConfig()) {
|
|
5541
|
+
return createErrorResult({ message: `${this.managerName} has no runtime config`, code: "not_configured" });
|
|
5542
|
+
}
|
|
5543
|
+
try {
|
|
5544
|
+
console.log(`[${this.managerName}] Fetching fresh credentials from monolith (${params.failureKind})...`);
|
|
5545
|
+
const excludeCredentials = listExhaustedCredentials(params.provider);
|
|
5546
|
+
await params.refresh(excludeCredentials.length > 0 ? { excludeCredentials } : {});
|
|
5547
|
+
if (params.isOauthNow()) {
|
|
5548
|
+
this.start().catch((error) => {
|
|
5549
|
+
console.error(`[${this.managerName}] Failed to restart OAuth refresh service after fallback:`, error);
|
|
5550
|
+
});
|
|
5551
|
+
}
|
|
5552
|
+
return createSuccessResult();
|
|
5553
|
+
} catch (error) {
|
|
5554
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5555
|
+
console.error(`[${this.managerName}] Failed to fetch fresh credentials:`, error);
|
|
5556
|
+
return createErrorResult({
|
|
5557
|
+
message,
|
|
5558
|
+
code: message.includes('"code":"no_credentials"') ? "no_credentials" : "refresh_failed"
|
|
5559
|
+
});
|
|
5560
|
+
}
|
|
5561
|
+
}
|
|
5449
5562
|
stop() {
|
|
5450
5563
|
if (!this.intervalHandle) {
|
|
5451
5564
|
return;
|
|
@@ -6465,26 +6578,14 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
|
|
|
6465
6578
|
if (data.scope) setAgentCredentialSnapshot("claude", { method: data.type, scope: data.scope });
|
|
6466
6579
|
console.log(`[ClaudeTokenManager] Credentials refreshed (method=${data.type})`);
|
|
6467
6580
|
}
|
|
6468
|
-
async fetchFreshCredentials(failureReason) {
|
|
6469
|
-
const
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
failureReason
|
|
6477
|
-
} : void 0);
|
|
6478
|
-
if (ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "oauth") {
|
|
6479
|
-
this.start().catch((error) => {
|
|
6480
|
-
console.error("[ClaudeTokenManager] Failed to restart OAuth refresh service after fallback:", error);
|
|
6481
|
-
});
|
|
6482
|
-
}
|
|
6483
|
-
return true;
|
|
6484
|
-
} catch (error) {
|
|
6485
|
-
console.error("[ClaudeTokenManager] Failed to fetch fresh credentials:", error);
|
|
6486
|
-
return false;
|
|
6487
|
-
}
|
|
6581
|
+
async fetchFreshCredentials(failureReason, failureKind = "rejected") {
|
|
6582
|
+
const failedMethod = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
|
|
6583
|
+
return this.swapCredentials({
|
|
6584
|
+
provider: "claude",
|
|
6585
|
+
failureKind,
|
|
6586
|
+
refresh: (exclusions) => this.refreshWithRequest(failedMethod && failedMethod !== "none" ? { failedMethod, failureReason, failureKind, ...exclusions } : exclusions),
|
|
6587
|
+
isOauthNow: () => ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "oauth"
|
|
6588
|
+
});
|
|
6488
6589
|
}
|
|
6489
6590
|
async applyCredentialsResponse(response) {
|
|
6490
6591
|
if (response.type === "oauth") {
|
|
@@ -6573,26 +6674,16 @@ var CodexTokenManager = class extends BaseRefreshManager {
|
|
|
6573
6674
|
if (data.scope) setAgentCredentialSnapshot("codex", { method: data.type, scope: data.scope });
|
|
6574
6675
|
console.log(`[CodexTokenManager] Credentials refreshed (method=${data.type})`);
|
|
6575
6676
|
}
|
|
6576
|
-
async fetchFreshCredentials(failureReason) {
|
|
6577
|
-
const
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
if (ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD === "oauth") {
|
|
6587
|
-
this.start().catch((error) => {
|
|
6588
|
-
console.error("[CodexTokenManager] Failed to restart OAuth refresh service after fallback:", error);
|
|
6589
|
-
});
|
|
6590
|
-
}
|
|
6591
|
-
return true;
|
|
6592
|
-
} catch (error) {
|
|
6593
|
-
console.error("[CodexTokenManager] Failed to fetch fresh credentials:", error);
|
|
6594
|
-
return false;
|
|
6595
|
-
}
|
|
6677
|
+
async fetchFreshCredentials(failureReason, failureKind = "rejected") {
|
|
6678
|
+
const failedMethod = ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD;
|
|
6679
|
+
return this.swapCredentials({
|
|
6680
|
+
provider: "codex",
|
|
6681
|
+
failureKind,
|
|
6682
|
+
refresh: (exclusions) => this.refreshWithRequest(
|
|
6683
|
+
failedMethod === "oauth" || failedMethod === "api_key" || failedMethod === "foundry" ? { failedMethod, failureReason, failureKind, ...exclusions } : exclusions
|
|
6684
|
+
),
|
|
6685
|
+
isOauthNow: () => ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD === "oauth"
|
|
6686
|
+
});
|
|
6596
6687
|
}
|
|
6597
6688
|
async applyCredentialsResponse(response) {
|
|
6598
6689
|
if (response.type === "oauth") {
|
|
@@ -6990,6 +7081,7 @@ var EnvironmentDetailsService = class {
|
|
|
6990
7081
|
details.cursorAuthMethod = detectCursorAuthMethod();
|
|
6991
7082
|
details.opencodeAuthMethod = detectOpencodeAuthMethod();
|
|
6992
7083
|
details.piAuthMethod = detectPiAuthMethod();
|
|
7084
|
+
details.credentialFallbacks = listCredentialFallbacks();
|
|
6993
7085
|
details.gitIdentityConfigured = gitIdentityConfigured;
|
|
6994
7086
|
const ghConfigured = existsSync2(GH_HOSTS_PATH);
|
|
6995
7087
|
details.githubAccessConfigured = ghConfigured;
|
|
@@ -8262,6 +8354,48 @@ async function removeTempImageFiles(paths) {
|
|
|
8262
8354
|
await Promise.allSettled(paths.map((path6) => unlink2(path6)));
|
|
8263
8355
|
}
|
|
8264
8356
|
|
|
8357
|
+
// src/managers/auth-fallback.ts
|
|
8358
|
+
var SWAPPABLE_AGENTS = {
|
|
8359
|
+
claude: claudeTokenManager,
|
|
8360
|
+
codex: codexTokenManager
|
|
8361
|
+
};
|
|
8362
|
+
var AuthFallbackCoordinator = class {
|
|
8363
|
+
constructor(provider, record) {
|
|
8364
|
+
this.provider = provider;
|
|
8365
|
+
this.record = record;
|
|
8366
|
+
}
|
|
8367
|
+
provider;
|
|
8368
|
+
record;
|
|
8369
|
+
async fallBack(reason, detail) {
|
|
8370
|
+
const tokenManager = SWAPPABLE_AGENTS[this.provider];
|
|
8371
|
+
if (!tokenManager) return "unavailable";
|
|
8372
|
+
const exhausted = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[this.provider];
|
|
8373
|
+
if (!exhausted) return "unavailable";
|
|
8374
|
+
recordExhaustedCredential(this.provider, exhausted);
|
|
8375
|
+
const swap = await tokenManager.fetchFreshCredentials(
|
|
8376
|
+
`${this.provider} ${reason === "out_of_credits" ? "is out of credits" : "hit its usage limit"}: ${detail}`,
|
|
8377
|
+
"exhausted"
|
|
8378
|
+
);
|
|
8379
|
+
if (!swap.ok && swap.error.code === "no_credentials") return "unavailable";
|
|
8380
|
+
const applied = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[this.provider] ?? exhausted;
|
|
8381
|
+
const moved = swap.ok && !(applied.method === exhausted.method && applied.scope === exhausted.scope);
|
|
8382
|
+
const payload = {
|
|
8383
|
+
provider: this.provider,
|
|
8384
|
+
reason,
|
|
8385
|
+
exhaustedMethod: exhausted.method,
|
|
8386
|
+
exhaustedScope: exhausted.scope,
|
|
8387
|
+
candidateMethod: applied.method,
|
|
8388
|
+
candidateScope: applied.scope,
|
|
8389
|
+
status: moved ? "switched" : "failed",
|
|
8390
|
+
detail: moved ? detail : "Replicas could not load an alternate credential."
|
|
8391
|
+
};
|
|
8392
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
8393
|
+
this.record({ timestamp: at, type: AUTH_FALLBACK_EVENT_TYPE, payload: { ...payload } });
|
|
8394
|
+
recordCredentialFallback({ ...payload, at });
|
|
8395
|
+
return moved ? "switched" : "failed";
|
|
8396
|
+
}
|
|
8397
|
+
};
|
|
8398
|
+
|
|
8265
8399
|
// src/services/message-queue-service.ts
|
|
8266
8400
|
var MessageQueueService = class {
|
|
8267
8401
|
queue = [];
|
|
@@ -8445,6 +8579,7 @@ var CodingAgentManager = class {
|
|
|
8445
8579
|
onEvent;
|
|
8446
8580
|
hostOnTurnComplete;
|
|
8447
8581
|
onProcessingChanged;
|
|
8582
|
+
authFallback;
|
|
8448
8583
|
compacting = false;
|
|
8449
8584
|
constructor(options) {
|
|
8450
8585
|
this.workingDirectory = options.workingDirectory ?? ENGINE_ENV.WORKSPACE_ROOT;
|
|
@@ -8454,6 +8589,14 @@ var CodingAgentManager = class {
|
|
|
8454
8589
|
this.hostOnTurnComplete = options.onTurnComplete;
|
|
8455
8590
|
this.onProcessingChanged = options.onProcessingChanged ?? (() => {
|
|
8456
8591
|
});
|
|
8592
|
+
this.authFallback = options.provider ? new AuthFallbackCoordinator(options.provider, (event) => {
|
|
8593
|
+
this.onEvent(event);
|
|
8594
|
+
this.getHistorySink()?.append(event);
|
|
8595
|
+
}) : null;
|
|
8596
|
+
}
|
|
8597
|
+
async fallBackCredential(reason, detail) {
|
|
8598
|
+
if (!this.authFallback) return "unavailable";
|
|
8599
|
+
return this.authFallback.fallBack(reason, detail);
|
|
8457
8600
|
}
|
|
8458
8601
|
onTurnComplete = async () => {
|
|
8459
8602
|
if (this.compacting) {
|
|
@@ -9444,7 +9587,16 @@ var ClaudeTransientTurnError = class extends Error {
|
|
|
9444
9587
|
}
|
|
9445
9588
|
midTurn;
|
|
9446
9589
|
};
|
|
9590
|
+
var ClaudeQuotaError = class extends Error {
|
|
9591
|
+
constructor(message, kind) {
|
|
9592
|
+
super(message);
|
|
9593
|
+
this.kind = kind;
|
|
9594
|
+
this.name = "ClaudeQuotaError";
|
|
9595
|
+
}
|
|
9596
|
+
kind;
|
|
9597
|
+
};
|
|
9447
9598
|
var MAX_AUTH_RETRIES = 2;
|
|
9599
|
+
var MAX_QUOTA_FALLBACKS = 2;
|
|
9448
9600
|
var MAX_TRANSIENT_RETRIES = 2;
|
|
9449
9601
|
var MAX_MIDTURN_CONTINUE_RETRIES = 2;
|
|
9450
9602
|
var TRANSIENT_RETRY_DELAYS_MS = [1e3, 2500];
|
|
@@ -9490,7 +9642,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
9490
9642
|
slashCommandsDiscovery = null;
|
|
9491
9643
|
authRetrying = false;
|
|
9492
9644
|
constructor(options) {
|
|
9493
|
-
super(options);
|
|
9645
|
+
super({ ...options, provider: AGENT.CLAUDE });
|
|
9494
9646
|
this.historyFilePath = options.historyFilePath ?? join16(homedir12(), ".replicas", "claude", "history.jsonl");
|
|
9495
9647
|
this.historyFile = new CodexHistoryFile(this.historyFilePath);
|
|
9496
9648
|
this.systemPromptOverride = options.systemPromptOverride;
|
|
@@ -9722,6 +9874,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
9722
9874
|
async processMessageInternal(request) {
|
|
9723
9875
|
let lastError;
|
|
9724
9876
|
let authRetryExhausted = false;
|
|
9877
|
+
let quotaFallbacks = 0;
|
|
9725
9878
|
let attempt = 0;
|
|
9726
9879
|
let authRetries = 0;
|
|
9727
9880
|
let transientRetries = 0;
|
|
@@ -9734,6 +9887,19 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
9734
9887
|
return;
|
|
9735
9888
|
} catch (error) {
|
|
9736
9889
|
lastError = error;
|
|
9890
|
+
const quotaKind = _ClaudeManager.quotaLimitKind(error);
|
|
9891
|
+
if (quotaKind) {
|
|
9892
|
+
const detail = error instanceof ClaudeQuotaError ? error.message : extractErrorText(error);
|
|
9893
|
+
const outcome = quotaFallbacks < MAX_QUOTA_FALLBACKS ? await this.fallBackCredential(quotaKind, detail) : "unavailable";
|
|
9894
|
+
if (outcome === "switched") {
|
|
9895
|
+
quotaFallbacks++;
|
|
9896
|
+
await this.tearDownSession();
|
|
9897
|
+
attempt++;
|
|
9898
|
+
continue;
|
|
9899
|
+
}
|
|
9900
|
+
await this.emitQuotaExhaustedEvent(quotaKind, outcome, detail);
|
|
9901
|
+
return;
|
|
9902
|
+
}
|
|
9737
9903
|
if (_ClaudeManager.isAuthError(error)) {
|
|
9738
9904
|
if (authRetries >= MAX_AUTH_RETRIES) {
|
|
9739
9905
|
authRetryExhausted = true;
|
|
@@ -9748,7 +9914,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
9748
9914
|
const refreshed = await claudeTokenManager.fetchFreshCredentials(
|
|
9749
9915
|
error instanceof Error ? error.message : String(error)
|
|
9750
9916
|
);
|
|
9751
|
-
if (!refreshed) {
|
|
9917
|
+
if (!refreshed.ok) {
|
|
9752
9918
|
authRetryExhausted = true;
|
|
9753
9919
|
throw error;
|
|
9754
9920
|
}
|
|
@@ -9840,6 +10006,11 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
9840
10006
|
return "Claude authentication failed after multiple attempts. Check your Claude, Anthropic API key, or Bedrock credentials in Settings \u2192 Agents and try again.";
|
|
9841
10007
|
}
|
|
9842
10008
|
}
|
|
10009
|
+
async emitQuotaExhaustedEvent(kind, outcome, detail) {
|
|
10010
|
+
const limit = kind === "out_of_credits" ? "Claude ran out of credits on the credential this workspace is using." : "Claude hit the usage limit on the credential this workspace is using.";
|
|
10011
|
+
const next = outcome === "failed" ? "Switching to the alternate credential failed. Check the credentials in Settings \u2192 Agents and try again." : "No other credential is available for this workspace. Add one in Settings \u2192 Agents, or try again once the limit resets.";
|
|
10012
|
+
await this.emitTerminalErrorResult(detail, [`${limit} ${next}`]);
|
|
10013
|
+
}
|
|
9843
10014
|
async emitMidTurnExhaustedEvent(error) {
|
|
9844
10015
|
const detail = error instanceof Error ? error.message : String(error);
|
|
9845
10016
|
await this.emitTerminalErrorResult(
|
|
@@ -10097,6 +10268,17 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10097
10268
|
}
|
|
10098
10269
|
return;
|
|
10099
10270
|
}
|
|
10271
|
+
const quotaError = _ClaudeManager.detectQuotaErrorInMessage(msg);
|
|
10272
|
+
if (quotaError) {
|
|
10273
|
+
suppressLinearResponseFlush = true;
|
|
10274
|
+
this.failPendingTurn(quotaError);
|
|
10275
|
+
try {
|
|
10276
|
+
response.close();
|
|
10277
|
+
} catch (err) {
|
|
10278
|
+
console.warn("[ClaudeManager] query.close() during quota error failed:", err);
|
|
10279
|
+
}
|
|
10280
|
+
return;
|
|
10281
|
+
}
|
|
10100
10282
|
const transientErrorMessage = _ClaudeManager.detectTransientTurnErrorInMessage(msg);
|
|
10101
10283
|
if (transientErrorMessage) {
|
|
10102
10284
|
suppressLinearResponseFlush = true;
|
|
@@ -10293,6 +10475,22 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
|
|
|
10293
10475
|
if (error instanceof ClaudeAuthError) return true;
|
|
10294
10476
|
return isClaudeAuthErrorText(error instanceof Error ? error.message : String(error));
|
|
10295
10477
|
}
|
|
10478
|
+
static quotaLimitKind(error) {
|
|
10479
|
+
if (error instanceof ClaudeQuotaError) return error.kind;
|
|
10480
|
+
return detectAgentQuotaLimit(extractErrorText(error));
|
|
10481
|
+
}
|
|
10482
|
+
static detectQuotaErrorInMessage(message) {
|
|
10483
|
+
if (message.type === "assistant" && "error" in message) {
|
|
10484
|
+
const content = Array.isArray(message.message?.content) ? message.message.content.map((block) => "text" in block ? block.text : "").join("\n") : "";
|
|
10485
|
+
const text2 = [typeof message.error === "string" ? message.error : "", content].filter(Boolean).join("\n");
|
|
10486
|
+
const kind2 = detectAgentQuotaLimit(text2);
|
|
10487
|
+
if (kind2) return new ClaudeQuotaError(text2, kind2);
|
|
10488
|
+
}
|
|
10489
|
+
if (message.type !== "result" || !message.is_error) return null;
|
|
10490
|
+
const text = message.subtype === "success" ? message.result : message.errors.join("\n");
|
|
10491
|
+
const kind = detectAgentQuotaLimit(text);
|
|
10492
|
+
return kind ? new ClaudeQuotaError(text, kind) : null;
|
|
10493
|
+
}
|
|
10296
10494
|
static isTransientTurnError(error) {
|
|
10297
10495
|
if (error instanceof ClaudeTransientTurnError) return true;
|
|
10298
10496
|
return _ClaudeManager.isTransientTurnErrorText(extractErrorText(error));
|
|
@@ -10670,7 +10868,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
|
|
|
10670
10868
|
var MIN_CODEX_CLI_VERSION = "0.144.6";
|
|
10671
10869
|
var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
|
|
10672
10870
|
var codexCliVersionEnsured = null;
|
|
10673
|
-
var ENGINE_PACKAGE_VERSION = "0.1.
|
|
10871
|
+
var ENGINE_PACKAGE_VERSION = "0.1.611";
|
|
10674
10872
|
var INITIALIZE_METHOD = "initialize";
|
|
10675
10873
|
var INITIALIZED_NOTIFICATION = "initialized";
|
|
10676
10874
|
var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
|
|
@@ -11267,7 +11465,7 @@ function isQuotaTurnFailure(turn) {
|
|
|
11267
11465
|
return false;
|
|
11268
11466
|
}
|
|
11269
11467
|
function isQuotaErrorMessage(error) {
|
|
11270
|
-
return error instanceof Error &&
|
|
11468
|
+
return error instanceof Error && detectAgentQuotaLimit(error.message) !== null;
|
|
11271
11469
|
}
|
|
11272
11470
|
function formatTurnFailure(turn) {
|
|
11273
11471
|
const turnError = turn.error;
|
|
@@ -11637,6 +11835,12 @@ var DuplicateDefaultChatError = class extends Error {
|
|
|
11637
11835
|
};
|
|
11638
11836
|
|
|
11639
11837
|
// src/managers/codex-asp/codex-asp-manager.ts
|
|
11838
|
+
var CodexQuotaError = class extends Error {
|
|
11839
|
+
constructor(message) {
|
|
11840
|
+
super(message);
|
|
11841
|
+
this.name = "CodexQuotaError";
|
|
11842
|
+
}
|
|
11843
|
+
};
|
|
11640
11844
|
var GOAL_TURN_CONTINUATION_GRACE_MS = 5e3;
|
|
11641
11845
|
var CODEX_SLASH_COMMANDS_CACHE_MS = 3e4;
|
|
11642
11846
|
var POLICY_REASON_INJECTION_TIMEOUT_MS = 5e3;
|
|
@@ -11721,7 +11925,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
11721
11925
|
steeredTempImagePaths = [];
|
|
11722
11926
|
steerLock = new AsyncLock();
|
|
11723
11927
|
constructor(options) {
|
|
11724
|
-
super(options);
|
|
11928
|
+
super({ ...options, provider: AGENT.CODEX });
|
|
11725
11929
|
this.historyFile = options.historyFilePath ? new CodexHistoryFile(options.historyFilePath) : null;
|
|
11726
11930
|
this.initializeManager(this.processMessageInternal.bind(this));
|
|
11727
11931
|
}
|
|
@@ -11901,6 +12105,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
11901
12105
|
}
|
|
11902
12106
|
async processMessageInternal(request) {
|
|
11903
12107
|
let userMessageRecorded = false;
|
|
12108
|
+
let quotaFallbackAttempted = false;
|
|
11904
12109
|
const recordUserMessage = (extraPayload = {}) => {
|
|
11905
12110
|
if (userMessageRecorded) return;
|
|
11906
12111
|
userMessageRecorded = true;
|
|
@@ -11946,17 +12151,26 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
11946
12151
|
await dispatch();
|
|
11947
12152
|
} catch (error) {
|
|
11948
12153
|
let terminalError = error;
|
|
12154
|
+
if (terminalError instanceof CodexQuotaError && !quotaFallbackAttempted) {
|
|
12155
|
+
quotaFallbackAttempted = true;
|
|
12156
|
+
const outcome = await this.fallBackCredential(
|
|
12157
|
+
detectAgentQuotaLimit(terminalError.message) ?? "rate_limit",
|
|
12158
|
+
terminalError.message
|
|
12159
|
+
);
|
|
12160
|
+
if (outcome === "switched") {
|
|
12161
|
+
const retryFailure = await this.restartAndRetry(dispatch);
|
|
12162
|
+
if (!retryFailure) return;
|
|
12163
|
+
terminalError = retryFailure.error;
|
|
12164
|
+
} else if (await this.confirmOutOfCredits(await getCodexAspHost())) {
|
|
12165
|
+
return;
|
|
12166
|
+
}
|
|
12167
|
+
}
|
|
11949
12168
|
if (isCodexAuthError(terminalError)) {
|
|
11950
12169
|
const refreshed = await codexTokenManager.fetchFreshCredentials(terminalError instanceof Error ? terminalError.message : String(terminalError));
|
|
11951
|
-
if (refreshed) {
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
await dispatch();
|
|
11956
|
-
return;
|
|
11957
|
-
} catch (retryError) {
|
|
11958
|
-
terminalError = retryError;
|
|
11959
|
-
}
|
|
12170
|
+
if (refreshed.ok) {
|
|
12171
|
+
const retryFailure = await this.restartAndRetry(dispatch);
|
|
12172
|
+
if (!retryFailure) return;
|
|
12173
|
+
terminalError = retryFailure.error;
|
|
11960
12174
|
}
|
|
11961
12175
|
}
|
|
11962
12176
|
const event = this.recordHistoryEvent("codex-asp-error", {
|
|
@@ -12008,16 +12222,14 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
12008
12222
|
try {
|
|
12009
12223
|
completedTurn = await runTurn(host, threadId);
|
|
12010
12224
|
} catch (error) {
|
|
12011
|
-
if (isQuotaErrorMessage(error)
|
|
12012
|
-
|
|
12225
|
+
if (isQuotaErrorMessage(error)) {
|
|
12226
|
+
throw new CodexQuotaError(error instanceof Error ? error.message : String(error));
|
|
12013
12227
|
}
|
|
12014
12228
|
throw error;
|
|
12015
12229
|
}
|
|
12016
12230
|
if (completedTurn.status === "failed") {
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
}
|
|
12020
|
-
throw new Error(formatTurnFailure(completedTurn));
|
|
12231
|
+
const failure = formatTurnFailure(completedTurn);
|
|
12232
|
+
throw isQuotaTurnFailure(completedTurn) ? new CodexQuotaError(failure) : new Error(failure);
|
|
12021
12233
|
}
|
|
12022
12234
|
this.emitQuotaEvent(this.quotaStatus.recordTurnSuccess());
|
|
12023
12235
|
}
|
|
@@ -12887,6 +13099,16 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
12887
13099
|
} catch {
|
|
12888
13100
|
}
|
|
12889
13101
|
}
|
|
13102
|
+
async restartAndRetry(dispatch) {
|
|
13103
|
+
try {
|
|
13104
|
+
await restartCodexAspHost();
|
|
13105
|
+
this.threadAttached = false;
|
|
13106
|
+
await dispatch();
|
|
13107
|
+
return null;
|
|
13108
|
+
} catch (error) {
|
|
13109
|
+
return { error };
|
|
13110
|
+
}
|
|
13111
|
+
}
|
|
12890
13112
|
async confirmOutOfCredits(host) {
|
|
12891
13113
|
await this.refreshQuotaSnapshot(host);
|
|
12892
13114
|
const event = this.quotaStatus.confirmOutOfCredits();
|
|
@@ -16653,7 +16875,7 @@ var ChatService = class {
|
|
|
16653
16875
|
}
|
|
16654
16876
|
}).catch(() => {
|
|
16655
16877
|
});
|
|
16656
|
-
if (event.type === "replicas-tool-input-request" || event.type === "replicas-tool-input-resolved" || event.type === "compaction-status" || event.type === AUTH_RETRY_STATUS_EVENT_TYPE || event.type === CHAT_GOAL_EVENT_TYPE || event.type === CLAUDE_ACTIVITY_STATUS_EVENT_TYPE || event.type === "claude-result" || event.type === "claude-system" && (event.payload.subtype === "init" || event.payload.subtype === "session_state_changed" || event.payload.subtype === "background_tasks_changed" || coerceBackgroundTaskPayload(event.payload) !== null)) {
|
|
16878
|
+
if (event.type === "replicas-tool-input-request" || event.type === "replicas-tool-input-resolved" || event.type === "compaction-status" || event.type === AUTH_RETRY_STATUS_EVENT_TYPE || event.type === AUTH_FALLBACK_EVENT_TYPE || event.type === CHAT_GOAL_EVENT_TYPE || event.type === CLAUDE_ACTIVITY_STATUS_EVENT_TYPE || event.type === "claude-result" || event.type === "claude-system" && (event.payload.subtype === "init" || event.payload.subtype === "session_state_changed" || event.payload.subtype === "background_tasks_changed" || coerceBackgroundTaskPayload(event.payload) !== null)) {
|
|
16657
16879
|
this.publish({
|
|
16658
16880
|
type: "chat.updated",
|
|
16659
16881
|
payload: { chat: this.toSummary(chat) }
|