@jyork0828/pi-pilot 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/http-proxy.ts","../src/api/workspaces.ts","../src/storage/resource-writer.ts","../src/storage/workspace-registry.ts","../src/storage/workspace-stats.ts","../src/workspace-manager.ts","../src/extensions/plan/schema.ts","../src/extensions/plan/factory.ts","../src/extensions/ask_user/schema.ts","../src/extensions/ask_user/registry.ts","../src/extensions/ask_user/factory.ts","../src/storage/builtin-extension-prefs.ts","../src/extensions/index.ts","../src/extensions/ask_user/cleanup.ts","../src/ws/bridge.ts","../src/ws/extension-ui.ts","../src/api/config.ts","../src/api/files.ts","../src/api/resources.ts","../src/api/fs.ts","../src/api/model-configs.ts","../src/ws/hub.ts"],"sourcesContent":["/**\n * pi-pilot server entry.\n *\n * Boots one process that serves both HTTP (Hono) and WebSocket (`ws`) on\n * the same port. Localhost only.\n */\n\nimport type { Server as HttpServer } from \"node:http\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, extname, join, resolve, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { serve } from \"@hono/node-server\";\nimport type { Context } from \"hono\";\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport { config } from \"./config.ts\";\nimport { configureHttpProxy } from \"./http-proxy.ts\";\nimport { workspacesRoute } from \"./api/workspaces.ts\";\nimport { fsRoute } from \"./api/fs.ts\";\nimport { modelConfigsRoute } from \"./api/model-configs.ts\";\nimport { attachWsHub } from \"./ws/hub.ts\";\nimport { workspaceManager } from \"./workspace-manager.ts\";\nimport { loadBuiltinPrefs } from \"./storage/builtin-extension-prefs.ts\";\n\n// Honor standard HTTPS_PROXY / HTTP_PROXY / NO_PROXY before any pi LLM call.\n// Must run early — the SDK's provider requests go through undici's global\n// dispatcher, which this installs (no-op/direct when no proxy env var is set).\nconfigureHttpProxy();\n\nconst app = new Hono();\nconst distDir = dirname(fileURLToPath(import.meta.url));\nconst webRoot = resolve(process.env.PI_PILOT_WEB_ROOT ?? join(distDir, \"..\", \"public\"));\nconst webIndexPath = join(webRoot, \"index.html\");\n\nconst mimeTypes: Record<string, string> = {\n\t\".css\": \"text/css; charset=utf-8\",\n\t\".html\": \"text/html; charset=utf-8\",\n\t\".ico\": \"image/x-icon\",\n\t\".js\": \"text/javascript; charset=utf-8\",\n\t\".json\": \"application/json; charset=utf-8\",\n\t\".map\": \"application/json; charset=utf-8\",\n\t\".png\": \"image/png\",\n\t\".svg\": \"image/svg+xml\",\n\t\".txt\": \"text/plain; charset=utf-8\",\n\t\".webp\": \"image/webp\",\n\t\".woff\": \"font/woff\",\n\t\".woff2\": \"font/woff2\",\n};\n\nfunction isApiOrWsPath(pathname: string): boolean {\n\treturn pathname === \"/api\" || pathname.startsWith(\"/api/\") || pathname === \"/ws\";\n}\n\nfunction safeResolveWebPath(pathname: string): string | undefined {\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(pathname);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tconst relativePath = decoded === \"/\" ? \"index.html\" : decoded.replace(/^\\/+/, \"\");\n\tconst candidate = resolve(webRoot, relativePath);\n\tif (candidate !== webRoot && !candidate.startsWith(`${webRoot}${sep}`)) {\n\t\treturn undefined;\n\t}\n\treturn candidate;\n}\n\nasync function readWebFile(path: string): Promise<Buffer | undefined> {\n\ttry {\n\t\treturn await readFile(path);\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tif (code === \"ENOENT\" || code === \"EISDIR\") return undefined;\n\t\tthrow err;\n\t}\n}\n\nasync function serveWeb(c: Context): Promise<Response> {\n\tconst pathname = new URL(c.req.url).pathname;\n\tif (isApiOrWsPath(pathname)) return c.notFound();\n\n\tconst assetPath = safeResolveWebPath(pathname);\n\tif (!assetPath) return c.text(\"invalid asset path\", 400);\n\n\tconst asset = await readWebFile(assetPath);\n\tconst body = asset ?? (await readFile(webIndexPath));\n\tconst filePath = asset ? assetPath : webIndexPath;\n\tconst headers: Record<string, string> = {\n\t\t\"Content-Type\": mimeTypes[extname(filePath)] ?? \"application/octet-stream\",\n\t\t\"Cache-Control\": asset && pathname.startsWith(\"/assets/\")\n\t\t\t? \"public, max-age=31536000, immutable\"\n\t\t\t: \"no-cache\",\n\t};\n\n\treturn new Response(body as unknown as BodyInit, { headers });\n}\n\napp.use(\n\t\"/api/*\",\n\tcors({\n\t\torigin: config.corsOrigin,\n\t\tallowMethods: [\"GET\", \"POST\", \"PUT\", \"DELETE\"],\n\t}),\n);\n\napp.get(\"/api/health\", (c) => c.json({ ok: true }));\napp.route(\"/api/workspaces\", workspacesRoute);\napp.route(\"/api/fs\", fsRoute);\napp.route(\"/api/model-configs\", modelConfigsRoute);\n\nif (existsSync(webIndexPath)) {\n\tapp.get(\"*\", serveWeb);\n} else {\n\tapp.get(\"/\", (c) =>\n\t\tc.text(\n\t\t\t\"pi-pilot server is running, but the web UI assets were not found. \" +\n\t\t\t\t\"Run `pnpm build` from the repository root, or set PI_PILOT_WEB_ROOT to a built web dist directory.\",\n\t\t\t500,\n\t\t),\n\t);\n}\n\n// Load builtin-extension on/off prefs into the in-memory cache before serving,\n// so the synchronous factory gate (extensions/index.ts) sees them on the first\n// lazily-built runtime. Top-level await blocks the listen until this resolves.\nawait loadBuiltinPrefs();\n\nconst server = serve(\n\t{\n\t\tfetch: app.fetch,\n\t\thostname: config.host,\n\t\tport: config.port,\n\t},\n\t(info) => {\n\t\tconsole.log(`[pi-pilot] http://${info.address}:${info.port}`);\n\t},\n);\n\nattachWsHub(server as unknown as HttpServer);\n\nasync function shutdown(reason: string): Promise<void> {\n\tconsole.log(`[pi-pilot] shutting down (${reason})`);\n\ttry {\n\t\tawait workspaceManager.disposeAll();\n\t} catch (e) {\n\t\tconsole.error(\"[pi-pilot] disposeAll error:\", e);\n\t}\n\tserver.close(() => process.exit(0));\n\t// Hard-kill if close hangs.\n\tsetTimeout(() => process.exit(1), 3000).unref();\n}\n\nprocess.on(\"SIGINT\", () => void shutdown(\"SIGINT\"));\nprocess.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\"));\n","/**\n * Process-wide config, sourced from env vars.\n *\n * We do not read any file at startup beyond what pi's SDK does internally.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const config = {\n\t/** HTTP + WS port. */\n\tport: Number(process.env.PI_PILOT_PORT ?? 5174),\n\n\t/** Bind address. Hard-coded localhost; do NOT make this configurable\n\t * without first reviewing the security implications of exposing bash. */\n\thost: \"127.0.0.1\",\n\n\t/** Where pi-pilot stores its own (non-pi) state. */\n\tdataDir: process.env.PI_PILOT_DATA_DIR ?? join(homedir(), \".pi\", \"webui\"),\n\n\t/** Dev origin allowed by CORS for /api. The Vite dev server. */\n\tcorsOrigin: process.env.PI_PILOT_CORS_ORIGIN ?? \"http://localhost:5173\",\n} as const;\n","/**\n * Route every outbound HTTP(S) request — including all pi LLM provider calls —\n * through the proxy named in the standard `HTTPS_PROXY` / `HTTP_PROXY` /\n * `NO_PROXY` env vars (lowercase variants honored too).\n *\n * pi's own CLI does this in `cli.ts` via `configureHttpDispatcher()`. pi-pilot\n * embeds the SDK in-process and never hits that entry, so without this the\n * provider SDKs (Anthropic / OpenAI / Gemini / Mistral — all go through the\n * global `fetch`/undici dispatcher) ignore proxy env vars entirely. We replicate\n * the dispatcher install here and call it once at startup, before the first\n * runtime is built.\n *\n * `undici` is pinned to the SDK's bundled version (8.3.0) so the dispatcher we\n * set is the same implementation the SDK's fetch reads from.\n */\n\nimport { EnvHttpProxyAgent, install, setGlobalDispatcher } from \"undici\";\n\nlet configured = false;\n\n/**\n * Install an `EnvHttpProxyAgent` as undici's global dispatcher. Idempotent.\n *\n * Safe to call unconditionally: when no proxy env var is set, the agent\n * degrades to a direct-connection `Agent`, so behavior is unchanged for users\n * who don't use a proxy. The agent reads the env at construction time, hence\n * this must run early (it does — before any LLM request).\n */\nexport function configureHttpProxy(): void {\n\tif (configured) return;\n\tconfigured = true;\n\t// allowH2:false + 300s idle timeout mirror pi's `core/http-dispatcher.ts`,\n\t// tuned for long-lived LLM streaming responses.\n\tsetGlobalDispatcher(\n\t\tnew EnvHttpProxyAgent({\n\t\t\tallowH2: false,\n\t\t\tbodyTimeout: 300_000,\n\t\t\theadersTimeout: 300_000,\n\t\t}),\n\t);\n\t// Keep `fetch` and the dispatcher on the same undici implementation (also\n\t// mirrors pi): avoids a mismatch where newer Node's bundled fetch consumes a\n\t// compressed response through the npm-undici dispatcher without decompressing.\n\tinstall?.();\n}\n","/**\n * /api/workspaces routes.\n */\n\nimport { stat } from \"node:fs/promises\";\nimport { basename, isAbsolute, resolve } from \"node:path\";\nimport { Hono } from \"hono\";\nimport type {\n\tAddWorkspaceRequest,\n\tAddWorkspaceResponse,\n\tHistoryResponse,\n\tListForkPointsResponse,\n\tListSessionsResponse,\n\tListWorkspacesResponse,\n\tOkResponse,\n} from \"@pi-pilot/shared\";\nimport { HttpError } from \"../storage/resource-writer.ts\";\nimport {\n\taddWorkspace,\n\tgetWorkspace,\n\tlistWorkspaces,\n\tremoveWorkspace,\n} from \"../storage/workspace-registry.ts\";\nimport { enrichWorkspace } from \"../storage/workspace-stats.ts\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { mountConfigRoutes } from \"./config.ts\";\nimport { mountFilesRoute } from \"./files.ts\";\nimport { mountResourcesRoute } from \"./resources.ts\";\n\nexport const workspacesRoute = new Hono();\n\nworkspacesRoute.get(\"/\", async (c) => {\n\tconst raw = await listWorkspaces();\n\tconst workspaces = await Promise.all(raw.map(enrichWorkspace));\n\tconst body: ListWorkspacesResponse = { workspaces };\n\treturn c.json(body);\n});\n\nworkspacesRoute.get(\"/:id/sessions\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst sessions = await workspaceManager.listSessions(id);\n\t\tconst body: ListSessionsResponse = { sessions };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] list sessions for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.delete(\"/:id/sessions\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\tconst sessionPath = c.req.query(\"path\");\n\tif (!sessionPath) {\n\t\treturn c.json({ ok: false, error: \"path query is required\" }, 400);\n\t}\n\n\ttry {\n\t\tawait workspaceManager.deleteSession(id, sessionPath);\n\t\tconst body: OkResponse = { ok: true };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tif (err instanceof HttpError) {\n\t\t\treturn c.json(\n\t\t\t\t{ ok: false, error: err.message },\n\t\t\t\terr.status as 400 | 404 | 409 | 500,\n\t\t\t);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] delete session for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.get(\"/:id/fork-points\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\tconst raw = runtime.session.getUserMessagesForForking();\n\t\tconst points = raw.map((p) => ({\n\t\t\tentryId: p.entryId,\n\t\t\ttext: p.text.length > 120 ? p.text.slice(0, 120) + \"…\" : p.text,\n\t\t}));\n\t\tconst body: ListForkPointsResponse = { points };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] fork-points for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.get(\"/:id/history\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tawait workspaceManager.getOrCreate(id);\n\t\tconst sessionPath = c.req.query(\"sessionPath\");\n\t\tconst body: HistoryResponse = workspaceManager.getSessionHistory(id, sessionPath);\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] history for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.post(\"/\", async (c) => {\n\tconst body = (await c.req.json()) as AddWorkspaceRequest;\n\tif (!body?.path || typeof body.path !== \"string\") {\n\t\treturn c.json({ ok: false, error: \"path is required\" }, 400);\n\t}\n\tif (!isAbsolute(body.path)) {\n\t\treturn c.json({ ok: false, error: \"path must be absolute\" }, 400);\n\t}\n\tconst resolved = resolve(body.path);\n\ttry {\n\t\tconst st = await stat(resolved);\n\t\tif (!st.isDirectory()) {\n\t\t\treturn c.json({ ok: false, error: \"path is not a directory\" }, 400);\n\t\t}\n\t} catch {\n\t\treturn c.json({ ok: false, error: \"path does not exist\" }, 400);\n\t}\n\n\tconst stored = await addWorkspace({\n\t\tpath: resolved,\n\t\tname: body.name?.trim() || basename(resolved) || resolved,\n\t});\n\tconst ws = await enrichWorkspace(stored);\n\tconst res: AddWorkspaceResponse = { workspace: ws };\n\treturn c.json(res);\n});\n\nworkspacesRoute.delete(\"/:id\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t// Dispose the runtime if it was open. Best-effort.\n\tawait workspaceManager.dispose(id);\n\tawait removeWorkspace(id);\n\n\tconst body: OkResponse = { ok: true };\n\treturn c.json(body);\n});\n\nmountConfigRoutes(workspacesRoute);\nmountResourcesRoute(workspacesRoute);\nmountFilesRoute(workspacesRoute);\n","/**\n * Filesystem CRUD for skills and prompt templates.\n *\n * pi's ResourceLoader is read-only from our perspective (`reload()` re-reads\n * from disk but doesn't write). So pi-pilot writes the files itself and then\n * asks the loader to re-scan.\n *\n * Layout (matches pi's discovery rules — see docs/skills.md, docs/prompt-templates.md):\n *\n * skills:\n * user → <agentDir>/skills/<name>/SKILL.md\n * project → <workspaceCwd>/.pi/skills/<name>/SKILL.md\n *\n * prompts:\n * user → <agentDir>/prompts/<name>.md\n * project → <workspaceCwd>/.pi/prompts/<name>.md\n *\n * Safety\n * - `name` is restricted to lowercase a-z / 0-9 / hyphens (skills) and\n * additionally underscores (prompts) so it cannot escape its parent dir.\n * - `assertUnder(path, allowedRoots)` double-checks the resolved file path\n * is actually inside one of the allowed roots before any write/unlink.\n * - We never delete a directory that contains anything other than known\n * skill content via this module \\u2014 see `deleteSkill` for the precise rule.\n *\n * Frontmatter handling\n * - Known fields (name, description, disable-model-invocation /\n * argument-hint) are parsed into typed slots and exposed to the\n * editor as discrete form inputs.\n * - Unknown keys (`allowed-tools`, `metadata`, `license`, \\u2026) are\n * captured as an opaque list of raw `key: value` lines and round-\n * tripped on save. The editor never displays or rewrites them; the\n * write path concatenates them back below the known fields in the\n * order they appeared in the source. This means custom YAML survives\n * edits, but multi-line scalars / anchors / nested maps still won't\n * because the parser is line-oriented (acceptable: pi's documented\n * skill/prompt fields are all single-line scalars).\n */\n\nimport {\n\tmkdir,\n\treadFile,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve, sep } from \"node:path\";\nimport type { ResourceScope } from \"@pi-pilot/shared\";\n\n// ---------- path helpers ----------\n\nconst SKILL_NAME_RE = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;\nconst PROMPT_NAME_RE = /^[a-z0-9_](?:[a-z0-9_-]{0,62}[a-z0-9_])?$/;\n\nfunction ensureSkillName(name: string): void {\n\tif (!SKILL_NAME_RE.test(name)) {\n\t\tthrow new HttpError(400, \"skill name must be lowercase a-z/0-9/hyphens, 1-64 chars, no leading/trailing/double hyphens\");\n\t}\n}\n\nfunction ensurePromptName(name: string): void {\n\tif (!PROMPT_NAME_RE.test(name)) {\n\t\tthrow new HttpError(400, \"prompt name must be lowercase a-z/0-9/hyphens/underscores, 1-64 chars\");\n\t}\n}\n\n/**\n * Throw if `target` does not resolve to a descendant of any path in `roots`.\n * Prevents `../../etc/passwd` style escapes even though the name regex above\n * already forbids slashes \\u2014 belt + suspenders.\n */\nfunction assertUnder(target: string, roots: string[]): void {\n\tconst t = resolve(target);\n\tconst ok = roots.some((root) => {\n\t\tconst r = resolve(root);\n\t\treturn t === r || t.startsWith(r + sep);\n\t});\n\tif (!ok) {\n\t\tthrow new HttpError(500, `refusing to touch path outside managed roots: ${target}`);\n\t}\n}\n\nfunction skillDirFor(scope: ResourceScope, name: string, roots: ResourceRoots): string {\n\tensureSkillName(name);\n\tconst base = scope === \"user\" ? roots.userSkills : roots.projectSkills;\n\treturn join(base, name);\n}\n\nfunction promptFileFor(scope: ResourceScope, name: string, roots: ResourceRoots): string {\n\tensurePromptName(name);\n\tconst base = scope === \"user\" ? roots.userPrompts : roots.projectPrompts;\n\treturn join(base, `${name}.md`);\n}\n\nexport interface ResourceRoots {\n\t/** ~/.pi/agent/skills */\n\tuserSkills: string;\n\t/** <cwd>/.pi/skills */\n\tprojectSkills: string;\n\t/** ~/.pi/agent/prompts */\n\tuserPrompts: string;\n\t/** <cwd>/.pi/prompts */\n\tprojectPrompts: string;\n}\n\nexport function resolveResourceRoots(opts: {\n\tagentDir: string;\n\tworkspaceCwd: string;\n}): ResourceRoots {\n\treturn {\n\t\tuserSkills: join(opts.agentDir, \"skills\"),\n\t\tprojectSkills: join(opts.workspaceCwd, \".pi\", \"skills\"),\n\t\tuserPrompts: join(opts.agentDir, \"prompts\"),\n\t\tprojectPrompts: join(opts.workspaceCwd, \".pi\", \"prompts\"),\n\t};\n}\n\n/** Map an absolute file path to its scope, by checking which root it sits in. */\nexport function scopeFor(absPath: string, roots: ResourceRoots): ResourceScope | undefined {\n\tconst p = resolve(absPath);\n\tfor (const [root, scope] of [\n\t\t[roots.userSkills, \"user\"],\n\t\t[roots.projectSkills, \"project\"],\n\t\t[roots.userPrompts, \"user\"],\n\t\t[roots.projectPrompts, \"project\"],\n\t] as const) {\n\t\tconst r = resolve(root);\n\t\tif (p === r || p.startsWith(r + sep)) return scope;\n\t}\n\treturn undefined;\n}\n\n// ---------- frontmatter ----------\n\n/** A frontmatter line we recognized. `raw` is the original `key: value`\n * text, used to round-trip unknown keys verbatim. */\ninterface FrontmatterLine {\n\tkey: string;\n\tvalue: string | boolean;\n\traw: string;\n}\n\ninterface ParsedFile {\n\tfrontmatter: Record<string, string | boolean>;\n\t/** Every frontmatter line in source order, with the original raw text. */\n\tlines: FrontmatterLine[];\n\tbody: string;\n}\n\n/**\n * Tiny YAML-frontmatter reader. Recognises `key: value`, `key: \"value\"`,\n * and `key: true|false`. Quoted strings honour `\\\"` and `\\\\` escapes;\n * everything else is treated as a raw scalar (trimmed). This is enough for\n * pi's documented skill/prompt frontmatter fields. Files with multi-line\n * scalars, anchors, etc. won't round-trip cleanly through pi-pilot.\n */\nfunction parseFile(content: string): ParsedFile {\n\tconst rawLines = content.split(/\\r?\\n/);\n\tif (rawLines[0] !== \"---\") {\n\t\treturn { frontmatter: {}, lines: [], body: content };\n\t}\n\tconst fm: Record<string, string | boolean> = {};\n\tconst lines: FrontmatterLine[] = [];\n\tlet i = 1;\n\twhile (i < rawLines.length && rawLines[i] !== \"---\") {\n\t\tconst line = rawLines[i] ?? \"\";\n\t\tconst m = line.match(/^([A-Za-z][A-Za-z0-9_-]*)\\s*:\\s*(.*?)\\s*$/);\n\t\tif (m) {\n\t\t\tconst key = m[1]!;\n\t\t\tconst rawVal = m[2] ?? \"\";\n\t\t\tconst value = parseScalar(rawVal);\n\t\t\tfm[key] = value;\n\t\t\tlines.push({ key, value, raw: line });\n\t\t}\n\t\ti++;\n\t}\n\tif (i >= rawLines.length) {\n\t\t// No closing `---` \\u2014 treat the whole thing as body, no frontmatter.\n\t\treturn { frontmatter: {}, lines: [], body: content };\n\t}\n\tconst body = rawLines.slice(i + 1).join(\"\\n\");\n\treturn { frontmatter: fm, lines, body };\n}\n\n/**\n * Filter `lines` (every frontmatter entry as it appeared on disk) down\n * to the entries whose keys are *not* in `knownKeys`. Used to preserve\n * arbitrary frontmatter (`allowed-tools`, `metadata`, `license`, \\u2026) on\n * write.\n *\n * Returns the raw source text of each kept line (without the trailing\n * newline). The build path emits these verbatim so values keep their\n * original quoting style.\n */\nfunction extraLines(lines: FrontmatterLine[], knownKeys: readonly string[]): string[] {\n\tconst known = new Set(knownKeys);\n\treturn lines.filter((l) => !known.has(l.key)).map((l) => l.raw);\n}\n\nfunction parseScalar(raw: string): string | boolean {\n\tif (raw === \"true\") return true;\n\tif (raw === \"false\") return false;\n\tif (raw.startsWith('\"') && raw.endsWith('\"') && raw.length >= 2) {\n\t\treturn raw.slice(1, -1).replace(/\\\\([\"\\\\])/g, \"$1\");\n\t}\n\tif (raw.startsWith(\"'\") && raw.endsWith(\"'\") && raw.length >= 2) {\n\t\treturn raw.slice(1, -1).replace(/''/g, \"'\");\n\t}\n\treturn raw;\n}\n\n/**\n * Emit a frontmatter+body file. Field order is stable so re-saves produce\n * deterministic diffs:\n *\n * 1. Known fields in the caller-supplied order (skipping undefined / \"\").\n * 2. Extra raw lines (unknown frontmatter keys captured on read) appended\n * verbatim. They keep their original ordering relative to each other.\n *\n * Boolean/string known values only; arrays and nested maps round-trip via\n * the raw `extras` channel only.\n */\nfunction buildFile(\n\tfields: Array<[string, string | boolean | undefined]>,\n\tbody: string,\n\textras: string[] = [],\n): string {\n\tconst out: string[] = [\"---\"];\n\tfor (const [key, value] of fields) {\n\t\tif (value === undefined || value === \"\") continue;\n\t\tif (typeof value === \"boolean\") {\n\t\t\tout.push(`${key}: ${value ? \"true\" : \"false\"}`);\n\t\t} else {\n\t\t\tout.push(`${key}: ${formatString(value)}`);\n\t\t}\n\t}\n\tfor (const raw of extras) out.push(raw);\n\tout.push(\"---\");\n\tconst trimmedBody = body.replace(/\\s+$/, \"\");\n\tconst text = `${out.join(\"\\n\")}\\n${trimmedBody}\\n`;\n\treturn text;\n}\n\n/** Frontmatter keys we render as form inputs. Everything else is opaque\n * passthrough. Keep these arrays here so read and write agree. */\nconst SKILL_KNOWN_KEYS = [\"name\", \"description\", \"disable-model-invocation\"] as const;\nconst PROMPT_KNOWN_KEYS = [\"description\", \"argument-hint\"] as const;\n\nfunction formatString(value: string): string {\n\t// Always double-quote so embedded `:`, `#`, leading/trailing whitespace,\n\t// and the like don't break YAML parsing. Escape `\\` and `\"`.\n\tconst escaped = value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n\treturn `\"${escaped}\"`;\n}\n\n// ---------- skills ----------\n\nexport interface CreateSkillOpts {\n\troots: ResourceRoots;\n\tscope: ResourceScope;\n\tname: string;\n\tdescription: string;\n\tbody: string;\n\tdisableModelInvocation?: boolean;\n}\n\nexport async function createSkill(opts: CreateSkillOpts): Promise<string> {\n\tconst dir = skillDirFor(opts.scope, opts.name, opts.roots);\n\tassertUnder(dir, [opts.roots.userSkills, opts.roots.projectSkills]);\n\tif (await exists(dir)) {\n\t\tthrow new HttpError(409, `skill already exists at ${dir}`);\n\t}\n\tconst file = join(dir, \"SKILL.md\");\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"name\", opts.name],\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"disable-model-invocation\", opts.disableModelInvocation],\n\t\t],\n\t\topts.body,\n\t);\n\tawait mkdir(dir, { recursive: true });\n\tawait writeFile(file, text, \"utf8\");\n\treturn file;\n}\n\nexport interface UpdateSkillOpts {\n\troots: ResourceRoots;\n\tfilePath: string;\n\tname: string;\n\tdescription: string;\n\tbody: string;\n\tdisableModelInvocation?: boolean;\n}\n\nexport async function updateSkill(opts: UpdateSkillOpts): Promise<string> {\n\tif (!isAbsolute(opts.filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(opts.filePath, [opts.roots.userSkills, opts.roots.projectSkills]);\n\tif (!(await exists(opts.filePath))) {\n\t\tthrow new HttpError(404, `skill file not found: ${opts.filePath}`);\n\t}\n\t// Round-trip arbitrary frontmatter (`allowed-tools`, `metadata`, \\u2026)\n\t// from the existing file. Re-read each save so a side edit on disk\n\t// between open and save isn't clobbered.\n\tconst existing = await readFile(opts.filePath, \"utf8\");\n\tconst extras = extraLines(parseFile(existing).lines, SKILL_KNOWN_KEYS);\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"name\", opts.name],\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"disable-model-invocation\", opts.disableModelInvocation],\n\t\t],\n\t\topts.body,\n\t\textras,\n\t);\n\tawait writeFile(opts.filePath, text, \"utf8\");\n\treturn opts.filePath;\n}\n\nexport async function deleteSkill(filePath: string, roots: ResourceRoots): Promise<void> {\n\tif (!isAbsolute(filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(filePath, [roots.userSkills, roots.projectSkills]);\n\tif (!(await exists(filePath))) {\n\t\tthrow new HttpError(404, `skill file not found: ${filePath}`);\n\t}\n\t// A skill can be either a bare .md file (root-level under skills/) or a\n\t// SKILL.md inside its own directory. Match pi's discovery: if the parent\n\t// directory is itself the skills root, delete just the .md file;\n\t// otherwise delete the parent directory (skill + scripts + assets).\n\tconst dir = dirname(filePath);\n\tconst parentIsRoot =\n\t\tresolve(dir) === resolve(roots.userSkills) ||\n\t\tresolve(dir) === resolve(roots.projectSkills);\n\tif (parentIsRoot) {\n\t\tawait unlink(filePath);\n\t\treturn;\n\t}\n\tassertUnder(dir, [roots.userSkills, roots.projectSkills]);\n\tawait rm(dir, { recursive: true, force: true });\n}\n\n// ---------- prompts ----------\n\nexport interface CreatePromptOpts {\n\troots: ResourceRoots;\n\tscope: ResourceScope;\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tbody: string;\n}\n\nexport async function createPrompt(opts: CreatePromptOpts): Promise<string> {\n\tconst file = promptFileFor(opts.scope, opts.name, opts.roots);\n\tassertUnder(file, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\tif (await exists(file)) {\n\t\tthrow new HttpError(409, `prompt already exists at ${file}`);\n\t}\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"argument-hint\", opts.argumentHint],\n\t\t],\n\t\topts.body,\n\t);\n\tawait mkdir(dirname(file), { recursive: true });\n\tawait writeFile(file, text, \"utf8\");\n\treturn file;\n}\n\nexport interface UpdatePromptOpts {\n\troots: ResourceRoots;\n\tfilePath: string;\n\t/** Stem of the new filename. If different from the existing file's stem,\n\t * the file is renamed. */\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tbody: string;\n}\n\nexport async function updatePrompt(opts: UpdatePromptOpts): Promise<string> {\n\tif (!isAbsolute(opts.filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(opts.filePath, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\tif (!(await exists(opts.filePath))) {\n\t\tthrow new HttpError(404, `prompt file not found: ${opts.filePath}`);\n\t}\n\tensurePromptName(opts.name);\n\tconst dir = dirname(opts.filePath);\n\tconst newPath = join(dir, `${opts.name}.md`);\n\tassertUnder(newPath, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\t// Preserve arbitrary frontmatter from the existing file (re-read so a\n\t// concurrent on-disk edit doesn't get silently dropped).\n\tconst existing = await readFile(opts.filePath, \"utf8\");\n\tconst extras = extraLines(parseFile(existing).lines, PROMPT_KNOWN_KEYS);\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"argument-hint\", opts.argumentHint],\n\t\t],\n\t\topts.body,\n\t\textras,\n\t);\n\tif (resolve(newPath) !== resolve(opts.filePath)) {\n\t\tif (await exists(newPath)) {\n\t\t\tthrow new HttpError(409, `prompt already exists at ${newPath}`);\n\t\t}\n\t\t// Rename is NOT a single syscall here — we have to write the new\n\t\t// content under a different name and then drop the old file. If the\n\t\t// unlink fails (perms, file lock, cross-fs handle, etc.) we must roll\n\t\t// back the new file; otherwise `loader.reload()` would surface an\n\t\t// orphaned `/oldname` prompt the user never asked to keep.\n\t\tawait writeFile(newPath, text, \"utf8\");\n\t\ttry {\n\t\t\tawait unlink(opts.filePath);\n\t\t} catch (err) {\n\t\t\tawait unlink(newPath).catch(() => undefined);\n\t\t\tthrow err;\n\t\t}\n\t\treturn newPath;\n\t}\n\tawait writeFile(opts.filePath, text, \"utf8\");\n\treturn opts.filePath;\n}\n\nexport async function deletePrompt(filePath: string, roots: ResourceRoots): Promise<void> {\n\tif (!isAbsolute(filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(filePath, [roots.userPrompts, roots.projectPrompts]);\n\tif (!(await exists(filePath))) {\n\t\tthrow new HttpError(404, `prompt file not found: ${filePath}`);\n\t}\n\tawait unlink(filePath);\n}\n\n// ---------- reads (for editor pre-fill) ----------\n\nexport interface ReadResourceFileResult {\n\tbody: string;\n\tname: string;\n\tdescription: string;\n\tdisableModelInvocation?: boolean;\n\targumentHint?: string;\n}\n\nexport async function readSkillFile(filePath: string, roots: ResourceRoots): Promise<ReadResourceFileResult> {\n\tassertUnder(filePath, [roots.userSkills, roots.projectSkills]);\n\tconst text = await readFile(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFile(text);\n\treturn {\n\t\tbody,\n\t\tname: stringOr(frontmatter.name, \"\"),\n\t\tdescription: stringOr(frontmatter.description, \"\"),\n\t\tdisableModelInvocation: booleanOr(frontmatter[\"disable-model-invocation\"], undefined),\n\t};\n}\n\nexport async function readPromptFile(filePath: string, roots: ResourceRoots): Promise<ReadResourceFileResult> {\n\tassertUnder(filePath, [roots.userPrompts, roots.projectPrompts]);\n\tconst text = await readFile(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFile(text);\n\t// Prompt's \"name\" is the filename stem, not a frontmatter field.\n\tconst stem = basename(filePath).replace(/\\.md$/, \"\");\n\treturn {\n\t\tbody,\n\t\tname: stem,\n\t\tdescription: stringOr(frontmatter.description, \"\"),\n\t\targumentHint: stringOr(frontmatter[\"argument-hint\"], undefined),\n\t};\n}\n\nfunction basename(p: string): string {\n\tconst parts = p.split(sep);\n\treturn parts.at(-1) || p;\n}\n\nfunction stringOr<T>(value: unknown, fallback: T): string | T {\n\treturn typeof value === \"string\" ? value : fallback;\n}\n\nfunction booleanOr<T>(value: unknown, fallback: T): boolean | T {\n\treturn typeof value === \"boolean\" ? value : fallback;\n}\n\n// ---------- misc ----------\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Tagged error so the route layer can map to specific HTTP statuses without\n * losing the message. Anything else thrown becomes a 500.\n */\nexport class HttpError extends Error {\n\tconstructor(public readonly status: number, message: string) {\n\t\tsuper(message);\n\t}\n}\n","/**\n * Persistence for the list of workspaces the user has registered.\n *\n * pi itself doesn't need this — it's tied to a single cwd at a time. We need\n * it because the WebUI shows a multi-workspace picker.\n *\n * File format (~/.pi/webui/workspaces.json):\n * { \"workspaces\": [{ \"id\", \"name\", \"path\", \"addedAt\" }] }\n *\n * Storage holds only the *persistent* fields. Derived telemetry\n * (gitBranch, fileCount) is added by the API layer via `enrichWorkspace`\n * — see `workspace-stats.ts`. Keeping them out of storage avoids stale\n * branch names on disk when the user switches branches outside the app.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { config } from \"../config.ts\";\n\n/** Persistent shape on disk. API responses augment this with derived fields. */\nexport interface StoredWorkspace {\n\tid: string;\n\tname: string;\n\tpath: string;\n\t/** ISO-8601 */\n\taddedAt: string;\n}\n\nconst REGISTRY_PATH = join(config.dataDir, \"workspaces.json\");\n\ninterface RegistryFile {\n\tworkspaces: StoredWorkspace[];\n}\n\nlet cache: RegistryFile | undefined;\n\nasync function load(): Promise<RegistryFile> {\n\tif (cache) return cache;\n\ttry {\n\t\tconst raw = await readFile(REGISTRY_PATH, \"utf8\");\n\t\tcache = JSON.parse(raw) as RegistryFile;\n\t\tif (!Array.isArray(cache.workspaces)) cache = { workspaces: [] };\n\t} catch (err) {\n\t\tif ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\tcache = { workspaces: [] };\n\t\t} else {\n\t\t\tthrow err;\n\t\t}\n\t}\n\treturn cache;\n}\n\nasync function save(): Promise<void> {\n\tif (!cache) return;\n\tawait mkdir(dirname(REGISTRY_PATH), { recursive: true });\n\tawait writeFile(REGISTRY_PATH, JSON.stringify(cache, null, 2), \"utf8\");\n}\n\nexport async function listWorkspaces(): Promise<StoredWorkspace[]> {\n\tconst r = await load();\n\treturn [...r.workspaces];\n}\n\nexport async function getWorkspace(id: string): Promise<StoredWorkspace | undefined> {\n\tconst r = await load();\n\treturn r.workspaces.find((w) => w.id === id);\n}\n\nexport async function getWorkspaceByPath(path: string): Promise<StoredWorkspace | undefined> {\n\tconst r = await load();\n\treturn r.workspaces.find((w) => w.path === path);\n}\n\nexport async function addWorkspace(input: { path: string; name: string }): Promise<StoredWorkspace> {\n\tconst r = await load();\n\tconst existing = r.workspaces.find((w) => w.path === input.path);\n\tif (existing) return existing;\n\tconst ws: StoredWorkspace = {\n\t\tid: randomUUID(),\n\t\tname: input.name,\n\t\tpath: input.path,\n\t\taddedAt: new Date().toISOString(),\n\t};\n\tr.workspaces.push(ws);\n\tawait save();\n\treturn ws;\n}\n\nexport async function removeWorkspace(id: string): Promise<boolean> {\n\tconst r = await load();\n\tconst before = r.workspaces.length;\n\tr.workspaces = r.workspaces.filter((w) => w.id !== id);\n\tif (r.workspaces.length === before) return false;\n\tawait save();\n\treturn true;\n}\n","/**\n * Derive instrument-panel telemetry from a workspace directory.\n *\n * Two probes today:\n * - current git branch (`git rev-parse --abbrev-ref HEAD`)\n * - tracked-file count (`git ls-files --cached --others --exclude-standard`)\n *\n * Both probes fail silently — non-git directories, missing `git` binary, or\n * broken repos all collapse to `null`. The UI fades the slot when both are\n * null, so a failure looks identical to \"not implemented yet\" from the\n * operator's perspective.\n *\n * In-memory 30-second TTL cache keyed by absolute path so the list endpoint\n * doesn't fork two child processes per workspace on every refresh. The TTL is\n * a deliberate trade: branch changes made outside the app will surface\n * within 30 seconds, which is the same staleness operators tolerate from\n * sidebar listings elsewhere.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { Workspace } from \"@pi-pilot/shared\";\nimport type { StoredWorkspace } from \"./workspace-registry.ts\";\n\nconst exec = promisify(execFile);\n\ninterface StatsCacheEntry {\n\tgitBranch: string | null;\n\tfileCount: number | null;\n\texpiresAt: number;\n}\n\nconst CACHE_TTL_MS = 30_000;\nconst cache = new Map<string, StatsCacheEntry>();\n\n/**\n * In-flight probes per path. Prevents the herd: if `enrichWorkspace` is called\n * twice for the same path before the first probe completes, both await the\n * same promise rather than each spawning two child processes.\n */\nconst inflight = new Map<string, Promise<StatsCacheEntry>>();\n\n/** Wrap a `StoredWorkspace` with derived stats. Never throws — failed probes\n * surface as nulls. */\nexport async function enrichWorkspace(ws: StoredWorkspace): Promise<Workspace> {\n\tconst stats = await getStats(ws.path);\n\treturn {\n\t\tid: ws.id,\n\t\tname: ws.name,\n\t\tpath: ws.path,\n\t\taddedAt: ws.addedAt,\n\t\tgitBranch: stats.gitBranch,\n\t\tfileCount: stats.fileCount,\n\t};\n}\n\nasync function getStats(path: string): Promise<StatsCacheEntry> {\n\tconst now = Date.now();\n\tconst cached = cache.get(path);\n\tif (cached && cached.expiresAt > now) return cached;\n\n\tconst pending = inflight.get(path);\n\tif (pending) return pending;\n\n\tconst probe = probeStats(path)\n\t\t.then((stats) => {\n\t\t\tconst entry: StatsCacheEntry = {\n\t\t\t\t...stats,\n\t\t\t\texpiresAt: Date.now() + CACHE_TTL_MS,\n\t\t\t};\n\t\t\tcache.set(path, entry);\n\t\t\treturn entry;\n\t\t})\n\t\t.finally(() => {\n\t\t\tinflight.delete(path);\n\t\t});\n\n\tinflight.set(path, probe);\n\treturn probe;\n}\n\nasync function probeStats(\n\tpath: string,\n): Promise<{ gitBranch: string | null; fileCount: number | null }> {\n\t// Run both probes in parallel; either may fail independently.\n\tconst [branchResult, filesResult] = await Promise.allSettled([\n\t\trunGit(path, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]),\n\t\trunGit(path, [\n\t\t\t\"ls-files\",\n\t\t\t\"--cached\",\n\t\t\t\"--others\",\n\t\t\t\"--exclude-standard\",\n\t\t]),\n\t]);\n\n\tlet gitBranch: string | null = null;\n\tif (branchResult.status === \"fulfilled\") {\n\t\tconst out = branchResult.value.trim();\n\t\t// `HEAD` means detached HEAD; show the shortened commit instead, but\n\t\t// keep it terse so it still fits the BRANCH slot.\n\t\tif (out && out !== \"HEAD\") {\n\t\t\tgitBranch = out;\n\t\t} else if (out === \"HEAD\") {\n\t\t\t// Try to grab the short SHA for the detached case.\n\t\t\ttry {\n\t\t\t\tconst sha = (await runGit(path, [\"rev-parse\", \"--short\", \"HEAD\"])).trim();\n\t\t\t\tgitBranch = sha ? `@${sha}` : null;\n\t\t\t} catch {\n\t\t\t\tgitBranch = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tlet fileCount: number | null = null;\n\tif (filesResult.status === \"fulfilled\") {\n\t\tconst out = filesResult.value;\n\t\tif (out) {\n\t\t\t// `git ls-files` prints one path per line. Count non-empty lines so\n\t\t\t// a single trailing newline doesn't add a phantom file.\n\t\t\tlet n = 0;\n\t\t\tfor (let i = 0; i < out.length; i++) {\n\t\t\t\tif (out.charCodeAt(i) === 0x0a) n++;\n\t\t\t}\n\t\t\t// Add 1 when the last line doesn't end in a newline (rare).\n\t\t\tif (out.length > 0 && out.charCodeAt(out.length - 1) !== 0x0a) n++;\n\t\t\tfileCount = n;\n\t\t} else {\n\t\t\tfileCount = 0;\n\t\t}\n\t}\n\n\treturn { gitBranch, fileCount };\n}\n\n/**\n * Run `git` with the given args in `cwd`. Resolves with stdout on exit code 0.\n * 2-second timeout — slow probes are treated as failures rather than blocking\n * the list endpoint. Output capped at 5MB so a pathologically huge repo can't\n * OOM the server.\n */\nasync function runGit(cwd: string, args: string[]): Promise<string> {\n\tconst { stdout } = await exec(\"git\", args, {\n\t\tcwd,\n\t\ttimeout: 2000,\n\t\tmaxBuffer: 5 * 1024 * 1024,\n\t\t// Don't inherit GIT_DIR / GIT_WORK_TREE from the server process —\n\t\t// could leak the server's own repo into a workspace probe.\n\t\tenv: { ...process.env, GIT_DIR: undefined, GIT_WORK_TREE: undefined },\n\t});\n\treturn stdout;\n}\n","/**\n * WorkspaceManager — lazy lifecycle of one AgentSessionRuntime per workspace.\n *\n * Invariants:\n * - At most one runtime per workspaceId at any time.\n * - The runtime's cwd matches the workspace's path.\n * - Callers must call `dispose(id)` (or `disposeAll()` on shutdown) — we\n * don't currently idle-evict.\n *\n * NOT thread-safe across concurrent `getOrCreate` calls for the same id;\n * Node is single-threaded so this is fine, but be aware if you ever add\n * worker_threads.\n */\n\nimport { unlink } from \"node:fs/promises\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport type { WebSocket } from \"ws\";\nimport {\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionRuntime,\n\tcreateAgentSessionServices,\n\tgetAgentDir,\n\tSessionManager,\n\ttype ExtensionError,\n\ttype SessionInfo,\n} from \"@earendil-works/pi-coding-agent\";\nimport { HttpError } from \"./storage/resource-writer.ts\";\nimport { getWorkspace } from \"./storage/workspace-registry.ts\";\nimport { builtinExtensionFactories } from \"./extensions/index.ts\";\nimport { reconcileAfterRestart } from \"./extensions/ask_user/cleanup.ts\";\nimport {\n\tcancelPendingExcept,\n\tcancelPendingForSession,\n} from \"./extensions/ask_user/registry.ts\";\nimport { shouldForwardDetails } from \"./ws/bridge.ts\";\nimport type {\n\tSessionSummary,\n\tHistoryItem,\n\tHistoryResponse,\n\tWsExtensionError,\n\tWsServerMessage,\n} from \"@pi-pilot/shared\";\nimport { ExtensionUIBridge } from \"./ws/extension-ui.ts\";\n\n/**\n * Two-tier extension stance:\n *\n * 1. Third-party / disk pi extensions are OFF by default. The pi\n * extension system is TUI-native (renderers, ANSI themes, modal\n * dialogs, keyboard overlays) and cannot be bridged to the Web.\n * `EXTENSIONS_ENABLED` (env: `PI_PILOT_ENABLE_EXTENSIONS=1`) is the\n * escape hatch — even then no `ctx.ui.*` is bridged, so extensions\n * that only register tools / slash commands work and anything\n * UI-driven appears broken. Exported so `/api/resources` can show\n * this state in the panel.\n *\n * 2. pi-pilot's own first-party builtin extensions ARE loaded\n * unconditionally via `extensionFactories` (see `extensions/`).\n * That channel is independent of disk scanning, so they ship with\n * pi-pilot regardless of the env flag. These are not \"plugins\" in\n * the third-party sense — they're features of pi-pilot that happen\n * to use the extension API to get richer registration hooks\n * (`pi.registerTool`, `pi.registerCommand`, lifecycle events).\n */\nexport const EXTENSIONS_ENABLED = process.env.PI_PILOT_ENABLE_EXTENSIONS === \"1\";\n\n/**\n * Build a fresh runtime bound to `cwd`. Used both for initial creation and\n * for in-place session replacement (newSession / switchSession / fork).\n */\nconst createRuntime: CreateAgentSessionRuntimeFactory = async ({\n\tcwd,\n\tsessionManager,\n\tsessionStartEvent,\n}) => {\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tresourceLoaderOptions: {\n\t\t\tnoExtensions: !EXTENSIONS_ENABLED,\n\t\t\textensionFactories: builtinExtensionFactories,\n\t\t},\n\t});\n\tconst sessionResult = await createAgentSessionFromServices({\n\t\tservices,\n\t\tsessionManager,\n\t\tsessionStartEvent,\n\t});\n\treturn {\n\t\t...sessionResult,\n\t\tservices,\n\t\tdiagnostics: services.diagnostics,\n\t};\n};\n\ninterface WorkspaceState {\n\truntime: AgentSessionRuntime;\n\tbridge: ExtensionUIBridge;\n}\n\nclass WorkspaceManager {\n\tprivate readonly states = new Map<string, WorkspaceState>();\n\t/**\n\t * Subscribers live independently of `states` so the hub can register a\n\t * WebSocket *before* `getOrCreate` triggers a runtime build (which may\n\t * fire `session_start` synchronously, and any UI request from a\n\t * session_start handler would otherwise broadcast to an empty set).\n\t */\n\tprivate readonly subscribers = new Map<string, Set<WebSocket>>();\n\t/** Per-workspace lock to serialize concurrent creations. */\n\tprivate readonly pending = new Map<string, Promise<WorkspaceState>>();\n\tprivate readonly rebindListeners = new Map<string, Set<() => void>>();\n\n\tprivate getOrCreateSubscriberSet(workspaceId: string): Set<WebSocket> {\n\t\tlet set = this.subscribers.get(workspaceId);\n\t\tif (!set) {\n\t\t\tset = new Set();\n\t\t\tthis.subscribers.set(workspaceId, set);\n\t\t}\n\t\treturn set;\n\t}\n\n\tasync getOrCreate(workspaceId: string): Promise<AgentSessionRuntime> {\n\t\tconst existing = this.states.get(workspaceId);\n\t\tif (existing) return existing.runtime;\n\n\t\tconst inflight = this.pending.get(workspaceId);\n\t\tif (inflight) return (await inflight).runtime;\n\n\t\tconst p = this.build(workspaceId);\n\t\tthis.pending.set(workspaceId, p);\n\t\ttry {\n\t\t\tconst state = await p;\n\t\t\tthis.states.set(workspaceId, state);\n\t\t\treturn state.runtime;\n\t\t} finally {\n\t\t\tthis.pending.delete(workspaceId);\n\t\t}\n\t}\n\n\tprivate async build(workspaceId: string): Promise<WorkspaceState> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\n\t\t// SessionManager.continueRecent → opens the most recently used session\n\t\t// in this cwd, or starts a new one if none exists. Matches what `pi`\n\t\t// itself does on launch.\n\t\tconst sessionManager = SessionManager.continueRecent(ws.path);\n\n\t\tconst runtime = await createAgentSessionRuntime(createRuntime, {\n\t\t\tcwd: ws.path,\n\t\t\tagentDir: getAgentDir(),\n\t\t\tsessionManager,\n\t\t});\n\n\t\t// Subscriber set is owned by the manager so it can pre-exist any\n\t\t// runtime build (extensions may fire `onError` from `session_start`\n\t\t// before any client has subscribed). The bridge itself is stateless\n\t\t// — see `ws/extension-ui.ts` for why every method is a no-op.\n\t\tconst subscribers = this.getOrCreateSubscriberSet(workspaceId);\n\t\tconst bridge = new ExtensionUIBridge();\n\n\t\t// `onError` fires whenever an extension event handler throws (or pi\n\t\t// rejects a registration). We forward to every subscriber so the user\n\t\t// sees the failure as a toast instead of having it disappear into\n\t\t// server logs. Re-bound on every session replacement below.\n\t\tconst onError = (err: ExtensionError) => {\n\t\t\tconst msg: WsExtensionError = {\n\t\t\t\ttype: \"extension_error\",\n\t\t\t\tworkspaceId,\n\t\t\t\textensionPath: err.extensionPath,\n\t\t\t\tevent: err.event,\n\t\t\t\tmessage: err.error,\n\t\t\t};\n\t\t\tbroadcastTo(subscribers, msg);\n\t\t\t// Mirror to the server log so the stack survives even if every\n\t\t\t// subscriber is gone (no tab open, reconnect window, …).\n\t\t\tconsole.error(\n\t\t\t\t`[ext-error] ${workspaceId} ${err.extensionPath}@${err.event}: ${err.error}` +\n\t\t\t\t\t(err.stack ? `\\n${err.stack}` : \"\"),\n\t\t\t);\n\t\t};\n\n\t\t// Bind initially, and re-bind on every session replacement so the\n\t\t// new `AgentSession` instance also sees our UI context.\n\t\tawait runtime.session.bindExtensions({ uiContext: bridge, onError });\n\t\t// Restart cleanup: write a customMessage explaining any dangling\n\t\t// `ask_user` toolCalls left over from a previous server crash.\n\t\t// See extensions/ask_user/cleanup.ts for the why.\n\t\tsafeReconcileAskUser(workspaceId, runtime.session.sessionManager);\n\t\truntime.setRebindSession(async () => {\n\t\t\tawait runtime.session.bindExtensions({ uiContext: bridge, onError });\n\t\t\t// pi does not abort in-flight tool signals on session switch\n\t\t\t// (Spike Q3), so any `ask_user` Promise bound to the old\n\t\t\t// session would hang forever. Release them here — anything\n\t\t\t// whose sessionFile doesn't match the freshly-bound session\n\t\t\t// gets rejected. The new session may itself have post-restart\n\t\t\t// dangling calls; reconcile that too before notifying.\n\t\t\tcancelPendingExcept(runtime.session.sessionFile ?? null);\n\t\t\tsafeReconcileAskUser(workspaceId, runtime.session.sessionManager);\n\t\t\tthis.notifySessionReplaced(workspaceId);\n\t\t});\n\n\t\treturn { runtime, bridge };\n\t}\n\n\tget(workspaceId: string): AgentSessionRuntime | undefined {\n\t\treturn this.states.get(workspaceId)?.runtime;\n\t}\n\n\t/**\n\t * Register a WS connection as a subscriber for `workspaceId`. Safe to\n\t * call before `getOrCreate`; the set is lazily created so the bridge,\n\t * when later built, sees the same Set instance and any pre-existing\n\t * subscribers.\n\t */\n\taddSubscriber(workspaceId: string, ws: WebSocket): void {\n\t\tthis.getOrCreateSubscriberSet(workspaceId).add(ws);\n\t}\n\n\tremoveSubscriber(workspaceId: string, ws: WebSocket): void {\n\t\tconst set = this.subscribers.get(workspaceId);\n\t\tif (!set) return;\n\t\tset.delete(ws);\n\t\tif (set.size === 0) this.subscribers.delete(workspaceId);\n\t}\n\n\t/**\n\t * Fan a server-initiated message out to every WS subscribed to the\n\t * workspace. Used by API handlers that mutate runtime state and need\n\t * to refresh derived snapshots (e.g. `context_usage` after `setModel`,\n\t * which pi's event stream doesn't surface unless thinking-level also\n\t * clamps).\n\t */\n\tbroadcast(workspaceId: string, msg: WsServerMessage): void {\n\t\tconst set = this.subscribers.get(workspaceId);\n\t\tif (!set || set.size === 0) return;\n\t\tbroadcastTo(set, msg);\n\t}\n\n\tonSessionReplaced(workspaceId: string, listener: () => void): () => void {\n\t\tlet listeners = this.rebindListeners.get(workspaceId);\n\t\tif (!listeners) {\n\t\t\tlisteners = new Set();\n\t\t\tthis.rebindListeners.set(workspaceId, listeners);\n\t\t}\n\t\tlisteners.add(listener);\n\t\treturn () => {\n\t\t\tconst current = this.rebindListeners.get(workspaceId);\n\t\t\tif (!current) return;\n\t\t\tcurrent.delete(listener);\n\t\t\tif (current.size === 0) {\n\t\t\t\tthis.rebindListeners.delete(workspaceId);\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate notifySessionReplaced(workspaceId: string): void {\n\t\tconst listeners = this.rebindListeners.get(workspaceId);\n\t\tif (!listeners) return;\n\t\tfor (const listener of [...listeners]) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch (e) {\n\t\t\t\tconsole.error(`[wm] rebind listener for ${workspaceId} failed:`, e);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync listSessions(workspaceId: string): Promise<SessionSummary[]> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\treturn sessions\n\t\t\t.slice()\n\t\t\t.sort((a, b) => b.modified.getTime() - a.modified.getTime())\n\t\t\t.map(toSessionSummary);\n\t}\n\n\tgetSessionHistory(workspaceId: string, sessionPath?: string): HistoryResponse {\n\t\tconst runtime = this.states.get(workspaceId)?.runtime;\n\t\tif (!runtime) return { items: [], isStreaming: false };\n\n\t\t// If the caller specifies a sessionPath, verify it matches the active\n\t\t// runtime. This prevents returning history from the wrong session when\n\t\t// the server restarted and picked a different \"recent\" session.\n\t\tif (sessionPath) {\n\t\t\tconst activeFile = runtime.session.sessionFile\n\t\t\t\t? resolve(runtime.session.sessionFile)\n\t\t\t\t: undefined;\n\t\t\tif (activeFile !== resolve(sessionPath)) {\n\t\t\t\treturn { items: [], isStreaming: false };\n\t\t\t}\n\t\t}\n\n\t\tconst isStreaming: boolean = runtime.session.isStreaming ?? false;\n\n\t\tconst branch = runtime.session.sessionManager.getBranch();\n\t\tconst items: HistoryItem[] = [];\n\t\t// toolCall args live in assistant messages; collect them so we can\n\t\t// attach them to the corresponding toolResult items.\n\t\tconst argsByCallId = new Map<string, string>();\n\n\t\tfor (const entry of branch) {\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tconst msg = entry.message;\n\t\t\tconst role = (msg as { role: string }).role;\n\n\t\t\tif (role === \"user\") {\n\t\t\t\tconst text = extractUserText(msg as { content: unknown });\n\t\t\t\tif (text) items.push({ kind: \"user\", text });\n\t\t\t} else if (role === \"assistant\") {\n\t\t\t\tconst { text, thinking, toolCalls } = extractAssistantContent(\n\t\t\t\t\tmsg as { content: unknown[] },\n\t\t\t\t);\n\t\t\t\tfor (const tc of toolCalls) {\n\t\t\t\t\targsByCallId.set(tc.id, tc.args);\n\t\t\t\t}\n\t\t\t\tif (text || thinking) items.push({ kind: \"assistant\", text, thinking });\n\t\t\t} else if (role === \"toolResult\") {\n\t\t\t\tconst tr = msg as {\n\t\t\t\t\ttoolCallId: string;\n\t\t\t\t\ttoolName: string;\n\t\t\t\t\tcontent: unknown[];\n\t\t\t\t\tisError: boolean;\n\t\t\t\t\tdetails?: unknown;\n\t\t\t\t};\n\t\t\t\titems.push({\n\t\t\t\t\tkind: \"tool\",\n\t\t\t\t\ttoolCallId: tr.toolCallId,\n\t\t\t\t\ttoolName: tr.toolName,\n\t\t\t\t\targs: argsByCallId.get(tr.toolCallId) ?? \"\",\n\t\t\t\t\ttext: extractContentText(tr.content),\n\t\t\t\t\tisError: tr.isError,\n\t\t\t\t\t// Mirror live wire whitelist (bridge.ts): only ship details\n\t\t\t\t\t// for tools whose cards need the structured shape, so the\n\t\t\t\t\t// history payload stays small for bash / edit / read.\n\t\t\t\t\t...(shouldForwardDetails(tr.toolName) && tr.details !== undefined\n\t\t\t\t\t\t? { details: tr.details }\n\t\t\t\t\t\t: {}),\n\t\t\t\t});\n\t\t\t} else if (role === \"bashExecution\") {\n\t\t\t\tconst be = msg as {\n\t\t\t\t\tcommand: string;\n\t\t\t\t\toutput: string;\n\t\t\t\t\texitCode: number | undefined;\n\t\t\t\t};\n\t\t\t\titems.push({\n\t\t\t\t\tkind: \"bash\",\n\t\t\t\t\tcommand: be.command,\n\t\t\t\t\toutput: be.output,\n\t\t\t\t\texitCode: be.exitCode,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn { items, isStreaming };\n\t}\n\n\t/**\n\t * Delete a session JSONL file belonging to this workspace.\n\t *\n\t * Errors are tagged with HTTP semantics via HttpError so the route layer\n\t * can map them to the right status code:\n\t * - 400: sessionPath not absolute\n\t * - 404: workspace gone, or session not in this workspace's list\n\t * - 409: file is the currently-active session (caller must switch first)\n\t *\n\t * Idempotent on ENOENT: if the file is missing at unlink time (e.g. a\n\t * concurrent external delete between list and unlink), we treat it as\n\t * success — the goal state has been reached.\n\t */\n\tasync deleteSession(workspaceId: string, sessionPath: string): Promise<void> {\n\t\t// Defensive re-check even though the route already validates the\n\t\t// workspace — keeps the manager API safe for any caller (extensions,\n\t\t// tests). Mirrors listSessions/switchSession.\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new HttpError(404, `Workspace not found: ${workspaceId}`);\n\t\tif (!isAbsolute(sessionPath)) {\n\t\t\tthrow new HttpError(400, \"Session path must be absolute\");\n\t\t}\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\tconst resolved = resolve(sessionPath);\n\t\tconst target = sessions.find((session) => resolve(session.path) === resolved);\n\t\tif (!target) {\n\t\t\tthrow new HttpError(404, `Session not found: ${sessionPath}`);\n\t\t}\n\n\t\tconst runtime = this.states.get(workspaceId)?.runtime;\n\t\tconst activePath = runtime?.session.sessionFile\n\t\t\t? resolve(runtime.session.sessionFile)\n\t\t\t: undefined;\n\t\tif (activePath === resolved) {\n\t\t\tthrow new HttpError(\n\t\t\t\t409,\n\t\t\t\t\"Cannot delete the currently active session — switch to another session first\",\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tawait unlink(resolved);\n\t\t} catch (err) {\n\t\t\tif ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[wm] deleteSession: ${resolved} was already gone at unlink time`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync switchSession(workspaceId: string, sessionPath: string): Promise<boolean> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\t\tif (!isAbsolute(sessionPath)) {\n\t\t\tthrow new Error(\"Session path must be absolute\");\n\t\t}\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\tconst resolved = resolve(sessionPath);\n\t\tconst target = sessions.find((session) => resolve(session.path) === resolved);\n\t\tif (!target) {\n\t\t\tthrow new Error(`Session not found: ${sessionPath}`);\n\t\t}\n\n\t\tconst runtime = await this.getOrCreate(workspaceId);\n\t\tconst currentPath = runtime.session.sessionFile ? resolve(runtime.session.sessionFile) : undefined;\n\t\tif (currentPath === resolved) return false;\n\t\tif (runtime.session.isStreaming) {\n\t\t\tthrow new Error(\"Cannot switch sessions while the agent is streaming\");\n\t\t}\n\n\t\tconst result = await runtime.switchSession(resolved, { cwdOverride: ws.path });\n\t\treturn !result.cancelled;\n\t}\n\n\tasync dispose(workspaceId: string): Promise<void> {\n\t\tconst state = this.states.get(workspaceId);\n\t\tif (!state) return;\n\t\tthis.states.delete(workspaceId);\n\t\tthis.rebindListeners.delete(workspaceId);\n\t\tthis.subscribers.delete(workspaceId);\n\t\t// Release any ask_user Promises bound to this workspace's active\n\t\t// session *before* disposing the runtime, so the rejected\n\t\t// `execute()` Promises have a clean signal/timeout teardown\n\t\t// instead of racing the runtime's own dispose.\n\t\tcancelPendingForSession(state.runtime.session.sessionFile ?? null);\n\t\ttry {\n\t\t\tstate.bridge.dispose();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[wm] dispose bridge ${workspaceId} failed:`, e);\n\t\t}\n\t\ttry {\n\t\t\tstate.runtime.session.dispose();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[wm] dispose ${workspaceId} failed:`, e);\n\t\t}\n\t}\n\n\tasync disposeAll(): Promise<void> {\n\t\t// Drain in-flight builds first so any runtime that's mid-`build()`\n\t\t// when shutdown lands doesn't leak. `getOrCreate` writes to `states`\n\t\t// from its own continuation on the same promise; because it\n\t\t// registered its `await` before us, microtask ordering guarantees\n\t\t// that write runs before our snapshot below. Failed builds never\n\t\t// reached `states` — `allSettled` lets us ignore them.\n\t\tawait Promise.allSettled([...this.pending.values()]);\n\t\tconst ids = [...this.states.keys()];\n\t\tawait Promise.all(ids.map((id) => this.dispose(id)));\n\t}\n}\n\n/**\n * Run ask_user post-restart reconciliation, swallowing any error so a\n * cleanup failure doesn't brick the workspace. The worst-case fallout\n * of skipping cleanup is the LLM seeing an unmatched ask_user toolCall\n * with no explanation — it will typically ignore or re-call; not\n * load-bearing.\n */\nfunction safeReconcileAskUser(workspaceId: string, sm: SessionManager): void {\n\ttry {\n\t\treconcileAfterRestart(sm);\n\t} catch (e) {\n\t\tconsole.error(`[wm] ask_user cleanup for ${workspaceId} failed:`, e);\n\t}\n}\n\nfunction toSessionSummary(info: SessionInfo): SessionSummary {\n\tconst preview = info.firstMessage.replace(/\\s+/g, \" \").trim();\n\treturn {\n\t\tpath: info.path,\n\t\tname: info.name,\n\t\tupdatedAt: info.modified.toISOString(),\n\t\tpreview: preview ? preview.slice(0, 160) : undefined,\n\t};\n}\n\n/** Extract text from a user message (string or content blocks). */\nfunction extractUserText(msg: { content: unknown }): string {\n\tif (typeof msg.content === \"string\") return msg.content;\n\treturn extractContentText(msg.content as unknown[]);\n}\n\n/** Extract text + thinking + toolCalls from an assistant message's content blocks. */\nfunction extractAssistantContent(msg: { content: unknown[] }): {\n\ttext: string;\n\tthinking: string;\n\ttoolCalls: { id: string; args: string }[];\n} {\n\tconst textParts: string[] = [];\n\tconst thinkingParts: string[] = [];\n\tconst toolCalls: { id: string; args: string }[] = [];\n\tfor (const block of msg.content ?? []) {\n\t\tif (!block || typeof block !== \"object\") continue;\n\t\tconst b = block as { type?: string; text?: string; thinking?: string; id?: string; arguments?: unknown };\n\t\tif (b.type === \"text\" && typeof b.text === \"string\") textParts.push(b.text);\n\t\telse if (b.type === \"thinking\" && typeof b.thinking === \"string\") thinkingParts.push(b.thinking);\n\t\telse if (b.type === \"toolCall\" && typeof b.id === \"string\") {\n\t\t\ttoolCalls.push({\n\t\t\t\tid: b.id,\n\t\t\t\targs: b.arguments != null ? JSON.stringify(b.arguments) : \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn { text: textParts.join(\"\"), thinking: thinkingParts.join(\"\"), toolCalls };\n}\n\n/** Extract text from a content block array (TextContent items). */\nfunction extractContentText(content: unknown[]): string {\n\tif (!Array.isArray(content)) return \"\";\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n\t\t\tconst text = (block as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.join(\"\");\n}\n\nexport const workspaceManager = new WorkspaceManager();\n\n/**\n * Best-effort fan-out of a server-initiated message to every connection\n * subscribed to a workspace. Mirrors `ExtensionUIBridge.broadcast` but\n * lives at the manager level so non-UI events (extension errors today;\n * extension_error variants later) can reuse it without needing a bridge\n * reference.\n */\nfunction broadcastTo(subscribers: Set<WebSocket>, msg: WsServerMessage): void {\n\tconst wire = JSON.stringify(msg);\n\tfor (const ws of subscribers) {\n\t\tif (ws.readyState !== ws.OPEN) continue;\n\t\ttry {\n\t\t\tws.send(wire);\n\t\t} catch {\n\t\t\t// Bad sockets drop on the next close event.\n\t\t}\n\t}\n}\n","/**\n * TypeBox schemas for the update_plan tool.\n *\n * Kept separate from factory.ts so future plan-related code (web shape\n * mirrors via shared types, future tooling) can import the parameter\n * shape without pulling pi runtime types into the dependency graph.\n */\n\nimport { Type, type Static } from \"typebox\";\n\nexport const planItemStatusSchema = Type.Union([\n\tType.Literal(\"pending\"),\n\tType.Literal(\"in_progress\"),\n\tType.Literal(\"completed\"),\n]);\n\nexport const planItemSchema = Type.Object({\n\tid: Type.String({ description: \"Short stable identifier for the item (kebab-case, e.g. \\\"wire-factory\\\").\" }),\n\ttitle: Type.String({ description: \"One-line description of the step. Specific and verifiable.\" }),\n\tstatus: planItemStatusSchema,\n\tnote: Type.Optional(Type.String({ description: \"Optional short context — blocker, decision, or follow-up.\" })),\n});\n\nexport const updatePlanParamsSchema = Type.Object({\n\titems: Type.Array(planItemSchema, {\n\t\tdescription: \"The full ordered plan. Always send the complete list; previous tool calls are not merged.\",\n\t}),\n});\n\nexport type PlanItem = Static<typeof planItemSchema>;\nexport type UpdatePlanParams = Static<typeof updatePlanParamsSchema>;\n","/**\n * pi-pilot's first-party plan extension.\n *\n * Registers:\n * - `update_plan` tool: agent emits the full plan (id/title/status[/note])\n * as one tool call to publish or refresh it. State machine: pending →\n * in_progress → completed. The full list is always sent — there is no\n * merging by id across calls; the most recent successful tool call IS\n * the current plan.\n * - `/plan` slash command: user-facing entry point. Expands into a user\n * message asking the agent to draft a plan and call update_plan\n * before starting work. The literal `/plan` text never reaches the\n * LLM — pi consumes the slash invocation and the handler injects the\n * rewritten message via `pi.sendUserMessage`.\n *\n * Why it's a builtin extension and not `customTools`: registering through\n * the extension API gives us the slash command (customTools can't) and\n * groups the tool + command + prompt guidelines in one place. The factory\n * runs inside the SDK's `extensionFactories` channel, which is independent\n * of the `noExtensions` disk-scan flag — so this loads even when the user\n * has third-party pi extensions turned off (the default for pi-pilot).\n *\n * Plan state is implicitly persisted in the session JSONL as tool call\n * history, so fork / new_session / replay get correct semantics for free;\n * we deliberately don't track plan state on our side.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { updatePlanParamsSchema } from \"./schema.ts\";\n\nexport const planExtensionFactory: ExtensionFactory = (pi) => {\n\tpi.registerTool({\n\t\tname: \"update_plan\",\n\t\tlabel: \"Plan\",\n\t\tdescription:\n\t\t\t\"Publish or refresh the working plan for the current task. Use for any task that takes 3+ discrete steps. Always send the full ordered list; previous calls are replaced, not merged.\",\n\t\tparameters: updatePlanParamsSchema,\n\t\tpromptSnippet:\n\t\t\t\"update_plan: maintain a live checklist for multi-step tasks; update statuses as you progress.\",\n\t\tpromptGuidelines: [\n\t\t\t\"For tasks with 3+ discrete steps, call update_plan once with the full list before starting work.\",\n\t\t\t\"After completing each step, call update_plan again with refreshed statuses, then continue immediately — do not pause the turn just because the plan was updated.\",\n\t\t\t\"Plan items should be specific and verifiable (e.g. \\\"Add typebox dependency to packages/server\\\"), not vague (\\\"Set up infrastructure\\\").\",\n\t\t\t\"Exactly one item should be in_progress at a time. Mark completed only when the work is actually done.\",\n\t\t],\n\t\texecute: async (_toolCallId, params) => ({\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext: `Plan updated (${params.items.length} item${params.items.length === 1 ? \"\" : \"s\"}).`,\n\t\t\t\t},\n\t\t\t],\n\t\t\tdetails: params,\n\t\t}),\n\t});\n\n\tpi.registerCommand(\"plan\", {\n\t\tdescription: \"Ask the agent to draft a plan for the current task.\",\n\t\thandler: async (args) => {\n\t\t\tconst task = args.trim();\n\t\t\tconst message = task\n\t\t\t\t? `Draft a plan for: ${task}. Use the update_plan tool to publish it before starting any work.`\n\t\t\t\t: \"Draft a plan for the current task. Use the update_plan tool to publish it before starting any work.\";\n\t\t\tpi.sendUserMessage(message);\n\t\t},\n\t});\n};\n","/**\n * TypeBox schemas for the ask_user tool.\n *\n * Kept separate from factory.ts so the registry and any future shared\n * mirrors (web shape via shared types, snapshot helpers) can import\n * parameter / answer shapes without pulling pi runtime types into the\n * dependency graph.\n *\n * The TypeBox version is forced workspace-wide via `pnpm.overrides` —\n * the `Kind` symbols here must come from the same module instance pi's\n * runtime validates against, otherwise tool registration will reject\n * our schema with a noisy mismatch error.\n */\n\nimport { Type, type Static } from \"typebox\";\n\nexport const askUserOptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tdescription: \"Short, distinct choice (1–5 words). User-facing.\",\n\t}),\n\tdescription: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Optional one-line clarification of what this choice means or implies.\",\n\t\t}),\n\t),\n});\n\n/**\n * `header` is capped at 12 chars because the web renders it as a chip\n * next to the question; longer strings get truncated visually anyway.\n * `options` is 2–4 to match common-sense UX (single-choice dialogs\n * become noisy past 4; less than 2 isn't a choice).\n * `timeoutSec` floor is 5s — anything shorter than human reaction time\n * is almost certainly a mistake.\n */\nexport const askUserParamsSchema = Type.Object({\n\tquestion: Type.String({\n\t\tdescription:\n\t\t\t\"Full question, specific and ending with '?'. Avoid yes/no when options can be more concrete.\",\n\t}),\n\theader: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: 12,\n\t\t\tdescription:\n\t\t\t\t\"Optional short chip label (≤ 12 chars), e.g. 'Auth method'. Shown next to the question.\",\n\t\t}),\n\t),\n\toptions: Type.Array(askUserOptionSchema, {\n\t\tminItems: 2,\n\t\tmaxItems: 4,\n\t\tdescription:\n\t\t\t\"2–4 mutually exclusive choices (unless multiSelect is true). Recommended option first; append ' (Recommended)' to its label.\",\n\t}),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"If true, render checkboxes and allow multiple selections. Default false.\",\n\t\t}),\n\t),\n\tallowOther: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"If true, show a freeform 'Other' input as an escape hatch. Default true.\",\n\t\t}),\n\t),\n\ttimeoutSec: Type.Optional(\n\t\tType.Integer({\n\t\t\tminimum: 5,\n\t\t\tdescription:\n\t\t\t\t\"Auto-resolve as skip after this many seconds. Only set for time-sensitive questions where a stale answer would be worse than no answer.\",\n\t\t}),\n\t),\n});\n\nexport type AskUserOption = Static<typeof askUserOptionSchema>;\nexport type AskUserParams = Static<typeof askUserParamsSchema>;\n\n/**\n * Structured detail attached to the tool result. Mirrors the wire-side\n * `WsAnswerQuestion[\"answer\"]` union plus a `timeout` variant the\n * server may synthesize when the agent-supplied `timeoutSec` fires\n * before any client answered. The web reads this on history replay to\n * highlight the selected option(s) in the resolved card.\n */\nexport type AskUserAnswerDetails =\n\t| { kind: \"option\"; indices: number[]; labels: string[] }\n\t| { kind: \"other\"; text: string }\n\t| { kind: \"skip\" }\n\t| { kind: \"timeout\"; afterMs: number };\n","/**\n * Module-level registry of pending ask_user calls.\n *\n * Why module-level: pi's toolCallId is globally unique, so a single map\n * keyed by id is enough — we don't need a (workspaceId, id) tuple. This\n * keeps the factory signature compatible with `ExtensionFactory` (just\n * `(pi) => void`); the WS hub and the in-flight snapshot helper import\n * functions from this file to read / mutate the map.\n *\n * Lifecycle:\n * - `register()` called from `execute()` at tool start. Returns a\n * Promise that resolves when one of these happens:\n * 1. `resolveAnswer()` — client sent `answer_question`.\n * 2. timeoutSec fired — auto-resolves as `timeout`.\n * 3. signal abort (user pressed Stop) — execute() rejects.\n * 4. `cancelPendingExcept()` — session replaced under our feet.\n * 5. `cancelPendingForSession()` — workspace disposed.\n * - The Promise itself lives inside `execute()` (the factory owns\n * `new Promise(...)`); this module only owns the resolver/rejecter\n * side and the metadata needed for snapshot/cancel routing.\n *\n * Stale-answer policy: when `resolveAnswer()` is called with an\n * unknown toolCallId — already-answered by another tab, server-restart\n * cleanup ran, session replaced — we return `false` and the caller\n * (the WS hub) silently ignores. We never emit an error toast for this;\n * a duplicate answer is benign and the loser tab's UI catches up via\n * the normal `tool_execution_end` broadcast.\n */\n\nimport type { AskUserParams } from \"./schema.ts\";\nimport type { WsAnswerQuestion } from \"@pi-pilot/shared\";\n\nexport type Answer = WsAnswerQuestion[\"answer\"];\n\ninterface PendingEntry {\n\tresolve(answer: Answer): void;\n\treject(reason: Error): void;\n\t/** Original tool args — needed by `inFlightToolCallsSnapshot` to\n\t * replay a synthetic `tool_execution_start` on (re)subscribe. */\n\targs: AskUserParams;\n\t/** Absolute session JSONL path at register time. Used to route\n\t * cancels (session replacement) and snapshots (per-subscriber\n\t * filter). `null` for in-memory / unpersisted sessions. */\n\tsessionFile: string | null;\n}\n\nconst pending = new Map<string, PendingEntry>();\n\nexport function register(\n\ttoolCallId: string,\n\tentry: PendingEntry,\n): void {\n\tpending.set(toolCallId, entry);\n}\n\nexport function unregister(toolCallId: string): void {\n\tpending.delete(toolCallId);\n}\n\n/**\n * Resolve a pending call. Returns `false` when the toolCallId is\n * unknown or the sessionFile guard fails (stale answer from a tab\n * still bound to a replaced session).\n */\nexport function resolveAnswer(\n\ttoolCallId: string,\n\tanswer: Answer,\n\texpectedSessionFile: string | null,\n): boolean {\n\tconst entry = pending.get(toolCallId);\n\tif (!entry) return false;\n\tif (entry.sessionFile !== expectedSessionFile) return false;\n\tpending.delete(toolCallId);\n\tentry.resolve(answer);\n\treturn true;\n}\n\n/**\n * Reject every pending entry whose `sessionFile` does NOT equal\n * `keepSessionFile`. Called from the rebind callback after a session\n * replacement (newSession / fork / switchSession): pi does not abort\n * in-flight tool signals on session switch, so we have to actively\n * cancel anything bound to the now-defunct session, otherwise the\n * Promise inside `execute()` would hang forever.\n *\n * `keepSessionFile` is the file path of the freshly-bound session\n * (typically taken from `runtime.session.sessionFile` after bind).\n * `null` is a legitimate value — an unpersisted in-memory session\n * matches `null` and is kept.\n */\nexport function cancelPendingExcept(keepSessionFile: string | null): void {\n\tfor (const [id, entry] of pending) {\n\t\tif (entry.sessionFile === keepSessionFile) continue;\n\t\tpending.delete(id);\n\t\tentry.reject(new Error(\"Session replaced before answer arrived\"));\n\t}\n}\n\n/**\n * Reject every pending entry whose `sessionFile` equals the given\n * path. Called from workspace dispose; we know the runtime is going\n * away and the Promise must release.\n */\nexport function cancelPendingForSession(sessionFile: string | null): void {\n\tfor (const [id, entry] of pending) {\n\t\tif (entry.sessionFile !== sessionFile) continue;\n\t\tpending.delete(id);\n\t\tentry.reject(new Error(\"Workspace disposed\"));\n\t}\n}\n\n/**\n * Snapshot of pending entries for a session. Used by\n * `inFlightToolCallsSnapshot` to synthesize `tool_execution_start`\n * events when a client (re)subscribes mid-question — without this,\n * the question card vanishes after a tab reload because the original\n * `tool_execution_start` fired before the subscription existed.\n *\n * Returns a fresh array each call; safe for the caller to iterate\n * while the registry continues to mutate.\n */\nexport function snapshotForSession(\n\tsessionFile: string | null,\n): { toolCallId: string; args: AskUserParams }[] {\n\tconst out: { toolCallId: string; args: AskUserParams }[] = [];\n\tfor (const [id, entry] of pending) {\n\t\tif (entry.sessionFile !== sessionFile) continue;\n\t\tout.push({ toolCallId: id, args: entry.args });\n\t}\n\treturn out;\n}\n","/**\n * pi-pilot's second first-party builtin extension: ask_user.\n *\n * Registers a single tool, `ask_user`, that lets the agent pause and\n * ask the user a structured multiple-choice question. Unlike\n * `update_plan` (which is a near-instant echo), `ask_user.execute()`\n * blocks on a Promise stored in `./registry.ts` until one of:\n *\n * - the client posts a `WsAnswerQuestion` over the WS hub (the\n * normal path), or\n * - the agent-supplied `timeoutSec` fires (auto-skip), or\n * - `signal` aborts (user pressed Stop), or\n * - the registry's `cancelPending*` helpers reject us because the\n * session was replaced or the workspace disposed.\n *\n * The question itself is rendered on the web by reading `args` off the\n * normal `tool_execution_start` event — we do not add a parallel\n * \"question\" stream. The tool result text is the agent-facing summary\n * of what the user picked; `details` carries the structured form so\n * the web can highlight the chosen option on history replay.\n *\n * Why a custom tool instead of pi's `ctx.ui.select/input`: those route\n * through `ExtensionUIContext`, and pi-pilot's `ExtensionUIBridge` is\n * deliberately a no-op (TUI-only, can't be bridged to the Web). A\n * custom tool persists both the call and the result in the session\n * JSONL automatically, so fork / new_session / history replay get\n * correct semantics for free — same trick as `update_plan`.\n *\n * Restart cleanup: when pi loads a session whose tail is an unmatched\n * `ask_user` toolCall (server crashed while the Promise was pending,\n * so the toolResult never landed), `./cleanup.ts` writes a\n * `customMessage` that's visible to the LLM explaining the cancel.\n * pi SDK doesn't expose a way to forge a real `toolResult` role\n * message, so the LLM will see \"my unmatched toolCall + a user\n * message saying it was cancelled\" — acceptable; the explanation\n * keeps it from looping on a phantom in-flight call.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport {\n\taskUserParamsSchema,\n\ttype AskUserAnswerDetails,\n\ttype AskUserParams,\n} from \"./schema.ts\";\nimport {\n\tregister,\n\tunregister,\n\ttype Answer,\n} from \"./registry.ts\";\nimport type { AgentToolResult } from \"@earendil-works/pi-coding-agent\";\n\ntype AskUserResult = AgentToolResult<AskUserAnswerDetails>;\n\nexport const askUserExtensionFactory: ExtensionFactory = (pi) => {\n\tpi.registerTool({\n\t\tname: \"ask_user\",\n\t\tlabel: \"Ask user\",\n\t\tdescription:\n\t\t\t\"Pause and ask the user a structured multiple-choice question. Use ONLY for genuine forks where the user's preference materially changes the work (UI layout, library choice, scope boundary). Returns the user's selection (or 'skip' if they dismissed).\",\n\t\tparameters: askUserParamsSchema,\n\t\tpromptSnippet:\n\t\t\t\"ask_user: pause and ask the user a structured multiple-choice question when a decision is genuinely theirs.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Call ask_user only for genuine forks where the user's preference materially changes the work — UI layout, library choice, scope boundary. Don't ask for opinions you can defend yourself.\",\n\t\t\t\"Each ask_user call asks ONE question. If you have several, call it multiple times in order so the user sees one at a time.\",\n\t\t\t\"ask_user options must be distinct and mutually exclusive (unless multiSelect is true). 2–4 options; put the recommended option first and append ' (Recommended)' to its label.\",\n\t\t\t\"Don't follow up ask_user with 'Should I proceed?' — the answer already authorizes the next step.\",\n\t\t\t\"Only set ask_user's timeoutSec for time-sensitive questions where a stale answer would be worse than no answer; otherwise let the user take as long as they need.\",\n\t\t],\n\t\texecute: async (toolCallId, params, signal, _onUpdate, ctx) => {\n\t\t\tconst sessionFile = ctx.sessionManager.getSessionFile() ?? null;\n\t\t\tconst startedAt = Date.now();\n\n\t\t\tconst answer = await waitForAnswer({\n\t\t\t\ttoolCallId,\n\t\t\t\tparams,\n\t\t\t\tsessionFile,\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\treturn formatResult(params, answer, startedAt);\n\t\t},\n\t});\n};\n\ninterface WaitArgs {\n\ttoolCallId: string;\n\tparams: AskUserParams;\n\tsessionFile: string | null;\n\tsignal: AbortSignal | undefined;\n}\n\n/**\n * Wire the pending Promise + the three concurrent cancel paths\n * (client answer / timeout / signal abort). Every resolution path\n * unregisters before resolving so a late event can't double-fire.\n *\n * Returns either:\n * - the client's `Answer` (from `resolveAnswer` in registry), or\n * - a synthetic `{ kind: \"timeout\", afterMs }` when the\n * agent-supplied `timeoutSec` fires.\n *\n * Throws on signal abort or session-replacement cancel — those\n * propagate out of `execute()` as a normal tool error and pi records\n * an error toolResult on its own.\n */\nfunction waitForAnswer({\n\ttoolCallId,\n\tparams,\n\tsessionFile,\n\tsignal,\n}: WaitArgs): Promise<Answer | { kind: \"timeout\"; afterMs: number }> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tunregister(toolCallId);\n\t\t};\n\n\t\tconst finishOk = (a: Answer | { kind: \"timeout\"; afterMs: number }) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(a);\n\t\t};\n\n\t\tconst finishErr = (err: Error) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(err);\n\t\t};\n\n\t\tconst onAbort = () => finishErr(new Error(\"Aborted by user\"));\n\t\tif (signal?.aborted) {\n\t\t\tfinishErr(new Error(\"Aborted by user\"));\n\t\t\treturn;\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", onAbort);\n\n\t\tif (typeof params.timeoutSec === \"number\" && params.timeoutSec > 0) {\n\t\t\tconst ms = params.timeoutSec * 1000;\n\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\tfinishOk({ kind: \"timeout\", afterMs: ms });\n\t\t\t}, ms);\n\t\t}\n\n\t\tregister(toolCallId, {\n\t\t\targs: params,\n\t\t\tsessionFile,\n\t\t\tresolve: (answer) => finishOk(answer),\n\t\t\treject: (err) => finishErr(err),\n\t\t});\n\t});\n}\n\n/**\n * Build the agent-facing tool result. The `text` is what the LLM\n * sees in the toolResult content; the `details` is for the web's\n * history-replay rendering (highlighting which option chip is\n * selected). All four outcomes are non-error — even `skip` and\n * `timeout` — because we explicitly want the agent to use its best\n * judgement and keep going rather than apologize-and-re-ask.\n */\nfunction formatResult(\n\tparams: AskUserParams,\n\tanswer: Answer | { kind: \"timeout\"; afterMs: number },\n\tstartedAt: number,\n): AskUserResult {\n\tif (answer.kind === \"option\") {\n\t\tconst labels = answer.indices\n\t\t\t.map((i) => params.options[i]?.label)\n\t\t\t.filter((l): l is string => typeof l === \"string\");\n\t\tconst text =\n\t\t\tlabels.length === 1\n\t\t\t\t? `User selected: \"${labels[0]}\"${descriptionSuffix(params, answer.indices[0]!)}.`\n\t\t\t\t: `User selected: ${labels.map((l) => `\"${l}\"`).join(\", \")}.`;\n\t\tconst details: AskUserAnswerDetails = {\n\t\t\tkind: \"option\",\n\t\t\tindices: answer.indices,\n\t\t\tlabels,\n\t\t};\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t}\n\n\tif (answer.kind === \"other\") {\n\t\tconst text = `User answered (freeform): \"${answer.text}\"`;\n\t\tconst details: AskUserAnswerDetails = { kind: \"other\", text: answer.text };\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t}\n\n\tif (answer.kind === \"skip\") {\n\t\tconst text =\n\t\t\t\"User chose to skip this question. Use your best judgement and proceed; you may ask again later if it still matters.\";\n\t\tconst details: AskUserAnswerDetails = { kind: \"skip\" };\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t}\n\n\t// timeout\n\tconst waited = Math.round((Date.now() - startedAt) / 1000);\n\tconst text = `No answer within ${waited}s (agent-supplied timeout). Use your best judgement and proceed; you may ask again later if it still matters.`;\n\tconst details: AskUserAnswerDetails = {\n\t\tkind: \"timeout\",\n\t\tafterMs: answer.afterMs,\n\t};\n\treturn {\n\t\tcontent: [{ type: \"text\", text }],\n\t\tdetails,\n\t};\n}\n\nfunction descriptionSuffix(params: AskUserParams, index: number): string {\n\tconst desc = params.options[index]?.description;\n\treturn desc ? ` — ${desc}` : \"\";\n}\n","/**\n * Persistence for which first-party builtin extensions the user has turned off.\n *\n * pi-pilot's builtins (`plan`, `ask_user`) load via the SDK's `extensionFactories`\n * channel (see `extensions/index.ts`). Each factory is gated on this disabled set\n * so a toggled-off builtin registers nothing. The preference is GLOBAL — one switch\n * per builtin, applied to every workspace — matching the user's choice; these are\n * app-level agent capabilities, not per-project resources.\n *\n * File format (~/.pi/webui/builtin-extensions.json):\n * { \"disabled\": [\"plan\"] }\n *\n * `isBuiltinDisabled` must be SYNCHRONOUS because the factory gate runs inside the\n * loader's synchronous factory loop. We populate the in-memory cache once at startup\n * via `loadBuiltinPrefs()` (awaited before the server accepts requests, so the cache\n * is ready before any runtime — and therefore any factory — is built). The gate fails\n * open (treats unknown state as enabled) on the off chance it runs pre-load.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { config } from \"../config.ts\";\n\nconst PREFS_PATH = join(config.dataDir, \"builtin-extensions.json\");\n\ninterface PrefsFile {\n\tdisabled: string[];\n}\n\n/** In-memory source of truth. Fail-open default until `loadBuiltinPrefs` runs. */\nlet cache: PrefsFile = { disabled: [] };\n\n/**\n * Read the prefs file into the module cache. Idempotent. Call once at server\n * startup before serving so the synchronous gate has fresh data. A missing file\n * is normal (no builtin has ever been disabled). Fails open: a corrupt or\n * unreadable file resets to all-enabled (and warns) rather than crashing\n * startup over a non-critical preference.\n */\nexport async function loadBuiltinPrefs(): Promise<void> {\n\ttry {\n\t\tconst raw = await readFile(PREFS_PATH, \"utf8\");\n\t\tconst parsed = JSON.parse(raw) as Partial<PrefsFile>;\n\t\tcache = { disabled: Array.isArray(parsed.disabled) ? parsed.disabled : [] };\n\t} catch (err) {\n\t\tcache = { disabled: [] };\n\t\tif ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n\t\t\tconsole.warn(`[builtin-prefs] ignoring unreadable ${PREFS_PATH}:`, err);\n\t\t}\n\t}\n}\n\n/** True when the user has turned this builtin off. Synchronous (cache read). */\nexport function isBuiltinDisabled(id: string): boolean {\n\treturn cache.disabled.includes(id);\n}\n\n/** Snapshot of the currently disabled builtin ids. */\nexport function getDisabledBuiltins(): string[] {\n\treturn [...cache.disabled];\n}\n\n/** Flip a builtin on/off, updating the cache and persisting to disk. */\nexport async function setBuiltinEnabled(id: string, enabled: boolean): Promise<void> {\n\tconst next = new Set(cache.disabled);\n\tif (enabled) next.delete(id);\n\telse next.add(id);\n\tcache = { disabled: [...next] };\n\tawait save();\n}\n\nasync function save(): Promise<void> {\n\tawait mkdir(dirname(PREFS_PATH), { recursive: true });\n\tawait writeFile(PREFS_PATH, JSON.stringify(cache, null, 2), \"utf8\");\n}\n","/**\n * Registry of pi-pilot's first-party builtin extensions.\n *\n * These factories are passed to pi via `DefaultResourceLoaderOptions.\n * extensionFactories`, which is an independent channel from the disk\n * extension scanner (see `resource-loader.js`). The `noExtensions` flag\n * only controls disk scanning; factories registered here always load.\n *\n * Two-tier stance: third-party / disk pi extensions stay off by default\n * (rendered as \"third-party not supported\" in the Resources panel), but\n * pi-pilot ships its own first-party features via this list. To add a new\n * builtin feature, drop a new directory under `extensions/<name>/` with a\n * `factory.ts` exporting an `ExtensionFactory`, then append an entry to\n * `BUILTIN_EXTENSIONS` below.\n *\n * Per-builtin on/off: each factory is wrapped in a gate that checks the\n * user's persisted preference (`isBuiltinDisabled`). A disabled builtin\n * registers nothing. The gate is re-evaluated on every `session.reload()`\n * (which re-runs the factories), so toggling a builtin + reloading applies\n * the change live — see `storage/builtin-extension-prefs.ts` and the\n * `PUT .../resources/builtin-extensions` route. Wrapping (rather than\n * filtering the array) keeps the factory-list reference stable across\n * reloads, which the loader assumes.\n *\n * NOT a barrel file — this defines values (the manifest + factories list),\n * not a re-export of unrelated symbols.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { planExtensionFactory } from \"./plan/factory.ts\";\nimport { askUserExtensionFactory } from \"./ask_user/factory.ts\";\nimport { isBuiltinDisabled } from \"../storage/builtin-extension-prefs.ts\";\n\n/** Static metadata + factory for one builtin. Drives both the gated factory\n * list and the Resources panel's friendly listing. */\nexport interface BuiltinExtensionDef {\n\t/** Stable id used by prefs + the toggle endpoint. */\n\tid: string;\n\t/** User-facing name. */\n\tname: string;\n\t/** One-line description shown in the panel. */\n\tdescription: string;\n\t/** Tool names the factory registers (for display). */\n\ttools: string[];\n\t/** Slash command names the factory registers, without \"/\" (for display). */\n\tcommands: string[];\n\tfactory: ExtensionFactory;\n}\n\nexport const BUILTIN_EXTENSIONS: BuiltinExtensionDef[] = [\n\t{\n\t\tid: \"plan\",\n\t\tname: \"Plan\",\n\t\tdescription:\n\t\t\t\"A live task checklist for multi-step work — adds the update_plan tool and the /plan command.\",\n\t\ttools: [\"update_plan\"],\n\t\tcommands: [\"plan\"],\n\t\tfactory: planExtensionFactory,\n\t},\n\t{\n\t\tid: \"ask_user\",\n\t\tname: \"Ask user\",\n\t\tdescription:\n\t\t\t\"Lets the agent pause and ask you a structured multiple-choice question — adds the ask_user tool.\",\n\t\ttools: [\"ask_user\"],\n\t\tcommands: [],\n\t\tfactory: askUserExtensionFactory,\n\t},\n];\n\n/** Wrap a factory so it registers nothing while the builtin is toggled off. */\nfunction gate(def: BuiltinExtensionDef): ExtensionFactory {\n\treturn (pi) => {\n\t\tif (isBuiltinDisabled(def.id)) return;\n\t\treturn def.factory(pi);\n\t};\n}\n\nexport const builtinExtensionFactories: ExtensionFactory[] =\n\tBUILTIN_EXTENSIONS.map(gate);\n","/**\n * Post-restart reconciliation for orphaned `ask_user` tool calls.\n *\n * Scenario: pi-pilot crashes (or is killed) while `ask_user.execute()`\n * is blocked waiting for a client answer. pi has already finalized the\n * assistant message containing the toolCall block (toolCalls get\n * persisted as part of the assistant message at `message_end`), but\n * the matching toolResult was never written because the Promise\n * inside `execute()` died with the process. When pi loads the session\n * on next boot, it sees an unmatched toolCall and feeds it straight to\n * the LLM (per Spike Q1: pi does not validate or auto-resolve dangling\n * tool calls — `session-manager.js:113–207`).\n *\n * What this function does, on first bind after such a restart:\n * 1. Walk the branch tail backwards from the leaf.\n * 2. Find every `ask_user` toolCall whose id has no matching\n * toolResult on the tail.\n * 3. If any exist, append a `customMessage` (LLM-visible per Spike\n * Q4) explaining the cancellation, listing the dangling ids.\n *\n * Why a customMessage and not a forged toolResult: pi SDK exposes\n * `appendCustomMessageEntry` (visible to LLM as a user message) and\n * `appendCustomEntry` (not visible). Neither lets us write a real\n * `role: \"toolResult\"` message — `appendMessage` accepts it in\n * theory but mixing in a synthetic toolResult would break the\n * causality pi reasoners rely on. The customMessage path is honest:\n * the LLM sees its own unmatched toolCall + a plain English note\n * explaining the session was interrupted. Documented in PROGRESS.md\n * Known gaps so future readers know why this looks asymmetric.\n *\n * Re-entrancy: if a previous restart cleanup already wrote a notice\n * for the same toolCall ids, we don't write another. The dedup key is\n * the customType `ask_user-restart-cancelled` combined with the ids\n * encoded into the message body — we scan tail entries first and\n * bail if we see one whose ids cover ours.\n */\n\nimport type { SessionManager } from \"@earendil-works/pi-coding-agent\";\n\nconst CUSTOM_TYPE = \"ask_user-restart-cancelled\";\n\nexport function reconcileAfterRestart(sessionManager: SessionManager): void {\n\tconst branch = sessionManager.getBranch();\n\tif (branch.length === 0) return;\n\n\tconst satisfied = new Set<string>();\n\tconst danglingIds: string[] = [];\n\tconst danglingAlreadyHandled = new Set<string>();\n\n\t// Walk backwards. Stop conditions:\n\t// - user message: end of the current \"agent reply\" span; anything\n\t// dangling further back would have been handled by an earlier\n\t// restart pass or doesn't matter (LLM has moved on).\n\t// - existing restart-cancellation customMessage: read the ids it\n\t// covers so we don't re-notify for the same calls.\n\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\tconst entry = branch[i]!;\n\n\t\tif (entry.type === \"custom_message\") {\n\t\t\tconst cm = entry as { customType?: string; details?: unknown };\n\t\t\tif (cm.customType === CUSTOM_TYPE) {\n\t\t\t\tconst ids = (cm.details as { ids?: unknown })?.ids;\n\t\t\t\tif (Array.isArray(ids)) {\n\t\t\t\t\tfor (const id of ids) {\n\t\t\t\t\t\tif (typeof id === \"string\") danglingAlreadyHandled.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst msg = entry.message as { role?: string; toolCallId?: string; content?: unknown };\n\n\t\tif (msg.role === \"toolResult\" && typeof msg.toolCallId === \"string\") {\n\t\t\tsatisfied.add(msg.toolCallId);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n\t\t\tfor (const block of msg.content) {\n\t\t\t\tif (!block || typeof block !== \"object\") continue;\n\t\t\t\tconst b = block as { type?: string; name?: string; id?: string };\n\t\t\t\tif (b.type !== \"toolCall\") continue;\n\t\t\t\tif (b.name !== \"ask_user\") continue;\n\t\t\t\tif (typeof b.id !== \"string\") continue;\n\t\t\t\tif (satisfied.has(b.id)) continue;\n\t\t\t\tif (danglingAlreadyHandled.has(b.id)) continue;\n\t\t\t\tdanglingIds.push(b.id);\n\t\t\t}\n\t\t\t// Whether we found dangling here or not, we stop after the\n\t\t\t// first assistant message: earlier turns are historical and\n\t\t\t// would have been reconciled by their own subsequent\n\t\t\t// toolResult or by an earlier cleanup run.\n\t\t\tbreak;\n\t\t}\n\n\t\tif (msg.role === \"user\") break;\n\t}\n\n\tif (danglingIds.length === 0) return;\n\n\tconst idList = danglingIds.join(\", \");\n\tconst text =\n\t\t`[pi-pilot] Your previous ask_user call(s) [${idList}] were cancelled ` +\n\t\t`because the server restarted before the user answered. Use your best ` +\n\t\t`judgement and proceed; you may re-call ask_user if the decision still matters.`;\n\n\tsessionManager.appendCustomMessageEntry(\n\t\tCUSTOM_TYPE,\n\t\ttext,\n\t\ttrue, // display in TUI too (no-op in pi-pilot, but harmless)\n\t\t{ ids: danglingIds },\n\t);\n}\n\n","/**\n * Translate a pi AgentSessionEvent into our narrow wire payload.\n *\n * Anything not in the switch is dropped (returns undefined). Adding support\n * for a new event type means: add a case here, and a matching variant in\n * `@pi-pilot/shared/ws-protocol.ts`.\n */\n\nimport type { AgentSessionEvent } from \"@earendil-works/pi-coding-agent\";\nimport type { PiEventPayload } from \"@pi-pilot/shared\";\nimport { snapshotForSession } from \"../extensions/ask_user/registry.ts\";\n\nexport function translatePiEvent(ev: AgentSessionEvent): PiEventPayload | undefined {\n\tswitch (ev.type) {\n\t\tcase \"agent_start\":\n\t\t\treturn { kind: \"agent_start\" };\n\n\t\tcase \"agent_end\":\n\t\t\treturn { kind: \"agent_end\", willRetry: ev.willRetry };\n\n\t\tcase \"turn_start\":\n\t\t\treturn { kind: \"turn_start\" };\n\n\t\tcase \"turn_end\":\n\t\t\treturn { kind: \"turn_end\" };\n\n\t\tcase \"message_start\": {\n\t\t\tconst role = roleOf(ev.message);\n\t\t\t// Carry user text on the wire so the client can reconcile its\n\t\t\t// optimistic bubble (or insert one when no optimistic push\n\t\t\t// happened — extension-command path). Other roles stream via\n\t\t\t// message_update deltas; a start-time snapshot would be stale.\n\t\t\tconst text = role === \"user\" ? extractUserText(ev.message) : undefined;\n\t\t\treturn { kind: \"message_start\", role, text };\n\t\t}\n\n\t\tcase \"message_end\":\n\t\t\treturn { kind: \"message_end\", role: roleOf(ev.message) };\n\n\t\tcase \"message_update\": {\n\t\t\tconst ame = ev.assistantMessageEvent;\n\t\t\tif (ame.type === \"text_delta\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"message_update\",\n\t\t\t\t\tdelta: { kind: \"text\", contentIndex: ame.contentIndex, text: ame.delta },\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (ame.type === \"thinking_delta\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"message_update\",\n\t\t\t\t\tdelta: { kind: \"thinking\", contentIndex: ame.contentIndex, text: ame.delta },\n\t\t\t\t};\n\t\t\t}\n\t\t\t// Other deltas (start/end, toolcall) are not surfaced in MVP.\n\t\t\treturn { kind: \"message_update\", delta: { kind: \"other\" } };\n\t\t}\n\n\t\tcase \"tool_execution_start\":\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_start\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\targs: ev.args,\n\t\t\t};\n\n\t\tcase \"tool_execution_update\":\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_update\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\tpartialText: extractText(ev.partialResult),\n\t\t\t};\n\n\t\tcase \"tool_execution_end\": {\n\t\t\t// Whitelist details forwarding: only tools whose web cards\n\t\t\t// need the structured shape get it on the wire. Today only\n\t\t\t// `ask_user` (so <AskUserCard>'s resolved state can read\n\t\t\t// `kind`/`indices`/`text` directly instead of regex-parsing\n\t\t\t// `formatResult`'s output). Built-in tools' details are\n\t\t\t// often large (bash stdout, edit diffs); keeping them off\n\t\t\t// the wire avoids double-transmission since `extractText`\n\t\t\t// already pulled the human-readable form.\n\t\t\t//\n\t\t\t// `ev.result` is typed `any` in the SDK; the `!== undefined`\n\t\t\t// guard mirrors `workspace-manager.ts/getSessionHistory` so\n\t\t\t// we never put a `details: undefined` field on the object —\n\t\t\t// it would JSON-stringify away on the wire but is noisy in\n\t\t\t// in-process logging.\n\t\t\tconst details = shouldForwardDetails(ev.toolName)\n\t\t\t\t? ev.result?.details\n\t\t\t\t: undefined;\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_end\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\tisError: ev.isError,\n\t\t\t\ttext: extractText(ev.result),\n\t\t\t\t...(details !== undefined ? { details } : {}),\n\t\t\t};\n\t\t}\n\n\t\tcase \"queue_update\":\n\t\t\treturn {\n\t\t\t\tkind: \"queue_update\",\n\t\t\t\tsteering: [...ev.steering],\n\t\t\t\tfollowUp: [...ev.followUp],\n\t\t\t};\n\n\t\tcase \"auto_retry_start\":\n\t\t\treturn {\n\t\t\t\tkind: \"auto_retry_start\",\n\t\t\t\tattempt: ev.attempt,\n\t\t\t\tmaxAttempts: ev.maxAttempts,\n\t\t\t\tdelayMs: ev.delayMs,\n\t\t\t\terrorMessage: ev.errorMessage,\n\t\t\t};\n\n\t\tcase \"auto_retry_end\":\n\t\t\treturn {\n\t\t\t\tkind: \"auto_retry_end\",\n\t\t\t\tsuccess: ev.success,\n\t\t\t\tattempt: ev.attempt,\n\t\t\t\tfinalError: ev.finalError,\n\t\t\t};\n\n\t\tcase \"compaction_start\":\n\t\t\treturn { kind: \"compaction_start\", reason: ev.reason };\n\n\t\tcase \"compaction_end\":\n\t\t\treturn {\n\t\t\t\tkind: \"compaction_end\",\n\t\t\t\treason: ev.reason,\n\t\t\t\taborted: ev.aborted,\n\t\t\t\twillRetry: ev.willRetry,\n\t\t\t\terrorMessage: ev.errorMessage,\n\t\t\t};\n\n\t\tcase \"session_info_changed\":\n\t\t\treturn { kind: \"session_info_changed\", name: ev.name };\n\n\t\tcase \"thinking_level_changed\":\n\t\t\treturn { kind: \"thinking_level_changed\", level: ev.level };\n\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Track unknown roles we've already warned about so a stream of messages\n * with the same novel role doesn't spam the log. Module-level — the warn\n * is informational and one per process lifetime is enough to flag the\n * mismatch during development.\n */\nconst warnedUnknownRoles = new Set<string>();\n\nfunction roleOf(message: unknown): \"user\" | \"assistant\" | \"toolResult\" | \"bashExecution\" {\n\tconst role = (message as { role?: string } | undefined)?.role;\n\tif (role === \"user\" || role === \"assistant\" || role === \"toolResult\" || role === \"bashExecution\") {\n\t\treturn role;\n\t}\n\t// Pi SDK added a message role we don't translate yet. Fall back to\n\t// \"assistant\" so the wire schema stays valid, but warn loudly — a\n\t// new role usually implies new event shapes that bridge.ts and the\n\t// chat store both need to learn about.\n\tconst key = typeof role === \"string\" ? role : `<${role === undefined ? \"missing\" : typeof role}>`;\n\tif (!warnedUnknownRoles.has(key)) {\n\t\twarnedUnknownRoles.add(key);\n\t\tconsole.warn(\n\t\t\t`[bridge] unknown message role \"${key}\" — treating as assistant. ` +\n\t\t\t\t`Pi SDK may have added a role bridge.ts and chat.ts don't handle yet.`,\n\t\t);\n\t}\n\treturn \"assistant\";\n}\n\n/**\n * Best-effort extraction of user-message text. Pi's user-role\n * `AgentMessage` has either `content: string` (single text body) or\n * `content: ContentBlock[]` (typed blocks; we keep only `TextContent`).\n * Returns `undefined` when neither shape yields text — the wire field\n * is optional so an undefined here is fine.\n */\nfunction extractUserText(message: unknown): string | undefined {\n\tif (!message || typeof message !== \"object\") return undefined;\n\tconst content = (message as { content?: unknown }).content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n\t\t\tconst text = (block as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.length === 0 ? undefined : parts.join(\"\");\n}\n\n/**\n * Build synthetic events that recreate an in-flight assistant turn on a\n * client that just (re)subscribed mid-stream.\n *\n * Why: when a workspace switch happens during streaming, the client wipes\n * its chat store on `subscribed`, and /history can't see the not-yet-\n * persisted assistant message (pi only commits to JSONL at message_end).\n * Live deltas that follow the rebind would land in `chat.ts`'s\n * `lastIndexOfStreamingAssistant` guard with no streaming item to attach\n * to, and silently drop. Emitting a synthetic `message_start(assistant)`\n * + accumulated `thinking` / `text` deltas reseats that anchor so the\n * subsequent live `message_update`s flow into the right item.\n *\n * Tool calls inside the in-flight assistant message are intentionally NOT\n * restored — any `tool_execution_*` events that fired during the\n * off-window are gone (pi's `session.subscribe` does not replay), and\n * the assistant `content` only carries the ToolCall block, not the\n * running/finished tool result. Pending tool restoration is a follow-up.\n *\n * Returns `undefined` when there's nothing to replay (no in-flight\n * message, non-assistant role, or only ToolCall content).\n */\nexport function inFlightAssistantSnapshot(\n\tstreamingMessage: unknown,\n): PiEventPayload[] | undefined {\n\tif (!streamingMessage || typeof streamingMessage !== \"object\") return undefined;\n\tconst m = streamingMessage as { role?: string; content?: unknown };\n\tif (m.role !== \"assistant\" || !Array.isArray(m.content)) return undefined;\n\n\tlet textAccum = \"\";\n\tlet thinkingAccum = \"\";\n\tfor (const block of m.content) {\n\t\tif (!block || typeof block !== \"object\") continue;\n\t\tconst b = block as { type?: string; text?: unknown; thinking?: unknown };\n\t\tif (b.type === \"text\" && typeof b.text === \"string\") {\n\t\t\ttextAccum += b.text;\n\t\t} else if (b.type === \"thinking\" && typeof b.thinking === \"string\") {\n\t\t\tthinkingAccum += b.thinking;\n\t\t}\n\t}\n\n\tif (!textAccum && !thinkingAccum) return undefined;\n\n\tconst events: PiEventPayload[] = [\n\t\t{ kind: \"message_start\", role: \"assistant\" },\n\t];\n\tif (thinkingAccum) {\n\t\tevents.push({\n\t\t\tkind: \"message_update\",\n\t\t\tdelta: { kind: \"thinking\", contentIndex: 0, text: thinkingAccum },\n\t\t});\n\t}\n\tif (textAccum) {\n\t\tevents.push({\n\t\t\tkind: \"message_update\",\n\t\t\tdelta: { kind: \"text\", contentIndex: 0, text: textAccum },\n\t\t});\n\t}\n\treturn events;\n}\n\n/**\n * Build synthetic `tool_execution_start` events for any pending\n * `ask_user` calls on the given session, so a client that (re)subscribes\n * mid-question still sees the interactive card.\n *\n * Why this is needed even though pi already persists the assistant\n * message with the toolCall block: a freshly-subscribed client\n * reconciles via REST `/history` (which only sees finalized\n * toolResults — for a still-pending call there isn't one), and live\n * `tool_execution_start` already fired before this client connected.\n * Without this snapshot the question card silently never appears.\n *\n * Symmetric with `inFlightAssistantSnapshot` above. Filtered by\n * sessionFile so a connection bound to workspace A doesn't see\n * workspace B's pending questions. Empty arrays are returned (rather\n * than undefined) when there's nothing to replay — the caller is a\n * `for` loop, this keeps the call site simple.\n */\nexport function inFlightToolCallsSnapshot(\n\tsessionFile: string | null,\n): PiEventPayload[] {\n\tconst pending = snapshotForSession(sessionFile);\n\treturn pending.map((p) => ({\n\t\tkind: \"tool_execution_start\",\n\t\ttoolCallId: p.toolCallId,\n\t\ttoolName: \"ask_user\",\n\t\targs: p.args,\n\t}));\n}\n\n/**\n * Tools allowed to ship `AgentToolResult.details` over the wire. Keep\n * this list tight — every entry doubles the per-result payload size\n * for that tool. New entries should be ones whose web rendering\n * genuinely benefits from the structured shape (interactive cards,\n * highlighted selections, etc.), not ones that just *could* use it.\n *\n * Exported so `workspace-manager.ts/getSessionHistory` can apply the\n * same filter on REST history — the live (`tool_execution_end`) and\n * persisted (`HistoryToolItem`) surfaces stay symmetric.\n */\nexport const DETAILS_FORWARD_WHITELIST = new Set<string>([\"ask_user\"]);\n\nexport function shouldForwardDetails(toolName: string): boolean {\n\treturn DETAILS_FORWARD_WHITELIST.has(toolName);\n}\n\n/** Best-effort extraction of human-readable text from a tool result-shaped object. */\nfunction extractText(result: unknown): string | undefined {\n\tif (!result || typeof result !== \"object\") return undefined;\n\tconst content = (result as { content?: unknown }).content;\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const c of content) {\n\t\tif (c && typeof c === \"object\" && (c as { type?: string }).type === \"text\") {\n\t\t\tconst text = (c as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.length === 0 ? undefined : parts.join(\"\");\n}\n","/**\n * No-op implementation of pi's `ExtensionUIContext`.\n *\n * pi-pilot does not support pi extensions (see README / workspace-manager).\n * The extension system is TUI-native — custom renderers, ANSI themes,\n * keyboard overlays, modal dialogs — and cannot be bridged to the Web in\n * any meaningful way. Rather than render a half-working subset that lies\n * about what's supported, every method here is an intentional no-op or\n * returns a sensible default.\n *\n * Why keep the class at all: the pi SDK still calls\n * `bindExtensions({ uiContext })`, and the type contract requires a\n * full `ExtensionUIContext`. When `PI_PILOT_ENABLE_EXTENSIONS=1`, the\n * loader will load extensions and any UI calls land here and quietly\n * do nothing. An extension that registers tools / slash commands but\n * doesn't touch `ctx.ui` still works; everything UI-driven is dead.\n *\n * Methods covered:\n * - Dialogs: select, confirm, input, editor → resolve to default\n * - Fire-and-forget: notify, setStatus, setWidget, setTitle,\n * setEditorText, pasteToEditor → no-op\n * - TUI-only: onTerminalInput, setWorkingMessage,\n * setWorkingVisible, setWorkingIndicator,\n * setHiddenThinkingLabel, setFooter, setHeader,\n * custom, getEditorText, addAutocompleteProvider,\n * setEditorComponent, getEditorComponent, theme,\n * getAllThemes, getTheme, setTheme,\n * getToolsExpanded, setToolsExpanded\n */\n\nimport type { ExtensionUIContext } from \"@earendil-works/pi-coding-agent\";\n\nexport class ExtensionUIBridge implements ExtensionUIContext {\n\t/** Symmetric with the old bridge so workspace-manager's dispose path\n\t * can call it uniformly. There is no state to release. */\n\tdispose(): void {}\n\n\t// ============== dialog methods (resolve to default) ==============\n\n\tselect(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\tconfirm(): Promise<boolean> {\n\t\treturn Promise.resolve(false);\n\t}\n\tinput(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\teditor(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\t// ============== fire-and-forget methods (no-op) ==============\n\n\tnotify(): void {}\n\tsetStatus(): void {}\n\tsetWidget(): void {}\n\tsetTitle(): void {}\n\tsetEditorText(): void {}\n\tpasteToEditor(): void {}\n\n\t// ============== TUI-only methods (no-op / defaults) ==============\n\n\tonTerminalInput(): () => void {\n\t\treturn () => {};\n\t}\n\tsetWorkingMessage(): void {}\n\tsetWorkingVisible(): void {}\n\tsetWorkingIndicator(): void {}\n\tsetHiddenThinkingLabel(): void {}\n\tsetFooter(): void {}\n\tsetHeader(): void {}\n\tasync custom<T>(): Promise<T> {\n\t\t// `custom()` is the primary TUI rendering API used by extensions to\n\t\t// draw arbitrary terminal dialogs/overlays. It cannot be bridged to\n\t\t// the Web. Match pi's documented RPC-mode behavior of returning\n\t\t// undefined; the SDK's callers handle that explicitly.\n\t\treturn undefined as unknown as T;\n\t}\n\tgetEditorText(): string {\n\t\treturn \"\";\n\t}\n\taddAutocompleteProvider(): void {}\n\tsetEditorComponent(): void {}\n\tgetEditorComponent(): undefined {\n\t\treturn undefined;\n\t}\n\tget theme(): never {\n\t\t// Extensions that read `ctx.ui.theme` directly see undefined. The\n\t\t// cast keeps the type structural.\n\t\treturn undefined as unknown as never;\n\t}\n\tgetAllThemes(): { name: string; path: string | undefined }[] {\n\t\treturn [];\n\t}\n\tgetTheme(): undefined {\n\t\treturn undefined;\n\t}\n\tsetTheme(): { success: boolean; error?: string } {\n\t\treturn { success: false, error: \"Theme switching not supported in pi-pilot\" };\n\t}\n\tgetToolsExpanded(): boolean {\n\t\treturn false;\n\t}\n\tsetToolsExpanded(): void {}\n}\n","/**\n * /api/workspaces/:id/config routes.\n *\n * Reads and mutates the runtime config (model, thinking level, tools) for a\n * workspace's active AgentSession. Mounted onto the workspaces Hono router\n * via `mountConfigRoutes`.\n */\n\nimport type { Hono, Context } from \"hono\";\nimport type { AgentSessionRuntime } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tConfigResponse,\n\tModelInfo,\n\tPiContextUsage,\n\tSetModelRequest,\n\tSetThinkingLevelRequest,\n\tSetToolsRequest,\n\tThinkingLevel,\n\tToolInfo as ToolInfoDTO,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\n\n// ---------- helpers ----------\n\nfunction buildConfigResponse(workspaceId: string): ConfigResponse {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new Error(\"runtime not initialized\");\n\n\tconst session = runtime.session;\n\tconst model = session.model;\n\n\tconst currentModel: ModelInfo | null = model\n\t\t? {\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodelId: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\treasoning: model.reasoning,\n\t\t\t}\n\t\t: null;\n\n\tconst availableModels: ModelInfo[] = session.modelRegistry\n\t\t.getAvailable()\n\t\t.map((m) => ({\n\t\t\tprovider: m.provider,\n\t\t\tmodelId: m.id,\n\t\t\tname: m.name,\n\t\t\treasoning: m.reasoning,\n\t\t}));\n\n\tconst allTools: ToolInfoDTO[] = session.getAllTools().map((t) => ({\n\t\tname: t.name,\n\t\tdescription: t.description,\n\t}));\n\n\treturn {\n\t\tcurrentModel,\n\t\tthinkingLevel: session.thinkingLevel as ThinkingLevel,\n\t\tavailableThinkingLevels: session.getAvailableThinkingLevels() as ThinkingLevel[],\n\t\tactiveTools: session.getActiveToolNames(),\n\t\tavailableModels,\n\t\tallTools,\n\t};\n}\n\n/** Return 404 early if workspace does not exist in the registry. */\nasync function requireWorkspace(c: Context, id: string): Promise<boolean> {\n\tconst ws = await getWorkspace(id);\n\tif (!ws) {\n\t\tc.status(404);\n\t\tc.header(\"Content-Type\", \"application/json\");\n\t\t// We must return the body via the route handler, so we set a flag.\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/** Return 409 if the agent is currently streaming. */\nfunction rejectIfStreaming(c: Context, workspaceId: string): boolean {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (runtime?.session.isStreaming) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * Push a fresh `context_usage` snapshot to every WS subscriber of the\n * workspace. Pi's event stream only surfaces context-usage shifts via\n * `agent_end` / `compaction_end` / `thinking_level_changed`, so a model\n * swap whose new contextWindow differs but doesn't clamp thinking level\n * would otherwise leave clients displaying the previous model's percent\n * until the next assistant turn.\n */\nfunction broadcastContextUsage(\n\tworkspaceId: string,\n\truntime: AgentSessionRuntime,\n): void {\n\tconst usage = runtime.session.getContextUsage();\n\tif (!usage) return;\n\tconst payload: PiContextUsage = {\n\t\tkind: \"context_usage\",\n\t\ttokens: usage.tokens,\n\t\tcontextWindow: usage.contextWindow,\n\t\tpercent: usage.percent,\n\t};\n\tworkspaceManager.broadcast(workspaceId, {\n\t\ttype: \"event\",\n\t\tworkspaceId,\n\t\tsessionPath: runtime.session.sessionFile ?? null,\n\t\tpayload,\n\t});\n}\n\n// ---------- mount ----------\n\nexport function mountConfigRoutes(app: Hono): void {\n\t// GET /api/workspaces/:id/config\n\tapp.get(\"/:id/config\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/model\n\tapp.put(\"/:id/config/model\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetModelRequest;\n\t\tif (!body?.provider || !body?.modelId) {\n\t\t\treturn c.json({ ok: false, error: \"provider and modelId are required\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change model while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst model = runtime.session.modelRegistry.getAvailable().find(\n\t\t\t\t(m) => m.provider === body.provider && m.id === body.modelId,\n\t\t\t);\n\t\t\tif (!model) {\n\t\t\t\treturn c.json({ ok: false, error: `model not found or no auth: ${body.provider}/${body.modelId}` }, 404);\n\t\t\t}\n\n\t\t\tawait runtime.session.setModel(model);\n\t\t\t// New model may have a different contextWindow. Pi's setModel only\n\t\t\t// emits an event when thinking-level gets clamped — when the new\n\t\t\t// model accepts the current level unchanged, no event fires and\n\t\t\t// the WS CTX indicator would stay pinned to the previous model's\n\t\t\t// percent until the next agent_end. Push a snapshot ourselves.\n\t\t\tbroadcastContextUsage(id, runtime);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/thinking-level\n\tapp.put(\"/:id/config/thinking-level\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetThinkingLevelRequest;\n\t\tconst validLevels: ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"];\n\t\tif (!body?.level || !validLevels.includes(body.level)) {\n\t\t\treturn c.json({ ok: false, error: `level must be one of: ${validLevels.join(\", \")}` }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change thinking level while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst runtime = workspaceManager.get(id);\n\t\t\tif (!runtime) {\n\t\t\t\treturn c.json({ ok: false, error: \"runtime not initialized\" }, 500);\n\t\t\t}\n\t\t\truntime.session.setThinkingLevel(body.level);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/tools\n\tapp.put(\"/:id/config/tools\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetToolsRequest;\n\t\tif (!body?.tools || !Array.isArray(body.tools)) {\n\t\t\treturn c.json({ ok: false, error: \"tools must be an array of tool names\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change tools while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst runtime = workspaceManager.get(id);\n\t\t\tif (!runtime) {\n\t\t\t\treturn c.json({ ok: false, error: \"runtime not initialized\" }, 500);\n\t\t\t}\n\t\t\truntime.session.setActiveToolsByName(body.tools);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n}\n","/**\n * /api/workspaces/:id/files/search — workspace-scoped file lookup for the\n * composer's `@` autocomplete.\n *\n * Source of truth is `git ls-files --cached --others --exclude-standard`,\n * which is fast and honors `.gitignore`. Non-git directories fall back to a\n * bounded recursive walk that skips obvious build/dep dirs. Filtering is\n * case-insensitive substring on the workspace-relative path; results are\n * ranked by where the match landed (basename beats path) and capped at\n * `limit` (default 30, max 100).\n *\n * The full file list per workspace is cached in-process with a 10-second\n * TTL so a user mashing keys in the composer doesn't fork a `git` child\n * per keystroke. Same in-flight dedup pattern as workspace-stats.ts.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { readdir } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { Hono } from \"hono\";\nimport type { SearchFilesResponse, SearchFileEntry } from \"@pi-pilot/shared\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\n\nconst exec = promisify(execFile);\n\nconst LIST_TTL_MS = 10_000;\n/** Soft cap on the per-workspace cache. Without it, every workspace a user\n * ever opened would stay resident even after they stopped using it (the TTL\n * only re-checks on next read of the *same* key). FIFO eviction is good\n * enough — the access pattern is \"the workspace I'm composing in right\n * now\", which gets re-inserted on every miss, naturally promoting itself. */\nconst MAX_CACHED_WORKSPACES = 16;\n/** Hard ceiling on files we ever consider, across both code paths. Keeps\n * search latency bounded on pathological repos and protects against a\n * bug in the walk. */\nconst MAX_FILES_TRACKED = 20_000;\nconst DEFAULT_LIMIT = 30;\nconst MAX_LIMIT = 100;\nconst WALK_MAX_DEPTH = 8;\nconst WALK_IGNORES = new Set([\n\t\".git\",\n\t\"node_modules\",\n\t\"dist\",\n\t\"build\",\n\t\".next\",\n\t\".turbo\",\n\t\".cache\",\n\t\".vite\",\n\t\"out\",\n\t\"coverage\",\n\t\"target\",\n\t\"venv\",\n\t\".venv\",\n\t\"__pycache__\",\n\t\".DS_Store\",\n]);\n\ninterface ListCacheEntry {\n\tfiles: string[];\n\texpiresAt: number;\n}\n\nconst listCache = new Map<string, ListCacheEntry>();\nconst inflight = new Map<string, Promise<ListCacheEntry>>();\n\nasync function getFileList(workspacePath: string): Promise<string[]> {\n\tconst now = Date.now();\n\tconst cached = listCache.get(workspacePath);\n\tif (cached && cached.expiresAt > now) return cached.files;\n\n\tconst pending = inflight.get(workspacePath);\n\tif (pending) return (await pending).files;\n\n\tconst probe = probeFileList(workspacePath)\n\t\t.then((files) => {\n\t\t\tconst entry: ListCacheEntry = {\n\t\t\t\tfiles,\n\t\t\t\texpiresAt: Date.now() + LIST_TTL_MS,\n\t\t\t};\n\t\t\t// Re-insert (delete + set) so this key moves to the back of the\n\t\t\t// Map's insertion order — the FIFO evictor below treats the head\n\t\t\t// as the oldest, so this keeps the \"actively used\" workspace warm.\n\t\t\tlistCache.delete(workspacePath);\n\t\t\tlistCache.set(workspacePath, entry);\n\t\t\twhile (listCache.size > MAX_CACHED_WORKSPACES) {\n\t\t\t\tconst oldest = listCache.keys().next().value;\n\t\t\t\tif (!oldest) break;\n\t\t\t\tlistCache.delete(oldest);\n\t\t\t}\n\t\t\treturn entry;\n\t\t})\n\t\t.finally(() => inflight.delete(workspacePath));\n\tinflight.set(workspacePath, probe);\n\treturn (await probe).files;\n}\n\nasync function probeFileList(workspacePath: string): Promise<string[]> {\n\t// Try git first — fastest, respects gitignore, single subprocess.\n\ttry {\n\t\tconst { stdout } = await exec(\n\t\t\t\"git\",\n\t\t\t[\"ls-files\", \"--cached\", \"--others\", \"--exclude-standard\"],\n\t\t\t{\n\t\t\t\tcwd: workspacePath,\n\t\t\t\ttimeout: 3000,\n\t\t\t\tmaxBuffer: 16 * 1024 * 1024,\n\t\t\t\tenv: { ...process.env, GIT_DIR: undefined, GIT_WORK_TREE: undefined },\n\t\t\t},\n\t\t);\n\t\tconst lines = stdout.split(\"\\n\");\n\t\tconst out: string[] = [];\n\t\tfor (const line of lines) {\n\t\t\tif (!line) continue;\n\t\t\tout.push(line);\n\t\t\tif (out.length >= MAX_FILES_TRACKED) break;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\t// Fall through to a bounded walk.\n\t}\n\n\tconst out: string[] = [];\n\tawait walkDir(workspacePath, workspacePath, 0, out);\n\treturn out;\n}\n\nasync function walkDir(\n\troot: string,\n\tdir: string,\n\tdepth: number,\n\tout: string[],\n): Promise<void> {\n\tif (out.length >= MAX_FILES_TRACKED) return;\n\tif (depth > WALK_MAX_DEPTH) return;\n\tlet dirents;\n\ttry {\n\t\tdirents = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (out.length >= MAX_FILES_TRACKED) return;\n\t\t// Surface dotfiles like .env / .eslintrc / .gitignore — only skip the\n\t\t// dotfile *directories* listed in WALK_IGNORES (.git, .next, .cache,\n\t\t// .turbo, .vite, .venv) plus the .DS_Store noise file.\n\t\tif (WALK_IGNORES.has(d.name)) continue;\n\t\tconst abs = join(dir, d.name);\n\t\tif (d.isDirectory()) {\n\t\t\tawait walkDir(root, abs, depth + 1, out);\n\t\t} else if (d.isFile()) {\n\t\t\tout.push(relative(root, abs).split(sep).join(\"/\"));\n\t\t}\n\t}\n}\n\n/** Score a relPath against a lowercase query. Higher = better. Returns\n * null if the path doesn't match at all. */\nfunction scoreMatch(relPath: string, q: string): number | null {\n\tconst lower = relPath.toLowerCase();\n\tconst slash = lower.lastIndexOf(\"/\");\n\tconst base = slash >= 0 ? lower.slice(slash + 1) : lower;\n\n\t// Tier 1: basename prefix match — best signal.\n\tif (base.startsWith(q)) return 1000 - relPath.length;\n\t// Tier 2: basename substring.\n\tconst baseIdx = base.indexOf(q);\n\tif (baseIdx >= 0) return 800 - baseIdx - relPath.length * 0.01;\n\t// Tier 3: anywhere in the path.\n\tconst pathIdx = lower.indexOf(q);\n\tif (pathIdx >= 0) return 500 - pathIdx - relPath.length * 0.01;\n\treturn null;\n}\n\nasync function ensureWorkspaceExists(id: string): Promise<string | null> {\n\tconst ws = await getWorkspace(id);\n\treturn ws ? ws.path : null;\n}\n\nexport function mountFilesRoute(app: Hono): void {\n\tapp.get(\"/:id/files/search\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst workspacePath = await ensureWorkspaceExists(id);\n\t\tif (!workspacePath) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst qRaw = (c.req.query(\"q\") ?? \"\").trim();\n\t\tconst limitRaw = c.req.query(\"limit\");\n\t\tlet limit = limitRaw ? Number(limitRaw) : DEFAULT_LIMIT;\n\t\tif (!Number.isFinite(limit) || limit <= 0) limit = DEFAULT_LIMIT;\n\t\tif (limit > MAX_LIMIT) limit = MAX_LIMIT;\n\n\t\ttry {\n\t\t\tconst all = await getFileList(workspacePath);\n\t\t\tlet entries: SearchFileEntry[];\n\t\t\tlet truncated = false;\n\t\t\tif (!qRaw) {\n\t\t\t\t// Empty query: just return the head of the list so the popover\n\t\t\t\t// has something to show before the user types.\n\t\t\t\tconst slice = all.slice(0, limit);\n\t\t\t\tentries = slice.map((relPath) => ({\n\t\t\t\t\tpath: join(workspacePath, relPath),\n\t\t\t\t\trelPath,\n\t\t\t\t}));\n\t\t\t\ttruncated = all.length > limit;\n\t\t\t} else {\n\t\t\t\tconst q = qRaw.toLowerCase();\n\t\t\t\tconst scored: { relPath: string; score: number }[] = [];\n\t\t\t\tlet matchCount = 0;\n\t\t\t\tfor (const relPath of all) {\n\t\t\t\t\tconst score = scoreMatch(relPath, q);\n\t\t\t\t\tif (score === null) continue;\n\t\t\t\t\tmatchCount++;\n\t\t\t\t\tscored.push({ relPath, score });\n\t\t\t\t}\n\t\t\t\tscored.sort((a, b) => b.score - a.score);\n\t\t\t\tconst top = scored.slice(0, limit);\n\t\t\t\tentries = top.map((e) => ({\n\t\t\t\t\tpath: join(workspacePath, e.relPath),\n\t\t\t\t\trelPath: e.relPath,\n\t\t\t\t}));\n\t\t\t\ttruncated = matchCount > limit;\n\t\t\t}\n\t\t\tconst body: SearchFilesResponse = { workspacePath, entries, truncated };\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tconsole.error(`[api/files] search for ${id} failed:`, err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n}\n","/**\n * /api/workspaces/:id/resources route family.\n *\n * GET → snapshot of skills / prompts / extensions discovered\n * by the workspace's pi `ResourceLoader` (read-only\n * inventory \\u2014 no filesystem touched here).\n * POST .../reload → ask the loader to re-scan; useful after creating\n * files outside pi-pilot or after editing.\n * GET .../skill?path → pre-fill data for the skill editor (parsed\n * frontmatter + body).\n * POST .../skills → create a new skill (directory + SKILL.md) under the\n * chosen scope, then reload + return fresh inventory.\n * PUT .../skills → update an existing skill in place.\n * DELETE .../skills → remove a skill (and its parent directory if it\n * wasn't a bare .md at the skills root).\n * GET .../prompt?path → pre-fill data for the prompt editor.\n * POST .../prompts → create a new prompt template.\n * PUT .../prompts → update; renames the file if `name` changed.\n * DELETE .../prompts → unlink the file.\n *\n * All mutation endpoints respond with the post-reload `ResourcesResponse`\n * so the client doesn't need a follow-up GET.\n */\n\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Context, Hono } from \"hono\";\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tBuiltinExtensionInfo,\n\tCreatePromptRequest,\n\tCreateSkillRequest,\n\tExtensionInfo,\n\tExtensionLoadError,\n\tPromptInfo,\n\tResourceFileResponse,\n\tResourceScope,\n\tResourceSource,\n\tResourcesResponse,\n\tSetBuiltinExtensionRequest,\n\tSkillInfo,\n\tUpdatePromptRequest,\n\tUpdateSkillRequest,\n} from \"@pi-pilot/shared\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\nimport {\n\tcreatePrompt,\n\tcreateSkill,\n\tdeletePrompt,\n\tdeleteSkill,\n\tHttpError,\n\treadPromptFile,\n\treadSkillFile,\n\tresolveResourceRoots,\n\ttype ResourceRoots,\n\tscopeFor,\n\tupdatePrompt,\n\tupdateSkill,\n} from \"../storage/resource-writer.ts\";\nimport { EXTENSIONS_ENABLED, workspaceManager } from \"../workspace-manager.ts\";\nimport { BUILTIN_EXTENSIONS } from \"../extensions/index.ts\";\nimport {\n\tgetDisabledBuiltins,\n\tsetBuiltinEnabled,\n} from \"../storage/builtin-extension-prefs.ts\";\n\ninterface PiSourceInfo {\n\tscope: \"user\" | \"project\" | \"temporary\";\n\tsource: string;\n\tpath: string;\n}\n\nfunction toResourceSource(info: PiSourceInfo): ResourceSource {\n\treturn {\n\t\tscope: info.scope,\n\t\tlabel: info.source,\n\t\tpath: info.path,\n\t};\n}\n\n/**\n * Scan the two extension discovery directories to detect extension files\n * that exist on disk but were not loaded (because extensions are disabled).\n * This lets the UI tell the user \"you have extensions installed but they\n * are disabled\" rather than leaving them to wonder why nothing works.\n *\n * Only meaningful when `EXTENSIONS_ENABLED === false`; callers should skip\n * the scan when the operator opted in via `PI_PILOT_ENABLE_EXTENSIONS=1`.\n */\nasync function scanExtensionDirs(workspaceCwd: string): Promise<string[]> {\n\tconst dirs = [join(getAgentDir(), \"extensions\"), join(workspaceCwd, \".pi\", \"extensions\")];\n\tconst found: string[] = [];\n\tfor (const dir of dirs) {\n\t\ttry {\n\t\t\tconst entries = await readdir(dir, { withFileTypes: true });\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.isFile() && (entry.name.endsWith(\".ts\") || entry.name.endsWith(\".js\"))) {\n\t\t\t\t\tfound.push(join(dir, entry.name));\n\t\t\t\t} else if (entry.isDirectory()) {\n\t\t\t\t\t// Could be a package-style extension; list the directory.\n\t\t\t\t\tfound.push(join(dir, entry.name));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Directory doesn't exist or isn't readable — that's fine.\n\t\t}\n\t}\n\treturn found;\n}\n\n/** Build a ResourcesResponse from the current loader state.\n *\n * `roots` is supplied so each skill/prompt's `managed` flag can be set:\n * resources whose filePath is under one of pi-pilot's writeable roots get\n * Edit/Delete affordances; resources that came from packages, settings\n * extras, ~/.agents/ paths, or CLI flags are read-only.\n *\n * `workspaceCwd` is the workspace's root directory, used to scan for\n * disabled extensions when extensions are off.\n */\nasync function snapshot(\n\tworkspaceId: string,\n\troots: ResourceRoots,\n\tworkspaceCwd: string,\n): Promise<ResourcesResponse> {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new HttpError(500, \"runtime not initialized\");\n\tconst loader = runtime.services.resourceLoader;\n\n\tconst { skills } = loader.getSkills();\n\tconst { prompts } = loader.getPrompts();\n\tconst extResult = loader.getExtensions();\n\n\t// Pi resolves command-name collisions across extensions by appending\n\t// `:N` to later registrations (two extensions registering `plan` become\n\t// invocation names `plan` and `plan:1`). We surface those resolved\n\t// names so the composer's autocomplete picks them up and a user-typed\n\t// `/plan:1` actually invokes the right handler. The runner is the only\n\t// public source of truth for resolved names — the raw per-extension\n\t// `Map<string, RegisteredCommand>.keys()` doesn't know about\n\t// cross-extension conflicts.\n\t//\n\t// `extensionRunner` becomes available after `bindExtensions()` (called\n\t// in workspace-manager.build); the `?? []` fallback is defensive for\n\t// the brief window after runtime creation but before bind.\n\tconst resolvedExtCommandsByPath = new Map<string, string[]>();\n\tconst resolvedCommands = runtime.session.extensionRunner?.getRegisteredCommands() ?? [];\n\tfor (const cmd of resolvedCommands) {\n\t\tconst key = cmd.sourceInfo.path;\n\t\tlet list = resolvedExtCommandsByPath.get(key);\n\t\tif (!list) {\n\t\t\tlist = [];\n\t\t\tresolvedExtCommandsByPath.set(key, list);\n\t\t}\n\t\tlist.push(cmd.invocationName);\n\t}\n\n\tconst skillsOut: SkillInfo[] = skills.map((s) => ({\n\t\tname: s.name,\n\t\tdescription: s.description,\n\t\tfilePath: s.filePath,\n\t\tdisableModelInvocation: s.disableModelInvocation,\n\t\tsource: toResourceSource(s.sourceInfo),\n\t\tmanaged: scopeFor(s.filePath, roots) !== undefined,\n\t}));\n\n\tconst promptsOut: PromptInfo[] = prompts.map((p) => ({\n\t\tname: p.name,\n\t\tdescription: p.description,\n\t\targumentHint: p.argumentHint,\n\t\tfilePath: p.filePath,\n\t\tcontent: p.content,\n\t\tsource: toResourceSource(p.sourceInfo),\n\t\tmanaged: scopeFor(p.filePath, roots) !== undefined,\n\t}));\n\n\tconst extensionsOut: ExtensionInfo[] = extResult.extensions.map((e) => {\n\t\t// Match on `sourceInfo.path` because that's what ResolvedCommand's\n\t\t// sourceInfo carries; `e.path` may differ for package-style\n\t\t// extensions. Fall back to the raw command keys if the runner\n\t\t// hasn't populated yet (defensive — shouldn't happen post-bind).\n\t\tconst resolved = resolvedExtCommandsByPath.get(e.sourceInfo.path);\n\t\treturn {\n\t\t\tpath: e.path,\n\t\t\tresolvedPath: e.resolvedPath,\n\t\t\tsource: toResourceSource(e.sourceInfo),\n\t\t\ttools: [...e.tools.keys()],\n\t\t\tcommands: resolved ?? [...e.commands.keys()],\n\t\t\tflags: [...e.flags.keys()],\n\t\t\tshortcuts: [...e.shortcuts.keys()],\n\t\t};\n\t});\n\n\tconst extensionErrors: ExtensionLoadError[] = extResult.errors.map((err) => ({\n\t\tpath: err.path,\n\t\terror: err.error,\n\t}));\n\n\t// Only surface \"you have extensions on disk that we skipped\" when we\n\t// actually skipped them. When the operator opted in via the env flag,\n\t// any extensions found on disk are represented by `extensions` /\n\t// `extensionErrors` instead, and surfacing them a second time here\n\t// would double-count.\n\tconst disabledExtensions = EXTENSIONS_ENABLED ? [] : await scanExtensionDirs(workspaceCwd);\n\n\t// Builtins are listed from the static manifest + global on/off prefs, not\n\t// from the live runner — so the panel reflects a just-toggled state even in\n\t// other workspaces whose runtimes haven't reloaded yet. Tools/commands are\n\t// the manifest's declared registrations (stable; collisions among our own\n\t// builtins don't happen).\n\tconst disabledBuiltins = new Set(getDisabledBuiltins());\n\tconst builtinExtensions: BuiltinExtensionInfo[] = BUILTIN_EXTENSIONS.map((d) => ({\n\t\tid: d.id,\n\t\tname: d.name,\n\t\tdescription: d.description,\n\t\tenabled: !disabledBuiltins.has(d.id),\n\t\ttools: d.tools,\n\t\tcommands: d.commands,\n\t}));\n\n\treturn {\n\t\tskills: skillsOut,\n\t\tprompts: promptsOut,\n\t\textensionsEnabled: EXTENSIONS_ENABLED,\n\t\textensions: extensionsOut,\n\t\textensionErrors,\n\t\tdisabledExtensions,\n\t\tbuiltinExtensions,\n\t};\n}\n\nasync function rootsFor(\n\tworkspaceId: string,\n): Promise<{ roots: ResourceRoots; workspaceCwd: string }> {\n\tconst ws = await getWorkspace(workspaceId);\n\tif (!ws) throw new HttpError(404, \"workspace not found\");\n\tconst roots = resolveResourceRoots({ agentDir: getAgentDir(), workspaceCwd: ws.path });\n\treturn { roots, workspaceCwd: ws.path };\n}\n\n/** Map a thrown error into a Hono JSON response. */\nfunction respondError(c: Context, err: unknown) {\n\tif (err instanceof HttpError) {\n\t\treturn c.json({ ok: false, error: err.message }, err.status as 400 | 404 | 409 | 500);\n\t}\n\tconst message = err instanceof Error ? err.message : String(err);\n\tconsole.error(`[api/resources] unexpected error:`, err);\n\treturn c.json({ ok: false, error: message }, 500);\n}\n\n/** Reload the loader so subsequent reads see the change we just made. */\nasync function reload(workspaceId: string): Promise<void> {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new HttpError(500, \"runtime not initialized\");\n\tawait runtime.services.resourceLoader.reload();\n}\n\nexport function mountResourcesRoute(app: Hono): void {\n\t// ---------- GET inventory ----------\n\tapp.get(\"/:id/resources\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST reload ----------\n\tapp.post(\"/:id/resources/reload\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tawait reload(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- GET skill body ----------\n\tapp.get(\"/:id/resources/skill\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tconst { roots } = await rootsFor(id);\n\t\t\tconst scope = scopeFor(filePath, roots);\n\t\t\tif (scope === undefined) {\n\t\t\t\treturn c.json({ ok: false, error: \"skill file is not under a managed root\" }, 400);\n\t\t\t}\n\t\t\tconst data = await readSkillFile(filePath, roots);\n\t\t\tconst body: ResourceFileResponse = {\n\t\t\t\tfilePath,\n\t\t\t\tscope,\n\t\t\t\tname: data.name,\n\t\t\t\tdescription: data.description,\n\t\t\t\tdisableModelInvocation: data.disableModelInvocation,\n\t\t\t\tbody: data.body,\n\t\t\t};\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST skills (create) ----------\n\tapp.post(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as CreateSkillRequest | null;\n\t\tif (!body || !isScope(body.scope) || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"scope, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait createSkill({\n\t\t\t\troots,\n\t\t\t\tscope: body.scope,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\tbody: body.body,\n\t\t\t\tdisableModelInvocation: body.disableModelInvocation,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT skills (update) ----------\n\tapp.put(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as UpdateSkillRequest | null;\n\t\tif (!body || typeof body.filePath !== \"string\" || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"filePath, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait updateSkill({\n\t\t\t\troots,\n\t\t\t\tfilePath: body.filePath,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\tbody: body.body,\n\t\t\t\tdisableModelInvocation: body.disableModelInvocation,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- DELETE skills ----------\n\tapp.delete(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait deleteSkill(filePath, roots);\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- GET prompt body ----------\n\tapp.get(\"/:id/resources/prompt\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tconst { roots } = await rootsFor(id);\n\t\t\tconst scope = scopeFor(filePath, roots);\n\t\t\tif (scope === undefined) {\n\t\t\t\treturn c.json({ ok: false, error: \"prompt file is not under a managed root\" }, 400);\n\t\t\t}\n\t\t\tconst data = await readPromptFile(filePath, roots);\n\t\t\tconst body: ResourceFileResponse = {\n\t\t\t\tfilePath,\n\t\t\t\tscope,\n\t\t\t\tname: data.name,\n\t\t\t\tdescription: data.description,\n\t\t\t\targumentHint: data.argumentHint,\n\t\t\t\tbody: data.body,\n\t\t\t};\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST prompts (create) ----------\n\tapp.post(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as CreatePromptRequest | null;\n\t\tif (!body || !isScope(body.scope) || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"scope, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait createPrompt({\n\t\t\t\troots,\n\t\t\t\tscope: body.scope,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\targumentHint: body.argumentHint,\n\t\t\t\tbody: body.body,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT prompts (update) ----------\n\tapp.put(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as UpdatePromptRequest | null;\n\t\tif (!body || typeof body.filePath !== \"string\" || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"filePath, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait updatePrompt({\n\t\t\t\troots,\n\t\t\t\tfilePath: body.filePath,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\targumentHint: body.argumentHint,\n\t\t\t\tbody: body.body,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- DELETE prompts ----------\n\tapp.delete(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait deletePrompt(filePath, roots);\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT builtin extension on/off ----------\n\tapp.put(\"/:id/resources/builtin-extensions\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as SetBuiltinExtensionRequest | null;\n\t\tif (!body || typeof body.id !== \"string\" || typeof body.enabled !== \"boolean\") {\n\t\t\treturn c.json({ ok: false, error: \"id and enabled are required\" }, 400);\n\t\t}\n\t\tif (!BUILTIN_EXTENSIONS.some((d) => d.id === body.id)) {\n\t\t\treturn c.json({ ok: false, error: `unknown builtin extension: ${body.id}` }, 400);\n\t\t}\n\t\ttry {\n\t\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\t\t// Reloading rebuilds the tool registry; doing so mid-stream would\n\t\t\t// yank tools out from under an in-flight turn (and could strand a\n\t\t\t// pending ask_user). Make the user finish first.\n\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{ ok: false, error: \"Stop the current turn before changing extensions\" },\n\t\t\t\t\t409,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Prefs are global; persist once. We only reload THIS workspace's\n\t\t\t// session so the change is live where the user is acting. Other\n\t\t\t// loaded workspaces keep their current toolset until they next\n\t\t\t// reload / restart, but their panels already reflect the new state\n\t\t\t// (snapshot reads prefs, not the live runner).\n\t\t\tawait setBuiltinEnabled(body.id, body.enabled);\n\t\t\tawait runtime.session.reload();\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n}\n\nfunction isScope(value: unknown): value is ResourceScope {\n\treturn value === \"user\" || value === \"project\";\n}\n","/**\n * /api/fs/browse — directory listing for the web's <FsBrowser>.\n *\n * Only directories are returned (we never list files). Hidden entries are\n * excluded by default; pass ?showHidden=1 to include them.\n *\n * The `path` query parameter must be absolute. If omitted, defaults to the\n * user's home directory.\n */\n\nimport { readdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { Hono } from \"hono\";\nimport type { BrowseFsResponse, FsEntry } from \"@pi-pilot/shared\";\n\nexport const fsRoute = new Hono();\n\nfsRoute.get(\"/browse\", async (c) => {\n\tconst rawPath = c.req.query(\"path\");\n\tconst showHidden = c.req.query(\"showHidden\") === \"1\";\n\n\tconst target = rawPath && isAbsolute(rawPath) ? resolve(rawPath) : homedir();\n\tlet dirents;\n\ttry {\n\t\tdirents = await readdir(target, { withFileTypes: true });\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tconst msg = code === \"EACCES\" ? \"permission denied\" : code === \"ENOENT\" ? \"not found\" : \"read failed\";\n\t\treturn c.json({ ok: false, error: msg, path: target }, 400);\n\t}\n\n\tconst entries: FsEntry[] = dirents\n\t\t.filter((d) => d.isDirectory())\n\t\t.filter((d) => showHidden || !d.name.startsWith(\".\"))\n\t\t.map((d) => ({\n\t\t\tname: d.name,\n\t\t\tpath: join(target, d.name),\n\t\t\ttype: \"dir\" as const,\n\t\t}))\n\t\t.sort((a, b) => a.name.localeCompare(b.name));\n\n\tconst parent = (() => {\n\t\tconst p = dirname(target);\n\t\treturn p === target ? null : p;\n\t})();\n\n\tconst body: BrowseFsResponse = { path: target, parent, entries };\n\treturn c.json(body);\n});\n","/**\n * /api/model-configs routes.\n *\n * Reads and writes ~/.pi/agent/models.json. After any mutation the active\n * workspace runtimes have their modelRegistry refreshed so changes take\n * effect immediately without restarting the server.\n */\n\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { Hono } from \"hono\";\nimport {\n\tgetAgentDir,\n} from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tModelsConfig,\n\tModelsConfigResponse,\n\tSetModelsConfigRequest,\n\tUpsertProviderRequest,\n\tAddModelToProviderRequest,\n\tUpdateModelRequest,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\n\nexport const modelConfigsRoute = new Hono();\n\n// ---------- helpers ----------\n\n/**\n * Serialize all read-modify-write operations on models.json. Although\n * Node.js is single-threaded, the async gaps between readFile and\n * writeFile allow interleaving of concurrent requests — without a lock\n * a later write could silently overwrite an earlier mutation.\n */\nlet writeLock = Promise.resolve();\nfunction withWriteLock<T>(fn: () => Promise<T>): Promise<T> {\n\tconst next = writeLock.then(fn, fn);\n\twriteLock = next.then(() => {}, () => {});\n\treturn next;\n}\n\nfunction modelsPath(): string {\n\treturn join(getAgentDir(), \"models.json\");\n}\n\nasync function readModelsJson(): Promise<ModelsConfig> {\n\ttry {\n\t\tconst raw = await readFile(modelsPath(), \"utf-8\");\n\t\treturn JSON.parse(raw) as ModelsConfig;\n\t} catch (err) {\n\t\tif ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n\t\t\treturn { providers: {} };\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nasync function writeModelsJson(config: ModelsConfig): Promise<void> {\n\tconst p = modelsPath();\n\tawait mkdir(dirname(p), { recursive: true });\n\tawait writeFile(p, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\n/** Lightweight error for validation failures inside withWriteLock. */\nclass ValidationError extends Error {\n\tconstructor(message: string, public readonly status: number) {\n\t\tsuper(message);\n\t}\n}\n\nfunction refreshRegistry(workspaceId?: string): void {\n\tif (!workspaceId) return;\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (runtime) {\n\t\ttry {\n\t\t\truntime.session.modelRegistry.refresh();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[model-configs] refresh registry for ${workspaceId} failed:`, e);\n\t\t}\n\t}\n}\n\n// ---------- routes ----------\n\n/** GET /api/model-configs */\nmodelConfigsRoute.get(\"/\", async (c) => {\n\ttry {\n\t\tconst config = await readModelsJson();\n\t\tconst body: ModelsConfigResponse = { config };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** PUT /api/model-configs — replace entire models.json */\nmodelConfigsRoute.put(\"/\", async (c) => {\n\tconst body = (await c.req.json()) as SetModelsConfigRequest;\n\tif (!body?.config?.providers) {\n\t\treturn c.json({ ok: false, error: \"config.providers is required\" }, 400);\n\t}\n\ttry {\n\t\tawait withWriteLock(async () => {\n\t\t\tawait writeModelsJson(body.config);\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config: body.config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** POST /api/model-configs/providers — add or update a provider */\nmodelConfigsRoute.post(\"/providers\", async (c) => {\n\tconst body = (await c.req.json()) as UpsertProviderRequest;\n\tif (!body?.name || !body?.provider) {\n\t\treturn c.json({ ok: false, error: \"name and provider are required\" }, 400);\n\t}\n\tif (!body.provider.baseUrl || !body.provider.api || !body.provider.apiKey) {\n\t\treturn c.json({ ok: false, error: \"provider must have baseUrl, api, and apiKey\" }, 400);\n\t}\n\tif (!Array.isArray(body.provider.models)) {\n\t\treturn c.json({ ok: false, error: \"provider.models must be an array\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tcfg.providers[body.name] = body.provider;\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** DELETE /api/model-configs/providers?name=... — delete a provider */\nmodelConfigsRoute.delete(\"/providers\", async (c) => {\n\tconst name = c.req.query(\"name\");\n\tif (!name) {\n\t\treturn c.json({ ok: false, error: \"name query param is required\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[name]) {\n\t\t\t\tthrow new ValidationError(`provider \"${name}\" not found`, 404);\n\t\t\t}\n\t\t\tdelete cfg.providers[name];\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** POST /api/model-configs/providers/:provider/models — add a model */\nmodelConfigsRoute.post(\"/providers/:provider/models\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst body = (await c.req.json()) as AddModelToProviderRequest;\n\tif (!body?.model?.id || !body?.model?.name) {\n\t\treturn c.json({ ok: false, error: \"model.id and model.name are required\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst existing = cfg.providers[provider].models.find(m => m.id === body.model.id);\n\t\t\tif (existing) {\n\t\t\t\tthrow new ValidationError(`model \"${body.model.id}\" already exists in provider \"${provider}\"`, 409);\n\t\t\t}\n\t\t\tcfg.providers[provider].models.push(body.model);\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404 | 409);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** PUT /api/model-configs/providers/:provider/models/:modelId — update a model */\nmodelConfigsRoute.put(\"/providers/:provider/models/:modelId\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst modelId = c.req.param(\"modelId\");\n\tconst body = (await c.req.json()) as UpdateModelRequest;\n\tif (!body?.model) {\n\t\treturn c.json({ ok: false, error: \"model is required\" }, 400);\n\t}\n\tif (body.model.id !== modelId) {\n\t\treturn c.json({ ok: false, error: \"model.id does not match URL parameter\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst idx = cfg.providers[provider].models.findIndex(m => m.id === modelId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new ValidationError(`model \"${modelId}\" not found in provider \"${provider}\"`, 404);\n\t\t\t}\n\t\t\tcfg.providers[provider].models[idx] = body.model;\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** DELETE /api/model-configs/providers/:provider/models/:modelId — delete a model */\nmodelConfigsRoute.delete(\"/providers/:provider/models/:modelId\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst modelId = c.req.param(\"modelId\");\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst idx = cfg.providers[provider].models.findIndex(m => m.id === modelId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new ValidationError(`model \"${modelId}\" not found in provider \"${provider}\"`, 404);\n\t\t\t}\n\t\t\tcfg.providers[provider].models.splice(idx, 1);\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n","/**\n * WebSocket hub.\n *\n * One connection ↔ at most one (workspace, session) subscription. The\n * client switches subscription with another `subscribe` message; we just\n * tear down the old listener and attach a new one.\n *\n * On disconnect, all per-connection state is released. The runtime in the\n * WorkspaceManager is NOT torn down (other connections or future ones\n * may reuse it).\n *\n * Subscribed connections are registered with the workspace manager so it\n * can fan server-initiated messages (extension load errors, future\n * workspace-level events) out to them. pi extensions themselves are not\n * supported — `ctx.ui.*` calls are no-ops, so no UI request/response\n * traffic flows over the WS.\n */\n\nimport { WebSocketServer, type WebSocket } from \"ws\";\nimport type { Server } from \"node:http\";\nimport type { AgentSessionRuntime } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tPiAssistantTiming,\n\tPiContextUsage,\n\tWsClientMessage,\n\tWsServerMessage,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport {\n\tinFlightAssistantSnapshot,\n\tinFlightToolCallsSnapshot,\n\ttranslatePiEvent,\n} from \"./bridge.ts\";\nimport { resolveAnswer } from \"../extensions/ask_user/registry.ts\";\n\ninterface ConnectionState {\n\tworkspaceId?: string;\n\tunsubscribeSession?: () => void;\n\tunsubscribeRebind?: () => void;\n}\n\n/**\n * Per-workspace session replacement lock. Serializes new_session, fork,\n * and switchSession calls so concurrent clicks or messages don't race\n * on the same runtime.\n */\nconst replacementLocks = new Map<string, Promise<void>>();\n\nfunction withReplacementLock(workspaceId: string, fn: () => Promise<void>): Promise<void> {\n\tconst prev = replacementLocks.get(workspaceId) ?? Promise.resolve();\n\tconst next = prev.then(fn, fn);\n\treplacementLocks.set(workspaceId, next);\n\t// Clean up reference when chain settles to avoid unbounded growth.\n\t// Use .then(cleanup, cleanup) so cleanup runs on rejection too and no\n\t// unhandled-rejection leaks from the derived promise.\n\tconst cleanup = () => {\n\t\tif (replacementLocks.get(workspaceId) === next) {\n\t\t\treplacementLocks.delete(workspaceId);\n\t\t}\n\t};\n\tnext.then(cleanup, cleanup);\n\treturn next;\n}\n\nexport function attachWsHub(httpServer: Server): WebSocketServer {\n\tconst wss = new WebSocketServer({ server: httpServer, path: \"/ws\" });\n\n\twss.on(\"connection\", (ws) => {\n\t\tconst state: ConnectionState = {};\n\n\t\tws.on(\"message\", async (raw) => {\n\t\t\tlet msg: WsClientMessage;\n\t\t\ttry {\n\t\t\t\tmsg = JSON.parse(raw.toString()) as WsClientMessage;\n\t\t\t} catch {\n\t\t\t\tsend(ws, { type: \"error\", message: \"invalid JSON\" });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait handle(ws, state, msg);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tsend(ws, { type: \"error\", message, command: msg.type });\n\t\t\t}\n\t\t});\n\n\t\tws.on(\"close\", () => {\n\t\t\tdetach(state, ws);\n\t\t});\n\t});\n\n\treturn wss;\n}\n\nasync function handle(ws: WebSocket, state: ConnectionState, msg: WsClientMessage): Promise<void> {\n\tswitch (msg.type) {\n\t\tcase \"subscribe\": {\n\t\t\tconst hadCurrentSubscription =\n\t\t\t\tstate.workspaceId === msg.workspaceId && !!state.unsubscribeSession;\n\n\t\t\t// Register this connection as a subscriber BEFORE building the\n\t\t\t// runtime. Extensions that fire `ctx.ui.notify(...)` from a\n\t\t\t// `session_start` handler emit it synchronously during\n\t\t\t// `getOrCreate`'s bindExtensions call — if no subscriber were\n\t\t\t// registered yet, the bridge would broadcast to an empty set and\n\t\t\t// the message would be lost.\n\t\t\tensureRebindListener(ws, state, msg.workspaceId);\n\t\t\tawait workspaceManager.getOrCreate(msg.workspaceId);\n\n\t\t\tlet switched = false;\n\t\t\tlet switchError: string | undefined;\n\t\t\tif (msg.sessionPath) {\n\t\t\t\tawait withReplacementLock(msg.workspaceId, async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tswitched = await workspaceManager.switchSession(msg.workspaceId, msg.sessionPath!);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tswitchError = err instanceof Error ? err.message : String(err);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!switched && !hadCurrentSubscription) {\n\t\t\t\tbindCurrentSession(ws, state, msg.workspaceId);\n\t\t\t}\n\t\t\tif (switchError) {\n\t\t\t\tsend(ws, { type: \"error\", message: switchError, command: \"subscribe\" });\n\t\t\t}\n\t\t\tsend(ws, { type: \"ack\", command: \"subscribe\" });\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"prompt\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Reject prompts while a session replacement is in progress.\n\t\t\tif (replacementLocks.has(wsId)) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"session switching in progress\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\tif (!runtime) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Fire-and-forget; success is observable as `event` messages\n\t\t\t// streaming back through the subscription, so no `ack` is sent.\n\t\t\tvoid runtime.session\n\t\t\t\t.prompt(msg.message, {\n\t\t\t\t\tstreamingBehavior: msg.streamingBehavior,\n\t\t\t\t})\n\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tsend(ws, { type: \"error\", message, command: \"prompt\" });\n\t\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"abort\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"abort\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (replacementLocks.has(wsId)) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"session switching in progress\", command: \"abort\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\tif (!runtime) return;\n\t\t\t// Success is observable as an `agent_end` event; no ack needed.\n\t\t\tawait runtime.session.abort();\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"new_session\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"new_session\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait withReplacementLock(msg.workspaceId, async () => {\n\t\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\t\tif (!runtime) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"new_session\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"cannot create session while streaming\", command: \"new_session\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = await runtime.newSession();\n\t\t\t\tif (result.cancelled) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"new session cancelled\", command: \"new_session\" });\n\t\t\t\t}\n\t\t\t\t// Success → rebind fires automatically → client gets `subscribed`.\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"fork\": {\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"fork\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait withReplacementLock(msg.workspaceId, async () => {\n\t\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\t\tif (!runtime) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"cannot fork while streaming\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = await runtime.fork(msg.entryId);\n\t\t\t\tif (result.cancelled) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"fork cancelled\", command: \"fork\" });\n\t\t\t\t}\n\t\t\t\t// Success → rebind fires automatically → client gets `subscribed`.\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"answer_question\": {\n\t\t\t// Stale answers (toolCallId not in registry, or sessionFile\n\t\t\t// mismatch) are silently ignored — see registry.ts header\n\t\t\t// \"Stale-answer policy\". The losing tab's UI catches up via\n\t\t\t// the normal `tool_execution_end` broadcast that the winning\n\t\t\t// tab's answer triggers.\n\t\t\tconst wsId = state.workspaceId;\n\t\t\tif (!wsId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"answer_question\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Defense-in-depth: skip during session replacement. Today\n\t\t\t// the registry's sessionFile guard already rejects answers\n\t\t\t// destined for a defunct session, so this is currently a\n\t\t\t// belt-and-suspenders match with prompt/abort. Keeping the\n\t\t\t// shape consistent across handlers means future changes to\n\t\t\t// how `sessionFile` is exposed during a transition (e.g.\n\t\t\t// transiently `null`) won't silently let an answer through.\n\t\t\tif (replacementLocks.has(wsId)) return;\n\t\t\tconst runtime = workspaceManager.get(wsId);\n\t\t\tif (!runtime) return;\n\t\t\tconst activeFile = runtime.session.sessionFile ?? null;\n\t\t\tresolveAnswer(msg.toolCallId, msg.answer, activeFile);\n\t\t\treturn;\n\t\t}\n\n\t\tdefault: {\n\t\t\t// Exhaustiveness check\n\t\t\tconst _: never = msg;\n\t\t\tvoid _;\n\t\t\tsend(ws, { type: \"error\", message: \"unknown command\" });\n\t\t}\n\t}\n}\n\nfunction ensureRebindListener(ws: WebSocket, state: ConnectionState, workspaceId: string): void {\n\tif (state.workspaceId === workspaceId && state.unsubscribeRebind) return;\n\n\tdetach(state, ws);\n\tstate.workspaceId = workspaceId;\n\tworkspaceManager.addSubscriber(workspaceId, ws);\n\tstate.unsubscribeRebind = workspaceManager.onSessionReplaced(workspaceId, () => {\n\t\tif (state.workspaceId !== workspaceId) return;\n\t\tbindCurrentSession(ws, state, workspaceId);\n\t});\n}\n\nfunction bindCurrentSession(ws: WebSocket, state: ConnectionState, workspaceId: string): void {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) {\n\t\tsend(ws, { type: \"error\", message: \"runtime gone\", command: \"subscribe\" });\n\t\treturn;\n\t}\n\n\tstate.unsubscribeSession?.();\n\tconst session = runtime.session;\n\tconst sessionPath = session.sessionFile ?? null;\n\n\t// Per-binding timing state. Lives in the subscribe closure so it\n\t// auto-resets on session replacement (each new bindCurrentSession\n\t// allocates a fresh closure). Tracks the wall-clock anchors needed to\n\t// derive `PiAssistantTiming` once the assistant turn completes.\n\tlet assistantStartAt: number | undefined;\n\tlet assistantFirstTokenAt: number | undefined;\n\n\tstate.unsubscribeSession = session.subscribe((ev) => {\n\t\tconst payload = translatePiEvent(ev);\n\t\tif (!payload) return;\n\n\t\t// Capture timing anchors *before* forwarding the event so the\n\t\t// firstToken stamp is taken at the moment the delta arrives at\n\t\t// the server, not at whatever later moment the listener runs.\n\t\tif (payload.kind === \"message_start\" && payload.role === \"assistant\") {\n\t\t\tassistantStartAt = performance.now();\n\t\t\tassistantFirstTokenAt = undefined;\n\t\t} else if (\n\t\t\tpayload.kind === \"message_update\" &&\n\t\t\tpayload.delta.kind === \"text\" &&\n\t\t\tassistantStartAt !== undefined &&\n\t\t\tassistantFirstTokenAt === undefined\n\t\t) {\n\t\t\tassistantFirstTokenAt = performance.now();\n\t\t}\n\n\t\tsend(ws, {\n\t\t\ttype: \"event\",\n\t\t\tworkspaceId,\n\t\t\tsessionPath,\n\t\t\tpayload,\n\t\t});\n\n\t\t// Emit timing telemetry once the assistant message closes. Skipped\n\t\t// if `message_start` wasn't observed (defensive — should not happen\n\t\t// in a healthy event stream, but a hot-reconnect mid-turn could).\n\t\tif (\n\t\t\tpayload.kind === \"message_end\" &&\n\t\t\tpayload.role === \"assistant\" &&\n\t\t\tassistantStartAt !== undefined\n\t\t) {\n\t\t\tconst now = performance.now();\n\t\t\tconst timing: PiAssistantTiming = {\n\t\t\t\tkind: \"assistant_timing\",\n\t\t\t\tfirstTokenMs:\n\t\t\t\t\tassistantFirstTokenAt !== undefined\n\t\t\t\t\t\t? Math.round(assistantFirstTokenAt - assistantStartAt)\n\t\t\t\t\t\t: null,\n\t\t\t\ttotalMs: Math.round(now - assistantStartAt),\n\t\t\t};\n\t\t\tsend(ws, {\n\t\t\t\ttype: \"event\",\n\t\t\t\tworkspaceId,\n\t\t\t\tsessionPath,\n\t\t\t\tpayload: timing,\n\t\t\t});\n\t\t\tassistantStartAt = undefined;\n\t\t\tassistantFirstTokenAt = undefined;\n\t\t}\n\n\t\t// Context usage shifts on turn completion (new assistant usage),\n\t\t// on compaction end (token count just reset), and on model swap\n\t\t// (context window may have changed). Sample after the event so the\n\t\t// client never sees a stale window. `getContextUsage()` returns\n\t\t// undefined when there's no model or no contextWindow — in that\n\t\t// case we don't bother sending; the existing snapshot stays.\n\t\tif (\n\t\t\tpayload.kind === \"agent_end\" ||\n\t\t\tpayload.kind === \"compaction_end\" ||\n\t\t\tpayload.kind === \"session_info_changed\" ||\n\t\t\tpayload.kind === \"thinking_level_changed\"\n\t\t) {\n\t\t\tsendContextUsage(ws, runtime, workspaceId, sessionPath);\n\t\t}\n\t});\n\n\tsend(ws, {\n\t\ttype: \"subscribed\",\n\t\tworkspaceId,\n\t\tsessionPath,\n\t\tsessionId: session.sessionId,\n\t});\n\n\t// If the agent is mid-stream when we (re)bind — typical case: user\n\t// switched workspaces during streaming and came back — replay the\n\t// in-flight assistant turn so the client has a streaming item for\n\t// subsequent live deltas to attach to. bindCurrentSession is\n\t// synchronous and JS is single-threaded, so no pi event can land\n\t// between the snapshot read and the subscribe above; future deltas\n\t// flow naturally through the cb. See bridge.ts/inFlightAssistantSnapshot.\n\tconst inFlight = inFlightAssistantSnapshot(runtime.session.state.streamingMessage);\n\tif (inFlight) {\n\t\tfor (const payload of inFlight) {\n\t\t\tsend(ws, {\n\t\t\t\ttype: \"event\",\n\t\t\t\tworkspaceId,\n\t\t\t\tsessionPath,\n\t\t\t\tpayload,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Re-emit `tool_execution_start` for any pending `ask_user` calls so\n\t// a (re)subscribing tab gets the interactive question card back —\n\t// the original tool_execution_start fired before this connection\n\t// existed, and REST /history only carries finalized toolResults.\n\t// See bridge.ts/inFlightToolCallsSnapshot.\n\tfor (const payload of inFlightToolCallsSnapshot(sessionPath)) {\n\t\tsend(ws, {\n\t\t\ttype: \"event\",\n\t\t\tworkspaceId,\n\t\t\tsessionPath,\n\t\t\tpayload,\n\t\t});\n\t}\n\n\t// Prime the client with a usage snapshot so the indicator isn't blank\n\t// until the next agent_end.\n\tsendContextUsage(ws, runtime, workspaceId, sessionPath);\n}\n\nfunction sendContextUsage(\n\tws: WebSocket,\n\truntime: AgentSessionRuntime,\n\tworkspaceId: string,\n\tsessionPath: string | null,\n): void {\n\tconst usage = runtime.session.getContextUsage();\n\tif (!usage) return;\n\tconst payload: PiContextUsage = {\n\t\tkind: \"context_usage\",\n\t\ttokens: usage.tokens,\n\t\tcontextWindow: usage.contextWindow,\n\t\tpercent: usage.percent,\n\t};\n\tsend(ws, {\n\t\ttype: \"event\",\n\t\tworkspaceId,\n\t\tsessionPath,\n\t\tpayload,\n\t});\n}\n\nfunction detach(state: ConnectionState, ws?: WebSocket): void {\n\tstate.unsubscribeSession?.();\n\tstate.unsubscribeSession = undefined;\n\tstate.unsubscribeRebind?.();\n\tstate.unsubscribeRebind = undefined;\n\tif (state.workspaceId && ws) {\n\t\tworkspaceManager.removeSubscriber(state.workspaceId, ws);\n\t}\n\tstate.workspaceId = undefined;\n}\n\nfunction send(ws: WebSocket, msg: WsServerMessage): void {\n\tif (ws.readyState !== ws.OPEN) return;\n\tws.send(JSON.stringify(msg));\n}\n"],"mappings":"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","names":["readFile","dirname","join","resolve","sep","Hono","stat","basename","isAbsolute","resolve","join","mkdir","readFile","writeFile","dirname","join","join","readFile","mkdir","dirname","writeFile","cache","pending","unlink","isAbsolute","resolve","Type","resolve","text","details","mkdir","readFile","writeFile","dirname","join","join","cache","readFile","save","mkdir","dirname","writeFile","pending","inflight","resolve","extractUserText","isAbsolute","unlink","app","exists","execFile","join","sep","promisify","exec","promisify","execFile","inflight","pending","out","join","sep","app","readdir","join","getAgentDir","join","getAgentDir","readdir","app","isAbsolute","resolve","stat","basename","readdir","homedir","dirname","isAbsolute","join","resolve","Hono","readFile","writeFile","mkdir","dirname","join","Hono","getAgentDir","Hono","join","getAgentDir","readFile","config","mkdir","dirname","writeFile","Hono","dirname","resolve","join","sep","readFile"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/http-proxy.ts","../src/api/workspaces.ts","../src/storage/resource-writer.ts","../src/storage/workspace-registry.ts","../src/storage/atomic-json.ts","../src/storage/workspace-stats.ts","../src/workspace-manager.ts","../src/storage/session-tool-prefs.ts","../src/extensions/todo/schema.ts","../src/extensions/todo/reducer.ts","../src/extensions/todo/factory.ts","../src/extensions/ask_user/schema.ts","../src/extensions/ask_user/registry.ts","../src/extensions/ask_user/factory.ts","../src/extensions/artifact/schema.ts","../src/extensions/artifact/factory.ts","../src/extensions/subagent/agents.ts","../src/extensions/subagent/builtin-agents.ts","../src/extensions/subagent/trust.ts","../src/extensions/subagent/child.ts","../src/extensions/subagent/schema.ts","../src/extensions/subagent/pi-bin.ts","../src/extensions/subagent/registry.ts","../src/extensions/subagent/factory.ts","../src/extensions/web_search/schema.ts","../src/storage/web-search-prefs.ts","../src/extensions/web_search/client.ts","../src/extensions/web_search/factory.ts","../src/storage/builtin-extension-prefs.ts","../src/extensions/index.ts","../src/extensions/ask_user/cleanup.ts","../src/extensions/subagent/cleanup.ts","../src/ws/bridge.ts","../src/ws/extension-ui.ts","../src/api/config.ts","../src/api/files.ts","../src/api/resources.ts","../src/api/tree.ts","../src/api/fs.ts","../src/api/model-configs.ts","../src/api/model-config-keys.ts","../src/api/web-search.ts","../src/ws/hub.ts","../src/security.ts"],"sourcesContent":["/**\n * pi-pilot server entry.\n *\n * Boots one process that serves both HTTP (Hono) and WebSocket (`ws`) on\n * the same port. Localhost only.\n */\n\nimport type { Server as HttpServer } from \"node:http\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, extname, join, resolve, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { serve } from \"@hono/node-server\";\nimport type { Context } from \"hono\";\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport { config } from \"./config.ts\";\nimport { configureHttpProxy } from \"./http-proxy.ts\";\nimport { workspacesRoute } from \"./api/workspaces.ts\";\nimport { fsRoute } from \"./api/fs.ts\";\nimport { modelConfigsRoute } from \"./api/model-configs.ts\";\nimport { webSearchRoute } from \"./api/web-search.ts\";\nimport { attachWsHub } from \"./ws/hub.ts\";\nimport { isAllowedHost } from \"./security.ts\";\nimport { workspaceManager } from \"./workspace-manager.ts\";\nimport { loadBuiltinPrefs } from \"./storage/builtin-extension-prefs.ts\";\nimport { loadSessionToolPrefs } from \"./storage/session-tool-prefs.ts\";\nimport { loadWebSearchPrefs } from \"./storage/web-search-prefs.ts\";\nimport { sweepOrphanedChildrenOnBoot } from \"./extensions/subagent/cleanup.ts\";\nimport { killAllChildren } from \"./extensions/subagent/registry.ts\";\n\n// Honor standard HTTPS_PROXY / HTTP_PROXY / NO_PROXY before any pi LLM call.\n// Must run early — the SDK's provider requests go through undici's global\n// dispatcher, which this installs (no-op/direct when no proxy env var is set).\nconfigureHttpProxy();\n\nconst app = new Hono();\nconst distDir = dirname(fileURLToPath(import.meta.url));\nconst webRoot = resolve(process.env.PI_PILOT_WEB_ROOT ?? join(distDir, \"..\", \"public\"));\nconst webIndexPath = join(webRoot, \"index.html\");\n\nconst mimeTypes: Record<string, string> = {\n\t\".css\": \"text/css; charset=utf-8\",\n\t\".html\": \"text/html; charset=utf-8\",\n\t\".ico\": \"image/x-icon\",\n\t\".js\": \"text/javascript; charset=utf-8\",\n\t\".json\": \"application/json; charset=utf-8\",\n\t\".map\": \"application/json; charset=utf-8\",\n\t\".png\": \"image/png\",\n\t\".svg\": \"image/svg+xml\",\n\t\".txt\": \"text/plain; charset=utf-8\",\n\t\".webp\": \"image/webp\",\n\t\".woff\": \"font/woff\",\n\t\".woff2\": \"font/woff2\",\n};\n\nfunction isApiOrWsPath(pathname: string): boolean {\n\treturn pathname === \"/api\" || pathname.startsWith(\"/api/\") || pathname === \"/ws\";\n}\n\nfunction safeResolveWebPath(pathname: string): string | undefined {\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(pathname);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tconst relativePath = decoded === \"/\" ? \"index.html\" : decoded.replace(/^\\/+/, \"\");\n\tconst candidate = resolve(webRoot, relativePath);\n\tif (candidate !== webRoot && !candidate.startsWith(`${webRoot}${sep}`)) {\n\t\treturn undefined;\n\t}\n\treturn candidate;\n}\n\nasync function readWebFile(path: string): Promise<Buffer | undefined> {\n\ttry {\n\t\treturn await readFile(path);\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tif (code === \"ENOENT\" || code === \"EISDIR\") return undefined;\n\t\tthrow err;\n\t}\n}\n\nasync function serveWeb(c: Context): Promise<Response> {\n\tconst pathname = new URL(c.req.url).pathname;\n\tif (isApiOrWsPath(pathname)) return c.notFound();\n\n\tconst assetPath = safeResolveWebPath(pathname);\n\tif (!assetPath) return c.text(\"invalid asset path\", 400);\n\n\tconst asset = await readWebFile(assetPath);\n\tconst body = asset ?? (await readFile(webIndexPath));\n\tconst filePath = asset ? assetPath : webIndexPath;\n\tconst headers: Record<string, string> = {\n\t\t\"Content-Type\": mimeTypes[extname(filePath)] ?? \"application/octet-stream\",\n\t\t\"Cache-Control\": asset && pathname.startsWith(\"/assets/\")\n\t\t\t? \"public, max-age=31536000, immutable\"\n\t\t\t: \"no-cache\",\n\t};\n\n\treturn new Response(body as unknown as BodyInit, { headers });\n}\n\napp.use(\"*\", async (c, next) => {\n\tif (!isAllowedHost(c.req.header(\"host\"))) {\n\t\treturn c.text(\"Forbidden\", 403);\n\t}\n\tawait next();\n});\n\napp.use(\n\t\"/api/*\",\n\tcors({\n\t\torigin: config.corsOrigin,\n\t\tallowMethods: [\"GET\", \"POST\", \"PUT\", \"DELETE\"],\n\t}),\n);\n\napp.get(\"/api/health\", (c) => c.json({ ok: true }));\napp.route(\"/api/workspaces\", workspacesRoute);\napp.route(\"/api/fs\", fsRoute);\napp.route(\"/api/model-configs\", modelConfigsRoute);\napp.route(\"/api/web-search\", webSearchRoute);\n\nif (existsSync(webIndexPath)) {\n\tapp.get(\"*\", serveWeb);\n} else {\n\tapp.get(\"/\", (c) =>\n\t\tc.text(\n\t\t\t\"pi-pilot server is running, but the web UI assets were not found. \" +\n\t\t\t\t\"Run `pnpm build` from the repository root, or set PI_PILOT_WEB_ROOT to a built web dist directory.\",\n\t\t\t500,\n\t\t),\n\t);\n}\n\n// Load persisted prefs into their in-memory caches before serving. The builtin\n// on/off prefs must be ready before the synchronous factory gate\n// (extensions/index.ts) runs on the first lazily-built runtime; the per-session\n// tool prefs must be ready before that runtime calls reapplyToolPrefs. Top-level\n// await blocks the listen until both resolve. The subagent orphan sweep must\n// also finish before the first runtime build so it can't race a fresh child.\nawait loadBuiltinPrefs();\nawait loadSessionToolPrefs();\nawait loadWebSearchPrefs();\nawait sweepOrphanedChildrenOnBoot();\n\nconst server = serve(\n\t{\n\t\tfetch: app.fetch,\n\t\thostname: config.host,\n\t\tport: config.port,\n\t},\n\t(info) => {\n\t\tconsole.log(`[pi-pilot] http://${info.address}:${info.port}`);\n\t},\n);\n\nattachWsHub(server as unknown as HttpServer);\n\nasync function shutdown(reason: string): Promise<void> {\n\tconsole.log(`[pi-pilot] shutting down (${reason})`);\n\ttry {\n\t\tawait workspaceManager.disposeAll();\n\t} catch (e) {\n\t\tconsole.error(\"[pi-pilot] disposeAll error:\", e);\n\t}\n\t// disposeAll's abort chain should have killed subagent children already;\n\t// sweep stragglers (SIGTERM — the 3s hard-exit below won't wait for the\n\t// SIGKILL escalation, an accepted gap for SIGTERM-ignoring children).\n\tconst sweptChildren = killAllChildren();\n\tif (sweptChildren > 0) {\n\t\tconsole.warn(`[pi-pilot] killed ${sweptChildren} lingering subagent child(ren)`);\n\t}\n\tserver.close(() => process.exit(0));\n\t// Hard-kill if close hangs.\n\tsetTimeout(() => process.exit(1), 3000).unref();\n}\n\nprocess.on(\"unhandledRejection\", (reason) => {\n\tconsole.error(\"[pi-pilot] unhandled rejection (process kept alive):\", reason);\n});\n\nprocess.on(\"uncaughtException\", (err) => {\n\tconsole.error(\"[pi-pilot] uncaught exception:\", err);\n\tvoid shutdown(\"uncaughtException\");\n});\n\nprocess.on(\"SIGINT\", () => void shutdown(\"SIGINT\"));\nprocess.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\"));\n","/**\n * Process-wide config, sourced from env vars.\n *\n * We do not read any file at startup beyond what pi's SDK does internally.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const config = {\n\t/** HTTP + WS port. */\n\tport: Number(process.env.PI_PILOT_PORT ?? 5174),\n\n\t/** Bind address. Hard-coded localhost; do NOT make this configurable\n\t * without first reviewing the security implications of exposing bash. */\n\thost: \"127.0.0.1\",\n\n\t/** Where pi-pilot stores its own (non-pi) state. */\n\tdataDir: process.env.PI_PILOT_DATA_DIR ?? join(homedir(), \".pi\", \"webui\"),\n\n\t/** Dev origin allowed by CORS for /api. The Vite dev server. */\n\tcorsOrigin: process.env.PI_PILOT_CORS_ORIGIN ?? \"http://localhost:5173\",\n} as const;\n","/**\n * Route every outbound HTTP(S) request — including all pi LLM provider calls —\n * through the proxy named in the standard `HTTPS_PROXY` / `HTTP_PROXY` /\n * `NO_PROXY` env vars (lowercase variants honored too).\n *\n * pi's own CLI does this in `cli.ts` via `configureHttpDispatcher()`. pi-pilot\n * embeds the SDK in-process and never hits that entry, so without this the\n * provider SDKs (Anthropic / OpenAI / Gemini / Mistral — all go through the\n * global `fetch`/undici dispatcher) ignore proxy env vars entirely. We replicate\n * the dispatcher install here and call it once at startup, before the first\n * runtime is built.\n *\n * `undici` is pinned to the SDK's bundled version (8.3.0) so the dispatcher we\n * set is the same implementation the SDK's fetch reads from.\n */\n\nimport { EnvHttpProxyAgent, install, setGlobalDispatcher } from \"undici\";\n\nlet configured = false;\n\n/**\n * Install an `EnvHttpProxyAgent` as undici's global dispatcher. Idempotent.\n *\n * Safe to call unconditionally: when no proxy env var is set, the agent\n * degrades to a direct-connection `Agent`, so behavior is unchanged for users\n * who don't use a proxy. The agent reads the env at construction time, hence\n * this must run early (it does — before any LLM request).\n */\nexport function configureHttpProxy(): void {\n\tif (configured) return;\n\tconfigured = true;\n\t// allowH2:false + 300s idle timeout mirror pi's `core/http-dispatcher.ts`,\n\t// tuned for long-lived LLM streaming responses.\n\tsetGlobalDispatcher(\n\t\tnew EnvHttpProxyAgent({\n\t\t\tallowH2: false,\n\t\t\tbodyTimeout: 300_000,\n\t\t\theadersTimeout: 300_000,\n\t\t}),\n\t);\n\t// Keep `fetch` and the dispatcher on the same undici implementation (also\n\t// mirrors pi): avoids a mismatch where newer Node's bundled fetch consumes a\n\t// compressed response through the npm-undici dispatcher without decompressing.\n\tinstall?.();\n}\n","/**\n * /api/workspaces routes.\n */\n\nimport { readFile, stat } from \"node:fs/promises\";\nimport { basename, isAbsolute, resolve } from \"node:path\";\nimport { Hono } from \"hono\";\nimport type {\n\tAddWorkspaceRequest,\n\tAddWorkspaceResponse,\n\tExportSessionResponse,\n\tHistoryResponse,\n\tListForkPointsResponse,\n\tListSessionsResponse,\n\tListWorkspacesResponse,\n\tOkResponse,\n\tReorderWorkspacesRequest,\n\tUpdateWorkspaceRequest,\n\tUpdateWorkspaceResponse,\n} from \"@pi-pilot/shared\";\nimport { HttpError } from \"../storage/resource-writer.ts\";\nimport {\n\taddWorkspace,\n\tgetWorkspace,\n\tlistWorkspaces,\n\tremoveWorkspace,\n\treorderWorkspaces,\n\tsetWorkspaceTrustProjectAgents,\n} from \"../storage/workspace-registry.ts\";\nimport { enrichWorkspace } from \"../storage/workspace-stats.ts\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { mountConfigRoutes } from \"./config.ts\";\nimport { mountFilesRoute } from \"./files.ts\";\nimport { mountResourcesRoute } from \"./resources.ts\";\nimport { treeRoute } from \"./tree.ts\";\n\nexport const workspacesRoute = new Hono();\n\nworkspacesRoute.get(\"/\", async (c) => {\n\tconst raw = await listWorkspaces();\n\tconst workspaces = await Promise.all(raw.map(enrichWorkspace));\n\tconst body: ListWorkspacesResponse = { workspaces };\n\treturn c.json(body);\n});\n\n// IMPORTANT: /reorder is registered BEFORE /:id routes so Hono matches it\n// literally; a future PUT /:id route would otherwise capture \"reorder\" as a param.\nworkspacesRoute.put(\"/reorder\", async (c) => {\n\tconst body = (await c.req.json()) as ReorderWorkspacesRequest;\n\tif (\n\t\t!body?.ids ||\n\t\t!Array.isArray(body.ids) ||\n\t\tbody.ids.length === 0 ||\n\t\t!body.ids.every((id) => typeof id === \"string\")\n\t) {\n\t\treturn c.json(\n\t\t\t{ ok: false, error: \"ids must be a non-empty array of strings\" },\n\t\t\t400,\n\t\t);\n\t}\n\tawait reorderWorkspaces(body.ids);\n\tconst raw = await listWorkspaces();\n\tconst workspaces = await Promise.all(raw.map(enrichWorkspace));\n\tconst resBody: ListWorkspacesResponse = { workspaces };\n\treturn c.json(resBody);\n});\n\nworkspacesRoute.get(\"/:id/sessions\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst sessions = await workspaceManager.listSessions(id);\n\t\tconst body: ListSessionsResponse = { sessions };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] list sessions for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.delete(\"/:id/sessions\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\tconst sessionPath = c.req.query(\"path\");\n\tif (!sessionPath) {\n\t\treturn c.json({ ok: false, error: \"path query is required\" }, 400);\n\t}\n\n\ttry {\n\t\tawait workspaceManager.deleteSession(id, sessionPath);\n\t\tconst body: OkResponse = { ok: true };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tif (err instanceof HttpError) {\n\t\t\treturn c.json(\n\t\t\t\t{ ok: false, error: err.message },\n\t\t\t\terr.status as 400 | 404 | 409 | 500,\n\t\t\t);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] delete session for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.get(\"/:id/fork-points\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\tconst raw = runtime.session.getUserMessagesForForking();\n\t\tconst points = raw.map((p) => ({\n\t\t\tentryId: p.entryId,\n\t\t\ttext: p.text.length > 120 ? p.text.slice(0, 120) + \"…\" : p.text,\n\t\t}));\n\t\tconst body: ListForkPointsResponse = { points };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] fork-points for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.get(\"/:id/export\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst sessionPath = c.req.query(\"sessionPath\");\n\t\tif (sessionPath) {\n\t\t\tconst err = await workspaceManager.validateSessionOwnership(id, sessionPath);\n\t\t\tif (err) return c.json({ ok: false, error: err }, 404);\n\t\t}\n\t\tconst runtime = await workspaceManager.getOrCreate(id, sessionPath || undefined);\n\t\tconst outputPath = await runtime.session.exportToHtml();\n\t\tconst html = await readFile(outputPath, \"utf-8\");\n\t\tconst filename = basename(outputPath);\n\t\tconst body: ExportSessionResponse = { html, filename };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] export for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.get(\"/:id/history\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tawait workspaceManager.getOrCreate(id);\n\t\tconst sessionPath = c.req.query(\"sessionPath\");\n\t\tconst body: HistoryResponse = workspaceManager.getSessionHistory(id, sessionPath);\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] history for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\nworkspacesRoute.post(\"/\", async (c) => {\n\tconst body = (await c.req.json()) as AddWorkspaceRequest;\n\tif (!body?.path || typeof body.path !== \"string\") {\n\t\treturn c.json({ ok: false, error: \"path is required\" }, 400);\n\t}\n\tif (!isAbsolute(body.path)) {\n\t\treturn c.json({ ok: false, error: \"path must be absolute\" }, 400);\n\t}\n\tconst resolved = resolve(body.path);\n\ttry {\n\t\tconst st = await stat(resolved);\n\t\tif (!st.isDirectory()) {\n\t\t\treturn c.json({ ok: false, error: \"path is not a directory\" }, 400);\n\t\t}\n\t} catch {\n\t\treturn c.json({ ok: false, error: \"path does not exist\" }, 400);\n\t}\n\n\tconst stored = await addWorkspace({\n\t\tpath: resolved,\n\t\tname: body.name?.trim() || basename(resolved) || resolved,\n\t});\n\tconst ws = await enrichWorkspace(stored);\n\tconst res: AddWorkspaceResponse = { workspace: ws };\n\treturn c.json(res);\n});\n\nworkspacesRoute.delete(\"/:id\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t// Dispose the runtime if it was open. Best-effort.\n\tawait workspaceManager.dispose(id);\n\tawait removeWorkspace(id);\n\n\tconst body: OkResponse = { ok: true };\n\treturn c.json(body);\n});\n\n// Mutable workspace settings. Only `trustProjectAgents` today — the opt-in\n// gate for repo-controlled subagent definitions (server-side state, never a\n// model-controllable argument). Applies to live sessions immediately: the\n// subagent extension re-reads workspaces.json on every call.\nworkspacesRoute.patch(\"/:id\", async (c) => {\n\tconst id = c.req.param(\"id\");\n\tconst body = (await c.req.json()) as UpdateWorkspaceRequest;\n\tif (typeof body?.trustProjectAgents !== \"boolean\") {\n\t\treturn c.json({ ok: false, error: \"trustProjectAgents must be a boolean\" }, 400);\n\t}\n\tconst updated = await setWorkspaceTrustProjectAgents(id, body.trustProjectAgents);\n\tif (!updated) return c.json({ ok: false, error: \"not found\" }, 404);\n\tconst res: UpdateWorkspaceResponse = { workspace: await enrichWorkspace(updated) };\n\treturn c.json(res);\n});\n\nmountConfigRoutes(workspacesRoute);\nmountResourcesRoute(workspacesRoute);\nmountFilesRoute(workspacesRoute);\nworkspacesRoute.route(\"/:id/tree\", treeRoute);\n","/**\n * Filesystem CRUD for skills and prompt templates.\n *\n * pi's ResourceLoader is read-only from our perspective (`reload()` re-reads\n * from disk but doesn't write). So pi-pilot writes the files itself and then\n * asks the loader to re-scan.\n *\n * Layout (matches pi's discovery rules — see docs/skills.md, docs/prompt-templates.md):\n *\n * skills:\n * user → <agentDir>/skills/<name>/SKILL.md\n * project → <workspaceCwd>/.pi/skills/<name>/SKILL.md\n *\n * prompts:\n * user → <agentDir>/prompts/<name>.md\n * project → <workspaceCwd>/.pi/prompts/<name>.md\n *\n * Safety\n * - `name` is restricted to lowercase a-z / 0-9 / hyphens (skills) and\n * additionally underscores (prompts) so it cannot escape its parent dir.\n * - `assertUnder(path, allowedRoots)` double-checks the resolved file path\n * is actually inside one of the allowed roots before any write/unlink.\n * - We never delete a directory that contains anything other than known\n * skill content via this module \\u2014 see `deleteSkill` for the precise rule.\n *\n * Frontmatter handling\n * - Known fields (name, description, disable-model-invocation /\n * argument-hint) are parsed into typed slots and exposed to the\n * editor as discrete form inputs.\n * - Unknown keys (`allowed-tools`, `metadata`, `license`, \\u2026) are\n * captured as an opaque list of raw `key: value` lines and round-\n * tripped on save. The editor never displays or rewrites them; the\n * write path concatenates them back below the known fields in the\n * order they appeared in the source. This means custom YAML survives\n * edits, but multi-line scalars / anchors / nested maps still won't\n * because the parser is line-oriented (acceptable: pi's documented\n * skill/prompt fields are all single-line scalars).\n */\n\nimport {\n\tmkdir,\n\treadFile,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, resolve, sep } from \"node:path\";\nimport type { ResourceScope } from \"@pi-pilot/shared\";\n\n// ---------- path helpers ----------\n\nconst SKILL_NAME_RE = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;\nconst PROMPT_NAME_RE = /^[a-z0-9_](?:[a-z0-9_-]{0,62}[a-z0-9_])?$/;\n\nfunction ensureSkillName(name: string): void {\n\tif (!SKILL_NAME_RE.test(name)) {\n\t\tthrow new HttpError(400, \"skill name must be lowercase a-z/0-9/hyphens, 1-64 chars, no leading/trailing/double hyphens\");\n\t}\n}\n\nfunction ensurePromptName(name: string): void {\n\tif (!PROMPT_NAME_RE.test(name)) {\n\t\tthrow new HttpError(400, \"prompt name must be lowercase a-z/0-9/hyphens/underscores, 1-64 chars\");\n\t}\n}\n\n/**\n * Throw if `target` does not resolve to a descendant of any path in `roots`.\n * Prevents `../../etc/passwd` style escapes even though the name regex above\n * already forbids slashes \\u2014 belt + suspenders.\n */\nfunction assertUnder(target: string, roots: string[]): void {\n\tconst t = resolve(target);\n\tconst ok = roots.some((root) => {\n\t\tconst r = resolve(root);\n\t\treturn t === r || t.startsWith(r + sep);\n\t});\n\tif (!ok) {\n\t\tthrow new HttpError(500, `refusing to touch path outside managed roots: ${target}`);\n\t}\n}\n\nfunction skillDirFor(scope: ResourceScope, name: string, roots: ResourceRoots): string {\n\tensureSkillName(name);\n\tconst base = scope === \"user\" ? roots.userSkills : roots.projectSkills;\n\treturn join(base, name);\n}\n\nfunction promptFileFor(scope: ResourceScope, name: string, roots: ResourceRoots): string {\n\tensurePromptName(name);\n\tconst base = scope === \"user\" ? roots.userPrompts : roots.projectPrompts;\n\treturn join(base, `${name}.md`);\n}\n\nexport interface ResourceRoots {\n\t/** ~/.pi/agent/skills */\n\tuserSkills: string;\n\t/** <cwd>/.pi/skills */\n\tprojectSkills: string;\n\t/** ~/.pi/agent/prompts */\n\tuserPrompts: string;\n\t/** <cwd>/.pi/prompts */\n\tprojectPrompts: string;\n}\n\nexport function resolveResourceRoots(opts: {\n\tagentDir: string;\n\tworkspaceCwd: string;\n}): ResourceRoots {\n\treturn {\n\t\tuserSkills: join(opts.agentDir, \"skills\"),\n\t\tprojectSkills: join(opts.workspaceCwd, \".pi\", \"skills\"),\n\t\tuserPrompts: join(opts.agentDir, \"prompts\"),\n\t\tprojectPrompts: join(opts.workspaceCwd, \".pi\", \"prompts\"),\n\t};\n}\n\n/** Map an absolute file path to its scope, by checking which root it sits in. */\nexport function scopeFor(absPath: string, roots: ResourceRoots): ResourceScope | undefined {\n\tconst p = resolve(absPath);\n\tfor (const [root, scope] of [\n\t\t[roots.userSkills, \"user\"],\n\t\t[roots.projectSkills, \"project\"],\n\t\t[roots.userPrompts, \"user\"],\n\t\t[roots.projectPrompts, \"project\"],\n\t] as const) {\n\t\tconst r = resolve(root);\n\t\tif (p === r || p.startsWith(r + sep)) return scope;\n\t}\n\treturn undefined;\n}\n\n// ---------- frontmatter ----------\n\n/** A frontmatter line we recognized. `raw` is the original `key: value`\n * text, used to round-trip unknown keys verbatim. */\ninterface FrontmatterLine {\n\tkey: string;\n\tvalue: string | boolean;\n\traw: string;\n}\n\ninterface ParsedFile {\n\tfrontmatter: Record<string, string | boolean>;\n\t/** Every frontmatter line in source order, with the original raw text. */\n\tlines: FrontmatterLine[];\n\tbody: string;\n}\n\n/**\n * Tiny YAML-frontmatter reader. Recognises `key: value`, `key: \"value\"`,\n * and `key: true|false`. Quoted strings honour `\\\"` and `\\\\` escapes;\n * everything else is treated as a raw scalar (trimmed). This is enough for\n * pi's documented skill/prompt frontmatter fields. Files with multi-line\n * scalars, anchors, etc. won't round-trip cleanly through pi-pilot.\n */\nfunction parseFile(content: string): ParsedFile {\n\tconst rawLines = content.split(/\\r?\\n/);\n\tif (rawLines[0] !== \"---\") {\n\t\treturn { frontmatter: {}, lines: [], body: content };\n\t}\n\tconst fm: Record<string, string | boolean> = {};\n\tconst lines: FrontmatterLine[] = [];\n\tlet i = 1;\n\twhile (i < rawLines.length && rawLines[i] !== \"---\") {\n\t\tconst line = rawLines[i] ?? \"\";\n\t\tconst m = line.match(/^([A-Za-z][A-Za-z0-9_-]*)\\s*:\\s*(.*?)\\s*$/);\n\t\tif (m) {\n\t\t\tconst key = m[1]!;\n\t\t\tconst rawVal = m[2] ?? \"\";\n\t\t\tconst value = parseScalar(rawVal);\n\t\t\tfm[key] = value;\n\t\t\tlines.push({ key, value, raw: line });\n\t\t}\n\t\ti++;\n\t}\n\tif (i >= rawLines.length) {\n\t\t// No closing `---` \\u2014 treat the whole thing as body, no frontmatter.\n\t\treturn { frontmatter: {}, lines: [], body: content };\n\t}\n\tconst body = rawLines.slice(i + 1).join(\"\\n\");\n\treturn { frontmatter: fm, lines, body };\n}\n\n/**\n * Filter `lines` (every frontmatter entry as it appeared on disk) down\n * to the entries whose keys are *not* in `knownKeys`. Used to preserve\n * arbitrary frontmatter (`allowed-tools`, `metadata`, `license`, \\u2026) on\n * write.\n *\n * Returns the raw source text of each kept line (without the trailing\n * newline). The build path emits these verbatim so values keep their\n * original quoting style.\n */\nfunction extraLines(lines: FrontmatterLine[], knownKeys: readonly string[]): string[] {\n\tconst known = new Set(knownKeys);\n\treturn lines.filter((l) => !known.has(l.key)).map((l) => l.raw);\n}\n\nfunction parseScalar(raw: string): string | boolean {\n\tif (raw === \"true\") return true;\n\tif (raw === \"false\") return false;\n\tif (raw.startsWith('\"') && raw.endsWith('\"') && raw.length >= 2) {\n\t\treturn raw.slice(1, -1).replace(/\\\\([\"\\\\])/g, \"$1\");\n\t}\n\tif (raw.startsWith(\"'\") && raw.endsWith(\"'\") && raw.length >= 2) {\n\t\treturn raw.slice(1, -1).replace(/''/g, \"'\");\n\t}\n\treturn raw;\n}\n\n/**\n * Emit a frontmatter+body file. Field order is stable so re-saves produce\n * deterministic diffs:\n *\n * 1. Known fields in the caller-supplied order (skipping undefined / \"\").\n * 2. Extra raw lines (unknown frontmatter keys captured on read) appended\n * verbatim. They keep their original ordering relative to each other.\n *\n * Boolean/string known values only; arrays and nested maps round-trip via\n * the raw `extras` channel only.\n */\nfunction buildFile(\n\tfields: Array<[string, string | boolean | undefined]>,\n\tbody: string,\n\textras: string[] = [],\n): string {\n\tconst out: string[] = [\"---\"];\n\tfor (const [key, value] of fields) {\n\t\tif (value === undefined || value === \"\") continue;\n\t\tif (typeof value === \"boolean\") {\n\t\t\tout.push(`${key}: ${value ? \"true\" : \"false\"}`);\n\t\t} else {\n\t\t\tout.push(`${key}: ${formatString(value)}`);\n\t\t}\n\t}\n\tfor (const raw of extras) out.push(raw);\n\tout.push(\"---\");\n\tconst trimmedBody = body.replace(/\\s+$/, \"\");\n\tconst text = `${out.join(\"\\n\")}\\n${trimmedBody}\\n`;\n\treturn text;\n}\n\n/** Frontmatter keys we render as form inputs. Everything else is opaque\n * passthrough. Keep these arrays here so read and write agree. */\nconst SKILL_KNOWN_KEYS = [\"name\", \"description\", \"disable-model-invocation\"] as const;\nconst PROMPT_KNOWN_KEYS = [\"description\", \"argument-hint\"] as const;\n\nfunction formatString(value: string): string {\n\t// Always double-quote so embedded `:`, `#`, leading/trailing whitespace,\n\t// and the like don't break YAML parsing. Escape `\\` and `\"`.\n\tconst escaped = value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n\treturn `\"${escaped}\"`;\n}\n\n// ---------- skills ----------\n\nexport interface CreateSkillOpts {\n\troots: ResourceRoots;\n\tscope: ResourceScope;\n\tname: string;\n\tdescription: string;\n\tbody: string;\n\tdisableModelInvocation?: boolean;\n}\n\nexport async function createSkill(opts: CreateSkillOpts): Promise<string> {\n\tconst dir = skillDirFor(opts.scope, opts.name, opts.roots);\n\tassertUnder(dir, [opts.roots.userSkills, opts.roots.projectSkills]);\n\tif (await exists(dir)) {\n\t\tthrow new HttpError(409, `skill already exists at ${dir}`);\n\t}\n\tconst file = join(dir, \"SKILL.md\");\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"name\", opts.name],\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"disable-model-invocation\", opts.disableModelInvocation],\n\t\t],\n\t\topts.body,\n\t);\n\tawait mkdir(dir, { recursive: true });\n\tawait writeFile(file, text, \"utf8\");\n\treturn file;\n}\n\nexport interface UpdateSkillOpts {\n\troots: ResourceRoots;\n\tfilePath: string;\n\tname: string;\n\tdescription: string;\n\tbody: string;\n\tdisableModelInvocation?: boolean;\n}\n\nexport async function updateSkill(opts: UpdateSkillOpts): Promise<string> {\n\tif (!isAbsolute(opts.filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(opts.filePath, [opts.roots.userSkills, opts.roots.projectSkills]);\n\tif (!(await exists(opts.filePath))) {\n\t\tthrow new HttpError(404, `skill file not found: ${opts.filePath}`);\n\t}\n\t// Round-trip arbitrary frontmatter (`allowed-tools`, `metadata`, \\u2026)\n\t// from the existing file. Re-read each save so a side edit on disk\n\t// between open and save isn't clobbered.\n\tconst existing = await readFile(opts.filePath, \"utf8\");\n\tconst extras = extraLines(parseFile(existing).lines, SKILL_KNOWN_KEYS);\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"name\", opts.name],\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"disable-model-invocation\", opts.disableModelInvocation],\n\t\t],\n\t\topts.body,\n\t\textras,\n\t);\n\tawait writeFile(opts.filePath, text, \"utf8\");\n\treturn opts.filePath;\n}\n\nexport async function deleteSkill(filePath: string, roots: ResourceRoots): Promise<void> {\n\tif (!isAbsolute(filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(filePath, [roots.userSkills, roots.projectSkills]);\n\tif (!(await exists(filePath))) {\n\t\tthrow new HttpError(404, `skill file not found: ${filePath}`);\n\t}\n\t// A skill can be either a bare .md file (root-level under skills/) or a\n\t// SKILL.md inside its own directory. Match pi's discovery: if the parent\n\t// directory is itself the skills root, delete just the .md file;\n\t// otherwise delete the parent directory (skill + scripts + assets).\n\tconst dir = dirname(filePath);\n\tconst parentIsRoot =\n\t\tresolve(dir) === resolve(roots.userSkills) ||\n\t\tresolve(dir) === resolve(roots.projectSkills);\n\tif (parentIsRoot) {\n\t\tawait unlink(filePath);\n\t\treturn;\n\t}\n\tassertUnder(dir, [roots.userSkills, roots.projectSkills]);\n\tawait rm(dir, { recursive: true, force: true });\n}\n\n// ---------- prompts ----------\n\nexport interface CreatePromptOpts {\n\troots: ResourceRoots;\n\tscope: ResourceScope;\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tbody: string;\n}\n\nexport async function createPrompt(opts: CreatePromptOpts): Promise<string> {\n\tconst file = promptFileFor(opts.scope, opts.name, opts.roots);\n\tassertUnder(file, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\tif (await exists(file)) {\n\t\tthrow new HttpError(409, `prompt already exists at ${file}`);\n\t}\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"argument-hint\", opts.argumentHint],\n\t\t],\n\t\topts.body,\n\t);\n\tawait mkdir(dirname(file), { recursive: true });\n\tawait writeFile(file, text, \"utf8\");\n\treturn file;\n}\n\nexport interface UpdatePromptOpts {\n\troots: ResourceRoots;\n\tfilePath: string;\n\t/** Stem of the new filename. If different from the existing file's stem,\n\t * the file is renamed. */\n\tname: string;\n\tdescription: string;\n\targumentHint?: string;\n\tbody: string;\n}\n\nexport async function updatePrompt(opts: UpdatePromptOpts): Promise<string> {\n\tif (!isAbsolute(opts.filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(opts.filePath, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\tif (!(await exists(opts.filePath))) {\n\t\tthrow new HttpError(404, `prompt file not found: ${opts.filePath}`);\n\t}\n\tensurePromptName(opts.name);\n\tconst dir = dirname(opts.filePath);\n\tconst newPath = join(dir, `${opts.name}.md`);\n\tassertUnder(newPath, [opts.roots.userPrompts, opts.roots.projectPrompts]);\n\t// Preserve arbitrary frontmatter from the existing file (re-read so a\n\t// concurrent on-disk edit doesn't get silently dropped).\n\tconst existing = await readFile(opts.filePath, \"utf8\");\n\tconst extras = extraLines(parseFile(existing).lines, PROMPT_KNOWN_KEYS);\n\tconst text = buildFile(\n\t\t[\n\t\t\t[\"description\", opts.description],\n\t\t\t[\"argument-hint\", opts.argumentHint],\n\t\t],\n\t\topts.body,\n\t\textras,\n\t);\n\tif (resolve(newPath) !== resolve(opts.filePath)) {\n\t\tif (await exists(newPath)) {\n\t\t\tthrow new HttpError(409, `prompt already exists at ${newPath}`);\n\t\t}\n\t\t// Rename is NOT a single syscall here — we have to write the new\n\t\t// content under a different name and then drop the old file. If the\n\t\t// unlink fails (perms, file lock, cross-fs handle, etc.) we must roll\n\t\t// back the new file; otherwise `loader.reload()` would surface an\n\t\t// orphaned `/oldname` prompt the user never asked to keep.\n\t\tawait writeFile(newPath, text, \"utf8\");\n\t\ttry {\n\t\t\tawait unlink(opts.filePath);\n\t\t} catch (err) {\n\t\t\tawait unlink(newPath).catch(() => undefined);\n\t\t\tthrow err;\n\t\t}\n\t\treturn newPath;\n\t}\n\tawait writeFile(opts.filePath, text, \"utf8\");\n\treturn opts.filePath;\n}\n\nexport async function deletePrompt(filePath: string, roots: ResourceRoots): Promise<void> {\n\tif (!isAbsolute(filePath)) {\n\t\tthrow new HttpError(400, \"filePath must be absolute\");\n\t}\n\tassertUnder(filePath, [roots.userPrompts, roots.projectPrompts]);\n\tif (!(await exists(filePath))) {\n\t\tthrow new HttpError(404, `prompt file not found: ${filePath}`);\n\t}\n\tawait unlink(filePath);\n}\n\n// ---------- reads (for editor pre-fill) ----------\n\nexport interface ReadResourceFileResult {\n\tbody: string;\n\tname: string;\n\tdescription: string;\n\tdisableModelInvocation?: boolean;\n\targumentHint?: string;\n}\n\nexport async function readSkillFile(filePath: string, roots: ResourceRoots): Promise<ReadResourceFileResult> {\n\tassertUnder(filePath, [roots.userSkills, roots.projectSkills]);\n\tconst text = await readFile(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFile(text);\n\treturn {\n\t\tbody,\n\t\tname: stringOr(frontmatter.name, \"\"),\n\t\tdescription: stringOr(frontmatter.description, \"\"),\n\t\tdisableModelInvocation: booleanOr(frontmatter[\"disable-model-invocation\"], undefined),\n\t};\n}\n\nexport async function readPromptFile(filePath: string, roots: ResourceRoots): Promise<ReadResourceFileResult> {\n\tassertUnder(filePath, [roots.userPrompts, roots.projectPrompts]);\n\tconst text = await readFile(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFile(text);\n\t// Prompt's \"name\" is the filename stem, not a frontmatter field.\n\tconst stem = basename(filePath, \".md\");\n\treturn {\n\t\tbody,\n\t\tname: stem,\n\t\tdescription: stringOr(frontmatter.description, \"\"),\n\t\targumentHint: stringOr(frontmatter[\"argument-hint\"], undefined),\n\t};\n}\n\nfunction stringOr<T>(value: unknown, fallback: T): string | T {\n\treturn typeof value === \"string\" ? value : fallback;\n}\n\nfunction booleanOr<T>(value: unknown, fallback: T): boolean | T {\n\treturn typeof value === \"boolean\" ? value : fallback;\n}\n\n// ---------- misc ----------\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Tagged error so the route layer can map to specific HTTP statuses without\n * losing the message. Anything else thrown becomes a 500.\n */\nexport class HttpError extends Error {\n\tconstructor(public readonly status: number, message: string) {\n\t\tsuper(message);\n\t}\n}\n","/**\n * Persistence for the list of workspaces the user has registered.\n *\n * pi itself doesn't need this — it's tied to a single cwd at a time. We need\n * it because the WebUI shows a multi-workspace picker.\n *\n * File format (~/.pi/webui/workspaces.json):\n * { \"workspaces\": [{ \"id\", \"name\", \"path\", \"addedAt\" }] }\n *\n * Storage holds only the *persistent* fields. Derived telemetry\n * (gitBranch, fileCount) is added by the API layer via `enrichWorkspace`\n * — see `workspace-stats.ts`. Keeping them out of storage avoids stale\n * branch names on disk when the user switches branches outside the app.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { config } from \"../config.ts\";\nimport { writeJsonAtomic } from \"./atomic-json.ts\";\n\n/** Persistent shape on disk. API responses augment this with derived fields. */\nexport interface StoredWorkspace {\n\tid: string;\n\tname: string;\n\tpath: string;\n\t/** ISO-8601 */\n\taddedAt: string;\n\t/**\n\t * Opt-in trust for repo-controlled subagent definitions\n\t * (`<path>/.pi/agents/*.md`). Absent/false = untrusted: project agent\n\t * files are ignored (they're an injection vector — see\n\t * extensions/subagent/trust.ts, which reads this file per call).\n\t */\n\ttrustProjectAgents?: boolean;\n}\n\nconst REGISTRY_PATH = join(config.dataDir, \"workspaces.json\");\n\ninterface RegistryFile {\n\tworkspaces: StoredWorkspace[];\n}\n\nlet cache: RegistryFile | undefined;\n\n/**\n * Serialize all write operations to prevent lost-update races when concurrent\n * requests (e.g. rapid reorder + add + delete) interleave read→modify→write.\n */\nlet writeChain: Promise<void> = Promise.resolve();\nfunction serializedWrite(fn: () => Promise<void>): Promise<void> {\n\twriteChain = writeChain.then(fn, fn);\n\treturn writeChain;\n}\n\nasync function load(): Promise<RegistryFile> {\n\tif (cache) return cache;\n\ttry {\n\t\tconst raw = await readFile(REGISTRY_PATH, \"utf8\");\n\t\tcache = JSON.parse(raw) as RegistryFile;\n\t\tif (!Array.isArray(cache.workspaces)) cache = { workspaces: [] };\n\t} catch (err) {\n\t\tif ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\tcache = { workspaces: [] };\n\t\t} else {\n\t\t\tthrow err;\n\t\t}\n\t}\n\treturn cache;\n}\n\nasync function save(): Promise<void> {\n\tif (!cache) return;\n\tawait writeJsonAtomic(REGISTRY_PATH, cache);\n}\n\nexport async function listWorkspaces(): Promise<StoredWorkspace[]> {\n\tconst r = await load();\n\treturn [...r.workspaces];\n}\n\nexport async function getWorkspace(id: string): Promise<StoredWorkspace | undefined> {\n\tconst r = await load();\n\treturn r.workspaces.find((w) => w.id === id);\n}\n\nexport async function getWorkspaceByPath(path: string): Promise<StoredWorkspace | undefined> {\n\tconst r = await load();\n\treturn r.workspaces.find((w) => w.path === path);\n}\n\nexport async function addWorkspace(input: { path: string; name: string }): Promise<StoredWorkspace> {\n\tlet result: StoredWorkspace | undefined;\n\tawait serializedWrite(async () => {\n\t\tconst r = await load();\n\t\tconst existing = r.workspaces.find((w) => w.path === input.path);\n\t\tif (existing) {\n\t\t\tresult = existing;\n\t\t\treturn;\n\t\t}\n\t\tconst ws: StoredWorkspace = {\n\t\t\tid: randomUUID(),\n\t\t\tname: input.name,\n\t\t\tpath: input.path,\n\t\t\taddedAt: new Date().toISOString(),\n\t\t};\n\t\tr.workspaces.push(ws);\n\t\tawait save();\n\t\tresult = ws;\n\t});\n\treturn result!;\n}\n\nexport async function removeWorkspace(id: string): Promise<boolean> {\n\tlet removed = false;\n\tawait serializedWrite(async () => {\n\t\tconst r = await load();\n\t\tconst before = r.workspaces.length;\n\t\tr.workspaces = r.workspaces.filter((w) => w.id !== id);\n\t\tif (r.workspaces.length === before) return;\n\t\tremoved = true;\n\t\tawait save();\n\t});\n\treturn removed;\n}\n\n/**\n * Flip the per-workspace `trustProjectAgents` opt-in. Returns the updated\n * record, or undefined when the id is unknown. Persisted immediately —\n * the subagent extension re-reads workspaces.json per call, so the change\n * applies to live sessions without a runtime rebuild.\n */\nexport async function setWorkspaceTrustProjectAgents(\n\tid: string,\n\ttrusted: boolean,\n): Promise<StoredWorkspace | undefined> {\n\tlet updated: StoredWorkspace | undefined;\n\tawait serializedWrite(async () => {\n\t\tconst r = await load();\n\t\tconst ws = r.workspaces.find((w) => w.id === id);\n\t\tif (!ws) return;\n\t\tif (trusted) ws.trustProjectAgents = true;\n\t\telse delete ws.trustProjectAgents; // keep the file shape minimal when off\n\t\tawait save();\n\t\tupdated = ws;\n\t});\n\treturn updated;\n}\n\n/**\n * Reorder workspaces to match the given ID sequence.\n * IDs not present in the list are appended at the end in their original order.\n */\nexport async function reorderWorkspaces(ids: string[]): Promise<void> {\n\tawait serializedWrite(async () => {\n\t\tconst r = await load();\n\t\tconst byId = new Map(r.workspaces.map((w) => [w.id, w]));\n\t\tconst reordered: typeof r.workspaces = [];\n\t\tconst seen = new Set<string>();\n\t\tfor (const id of ids) {\n\t\t\tconst ws = byId.get(id);\n\t\t\tif (ws && !seen.has(id)) {\n\t\t\t\treordered.push(ws);\n\t\t\t\tseen.add(id);\n\t\t\t}\n\t\t}\n\t\t// Append any remaining workspaces not in the ids list\n\t\tfor (const ws of r.workspaces) {\n\t\t\tif (!seen.has(ws.id)) reordered.push(ws);\n\t\t}\n\t\tr.workspaces = reordered;\n\t\tawait save();\n\t});\n}\n","/**\n * Atomic JSON persistence — write temp file then rename into place.\n *\n * Prevents truncated/corrupt JSON when the process crashes or the disk fills\n * mid-write. The scratch file gets a unique name so two concurrent writers to\n * the same path never share it (a fixed `<path>.tmp` would let one writer's\n * rename race the other's write — the corruption this helper exists to\n * prevent), meaning callers don't have to serialize. Optional 0600 mode for\n * secret-bearing files.\n */\n\nimport { chmod, mkdir, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\n\nexport async function writeJsonAtomic(\n\tfilePath: string,\n\tdata: unknown,\n\topts?: { mode?: number },\n): Promise<void> {\n\tawait mkdir(dirname(filePath), { recursive: true });\n\tconst tmp = `${filePath}.${randomUUID()}.tmp`;\n\tconst text = JSON.stringify(data, null, 2);\n\ttry {\n\t\tif (opts?.mode !== undefined) {\n\t\t\tawait writeFile(tmp, text, { encoding: \"utf8\", mode: opts.mode });\n\t\t} else {\n\t\t\tawait writeFile(tmp, text, \"utf8\");\n\t\t}\n\t\tawait rename(tmp, filePath);\n\t} catch (err) {\n\t\t// Don't leak the scratch file if we failed before the rename landed.\n\t\tawait rm(tmp, { force: true }).catch(() => {});\n\t\tthrow err;\n\t}\n\tif (opts?.mode !== undefined) {\n\t\ttry {\n\t\t\tawait chmod(filePath, opts.mode);\n\t\t} catch {\n\t\t\t/* non-POSIX fs — ignore */\n\t\t}\n\t}\n}","/**\n * Derive instrument-panel telemetry from a workspace directory.\n *\n * Two probes today:\n * - current git branch (`git rev-parse --abbrev-ref HEAD`)\n * - tracked-file count (`git ls-files --cached --others --exclude-standard`)\n *\n * Both probes fail silently — non-git directories, missing `git` binary, or\n * broken repos all collapse to `null`. The UI fades the slot when both are\n * null, so a failure looks identical to \"not implemented yet\" from the\n * operator's perspective.\n *\n * In-memory 30-second TTL cache keyed by absolute path so the list endpoint\n * doesn't fork two child processes per workspace on every refresh. The TTL is\n * a deliberate trade: branch changes made outside the app will surface\n * within 30 seconds, which is the same staleness operators tolerate from\n * sidebar listings elsewhere.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { Workspace } from \"@pi-pilot/shared\";\nimport type { StoredWorkspace } from \"./workspace-registry.ts\";\n\nconst exec = promisify(execFile);\n\ninterface StatsCacheEntry {\n\tgitBranch: string | null;\n\tfileCount: number | null;\n\texpiresAt: number;\n}\n\nconst CACHE_TTL_MS = 30_000;\nconst cache = new Map<string, StatsCacheEntry>();\n\n/**\n * In-flight probes per path. Prevents the herd: if `enrichWorkspace` is called\n * twice for the same path before the first probe completes, both await the\n * same promise rather than each spawning two child processes.\n */\nconst inflight = new Map<string, Promise<StatsCacheEntry>>();\n\n/** Wrap a `StoredWorkspace` with derived stats. Never throws — failed probes\n * surface as nulls. */\nexport async function enrichWorkspace(ws: StoredWorkspace): Promise<Workspace> {\n\tconst stats = await getStats(ws.path);\n\treturn {\n\t\tid: ws.id,\n\t\tname: ws.name,\n\t\tpath: ws.path,\n\t\taddedAt: ws.addedAt,\n\t\tgitBranch: stats.gitBranch,\n\t\tfileCount: stats.fileCount,\n\t\ttrustProjectAgents: ws.trustProjectAgents === true,\n\t};\n}\n\nasync function getStats(path: string): Promise<StatsCacheEntry> {\n\tconst now = Date.now();\n\tconst cached = cache.get(path);\n\tif (cached && cached.expiresAt > now) return cached;\n\n\tconst pending = inflight.get(path);\n\tif (pending) return pending;\n\n\tconst probe = probeStats(path)\n\t\t.then((stats) => {\n\t\t\tconst entry: StatsCacheEntry = {\n\t\t\t\t...stats,\n\t\t\t\texpiresAt: Date.now() + CACHE_TTL_MS,\n\t\t\t};\n\t\t\tcache.set(path, entry);\n\t\t\treturn entry;\n\t\t})\n\t\t.finally(() => {\n\t\t\tinflight.delete(path);\n\t\t});\n\n\tinflight.set(path, probe);\n\treturn probe;\n}\n\nasync function probeStats(\n\tpath: string,\n): Promise<{ gitBranch: string | null; fileCount: number | null }> {\n\t// Run both probes in parallel; either may fail independently.\n\tconst [branchResult, filesResult] = await Promise.allSettled([\n\t\trunGit(path, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]),\n\t\trunGit(path, [\n\t\t\t\"ls-files\",\n\t\t\t\"--cached\",\n\t\t\t\"--others\",\n\t\t\t\"--exclude-standard\",\n\t\t]),\n\t]);\n\n\tlet gitBranch: string | null = null;\n\tif (branchResult.status === \"fulfilled\") {\n\t\tconst out = branchResult.value.trim();\n\t\t// `HEAD` means detached HEAD; show the shortened commit instead, but\n\t\t// keep it terse so it still fits the BRANCH slot.\n\t\tif (out && out !== \"HEAD\") {\n\t\t\tgitBranch = out;\n\t\t} else if (out === \"HEAD\") {\n\t\t\t// Try to grab the short SHA for the detached case.\n\t\t\ttry {\n\t\t\t\tconst sha = (await runGit(path, [\"rev-parse\", \"--short\", \"HEAD\"])).trim();\n\t\t\t\tgitBranch = sha ? `@${sha}` : null;\n\t\t\t} catch {\n\t\t\t\tgitBranch = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tlet fileCount: number | null = null;\n\tif (filesResult.status === \"fulfilled\") {\n\t\tconst out = filesResult.value;\n\t\tif (out) {\n\t\t\t// `git ls-files` prints one path per line. Count non-empty lines so\n\t\t\t// a single trailing newline doesn't add a phantom file.\n\t\t\tlet n = 0;\n\t\t\tfor (let i = 0; i < out.length; i++) {\n\t\t\t\tif (out.charCodeAt(i) === 0x0a) n++;\n\t\t\t}\n\t\t\t// Add 1 when the last line doesn't end in a newline (rare).\n\t\t\tif (out.length > 0 && out.charCodeAt(out.length - 1) !== 0x0a) n++;\n\t\t\tfileCount = n;\n\t\t} else {\n\t\t\tfileCount = 0;\n\t\t}\n\t}\n\n\treturn { gitBranch, fileCount };\n}\n\n/**\n * Run `git` with the given args in `cwd`. Resolves with stdout on exit code 0.\n * 2-second timeout — slow probes are treated as failures rather than blocking\n * the list endpoint. Output capped at 5MB so a pathologically huge repo can't\n * OOM the server.\n */\nasync function runGit(cwd: string, args: string[]): Promise<string> {\n\tconst { stdout } = await exec(\"git\", args, {\n\t\tcwd,\n\t\ttimeout: 2000,\n\t\tmaxBuffer: 5 * 1024 * 1024,\n\t\t// Don't inherit GIT_DIR / GIT_WORK_TREE from the server process —\n\t\t// could leak the server's own repo into a workspace probe.\n\t\tenv: { ...process.env, GIT_DIR: undefined, GIT_WORK_TREE: undefined },\n\t});\n\treturn stdout;\n}\n","/**\n * SessionRuntimeManager — lazy lifecycle of one AgentSessionRuntime per\n * (workspace, session), so a single workspace can host several concurrently\n * live sessions (one streaming while you read another).\n *\n * Keying: runtimes are stored under `runtimeKey = workspaceId \\0 sessionIdentity`\n * where `sessionIdentity` is the resolved session-file path (or the sessionId\n * for an as-yet-unpersisted session). Switching sessions within a workspace is\n * therefore just \"get-or-create a different runtime and make it primary\" — the\n * same non-destructive promote/demote the WS hub already does for workspaces.\n *\n * Active-runtime pointer: the per-workspace REST routes (config / resources /\n * model-configs / tree) still ask for \"the workspace's runtime\" via\n * `get(workspaceId)`. We answer with the *active* session's runtime — the one\n * the hub last made primary (`setActive`) — falling back to any live runtime\n * for the workspace. This keeps those routes session-agnostic while the hub\n * drives which session is active.\n *\n * Registered runtimes never replace their own AgentSession (we build a fresh\n * runtime for new_session / fork instead of mutating), so a runtime's key is\n * stable for its lifetime and the old rebind machinery is gone.\n *\n * NOT thread-safe across concurrent calls for the same key; Node is\n * single-threaded so this is fine, but be aware if you ever add worker_threads.\n */\n\nimport { unlink } from \"node:fs/promises\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport type { WebSocket } from \"ws\";\nimport {\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionRuntime,\n\tcreateAgentSessionServices,\n\tgetAgentDir,\n\tSessionManager,\n\ttype ExtensionError,\n\ttype SessionInfo,\n} from \"@earendil-works/pi-coding-agent\";\nimport { HttpError } from \"./storage/resource-writer.ts\";\nimport { getWorkspace } from \"./storage/workspace-registry.ts\";\nimport { forgetSessionToolPrefs, reapplyToolPrefs } from \"./storage/session-tool-prefs.ts\";\nimport { builtinExtensionFactories } from \"./extensions/index.ts\";\nimport { reconcileAfterRestart } from \"./extensions/ask_user/cleanup.ts\";\nimport { cancelPendingForSession } from \"./extensions/ask_user/registry.ts\";\nimport { reconcileAfterRestart as reconcileSubagentAfterRestart } from \"./extensions/subagent/cleanup.ts\";\nimport { killChildrenForSession } from \"./extensions/subagent/registry.ts\";\nimport { shouldForwardDetails } from \"./ws/bridge.ts\";\nimport type {\n\tSessionSummary,\n\tHistoryItem,\n\tHistoryResponse,\n\tWsExtensionError,\n\tWsServerMessage,\n} from \"@pi-pilot/shared\";\nimport { ExtensionUIBridge } from \"./ws/extension-ui.ts\";\n\n/**\n * Two-tier extension stance:\n *\n * 1. Third-party / disk pi extensions are OFF by default. The pi\n * extension system is TUI-native (renderers, ANSI themes, modal\n * dialogs, keyboard overlays) and cannot be bridged to the Web.\n * `EXTENSIONS_ENABLED` (env: `PI_PILOT_ENABLE_EXTENSIONS=1`) is the\n * escape hatch — even then no `ctx.ui.*` is bridged, so extensions\n * that only register tools / slash commands work and anything\n * UI-driven appears broken. Exported so `/api/resources` can show\n * this state in the panel.\n *\n * 2. pi-pilot's own first-party builtin extensions ARE loaded\n * unconditionally via `extensionFactories` (see `extensions/`).\n * That channel is independent of disk scanning, so they ship with\n * pi-pilot regardless of the env flag. These are not \"plugins\" in\n * the third-party sense — they're features of pi-pilot that happen\n * to use the extension API to get richer registration hooks\n * (`pi.registerTool`, `pi.registerCommand`, lifecycle events).\n */\nexport const EXTENSIONS_ENABLED = process.env.PI_PILOT_ENABLE_EXTENSIONS === \"1\";\n\n/**\n * Soft cap on concurrently-live runtimes. When exceeded, the least-recently\n * touched IDLE (not streaming, no pending tools) runtime is disposed — its\n * state is on disk and rehydrates on next open. A streaming runtime is never\n * evicted. Bounds memory for users who open many sessions in one run.\n */\nconst MAX_LIVE_RUNTIMES = 12;\n\n/**\n * Build a fresh runtime bound to `cwd`. Used for every runtime we create.\n */\nconst createRuntime: CreateAgentSessionRuntimeFactory = async ({\n\tcwd,\n\tsessionManager,\n\tsessionStartEvent,\n}) => {\n\tconst services = await createAgentSessionServices({\n\t\tcwd,\n\t\tresourceLoaderOptions: {\n\t\t\tnoExtensions: !EXTENSIONS_ENABLED,\n\t\t\textensionFactories: builtinExtensionFactories,\n\t\t},\n\t});\n\tconst sessionResult = await createAgentSessionFromServices({\n\t\tservices,\n\t\tsessionManager,\n\t\tsessionStartEvent,\n\t});\n\treturn {\n\t\t...sessionResult,\n\t\tservices,\n\t\tdiagnostics: services.diagnostics,\n\t};\n};\n\ninterface RuntimeState {\n\truntime: AgentSessionRuntime;\n\tbridge: ExtensionUIBridge;\n\tworkspaceId: string;\n\t/** Resolved session-file path this runtime is bound to (null if in-memory). */\n\tsessionPath: string | null;\n\t/** Monotonic touch counter for LRU idle eviction. */\n\ttouchedAt: number;\n}\n\nconst KEY_SEP = \"\\u0000\";\n\nclass SessionRuntimeManager {\n\t/** All live runtimes, keyed by `runtimeKey`. */\n\tprivate readonly runtimes = new Map<string, RuntimeState>();\n\t/** Per-build lock keyed by `runtimeKey` to serialize concurrent creations. */\n\tprivate readonly pending = new Map<string, Promise<RuntimeState>>();\n\t/** The runtime the hub last made primary, per workspace. Drives `get`. */\n\tprivate readonly activeByWorkspace = new Map<string, string>();\n\t/**\n\t * WS subscribers, keyed by workspaceId (not runtimeKey): a connection\n\t * viewing any session of a workspace receives that workspace's\n\t * server-initiated broadcasts (extension errors, context_usage). Owned by\n\t * the manager so it can pre-exist any runtime build (extensions may fire\n\t * `onError` from `session_start` before any client subscribed).\n\t */\n\tprivate readonly subscribers = new Map<string, Set<WebSocket>>();\n\tprivate touchSeq = 0;\n\n\t/** `runtimeKey` for a (workspace, session identity). */\n\tprivate keyOf(workspaceId: string, sessionIdentity: string): string {\n\t\treturn `${workspaceId}${KEY_SEP}${sessionIdentity}`;\n\t}\n\n\t/** `runtimeKey` for a built runtime, from its session file (or sessionId). */\n\tprivate keyForRuntime(workspaceId: string, runtime: AgentSessionRuntime): string {\n\t\tconst file = runtime.session.sessionFile;\n\t\treturn this.keyOf(workspaceId, file ? resolve(file) : runtime.session.sessionId);\n\t}\n\n\t/** Public so the WS hub derives the exact same key for a returned runtime. */\n\truntimeKeyFor(workspaceId: string, runtime: AgentSessionRuntime): string {\n\t\treturn this.keyForRuntime(workspaceId, runtime);\n\t}\n\n\tprivate getOrCreateSubscriberSet(workspaceId: string): Set<WebSocket> {\n\t\tlet set = this.subscribers.get(workspaceId);\n\t\tif (!set) {\n\t\t\tset = new Set();\n\t\t\tthis.subscribers.set(workspaceId, set);\n\t\t}\n\t\treturn set;\n\t}\n\n\t/**\n\t * Build (but do not register) a runtime for `workspaceId` from a\n\t * SessionManager factory. Binds the UI bridge + onError and runs ask_user\n\t * post-restart cleanup. The bridge broadcasts to the workspace's subscriber\n\t * set, resolved lazily so it works even if the set is created later.\n\t */\n\tprivate async buildState(\n\t\tworkspaceId: string,\n\t\tcwd: string,\n\t\tmakeSessionManager: () => SessionManager,\n\t): Promise<RuntimeState> {\n\t\tconst runtime = await createAgentSessionRuntime(createRuntime, {\n\t\t\tcwd,\n\t\t\tagentDir: getAgentDir(),\n\t\t\tsessionManager: makeSessionManager(),\n\t\t});\n\t\tconst bridge = new ExtensionUIBridge();\n\t\tawait this.bindExtensions(workspaceId, runtime, bridge);\n\t\t// Restore this session's per-tool selection onto the freshly built (or\n\t\t// rehydrated-after-eviction/restart) registry, which starts from pi's\n\t\t// default active set (core defaults + all extension tools).\n\t\treapplyToolPrefs(workspaceId, runtime.session);\n\t\tsafeReconcileBuiltins(workspaceId, runtime.session.sessionManager);\n\t\treturn {\n\t\t\truntime,\n\t\t\tbridge,\n\t\t\tworkspaceId,\n\t\t\tsessionPath: runtime.session.sessionFile ? resolve(runtime.session.sessionFile) : null,\n\t\t\ttouchedAt: ++this.touchSeq,\n\t\t};\n\t}\n\n\t/** Bind (or re-bind, after a fork) the UI context + onError on a session. */\n\tprivate async bindExtensions(\n\t\tworkspaceId: string,\n\t\truntime: AgentSessionRuntime,\n\t\tbridge: ExtensionUIBridge,\n\t): Promise<void> {\n\t\tconst onError = (err: ExtensionError) => {\n\t\t\tconst msg: WsExtensionError = {\n\t\t\t\ttype: \"extension_error\",\n\t\t\t\tworkspaceId,\n\t\t\t\textensionPath: err.extensionPath,\n\t\t\t\tevent: err.event,\n\t\t\t\tmessage: err.error,\n\t\t\t};\n\t\t\tconst set = this.subscribers.get(workspaceId);\n\t\t\tif (set) broadcastTo(set, msg);\n\t\t\tconsole.error(\n\t\t\t\t`[ext-error] ${workspaceId} ${err.extensionPath}@${err.event}: ${err.error}` +\n\t\t\t\t\t(err.stack ? `\\n${err.stack}` : \"\"),\n\t\t\t);\n\t\t};\n\t\tawait runtime.session.bindExtensions({ uiContext: bridge, onError });\n\t}\n\n\t/** Register a freshly-built state under its session key, deduping against a\n\t * concurrent build of the same session. Returns the winning state. */\n\tprivate async adopt(state: RuntimeState): Promise<RuntimeState> {\n\t\tconst key = this.keyForRuntime(state.workspaceId, state.runtime);\n\t\tconst existing = this.runtimes.get(key);\n\t\tif (existing) {\n\t\t\t// Someone else built the same session first — keep theirs.\n\t\t\tawait this.disposeState(state);\n\t\t\treturn existing;\n\t\t}\n\t\tthis.runtimes.set(key, state);\n\t\tthis.evictIfOverCap(key);\n\t\treturn state;\n\t}\n\n\t/**\n\t * Ensure a runtime exists for the target session and return it.\n\t *\n\t * - With `sessionPath`: opens that specific session (deduped by key).\n\t * - Without: returns the workspace's active/any live runtime, or builds the\n\t * \"continue recent\" default if none exists yet.\n\t *\n\t * Does NOT change the active pointer — the hub owns that via `setActive`.\n\t */\n\tasync getOrCreate(workspaceId: string, sessionPath?: string): Promise<AgentSessionRuntime> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\n\t\tif (sessionPath) {\n\t\t\tif (!isAbsolute(sessionPath)) throw new Error(\"Session path must be absolute\");\n\t\t\tconst resolved = resolve(sessionPath);\n\t\t\tconst key = this.keyOf(workspaceId, resolved);\n\t\t\tconst existing = this.runtimes.get(key);\n\t\t\tif (existing) {\n\t\t\t\texisting.touchedAt = ++this.touchSeq;\n\t\t\t\treturn existing.runtime;\n\t\t\t}\n\t\t\tconst inflight = this.pending.get(key);\n\t\t\tif (inflight) return (await inflight).runtime;\n\t\t\tconst p = this.buildState(workspaceId, ws.path, () =>\n\t\t\t\tSessionManager.open(resolved, undefined, ws.path),\n\t\t\t).then((s) => this.adopt(s));\n\t\t\tthis.pending.set(key, p);\n\t\t\ttry {\n\t\t\t\treturn (await p).runtime;\n\t\t\t} finally {\n\t\t\t\tthis.pending.delete(key);\n\t\t\t}\n\t\t}\n\n\t\t// Default (no session requested): reuse the active/any runtime if one\n\t\t// exists; otherwise build the recent-or-new default. Guard concurrent\n\t\t// default builds with a per-workspace pending slot.\n\t\tconst existing = this.get(workspaceId);\n\t\tif (existing) return existing;\n\t\tconst defaultKey = this.keyOf(workspaceId, \"<default>\");\n\t\tconst inflight = this.pending.get(defaultKey);\n\t\tif (inflight) return (await inflight).runtime;\n\t\tconst p = this.buildState(workspaceId, ws.path, () =>\n\t\t\tSessionManager.continueRecent(ws.path),\n\t\t).then((s) => this.adopt(s));\n\t\tthis.pending.set(defaultKey, p);\n\t\ttry {\n\t\t\treturn (await p).runtime;\n\t\t} finally {\n\t\t\tthis.pending.delete(defaultKey);\n\t\t}\n\t}\n\n\t/** Create a brand-new empty session + runtime for the workspace (does not\n\t * touch any existing runtime, so a streaming session keeps running). */\n\tasync createSession(workspaceId: string): Promise<AgentSessionRuntime> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\t\tconst state = await this.buildState(workspaceId, ws.path, () =>\n\t\t\tSessionManager.create(ws.path),\n\t\t);\n\t\treturn (await this.adopt(state)).runtime;\n\t}\n\n\t/**\n\t * Fork an existing session at `entryId` into a new branched session and\n\t * return a runtime bound to the branch. The source session's own runtime\n\t * (if any) is untouched. Returns `{ cancelled: true }` if pi cancelled the\n\t * fork (e.g. a `session_before_switch` veto).\n\t */\n\tasync fork(\n\t\tworkspaceId: string,\n\t\tsourceSessionPath: string,\n\t\tentryId: string,\n\t): Promise<{ cancelled: boolean; runtime?: AgentSessionRuntime }> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\t\tif (!isAbsolute(sourceSessionPath)) throw new Error(\"Session path must be absolute\");\n\n\t\t// Build a runtime on the source session and fork it. pi's fork uses\n\t\t// `createBranchedSession` / `newSession`, so this REPLACES the runtime's\n\t\t// session with a fresh branch FILE — a different key — which we then\n\t\t// register. The source's own runtime (if live) is never touched, so the\n\t\t// hub can keep it streaming in the background after the fork.\n\t\tconst state = await this.buildState(workspaceId, ws.path, () =>\n\t\t\tSessionManager.open(resolve(sourceSessionPath), undefined, ws.path),\n\t\t);\n\t\tlet result: { cancelled: boolean };\n\t\ttry {\n\t\t\tresult = await state.runtime.fork(entryId);\n\t\t} catch (err) {\n\t\t\t// fork() rejected (e.g. invalid entry id). The throwaway runtime was\n\t\t\t// never registered, so nothing else will dispose it — do it here.\n\t\t\tawait this.disposeState(state);\n\t\t\tthrow err;\n\t\t}\n\t\tif (result.cancelled) {\n\t\t\tawait this.disposeState(state);\n\t\t\treturn { cancelled: true };\n\t\t}\n\t\t// Re-bind the UI context onto the branch (fork swapped the AgentSession)\n\t\t// and reconcile any dangling ask_user left in the forked history. We do\n\t\t// NOT call `cancelPendingExcept` here: unlike the old destructive switch,\n\t\t// fork abandons no live session (the source keeps its runtime), and that\n\t\t// helper is process-wide — it would wrongly reject pending questions in\n\t\t// other concurrently-live sessions.\n\t\tawait this.bindExtensions(workspaceId, state.runtime, state.bridge);\n\t\tsafeReconcileBuiltins(workspaceId, state.runtime.session.sessionManager);\n\t\tstate.sessionPath = state.runtime.session.sessionFile\n\t\t\t? resolve(state.runtime.session.sessionFile)\n\t\t\t: null;\n\t\tconst winner = await this.adopt(state);\n\t\treturn { cancelled: false, runtime: winner.runtime };\n\t}\n\n\t/** The active session's runtime for this workspace (hub-designated), or any\n\t * live runtime for it, or undefined. Used by per-workspace REST routes. */\n\tget(workspaceId: string): AgentSessionRuntime | undefined {\n\t\tconst activeKey = this.activeByWorkspace.get(workspaceId);\n\t\tif (activeKey) {\n\t\t\tconst active = this.runtimes.get(activeKey);\n\t\t\tif (active) return active.runtime;\n\t\t}\n\t\tfor (const state of this.runtimes.values()) {\n\t\t\tif (state.workspaceId === workspaceId) return state.runtime;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** The runtime bound to a specific (workspace, session), if live. */\n\tgetForSession(workspaceId: string, sessionPath: string): AgentSessionRuntime | undefined {\n\t\treturn this.runtimes.get(this.keyOf(workspaceId, resolve(sessionPath)))?.runtime;\n\t}\n\n\t/** Mark `runtime` as the active session for its workspace (hub on primary\n\t * bind), so per-workspace routes resolve to it. */\n\tsetActive(workspaceId: string, runtime: AgentSessionRuntime): void {\n\t\tconst key = this.keyForRuntime(workspaceId, runtime);\n\t\tthis.activeByWorkspace.set(workspaceId, key);\n\t\tconst state = this.runtimes.get(key);\n\t\tif (state) state.touchedAt = ++this.touchSeq;\n\t}\n\n\t/**\n\t * Register a WS connection as a subscriber for `workspaceId` (server-\n\t * initiated broadcasts: extension errors, context_usage). Safe to call\n\t * before any runtime build.\n\t */\n\taddSubscriber(workspaceId: string, ws: WebSocket): void {\n\t\tthis.getOrCreateSubscriberSet(workspaceId).add(ws);\n\t}\n\n\tremoveSubscriber(workspaceId: string, ws: WebSocket): void {\n\t\tconst set = this.subscribers.get(workspaceId);\n\t\tif (!set) return;\n\t\tset.delete(ws);\n\t\tif (set.size === 0) this.subscribers.delete(workspaceId);\n\t}\n\n\t/** Fan a server-initiated message out to every WS subscribed to the\n\t * workspace (e.g. context_usage after setModel). */\n\tbroadcast(workspaceId: string, msg: WsServerMessage): void {\n\t\tconst set = this.subscribers.get(workspaceId);\n\t\tif (!set || set.size === 0) return;\n\t\tbroadcastTo(set, msg);\n\t}\n\n\t/**\n\t * Check that `sessionPath` belongs to the given workspace's session list.\n\t * Returns an error string if validation fails, or `null` if the path is\n\t * owned by this workspace.\n\t */\n\tasync validateSessionOwnership(workspaceId: string, sessionPath: string): Promise<string | null> {\n\t\tif (!isAbsolute(sessionPath)) return \"session path must be absolute\";\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) return `workspace not found: ${workspaceId}`;\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\tconst resolved = resolve(sessionPath);\n\t\tconst found = sessions.some((s) => resolve(s.path) === resolved);\n\t\tif (!found) return `session not found in workspace: ${sessionPath}`;\n\t\treturn null;\n\t}\n\n\tasync listSessions(workspaceId: string): Promise<SessionSummary[]> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new Error(`Workspace not found: ${workspaceId}`);\n\n\t\t// Sessions with a live, streaming runtime get a `running` badge.\n\t\tconst streaming = new Set<string>();\n\t\tfor (const state of this.runtimes.values()) {\n\t\t\tif (state.workspaceId !== workspaceId) continue;\n\t\t\tif (state.sessionPath && state.runtime.session.isStreaming) {\n\t\t\t\tstreaming.add(state.sessionPath);\n\t\t\t}\n\t\t}\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\treturn sessions\n\t\t\t.slice()\n\t\t\t.sort((a, b) => b.modified.getTime() - a.modified.getTime())\n\t\t\t.map((info) => toSessionSummary(info, streaming.has(resolve(info.path))));\n\t}\n\n\tgetSessionHistory(workspaceId: string, sessionPath?: string): HistoryResponse {\n\t\tconst runtime = sessionPath\n\t\t\t? this.getForSession(workspaceId, sessionPath)\n\t\t\t: this.get(workspaceId);\n\t\tif (!runtime) return { items: [], isStreaming: false };\n\n\t\tconst isStreaming: boolean = runtime.session.isStreaming ?? false;\n\n\t\tconst branch = runtime.session.sessionManager.getBranch();\n\t\tconst items: HistoryItem[] = [];\n\t\t// toolCall args live in assistant messages; collect them so we can\n\t\t// attach them to the corresponding toolResult items.\n\t\tconst argsByCallId = new Map<string, string>();\n\n\t\tfor (const entry of branch) {\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tconst msg = entry.message;\n\t\t\tconst role = (msg as { role: string }).role;\n\n\t\t\tif (role === \"user\") {\n\t\t\t\tconst text = extractUserText(msg as { content: unknown });\n\t\t\t\tif (text) items.push({ kind: \"user\", text, entryId: entry.id });\n\t\t\t} else if (role === \"assistant\") {\n\t\t\t\tconst { text, thinking, toolCalls } = extractAssistantContent(\n\t\t\t\t\tmsg as { content: unknown[] },\n\t\t\t\t);\n\t\t\t\tfor (const tc of toolCalls) {\n\t\t\t\t\targsByCallId.set(tc.id, tc.args);\n\t\t\t\t}\n\t\t\t\tif (text || thinking) items.push({ kind: \"assistant\", text, thinking });\n\t\t\t} else if (role === \"toolResult\") {\n\t\t\t\tconst tr = msg as {\n\t\t\t\t\ttoolCallId: string;\n\t\t\t\t\ttoolName: string;\n\t\t\t\t\tcontent: unknown[];\n\t\t\t\t\tisError: boolean;\n\t\t\t\t\tdetails?: unknown;\n\t\t\t\t};\n\t\t\t\titems.push({\n\t\t\t\t\tkind: \"tool\",\n\t\t\t\t\ttoolCallId: tr.toolCallId,\n\t\t\t\t\ttoolName: tr.toolName,\n\t\t\t\t\targs: argsByCallId.get(tr.toolCallId) ?? \"\",\n\t\t\t\t\ttext: extractContentText(tr.content),\n\t\t\t\t\tisError: tr.isError,\n\t\t\t\t\t// Mirror live wire whitelist (bridge.ts): only ship details\n\t\t\t\t\t// for tools whose cards need the structured shape, so the\n\t\t\t\t\t// history payload stays small for bash / edit / read.\n\t\t\t\t\t...(shouldForwardDetails(tr.toolName) && tr.details !== undefined\n\t\t\t\t\t\t? { details: tr.details }\n\t\t\t\t\t\t: {}),\n\t\t\t\t});\n\t\t\t} else if (role === \"bashExecution\") {\n\t\t\t\tconst be = msg as {\n\t\t\t\t\tcommand: string;\n\t\t\t\t\toutput: string;\n\t\t\t\t\texitCode: number | undefined;\n\t\t\t\t};\n\t\t\t\titems.push({\n\t\t\t\t\tkind: \"bash\",\n\t\t\t\t\tcommand: be.command,\n\t\t\t\t\toutput: be.output,\n\t\t\t\t\texitCode: be.exitCode,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn { items, isStreaming };\n\t}\n\n\t/**\n\t * Delete a session JSONL file belonging to this workspace.\n\t *\n\t * HTTP-tagged errors (HttpError) map to status codes at the route layer:\n\t * - 400: sessionPath not absolute\n\t * - 404: workspace gone, or session not in this workspace's list\n\t * - 409: a live runtime is bound to it and is streaming (stop it first)\n\t *\n\t * If a live but idle runtime is bound to the session, it is disposed before\n\t * the file is unlinked. Idempotent on ENOENT.\n\t */\n\tasync deleteSession(workspaceId: string, sessionPath: string): Promise<void> {\n\t\tconst ws = await getWorkspace(workspaceId);\n\t\tif (!ws) throw new HttpError(404, `Workspace not found: ${workspaceId}`);\n\t\tif (!isAbsolute(sessionPath)) {\n\t\t\tthrow new HttpError(400, \"Session path must be absolute\");\n\t\t}\n\n\t\tconst sessions = await SessionManager.list(ws.path);\n\t\tconst resolved = resolve(sessionPath);\n\t\tconst target = sessions.find((session) => resolve(session.path) === resolved);\n\t\tif (!target) {\n\t\t\tthrow new HttpError(404, `Session not found: ${sessionPath}`);\n\t\t}\n\n\t\tconst key = this.keyOf(workspaceId, resolved);\n\t\tconst live = this.runtimes.get(key);\n\t\tif (live) {\n\t\t\tif (live.runtime.session.isStreaming) {\n\t\t\t\tthrow new HttpError(\n\t\t\t\t\t409,\n\t\t\t\t\t\"Cannot delete a streaming session — stop it first\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait this.disposeState(live, key);\n\t\t}\n\n\t\ttry {\n\t\t\tawait unlink(resolved);\n\t\t} catch (err) {\n\t\t\tif ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[wm] deleteSession: ${resolved} was already gone at unlink time`,\n\t\t\t\t);\n\t\t\t\tawait forgetSessionToolPrefs(resolved);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t\t// The session file is gone; drop any per-session tool prefs so the prefs\n\t\t// file doesn't accumulate dead entries.\n\t\tawait forgetSessionToolPrefs(resolved);\n\t}\n\n\t/** Dispose every runtime for a workspace (e.g. when it's removed). */\n\tasync dispose(workspaceId: string): Promise<void> {\n\t\tthis.activeByWorkspace.delete(workspaceId);\n\t\tthis.subscribers.delete(workspaceId);\n\t\tconst doomed = [...this.runtimes].filter(([, s]) => s.workspaceId === workspaceId);\n\t\tfor (const [key, state] of doomed) {\n\t\t\tawait this.disposeState(state, key);\n\t\t}\n\t}\n\n\tasync disposeAll(): Promise<void> {\n\t\t// Drain in-flight builds first so a runtime mid-build when shutdown\n\t\t// lands doesn't leak; failed builds never reached `runtimes`.\n\t\tawait Promise.allSettled([...this.pending.values()]);\n\t\tconst states = [...this.runtimes.entries()];\n\t\tthis.runtimes.clear();\n\t\tthis.activeByWorkspace.clear();\n\t\tthis.subscribers.clear();\n\t\tawait Promise.all(states.map(([, state]) => this.disposeState(state)));\n\t}\n\n\t/** Tear down a single runtime + its bridge, releasing any ask_user Promises\n\t * bound to it first. Removes it from `runtimes` when `key` is given. */\n\tprivate async disposeState(state: RuntimeState, key?: string): Promise<void> {\n\t\tif (key) this.runtimes.delete(key);\n\t\t// If this was the workspace's active runtime, clear the pointer.\n\t\tif (key && this.activeByWorkspace.get(state.workspaceId) === key) {\n\t\t\tthis.activeByWorkspace.delete(state.workspaceId);\n\t\t}\n\t\tcancelPendingForSession(state.runtime.session.sessionFile ?? null);\n\t\ttry {\n\t\t\tstate.bridge.dispose();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[wm] dispose bridge ${state.workspaceId} failed:`, e);\n\t\t}\n\t\ttry {\n\t\t\tawait state.runtime.dispose();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[wm] dispose ${state.workspaceId} failed:`, e);\n\t\t}\n\t\t// dispose() → agent.abort() should have torn down subagent children via\n\t\t// the execute() signal; sweep whatever that chain missed (e.g. dispose\n\t\t// threw, or a child registered between abort and teardown).\n\t\tconst sweptChildren = killChildrenForSession(state.runtime.session.sessionFile ?? null);\n\t\tif (sweptChildren > 0) {\n\t\t\tconsole.warn(\n\t\t\t\t`[wm] killed ${sweptChildren} lingering subagent child(ren) for ${state.workspaceId}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Keep the live-runtime count under `MAX_LIVE_RUNTIMES` by disposing the\n\t * least-recently-touched IDLE runtime (never streaming, never the freshly-\n\t * registered one, never a workspace's active pointer). Best-effort: if every\n\t * runtime is busy we simply exceed the cap until one frees up.\n\t */\n\tprivate evictIfOverCap(justRegistered: string): void {\n\t\twhile (this.runtimes.size > MAX_LIVE_RUNTIMES) {\n\t\t\tlet victimKey: string | undefined;\n\t\t\tlet victimTouched = Infinity;\n\t\t\tfor (const [key, state] of this.runtimes) {\n\t\t\t\tif (key === justRegistered) continue;\n\t\t\t\tif (this.activeByWorkspace.get(state.workspaceId) === key) continue;\n\t\t\t\tif (state.runtime.session.isStreaming) continue;\n\t\t\t\tif (state.touchedAt < victimTouched) {\n\t\t\t\t\tvictimTouched = state.touchedAt;\n\t\t\t\t\tvictimKey = key;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!victimKey) break; // everything is busy/active — let it ride\n\t\t\tconst victim = this.runtimes.get(victimKey);\n\t\t\tif (!victim) break;\n\t\t\t// Fire-and-forget; dispose is async but eviction is best-effort.\n\t\t\tvoid this.disposeState(victim, victimKey);\n\t\t}\n\t}\n}\n\n/**\n * Run the builtins' post-restart reconciliations (ask_user + subagent\n * dangling-toolCall notices), swallowing any error so a cleanup failure\n * doesn't brick the workspace. The worst-case fallout of skipping cleanup\n * is the LLM seeing an unmatched toolCall with no explanation — it will\n * typically ignore or re-call; not load-bearing.\n */\nfunction safeReconcileBuiltins(workspaceId: string, sm: SessionManager): void {\n\ttry {\n\t\treconcileAfterRestart(sm);\n\t} catch (e) {\n\t\tconsole.error(`[wm] ask_user cleanup for ${workspaceId} failed:`, e);\n\t}\n\ttry {\n\t\treconcileSubagentAfterRestart(sm);\n\t} catch (e) {\n\t\tconsole.error(`[wm] subagent cleanup for ${workspaceId} failed:`, e);\n\t}\n}\n\nfunction toSessionSummary(info: SessionInfo, running: boolean): SessionSummary {\n\tconst preview = info.firstMessage.replace(/\\s+/g, \" \").trim();\n\treturn {\n\t\tpath: info.path,\n\t\tname: info.name,\n\t\tupdatedAt: info.modified.toISOString(),\n\t\tpreview: preview ? preview.slice(0, 160) : undefined,\n\t\t...(running ? { running: true } : {}),\n\t};\n}\n\n/** Extract text from a user message (string or content blocks). */\nfunction extractUserText(msg: { content: unknown }): string {\n\tif (typeof msg.content === \"string\") return msg.content;\n\treturn extractContentText(msg.content as unknown[]);\n}\n\n/** Extract text + thinking + toolCalls from an assistant message's content blocks. */\nfunction extractAssistantContent(msg: { content: unknown[] }): {\n\ttext: string;\n\tthinking: string;\n\ttoolCalls: { id: string; args: string }[];\n} {\n\tconst textParts: string[] = [];\n\tconst thinkingParts: string[] = [];\n\tconst toolCalls: { id: string; args: string }[] = [];\n\tfor (const block of msg.content ?? []) {\n\t\tif (!block || typeof block !== \"object\") continue;\n\t\tconst b = block as { type?: string; text?: string; thinking?: string; id?: string; arguments?: unknown };\n\t\tif (b.type === \"text\" && typeof b.text === \"string\") textParts.push(b.text);\n\t\telse if (b.type === \"thinking\" && typeof b.thinking === \"string\") thinkingParts.push(b.thinking);\n\t\telse if (b.type === \"toolCall\" && typeof b.id === \"string\") {\n\t\t\ttoolCalls.push({\n\t\t\t\tid: b.id,\n\t\t\t\targs: b.arguments != null ? JSON.stringify(b.arguments) : \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn { text: textParts.join(\"\"), thinking: thinkingParts.join(\"\"), toolCalls };\n}\n\n/** Extract text from a content block array (TextContent items). */\nfunction extractContentText(content: unknown[]): string {\n\tif (!Array.isArray(content)) return \"\";\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n\t\t\tconst text = (block as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.join(\"\");\n}\n\nexport const workspaceManager = new SessionRuntimeManager();\n\n/**\n * Best-effort fan-out of a server-initiated message to every connection\n * subscribed to a workspace.\n */\nfunction broadcastTo(subscribers: Set<WebSocket>, msg: WsServerMessage): void {\n\tconst wire = JSON.stringify(msg);\n\tfor (const ws of subscribers) {\n\t\tif (ws.readyState !== ws.OPEN) continue;\n\t\ttry {\n\t\t\tws.send(wire);\n\t\t} catch {\n\t\t\t// Bad sockets drop on the next close event.\n\t\t}\n\t}\n}\n","/**\n * Per-session active-tool memory for Settings → Session → Tools.\n *\n * Why this exists: a `session.reload()` (fired when a builtin extension is\n * toggled in Resources → Extensions) rebuilds the tool registry and\n * force-reactivates every extension-registered tool — it preserves core-tool\n * state but unions all extension tools back into the active set (pi's\n * AgentSession._refreshToolRegistry, called with includeAllExtensionTools).\n * Runtime eviction and server restart likewise rebuild from pi's default active\n * set. pi persists a session's model and thinking level but NOT which tools are\n * active, so without this module a user's per-tool selections silently revert.\n *\n * Two layered controls — keep them straight:\n * - Resources → Extensions: GLOBAL master switches deciding whether a builtin\n * registers its tools at all (see storage/builtin-extension-prefs.ts).\n * - Settings → Session → Tools: PER-SESSION switches deciding which of the\n * currently registered tools the model may call. This module persists those.\n *\n * We store the user's DISABLED tool names per session, so:\n *\n * active tools = currently registered tools − stored-disabled\n *\n * Storing disabled (not active) names means newly registered tools are active\n * by default, while a tool the user explicitly hid stays hidden even if its\n * extension is turned off and later back on.\n *\n * Persisted at ~/.pi/webui/session-tools.json. This is convenience state —\n * load/parse failures fail open to \"nothing disabled\" rather than blocking\n * startup.\n *\n * Assumption: `persistActiveTools` (PUT /config/tools) is the SOLE writer of a\n * session's active-tool set, so the stored disabled set always tracks the live\n * session. pi-pilot's builtins (todo / ask_user / artifact) never mutate the\n * tool set programmatically; if one ever does, capture the live set before\n * `reload()` and fold it into storage there.\n */\n\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport type { AgentSession } from \"@earendil-works/pi-coding-agent\";\nimport { config } from \"../config.ts\";\n\nconst PREFS_PATH = join(config.dataDir, \"session-tools.json\");\n\ninterface SessionEntry {\n\tdisabled: string[];\n\tupdatedAt: string;\n}\n\ninterface PrefsFile {\n\tsessions: Record<string, SessionEntry>;\n}\n\n/** Identity of a session — enough to derive its stable storage key. */\ntype SessionIdent = Pick<AgentSession, \"sessionFile\" | \"sessionId\">;\n/** The tool-registry surface this module reads and mutates on a live session. */\ntype SessionTools = Pick<AgentSession, \"getAllTools\" | \"setActiveToolsByName\">;\n\n/** In-memory source of truth. Loaded once at startup, updated on writes. */\nlet cache: PrefsFile = { sessions: {} };\n\n/**\n * Read the prefs file into the module cache. Call once at server startup. A\n * missing file is normal (no session has hidden a tool). Fails open: a corrupt\n * or unreadable file resets to \"nothing disabled\" (and warns) rather than\n * crashing startup over non-critical convenience state.\n */\nexport async function loadSessionToolPrefs(): Promise<void> {\n\ttry {\n\t\tconst raw = await readFile(PREFS_PATH, \"utf8\");\n\t\tconst parsed = JSON.parse(raw) as Partial<PrefsFile>;\n\t\tcache = { sessions: normalizeSessions(parsed.sessions) };\n\t} catch (err) {\n\t\tcache = { sessions: {} };\n\t\tif ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n\t\t\tconsole.warn(`[session-tool-prefs] ignoring unreadable ${PREFS_PATH}:`, err);\n\t\t}\n\t}\n}\n\n/**\n * Stable per-session key. Persisted sessions key on their resolved JSONL path\n * (globally unique); unpersisted ones fall back to `${workspaceId}:${sessionId}`,\n * where `workspaceId` only scopes the otherwise-not-globally-unique session id.\n */\nfunction keyOf(workspaceId: string, session: SessionIdent): string {\n\treturn session.sessionFile\n\t\t? resolve(session.sessionFile)\n\t\t: `${workspaceId}:${session.sessionId}`;\n}\n\n/** Stored disabled tool names for a session (including currently-unregistered). */\nfunction storedDisabled(workspaceId: string, session: SessionIdent): string[] {\n\treturn cache.sessions[keyOf(workspaceId, session)]?.disabled ?? [];\n}\n\n/**\n * Persist a session's chosen active-tool set and apply it to the live session.\n * This is the single writer of both storage and the live active-tool list.\n *\n * disabled = (registered − active) ∪ (prior stored-disabled not now registered)\n *\n * The second term preserves memory for tools whose extension is currently off,\n * so a later re-enable of that extension doesn't resurface a tool the user hid.\n */\nexport async function persistActiveTools(\n\tworkspaceId: string,\n\tsession: SessionIdent & SessionTools,\n\tactiveNames: Iterable<string>,\n): Promise<void> {\n\tconst registered = session.getAllTools().map((t) => t.name);\n\tconst registeredSet = new Set(registered);\n\tconst active = new Set(activeNames);\n\n\tconst disabled = new Set<string>();\n\tfor (const name of registered) {\n\t\tif (!active.has(name)) disabled.add(name);\n\t}\n\tfor (const name of storedDisabled(workspaceId, session)) {\n\t\tif (!registeredSet.has(name)) disabled.add(name);\n\t}\n\n\tcache = {\n\t\tsessions: {\n\t\t\t...cache.sessions,\n\t\t\t[keyOf(workspaceId, session)]: {\n\t\t\t\tdisabled: sortUnique(disabled),\n\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t},\n\t\t},\n\t};\n\tawait save();\n\n\tsession.setActiveToolsByName(registered.filter((name) => !disabled.has(name)));\n}\n\n/**\n * Re-apply a session's stored tool selection onto its live registry. Call after\n * building a runtime and after `session.reload()`. No-op when nothing is stored,\n * so fresh sessions keep every registered tool active.\n */\nexport function reapplyToolPrefs(\n\tworkspaceId: string,\n\tsession: SessionIdent & SessionTools,\n): void {\n\tconst disabled = storedDisabled(workspaceId, session);\n\tif (disabled.length === 0) return;\n\tconst disabledSet = new Set(disabled);\n\tconst registered = session.getAllTools().map((t) => t.name);\n\tsession.setActiveToolsByName(registered.filter((name) => !disabledSet.has(name)));\n}\n\n/**\n * Drop a session's stored tool prefs. Call when its JSONL is deleted so the file\n * doesn't accumulate entries for sessions that no longer exist. Keyed by the\n * resolved path (deleted sessions are always persisted). No-op when absent.\n */\nexport async function forgetSessionToolPrefs(sessionPath: string): Promise<void> {\n\tconst key = resolve(sessionPath);\n\tif (!(key in cache.sessions)) return;\n\tconst next = { ...cache.sessions };\n\tdelete next[key];\n\tcache = { sessions: next };\n\tawait save();\n}\n\nfunction sortUnique(values: Iterable<string>): string[] {\n\treturn [...new Set(values)].sort((a, b) => a.localeCompare(b));\n}\n\n/** Coerce a parsed `sessions` map into a clean shape; drop malformed entries. */\nfunction normalizeSessions(value: unknown): Record<string, SessionEntry> {\n\tif (!value || typeof value !== \"object\") return {};\n\tconst out: Record<string, SessionEntry> = {};\n\tfor (const [key, entry] of Object.entries(value as Record<string, unknown>)) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst disabled = (entry as { disabled?: unknown }).disabled;\n\t\tif (!Array.isArray(disabled)) continue;\n\t\tconst updatedAt = (entry as { updatedAt?: unknown }).updatedAt;\n\t\tout[key] = {\n\t\t\tdisabled: sortUnique(disabled.filter((n): n is string => typeof n === \"string\")),\n\t\t\tupdatedAt: typeof updatedAt === \"string\" ? updatedAt : new Date(0).toISOString(),\n\t\t};\n\t}\n\treturn out;\n}\n\n/**\n * Serialized, atomic persistence. Writes are chained so two rapid toggles can't\n * race at the filesystem, and each lands in a temp file then `rename`s into\n * place so a crash mid-write can't leave a half-written (corrupt) file. Each\n * write serializes the live `cache`, so the last enqueued write persists the\n * latest state.\n */\nlet writeChain: Promise<void> = Promise.resolve();\n\nfunction save(): Promise<void> {\n\twriteChain = writeChain\n\t\t.catch(() => {})\n\t\t.then(async () => {\n\t\t\tawait mkdir(dirname(PREFS_PATH), { recursive: true });\n\t\t\tconst tmp = `${PREFS_PATH}.tmp`;\n\t\t\tawait writeFile(tmp, JSON.stringify(cache, null, 2), \"utf8\");\n\t\t\tawait rename(tmp, PREFS_PATH);\n\t\t});\n\treturn writeChain;\n}\n","/**\n * TypeBox schemas and domain types for the `todo` tool.\n *\n * Modeled after rpiv-todo's CRUD-style schema: actions are\n * create/update/list/get/delete/clear, with a 4-state machine\n * (pending → in_progress → completed, plus deleted tombstone).\n */\n\nimport { Type, type Static } from \"typebox\";\n\n// ---------------------------------------------------------------------------\n// Domain types\n// ---------------------------------------------------------------------------\n\nexport type TaskStatus = \"pending\" | \"in_progress\" | \"completed\" | \"deleted\";\nexport type TaskAction = \"create\" | \"update\" | \"list\" | \"get\" | \"delete\" | \"clear\";\n\nexport interface Task {\n\tid: number;\n\tsubject: string;\n\tdescription?: string;\n\tstatus: TaskStatus;\n}\n\nexport interface TaskState {\n\ttasks: Task[];\n\tnextId: number;\n}\n\nexport const EMPTY_STATE: TaskState = { tasks: [], nextId: 1 };\n\n/**\n * Snapshot returned in tool `details` — consumed by the web for rendering\n * the TodoRail/TodoCard, and rebuilt on session_start / session_compact /\n * session_tree via `replayFromBranch` in `factory.ts`.\n */\nexport interface TaskDetails {\n\taction: TaskAction;\n\ttasks: Task[];\n\tnextId: number;\n\terror?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Status transitions\n// ---------------------------------------------------------------------------\n\nconst VALID_TRANSITIONS: Record<TaskStatus, ReadonlySet<TaskStatus>> = {\n\tpending: new Set([\"in_progress\", \"completed\", \"deleted\"]),\n\tin_progress: new Set([\"pending\", \"completed\", \"deleted\"]),\n\tcompleted: new Set([\"deleted\"]),\n\tdeleted: new Set(),\n};\n\nexport function isTransitionValid(from: TaskStatus, to: TaskStatus): boolean {\n\tif (from === to) return true;\n\treturn VALID_TRANSITIONS[from].has(to);\n}\n\n// ---------------------------------------------------------------------------\n// TypeBox parameter schema\n// ---------------------------------------------------------------------------\n\nconst ActionEnum = Type.Union([\n\tType.Literal(\"create\"),\n\tType.Literal(\"update\"),\n\tType.Literal(\"list\"),\n\tType.Literal(\"get\"),\n\tType.Literal(\"delete\"),\n\tType.Literal(\"clear\"),\n]);\n\nconst StatusEnum = Type.Union([\n\tType.Literal(\"pending\"),\n\tType.Literal(\"in_progress\"),\n\tType.Literal(\"completed\"),\n\tType.Literal(\"deleted\"),\n]);\n\nexport const todoParamsSchema = Type.Object({\n\taction: ActionEnum,\n\tsubject: Type.Optional(Type.String({ description: \"Task subject line (required for create)\" })),\n\tdescription: Type.Optional(Type.String({ description: \"Long-form task description\" })),\n\tstatus: Type.Optional(StatusEnum),\n\tid: Type.Optional(Type.Number({ description: \"Task id (required for update, get, delete)\" })),\n\tincludeDeleted: Type.Optional(Type.Boolean({\n\t\tdescription: \"If true, list action returns deleted (tombstoned) tasks as well. Default: false.\",\n\t})),\n});\n\nexport type TodoParams = Static<typeof todoParamsSchema>;\n","/**\n * Pure reducer: (state, action, params) → (state, result).\n *\n * No side effects — the factory commits the returned state to the\n * module-level store and formats the tool response envelope.\n */\n\nimport {\n\tisTransitionValid,\n\ttype Task,\n\ttype TaskAction,\n\ttype TaskState,\n\ttype TodoParams,\n} from \"./schema.ts\";\n\nexport interface ReducerResult {\n\tstate: TaskState;\n\ttext: string;\n\terror?: string;\n}\n\nfunction err(state: TaskState, message: string): ReducerResult {\n\treturn { state, text: `Error: ${message}`, error: message };\n}\n\nfunction formatListLine(t: Task): string {\n\treturn `[${t.status}] #${t.id} ${t.subject}`;\n}\n\nexport function applyTodoAction(state: TaskState, action: TaskAction, params: TodoParams): ReducerResult {\n\tswitch (action) {\n\t\tcase \"create\": {\n\t\t\tif (!params.subject?.trim()) {\n\t\t\t\treturn err(state, \"subject required for create\");\n\t\t\t}\n\t\t\tconst task: Task = {\n\t\t\t\tid: state.nextId,\n\t\t\t\tsubject: params.subject,\n\t\t\t\tstatus: \"pending\",\n\t\t\t};\n\t\t\tif (params.description) task.description = params.description;\n\t\t\tconst newTasks = [...state.tasks, task];\n\t\t\treturn {\n\t\t\t\tstate: { tasks: newTasks, nextId: state.nextId + 1 },\n\t\t\t\ttext: `Created #${task.id}: ${task.subject} (pending)`,\n\t\t\t};\n\t\t}\n\n\t\tcase \"update\": {\n\t\t\tif (params.id === undefined) return err(state, \"id required for update\");\n\t\t\tconst idx = state.tasks.findIndex((t) => t.id === params.id);\n\t\t\tif (idx === -1) return err(state, `#${params.id} not found`);\n\t\t\tconst current = state.tasks[idx]!;\n\n\t\t\tconst hasMutation =\n\t\t\t\tparams.subject !== undefined ||\n\t\t\t\tparams.description !== undefined ||\n\t\t\t\tparams.status !== undefined;\n\t\t\tif (!hasMutation) return err(state, \"update requires at least one mutable field\");\n\n\t\t\tlet newStatus = current.status;\n\t\t\tif (params.status !== undefined) {\n\t\t\t\tif (!isTransitionValid(current.status, params.status)) {\n\t\t\t\t\treturn err(state, `illegal transition ${current.status} → ${params.status}`);\n\t\t\t\t}\n\t\t\t\tnewStatus = params.status;\n\t\t\t}\n\n\t\t\tconst updated: Task = { ...current, status: newStatus };\n\t\t\tif (params.subject !== undefined) updated.subject = params.subject;\n\t\t\tif (params.description !== undefined) updated.description = params.description;\n\n\t\t\tconst newTasks = [...state.tasks];\n\t\t\tnewTasks[idx] = updated;\n\t\t\tconst transition = current.status !== newStatus ? ` (${current.status} → ${newStatus})` : \"\";\n\t\t\treturn {\n\t\t\t\tstate: { tasks: newTasks, nextId: state.nextId },\n\t\t\t\ttext: `Updated #${updated.id}${transition}`,\n\t\t\t};\n\t\t}\n\n\t\tcase \"list\": {\n\t\t\tlet view = state.tasks;\n\t\t\tif (!params.includeDeleted) view = view.filter((t) => t.status !== \"deleted\");\n\t\t\tif (params.status) view = view.filter((t) => t.status === params.status);\n\t\t\treturn {\n\t\t\t\tstate,\n\t\t\t\ttext: view.length === 0 ? \"No tasks\" : view.map(formatListLine).join(\"\\n\"),\n\t\t\t};\n\t\t}\n\n\t\tcase \"get\": {\n\t\t\tif (params.id === undefined) return err(state, \"id required for get\");\n\t\t\tconst task = state.tasks.find((t) => t.id === params.id);\n\t\t\tif (!task) return err(state, `#${params.id} not found`);\n\t\t\tconst lines = [`#${task.id} [${task.status}] ${task.subject}`];\n\t\t\tif (task.description) lines.push(` description: ${task.description}`);\n\t\t\treturn { state, text: lines.join(\"\\n\") };\n\t\t}\n\n\t\tcase \"delete\": {\n\t\t\tif (params.id === undefined) return err(state, \"id required for delete\");\n\t\t\tconst idx = state.tasks.findIndex((t) => t.id === params.id);\n\t\t\tif (idx === -1) return err(state, `#${params.id} not found`);\n\t\t\tconst current = state.tasks[idx]!;\n\t\t\tif (current.status === \"deleted\") return err(state, `#${current.id} is already deleted`);\n\t\t\tconst updated: Task = { ...current, status: \"deleted\" };\n\t\t\tconst newTasks = [...state.tasks];\n\t\t\tnewTasks[idx] = updated;\n\t\t\treturn {\n\t\t\t\tstate: { tasks: newTasks, nextId: state.nextId },\n\t\t\t\ttext: `Deleted #${updated.id}: ${updated.subject}`,\n\t\t\t};\n\t\t}\n\n\t\tcase \"clear\": {\n\t\t\tconst count = state.tasks.length;\n\t\t\treturn {\n\t\t\t\tstate: { tasks: [], nextId: 1 },\n\t\t\t\ttext: `Cleared ${count} tasks`,\n\t\t\t};\n\t\t}\n\t}\n}\n","/**\n * pi-pilot's first-party todo extension.\n *\n * Registers:\n * - `todo` tool: CRUD task management with a 4-state machine\n * (pending → in_progress → completed, plus deleted tombstone).\n * - `/todos` slash command: prints the current todo list.\n *\n * State management follows rpiv-todo's branch-replay pattern: state is\n * rebuilt from the session's branch (JSONL history) at every session\n * lifecycle boundary (session_start, session_compact, session_tree).\n * The module-level `state` variable is a cache — the branch is always\n * the source of truth. This ensures correct behavior across fork, new\n * session, resume, compact, and tree navigation.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport {\n\tEMPTY_STATE,\n\ttodoParamsSchema,\n\ttype TaskDetails,\n\ttype TaskState,\n} from \"./schema.ts\";\nimport { applyTodoAction } from \"./reducer.ts\";\n\nconst TOOL_NAME = \"todo\";\n\n/**\n * Replay state from the session branch. Walks the branch chronologically;\n * the LAST successful `todo` toolResult's `details` snapshot wins\n * (last-write-wins, same as rpiv-todo). Returns EMPTY_STATE when no\n * matching entry exists.\n */\nexport function replayFromBranch(ctx: { sessionManager: { getBranch(): Iterable<unknown> } }): TaskState {\n\tlet result: TaskState = { tasks: [...EMPTY_STATE.tasks], nextId: EMPTY_STATE.nextId };\n\tfor (const entry of ctx.sessionManager.getBranch()) {\n\t\tconst e = entry as { type?: string; message?: { role?: string; toolName?: string; details?: unknown; isError?: boolean } };\n\t\tif (e.type !== \"message\") continue;\n\t\tconst msg = e.message;\n\t\tif (msg?.role !== \"toolResult\" || msg.toolName !== TOOL_NAME) continue;\n\t\tif (msg.isError) continue;\n\t\tconst details = msg.details as TaskDetails | undefined;\n\t\tif (!details || !Array.isArray(details.tasks) || typeof details.nextId !== \"number\") continue;\n\t\tif (details.error) continue;\n\t\tresult = {\n\t\t\ttasks: details.tasks.map((t) => ({ ...t })),\n\t\t\tnextId: details.nextId,\n\t\t};\n\t}\n\treturn result;\n}\n\nexport const todoExtensionFactory: ExtensionFactory = (pi) => {\n\t// Per-session state. The factory runs once per AgentSession, so this closure\n\t// variable isolates todo state between concurrently-live sessions of the\n\t// same workspace — a module-level singleton would let two streaming sessions\n\t// clobber each other's task list. The branch (JSONL) stays the source of\n\t// truth; this is only a cache, rebuilt from it at every session lifecycle\n\t// boundary below.\n\tlet state: TaskState = { tasks: [...EMPTY_STATE.tasks], nextId: EMPTY_STATE.nextId };\n\n\tpi.registerTool({\n\t\tname: TOOL_NAME,\n\t\tlabel: \"Todo\",\n\t\tdescription:\n\t\t\t\"Manage a task list for tracking multi-step progress. Actions: create (new task), update (change status/fields), list (all tasks, optionally filtered), get (single task), delete (tombstone), clear (reset). Status: pending → in_progress → completed, plus deleted tombstone.\",\n\t\tpromptSnippet: \"Manage a task list to track multi-step progress.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `todo` for complex work with 3+ steps or when the user gives you an explicit list of tasks. Skip it for single trivial tasks and purely conversational requests.\",\n\t\t\t\"When starting any task, mark it in_progress BEFORE beginning work. Mark it completed IMMEDIATELY when done — never batch completions. Exactly one task should be in_progress at a time.\",\n\t\t\t\"Never mark a task completed if tests are failing, the implementation is partial, or you hit unresolved errors — keep it in_progress and address the issue first.\",\n\t\t],\n\t\tparameters: todoParamsSchema,\n\n\t\tasync execute(_toolCallId, params) {\n\t\t\tconst result = applyTodoAction(state, params.action, params);\n\t\t\tstate = result.state;\n\t\t\tconst details: TaskDetails = {\n\t\t\t\taction: params.action,\n\t\t\t\ttasks: state.tasks,\n\t\t\t\tnextId: state.nextId,\n\t\t\t\t...(result.error ? { error: result.error } : {}),\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: result.text }],\n\t\t\t\tdetails,\n\t\t\t};\n\t\t},\n\t});\n\n\tpi.registerCommand(\"todos\", {\n\t\tdescription: \"Show current todo list grouped by status.\",\n\t\thandler: async () => {\n\t\t\tconst visible = state.tasks.filter((t) => t.status !== \"deleted\");\n\t\t\tif (visible.length === 0) {\n\t\t\t\tpi.sendUserMessage(\"Show the current todo list.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst pending = visible.filter((t) => t.status === \"pending\");\n\t\t\tconst inProgress = visible.filter((t) => t.status === \"in_progress\");\n\t\t\tconst completed = visible.filter((t) => t.status === \"completed\");\n\n\t\t\tconst lines: string[] = [];\n\t\t\tconst total = visible.length;\n\t\t\tconst doneCount = completed.length;\n\t\t\tlines.push(`Todos (${doneCount}/${total})`);\n\n\t\t\tif (inProgress.length > 0) {\n\t\t\t\tlines.push(\"── In Progress ──\");\n\t\t\t\tfor (const t of inProgress) lines.push(` ◐ #${t.id} ${t.subject}`);\n\t\t\t}\n\t\t\tif (pending.length > 0) {\n\t\t\t\tlines.push(\"── Pending ──\");\n\t\t\t\tfor (const t of pending) lines.push(` ○ #${t.id} ${t.subject}`);\n\t\t\t}\n\t\t\tif (completed.length > 0) {\n\t\t\t\tlines.push(\"── Completed ──\");\n\t\t\t\tfor (const t of completed) lines.push(` ✓ #${t.id} ${t.subject}`);\n\t\t\t}\n\n\t\t\tpi.sendUserMessage(`Current todos:\\n${lines.join(\"\\n\")}`);\n\t\t},\n\t});\n\n\t// --- Session lifecycle: rebuild state from branch ---\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tstate = replayFromBranch(ctx);\n\t});\n\n\tpi.on(\"session_compact\", async (_event, ctx) => {\n\t\ttry {\n\t\t\tstate = replayFromBranch(ctx);\n\t\t} catch {\n\t\t\t// Stale ctx after session replacement — keep current state;\n\t\t\t// the replacement session's session_start will replay correctly.\n\t\t}\n\t});\n\n\tpi.on(\"session_tree\", async (_event, ctx) => {\n\t\ttry {\n\t\t\tstate = replayFromBranch(ctx);\n\t\t} catch {\n\t\t\t// Same stale-ctx guard as session_compact.\n\t\t}\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tstate = { tasks: [...EMPTY_STATE.tasks], nextId: EMPTY_STATE.nextId };\n\t});\n};\n","/**\n * TypeBox schemas for the ask_user tool.\n *\n * Kept separate from factory.ts so the registry and any future shared\n * mirrors (web shape via shared types, snapshot helpers) can import\n * parameter / answer shapes without pulling pi runtime types into the\n * dependency graph.\n *\n * The TypeBox version is forced workspace-wide via `pnpm.overrides` —\n * the `Kind` symbols here must come from the same module instance pi's\n * runtime validates against, otherwise tool registration will reject\n * our schema with a noisy mismatch error.\n */\n\nimport { Type, type Static } from \"typebox\";\n\nexport const askUserOptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tdescription: \"Short, distinct choice (1–5 words). User-facing.\",\n\t}),\n\tdescription: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Optional one-line clarification of what this choice means or implies.\",\n\t\t}),\n\t),\n});\n\n/**\n * `header` is capped at 12 chars because the web renders it as a chip\n * next to the question; longer strings get truncated visually anyway.\n * `options` is 2–4 to match common-sense UX (single-choice dialogs\n * become noisy past 4; less than 2 isn't a choice).\n * `timeoutSec` floor is 5s — anything shorter than human reaction time\n * is almost certainly a mistake.\n */\nexport const askUserParamsSchema = Type.Object({\n\tquestion: Type.String({\n\t\tdescription:\n\t\t\t\"Full question, specific and ending with '?'. Avoid yes/no when options can be more concrete.\",\n\t}),\n\theader: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: 12,\n\t\t\tdescription:\n\t\t\t\t\"Optional short chip label (≤ 12 chars), e.g. 'Auth method'. Shown next to the question.\",\n\t\t}),\n\t),\n\toptions: Type.Array(askUserOptionSchema, {\n\t\tminItems: 2,\n\t\tmaxItems: 4,\n\t\tdescription:\n\t\t\t\"2–4 mutually exclusive choices (unless multiSelect is true). Recommended option first; append ' (Recommended)' to its label.\",\n\t}),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"If true, render checkboxes and allow multiple selections. Default false.\",\n\t\t}),\n\t),\n\tallowOther: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"If true, show a freeform 'Other' input as an escape hatch. Default true.\",\n\t\t}),\n\t),\n\ttimeoutSec: Type.Optional(\n\t\tType.Integer({\n\t\t\tminimum: 5,\n\t\t\tdescription:\n\t\t\t\t\"Auto-resolve as skip after this many seconds. Only set for time-sensitive questions where a stale answer would be worse than no answer.\",\n\t\t}),\n\t),\n});\n\nexport type AskUserOption = Static<typeof askUserOptionSchema>;\nexport type AskUserParams = Static<typeof askUserParamsSchema>;\n\n/**\n * Structured detail attached to the tool result. Mirrors the wire-side\n * `WsAnswerQuestion[\"answer\"]` union plus a `timeout` variant the\n * server may synthesize when the agent-supplied `timeoutSec` fires\n * before any client answered. The web reads this on history replay to\n * highlight the selected option(s) in the resolved card.\n */\nexport type AskUserAnswerDetails =\n\t| { kind: \"option\"; indices: number[]; labels: string[] }\n\t| { kind: \"other\"; text: string }\n\t| { kind: \"skip\" }\n\t| { kind: \"timeout\"; afterMs: number };\n","/**\n * Module-level registry of pending ask_user calls.\n *\n * Why module-level: pi's toolCallId is globally unique, so a single map\n * keyed by id is enough — we don't need a (workspaceId, id) tuple. This\n * keeps the factory signature compatible with `ExtensionFactory` (just\n * `(pi) => void`); the WS hub and the in-flight snapshot helper import\n * functions from this file to read / mutate the map.\n *\n * Lifecycle:\n * - `register()` called from `execute()` at tool start. Returns a\n * Promise that resolves when one of these happens:\n * 1. `resolveAnswer()` — client sent `answer_question`.\n * 2. timeoutSec fired — auto-resolves as `timeout`.\n * 3. signal abort (user pressed Stop) — execute() rejects.\n * 4. `cancelPendingExcept()` — session replaced under our feet.\n * 5. `cancelPendingForSession()` — workspace disposed.\n * - The Promise itself lives inside `execute()` (the factory owns\n * `new Promise(...)`); this module only owns the resolver/rejecter\n * side and the metadata needed for snapshot/cancel routing.\n *\n * Stale-answer policy: when `resolveAnswer()` is called with an\n * unknown toolCallId — already-answered by another tab, server-restart\n * cleanup ran, session replaced — we return `false` and the caller\n * (the WS hub) silently ignores. We never emit an error toast for this;\n * a duplicate answer is benign and the loser tab's UI catches up via\n * the normal `tool_execution_end` broadcast.\n */\n\nimport type { AskUserParams } from \"./schema.ts\";\nimport type { WsAnswerQuestion } from \"@pi-pilot/shared\";\n\nexport type Answer = WsAnswerQuestion[\"answer\"];\n\ninterface PendingEntry {\n\tresolve(answer: Answer): void;\n\treject(reason: Error): void;\n\t/** Original tool args — needed by `inFlightToolCallsSnapshot` to\n\t * replay a synthetic `tool_execution_start` on (re)subscribe. */\n\targs: AskUserParams;\n\t/** Absolute session JSONL path at register time. Used to route\n\t * cancels (session replacement) and snapshots (per-subscriber\n\t * filter). `null` for in-memory / unpersisted sessions. */\n\tsessionFile: string | null;\n}\n\nconst pending = new Map<string, PendingEntry>();\n\nexport function register(\n\ttoolCallId: string,\n\tentry: PendingEntry,\n): void {\n\tpending.set(toolCallId, entry);\n}\n\nexport function unregister(toolCallId: string): void {\n\tpending.delete(toolCallId);\n}\n\n/**\n * Resolve a pending call. Returns `false` when the toolCallId is\n * unknown or the sessionFile guard fails (stale answer from a tab\n * still bound to a replaced session).\n */\nexport function resolveAnswer(\n\ttoolCallId: string,\n\tanswer: Answer,\n\texpectedSessionFile: string | null,\n): boolean {\n\tconst entry = pending.get(toolCallId);\n\tif (!entry) return false;\n\tif (entry.sessionFile !== expectedSessionFile) return false;\n\tpending.delete(toolCallId);\n\tentry.resolve(answer);\n\treturn true;\n}\n\n/**\n * Reject every pending entry whose `sessionFile` does NOT equal\n * `keepSessionFile`. Called from the rebind callback after a session\n * replacement (newSession / fork / switchSession): pi does not abort\n * in-flight tool signals on session switch, so we have to actively\n * cancel anything bound to the now-defunct session, otherwise the\n * Promise inside `execute()` would hang forever.\n *\n * `keepSessionFile` is the file path of the freshly-bound session\n * (typically taken from `runtime.session.sessionFile` after bind).\n * `null` is a legitimate value — an unpersisted in-memory session\n * matches `null` and is kept.\n */\nexport function cancelPendingExcept(keepSessionFile: string | null): void {\n\tfor (const [id, entry] of pending) {\n\t\tif (entry.sessionFile === keepSessionFile) continue;\n\t\tpending.delete(id);\n\t\tentry.reject(new Error(\"Session replaced before answer arrived\"));\n\t}\n}\n\n/**\n * Reject every pending entry whose `sessionFile` equals the given\n * path. Called from workspace dispose; we know the runtime is going\n * away and the Promise must release.\n */\nexport function cancelPendingForSession(sessionFile: string | null): void {\n\tfor (const [id, entry] of pending) {\n\t\tif (entry.sessionFile !== sessionFile) continue;\n\t\tpending.delete(id);\n\t\tentry.reject(new Error(\"Workspace disposed\"));\n\t}\n}\n\n/**\n * Snapshot of pending entries for a session. Used by\n * `inFlightToolCallsSnapshot` to synthesize `tool_execution_start`\n * events when a client (re)subscribes mid-question — without this,\n * the question card vanishes after a tab reload because the original\n * `tool_execution_start` fired before the subscription existed.\n *\n * Returns a fresh array each call; safe for the caller to iterate\n * while the registry continues to mutate.\n */\nexport function snapshotForSession(\n\tsessionFile: string | null,\n): { toolCallId: string; args: AskUserParams }[] {\n\tconst out: { toolCallId: string; args: AskUserParams }[] = [];\n\tfor (const [id, entry] of pending) {\n\t\tif (entry.sessionFile !== sessionFile) continue;\n\t\tout.push({ toolCallId: id, args: entry.args });\n\t}\n\treturn out;\n}\n","/**\n * pi-pilot's second first-party builtin extension: ask_user.\n *\n * Registers a single tool, `ask_user`, that lets the agent pause and\n * ask the user a structured multiple-choice question. Unlike\n * `todo` (which is a near-instant echo), `ask_user.execute()`\n * blocks on a Promise stored in `./registry.ts` until one of:\n *\n * - the client posts a `WsAnswerQuestion` over the WS hub (the\n * normal path), or\n * - the agent-supplied `timeoutSec` fires (auto-skip), or\n * - `signal` aborts (user pressed Stop), or\n * - the registry's `cancelPending*` helpers reject us because the\n * session was replaced or the workspace disposed.\n *\n * The question itself is rendered on the web by reading `args` off the\n * normal `tool_execution_start` event — we do not add a parallel\n * \"question\" stream. The tool result text is the agent-facing summary\n * of what the user picked; `details` carries the structured form so\n * the web can highlight the chosen option on history replay.\n *\n * Why a custom tool instead of pi's `ctx.ui.select/input`: those route\n * through `ExtensionUIContext`, and pi-pilot's `ExtensionUIBridge` is\n * deliberately a no-op (TUI-only, can't be bridged to the Web). A\n * custom tool persists both the call and the result in the session\n * JSONL automatically, so fork / new_session / history replay get\n * correct semantics for free — same trick as `todo`.\n *\n * Restart cleanup: when pi loads a session whose tail is an unmatched\n * `ask_user` toolCall (server crashed while the Promise was pending,\n * so the toolResult never landed), `./cleanup.ts` writes a\n * `customMessage` that's visible to the LLM explaining the cancel.\n * pi SDK doesn't expose a way to forge a real `toolResult` role\n * message, so the LLM will see \"my unmatched toolCall + a user\n * message saying it was cancelled\" — acceptable; the explanation\n * keeps it from looping on a phantom in-flight call.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport {\n\taskUserParamsSchema,\n\ttype AskUserAnswerDetails,\n\ttype AskUserParams,\n} from \"./schema.ts\";\nimport {\n\tregister,\n\tunregister,\n\ttype Answer,\n} from \"./registry.ts\";\nimport type { AgentToolResult } from \"@earendil-works/pi-coding-agent\";\n\ntype AskUserResult = AgentToolResult<AskUserAnswerDetails>;\n\nexport const askUserExtensionFactory: ExtensionFactory = (pi) => {\n\tpi.registerTool({\n\t\tname: \"ask_user\",\n\t\tlabel: \"Ask user\",\n\t\tdescription:\n\t\t\t\"Pause and ask the user a structured multiple-choice question. Use ONLY for genuine forks where the user's preference materially changes the work (UI layout, library choice, scope boundary). Returns the user's selection (or 'skip' if they dismissed).\",\n\t\tparameters: askUserParamsSchema,\n\t\tpromptSnippet:\n\t\t\t\"ask_user: pause and ask the user a structured multiple-choice question when a decision is genuinely theirs.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Call ask_user only for genuine forks where the user's preference materially changes the work — UI layout, library choice, scope boundary. Don't ask for opinions you can defend yourself.\",\n\t\t\t\"Each ask_user call asks ONE question. If you have several, call it multiple times in order so the user sees one at a time.\",\n\t\t\t\"ask_user options must be distinct and mutually exclusive (unless multiSelect is true). 2–4 options; put the recommended option first and append ' (Recommended)' to its label.\",\n\t\t\t\"Don't follow up ask_user with 'Should I proceed?' — the answer already authorizes the next step.\",\n\t\t\t\"Only set ask_user's timeoutSec for time-sensitive questions where a stale answer would be worse than no answer; otherwise let the user take as long as they need.\",\n\t\t],\n\t\texecute: async (toolCallId, params, signal, _onUpdate, ctx) => {\n\t\t\tconst sessionFile = ctx.sessionManager.getSessionFile() ?? null;\n\t\t\tconst startedAt = Date.now();\n\n\t\t\tconst answer = await waitForAnswer({\n\t\t\t\ttoolCallId,\n\t\t\t\tparams,\n\t\t\t\tsessionFile,\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\treturn formatResult(params, answer, startedAt);\n\t\t},\n\t});\n};\n\ninterface WaitArgs {\n\ttoolCallId: string;\n\tparams: AskUserParams;\n\tsessionFile: string | null;\n\tsignal: AbortSignal | undefined;\n}\n\n/**\n * Wire the pending Promise + the three concurrent cancel paths\n * (client answer / timeout / signal abort). Every resolution path\n * unregisters before resolving so a late event can't double-fire.\n *\n * Returns either:\n * - the client's `Answer` (from `resolveAnswer` in registry), or\n * - a synthetic `{ kind: \"timeout\", afterMs }` when the\n * agent-supplied `timeoutSec` fires.\n *\n * Throws on signal abort or session-replacement cancel — those\n * propagate out of `execute()` as a normal tool error and pi records\n * an error toolResult on its own.\n */\nfunction waitForAnswer({\n\ttoolCallId,\n\tparams,\n\tsessionFile,\n\tsignal,\n}: WaitArgs): Promise<Answer | { kind: \"timeout\"; afterMs: number }> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tunregister(toolCallId);\n\t\t};\n\n\t\tconst finishOk = (a: Answer | { kind: \"timeout\"; afterMs: number }) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(a);\n\t\t};\n\n\t\tconst finishErr = (err: Error) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(err);\n\t\t};\n\n\t\tconst onAbort = () => finishErr(new Error(\"Aborted by user\"));\n\t\tif (signal?.aborted) {\n\t\t\tfinishErr(new Error(\"Aborted by user\"));\n\t\t\treturn;\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", onAbort);\n\n\t\tif (typeof params.timeoutSec === \"number\" && params.timeoutSec > 0) {\n\t\t\tconst ms = params.timeoutSec * 1000;\n\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\tfinishOk({ kind: \"timeout\", afterMs: ms });\n\t\t\t}, ms);\n\t\t}\n\n\t\tregister(toolCallId, {\n\t\t\targs: params,\n\t\t\tsessionFile,\n\t\t\tresolve: (answer) => finishOk(answer),\n\t\t\treject: (err) => finishErr(err),\n\t\t});\n\t});\n}\n\n/**\n * Build the agent-facing tool result. The `text` is what the LLM\n * sees in the toolResult content; the `details` is for the web's\n * history-replay rendering (highlighting which option chip is\n * selected). All four outcomes are non-error — even `skip` and\n * `timeout` — because we explicitly want the agent to use its best\n * judgement and keep going rather than apologize-and-re-ask.\n */\nfunction formatResult(\n\tparams: AskUserParams,\n\tanswer: Answer | { kind: \"timeout\"; afterMs: number },\n\tstartedAt: number,\n): AskUserResult {\n\tif (answer.kind === \"option\") {\n\t\tconst labels = answer.indices\n\t\t\t.map((i) => params.options[i]?.label)\n\t\t\t.filter((l): l is string => typeof l === \"string\");\n\t\tconst text =\n\t\t\tlabels.length === 1\n\t\t\t\t? `User selected: \"${labels[0]}\"${descriptionSuffix(params, answer.indices[0]!)}.`\n\t\t\t\t: `User selected: ${labels.map((l) => `\"${l}\"`).join(\", \")}.`;\n\t\tconst details: AskUserAnswerDetails = {\n\t\t\tkind: \"option\",\n\t\t\tindices: answer.indices,\n\t\t\tlabels,\n\t\t};\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t}\n\n\tif (answer.kind === \"other\") {\n\t\tconst text = `User answered (freeform): \"${answer.text}\"`;\n\t\tconst details: AskUserAnswerDetails = { kind: \"other\", text: answer.text };\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t}\n\n\tif (answer.kind === \"skip\") {\n\t\tconst text =\n\t\t\t\"User chose to skip this question. Use your best judgement and proceed; you may ask again later if it still matters.\";\n\t\tconst details: AskUserAnswerDetails = { kind: \"skip\" };\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails,\n\t\t};\n\t}\n\n\t// timeout\n\tconst waited = Math.round((Date.now() - startedAt) / 1000);\n\tconst text = `No answer within ${waited}s (agent-supplied timeout). Use your best judgement and proceed; you may ask again later if it still matters.`;\n\tconst details: AskUserAnswerDetails = {\n\t\tkind: \"timeout\",\n\t\tafterMs: answer.afterMs,\n\t};\n\treturn {\n\t\tcontent: [{ type: \"text\", text }],\n\t\tdetails,\n\t};\n}\n\nfunction descriptionSuffix(params: AskUserParams, index: number): string {\n\tconst desc = params.options[index]?.description;\n\treturn desc ? ` — ${desc}` : \"\";\n}\n","/**\n * TypeBox schema + domain types for the `create_artifact` tool.\n *\n * An artifact is substantial, self-contained content the model produces for the\n * user to view and iterate on (a web page, an SVG diagram, a document, a code\n * file). The full content rides the tool-call `args` — which pi persists on the\n * assistant message and the web rehydrates from `/history` — so the web needs\n * no `details` payload to render it; `details` here is just small metadata.\n *\n * The TypeBox version is forced workspace-wide via `pnpm.overrides` — the\n * `Kind` symbols here must come from the same module instance pi's runtime\n * validates against, otherwise tool registration rejects the schema with a\n * noisy mismatch error.\n */\n\nimport { Type, type Static } from \"typebox\";\n\nexport type ArtifactType = \"html\" | \"svg\" | \"markdown\" | \"code\";\n\nconst TypeEnum = Type.Union(\n\t[\n\t\tType.Literal(\"html\"),\n\t\tType.Literal(\"svg\"),\n\t\tType.Literal(\"markdown\"),\n\t\tType.Literal(\"code\"),\n\t],\n\t{\n\t\tdescription:\n\t\t\t'How to render the content: \"html\" (a self-contained HTML document or fragment, run in a sandboxed iframe), \"svg\" (SVG markup), \"markdown\" (rich text), or \"code\" (a source file shown with syntax highlighting).',\n\t},\n);\n\nexport const createArtifactParamsSchema = Type.Object({\n\tid: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t'Stable identifier. Omit on first creation. To REVISE an existing artifact, pass the same id you used before — that records a new version instead of a separate artifact. Use a short slug like \"landing-page\".',\n\t\t}),\n\t),\n\ttype: TypeEnum,\n\ttitle: Type.String({\n\t\tdescription: \"Short human-readable title shown in the artifact panel.\",\n\t}),\n\tcontent: Type.String({\n\t\tdescription:\n\t\t\t\"The full artifact content — the complete document, markup, or source.\",\n\t}),\n\tlanguage: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t'For type=\"code\", the language id for syntax highlighting (e.g. \"python\", \"typescript\").',\n\t\t}),\n\t),\n});\n\nexport type CreateArtifactParams = Static<typeof createArtifactParamsSchema>;\n\n/**\n * Small metadata snapshot returned in the tool `details`. The full `content`\n * is deliberately NOT duplicated here — it already rides the tool-call `args`,\n * which the web reads directly (live and from history).\n */\nexport interface ArtifactDetails {\n\tid: string;\n\ttype: ArtifactType;\n\ttitle: string;\n}\n","/**\n * pi-pilot's `create_artifact` builtin extension.\n *\n * Registers one tool, `create_artifact`, that lets the model publish a titled,\n * self-contained artifact (web page, SVG, document, code file) which the web\n * renders in a dedicated side panel. As with `todo`/`ask_user`, persisting the\n * tool call in the session JSONL gives fork / new-session / history-replay\n * correct semantics for free.\n *\n * Stateless by design: the full content rides the tool-call `args` (pi persists\n * those on the assistant message and the web rehydrates them from `/history`),\n * so `execute` does no bookkeeping — it acks and echoes the resolved id so the\n * model knows what to reuse when revising. Versioning is derived web-side: each\n * call with the same id is a new version.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport {\n\tcreateArtifactParamsSchema,\n\ttype ArtifactDetails,\n} from \"./schema.ts\";\n\nconst TOOL_NAME = \"create_artifact\";\n\nexport const artifactExtensionFactory: ExtensionFactory = (pi) => {\n\tpi.registerTool({\n\t\tname: TOOL_NAME,\n\t\tlabel: \"Create artifact\",\n\t\tdescription:\n\t\t\t'Publish a substantial, self-contained piece of content as an \"artifact\" the user can view and iterate on in a dedicated side panel: a web page (html), an SVG diagram (svg), a document (markdown), or a source file (code). Reuse the same `id` to revise an existing artifact (records a new version).',\n\t\tpromptSnippet:\n\t\t\t\"create_artifact: render substantial, self-contained content (web page / SVG / document / code file) in a side panel the user can view and iterate on.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use create_artifact for substantial, self-contained, reusable content the user will want to view, keep, or iterate on — a runnable HTML page, an SVG diagram, a full document, or a standalone code file. Do NOT use it for short snippets, command output, or your normal conversational answer; a fenced code block in your reply is better for those.\",\n\t\t\t\"To revise an artifact, call create_artifact again with the SAME id and the full updated content — this records a new version the user can step through. Don't spawn a near-duplicate artifact under a new id.\",\n\t\t\t'Put the entire content in `content`, give it a concise `title`, and pick the `type` that matches how it should render. For type=\"code\", set `language`.',\n\t\t\t\"After creating an artifact, keep your chat reply short — the content lives in the panel, so don't paste it again in prose.\",\n\t\t],\n\t\tparameters: createArtifactParamsSchema,\n\n\t\texecute: async (toolCallId, params) => {\n\t\t\tconst id = params.id?.trim() || toolCallId;\n\t\t\tconst details: ArtifactDetails = {\n\t\t\t\tid,\n\t\t\t\ttype: params.type,\n\t\t\t\ttitle: params.title,\n\t\t\t};\n\t\t\tconst text =\n\t\t\t\t`Artifact \"${params.title}\" (${params.type}) is now shown to the user in the Artifacts panel. ` +\n\t\t\t\t`Its id is \"${id}\" — pass that same id to create_artifact to revise it.`;\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails,\n\t\t\t};\n\t\t},\n\t});\n};\n","/**\n * Subagent roster discovery: builtin presets merged with user agent files,\n * plus project agent files when the workspace is trusted.\n *\n * User agents live in `<agentDir>/agents/*.md` — the same convention as\n * pi's official subagent example, so definitions stay portable between\n * pi-pilot and the pi TUI (and respect PI_CODING_AGENT_DIR via\n * `getAgentDir()`). YAML frontmatter: `name` (defaults to the filename),\n * `description`, `tools` (comma string or array), `model`; the body is the\n * agent's system prompt.\n *\n * Project agents live in `<workspace>/.pi/agents/*.md` and are read ONLY\n * when the workspace's `trustProjectAgents` toggle is on (see trust.ts —\n * repo-controlled prompts are an injection vector, so trust is explicit,\n * server-side state). Precedence: most specific wins — a project file\n * overrides a same-name user file, which overrides a builtin preset\n * (Claude Code's subagent precedence).\n *\n * Re-discovered on every tool invocation — one readdir is cheap and lets\n * the user edit agents mid-session.\n *\n * Sync fs on purpose: callers are the (sync) factory and execute() — one\n * small directory read isn't worth dual code paths.\n */\n\nimport { readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getAgentDir, parseFrontmatter } from \"@earendil-works/pi-coding-agent\";\nimport { BUILTIN_AGENTS } from \"./builtin-agents.ts\";\nimport { isProjectDirTrusted } from \"./trust.ts\";\n\nexport interface AgentDefinition {\n\tname: string;\n\t/** One-liner shown to the parent model — this is the delegation routing signal. */\n\tdescription: string;\n\t/** System prompt appended to the child's default prompt. */\n\tsystemPrompt: string;\n\t/** Allowlist for the child's `--tools`; undefined = full default toolset. */\n\ttools?: string[];\n\t/** Model pattern for the child's `--model`; undefined = inherit the parent session's model. */\n\tmodel?: string;\n\tsource: \"builtin\" | \"user\" | \"project\";\n}\n\n/** Directory user agent files are read from. Exported for display strings. */\nexport function userAgentsDir(): string {\n\treturn join(getAgentDir(), \"agents\");\n}\n\n/** Directory project agent files are read from (trust-gated). */\nexport function projectAgentsDir(projectDir: string): string {\n\treturn join(projectDir, \".pi\", \"agents\");\n}\n\n/**\n * Build the roster. `projectDir` (the parent session's cwd) opts the\n * workspace's `.pi/agents/` into discovery — but only when that exact\n * directory is trusted in workspaces.json.\n */\nexport function discoverAgents(projectDir?: string): Map<string, AgentDefinition> {\n\tconst roster = new Map<string, AgentDefinition>();\n\tfor (const agent of BUILTIN_AGENTS) roster.set(agent.name, agent);\n\n\tmergeDir(roster, userAgentsDir(), \"user\");\n\tif (projectDir && isProjectDirTrusted(projectDir)) {\n\t\tmergeDir(roster, projectAgentsDir(projectDir), \"project\");\n\t}\n\treturn roster;\n}\n\nfunction mergeDir(\n\troster: Map<string, AgentDefinition>,\n\tdir: string,\n\tsource: \"user\" | \"project\",\n): void {\n\tlet files: string[];\n\ttry {\n\t\tfiles = readdirSync(dir)\n\t\t\t.filter((f) => f.endsWith(\".md\"))\n\t\t\t.sort();\n\t} catch {\n\t\treturn; // directory absent — nothing to merge\n\t}\n\tfor (const file of files) {\n\t\tconst def = parseAgentFile(join(dir, file), file, source);\n\t\tif (def) roster.set(def.name, def);\n\t}\n}\n\n/** One bullet per agent, for the tool description and unknown-agent errors. */\nexport function rosterSummary(roster: Map<string, AgentDefinition>): string {\n\treturn [...roster.values()]\n\t\t.map((a) => `- ${a.name}: ${a.description || \"(no description)\"}`)\n\t\t.join(\"\\n\");\n}\n\nfunction parseAgentFile(\n\tpath: string,\n\tfilename: string,\n\tsource: \"user\" | \"project\",\n): AgentDefinition | undefined {\n\ttry {\n\t\tconst raw = readFileSync(path, \"utf8\");\n\t\tconst { frontmatter, body } = parseFrontmatter(raw);\n\t\tconst name = strField(frontmatter.name) ?? filename.replace(/\\.md$/, \"\").trim();\n\t\tif (!name) return undefined;\n\t\treturn {\n\t\t\tname,\n\t\t\tdescription: strField(frontmatter.description) ?? \"\",\n\t\t\tsystemPrompt: body.trim(),\n\t\t\ttools: toolsField(frontmatter.tools),\n\t\t\tmodel: strField(frontmatter.model),\n\t\t\tsource,\n\t\t};\n\t} catch {\n\t\treturn undefined; // unreadable / malformed file — skip, never break the roster\n\t}\n}\n\nfunction strField(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction toolsField(value: unknown): string[] | undefined {\n\tif (typeof value === \"string\") {\n\t\tconst parts = value\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean);\n\t\treturn parts.length > 0 ? parts : undefined;\n\t}\n\tif (Array.isArray(value)) {\n\t\tconst parts = value.filter((v): v is string => typeof v === \"string\" && v.trim() !== \"\");\n\t\treturn parts.length > 0 ? parts.map((s) => s.trim()) : undefined;\n\t}\n\treturn undefined;\n}\n","/**\n * pi-pilot's built-in subagent presets: scout / worker / reviewer.\n *\n * These make the subagent tool useful out of the box, before the user\n * writes any `~/.pi/agent/agents/*.md`; a user file with the same name\n * overrides the preset (agents.ts merges user over builtin). Provider-\n * agnostic by design: no model pin — children inherit the parent session's\n * model unless an agent file says otherwise.\n *\n * Every prompt repeats two hard rules the harness can only partially\n * enforce structurally: never invoke the `pi` CLI (children get\n * --no-extensions so the subagent tool itself is unreachable, but bash\n * could still reach a globally installed `pi` — the prompt closes that\n * loop), and never wait for user input (children run headless; prompts\n * auto-deny).\n */\n\nimport type { AgentDefinition } from \"./agents.ts\";\n\nconst COMMON_RULES = `Hard rules:\n- You run headless as a subagent: there is NO user to talk to. Never ask questions, never wait for confirmation — decide and proceed.\n- NEVER run the \\`pi\\` CLI or spawn any other agent. No nesting.\n- End with ONE final message that is a complete, self-contained report — it is the ONLY thing returned to the agent that delegated to you. Keep it under ~8000 characters and reference code as \\`path:line\\`.`;\n\nconst scout: AgentDefinition = {\n\tname: \"scout\",\n\tsource: \"builtin\",\n\tdescription:\n\t\t\"Fast read-only codebase recon: locate files, symbols, flows, conventions, and report compressed findings. Cheap to use; cannot edit anything.\",\n\ttools: [\"read\", \"grep\", \"find\", \"ls\"],\n\tsystemPrompt: `You are \"scout\", a read-only reconnaissance subagent.\n\n${COMMON_RULES}\n\nMethod: start broad (find/ls/grep), then read only the spans that matter. Prefer reading slices over whole files. Stop as soon as you can answer confidently.\n\nFinal report shape: a short answer first, then the supporting map — relevant files with one-line roles, key symbols as \\`path:line\\`, and any conventions or gotchas the delegator should know. Say explicitly what you did NOT verify.`,\n};\n\nconst worker: AgentDefinition = {\n\tname: \"worker\",\n\tsource: \"builtin\",\n\tdescription:\n\t\t\"General-purpose implementer with the full default toolset (bash/edit/write). Use for a self-contained change with a precise brief; verifies its own work.\",\n\tsystemPrompt: `You are \"worker\", an implementation subagent.\n\n${COMMON_RULES}\n\nMethod: read the relevant code before changing it; follow the surrounding style exactly; make the smallest change that satisfies the brief. Verify with the project's own commands (typecheck / tests / build) when available — report what you ran and its outcome honestly.\n\nFinal report shape: what changed (file by file, one line each), how it was verified (commands + results), and any caveats or follow-ups. If you could not finish, say precisely how far you got and what is left.`,\n};\n\nconst reviewer: AgentDefinition = {\n\tname: \"reviewer\",\n\tsource: \"builtin\",\n\tdescription:\n\t\t\"Code review of specific files or diffs: correctness, edge cases, convention drift. Read-mostly (bash for git diff / running tests). Returns prioritized findings.\",\n\ttools: [\"read\", \"grep\", \"find\", \"ls\", \"bash\"],\n\tsystemPrompt: `You are \"reviewer\", a code-review subagent. Your job is to FIND problems, not to fix them — do not edit any file.\n\n${COMMON_RULES}\n\nMethod: read the target code fully before judging; use bash only for read-only inspection (git diff/log, running existing tests). Hunt real defects first — correctness, edge cases, lifecycle/cleanup holes — then convention drift. Verify each suspicion against the actual code before reporting it.\n\nFinal report shape: findings ordered by severity, each with \\`path:line\\`, what's wrong, why it matters, and a concrete suggested fix. End with what you checked and found clean, so silence isn't ambiguous.`,\n};\n\nexport const BUILTIN_AGENTS: AgentDefinition[] = [scout, worker, reviewer];\n","/**\n * Per-workspace trust gate for project-local `.pi/agents/`.\n *\n * Repo-controlled agent prompts are an injection vector: cloning a\n * malicious repo must never silently hand its files a subagent system\n * prompt. So project agents only load when the user flipped the\n * workspace's `trustProjectAgents` switch (Resources panel → persisted in\n * `~/.pi/webui/workspaces.json`). The flag is SERVER-side state keyed by\n * the workspace path — it is never a tool argument, so the model cannot\n * grant trust to itself.\n *\n * Read synchronously from disk on every call (same re-read-per-call\n * discipline as roster discovery — one small JSON read, and the Resources\n * toggle applies to live sessions without a runtime rebuild). The data dir\n * resolves from `PI_PILOT_DATA_DIR` at CALL time (mirroring `config.ts`'s\n * default) rather than importing `config.ts`, whose module-load env capture\n * would defeat test isolation — the same reason agents.ts leans on\n * `getAgentDir()` honoring `PI_CODING_AGENT_DIR` lazily.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\n\nexport function isProjectDirTrusted(projectDir: string): boolean {\n\tconst registryPath = join(\n\t\tprocess.env.PI_PILOT_DATA_DIR ?? join(homedir(), \".pi\", \"webui\"),\n\t\t\"workspaces.json\",\n\t);\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(registryPath, \"utf8\")) as {\n\t\t\tworkspaces?: { path?: unknown; trustProjectAgents?: unknown }[];\n\t\t};\n\t\tif (!Array.isArray(raw.workspaces)) return false;\n\t\tconst wanted = resolve(projectDir);\n\t\treturn raw.workspaces.some(\n\t\t\t(w) =>\n\t\t\t\ttypeof w?.path === \"string\" &&\n\t\t\t\tresolve(w.path) === wanted &&\n\t\t\t\tw.trustProjectAgents === true,\n\t\t);\n\t} catch {\n\t\treturn false; // no registry / malformed — never trust by accident\n\t}\n}\n","/**\n * Spawn + supervise one pinned-CLI subagent child process.\n *\n * Invocation: `node <pinned cli.js> --mode json -p --no-session\n * --no-extensions --no-skills [--model p/id] [--tools a,b]\n * --append-system-prompt <tmpfile> \"<task>\"`, in the parent session's cwd.\n * `--mode json -p` streams the child's full event feed as NDJSON on\n * stdout; we consume assistant `message_end` (usage, tool-call activity,\n * final text — everything else is derivable) and tee EVERY raw line to a\n * transcript file so failed runs stay debuggable after the fact. In json\n * print mode an agent-level error still exits 0 — `stopReason` is the\n * primary failure signal, exitCode is secondary.\n *\n * Kill discipline: SIGTERM first — the child CLI's own SIGTERM handler is\n * what reaps its detached bash grandchildren (a process-group kill could\n * not: pi spawns bash with detached:true into separate groups) — then\n * SIGKILL after a grace window if it ignored us, accepting that this may\n * orphan grandchildren (documented Known gap). The system-prompt temp file\n * lives under a `pi-pilot-subagent-*` directory so its path in the child's\n * argv doubles as the crash-sweep marker (see cleanup.ts).\n */\n\nimport { spawn } from \"node:child_process\";\nimport {\n\tcreateWriteStream,\n\tmkdirSync,\n\tmkdtempSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentDefinition } from \"./agents.ts\";\nimport {\n\temptyUsage,\n\ttype SubagentActivityItem,\n\ttype SubagentUsage,\n} from \"./schema.ts\";\nimport { resolvePinnedPiCli } from \"./pi-bin.ts\";\nimport { registerChild, unregisterChild } from \"./registry.ts\";\n\n/** Marker prefix: appears in every child's argv via the prompt-file path. */\nexport const PROMPT_DIR_PREFIX = \"pi-pilot-subagent-\";\n\n/** Where child NDJSON transcripts are teed (OS temp — may be reclaimed). */\nexport const TRANSCRIPTS_DIR = join(tmpdir(), \"pi-pilot-subagents\", \"transcripts\");\n\nconst ACTIVITY_MAX = 30;\nconst LABEL_MAX = 160;\nconst STDERR_TAIL_MAX = 2048;\nconst FINAL_TEXT_MAX = 200_000;\nconst SIGKILL_DELAY_MS = 5000;\n\nexport interface RunChildOptions {\n\ttoolCallId: string;\n\tagent: AgentDefinition;\n\ttask: string;\n\tcwd: string;\n\tsessionFile: string | null;\n\t/** Full text appended to the child's default system prompt (contract + agent prompt). */\n\tappendSystemPrompt: string;\n\t/** \"provider/id\" used for --model when the agent doesn't pin one. */\n\tinheritModel?: string;\n\tsignal: AbortSignal | undefined;\n\t/** Total wall-clock ceiling from spawn. A loose backstop — healthy long\n\t * runs are the norm; hang detection is `stallTimeoutMs`'s job. */\n\ttimeoutMs: number;\n\t/** Kill when stdout AND stderr stay completely silent this long. This is\n\t * the actual hang detector (provider stall, wedged process): while a\n\t * child works, the NDJSON event stream ticks constantly; sustained\n\t * silence longer than any plausible quiet bash command means dead. */\n\tstallTimeoutMs: number;\n\t/** Kill the child once accumulated cost crosses this (USD). */\n\tcostCeilingUsd: number;\n\tonProgress: (progress: ChildProgress) => void;\n\t/** Test seam: override the spawned CLI entry. */\n\tcliPath?: string;\n}\n\nexport interface ChildProgress {\n\tusage: SubagentUsage;\n\tmodel?: string;\n\tactivity: SubagentActivityItem[];\n\tlastLabel?: string;\n}\n\nexport interface ChildOutcome {\n\tstatus: \"done\" | \"failed\" | \"timeout\" | \"aborted\";\n\tfinalText: string;\n\tusage: SubagentUsage;\n\tmodel?: string;\n\tstopReason?: string;\n\terrorMessage?: string;\n\tstderrTail: string;\n\texitCode: number;\n\tdurationMs: number;\n\ttranscriptPath?: string;\n\tactivity: SubagentActivityItem[];\n}\n\nexport async function runChild(opts: RunChildOptions): Promise<ChildOutcome> {\n\tconst startedAt = Date.now();\n\t// PI_PILOT_SUBAGENT_CLI: escape hatch to point children at a different pi\n\t// build (and the seam factory-level tests use to inject a fake CLI).\n\tconst cli = opts.cliPath ?? process.env.PI_PILOT_SUBAGENT_CLI ?? resolvePinnedPiCli();\n\n\t// Marker dir doubles as the crash-sweep record: cleanup.ts reads the\n\t// pidfile written after spawn. An argv marker would NOT work — the pi\n\t// CLI sets process.title, which on macOS clobbers the whole visible\n\t// argv (ps/pgrep -f see only \"pi\"), verified live.\n\tconst promptDir = mkdtempSync(join(tmpdir(), PROMPT_DIR_PREFIX));\n\tconst promptPath = join(promptDir, \"prompt.md\");\n\twriteFileSync(promptPath, opts.appendSystemPrompt, { mode: 0o600 });\n\n\tlet transcriptPath: string | undefined;\n\tlet tee: ReturnType<typeof createWriteStream> | undefined;\n\ttry {\n\t\tmkdirSync(TRANSCRIPTS_DIR, { recursive: true });\n\t\ttranscriptPath = join(TRANSCRIPTS_DIR, `${sanitizeId(opts.toolCallId)}.ndjson`);\n\t\ttee = createWriteStream(transcriptPath, { flags: \"w\" });\n\t\ttee.on(\"error\", () => {\n\t\t\t// Transcript is best-effort; never fail the run over it.\n\t\t});\n\t} catch {\n\t\ttranscriptPath = undefined;\n\t}\n\n\tconst args = [cli, \"--mode\", \"json\", \"-p\", \"--no-session\", \"--no-extensions\", \"--no-skills\"];\n\tconst model = opts.agent.model ?? opts.inheritModel;\n\tif (model) args.push(\"--model\", model);\n\tif (opts.agent.tools && opts.agent.tools.length > 0) {\n\t\targs.push(\"--tools\", opts.agent.tools.join(\",\"));\n\t}\n\targs.push(\"--append-system-prompt\", promptPath);\n\targs.push(opts.task);\n\n\tconst usage = emptyUsage();\n\tconst activity: SubagentActivityItem[] = [];\n\tlet modelSeen: string | undefined;\n\tlet finalText = \"\";\n\tlet stopReason: string | undefined;\n\tlet errorMessage: string | undefined;\n\tlet stderrAccum = \"\";\n\tlet aborted = false;\n\tlet timedOut = false;\n\tlet costKilled = false;\n\n\tconst child = spawn(process.execPath, args, {\n\t\tcwd: opts.cwd,\n\t\tenv: { ...process.env, PI_PILOT_SUBAGENT: opts.toolCallId },\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\tshell: false,\n\t});\n\tif (child.pid !== undefined) {\n\t\ttry {\n\t\t\t// Line 1: child pid. Line 2: OWNER (this server) pid — the sweep\n\t\t\t// skips records whose owner still lives, so an out-of-process\n\t\t\t// sweep run (second instance, a vitest suite — a reviewer subagent\n\t\t\t// once SIGTERMed itself and its siblings by running our own tests,\n\t\t\t// which swept the real tmpdir) can never kill a managed child.\n\t\t\twriteFileSync(join(promptDir, \"pid\"), `${child.pid}\\n${process.pid}`);\n\t\t} catch {\n\t\t\t// Sweep record is best-effort; the run itself doesn't depend on it.\n\t\t}\n\t}\n\n\tlet killed = false;\n\tlet killTimer: NodeJS.Timeout | undefined;\n\tconst killGracefully = () => {\n\t\tif (killed) return;\n\t\tkilled = true;\n\t\ttry {\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t} catch {\n\t\t\t// already gone\n\t\t}\n\t\tkillTimer = setTimeout(() => {\n\t\t\ttry {\n\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t} catch {\n\t\t\t\t// already gone\n\t\t\t}\n\t\t}, SIGKILL_DELAY_MS);\n\t};\n\n\tregisterChild(opts.toolCallId, {\n\t\tsessionFile: opts.sessionFile,\n\t\tagent: opts.agent.name,\n\t\tkill: killGracefully,\n\t});\n\n\tconst onAbort = () => {\n\t\taborted = true;\n\t\tkillGracefully();\n\t};\n\tif (opts.signal?.aborted) onAbort();\n\telse opts.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\tlet stalled = false;\n\tconst timeoutTimer = setTimeout(() => {\n\t\ttimedOut = true;\n\t\tkillGracefully();\n\t}, opts.timeoutMs);\n\n\t// Hang detector: re-armed on EVERY byte of child output (stdout or\n\t// stderr). Distinct from the total ceiling so a healthy multi-hour-ish\n\t// run is never killed mid-stride just for taking long.\n\tlet stallTimer: NodeJS.Timeout | undefined;\n\tconst armStallTimer = () => {\n\t\tif (killed) return;\n\t\tif (stallTimer) clearTimeout(stallTimer);\n\t\tstallTimer = setTimeout(() => {\n\t\t\tstalled = true;\n\t\t\ttimedOut = true;\n\t\t\tkillGracefully();\n\t\t}, opts.stallTimeoutMs);\n\t};\n\tarmStallTimer();\n\n\tconst emitProgress = () => {\n\t\topts.onProgress({\n\t\t\tusage: { ...usage },\n\t\t\tmodel: modelSeen,\n\t\t\tactivity: [...activity],\n\t\t\tlastLabel: activity[activity.length - 1]?.label,\n\t\t});\n\t};\n\n\tconst handleLine = (line: string) => {\n\t\tif (!line.trim()) return;\n\t\ttee?.write(line + \"\\n\");\n\t\tlet event: unknown;\n\t\ttry {\n\t\t\tevent = JSON.parse(line);\n\t\t} catch {\n\t\t\treturn; // non-JSON noise on stdout — teed above, otherwise ignored\n\t\t}\n\t\tconst ev = event as { type?: string; message?: unknown };\n\t\tif (ev.type !== \"message_end\" || !ev.message || typeof ev.message !== \"object\") return;\n\t\tconst msg = ev.message as {\n\t\t\trole?: string;\n\t\t\tmodel?: string;\n\t\t\tstopReason?: string;\n\t\t\terrorMessage?: string;\n\t\t\tusage?: {\n\t\t\t\tinput?: number;\n\t\t\t\toutput?: number;\n\t\t\t\tcacheRead?: number;\n\t\t\t\tcacheWrite?: number;\n\t\t\t\ttotalTokens?: number;\n\t\t\t\tcost?: { total?: number };\n\t\t\t};\n\t\t\tcontent?: unknown;\n\t\t};\n\t\tif (msg.role !== \"assistant\") return;\n\n\t\tusage.turns++;\n\t\tconst u = msg.usage;\n\t\tif (u) {\n\t\t\tusage.input += u.input ?? 0;\n\t\t\tusage.output += u.output ?? 0;\n\t\t\tusage.cacheRead += u.cacheRead ?? 0;\n\t\t\tusage.cacheWrite += u.cacheWrite ?? 0;\n\t\t\tusage.cost += u.cost?.total ?? 0;\n\t\t\tusage.contextTokens = u.totalTokens ?? usage.contextTokens;\n\t\t}\n\t\tif (!modelSeen && typeof msg.model === \"string\") modelSeen = msg.model;\n\t\tif (typeof msg.stopReason === \"string\") stopReason = msg.stopReason;\n\t\tif (typeof msg.errorMessage === \"string\") errorMessage = msg.errorMessage;\n\n\t\tif (Array.isArray(msg.content)) {\n\t\t\tconst textParts: string[] = [];\n\t\t\tfor (const block of msg.content) {\n\t\t\t\tif (!block || typeof block !== \"object\") continue;\n\t\t\t\tconst b = block as { type?: string; text?: unknown; name?: unknown; arguments?: unknown };\n\t\t\t\tif (b.type === \"text\" && typeof b.text === \"string\") {\n\t\t\t\t\ttextParts.push(b.text);\n\t\t\t\t} else if (b.type === \"toolCall\" && typeof b.name === \"string\") {\n\t\t\t\t\tpushActivity(activity, b.name, b.arguments);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst text = textParts.join(\"\").trim();\n\t\t\tif (text) finalText = text.slice(0, FINAL_TEXT_MAX);\n\t\t}\n\n\t\tif (usage.cost > opts.costCeilingUsd && !costKilled) {\n\t\t\tcostKilled = true;\n\t\t\tkillGracefully();\n\t\t}\n\t\temitProgress();\n\t};\n\n\tlet buf = \"\";\n\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\tarmStallTimer();\n\t\tbuf += chunk.toString(\"utf8\");\n\t\tlet nl: number;\n\t\twhile ((nl = buf.indexOf(\"\\n\")) >= 0) {\n\t\t\thandleLine(buf.slice(0, nl));\n\t\t\tbuf = buf.slice(nl + 1);\n\t\t}\n\t});\n\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\tarmStallTimer();\n\t\tstderrAccum = (stderrAccum + chunk.toString(\"utf8\")).slice(-STDERR_TAIL_MAX);\n\t});\n\n\tconst exitCode = await new Promise<number>((resolve) => {\n\t\tchild.on(\"error\", (err) => {\n\t\t\terrorMessage ??= err instanceof Error ? err.message : String(err);\n\t\t\tresolve(-1);\n\t\t});\n\t\tchild.on(\"close\", (code) => resolve(code ?? -1));\n\t});\n\n\tif (buf) handleLine(buf);\n\tclearTimeout(timeoutTimer);\n\tif (stallTimer) clearTimeout(stallTimer);\n\tif (killTimer) clearTimeout(killTimer);\n\topts.signal?.removeEventListener(\"abort\", onAbort);\n\tunregisterChild(opts.toolCallId);\n\ttee?.end();\n\tawait rm(promptDir, { recursive: true, force: true }).catch(() => {});\n\n\tif (costKilled && !errorMessage) {\n\t\terrorMessage = `cost ceiling ($${opts.costCeilingUsd}) exceeded — child terminated`;\n\t}\n\tif (timedOut && !aborted && !errorMessage) {\n\t\terrorMessage = stalled\n\t\t\t? `no output for ${Math.round(opts.stallTimeoutMs / 1000)}s — presumed hung`\n\t\t\t: `wall-clock limit (${Math.round(opts.timeoutMs / 1000)}s) reached while still active`;\n\t}\n\tconst failed =\n\t\texitCode !== 0 || stopReason === \"error\" || stopReason === \"aborted\" || costKilled;\n\tconst status: ChildOutcome[\"status\"] = aborted\n\t\t? \"aborted\"\n\t\t: timedOut\n\t\t\t? \"timeout\"\n\t\t\t: failed\n\t\t\t\t? \"failed\"\n\t\t\t\t: \"done\";\n\n\treturn {\n\t\tstatus,\n\t\tfinalText,\n\t\tusage,\n\t\tmodel: modelSeen,\n\t\tstopReason,\n\t\terrorMessage,\n\t\tstderrTail: stderrAccum.trim(),\n\t\texitCode,\n\t\tdurationMs: Date.now() - startedAt,\n\t\ttranscriptPath,\n\t\tactivity,\n\t};\n}\n\nfunction sanitizeId(id: string): string {\n\treturn id.replace(/[^A-Za-z0-9._-]/g, \"_\");\n}\n\nfunction pushActivity(activity: SubagentActivityItem[], name: string, args: unknown): void {\n\tconst label = toolLabel(name, args).slice(0, LABEL_MAX);\n\tactivity.push({ kind: \"tool\", label });\n\tif (activity.length > ACTIVITY_MAX) {\n\t\tactivity.splice(0, activity.length - ACTIVITY_MAX);\n\t}\n}\n\nfunction toolLabel(name: string, args: unknown): string {\n\tconst a = (args && typeof args === \"object\" ? args : {}) as Record<string, unknown>;\n\tconst pick = (...keys: string[]): string | undefined => {\n\t\tfor (const key of keys) {\n\t\t\tconst v = a[key];\n\t\t\tif (typeof v === \"string\" && v.trim()) return v;\n\t\t}\n\t\treturn undefined;\n\t};\n\tlet detail: string | undefined;\n\tswitch (name) {\n\t\tcase \"bash\":\n\t\t\tdetail = pick(\"command\");\n\t\t\tbreak;\n\t\tcase \"read\":\n\t\tcase \"write\":\n\t\tcase \"edit\":\n\t\t\tdetail = pick(\"path\", \"file_path\");\n\t\t\tbreak;\n\t\tcase \"grep\":\n\t\t\tdetail = pick(\"pattern\");\n\t\t\tbreak;\n\t\tcase \"find\":\n\t\t\tdetail = pick(\"pattern\", \"path\");\n\t\t\tbreak;\n\t\tcase \"ls\":\n\t\t\tdetail = pick(\"path\");\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tfor (const v of Object.values(a)) {\n\t\t\t\tif (typeof v === \"string\" && v.trim()) {\n\t\t\t\t\tdetail = v;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tconst clean = detail?.replace(/\\s+/g, \" \").trim();\n\treturn clean ? `${name}: ${clean}` : name;\n}\n","/**\n * TypeBox params schema + structured detail types for the subagent tool.\n *\n * Kept separate from factory.ts (ask_user precedent) so the child-process\n * supervisor, registry and tests can share shapes without importing the\n * factory. These are server-side truth; the web narrows the same shapes\n * defensively in `packages/web/src/lib/subagent.ts` and never imports them\n * (zero pi / zero cross-package imports rule).\n *\n * Two call forms share one schema: single (`agent` + `task`) and parallel\n * (`tasks: [{agent, task}, …]`, all spawned concurrently). Exactly one form\n * must be used per call — execute() validates that (typebox can't express\n * the exclusive-or without a union, which renders poorly for models).\n */\n\nimport { Type, type Static } from \"typebox\";\n\n/** Hard cap on `tasks[]` length per call (the aggregate-output and context\n * budgets are sized for this; the global semaphore is 8 slots anyway). */\nexport const MAX_TASKS_PER_CALL = 8;\n\nconst taskBriefDescription =\n\t\"Complete, self-contained task brief. The subagent sees NOTHING of this conversation — include all relevant paths, constraints, context, and the exact shape of the answer you want back.\";\n\nexport const subagentParamsSchema = Type.Object({\n\tagent: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Single mode: agent to delegate to, by name. The roster is listed in the tool description; an unknown name returns the available roster. Use together with `task`; omit when using `tasks`.\",\n\t\t}),\n\t),\n\ttask: Type.Optional(Type.String({ description: taskBriefDescription })),\n\ttasks: Type.Optional(\n\t\tType.Array(\n\t\t\tType.Object({\n\t\t\t\tagent: Type.String({ description: \"Agent to delegate this task to, by name.\" }),\n\t\t\t\ttask: Type.String({ description: taskBriefDescription }),\n\t\t\t}),\n\t\t\t{\n\t\t\t\tmaxItems: MAX_TASKS_PER_CALL,\n\t\t\t\tdescription:\n\t\t\t\t\t`Parallel mode: up to ${MAX_TASKS_PER_CALL} INDEPENDENT task briefs, run concurrently. ` +\n\t\t\t\t\t\"All results return together in one combined report. Only for tasks with no ordering \" +\n\t\t\t\t\t\"dependency — sequence dependent steps as separate subagent calls instead. \" +\n\t\t\t\t\t\"Omit when using `agent`/`task`.\",\n\t\t\t},\n\t\t),\n\t),\n});\n\nexport type SubagentParams = Static<typeof subagentParamsSchema>;\n\n/** Child usage, accumulated across the child's assistant messages. */\nexport interface SubagentUsage {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\tcost: number;\n\tcontextTokens: number;\n\tturns: number;\n}\n\n/**\n * One line of the activity trail (a child tool call). Labels are capped at\n * source (see child.ts) so a giant bash heredoc can't bloat the session\n * JSONL or the wire.\n */\nexport interface SubagentActivityItem {\n\tkind: \"tool\";\n\tlabel: string;\n}\n\nexport type SubagentTaskStatus =\n\t| \"queued\"\n\t| \"running\"\n\t| \"done\"\n\t| \"failed\"\n\t| \"timeout\"\n\t| \"aborted\";\n\nexport interface SubagentTaskDetails {\n\tagent: string;\n\tagentSource: \"builtin\" | \"user\" | \"project\";\n\ttask: string;\n\tstatus: SubagentTaskStatus;\n\t/** Last N activity items (ring buffer, oldest dropped). */\n\tactivity: SubagentActivityItem[];\n\tusage: SubagentUsage;\n\tmodel?: string;\n\tstopReason?: string;\n\terrorMessage?: string;\n\tstderrTail?: string;\n\texitCode?: number;\n\tdurationMs?: number;\n\t/** Tail-capped final text for the card; the parent-visible text rides content. */\n\tfinalPreview?: string;\n\t/** Absolute path of the child's full NDJSON transcript (OS temp dir — may be reclaimed). */\n\ttranscriptPath?: string;\n}\n\n/**\n * The details payload persisted on the toolResult and attached to every\n * onUpdate. Versioned so the web card can hard-skip shapes it doesn't know.\n */\nexport interface SubagentDetails {\n\tversion: 1;\n\tmode: \"single\" | \"parallel\";\n\ttasks: SubagentTaskDetails[];\n}\n\nexport function emptyUsage(): SubagentUsage {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\tcost: 0,\n\t\tcontextTokens: 0,\n\t\tturns: 0,\n\t};\n}\n\n/** Structural guard used by the tool_result hook (and mirrored web-side). */\nexport function isSubagentDetails(value: unknown): value is SubagentDetails {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst v = value as { version?: unknown; mode?: unknown; tasks?: unknown };\n\treturn (\n\t\tv.version === 1 &&\n\t\t(v.mode === \"single\" || v.mode === \"parallel\") &&\n\t\tArray.isArray(v.tasks)\n\t);\n}\n","/**\n * Resolve the EXACT-PINNED pi CLI entry for spawning subagent children.\n *\n * Children must run the same pi version the server's SDK is pinned to —\n * the floating global `pi` binary has already caused real streaming hangs\n * from version skew (PROGRESS history: 0.75.4 vs 0.78.0). `require.resolve`\n * can't locate the package here: its `exports` map exposes only \".\" and\n * \"./hooks\", so resolving \"package.json\" throws ERR_PACKAGE_PATH_NOT_EXPORTED.\n * `import.meta.resolve` of the bare specifier works under Node 22 + tsx\n * (verified) and is pnpm-layout-agnostic; the symlink under this package's\n * own node_modules is the fallback for loaders without import.meta.resolve.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nlet cached: string | undefined;\n\nexport function resolvePinnedPiCli(): string {\n\tif (cached) return cached;\n\n\t// Primary: resolve the package main (dist/index.js), then sibling cli.js.\n\ttry {\n\t\tconst entry = fileURLToPath(\n\t\t\timport.meta.resolve(\"@earendil-works/pi-coding-agent\"),\n\t\t);\n\t\tconst candidate = join(dirname(entry), \"cli.js\");\n\t\tif (existsSync(candidate)) {\n\t\t\tcached = candidate;\n\t\t\treturn candidate;\n\t\t}\n\t} catch {\n\t\t// fall through to the symlink fallback\n\t}\n\n\tconst fallback = fileURLToPath(\n\t\tnew URL(\n\t\t\t\"../../../node_modules/@earendil-works/pi-coding-agent/dist/cli.js\",\n\t\t\timport.meta.url,\n\t\t),\n\t);\n\tif (existsSync(fallback)) {\n\t\tcached = fallback;\n\t\treturn fallback;\n\t}\n\n\tthrow new Error(\n\t\t\"subagent: cannot locate the pinned @earendil-works/pi-coding-agent CLI \" +\n\t\t\t\"(tried import.meta.resolve and the package-local node_modules symlink)\",\n\t);\n}\n","/**\n * Module-level registry of live subagent child processes + the global\n * concurrency gate.\n *\n * Why module-level (ask_user precedent): toolCallId is globally unique,\n * and the kill/sweep callers (workspace-manager dispose, server shutdown)\n * live outside any one session's factory closure. Entries carry the owning\n * session's file path for dispose routing — `null` for unpersisted\n * sessions, mirroring ask_user's documented edge (all unpersisted sessions\n * share the null bucket).\n *\n * Kill paths are belt-and-braces: the primary child-teardown route is\n * runtime.dispose() → agent.abort() → execute()'s AbortSignal → child.ts\n * kills and unregisters. `killChildrenForSession` / `killAllChildren`\n * sweep whatever that chain missed.\n *\n * The semaphore is process-global, NOT per-session: the resource it\n * protects (local CPU + provider spend) is process-global. FIFO; aborting\n * a queued waiter removes it without ever consuming a slot.\n */\n\nexport interface ChildHandle {\n\tsessionFile: string | null;\n\tagent: string;\n\t/** Graceful kill (SIGTERM, then SIGKILL escalation). Idempotent. */\n\tkill: () => void;\n}\n\nconst children = new Map<string, ChildHandle>();\n\nexport function registerChild(toolCallId: string, handle: ChildHandle): void {\n\tchildren.set(toolCallId, handle);\n}\n\nexport function unregisterChild(toolCallId: string): void {\n\tchildren.delete(toolCallId);\n}\n\n/** Kill every live child bound to the given session. Returns the count. */\nexport function killChildrenForSession(sessionFile: string | null): number {\n\tlet killed = 0;\n\tfor (const [id, handle] of children) {\n\t\tif (handle.sessionFile !== sessionFile) continue;\n\t\tchildren.delete(id);\n\t\thandle.kill();\n\t\tkilled++;\n\t}\n\treturn killed;\n}\n\n/** Kill every live child (server shutdown sweep). Returns the count. */\nexport function killAllChildren(): number {\n\tlet killed = 0;\n\tfor (const [id, handle] of children) {\n\t\tchildren.delete(id);\n\t\thandle.kill();\n\t\tkilled++;\n\t}\n\treturn killed;\n}\n\n/**\n * Max concurrently running children across ALL sessions. A parent turn can\n * legally issue several subagent calls at once (executionMode \"parallel\"\n * and/or a `tasks[]` fan-out), and several sessions can stream concurrently\n * — without this cap one message could fork-bomb the machine and the\n * provider bill.\n */\nexport const MAX_CONCURRENT_CHILDREN = 8;\n\n/**\n * Max concurrently running children per SESSION. Keeps one session's\n * `tasks[]` fan-out from monopolizing every global slot while another\n * session waits; the surplus tasks sit \"queued\" (visible in the card) and\n * start FIFO as siblings finish. Sessions are keyed by session file; all\n * unpersisted sessions share the `null` bucket (ask_user precedent).\n */\nexport const MAX_CONCURRENT_PER_SESSION = 4;\n\ninterface Waiter {\n\tgrant: () => void;\n\tsessionKey: string;\n\tsignal: AbortSignal | undefined;\n\tonAbort: (() => void) | undefined;\n}\n\nlet running = 0;\nconst runningPerSession = new Map<string, number>();\nconst waiters: Waiter[] = [];\n\nfunction keyOf(sessionFile: string | null | undefined): string {\n\treturn sessionFile ?? \"<unpersisted>\";\n}\n\n/** Observability for status lines and tests. */\nexport function childSlotStats(): { running: number; queued: number } {\n\treturn { running, queued: waiters.length };\n}\n\nfunction hasCapacity(sessionKey: string): boolean {\n\treturn (\n\t\trunning < MAX_CONCURRENT_CHILDREN &&\n\t\t(runningPerSession.get(sessionKey) ?? 0) < MAX_CONCURRENT_PER_SESSION\n\t);\n}\n\nfunction take(sessionKey: string): void {\n\trunning++;\n\trunningPerSession.set(sessionKey, (runningPerSession.get(sessionKey) ?? 0) + 1);\n}\n\n/**\n * Acquire a child slot, waiting when the global gate (8) or the caller's\n * per-session gate (4) is full. Grants are FIFO among *eligible* waiters —\n * a session at its cap never head-of-line-blocks a different session.\n * Resolves with an idempotent release function; rejects if `signal` aborts\n * first (the waiter is removed without consuming a slot).\n */\nexport function acquireChildSlot(\n\tsignal?: AbortSignal,\n\tsessionFile?: string | null,\n): Promise<() => void> {\n\tconst sessionKey = keyOf(sessionFile);\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new Error(\"Aborted by user\"));\n\t\t\treturn;\n\t\t}\n\t\tif (hasCapacity(sessionKey)) {\n\t\t\ttake(sessionKey);\n\t\t\tresolve(makeRelease(sessionKey));\n\t\t\treturn;\n\t\t}\n\t\tconst waiter: Waiter = {\n\t\t\tgrant: () => resolve(makeRelease(sessionKey)),\n\t\t\tsessionKey,\n\t\t\tsignal,\n\t\t\tonAbort: undefined,\n\t\t};\n\t\tif (signal) {\n\t\t\tconst onAbort = () => {\n\t\t\t\tconst i = waiters.indexOf(waiter);\n\t\t\t\tif (i >= 0) waiters.splice(i, 1);\n\t\t\t\treject(new Error(\"Aborted by user\"));\n\t\t\t};\n\t\t\twaiter.onAbort = onAbort;\n\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t\twaiters.push(waiter);\n\t});\n}\n\nfunction makeRelease(sessionKey: string): () => void {\n\tlet released = false;\n\treturn () => {\n\t\tif (released) return;\n\t\treleased = true;\n\t\trunning--;\n\t\tconst n = (runningPerSession.get(sessionKey) ?? 1) - 1;\n\t\tif (n <= 0) runningPerSession.delete(sessionKey);\n\t\telse runningPerSession.set(sessionKey, n);\n\t\tpump();\n\t};\n}\n\nfunction pump(): void {\n\tfor (let i = 0; i < waiters.length && running < MAX_CONCURRENT_CHILDREN; ) {\n\t\tconst waiter = waiters[i]!;\n\t\t// A waiter whose signal aborted was already removed by onAbort; this\n\t\t// guard only covers an abort that raced the scan.\n\t\tif (waiter.signal?.aborted) {\n\t\t\twaiters.splice(i, 1);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!hasCapacity(waiter.sessionKey)) {\n\t\t\ti++; // session at cap — skip, keep scanning for eligible waiters\n\t\t\tcontinue;\n\t\t}\n\t\twaiters.splice(i, 1);\n\t\tif (waiter.signal && waiter.onAbort) {\n\t\t\twaiter.signal.removeEventListener(\"abort\", waiter.onAbort);\n\t\t}\n\t\ttake(waiter.sessionKey);\n\t\twaiter.grant();\n\t}\n}\n","/**\n * pi-pilot's `subagent` builtin extension (P1: single + parallel modes).\n *\n * Registers one tool, `subagent`, that delegates self-contained tasks to\n * child agents running in ISOLATED contexts — each a separate process of\n * the exact-pinned pi CLI (./pi-bin.ts; never the floating global `pi`,\n * never an in-process AgentSession — pi-ai's provider registries are\n * process-global). Children get `--no-extensions --no-skills`, so they can\n * never see this tool (no nesting) nor ask_user/artifact (nothing\n * interactive to hang on headless).\n *\n * Call forms: `agent` + `task` runs one delegation; `tasks: [{agent,\n * task}, …]` fans out up to MAX_TASKS_PER_CALL independent delegations\n * concurrently (bounded by the global 8-child semaphore and the 4-per-\n * session gate — surplus tasks surface as \"queued\" rows in the card and\n * start FIFO as siblings finish).\n *\n * Result contract (Claude Code Task-tool semantics): only each child's\n * final message returns to the parent — per task tail-capped at\n * TASK_OUTPUT_CAP and, in parallel mode, the per-task allowance shrinks so\n * the combined report never exceeds AGGREGATE_OUTPUT_CAP (a fan-out can't\n * flood the very context it exists to protect). Full NDJSON transcripts\n * are teed to temp files whose paths ride `details.tasks[i]\n * .transcriptPath`. Failures come back as normal results with diagnostics\n * (stopReason primary — json print mode exits 0 on agent errors) and get\n * isError patched on via the `tool_result` hook: 0.78.0 hardcodes\n * isError:false for returned results, but the hook may patch it. The same\n * hook re-attaches our details snapshot when the abort path throws (pi\n * replaces thrown results with details:{}, which would erase the\n * usage/cost from the JSONL).\n *\n * Live progress rides onUpdate as a one-line status (wire `partialText`)\n * PLUS the structured details snapshot — the bridge forwards whitelisted\n * details on tool updates too, so the web card renders live per-task rows.\n * Emission is throttled, status transitions always flush, and the trailing\n * timer is cancelled on settle so no stale update can fire after the\n * result lands.\n *\n * State discipline: per-call state lives in execute(); the only closure\n * state is `lastDetails` (toolCallId → final snapshot for the tool_result\n * hook), per-session by construction (factories run per session — the\n * todo singleton lesson).\n */\n\nimport type { AgentToolResult, ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { discoverAgents, rosterSummary, userAgentsDir, type AgentDefinition } from \"./agents.ts\";\nimport { runChild, type ChildOutcome } from \"./child.ts\";\nimport { acquireChildSlot } from \"./registry.ts\";\nimport {\n\temptyUsage,\n\tisSubagentDetails,\n\tMAX_TASKS_PER_CALL,\n\tsubagentParamsSchema,\n\ttype SubagentDetails,\n\ttype SubagentParams,\n\ttype SubagentTaskDetails,\n} from \"./schema.ts\";\n\n/** Parent-visible cap on one child's final text (chars). */\nconst TASK_OUTPUT_CAP = 12 * 1024;\n/** Parent-visible cap on a parallel call's COMBINED text (chars) — the\n * per-task allowance shrinks to fit (review lesson: 12KB × 8 tasks is a\n * parent-context bomb, not a result). */\nconst AGGREGATE_OUTPUT_CAP = 48 * 1024;\n/** Cap on the card-facing finalPreview persisted in details (chars). */\nconst PREVIEW_CAP = 8 * 1024;\n/** Cap on the stderr tail persisted in details (chars). */\nconst STDERR_DETAILS_CAP = 1024;\n/** Min interval between onUpdate emissions. */\nconst UPDATE_THROTTLE_MS = 500;\n\n/** Env-tunable knob (read at module load; server restart applies changes). */\nfunction tunable(envName: string, fallback: number): number {\n\tconst raw = process.env[envName];\n\tif (!raw) return fallback;\n\tconst n = Number.parseFloat(raw);\n\treturn Number.isFinite(n) && n > 0 ? n : fallback;\n}\n\n/**\n * Lifecycle knobs. Calibration matters here: the kill switches exist for\n * RUNAWAY children (hung provider, wedged process, cost leak), not for\n * healthy-but-long work — a worker implementing + verifying on a slow\n * model legitimately runs tens of minutes, and killing steady progress at\n * an arbitrary wall-clock mark just wastes the spend (learned from real\n * use: the original 900s total killed healthy runs constantly).\n *\n * - Stall is the real hang detector: while a child works, its NDJSON\n * stream ticks on every delta/tool event; sustained full silence longer\n * than any plausible quiet bash command means dead.\n * - The total ceiling is a loose backstop, not a budget.\n * - The cost ceiling is a circuit breaker, not a budget — the card shows\n * live $ and Stop is always available. Applied PER CHILD.\n */\nconst TASK_TIMEOUT_MS = tunable(\"PI_PILOT_SUBAGENT_TIMEOUT_SEC\", 3600) * 1000;\nconst STALL_TIMEOUT_MS = tunable(\"PI_PILOT_SUBAGENT_STALL_SEC\", 600) * 1000;\nconst COST_CEILING_USD = tunable(\"PI_PILOT_SUBAGENT_COST_USD\", 20);\n\ntype SubagentResult = AgentToolResult<SubagentDetails>;\n\nexport const subagentExtensionFactory: ExtensionFactory = (pi) => {\n\t/** Final snapshots for the tool_result hook (abort re-attach + isError). */\n\tconst lastDetails = new Map<string, SubagentDetails>();\n\n\tconst rosterAtRegistration = discoverAgents();\n\n\tpi.registerTool({\n\t\tname: \"subagent\",\n\t\tlabel: \"Subagent\",\n\t\tdescription:\n\t\t\t\"Delegate self-contained tasks to subagents running in isolated contexts \" +\n\t\t\t\"(separate pi processes that see nothing of this conversation). Returns only \" +\n\t\t\t\"the subagents' final reports. Pass `agent` + `task` for one delegation, or \" +\n\t\t\t`\\`tasks\\` (up to ${MAX_TASKS_PER_CALL}) to run INDEPENDENT delegations in parallel. ` +\n\t\t\t\"Available agents:\\n\" +\n\t\t\trosterSummary(rosterAtRegistration) +\n\t\t\t`\\nThe roster is re-read on every call from ${userAgentsDir()}/*.md ` +\n\t\t\t\"(builtin presets scout/worker/reviewer; a same-name user file overrides its preset; \" +\n\t\t\t\"workspaces with project agents trusted also merge <cwd>/.pi/agents/*.md, which win over both).\",\n\t\tparameters: subagentParamsSchema,\n\t\texecutionMode: \"parallel\",\n\t\tpromptSnippet:\n\t\t\t\"subagent: delegate self-contained tasks (recon, a bounded implementation step, a review pass) to isolated child agents — only their final reports return, keeping large searches and side work out of this context. Independent tasks can fan out in parallel via `tasks`.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use subagent when a task is self-contained and would otherwise flood this context (broad codebase recon, a bounded implementation step, a review pass). Don't delegate trivial lookups — a single read/grep inline is faster and cheaper than a child agent.\",\n\t\t\t\"Write complete subagent briefs: the child sees NOTHING of this conversation. Include the relevant paths, constraints, acceptance criteria, and the exact shape of the report you want back.\",\n\t\t\t\"Pick the cheapest sufficient agent: scout for read-only recon, reviewer for read-mostly review, worker only when files must change.\",\n\t\t\t`Use \\`tasks\\` (max ${MAX_TASKS_PER_CALL} per call) ONLY for independent work — no task's input may depend on another's output, and parallel workers must never edit the same files. All results return together; sequence dependent steps as separate calls instead.`,\n\t\t\t\"Subagents cannot ask the user questions and cannot spawn further subagents. Resolve user decisions (ask_user) BEFORE delegating, and sequence dependent work yourself: delegate, read the report, then issue the next call with the context it needs.\",\n\t\t],\n\n\t\texecute: async (toolCallId, params, signal, onUpdate, ctx): Promise<SubagentResult> => {\n\t\t\tconst roster = discoverAgents(ctx.cwd);\n\t\t\tconst calls = normalizeCalls(params);\n\t\t\tif (typeof calls === \"string\") {\n\t\t\t\treturn invalidCallResult(calls, params);\n\t\t\t}\n\t\t\tconst { mode, briefs } = calls;\n\n\t\t\tconst unknown = [...new Set(briefs.map((b) => b.agent.trim()))].filter(\n\t\t\t\t(name) => !roster.has(name),\n\t\t\t);\n\t\t\tif (unknown.length > 0) {\n\t\t\t\treturn invalidCallResult(\n\t\t\t\t\t`Unknown agent${unknown.length > 1 ? \"s\" : \"\"} ${unknown.map((n) => `\"${n}\"`).join(\", \")}. Available agents:\\n` +\n\t\t\t\t\t\trosterSummary(roster) +\n\t\t\t\t\t\t\"\\nCall subagent again with one of these names.\",\n\t\t\t\t\tparams,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (signal?.aborted) throw new Error(\"Aborted by user\");\n\n\t\t\tconst sessionFile = ctx.sessionManager.getSessionFile() ?? null;\n\t\t\tconst resolved = briefs.map((b) => ({\n\t\t\t\tagent: roster.get(b.agent.trim())!,\n\t\t\t\ttask: b.task,\n\t\t\t}));\n\t\t\tconst tasks: SubagentTaskDetails[] = resolved.map((r) => ({\n\t\t\t\tagent: r.agent.name,\n\t\t\t\tagentSource: r.agent.source,\n\t\t\t\ttask: r.task,\n\t\t\t\tstatus: \"queued\",\n\t\t\t\tactivity: [],\n\t\t\t\tusage: emptyUsage(),\n\t\t\t}));\n\t\t\tconst details: SubagentDetails = { version: 1, mode, tasks };\n\t\t\tconst emitter = makeThrottledEmitter(onUpdate, details);\n\n\t\t\t// First snapshot up front: the card gets its row(s) — all \"queued\" —\n\t\t\t// before any slot resolves, so a gated fan-out is visible immediately.\n\t\t\temitter.emit(true);\n\n\t\t\tconst runOne = async (i: number): Promise<ChildOutcome | undefined> => {\n\t\t\t\tconst task = tasks[i]!;\n\t\t\t\tconst r = resolved[i]!;\n\t\t\t\tlet release: () => void;\n\t\t\t\ttry {\n\t\t\t\t\trelease = await acquireChildSlot(signal, sessionFile);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (!signal?.aborted) throw err;\n\t\t\t\t\t// Aborted while still queued — this task never ran.\n\t\t\t\t\ttask.status = \"aborted\";\n\t\t\t\t\temitter.emit(true);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\ttask.status = \"running\";\n\t\t\t\t\temitter.emit(true);\n\n\t\t\t\t\tconst outcome = await runChild({\n\t\t\t\t\t\t// Per-child id: registry entries, pidfiles and transcript\n\t\t\t\t\t\t// files must not collide across one call's siblings.\n\t\t\t\t\t\ttoolCallId: mode === \"single\" ? toolCallId : `${toolCallId}.${i}`,\n\t\t\t\t\t\tagent: r.agent,\n\t\t\t\t\t\ttask: r.task,\n\t\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\t\tsessionFile,\n\t\t\t\t\t\tappendSystemPrompt: composeChildPrompt(r.agent),\n\t\t\t\t\t\tinheritModel:\n\t\t\t\t\t\t\tr.agent.model ??\n\t\t\t\t\t\t\t(ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined),\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeoutMs: TASK_TIMEOUT_MS,\n\t\t\t\t\t\tstallTimeoutMs: STALL_TIMEOUT_MS,\n\t\t\t\t\t\tcostCeilingUsd: COST_CEILING_USD,\n\t\t\t\t\t\tonProgress: (p) => {\n\t\t\t\t\t\t\ttask.usage = p.usage;\n\t\t\t\t\t\t\ttask.model = p.model ?? task.model;\n\t\t\t\t\t\t\ttask.activity = p.activity;\n\t\t\t\t\t\t\temitter.emit();\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tmergeOutcome(task, outcome);\n\t\t\t\t\temitter.emit(true);\n\t\t\t\t\treturn outcome;\n\t\t\t\t} finally {\n\t\t\t\t\trelease();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tconst outcomes = await Promise.all(resolved.map((_, i) => runOne(i)));\n\n\t\t\t\temitter.cancel();\n\t\t\t\tlastDetails.set(toolCallId, details);\n\n\t\t\t\tif (tasks.some((t) => t.status === \"aborted\")) {\n\t\t\t\t\t// Throw so pi records the standard error toolResult; the\n\t\t\t\t\t// tool_result hook below re-attaches `details` (pi wipes them\n\t\t\t\t\t// on the throw path) so usage/cost survive in the JSONL.\n\t\t\t\t\tthrow new Error(\"Aborted by user\");\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: combinedParentText(mode, resolved, outcomes) }],\n\t\t\t\t\tdetails,\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\temitter.cancel();\n\t\t\t}\n\t\t},\n\t});\n\n\tpi.on(\"tool_result\", (ev) => {\n\t\tif (ev.toolName !== \"subagent\") return undefined;\n\t\tconst fromMap = lastDetails.get(ev.toolCallId);\n\t\tlastDetails.delete(ev.toolCallId);\n\t\tconst details = isSubagentDetails(ev.details) ? ev.details : fromMap;\n\t\tif (!details) return undefined;\n\n\t\tconst failed = details.tasks.some(\n\t\t\t(t) => t.status === \"failed\" || t.status === \"timeout\",\n\t\t);\n\t\tconst patch: { isError?: boolean; details?: unknown } = {};\n\t\tif (failed && !ev.isError) patch.isError = true;\n\t\tif (ev.isError && !isSubagentDetails(ev.details) && fromMap) patch.details = fromMap;\n\t\treturn patch.isError !== undefined || patch.details !== undefined ? patch : undefined;\n\t});\n};\n\n/**\n * Validate the two call forms into a flat brief list. Returns a\n * model-facing correction string when the shape is wrong — those come back\n * as a normal (hook-flagged) result, not a throw, so the model can retry.\n */\nfunction normalizeCalls(\n\tparams: SubagentParams,\n): { mode: \"single\" | \"parallel\"; briefs: { agent: string; task: string }[] } | string {\n\tconst hasSingle = params.agent !== undefined || params.task !== undefined;\n\tconst hasTasks = Array.isArray(params.tasks) && params.tasks.length > 0;\n\n\tif (hasSingle && hasTasks) {\n\t\treturn \"Pass EITHER `agent` + `task` (single delegation) OR `tasks` (parallel fan-out) — not both in one call.\";\n\t}\n\tif (hasTasks) {\n\t\tconst list = params.tasks!;\n\t\tif (list.length > MAX_TASKS_PER_CALL) {\n\t\t\treturn `tasks[] is capped at ${MAX_TASKS_PER_CALL} per call (got ${list.length}). Split the fan-out into multiple subagent calls.`;\n\t\t}\n\t\tif (list.some((t) => !t?.agent?.trim() || !t?.task?.trim())) {\n\t\t\treturn \"Every tasks[] entry needs a non-empty `agent` and `task`.\";\n\t\t}\n\t\treturn { mode: \"parallel\", briefs: list.map((t) => ({ agent: t.agent, task: t.task })) };\n\t}\n\tif (params.agent?.trim() && params.task?.trim()) {\n\t\treturn { mode: \"single\", briefs: [{ agent: params.agent, task: params.task }] };\n\t}\n\treturn \"Provide either `agent` + `task` (single delegation) or `tasks` (parallel fan-out of independent briefs).\";\n}\n\n/** Shape-error / unknown-agent result: empty-tasks details (\"nothing ran\"). */\nfunction invalidCallResult(text: string, params: SubagentParams): SubagentResult {\n\treturn {\n\t\tcontent: [{ type: \"text\", text }],\n\t\tdetails: {\n\t\t\tversion: 1,\n\t\t\tmode: Array.isArray(params.tasks) && params.tasks.length > 0 ? \"parallel\" : \"single\",\n\t\t\ttasks: [],\n\t\t},\n\t};\n}\n\n/** Child system-prompt = pi-pilot's subagent contract + the agent's own prompt. */\nfunction composeChildPrompt(agent: AgentDefinition): string {\n\tconst header =\n\t\t`You are running as the \"${agent.name}\" subagent, delegated one task by another agent via pi-pilot. ` +\n\t\t\"Work only within the project's working directory.\";\n\treturn agent.systemPrompt ? `${header}\\n\\n${agent.systemPrompt}` : header;\n}\n\nfunction mergeOutcome(task: SubagentTaskDetails, outcome: ChildOutcome): void {\n\ttask.status = outcome.status;\n\ttask.usage = outcome.usage;\n\ttask.activity = outcome.activity;\n\ttask.model = outcome.model ?? task.model;\n\ttask.stopReason = outcome.stopReason;\n\ttask.errorMessage = outcome.errorMessage;\n\ttask.stderrTail = outcome.stderrTail ? outcome.stderrTail.slice(-STDERR_DETAILS_CAP) : undefined;\n\ttask.exitCode = outcome.exitCode;\n\ttask.durationMs = outcome.durationMs;\n\ttask.transcriptPath = outcome.transcriptPath;\n\ttask.finalPreview = outcome.finalText ? tailCap(outcome.finalText, PREVIEW_CAP) : undefined;\n}\n\n/** The parent-visible text for the whole call (single report or fan-out digest). */\nfunction combinedParentText(\n\tmode: \"single\" | \"parallel\",\n\tresolved: { agent: AgentDefinition }[],\n\toutcomes: (ChildOutcome | undefined)[],\n): string {\n\tif (mode === \"single\") {\n\t\treturn formatParentText(resolved[0]!.agent, outcomes[0]!, TASK_OUTPUT_CAP);\n\t}\n\tconst n = outcomes.length;\n\tconst perTaskCap = Math.min(TASK_OUTPUT_CAP, Math.floor(AGGREGATE_OUTPUT_CAP / n));\n\tconst counts = new Map<string, number>();\n\tfor (const o of outcomes) {\n\t\tconst s = o?.status ?? \"aborted\";\n\t\tcounts.set(s, (counts.get(s) ?? 0) + 1);\n\t}\n\tconst summary = [...counts.entries()].map(([s, c]) => `${c} ${s}`).join(\", \");\n\tconst sections = outcomes.map((o, i) => {\n\t\tconst head = `=== task ${i + 1}/${n} ===`;\n\t\tconst body = o\n\t\t\t? formatParentText(resolved[i]!.agent, o, perTaskCap)\n\t\t\t: `[${resolved[i]!.agent.name}] never started (aborted while queued)`;\n\t\treturn `${head}\\n${body}`;\n\t});\n\treturn [`[subagent] ${n} parallel tasks: ${summary}`, ...sections].join(\"\\n\\n\");\n}\n\nfunction formatParentText(\n\tagent: AgentDefinition,\n\toutcome: ChildOutcome,\n\toutputCap: number,\n): string {\n\tconst stats =\n\t\t`${Math.round(outcome.durationMs / 1000)}s · ${outcome.usage.turns} turns · ` +\n\t\t`${fmtTokens(outcome.usage.input + outcome.usage.output)} tokens · $${outcome.usage.cost.toFixed(3)}`;\n\n\tif (outcome.status === \"done\") {\n\t\tconst body = outcome.finalText || \"(the subagent produced no final text)\";\n\t\treturn `[${agent.name}] done in ${stats}\\n\\n${tailCap(body, outputCap)}`;\n\t}\n\n\tconst head =\n\t\toutcome.status === \"timeout\"\n\t\t\t? `[${agent.name}] TIMED OUT after ${stats}`\n\t\t\t: `[${agent.name}] FAILED (stopReason=${outcome.stopReason ?? \"?\"}, exit ${outcome.exitCode}) after ${stats}`;\n\tconst parts = [head];\n\tif (outcome.errorMessage) parts.push(`error: ${outcome.errorMessage}`);\n\tif (outcome.finalText) parts.push(`partial output:\\n${tailCap(outcome.finalText, 2 * 1024)}`);\n\tif (outcome.stderrTail) parts.push(`stderr tail:\\n${outcome.stderrTail.slice(-STDERR_DETAILS_CAP)}`);\n\tif (outcome.transcriptPath) parts.push(`full transcript: ${outcome.transcriptPath}`);\n\tif (outcome.status === \"timeout\") {\n\t\tparts.push(\n\t\t\t\"note for the user: limits are env-tunable — PI_PILOT_SUBAGENT_STALL_SEC (silence detector) / PI_PILOT_SUBAGENT_TIMEOUT_SEC (total ceiling), server restart applies.\",\n\t\t);\n\t}\n\treturn parts.join(\"\\n\");\n}\n\n/** Keep the TAIL — a report's conclusion outranks its preamble. */\nfunction tailCap(text: string, max: number): string {\n\tif (text.length <= max) return text;\n\tconst dropped = text.length - max;\n\treturn `…(${dropped} chars truncated — full transcript in details)\\n${text.slice(-max)}`;\n}\n\nfunction fmtTokens(n: number): string {\n\treturn n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);\n}\n\ninterface ThrottledEmitter {\n\t/** Emit now when forced/due, else arm a trailing timer. */\n\temit: (force?: boolean) => void;\n\t/** Cancel any armed timer and refuse all further emissions. */\n\tcancel: () => void;\n}\n\ntype UpdateFn = ((update: { content: { type: \"text\"; text: string }[]; details: SubagentDetails }) => void) | undefined;\n\nfunction makeThrottledEmitter(onUpdate: UpdateFn, details: SubagentDetails): ThrottledEmitter {\n\tlet lastEmit = 0;\n\tlet timer: NodeJS.Timeout | undefined;\n\tlet cancelled = false;\n\n\tconst fire = () => {\n\t\ttimer = undefined;\n\t\tlastEmit = Date.now();\n\t\t// structuredClone: updates are dispatched synchronously but the\n\t\t// snapshot keeps mutating — never share the live object.\n\t\tonUpdate?.({\n\t\t\tcontent: [{ type: \"text\", text: statusLine(details) }],\n\t\t\tdetails: structuredClone(details),\n\t\t});\n\t};\n\n\treturn {\n\t\temit: (force = false) => {\n\t\t\tif (cancelled || !onUpdate) return;\n\t\t\tconst elapsed = Date.now() - lastEmit;\n\t\t\tif (force || elapsed >= UPDATE_THROTTLE_MS) {\n\t\t\t\tif (timer) {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\ttimer = undefined;\n\t\t\t\t}\n\t\t\t\tfire();\n\t\t\t} else if (!timer) {\n\t\t\t\ttimer = setTimeout(fire, UPDATE_THROTTLE_MS - elapsed);\n\t\t\t}\n\t\t},\n\t\tcancel: () => {\n\t\t\tcancelled = true;\n\t\t\tif (timer) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\ttimer = undefined;\n\t\t\t}\n\t\t},\n\t};\n}\n\n/** One human-readable line; rides the wire as `partialText` (collapsed card text). */\nfunction statusLine(details: SubagentDetails): string {\n\tif (details.tasks.length > 1) {\n\t\tconst counts = new Map<string, number>();\n\t\tlet cost = 0;\n\t\tlet turns = 0;\n\t\tfor (const t of details.tasks) {\n\t\t\tcounts.set(t.status, (counts.get(t.status) ?? 0) + 1);\n\t\t\tcost += t.usage.cost;\n\t\t\tturns += t.usage.turns;\n\t\t}\n\t\tconst bits = [`${details.tasks.length} tasks`];\n\t\tfor (const status of [\"running\", \"queued\", \"done\", \"failed\", \"timeout\", \"aborted\"]) {\n\t\t\tconst c = counts.get(status);\n\t\t\tif (c) bits.push(`${c} ${status}`);\n\t\t}\n\t\tif (turns > 0) bits.push(`$${cost.toFixed(3)}`);\n\t\treturn bits.join(\" · \");\n\t}\n\tconst t = details.tasks[0];\n\tif (!t) return \"subagent\";\n\tconst bits = [t.agent, t.status];\n\tif (t.usage.turns > 0) {\n\t\tbits.push(`${t.usage.turns} turns`);\n\t\tbits.push(`${fmtTokens(t.usage.input + t.usage.output)} tok`);\n\t\tbits.push(`$${t.usage.cost.toFixed(3)}`);\n\t}\n\tconst last = t.activity[t.activity.length - 1];\n\tif (last && t.status === \"running\") bits.push(last.label);\n\treturn bits.join(\" · \");\n}\n","/**\n * TypeBox parameter schemas + `details` domain types for the `web_search`\n * and `web_fetch` tools. Both are thin wrappers over Tavily (./client.ts).\n *\n * Params stay small and agent-friendly. The `details` snapshots are what\n * the web cards render — `web/src/lib/websearch.ts` re-narrows these wire\n * shapes field-by-field and never imports this file (the shared-package\n * rule, same as subagent). Keep the shapes in sync by hand; bump `version`\n * if a breaking change lands so a stale card degrades to its arg fallback\n * instead of mis-rendering.\n */\n\nimport { Type, type Static } from \"typebox\";\n\n// ---------------------------------------------------------------------------\n// web_search\n// ---------------------------------------------------------------------------\n\nexport const webSearchParamsSchema = Type.Object({\n\tquery: Type.String({\n\t\tdescription:\n\t\t\t\"The search query. Phrase it like a search-engine query (keywords, entities), not a chat sentence.\",\n\t}),\n\tmax_results: Type.Optional(\n\t\tType.Number({\n\t\t\tdescription: \"How many results to return (1–10). Default 5.\",\n\t\t\tminimum: 1,\n\t\t\tmaximum: 10,\n\t\t}),\n\t),\n\ttopic: Type.Optional(\n\t\tType.Union([Type.Literal(\"general\"), Type.Literal(\"news\")], {\n\t\t\tdescription: 'Search topic. Use \"news\" for recent/current events. Default \"general\".',\n\t\t}),\n\t),\n\tsearch_depth: Type.Optional(\n\t\tType.Union([Type.Literal(\"basic\"), Type.Literal(\"advanced\")], {\n\t\t\tdescription:\n\t\t\t\t'\"advanced\" digs deeper (slower, costs more credits); \"basic\" is usually enough. Default \"basic\".',\n\t\t}),\n\t),\n});\nexport type WebSearchParams = Static<typeof webSearchParamsSchema>;\n\n/** One ranked result, as persisted in `details` for the card. `content` is\n * a snippet truncated to a card-friendly length (the model gets a fuller\n * snippet in the tool's text output). */\nexport interface WebSearchResultItem {\n\ttitle: string;\n\turl: string;\n\tcontent: string;\n\tscore?: number;\n\tpublishedDate?: string;\n}\n\nexport interface WebSearchDetails {\n\tversion: 1;\n\tkind: \"search\";\n\tquery: string;\n\t/** Tavily's LLM-generated direct answer, when present. */\n\tanswer?: string;\n\tresults: WebSearchResultItem[];\n}\n\n// ---------------------------------------------------------------------------\n// web_fetch\n// ---------------------------------------------------------------------------\n\nexport const webFetchParamsSchema = Type.Object({\n\turls: Type.Array(\n\t\tType.String({ description: \"An absolute http(s) URL.\" }),\n\t\t{\n\t\t\tdescription: \"URLs to fetch and extract the main text from (1–5).\",\n\t\t\tminItems: 1,\n\t\t\tmaxItems: 5,\n\t\t},\n\t),\n});\nexport type WebFetchParams = Static<typeof webFetchParamsSchema>;\n\n/** One successfully extracted page. `content` is a preview truncated for the\n * card; `chars` is the full extracted length (the model gets the full text,\n * budget-capped, in the tool's text output). */\nexport interface WebFetchResultItem {\n\turl: string;\n\tcontent: string;\n\tchars: number;\n}\n\nexport interface WebFetchFailure {\n\turl: string;\n\terror: string;\n}\n\nexport interface WebFetchDetails {\n\tversion: 1;\n\tkind: \"fetch\";\n\tresults: WebFetchResultItem[];\n\tfailed: WebFetchFailure[];\n}\n","/**\n * Persistence for the web_search / web_fetch Tavily API key, configurable from\n * the Settings UI (PUT /api/web-search) and stored in\n * ~/.pi/webui/web-search.json.\n *\n * Resolution order for the LIVE key: the Settings value wins, falling back to\n * the TAVILY_API_KEY env var — so an existing env/.env setup keeps working and\n * a UI value transparently overrides it. The client reads `getTavilyApiKey()`\n * synchronously from the in-memory cache (no await on the request hot path),\n * populated once at startup by `loadWebSearchPrefs()` and updated on every\n * save — mirroring builtin-extension-prefs.ts.\n *\n * The file holds a secret: it's written 0600 and the key is NEVER echoed back\n * over the API — callers get only `configured`, which `source` is live, and a\n * masked tail (`…6w7B`).\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { config } from \"../config.ts\";\nimport { writeJsonAtomic } from \"./atomic-json.ts\";\n\nconst PREFS_PATH = join(config.dataDir, \"web-search.json\");\n\ninterface PrefsFile {\n\ttavilyApiKey?: string;\n}\n\n/** In-memory source of truth, populated by `loadWebSearchPrefs` at startup. */\nlet cache: PrefsFile = {};\n\n/**\n * Read the prefs file into the module cache. Idempotent; call once at server\n * startup before serving. A missing file is normal (no key saved yet). Fails\n * open (empty) on a corrupt/unreadable file rather than crashing startup.\n */\nexport async function loadWebSearchPrefs(): Promise<void> {\n\ttry {\n\t\tconst raw = await readFile(PREFS_PATH, \"utf8\");\n\t\tconst parsed = JSON.parse(raw) as Partial<PrefsFile>;\n\t\tcache = { tavilyApiKey: typeof parsed.tavilyApiKey === \"string\" ? parsed.tavilyApiKey : undefined };\n\t} catch (err) {\n\t\tcache = {};\n\t\tif ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n\t\t\tconsole.warn(`[web-search-prefs] ignoring unreadable ${PREFS_PATH}:`, err);\n\t\t}\n\t}\n}\n\n/**\n * The live Tavily key: the Settings value wins, else the TAVILY_API_KEY env\n * var. Synchronous (cache read) — the client calls this per request.\n */\nexport function getTavilyApiKey(): string | undefined {\n\tconst fromSettings = cache.tavilyApiKey?.trim();\n\tif (fromSettings) return fromSettings;\n\tconst fromEnv = process.env.TAVILY_API_KEY?.trim();\n\treturn fromEnv || undefined;\n}\n\nexport type KeySource = \"settings\" | \"env\" | \"none\";\n\nexport interface KeyStatus {\n\tconfigured: boolean;\n\tsource: KeySource;\n\t/** Masked tail of the live key (e.g. \"…6w7B\") — never the full secret. */\n\thint?: string;\n}\n\n/** Status for the API: configured?, which source is live, masked tail. Never\n * exposes the secret itself. */\nexport function getKeyStatus(): KeyStatus {\n\tconst fromSettings = cache.tavilyApiKey?.trim();\n\tconst fromEnv = process.env.TAVILY_API_KEY?.trim();\n\tconst live = fromSettings || fromEnv || undefined;\n\tconst source: KeySource = fromSettings ? \"settings\" : fromEnv ? \"env\" : \"none\";\n\treturn {\n\t\tconfigured: Boolean(live),\n\t\tsource,\n\t\t...(live ? { hint: maskKey(live) } : {}),\n\t};\n}\n\nfunction maskKey(key: string): string {\n\treturn `…${key.slice(-4)}`;\n}\n\n/** Save (non-empty) or clear (empty/blank) the Settings key; updates cache + disk. */\nexport async function setTavilyApiKey(key: string): Promise<void> {\n\tconst trimmed = key.trim();\n\tcache = trimmed ? { tavilyApiKey: trimmed } : {};\n\tawait save();\n}\n\n/** Remove the Settings key (the live key then falls back to env, if any). */\nexport async function clearTavilyApiKey(): Promise<void> {\n\tcache = {};\n\tawait save();\n}\n\nasync function save(): Promise<void> {\n\tawait writeJsonAtomic(PREFS_PATH, cache, { mode: 0o600 });\n}\n","/**\n * Minimal Tavily REST client for the web_search / web_fetch tools.\n *\n * No SDK dependency — just `fetch` against the two documented endpoints\n * (https://docs.tavily.com): POST /search and POST /extract. The API key is\n * read from TAVILY_API_KEY per-call (so setting it doesn't require a server\n * restart) and sent as a Bearer token — never in the request body, so it\n * can't leak into body logging.\n *\n * Each call is bounded by BOTH a timeout and the tool's AbortSignal (the\n * user pressing Stop). A user abort is re-thrown as-is so pi handles it on\n * its normal abort path; a timeout / HTTP / network failure is normalised\n * into a `WebSearchError` with an actionable message for the model.\n */\n\nimport { getTavilyApiKey } from \"../../storage/web-search-prefs.ts\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/** Tavily API base. Read per-call (like the API key below) so changing the\n * env var doesn't strictly require a code reload. */\nfunction baseUrl(): string {\n\treturn process.env.TAVILY_BASE_URL ?? \"https://api.tavily.com\";\n}\n\n/** A failure we surface to the model as a clean tool error (vs. a user abort,\n * which we let propagate untouched). */\nexport class WebSearchError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"WebSearchError\";\n\t}\n}\n\nfunction apiKey(): string {\n\tconst key = getTavilyApiKey();\n\tif (!key) {\n\t\tthrow new WebSearchError(\n\t\t\t\"Web search is not configured: add a Tavily API key in Settings → Web search, \" +\n\t\t\t\t\"or set the TAVILY_API_KEY environment variable. Get a free key at https://app.tavily.com.\",\n\t\t);\n\t}\n\treturn key;\n}\n\n// --- Tavily wire shapes (only the fields we read) --------------------------\n\nexport interface TavilySearchResultRaw {\n\ttitle?: string;\n\turl?: string;\n\tcontent?: string;\n\tscore?: number;\n\tpublished_date?: string;\n}\nexport interface TavilySearchResponse {\n\tquery?: string;\n\tanswer?: string;\n\tresults?: TavilySearchResultRaw[];\n}\nexport interface TavilyExtractResultRaw {\n\turl?: string;\n\traw_content?: string;\n}\nexport interface TavilyExtractFailureRaw {\n\turl?: string;\n\terror?: string;\n}\nexport interface TavilyExtractResponse {\n\tresults?: TavilyExtractResultRaw[];\n\tfailed_results?: TavilyExtractFailureRaw[];\n}\n\n// --- core POST -------------------------------------------------------------\n\nasync function postJson<T>(\n\tpath: string,\n\tbody: unknown,\n\tsignal: AbortSignal | undefined,\n): Promise<T> {\n\tconst key = apiKey();\n\n\t// Combine the caller's AbortSignal (Stop) with our own timeout into one\n\t// controller so either can cancel the in-flight fetch.\n\tconst ctl = new AbortController();\n\tconst onAbort = () => ctl.abort(signal?.reason);\n\tif (signal) {\n\t\tif (signal.aborted) ctl.abort(signal.reason);\n\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t}\n\tconst timer = setTimeout(\n\t\t() => ctl.abort(new WebSearchError(`Tavily request timed out after ${DEFAULT_TIMEOUT_MS / 1000}s.`)),\n\t\tDEFAULT_TIMEOUT_MS,\n\t);\n\n\tlet res: Response;\n\ttry {\n\t\tres = await fetch(`${baseUrl()}${path}`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\tauthorization: `Bearer ${key}`,\n\t\t\t},\n\t\t\tbody: JSON.stringify(body),\n\t\t\tsignal: ctl.signal,\n\t\t});\n\t} catch (err) {\n\t\t// User pressed Stop — propagate the abort so pi handles it normally.\n\t\tif (signal?.aborted) throw err;\n\t\t// Our timeout fired: surface the friendly WebSearchError, not the raw\n\t\t// AbortError the fetch rejected with.\n\t\tconst reason = ctl.signal.reason;\n\t\tif (reason instanceof WebSearchError) throw reason;\n\t\tthrow new WebSearchError(`Tavily request failed: ${errMsg(err)}`);\n\t} finally {\n\t\tclearTimeout(timer);\n\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t}\n\n\tif (!res.ok) {\n\t\tthrow new WebSearchError(await describeHttpError(res));\n\t}\n\ttry {\n\t\treturn (await res.json()) as T;\n\t} catch {\n\t\tthrow new WebSearchError(\n\t\t\t`Tavily returned a non-JSON response (HTTP ${res.status}) — the service may be down or returning an error page.`,\n\t\t);\n\t}\n}\n\nasync function describeHttpError(res: Response): Promise<string> {\n\tlet detail = \"\";\n\ttry {\n\t\tconst data = (await res.json()) as { detail?: unknown; error?: unknown };\n\t\tconst d = data?.detail ?? data?.error;\n\t\tif (typeof d === \"string\") detail = d;\n\t\telse if (d && typeof d === \"object\" && typeof (d as { error?: unknown }).error === \"string\") {\n\t\t\tdetail = (d as { error: string }).error;\n\t\t}\n\t} catch {\n\t\t/* non-JSON error body — fall through to the status-only message */\n\t}\n\tconst suffix = detail ? `: ${detail}` : \"\";\n\tif (res.status === 401 || res.status === 403) {\n\t\treturn `Tavily rejected the API key (HTTP ${res.status})${suffix}. Check TAVILY_API_KEY.`;\n\t}\n\tif (res.status === 429) {\n\t\treturn `Tavily rate limit / quota exceeded (HTTP 429)${suffix}.`;\n\t}\n\treturn `Tavily request failed (HTTP ${res.status})${suffix}.`;\n}\n\nfunction errMsg(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\n// --- public API ------------------------------------------------------------\n\nexport interface SearchOptions {\n\tquery: string;\n\tmaxResults: number;\n\ttopic: \"general\" | \"news\";\n\tsearchDepth: \"basic\" | \"advanced\";\n}\n\nexport function tavilySearch(\n\topts: SearchOptions,\n\tsignal: AbortSignal | undefined,\n): Promise<TavilySearchResponse> {\n\treturn postJson<TavilySearchResponse>(\n\t\t\"/search\",\n\t\t{\n\t\t\tquery: opts.query,\n\t\t\tmax_results: opts.maxResults,\n\t\t\ttopic: opts.topic,\n\t\t\tsearch_depth: opts.searchDepth,\n\t\t\tinclude_answer: true,\n\t\t},\n\t\tsignal,\n\t);\n}\n\nexport function tavilyExtract(\n\turls: string[],\n\tsignal: AbortSignal | undefined,\n): Promise<TavilyExtractResponse> {\n\treturn postJson<TavilyExtractResponse>(\"/extract\", { urls }, signal);\n}\n","/**\n * pi-pilot's first-party `web_search` builtin extension.\n *\n * Registers two read-only network tools, both backed by Tavily (./client.ts):\n * - `web_search`: query → LLM answer + ranked results (title/url/snippet).\n * - `web_fetch`: URL(s) → extracted main page text (no HTML/boilerplate).\n *\n * Each tool returns the model-facing text in `content` AND a structured\n * `details` snapshot (whitelisted in ws/bridge.ts) that the web cards render.\n * Failures (missing key, bad key, rate limit, network, timeout) throw a\n * `WebSearchError` so pi marks the result `isError` and the model sees an\n * actionable message — there's no useful card state for a hard failure, so\n * the card degrades to its arg-derived header + the error text.\n *\n * No closure/module state: both tools are pure request → response, so unlike\n * todo/subagent there's nothing per-session to isolate here.\n */\n\nimport type { AgentToolResult, ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport {\n\twebSearchParamsSchema,\n\twebFetchParamsSchema,\n\ttype WebSearchDetails,\n\ttype WebSearchResultItem,\n\ttype WebFetchDetails,\n\ttype WebFetchResultItem,\n\ttype WebFetchFailure,\n} from \"./schema.ts\";\nimport {\n\ttavilySearch,\n\ttavilyExtract,\n\tWebSearchError,\n\ttype TavilySearchResultRaw,\n\ttype TavilyExtractResponse,\n} from \"./client.ts\";\n\n/** Per-result snippet kept in `details` (card preview). */\nconst SNIPPET_CARD_CAP = 320;\n/** Per-result snippet handed to the model in the text output. */\nconst SNIPPET_MODEL_CAP = 1024;\n/** Per-page extracted-text preview kept in `details` (card). */\nconst FETCH_PREVIEW_CAP = 2 * 1024;\n/** Per-page extracted text handed to the model. */\nconst FETCH_PER_URL_CAP = 8 * 1024;\n/** Combined extracted text handed to the model across all pages in one call. */\nconst FETCH_TOTAL_CAP = 24 * 1024;\n\nfunction clampInt(v: number | undefined, lo: number, hi: number, dflt: number): number {\n\tif (typeof v !== \"number\" || !Number.isFinite(v)) return dflt;\n\treturn Math.max(lo, Math.min(hi, Math.round(v)));\n}\n\nfunction truncate(s: string, cap: number): string {\n\treturn s.length <= cap ? s : `${s.slice(0, cap)}…`;\n}\n\nexport const webSearchExtensionFactory: ExtensionFactory = (pi) => {\n\t// ----- web_search -------------------------------------------------------\n\tpi.registerTool({\n\t\tname: \"web_search\",\n\t\tlabel: \"Web search\",\n\t\tdescription:\n\t\t\t\"Search the web and get back a short answer plus ranked results (title, URL, snippet). \" +\n\t\t\t\"Use it for current events, external documentation, or facts you're unsure of or that may \" +\n\t\t\t\"have changed since your training cutoff. Follow up with `web_fetch` to read a result's \" +\n\t\t\t\"full page when the snippet isn't enough.\",\n\t\tparameters: webSearchParamsSchema,\n\t\texecutionMode: \"parallel\",\n\t\tpromptSnippet:\n\t\t\t\"web_search: search the web (answer + ranked results) for current or uncertain facts; pair with web_fetch to read a page in full.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Reach for web_search when the answer depends on current / post-cutoff information, external docs, or facts you can't verify from the repo or your own knowledge — not for things you already know.\",\n\t\t\t'Phrase `query` like a search-engine query (keywords, entities), not a chat sentence. Set `topic: \"news\"` for recent events.',\n\t\t\t\"Cite the URLs you relied on, and use web_fetch to read a page in full before trusting details beyond the snippet.\",\n\t\t],\n\t\tasync execute(_toolCallId, params, signal): Promise<AgentToolResult<WebSearchDetails>> {\n\t\t\tconst query = params.query.trim();\n\t\t\tif (!query) throw new WebSearchError(\"web_search needs a non-empty query.\");\n\t\t\tconst maxResults = clampInt(params.max_results, 1, 10, 5);\n\t\t\tconst topic = params.topic === \"news\" ? \"news\" : \"general\";\n\t\t\tconst searchDepth = params.search_depth === \"advanced\" ? \"advanced\" : \"basic\";\n\n\t\t\tconst data = await tavilySearch({ query, maxResults, topic, searchDepth }, signal);\n\n\t\t\tconst raw = (data.results ?? []).filter(\n\t\t\t\t(r): r is TavilySearchResultRaw & { url: string } => typeof r.url === \"string\",\n\t\t\t);\n\t\t\tconst answer =\n\t\t\t\ttypeof data.answer === \"string\" && data.answer.trim() ? data.answer.trim() : undefined;\n\n\t\t\tconst results: WebSearchResultItem[] = raw.map((r) => ({\n\t\t\t\ttitle: (r.title ?? \"\").trim() || r.url,\n\t\t\t\turl: r.url,\n\t\t\t\tcontent: truncate((r.content ?? \"\").trim(), SNIPPET_CARD_CAP),\n\t\t\t\tscore: typeof r.score === \"number\" ? r.score : undefined,\n\t\t\t\tpublishedDate: typeof r.published_date === \"string\" ? r.published_date : undefined,\n\t\t\t}));\n\n\t\t\tconst details: WebSearchDetails = { version: 1, kind: \"search\", query, answer, results };\n\t\t\treturn { content: [{ type: \"text\", text: formatSearch(query, answer, raw) }], details };\n\t\t},\n\t});\n\n\t// ----- web_fetch --------------------------------------------------------\n\tpi.registerTool({\n\t\tname: \"web_fetch\",\n\t\tlabel: \"Web fetch\",\n\t\tdescription:\n\t\t\t\"Fetch one or more web pages and extract their main text as clean, readable content \" +\n\t\t\t\"(stripped of HTML/navigation/boilerplate). Use it to read a page in full — a URL returned \" +\n\t\t\t\"by web_search, a documentation link, or a URL the user gave you.\",\n\t\tparameters: webFetchParamsSchema,\n\t\texecutionMode: \"parallel\",\n\t\tpromptSnippet:\n\t\t\t\"web_fetch: fetch URL(s) and extract the main page text — read a web_search result or a user-given link in full.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use web_fetch to read the full text of a page when a web_search snippet isn't enough, or whenever the user hands you a URL.\",\n\t\t\t\"Pass up to 5 absolute http(s) URLs in one call to read them together.\",\n\t\t],\n\t\tasync execute(_toolCallId, params, signal): Promise<AgentToolResult<WebFetchDetails>> {\n\t\t\tconst urls = params.urls.map((u) => u.trim()).filter(Boolean);\n\t\t\tif (urls.length === 0) throw new WebSearchError(\"web_fetch needs at least one non-empty URL.\");\n\t\t\tconst data = await tavilyExtract(urls, signal);\n\n\t\t\tconst results: WebFetchResultItem[] = (data.results ?? [])\n\t\t\t\t.filter((r): r is { url: string; raw_content?: string } => typeof r.url === \"string\")\n\t\t\t\t.map((r) => {\n\t\t\t\t\tconst full = (r.raw_content ?? \"\").trim();\n\t\t\t\t\treturn { url: r.url, content: truncate(full, FETCH_PREVIEW_CAP), chars: full.length };\n\t\t\t\t});\n\t\t\tconst failed: WebFetchFailure[] = (data.failed_results ?? [])\n\t\t\t\t.filter((f): f is { url: string; error?: string } => typeof f.url === \"string\")\n\t\t\t\t.map((f) => ({ url: f.url, error: (f.error ?? \"extraction failed\").toString() }));\n\n\t\t\tconst details: WebFetchDetails = { version: 1, kind: \"fetch\", results, failed };\n\t\t\treturn { content: [{ type: \"text\", text: formatFetch(data) }], details };\n\t\t},\n\t});\n};\n\n// --- model-facing text formatting ------------------------------------------\n\nfunction formatSearch(\n\tquery: string,\n\tanswer: string | undefined,\n\tresults: Array<TavilySearchResultRaw & { url: string }>,\n): string {\n\tconst lines: string[] = [];\n\tlines.push(`Search results for \"${query}\" (${results.length} result${results.length === 1 ? \"\" : \"s\"}):`);\n\tif (answer) {\n\t\tlines.push(\"\");\n\t\tlines.push(`Answer: ${answer}`);\n\t}\n\tresults.forEach((r, i) => {\n\t\tlines.push(\"\");\n\t\tlines.push(`${i + 1}. ${(r.title ?? \"\").trim() || r.url}`);\n\t\tlines.push(` ${r.url}`);\n\t\tconst snippet = (r.content ?? \"\").trim();\n\t\tif (snippet) lines.push(` ${truncate(snippet, SNIPPET_MODEL_CAP)}`);\n\t});\n\tif (results.length === 0) {\n\t\tlines.push(\"\");\n\t\tlines.push(\"No results found.\");\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nfunction formatFetch(data: TavilyExtractResponse): string {\n\tconst results = (data.results ?? []).filter(\n\t\t(r): r is { url: string; raw_content?: string } => typeof r.url === \"string\",\n\t);\n\tconst failed = (data.failed_results ?? []).filter(\n\t\t(f): f is { url: string; error?: string } => typeof f.url === \"string\",\n\t);\n\n\tconst lines: string[] = [];\n\tlet budget = FETCH_TOTAL_CAP;\n\tfor (const r of results) {\n\t\tconst full = (r.raw_content ?? \"\").trim();\n\t\tconst cap = Math.min(FETCH_PER_URL_CAP, budget);\n\t\tconst slice = full.length > cap ? `${full.slice(0, cap)}…` : full;\n\t\tbudget -= Math.min(full.length, cap);\n\t\tlines.push(`## ${r.url}`);\n\t\tlines.push(slice || \"(no extractable content)\");\n\t\tlines.push(\"\");\n\t\tif (budget <= 0) {\n\t\t\tlines.push(\"… (remaining pages omitted to fit the context budget)\");\n\t\t\tbreak;\n\t\t}\n\t}\n\tfor (const f of failed) {\n\t\tlines.push(`Failed to fetch ${f.url}: ${f.error ?? \"extraction failed\"}`);\n\t}\n\tif (results.length === 0 && failed.length === 0) lines.push(\"No content extracted.\");\n\treturn lines.join(\"\\n\").trim();\n}\n","/**\n * Persistence for which first-party builtin extensions the user has turned off.\n *\n * pi-pilot's builtins (`todo`, `ask_user`) load via the SDK's `extensionFactories`\n * channel (see `extensions/index.ts`). Each factory is gated on this disabled set\n * so a toggled-off builtin registers nothing. The preference is GLOBAL — one switch\n * per builtin, applied to every workspace — matching the user's choice; these are\n * app-level agent capabilities, not per-project resources.\n *\n * File format (~/.pi/webui/builtin-extensions.json):\n * { \"disabled\": [\"plan\"] }\n *\n * `isBuiltinDisabled` must be SYNCHRONOUS because the factory gate runs inside the\n * loader's synchronous factory loop. We populate the in-memory cache once at startup\n * via `loadBuiltinPrefs()` (awaited before the server accepts requests, so the cache\n * is ready before any runtime — and therefore any factory — is built). The gate fails\n * open (treats unknown state as enabled) on the off chance it runs pre-load.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { config } from \"../config.ts\";\nimport { writeJsonAtomic } from \"./atomic-json.ts\";\n\nconst PREFS_PATH = join(config.dataDir, \"builtin-extensions.json\");\n\ninterface PrefsFile {\n\tdisabled: string[];\n}\n\n/** In-memory source of truth. Fail-open default until `loadBuiltinPrefs` runs. */\nlet cache: PrefsFile = { disabled: [] };\n\n/**\n * Read the prefs file into the module cache. Idempotent. Call once at server\n * startup before serving so the synchronous gate has fresh data. A missing file\n * is normal (no builtin has ever been disabled). Fails open: a corrupt or\n * unreadable file resets to all-enabled (and warns) rather than crashing\n * startup over a non-critical preference.\n */\nexport async function loadBuiltinPrefs(): Promise<void> {\n\ttry {\n\t\tconst raw = await readFile(PREFS_PATH, \"utf8\");\n\t\tconst parsed = JSON.parse(raw) as Partial<PrefsFile>;\n\t\tcache = { disabled: Array.isArray(parsed.disabled) ? parsed.disabled : [] };\n\t} catch (err) {\n\t\tcache = { disabled: [] };\n\t\tif ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n\t\t\tconsole.warn(`[builtin-prefs] ignoring unreadable ${PREFS_PATH}:`, err);\n\t\t}\n\t}\n}\n\n/**\n * True when the user has turned this builtin off. Synchronous (cache read).\n *\n * Legacy migration: `\"plan\"` in the disabled list is treated as `\"todo\"`\n * (the plan builtin was renamed to todo). This avoids requiring users to\n * manually edit their prefs file after upgrade.\n */\nexport function isBuiltinDisabled(id: string): boolean {\n\tif (cache.disabled.includes(id)) return true;\n\tif (id === \"todo\" && cache.disabled.includes(\"plan\")) return true;\n\treturn false;\n}\n\n/** Snapshot of the currently disabled builtin ids. */\nexport function getDisabledBuiltins(): string[] {\n\treturn [...cache.disabled];\n}\n\n/** Flip a builtin on/off, updating the cache and persisting to disk. */\nexport async function setBuiltinEnabled(id: string, enabled: boolean): Promise<void> {\n\tconst next = new Set(cache.disabled);\n\tif (enabled) next.delete(id);\n\telse next.add(id);\n\tcache = { disabled: [...next] };\n\tawait save();\n}\n\nasync function save(): Promise<void> {\n\tawait writeJsonAtomic(PREFS_PATH, cache);\n}\n","/**\n * Registry of pi-pilot's first-party builtin extensions.\n *\n * These factories are passed to pi via `DefaultResourceLoaderOptions.\n * extensionFactories`, which is an independent channel from the disk\n * extension scanner (see `resource-loader.js`). The `noExtensions` flag\n * only controls disk scanning; factories registered here always load.\n *\n * Two-tier stance: third-party / disk pi extensions stay off by default\n * (rendered as \"third-party not supported\" in the Resources panel), but\n * pi-pilot ships its own first-party features via this list. To add a new\n * builtin feature, drop a new directory under `extensions/<name>/` with a\n * `factory.ts` exporting an `ExtensionFactory`, then append an entry to\n * `BUILTIN_EXTENSIONS` below.\n *\n * Per-builtin on/off: each factory is wrapped in a gate that checks the\n * user's persisted preference (`isBuiltinDisabled`). A disabled builtin\n * registers nothing. The gate is re-evaluated on every `session.reload()`\n * (which re-runs the factories), so toggling a builtin + reloading applies\n * the change live — see `storage/builtin-extension-prefs.ts` and the\n * `PUT .../resources/builtin-extensions` route. Wrapping (rather than\n * filtering the array) keeps the factory-list reference stable across\n * reloads, which the loader assumes.\n *\n * NOT a barrel file — this defines values (the manifest + factories list),\n * not a re-export of unrelated symbols.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { todoExtensionFactory } from \"./todo/factory.ts\";\nimport { askUserExtensionFactory } from \"./ask_user/factory.ts\";\nimport { artifactExtensionFactory } from \"./artifact/factory.ts\";\nimport { subagentExtensionFactory } from \"./subagent/factory.ts\";\nimport { webSearchExtensionFactory } from \"./web_search/factory.ts\";\nimport { isBuiltinDisabled } from \"../storage/builtin-extension-prefs.ts\";\n\n/** Static metadata + factory for one builtin. Drives both the gated factory\n * list and the Resources panel's friendly listing. */\nexport interface BuiltinExtensionDef {\n\t/** Stable id used by prefs + the toggle endpoint. */\n\tid: string;\n\t/** User-facing name. */\n\tname: string;\n\t/** One-line description shown in the panel. */\n\tdescription: string;\n\t/** Tool names the factory registers (for display). */\n\ttools: string[];\n\t/** Slash command names the factory registers, without \"/\" (for display). */\n\tcommands: string[];\n\tfactory: ExtensionFactory;\n}\n\nexport const BUILTIN_EXTENSIONS: BuiltinExtensionDef[] = [\n\t{\n\t\tid: \"todo\",\n\t\tname: \"Todo\",\n\t\tdescription:\n\t\t\t\"A CRUD task list for tracking multi-step work — adds the todo tool and the /todos command.\",\n\t\ttools: [\"todo\"],\n\t\tcommands: [\"todos\"],\n\t\tfactory: todoExtensionFactory,\n\t},\n\t{\n\t\tid: \"ask_user\",\n\t\tname: \"Ask user\",\n\t\tdescription:\n\t\t\t\"Lets the agent pause and ask you a structured multiple-choice question — adds the ask_user tool.\",\n\t\ttools: [\"ask_user\"],\n\t\tcommands: [],\n\t\tfactory: askUserExtensionFactory,\n\t},\n\t{\n\t\tid: \"artifact\",\n\t\tname: \"Artifacts\",\n\t\tdescription:\n\t\t\t\"Lets the agent publish substantial, self-contained content — web pages, SVG diagrams, documents, code files — as versioned artifacts rendered in a side panel. Adds the create_artifact tool.\",\n\t\ttools: [\"create_artifact\"],\n\t\tcommands: [],\n\t\tfactory: artifactExtensionFactory,\n\t},\n\t{\n\t\tid: \"subagent\",\n\t\tname: \"Subagent\",\n\t\tdescription:\n\t\t\t\"Lets the agent delegate self-contained tasks to isolated child agents (separate pinned-pi processes; only their final report returns). Builtin presets scout/worker/reviewer; user agents from ~/.pi/agent/agents/*.md. Adds the subagent tool.\",\n\t\ttools: [\"subagent\"],\n\t\tcommands: [],\n\t\tfactory: subagentExtensionFactory,\n\t},\n\t{\n\t\tid: \"web\",\n\t\tname: \"Web search\",\n\t\tdescription:\n\t\t\t\"Lets the agent search the web and read pages — adds the web_search and web_fetch tools (backed by Tavily; needs the TAVILY_API_KEY environment variable).\",\n\t\ttools: [\"web_search\", \"web_fetch\"],\n\t\tcommands: [],\n\t\tfactory: webSearchExtensionFactory,\n\t},\n];\n\n/** Wrap a factory so it registers nothing while the builtin is toggled off. */\nfunction gate(def: BuiltinExtensionDef): ExtensionFactory {\n\treturn (pi) => {\n\t\tif (isBuiltinDisabled(def.id)) return;\n\t\treturn def.factory(pi);\n\t};\n}\n\nexport const builtinExtensionFactories: ExtensionFactory[] =\n\tBUILTIN_EXTENSIONS.map(gate);\n","/**\n * Post-restart reconciliation for orphaned `ask_user` tool calls.\n *\n * Scenario: pi-pilot crashes (or is killed) while `ask_user.execute()`\n * is blocked waiting for a client answer. pi has already finalized the\n * assistant message containing the toolCall block (toolCalls get\n * persisted as part of the assistant message at `message_end`), but\n * the matching toolResult was never written because the Promise\n * inside `execute()` died with the process. When pi loads the session\n * on next boot, it sees an unmatched toolCall and feeds it straight to\n * the LLM (per Spike Q1: pi does not validate or auto-resolve dangling\n * tool calls — `session-manager.js:113–207`).\n *\n * What this function does, on first bind after such a restart:\n * 1. Walk the branch tail backwards from the leaf.\n * 2. Find every `ask_user` toolCall whose id has no matching\n * toolResult on the tail.\n * 3. If any exist, append a `customMessage` (LLM-visible per Spike\n * Q4) explaining the cancellation, listing the dangling ids.\n *\n * Why a customMessage and not a forged toolResult: pi SDK exposes\n * `appendCustomMessageEntry` (visible to LLM as a user message) and\n * `appendCustomEntry` (not visible). Neither lets us write a real\n * `role: \"toolResult\"` message — `appendMessage` accepts it in\n * theory but mixing in a synthetic toolResult would break the\n * causality pi reasoners rely on. The customMessage path is honest:\n * the LLM sees its own unmatched toolCall + a plain English note\n * explaining the session was interrupted. Documented in PROGRESS.md\n * Known gaps so future readers know why this looks asymmetric.\n *\n * Re-entrancy: if a previous restart cleanup already wrote a notice\n * for the same toolCall ids, we don't write another. The dedup key is\n * the customType `ask_user-restart-cancelled` combined with the ids\n * encoded into the message body — we scan tail entries first and\n * bail if we see one whose ids cover ours.\n */\n\nimport type { SessionManager } from \"@earendil-works/pi-coding-agent\";\n\nconst CUSTOM_TYPE = \"ask_user-restart-cancelled\";\n\nexport function reconcileAfterRestart(sessionManager: SessionManager): void {\n\tconst branch = sessionManager.getBranch();\n\tif (branch.length === 0) return;\n\n\tconst satisfied = new Set<string>();\n\tconst danglingIds: string[] = [];\n\tconst danglingAlreadyHandled = new Set<string>();\n\n\t// Walk backwards. Stop conditions:\n\t// - user message: end of the current \"agent reply\" span; anything\n\t// dangling further back would have been handled by an earlier\n\t// restart pass or doesn't matter (LLM has moved on).\n\t// - existing restart-cancellation customMessage: read the ids it\n\t// covers so we don't re-notify for the same calls.\n\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\tconst entry = branch[i]!;\n\n\t\tif (entry.type === \"custom_message\") {\n\t\t\tconst cm = entry as { customType?: string; details?: unknown };\n\t\t\tif (cm.customType === CUSTOM_TYPE) {\n\t\t\t\tconst ids = (cm.details as { ids?: unknown })?.ids;\n\t\t\t\tif (Array.isArray(ids)) {\n\t\t\t\t\tfor (const id of ids) {\n\t\t\t\t\t\tif (typeof id === \"string\") danglingAlreadyHandled.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst msg = entry.message as { role?: string; toolCallId?: string; content?: unknown };\n\n\t\tif (msg.role === \"toolResult\" && typeof msg.toolCallId === \"string\") {\n\t\t\tsatisfied.add(msg.toolCallId);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n\t\t\tfor (const block of msg.content) {\n\t\t\t\tif (!block || typeof block !== \"object\") continue;\n\t\t\t\tconst b = block as { type?: string; name?: string; id?: string };\n\t\t\t\tif (b.type !== \"toolCall\") continue;\n\t\t\t\tif (b.name !== \"ask_user\") continue;\n\t\t\t\tif (typeof b.id !== \"string\") continue;\n\t\t\t\tif (satisfied.has(b.id)) continue;\n\t\t\t\tif (danglingAlreadyHandled.has(b.id)) continue;\n\t\t\t\tdanglingIds.push(b.id);\n\t\t\t}\n\t\t\t// Whether we found dangling here or not, we stop after the\n\t\t\t// first assistant message: earlier turns are historical and\n\t\t\t// would have been reconciled by their own subsequent\n\t\t\t// toolResult or by an earlier cleanup run.\n\t\t\tbreak;\n\t\t}\n\n\t\tif (msg.role === \"user\") break;\n\t}\n\n\tif (danglingIds.length === 0) return;\n\n\tconst idList = danglingIds.join(\", \");\n\tconst text =\n\t\t`[pi-pilot] Your previous ask_user call(s) [${idList}] were cancelled ` +\n\t\t`because the server restarted before the user answered. Use your best ` +\n\t\t`judgement and proceed; you may re-call ask_user if the decision still matters.`;\n\n\tsessionManager.appendCustomMessageEntry(\n\t\tCUSTOM_TYPE,\n\t\ttext,\n\t\ttrue, // display in TUI too (no-op in pi-pilot, but harmless)\n\t\t{ ids: danglingIds },\n\t);\n}\n\n","/**\n * Subagent crash hygiene: post-restart transcript reconciliation + boot\n * sweep of orphaned child processes.\n *\n * `reconcileAfterRestart` is the ask_user pattern (see\n * ../ask_user/cleanup.ts for the long rationale): if the server died while\n * `subagent.execute()` was mid-run, the assistant message with the\n * toolCall block is already persisted but no toolResult ever landed. On\n * next load the LLM would see a phantom in-flight delegation — we append\n * an LLM-visible customMessage explaining the cancellation, deduped via\n * the customType + covered ids.\n *\n * `sweepOrphanedChildrenOnBoot` closes the process half of the same crash:\n * children of a SIGKILLed server keep running. Discovery is pidfile-based\n * (child.ts writes `<marker dir>/pid` after spawn) because argv/pgrep\n * matching does NOT work: the pi CLI sets process.title, which on macOS\n * clobbers the entire visible argv — ps and pgrep -f see only \"pi\"\n * (verified live; env display via `ps -E` dies with it). Before killing,\n * the pid is verified to still be a node/pi executable via `ps -o ucomm=`\n * (kernel exec name — the one field a title rewrite can't touch), which\n * closes the pid-reuse race. SIGTERM only: the child's own handler reaps\n * its detached bash grandchildren. Stale marker dirs are removed in the\n * same pass. Single-instance assumption: a second concurrently-running\n * pi-pilot server's children would also match, which the port-5174 lock\n * already rules out.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { readdir, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { SessionManager } from \"@earendil-works/pi-coding-agent\";\nimport { PROMPT_DIR_PREFIX } from \"./child.ts\";\n\nconst CUSTOM_TYPE = \"subagent-restart-cancelled\";\n\nexport function reconcileAfterRestart(sessionManager: SessionManager): void {\n\tconst branch = sessionManager.getBranch();\n\tif (branch.length === 0) return;\n\n\tconst satisfied = new Set<string>();\n\tconst danglingIds: string[] = [];\n\tconst danglingAlreadyHandled = new Set<string>();\n\n\t// Walk backwards; same stop conditions as ask_user's reconciler — a user\n\t// message ends the current agent-reply span, and an existing cancellation\n\t// customMessage tells us which ids were already notified.\n\tfor (let i = branch.length - 1; i >= 0; i--) {\n\t\tconst entry = branch[i]!;\n\n\t\tif (entry.type === \"custom_message\") {\n\t\t\tconst cm = entry as { customType?: string; details?: unknown };\n\t\t\tif (cm.customType === CUSTOM_TYPE) {\n\t\t\t\tconst ids = (cm.details as { ids?: unknown })?.ids;\n\t\t\t\tif (Array.isArray(ids)) {\n\t\t\t\t\tfor (const id of ids) {\n\t\t\t\t\t\tif (typeof id === \"string\") danglingAlreadyHandled.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst msg = entry.message as { role?: string; toolCallId?: string; content?: unknown };\n\n\t\tif (msg.role === \"toolResult\" && typeof msg.toolCallId === \"string\") {\n\t\t\tsatisfied.add(msg.toolCallId);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n\t\t\tfor (const block of msg.content) {\n\t\t\t\tif (!block || typeof block !== \"object\") continue;\n\t\t\t\tconst b = block as { type?: string; name?: string; id?: string };\n\t\t\t\tif (b.type !== \"toolCall\") continue;\n\t\t\t\tif (b.name !== \"subagent\") continue;\n\t\t\t\tif (typeof b.id !== \"string\") continue;\n\t\t\t\tif (satisfied.has(b.id)) continue;\n\t\t\t\tif (danglingAlreadyHandled.has(b.id)) continue;\n\t\t\t\tdanglingIds.push(b.id);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tif (msg.role === \"user\") break;\n\t}\n\n\tif (danglingIds.length === 0) return;\n\n\tconst idList = danglingIds.join(\", \");\n\tconst text =\n\t\t`[pi-pilot] Your previous subagent delegation(s) [${idList}] were cancelled ` +\n\t\t`because the server restarted mid-run. The child agent's work may be partially ` +\n\t\t`applied to the working tree — verify before assuming anything happened. ` +\n\t\t`Re-delegate if the task still matters.`;\n\n\tsessionManager.appendCustomMessageEntry(\n\t\tCUSTOM_TYPE,\n\t\ttext,\n\t\ttrue, // display flag — no-op in pi-pilot, harmless in the TUI\n\t\t{ ids: danglingIds },\n\t);\n}\n\n/**\n * SIGTERM any subagent child whose OWNING SERVER is dead, then delete the\n * stale marker dirs. Never throws. Must run before the first runtime build\n * so it can't race a freshly spawned child.\n *\n * The owner guard is load-bearing, not paranoia: the pidfile's second line\n * records the spawning server's pid, and any record whose owner still\n * lives is skipped UNTOUCHED. Without it, ANY out-of-process run of this\n * sweep is a kill-broadcast to every live subagent on the machine — which\n * actually happened: a reviewer subagent auditing this repo ran\n * `pnpm test`, the test suite invoked the sweep against the shared\n * tmpdir, and all three live reviewers (their own pidfiles included) got\n * SIGTERMed mid-review at the 74s mark. `rootDir` is injectable for the\n * same reason — tests sweep an isolated directory, never the real tmpdir.\n */\nexport async function sweepOrphanedChildrenOnBoot(rootDir: string = tmpdir()): Promise<void> {\n\tlet dirNames: string[];\n\ttry {\n\t\tdirNames = (await readdir(rootDir)).filter((n) => n.startsWith(PROMPT_DIR_PREFIX));\n\t} catch {\n\t\treturn;\n\t}\n\n\tlet swept = 0;\n\tfor (const name of dirNames) {\n\t\tconst dir = join(rootDir, name);\n\t\tconst pidRaw = await readFile(join(dir, \"pid\"), \"utf8\").catch(() => \"\");\n\t\tconst [childLine, ownerLine] = pidRaw.split(\"\\n\");\n\t\tconst childPid = Number.parseInt((childLine ?? \"\").trim(), 10);\n\t\tconst ownerPid = Number.parseInt((ownerLine ?? \"\").trim(), 10);\n\n\t\t// Owner still alive (and actually a node process, guarding pid reuse)\n\t\t// → this is a LIVE delegation managed by another server instance, or\n\t\t// we're being run out-of-process. Hands off, dir and all.\n\t\tif (Number.isInteger(ownerPid) && ownerPid > 1 && (await isLiveNodeProcess(ownerPid))) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tif (Number.isInteger(childPid) && childPid > 1 && (await isLiveNodeProcess(childPid))) {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.kill(childPid, \"SIGTERM\");\n\t\t\t\t\tswept++;\n\t\t\t\t} catch {\n\t\t\t\t\t// already gone between the check and the kill\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tawait rm(dir, { recursive: true, force: true }).catch(() => {});\n\t\t}\n\t}\n\tif (swept > 0) {\n\t\tconsole.warn(`[subagent] swept ${swept} orphaned child(ren) from a previous run`);\n\t}\n}\n\n/**\n * Liveness + pid-reuse guard: alive AND the kernel executable name is\n * node (how we spawn both the server and the pinned CLI) or pi (a\n * compiled pi binary, future-proofing). `ps -o ucomm=` reads the kernel\n * exec name — the one field pi's process.title rewrite can't clobber.\n */\nfunction isLiveNodeProcess(pid: number): Promise<boolean> {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t} catch {\n\t\treturn Promise.resolve(false); // not alive (or not ours to signal)\n\t}\n\treturn new Promise((resolve) => {\n\t\texecFile(\"ps\", [\"-o\", \"ucomm=\", \"-p\", String(pid)], (err, stdout) => {\n\t\t\tif (err) {\n\t\t\t\tresolve(false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst name = stdout.trim().toLowerCase();\n\t\t\tresolve(name === \"node\" || name === \"pi\");\n\t\t});\n\t});\n}\n","/**\n * Translate a pi AgentSessionEvent into our narrow wire payload.\n *\n * Anything not in the switch is dropped (returns undefined). Adding support\n * for a new event type means: add a case here, and a matching variant in\n * `@pi-pilot/shared/ws-protocol.ts`.\n */\n\nimport type { AgentSessionEvent } from \"@earendil-works/pi-coding-agent\";\nimport type { PiEventPayload } from \"@pi-pilot/shared\";\nimport { snapshotForSession } from \"../extensions/ask_user/registry.ts\";\n\nexport function translatePiEvent(ev: AgentSessionEvent): PiEventPayload | undefined {\n\tswitch (ev.type) {\n\t\tcase \"agent_start\":\n\t\t\treturn { kind: \"agent_start\" };\n\n\t\tcase \"agent_end\":\n\t\t\treturn { kind: \"agent_end\", willRetry: ev.willRetry };\n\n\t\tcase \"turn_start\":\n\t\t\treturn { kind: \"turn_start\" };\n\n\t\tcase \"turn_end\":\n\t\t\treturn { kind: \"turn_end\" };\n\n\t\tcase \"message_start\": {\n\t\t\tconst role = roleOf(ev.message);\n\t\t\t// Carry user text on the wire so the client can reconcile its\n\t\t\t// optimistic bubble (or insert one when no optimistic push\n\t\t\t// happened — extension-command path). Other roles stream via\n\t\t\t// message_update deltas; a start-time snapshot would be stale.\n\t\t\tconst text = role === \"user\" ? extractUserText(ev.message) : undefined;\n\t\t\treturn { kind: \"message_start\", role, text };\n\t\t}\n\n\t\tcase \"message_end\":\n\t\t\treturn { kind: \"message_end\", role: roleOf(ev.message) };\n\n\t\tcase \"message_update\": {\n\t\t\tconst ame = ev.assistantMessageEvent;\n\t\t\tif (ame.type === \"text_delta\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"message_update\",\n\t\t\t\t\tdelta: { kind: \"text\", contentIndex: ame.contentIndex, text: ame.delta },\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (ame.type === \"thinking_delta\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"message_update\",\n\t\t\t\t\tdelta: { kind: \"thinking\", contentIndex: ame.contentIndex, text: ame.delta },\n\t\t\t\t};\n\t\t\t}\n\t\t\t// Other deltas (start/end, toolcall) are not surfaced in MVP.\n\t\t\treturn { kind: \"message_update\", delta: { kind: \"other\" } };\n\t\t}\n\n\t\tcase \"tool_execution_start\":\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_start\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\targs: ev.args,\n\t\t\t};\n\n\t\tcase \"tool_execution_update\": {\n\t\t\t// Same whitelist as tool_execution_end below: tools whose card\n\t\t\t// renders structured LIVE state (subagent per-task rows) ship\n\t\t\t// their onUpdate details snapshot; everything else stays\n\t\t\t// text-only so chatty tools don't double-transmit stdout.\n\t\t\tconst updateDetails = shouldForwardDetails(ev.toolName)\n\t\t\t\t? (ev.partialResult as { details?: unknown } | undefined)?.details\n\t\t\t\t: undefined;\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_update\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\tpartialText: extractText(ev.partialResult),\n\t\t\t\t...(updateDetails !== undefined ? { details: updateDetails } : {}),\n\t\t\t};\n\t\t}\n\n\t\tcase \"tool_execution_end\": {\n\t\t\t// Whitelist details forwarding: only tools whose web cards\n\t\t\t// need the structured shape get it on the wire. Today only\n\t\t\t// `ask_user` (so <AskUserCard>'s resolved state can read\n\t\t\t// `kind`/`indices`/`text` directly instead of regex-parsing\n\t\t\t// `formatResult`'s output). Built-in tools' details are\n\t\t\t// often large (bash stdout, edit diffs); keeping them off\n\t\t\t// the wire avoids double-transmission since `extractText`\n\t\t\t// already pulled the human-readable form.\n\t\t\t//\n\t\t\t// `ev.result` is typed `any` in the SDK; the `!== undefined`\n\t\t\t// guard mirrors `workspace-manager.ts/getSessionHistory` so\n\t\t\t// we never put a `details: undefined` field on the object —\n\t\t\t// it would JSON-stringify away on the wire but is noisy in\n\t\t\t// in-process logging.\n\t\t\tconst details = shouldForwardDetails(ev.toolName)\n\t\t\t\t? ev.result?.details\n\t\t\t\t: undefined;\n\t\t\treturn {\n\t\t\t\tkind: \"tool_execution_end\",\n\t\t\t\ttoolCallId: ev.toolCallId,\n\t\t\t\ttoolName: ev.toolName,\n\t\t\t\tisError: ev.isError,\n\t\t\t\ttext: extractText(ev.result),\n\t\t\t\t...(details !== undefined ? { details } : {}),\n\t\t\t};\n\t\t}\n\n\t\tcase \"queue_update\":\n\t\t\treturn {\n\t\t\t\tkind: \"queue_update\",\n\t\t\t\tsteering: [...ev.steering],\n\t\t\t\tfollowUp: [...ev.followUp],\n\t\t\t};\n\n\t\tcase \"auto_retry_start\":\n\t\t\treturn {\n\t\t\t\tkind: \"auto_retry_start\",\n\t\t\t\tattempt: ev.attempt,\n\t\t\t\tmaxAttempts: ev.maxAttempts,\n\t\t\t\tdelayMs: ev.delayMs,\n\t\t\t\terrorMessage: ev.errorMessage,\n\t\t\t};\n\n\t\tcase \"auto_retry_end\":\n\t\t\treturn {\n\t\t\t\tkind: \"auto_retry_end\",\n\t\t\t\tsuccess: ev.success,\n\t\t\t\tattempt: ev.attempt,\n\t\t\t\tfinalError: ev.finalError,\n\t\t\t};\n\n\t\tcase \"compaction_start\":\n\t\t\treturn { kind: \"compaction_start\", reason: ev.reason };\n\n\t\tcase \"compaction_end\":\n\t\t\treturn {\n\t\t\t\tkind: \"compaction_end\",\n\t\t\t\treason: ev.reason,\n\t\t\t\taborted: ev.aborted,\n\t\t\t\twillRetry: ev.willRetry,\n\t\t\t\terrorMessage: ev.errorMessage,\n\t\t\t};\n\n\t\tcase \"session_info_changed\":\n\t\t\treturn { kind: \"session_info_changed\", name: ev.name };\n\n\t\tcase \"thinking_level_changed\":\n\t\t\treturn { kind: \"thinking_level_changed\", level: ev.level };\n\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Track unknown roles we've already warned about so a stream of messages\n * with the same novel role doesn't spam the log. Module-level — the warn\n * is informational and one per process lifetime is enough to flag the\n * mismatch during development.\n */\nconst warnedUnknownRoles = new Set<string>();\n\nfunction roleOf(message: unknown): \"user\" | \"assistant\" | \"toolResult\" | \"bashExecution\" {\n\tconst role = (message as { role?: string } | undefined)?.role;\n\tif (role === \"user\" || role === \"assistant\" || role === \"toolResult\" || role === \"bashExecution\") {\n\t\treturn role;\n\t}\n\t// Pi SDK added a message role we don't translate yet. Fall back to\n\t// \"assistant\" so the wire schema stays valid, but warn loudly — a\n\t// new role usually implies new event shapes that bridge.ts and the\n\t// chat store both need to learn about.\n\tconst key = typeof role === \"string\" ? role : `<${role === undefined ? \"missing\" : typeof role}>`;\n\tif (!warnedUnknownRoles.has(key)) {\n\t\twarnedUnknownRoles.add(key);\n\t\tconsole.warn(\n\t\t\t`[bridge] unknown message role \"${key}\" — treating as assistant. ` +\n\t\t\t\t`Pi SDK may have added a role bridge.ts and chat.ts don't handle yet.`,\n\t\t);\n\t}\n\treturn \"assistant\";\n}\n\n/**\n * Best-effort extraction of user-message text. Pi's user-role\n * `AgentMessage` has either `content: string` (single text body) or\n * `content: ContentBlock[]` (typed blocks; we keep only `TextContent`).\n * Returns `undefined` when neither shape yields text — the wire field\n * is optional so an undefined here is fine.\n */\nfunction extractUserText(message: unknown): string | undefined {\n\tif (!message || typeof message !== \"object\") return undefined;\n\tconst content = (message as { content?: unknown }).content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n\t\t\tconst text = (block as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.length === 0 ? undefined : parts.join(\"\");\n}\n\n/**\n * Build synthetic events that recreate an in-flight assistant turn on a\n * client that just (re)subscribed mid-stream.\n *\n * Why: when a workspace switch happens during streaming, the client wipes\n * its chat store on `subscribed`, and /history can't see the not-yet-\n * persisted assistant message (pi only commits to JSONL at message_end).\n * Live deltas that follow the rebind would land in `chat.ts`'s\n * `lastIndexOfStreamingAssistant` guard with no streaming item to attach\n * to, and silently drop. Emitting a synthetic `message_start(assistant)`\n * + accumulated `thinking` / `text` deltas reseats that anchor so the\n * subsequent live `message_update`s flow into the right item.\n *\n * This helper handles only the streaming assistant text/thinking. Tools that\n * are *executing* during the off-window are restored separately by\n * `inFlightRunningToolsSnapshot` (general tools) and `inFlightToolCallsSnapshot`\n * (the interactive `ask_user` card) — together they close the old \"switch away\n * mid-tool loses the running tool card\" gap. Finished tools committed earlier in\n * the turn come back through the REST `/history` rebuild.\n *\n * Returns `undefined` when there's nothing to replay (no in-flight\n * message, non-assistant role, or only ToolCall content).\n */\nexport function inFlightAssistantSnapshot(\n\tstreamingMessage: unknown,\n): PiEventPayload[] | undefined {\n\tif (!streamingMessage || typeof streamingMessage !== \"object\") return undefined;\n\tconst m = streamingMessage as { role?: string; content?: unknown };\n\tif (m.role !== \"assistant\" || !Array.isArray(m.content)) return undefined;\n\n\tlet textAccum = \"\";\n\tlet thinkingAccum = \"\";\n\tfor (const block of m.content) {\n\t\tif (!block || typeof block !== \"object\") continue;\n\t\tconst b = block as { type?: string; text?: unknown; thinking?: unknown };\n\t\tif (b.type === \"text\" && typeof b.text === \"string\") {\n\t\t\ttextAccum += b.text;\n\t\t} else if (b.type === \"thinking\" && typeof b.thinking === \"string\") {\n\t\t\tthinkingAccum += b.thinking;\n\t\t}\n\t}\n\n\tif (!textAccum && !thinkingAccum) return undefined;\n\n\tconst events: PiEventPayload[] = [\n\t\t{ kind: \"message_start\", role: \"assistant\" },\n\t];\n\tif (thinkingAccum) {\n\t\tevents.push({\n\t\t\tkind: \"message_update\",\n\t\t\tdelta: { kind: \"thinking\", contentIndex: 0, text: thinkingAccum },\n\t\t});\n\t}\n\tif (textAccum) {\n\t\tevents.push({\n\t\t\tkind: \"message_update\",\n\t\t\tdelta: { kind: \"text\", contentIndex: 0, text: textAccum },\n\t\t});\n\t}\n\treturn events;\n}\n\n/**\n * Build synthetic `tool_execution_start` events for tools that are *currently\n * executing* when a client (re)subscribes mid-tool, so the running tool card\n * appears immediately instead of only after the turn commits.\n *\n * Why this is needed even though the client also rebuilds from REST `/history`:\n * a still-running tool has emitted its assistant `toolCall` block (committed at\n * the assistant message's `message_end`) but has NO `toolResult` yet, and\n * `getSessionHistory` only renders a tool card once the result lands — so a\n * running tool is invisible in `/history`. We recover the card from\n * `state.pendingToolCalls` (the ids executing right now) joined against the\n * `toolCall` blocks in the in-memory transcript (for tool name + args). This is\n * what closes the \"switch away mid-tool loses the tool card\" gap (PROGRESS.md).\n *\n * `ask_user` is intentionally excluded: its interactive card is restored by\n * `inFlightToolCallsSnapshot` from the question registry (which also carries the\n * pending-answer state), so emitting it here too would duplicate the card.\n *\n * Symmetric with `inFlightAssistantSnapshot`; takes plain data (not the SDK\n * session) so it stays unit-testable with structural fakes. Returns `[]` when\n * nothing is executing.\n */\nexport function inFlightRunningToolsSnapshot(\n\tpendingToolCalls: Iterable<string>,\n\tmessages: readonly unknown[],\n): PiEventPayload[] {\n\tconst pending = new Set(pendingToolCalls);\n\tif (pending.size === 0) return [];\n\n\t// Join the executing ids against assistant `toolCall` blocks to recover the\n\t// tool name + args. A `toolCall` block is `{ type, id, name, arguments }`.\n\tconst infoById = new Map<string, { name: string; args: unknown }>();\n\tfor (const message of messages) {\n\t\tif (!message || typeof message !== \"object\") continue;\n\t\tif ((message as { role?: string }).role !== \"assistant\") continue;\n\t\tconst content = (message as { content?: unknown }).content;\n\t\tif (!Array.isArray(content)) continue;\n\t\tfor (const block of content) {\n\t\t\tif (!block || typeof block !== \"object\") continue;\n\t\t\tconst b = block as { type?: string; id?: string; name?: string; arguments?: unknown };\n\t\t\tif (b.type === \"toolCall\" && typeof b.id === \"string\" && pending.has(b.id)) {\n\t\t\t\tinfoById.set(b.id, { name: typeof b.name === \"string\" ? b.name : \"tool\", args: b.arguments });\n\t\t\t}\n\t\t}\n\t}\n\n\tconst events: PiEventPayload[] = [];\n\tfor (const toolCallId of pending) {\n\t\tconst info = infoById.get(toolCallId);\n\t\t// Can't recover name/args (not in the transcript yet) — skip rather than\n\t\t// emit a bogus card; the live `tool_execution_end` / history rebuild\n\t\t// will surface it once it commits.\n\t\tif (!info) continue;\n\t\t// ask_user is restored separately, with its registry answer-state.\n\t\tif (info.name === \"ask_user\") continue;\n\t\tevents.push({\n\t\t\tkind: \"tool_execution_start\",\n\t\t\ttoolCallId,\n\t\t\ttoolName: info.name,\n\t\t\targs: info.args,\n\t\t});\n\t}\n\treturn events;\n}\n\n/**\n * Build synthetic `tool_execution_start` events for any pending\n * `ask_user` calls on the given session, so a client that (re)subscribes\n * mid-question still sees the interactive card.\n *\n * Why this is needed even though pi already persists the assistant\n * message with the toolCall block: a freshly-subscribed client\n * reconciles via REST `/history` (which only sees finalized\n * toolResults — for a still-pending call there isn't one), and live\n * `tool_execution_start` already fired before this client connected.\n * Without this snapshot the question card silently never appears.\n *\n * Symmetric with `inFlightAssistantSnapshot` above. Filtered by\n * sessionFile so a connection bound to workspace A doesn't see\n * workspace B's pending questions. Empty arrays are returned (rather\n * than undefined) when there's nothing to replay — the caller is a\n * `for` loop, this keeps the call site simple.\n */\nexport function inFlightToolCallsSnapshot(\n\tsessionFile: string | null,\n): PiEventPayload[] {\n\tconst pending = snapshotForSession(sessionFile);\n\treturn pending.map((p) => ({\n\t\tkind: \"tool_execution_start\",\n\t\ttoolCallId: p.toolCallId,\n\t\ttoolName: \"ask_user\",\n\t\targs: p.args,\n\t}));\n}\n\n/**\n * Tools allowed to ship `AgentToolResult.details` over the wire. Keep\n * this list tight — every entry doubles the per-result payload size\n * for that tool. New entries should be ones whose web rendering\n * genuinely benefits from the structured shape (interactive cards,\n * highlighted selections, etc.), not ones that just *could* use it.\n *\n * Exported so `workspace-manager.ts/getSessionHistory` can apply the\n * same filter on REST history — the live (`tool_execution_end`) and\n * persisted (`HistoryToolItem`) surfaces stay symmetric.\n */\nexport const DETAILS_FORWARD_WHITELIST = new Set<string>([\n\t\"ask_user\",\n\t\"todo\",\n\t\"subagent\",\n\t\"web_search\",\n\t\"web_fetch\",\n]);\n\nexport function shouldForwardDetails(toolName: string): boolean {\n\treturn DETAILS_FORWARD_WHITELIST.has(toolName);\n}\n\n/** Best-effort extraction of human-readable text from a tool result-shaped object. */\nfunction extractText(result: unknown): string | undefined {\n\tif (!result || typeof result !== \"object\") return undefined;\n\tconst content = (result as { content?: unknown }).content;\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const c of content) {\n\t\tif (c && typeof c === \"object\" && (c as { type?: string }).type === \"text\") {\n\t\t\tconst text = (c as { text?: unknown }).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\treturn parts.length === 0 ? undefined : parts.join(\"\");\n}\n","/**\n * No-op implementation of pi's `ExtensionUIContext`.\n *\n * pi-pilot does not support pi extensions (see README / workspace-manager).\n * The extension system is TUI-native — custom renderers, ANSI themes,\n * keyboard overlays, modal dialogs — and cannot be bridged to the Web in\n * any meaningful way. Rather than render a half-working subset that lies\n * about what's supported, every method here is an intentional no-op or\n * returns a sensible default.\n *\n * Why keep the class at all: the pi SDK still calls\n * `bindExtensions({ uiContext })`, and the type contract requires a\n * full `ExtensionUIContext`. When `PI_PILOT_ENABLE_EXTENSIONS=1`, the\n * loader will load extensions and any UI calls land here and quietly\n * do nothing. An extension that registers tools / slash commands but\n * doesn't touch `ctx.ui` still works; everything UI-driven is dead.\n *\n * Methods covered:\n * - Dialogs: select, confirm, input, editor → resolve to default\n * - Fire-and-forget: notify, setStatus, setWidget, setTitle,\n * setEditorText, pasteToEditor → no-op\n * - TUI-only: onTerminalInput, setWorkingMessage,\n * setWorkingVisible, setWorkingIndicator,\n * setHiddenThinkingLabel, setFooter, setHeader,\n * custom, getEditorText, addAutocompleteProvider,\n * setEditorComponent, getEditorComponent, theme,\n * getAllThemes, getTheme, setTheme,\n * getToolsExpanded, setToolsExpanded\n */\n\nimport type { ExtensionUIContext } from \"@earendil-works/pi-coding-agent\";\n\nexport class ExtensionUIBridge implements ExtensionUIContext {\n\t/** Symmetric with the old bridge so workspace-manager's dispose path\n\t * can call it uniformly. There is no state to release. */\n\tdispose(): void {}\n\n\t// ============== dialog methods (resolve to default) ==============\n\n\tselect(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\tconfirm(): Promise<boolean> {\n\t\treturn Promise.resolve(false);\n\t}\n\tinput(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\teditor(): Promise<string | undefined> {\n\t\treturn Promise.resolve(undefined);\n\t}\n\n\t// ============== fire-and-forget methods (no-op) ==============\n\n\tnotify(): void {}\n\tsetStatus(): void {}\n\tsetWidget(): void {}\n\tsetTitle(): void {}\n\tsetEditorText(): void {}\n\tpasteToEditor(): void {}\n\n\t// ============== TUI-only methods (no-op / defaults) ==============\n\n\tonTerminalInput(): () => void {\n\t\treturn () => {};\n\t}\n\tsetWorkingMessage(): void {}\n\tsetWorkingVisible(): void {}\n\tsetWorkingIndicator(): void {}\n\tsetHiddenThinkingLabel(): void {}\n\tsetFooter(): void {}\n\tsetHeader(): void {}\n\tasync custom<T>(): Promise<T> {\n\t\t// `custom()` is the primary TUI rendering API used by extensions to\n\t\t// draw arbitrary terminal dialogs/overlays. It cannot be bridged to\n\t\t// the Web. Match pi's documented RPC-mode behavior of returning\n\t\t// undefined; the SDK's callers handle that explicitly.\n\t\treturn undefined as unknown as T;\n\t}\n\tgetEditorText(): string {\n\t\treturn \"\";\n\t}\n\taddAutocompleteProvider(): void {}\n\tsetEditorComponent(): void {}\n\tgetEditorComponent(): undefined {\n\t\treturn undefined;\n\t}\n\tget theme(): never {\n\t\t// Extensions that read `ctx.ui.theme` directly see undefined. The\n\t\t// cast keeps the type structural.\n\t\treturn undefined as unknown as never;\n\t}\n\tgetAllThemes(): { name: string; path: string | undefined }[] {\n\t\treturn [];\n\t}\n\tgetTheme(): undefined {\n\t\treturn undefined;\n\t}\n\tsetTheme(): { success: boolean; error?: string } {\n\t\treturn { success: false, error: \"Theme switching not supported in pi-pilot\" };\n\t}\n\tgetToolsExpanded(): boolean {\n\t\treturn false;\n\t}\n\tsetToolsExpanded(): void {}\n}\n","/**\n * /api/workspaces/:id/config routes.\n *\n * Reads and mutates the runtime config (model, thinking level, tools) for a\n * workspace's active AgentSession. Mounted onto the workspaces Hono router\n * via `mountConfigRoutes`.\n */\n\nimport type { Hono, Context } from \"hono\";\nimport type { AgentSessionRuntime } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tConfigResponse,\n\tModelInfo,\n\tPiContextUsage,\n\tSetModelRequest,\n\tSetSessionNameRequest,\n\tSetSessionNameResponse,\n\tSetThinkingLevelRequest,\n\tSetToolsRequest,\n\tThinkingLevel,\n\tToolInfo as ToolInfoDTO,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\nimport { persistActiveTools } from \"../storage/session-tool-prefs.ts\";\nimport { BUILTIN_EXTENSIONS } from \"../extensions/index.ts\";\n\n// ---------- helpers ----------\n\n/**\n * tool name → builtin extension display name, for the source badge in the Tools\n * settings list. Static — derived once from the builtin manifest. Only our\n * first-party builtins are mapped; core and third-party tools get no badge.\n */\nconst BUILTIN_TOOL_SOURCE: ReadonlyMap<string, string> = new Map(\n\tBUILTIN_EXTENSIONS.flatMap((d) => d.tools.map((tool) => [tool, d.name] as const)),\n);\n\nfunction buildConfigResponse(workspaceId: string): ConfigResponse {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new Error(\"runtime not initialized\");\n\n\tconst session = runtime.session;\n\tconst model = session.model;\n\n\tconst currentModel: ModelInfo | null = model\n\t\t? {\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodelId: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\treasoning: model.reasoning,\n\t\t\t}\n\t\t: null;\n\n\tconst availableModels: ModelInfo[] = session.modelRegistry\n\t\t.getAvailable()\n\t\t.map((m) => ({\n\t\t\tprovider: m.provider,\n\t\t\tmodelId: m.id,\n\t\t\tname: m.name,\n\t\t\treasoning: m.reasoning,\n\t\t}));\n\n\tconst allTools: ToolInfoDTO[] = session.getAllTools().map((t) => {\n\t\tconst builtinExtension = BUILTIN_TOOL_SOURCE.get(t.name);\n\t\treturn {\n\t\t\tname: t.name,\n\t\t\tdescription: t.description,\n\t\t\t...(builtinExtension ? { builtinExtension } : {}),\n\t\t};\n\t});\n\n\treturn {\n\t\tcurrentModel,\n\t\tthinkingLevel: session.thinkingLevel as ThinkingLevel,\n\t\tavailableThinkingLevels: session.getAvailableThinkingLevels() as ThinkingLevel[],\n\t\tactiveTools: session.getActiveToolNames(),\n\t\tavailableModels,\n\t\tallTools,\n\t};\n}\n\n/** Return 404 early if workspace does not exist in the registry. */\nasync function requireWorkspace(c: Context, id: string): Promise<boolean> {\n\tconst ws = await getWorkspace(id);\n\tif (!ws) {\n\t\tc.status(404);\n\t\tc.header(\"Content-Type\", \"application/json\");\n\t\t// We must return the body via the route handler, so we set a flag.\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/** Return 409 if the agent is currently streaming. */\nfunction rejectIfStreaming(c: Context, workspaceId: string): boolean {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (runtime?.session.isStreaming) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * Push a fresh `context_usage` snapshot to every WS subscriber of the\n * workspace. Pi's event stream only surfaces context-usage shifts via\n * `agent_end` / `compaction_end` / `thinking_level_changed`, so a model\n * swap whose new contextWindow differs but doesn't clamp thinking level\n * would otherwise leave clients displaying the previous model's percent\n * until the next assistant turn.\n */\nfunction broadcastContextUsage(\n\tworkspaceId: string,\n\truntime: AgentSessionRuntime,\n): void {\n\tconst usage = runtime.session.getContextUsage();\n\tif (!usage) return;\n\tconst payload: PiContextUsage = {\n\t\tkind: \"context_usage\",\n\t\ttokens: usage.tokens,\n\t\tcontextWindow: usage.contextWindow,\n\t\tpercent: usage.percent,\n\t};\n\tworkspaceManager.broadcast(workspaceId, {\n\t\ttype: \"event\",\n\t\tworkspaceId,\n\t\tsessionPath: runtime.session.sessionFile ?? null,\n\t\tpayload,\n\t});\n}\n\n// ---------- mount ----------\n\nexport function mountConfigRoutes(app: Hono): void {\n\t// GET /api/workspaces/:id/config\n\tapp.get(\"/:id/config\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/model\n\tapp.put(\"/:id/config/model\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetModelRequest;\n\t\tif (!body?.provider || !body?.modelId) {\n\t\t\treturn c.json({ ok: false, error: \"provider and modelId are required\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change model while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst model = runtime.session.modelRegistry.getAvailable().find(\n\t\t\t\t(m) => m.provider === body.provider && m.id === body.modelId,\n\t\t\t);\n\t\t\tif (!model) {\n\t\t\t\treturn c.json({ ok: false, error: `model not found or no auth: ${body.provider}/${body.modelId}` }, 404);\n\t\t\t}\n\n\t\t\tawait runtime.session.setModel(model);\n\t\t\t// New model may have a different contextWindow. Pi's setModel only\n\t\t\t// emits an event when thinking-level gets clamped — when the new\n\t\t\t// model accepts the current level unchanged, no event fires and\n\t\t\t// the WS CTX indicator would stay pinned to the previous model's\n\t\t\t// percent until the next agent_end. Push a snapshot ourselves.\n\t\t\tbroadcastContextUsage(id, runtime);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/thinking-level\n\tapp.put(\"/:id/config/thinking-level\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetThinkingLevelRequest;\n\t\tconst validLevels: ThinkingLevel[] = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"];\n\t\tif (!body?.level || !validLevels.includes(body.level)) {\n\t\t\treturn c.json({ ok: false, error: `level must be one of: ${validLevels.join(\", \")}` }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change thinking level while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst runtime = workspaceManager.get(id);\n\t\t\tif (!runtime) {\n\t\t\t\treturn c.json({ ok: false, error: \"runtime not initialized\" }, 500);\n\t\t\t}\n\t\t\truntime.session.setThinkingLevel(body.level);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/tools\n\tapp.put(\"/:id/config/tools\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetToolsRequest;\n\t\tif (!body?.tools || !Array.isArray(body.tools)) {\n\t\t\treturn c.json({ ok: false, error: \"tools must be an array of tool names\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tif (rejectIfStreaming(c, id)) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot change tools while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\tconst runtime = workspaceManager.get(id);\n\t\t\tif (!runtime) {\n\t\t\t\treturn c.json({ ok: false, error: \"runtime not initialized\" }, 500);\n\t\t\t}\n\t\t\tawait persistActiveTools(id, runtime.session, body.tools);\n\t\t\treturn c.json(buildConfigResponse(id));\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n\n\t// PUT /api/workspaces/:id/config/session-name\n\tapp.put(\"/:id/config/session-name\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst exists = await requireWorkspace(c, id);\n\t\tif (!exists) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst body = (await c.req.json()) as SetSessionNameRequest;\n\t\tif (typeof body?.name !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"name is required\" }, 400);\n\t\t}\n\t\tconst trimmed = body.name.trim();\n\t\tif (!trimmed) {\n\t\t\treturn c.json({ ok: false, error: \"name must not be empty\" }, 400);\n\t\t}\n\n\t\ttry {\n\t\t\tconst sessionPath = c.req.query(\"sessionPath\");\n\t\t\tif (sessionPath) {\n\t\t\t\tconst err = await workspaceManager.validateSessionOwnership(id, sessionPath);\n\t\t\t\tif (err) return c.json({ ok: false, error: err }, 404);\n\t\t\t}\n\t\t\tconst runtime = await workspaceManager.getOrCreate(id, sessionPath || undefined);\n\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\treturn c.json({ ok: false, error: \"cannot rename while the agent is streaming\" }, 409);\n\t\t\t}\n\n\t\t\truntime.session.setSessionName(trimmed);\n\t\t\treturn c.json({ name: trimmed } satisfies SetSessionNameResponse);\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n}\n","/**\n * /api/workspaces/:id/files/search — workspace-scoped file lookup for the\n * composer's `@` autocomplete.\n *\n * Source of truth is `git ls-files --cached --others --exclude-standard`,\n * which is fast and honors `.gitignore`. Non-git directories fall back to a\n * bounded recursive walk that skips obvious build/dep dirs. Filtering is\n * case-insensitive substring on the workspace-relative path; results are\n * ranked by where the match landed (basename beats path) and capped at\n * `limit` (default 30, max 100).\n *\n * The full file list per workspace is cached in-process with a 10-second\n * TTL so a user mashing keys in the composer doesn't fork a `git` child\n * per keystroke. Same in-flight dedup pattern as workspace-stats.ts.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { readdir } from \"node:fs/promises\";\nimport { join, relative, sep } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { Hono } from \"hono\";\nimport type { SearchFilesResponse, SearchFileEntry } from \"@pi-pilot/shared\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\n\nconst exec = promisify(execFile);\n\nconst LIST_TTL_MS = 10_000;\n/** Soft cap on the per-workspace cache. Without it, every workspace a user\n * ever opened would stay resident even after they stopped using it (the TTL\n * only re-checks on next read of the *same* key). FIFO eviction is good\n * enough — the access pattern is \"the workspace I'm composing in right\n * now\", which gets re-inserted on every miss, naturally promoting itself. */\nconst MAX_CACHED_WORKSPACES = 16;\n/** Hard ceiling on files we ever consider, across both code paths. Keeps\n * search latency bounded on pathological repos and protects against a\n * bug in the walk. */\nconst MAX_FILES_TRACKED = 20_000;\nconst DEFAULT_LIMIT = 30;\nconst MAX_LIMIT = 100;\nconst WALK_MAX_DEPTH = 8;\nconst WALK_IGNORES = new Set([\n\t\".git\",\n\t\"node_modules\",\n\t\"dist\",\n\t\"build\",\n\t\".next\",\n\t\".turbo\",\n\t\".cache\",\n\t\".vite\",\n\t\"out\",\n\t\"coverage\",\n\t\"target\",\n\t\"venv\",\n\t\".venv\",\n\t\"__pycache__\",\n\t\".DS_Store\",\n]);\n\ninterface ListCacheEntry {\n\tfiles: string[];\n\texpiresAt: number;\n}\n\nconst listCache = new Map<string, ListCacheEntry>();\nconst inflight = new Map<string, Promise<ListCacheEntry>>();\n\nasync function getFileList(workspacePath: string): Promise<string[]> {\n\tconst now = Date.now();\n\tconst cached = listCache.get(workspacePath);\n\tif (cached && cached.expiresAt > now) return cached.files;\n\n\tconst pending = inflight.get(workspacePath);\n\tif (pending) return (await pending).files;\n\n\tconst probe = probeFileList(workspacePath)\n\t\t.then((files) => {\n\t\t\tconst entry: ListCacheEntry = {\n\t\t\t\tfiles,\n\t\t\t\texpiresAt: Date.now() + LIST_TTL_MS,\n\t\t\t};\n\t\t\t// Re-insert (delete + set) so this key moves to the back of the\n\t\t\t// Map's insertion order — the FIFO evictor below treats the head\n\t\t\t// as the oldest, so this keeps the \"actively used\" workspace warm.\n\t\t\tlistCache.delete(workspacePath);\n\t\t\tlistCache.set(workspacePath, entry);\n\t\t\twhile (listCache.size > MAX_CACHED_WORKSPACES) {\n\t\t\t\tconst oldest = listCache.keys().next().value;\n\t\t\t\tif (!oldest) break;\n\t\t\t\tlistCache.delete(oldest);\n\t\t\t}\n\t\t\treturn entry;\n\t\t})\n\t\t.finally(() => inflight.delete(workspacePath));\n\tinflight.set(workspacePath, probe);\n\treturn (await probe).files;\n}\n\nasync function probeFileList(workspacePath: string): Promise<string[]> {\n\t// Try git first — fastest, respects gitignore, single subprocess.\n\ttry {\n\t\tconst { stdout } = await exec(\n\t\t\t\"git\",\n\t\t\t[\"ls-files\", \"--cached\", \"--others\", \"--exclude-standard\"],\n\t\t\t{\n\t\t\t\tcwd: workspacePath,\n\t\t\t\ttimeout: 3000,\n\t\t\t\tmaxBuffer: 16 * 1024 * 1024,\n\t\t\t\tenv: { ...process.env, GIT_DIR: undefined, GIT_WORK_TREE: undefined },\n\t\t\t},\n\t\t);\n\t\tconst lines = stdout.split(\"\\n\");\n\t\tconst out: string[] = [];\n\t\tfor (const line of lines) {\n\t\t\tif (!line) continue;\n\t\t\tout.push(line);\n\t\t\tif (out.length >= MAX_FILES_TRACKED) break;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\t// Fall through to a bounded walk.\n\t}\n\n\tconst out: string[] = [];\n\tawait walkDir(workspacePath, workspacePath, 0, out);\n\treturn out;\n}\n\nasync function walkDir(\n\troot: string,\n\tdir: string,\n\tdepth: number,\n\tout: string[],\n): Promise<void> {\n\tif (out.length >= MAX_FILES_TRACKED) return;\n\tif (depth > WALK_MAX_DEPTH) return;\n\tlet dirents;\n\ttry {\n\t\tdirents = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (out.length >= MAX_FILES_TRACKED) return;\n\t\t// Surface dotfiles like .env / .eslintrc / .gitignore — only skip the\n\t\t// dotfile *directories* listed in WALK_IGNORES (.git, .next, .cache,\n\t\t// .turbo, .vite, .venv) plus the .DS_Store noise file.\n\t\tif (WALK_IGNORES.has(d.name)) continue;\n\t\tconst abs = join(dir, d.name);\n\t\tif (d.isDirectory()) {\n\t\t\tawait walkDir(root, abs, depth + 1, out);\n\t\t} else if (d.isFile()) {\n\t\t\tout.push(relative(root, abs).split(sep).join(\"/\"));\n\t\t}\n\t}\n}\n\n/** Score a relPath against a lowercase query. Higher = better. Returns\n * null if the path doesn't match at all. */\nfunction scoreMatch(relPath: string, q: string): number | null {\n\tconst lower = relPath.toLowerCase();\n\tconst slash = lower.lastIndexOf(\"/\");\n\tconst base = slash >= 0 ? lower.slice(slash + 1) : lower;\n\n\t// Tier 1: basename prefix match — best signal.\n\tif (base.startsWith(q)) return 1000 - relPath.length;\n\t// Tier 2: basename substring.\n\tconst baseIdx = base.indexOf(q);\n\tif (baseIdx >= 0) return 800 - baseIdx - relPath.length * 0.01;\n\t// Tier 3: anywhere in the path.\n\tconst pathIdx = lower.indexOf(q);\n\tif (pathIdx >= 0) return 500 - pathIdx - relPath.length * 0.01;\n\treturn null;\n}\n\nasync function ensureWorkspaceExists(id: string): Promise<string | null> {\n\tconst ws = await getWorkspace(id);\n\treturn ws ? ws.path : null;\n}\n\nexport function mountFilesRoute(app: Hono): void {\n\tapp.get(\"/:id/files/search\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst workspacePath = await ensureWorkspaceExists(id);\n\t\tif (!workspacePath) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\t\tconst qRaw = (c.req.query(\"q\") ?? \"\").trim();\n\t\tconst limitRaw = c.req.query(\"limit\");\n\t\tlet limit = limitRaw ? Number(limitRaw) : DEFAULT_LIMIT;\n\t\tif (!Number.isFinite(limit) || limit <= 0) limit = DEFAULT_LIMIT;\n\t\tif (limit > MAX_LIMIT) limit = MAX_LIMIT;\n\n\t\ttry {\n\t\t\tconst all = await getFileList(workspacePath);\n\t\t\tlet entries: SearchFileEntry[];\n\t\t\tlet truncated = false;\n\t\t\tif (!qRaw) {\n\t\t\t\t// Empty query: just return the head of the list so the popover\n\t\t\t\t// has something to show before the user types.\n\t\t\t\tconst slice = all.slice(0, limit);\n\t\t\t\tentries = slice.map((relPath) => ({\n\t\t\t\t\tpath: join(workspacePath, relPath),\n\t\t\t\t\trelPath,\n\t\t\t\t}));\n\t\t\t\ttruncated = all.length > limit;\n\t\t\t} else {\n\t\t\t\tconst q = qRaw.toLowerCase();\n\t\t\t\tconst scored: { relPath: string; score: number }[] = [];\n\t\t\t\tlet matchCount = 0;\n\t\t\t\tfor (const relPath of all) {\n\t\t\t\t\tconst score = scoreMatch(relPath, q);\n\t\t\t\t\tif (score === null) continue;\n\t\t\t\t\tmatchCount++;\n\t\t\t\t\tscored.push({ relPath, score });\n\t\t\t\t}\n\t\t\t\tscored.sort((a, b) => b.score - a.score);\n\t\t\t\tconst top = scored.slice(0, limit);\n\t\t\t\tentries = top.map((e) => ({\n\t\t\t\t\tpath: join(workspacePath, e.relPath),\n\t\t\t\t\trelPath: e.relPath,\n\t\t\t\t}));\n\t\t\t\ttruncated = matchCount > limit;\n\t\t\t}\n\t\t\tconst body: SearchFilesResponse = { workspacePath, entries, truncated };\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tconsole.error(`[api/files] search for ${id} failed:`, err);\n\t\t\treturn c.json({ ok: false, error: message }, 500);\n\t\t}\n\t});\n}\n","/**\n * /api/workspaces/:id/resources route family.\n *\n * GET → snapshot of skills / prompts / extensions discovered\n * by the workspace's pi `ResourceLoader` (read-only\n * inventory \\u2014 no filesystem touched here).\n * POST .../reload → ask the loader to re-scan; useful after creating\n * files outside pi-pilot or after editing.\n * GET .../skill?path → pre-fill data for the skill editor (parsed\n * frontmatter + body).\n * POST .../skills → create a new skill (directory + SKILL.md) under the\n * chosen scope, then reload + return fresh inventory.\n * PUT .../skills → update an existing skill in place.\n * DELETE .../skills → remove a skill (and its parent directory if it\n * wasn't a bare .md at the skills root).\n * GET .../prompt?path → pre-fill data for the prompt editor.\n * POST .../prompts → create a new prompt template.\n * PUT .../prompts → update; renames the file if `name` changed.\n * DELETE .../prompts → unlink the file.\n *\n * All mutation endpoints respond with the post-reload `ResourcesResponse`\n * so the client doesn't need a follow-up GET.\n */\n\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { Context, Hono } from \"hono\";\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tBuiltinExtensionInfo,\n\tCreatePromptRequest,\n\tCreateSkillRequest,\n\tExtensionInfo,\n\tExtensionLoadError,\n\tPromptInfo,\n\tResourceFileResponse,\n\tResourceScope,\n\tResourceSource,\n\tResourcesResponse,\n\tSetBuiltinExtensionRequest,\n\tSkillInfo,\n\tUpdatePromptRequest,\n\tUpdateSkillRequest,\n} from \"@pi-pilot/shared\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\nimport {\n\tcreatePrompt,\n\tcreateSkill,\n\tdeletePrompt,\n\tdeleteSkill,\n\tHttpError,\n\treadPromptFile,\n\treadSkillFile,\n\tresolveResourceRoots,\n\ttype ResourceRoots,\n\tscopeFor,\n\tupdatePrompt,\n\tupdateSkill,\n} from \"../storage/resource-writer.ts\";\nimport { EXTENSIONS_ENABLED, workspaceManager } from \"../workspace-manager.ts\";\nimport { BUILTIN_EXTENSIONS } from \"../extensions/index.ts\";\nimport {\n\tgetDisabledBuiltins,\n\tsetBuiltinEnabled,\n} from \"../storage/builtin-extension-prefs.ts\";\nimport { reapplyToolPrefs } from \"../storage/session-tool-prefs.ts\";\n\ninterface PiSourceInfo {\n\tscope: \"user\" | \"project\" | \"temporary\";\n\tsource: string;\n\tpath: string;\n}\n\nfunction toResourceSource(info: PiSourceInfo): ResourceSource {\n\treturn {\n\t\tscope: info.scope,\n\t\tlabel: info.source,\n\t\tpath: info.path,\n\t};\n}\n\n/**\n * Scan the two extension discovery directories to detect extension files\n * that exist on disk but were not loaded (because extensions are disabled).\n * This lets the UI tell the user \"you have extensions installed but they\n * are disabled\" rather than leaving them to wonder why nothing works.\n *\n * Only meaningful when `EXTENSIONS_ENABLED === false`; callers should skip\n * the scan when the operator opted in via `PI_PILOT_ENABLE_EXTENSIONS=1`.\n */\nasync function scanExtensionDirs(workspaceCwd: string): Promise<string[]> {\n\tconst dirs = [join(getAgentDir(), \"extensions\"), join(workspaceCwd, \".pi\", \"extensions\")];\n\tconst found: string[] = [];\n\tfor (const dir of dirs) {\n\t\ttry {\n\t\t\tconst entries = await readdir(dir, { withFileTypes: true });\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.isFile() && (entry.name.endsWith(\".ts\") || entry.name.endsWith(\".js\"))) {\n\t\t\t\t\tfound.push(join(dir, entry.name));\n\t\t\t\t} else if (entry.isDirectory()) {\n\t\t\t\t\t// Could be a package-style extension; list the directory.\n\t\t\t\t\tfound.push(join(dir, entry.name));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Directory doesn't exist or isn't readable — that's fine.\n\t\t}\n\t}\n\treturn found;\n}\n\n/** Build a ResourcesResponse from the current loader state.\n *\n * `roots` is supplied so each skill/prompt's `managed` flag can be set:\n * resources whose filePath is under one of pi-pilot's writeable roots get\n * Edit/Delete affordances; resources that came from packages, settings\n * extras, ~/.agents/ paths, or CLI flags are read-only.\n *\n * `workspaceCwd` is the workspace's root directory, used to scan for\n * disabled extensions when extensions are off.\n */\nasync function snapshot(\n\tworkspaceId: string,\n\troots: ResourceRoots,\n\tworkspaceCwd: string,\n): Promise<ResourcesResponse> {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new HttpError(500, \"runtime not initialized\");\n\tconst loader = runtime.services.resourceLoader;\n\n\tconst { skills } = loader.getSkills();\n\tconst { prompts } = loader.getPrompts();\n\tconst extResult = loader.getExtensions();\n\n\t// Pi resolves command-name collisions across extensions by appending\n\t// `:N` to later registrations (two extensions registering `plan` become\n\t// invocation names `plan` and `plan:1`). We surface those resolved\n\t// names so the composer's autocomplete picks them up and a user-typed\n\t// `/plan:1` actually invokes the right handler. The runner is the only\n\t// public source of truth for resolved names — the raw per-extension\n\t// `Map<string, RegisteredCommand>.keys()` doesn't know about\n\t// cross-extension conflicts.\n\t//\n\t// `extensionRunner` becomes available after `bindExtensions()` (called\n\t// in workspace-manager.build); the `?? []` fallback is defensive for\n\t// the brief window after runtime creation but before bind.\n\tconst resolvedExtCommandsByPath = new Map<string, string[]>();\n\tconst resolvedCommands = runtime.session.extensionRunner?.getRegisteredCommands() ?? [];\n\tfor (const cmd of resolvedCommands) {\n\t\tconst key = cmd.sourceInfo.path;\n\t\tlet list = resolvedExtCommandsByPath.get(key);\n\t\tif (!list) {\n\t\t\tlist = [];\n\t\t\tresolvedExtCommandsByPath.set(key, list);\n\t\t}\n\t\tlist.push(cmd.invocationName);\n\t}\n\n\tconst skillsOut: SkillInfo[] = skills.map((s) => ({\n\t\tname: s.name,\n\t\tdescription: s.description,\n\t\tfilePath: s.filePath,\n\t\tdisableModelInvocation: s.disableModelInvocation,\n\t\tsource: toResourceSource(s.sourceInfo),\n\t\tmanaged: scopeFor(s.filePath, roots) !== undefined,\n\t}));\n\n\tconst promptsOut: PromptInfo[] = prompts.map((p) => ({\n\t\tname: p.name,\n\t\tdescription: p.description,\n\t\targumentHint: p.argumentHint,\n\t\tfilePath: p.filePath,\n\t\tcontent: p.content,\n\t\tsource: toResourceSource(p.sourceInfo),\n\t\tmanaged: scopeFor(p.filePath, roots) !== undefined,\n\t}));\n\n\tconst extensionsOut: ExtensionInfo[] = extResult.extensions.map((e) => {\n\t\t// Match on `sourceInfo.path` because that's what ResolvedCommand's\n\t\t// sourceInfo carries; `e.path` may differ for package-style\n\t\t// extensions. Fall back to the raw command keys if the runner\n\t\t// hasn't populated yet (defensive — shouldn't happen post-bind).\n\t\tconst resolved = resolvedExtCommandsByPath.get(e.sourceInfo.path);\n\t\treturn {\n\t\t\tpath: e.path,\n\t\t\tresolvedPath: e.resolvedPath,\n\t\t\tsource: toResourceSource(e.sourceInfo),\n\t\t\ttools: [...e.tools.keys()],\n\t\t\tcommands: resolved ?? [...e.commands.keys()],\n\t\t\tflags: [...e.flags.keys()],\n\t\t\tshortcuts: [...e.shortcuts.keys()],\n\t\t};\n\t});\n\n\tconst extensionErrors: ExtensionLoadError[] = extResult.errors.map((err) => ({\n\t\tpath: err.path,\n\t\terror: err.error,\n\t}));\n\n\t// Only surface \"you have extensions on disk that we skipped\" when we\n\t// actually skipped them. When the operator opted in via the env flag,\n\t// any extensions found on disk are represented by `extensions` /\n\t// `extensionErrors` instead, and surfacing them a second time here\n\t// would double-count.\n\tconst disabledExtensions = EXTENSIONS_ENABLED ? [] : await scanExtensionDirs(workspaceCwd);\n\n\t// Builtins are listed from the static manifest + global on/off prefs, not\n\t// from the live runner — so the panel reflects a just-toggled state even in\n\t// other workspaces whose runtimes haven't reloaded yet. Tools/commands are\n\t// the manifest's declared registrations (stable; collisions among our own\n\t// builtins don't happen).\n\tconst disabledBuiltins = new Set(getDisabledBuiltins());\n\tconst builtinExtensions: BuiltinExtensionInfo[] = BUILTIN_EXTENSIONS.map((d) => ({\n\t\tid: d.id,\n\t\tname: d.name,\n\t\tdescription: d.description,\n\t\tenabled: !disabledBuiltins.has(d.id),\n\t\ttools: d.tools,\n\t\tcommands: d.commands,\n\t}));\n\n\treturn {\n\t\tskills: skillsOut,\n\t\tprompts: promptsOut,\n\t\textensionsEnabled: EXTENSIONS_ENABLED,\n\t\textensions: extensionsOut,\n\t\textensionErrors,\n\t\tdisabledExtensions,\n\t\tbuiltinExtensions,\n\t};\n}\n\nasync function rootsFor(\n\tworkspaceId: string,\n): Promise<{ roots: ResourceRoots; workspaceCwd: string }> {\n\tconst ws = await getWorkspace(workspaceId);\n\tif (!ws) throw new HttpError(404, \"workspace not found\");\n\tconst roots = resolveResourceRoots({ agentDir: getAgentDir(), workspaceCwd: ws.path });\n\treturn { roots, workspaceCwd: ws.path };\n}\n\n/** Map a thrown error into a Hono JSON response. */\nfunction respondError(c: Context, err: unknown) {\n\tif (err instanceof HttpError) {\n\t\treturn c.json({ ok: false, error: err.message }, err.status as 400 | 404 | 409 | 500);\n\t}\n\tconst message = err instanceof Error ? err.message : String(err);\n\tconsole.error(`[api/resources] unexpected error:`, err);\n\treturn c.json({ ok: false, error: message }, 500);\n}\n\n/** Reload the loader so subsequent reads see the change we just made. */\nasync function reload(workspaceId: string): Promise<void> {\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (!runtime) throw new HttpError(500, \"runtime not initialized\");\n\tawait runtime.services.resourceLoader.reload();\n}\n\nexport function mountResourcesRoute(app: Hono): void {\n\t// ---------- GET inventory ----------\n\tapp.get(\"/:id/resources\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST reload ----------\n\tapp.post(\"/:id/resources/reload\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tawait reload(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- GET skill body ----------\n\tapp.get(\"/:id/resources/skill\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tconst { roots } = await rootsFor(id);\n\t\t\tconst scope = scopeFor(filePath, roots);\n\t\t\tif (scope === undefined) {\n\t\t\t\treturn c.json({ ok: false, error: \"skill file is not under a managed root\" }, 400);\n\t\t\t}\n\t\t\tconst data = await readSkillFile(filePath, roots);\n\t\t\tconst body: ResourceFileResponse = {\n\t\t\t\tfilePath,\n\t\t\t\tscope,\n\t\t\t\tname: data.name,\n\t\t\t\tdescription: data.description,\n\t\t\t\tdisableModelInvocation: data.disableModelInvocation,\n\t\t\t\tbody: data.body,\n\t\t\t};\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST skills (create) ----------\n\tapp.post(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as CreateSkillRequest | null;\n\t\tif (!body || !isScope(body.scope) || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"scope, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait createSkill({\n\t\t\t\troots,\n\t\t\t\tscope: body.scope,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\tbody: body.body,\n\t\t\t\tdisableModelInvocation: body.disableModelInvocation,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT skills (update) ----------\n\tapp.put(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as UpdateSkillRequest | null;\n\t\tif (!body || typeof body.filePath !== \"string\" || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"filePath, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait updateSkill({\n\t\t\t\troots,\n\t\t\t\tfilePath: body.filePath,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\tbody: body.body,\n\t\t\t\tdisableModelInvocation: body.disableModelInvocation,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- DELETE skills ----------\n\tapp.delete(\"/:id/resources/skills\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait deleteSkill(filePath, roots);\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- GET prompt body ----------\n\tapp.get(\"/:id/resources/prompt\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tconst { roots } = await rootsFor(id);\n\t\t\tconst scope = scopeFor(filePath, roots);\n\t\t\tif (scope === undefined) {\n\t\t\t\treturn c.json({ ok: false, error: \"prompt file is not under a managed root\" }, 400);\n\t\t\t}\n\t\t\tconst data = await readPromptFile(filePath, roots);\n\t\t\tconst body: ResourceFileResponse = {\n\t\t\t\tfilePath,\n\t\t\t\tscope,\n\t\t\t\tname: data.name,\n\t\t\t\tdescription: data.description,\n\t\t\t\targumentHint: data.argumentHint,\n\t\t\t\tbody: data.body,\n\t\t\t};\n\t\t\treturn c.json(body);\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- POST prompts (create) ----------\n\tapp.post(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as CreatePromptRequest | null;\n\t\tif (!body || !isScope(body.scope) || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"scope, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait createPrompt({\n\t\t\t\troots,\n\t\t\t\tscope: body.scope,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\targumentHint: body.argumentHint,\n\t\t\t\tbody: body.body,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT prompts (update) ----------\n\tapp.put(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as UpdatePromptRequest | null;\n\t\tif (!body || typeof body.filePath !== \"string\" || typeof body.name !== \"string\" || typeof body.description !== \"string\" || typeof body.body !== \"string\") {\n\t\t\treturn c.json({ ok: false, error: \"filePath, name, description, body are required\" }, 400);\n\t\t}\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait updatePrompt({\n\t\t\t\troots,\n\t\t\t\tfilePath: body.filePath,\n\t\t\t\tname: body.name,\n\t\t\t\tdescription: body.description,\n\t\t\t\targumentHint: body.argumentHint,\n\t\t\t\tbody: body.body,\n\t\t\t});\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- DELETE prompts ----------\n\tapp.delete(\"/:id/resources/prompts\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst filePath = c.req.query(\"path\");\n\t\tif (!filePath) return c.json({ ok: false, error: \"path query is required\" }, 400);\n\t\ttry {\n\t\t\tawait workspaceManager.getOrCreate(id);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\tawait deletePrompt(filePath, roots);\n\t\t\tawait reload(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n\n\t// ---------- PUT builtin extension on/off ----------\n\tapp.put(\"/:id/resources/builtin-extensions\", async (c) => {\n\t\tconst id = c.req.param(\"id\");\n\t\tconst ws = await getWorkspace(id);\n\t\tif (!ws) return c.json({ ok: false, error: \"not found\" }, 404);\n\t\tconst body = (await c.req.json().catch(() => null)) as SetBuiltinExtensionRequest | null;\n\t\tif (!body || typeof body.id !== \"string\" || typeof body.enabled !== \"boolean\") {\n\t\t\treturn c.json({ ok: false, error: \"id and enabled are required\" }, 400);\n\t\t}\n\t\tif (!BUILTIN_EXTENSIONS.some((d) => d.id === body.id)) {\n\t\t\treturn c.json({ ok: false, error: `unknown builtin extension: ${body.id}` }, 400);\n\t\t}\n\t\ttry {\n\t\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\t\t// Reloading rebuilds the tool registry; doing so mid-stream would\n\t\t\t// yank tools out from under an in-flight turn (and could strand a\n\t\t\t// pending ask_user). Make the user finish first.\n\t\t\tif (runtime.session.isStreaming) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{ ok: false, error: \"Stop the current turn before changing extensions\" },\n\t\t\t\t\t409,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Prefs are global; persist once. We only reload THIS workspace's\n\t\t\t// session so the change is live where the user is acting. Other\n\t\t\t// loaded workspaces keep their current toolset until they next\n\t\t\t// reload / restart, but their panels already reflect the new state\n\t\t\t// (snapshot reads prefs, not the live runner).\n\t\t\tawait setBuiltinEnabled(body.id, body.enabled);\n\t\t\tawait runtime.session.reload();\n\t\t\t// reload() force-reactivates every extension tool (it preserves\n\t\t\t// core-tool state but unions all extension tools back into the\n\t\t\t// active set), so restore this session's selection — otherwise\n\t\t\t// toggling an extension silently re-enables tools the user turned\n\t\t\t// off here.\n\t\t\treapplyToolPrefs(id, runtime.session);\n\t\t\tconst { roots, workspaceCwd } = await rootsFor(id);\n\t\t\treturn c.json(await snapshot(id, roots, workspaceCwd));\n\t\t} catch (err) {\n\t\t\treturn respondError(c, err);\n\t\t}\n\t});\n}\n\nfunction isScope(value: unknown): value is ResourceScope {\n\treturn value === \"user\" || value === \"project\";\n}\n","/**\n * GET /api/workspaces/:id/tree — session tree endpoint.\n *\n * Returns the full session tree as a flat DFS-ordered array of TreeNodeDTO.\n * The server reads `sessionManager.getTree()` (recursive SessionTreeNode[])\n * and flattens it, marking the active branch (root→leaf path) and computing\n * preview text for each entry type.\n */\n\nimport { Hono } from \"hono\";\nimport type { SessionTreeResponse, TreeEntryType, TreeMessageRole, TreeNodeDTO } from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { getWorkspace } from \"../storage/workspace-registry.ts\";\n\nexport const treeRoute = new Hono();\n\ntreeRoute.get(\"/\", async (c) => {\n\tconst id = c.req.param(\"id\") ?? \"\";\n\tconst existed = await getWorkspace(id);\n\tif (!existed) return c.json({ ok: false, error: \"not found\" }, 404);\n\n\ttry {\n\t\tconst runtime = await workspaceManager.getOrCreate(id);\n\t\tconst sm = runtime.session.sessionManager;\n\t\tconst tree = sm.getTree() as unknown as TreeNode[];\n\t\tconst leafId = sm.getLeafId();\n\n\t\t// Build the set of entry IDs on the active branch (leaf → root path).\n\t\tconst activeIds = new Set<string>();\n\t\tif (leafId) {\n\t\t\tconst branch = sm.getBranch(leafId);\n\t\t\tfor (const entry of branch) {\n\t\t\t\tactiveIds.add(entry.id);\n\t\t\t}\n\t\t}\n\n\t\tconst nodes: TreeNodeDTO[] = [];\n\t\tflattenTree(tree, leafId, activeIds, nodes, 0);\n\n\t\tconst body: SessionTreeResponse = { nodes, leafId };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(`[api] tree for ${id} failed:`, err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n// ── DFS tree flattening ──────────────────────────────────────────────────\n\n/**\n * Structural type matching SessionTreeNode from pi SDK. We use this to\n * access the entry fields loosely — the actual pi type is complex (union\n * of 9+ entry kinds) and we only need a handful of fields per kind.\n */\ninterface TreeNode {\n\tentry: Record<string, unknown> & {\n\t\ttype: string;\n\t\tid: string;\n\t\tparentId: string | null;\n\t\ttimestamp: string;\n\t};\n\tchildren: TreeNode[];\n\tlabel?: string;\n}\n\n/**\n * DFS pre-order traversal, appending to `out`. Children of each node are\n * sorted by timestamp (earliest first) so the tree reads chronologically.\n */\nfunction flattenTree(\n\tnodes: readonly TreeNode[],\n\tleafId: string | null,\n\tactiveIds: Set<string>,\n\tout: TreeNodeDTO[],\n\tdepth: number,\n): void {\n\t// Sort children by timestamp at each level.\n\tconst sorted = [...nodes].sort(\n\t\t(a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime(),\n\t);\n\n\tfor (const node of sorted) {\n\t\tconst entry = node.entry;\n\t\tconst id = entry.id;\n\n\t\tout.push({\n\t\t\tid,\n\t\t\tparentId: entry.parentId,\n\t\t\tdepth,\n\t\t\tentryType: mapEntryType(entry.type),\n\t\t\tmessageRole: entry.type === \"message\" ? mapMessageRole(entry[\"message\"]) : undefined,\n\t\t\ttimestamp: entry.timestamp,\n\t\t\tpreview: extractPreview(entry),\n\t\t\tactive: activeIds.has(id),\n\t\t\tisLeaf: id === leafId,\n\t\t\tchildCount: node.children.length,\n\t\t\tlabel: node.label,\n\t\t});\n\n\t\tif (node.children.length > 0) {\n\t\t\tflattenTree(node.children, leafId, activeIds, out, depth + 1);\n\t\t}\n\t}\n}\n\nfunction mapEntryType(type: string): TreeEntryType {\n\tswitch (type) {\n\t\tcase \"message\": return \"message\";\n\t\tcase \"compaction\": return \"compaction\";\n\t\tcase \"branch_summary\": return \"branch_summary\";\n\t\tcase \"model_change\": return \"model_change\";\n\t\tcase \"thinking_level_change\": return \"thinking_level_change\";\n\t\tcase \"custom\": return \"custom\";\n\t\tcase \"custom_message\": return \"custom_message\";\n\t\tcase \"label\": return \"label\";\n\t\tcase \"session_info\": return \"session_info\";\n\t\tdefault:\n\t\t\tconsole.warn(`[tree] unknown entry type \"${type}\" — mapping to \"custom\". pi SDK may have added a type tree.ts doesn't handle yet.`);\n\t\t\treturn \"custom\";\n\t}\n}\n\nfunction mapMessageRole(msg: unknown): TreeMessageRole | undefined {\n\tif (!msg || typeof msg !== \"object\") return undefined;\n\tconst role = (msg as { role?: string }).role;\n\tswitch (role) {\n\t\tcase \"user\": return \"user\";\n\t\tcase \"assistant\": return \"assistant\";\n\t\tcase \"toolResult\": return \"toolResult\";\n\t\tcase \"bashExecution\": return \"bashExecution\";\n\t\tcase \"custom\": return \"custom\";\n\t\tcase \"branchSummary\": return \"branchSummary\";\n\t\tcase \"compactionSummary\": return \"compactionSummary\";\n\t\tdefault: return undefined;\n\t}\n}\n\nconst PREVIEW_MAX = 120;\n\nfunction extractPreview(entry: TreeNode[\"entry\"]): string {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\treturn extractMessagePreview(entry[\"message\"]);\n\t\tcase \"compaction\":\n\t\t\treturn truncate(String(entry[\"summary\"] ?? \"Compaction\"), PREVIEW_MAX);\n\t\tcase \"branch_summary\":\n\t\t\treturn truncate(String(entry[\"summary\"] ?? \"Branch summary\"), PREVIEW_MAX);\n\t\tcase \"model_change\":\n\t\t\treturn `${entry[\"provider\"] ?? \"\"}/${entry[\"modelId\"] ?? \"\"}`;\n\t\tcase \"thinking_level_change\":\n\t\t\treturn `Thinking: ${entry[\"thinkingLevel\"] ?? \"\"}`;\n\t\tcase \"session_info\":\n\t\t\treturn entry[\"name\"] ? `Name: ${entry[\"name\"]}` : \"Session info\";\n\t\tcase \"custom_message\":\n\t\t\treturn truncate(extractContentText(entry[\"content\"]), PREVIEW_MAX) || \"Extension message\";\n\t\tcase \"custom\":\n\t\t\treturn `Custom: ${entry[\"customType\"] ?? \"\"}`;\n\t\tcase \"label\":\n\t\t\treturn \"Label\";\n\t\tdefault:\n\t\t\treturn \"\";\n\t}\n}\n\nfunction extractMessagePreview(msg: unknown): string {\n\tif (!msg || typeof msg !== \"object\") return \"\";\n\tconst m = msg as { role?: string; content?: unknown; command?: string; output?: string };\n\n\tif (m.role === \"bashExecution\") {\n\t\treturn truncate(`$ ${m.command ?? \"\"}`, PREVIEW_MAX);\n\t}\n\n\treturn truncate(extractContentText(m.content), PREVIEW_MAX);\n}\n\nfunction extractContentText(content: unknown): string {\n\tif (typeof content === \"string\") return content.replace(/\\s+/g, \" \").trim();\n\tif (!Array.isArray(content)) return \"\";\n\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (!block || typeof block !== \"object\") continue;\n\t\tconst b = block as { type?: string; text?: string; thinking?: string };\n\t\tif (b.type === \"text\" && typeof b.text === \"string\") {\n\t\t\tparts.push(b.text);\n\t\t} else if (b.type === \"thinking\" && typeof b.thinking === \"string\") {\n\t\t\tparts.push(b.thinking);\n\t\t}\n\t}\n\treturn parts.join(\" \").replace(/\\s+/g, \" \").trim();\n}\n\nfunction truncate(text: string, max: number): string {\n\tif (text.length <= max) return text;\n\treturn text.slice(0, max) + \"…\";\n}\n","/**\n * /api/fs/browse — directory listing for the web's <FsBrowser>.\n *\n * Only directories are returned (we never list files). Hidden entries are\n * excluded by default; pass ?showHidden=1 to include them.\n *\n * The `path` query parameter must be absolute. If omitted, defaults to the\n * user's home directory.\n */\n\nimport { readdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { Hono } from \"hono\";\nimport type { BrowseFsResponse, FsEntry } from \"@pi-pilot/shared\";\n\nexport const fsRoute = new Hono();\n\nfsRoute.get(\"/browse\", async (c) => {\n\tconst rawPath = c.req.query(\"path\");\n\tconst showHidden = c.req.query(\"showHidden\") === \"1\";\n\n\tconst target = rawPath && isAbsolute(rawPath) ? resolve(rawPath) : homedir();\n\tlet dirents;\n\ttry {\n\t\tdirents = await readdir(target, { withFileTypes: true });\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tconst msg = code === \"EACCES\" ? \"permission denied\" : code === \"ENOENT\" ? \"not found\" : \"read failed\";\n\t\treturn c.json({ ok: false, error: msg, path: target }, 400);\n\t}\n\n\tconst entries: FsEntry[] = dirents\n\t\t.filter((d) => d.isDirectory())\n\t\t.filter((d) => showHidden || !d.name.startsWith(\".\"))\n\t\t.map((d) => ({\n\t\t\tname: d.name,\n\t\t\tpath: join(target, d.name),\n\t\t\ttype: \"dir\" as const,\n\t\t}))\n\t\t.sort((a, b) => a.name.localeCompare(b.name));\n\n\tconst parent = (() => {\n\t\tconst p = dirname(target);\n\t\treturn p === target ? null : p;\n\t})();\n\n\tconst body: BrowseFsResponse = { path: target, parent, entries };\n\treturn c.json(body);\n});\n","/**\n * /api/model-configs routes.\n *\n * Reads and writes ~/.pi/agent/models.json. After any mutation the active\n * workspace runtimes have their modelRegistry refreshed so changes take\n * effect immediately without restarting the server.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { Hono } from \"hono\";\nimport {\n\tgetAgentDir,\n} from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tModelsConfig,\n\tModelsConfigResponse,\n\tSetModelsConfigRequest,\n\tUpsertProviderRequest,\n\tAddModelToProviderRequest,\n\tUpdateModelRequest,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport { writeJsonAtomic } from \"../storage/atomic-json.ts\";\nimport {\n\tmaskConfigForResponse,\n\tpreserveApiKeys,\n\tresolveUpsertApiKey,\n} from \"./model-config-keys.ts\";\n\nexport const modelConfigsRoute = new Hono();\n\n// ---------- helpers ----------\n\n/**\n * Serialize all read-modify-write operations on models.json. Although\n * Node.js is single-threaded, the async gaps between readFile and\n * writeFile allow interleaving of concurrent requests — without a lock\n * a later write could silently overwrite an earlier mutation.\n */\nlet writeLock = Promise.resolve();\nfunction withWriteLock<T>(fn: () => Promise<T>): Promise<T> {\n\tconst next = writeLock.then(fn, fn);\n\twriteLock = next.then(() => {}, () => {});\n\treturn next;\n}\n\nfunction modelsPath(): string {\n\treturn join(getAgentDir(), \"models.json\");\n}\n\nasync function readModelsJson(): Promise<ModelsConfig> {\n\ttry {\n\t\tconst raw = await readFile(modelsPath(), \"utf-8\");\n\t\treturn JSON.parse(raw) as ModelsConfig;\n\t} catch (err) {\n\t\tif ((err as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n\t\t\treturn { providers: {} };\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nasync function writeModelsJson(config: ModelsConfig): Promise<void> {\n\tawait writeJsonAtomic(modelsPath(), config, { mode: 0o600 });\n}\n\n/** Lightweight error for validation failures inside withWriteLock. */\nclass ValidationError extends Error {\n\tconstructor(message: string, public readonly status: number) {\n\t\tsuper(message);\n\t}\n}\n\nfunction refreshRegistry(workspaceId?: string): void {\n\tif (!workspaceId) return;\n\tconst runtime = workspaceManager.get(workspaceId);\n\tif (runtime) {\n\t\ttry {\n\t\t\truntime.session.modelRegistry.refresh();\n\t\t} catch (e) {\n\t\t\tconsole.error(`[model-configs] refresh registry for ${workspaceId} failed:`, e);\n\t\t}\n\t}\n}\n\n// ---------- routes ----------\n\n/** GET /api/model-configs */\nmodelConfigsRoute.get(\"/\", async (c) => {\n\ttry {\n\t\tconst config = await readModelsJson();\n\t\tconst body: ModelsConfigResponse = { config: maskConfigForResponse(config) };\n\t\treturn c.json(body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** PUT /api/model-configs — replace entire models.json */\nmodelConfigsRoute.put(\"/\", async (c) => {\n\tconst body = (await c.req.json()) as SetModelsConfigRequest;\n\tif (!body?.config?.providers) {\n\t\treturn c.json({ ok: false, error: \"config.providers is required\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst existing = await readModelsJson();\n\t\t\tconst merged = preserveApiKeys(body.config, existing);\n\t\t\tawait writeModelsJson(merged);\n\t\t\treturn merged;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config: maskConfigForResponse(config) };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** POST /api/model-configs/providers — add or update a provider */\nmodelConfigsRoute.post(\"/providers\", async (c) => {\n\tconst body = (await c.req.json()) as UpsertProviderRequest;\n\tif (!body?.name || !body?.provider) {\n\t\treturn c.json({ ok: false, error: \"name and provider are required\" }, 400);\n\t}\n\tif (!body.provider.baseUrl || !body.provider.api) {\n\t\treturn c.json({ ok: false, error: \"provider must have baseUrl and api\" }, 400);\n\t}\n\tif (!Array.isArray(body.provider.models)) {\n\t\treturn c.json({ ok: false, error: \"provider.models must be an array\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tconst apiKey = resolveUpsertApiKey(body.provider.apiKey, cfg.providers[body.name]?.apiKey);\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new ValidationError(\"apiKey is required for a new provider\", 400);\n\t\t\t}\n\t\t\tcfg.providers[body.name] = { ...body.provider, apiKey };\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config: maskConfigForResponse(config) };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 400);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** DELETE /api/model-configs/providers?name=... — delete a provider */\nmodelConfigsRoute.delete(\"/providers\", async (c) => {\n\tconst name = c.req.query(\"name\");\n\tif (!name) {\n\t\treturn c.json({ ok: false, error: \"name query param is required\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[name]) {\n\t\t\t\tthrow new ValidationError(`provider \"${name}\" not found`, 404);\n\t\t\t}\n\t\t\tdelete cfg.providers[name];\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config: maskConfigForResponse(config) };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** POST /api/model-configs/providers/:provider/models — add a model */\nmodelConfigsRoute.post(\"/providers/:provider/models\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst body = (await c.req.json()) as AddModelToProviderRequest;\n\tif (!body?.model?.id || !body?.model?.name) {\n\t\treturn c.json({ ok: false, error: \"model.id and model.name are required\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst existing = cfg.providers[provider].models.find(m => m.id === body.model.id);\n\t\t\tif (existing) {\n\t\t\t\tthrow new ValidationError(`model \"${body.model.id}\" already exists in provider \"${provider}\"`, 409);\n\t\t\t}\n\t\t\tcfg.providers[provider].models.push(body.model);\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config: maskConfigForResponse(config) };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404 | 409);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** PUT /api/model-configs/providers/:provider/models/:modelId — update a model */\nmodelConfigsRoute.put(\"/providers/:provider/models/:modelId\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst modelId = c.req.param(\"modelId\");\n\tconst body = (await c.req.json()) as UpdateModelRequest;\n\tif (!body?.model) {\n\t\treturn c.json({ ok: false, error: \"model is required\" }, 400);\n\t}\n\tif (body.model.id !== modelId) {\n\t\treturn c.json({ ok: false, error: \"model.id does not match URL parameter\" }, 400);\n\t}\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst idx = cfg.providers[provider].models.findIndex(m => m.id === modelId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new ValidationError(`model \"${modelId}\" not found in provider \"${provider}\"`, 404);\n\t\t\t}\n\t\t\tcfg.providers[provider].models[idx] = body.model;\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config: maskConfigForResponse(config) };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** DELETE /api/model-configs/providers/:provider/models/:modelId — delete a model */\nmodelConfigsRoute.delete(\"/providers/:provider/models/:modelId\", async (c) => {\n\tconst provider = c.req.param(\"provider\");\n\tconst modelId = c.req.param(\"modelId\");\n\ttry {\n\t\tconst config = await withWriteLock(async () => {\n\t\t\tconst cfg = await readModelsJson();\n\t\t\tif (!cfg.providers[provider]) {\n\t\t\t\tthrow new ValidationError(`provider \"${provider}\" not found`, 404);\n\t\t\t}\n\t\t\tconst idx = cfg.providers[provider].models.findIndex(m => m.id === modelId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new ValidationError(`model \"${modelId}\" not found in provider \"${provider}\"`, 404);\n\t\t\t}\n\t\t\tcfg.providers[provider].models.splice(idx, 1);\n\t\t\tawait writeModelsJson(cfg);\n\t\t\treturn cfg;\n\t\t});\n\t\tconst workspaceId = c.req.query(\"workspaceId\");\n\t\trefreshRegistry(workspaceId ?? undefined);\n\t\tconst resp: ModelsConfigResponse = { config: maskConfigForResponse(config) };\n\t\treturn c.json(resp);\n\t} catch (err) {\n\t\tif (err instanceof ValidationError) {\n\t\t\treturn c.json({ ok: false, error: err.message }, err.status as 404);\n\t\t}\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n","/**\n * API-key masking and merge helpers for ~/.pi/agent/models.json routes.\n *\n * GET responses never echo full provider keys; writes accept blank/masked\n * placeholders and preserve the on-disk secret.\n */\n\nimport type { ModelsConfig } from \"@pi-pilot/shared\";\n\nconst MASKED_KEY_RE = /^….{1,8}$/;\n\nexport function maskApiKey(key: string): string {\n\treturn `…${key.slice(-4)}`;\n}\n\nexport function isPreservedApiKey(key: string): boolean {\n\treturn key === \"\" || MASKED_KEY_RE.test(key);\n}\n\nexport function maskConfigForResponse(config: ModelsConfig): ModelsConfig {\n\tconst providers: ModelsConfig[\"providers\"] = {};\n\tfor (const [name, provider] of Object.entries(config.providers)) {\n\t\tproviders[name] = {\n\t\t\t...provider,\n\t\t\tapiKey: provider.apiKey ? maskApiKey(provider.apiKey) : \"\",\n\t\t};\n\t}\n\treturn { providers };\n}\n\nexport function preserveApiKeys(incoming: ModelsConfig, existing: ModelsConfig): ModelsConfig {\n\tconst providers: ModelsConfig[\"providers\"] = { ...incoming.providers };\n\tfor (const [name, provider] of Object.entries(providers)) {\n\t\tif (!isPreservedApiKey(provider.apiKey)) continue;\n\t\tconst prev = existing.providers[name]?.apiKey;\n\t\tif (prev) {\n\t\t\tproviders[name] = { ...provider, apiKey: prev };\n\t\t}\n\t}\n\treturn { providers };\n}\n\n/**\n * Resolve the effective apiKey for a single-provider upsert (POST /providers).\n * A real incoming key is used as-is; a blank OR masked placeholder falls back\n * to the existing on-disk key. Returns undefined when a placeholder has\n * nothing to preserve — the caller treats that as \"apiKey required\", so a\n * masked value sent for a brand-new provider can never be persisted verbatim.\n */\nexport function resolveUpsertApiKey(\n\tincomingKey: string,\n\texistingKey: string | undefined,\n): string | undefined {\n\tif (!isPreservedApiKey(incomingKey)) return incomingKey;\n\treturn existingKey || undefined;\n}","/**\n * /api/web-search routes — manage the Tavily API key used by the web_search /\n * web_fetch builtin. Persisted (0600) in ~/.pi/webui/web-search.json via\n * storage/web-search-prefs.ts; a Settings value overrides the TAVILY_API_KEY\n * env var. The full secret is NEVER returned — only whether a key is\n * configured, which source is live, and a masked tail.\n */\n\nimport { Hono } from \"hono\";\nimport type { WebSearchConfigResponse, SetWebSearchConfigRequest } from \"@pi-pilot/shared\";\nimport { getKeyStatus, setTavilyApiKey, clearTavilyApiKey } from \"../storage/web-search-prefs.ts\";\n\nexport const webSearchRoute = new Hono();\n\nfunction status(): WebSearchConfigResponse {\n\treturn getKeyStatus();\n}\n\n/** GET /api/web-search — is a key configured, from where, masked tail. */\nwebSearchRoute.get(\"/\", (c) => c.json(status()));\n\n/** PUT /api/web-search — set (non-empty) or clear (empty) the Settings key. */\nwebSearchRoute.put(\"/\", async (c) => {\n\tconst body = (await c.req.json().catch(() => null)) as SetWebSearchConfigRequest | null;\n\tif (!body || typeof body.apiKey !== \"string\") {\n\t\treturn c.json({ ok: false, error: \"apiKey (string) is required\" }, 400);\n\t}\n\ttry {\n\t\tawait setTavilyApiKey(body.apiKey);\n\t\treturn c.json(status());\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n\n/** DELETE /api/web-search — clear the Settings key (live key falls back to env). */\nwebSearchRoute.delete(\"/\", async (c) => {\n\ttry {\n\t\tawait clearTavilyApiKey();\n\t\treturn c.json(status());\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\treturn c.json({ ok: false, error: message }, 500);\n\t}\n});\n","/**\n * WebSocket hub.\n *\n * A connection has one \"primary\" runtime (commands target it) and zero or more\n * \"background\" runtimes (event-only, no command routing), each identified by a\n * `runtimeKey` = (workspace, session). A runtime is a single AgentSession, so a\n * single workspace can have several live at once (one streaming while you read\n * another).\n *\n * Switching — to another workspace OR to another session of the same workspace —\n * is the same non-destructive move: demote the current primary to a background\n * subscription that keeps forwarding events (so the client's snapshot cache\n * stays fresh, incl. tool calls), then promote the target. new_session / fork\n * build a *new* runtime and promote it, leaving the previous (possibly\n * streaming) session alive in the background. None of these block on streaming.\n *\n * Background subscriptions are capped at BACKGROUND_CAP per connection; the\n * oldest is evicted (and the client told via `background_evicted`) when the cap\n * is exceeded. On disconnect all per-connection state is released; the runtimes\n * in the manager are NOT torn down (other/future connections may reuse them).\n */\n\nimport { WebSocketServer, type WebSocket } from \"ws\";\nimport type { Server } from \"node:http\";\nimport { isAllowedHost, isAllowedWsOrigin } from \"../security.ts\";\nimport type { AgentSessionRuntime } from \"@earendil-works/pi-coding-agent\";\nimport type {\n\tPiAssistantTiming,\n\tPiContextUsage,\n\tWsClientMessage,\n\tWsServerMessage,\n} from \"@pi-pilot/shared\";\nimport { workspaceManager } from \"../workspace-manager.ts\";\nimport {\n\tinFlightAssistantSnapshot,\n\tinFlightRunningToolsSnapshot,\n\tinFlightToolCallsSnapshot,\n\ttranslatePiEvent,\n} from \"./bridge.ts\";\nimport { resolveAnswer } from \"../extensions/ask_user/registry.ts\";\n\nconst BACKGROUND_CAP = 4;\n\ninterface PrimarySub {\n\truntimeKey: string;\n\tworkspaceId: string;\n\tsessionPath: string | null;\n\truntime: AgentSessionRuntime;\n\tunsubscribe: () => void;\n}\n\ninterface BackgroundSub {\n\tworkspaceId: string;\n\tsessionPath: string | null;\n\tunsubscribeSession: () => void;\n}\n\ninterface ConnectionState {\n\tprimary?: PrimarySub;\n\t/** Insertion-ordered; oldest evicted when cap exceeded. Keyed by runtimeKey. */\n\tbackground: Map<string, BackgroundSub>;\n}\n\n/**\n * Per-workspace lock serializing the runtime-replacing commands (new_session,\n * fork, navigate_tree) so concurrent clicks don't race. Plain session switches\n * are non-destructive and don't take it.\n */\nconst replacementLocks = new Map<string, Promise<void>>();\n\nfunction withReplacementLock(workspaceId: string, fn: () => Promise<void>): Promise<void> {\n\tconst prev = replacementLocks.get(workspaceId) ?? Promise.resolve();\n\tconst next = prev.then(fn, fn);\n\treplacementLocks.set(workspaceId, next);\n\tconst cleanup = () => {\n\t\tif (replacementLocks.get(workspaceId) === next) {\n\t\t\treplacementLocks.delete(workspaceId);\n\t\t}\n\t};\n\tnext.then(cleanup, cleanup);\n\treturn next;\n}\n\nexport function attachWsHub(httpServer: Server): WebSocketServer {\n\tconst wss = new WebSocketServer({\n\t\tserver: httpServer,\n\t\tpath: \"/ws\",\n\t\tverifyClient: (info, cb) => {\n\t\t\tconst host = info.req.headers.host;\n\t\t\tconst origin = info.origin;\n\t\t\tif (!isAllowedHost(host) || !isAllowedWsOrigin(origin)) {\n\t\t\t\tcb(false, 403, \"Forbidden\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcb(true);\n\t\t},\n\t});\n\n\twss.on(\"connection\", (ws) => {\n\t\tconst state: ConnectionState = { background: new Map() };\n\t\tlet inbound = Promise.resolve();\n\n\t\tws.on(\"message\", (raw) => {\n\t\t\tinbound = inbound\n\t\t\t\t.then(async () => {\n\t\t\t\t\tlet msg: WsClientMessage;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmsg = JSON.parse(raw.toString()) as WsClientMessage;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tsend(ws, { type: \"error\", message: \"invalid JSON\" });\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait handle(ws, state, msg);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\t\tsend(ws, { type: \"error\", message, command: msg.type });\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tconsole.error(\"[ws] inbound chain error:\", err);\n\t\t\t\t});\n\t\t});\n\n\t\tws.on(\"close\", () => {\n\t\t\tdetach(state, ws);\n\t\t});\n\t});\n\n\treturn wss;\n}\n\nasync function handle(ws: WebSocket, state: ConnectionState, msg: WsClientMessage): Promise<void> {\n\tswitch (msg.type) {\n\t\tcase \"subscribe\": {\n\t\t\t// Resolve (build if needed) the runtime for the requested\n\t\t\t// (workspace, session). Omitted sessionPath → workspace default.\n\t\t\tlet runtime: AgentSessionRuntime;\n\t\t\ttry {\n\t\t\t\truntime = await workspaceManager.getOrCreate(msg.workspaceId, msg.sessionPath);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tsend(ws, { type: \"error\", message, command: \"subscribe\" });\n\t\t\t\tsend(ws, { type: \"ack\", command: \"subscribe\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpromoteToPrimary(ws, state, msg.workspaceId, runtime);\n\t\t\tsend(ws, { type: \"ack\", command: \"subscribe\" });\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"unsubscribe\": {\n\t\t\t// Drop any background subscriptions for this workspace. No-op for the\n\t\t\t// primary or for already-gone runtimes.\n\t\t\tfor (const [key, bg] of [...state.background]) {\n\t\t\t\tif (bg.workspaceId === msg.workspaceId) teardownBackground(state, key, ws);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"prompt\": {\n\t\t\tconst primary = state.primary;\n\t\t\tif (!primary) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (replacementLocks.has(primary.workspaceId)) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"session switching in progress\", command: \"prompt\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvoid primary.runtime.session\n\t\t\t\t.prompt(msg.message, { streamingBehavior: msg.streamingBehavior })\n\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tsend(ws, { type: \"error\", message, command: \"prompt\" });\n\t\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"abort\": {\n\t\t\tconst primary = state.primary;\n\t\t\tif (!primary) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"abort\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait primary.runtime.session.abort();\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"new_session\": {\n\t\t\t// new_session names its own workspace, so it works even before a\n\t\t\t// primary exists (initial \"new chat\"). The current session, if any,\n\t\t\t// is left streaming in the background.\n\t\t\tconst workspaceId = msg.workspaceId;\n\t\t\tawait withReplacementLock(workspaceId, async () => {\n\t\t\t\tlet runtime: AgentSessionRuntime;\n\t\t\t\ttry {\n\t\t\t\t\t// A fresh session in its own runtime — the current (possibly\n\t\t\t\t\t// streaming) session is left alone, demoted to background.\n\t\t\t\t\truntime = await workspaceManager.createSession(workspaceId);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tsend(ws, { type: \"error\", message, command: \"new_session\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tpromoteToPrimary(ws, state, workspaceId, runtime);\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"fork\": {\n\t\t\tconst primary = state.primary;\n\t\t\tif (!primary) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"fork\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst workspaceId = primary.workspaceId;\n\t\t\tawait withReplacementLock(workspaceId, async () => {\n\t\t\t\tconst source = state.primary;\n\t\t\t\tif (!source) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!source.sessionPath) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"cannot fork an unsaved session\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Forking reads the source's committed history; doing it mid-stream\n\t\t\t\t// is undefined, so require the source to be settled.\n\t\t\t\tif (source.runtime.session.isStreaming) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"cannot fork while streaming\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet result: { cancelled: boolean; runtime?: AgentSessionRuntime };\n\t\t\t\ttry {\n\t\t\t\t\tresult = await workspaceManager.fork(workspaceId, source.sessionPath, msg.entryId);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\t\tsend(ws, { type: \"error\", message, command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (result.cancelled || !result.runtime) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"fork cancelled\", command: \"fork\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tpromoteToPrimary(ws, state, workspaceId, result.runtime);\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"answer_question\": {\n\t\t\tconst primary = state.primary;\n\t\t\tif (!primary) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"answer_question\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolveAnswer(msg.toolCallId, msg.answer, primary.runtime.session.sessionFile ?? null);\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"navigate_tree\": {\n\t\t\tconst primary = state.primary;\n\t\t\tif (!primary) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"navigate_tree\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (msg.workspaceId !== primary.workspaceId) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"workspace mismatch\", command: \"navigate_tree\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait withReplacementLock(primary.workspaceId, async () => {\n\t\t\t\tconst current = state.primary;\n\t\t\t\tif (!current) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"navigate_tree\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Tree navigation moves the leaf in-place within the active\n\t\t\t\t// session, so it can't run mid-stream.\n\t\t\t\tif (current.runtime.session.isStreaming) {\n\t\t\t\t\tsend(ws, { type: \"error\", message: \"cannot navigate tree while streaming\", command: \"navigate_tree\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst result = await current.runtime.session.navigateTree(msg.targetId, {\n\t\t\t\t\tsummarize: msg.summarize,\n\t\t\t\t\tcustomInstructions: msg.customInstructions,\n\t\t\t\t});\n\t\t\t\tsend(ws, {\n\t\t\t\t\ttype: \"navigate_tree_result\",\n\t\t\t\t\tworkspaceId: current.workspaceId,\n\t\t\t\t\teditorText: result.editorText,\n\t\t\t\t\tcancelled: result.cancelled,\n\t\t\t\t});\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tcase \"compact\": {\n\t\t\tconst primary = state.primary;\n\t\t\tif (!primary) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"not subscribed\", command: \"compact\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (primary.runtime.session.isStreaming) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"cannot compact while streaming\", command: \"compact\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (primary.runtime.session.isCompacting) {\n\t\t\t\tsend(ws, { type: \"error\", message: \"compaction already in progress\", command: \"compact\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprimary.runtime.session.compact().catch((err: unknown) => {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tsend(ws, { type: \"error\", message, command: \"compact\" });\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tdefault: {\n\t\t\tconst _: never = msg;\n\t\t\tvoid _;\n\t\t\tsend(ws, { type: \"error\", message: \"unknown command\" });\n\t\t}\n\t}\n}\n\n/* ── Primary / background promotion ────────────────────────────────────── */\n\n/**\n * Make `runtime` this connection's primary. If it's already primary, just\n * re-send `subscribed` (so the client clears any `switching` spinner — e.g.\n * re-selecting the active session). Otherwise demote the current primary to\n * background, pull the target out of background if present, and bind it.\n */\nfunction promoteToPrimary(\n\tws: WebSocket,\n\tstate: ConnectionState,\n\tworkspaceId: string,\n\truntime: AgentSessionRuntime,\n): void {\n\tconst runtimeKey = workspaceManager.runtimeKeyFor(workspaceId, runtime);\n\n\tif (state.primary?.runtimeKey === runtimeKey) {\n\t\tworkspaceManager.setActive(workspaceId, runtime);\n\t\tsendSubscribed(ws, workspaceId, runtime);\n\t\treturn;\n\t}\n\n\tif (state.primary) demotePrimaryToBackground(ws, state);\n\tif (state.background.has(runtimeKey)) teardownBackground(state, runtimeKey, ws);\n\tbindPrimary(ws, state, workspaceId, runtime);\n}\n\nfunction bindPrimary(\n\tws: WebSocket,\n\tstate: ConnectionState,\n\tworkspaceId: string,\n\truntime: AgentSessionRuntime,\n): void {\n\tworkspaceManager.addSubscriber(workspaceId, ws);\n\tworkspaceManager.setActive(workspaceId, runtime);\n\n\tconst session = runtime.session;\n\tconst sessionPath = session.sessionFile ?? null;\n\tconst runtimeKey = workspaceManager.runtimeKeyFor(workspaceId, runtime);\n\n\tlet assistantStartAt: number | undefined;\n\tlet assistantFirstTokenAt: number | undefined;\n\n\tconst unsubscribe = session.subscribe((ev) => {\n\t\tconst payload = translatePiEvent(ev);\n\t\tif (!payload) return;\n\n\t\tif (payload.kind === \"message_start\" && payload.role === \"assistant\") {\n\t\t\tassistantStartAt = performance.now();\n\t\t\tassistantFirstTokenAt = undefined;\n\t\t} else if (\n\t\t\tpayload.kind === \"message_update\" &&\n\t\t\tpayload.delta.kind === \"text\" &&\n\t\t\tassistantStartAt !== undefined &&\n\t\t\tassistantFirstTokenAt === undefined\n\t\t) {\n\t\t\tassistantFirstTokenAt = performance.now();\n\t\t}\n\n\t\tsend(ws, { type: \"event\", workspaceId, sessionPath, payload });\n\n\t\tif (\n\t\t\tpayload.kind === \"message_end\" &&\n\t\t\tpayload.role === \"assistant\" &&\n\t\t\tassistantStartAt !== undefined\n\t\t) {\n\t\t\tconst now = performance.now();\n\t\t\tconst timing: PiAssistantTiming = {\n\t\t\t\tkind: \"assistant_timing\",\n\t\t\t\tfirstTokenMs:\n\t\t\t\t\tassistantFirstTokenAt !== undefined\n\t\t\t\t\t\t? Math.round(assistantFirstTokenAt - assistantStartAt)\n\t\t\t\t\t\t: null,\n\t\t\t\ttotalMs: Math.round(now - assistantStartAt),\n\t\t\t};\n\t\t\tsend(ws, { type: \"event\", workspaceId, sessionPath, payload: timing });\n\t\t\tassistantStartAt = undefined;\n\t\t\tassistantFirstTokenAt = undefined;\n\t\t}\n\n\t\tif (\n\t\t\tpayload.kind === \"agent_end\" ||\n\t\t\tpayload.kind === \"compaction_end\" ||\n\t\t\tpayload.kind === \"session_info_changed\" ||\n\t\t\tpayload.kind === \"thinking_level_changed\"\n\t\t) {\n\t\t\tsendContextUsage(ws, runtime, workspaceId, sessionPath);\n\t\t}\n\t});\n\n\tstate.primary = { runtimeKey, workspaceId, sessionPath, runtime, unsubscribe };\n\n\tsendSubscribed(ws, workspaceId, runtime);\n\n\t// Restore an interrupted turn for a client that (re)subscribes mid-stream:\n\t// 1. running tool cards (committed toolCall, no result yet → invisible to\n\t// REST /history) — recovered from pendingToolCalls,\n\t// 2. the streaming assistant text/thinking (not yet committed),\n\t// 3. pending ask_user interactive cards (from the question registry).\n\t// Running tools first so they precede the still-streaming reply.\n\tconst streamingMessage = runtime.session.state.streamingMessage;\n\tconst scanMessages = streamingMessage\n\t\t? [...runtime.session.messages, streamingMessage]\n\t\t: runtime.session.messages;\n\tfor (const payload of inFlightRunningToolsSnapshot(\n\t\truntime.session.state.pendingToolCalls,\n\t\tscanMessages,\n\t)) {\n\t\tsend(ws, { type: \"event\", workspaceId, sessionPath, payload });\n\t}\n\n\tconst inFlight = inFlightAssistantSnapshot(streamingMessage);\n\tif (inFlight) {\n\t\tfor (const payload of inFlight) {\n\t\t\tsend(ws, { type: \"event\", workspaceId, sessionPath, payload });\n\t\t}\n\t}\n\n\tfor (const payload of inFlightToolCallsSnapshot(sessionPath)) {\n\t\tsend(ws, { type: \"event\", workspaceId, sessionPath, payload });\n\t}\n\n\tsendContextUsage(ws, runtime, workspaceId, sessionPath);\n}\n\n/**\n * Demote the current primary to a background subscription: tear down the\n * primary (timing-aware) listener and bind a lightweight one that keeps\n * forwarding core events + context_usage. The runtime stays alive.\n */\nfunction demotePrimaryToBackground(ws: WebSocket, state: ConnectionState): void {\n\tconst primary = state.primary;\n\tif (!primary) return;\n\n\tprimary.unsubscribe();\n\tstate.primary = undefined;\n\n\tconst session = primary.runtime.session;\n\tconst sessionPath = primary.sessionPath;\n\tconst unsubscribeSession = session.subscribe((ev) => {\n\t\tconst payload = translatePiEvent(ev);\n\t\tif (!payload) return;\n\t\tsend(ws, { type: \"event\", workspaceId: primary.workspaceId, sessionPath, payload });\n\t\tif (\n\t\t\tpayload.kind === \"agent_end\" ||\n\t\t\tpayload.kind === \"compaction_end\" ||\n\t\t\tpayload.kind === \"session_info_changed\" ||\n\t\t\tpayload.kind === \"thinking_level_changed\"\n\t\t) {\n\t\t\tsendContextUsage(ws, primary.runtime, primary.workspaceId, sessionPath);\n\t\t}\n\t});\n\n\tstate.background.set(primary.runtimeKey, {\n\t\tworkspaceId: primary.workspaceId,\n\t\tsessionPath,\n\t\tunsubscribeSession,\n\t});\n\n\t// Enforce cap: evict oldest, telling the client so it marks that snapshot\n\t// stale (events stop flowing) and rebuilds authoritatively on return.\n\twhile (state.background.size > BACKGROUND_CAP) {\n\t\tconst oldestKey = state.background.keys().next().value;\n\t\tif (oldestKey === undefined) break;\n\t\tconst evicted = state.background.get(oldestKey);\n\t\tteardownBackground(state, oldestKey, ws);\n\t\tif (evicted) {\n\t\t\tsend(ws, {\n\t\t\t\ttype: \"background_evicted\",\n\t\t\t\tworkspaceId: evicted.workspaceId,\n\t\t\t\tsessionPath: evicted.sessionPath,\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction teardownBackground(state: ConnectionState, runtimeKey: string, ws?: WebSocket): void {\n\tconst bg = state.background.get(runtimeKey);\n\tif (!bg) return;\n\tbg.unsubscribeSession();\n\tstate.background.delete(runtimeKey);\n\tif (ws) unrefWorkspaceSubscriber(state, bg.workspaceId, ws);\n}\n\n/* ── Shared helpers ────────────────────────────────────────────────────── */\n\nfunction sendSubscribed(ws: WebSocket, workspaceId: string, runtime: AgentSessionRuntime): void {\n\tsend(ws, {\n\t\ttype: \"subscribed\",\n\t\tworkspaceId,\n\t\tsessionPath: runtime.session.sessionFile ?? null,\n\t\tsessionId: runtime.session.sessionId,\n\t});\n}\n\n/**\n * Drop the connection's workspace-level subscriber registration once it no\n * longer has any (primary or background) interest in that workspace. The\n * registration drives manager broadcasts (extension errors, context_usage).\n */\nfunction unrefWorkspaceSubscriber(state: ConnectionState, workspaceId: string, ws: WebSocket): void {\n\tconst stillUsed =\n\t\tstate.primary?.workspaceId === workspaceId ||\n\t\t[...state.background.values()].some((b) => b.workspaceId === workspaceId);\n\tif (!stillUsed) workspaceManager.removeSubscriber(workspaceId, ws);\n}\n\nfunction sendContextUsage(\n\tws: WebSocket,\n\truntime: AgentSessionRuntime,\n\tworkspaceId: string,\n\tsessionPath: string | null,\n): void {\n\tconst usage = runtime.session.getContextUsage();\n\tif (!usage) return;\n\tconst payload: PiContextUsage = {\n\t\tkind: \"context_usage\",\n\t\ttokens: usage.tokens,\n\t\tcontextWindow: usage.contextWindow,\n\t\tpercent: usage.percent,\n\t};\n\tsend(ws, { type: \"event\", workspaceId, sessionPath, payload });\n}\n\nfunction detachPrimary(state: ConnectionState, ws?: WebSocket): void {\n\tconst primary = state.primary;\n\tif (!primary) return;\n\tprimary.unsubscribe();\n\tstate.primary = undefined;\n\tif (ws) unrefWorkspaceSubscriber(state, primary.workspaceId, ws);\n}\n\nfunction detach(state: ConnectionState, ws?: WebSocket): void {\n\tdetachPrimary(state, ws);\n\tfor (const runtimeKey of [...state.background.keys()]) {\n\t\tteardownBackground(state, runtimeKey, ws);\n\t}\n}\n\nfunction send(ws: WebSocket, msg: WsServerMessage): void {\n\tif (ws.readyState !== ws.OPEN) return;\n\tws.send(JSON.stringify(msg));\n}\n","/**\n * Localhost request guards — DNS-rebinding and drive-by WebSocket defense.\n *\n * CORS only covers cross-origin HTTP fetches; WebSocket handshakes and\n * same-port rebinding bypass it. We whitelist Host (HTTP + WS upgrade) and\n * Origin (WS only) to loopback dev/prod origins.\n */\n\nimport { config } from \"./config.ts\";\n\nconst LOOPBACK_HOSTNAMES = [\"127.0.0.1\", \"localhost\"] as const;\n\nfunction buildAllowedHosts(): ReadonlySet<string> {\n\tconst ports = new Set([config.port, 5173]);\n\tconst hosts = new Set<string>();\n\tfor (const name of LOOPBACK_HOSTNAMES) {\n\t\tfor (const port of ports) {\n\t\t\thosts.add(`${name}:${port}`);\n\t\t}\n\t}\n\treturn hosts;\n}\n\nfunction buildAllowedWsOrigins(): ReadonlySet<string> {\n\tconst origins = new Set<string>([config.corsOrigin]);\n\tfor (const name of LOOPBACK_HOSTNAMES) {\n\t\torigins.add(`http://${name}:${config.port}`);\n\t\torigins.add(`http://${name}:5173`);\n\t}\n\treturn origins;\n}\n\nconst allowedHosts = buildAllowedHosts();\nconst allowedWsOrigins = buildAllowedWsOrigins();\n\nexport function isAllowedHost(host: string | undefined): boolean {\n\tif (!host) return false;\n\treturn allowedHosts.has(host.toLowerCase());\n}\n\nexport function isAllowedWsOrigin(origin: string | undefined): boolean {\n\tif (!origin) return false;\n\treturn allowedWsOrigins.has(origin);\n}"],"mappings":"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","names":["existsSync","readFile","dirname","join","resolve","sep","fileURLToPath","Hono","readFile","stat","basename","isAbsolute","resolve","Hono","join","err","status","readFile","join","randomUUID","mkdir","rm","writeFile","dirname","err","join","readFile","err","randomUUID","cache","cached","pending","unlink","isAbsolute","resolve","getAgentDir","mkdir","readFile","rename","writeFile","dirname","join","resolve","join","cache","readFile","err","resolve","save","writeChain","mkdir","dirname","writeFile","rename","pending","Type","resolve","err","text","details","Type","TOOL_NAME","readFileSync","join","homedir","join","resolve","join","readFileSync","rm","join","Type","dirname","join","handle","keyOf","keyOf","resolve","join","resolve","err","rm","status","err","t","bits","status","Type","readFile","join","PREFS_PATH","join","cache","readFile","err","save","err","readFile","join","PREFS_PATH","join","cache","readFile","err","save","execFile","readFile","rm","tmpdir","join","CUSTOM_TYPE","reconcileAfterRestart","tmpdir","join","readFile","rm","resolve","execFile","err","pending","resolve","getAgentDir","err","isAbsolute","existing","inflight","p","extractUserText","unlink","reconcileAfterRestart","running","app","exists","err","execFile","readdir","join","sep","promisify","exec","promisify","execFile","inflight","cached","pending","out","readdir","join","sep","app","err","readdir","join","getAgentDir","join","getAgentDir","readdir","err","app","err","truncate","extractContentText","Hono","err","readFile","basename","isAbsolute","resolve","stat","readdir","homedir","dirname","isAbsolute","join","resolve","Hono","err","readFile","join","Hono","getAgentDir","config","Hono","join","getAgentDir","readFile","err","config","status","apiKey","Hono","Hono","err","err","Hono","dirname","fileURLToPath","resolve","join","sep","readFile","err","existsSync"]}