@tangle-network/agent-app 0.45.4 → 0.45.5

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.
@@ -100,7 +100,7 @@ import {
100
100
  flattenHistory,
101
101
  readSandboxBinaryBytes,
102
102
  statSandboxFileSize
103
- } from "../chunk-CS6MJDIM.js";
103
+ } from "../chunk-QGHQ2IRC.js";
104
104
  import "../chunk-LWSJK546.js";
105
105
  import "../chunk-73F3CKVK.js";
106
106
  import "../chunk-ITCINLSU.js";
@@ -155,6 +155,206 @@ async function readSandboxBinaryBytes(box, absolutePath, expectedSize, options)
155
155
  return { succeeded: true, value: { bytes, size: expectedSize } };
156
156
  }
157
157
 
158
+ // src/sandbox/diagnostics.ts
159
+ var EGRESS_PROXY_RECOVERY_REQUIRED = "EGRESS_PROXY_RECOVERY_REQUIRED";
160
+ var SAFE_ERROR_FIELDS = [
161
+ "code",
162
+ "status",
163
+ "phase",
164
+ "endpoint",
165
+ "origin",
166
+ "retryAfterMs",
167
+ "sidecarVersion",
168
+ "containerImage"
169
+ ];
170
+ function isRecord(value) {
171
+ return typeof value === "object" && value !== null;
172
+ }
173
+ function asSafeScalar(value) {
174
+ if (typeof value === "string" && value.length > 0) return redactDiagnosticText(value);
175
+ if (typeof value === "number" && Number.isFinite(value)) return value;
176
+ return void 0;
177
+ }
178
+ function redactDiagnosticText(value) {
179
+ const secretKeyPattern = "(?:(?:[A-Z0-9]+_)*(?:api_?key|auth_?token|access_?token|refresh_?token|key|token|password|secret|credential)|apiKey|authToken|accessToken|refreshToken)";
180
+ return value.replace(/(['"]authorization['"]\s*:\s*)(['"])[^'"]*\2/gi, "$1$2[REDACTED]$2").replace(/\b(authorization\s*[:=]\s*)(['"])[^'"]*\2/gi, "$1$2[REDACTED]$2").replace(/\b(authorization\s*[:=]\s*)Digest\b[^;}\n]*(?:\r?\n[ \t]+[^;}\n]*)*/gi, "$1[REDACTED]").replace(/\b(authorization\s*[:=]\s*)(?:Basic|Bearer|Negotiate)\s+[^\s;,&}\n]+(?:\r?\n[ \t]+[^\s;,&}\n]+)*/gi, "$1[REDACTED]").replace(new RegExp(`\\b(authorization\\s*[:=]\\s*)(?!(?:Basic|Bearer|Digest|Negotiate)\\b)([A-Za-z][A-Za-z0-9._-]+)\\s+(?!(?:authorization|${secretKeyPattern})\\s*[:=]|['"](?:authorization|${secretKeyPattern})['"]\\s*:)([^}\\n]*(?:\\r?\\n[ \\t]+[^}\\n]*)*)`, "gi"), (_match, prefix, _scheme, valuePart) => {
181
+ if (valuePart.includes(";") || /\r?\n[ \t]+/.test(valuePart)) return `${prefix}[REDACTED]`;
182
+ const tokenMatch = valuePart.match(/^\S+(.*)$/);
183
+ return `${prefix}[REDACTED]${tokenMatch?.[1] ?? ""}`;
184
+ }).replace(/\b(authorization\s*[:=]\s*)[A-Za-z][\w.-]+\s+[^\s;,&}\n]+(?=[;&}\n]|$)/gi, "$1[REDACTED]").replace(/\b(authorization\s*[:=]\s*)(?!\[REDACTED\])[^'"\s&}]+(?:[;,][^'"\s&}]+)*/gi, "$1[REDACTED]").replace(/\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi, "Bearer [REDACTED]").replace(/\bsk-[A-Za-z0-9_-]{6,}\b/g, "sk-[REDACTED]").replace(
185
+ /([?&][^=&#\s]*(?:token|key|secret|password|authorization)[^=&#\s]*=)[^&#\s]+/gi,
186
+ "$1[REDACTED]"
187
+ ).replace(
188
+ new RegExp(`(['"])(${secretKeyPattern})\\1\\s*:\\s*(['"])[^'"]+\\3`, "gi"),
189
+ "$1$2$1:$3[REDACTED]$3"
190
+ ).replace(
191
+ new RegExp(`\\b(${secretKeyPattern})\\s*([:=])(\\s*)(['"]?)(?!Bearer\\b)[^'"\\s;,&}]+(?:[;,]\\s*[^'"\\s;,&}]+)*`, "gi"),
192
+ "$1$2$3$4[REDACTED]"
193
+ );
194
+ }
195
+ function readMessage(value) {
196
+ if (value instanceof Error) return redactDiagnosticText(value.message);
197
+ if (typeof value === "string" && value.length > 0) return redactDiagnosticText(value);
198
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
199
+ if (typeof value === "boolean") return String(value);
200
+ if (!isRecord(value)) return void 0;
201
+ const message = value.message;
202
+ return typeof message === "string" && message.length > 0 ? redactDiagnosticText(message) : void 0;
203
+ }
204
+ function readName(value) {
205
+ if (value instanceof Error) return redactDiagnosticText(value.name);
206
+ if (!isRecord(value)) return void 0;
207
+ const name = value.name;
208
+ return typeof name === "string" && name.length > 0 ? redactDiagnosticText(name) : void 0;
209
+ }
210
+ function readCause(value) {
211
+ if (!isRecord(value)) return void 0;
212
+ return value.cause;
213
+ }
214
+ function serializeSandboxProvisioningError(error, options = {}) {
215
+ const maxDepth = options.maxDepth ?? 6;
216
+ const causes = [];
217
+ const seen = /* @__PURE__ */ new Set();
218
+ let truncatedAtDepth;
219
+ let cycle = false;
220
+ let current = error;
221
+ let depth = 0;
222
+ for (; depth < maxDepth && current !== void 0 && current !== null; depth += 1) {
223
+ if (seen.has(current)) {
224
+ cycle = true;
225
+ causes.push({
226
+ name: "CauseChainCycle",
227
+ message: `cause chain cycle detected after ${depth} entries`
228
+ });
229
+ break;
230
+ }
231
+ if (isRecord(current)) seen.add(current);
232
+ const cause = {};
233
+ const name = readName(current);
234
+ const message = readMessage(current);
235
+ if (name) cause.name = name;
236
+ if (message) cause.message = message;
237
+ if (isRecord(current)) {
238
+ for (const field of SAFE_ERROR_FIELDS) {
239
+ const safeValue = asSafeScalar(current[field]);
240
+ if (safeValue !== void 0) {
241
+ Object.assign(cause, { [field]: safeValue });
242
+ }
243
+ }
244
+ }
245
+ if (Object.keys(cause).length > 0) causes.push(cause);
246
+ current = readCause(current);
247
+ }
248
+ if (!cycle && isRecord(current) && seen.has(current)) {
249
+ cycle = true;
250
+ causes.push({
251
+ name: "CauseChainCycle",
252
+ message: `cause chain cycle detected after ${depth} entries`
253
+ });
254
+ } else if (current !== void 0 && current !== null && depth >= maxDepth) {
255
+ truncatedAtDepth = maxDepth;
256
+ causes.push({
257
+ name: "CauseChainTruncated",
258
+ message: `cause chain truncated after ${maxDepth} entries`
259
+ });
260
+ }
261
+ return {
262
+ message: readMessage(error) ?? "Sandbox unavailable",
263
+ causes,
264
+ truncated: truncatedAtDepth !== void 0,
265
+ truncatedAtDepth,
266
+ cycle
267
+ };
268
+ }
269
+ function formatSandboxProvisioningSupportDetails(diagnostics) {
270
+ const actionableCause = diagnostics.causes.find(
271
+ (cause) => cause.code !== void 0 || cause.status !== void 0 || cause.phase !== void 0 || cause.endpoint !== void 0 || cause.origin !== void 0 || cause.retryAfterMs !== void 0 || cause.sidecarVersion !== void 0 || cause.containerImage !== void 0
272
+ ) ?? diagnostics.causes[1];
273
+ if (!actionableCause) return "Support details: no nested sandbox cause details were available.";
274
+ const details = [
275
+ typeof actionableCause.name === "string" ? redactDiagnosticText(actionableCause.name) : void 0,
276
+ actionableCause.code !== void 0 ? `code=${actionableCause.code}` : void 0,
277
+ actionableCause.status !== void 0 ? `status=${actionableCause.status}` : void 0,
278
+ actionableCause.phase !== void 0 ? `phase=${redactDiagnosticText(actionableCause.phase)}` : void 0,
279
+ actionableCause.endpoint !== void 0 ? `endpoint=${redactDiagnosticText(actionableCause.endpoint)}` : void 0,
280
+ actionableCause.origin !== void 0 ? `origin=${redactDiagnosticText(actionableCause.origin)}` : void 0,
281
+ actionableCause.retryAfterMs !== void 0 ? `retryAfterMs=${actionableCause.retryAfterMs}` : void 0,
282
+ actionableCause.sidecarVersion !== void 0 ? `sidecarVersion=${redactDiagnosticText(actionableCause.sidecarVersion)}` : void 0,
283
+ actionableCause.containerImage !== void 0 ? `containerImage=${redactDiagnosticText(actionableCause.containerImage)}` : void 0,
284
+ typeof actionableCause.message === "string" ? redactDiagnosticText(actionableCause.message) : void 0
285
+ ].filter(Boolean);
286
+ if (details.length === 0) return "Support details: no nested sandbox cause details were available.";
287
+ return `Support details: ${details.join("; ")}`;
288
+ }
289
+ function isSandboxAuthFailure(diagnostics) {
290
+ return diagnostics.causes.some((cause) => {
291
+ const code = typeof cause.code === "string" ? cause.code.toUpperCase() : void 0;
292
+ const status = typeof cause.status === "number" ? cause.status : typeof cause.status === "string" ? Number.parseInt(cause.status, 10) : void 0;
293
+ const name = typeof cause.name === "string" ? cause.name.toLowerCase() : "";
294
+ const message = typeof cause.message === "string" ? cause.message.toLowerCase() : "";
295
+ return code === "AUTH_ERROR" || status === 401 || name.includes("autherror") || message.includes("missing or invalid authentication");
296
+ });
297
+ }
298
+ function isSandboxApiBearerAuthFailure(diagnostics) {
299
+ return diagnostics.causes.some((cause) => {
300
+ const status = typeof cause.status === "number" ? cause.status : typeof cause.status === "string" ? Number.parseInt(cause.status, 10) : void 0;
301
+ if (status !== 401) return false;
302
+ if (cause.origin !== "sandbox-api") return false;
303
+ if (typeof cause.endpoint !== "string") return false;
304
+ const endpointPath = sandboxApiEndpointPath(cause.endpoint);
305
+ if (!endpointPath) return false;
306
+ return /^\/v1\/sandboxes\/[^/?#]+(?:\/(?!runtime(?:[/?#]|$))[^?#]*)?(?:[?#].*)?$/.test(endpointPath);
307
+ });
308
+ }
309
+ function isSandboxApiSandboxMissingFailure(diagnostics) {
310
+ return diagnostics.causes.some((cause) => {
311
+ const status = typeof cause.status === "number" ? cause.status : typeof cause.status === "string" ? Number.parseInt(cause.status, 10) : void 0;
312
+ if (status !== 404) return false;
313
+ if (cause.origin !== "sandbox-api") return false;
314
+ if (typeof cause.endpoint !== "string") return false;
315
+ const endpointPath = sandboxApiEndpointPath(cause.endpoint);
316
+ if (!endpointPath) return false;
317
+ return /^\/v1\/sandboxes\/[^/?#]+(?:\/(?!runtime(?:[/?#]|$))[^?#]*)?(?:[?#].*)?$/.test(endpointPath);
318
+ });
319
+ }
320
+ function isSandboxHostCapacityFailure(diagnostics) {
321
+ return diagnostics.causes.some((cause) => {
322
+ if (cause.origin !== "sandbox-api") return false;
323
+ if (typeof cause.message !== "string") return false;
324
+ return /host has no available slot|host capacity reservation failed/i.test(cause.message);
325
+ });
326
+ }
327
+ function sandboxApiEndpointPath(endpoint) {
328
+ if (endpoint.startsWith("/")) return endpoint;
329
+ try {
330
+ const url = new URL(endpoint);
331
+ return `${url.pathname}${url.search}${url.hash}`;
332
+ } catch {
333
+ return null;
334
+ }
335
+ }
336
+ function formatSandboxProvisioningUserMessage(diagnostics) {
337
+ if (diagnostics.causes.some((cause) => cause.code === EGRESS_PROXY_RECOVERY_REQUIRED)) {
338
+ return "Sandbox recovery is required before chat can continue. The stopped sandbox has not been deleted.";
339
+ }
340
+ if (diagnostics.causes.some((cause) => cause.code === "vault.hydration_incomplete")) {
341
+ return "I couldn't finish copying your Vault into the sandbox, so I stopped rather than work from a partial copy. The copy resumes where it left off \u2014 try again in a moment.";
342
+ }
343
+ if (isSandboxApiBearerAuthFailure(diagnostics)) {
344
+ return "I'm unable to reconnect to the sandbox because its sandbox API credential was rejected. Another request may have rotated the bearer used for this operation.";
345
+ }
346
+ if (isSandboxAuthFailure(diagnostics)) {
347
+ return "I'm unable to reconnect to the sandbox because its runtime authentication failed. This can happen when an existing sandbox is reused with stale credentials.";
348
+ }
349
+ if (diagnostics.causes.some((cause) => cause.code === "PAYLOAD_TOO_LARGE" || cause.code === "FILE_TOO_LARGE" || cause.status === 413 || cause.status === "413")) {
350
+ return "An attachment is too large for the sandbox to accept. Use a smaller file and try again.";
351
+ }
352
+ if (diagnostics.causes.some((cause) => cause.code === "UPLOAD_BUDGET_EXHAUSTED")) {
353
+ return "Too much attachment data is staged in the sandbox at once. Retry shortly.";
354
+ }
355
+ return "I'm unable to connect to the sandbox right now. This usually means the sandbox service is not configured or is temporarily unavailable.";
356
+ }
357
+
158
358
  // src/sandbox/workspace-sandbox-manager.ts
159
359
  function createWorkspaceSandboxManager(opts) {
160
360
  return {
@@ -1250,10 +1450,14 @@ async function ensureWorkspaceSandbox(shell, options) {
1250
1450
  } catch (err) {
1251
1451
  if (!shell.replaceUnbringableBox) throw err;
1252
1452
  if (options.forceNew) throw err;
1253
- if (!(err instanceof SandboxRecoveryFailedError)) throw err;
1453
+ if (!isUnbringableBoxError(err)) throw err;
1254
1454
  return await provisionWorkspaceSandbox(shell, { ...options, forceNew: true });
1255
1455
  }
1256
1456
  }
1457
+ function isUnbringableBoxError(error) {
1458
+ if (error instanceof SandboxRecoveryFailedError) return true;
1459
+ return isSandboxHostCapacityFailure(serializeSandboxProvisioningError(error));
1460
+ }
1257
1461
  async function provisionWorkspaceSandbox(shell, options) {
1258
1462
  const { workspaceId, userId, harness, forceNew, onProgress, billingOwnerId } = options;
1259
1463
  const resolved = await resolveWorkspaceSandboxClient(shell, workspaceId, userId);
@@ -1735,6 +1939,14 @@ export {
1735
1939
  shellQuote,
1736
1940
  statSandboxFileSize,
1737
1941
  readSandboxBinaryBytes,
1942
+ EGRESS_PROXY_RECOVERY_REQUIRED,
1943
+ serializeSandboxProvisioningError,
1944
+ formatSandboxProvisioningSupportDetails,
1945
+ isSandboxAuthFailure,
1946
+ isSandboxApiBearerAuthFailure,
1947
+ isSandboxApiSandboxMissingFailure,
1948
+ isSandboxHostCapacityFailure,
1949
+ formatSandboxProvisioningUserMessage,
1738
1950
  createWorkspaceSandboxManager,
1739
1951
  createSandboxTerminalConnectionRoute,
1740
1952
  createSandboxPrewarmer,
@@ -1786,4 +1998,4 @@ export {
1786
1998
  isTerminalPromptEvent,
1787
1999
  detectInteractiveQuestion
1788
2000
  };
1789
- //# sourceMappingURL=chunk-CS6MJDIM.js.map
2001
+ //# sourceMappingURL=chunk-QGHQ2IRC.js.map