UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

1,559 lines • 68.1 kB
import { randomBytes } from "node:crypto";
import { assertRetentionPolicy } from "./retention-policy.js";
import { uuidv7 } from "../internal/harness.js";
import { canAccessAgentRecord, BackgroundAgentStoreError, clearRevivedRowTerminalPayload, REVIVED_ROW_CLEARED_FIELDS, STALE_RUNNING_REAP_ATTRIBUTION, } from "./background-agent-store.js";
import { shutdownDebug } from "./shutdown-debug.js";
import { delimitUntrusted } from "./untrusted-text.js";
import { boundedRedactedSummary } from "./untrusted-egress.js";
import { mintCompletionId, commitCompletionIdIfEmpty, clipTaskOutput, assertOwnership, sleepPollStep, alreadyTerminalStopNote, canAccess, normalizeAgentName, closestName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR, } from "./task-registry-shared.js";
import { buildToolResultRef, OFFLOAD_TOOL_NAME, toolResultProvenanceOf } from "./tool-result-store.js";
import { thrownLabel } from "./thrown-value.js";
import { agentStateSourceOfHandle, agentStateSummary, readAgentState } from "./agent-state.js";
export function activeDelegationHandlesLane(core, scope, rootSessionId) {
    const out = [];
    for (const h of core.handles.values()) {
        if (h.type !== "background_agent")
            continue;
        if (h.status !== "running" && h.status !== "pending")
            continue;
        if (h.scope !== scope)
            continue;
        const bh = h;
        const root = bh.rootSessionId ?? bh.parentSessionId ?? (bh.sessionScoped ? bh.owner : undefined);
        if (root !== rootSessionId)
            continue;
        out.push(h.id);
    }
    return out;
}
export function ensureDurableHeartbeatLane(core) {
    if (core.durableHeartbeatTimer !== undefined)
        return;
    const t = setInterval(() => {
        let liveLanes = 0;
        for (const h of core.handles.values()) {
            if (h.type !== "background_agent")
                continue;
            const bh = h;
            if (!bh.durable || bh.durable.poisoned)
                continue;
            if (bh.status === "running") {
                liveLanes++;
                durableAgentWriteLane(bh, {});
            }
            else if (bh.durable.flushFailed) {
                if ((bh.durable.redrives = (bh.durable.redrives ?? 0) + 1) > 60) {
                    bh.durable.poisoned = true;
                    process.emitWarning(`sema durable-agents: terminal flush for ${bh.durable.record.handle} unrecoverable after 60 heartbeat re-drives — durable writes for this row stop here`);
                    continue;
                }
                liveLanes++;
                durableAgentWriteLane(bh, {});
            }
        }
        if (liveLanes === 0) {
            clearInterval(t);
            core.durableHeartbeatTimer = undefined;
        }
    }, DURABLE_AGENT_HEARTBEAT_MS);
    t.unref?.();
    core.durableHeartbeatTimer = t;
}
export function durableAgentWriteLane(handle, patch, clear) {
    const lane = handle.durable;
    if (!lane || lane.poisoned)
        return;
    lane.chain = lane.chain.then(async () => {
        if (lane.poisoned)
            return;
        Object.assign(lane.record, patch);
        if (clear !== undefined)
            for (const k of clear)
                delete lane.record[k];
        lane.record.updatedAt = Date.now();
        const poison = (why) => {
            lane.poisoned = true;
            process.emitWarning(`sema durable-agents: ${why} for ${lane.record.handle} — durable writes for this row stop here`);
        };
        const adoptOwnRow = async () => {
            try {
                const live = await lane.store.get(lane.record.handle, lane.record.scope);
                if (!live)
                    return "gone";
                if (live.writerId !== lane.record.writerId)
                    return "foreign";
                if (live.writerEpoch !== lane.record.writerEpoch)
                    return "foreign";
                lane.record.rev = live.rev;
                lane.written = true;
                return "adopted";
            }
            catch {
                return "unreadable";
            }
        };
        for (let attempt = 0; attempt < 3; attempt++) {
            if (lane.poisoned)
                return;
            if (attempt > 0) {
                await new Promise((r) => {
                    const t = setTimeout(r, 50 * attempt);
                    t.unref?.();
                });
            }
            try {
                if (!lane.written) {
                    await lane.store.put(structuredClone(lane.record));
                    lane.written = true;
                    lane.flushFailed = false;
                    lane.redrives = 0;
                    return;
                }
                const ok = await lane.store.update(lane.record.handle, lane.record.scope, structuredClone(lane.record), {
                    rev: lane.record.rev,
                });
                if (ok) {
                    lane.record.rev += 1;
                    lane.flushFailed = false;
                    lane.redrives = 0;
                    return;
                }
                const verdict = await adoptOwnRow();
                if (lane.poisoned)
                    return;
                if (verdict === "adopted")
                    continue;
                if (verdict === "unreadable")
                    continue;
                poison(verdict === "gone" ? "row reaped mid-write" : "CAS lost to a foreign writer");
                return;
            }
            catch (e) {
                if (e instanceof BackgroundAgentStoreError && e.code === "agent_record.already_exists") {
                    const verdict = await adoptOwnRow();
                    if (lane.poisoned)
                        return;
                    if (verdict === "adopted")
                        continue;
                    if (verdict === "unreadable")
                        continue;
                    if (verdict === "gone")
                        continue;
                    poison("handle collision (foreign durable row left untouched)");
                    return;
                }
                shutdownDebug(`durable-agents write failed for ${lane.record.handle} (attempt ${attempt + 1}/3)`, { error: thrownLabel(e) });
            }
        }
        lane.flushFailed = true;
    });
}
export function durableAgentArmedLane(core, id) {
    const h = core.handles.get(id);
    if (!h || h.type !== "background_agent")
        return false;
    const lane = h.durable;
    return lane !== undefined && !lane.poisoned;
}
export function durableAgentRowProbeLane(core, id) {
    const h = core.handles.get(id);
    if (!h || h.type !== "background_agent")
        return undefined;
    const handle = h;
    if (!handle.durable)
        return undefined;
    return async () => {
        const lane = handle.durable;
        if (lane === undefined)
            return false;
        await lane.chain.catch(() => undefined);
        return lane.written && !lane.poisoned && !lane.flushFailed;
    };
}
export function beginDurableClaimLane(core, id) {
    const live = core.handles.get(id);
    if (live !== undefined && (live.status === "running" || live.status === "pending"))
        return false;
    if (core.reapingHandles.has(id) || core.claimingHandles.has(id))
        return false;
    core.claimingHandles.add(id);
    return true;
}
export function endDurableClaimLane(core, id) {
    core.claimingHandles.delete(id);
}
export async function reapDurableAgentsLane(core, scope, deps, policy) {
    assertRetentionPolicy("reapDurableAgents", policy);
    const now = policy.now ?? Date.now();
    if (policy.staleRunningMaxAgeMs !== undefined) {
        await deps.store.reap(scope, now, { staleRunningMaxAgeMs: policy.staleRunningMaxAgeMs });
    }
    if (policy.maxAgeMs === undefined && policy.keep === undefined)
        return { rowsReaped: 0, sessionsReleased: 0, skippedNoSessions: 0, orphanPlacedReleased: 0 };
    const terminal = (await deps.store.listByScope(scope)).filter((r) => r.status !== "running" && r.status !== "parked");
    terminal.sort((a, b) => b.spawnedAt - a.spawnedAt);
    const doomed = new Map();
    if (policy.maxAgeMs !== undefined) {
        for (const r of terminal) {
            if ((r.settledAt ?? r.spawnedAt) < now - policy.maxAgeMs)
                doomed.set(r.handle, r);
        }
    }
    if (policy.keep !== undefined) {
        for (const r of terminal.slice(Math.max(0, policy.keep)))
            doomed.set(r.handle, r);
    }
    let rowsReaped = 0;
    let sessionsReleased = 0;
    let skippedNoSessions = 0;
    for (const r of doomed.values()) {
        if (core.claimingHandles.has(r.handle))
            continue;
        core.reapingHandles.add(r.handle);
        try {
            let live;
            try {
                live = await deps.store.get(r.handle, scope);
            }
            catch {
                continue;
            }
            if (!live || live.status === "running" || live.status === "parked")
                continue;
            const inProc = core.handles.get(r.handle);
            if (inProc !== undefined && (inProc.status === "running" || inProc.status === "pending" || inProc.status === "parked"))
                continue;
            if (live.sessionId !== undefined && deps.sessions === undefined) {
                skippedNoSessions++;
                continue;
            }
            try {
                if (!(await deps.store.delete(r.handle, scope, { rev: live.rev })))
                    continue;
            }
            catch {
                continue;
            }
            rowsReaped++;
            {
                const staleInProc = core.handles.get(r.handle);
                if (staleInProc !== undefined && staleInProc.status !== "running" && staleInProc.status !== "pending" && staleInProc.status !== "parked") {
                    core.handles.delete(r.handle);
                }
            }
            core.reapedHandles.add(r.handle);
            if (deps.mailbox !== undefined) {
                try {
                    await deps.mailbox.drop(scope, r.handle);
                }
                catch {
                }
            }
            if (live.sessionId !== undefined && deps.sessions !== undefined) {
                try {
                    await deps.sessions.unpin?.(live.sessionId);
                    await deps.sessions.release(live.sessionId);
                    sessionsReleased++;
                }
                catch {
                }
            }
        }
        finally {
            core.reapingHandles.delete(r.handle);
            core.reapedHandles.delete(r.handle);
        }
    }
    let orphanPlacedReleased = 0;
    if (policy.maxAgeMs !== undefined && deps.sessions !== undefined && typeof deps.sessions.listPlaced === "function") {
        let placed = [];
        try {
            placed = await deps.sessions.listPlaced("subagent", { olderThanMs: policy.maxAgeMs, scope });
        }
        catch {
            placed = [];
        }
        for (const p of placed) {
            if ("tupleIncomplete" in p)
                continue;
            if (p.scope !== scope)
                continue;
            if (core.claimingHandles.has(p.handle) || core.reapingHandles.has(p.handle))
                continue;
            let row;
            try {
                row = await deps.store.get(p.handle, scope);
            }
            catch {
                continue;
            }
            if (row !== null)
                continue;
            const inProc = core.handles.get(p.handle);
            if (inProc !== undefined && (inProc.status === "running" || inProc.status === "pending" || inProc.status === "parked"))
                continue;
            try {
                await deps.sessions.unpin?.(p.sessionId);
                await deps.sessions.release(p.sessionId);
                orphanPlacedReleased++;
            }
            catch {
            }
            if (deps.mailbox !== undefined) {
                try {
                    await deps.mailbox.drop(scope, p.handle);
                }
                catch {
                }
            }
        }
    }
    return { rowsReaped, sessionsReleased, skippedNoSessions, orphanPlacedReleased };
}
export function releaseDurableTranscriptAnchorLane(core, id) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent")
        return;
    durableAgentWriteLane(handle, {}, ["sessionId"]);
}
export function bindBackgroundAgentSessionLane(core, id, sessionId) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent")
        return;
    handle.sessionId = sessionId;
    durableAgentWriteLane(handle, { sessionId });
}
export function recordBackgroundAgentOrgAdmissionLane(core, id, verdict) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent")
        return;
    const lane = handle.durable;
    if (lane === undefined)
        return;
    const disclose = (why) => {
        process.emitWarning(`sema durable-agents: org-admission record for ${lane.record.handle} was not persisted (${why}) — the row keeps the previously recorded verdict, which a later revival will seed from`);
    };
    if (lane.poisoned) {
        disclose("durable lane poisoned");
        return;
    }
    durableAgentWriteLane(handle, { admittedOrgScopes: [...verdict.scopes], admittedOrgWriteScope: verdict.writeScope });
    void lane.chain.then(() => {
        if (lane.poisoned)
            disclose("durable lane poisoned");
        else if (lane.flushFailed)
            disclose("durable write failed");
    });
}
export function registerBackgroundAgentLane(core, input) {
    assertOwnership(input, "registerBackgroundAgent");
    if (input.id !== undefined && !DURABLE_AGENT_HANDLE_RE.test(input.id)) {
        throw new Error(`TaskRegistry: background-agent id ${JSON.stringify(input.id)} does not match the a<hex> handle form`);
    }
    const id = input.id ?? core.mintTaskId("background_agent");
    if (input.durableSeed !== undefined) {
        const prior = core.handles.get(id);
        if (prior !== undefined) {
            const parkedHandoff = prior.status === "parked" && input.initialStatus === "parked";
            if (prior.type !== "background_agent" || prior.status === "running" || prior.status === "pending" || (prior.status === "parked" && !parkedHandoff)) {
                throw new Error(`TaskRegistry: task id already registered: ${id}`);
            }
            const priorLane = prior.durable;
            if (priorLane !== undefined)
                priorLane.poisoned = true;
            core.handles.delete(id);
        }
    }
    if (core.handles.has(id))
        throw new Error(`TaskRegistry: task id already registered: ${id}`);
    const now = input.now ?? Date.now();
    core.handles.set(id, {
        id,
        type: "background_agent",
        status: input.initialStatus ?? "running",
        ...(input.parkedCheckpointToken !== undefined ? { parkedCheckpointToken: input.parkedCheckpointToken } : {}),
        ...(input.resolveParkedStop !== undefined ? { resolveParkedStop: input.resolveParkedStop } : {}),
        ...(input.sessionScoped ? { sessionScoped: true } : {}),
        ...(input.description !== undefined ? { description: input.description } : {}),
        ...(input.name !== undefined ? { name: input.name } : {}),
        ...(input.agentType !== undefined ? { agentType: input.agentType } : {}),
        ...(input.parentTaskId !== undefined ? { parentTaskId: input.parentTaskId } : {}),
        ...(input.parentSessionId !== undefined ? { parentSessionId: input.parentSessionId } : {}),
        ...(input.rootSessionId !== undefined ? { rootSessionId: input.rootSessionId } : {}),
        ...(input.deliveryChannel !== undefined ? { channelState: input.deliveryChannel } : {}),
        ...(input.retainedContinuation !== undefined ? { retainedContinuation: input.retainedContinuation } : {}),
        ...(input.owner !== undefined ? { owner: input.owner } : {}),
        ...(input.scope !== undefined ? { scope: input.scope } : {}),
        ...(input.toolUseId !== undefined ? { toolUseId: input.toolUseId } : {}),
        cycleSeq: input.durableSeed !== undefined ? (input.durableSeed.record.seq ?? 1) : 1,
        createdAt: now,
        updatedAt: now,
        abort: input.abort,
    });
    if (input.store) {
        const handle = core.handles.get(id);
        if (input.durableSeed !== undefined) {
            const seeded = structuredClone(input.durableSeed.record);
            seeded.rev = input.durableSeed.rev;
            handle.durable = {
                store: input.store,
                record: seeded,
                chain: Promise.resolve(),
                written: true,
                poisoned: false,
                flushFailed: false,
            };
            ensureDurableHeartbeatLane(core);
            return id;
        }
        handle.durable = {
            store: input.store,
            record: {
                handle: id,
                scope: input.scope,
                owner: input.owner,
                sessionScoped: input.sessionScoped === true,
                ...(input.parentSessionId !== undefined ? { parentSessionId: input.parentSessionId } : {}),
                ...(input.rootSessionId !== undefined ? { rootSessionId: input.rootSessionId } : {}),
                ...(input.parentTaskId !== undefined ? { parentTaskId: input.parentTaskId } : {}),
                ...(input.name !== undefined ? { name: input.name } : {}),
                ...(input.agentType !== undefined ? { agentType: input.agentType } : {}),
                ...(input.description !== undefined ? { description: input.description } : {}),
                ...(input.model !== undefined ? { model: input.model } : {}),
                writerId: core.writerId,
                writerEpoch: 1,
                spawnedAt: now,
                updatedAt: now,
                status: "running",
                rev: 0,
            },
            chain: Promise.resolve(),
            written: false,
            poisoned: false,
            flushFailed: false,
        };
        durableAgentWriteLane(handle, {});
        ensureDurableHeartbeatLane(core);
    }
    return id;
}
export function parkBackgroundAgentLane(core, id, park) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent")
        return undefined;
    if (handle.status !== "running")
        return undefined;
    handle.status = "parked";
    handle.parkedCheckpointToken = park.checkpointToken;
    handle.resolveParkedStop = park.resolveStop;
    handle.notify = undefined;
    for (const [, , qResolve] of handle.preAttachQueue ?? [])
        qResolve?.({ ok: false, reason: "not_running" });
    handle.preAttachQueue = undefined;
    for (const r of [...(handle.inFlightDirect ?? [])])
        r({ ok: false, reason: "not_running" });
    handle.inFlightDirect = undefined;
    handle.updatedAt = Date.now();
    if (park.seq !== undefined)
        handle.cycleSeq = park.seq;
    core.pokeBgQuiescence(handle.owner);
    durableAgentWriteLane(handle, {
        status: "parked",
        parkedCheckpointToken: park.checkpointToken,
        parkedAt: Date.now(),
        ...(park.seq !== undefined ? { seq: park.seq } : {}),
    }, ["parkClaimId"]);
    return "parked";
}
export async function reconcileParkedAgentsLane(core, stores, scope, now, opts) {
    let liveFailed = 0;
    for (const handle of core.handles.values()) {
        if (handle.type !== "background_agent" || handle.status !== "parked")
            continue;
        if (handle.scope !== undefined && handle.scope !== scope)
            continue;
        const token = handle.parkedCheckpointToken;
        let state;
        if (token === undefined)
            state = "missing";
        else {
            try {
                const cp = await stores.checkpointStore.get(token);
                state = cp === null ? "missing" : cp.status;
            }
            catch {
                state = "unreadable";
            }
        }
        let error;
        if (state === "expired" || state === "missing") {
            error = `approval checkpoint ${state} while parked (reconciled)`;
        }
        else if (state === "resolved") {
            let row;
            try {
                row = await stores.agentStore.get(handle.id, scope);
            }
            catch {
                continue;
            }
            if (row === null || row.status !== "parked" || row.parkClaimId !== undefined)
                continue;
            try {
                const reread = (await stores.checkpointStore.get(token));
                if (reread === null || reread.status !== "resolved")
                    continue;
            }
            catch {
                continue;
            }
            error =
                "approval checkpoint resolved outside the registry's claim path while parked — the decision was " +
                    "redeemed by another leg and this row's outcome is unknown (reconciled)";
        }
        else {
            continue;
        }
        if (handle.status !== "parked")
            continue;
        if (handle.parkedCheckpointToken !== token)
            continue;
        handle.status = "failed";
        handle.error = error;
        handle.parkedCheckpointToken = undefined;
        handle.resolveParkedStop = undefined;
        handle.updatedAt = now;
        mintCompletionId(handle);
        core.pokeBgQuiescence(handle.owner);
        durableAgentWriteLane(handle, { status: "failed", error: handle.error, settledAt: now, stoppedBy: "system", completionId: handle.completionId }, ["parkedCheckpointToken", "parkClaimId", "parkedAt"]);
        liveFailed++;
    }
    const { reconcileParkedAgents: storeReconcile } = await import("./background-agent-store.js");
    const liveHandles = new Set();
    for (const h of core.handles.values())
        if (h.type === "background_agent")
            liveHandles.add(h.id);
    const storeHalf = await storeReconcile(stores, scope, now, {
        ...(opts?.staleClaimMaxAgeMs !== undefined ? { staleClaimMaxAgeMs: opts.staleClaimMaxAgeMs } : {}),
        excludeHandles: liveHandles,
    });
    return { failed: storeHalf.failed + liveFailed, rolledBack: storeHalf.rolledBack };
}
export async function claimParkedAgentLane(stores, handle, access) {
    if (access.scope === undefined)
        return { ok: false, reason: "not_found" };
    let row;
    try {
        row = await stores.agentStore.get(handle, access.scope);
    }
    catch {
        return { ok: false, reason: "store_unreachable" };
    }
    if (!row || !canAccessAgentRecord(row, access))
        return { ok: false, reason: "not_found" };
    if (row.status !== "parked")
        return { ok: false, reason: "not_parked" };
    const token = row.parkedCheckpointToken;
    if (token === undefined || row.sessionId === undefined)
        return { ok: false, reason: "binding_broken" };
    let cp;
    try {
        cp = (await stores.checkpointStore.get(token));
    }
    catch {
        return { ok: false, reason: "store_unreachable" };
    }
    if (cp === null || cp.sessionId !== row.sessionId)
        return { ok: false, reason: "binding_broken" };
    if (cp.status !== "pending")
        return { ok: false, reason: "checkpoint_not_pending" };
    const claimId = randomBytes(8).toString("hex");
    const reserved = structuredClone(row);
    reserved.parkClaimId = claimId;
    reserved.updatedAt = Date.now();
    let won;
    try {
        won = await stores.agentStore.updateIf(handle, access.scope, reserved, {
            rev: row.rev,
            status: "parked",
            parkedCheckpointToken: token,
            parkClaimId: null,
        });
    }
    catch {
        return { ok: false, reason: "store_unreachable" };
    }
    if (!won)
        return { ok: false, reason: "claim_lost" };
    return {
        ok: true,
        ticket: { handle, scope: access.scope, token, claimId, reservedRev: row.rev + 1, row: structuredClone(row) },
    };
}
export async function rollbackParkedClaimLane(core, stores, ticket) {
    let cpStatus;
    try {
        const cp = (await stores.checkpointStore.get(ticket.token));
        cpStatus = cp === null ? "missing" : cp.status;
    }
    catch {
        return "retry";
    }
    const now = Date.now();
    const rollbackCompletionIdCandidate = uuidv7();
    const settleLocalParkedHandle = (error) => {
        const h = core.handles.get(ticket.handle);
        if (h !== undefined && h.type === "background_agent" && h.status === "parked") {
            h.status = "failed";
            h.error = error;
            h.parkedCheckpointToken = undefined;
            h.resolveParkedStop = undefined;
            h.updatedAt = now;
            commitCompletionIdIfEmpty(h, rollbackCompletionIdCandidate);
            core.pokeBgQuiescence(h.owner);
        }
    };
    let liveRow;
    try {
        liveRow = await stores.agentStore.get(ticket.handle, ticket.scope);
    }
    catch {
        return "retry";
    }
    if (!liveRow || liveRow.parkClaimId !== ticket.claimId)
        return "lost";
    if (cpStatus === "pending") {
        const restored = structuredClone(liveRow);
        delete restored.parkClaimId;
        restored.updatedAt = now;
        restored.writerEpoch = (liveRow.writerEpoch ?? 0) + 1;
        const liveHandle = core.handles.get(ticket.handle);
        if (liveHandle?.type === "background_agent" &&
            liveHandle.durable !== undefined &&
            liveHandle.durable.record.writerId === liveRow.writerId &&
            (liveHandle.durable.record.writerEpoch ?? 0) === (liveRow.writerEpoch ?? 0)) {
            liveHandle.durable.record.writerEpoch = restored.writerEpoch;
        }
        try {
            const won = await stores.agentStore.updateIf(ticket.handle, ticket.scope, restored, {
                rev: liveRow.rev,
                status: "parked",
                parkedCheckpointToken: ticket.token,
                parkClaimId: ticket.claimId,
            });
            if (!won)
                return "lost";
            try {
                const cp2 = (await stores.checkpointStore.get(ticket.token));
                if (cp2 !== null && cp2.status === "pending")
                    return "rolled_back";
                const post = await stores.agentStore.get(ticket.handle, ticket.scope);
                if (post && post.status === "parked") {
                    const term = structuredClone(post);
                    term.status = "failed";
                    term.error = `approval checkpoint ${cp2 === null ? "missing" : cp2.status} under the resume claim (claim rollback)`;
                    term.stoppedBy = "system";
                    term.settledAt = now;
                    term.updatedAt = now;
                    term.completionId = rollbackCompletionIdCandidate;
                    delete term.parkedCheckpointToken;
                    delete term.parkClaimId;
                    delete term.parkedAt;
                    if (await stores.agentStore.updateIf(ticket.handle, ticket.scope, term, { rev: post.rev, status: "parked" })) {
                        settleLocalParkedHandle(term.error);
                        return "failed";
                    }
                }
                return "lost";
            }
            catch {
                return "rolled_back";
            }
        }
        catch {
            return "retry";
        }
    }
    const error = cpStatus === "resolved"
        ? "approval consumed but the resume could not proceed — outcome unknown (claim rollback)"
        : `approval checkpoint ${cpStatus} under the resume claim (claim rollback)`;
    if (liveRow.status !== "parked")
        return "lost";
    const failed = structuredClone(liveRow);
    failed.status = "failed";
    failed.error = error;
    failed.stoppedBy = "system";
    failed.settledAt = now;
    failed.updatedAt = now;
    failed.completionId = rollbackCompletionIdCandidate;
    delete failed.parkedCheckpointToken;
    delete failed.parkClaimId;
    delete failed.parkedAt;
    try {
        const won = await stores.agentStore.updateIf(ticket.handle, ticket.scope, failed, {
            rev: liveRow.rev,
            status: "parked",
            parkClaimId: ticket.claimId,
        });
        if (won)
            settleLocalParkedHandle(error);
        return won ? "failed" : "lost";
    }
    catch {
        return "retry";
    }
}
export async function consumeParkedFlipLane(core, id, stores, ticket) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent" || handle.status !== "parked")
        return false;
    const lane = handle.durable;
    const doFlip = async () => {
        let live;
        try {
            live = await stores.agentStore.get(ticket.handle, ticket.scope);
        }
        catch {
            return undefined;
        }
        if (!live || live.status !== "parked" || live.parkClaimId !== ticket.claimId || live.parkedCheckpointToken !== ticket.token)
            return undefined;
        const flipped = structuredClone(live);
        flipped.status = "running";
        flipped.writerId = core.writerId;
        flipped.writerEpoch = (live.writerEpoch ?? 0) + 1;
        flipped.updatedAt = Date.now();
        flipped.seq = (live.seq ?? 1) + 1;
        delete flipped.parkedCheckpointToken;
        delete flipped.parkedAt;
        try {
            const won = await stores.agentStore.updateIf(ticket.handle, ticket.scope, flipped, {
                rev: live.rev,
                status: "parked",
                parkedCheckpointToken: ticket.token,
                parkClaimId: ticket.claimId,
            });
            if (!won)
                return undefined;
        }
        catch {
            return undefined;
        }
        flipped.rev = live.rev + 1;
        return flipped;
    };
    let flipped;
    if (lane !== undefined && !lane.poisoned) {
        const p = lane.chain.then(doFlip, doFlip);
        lane.chain = p.then((f) => {
            if (f !== undefined) {
                lane.record = structuredClone(f);
                lane.written = true;
            }
        });
        flipped = await p;
    }
    else {
        flipped = await doFlip();
    }
    if (flipped === undefined)
        return false;
    handle.status = "running";
    handle.parkedCheckpointToken = undefined;
    handle.resolveParkedStop = undefined;
    handle.updatedAt = Date.now();
    handle.completionId = undefined;
    handle.terminalNotified = undefined;
    if (flipped.seq !== undefined)
        handle.cycleSeq = flipped.seq;
    return true;
}
export function finalizeParkedResumeLane(core, id) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent")
        return;
    durableAgentWriteLane(handle, {}, ["parkClaimId"]);
}
export function settleBackgroundAgentLane(core, id, outcome) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent")
        return undefined;
    if (outcome.cycle === undefined && (handle.reviveCycle ?? 0) !== 0) {
        throw new Error(`settleBackgroundAgent("${id}"): the row has been revived (cycle ${handle.reviveCycle}) — pass the cycle this settle speaks for, or use settleRevivedAgent`);
    }
    if ((outcome.cycle ?? 0) !== (handle.reviveCycle ?? 0))
        return undefined;
    if (handle.status !== "running") {
        if (handle.status === "killed") {
            mintCompletionId(handle);
            let backfilled = false;
            if (handle.result === undefined && outcome.result !== undefined && outcome.result !== "") {
                handle.result = outcome.result;
                handle.resultIsPartial = true;
                backfilled = true;
                if (outcome.resultFull !== undefined)
                    handle.resultFull = outcome.resultFull;
            }
            if (handle.error === undefined && outcome.error !== undefined && outcome.error !== BG_AGENT_REAP_STOP_ERROR) {
                handle.error = outcome.error;
                backfilled = true;
            }
            if (backfilled) {
                handle.updatedAt = Date.now();
                durableAgentWriteLane(handle, {
                    ...(handle.result !== undefined ? { finalOutput: handle.result } : {}),
                    ...(handle.resultFull !== undefined ? { finalOutputFull: handle.resultFull } : {}),
                    ...(handle.resultIsPartial ? { resultIsPartial: true } : {}),
                    ...(handle.error !== undefined ? { error: handle.error } : {}),
                    ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
                });
            }
        }
        return handle.status === "completed" || handle.status === "failed" || handle.status === "killed" ? handle.status : undefined;
    }
    handle.status = outcome.status;
    mintCompletionId(handle);
    handle.notify = undefined;
    handle.onReapTerminal = undefined;
    for (const [, , qResolve] of handle.preAttachQueue ?? [])
        qResolve?.({ ok: false, reason: "not_running" });
    handle.preAttachQueue = undefined;
    for (const r of [...(handle.inFlightDirect ?? [])])
        r({ ok: false, reason: "not_running" });
    handle.inFlightDirect = undefined;
    if (outcome.status === "killed" && handle.stoppedBy === undefined) {
        handle.stoppedBy = outcome.stoppedBy ?? handle.stopSource ?? "system";
    }
    if (outcome.result !== undefined)
        handle.result = outcome.result;
    if (outcome.resultFull !== undefined)
        handle.resultFull = outcome.resultFull;
    if (outcome.status === "killed" && outcome.result !== undefined && outcome.result !== "") {
        handle.resultIsPartial = true;
    }
    if (outcome.error !== undefined && !(outcome.status === "killed" && outcome.error === BG_AGENT_REAP_STOP_ERROR)) {
        handle.error = outcome.error;
        if (outcome.errorCode !== undefined)
            handle.errorCode = outcome.errorCode;
        if (outcome.retryable !== undefined)
            handle.errorRetryable = outcome.retryable;
        if (outcome.errorKind !== undefined)
            handle.errorKind = outcome.errorKind;
        if (outcome.retryAfterMs !== undefined)
            handle.errorRetryAfterMs = outcome.retryAfterMs;
    }
    handle.updatedAt = Date.now();
    if (outcome.seq !== undefined)
        handle.cycleSeq = outcome.seq;
    core.pokeBgQuiescence(handle.owner);
    durableAgentWriteLane(handle, {
        status: outcome.status,
        ...(outcome.seq !== undefined ? { seq: outcome.seq } : {}),
        settledAt: Date.now(),
        ...(handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
        ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
        ...(handle.result !== undefined ? { finalOutput: handle.result } : {}),
        ...(handle.resultFull !== undefined ? { finalOutputFull: handle.resultFull } : {}),
        ...(handle.resultIsPartial ? { resultIsPartial: true } : {}),
        ...(handle.error !== undefined ? { error: handle.error } : {}),
        ...(handle.errorCode !== undefined ? { errorCode: handle.errorCode } : {}),
        ...(handle.errorRetryable !== undefined ? { errorRetryable: handle.errorRetryable } : {}),
        ...(handle.errorKind !== undefined ? { errorKind: handle.errorKind } : {}),
        ...(handle.errorRetryAfterMs !== undefined ? { errorRetryAfterMs: handle.errorRetryAfterMs } : {}),
    }, ["parkedCheckpointToken", "parkClaimId", "parkedAt"]);
    return outcome.status;
}
export function abortBackgroundAgentsForOwnerLane(core, access, opts) {
    let aborted = 0;
    for (const handle of core.handles.values()) {
        if (handle.type !== "background_agent" || handle.status !== "running" || !canAccess(handle, access))
            continue;
        if (opts?.skipSessionScoped && handle.sessionScoped)
            continue;
        if (opts?.sessionScopedOnly && !handle.sessionScoped)
            continue;
        core.markStopSource(handle.id, "parent");
        handle.abort.abort();
        aborted++;
    }
    return aborted;
}
export function resolveBackgroundAgentByNameLane(core, name, access, opts) {
    const key = normalizeAgentName(name);
    if (key === "")
        return { status: "not_found" };
    const latestOf = (pool) => {
        let best;
        for (const h of pool)
            if (best === undefined || h.createdAt >= best.createdAt)
                best = h;
        return best;
    };
    const named = [];
    for (const h of core.handles.values()) {
        if (h.type !== "background_agent" || h.name === undefined || !canAccess(h, access))
            continue;
        if (h.name === name || normalizeAgentName(h.name) === key)
            named.push(h);
    }
    if (opts?.preferRunning === true) {
        const namedRunning = named.filter((h) => h.status === "running");
        if (namedRunning.length > 1) {
            return {
                status: "ambiguous",
                message: `Multiple RUNNING background agents match "${name}": ${namedRunning.map((h) => `${h.id} (${h.name ?? h.description ?? "unnamed"})`).join(", ")}. Use the task ID.`,
            };
        }
        if (namedRunning.length === 1)
            return { status: "found", handle: namedRunning[0] };
    }
    const namedWinner = latestOf(named);
    if (namedWinner !== undefined)
        return { status: "found", handle: namedWinner };
    const exact = [];
    for (const h of core.handles.values()) {
        if (h.type !== "background_agent" || !canAccess(h, access))
            continue;
        if (h.description === name)
            exact.push(h);
    }
    const exactRunning = exact.filter((h) => h.status === "running");
    const exactPool = exactRunning.length > 0 ? exactRunning : exact;
    if (exactPool.length === 1)
        return { status: "found", handle: exactPool[0] };
    if (exactPool.length > 1) {
        return {
            status: "ambiguous",
            message: `Multiple background agents match "${name}": ${exactPool.map((h) => (h.description ? `${h.id} (${h.description})` : h.id)).join(", ")}. Use the task ID.`,
        };
    }
    const matches = [];
    for (const h of core.handles.values()) {
        if (h.type !== "background_agent" || !canAccess(h, access))
            continue;
        if (h.description !== undefined && normalizeAgentName(h.description) === key)
            matches.push(h);
    }
    const running = matches.filter((h) => h.status === "running");
    const pool = running.length > 0 ? running : matches;
    if (pool.length === 1)
        return { status: "found", handle: pool[0] };
    if (pool.length > 1) {
        return {
            status: "ambiguous",
            message: `Multiple background agents match "${name}": ${pool.map((h) => (h.description ? `${h.id} (${h.description})` : h.id)).join(", ")}. Use the task ID.`,
        };
    }
    const byKey = new Map();
    for (const h of core.handles.values()) {
        if (h.type !== "background_agent" || h.status !== "running" || !canAccess(h, access))
            continue;
        for (const label of [h.name, h.description]) {
            if (label === undefined)
                continue;
            const k = normalizeAgentName(label);
            if (k !== "" && !byKey.has(k))
                byKey.set(k, label);
        }
    }
    const closest = closestName(key, byKey.keys());
    return { status: "not_found", ...(closest !== undefined ? { suggestion: byKey.get(closest) } : {}) };
}
export function markRetainedContinuationLane(core, id) {
    const handle = core.handles.get(id);
    if (handle && handle.type === "background_agent")
        handle.retainedContinuation = true;
}
export function backgroundAgentCycleSeqLane(core, id) {
    const handle = core.handles.get(id);
    if (handle === undefined || handle.type !== "background_agent")
        return undefined;
    return handle.status === "parked" ? (handle.cycleSeq ?? 1) + 1 : handle.cycleSeq;
}
async function claimTerminalRowForRevive(core, store, handle, scope) {
    for (let attempt = 0; attempt < 3; attempt++) {
        let live;
        try {
            live = await store.get(handle, scope);
        }
        catch {
            return { status: "still_running" };
        }
        if (live === null)
            return { status: "not_found" };
        if (live.status === "running" || live.status === "parked")
            return { status: "still_running" };
        const claimed = structuredClone(live);
        claimed.status = "running";
        clearRevivedRowTerminalPayload(claimed);
        claimed.writerId = core.writerId;
        claimed.writerEpoch = (live.writerEpoch ?? 0) + 1;
        claimed.updatedAt = Date.now();
        let won = false;
        try {
            won = await store.updateIf(handle, scope, claimed, { rev: live.rev, status: live.status });
        }
        catch {
            let after;
            try {
                after = await store.get(handle, scope);
            }
            catch {
                return { status: "still_running" };
            }
            if (after !== null && after.status === "running" && after.writerId === core.writerId && after.writerEpoch === claimed.writerEpoch) {
                return { status: "claimed", row: after, previous: live };
            }
            return { status: "still_running" };
        }
        if (!won)
            continue;
        claimed.rev = live.rev + 1;
        return { status: "claimed", row: claimed, previous: live };
    }
    return { status: "still_running" };
}
async function rollbackRevivalClaim(store, claim) {
    const restored = structuredClone(claim.previous);
    restored.writerEpoch = (claim.row.writerEpoch ?? 0) + 1;
    restored.updatedAt = Date.now();
    try {
        await store.updateIf(restored.handle, restored.scope, restored, { rev: claim.row.rev, status: "running" });
    }
    catch {
    }
}
export async function reviveBackgroundAgentLane(core, id, access, abort) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent" || !canAccess(handle, access))
        return { ok: false, reason: "not_found" };
    if (core.reapedHandles.has(id))
        return { ok: false, reason: "not_found" };
    if (core.reapingHandles.has(id) || core.claimingHandles.has(id))
        return { ok: false, reason: "recycling" };
    if (handle.status === "running" || handle.status === "pending")
        return { ok: false, reason: "still_running" };
    if (handle.status === "parked")
        return { ok: false, reason: "still_running" };
    const lane = handle.durable;
    let plainDurableWrite = lane === undefined;
    if (lane !== undefined) {
        core.claimingHandles.add(id);
        let claim;
        try {
            const run = () => claimTerminalRowForRevive(core, lane.store, lane.record.handle, lane.record.scope);
            const adopt = (row) => {
                lane.poisoned = true;
                handle.durable = { store: lane.store, record: structuredClone(row), chain: Promise.resolve(), written: true, poisoned: false, flushFailed: false };
            };
            if (lane.poisoned) {
                claim = await run();
                if (claim.status === "claimed")
                    adopt(claim.row);
            }
            else {
                const p = lane.chain.then(run, run);
                lane.chain = p.then((r) => {
                    if (r.status === "claimed")
                        adopt(r.row);
                });
                claim = await p;
            }
        }
        finally {
            core.claimingHandles.delete(id);
        }
        if (claim.status !== "claimed") {
            if (claim.status === "not_found" && !lane.written && !lane.poisoned)
                plainDurableWrite = true;
            else
                return { ok: false, reason: claim.status };
        }
        if (core.handles.get(id) !== handle) {
            if (claim.status === "claimed")
                await rollbackRevivalClaim(lane.store, claim);
            return { ok: false, reason: "still_running" };
        }
        ensureDurableHeartbeatLane(core);
        if (claim.status === "claimed" && typeof claim.row.seq === "number" && (handle.cycleSeq ?? 1) < claim.row.seq) {
            handle.cycleSeq = claim.row.seq;
        }
    }
    handle.status = "running";
    handle.channelState = "attaching";
    handle.notify = undefined;
    if (abort !== undefined)
        handle.abort = abort;
    handle.result = undefined;
    handle.resultFull = undefined;
    handle.spillRef = undefined;
    handle.spillFailed = undefined;
    handle.error = undefined;
    handle.errorCode = undefined;
    handle.errorRetryable = undefined;
    handle.errorKind = undefined;
    handle.errorRetryAfterMs = undefined;
    handle.resultIsPartial = undefined;
    handle.stopSource = undefined;
    handle.stoppedBy = undefined;
    handle.completionId = undefined;
    handle.terminalNotified = undefined;
    handle.reviveCycle = (handle.reviveCycle ?? 0) + 1;
    handle.cycleSeq = (handle.cycleSeq ?? 1) + 1;
    handle.updatedAt = Date.now();
    if (plainDurableWrite)
        durableAgentWriteLane(handle, { status: "running" }, REVIVED_ROW_CLEARED_FIELDS);
    return { ok: true, cycle: handle.reviveCycle };
}
export function settleRevivedAgentLane(core, id, cycle, outcome) {
    return settleBackgroundAgentLane(core, id, { ...outcome, cycle });
}
export function noteBackgroundAgentActivityLane(core, id, now = Date.now()) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent" || handle.status !== "running")
        return;
    handle.updatedAt = now;
}
export function reapStaleSessionBackgroundAgentsLane(core, staleMs, now = Date.now(), onTerminal) {
    if (!Number.isFinite(staleMs) || staleMs <= 0) {
        throw new Error(`reapStaleSessionBackgroundAgents: staleMs must be a positive finite number of milliseconds (got ${String(staleMs)})`);
    }
    let reaped = 0;
    for (const handle of core.handles.values()) {
        if (handle.type !== "background_agent" || handle.status !== "running" || handle.sessionScoped !== true)
            continue;
        if (now - handle.updatedAt <= staleMs)
            continue;
        core.markStopSource(handle.id, "system");
        const reapTerminalNote = handle.onReapTerminal;
        handle.abort.abort();
        settleBackgroundAgentLane(core, handle.id, {
            status: "killed",
            error: STALE_RUNNING_REAP_ATTRIBUTION,
            cycle: handle.reviveCycle ?? 0,
        });
        if (reapTerminalNote !== undefined && handle.terminalNotified !== true) {
            handle.terminalNotified = true;
            onTerminal?.(reapTerminalNote);
        }
        reaped++;
    }
    return reaped;
}
export function unmarkRetainedContinuationLane(core, id) {
    const handle = core.handles.get(id);
    if (handle && handle.type === "background_agent")
        handle.retainedContinuation = false;
}
export function attachAgentNotifyLane(core, id, notify, cycle) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent" || handle.status !== "running")
        return;
    if (cycle !== undefined && (handle.reviveCycle ?? 0) !== cycle)
        return;
    handle.notify = notify;
    handle.channelState = "attaching";
    const live = handle.preAttachQueue;
    if (live !== undefined && live.length > 0) {
        void (async () => {
            while (handle.preAttachQueue === live && live.length > 0) {
                const [qPayload, qOpts, qResolve] = live[0];
                let result;
                try {
                    const d = await notify(qPayload, qOpts);
                    result =
                        d === "parked" && handle.retainedContinuation !== true
                            ? { ok: false, reason: "not_running" }
                            : d === "parked"
                                ? { ok: true, disposition: "parked" }
                                : { ok: true, disposition: "queued" };
                }
                catch {
                    result = { ok: false, reason: "not_running" };
                }
                if (handle.preAttachQueue !== live)
                    return;
                live.shift();
                qResolve?.(result);
            }
            if (handle.preAttachQueue === live) {
                handle.preAttachQueue = undefined;
                handle.channelState = "attached";
            }
        })();
    }
    else {
        handle.preAttachQueue = undefined;
        handle.channelState = "attached";
    }
}
export async function deliverToRunningAgentLane(core, id, access, notification, opts) {
    const handle = core.handles.get(id);
    if (!handle || handle.type !== "background_agent" || !canAccess(handle, access))
        return { ok: false, reason: "not_found" };
    if (handle.status !== "running")
        return { ok: false, reason: "not_running" };
    const bounded = (p, fallback, onTimeout) => new Promise((resolve) => {
        const t = setTimeout(() => {
            try {
                onTimeout?.();
            }
            catch {
            }
            resolve(fallback);
        }, core.deliverConfirmTimeoutMs);
        t.unref?.();
        void p.then((v) => {
            clearTimeout(t);
            resolve(v);
        }, () => {
            clearTimeout(t);
            resolve(fallback);
        });
    });
    if (handle.notify === undefined || handle.channelState === "attaching") {
        if (handle.channelState !== "attaching")
            return { ok: false, reason: "no_channel" };
        const q = (handle.preAttachQueue ??= []);
        if (q.length >= 8)
            return { ok: false, reason: "queue_full" };
        return bounded(new Promise((resolve) => {
            q.push([notification, opts, resolve]);
        }), { ok: true, disposition: "pending" });
    }
    let detachOnTimeout;
    return await bounded(new Promise((resolve) => {
        const inflight = (handle.inFlightDirect ??= new Set());
        const settleOnce = (r) => {
            if (!inflight.has(settleOnce))
                return;
            inflight.delete(settleOnce);
            resolve(r);
        };
        inflight.add(settleOnce);
        detachOnTimeout = () => inflight.delete(settleOnce);
        void (async () => {
            let result;
            try {
                const d = await handle.notify?.(notification, opts);
                if (d === "parked" || d === "parked_durably") {
                    result = handle.retainedContinuation === true ? { ok: true, disposition: "parked" } : { ok: false, reason: "not_running" };
                }
                else {
                    result = { ok: true, disposition: "queued" };
                }
            }
            catch {
                result = { ok: false, reason: "not_running" };
            }
            settleOnce(result);
        })();
    }), { ok: true, disposition: "pending" }, () => detachOnTimeout?.());
}
export function runningBackgroundAgentLabelsLane(core, access) {
    const out = [];
    for (const h of core.handles.values()) {
        if (h.type !== "background_agent" || h.status !== "running" || !canAccess(h, access))
            continue;
        const label = h.name ?? h.description;
        out.push(label ? `${h.id} (${label})` : h.id);
    }
    return out;
}
export function runningAgentFooterLane(core, access) {
    const named = [];
    const background = [];
    for (const h of core.handles.values()) {
        if (h.type !== "background_agent" || h.status !== "running" || !canAccess(h, access))
            continue;
        if (h.name !== undefined && h.name !== "") {
            named.push(h.name);
            continue;
        }
        background.push(h.description ? `${h.id} (${h.description})` : h.id);
    }
    return { named, background };
}
export function notFoundRunningAgentsTail(footer) {
    return ((footer.named.length > 0 ? `. Running named agents: ${footer.named.join(", ")}` : "") +
        (footer.background.length > 0 ? `. Running background agents: ${footer.background.join(", ")}` : ""));
}
export function buildAgentPollDetails(input) {
    const failed = input.status === "failed";
    return {
        task_id: input.taskId,
        type: "background_agent",
        status: input.status,
        retrieval_status: input.retrievalStatus,
        ...(input.seq !== undefined ? { seq: input.seq } : {}),
        ...(input.stoppedBy !== undefined ? { stoppedBy: input.stoppedBy } : {}),
        ...(failed && input.error !== undefined ? { error: delimitUntrusted("agent error", boundedRedactedSummary(input.error, 300)) } : {}),
        ...(failed && input.errorCode !== undefined ? { errorCode: input.errorCode } : {}),
        ...(failed && input.errorRetryable !== undefined ? { retryable: input.errorRetryable } : {}),
        ...(failed && input.errorRetryAfterMs !== undefined ? { retryAfterMs: input.errorRetryAfterMs } : {}),
        ...(input.resultIsPartial === true ? { partial_result: true } : {}),
        ...(input.completionId !== undefined ? { completionId: input.completionId } : {}),
        ...(input.agentState !== undefined ? { agentState: input.agentState } : {}),
    };
}
export function serveDurableAgentRowLane(row, agentState) {
    const stateLine = agentState !== undefined ? `state: ${agentStateSummary(agentState)}\n` : "";
    if (row.status === "parked") {
        return {
            content: delimitUntrusted(`TaskOutput ${row.handle}`, `status: parked
${stateLine}The agent is durably suspended, waiting for an approval decision. It resumes when the pending approval is decided (durable approval inbox), or lands failed if the approval expires.`),
            details: buildAgentPollDetails({
                taskId: row.handle,
                status: "parked",
                retrievalStatus: "success",
                ...(row.seq !== undefined ? { seq: row.seq } : {}),
                ...(agentState !== undefined ? { agentState } : {}),
            }),
        };
    }
    const kindClause = row.status === "failed" && row.errorKind !== undefined && row.errorRetryable !== undefined
        ? ` (error_kind: ${row.errorKind}, retryable: ${row.errorRetryable}${row.errorRetryAfterMs !== undefined ? `, retry_after_ms: ${row.errorRetryAfterMs}` : ""})`
        : "";
    const body = `status: ${row.status}
${stateLine}${row.error ? `error: ${row.error}${kindClause}
` : ""}${row.finalOutput ? `--- result${row.resultIsPartial ? " (partial — produced before the task was stopped)" : ""} ---
${clipTaskOutput(row.finalOutputFull ?? row.finalOutput)}` : "(no result text)"}`;
    return {
        content: delimitUntrusted(`TaskOutput ${row.handle}`, body),
        details: buildAgentPollDetails({
            taskId: row.handle,
            status: row.status,
            retrievalStatus: "success",
            ...(row.seq !== undefined ? { seq: row.seq } : {}),
            ...(row.stoppedBy !== undefined ? { stoppedBy: row.stoppedBy } : {}),
            ...(row.error !== undefined ? { error: row.error } : {}),
            ...(row.errorCode !== undefined ? { errorCode: row.errorCode } : {}),
            ...(row.errorRetryable !== undefined ? { errorRetryable: row.errorRetryable } : {}),
            ...(row.errorRetryAfterMs !== undefined ? { errorRetryAfterMs: row.errorRetryAfterMs } : {}),
            ...(row.resultIsPartial === true ? { resultIsPartial: true } : {}),
            ...(row.completionId !== undefined ? { completionId: row.completionId } : {}),
            ...(agentState !== undefined ? { agentState } : {}),
        }),
        ...(row.status === "failed" ? { isError: true } : {}),
    };
}
const AGENT_SPILL_FAILED_NOTE = "\n\n[full output could not be spilled to the offload store (the write failed or went unconfirmed) — no ref is available to read the dropped middle back.]";
export async function spillClippedAgentResult(handle, full, clipped, store, sessionId) {
    if (clipped === full)
        return clipped;
    if (store === undefined)
        return clipped;
    const disclose = (ref) => `${clipped}\n\n[full output persisted — call ${OFFLOAD_TOOL_NAME} with ref "${ref}" to read it back.]`;
    if (handle.spillRef !== undefined)
        return disclose(handle.spillRef);
    if (handle.spillFailed === true)
        return `${clipped}${AGENT_SPILL_FAILED_NOTE}`;
    const mintCycle = handle.reviveCycle ?? 0;
    const ref = buildToolResultRef(sessionId ?? "no-session", handle.id, `c${mintCycle}`);
    try {
        await store.put(ref, full, sessionId === undefined ? undefined : toolResultProvenanceOf(sessionId));
    }
    catch {
        if ((handle.reviveCycle ?? 0) === mintCycle) {
            if (handle.spillRef !== undefined)
                return disclose(handle.spillRef);
            handle.spillFailed = true;
        }
        return `${clipped}${AGENT_SPILL_FAILED_NOTE}`;
    }
    if ((handle.reviveCycle ?? 0) === mintCycle)
        handle.spillRef = ref;
    return disclose(ref);
}
export async function pollBackgroundAgentLane(handle, deadline, signal, oneShot, store, sessionId, checkpointSeat) {
    while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
        await sleepPollStep(deadline, signal);
    }
    const running = handle.status === "running";
    const retrieval = running && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success";
    if (handle.status === "parked") {
        const parkedSource = agentStateSourceOfHandle(handle);
        const parkedSeq = handle.cycleSeq;
        const agentState = await readAgentState(parkedSource, checkpointSeat, Date.now());
        return {
            content: delimitUntrusted(`TaskOutput ${handle.id}`, `status: parked
state: ${agentStateSummary(agentState)}
The agent is durably suspended, waiting for an approval decision. It resumes when the pending approval is decided (durable approval inbox), or lands failed if the approval expires.`),
            details: buildAgentPollDetails({
                taskId: handle.id,
                status: "parked",
                retrievalStatus: "success",
                ...(parkedSeq !== undefined ? { seq: parkedSeq } : {}),
                agentState,
            }),
        };
    }
    const snap = {
        status: handle.status,
        result: handle.result,
        resultIsPartial: handle.resultIsPartial,
        error: handle.error,
        errorKind: handle.errorKind,
        errorCode: handle.errorCode,
        errorRetryable: handle.errorRetryable,
        errorRetryAfterMs: handle.errorRetryAfterMs,
        cycleSeq: handle.cycleSeq,
        stoppedBy: handle.stoppedBy,
        completionId: handle.completionId,
        updatedAt: handle.updatedAt,
    };
    const stateRead = readAgentState(agentStateSourceOfHandle({ status: snap.status, updatedAt: snap.updatedAt }), checkpointSeat, Date.now());
    const agentState = stateRead instanceof Promise ? await stateRead : stateRead;
    const fullResult = snap.result ? (handle.resultFull ?? snap.result) : undefined;
    const resultText = fullResult !== undefined ? await spillClippedAgentResult(handle, fullResult, clipTaskOutput(fullResult, handle.outputFile), store, sessionId) : undefined;
    const kindClause = snap.status === "failed" && snap.errorKind !== undefined && snap.errorRetryable !== undefined
        ? ` (error_kind: ${snap.errorKind}, retryable: ${snap.errorRetryable}${snap.errorRetryAfterMs !== undefined ? `, retry_after_ms: ${snap.errorRetryAfterMs}` : ""})`
        : "";
    const stateLine = `state: ${agentStateSummary(agentState)}\n`;
    const body = running
        ? oneShot === true
            ? `status: running
${stateLine}This is a ONE-SHOT submission — there is no later turn for a background notification to land in, so do NOT end your turn expecting one. Actively wait instead: TaskOutput({ task_id: "${handle.id}", block: true }). If it is still running after the wait, wait again (bounded) rather than ending the turn, or write out your best available answer now if you are near your own time budget.`
            : `status: running
${stateLine}The agent is still working — you will be notified when it completes.`
        : `status: ${snap.status}
${stateLine}${snap.error ? `error: ${snap.error}${kindClause}
` : ""}${snap.result ? `--- result${snap.resultIsPartial ? " (partial — produced before the task was stopped)" : ""} ---
${resultText}` : "(no result text)"}`;
    return {
        content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
        details: buildAgentPollDetails({
            taskId: handle.id,
            status: snap.status,
            retrievalStatus: retrieval,
            ...(snap.cycleSeq !== undefined ? { seq: snap.cycleSeq } : {}),
            ...(snap.stoppedBy !== undefined ? { stoppedBy: snap.stoppedBy } : {}),
            ...(snap.error !== undefined ? { error: snap.error } : {}),
            ...(snap.errorCode !== undefined ? { errorCode: snap.errorCode } : {}),
            ...(snap.errorRetryable !== undefined ? { errorRetryable: snap.errorRetryable } : {}),
            ...(snap.errorRetryAfterMs !== undefined ? { errorRetryAfterMs: snap.errorRetryAfterMs } : {}),
            ...(snap.resultIsPartial === true ? { resultIsPartial: true } : {}),
            ...(snap.completionId !== undefined ? { completionId: snap.completionId } : {}),
            agentState,
        }),
        ...(snap.status === "failed" ? { isError: true } : {}),
    };
}
export async function stopBackgroundAgentLane(core, handle) {
    const entryStatus = handle.status;
    if (handle.status === "parked") {
        const arbiter = handle.resolveParkedStop;
        if (arbiter === undefined) {
            return {
                content: `${handle.id} is parked on a pending approval — stop it by deciding (deny/expire) the pending approval in the durable approval inbox.`,
                details: { task_id: handle.id, type: "background_agent", status: "parked", retrieval_status: "not_ready", error: "parked_pending_approval", code: "parked_pending_approval" },
            };
        }
        let stopWon = false;
        let arbiterUnreachable = false;
        try {
            stopWon = await arbiter();
        }
        catch {
            stopWon = false;
            arbiterUnreachable = true;
        }
        if (stopWon && handle.status === "parked") {
            core.markStopSource(handle.id, "parent");
            handle.status = "killed";
            handle.stoppedBy = handle.stopSource ?? "parent";
            handle.updatedAt = Date.now();
            mintCompletionId(handle);
            handle.parkedCheckpointToken = undefined;
            handle.resolveParkedStop = undefined;
            core.pokeBgQuiescence(handle.owner);
            {
                const lane = handle.durable;
                if (lane !== undefined) {
                    try {
                        const live = await lane.store.get(handle.id, lane.record.scope);
                        if (live && (live.status === "parked" || live.status === "running")) {
                            const term = structuredClone(live);
                            term.status = "killed";
                            if (handle.stoppedBy !== undefined)
                                term.stoppedBy = handle.stoppedBy;
                            term.settledAt = Date.now();
                            term.updatedAt = Date.now();
                            delete term.parkedCheckpointToken;
                            delete term.parkClaimId;
                            delete term.parkedAt;
                            await lane.store.update(handle.id, lane.record.scope, term, { rev: live.rev });
                        }
                    }
                    catch {
                    }
                }
            }
            durableAgentWriteLane(handle, {
                status: "killed",
                settledAt: Date.now(),
                ...(handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
                ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
            }, ["parkedCheckpointToken", "parkClaimId"]);
            return {
                content: `Terminated ${handle.id} (pending approval expired).`,
                details: {
                    task_id: handle.id,
                    type: "background_agent",
                    status: "killed",
                    retrieval_status: "success",
                    ...(handle.cycleSeq !== undefined ? { seq: handle.cycleSeq } : {}),
                    ...(handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
                    ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
                },
            };
        }
        if (arbiterUnreachable) {
            return {
                content: `${handle.id} was not stopped: the approval arbitration store was unreachable — the row stays parked; retry the stop.`,
                details: { task_id: handle.id, type: "background_agent", status: handle.status, retrieval_status: "not_ready", error: "park_arbiter_unreachable", code: "park_arbiter_unreachable" },
                isError: true,
            };
        }
        return {
            content: `${handle.id} was not stopped: its pending approval was already consumed (a resume won the arbitration).`,
            details: { task_id: handle.id, type: "background_agent", status: handle.status, retrieval_status: "not_ready", error: "park_resume_won", code: "park_resume_won" },
            isError: true,
        };
    }
    if (handle.status === "running") {
        core.markStopSource(handle.id, "parent");
        handle.abort.abort();
        handle.status = "killed";
        handle.stoppedBy = handle.stopSource ?? "parent";
        handle.updatedAt = Date.now();
        mintCompletionId(handle);
        handle.notify = undefined;
        for (const [, , qResolve] of handle.preAttachQueue ?? [])
            qResolve?.({ ok: false, reason: "not_running" });
        handle.preAttachQueue = undefined;
        for (const r of [...(handle.inFlightDirect ?? [])])
            r({ ok: false, reason: "not_running" });
        handle.inFlightDirect = undefined;
        core.pokeBgQuiescence(handle.owner);
        durableAgentWriteLane(handle, {
            status: "killed",
            settledAt: Date.now(),
            ...(handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
            ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
        });
    }
    return {
        content: entryStatus === "running" ? `Terminated ${handle.id}.` : alreadyTerminalStopNote(handle.id, handle.status),
        details: {
            task_id: handle.id,
            type: "background_agent",
            status: handle.status,
            retrieval_status: "success",
            ...(handle.cycleSeq !== undefined ? { seq: handle.cycleSeq } : {}),
            ...(handle.status === "killed" && handle.stoppedBy !== undefined ? { stoppedBy: handle.stoppedBy } : {}),
            ...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
        },
    };
}