@jyork0828/pi-pilot 0.0.1 → 0.0.2
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 +102 -12
- package/dist/index.js.map +1 -1
- package/package.json +13 -11
- package/public/assets/index-Cpw6tfat.css +1 -0
- package/public/assets/index-Dw_Ppxym.js +223 -0
- package/public/index.html +2 -2
- package/public/assets/index-Bqpj-esU.css +0 -1
- package/public/assets/index-e1hZMoPP.js +0 -218
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.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/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","../src/ws/bridge.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 { 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\";\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\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 * /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 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 * pi-pilot does not support pi extensions. The extension system is\n * TUI-native (custom renderers, ANSI themes, keyboard overlays, modal\n * dialogs) and cannot be bridged to the Web in any meaningful way.\n *\n * Therefore every pi extension is disabled by default. The runtime still\n * loads skills and prompts; only extension modules are skipped.\n *\n * `PI_PILOT_ENABLE_EXTENSIONS=1` is an escape hatch for experimentation.\n * Even with the flag, no `ctx.ui.*` calls are bridged to the Web — see\n * `ws/extension-ui.ts`. Extensions that only register tools / slash\n * commands without touching `ctx.ui` will still work; everything else\n * appears broken.\n *\n * Exported so `/api/resources` can surface the current state to the UI.\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: EXTENSIONS_ENABLED ? undefined : { noExtensions: true },\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\truntime.setRebindSession(async () => {\n\t\t\tawait runtime.session.bindExtensions({ uiContext: bridge, onError });\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};\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});\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\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\tconst ids = [...this.states.keys()];\n\t\tawait Promise.all(ids.map((id) => this.dispose(id)));\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 * 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\tCreatePromptRequest,\n\tCreateSkillRequest,\n\tExtensionInfo,\n\tExtensionLoadError,\n\tPromptInfo,\n\tResourceFileResponse,\n\tResourceScope,\n\tResourceSource,\n\tResourcesResponse,\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\";\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\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\tpath: e.path,\n\t\tresolvedPath: e.resolvedPath,\n\t\tsource: toResourceSource(e.sourceInfo),\n\t\ttools: [...e.tools.keys()],\n\t\tcommands: [...e.commands.keys()],\n\t\tflags: [...e.flags.keys()],\n\t\tshortcuts: [...e.shortcuts.keys()],\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\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};\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\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 { translatePiEvent } from \"./bridge.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\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\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","/**\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\";\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\treturn { kind: \"message_start\", role: roleOf(ev.message) };\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\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};\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\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\treturn \"assistant\";\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"],"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","unlink","isAbsolute","resolve","inflight","resolve","isAbsolute","unlink","app","exists","execFile","join","sep","promisify","exec","promisify","execFile","inflight","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/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/index.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","../src/ws/bridge.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 { 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\";\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\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 * /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 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\truntime.setRebindSession(async () => {\n\t\t\tawait runtime.session.bindExtensions({ uiContext: bridge, onError });\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};\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});\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\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\tconst ids = [...this.states.keys()];\n\t\tawait Promise.all(ids.map((id) => this.dispose(id)));\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 * 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 \"not supported\" in the Resources panel), but pi-pilot\n * ships its own first-party features via this list. To add a new builtin\n * feature, drop a new directory under `extensions/<name>/` with a\n * `factory.ts` exporting an `ExtensionFactory`, then append it here.\n *\n * NOT a barrel file — this defines a value (the factories list), not a\n * re-export of unrelated symbols.\n */\n\nimport type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { planExtensionFactory } from \"./plan/factory.ts\";\n\nexport const builtinExtensionFactories: ExtensionFactory[] = [planExtensionFactory];\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\tCreatePromptRequest,\n\tCreateSkillRequest,\n\tExtensionInfo,\n\tExtensionLoadError,\n\tPromptInfo,\n\tResourceFileResponse,\n\tResourceScope,\n\tResourceSource,\n\tResourcesResponse,\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\";\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\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};\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\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 { translatePiEvent } from \"./bridge.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\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\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","/**\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\";\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\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};\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\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\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/** 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"],"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","unlink","isAbsolute","resolve","inflight","resolve","isAbsolute","unlink","app","exists","execFile","join","sep","promisify","exec","promisify","execFile","inflight","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","extractUserText","Hono","dirname","resolve","join","sep","readFile"]}
|