@tangle-network/agent-app 0.45.5 → 0.45.6
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-
|
|
103
|
+
} from "../chunk-4XNCMUVI.js";
|
|
104
104
|
import "../chunk-LWSJK546.js";
|
|
105
105
|
import "../chunk-73F3CKVK.js";
|
|
106
106
|
import "../chunk-ITCINLSU.js";
|
|
@@ -155,8 +155,213 @@ async function readSandboxBinaryBytes(box, absolutePath, expectedSize, options)
|
|
|
155
155
|
return { succeeded: true, value: { bytes, size: expectedSize } };
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
// src/sandbox/
|
|
158
|
+
// src/sandbox/recovery.ts
|
|
159
159
|
var EGRESS_PROXY_RECOVERY_REQUIRED = "EGRESS_PROXY_RECOVERY_REQUIRED";
|
|
160
|
+
var EGRESS_PROXY_RECOVERY_PHASE = "egress_proxy_recovery";
|
|
161
|
+
var WORKSPACE_SANDBOX_MISSING = "WORKSPACE_SANDBOX_MISSING";
|
|
162
|
+
var WORKSPACE_SANDBOX_HOST_EXHAUSTED = "WORKSPACE_SANDBOX_HOST_EXHAUSTED";
|
|
163
|
+
var WORKSPACE_SANDBOX_UNRECOVERABLE = "WORKSPACE_SANDBOX_UNRECOVERABLE";
|
|
164
|
+
var WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
165
|
+
var CODES = {
|
|
166
|
+
[EGRESS_PROXY_RECOVERY_REQUIRED]: { snapshotUsable: true },
|
|
167
|
+
[WORKSPACE_SANDBOX_MISSING]: { snapshotUsable: false },
|
|
168
|
+
[WORKSPACE_SANDBOX_HOST_EXHAUSTED]: { snapshotUsable: false },
|
|
169
|
+
[WORKSPACE_SANDBOX_UNRECOVERABLE]: { snapshotUsable: false }
|
|
170
|
+
};
|
|
171
|
+
var ACTIONS = {
|
|
172
|
+
confirmation_required: { replacementChosen: false },
|
|
173
|
+
deletion_declined: { replacementChosen: false },
|
|
174
|
+
replacement_authorized: { replacementChosen: false },
|
|
175
|
+
snapshot_replacement_authorized: { replacementChosen: false },
|
|
176
|
+
replacement_started: { replacementChosen: true },
|
|
177
|
+
replacement_completed: { replacementChosen: true },
|
|
178
|
+
snapshot_replacement_started: { replacementChosen: true },
|
|
179
|
+
snapshot_restore_failed: { replacementChosen: true },
|
|
180
|
+
snapshot_replacement_completed: { replacementChosen: true },
|
|
181
|
+
missing_replacement_started: { replacementChosen: true },
|
|
182
|
+
missing_replacement_completed: { replacementChosen: true },
|
|
183
|
+
unrecoverable_replacement_started: { replacementChosen: true },
|
|
184
|
+
unrecoverable_replacement_completed: { replacementChosen: true }
|
|
185
|
+
};
|
|
186
|
+
var WorkspaceSandboxRecoveryRequiredError = class extends Error {
|
|
187
|
+
code = EGRESS_PROXY_RECOVERY_REQUIRED;
|
|
188
|
+
status = 409;
|
|
189
|
+
phase = EGRESS_PROXY_RECOVERY_PHASE;
|
|
190
|
+
recovery;
|
|
191
|
+
constructor(recovery, cause) {
|
|
192
|
+
super(workspaceSandboxRecoveryMessage(recovery), { cause });
|
|
193
|
+
this.name = "WorkspaceSandboxRecoveryRequiredError";
|
|
194
|
+
this.recovery = recovery;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
function isRecord(value) {
|
|
198
|
+
return typeof value === "object" && value !== null;
|
|
199
|
+
}
|
|
200
|
+
function asNonEmptyString(value) {
|
|
201
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
202
|
+
}
|
|
203
|
+
function errorChain(error) {
|
|
204
|
+
const pending = [error];
|
|
205
|
+
const visited = /* @__PURE__ */ new Set();
|
|
206
|
+
const chain = [];
|
|
207
|
+
while (pending.length > 0) {
|
|
208
|
+
const current = pending.shift();
|
|
209
|
+
if (current === void 0 || current === null || visited.has(current)) continue;
|
|
210
|
+
visited.add(current);
|
|
211
|
+
chain.push(current);
|
|
212
|
+
if (!isRecord(current)) continue;
|
|
213
|
+
if (current.cause !== void 0) pending.push(current.cause);
|
|
214
|
+
if (Array.isArray(current.errors)) pending.push(...current.errors);
|
|
215
|
+
}
|
|
216
|
+
return chain;
|
|
217
|
+
}
|
|
218
|
+
function isEgressProxyRecoveryRequiredError(error) {
|
|
219
|
+
return errorChain(error).some(
|
|
220
|
+
(current) => isRecord(current) && current.code === EGRESS_PROXY_RECOVERY_REQUIRED
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
function isWorkspaceSandboxSnapshotRestoreError(error) {
|
|
224
|
+
return errorChain(error).some((current) => {
|
|
225
|
+
const message = current instanceof Error ? current.message : isRecord(current) && typeof current.message === "string" ? current.message : "";
|
|
226
|
+
return /snapshot|fromSnapshot|restore/i.test(message);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
function assessWorkspaceSandboxSnapshot(snapshot, sandboxId, now = Date.now()) {
|
|
230
|
+
if (!snapshot) return { availability: "missing", freshness: "unknown" };
|
|
231
|
+
const createdAt = Date.parse(snapshot.createdAt);
|
|
232
|
+
const ageMs = now - createdAt;
|
|
233
|
+
const isFresh = snapshot.fromSandboxId === sandboxId && Number.isFinite(createdAt) && ageMs >= 0 && ageMs <= WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS;
|
|
234
|
+
return isFresh ? { availability: "available", freshness: "fresh", snapshot } : { availability: "stale", freshness: "stale", snapshot };
|
|
235
|
+
}
|
|
236
|
+
function isSnapshotAssessment(value) {
|
|
237
|
+
if (!isRecord(value)) return false;
|
|
238
|
+
const availability = value.availability;
|
|
239
|
+
const freshness = value.freshness;
|
|
240
|
+
return (availability === "available" || availability === "missing" || availability === "stale") && (freshness === "fresh" || freshness === "stale" || freshness === "unknown");
|
|
241
|
+
}
|
|
242
|
+
function isWorkspaceSandboxRecoveryAction(value) {
|
|
243
|
+
return typeof value === "string" && Object.hasOwn(ACTIONS, value);
|
|
244
|
+
}
|
|
245
|
+
function isWorkspaceSandboxRecoveryCode(value) {
|
|
246
|
+
return typeof value === "string" && Object.hasOwn(CODES, value);
|
|
247
|
+
}
|
|
248
|
+
function isWorkspaceSandboxRecoveryState(value) {
|
|
249
|
+
if (!isRecord(value)) return false;
|
|
250
|
+
return isWorkspaceSandboxRecoveryCode(value.code) && !!asNonEmptyString(value.sandboxId) && !!asNonEmptyString(value.detectedAt) && isSnapshotAssessment(value.snapshot) && isWorkspaceSandboxRecoveryAction(value.action);
|
|
251
|
+
}
|
|
252
|
+
function workspaceSandboxRecoveryFromError(error) {
|
|
253
|
+
for (const current of errorChain(error)) {
|
|
254
|
+
if (!isRecord(current) || !isWorkspaceSandboxRecoveryState(current.recovery)) continue;
|
|
255
|
+
return current.recovery;
|
|
256
|
+
}
|
|
257
|
+
return void 0;
|
|
258
|
+
}
|
|
259
|
+
function workspaceSandboxRecoveryMessage(recovery) {
|
|
260
|
+
if (recovery.code === WORKSPACE_SANDBOX_MISSING) {
|
|
261
|
+
return "The sandbox behind this workspace no longer exists on the platform. A replacement is being provisioned and your saved work is restored into it.";
|
|
262
|
+
}
|
|
263
|
+
if (!CODES[recovery.code].snapshotUsable) {
|
|
264
|
+
return "The sandbox behind this workspace could not be started again. A replacement is being provisioned and your saved work is restored into it.";
|
|
265
|
+
}
|
|
266
|
+
if (recovery.action === "deletion_declined") {
|
|
267
|
+
return "Sandbox recovery remains paused because replacement was declined. The stopped sandbox has not been deleted.";
|
|
268
|
+
}
|
|
269
|
+
if (recovery.action === "confirmation_required") {
|
|
270
|
+
const snapshotReason = recovery.snapshot.availability === "missing" ? "No restorable sandbox snapshot is available." : "The available sandbox snapshot is stale.";
|
|
271
|
+
return `Sandbox recovery requires owner confirmation before replacement. ${snapshotReason} The stopped sandbox has not been deleted.`;
|
|
272
|
+
}
|
|
273
|
+
if (recovery.action === "snapshot_restore_failed") {
|
|
274
|
+
return "Sandbox replacement could not restore its verified snapshot, and an empty fallback was not created because it could lose workspace state.";
|
|
275
|
+
}
|
|
276
|
+
return "Sandbox recovery is required before chat can continue. The stopped sandbox has not been deleted.";
|
|
277
|
+
}
|
|
278
|
+
function workspaceSandboxRecoveryRecommendedActions(recovery) {
|
|
279
|
+
if (!CODES[recovery.code].snapshotUsable) {
|
|
280
|
+
return ["Retry after the replacement sandbox finishes provisioning."];
|
|
281
|
+
}
|
|
282
|
+
if (recovery.action === "deletion_declined") {
|
|
283
|
+
return ["Keep the stopped sandbox for support or ask a workspace owner to authorize replacement."];
|
|
284
|
+
}
|
|
285
|
+
if (recovery.action === "confirmation_required") {
|
|
286
|
+
return ["An owner can explicitly replace the sandbox after accepting loss of unsnapshotted sandbox-local changes."];
|
|
287
|
+
}
|
|
288
|
+
if (recovery.action === "snapshot_restore_failed") {
|
|
289
|
+
return ["Retry snapshot restoration or contact support. An empty replacement is not created automatically."];
|
|
290
|
+
}
|
|
291
|
+
return ["Retry after sandbox recovery completes."];
|
|
292
|
+
}
|
|
293
|
+
function workspaceSandboxRecoveryDiagnostic(recovery) {
|
|
294
|
+
return {
|
|
295
|
+
sandboxId: recovery.sandboxId,
|
|
296
|
+
recoveryCode: recovery.code,
|
|
297
|
+
snapshotAvailability: recovery.snapshot.availability,
|
|
298
|
+
snapshotFreshness: recovery.snapshot.freshness,
|
|
299
|
+
selectedRecoveryAction: recovery.action,
|
|
300
|
+
replacementSandboxId: recovery.replacementSandboxId
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function preferredWorkspaceSandboxRecoveryBoxKey(recovery) {
|
|
304
|
+
if (!recovery?.replacementBoxKey) return void 0;
|
|
305
|
+
return ACTIONS[recovery.action].replacementChosen ? recovery.replacementBoxKey : void 0;
|
|
306
|
+
}
|
|
307
|
+
function shouldRestoreWorkspaceSandboxRecovery(recovery) {
|
|
308
|
+
if (!recovery) return false;
|
|
309
|
+
if (!CODES[recovery.code].snapshotUsable) return false;
|
|
310
|
+
return !!preferredWorkspaceSandboxRecoveryBoxKey(recovery) && recovery.snapshot.availability === "available";
|
|
311
|
+
}
|
|
312
|
+
function createWorkspaceSandboxRecoveryManager(store) {
|
|
313
|
+
async function record(workspaceId, recovery) {
|
|
314
|
+
await store.write(workspaceId, recovery);
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
read: store.read,
|
|
318
|
+
record,
|
|
319
|
+
async decide({ workspaceId, sandboxId, decision, replacementBoxKey }) {
|
|
320
|
+
const current = await store.read(workspaceId);
|
|
321
|
+
if (!current || current.sandboxId !== sandboxId) return void 0;
|
|
322
|
+
const next = {
|
|
323
|
+
...current,
|
|
324
|
+
action: decision === "replace" ? current.snapshot.availability === "available" ? "snapshot_replacement_authorized" : "replacement_authorized" : "deletion_declined",
|
|
325
|
+
confirmedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
326
|
+
...decision === "replace" && replacementBoxKey ? { replacementBoxKey } : {}
|
|
327
|
+
};
|
|
328
|
+
await record(workspaceId, next);
|
|
329
|
+
return next;
|
|
330
|
+
},
|
|
331
|
+
async complete({ workspaceId, replacementSandboxId }) {
|
|
332
|
+
const current = await store.read(workspaceId);
|
|
333
|
+
if (!current) return void 0;
|
|
334
|
+
const next = {
|
|
335
|
+
...current,
|
|
336
|
+
action: completionFor(current.action),
|
|
337
|
+
replacementSandboxId
|
|
338
|
+
};
|
|
339
|
+
await record(workspaceId, next);
|
|
340
|
+
return next;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function completionFor(action) {
|
|
345
|
+
switch (action) {
|
|
346
|
+
case "missing_replacement_started":
|
|
347
|
+
return "missing_replacement_completed";
|
|
348
|
+
case "unrecoverable_replacement_started":
|
|
349
|
+
return "unrecoverable_replacement_completed";
|
|
350
|
+
case "snapshot_replacement_started":
|
|
351
|
+
case "snapshot_replacement_authorized":
|
|
352
|
+
return "snapshot_replacement_completed";
|
|
353
|
+
default:
|
|
354
|
+
return "replacement_completed";
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
var WORKSPACE_SANDBOX_RECOVERY_ACTIONS = Object.keys(
|
|
358
|
+
ACTIONS
|
|
359
|
+
);
|
|
360
|
+
var WORKSPACE_SANDBOX_RECOVERY_CODES = Object.keys(
|
|
361
|
+
CODES
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
// src/sandbox/diagnostics.ts
|
|
160
365
|
var SAFE_ERROR_FIELDS = [
|
|
161
366
|
"code",
|
|
162
367
|
"status",
|
|
@@ -167,7 +372,7 @@ var SAFE_ERROR_FIELDS = [
|
|
|
167
372
|
"sidecarVersion",
|
|
168
373
|
"containerImage"
|
|
169
374
|
];
|
|
170
|
-
function
|
|
375
|
+
function isRecord2(value) {
|
|
171
376
|
return typeof value === "object" && value !== null;
|
|
172
377
|
}
|
|
173
378
|
function asSafeScalar(value) {
|
|
@@ -197,18 +402,18 @@ function readMessage(value) {
|
|
|
197
402
|
if (typeof value === "string" && value.length > 0) return redactDiagnosticText(value);
|
|
198
403
|
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
199
404
|
if (typeof value === "boolean") return String(value);
|
|
200
|
-
if (!
|
|
405
|
+
if (!isRecord2(value)) return void 0;
|
|
201
406
|
const message = value.message;
|
|
202
407
|
return typeof message === "string" && message.length > 0 ? redactDiagnosticText(message) : void 0;
|
|
203
408
|
}
|
|
204
409
|
function readName(value) {
|
|
205
410
|
if (value instanceof Error) return redactDiagnosticText(value.name);
|
|
206
|
-
if (!
|
|
411
|
+
if (!isRecord2(value)) return void 0;
|
|
207
412
|
const name = value.name;
|
|
208
413
|
return typeof name === "string" && name.length > 0 ? redactDiagnosticText(name) : void 0;
|
|
209
414
|
}
|
|
210
415
|
function readCause(value) {
|
|
211
|
-
if (!
|
|
416
|
+
if (!isRecord2(value)) return void 0;
|
|
212
417
|
return value.cause;
|
|
213
418
|
}
|
|
214
419
|
function serializeSandboxProvisioningError(error, options = {}) {
|
|
@@ -228,13 +433,13 @@ function serializeSandboxProvisioningError(error, options = {}) {
|
|
|
228
433
|
});
|
|
229
434
|
break;
|
|
230
435
|
}
|
|
231
|
-
if (
|
|
436
|
+
if (isRecord2(current)) seen.add(current);
|
|
232
437
|
const cause = {};
|
|
233
438
|
const name = readName(current);
|
|
234
439
|
const message = readMessage(current);
|
|
235
440
|
if (name) cause.name = name;
|
|
236
441
|
if (message) cause.message = message;
|
|
237
|
-
if (
|
|
442
|
+
if (isRecord2(current)) {
|
|
238
443
|
for (const field of SAFE_ERROR_FIELDS) {
|
|
239
444
|
const safeValue = asSafeScalar(current[field]);
|
|
240
445
|
if (safeValue !== void 0) {
|
|
@@ -245,7 +450,7 @@ function serializeSandboxProvisioningError(error, options = {}) {
|
|
|
245
450
|
if (Object.keys(cause).length > 0) causes.push(cause);
|
|
246
451
|
current = readCause(current);
|
|
247
452
|
}
|
|
248
|
-
if (!cycle &&
|
|
453
|
+
if (!cycle && isRecord2(current) && seen.has(current)) {
|
|
249
454
|
cycle = true;
|
|
250
455
|
causes.push({
|
|
251
456
|
name: "CauseChainCycle",
|
|
@@ -1940,6 +2145,27 @@ export {
|
|
|
1940
2145
|
statSandboxFileSize,
|
|
1941
2146
|
readSandboxBinaryBytes,
|
|
1942
2147
|
EGRESS_PROXY_RECOVERY_REQUIRED,
|
|
2148
|
+
EGRESS_PROXY_RECOVERY_PHASE,
|
|
2149
|
+
WORKSPACE_SANDBOX_MISSING,
|
|
2150
|
+
WORKSPACE_SANDBOX_HOST_EXHAUSTED,
|
|
2151
|
+
WORKSPACE_SANDBOX_UNRECOVERABLE,
|
|
2152
|
+
WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS,
|
|
2153
|
+
WorkspaceSandboxRecoveryRequiredError,
|
|
2154
|
+
isEgressProxyRecoveryRequiredError,
|
|
2155
|
+
isWorkspaceSandboxSnapshotRestoreError,
|
|
2156
|
+
assessWorkspaceSandboxSnapshot,
|
|
2157
|
+
isWorkspaceSandboxRecoveryAction,
|
|
2158
|
+
isWorkspaceSandboxRecoveryCode,
|
|
2159
|
+
isWorkspaceSandboxRecoveryState,
|
|
2160
|
+
workspaceSandboxRecoveryFromError,
|
|
2161
|
+
workspaceSandboxRecoveryMessage,
|
|
2162
|
+
workspaceSandboxRecoveryRecommendedActions,
|
|
2163
|
+
workspaceSandboxRecoveryDiagnostic,
|
|
2164
|
+
preferredWorkspaceSandboxRecoveryBoxKey,
|
|
2165
|
+
shouldRestoreWorkspaceSandboxRecovery,
|
|
2166
|
+
createWorkspaceSandboxRecoveryManager,
|
|
2167
|
+
WORKSPACE_SANDBOX_RECOVERY_ACTIONS,
|
|
2168
|
+
WORKSPACE_SANDBOX_RECOVERY_CODES,
|
|
1943
2169
|
serializeSandboxProvisioningError,
|
|
1944
2170
|
formatSandboxProvisioningSupportDetails,
|
|
1945
2171
|
isSandboxAuthFailure,
|
|
@@ -1998,4 +2224,4 @@ export {
|
|
|
1998
2224
|
isTerminalPromptEvent,
|
|
1999
2225
|
detectInteractiveQuestion
|
|
2000
2226
|
};
|
|
2001
|
-
//# sourceMappingURL=chunk-
|
|
2227
|
+
//# sourceMappingURL=chunk-4XNCMUVI.js.map
|