import * as crypto from "node:crypto"; import * as fs from "node:fs"; import * as path from "node:path"; import { DEFAULT_PATHS } from "../config/defaults.ts"; import { type ConflictReport, detectImportConflicts } from "../runtime/delta-conflict.ts"; import { atomicWriteFile } from "../state/atomic-write.ts"; import { logInternalError } from "../utils/internal-error.ts"; import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts"; import { assertSafePathId, resolveContainedRelativePath, resolveRealContainedPath } from "../utils/safe-paths.ts"; import { assertRunBundle } from "./run-bundle-schema.ts"; export interface ImportedRunBundleInfo { runId: string; importedAt: string; bundlePath: string; summaryPath: string; conflictReport?: ConflictReport; } // DI-1: DoS guard — cap the size of an import bundle. Exported run bundles are // bounded in practice (JSON manifest + tasks + events for one run); an oversized // file is either corrupted or a hostile DoS attempt (memory exhaustion from // reading + JSON.parse). Stat BEFORE reading so we never buffer a huge file. export const MAX_IMPORT_BUNDLE_BYTES = 50 * 1024 * 1024; /** * W7B (imported-manifest-goal-raw-render): output-encoding seam for untrusted * strings imported from run bundles (manifest.goal/team/workflow/status, * exportedAt, task.error/id/role/agent) before they are rendered into the * parent-LLM team-tool listing (lifecycle-actions.ts handleImports) or the * durable import README. Bundle files are repo-authored / plant-able content — * assertRunBundle validates types only, not content — so every rendered field * is forced into inert single-line data: * 1. strip well-formed ANSI CSI escape sequences (colors / cursor moves), * 2. collapse remaining C0/C1 controls + DEL to spaces (kills forged * multi-line listing entries and injected directive lines), * 3. strip angle brackets (kills fake fences and *