replicas-engine 0.1.1293 → 0.1.1294

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.
@@ -8,12 +8,12 @@ import {
8
8
  listRunningCodexModels,
9
9
  restartCodexAspHost,
10
10
  restartCodexAspHostIfRunning
11
- } from "./chunk-FI7FILEX.js";
12
- import "./chunk-5MO5TWHJ.js";
13
- import "./chunk-6C472IYE.js";
14
- import "./chunk-CYBM2QOH.js";
11
+ } from "./chunk-ILADSZLY.js";
12
+ import "./chunk-22SSG3WW.js";
13
+ import "./chunk-BGMMK24G.js";
14
+ import "./chunk-ZGHPW4OS.js";
15
15
  import "./chunk-UZSNFLDQ.js";
16
- import "./chunk-BG4HQ56D.js";
16
+ import "./chunk-4A6I7LPY.js";
17
17
  import "./chunk-VEQXQN22.js";
18
18
  export {
19
19
  CODEX_ULTRAFAST_SERVICE_TIER,
@@ -4,7 +4,7 @@ const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  SUBPROCESS_MAX_BUFFER,
6
6
  execFileAsync
7
- } from "./chunk-CYBM2QOH.js";
7
+ } from "./chunk-ZGHPW4OS.js";
8
8
  import {
9
9
  isRecord as isRecord2
10
10
  } from "./chunk-UZSNFLDQ.js";
@@ -22,7 +22,7 @@ import {
22
22
  provisionedDeviceSchema,
23
23
  readReplicasRuntimeEnv,
24
24
  serializePosixEnvFile
25
- } from "./chunk-BG4HQ56D.js";
25
+ } from "./chunk-4A6I7LPY.js";
26
26
 
27
27
  // src/runtime-env-loader.ts
28
28
  import { readFileSync } from "fs";
@@ -891,7 +891,11 @@ function paginateEngineLogContent(input) {
891
891
  }
892
892
 
893
893
  // ../shared/src/agent-quota.ts
894
- var AGENT_FALLBACK_REASONS = ["rate_limit", "out_of_credits", "credits_required", "auth_failed"];
894
+ var AGENT_FALLBACK_REASONS = ["rate_limit", "out_of_credits", "credits_required", "auth_failed", "model_unavailable"];
895
+ var EXHAUSTED_COOLDOWN_MS = 60 * 60 * 1e3;
896
+ function exhaustedUntil(resetsAt) {
897
+ return new Date(resetsAt ?? Date.now() + EXHAUSTED_COOLDOWN_MS);
898
+ }
895
899
  function isAgentFallbackReason(value) {
896
900
  return AGENT_FALLBACK_REASONS.some((reason) => reason === value);
897
901
  }
@@ -2250,7 +2254,7 @@ function describeAuthFallback(payload) {
2250
2254
  });
2251
2255
  const switched = payload.status === "switched";
2252
2256
  return {
2253
- headline: payload.reason === "out_of_credits" ? `${agent} is out of credits on ${exhausted}` : payload.reason === "auth_failed" ? `${agent} could not authenticate with ${exhausted}` : payload.reason === "credits_required" ? `The selected model needs usage credits on ${exhausted}` : `${agent} hit its usage limit on ${exhausted}`,
2257
+ headline: payload.reason === "out_of_credits" ? `${agent} is out of credits on ${exhausted}` : payload.reason === "auth_failed" ? `${agent} could not authenticate with ${exhausted}` : payload.reason === "credits_required" ? `The selected model needs usage credits on ${exhausted}` : payload.reason === "model_unavailable" ? `The selected model is not available on ${exhausted}` : `${agent} hit its usage limit on ${exhausted}`,
2254
2258
  outcomeClause: switched ? `switched to ${describeCredential(payload.provider, {
2255
2259
  method: payload.candidateMethod,
2256
2260
  scope: payload.candidateScope,
@@ -2333,6 +2337,7 @@ var forkChatRequestSchema = z10.object({
2333
2337
  function describeQuotaLimit(reason) {
2334
2338
  if (reason === "auth_failed") return "could not authenticate";
2335
2339
  if (reason === "credits_required") return "needed usage credits for the selected model";
2340
+ if (reason === "model_unavailable") return "could not serve the selected model";
2336
2341
  return reason === "out_of_credits" ? "ran out of credits" : "hit its usage limit";
2337
2342
  }
2338
2343
  function describeChatForkReason(reason, provider2) {
@@ -10567,6 +10572,7 @@ export {
10567
10572
  isValidThinkingLevel,
10568
10573
  codexReasoningEffortForThinkingLevel,
10569
10574
  getProviderDisplayName,
10575
+ exhaustedUntil,
10570
10576
  detectAgentQuotaLimit,
10571
10577
  listOpenAICompatibleModels,
10572
10578
  resolveOpenAICompatibleModelsUrl,
@@ -5,14 +5,14 @@ import {
5
5
  getOriginDefaultBranch,
6
6
  monolithRequest,
7
7
  runGitCommand
8
- } from "./chunk-5MO5TWHJ.js";
8
+ } from "./chunk-22SSG3WW.js";
9
9
  import {
10
10
  extractCommandProtectionCommandText,
11
11
  findCommandProtectionGitLookups,
12
12
  findGitCommitSignals,
13
13
  findPrMergeSignals,
14
14
  parseCommandProtectionRequest
15
- } from "./chunk-BG4HQ56D.js";
15
+ } from "./chunk-4A6I7LPY.js";
16
16
 
17
17
  // src/services/command-protection-service.ts
18
18
  import { readFile, stat } from "fs/promises";
@@ -3,10 +3,10 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  execFileAsync
6
- } from "./chunk-CYBM2QOH.js";
6
+ } from "./chunk-ZGHPW4OS.js";
7
7
  import {
8
8
  isVersionBelow
9
- } from "./chunk-BG4HQ56D.js";
9
+ } from "./chunk-4A6I7LPY.js";
10
10
 
11
11
  // src/utils/codex-agent-env.ts
12
12
  function buildCodexAgentEnv(source = process.env) {
@@ -240,7 +240,7 @@ function getCodexModelProvider(method) {
240
240
  if (method === "foundry") return "azure";
241
241
  return method === "ollama" || method === "openai-compatible" ? "replicas_openai_compatible" : "openai";
242
242
  }
243
- var ENGINE_PACKAGE_VERSION = "0.1.1293";
243
+ var ENGINE_PACKAGE_VERSION = "0.1.1294";
244
244
  var INITIALIZE_METHOD = "initialize";
245
245
  var INITIALIZED_NOTIFICATION = "initialized";
246
246
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -5,7 +5,7 @@ import {
5
5
  extractCommandProtectionCommandText,
6
6
  isRecord,
7
7
  parseCodeHostPrUrl
8
- } from "./chunk-BG4HQ56D.js";
8
+ } from "./chunk-4A6I7LPY.js";
9
9
 
10
10
  // src/services/published-pull-request-urls.ts
11
11
  function publishedPullRequestUrls(result) {
@@ -10,15 +10,15 @@ import {
10
10
  resolveDiffBase,
11
11
  runGitCommand,
12
12
  setAgentCredentialSnapshot
13
- } from "./chunk-5MO5TWHJ.js";
13
+ } from "./chunk-22SSG3WW.js";
14
14
  import {
15
15
  AppServerProcess,
16
16
  buildCodexAgentEnv
17
- } from "./chunk-6C472IYE.js";
17
+ } from "./chunk-BGMMK24G.js";
18
18
  import {
19
19
  SUBPROCESS_MAX_BUFFER,
20
20
  execFileAsync
21
- } from "./chunk-CYBM2QOH.js";
21
+ } from "./chunk-ZGHPW4OS.js";
22
22
  import {
23
23
  isRecord
24
24
  } from "./chunk-UZSNFLDQ.js";
@@ -43,6 +43,7 @@ import {
43
43
  createSuccessResult,
44
44
  decodePathSegments,
45
45
  detectAgentQuotaLimit,
46
+ exhaustedUntil,
46
47
  fileContentDiff,
47
48
  isGitHubUrl,
48
49
  isSameCredential,
@@ -55,7 +56,7 @@ import {
55
56
  resolveOpenAICompatibleModelsUrl,
56
57
  splitMothershipInstructions,
57
58
  withTimeout
58
- } from "./chunk-BG4HQ56D.js";
59
+ } from "./chunk-4A6I7LPY.js";
59
60
 
60
61
  // src/managers/codex-token-manager.ts
61
62
  import { promises as fs } from "fs";
@@ -81,9 +82,19 @@ function applyAuthEnvTransition(params) {
81
82
  // src/services/credential-fallbacks.ts
82
83
  var fallbacksByAgent = /* @__PURE__ */ new Map();
83
84
  var exhaustedByAgent = /* @__PURE__ */ new Map();
85
+ var fallbackModels = /* @__PURE__ */ new Map();
84
86
  function clearCredentialFallbacks(provider) {
85
87
  fallbacksByAgent.delete(provider);
86
88
  exhaustedByAgent.delete(provider);
89
+ fallbackModels.delete(provider);
90
+ }
91
+ function recordFallbackModel(provider, credential, model) {
92
+ fallbackModels.set(provider, { credential, model });
93
+ }
94
+ function getFallbackModel(provider) {
95
+ const chosen = fallbackModels.get(provider);
96
+ const live = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[provider];
97
+ return chosen && live && isSameCredential(chosen.credential, live) ? chosen.model : void 0;
87
98
  }
88
99
  function recordCredentialFallback(notice) {
89
100
  fallbacksByAgent.set(notice.provider, notice);
@@ -96,7 +107,8 @@ function listCredentialFallbacks() {
96
107
  });
97
108
  }
98
109
  function listExhaustedCredentials(provider) {
99
- return [...exhaustedByAgent.get(provider)?.values() ?? []];
110
+ const now = Date.now();
111
+ return [...exhaustedByAgent.get(provider)?.values() ?? []].filter((spent) => spent.until > now).map((spent) => spent.credential);
100
112
  }
101
113
  function routineRefreshRequest(provider) {
102
114
  const live = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[provider];
@@ -106,9 +118,12 @@ function routineRefreshRequest(provider) {
106
118
  ...live ? { currentCredential: live } : {}
107
119
  };
108
120
  }
109
- function recordExhaustedCredential(provider, credential) {
121
+ function recordExhaustedCredential(provider, credential, resetsAt) {
110
122
  const spent = exhaustedByAgent.get(provider) ?? /* @__PURE__ */ new Map();
111
- spent.set(`${credential.method}|${credential.scope}|${credential.credentialId ?? ""}`, credential);
123
+ spent.set(`${credential.method}|${credential.scope}|${credential.credentialId ?? ""}`, {
124
+ credential,
125
+ until: exhaustedUntil(resetsAt).getTime()
126
+ });
112
127
  exhaustedByAgent.set(provider, spent);
113
128
  }
114
129
 
@@ -191,9 +206,9 @@ var BaseRefreshManager = class {
191
206
  ...excludeCredentials.length > 0 ? { excludeCredentials } : {},
192
207
  ...params.allowedMethods ? { allowedMethods: [...params.allowedMethods] } : {}
193
208
  });
194
- if (params.isOauthNow()) {
209
+ if (params.keepRefreshing()) {
195
210
  this.start().catch((error) => {
196
- console.error(`[${this.managerName}] Failed to restart OAuth refresh service after fallback:`, error);
211
+ console.error(`[${this.managerName}] Failed to restart the refresh service after fallback:`, error);
197
212
  });
198
213
  }
199
214
  return createSuccessResult();
@@ -291,7 +306,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
291
306
  await this.applyCredentialUpdate(++this.credentialGeneration, async () => {
292
307
  await this.applyCredentialsResponse(data);
293
308
  setAgentCredentialSnapshot("codex", data);
294
- const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-4YRJNFYD.js");
309
+ const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-RKGMFLDD.js");
295
310
  await restartCodexAspHostIfRunning2();
296
311
  });
297
312
  await this.start(true);
@@ -313,7 +328,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
313
328
  const accountChanged = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.codex?.credentialId !== data.credentialId;
314
329
  await this.applyCredentialsResponse(data);
315
330
  if (restartOnChange && (accountChanged || CODEX_AUTH_ENV_KEYS.some((key, index) => process.env[key] !== previousEnv[index]))) {
316
- const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-4YRJNFYD.js");
331
+ const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-RKGMFLDD.js");
317
332
  await restartCodexAspHostIfRunning2();
318
333
  }
319
334
  setAgentCredentialSnapshot("codex", data);
@@ -381,7 +396,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
381
396
  } : exclusions
382
397
  );
383
398
  },
384
- isOauthNow: () => ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD === "oauth"
399
+ keepRefreshing: () => ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD === "oauth"
385
400
  });
386
401
  }
387
402
  async applyCredentialsResponse(response) {
@@ -2028,6 +2043,8 @@ async function listRunningCodexModels() {
2028
2043
 
2029
2044
  export {
2030
2045
  clearCredentialFallbacks,
2046
+ recordFallbackModel,
2047
+ getFallbackModel,
2031
2048
  recordCredentialFallback,
2032
2049
  listCredentialFallbacks,
2033
2050
  routineRefreshRequest,
@@ -3,7 +3,7 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  AspClient
6
- } from "./chunk-6C472IYE.js";
6
+ } from "./chunk-BGMMK24G.js";
7
7
  import {
8
8
  ASTER_BASE_URL,
9
9
  ASTER_PROVIDER,
@@ -15,7 +15,7 @@ import {
15
15
  isRecord,
16
16
  readOllamaAuthEnv,
17
17
  readOpenAICompatibleAuthEnv
18
- } from "./chunk-BG4HQ56D.js";
18
+ } from "./chunk-4A6I7LPY.js";
19
19
 
20
20
  // src/managers/codex-asp/notification-dispatch.ts
21
21
  var TURN_STARTED_METHOD = "turn/started";
@@ -3,7 +3,7 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  HOOK_EXEC_MAX_BUFFER_BYTES
6
- } from "./chunk-BG4HQ56D.js";
6
+ } from "./chunk-4A6I7LPY.js";
7
7
 
8
8
  // src/utils/exec.ts
9
9
  import { exec, execFile } from "child_process";
@@ -3,13 +3,13 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  evaluateCommandProtection
6
- } from "./chunk-W7IRX3ZE.js";
7
- import "./chunk-5MO5TWHJ.js";
8
- import "./chunk-CYBM2QOH.js";
6
+ } from "./chunk-A2KHDTEF.js";
7
+ import "./chunk-22SSG3WW.js";
8
+ import "./chunk-ZGHPW4OS.js";
9
9
  import {
10
10
  isRecord
11
11
  } from "./chunk-UZSNFLDQ.js";
12
- import "./chunk-BG4HQ56D.js";
12
+ import "./chunk-4A6I7LPY.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/command-protection-hook.ts
@@ -14,12 +14,12 @@ import {
14
14
  recoverCompletedTurn,
15
15
  spawnMuseMsp,
16
16
  startMuseSession
17
- } from "./chunk-B3D66EN3.js";
17
+ } from "./chunk-JFKNSCBZ.js";
18
18
  import {
19
19
  AppServerProcess,
20
20
  buildCodexAgentEnv
21
- } from "./chunk-6C472IYE.js";
22
- import "./chunk-CYBM2QOH.js";
21
+ } from "./chunk-BGMMK24G.js";
22
+ import "./chunk-ZGHPW4OS.js";
23
23
  import {
24
24
  AGENT,
25
25
  OPENCODE_GO_PROVIDER,
@@ -29,7 +29,7 @@ import {
29
29
  headlessAgentUsagePath,
30
30
  isRecord,
31
31
  withTimeout
32
- } from "./chunk-BG4HQ56D.js";
32
+ } from "./chunk-4A6I7LPY.js";
33
33
  import "./chunk-VEQXQN22.js";
34
34
 
35
35
  // src/headless-agent.ts
package/dist/src/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  evaluateCommandProtection,
28
28
  extractToolCommand,
29
29
  reportCommandProtectionBlock
30
- } from "./chunk-W7IRX3ZE.js";
30
+ } from "./chunk-A2KHDTEF.js";
31
31
  import {
32
32
  ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
33
33
  AGENT_MESSAGE_DELTA_METHOD,
@@ -65,10 +65,10 @@ import {
65
65
  spawnMuseMsp,
66
66
  startMuseSession,
67
67
  uuidV7
68
- } from "./chunk-B3D66EN3.js";
68
+ } from "./chunk-JFKNSCBZ.js";
69
69
  import {
70
70
  publishedPullRequestUrls
71
- } from "./chunk-TZ43BQUR.js";
71
+ } from "./chunk-DGO2DXHA.js";
72
72
  import {
73
73
  ACCOUNT_RATE_LIMITS_READ_METHOD,
74
74
  AsyncLock,
@@ -117,6 +117,7 @@ import {
117
117
  formatTurnFailure,
118
118
  getAgentAdditionalDirectories,
119
119
  getCodexAspHost,
120
+ getFallbackModel,
120
121
  getReconciledActiveTurnId,
121
122
  gitService,
122
123
  isQuotaErrorMessage,
@@ -129,6 +130,7 @@ import {
129
130
  normalizeImages,
130
131
  recordCredentialFallback,
131
132
  recordExhaustedCredential,
133
+ recordFallbackModel,
132
134
  refreshDelayMs,
133
135
  removeCredentialFileLines,
134
136
  removeTempImageFiles,
@@ -141,7 +143,7 @@ import {
141
143
  timestampFromSeconds,
142
144
  upsertCredentialFileLines,
143
145
  writeSecureCredentialFile
144
- } from "./chunk-FI7FILEX.js";
146
+ } from "./chunk-ILADSZLY.js";
145
147
  import {
146
148
  ENGINE_ENV,
147
149
  monolithRequest,
@@ -151,15 +153,15 @@ import {
151
153
  resolveDiffBase,
152
154
  setAgentCredentialSnapshot,
153
155
  setLinearSessionId
154
- } from "./chunk-5MO5TWHJ.js";
156
+ } from "./chunk-22SSG3WW.js";
155
157
  import {
156
158
  getCodexModelProvider
157
- } from "./chunk-6C472IYE.js";
159
+ } from "./chunk-BGMMK24G.js";
158
160
  import {
159
161
  SUBPROCESS_MAX_BUFFER,
160
162
  execAsync,
161
163
  execFileAsync
162
- } from "./chunk-CYBM2QOH.js";
164
+ } from "./chunk-ZGHPW4OS.js";
163
165
  import {
164
166
  isRecord as isRecord2
165
167
  } from "./chunk-UZSNFLDQ.js";
@@ -409,7 +411,7 @@ import {
409
411
  workspaceTokenRequestSchema,
410
412
  workspaceTokenResponseSchema,
411
413
  wrapInTag
412
- } from "./chunk-BG4HQ56D.js";
414
+ } from "./chunk-4A6I7LPY.js";
413
415
  import {
414
416
  cloneMcpConfig,
415
417
  ensureCompatibilityImports,
@@ -8815,6 +8817,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
8815
8817
  wipeCheckRunning = false;
8816
8818
  wipeCheckPending = false;
8817
8819
  refreshQueue = Promise.resolve();
8820
+ fellBack = false;
8818
8821
  constructor() {
8819
8822
  super("ClaudeTokenManager");
8820
8823
  }
@@ -8829,7 +8832,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
8829
8832
  }
8830
8833
  getSkipReason() {
8831
8834
  const method = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
8832
- if (method === "ollama" || method === "api_key" || method === "bedrock" || method === "foundry") {
8835
+ if ((method === "ollama" || method === "api_key" || method === "bedrock" || method === "foundry") && !this.fellBack) {
8833
8836
  return `auth method is ${method}`;
8834
8837
  }
8835
8838
  return null;
@@ -8838,6 +8841,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
8838
8841
  await this.refreshWithRequest(() => routineRefreshRequest("claude"));
8839
8842
  }
8840
8843
  async switchCredentials(data) {
8844
+ this.fellBack = false;
8841
8845
  await this.applyCredentialUpdate(++this.credentialGeneration, () => this.applyCredentialsResponse(data));
8842
8846
  await this.start(true);
8843
8847
  }
@@ -8860,6 +8864,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
8860
8864
  async fetchFreshCredentials(failureReason, failureKind = "rejected", failedCredential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.claude, allowedMethods, resetsAt) {
8861
8865
  const configuredMethod = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
8862
8866
  const failedMethod = failedCredential?.method === "ollama" || failedCredential?.method === "oauth" || failedCredential?.method === "api_key" || failedCredential?.method === "bedrock" || failedCredential?.method === "foundry" ? failedCredential.method : configuredMethod === "ollama" || configuredMethod === "oauth" || configuredMethod === "api_key" || configuredMethod === "bedrock" || configuredMethod === "foundry" ? configuredMethod : void 0;
8867
+ this.fellBack = true;
8863
8868
  return this.swapCredentials({
8864
8869
  provider: "claude",
8865
8870
  failureKind,
@@ -8872,7 +8877,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
8872
8877
  ...resetsAt ? { resetsAt } : {},
8873
8878
  ...exclusions
8874
8879
  } : exclusions),
8875
- isOauthNow: () => ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "oauth"
8880
+ keepRefreshing: () => !this.getSkipReason()
8876
8881
  });
8877
8882
  }
8878
8883
  // A one-year token goes in CLAUDE_CODE_OAUTH_TOKEN, and a leftover file login would otherwise be refreshed and wiped.
@@ -11226,7 +11231,7 @@ var MuseTokenManager = class extends BaseRefreshManager {
11226
11231
  await writeSecureCredentialFile(MUSE_AUTH_PATH, serializeMuseAuthFile(data), { ensureParentDir: true });
11227
11232
  setAgentCredentialSnapshot("muse", data);
11228
11233
  },
11229
- isOauthNow: () => false
11234
+ keepRefreshing: () => false
11230
11235
  });
11231
11236
  }
11232
11237
  };
@@ -11271,7 +11276,7 @@ var OpencodeTokenManager = class extends BaseRefreshManager {
11271
11276
  Object.assign(process.env, data.auth.env);
11272
11277
  setAgentCredentialSnapshot("opencode", data);
11273
11278
  },
11274
- isOauthNow: () => false
11279
+ keepRefreshing: () => false
11275
11280
  });
11276
11281
  }
11277
11282
  };
@@ -11331,7 +11336,7 @@ var AuthFallbackCoordinator = class {
11331
11336
  const live = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[this.provider];
11332
11337
  const exhausted = attempted ?? live;
11333
11338
  const alreadyReplaced = !!exhausted && !!live && !isSameCredential(live, exhausted);
11334
- if (exhausted) recordExhaustedCredential(this.provider, exhausted);
11339
+ if (exhausted) recordExhaustedCredential(this.provider, exhausted, resetsAt);
11335
11340
  const { userId, excludeProviders, forkable } = this.context();
11336
11341
  const plan = await monolithService.getFallbackPlan({
11337
11342
  provider: this.provider,
@@ -11348,20 +11353,18 @@ var AuthFallbackCoordinator = class {
11348
11353
  for (const step of steps) {
11349
11354
  if (step.kind === "harness") {
11350
11355
  if (!forkable || !isAgentAvailable(step.provider)) continue;
11351
- if (reason === "auth_failed") break;
11356
+ if (reason === "auth_failed" || reason === "model_unavailable") continue;
11352
11357
  return { outcome: "fork", target: step };
11353
11358
  }
11354
11359
  if (alreadyReplaced && live) {
11355
11360
  if (step.methods.length > 0 && !step.methods.includes(live.method)) continue;
11356
- this.recordNotice(reason, exhausted, live, "switched", detail);
11357
- const model2 = step.models?.[live.method];
11358
- return { outcome: "switched", ...model2 ? { model: model2 } : {} };
11361
+ return this.switched(reason, exhausted, live, step, detail);
11359
11362
  }
11360
11363
  if (!tokenManager || !exhausted) continue;
11361
11364
  const swap = await tokenManager.fetchFreshCredentials(
11362
11365
  `${this.provider} ${describeQuotaLimit(reason)}: ${detail}`,
11363
11366
  // Skipped via the exclusion above, not rested, so Settings doesn't show a usage limit.
11364
- reason === "auth_failed" || reason === "credits_required" ? "rejected" : "exhausted",
11367
+ reason === "auth_failed" || reason === "credits_required" || reason === "model_unavailable" ? "rejected" : "exhausted",
11365
11368
  exhausted,
11366
11369
  step.methods.length > 0 ? step.methods : void 0,
11367
11370
  resetsAt
@@ -11375,15 +11378,19 @@ var AuthFallbackCoordinator = class {
11375
11378
  failed = true;
11376
11379
  continue;
11377
11380
  }
11378
- this.recordNotice(reason, exhausted, applied, "switched", detail);
11379
- const model = step.models?.[applied.method];
11380
- return { outcome: "switched", ...model ? { model } : {} };
11381
+ return this.switched(reason, exhausted, applied, step, detail);
11381
11382
  }
11382
11383
  if (!tokenManager || !exhausted) return { outcome: "unavailable" };
11383
11384
  if (!failed) return { outcome: "not_configured" };
11384
11385
  this.recordNotice(reason, exhausted, exhausted, "failed", "Replicas could not load an alternate credential.");
11385
11386
  return { outcome: "failed" };
11386
11387
  }
11388
+ switched(reason, exhausted, applied, step, detail) {
11389
+ this.recordNotice(reason, exhausted, applied, "switched", detail);
11390
+ const model = step.models?.[applied.method];
11391
+ if (model) recordFallbackModel(this.provider, applied, model);
11392
+ return { outcome: "switched", ...model ? { model } : {} };
11393
+ }
11387
11394
  recordNotice(reason, exhausted, applied, status, detail) {
11388
11395
  const payload = {
11389
11396
  provider: this.provider,
@@ -12970,6 +12977,12 @@ var ClaudeAuthError = class extends Error {
12970
12977
  this.name = "ClaudeAuthError";
12971
12978
  }
12972
12979
  };
12980
+ var ClaudeModelUnavailableError = class extends Error {
12981
+ constructor(message) {
12982
+ super(message);
12983
+ this.name = "ClaudeModelUnavailableError";
12984
+ }
12985
+ };
12973
12986
  var ClaudeTransientTurnError = class extends Error {
12974
12987
  constructor(message, midTurn = false) {
12975
12988
  super(message);
@@ -13442,8 +13455,9 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13442
13455
  if (this.turnPromptRecorded) {
13443
13456
  currentRequest = { ...currentRequest, message: CLAUDE_RETRY_PROMPT, images: [], goalMode: false };
13444
13457
  }
13445
- const quotaKind = _ClaudeManager.quotaLimitKind(error);
13446
- const isAuthError = !quotaKind && _ClaudeManager.isAuthError(error);
13458
+ const modelUnavailable = error instanceof ClaudeModelUnavailableError;
13459
+ const quotaKind = modelUnavailable ? null : _ClaudeManager.quotaLimitKind(error);
13460
+ const isAuthError = !quotaKind && !modelUnavailable && _ClaudeManager.isAuthError(error);
13447
13461
  if (isAuthError && authRetries < MAX_AUTH_RETRIES) {
13448
13462
  authRetries++;
13449
13463
  console.error(
@@ -13454,7 +13468,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13454
13468
  const refreshed = await claudeTokenManager.fetchFreshCredentials(
13455
13469
  error instanceof Error ? error.message : String(error),
13456
13470
  "rejected",
13457
- attemptedCredential
13471
+ attemptedCredential,
13472
+ attemptedCredential ? [attemptedCredential.method] : void 0
13458
13473
  );
13459
13474
  if (refreshed.ok) {
13460
13475
  const live = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.claude;
@@ -13469,9 +13484,9 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13469
13484
  loginRevoked = (method === "oauth" || method === "unknown") && failure?.code === "oauth_disconnected";
13470
13485
  namedLogin = failure?.credential;
13471
13486
  }
13472
- if (quotaKind || isAuthError) {
13473
- const reason = quotaKind ?? "auth_failed";
13474
- const detail = error instanceof ClaudeQuotaError ? error.message : extractErrorText(error);
13487
+ if (quotaKind || isAuthError || modelUnavailable) {
13488
+ const reason = quotaKind ?? (modelUnavailable ? "model_unavailable" : "auth_failed");
13489
+ const detail = error instanceof ClaudeQuotaError || error instanceof ClaudeModelUnavailableError ? error.message : extractErrorText(error);
13475
13490
  const repeated = !!attemptedCredential && failedCredentials.some((failed) => isSameCredential(failed, attemptedCredential));
13476
13491
  if (attemptedCredential) failedCredentials.push(attemptedCredential);
13477
13492
  const fallback = repeated ? { outcome: "unavailable" } : await this.fallBackCredential(reason, detail, attemptedCredential, error instanceof ClaudeQuotaError ? error.resetsAt : void 0);
@@ -13491,6 +13506,10 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13491
13506
  await this.emitQuotaExhaustedEvent(quotaKind, fallback, detail, attemptedCredential);
13492
13507
  return;
13493
13508
  }
13509
+ if (modelUnavailable) {
13510
+ await this.emitTerminalErrorResult(detail, [`${detail} No other credential in the fallback chain could serve it. Pick a different model, or check Settings \u2192 Fallbacks.`]);
13511
+ return;
13512
+ }
13494
13513
  failedLogin = { credential: attemptedCredential ?? namedLogin, revoked: loginRevoked };
13495
13514
  throw error;
13496
13515
  }
@@ -13628,7 +13647,10 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13628
13647
  this.buildCombinedInstructions(customInstructions, true),
13629
13648
  "Background tasks must have a concrete completion condition. Use foreground commands for sequential work and managed workspace services for servers. Reserve persistent Monitor tasks for ongoing watches the user requested. You may end a turn while actual background work continues, but do not promise a later update unless a task was successfully launched. Report completion or failure when its notification arrives. Always give background commands a short description: the chat shows it to the user when the task finishes."
13630
13649
  ].filter(Boolean).join("\n\n");
13631
- const resolvedModel = normalizeClaudeModel(model) || (resolveClaudeAuthMethodForMessage() === "ollama" ? DEFAULT_OLLAMA_MODEL : void 0) || DEFAULT_CLAUDE_MODEL;
13650
+ const requestedModel = normalizeClaudeModel(model);
13651
+ const authMethod = resolveClaudeAuthMethodForMessage();
13652
+ const isClaudeModel = !!requestedModel && /^claude-/i.test(requestedModel);
13653
+ const resolvedModel = authMethod === "ollama" ? getFallbackModel("claude") ?? (requestedModel && !isClaudeModel ? requestedModel : DEFAULT_OLLAMA_MODEL) : (authMethod === "oauth" || authMethod === "api_key") && requestedModel && !isClaudeModel ? DEFAULT_CLAUDE_MODEL : requestedModel || DEFAULT_CLAUDE_MODEL;
13632
13654
  const claudeCodeModel = toClaudeCodeModel(
13633
13655
  resolvedModel,
13634
13656
  ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "bedrock" || ENGINE_ENV.CLAUDE_CODE_USE_BEDROCK === "1"
@@ -13911,7 +13933,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13911
13933
  }
13912
13934
  const authErrorMessage = _ClaudeManager.detectAuthErrorInMessage(msg);
13913
13935
  const transientErrorMessage = _ClaudeManager.detectTransientTurnErrorInMessage(msg);
13914
- const turnError = authErrorMessage ? new ClaudeAuthError(authErrorMessage) : msg.type === "result" && msg.subtype !== "success" && msg.num_turns === 0 && msg.errors.some((err) => err.startsWith("No conversation found with session ID")) ? new ClaudeMissingSessionError(msg.errors.join("\n")) : _ClaudeManager.detectQuotaErrorInMessage(msg, creditsRequired, resetsAt) ?? (transientErrorMessage ? new ClaudeTransientTurnError(transientErrorMessage, this.pendingTurn?.sawActivity ?? false) : null);
13936
+ const turnError = authErrorMessage ? new ClaudeAuthError(authErrorMessage) : _ClaudeManager.isModelUnavailableMessage(msg) ? new ClaudeModelUnavailableError(_ClaudeManager.assistantText(msg)) : msg.type === "result" && msg.subtype !== "success" && msg.num_turns === 0 && msg.errors.some((err) => err.startsWith("No conversation found with session ID")) ? new ClaudeMissingSessionError(msg.errors.join("\n")) : _ClaudeManager.detectQuotaErrorInMessage(msg, creditsRequired, resetsAt) ?? (transientErrorMessage ? new ClaudeTransientTurnError(transientErrorMessage, this.pendingTurn?.sawActivity ?? false) : null);
13915
13937
  if (turnError) {
13916
13938
  suppressLinearResponseFlush = true;
13917
13939
  turnFailed = true;
@@ -14203,6 +14225,12 @@ ${msg.result}` } : { ...msg, errors: [loginFailure, ...msg.errors] } : msg);
14203
14225
  console.warn("[ClaudeManager] Failed to record context usage:", error instanceof Error ? error.message : error);
14204
14226
  }
14205
14227
  }
14228
+ static isModelUnavailableMessage(message) {
14229
+ return message.type === "assistant" && "error" in message && message.error === "model_not_found";
14230
+ }
14231
+ static assistantText(message) {
14232
+ return message.type === "assistant" && Array.isArray(message.message?.content) ? message.message.content.map((block) => "text" in block ? block.text : "").join("\n") : "";
14233
+ }
14206
14234
  static isAuthError(error) {
14207
14235
  if (error instanceof ClaudeAuthError) return true;
14208
14236
  return isClaudeAuthErrorText(error instanceof Error ? error.message : String(error));
@@ -14214,8 +14242,7 @@ ${msg.result}` } : { ...msg, errors: [loginFailure, ...msg.errors] } : msg);
14214
14242
  static detectQuotaErrorInMessage(message, creditsRequired, resetsAt) {
14215
14243
  const quotaError = (text2, kind2) => new ClaudeQuotaError(text2, creditsRequired ? "credits_required" : kind2, resetsAt);
14216
14244
  if (message.type === "assistant" && "error" in message) {
14217
- const content = Array.isArray(message.message?.content) ? message.message.content.map((block) => "text" in block ? block.text : "").join("\n") : "";
14218
- const text2 = [typeof message.error === "string" ? message.error : "", content].filter(Boolean).join("\n");
14245
+ const text2 = [typeof message.error === "string" ? message.error : "", _ClaudeManager.assistantText(message)].filter(Boolean).join("\n");
14219
14246
  const kind2 = detectAgentQuotaLimit(text2);
14220
14247
  if (kind2) return quotaError(text2, kind2);
14221
14248
  }
@@ -61379,7 +61406,8 @@ ${note}`;
61379
61406
  chatId: persisted.id,
61380
61407
  originChatId,
61381
61408
  chatTitle: () => persisted.title,
61382
- onModelSwitched,
61409
+ // Claude maps the model on each turn while the fallback credential is live, so the chat keeps its own for when the primary returns.
61410
+ ...baseProvider === "claude" ? {} : { onModelSwitched },
61383
61411
  onChatCredential,
61384
61412
  lastSenderKey: persisted.lastSenderKey,
61385
61413
  onLastSenderKeyChanged,
@@ -63596,7 +63624,9 @@ data: ${JSON.stringify("Terminal session not found")}
63596
63624
  return c.json(await environmentDetailsService.getDetails());
63597
63625
  });
63598
63626
  app2.post("/claude/refresh-now", async (c) => {
63599
- const result = await claudeTokenManager.refreshOnce();
63627
+ const reconnected = c.req.query("reconnected") === "1";
63628
+ if (reconnected) clearCredentialFallbacks("claude");
63629
+ const result = await claudeTokenManager.refreshOnce(reconnected);
63600
63630
  if (!result.ok) {
63601
63631
  return c.json(jsonError("Failed to refresh Claude credentials", result.error.message), 502);
63602
63632
  }
@@ -3,13 +3,13 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  publishedPullRequestUrls
6
- } from "./chunk-TZ43BQUR.js";
6
+ } from "./chunk-DGO2DXHA.js";
7
7
  import {
8
8
  isRecord
9
9
  } from "./chunk-UZSNFLDQ.js";
10
10
  import {
11
11
  shouldRefreshPullRequests
12
- } from "./chunk-BG4HQ56D.js";
12
+ } from "./chunk-4A6I7LPY.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/services/post-tool-pr-notifier.ts
@@ -11,7 +11,7 @@ import {
11
11
  spawnSubagentRequestSchema,
12
12
  subagentResponseSchema,
13
13
  waitSubagentRequestSchema
14
- } from "./chunk-BG4HQ56D.js";
14
+ } from "./chunk-4A6I7LPY.js";
15
15
  import "./chunk-VEQXQN22.js";
16
16
 
17
17
  // src/subagent-mcp.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.1293",
3
+ "version": "0.1.1294",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",