/** design/135 G2: `"event"` is the ONE non-terminal member — a Monitor batch notification (new stdout * lines from a still-running watch). Everything else is a terminal completion notification. */ export type TaskNotificationStatus = "completed" | "failed" | "killed" | "cancelled" | "event"; export type SystemInjectionPriority = "now" | "next" | "later"; export interface TaskNotificationPayload { task_id: string; task_type: "background_bash" | "workflow" | "background_agent" | "monitor"; toolUseId?: string; status: TaskNotificationStatus; /** design/135 G2 (Monitor): the batched stdout lines of ONE `status:"event"` notification (lines that * arrived within the same batch window). `result` carries the same text joined/clipped for the model; * this structured mirror is for SDK/UI consumers. */ lines?: string[]; /** design/135 G2 (Monitor): per-task monotonically increasing batch counter. Load-bearing for dedup — * {@link taskNotificationDedupKey} folds it in, so successive event batches (same task_id+status) * don't collapse into one queue entry. Terminal notifications omit it (one per status, as before). */ seq?: number; /** design/134 §3.3: only on `status:"killed"` — who stopped it. MIRROR of task-registry `StopSource` * (the single source; inlined here to avoid an import cycle — task-registry imports this module; * third mirror: types.ts `BackgroundChildEvent.stoppedBy` — keep all three in sync). OPEN ENUM: * consumers must tolerate unknown values (a future "timeout" etc. is not breaking) and fall back * to default copy. */ stoppedBy?: "user" | "parent" | "system" | (string & {}); summary: string; result?: string; output_file?: string; usage?: unknown; /** 黑板 [428]b — the child run's SESSION id, when known. A shell keys its fleet/footer rows by its own * local ids; the a* task_id alone can be unmappable on its side — sessionId is the stable cross-system * anchor (the same handle TaskResult.sessionId / fleet rows carry). Optional: bash lane has none. */ sessionId?: string; /** 黑板 [558] A — the child's last ≤10 completed tool steps, so the parent can write a precise resume * prompt for a killed/stopped child without reading the transcript. Rendered as a compact block. * Background-agent lane only; bounded upstream. */ recentSteps?: import("../agents/subagent-steps.js").SubagentStep[]; /** 黑板 [558] D (core half) — files the child mutated with edit counts. Background-agent lane only. */ editedFiles?: import("../agents/subagent-steps.js").SubagentEditedFile[]; /** 黑板 [558] E — `true` when the child can be revived via SendMessage (retained + not user-stopped). * Lets the parent decide continue-vs-restart without trial-and-error. Background-agent lane only. */ resumable?: boolean; } export interface SystemInjection { kind: "task_notification"; /** design/116 §7 — priority is ENFORCED at the runner's subscribe listener (runtask.ts): now/next → * harness.steer() (next turn boundary, mid-work), later → harness.followUp() (only when the agent * would otherwise stop). CC's Sleep-gated later-drain is N/A (Sleep retired, design/136 §2.2). A * delivery that races the agent going idle parks on PendingSessionNotifications for the session's * next run ([492]②/[496]④). `drain()` serves that parked lane. */ priority: SystemInjectionPriority; dedupKey: string; payload: TPayload; } export declare function taskNotificationDedupKey(n: Pick): string; export declare function renderTaskNotificationXml(n: TaskNotificationPayload): string; /** * 飞轮 [492]② / 黑板 [494]② — the BETWEEN-TURNS pending lane. A task notification born while NO turn is * live (the run's notification lane is torn down, or the harness rejected the injection because the agent * was already idle) used to be silently unreachable: the Monitor main use-case (long-watch events between * turns) and the [496]④ stoppedBy notifications (a killed-evicted child whose registry row is already gone) * both land exactly in that window. This store parks them PER SESSION; the session's next run drains it at * start and rides the payloads down the SAME notification lane (steer at the next turn boundary), so * in-turn and between-turn events keep one shape. * * Bounds (1.254 F4 家族纪律): per-task cap + per-session cap + session-count cap, oldest dropped first — * preferring NON-terminal victims (an exit/killed terminal is the single most load-bearing notification of * a watch and dies only when nothing else can) — and every drop is counted per task and DISCLOSED on * delivery ({@link discloseDroppedPending}), never silent. * * NOT checkpointed (durable-resume ruling, recorded): the registry itself (`defaultTaskRegistry`) is * process-local — a cross-process durable resume cannot reconnect a monitor's watcher/process anyway, so a * pending queue that outlives the process would advertise events for handles that no longer exist. Same- * process resume legs share this store via the Runner and drain normally. */ export declare const MAX_PENDING_EVENTS_PER_TASK = 50; export declare const MAX_PENDING_EVENTS_PER_SESSION = 200; export declare const MAX_PENDING_SESSIONS = 100; export interface DrainedPendingNotifications { /** Chronological (pend order) payloads still held when the session's next run drained. */ items: TaskNotificationPayload[]; /** task_id → notifications evicted by the bounds while pending (never delivered). `taskType` remembers * the victim's lane so a survivors-none disclosure can still render an honest synthetic payload. */ dropped: Map; } export declare class PendingSessionNotifications { private readonly sessions; /** Whole sessions evicted by {@link MAX_PENDING_SESSIONS} (their pendings were never delivered). */ droppedSessions: number; pend(sessionId: string, n: TaskNotificationPayload): void; /** Remove and return the session's pendings (one-shot — the next run consumes them exactly once). */ drain(sessionId: string): DrainedPendingNotifications | undefined; get size(): number; } /** * Delivery-side overflow disclosure: fold the per-task drop counts into the drained payloads. The first * surviving payload of a task that lost events gets a `[task_id]`-prefixed disclosure line prepended to its * summary (backgroundTasks 同规: the id keeps the loss addressable via TaskOutput). A task whose EVERY * pending item was evicted still gets one honest synthetic `event` payload saying so — a fully silent loss * is never allowed. */ export declare function discloseDroppedPending(drained: DrainedPendingNotifications): TaskNotificationPayload[]; export declare class SystemInjectionQueue { private readonly queues; private readonly dedup; private readonly listeners; /** F4 observability: how many queued items were evicted by the retention cap (never delivered). */ droppedOldest: number; enqueue(item: SystemInjection): boolean; drain(priority?: SystemInjectionPriority): Array>; subscribe(listener: (item: SystemInjection) => void): () => void; get size(): number; } //# sourceMappingURL=task-notification.d.ts.map