import type { ExtensionAPI, SessionStartEvent } from "@earendil-works/pi-coding-agent"; import type { Injection } from "../inbox"; import { threadingToolNames } from "../tools/index"; import type { ThreadingHookHandler as Handler } from "./shared"; function hasThreadId(pi: ExtensionAPI): boolean { const id = pi.getFlag("thread-id"); return typeof id === "string" && id.length > 0; } export const sessionStart: Handler = async ( { pi, store, inbox, state }, _, ctx, ) => { state.active = hasThreadId(pi); if (!state.active) { pi.setActiveTools(pi.getActiveTools().filter(name => !threadingToolNames.includes(name))); return; } await store.init(ctx); setImmediate(() => void inbox.drain(ctx)); store.startWatcher(inbox.drain, ctx); store.startHeartbeat(async () => { // Coalesce both heartbeat-driven sources into ONE inject() per tick // (ยง7.5, Errata 3): if drain injected on its own here, its // idle-time inFlightSince write would gate out the deadline check for // a full heartbeat interval. const parts: Injection[] = []; await inbox.drain(ctx, parts); await inbox.checkDeadlines(ctx, parts); inbox.inject(parts, ctx); }); };