replicas-engine 0.1.325 → 0.1.326

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.
Files changed (2) hide show
  1. package/dist/src/index.js +165 -23
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -287,7 +287,7 @@ var WORKSPACE_SIZES = ["small", "large"];
287
287
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
288
288
 
289
289
  // ../shared/src/e2b.ts
290
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-16-v4";
290
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-17-v1";
291
291
 
292
292
  // ../shared/src/runtime-env.ts
293
293
  function parsePosixEnvFile(content) {
@@ -2034,6 +2034,75 @@ function parseReplicasConfigString(content, filename) {
2034
2034
  return parseReplicasConfig(parsed, filename);
2035
2035
  }
2036
2036
 
2037
+ // ../shared/src/errors.ts
2038
+ var TRANSIENT_NETWORK_ERROR_PATTERNS = [
2039
+ /socket connection was closed unexpectedly/,
2040
+ /fetch failed/,
2041
+ /network error/,
2042
+ /network request failed/,
2043
+ /econnreset/,
2044
+ /econnrefused/,
2045
+ /etimedout/,
2046
+ /enotfound/,
2047
+ /eai_again/,
2048
+ /und_err_socket/,
2049
+ /und_err_connect_timeout/,
2050
+ /headers timeout/,
2051
+ /body timeout/,
2052
+ /connection reset/,
2053
+ /connection closed/,
2054
+ /closed unexpectedly/
2055
+ ];
2056
+ var HTTP_CONTEXT_PATTERN = /(api error|request|response|status|http)/;
2057
+ var HTTP_STATUS_PATTERN = /(^|[^0-9])([1-5][0-9]{2})(?=$|[^0-9])/g;
2058
+ function extractErrorText(error) {
2059
+ const parts = [];
2060
+ const seen = /* @__PURE__ */ new Set();
2061
+ const collect = (value) => {
2062
+ if (value === null || value === void 0 || seen.has(value)) return;
2063
+ seen.add(value);
2064
+ if (typeof value === "string") {
2065
+ parts.push(value);
2066
+ return;
2067
+ }
2068
+ if (value instanceof Error) {
2069
+ parts.push(value.name, value.message);
2070
+ collect(value.cause);
2071
+ return;
2072
+ }
2073
+ if (typeof value === "object") {
2074
+ const record = value;
2075
+ for (const key of ["name", "message", "code", "error", "cause"]) {
2076
+ collect(record[key]);
2077
+ }
2078
+ const errors = record.errors;
2079
+ if (Array.isArray(errors)) {
2080
+ for (const err of errors) collect(err);
2081
+ }
2082
+ return;
2083
+ }
2084
+ parts.push(String(value));
2085
+ };
2086
+ collect(error);
2087
+ return parts.filter(Boolean).join("\n");
2088
+ }
2089
+ function isTransientErrorText(text, options = {}) {
2090
+ const normalized = text.toLowerCase();
2091
+ if (!normalized) return false;
2092
+ if (TRANSIENT_NETWORK_ERROR_PATTERNS.some((pattern) => pattern.test(normalized))) return true;
2093
+ if (options.includeRateLimit && /rate limit/.test(normalized)) return true;
2094
+ if (options.extraPatterns?.some((pattern) => pattern.test(normalized))) return true;
2095
+ const httpStatuses = new Set(options.httpStatuses ?? []);
2096
+ const httpStatusClasses = new Set(options.httpStatusClasses ?? []);
2097
+ if (httpStatuses.size === 0 && httpStatusClasses.size === 0) return false;
2098
+ if (options.requireHttpContext !== false && !HTTP_CONTEXT_PATTERN.test(normalized)) return false;
2099
+ for (const match of normalized.matchAll(HTTP_STATUS_PATTERN)) {
2100
+ const status = Number(match[2]);
2101
+ if (httpStatuses.has(status) || httpStatusClasses.has(Math.floor(status / 100))) return true;
2102
+ }
2103
+ return false;
2104
+ }
2105
+
2037
2106
  // ../shared/src/claude-auth.ts
2038
2107
  function isClaudeAuthErrorText(text) {
2039
2108
  const lower = text.toLowerCase();
@@ -6220,6 +6289,16 @@ var ClaudeAuthError = class extends Error {
6220
6289
  this.name = "ClaudeAuthError";
6221
6290
  }
6222
6291
  };
6292
+ var ClaudeTransientTurnError = class extends Error {
6293
+ constructor(message) {
6294
+ super(message);
6295
+ this.name = "ClaudeTransientTurnError";
6296
+ }
6297
+ };
6298
+ var MAX_AUTH_RETRIES = 2;
6299
+ var MAX_TRANSIENT_RETRIES = 2;
6300
+ var TRANSIENT_RETRY_DELAYS_MS = [1e3, 2500];
6301
+ var CLAUDE_TRANSIENT_HTTP_STATUSES = [408, 500, 502, 503, 504, 529];
6223
6302
  var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6224
6303
  historyFile;
6225
6304
  sessionId = null;
@@ -6384,35 +6463,52 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6384
6463
  };
6385
6464
  }
6386
6465
  async processMessageInternal(request) {
6387
- const MAX_AUTH_RETRIES = 2;
6388
6466
  let lastError;
6389
6467
  let authRetryExhausted = false;
6468
+ let attempt = 0;
6469
+ let authRetries = 0;
6470
+ let transientRetries = 0;
6390
6471
  try {
6391
- for (let attempt = 0; attempt <= MAX_AUTH_RETRIES; attempt++) {
6472
+ while (true) {
6392
6473
  try {
6393
6474
  await this.executeQuery(request, { skipUserMessageRecord: attempt > 0 });
6394
6475
  return;
6395
6476
  } catch (error) {
6396
6477
  lastError = error;
6397
- if (!_ClaudeManager.isAuthError(error)) {
6398
- throw error;
6399
- }
6400
- if (attempt === MAX_AUTH_RETRIES) {
6401
- authRetryExhausted = true;
6402
- throw error;
6478
+ if (_ClaudeManager.isAuthError(error)) {
6479
+ if (authRetries >= MAX_AUTH_RETRIES) {
6480
+ authRetryExhausted = true;
6481
+ throw error;
6482
+ }
6483
+ authRetries++;
6484
+ console.error(
6485
+ `[ClaudeManager] Auth failure detected (attempt ${authRetries}/${MAX_AUTH_RETRIES}), refreshing credentials and retrying...`,
6486
+ error
6487
+ );
6488
+ this.setAuthRetrying(true);
6489
+ const refreshed = await claudeTokenManager.fetchFreshCredentials(
6490
+ error instanceof Error ? error.message : String(error)
6491
+ );
6492
+ if (!refreshed) {
6493
+ authRetryExhausted = true;
6494
+ throw error;
6495
+ }
6496
+ attempt++;
6497
+ continue;
6403
6498
  }
6404
- console.error(
6405
- `[ClaudeManager] Auth failure detected (attempt ${attempt + 1}/${MAX_AUTH_RETRIES + 1}), refreshing credentials and retrying...`,
6406
- error
6407
- );
6408
- this.setAuthRetrying(true);
6409
- const refreshed = await claudeTokenManager.fetchFreshCredentials(
6410
- error instanceof Error ? error.message : String(error)
6411
- );
6412
- if (!refreshed) {
6413
- authRetryExhausted = true;
6414
- throw error;
6499
+ if (_ClaudeManager.isTransientTurnError(error) && transientRetries < MAX_TRANSIENT_RETRIES) {
6500
+ transientRetries++;
6501
+ const delayMs = TRANSIENT_RETRY_DELAYS_MS[transientRetries - 1] ?? TRANSIENT_RETRY_DELAYS_MS[TRANSIENT_RETRY_DELAYS_MS.length - 1];
6502
+ console.warn(
6503
+ `[ClaudeManager] Transient turn failure detected (attempt ${transientRetries}/${MAX_TRANSIENT_RETRIES}), retrying in ${delayMs}ms...`,
6504
+ error
6505
+ );
6506
+ await this.tearDownSession();
6507
+ await new Promise((resolve4) => setTimeout(resolve4, delayMs));
6508
+ attempt++;
6509
+ continue;
6415
6510
  }
6511
+ throw error;
6416
6512
  }
6417
6513
  }
6418
6514
  } finally {
@@ -6526,7 +6622,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6526
6622
  return;
6527
6623
  }
6528
6624
  await new Promise((resolve4, reject) => {
6529
- this.pendingTurn = { resolve: resolve4, reject };
6625
+ this.pendingTurn = { resolve: resolve4, reject, sawActivity: false };
6530
6626
  promptStream.push(userMessage);
6531
6627
  });
6532
6628
  }
@@ -6659,11 +6755,13 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6659
6755
  async runSessionLoop(response) {
6660
6756
  const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
6661
6757
  const iterator = response[Symbol.asyncIterator]();
6758
+ let loopError = null;
6662
6759
  try {
6663
6760
  while (true) {
6664
6761
  const next = await iterator.next();
6665
6762
  if (next.done) break;
6666
6763
  const msg = next.value;
6764
+ if (!msg) break;
6667
6765
  const authErrorMessage = _ClaudeManager.detectAuthErrorInMessage(msg);
6668
6766
  if (authErrorMessage) {
6669
6767
  this.failPendingTurn(new ClaudeAuthError(authErrorMessage));
@@ -6674,7 +6772,23 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6674
6772
  }
6675
6773
  return;
6676
6774
  }
6775
+ const transientErrorMessage = _ClaudeManager.detectTransientTurnErrorInMessage(
6776
+ msg,
6777
+ this.pendingTurn?.sawActivity ?? false
6778
+ );
6779
+ if (transientErrorMessage) {
6780
+ this.failPendingTurn(new ClaudeTransientTurnError(transientErrorMessage));
6781
+ try {
6782
+ response.close();
6783
+ } catch (err) {
6784
+ console.warn("[ClaudeManager] query.close() during transient failure failed:", err);
6785
+ }
6786
+ return;
6787
+ }
6677
6788
  this.setAuthRetrying(false);
6789
+ if (this.pendingTurn && _ClaudeManager.isTurnActivity(msg)) {
6790
+ this.pendingTurn.sawActivity = true;
6791
+ }
6678
6792
  await this.handleMessage(msg);
6679
6793
  _ClaudeManager.updateBackgroundTaskState(msg, this.activeBackgroundTasks);
6680
6794
  if (linearSessionId && this.sessionLinearForwarder) {
@@ -6687,9 +6801,16 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6687
6801
  this.resolvePendingTurn();
6688
6802
  }
6689
6803
  }
6804
+ } catch (error) {
6805
+ loopError = error;
6806
+ if (!_ClaudeManager.isTransientTurnError(error)) {
6807
+ console.error("[ClaudeManager] Session loop crashed:", error);
6808
+ }
6690
6809
  } finally {
6691
6810
  this.sessionLinearForwarder?.flushThoughtAsResponse();
6692
- this.failPendingTurn(new Error("Claude session ended unexpectedly"));
6811
+ const pending = this.pendingTurn;
6812
+ const pendingError = loopError ? pending?.sawActivity && _ClaudeManager.isTransientTurnError(loopError) ? new Error("Claude session ended unexpectedly") : loopError : pending && !pending.sawActivity ? new ClaudeTransientTurnError("Claude session ended unexpectedly before producing a response") : new Error("Claude session ended unexpectedly");
6813
+ this.failPendingTurn(pendingError);
6693
6814
  if (this.activeQuery === response) {
6694
6815
  this.clearSessionState();
6695
6816
  }
@@ -6803,6 +6924,27 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6803
6924
  if (error instanceof ClaudeAuthError) return true;
6804
6925
  return isClaudeAuthErrorText(error instanceof Error ? error.message : String(error));
6805
6926
  }
6927
+ static isTransientTurnError(error) {
6928
+ if (error instanceof ClaudeTransientTurnError) return true;
6929
+ return _ClaudeManager.isTransientTurnErrorText(extractErrorText(error));
6930
+ }
6931
+ static isTransientTurnErrorText(text) {
6932
+ return isTransientErrorText(text, { httpStatuses: CLAUDE_TRANSIENT_HTTP_STATUSES });
6933
+ }
6934
+ static detectTransientTurnErrorInMessage(message, sawActivity) {
6935
+ if (sawActivity) return null;
6936
+ if (message.type === "assistant" && "error" in message && typeof message.error === "string") {
6937
+ return _ClaudeManager.isTransientTurnErrorText(message.error) ? message.error : null;
6938
+ }
6939
+ if (message.type === "result" && message.is_error) {
6940
+ const text = message.subtype === "success" ? message.result : message.errors.join("\n");
6941
+ return _ClaudeManager.isTransientTurnErrorText(text) ? text : null;
6942
+ }
6943
+ return null;
6944
+ }
6945
+ static isTurnActivity(message) {
6946
+ return message.type === "assistant" || message.type === "stream_event";
6947
+ }
6806
6948
  // Done at the message-stream layer (rather than letting the failure flow
6807
6949
  // downstream as a normal result) so we can suppress emission before the
6808
6950
  // dashboard renders the raw 401 to the user.
@@ -7149,7 +7291,7 @@ var AspClient = class {
7149
7291
  // src/managers/codex-asp/app-server-process.ts
7150
7292
  var DEFAULT_CODEX_BINARY = "codex";
7151
7293
  var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
7152
- var ENGINE_PACKAGE_VERSION = "0.1.325";
7294
+ var ENGINE_PACKAGE_VERSION = "0.1.326";
7153
7295
  var INITIALIZE_METHOD = "initialize";
7154
7296
  var INITIALIZED_NOTIFICATION = "initialized";
7155
7297
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.325",
3
+ "version": "0.1.326",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",