/** * sai-deci — the bundled Sai decisions extension. * * One entrypoint composes the Sai components so the host loads one extension instead of several: * the advisory routes own their input/lifecycle handlers, `ask_sai`/`sai_find` own their tools, the * tool ranker owns its `tool_search` registration and `session_shutdown` disposal. The components already share the single * client in `src/decisions.ts`; this file adds no transport, routing policy, or lifecycle of its own. * * It also contributes the Sai tools to pi-subagents' code-facing builtin agents. Children load * only the tool component, never the advisory routes or ranker. */ import { appendFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import type { ExtensionAPI } from "@selesai/code"; import saiAdvisoryRoutingExtension from "./src/advisory-routing.ts"; import saiAskToolExtension from "./src/ask-tool.ts"; import saiToolRankerExtension from "./src/tool-ranker.ts"; /** Compose the Sai components; each factory registers its own handlers, tools, and ranker. */ export default function saiDeciExtension(pi: ExtensionAPI): void { saiAdvisoryRoutingExtension(pi); saiAskToolExtension(pi); saiToolRankerExtension(pi); registerSubagentAugmentations(pi); // Debug sink: `SAI_ROUTING_LOG=/path/file.jsonl` appends the content-free `sai-routing` events // for `scripts/summarize-sai-routing.mjs`. Unset by default, so nothing is written or subscribed. const log = process.env.SAI_ROUTING_LOG; if (log) { pi.events.on("sai-routing", (event: unknown) => { try { appendFileSync(log, `${JSON.stringify({ t: Date.now(), ...(event as object) })}\n`); } catch { // Telemetry never changes behavior. } }); } } type AugmentationRequest = { register(augmentation: { id: string; agentNames: readonly string[]; addTools: readonly string[]; subagentOnlyExtensions: readonly string[] }): void; }; /** * `sai_find` only reads files, so every code-facing builtin gets it. `ask_sai` can run a shell * `command`, so it goes only to builtins that already have `bash`; `reviewer` has none. */ function registerSubagentAugmentations(pi: ExtensionAPI): void { pi.events.on("pi-subagents:request-builtin-agent-augmentations", (request: unknown) => { if (!request || typeof (request as Partial).register !== "function") return; const toolExtension = fileURLToPath(new URL("./src/ask-tool.ts", import.meta.url)); const { register } = request as AugmentationRequest; register({ id: "sai-deci-find", agentNames: ["scout", "reviewer", "worker", "delegate", "oracle"], addTools: ["sai_find"], subagentOnlyExtensions: [toolExtension], }); register({ id: "sai-deci-ask", agentNames: ["scout", "worker", "delegate", "oracle"], addTools: ["ask_sai"], subagentOnlyExtensions: [toolExtension], }); }); }