Files
PaperClipAI/packages/shared/src/workspace-restore.ts
T
DottaandPaperclip 14795136f5 fix(runtime): finalize and recover sandbox workspace exports safely (#14402)
Serialize native workspace finalization, validate streamed archives within bounded limits, and quietly recover unsafe exports from saved results. Preserve exact allocations for exhausted transient failures and provide export-only retry without rerunning the provider.

Consolidates #14314, #14315, #14329, and #14334 while preserving the already-merged finalization label changes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-28 10:57:32 -05:00

27 lines
1.3 KiB
TypeScript

/** Closed, path-free classifications persisted by adapter settlement. */
export const WORKSPACE_RESTORE_FAILURE_CODES = [
"restore_permission_denied",
"restore_lock_timeout",
"restore_unsafe_archive",
"restore_failed",
] as const;
/** These failures preserve an accepted native result for board-owned export repair. */
export function isNativeWorkspaceExportRepairCause(value: unknown): boolean {
return value === "native_workspace_sync_out_retry_exhausted";
}
export function hasWorkspaceRestoreFailure(result: Record<string, unknown> | null | undefined): boolean {
return WORKSPACE_RESTORE_FAILURE_CODES.some((code) => result?.workspaceRestoreFailure === code);
}
/** Display only ordinary repository paths, never targets, host paths or temp IDs. */
export function safeWorkspaceRestorePath(value: unknown): string | null {
if (typeof value !== "string" || value.length > 180) return null;
const relative = value.replace(/^\.\//, "");
if (!relative || !/^[a-zA-Z0-9_.\/-]+$/.test(relative) || relative.startsWith("/")) return null;
if (relative.split("/").some((part) => !part || part === "." || part === "..")) return null;
if (/(?:[a-f0-9]{8}-[a-f0-9-]{27,}|[a-zA-Z0-9_-]{32,}|(?:^|\/)(?:tmp|temp|paperclip-clone)[^/]*)(?:\/|$)/i.test(relative)) return null;
return relative;
}