@aiden-ade/sandbox-agent 0.1.38 → 0.1.39

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/index.cjs +221 -35
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -15717,10 +15717,107 @@ function getCodexPermissionArgs(config) {
15717
15717
  const sandbox = shouldUseReadOnlyRuntimePermissions(config) ? "read-only" : "danger-full-access";
15718
15718
  return ["--ask-for-approval", "never", "--sandbox", sandbox];
15719
15719
  }
15720
+ var ERROR_SPECS = {
15721
+ model_mismatch: {
15722
+ message: "The selected model isn't available, or you don't have access to it. Pick a different model, then send your message again.",
15723
+ recoveryClass: "user_fixable"
15724
+ },
15725
+ auth_invalid: {
15726
+ message: "API key invalid or expired. Check your provider settings, then retry.",
15727
+ recoveryClass: "user_fixable"
15728
+ },
15729
+ not_logged_in: {
15730
+ message: "Not logged in to this provider. Sign in from provider settings, then retry.",
15731
+ recoveryClass: "user_fixable"
15732
+ },
15733
+ not_authenticated: {
15734
+ message: "The agent CLI isn't signed in on this runtime. Authenticate it there, then retry.",
15735
+ recoveryClass: "needs_env"
15736
+ },
15737
+ auth_expired: {
15738
+ message: "The provider connection expired. Reconnect it in Cloud settings, then restart or recreate the machine.",
15739
+ recoveryClass: "user_fixable"
15740
+ },
15741
+ subscription_required: {
15742
+ message: "This model requires a paid subscription. Switch to a supported model or upgrade your plan.",
15743
+ recoveryClass: "user_fixable"
15744
+ },
15745
+ quota_exceeded: {
15746
+ message: "API quota exceeded. Wait for it to reset or check your billing, then retry.",
15747
+ recoveryClass: "user_fixable"
15748
+ },
15749
+ usage_limit: {
15750
+ message: "Provider usage limit reached. Switch model or wait for the limit to reset, then retry.",
15751
+ recoveryClass: "user_fixable"
15752
+ },
15753
+ context_length: {
15754
+ message: "Context window exceeded \u2014 the conversation is too long. Start a new session or send a shorter message.",
15755
+ recoveryClass: "user_fixable"
15756
+ },
15757
+ rate_limited: {
15758
+ message: "Rate limited by the provider. Retrying automatically shortly.",
15759
+ recoveryClass: "auto_recovering"
15760
+ },
15761
+ overloaded: {
15762
+ message: "The provider is overloaded. Retrying automatically.",
15763
+ recoveryClass: "auto_recovering"
15764
+ },
15765
+ network: {
15766
+ message: "Network connection failed. Check your internet connection \u2014 retrying automatically.",
15767
+ recoveryClass: "auto_recovering"
15768
+ },
15769
+ provider_timeout: {
15770
+ message: "The provider timed out while continuing the turn. Send your message again; if it repeats, check network/API latency or reduce slow tool calls.",
15771
+ recoveryClass: "retry"
15772
+ },
15773
+ resume_failed: {
15774
+ message: "The previous session could not be resumed \u2014 its stored session was missing or expired. The next message starts a fresh session without earlier context.",
15775
+ recoveryClass: "retry"
15776
+ },
15777
+ permission_denied: {
15778
+ message: "Permission denied. Check file or command permissions on the runtime.",
15779
+ recoveryClass: "needs_env"
15780
+ },
15781
+ out_of_memory: {
15782
+ message: "Out of memory on the runtime. Close other work or use a larger machine, then retry.",
15783
+ recoveryClass: "needs_env"
15784
+ },
15785
+ provider_error: {
15786
+ message: "The provider returned an error. Send your message again to retry.",
15787
+ recoveryClass: "retry"
15788
+ },
15789
+ unknown_cli_error: {
15790
+ message: "The agent stopped unexpectedly. Send your message again to retry.",
15791
+ recoveryClass: "retry"
15792
+ }
15793
+ };
15794
+ function specForErrorKind(errorKind) {
15795
+ const spec = ERROR_SPECS[errorKind];
15796
+ return { message: spec.message, errorKind, recoveryClass: spec.recoveryClass };
15797
+ }
15720
15798
  function isLikelyProviderAuthError(stderr) {
15721
15799
  const lower = stderr.toLowerCase();
15722
15800
  return lower.includes("api key") || lower.includes("api_key") || lower.includes("invalid api key") || lower.includes("api key invalid") || lower.includes("unauthorized") || lower.includes("authentication failed") || lower.includes("authentication error") || lower.includes("provider settings") || lower.includes("invalid token") || lower.includes("token expired");
15723
15801
  }
15802
+ function mapProviderApiError(errorCode, apiStatus) {
15803
+ const code = errorCode?.trim().toLowerCase();
15804
+ if (code) {
15805
+ if (code.includes("model_not_found") || code.includes("model_not_available")) {
15806
+ return "model_mismatch";
15807
+ }
15808
+ if (code.includes("authentication") || code.includes("permission_error")) return "auth_invalid";
15809
+ if (code.includes("rate_limit")) return "rate_limited";
15810
+ if (code.includes("overloaded")) return "overloaded";
15811
+ if (code.includes("insufficient_quota") || code.includes("billing")) return "quota_exceeded";
15812
+ }
15813
+ if (typeof apiStatus === "number") {
15814
+ if (apiStatus === 404) return "model_mismatch";
15815
+ if (apiStatus === 401 || apiStatus === 403) return "auth_invalid";
15816
+ if (apiStatus === 429) return "rate_limited";
15817
+ if (apiStatus >= 500) return "overloaded";
15818
+ }
15819
+ return null;
15820
+ }
15724
15821
  function normalizeCodexCliErrorMessage(message) {
15725
15822
  const lower = message.toLowerCase();
15726
15823
  if (lower.includes("reconnecting") && lower.includes("request timed out")) {
@@ -15731,40 +15828,73 @@ function normalizeCodexCliErrorMessage(message) {
15731
15828
  }
15732
15829
  return message;
15733
15830
  }
15734
- function classifyCliError(stderr, exitCode) {
15831
+ function classifyCliErrorDetailed(stderr, _exitCode, opts) {
15735
15832
  const normalizedCodexError = normalizeCodexCliErrorMessage(stderr);
15736
- if (normalizedCodexError !== stderr) return normalizedCodexError;
15737
- const lower = stderr.toLowerCase();
15738
- if (lower.includes("authentication required") || lower.includes("not authenticated"))
15739
- return "Cursor Agent is not authenticated. Log in with `cursor-agent login` on this machine, then retry.";
15740
- if (lower.includes("out of usage") || lower.includes("increase your limit"))
15741
- return "Provider usage limit reached. Switch model/plan or wait for the limit to reset, then retry.";
15742
- if (lower.includes("no chat found") || lower.includes("chat not found") || lower.includes("session not found") || lower.includes("could not resume"))
15743
- return "The previous session could not be resumed \u2014 the CLI's stored session was missing or expired. The next message starts a fresh session without earlier context.";
15744
- if (lower.includes("rate_limit") || lower.includes("rate limit") || lower.includes("429"))
15745
- return "Rate limited by API. Try again shortly.";
15746
- if (isLikelyProviderAuthError(stderr))
15747
- return "API key invalid or expired. Check your provider settings.";
15748
- if (lower.includes("not logged in") || lower.includes("login required") || lower.includes("please sign in") || lower.includes("please log in") || lower.includes("sign in to") || lower.includes("log in to"))
15749
- return "Not logged in. Please authenticate with this provider first.";
15750
- if (lower.includes("model not supported") || lower.includes("unsupported model") || lower.includes("model not found") || lower.includes("model not available") || lower.includes("model is not available") || lower.includes("invalid model") || lower.includes("unknown model") || lower.includes("no such model") || lower.includes("llm not set"))
15751
- return "Model not supported by this provider. Check your provider's model list.";
15752
- if (lower.includes("subscription") || lower.includes("paid plan") || lower.includes("plan required") || lower.includes("upgrade your plan") || lower.includes("only supports free") || lower.includes("not available on your") || lower.includes("requires a paid"))
15753
- return "This model requires a paid subscription. Switch to a supported model or upgrade your plan.";
15754
- if (lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("daily limit") || lower.includes("monthly limit") || lower.includes("usage limit"))
15755
- return "API quota exceeded. Wait for it to reset or check your billing.";
15756
- if (lower.includes("overloaded") || lower.includes("503") || lower.includes("service unavailable"))
15757
- return "API is overloaded. Will retry automatically.";
15758
- if (lower.includes("context_length") || lower.includes("too long") || lower.includes("max tokens") || lower.includes("context window"))
15759
- return "Context window exceeded. Try a shorter prompt or start a new session.";
15760
- if (lower.includes("econnrefused") || lower.includes("network") || lower.includes("enotfound") || lower.includes("timeout") || lower.includes("etimedout"))
15761
- return "Network connection failed. Check your internet connection.";
15762
- if (lower.includes("permission denied") || lower.includes("eacces"))
15763
- return "Permission denied. Check file or command permissions.";
15764
- if (lower.includes("out of memory") || lower.includes("enomem"))
15765
- return "Out of memory. Close other applications and try again.";
15833
+ if (normalizedCodexError !== stderr) {
15834
+ const kind = /timed out/i.test(normalizedCodexError) ? "provider_timeout" : "auth_expired";
15835
+ return {
15836
+ message: normalizedCodexError,
15837
+ errorKind: kind,
15838
+ recoveryClass: ERROR_SPECS[kind].recoveryClass
15839
+ };
15840
+ }
15841
+ const haystack = [stderr, ...opts?.extraSignals ?? []].join("\n").toLowerCase();
15842
+ const has = (...needles) => needles.some((n) => haystack.includes(n));
15843
+ if (has("authentication required", "not authenticated"))
15844
+ return specForErrorKind("not_authenticated");
15845
+ if (has("out of usage", "increase your limit")) return specForErrorKind("usage_limit");
15846
+ if (has("no chat found", "chat not found", "session not found", "could not resume"))
15847
+ return specForErrorKind("resume_failed");
15848
+ if (has("rate_limit", "rate limit", "429")) return specForErrorKind("rate_limited");
15849
+ if (isLikelyProviderAuthError(haystack)) return specForErrorKind("auth_invalid");
15850
+ if (has(
15851
+ "not logged in",
15852
+ "login required",
15853
+ "please sign in",
15854
+ "please log in",
15855
+ "sign in to",
15856
+ "log in to"
15857
+ ))
15858
+ return specForErrorKind("not_logged_in");
15859
+ if (has(
15860
+ "model not supported",
15861
+ "unsupported model",
15862
+ "model not found",
15863
+ "model not available",
15864
+ "model is not available",
15865
+ "invalid model",
15866
+ "unknown model",
15867
+ "no such model",
15868
+ "llm not set",
15869
+ // Claude Code phrasings (safety net; the structured decoder catches these first).
15870
+ "issue with the selected model",
15871
+ "may not exist or you may not have access"
15872
+ ))
15873
+ return specForErrorKind("model_mismatch");
15874
+ if (has(
15875
+ "subscription",
15876
+ "paid plan",
15877
+ "plan required",
15878
+ "upgrade your plan",
15879
+ "only supports free",
15880
+ "not available on your",
15881
+ "requires a paid"
15882
+ ))
15883
+ return specForErrorKind("subscription_required");
15884
+ if (has("quota exceeded", "quota limit", "daily limit", "monthly limit", "usage limit"))
15885
+ return specForErrorKind("quota_exceeded");
15886
+ if (has("overloaded", "503", "service unavailable")) return specForErrorKind("overloaded");
15887
+ if (has("context_length", "too long", "max tokens", "context window"))
15888
+ return specForErrorKind("context_length");
15889
+ if (has("econnrefused", "network", "enotfound", "timeout", "etimedout"))
15890
+ return specForErrorKind("network");
15891
+ if (has("permission denied", "eacces")) return specForErrorKind("permission_denied");
15892
+ if (has("out of memory", "enomem")) return specForErrorKind("out_of_memory");
15766
15893
  const raw = stderr.trim().slice(0, 200);
15767
- return raw || `Process exited with code ${exitCode}`;
15894
+ if (raw) {
15895
+ return { message: raw, errorKind: "unknown_cli_error", recoveryClass: "unknown" };
15896
+ }
15897
+ return specForErrorKind("unknown_cli_error");
15768
15898
  }
15769
15899
  function isTransientError(errorMessage) {
15770
15900
  const lower = errorMessage.toLowerCase();
@@ -16204,13 +16334,34 @@ function createGenericCliBackend(options) {
16204
16334
  };
16205
16335
  }
16206
16336
  const summary = state.summary.trim() || state.error?.trim() || (failed ? "Task failed" : "Task completed");
16337
+ let classifiedError;
16338
+ let classifiedErrorKind;
16339
+ let classifiedRecoveryClass;
16340
+ if (failed) {
16341
+ if (state.errorKind && state.error?.trim()) {
16342
+ classifiedError = state.error.trim();
16343
+ classifiedErrorKind = state.errorKind;
16344
+ classifiedRecoveryClass = state.recoveryClass;
16345
+ } else {
16346
+ const detailed = classifyCliErrorDetailed(
16347
+ state.error?.trim() || stderrText || "",
16348
+ exitCode,
16349
+ { extraSignals: state.summary ? [state.summary] : void 0 }
16350
+ );
16351
+ classifiedError = detailed.message;
16352
+ classifiedErrorKind = detailed.errorKind;
16353
+ classifiedRecoveryClass = detailed.recoveryClass;
16354
+ }
16355
+ }
16207
16356
  return {
16208
16357
  success: !failed,
16209
16358
  summary,
16210
16359
  filesModified: [],
16211
16360
  planFilesCreated: [],
16212
16361
  iterations: Math.max(state.iterations, 1),
16213
- error: failed ? classifyCliError(state.error?.trim() || stderrText || "", exitCode) : void 0,
16362
+ error: classifiedError,
16363
+ ...classifiedErrorKind ? { errorKind: classifiedErrorKind } : {},
16364
+ ...classifiedRecoveryClass ? { recoveryClass: classifiedRecoveryClass } : {},
16214
16365
  providerSessionId: state.runtimeSessionId,
16215
16366
  runtimeSessionId: state.runtimeSessionId,
16216
16367
  backendKind: options.kind,
@@ -16884,6 +17035,9 @@ function handleClaudeStructuredEvent(parsed, context, state) {
16884
17035
  if (typeof parsed.session_id === "string") {
16885
17036
  state.runtimeSessionId = parsed.session_id;
16886
17037
  }
17038
+ if (typeof parsed.error === "string" && parsed.error.trim()) {
17039
+ state.providerErrorCode = parsed.error.trim();
17040
+ }
16887
17041
  const parentToolUseId = typeof parsed.parent_tool_use_id === "string" ? parsed.parent_tool_use_id : null;
16888
17042
  switch (type) {
16889
17043
  case "system": {
@@ -17096,6 +17250,27 @@ function handleClaudeStructuredEvent(parsed, context, state) {
17096
17250
  })
17097
17251
  );
17098
17252
  }
17253
+ const isApiError = parsed.is_error === true || parsed.terminal_reason === "api_error" || typeof parsed.api_error_status === "number";
17254
+ if (isApiError && !state.error) {
17255
+ const apiStatus = typeof parsed.api_error_status === "number" ? parsed.api_error_status : void 0;
17256
+ state.apiErrorStatus = apiStatus;
17257
+ const mappedKind = mapProviderApiError(state.providerErrorCode, apiStatus);
17258
+ const spec = mappedKind ? specForErrorKind(mappedKind) : specForErrorKind("provider_error");
17259
+ state.error = mappedKind || typeof parsed.result !== "string" || !parsed.result.trim() ? spec.message : parsed.result.trim();
17260
+ state.errorKind = spec.errorKind;
17261
+ state.recoveryClass = spec.recoveryClass;
17262
+ console.error(
17263
+ "[claude_cli] result flagged API error:",
17264
+ JSON.stringify({
17265
+ is_error: parsed.is_error,
17266
+ terminal_reason: parsed.terminal_reason,
17267
+ api_error_status: parsed.api_error_status,
17268
+ providerErrorCode: state.providerErrorCode,
17269
+ mappedKind,
17270
+ result: typeof parsed.result === "string" ? parsed.result.slice(0, 200) : void 0
17271
+ })
17272
+ );
17273
+ }
17099
17274
  debugAskLog(
17100
17275
  `[claude_cli] result event \u2014 pendingAskUserToolIds size=${presenter.pendingAskUserToolIds?.size ?? "undefined"}, hasOwnProp=${Object.hasOwn(presenter, "pendingAskUserToolIds")}`
17101
17276
  );
@@ -21829,6 +22004,10 @@ var ANTIGRAVITY_MODEL_DISCOVERY_TIMEOUT_MS = 12e3;
21829
22004
  var OPENCODE_ZEN_MODEL_IDS = OPENCODE_ZEN_MODELS.map((model) => model.id);
21830
22005
  var CLAUDE_MODEL_ID_PATTERN = /claude-(?:sonnet|opus|haiku|fable)-[a-z0-9][a-z0-9-]*/g;
21831
22006
  var CLAUDE_MODEL_ALIASES = ["sonnet", "opus", "haiku", "fable"];
22007
+ var CLAUDE_MODEL_QUALIFIER_SEGMENTS = /* @__PURE__ */ new Set(["fast", "thinking", "latest", "preview"]);
22008
+ var PROTECTED_CATALOG_MODEL_IDS = new Set(
22009
+ AVAILABLE_MODELS.map((model) => model.id).filter((id) => id.length > 0)
22010
+ );
21832
22011
  var CATALOG_MODEL_IDS_BY_PROVIDER = Object.fromEntries(
21833
22012
  Object.entries(PROVIDER_MODELS).map(([provider, models]) => [
21834
22013
  provider,
@@ -21919,15 +22098,22 @@ function extractModelIdsFromLabeledList(value2) {
21919
22098
  }
21920
22099
  function isValidClaudeModelId(id) {
21921
22100
  if (CLAUDE_MODEL_ALIASES.includes(id)) return true;
21922
- if (!/^claude-(sonnet|opus|haiku|fable)-[a-z0-9][a-z0-9-]*$/.test(id)) return false;
22101
+ const match = id.match(/^claude-(sonnet|opus|haiku|fable)-([a-z0-9][a-z0-9-]*)$/);
22102
+ if (!match) return false;
21923
22103
  if (id.includes(".")) return false;
21924
22104
  if (/-\d{8}$/.test(id)) return false;
21925
22105
  if (/-v\d+$/.test(id)) return false;
22106
+ for (const segment of match[2].split("-")) {
22107
+ if (/^\d+$/.test(segment)) continue;
22108
+ if (CLAUDE_MODEL_QUALIFIER_SEGMENTS.has(segment)) continue;
22109
+ return false;
22110
+ }
21926
22111
  return true;
21927
22112
  }
21928
22113
  function filterClaudePrefixModelIds(ids) {
21929
22114
  const set = new Set(ids);
21930
22115
  return ids.filter((id) => {
22116
+ if (PROTECTED_CATALOG_MODEL_IDS.has(id)) return true;
21931
22117
  for (const other of set) {
21932
22118
  if (other !== id && other.startsWith(`${id}-`)) return false;
21933
22119
  }
@@ -22250,7 +22436,7 @@ var RunStartGate = class {
22250
22436
  };
22251
22437
 
22252
22438
  // src/version.ts
22253
- var AGENT_VERSION = "0.1.38";
22439
+ var AGENT_VERSION = "0.1.39";
22254
22440
 
22255
22441
  // src/workspace-relocation.ts
22256
22442
  var import_node_child_process3 = require("child_process");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiden-ade/sandbox-agent",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "type": "module",
5
5
  "description": "Alan agent runtime — cloud sandbox and local daemon (alan-agent CLI)",
6
6
  "bin": {