/** * v0.8.1 — `--merge` conflict resolution. * * Per docs/plan/v0.8.1-security-lifecycle-pair.md §4.3. Each helper is * independently testable: this file owns *policy* (what to do on a clash) * but not *I/O orchestration* (which lives in `import.ts`). * * Invariant: the existing workspace bytes are never modified silently. A * conflict is either resolved (dedup, append) or surfaced (rejected with a * structured reason, or persisted to a `*.imported.md` sibling for manual * merge). */ export type MergeDecisionKind = "write" | "skip" | "append-dedup" | "rename-sibling" | "reject"; export interface MergeDecision { kind: MergeDecisionKind; /** Final disk path (set for write / append-dedup / rename-sibling). */ targetPath?: string; /** Final bytes to emit for `write`. */ bytes?: Buffer; /** Reason — surfaced to the user via journal + CLI summary. */ reason: string; } /** * Merge two `.jsonl` payloads, deduping by (content hash, ts) per §4.3. * * Order: existing lines first (preserved), then any *new* incoming lines * appended in their original order. A line whose normalized JSON matches an * existing line is dropped. * * Robustness: malformed JSON lines are treated as raw strings (still * dedup-able by string identity). Trailing blank lines are stripped. */ export declare function mergeJsonlBuffers(existing: Buffer, incoming: Buffer): Buffer; export interface ConflictContext { /** Path inside `/`. */ relPath: string; /** Incoming bytes from archive. */ incomingBytes: Buffer; /** Existing bytes on disk, or null if the file doesn't exist. */ existingBytes: Buffer | null; /** Absolute path on the destination filesystem. */ absPath: string; /** "merge" (default) or "replace". */ mode: "merge" | "replace"; } /** * Decide what to do with a single incoming file. Workflows/goals id * conflicts are *not* handled here — they live in `decideOrgConflict` because * they need higher-level context (the org slug + the manifest of incoming * workflow ids). */ export declare function decideFileConflict(ctx: ConflictContext): MergeDecision; export interface IdConflictReport { workflowConflicts: string[]; goalConflicts: string[]; } /** * Walk an existing org's workflows/ + goals/ folders and an incoming map of * archive paths to surface any id collision. Caller decides whether to * reject the import or fall back to `--replace`. */ export declare function detectIdConflicts(opts: { orgDir: string; incomingWorkflowIds: ReadonlySet; incomingGoalIds: ReadonlySet; }): IdConflictReport; /** * Deep-merge two parsed YAML objects for `agent-profile.yaml`. * * Per §4.3: deep-merge with the **narrowing-only invariant**. If the * incoming profile *widens* a key (e.g. sets a budget higher than the * existing one), the merge is rejected. Callers receive `null` to indicate * rejection. * * Narrowing rule (intentionally simple — full validation lives in the * agent-profile validator): * - Numeric: incoming ≤ existing * - Boolean: incoming === existing OR existing === false ↔ incoming === true is rejected * - String/array/object: equal or strict subset */ export declare function mergeAgentProfile(existing: Record, incoming: Record): { ok: true; merged: Record; } | { ok: false; violations: string[]; };