replicas-engine 0.1.1292 → 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-DYH6IOEA.js";
12
- import "./chunk-THUCGBYF.js";
13
- import "./chunk-34PT4Z5G.js";
14
- import "./chunk-3DNCWVQY.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-2G5ZU67U.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-3DNCWVQY.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-2G5ZU67U.js";
25
+ } from "./chunk-4A6I7LPY.js";
26
26
 
27
27
  // src/runtime-env-loader.ts
28
28
  import { readFileSync } from "fs";
@@ -271,6 +271,7 @@ var WEB_APP_PATHS = {
271
271
  integrations: `${DASHBOARD_PATH}/integrations`,
272
272
  integrationsGithub: `${DASHBOARD_PATH}/integrations/github`,
273
273
  integrationsGitlab: `${DASHBOARD_PATH}/integrations/gitlab`,
274
+ integrationsSlack: `${DASHBOARD_PATH}/integrations/slack`,
274
275
  settings: `${DASHBOARD_PATH}/settings`,
275
276
  billing: `${DASHBOARD_PATH}/billing`,
276
277
  members: `${DASHBOARD_PATH}/members`,
@@ -890,7 +891,11 @@ function paginateEngineLogContent(input) {
890
891
  }
891
892
 
892
893
  // ../shared/src/agent-quota.ts
893
- 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
+ }
894
899
  function isAgentFallbackReason(value) {
895
900
  return AGENT_FALLBACK_REASONS.some((reason) => reason === value);
896
901
  }
@@ -2249,7 +2254,7 @@ function describeAuthFallback(payload) {
2249
2254
  });
2250
2255
  const switched = payload.status === "switched";
2251
2256
  return {
2252
- 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}`,
2253
2258
  outcomeClause: switched ? `switched to ${describeCredential(payload.provider, {
2254
2259
  method: payload.candidateMethod,
2255
2260
  scope: payload.candidateScope,
@@ -2332,6 +2337,7 @@ var forkChatRequestSchema = z10.object({
2332
2337
  function describeQuotaLimit(reason) {
2333
2338
  if (reason === "auth_failed") return "could not authenticate";
2334
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";
2335
2341
  return reason === "out_of_credits" ? "ran out of credits" : "hit its usage limit";
2336
2342
  }
2337
2343
  function describeChatForkReason(reason, provider2) {
@@ -7506,6 +7512,7 @@ function coerceClaudeResultPayload(payload) {
7506
7512
  is_error: typeof payload.is_error === "boolean" ? payload.is_error : void 0,
7507
7513
  subtype: typeof payload.subtype === "string" ? payload.subtype : void 0,
7508
7514
  errors: Array.isArray(payload.errors) ? payload.errors.filter((e) => typeof e === "string") : void 0,
7515
+ result: typeof payload.result === "string" ? payload.result : void 0,
7509
7516
  terminal_reason: typeof payload.terminal_reason === "string" ? payload.terminal_reason : void 0
7510
7517
  };
7511
7518
  }
@@ -7859,7 +7866,7 @@ function parseClaudeEvents(events, parentToolUseId) {
7859
7866
  messages.push({
7860
7867
  id: `error-${event.timestamp}`,
7861
7868
  type: "error",
7862
- message: genuineErrors.length > 0 ? genuineErrors.join("\n") : "Claude session encountered an unexpected error.",
7869
+ message: genuineErrors.length > 0 ? genuineErrors.join("\n") : payload.result || "Claude session encountered an unexpected error.",
7863
7870
  timestamp: event.timestamp
7864
7871
  });
7865
7872
  }
@@ -10565,6 +10572,7 @@ export {
10565
10572
  isValidThinkingLevel,
10566
10573
  codexReasoningEffortForThinkingLevel,
10567
10574
  getProviderDisplayName,
10575
+ exhaustedUntil,
10568
10576
  detectAgentQuotaLimit,
10569
10577
  listOpenAICompatibleModels,
10570
10578
  resolveOpenAICompatibleModelsUrl,
@@ -5,14 +5,14 @@ import {
5
5
  getOriginDefaultBranch,
6
6
  monolithRequest,
7
7
  runGitCommand
8
- } from "./chunk-THUCGBYF.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-2G5ZU67U.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-3DNCWVQY.js";
6
+ } from "./chunk-ZGHPW4OS.js";
7
7
  import {
8
8
  isVersionBelow
9
- } from "./chunk-2G5ZU67U.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.1292";
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-2G5ZU67U.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-THUCGBYF.js";
13
+ } from "./chunk-22SSG3WW.js";
14
14
  import {
15
15
  AppServerProcess,
16
16
  buildCodexAgentEnv
17
- } from "./chunk-34PT4Z5G.js";
17
+ } from "./chunk-BGMMK24G.js";
18
18
  import {
19
19
  SUBPROCESS_MAX_BUFFER,
20
20
  execFileAsync
21
- } from "./chunk-3DNCWVQY.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-2G5ZU67U.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-HGZPMM5B.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-HGZPMM5B.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-34PT4Z5G.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-2G5ZU67U.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-2G5ZU67U.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-6Q2GHJ6S.js";
7
- import "./chunk-THUCGBYF.js";
8
- import "./chunk-3DNCWVQY.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-2G5ZU67U.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-YNXJCLZW.js";
17
+ } from "./chunk-JFKNSCBZ.js";
18
18
  import {
19
19
  AppServerProcess,
20
20
  buildCodexAgentEnv
21
- } from "./chunk-34PT4Z5G.js";
22
- import "./chunk-3DNCWVQY.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-2G5ZU67U.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-6Q2GHJ6S.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-YNXJCLZW.js";
68
+ } from "./chunk-JFKNSCBZ.js";
69
69
  import {
70
70
  publishedPullRequestUrls
71
- } from "./chunk-TXAJDSKC.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-DYH6IOEA.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-THUCGBYF.js";
156
+ } from "./chunk-22SSG3WW.js";
155
157
  import {
156
158
  getCodexModelProvider
157
- } from "./chunk-34PT4Z5G.js";
159
+ } from "./chunk-BGMMK24G.js";
158
160
  import {
159
161
  SUBPROCESS_MAX_BUFFER,
160
162
  execAsync,
161
163
  execFileAsync
162
- } from "./chunk-3DNCWVQY.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-2G5ZU67U.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
  };
@@ -11300,8 +11305,14 @@ function describeLoginFailure(provider, login, configuredMethod, revoked) {
11300
11305
  const agent = getProviderDisplayName(provider);
11301
11306
  const method = login?.method ?? configuredMethod;
11302
11307
  const name = login ? describeCredential(provider, login) : `the ${agent} credential this workspace uses`;
11308
+ const orgLogin = login?.scope === CREDENTIAL_SCOPE.ORG;
11309
+ const agentsPage = orgLogin ? `Organization \u2192 Coding Agents: ${DEFAULT_WEB_APP_URL}${WEB_APP_PATHS.agents}` : `Personal \u2192 Coding Agents: ${DEFAULT_WEB_APP_URL}${WEB_APP_PATHS.accountAgents}`;
11310
+ if (provider === "claude" && (method === "oauth" || revoked)) {
11311
+ const fixer = orgLogin ? "an organization admin" : login?.account?.ownerName ?? "you";
11312
+ return `${agent} could not sign in with ${name}. The login expired or was revoked. Replicas recently switched to one-year Claude logins, so ${fixer} should update the Replicas CLI (\`npm install -g replicas-cli@latest\`) and reconnect with \`replicas claude-auth${orgLogin ? " --org" : ""}\`. Claude logins are listed in ${agentsPage}`;
11313
+ }
11303
11314
  const why = revoked ? "It was signed out, revoked, or expired." : method === "api_key" ? "The key may be invalid, revoked, out of credits, or missing access to the selected model." : method === "bedrock" ? "Check its credentials, region, IAM permissions, and Claude model access." : method === "foundry" ? `Check its API key, resource, and ${agent} deployment names.` : "It was rejected even after a refresh.";
11304
- const reconnect = login?.scope === CREDENTIAL_SCOPE.ORG ? `An organization admin can reconnect it in Organization \u2192 Coding Agents: ${DEFAULT_WEB_APP_URL}${WEB_APP_PATHS.agents}` : `Reconnect it in Personal \u2192 Coding Agents: ${DEFAULT_WEB_APP_URL}${WEB_APP_PATHS.accountAgents}`;
11315
+ const reconnect = orgLogin ? `An organization admin can reconnect it in ${agentsPage}` : `Reconnect it in ${agentsPage}`;
11305
11316
  return `${agent} could not sign in with ${name}. ${why} ${reconnect}`;
11306
11317
  }
11307
11318
  var fallbackLocks = /* @__PURE__ */ new Map();
@@ -11325,7 +11336,7 @@ var AuthFallbackCoordinator = class {
11325
11336
  const live = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[this.provider];
11326
11337
  const exhausted = attempted ?? live;
11327
11338
  const alreadyReplaced = !!exhausted && !!live && !isSameCredential(live, exhausted);
11328
- if (exhausted) recordExhaustedCredential(this.provider, exhausted);
11339
+ if (exhausted) recordExhaustedCredential(this.provider, exhausted, resetsAt);
11329
11340
  const { userId, excludeProviders, forkable } = this.context();
11330
11341
  const plan = await monolithService.getFallbackPlan({
11331
11342
  provider: this.provider,
@@ -11342,20 +11353,18 @@ var AuthFallbackCoordinator = class {
11342
11353
  for (const step of steps) {
11343
11354
  if (step.kind === "harness") {
11344
11355
  if (!forkable || !isAgentAvailable(step.provider)) continue;
11345
- if (reason === "auth_failed") break;
11356
+ if (reason === "auth_failed" || reason === "model_unavailable") continue;
11346
11357
  return { outcome: "fork", target: step };
11347
11358
  }
11348
11359
  if (alreadyReplaced && live) {
11349
11360
  if (step.methods.length > 0 && !step.methods.includes(live.method)) continue;
11350
- this.recordNotice(reason, exhausted, live, "switched", detail);
11351
- const model2 = step.models?.[live.method];
11352
- return { outcome: "switched", ...model2 ? { model: model2 } : {} };
11361
+ return this.switched(reason, exhausted, live, step, detail);
11353
11362
  }
11354
11363
  if (!tokenManager || !exhausted) continue;
11355
11364
  const swap = await tokenManager.fetchFreshCredentials(
11356
11365
  `${this.provider} ${describeQuotaLimit(reason)}: ${detail}`,
11357
11366
  // Skipped via the exclusion above, not rested, so Settings doesn't show a usage limit.
11358
- reason === "auth_failed" || reason === "credits_required" ? "rejected" : "exhausted",
11367
+ reason === "auth_failed" || reason === "credits_required" || reason === "model_unavailable" ? "rejected" : "exhausted",
11359
11368
  exhausted,
11360
11369
  step.methods.length > 0 ? step.methods : void 0,
11361
11370
  resetsAt
@@ -11369,15 +11378,19 @@ var AuthFallbackCoordinator = class {
11369
11378
  failed = true;
11370
11379
  continue;
11371
11380
  }
11372
- this.recordNotice(reason, exhausted, applied, "switched", detail);
11373
- const model = step.models?.[applied.method];
11374
- return { outcome: "switched", ...model ? { model } : {} };
11381
+ return this.switched(reason, exhausted, applied, step, detail);
11375
11382
  }
11376
11383
  if (!tokenManager || !exhausted) return { outcome: "unavailable" };
11377
11384
  if (!failed) return { outcome: "not_configured" };
11378
11385
  this.recordNotice(reason, exhausted, exhausted, "failed", "Replicas could not load an alternate credential.");
11379
11386
  return { outcome: "failed" };
11380
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
+ }
11381
11394
  recordNotice(reason, exhausted, applied, status, detail) {
11382
11395
  const payload = {
11383
11396
  provider: this.provider,
@@ -12964,6 +12977,12 @@ var ClaudeAuthError = class extends Error {
12964
12977
  this.name = "ClaudeAuthError";
12965
12978
  }
12966
12979
  };
12980
+ var ClaudeModelUnavailableError = class extends Error {
12981
+ constructor(message) {
12982
+ super(message);
12983
+ this.name = "ClaudeModelUnavailableError";
12984
+ }
12985
+ };
12967
12986
  var ClaudeTransientTurnError = class extends Error {
12968
12987
  constructor(message, midTurn = false) {
12969
12988
  super(message);
@@ -13030,6 +13049,8 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13030
13049
  sessionLinearForwarder = null;
13031
13050
  pendingTurn = null;
13032
13051
  attemptSawActivity = false;
13052
+ // A broken login the turn moved past; if the turn still fails, it is the error the user can act on.
13053
+ turnLoginFailure = null;
13033
13054
  sessionActivity = createClaudeSessionActivity();
13034
13055
  backgroundContinuationTimer = null;
13035
13056
  pendingInterrupt = false;
@@ -13416,6 +13437,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13416
13437
  let missingSessionReset = false;
13417
13438
  let currentRequest = request;
13418
13439
  let carriedSteers = /* @__PURE__ */ new Map();
13440
+ this.turnLoginFailure = null;
13419
13441
  await this.goalClear?.catch(() => {
13420
13442
  });
13421
13443
  try {
@@ -13433,8 +13455,9 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13433
13455
  if (this.turnPromptRecorded) {
13434
13456
  currentRequest = { ...currentRequest, message: CLAUDE_RETRY_PROMPT, images: [], goalMode: false };
13435
13457
  }
13436
- const quotaKind = _ClaudeManager.quotaLimitKind(error);
13437
- 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);
13438
13461
  if (isAuthError && authRetries < MAX_AUTH_RETRIES) {
13439
13462
  authRetries++;
13440
13463
  console.error(
@@ -13445,9 +13468,14 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13445
13468
  const refreshed = await claudeTokenManager.fetchFreshCredentials(
13446
13469
  error instanceof Error ? error.message : String(error),
13447
13470
  "rejected",
13448
- attemptedCredential
13471
+ attemptedCredential,
13472
+ attemptedCredential ? [attemptedCredential.method] : void 0
13449
13473
  );
13450
13474
  if (refreshed.ok) {
13475
+ const live = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.claude;
13476
+ if (attemptedCredential && live && !isSameCredential(attemptedCredential, live)) {
13477
+ this.turnLoginFailure ??= describeLoginFailure("claude", attemptedCredential, resolveClaudeAuthMethodForMessage(), false);
13478
+ }
13451
13479
  attempt++;
13452
13480
  continue;
13453
13481
  }
@@ -13456,13 +13484,16 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13456
13484
  loginRevoked = (method === "oauth" || method === "unknown") && failure?.code === "oauth_disconnected";
13457
13485
  namedLogin = failure?.credential;
13458
13486
  }
13459
- if (quotaKind || isAuthError) {
13460
- const reason = quotaKind ?? "auth_failed";
13461
- 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);
13462
13490
  const repeated = !!attemptedCredential && failedCredentials.some((failed) => isSameCredential(failed, attemptedCredential));
13463
13491
  if (attemptedCredential) failedCredentials.push(attemptedCredential);
13464
13492
  const fallback = repeated ? { outcome: "unavailable" } : await this.fallBackCredential(reason, detail, attemptedCredential, error instanceof ClaudeQuotaError ? error.resetsAt : void 0);
13465
13493
  if (fallback.outcome === "switched") {
13494
+ if (isAuthError) {
13495
+ this.turnLoginFailure ??= describeLoginFailure("claude", attemptedCredential ?? namedLogin, resolveClaudeAuthMethodForMessage(), loginRevoked);
13496
+ }
13466
13497
  if (fallback.model) currentRequest = { ...currentRequest, model: fallback.model };
13467
13498
  authRetries = 0;
13468
13499
  loginRevoked = false;
@@ -13475,6 +13506,10 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13475
13506
  await this.emitQuotaExhaustedEvent(quotaKind, fallback, detail, attemptedCredential);
13476
13507
  return;
13477
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
+ }
13478
13513
  failedLogin = { credential: attemptedCredential ?? namedLogin, revoked: loginRevoked };
13479
13514
  throw error;
13480
13515
  }
@@ -13525,6 +13560,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13525
13560
  if (failedLogin) {
13526
13561
  await this.emitAuthRetryExhaustedEvent(lastError, failedLogin.credential, failedLogin.revoked);
13527
13562
  }
13563
+ this.turnLoginFailure = null;
13528
13564
  try {
13529
13565
  await this.historyFile.flush();
13530
13566
  await this.onTurnComplete();
@@ -13544,7 +13580,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13544
13580
  subtype: "error_during_execution",
13545
13581
  is_error: true,
13546
13582
  result,
13547
- errors,
13583
+ errors: [.../* @__PURE__ */ new Set([...this.turnLoginFailure ? [this.turnLoginFailure] : [], ...errors])],
13548
13584
  session_id: this.sessionId ?? "",
13549
13585
  parent_tool_use_id: null
13550
13586
  }
@@ -13611,7 +13647,10 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13611
13647
  this.buildCombinedInstructions(customInstructions, true),
13612
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."
13613
13649
  ].filter(Boolean).join("\n\n");
13614
- 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;
13615
13654
  const claudeCodeModel = toClaudeCodeModel(
13616
13655
  resolvedModel,
13617
13656
  ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD === "bedrock" || ENGINE_ENV.CLAUDE_CODE_USE_BEDROCK === "1"
@@ -13894,7 +13933,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13894
13933
  }
13895
13934
  const authErrorMessage = _ClaudeManager.detectAuthErrorInMessage(msg);
13896
13935
  const transientErrorMessage = _ClaudeManager.detectTransientTurnErrorInMessage(msg);
13897
- 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);
13898
13937
  if (turnError) {
13899
13938
  suppressLinearResponseFlush = true;
13900
13939
  turnFailed = true;
@@ -13931,7 +13970,9 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13931
13970
  if (!this.pendingTurn && msg.type === "system" && (msg.subtype === "init" || msg.subtype === "session_state_changed" && msg.state === "running")) {
13932
13971
  this.pendingBackgroundCompletion = true;
13933
13972
  }
13934
- await this.handleMessage(msg);
13973
+ const loginFailure = msg.type === "result" && msg.user_message_uuid && this.pendingTurn?.userMessageUuids.has(msg.user_message_uuid) ? this.turnLoginFailure : null;
13974
+ await this.handleMessage(msg.type === "result" && loginFailure && isClaudeResultError(msg) ? msg.subtype === "success" ? { ...msg, result: `${loginFailure}
13975
+ ${msg.result}` } : { ...msg, errors: [loginFailure, ...msg.errors] } : msg);
13935
13976
  if (this.activeQuery !== response) break;
13936
13977
  const linearSessionId = this.linearSessionId;
13937
13978
  if (linearSessionId && this.sessionLinearForwarder) {
@@ -14184,6 +14225,12 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
14184
14225
  console.warn("[ClaudeManager] Failed to record context usage:", error instanceof Error ? error.message : error);
14185
14226
  }
14186
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
+ }
14187
14234
  static isAuthError(error) {
14188
14235
  if (error instanceof ClaudeAuthError) return true;
14189
14236
  return isClaudeAuthErrorText(error instanceof Error ? error.message : String(error));
@@ -14195,8 +14242,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
14195
14242
  static detectQuotaErrorInMessage(message, creditsRequired, resetsAt) {
14196
14243
  const quotaError = (text2, kind2) => new ClaudeQuotaError(text2, creditsRequired ? "credits_required" : kind2, resetsAt);
14197
14244
  if (message.type === "assistant" && "error" in message) {
14198
- const content = Array.isArray(message.message?.content) ? message.message.content.map((block) => "text" in block ? block.text : "").join("\n") : "";
14199
- 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");
14200
14246
  const kind2 = detectAgentQuotaLimit(text2);
14201
14247
  if (kind2) return quotaError(text2, kind2);
14202
14248
  }
@@ -60049,7 +60095,7 @@ function terminalErrorsFromEvent(event, provider) {
60049
60095
  const payload = coerceClaudeResultPayload(event.payload);
60050
60096
  if (!isClaudeResultError(payload)) return null;
60051
60097
  const errors2 = stripAgentDiagnosticErrors(payload.errors ?? []);
60052
- return errors2.length > 0 ? errors2 : ["Claude run failed"];
60098
+ return errors2.length > 0 ? errors2 : [payload.result || "Claude run failed"];
60053
60099
  }
60054
60100
  if (event.type === CODEX_QUOTA_STATUS_EVENT_TYPE) {
60055
60101
  return event.payload.state === "out_of_credits" ? ["Codex is out of credits. Top up the connected OpenAI account to resume."] : null;
@@ -61360,7 +61406,8 @@ ${note}`;
61360
61406
  chatId: persisted.id,
61361
61407
  originChatId,
61362
61408
  chatTitle: () => persisted.title,
61363
- 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 },
61364
61411
  onChatCredential,
61365
61412
  lastSenderKey: persisted.lastSenderKey,
61366
61413
  onLastSenderKeyChanged,
@@ -63577,7 +63624,9 @@ data: ${JSON.stringify("Terminal session not found")}
63577
63624
  return c.json(await environmentDetailsService.getDetails());
63578
63625
  });
63579
63626
  app2.post("/claude/refresh-now", async (c) => {
63580
- 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);
63581
63630
  if (!result.ok) {
63582
63631
  return c.json(jsonError("Failed to refresh Claude credentials", result.error.message), 502);
63583
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-TXAJDSKC.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-2G5ZU67U.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-2G5ZU67U.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.1292",
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",