/** * Read pi-subagents' background-work state without importing its source. * * pi-subagents publishes its registry on a documented global symbol * (`Symbol.for("pi-subagents.background-work.v1")`). Reading that symbol keeps * this package working whether pi-subagents is bundled, installed separately, or * absent, and avoids importing a `.ts` file from under `node_modules`, which * Node refuses to type-strip. */ export const BACKGROUND_WORK_REGISTRY_KEY = "pi-subagents.background-work.v1"; interface BackgroundWorkItem { id: string; sessionId: string; } interface BackgroundWorkProvider { name: string; listActiveWork(): readonly BackgroundWorkItem[]; reconcile?(context: { sessionId: string; nowMs: number }): void; } /** Returns undefined when pi-subagents is not loaded at all. */ function providers(): readonly BackgroundWorkProvider[] | undefined { const registry = (globalThis as Record)[Symbol.for(BACKGROUND_WORK_REGISTRY_KEY)]; if (!registry || typeof registry !== "object") return undefined; const candidate = (registry as { providers?: unknown }).providers; if (!(candidate instanceof Map)) return undefined; return [...candidate.values()] as BackgroundWorkProvider[]; } /** * Label every background item pi-subagents still considers active for this * session, as `provider:id`. * * Returns an empty array when nothing is pending. Returns undefined when the * registry is absent, which means "nothing is tracking work" rather than * "no work exists" — the caller decides what to do with that distinction. */ export function listPendingWorkLabels(sessionId: string, nowMs = Date.now()): string[] | undefined { const active = providers(); if (!active) return undefined; const labels: string[] = []; for (const provider of active) { // A provider that throws must not block the orchestration loop; treat it // as contributing no items and keep inspecting the rest. try { provider.reconcile?.({ sessionId, nowMs }); } catch { // Reconciliation is advisory. } let items: readonly BackgroundWorkItem[]; try { items = provider.listActiveWork(); } catch { continue; } if (!Array.isArray(items)) continue; for (const item of items) { if (!item || typeof item !== "object") continue; if (item.sessionId !== sessionId) continue; labels.push(`${provider.name}:${item.id}`); } } return labels; }