@tangle-network/agent-app 0.45.3 → 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-AOQ4FCSH.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 {
@@ -1245,6 +1445,20 @@ async function assertExistingBoxEgress(box, desired, stage, name) {
1245
1445
  );
1246
1446
  }
1247
1447
  async function ensureWorkspaceSandbox(shell, options) {
1448
+ try {
1449
+ return await provisionWorkspaceSandbox(shell, options);
1450
+ } catch (err) {
1451
+ if (!shell.replaceUnbringableBox) throw err;
1452
+ if (options.forceNew) throw err;
1453
+ if (!isUnbringableBoxError(err)) throw err;
1454
+ return await provisionWorkspaceSandbox(shell, { ...options, forceNew: true });
1455
+ }
1456
+ }
1457
+ function isUnbringableBoxError(error) {
1458
+ if (error instanceof SandboxRecoveryFailedError) return true;
1459
+ return isSandboxHostCapacityFailure(serializeSandboxProvisioningError(error));
1460
+ }
1461
+ async function provisionWorkspaceSandbox(shell, options) {
1248
1462
  const { workspaceId, userId, harness, forceNew, onProgress, billingOwnerId } = options;
1249
1463
  const resolved = await resolveWorkspaceSandboxClient(shell, workspaceId, userId);
1250
1464
  const { scope, client } = resolved;
@@ -1725,6 +1939,14 @@ export {
1725
1939
  shellQuote,
1726
1940
  statSandboxFileSize,
1727
1941
  readSandboxBinaryBytes,
1942
+ EGRESS_PROXY_RECOVERY_REQUIRED,
1943
+ serializeSandboxProvisioningError,
1944
+ formatSandboxProvisioningSupportDetails,
1945
+ isSandboxAuthFailure,
1946
+ isSandboxApiBearerAuthFailure,
1947
+ isSandboxApiSandboxMissingFailure,
1948
+ isSandboxHostCapacityFailure,
1949
+ formatSandboxProvisioningUserMessage,
1728
1950
  createWorkspaceSandboxManager,
1729
1951
  createSandboxTerminalConnectionRoute,
1730
1952
  createSandboxPrewarmer,
@@ -1776,4 +1998,4 @@ export {
1776
1998
  isTerminalPromptEvent,
1777
1999
  detectInteractiveQuestion
1778
2000
  };
1779
- //# sourceMappingURL=chunk-AOQ4FCSH.js.map
2001
+ //# sourceMappingURL=chunk-QGHQ2IRC.js.map