/** * Pending dispatch registry for tracking in-flight subagent delegations. * * Stores entries keyed by tool callID or child session ID, with a TTL-based * auto-cleanup mechanism. Entries survive PostToolUse so the child recorder * can read actor/model attribution from the registry after dispatch starts. * * @module session-tracker/persistence/pending-dispatch-registry */ /** * A pending dispatch entry tracking an in-flight delegation. */ export interface PendingDispatchEntry { /** Parent session that dispatched the delegation. */ parentSessionID: string; /** Tool call identifier from the hook event. */ callID: string; /** Subagent type (used for actor attribution). */ subagentType: string; /** Tool name (task or delegate-task). */ tool?: string; /** Timestamp of the last activity (used for TTL). */ timestamp: number; /** Model identifier (e.g. "claude-sonnet-4-20250514"). */ model?: string; /** Child session ID assigned after dispatch resolves. */ childSessionID?: string; /** Delegation depth (1 for direct, >1 for nested). */ delegationDepth?: number; /** Last message content captured by message-capture.ts. */ lastMessage?: string; } /** * Registry that tracks pending subagent dispatches. * * Three indexing strategies: * 1. **dispatches** — Map keyed by `call:` or child sessionID. * 2. **byParent** — Map> tracking which callIDs belong to which parent. * 3. **callIDToChild** — Map bridging callID → child sessionID (re-keyed after updateWithChildID). * * Entries survive PostToolUse (see Bug D-1): callers use `refreshTimestamp()` * instead of `removeByCallID()` at PostToolUse. * `removeByCallID()` now requires an explicit `"completed"` reason to delete. */ export declare class PendingDispatchRegistry { private readonly dispatches; private readonly byParent; private readonly callIDToChild; clear(): void; /** * Adds a new pending dispatch entry. * * @param entry - The entry to add. */ add(entry: PendingDispatchEntry): void; /** * Checks if a pending dispatch entry exists for the given key. * * The key can be a `call:` string, a `callID`, or a child session ID. * Runs cleanupStale() on every call. * * @param key - The key to check (callID, call:callID, or sessionID). * @returns `true` if a non-stale entry exists. */ has(key: string): boolean; /** * Gets a pending dispatch entry for the given key. * * The key can be a `call:` string, a `callID`, or a child session ID. * Runs cleanupStale() on every call. * * @param key - The key to look up. * @returns The entry, or `undefined` if not found (or stale). */ get(key: string): PendingDispatchEntry | undefined; /** * Gets the number of non-stale pending dispatch entries. */ get size(): number; /** * Removes a pending dispatch entry by child session ID. * * @param sessionID - The session identifier to remove. */ remove(sessionID: string): void; /** * Gets entries for a parent session, maintaining insertion order. * * @param parentSessionID - The parent session ID to query. * @returns An array of entries, or `undefined` if no entries exist. */ getByParent(parentSessionID: string): PendingDispatchEntry[] | undefined; /** * Re-keys a callID to use the child session ID after dispatch resolves. * * Prevents dispatches from cleaning stale callID entries while the child * session is still being tracked. * * @param callID - The original tool call identifier. * @param childSessionID - The child session ID assigned by dispatch. */ updateWithChildID(callID: string, childSessionID: string): void; /** * Refreshes the timestamp for a pending dispatch entry. * * Used at PostToolUse to keep the entry alive for actor attribution * instead of prematurely removing it (Bug D-1). * * @param callID - The tool call identifier to refresh. */ refreshTimestamp(callID: string): void; /** * Removes a pending dispatch entry by callID. * * **Bug D-1 hardening:** Only deletes when `reason === "completed"`. * For `"postToolUse"` and `"stale"`, does nothing — callers should use * `refreshTimestamp()` to keep entries alive for actor attribution. * * @param callID - The tool call identifier to remove. * @param reason - The reason for removal. Only `"completed"` triggers deletion. */ removeByCallID(callID: string, reason?: "completed" | "postToolUse" | "stale"): void; /** * Removes ALL pending dispatch entries whose timestamp exceeds * STALE_THRESHOLD_MS. Called automatically by has() and get(). * * Per SPEC §2.2: stale entries (>30s) auto-purged on next classification check. */ cleanupStale(): void; /** * Returns the list of pending keys for debugging purposes. * * @returns Array of active dispatch keys. */ keys(): string[]; /** * Gets ANY active entry from the registry (for Gate 0 classification). * * When session.created fires and we don't know the sessionID yet, we need * to check if ANY pending dispatch exists. This returns the first non-stale * entry found (usually the most recent one). * * @returns The first active entry, or `undefined` if no entries exist. */ getAnyActiveEntry(): PendingDispatchEntry | undefined; /** * Gets the subagent type for a given key (session ID or callID). * * @param key - The session ID or callID to look up. * @returns The subagent type, or "unknown" if not found. */ getSubagentType(key: string): string; /** * Normalizes a lookup key. * * If the key starts with "call:" it is returned as-is. * If the key maps to a child session via callIDToChild, the child session ID is returned. * Otherwise the key is treated as a session ID. * * @param key - The raw lookup key. * @returns The normalized key for the dispatches map. */ private normalizeKey; } //# sourceMappingURL=pending-dispatch-registry.d.ts.map