@overdeck/core 0.49.0 → 0.49.1
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/{build-info-Ck2TzOSQ.js → build-info-Bts-0E0B.js} +3 -3
- package/dist/{build-info-Ck2TzOSQ.js.map → build-info-Bts-0E0B.js.map} +1 -1
- package/dist/build-info-CD0O1MvT.js +2 -0
- package/dist/cli/index.js +4 -4
- package/dist/dashboard/{build-info-DXfOjbg3.js → build-info-DTX8JwVP.js} +3 -3
- package/dist/dashboard/{build-info-DXfOjbg3.js.map → build-info-DTX8JwVP.js.map} +1 -1
- package/dist/dashboard/build-info-PQlh_Rhb.js +2 -0
- package/dist/dashboard/{deacon-CeqVPw0o.js → deacon-BEH-IY0v.js} +2 -2
- package/dist/dashboard/{deacon-CQxv6Qwv.js → deacon-TPYssFYt.js} +8 -8
- package/dist/dashboard/{deacon-CQxv6Qwv.js.map → deacon-TPYssFYt.js.map} +1 -1
- package/dist/dashboard/{deacon-merge-ugZ7jekt.js → deacon-merge-BOGFEV8_.js} +5 -5
- package/dist/dashboard/{deacon-merge-ugZ7jekt.js.map → deacon-merge-BOGFEV8_.js.map} +1 -1
- package/dist/dashboard/{deacon-pending-lifecycle-Cxu6Rezs.js → deacon-pending-lifecycle-DVHjOLje.js} +2 -2
- package/dist/dashboard/{deacon-pending-lifecycle-Cxu6Rezs.js.map → deacon-pending-lifecycle-DVHjOLje.js.map} +1 -1
- package/dist/dashboard/deacon.js +2 -2
- package/dist/dashboard/{deploy-patrol-DxnpZcnl.js → deploy-patrol-BXjlvoV3.js} +2 -2
- package/dist/dashboard/{deploy-patrol-DxnpZcnl.js.map → deploy-patrol-BXjlvoV3.js.map} +1 -1
- package/dist/dashboard/{identity-BnxKo9-z.js → identity-Bwh6VGeL.js} +2 -2
- package/dist/dashboard/{identity-BnxKo9-z.js.map → identity-Bwh6VGeL.js.map} +1 -1
- package/dist/dashboard/{lifecycle-ozA4GZNs.js → lifecycle-B0rL38D3.js} +2 -2
- package/dist/dashboard/{lifecycle-ozA4GZNs.js.map → lifecycle-B0rL38D3.js.map} +1 -1
- package/dist/dashboard/{memory-governor-W8994ASc.js → memory-governor-A4O6vI9d.js} +3 -3
- package/dist/dashboard/{memory-governor-W8994ASc.js.map → memory-governor-A4O6vI9d.js.map} +1 -1
- package/dist/dashboard/{memory-pressure-patrol-D_T_QLFC.js → memory-pressure-patrol-BWlWywmA.js} +2 -2
- package/dist/dashboard/{memory-pressure-patrol-D_T_QLFC.js.map → memory-pressure-patrol-BWlWywmA.js.map} +1 -1
- package/dist/dashboard/{merge-agent-B8YsWRDu.js → merge-agent-BRSz26Oh.js} +1 -1
- package/dist/dashboard/{merge-agent-DxecsTre.js → merge-agent-GWhmnxFk.js} +2 -2
- package/dist/dashboard/{merge-agent-DxecsTre.js.map → merge-agent-GWhmnxFk.js.map} +1 -1
- package/dist/dashboard/merge-ops-DtHx17Vd.js +2 -0
- package/dist/dashboard/{post-merge-lifecycle-worker-DC-i1kqz.js → post-merge-lifecycle-worker-KK1RTKRE.js} +2 -2
- package/dist/dashboard/{post-merge-lifecycle-worker-DC-i1kqz.js.map → post-merge-lifecycle-worker-KK1RTKRE.js.map} +1 -1
- package/dist/dashboard/{resolver-BdHzKhNL.js → resolver-C8rooxoz.js} +2 -2
- package/dist/dashboard/{resolver-BdHzKhNL.js.map → resolver-C8rooxoz.js.map} +1 -1
- package/dist/dashboard/server.js +28 -28
- package/dist/dashboard/{service-0hbSVeHz.js → service-BLb_CyPL.js} +6 -6
- package/dist/dashboard/{service-0hbSVeHz.js.map → service-BLb_CyPL.js.map} +1 -1
- package/dist/dashboard/{specialists-C2ZVszIW.js → specialists-DocAK-7A.js} +5 -5
- package/dist/dashboard/{specialists-C2ZVszIW.js.map → specialists-DocAK-7A.js.map} +1 -1
- package/dist/dashboard/{specialists-DhLEuxVX.js → specialists-xXcJHAyg.js} +1 -1
- package/dist/dashboard/{stall-sweeper-o4-q0vqY.js → stall-sweeper-DOyIfeLR.js} +2 -2
- package/dist/dashboard/{stall-sweeper-o4-q0vqY.js.map → stall-sweeper-DOyIfeLR.js.map} +1 -1
- package/dist/dashboard/{uat-train-fK0FbCx7.js → uat-train-COSy-vEX.js} +1 -1
- package/dist/dashboard/{uat-train-CSOval9z.js → uat-train-DVUNW03P.js} +0 -0
- package/dist/dashboard/{uat-train-CSOval9z.js.map → uat-train-DVUNW03P.js.map} +1 -1
- package/dist/dashboard/{workflows-BnNc1QlT.js → workflows-BYwR2aIN.js} +2 -2
- package/dist/dashboard/{workflows-BnNc1QlT.js.map → workflows-BYwR2aIN.js.map} +1 -1
- package/dist/dashboard/workflows-Cf4LAnhg.js +2 -0
- package/dist/dashboard/{workspaces-CqeaoIK6.js → workspaces-BlpbOPZ7.js} +11 -11
- package/dist/dashboard/{workspaces-CqeaoIK6.js.map → workspaces-BlpbOPZ7.js.map} +1 -1
- package/dist/{lifecycle-Bhu9mEsV.js → lifecycle-BhAciIXh.js} +1 -1
- package/dist/{lifecycle-CMRdm3mR.js → lifecycle-D8m3w850.js} +2 -2
- package/dist/{lifecycle-CMRdm3mR.js.map → lifecycle-D8m3w850.js.map} +1 -1
- package/dist/{merge-agent-DQ7DOXE2.js → merge-agent-CUnIrr8R.js} +2 -2
- package/dist/{merge-agent-DQ7DOXE2.js.map → merge-agent-CUnIrr8R.js.map} +1 -1
- package/dist/{merge-agent-D33x_y_T.js → merge-agent-DwfVId_E.js} +1 -1
- package/package.json +1 -1
- package/dist/build-info-B9bbKbXz.js +0 -2
- package/dist/dashboard/build-info-CrBYH-L2.js +0 -2
- package/dist/dashboard/merge-ops-C9wpFz1G.js +0 -2
- package/dist/dashboard/workflows-C6MkcBR_.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specialists-C2ZVszIW.js","names":["yaml","constantTimeTokenEqual","createHash","pathExists","getAgentWorkspace","execFileAsync","execAsync","execFileAsync","readJsonBody","getAgentWorkspaceShared","getAgentJsonlPathShared","getAgentPendingQuestionsShared","execAsync","readJsonBody","randomUUID","execAsync","execFileAsync","randomUUID","randomUUID","httpRequest","Result","readJsonBody","getAgentJsonlPath","readJsonBody","readJsonBody","readJsonBody","readJsonBody","execAsync","execFileAsync","readJsonBody","loadWorkspaceMetadataFn","execAsync","setReviewStatusBase"],"sources":["../../src/dashboard/server/http-helpers.ts","../../src/lib/default-cwd.ts","../../src/dashboard/server/routes/http-handler.ts","../../src/dashboard/server/routes/origin-validation.ts","../../src/dashboard/server/services/pending-respawn.ts","../../src/dashboard/server/routes/dashboard-auth.ts","../../src/dashboard/server/routes/jsonl-resolver.ts","../../src/lib/pan-cli-invocation.ts","../../src/lib/work-agent-lifecycle.ts","../../src/dashboard/server/services/git-info.ts","../../src/lib/composer-commands/executors.ts","../../src/lib/composer-commands/detached.ts","../../src/dashboard/server/routes/agents/shared.ts","../../src/dashboard/server/routes/agents/listing.ts","../../src/dashboard/server/routes/agents/conversation.ts","../../src/lib/composer-commands/confirmations.ts","../../src/lib/composer-commands/parser.ts","../../src/lib/composer-commands/policy.ts","../../src/lib/composer-commands/router.ts","../../src/dashboard/server/routes/agents/messaging.ts","../../src/lib/runtimes/conversation-control.ts","../../src/lib/ohmypi-codex-auth.ts","../../src/dashboard/server/services/conversation-attachments.ts","../../src/lib/overdeck/resume-contract-delivery.ts","../../src/lib/overdeck/conversation-runtime.ts","../../src/lib/overdeck/conversation-delivery.ts","../../src/lib/session-pane-choice.ts","../../src/lib/channels/permission-payload.ts","../../src/dashboard/server/routes/agent-permissions.ts","../../src/dashboard/server/routes/agents/permissions.ts","../../src/dashboard/server/services/agent-event-utils.ts","../../src/dashboard/server/routes/agents/runtime-events.ts","../../src/dashboard/server/services/agent-projection.ts","../../src/dashboard/server/routes/agents/lifecycle-stop.ts","../../src/dashboard/server/routes/agents/lifecycle-restart.ts","../../src/dashboard/server/routes/agents/control.ts","../../src/lib/orders/eligibility.ts","../../src/lib/orders/dispatch-gate.ts","../../src/lib/orders/dispatch-reservation.ts","../../src/lib/xbrief/auto-synthesize.ts","../../src/dashboard/server/routes/agents/spawn-helpers.ts","../../src/dashboard/server/routes/agents/spawn.ts","../../src/dashboard/server/routes/agents.ts","../../src/dashboard/server/routes/tiered-inspect-escalation.ts","../../src/dashboard/server/routes/specialists/shared.ts","../../src/dashboard/server/routes/specialists/legacy-routes.ts","../../src/dashboard/server/routes/specialists/project-routes.ts","../../src/dashboard/server/routes/specialists.ts"],"sourcesContent":["/**\n * Workaround for Effect 4.0.0-beta.45 bug where HttpServerResponse.json()\n * creates a response without a proper body._tag, causing NodeHttpServer to crash\n * with \"undefined is not an object (evaluating 'body._tag')\".\n * \n * HttpServerResponse.text() works fine, so we use it with manual JSON.stringify.\n * Remove this workaround when Effect stabilizes HttpServerResponse.json().\n */\nimport { HttpServerResponse } from 'effect/unstable/http';\n\nexport function jsonResponse(\n data: unknown,\n options?: number | { status?: number; headers?: Record<string, string> },\n): HttpServerResponse.HttpServerResponse {\n return jsonStringResponse(JSON.stringify(data), options);\n}\n\nexport function jsonStringResponse(\n json: string,\n options?: number | { status?: number; headers?: Record<string, string> },\n): HttpServerResponse.HttpServerResponse {\n const opts = typeof options === 'number' ? { status: options } : options;\n return HttpServerResponse.text(\n json,\n {\n status: opts?.status ?? 200,\n contentType: 'application/json',\n headers: opts?.headers,\n }\n );\n}\n","import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Resolve the default cwd used when no project is selected — the \"devroot\"\n * fallback. Prefers `~/Projects` if it exists, otherwise the user's home\n * directory.\n *\n * Replaces the deprecated `sync.devroot` config (PAN-1201) for the runtime\n * code paths that still need a sensible default working directory:\n *\n * - New conversations created without a `projectKey` (conversations.ts)\n * - The global editor-launcher in the dashboard top bar (PanOpenInPicker)\n */\nexport function getDefaultCwd(): string {\n const candidate = join(homedir(), 'Projects');\n return existsSync(candidate) ? candidate : homedir();\n}\n","/**\n * httpHandler — typed error-to-HTTP mapping wrapper (PAN-470)\n *\n * Wraps an Effect route body and catches all typed TaggedErrors,\n * mapping them to appropriate HTTP status codes. Unknown/unhandled\n * errors fall through as 500 Internal Server Error.\n *\n * Usage:\n * HttpRouter.add('GET', '/api/foo', httpHandler(Effect.gen(function* () {\n * const result = yield* someService.doThing();\n * return jsonResponse(result);\n * })))\n */\n\nimport { Cause, Effect, Option } from 'effect';\nimport { HttpServerRequest, HttpServerResponse as HttpServerResponseModule } from 'effect/unstable/http';\n\ntype HttpServerResponse = HttpServerResponseModule.HttpServerResponse;\n\nimport { jsonResponse } from '../http-helpers.js';\nimport {\n AgentAlreadyRunning,\n AgentStartError,\n IssueNotFound,\n PlanEmpty,\n RateLimited,\n TasksNotInitialized,\n TrackerApiError,\n TrackerNotConfigured,\n WorkspaceCreateError,\n WorkspaceNotFound,\n} from '../services/typed-errors.js';\n\n// Burst-control for the opaque-defect log: log the first OPAQUE_FULL_BURST\n// occurrences in full, then once per OPAQUE_BURST_INTERVAL_MS, emit a summary\n// with the count of suppressed events. This stops a runaway null-defect source\n// from filling the log + saturating the event loop with formatting work, while\n// preserving full detail for the first few occurrences so diagnosis is possible.\nconst OPAQUE_FULL_BURST = 5;\nconst OPAQUE_BURST_INTERVAL_MS = 30_000;\nlet opaqueLogged = 0;\nlet opaqueSuppressed = 0;\nlet opaqueLastSummaryAt = 0;\n\n/**\n * Wraps a route effect with typed error-to-HTTP status mapping.\n *\n * Error → Status mapping:\n * IssueNotFound, WorkspaceNotFound → 404 Not Found\n * TrackerNotConfigured → 503 Service Unavailable\n * RateLimited → 429 Too Many Requests\n * AgentAlreadyRunning → 409 Conflict\n * PlanEmpty, TasksNotInitialized → 422 Unprocessable Entity\n * TrackerApiError → 502 Bad Gateway\n * WorkspaceCreateError, AgentStartError → 500 Internal Server Error\n * Unknown errors → 500 Internal Server Error\n */\nexport function httpHandler<R, E>(\n effect: Effect.Effect<HttpServerResponse, E, R>\n): Effect.Effect<HttpServerResponse, never, R> {\n // Capture the registration call site (where the route called httpHandler(...))\n // so when a fiber emits a typed Fail with no stack — e.g. Effect.fail(null) —\n // we can still tell which route module the handler belongs to.\n const registeredAt = (new Error('httpHandler registered')).stack ?? '<no stack>';\n type AllKnownErrors =\n | IssueNotFound\n | WorkspaceNotFound\n | TrackerNotConfigured\n | RateLimited\n | AgentAlreadyRunning\n | PlanEmpty\n | TasksNotInitialized\n | TrackerApiError\n | WorkspaceCreateError\n | AgentStartError;\n return (effect as Effect.Effect<HttpServerResponse, AllKnownErrors, R>).pipe(\n Effect.catchTag('IssueNotFound', (err: IssueNotFound) =>\n Effect.succeed(jsonResponse({ error: `Issue not found: ${err.id}` }, { status: 404 }))\n ),\n Effect.catchTag('WorkspaceNotFound', (err: WorkspaceNotFound) =>\n Effect.succeed(jsonResponse({ error: `Workspace not found: ${err.id}` }, { status: 404 }))\n ),\n Effect.catchTag('TrackerNotConfigured', (err: TrackerNotConfigured) =>\n Effect.succeed(jsonResponse({ error: `Tracker not configured: ${err.tracker}` }, { status: 503 }))\n ),\n Effect.catchTag('RateLimited', (err: RateLimited) =>\n Effect.succeed(\n jsonResponse({ error: 'Rate limited', retryAfter: err.retryAfter }, { status: 429 })\n )\n ),\n Effect.catchTag('AgentAlreadyRunning', (err: AgentAlreadyRunning) =>\n Effect.succeed(jsonResponse({ error: `Agent already running for issue: ${err.id}` }, { status: 409 }))\n ),\n Effect.catchTag('PlanEmpty', (err: PlanEmpty) =>\n Effect.succeed(jsonResponse({ error: `Plan is empty for issue: ${err.id}` }, { status: 422 }))\n ),\n Effect.catchTag('TasksNotInitialized', (err: TasksNotInitialized) =>\n Effect.succeed(jsonResponse({ error: `Tasks not initialized for workspace: ${err.workspace}` }, { status: 422 }))\n ),\n Effect.catchTag('TrackerApiError', (err: TrackerApiError) =>\n Effect.succeed(\n jsonResponse({ error: `Tracker API error (${err.tracker}): ${err.message}` }, { status: 502 })\n )\n ),\n Effect.catchTag('WorkspaceCreateError', (err: WorkspaceCreateError) =>\n Effect.succeed(\n jsonResponse({ error: `Workspace creation failed for ${err.id}: ${err.message}` }, { status: 500 })\n )\n ),\n Effect.catchTag('AgentStartError', (err: AgentStartError) =>\n Effect.succeed(\n jsonResponse({ error: `Agent start failed for ${err.id}: ${err.message}` }, { status: 500 })\n )\n ),\n Effect.catchCause((cause) =>\n Effect.gen(function* () {\n // Interrupt-only causes mean the consumer (browser tab, abort signal) cancelled\n // the request. That isn't a server error — silence it instead of spamming logs.\n if (Cause.hasInterruptsOnly(cause)) {\n return jsonResponse({ error: 'Request cancelled' }, { status: 499 });\n }\n const error = Cause.squash(cause);\n const message = error instanceof Error ? error.message : 'Internal server error';\n const reqOption = yield* Effect.serviceOption(HttpServerRequest.HttpServerRequest);\n const route = Option.match(reqOption, {\n onNone: () => 'unknown',\n onSome: (req) => `${req.method} ${req.url}`,\n });\n // When the squashed defect is null/undefined or a non-Error value the pretty\n // printer produces `Error: Unknown error: null` with no stack, which leaves\n // the source impossible to locate. Capture extra context for those cases:\n // the JSON shape of the cause (failures + defects), the typeof, and a stack\n // sampled from this catch site so we can at least see what caller chained\n // into httpHandler.\n const isOpaque = !(error instanceof Error);\n if (isOpaque) {\n if (opaqueLogged < OPAQUE_FULL_BURST) {\n opaqueLogged += 1;\n let causeJson: string;\n try {\n causeJson = JSON.stringify(cause, (_k, v) => (typeof v === 'function' ? '[fn]' : v), 2);\n } catch {\n causeJson = '<cause not serializable>';\n }\n console.error(\n `[httpHandler] Opaque non-Error defect on ${route} (#${opaqueLogged}): typeof=${typeof error} value=${String(error)}\\n` +\n `cause=${causeJson}\\n` +\n `registeredAt=${registeredAt}`,\n );\n } else {\n opaqueSuppressed += 1;\n const now = Date.now();\n if (now - opaqueLastSummaryAt >= OPAQUE_BURST_INTERVAL_MS) {\n opaqueLastSummaryAt = now;\n console.error(\n `[httpHandler] Opaque non-Error defect on ${route} — ${opaqueSuppressed} more suppressed since last summary ` +\n `(set DEBUG_OPAQUE_DEFECTS=1 to see all). value=${String(error)}`,\n );\n opaqueSuppressed = 0;\n }\n }\n } else {\n console.error(`[httpHandler] Unhandled error on ${route}:\\n${Cause.pretty(cause)}`);\n }\n return jsonResponse({ error: message }, { status: 500 });\n }),\n )\n ) as Effect.Effect<HttpServerResponse, never, R>;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport yaml from 'js-yaml';\nimport type { HttpServerRequest } from 'effect/unstable/http';\n\nexport type HeaderMap = Record<string, string | string[] | undefined>;\n\nlet cachedTrustedOrigins: string[] | undefined;\n\nfunction addTrustedOrigin(origins: Set<string>, raw: string | undefined): void {\n if (!raw) return;\n const normalized = normalizeOrigin(raw.trim());\n if (normalized) origins.add(normalized);\n}\n\ninterface TraefikYamlConfig {\n enabled: boolean;\n domain: string;\n}\n\n/**\n * Last-resort origin config for launches that carry no origin env at all\n * (manual `node dist/...`, ad-hoc re-exec). `pan up` builds its launch env from\n * this same config.yaml, so reading it here keeps every launch path trusting the\n * same Traefik domain instead of 403ing /ws/* into a \"Reconnecting\" loop.\n */\nfunction readTraefikConfigFromYaml(): TraefikYamlConfig | null {\n try {\n const home = process.env['OVERDECK_HOME'] || join(homedir(), '.overdeck');\n const configPath = join(home, 'config.yaml');\n if (!existsSync(configPath)) return null;\n const parsed = yaml.load(readFileSync(configPath, 'utf8')) as\n | { traefik?: { enabled?: unknown; domain?: unknown } }\n | null\n | undefined;\n const traefik = parsed?.traefik;\n if (!traefik || typeof traefik !== 'object') return null;\n if (typeof traefik.domain !== 'string' || traefik.domain.trim() === '') return null;\n return { enabled: traefik.enabled === true, domain: traefik.domain.trim() };\n } catch {\n return null; // unreadable/invalid config → env-only behavior, same as before\n }\n}\n\nexport function getTrustedOrigins(): string[] {\n if (cachedTrustedOrigins !== undefined) {\n return cachedTrustedOrigins;\n }\n const port = parseInt(process.env['API_PORT'] ?? process.env['PORT'] ?? '3011', 10);\n const dashboardUrl = process.env['DASHBOARD_URL'] ?? `http://localhost:${port}`;\n const origins = new Set<string>();\n addTrustedOrigin(origins, dashboardUrl);\n addTrustedOrigin(origins, `http://localhost:${port}`);\n addTrustedOrigin(origins, `http://127.0.0.1:${port}`);\n addTrustedOrigin(origins, 'http://localhost:3010');\n addTrustedOrigin(origins, 'http://127.0.0.1:3010');\n\n const trustedOrigins = process.env['OVERDECK_TRUSTED_ORIGINS'];\n for (const origin of trustedOrigins?.split(',') ?? []) {\n addTrustedOrigin(origins, origin);\n }\n\n const traefikDomain = process.env['OVERDECK_TRAEFIK_DOMAIN'] ?? process.env['TRAEFIK_DOMAIN'];\n if (process.env['OVERDECK_TRAEFIK_ENABLED'] === '1' && traefikDomain) {\n addTrustedOrigin(origins, `https://${traefikDomain}`);\n }\n\n // Bare-launch fallback: when the launcher provided NO origin configuration at\n // all, trust the domain from the operator's config.yaml. Any explicit env\n // (even an explicit disable) wins over the file.\n const hasEnvOriginConfig =\n process.env['OVERDECK_TRAEFIK_ENABLED'] !== undefined ||\n traefikDomain !== undefined ||\n process.env['OVERDECK_TRUSTED_ORIGINS'] !== undefined;\n if (!hasEnvOriginConfig) {\n const fromYaml = readTraefikConfigFromYaml();\n if (fromYaml?.enabled) {\n addTrustedOrigin(origins, `https://${fromYaml.domain}`);\n }\n }\n\n if (process.env['NODE_ENV'] === 'development') {\n addTrustedOrigin(origins, 'http://localhost:3000');\n addTrustedOrigin(origins, 'http://127.0.0.1:3000');\n }\n cachedTrustedOrigins = Array.from(origins);\n return cachedTrustedOrigins;\n}\n\nexport function normalizeOrigin(origin: string): string | null {\n try {\n const url = new URL(origin);\n return `${url.protocol}//${url.host}`;\n } catch {\n return null;\n }\n}\n\nexport function getHeaderFromMap(headers: HeaderMap, name: string): string | undefined {\n const direct = headers[name];\n if (Array.isArray(direct)) return direct[0];\n if (direct) return direct;\n\n const lowerName = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() !== lowerName) continue;\n return Array.isArray(value) ? value[0] : value;\n }\n return undefined;\n}\n\nexport function validateOriginHeaders(\n headers: HeaderMap,\n method: string,\n): { ok: true } | { ok: false; error: string } {\n const origin = getHeaderFromMap(headers, 'origin');\n const referer = getHeaderFromMap(headers, 'referer');\n const trusted = getTrustedOrigins();\n\n if (!origin && !referer) {\n const upperMethod = method.toUpperCase();\n if (upperMethod === 'GET' || upperMethod === 'HEAD') {\n return { ok: true };\n }\n return { ok: false, error: 'Missing origin' };\n }\n\n if (origin) {\n const normalized = normalizeOrigin(origin);\n if (normalized && trusted.includes(normalized)) {\n return { ok: true };\n }\n return { ok: false, error: 'Invalid origin' };\n }\n\n if (!referer) {\n return { ok: false, error: 'Invalid referer' };\n }\n const normalized = normalizeOrigin(referer);\n if (normalized && trusted.includes(normalized)) {\n return { ok: true };\n }\n return { ok: false, error: 'Invalid referer' };\n}\n\nexport function validateOrigin(\n request: HttpServerRequest.HttpServerRequest,\n): { ok: true } | { ok: false; error: string } {\n return validateOriginHeaders(request.headers as HeaderMap, request.method);\n}\n\nexport function _resetTrustedOriginsForTests(): void {\n cachedTrustedOrigins = undefined;\n}\n\n/**\n * Origin trust check used by raw WebSocket upgrade paths (autopreso, voice).\n * Only explicitly configured dashboard origins are trusted; the request Host\n * header is attacker-controlled and must not influence this decision.\n */\nexport function isTrustedOriginForHost(\n origin: string | undefined,\n _host: string | string[] | undefined,\n): boolean {\n if (!origin) return false;\n const normalized = normalizeOrigin(origin);\n if (!normalized) return false;\n return getTrustedOrigins().includes(normalized);\n}\n","/**\n * Tracks tmux sessions that are mid-respawn so that `/ws/terminal`\n * reconnects landing in the kill→spawn gap don't get a fatal 4404.\n *\n * Background: switch-model, resume, and restart-all all do\n *\n * await killSessionAsync(name) // old tmux dies, PTY exits,\n * // ws-terminal closes clients with 1000\n * await spawnConversationSession(...) // new tmux session with same name\n * await waitForTmuxSession(name)\n * await waitForReadySignal(name, 30) // up to 30s\n *\n * The terminal frontend (`XTerminal.tsx`) reconnects on close 1000 with\n * exponential backoff (1s, 2s, 4s, 8s, 16s). Every reconnect re-enters\n * `ws-terminal.ts`, which checks the tmux session list upfront and\n * `close(4404, 'session-not-found')` if the name isn't there yet.\n * 4404 is treated as fatal on the client (no retry — the session is\n * presumed gone), so the panel sticks on \"Could not reconnect\" even\n * after the respawn completes a moment later.\n *\n * This registry lets `ws-terminal.ts` distinguish \"transient absence\n * during respawn\" from \"session is actually gone\". Respawn sites mark\n * the name with `markRespawnPending()` for the duration of their kill+\n * spawn block; ws-terminal calls `waitForSessionRespawn()` instead of\n * 4404-ing immediately when the session is missing AND\n * `isRespawnPending()` is true.\n */\n\nimport { Effect } from 'effect';\nimport { sessionExists } from '../../../lib/tmux.js';\n\nconst pendingRespawns = new Set<string>();\n\n/**\n * Mark a tmux session as mid-respawn. The returned `done()` must run in\n * a `finally` so the marker is cleared even if the respawn throws.\n */\nexport function markRespawnPending(sessionName: string): { done: () => void } {\n pendingRespawns.add(sessionName);\n return {\n done: () => {\n pendingRespawns.delete(sessionName);\n },\n };\n}\n\n/** True if a respawn is currently in progress for this session name. */\nexport function isRespawnPending(sessionName: string): boolean {\n return pendingRespawns.has(sessionName);\n}\n\nconst POLL_INTERVAL_MS = 200;\n\n/**\n * Poll for the session to come back, up to `timeoutMs`. Returns whether\n * it did. Bails early once `pendingRespawns` no longer contains the name\n * (the wrapping respawn block finished, success or failure) and the\n * session still isn't there — at that point the absence is real.\n *\n * `timeoutMs` should comfortably exceed the longest respawn window. The\n * dominant cost is `waitForReadySignal`'s 30s ceiling in the conversation\n * routes, so 35s is the right default for those callers.\n */\nexport async function waitForSessionRespawn(\n sessionName: string,\n timeoutMs: number,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await Effect.runPromise(sessionExists(sessionName))) return true;\n if (!pendingRespawns.has(sessionName)) {\n return Effect.runPromise(sessionExists(sessionName));\n }\n await new Promise<void>((r) => setTimeout(r, POLL_INTERVAL_MS));\n }\n return Effect.runPromise(sessionExists(sessionName));\n}\n","import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { networkInterfaces, type NetworkInterfaceInfo } from 'node:os';\n\nimport { Option } from 'effect';\nimport { HttpServerRequest, HttpServerResponse } from 'effect/unstable/http';\n\nimport { getInternalTokenSync, INTERNAL_TOKEN_HEADER } from '../../../lib/internal-token.js';\nimport { jsonResponse } from '../http-helpers.js';\nimport { getHeaderFromMap, getTrustedOrigins, normalizeOrigin, type HeaderMap } from './origin-validation.js';\n\nexport const DASHBOARD_SESSION_COOKIE = 'overdeck_session';\nexport const DASHBOARD_CSRF_HEADER = 'x-overdeck-csrf-token';\n// Session cookie lifetime. Without Max-Age the cookie was a *session* cookie that\n// died when the browser fully closed — so a reopened tab on a trusted origin had\n// no cookie, its mint 401'd, and every mutation failed with \"CSRF token\n// unavailable\" until the operator re-bootstrapped via the one-time URL token.\n// A 30-day rolling expiry means the operator bootstraps once per browser.\nconst DASHBOARD_SESSION_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;\n\nlet browserSessionToken: string | undefined;\nlet browserCsrfToken: string | undefined;\n\nexport function _resetDashboardSessionTokenForTests(): void {\n browserSessionToken = undefined;\n browserCsrfToken = undefined;\n}\n\nfunction getDashboardSessionToken(): string {\n if (browserSessionToken) return browserSessionToken;\n const override = process.env['OVERDECK_DASHBOARD_SESSION_TOKEN'];\n if (override) {\n browserSessionToken = override;\n return browserSessionToken;\n }\n // Derive a stable session token from the persisted internal token so the\n // browser's session cookie survives dashboard restarts. Without this the\n // token was random per-process, so every restart invalidated open tabs'\n // cookies — and a plain refresh could no longer re-mint, since the one-time\n // bootstrap hash token is already consumed on first load. Falls back to a\n // random token only when no internal token is configured, in which case the\n // auth gate already 503s before the session token is ever consulted.\n const internal = getInternalTokenSync();\n browserSessionToken = internal\n ? createHmac('sha256', internal).update('overdeck-dashboard-session-v1').digest('base64url')\n : randomBytes(32).toString('base64url');\n return browserSessionToken;\n}\n\nexport function dashboardCsrfToken(): string {\n if (browserCsrfToken) return browserCsrfToken;\n const override = process.env['OVERDECK_DASHBOARD_CSRF_TOKEN'];\n if (override) {\n browserCsrfToken = override;\n return browserCsrfToken;\n }\n // Same restart-survival derivation as the session token above: the frontend\n // caches this token once per page load (wsTransport session mint), so a\n // random-per-process value 403'd every mutation from open tabs after each\n // dashboard restart — and the flywheel restarts the dashboard on every\n // post-merge deploy. Distinct context string keeps it independent of the\n // session token.\n const internal = getInternalTokenSync();\n browserCsrfToken = internal\n ? createHmac('sha256', internal).update('overdeck-dashboard-csrf-v1').digest('base64url')\n : randomBytes(32).toString('base64url');\n return browserCsrfToken;\n}\n\nfunction getHeader(\n request: HttpServerRequest.HttpServerRequest,\n name: string,\n): string | undefined {\n return getHeaderFromMap(request.headers as HeaderMap, name);\n}\n\nfunction constantTimeTokenEqual(provided: string | undefined, expected: string): boolean {\n if (!provided) return false;\n const providedHash = createHash('sha256').update(provided).digest();\n const expectedHash = createHash('sha256').update(expected).digest();\n return timingSafeEqual(providedHash, expectedHash);\n}\n\nfunction cookieValue(cookieHeader: string | undefined, name: string): string | undefined {\n if (!cookieHeader) return undefined;\n for (const part of cookieHeader.split(';')) {\n const [rawKey, ...rawValue] = part.trim().split('=');\n if (rawKey !== name) continue;\n try {\n return decodeURIComponent(rawValue.join('='));\n } catch {\n return undefined;\n }\n }\n return undefined;\n}\n\nexport function dashboardSessionCookieHeader(options: { secure?: boolean } = {}): string {\n const secure = options.secure ? '; Secure' : '';\n return `${DASHBOARD_SESSION_COOKIE}=${encodeURIComponent(getDashboardSessionToken())}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${DASHBOARD_SESSION_MAX_AGE_SECONDS}${secure}`;\n}\n\nexport function hasDashboardInternalTokenHeaders(headers: HeaderMap): boolean {\n const expected = getInternalTokenSync();\n if (!expected) return false;\n\n const internalHeader = getHeaderFromMap(headers, INTERNAL_TOKEN_HEADER);\n if (constantTimeTokenEqual(internalHeader, expected)) return true;\n\n const authorization = getHeaderFromMap(headers, 'authorization');\n if (authorization) {\n const [scheme, token] = authorization.split(/\\s+/);\n if (scheme?.toLowerCase() === 'bearer' && constantTimeTokenEqual(token, expected)) return true;\n }\n\n return false;\n}\n\nexport function hasDashboardInternalToken(request: HttpServerRequest.HttpServerRequest): boolean {\n return hasDashboardInternalTokenHeaders(request.headers as HeaderMap);\n}\n\nexport function hasDashboardAuthHeaders(headers: HeaderMap): boolean {\n return hasDashboardInternalTokenHeaders(headers) || constantTimeTokenEqual(cookieValue(getHeaderFromMap(headers, 'cookie'), DASHBOARD_SESSION_COOKIE), getDashboardSessionToken());\n}\n\nexport function hasDashboardAuth(request: HttpServerRequest.HttpServerRequest): boolean {\n return hasDashboardAuthHeaders(request.headers as HeaderMap);\n}\n\nfunction isJsonContentType(headers: HeaderMap): boolean {\n const contentType = getHeaderFromMap(headers, 'content-type');\n if (!contentType) return false;\n const [mime] = contentType.toLowerCase().split(';');\n return mime.trim() === 'application/json';\n}\n\nfunction hasTrustedExactOrigin(headers: HeaderMap): boolean {\n const origin = getHeaderFromMap(headers, 'origin');\n if (!origin) return false;\n const normalized = normalizeOrigin(origin);\n return !!normalized && getTrustedOrigins().includes(normalized);\n}\n\nfunction hasValidCsrfToken(headers: HeaderMap): boolean {\n return constantTimeTokenEqual(getHeaderFromMap(headers, DASHBOARD_CSRF_HEADER), dashboardCsrfToken());\n}\n\nexport function rejectUnsafeDashboardMutationRequest(\n request: HttpServerRequest.HttpServerRequest,\n): HttpServerResponse.HttpServerResponse | null {\n const authError = rejectUnauthorizedDashboardRequest(request);\n if (authError) return authError;\n\n const headers = request.headers as HeaderMap;\n if (!isJsonContentType(headers)) {\n return jsonResponse({ error: 'Content-Type must be application/json' }, { status: 400 });\n }\n\n if (hasDashboardInternalToken(request)) return null;\n\n const origin = getHeaderFromMap(headers, 'origin');\n if (origin && !hasTrustedExactOrigin(headers)) {\n return jsonResponse({ error: 'Invalid origin' }, { status: 403 });\n }\n if (!hasValidCsrfToken(headers)) {\n return jsonResponse({ error: 'Invalid CSRF token' }, { status: 403 });\n }\n return null;\n}\n\nfunction ipv4ToInt(value: string): number | null {\n const parts = value.split('.');\n if (parts.length !== 4) return null;\n let result = 0;\n for (const part of parts) {\n const octet = Number(part);\n if (!Number.isInteger(octet) || octet < 0 || octet > 255) return null;\n result = ((result << 8) | octet) >>> 0;\n }\n return result;\n}\n\n/** RFC1918 private ranges — Docker bridges always fall inside one of these. */\nfunction isRfc1918(ip: number): boolean {\n return (\n (ip & 0xff000000) >>> 0 === 0x0a000000 || // 10.0.0.0/8\n (ip & 0xfff00000) >>> 0 === 0xac100000 || // 172.16.0.0/12\n (ip & 0xffff0000) >>> 0 === 0xc0a80000 // 192.168.0.0/16\n );\n}\n\n/**\n * True when `addr` is the IP of a container on one of THIS host's Docker bridge\n * interfaces (docker0 / br-*). Such a peer can only be a process running on this\n * machine — our own host-local Traefik fronting overdeck.localhost, or another\n * Overdeck container — never a LAN client (a direct LAN hit to the 0.0.0.0\n * API port arrives with a LAN peer that is not inside any Docker bridge subnet).\n *\n * Pure for testability: the interface map is injected. We only ever trust\n * RFC1918 bridge subnets, so a misconfigured public-range br-* cannot widen\n * trust beyond the local Docker fabric.\n */\nexport function peerIsHostLocalDockerBridge(\n addr: string,\n interfaces: NodeJS.Dict<NetworkInterfaceInfo[]>,\n): boolean {\n const peer = ipv4ToInt(addr);\n if (peer === null) return false;\n for (const [name, addrs] of Object.entries(interfaces)) {\n if (name !== 'docker0' && !name.startsWith('br-')) continue;\n for (const info of addrs ?? []) {\n if (info.family !== 'IPv4') continue;\n const ifaceIp = ipv4ToInt(info.address);\n const mask = ipv4ToInt(info.netmask);\n if (ifaceIp === null || mask === null) continue;\n const network = (ifaceIp & mask) >>> 0;\n if (!isRfc1918(network)) continue;\n if (((peer & mask) >>> 0) === network) return true;\n }\n }\n return false;\n}\n\n/**\n * True when `addr` shares an RFC1918 subnet with one of THIS process's own\n * network interfaces. This is the in-container counterpart of\n * peerIsHostLocalDockerBridge: inside a container the Docker bridge is\n * invisible — the container sees only its veth endpoints (eth0/eth1), so the\n * docker0/br-* name filter never matches and the host-local Traefik fronting\n * the stack 401'd every session mint (first hit live on the PAN-1737 UAT\n * stack). A peer inside one of the container's attached Docker-network\n * subnets can only be another container on those networks — the server\n * publishes no host port (Traefik labels only), so no LAN client can ever\n * present such an address. Only consulted when running inside a container.\n */\nexport function peerIsLocalContainerNetwork(\n addr: string,\n interfaces: NodeJS.Dict<NetworkInterfaceInfo[]>,\n): boolean {\n const peer = ipv4ToInt(addr);\n if (peer === null) return false;\n for (const addrs of Object.values(interfaces)) {\n for (const info of addrs ?? []) {\n if (info.family !== 'IPv4' || info.internal) continue;\n const ifaceIp = ipv4ToInt(info.address);\n const mask = ipv4ToInt(info.netmask);\n if (ifaceIp === null || mask === null) continue;\n const network = (ifaceIp & mask) >>> 0;\n if (!isRfc1918(network)) continue;\n if (((peer & mask) >>> 0) === network) return true;\n }\n }\n return false;\n}\n\n/** Docker creates /.dockerenv in every container — stable in-container signal. */\nconst runningInContainer = existsSync('/.dockerenv');\n\n/**\n * A request we trust to mint a session because it originated from this machine —\n * the local browser hitting the dashboard directly (127.0.0.1), or the\n * host-local Traefik that fronts overdeck.localhost. In Docker, that Traefik reaches\n * the host server from a Docker-bridge IP (e.g. 172.18.0.2), NOT 127.0.0.1, so\n * a literal-loopback check alone silently breaks the zero-step bootstrap for\n * every overdeck.localhost user — their session mint 401s and no cookie is ever set.\n *\n * This is the auto-bootstrap that removes the manual one-time #overdeck_token\n * step: any browser on overdeck.localhost gets a session with no user action. It is\n * NOT a security downgrade for the LAN — the raw API ports bind 0.0.0.0, but a\n * direct LAN hit has a non-loopback, non-Docker-bridge peer and is rejected. We\n * read ONLY the real TCP peer (request.remoteAddress), never X-Forwarded-For,\n * which a caller could spoof.\n */\nfunction isLoopbackPeer(request: HttpServerRequest.HttpServerRequest): boolean {\n const remoteAddress = (request as { remoteAddress?: unknown }).remoteAddress;\n const raw = remoteAddress && typeof remoteAddress === 'object' && '_tag' in remoteAddress\n ? Option.getOrElse(remoteAddress as Option.Option<string>, () => '')\n : typeof remoteAddress === 'string'\n ? remoteAddress\n : '';\n if (!raw) return false;\n // Node reports dual-stack IPv4 peers as IPv4-mapped IPv6 (::ffff:a.b.c.d).\n const addr = raw.startsWith('::ffff:') ? raw.slice('::ffff:'.length) : raw;\n if (addr === '127.0.0.1' || addr === '::1') return true;\n if (peerIsHostLocalDockerBridge(addr, networkInterfaces())) return true;\n return runningInContainer && peerIsLocalContainerNetwork(addr, networkInterfaces());\n}\n\nexport function rejectUnauthorizedDashboardSessionMintRequest(\n request: HttpServerRequest.HttpServerRequest,\n): HttpServerResponse.HttpServerResponse | null {\n const expected = getInternalTokenSync();\n if (!expected) {\n return jsonResponse({ error: 'dashboard session token not configured' }, { status: 503 });\n }\n if (!hasDashboardInternalToken(request) && !isLoopbackPeer(request)) {\n return jsonResponse({ error: 'unauthorized' }, { status: 401 });\n }\n return null;\n}\n\nexport function rejectUnauthorizedDashboardRequest(\n request: HttpServerRequest.HttpServerRequest,\n): HttpServerResponse.HttpServerResponse | null {\n const expected = getInternalTokenSync();\n if (!expected) {\n return jsonResponse({ error: 'dashboard session token not configured' }, { status: 503 });\n }\n if (!hasDashboardAuth(request)) {\n return jsonResponse({ error: 'unauthorized' }, { status: 401 });\n }\n return null;\n}\n","/**\n * JSONL transcript resolver for the Command Deck (PAN-830).\n *\n * Maps an agent ID (e.g. `agent-pan-830`, `planning-pan-830`, or a canonical\n * specialist tmux session name) to the agent's JSONL transcript file on disk.\n *\n * Codex agents (PAN-1805) write rollout JSONLs under the per-agent\n * `codex-home/sessions/` tree — resolution dispatches on the harness recorded\n * in state.json (thread-id fast path, then latest-rollout fallback).\n *\n * For claude-code agents the JSONL filename is the *Claude session ID* (a UUID\n * written by Claude Code itself), NOT the agent/tmux name. Lookup order:\n * 1. session.id — UUID pinned before fresh work-agent launch and updated\n * by auto-suspend/resume flows\n * 2. sessions.json — array of UUIDs the agent has used; the heartbeat hook\n * APPENDS new IDs and dedupes, so once a session has been\n * seen its array index never moves. After a `pan resume`\n * brings an older session back as the live one, the array\n * order no longer reflects time-recency. We disambiguate\n * across all listed IDs by JSONL mtime.\n * 3. runtime state — in-process mirror, populated by hooks\n * 4. agents registry — the DB row's session_id recorded at spawn; survives\n * janitor removal of the agent directory\n *\n * Async-only (fs/promises) because this code path runs inside the dashboard\n * server's event loop.\n */\nimport { access, readFile, stat, readdir } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { getAgentRuntimeState, getAgentStateSync } from '../../../lib/agents.js';\nimport { encodeClaudeProjectDir, getOverdeckHome } from '../../../lib/paths.js';\nimport { getAgentWorkspace } from '../../../lib/agent-enrichment.js';\nimport { getHarnessBehavior } from '../../../lib/runtimes/behavior.js';\nimport type { HarnessName } from '../../../lib/runtimes/types.js';\nimport { logAgentLifecycleSync } from '../../../lib/persistent-logger.js';\n\nexport interface ResolveJsonlPathOptions {\n /** Override the ~/.overdeck/agents directory (test hook). */\n agentsDirOverride?: string;\n /** Override the ~/.claude/projects directory (test hook). */\n claudeProjectsDirOverride?: string;\n /** Override the ~/.kimi-code directory (test hook). */\n kimiHomeOverride?: string;\n /**\n * Explicit workspace path for resolveKimiWirePath. Conversation rows have\n * no AgentState (readRecordedState/getAgentStateSync return nothing), so\n * dashboard conversation callers must supply conv.cwd directly instead of\n * relying on the agent-state lookup (PAN-1837 review fix).\n */\n workspaceOverride?: string;\n /** Override the runtime-state lookup (test hook). */\n getRuntimeStateAsync?: (agentId: string) => Promise<{ claudeSessionId?: string } | null>;\n /** Override forensic logging (test hook). */\n logDiagnostic?: (agentId: string, message: string) => void;\n}\n\n// Command Deck polling can resolve the same session repeatedly. Keep forensic\n// logging useful by writing only when the resolution outcome changes.\nconst transcriptResolutionSignatures = new Map<string, string>();\n\nfunction logTranscriptResolution(\n agentId: string,\n signature: string,\n message: string,\n opts: ResolveJsonlPathOptions,\n): void {\n const signatureKey = `${agentId}:${opts.agentsDirOverride ?? 'live'}`;\n if (transcriptResolutionSignatures.get(signatureKey) === signature) return;\n transcriptResolutionSignatures.set(signatureKey, signature);\n const logger = opts.logDiagnostic\n ?? (opts.agentsDirOverride ? undefined : logAgentLifecycleSync);\n logger?.(agentId, `transcript resolution: ${message}`);\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n return access(p).then(() => true, () => false);\n}\n\nasync function readOptional(p: string): Promise<string | null> {\n return readFile(p, 'utf-8').catch(() => null);\n}\n\nfunction behaviorForHarness(harness: string | null | undefined) {\n return getHarnessBehavior(harness as HarnessName | null | undefined);\n}\n\n/**\n * Pick the candidate UUID whose JSONL transcript has the most recent mtime.\n *\n * The heartbeat hook appends to sessions.json and dedupes — so once an ID is\n * recorded, its array index never moves even if a later resume makes it the\n * current session again. Choosing by JSONL mtime is the only reliable signal\n * for \"which session is actually live right now.\"\n *\n * Returns null if no candidate has a corresponding JSONL on disk. Falls back\n * to scanning every project dir if the workspace path can't be resolved\n * (covers specialist sessions whose workspace lives elsewhere).\n */\nasync function pickFreshestSessionId(\n candidates: ReadonlyArray<string>,\n agentId: string,\n opts: ResolveJsonlPathOptions,\n): Promise<string | null> {\n if (candidates.length === 0) return null;\n if (candidates.length === 1) return candidates[0]!;\n\n const projectsRoot = opts.claudeProjectsDirOverride ?? join(homedir(), '.claude', 'projects');\n\n // Fast path: derive the agent's workspace and look only in the matching\n // project dir. Avoids fanning out across every Claude project.\n let candidatePaths: Array<{ id: string; path: string }> = [];\n try {\n const workspace = await Effect.runPromise(getAgentWorkspace(agentId));\n if (workspace) {\n const projectDir = join(projectsRoot, encodeClaudeProjectDir(workspace));\n candidatePaths = candidates.map((id) => ({ id, path: join(projectDir, `${id}.jsonl`) }));\n }\n } catch { /* non-fatal — fall back to project-dir scan */ }\n\n // Slow path: scan all project dirs (specialist agents, multi-workspace cases).\n if (candidatePaths.length === 0) {\n try {\n const dirs = await readdir(projectsRoot);\n const SAFE_DIR = /^[a-zA-Z0-9_.-]+$/;\n for (const id of candidates) {\n for (const dir of dirs) {\n if (!SAFE_DIR.test(dir)) continue;\n candidatePaths.push({ id, path: join(projectsRoot, dir, `${id}.jsonl`) });\n }\n }\n } catch { /* fall through to no-mtime path */ }\n }\n\n // Stat each candidate path; pick the (id, mtime) with the newest mtime.\n let best: { id: string; mtimeMs: number } | null = null;\n for (const { id, path } of candidatePaths) {\n try {\n const s = await stat(path);\n if (!best || s.mtimeMs > best.mtimeMs) {\n best = { id, mtimeMs: s.mtimeMs };\n }\n } catch { /* missing file — skip */ }\n }\n\n // If no candidate has a JSONL on disk, fall back to the historical \"last\n // appended\" heuristic — the previous behavior. Better than returning null\n // for callers that only need a session ID, not necessarily a live JSONL.\n return best ? best.id : (candidates[candidates.length - 1] ?? null);\n}\n\n/** Async equivalent of getLatestSessionId from lib/agents.ts. */\nexport async function resolveClaudeSessionId(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const agentDir = join(agentsRoot, agentId);\n\n // 1. session.id — UUID pinned before fresh work-agent launch and updated by\n // suspend/resume flows. Authoritative when present.\n const sessionIdRaw = await readOptional(join(agentDir, 'session.id'));\n const sessionIdTrimmed = sessionIdRaw?.trim();\n if (sessionIdTrimmed) return sessionIdTrimmed;\n\n // 2. sessions.json — array of UUIDs the agent has ever used. We can't trust\n // array order (see file-level docs), so disambiguate by JSONL mtime.\n const sessionsRaw = await readOptional(join(agentDir, 'sessions.json'));\n if (sessionsRaw) {\n try {\n const parsed: unknown = JSON.parse(sessionsRaw);\n if (Array.isArray(parsed)) {\n const valid = parsed.filter((v): v is string => typeof v === 'string' && v.trim().length > 0);\n const fresh = await pickFreshestSessionId(valid, agentId, opts);\n if (fresh) return fresh;\n }\n } catch { /* non-fatal */ }\n }\n\n // 3. runtime state claudeSessionId (in-process mirror)\n try {\n const lookup = opts.getRuntimeStateAsync ?? ((id: string) => Effect.runPromise(getAgentRuntimeState(id)));\n const runtimeState = await lookup(agentId);\n if (runtimeState?.claudeSessionId) return runtimeState.claudeSessionId;\n } catch { /* non-fatal */ }\n\n // 4. agents registry — the DB row records session_id at spawn (PAN-1908) and\n // survives janitors removing the agent directory, so an ended session's\n // transcript stays resolvable. Skipped under agentsDirOverride (test hook)\n // to keep resolver tests hermetic, mirroring readRecordedState.\n if (!opts.agentsDirOverride) {\n const registrySessionId = getAgentStateSync(agentId)?.sessionId?.trim();\n if (registrySessionId) return registrySessionId;\n }\n\n return null;\n}\n\n/**\n * Read the Claude session id PINNED into an agent/conversation launcher.sh.\n *\n * The launcher is what spawns the live tmux pane: it runs\n * `claude … --session-id <uuid>` (or `--resume <uuid>`), so the pinned id is the\n * EXACT session the Terminal tab attaches to — the only deterministic ground\n * truth for \"which session is live right now.\" Resolving the transcript panel\n * from this id makes the Conversation tab match the Terminal tab by construction,\n * instead of guessing via JSONL mtime (racy: an older session's file gets touched\n * by a compaction summary write-back or a transient relaunch, its mtime jumps\n * ahead of the live session's, and the panel renders the wrong transcript).\n *\n * Checks the conversation launcher dir first, then the agent launcher dir.\n * Returns the uuid, or null when no launcher exists or it pins no session id.\n */\nconst LAUNCHER_UUID =\n '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';\nconst LAUNCHER_SESSION_ID_RE = new RegExp(`--session-id\\\\s+'?(${LAUNCHER_UUID})'?`);\nconst LAUNCHER_RESUME_RE = new RegExp(`--resume\\\\s+'?(${LAUNCHER_UUID})'?`);\n\nexport async function readLauncherPinnedSessionId(\n tmuxSession: string,\n opts: { overdeckHomeOverride?: string } = {},\n): Promise<string | null> {\n const home = opts.overdeckHomeOverride ?? getOverdeckHome();\n const candidates = [\n join(home, 'conversations', tmuxSession, 'launcher.sh'),\n join(home, 'agents', tmuxSession, 'launcher.sh'),\n ];\n for (const path of candidates) {\n const text = await readOptional(path);\n if (!text) continue;\n const match = LAUNCHER_SESSION_ID_RE.exec(text) ?? LAUNCHER_RESUME_RE.exec(text);\n if (match) return match[1]!;\n }\n return null;\n}\n\n/**\n * Read the harness + workspace recorded for an agent, honoring the test\n * override dir. Used by resolveAgentHarness / agentHasClaudeTranscript so the\n * stale-harness self-correction runs identically in prod and under test.\n */\nasync function readRecordedState(\n agentId: string,\n opts: ResolveJsonlPathOptions,\n): Promise<{ harness: string | null; workspace?: string }> {\n if (opts.agentsDirOverride) {\n try {\n const raw = await readFile(join(opts.agentsDirOverride, agentId, 'state.json'), 'utf8');\n const s = JSON.parse(raw) as { harness?: unknown; workspace?: unknown };\n return {\n harness: typeof s.harness === 'string' ? s.harness : null,\n workspace: typeof s.workspace === 'string' ? s.workspace : undefined,\n };\n } catch {\n return { harness: null };\n }\n }\n const st = getAgentStateSync(agentId);\n return { harness: st?.harness ?? null, workspace: st?.workspace };\n}\n\n/**\n * Resolve the harness for transcript routing. Reads state.json, then\n * self-corrects when the recorded value is stale.\n *\n * Trust an explicit non-default harness (codex / pi / ohmypi / acp) — it was chosen\n * by resolveHarness at spawn time and is not the generic fallback. The\n * 'claude-code' default, however, goes stale in one observed case: a\n * wipe-and-respawn that changed the provider-default harness left state.json\n * carrying the pre-respawn 'claude-code' while the fresh launch was\n * codex/ohmypi. The resolver then took the claude-code branch, found no\n * claudeSessionId, and the dashboard showed \"No conversation data available\"\n * for a live agent whose real transcript sat untouched under codex-home.\n *\n * Self-correction (claude-code/null recorded only): if the agent has NO\n * claude-code transcript on disk but DOES have a codex rollout / pi session,\n * the on-disk runtime wins — those artifacts are written only by that runtime,\n * so this cannot surface a wrong transcript. When a claude-code transcript IS\n * present it always wins, so a past codex run's codex-home can't shadow a\n * current claude-code session.\n */\nexport async function resolveAgentHarness(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const recorded = (await readRecordedState(agentId, opts)).harness;\n if (recorded === 'codex' || recorded === 'pi' || recorded === 'ohmypi' || recorded === 'acp' || recorded === 'kimi-code') {\n return recorded;\n }\n // 'claude-code' (or null) is the default that can go stale. Correct it from\n // on-disk artifacts only when no claude-code transcript exists for the agent.\n if (await agentHasClaudeTranscript(agentId, opts)) {\n return recorded ?? 'claude-code';\n }\n if (await resolveAcpTranscriptPath(agentId, opts)) return 'acp';\n if (await resolveCodexRolloutPath(agentId, opts)) return 'codex';\n if (await resolvePiSessionPath(agentId, opts)) return 'ohmypi';\n if (await resolveKimiWirePath(agentId, opts)) return 'kimi-code';\n return recorded;\n}\n\n/**\n * Whether the agent has a resolvable claude-code transcript on disk. Used by\n * resolveAgentHarness to decide if a claude-code recording is still live\n * (vs. a stale default that should be corrected to codex/ohmypi).\n */\nasync function agentHasClaudeTranscript(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<boolean> {\n const sessionId = await resolveClaudeSessionId(agentId, opts);\n if (!sessionId) return false;\n const workspace = (await readRecordedState(agentId, opts)).workspace;\n if (!workspace) return false;\n const projectsRoot = opts.claudeProjectsDirOverride ?? join(homedir(), '.claude', 'projects');\n return pathExists(join(projectsRoot, encodeClaudeProjectDir(workspace), `${sessionId}.jsonl`));\n}\n\n/**\n * Resolve the Codex rollout JSONL for a codex-harness agent (PAN-1805).\n *\n * Fast path: the persisted codex-thread-id maps directly to its rollout file.\n * Lazy fallback (same shape as the conversation panel's PAN-1690 fix): codex\n * writes the rollout only on the first turn, so a spawn-time thread-id capture\n * can miss it — the per-agent CODEX_HOME holds only this agent's rollouts, so\n * the newest one is its current thread.\n */\nexport async function resolveCodexRolloutPath(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const agentDir = join(agentsRoot, agentId);\n const codexHome = join(agentDir, 'codex-home');\n if (!(await pathExists(codexHome))) return null;\n\n const { findRolloutPath, findLatestRollout } = await import('../../../lib/runtimes/codex.js');\n\n const threadId = (await readOptional(join(agentDir, 'codex-thread-id')))?.trim();\n if (threadId) {\n const rollout = findRolloutPath(codexHome, threadId);\n if (rollout) return rollout;\n }\n return findLatestRollout(codexHome);\n}\n\n/** Resolve the normalized transcript written by the persistent ACP host. */\nexport async function resolveAcpTranscriptPath(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const transcriptPath = join(agentsRoot, agentId, 'acp-session.jsonl');\n return await pathExists(transcriptPath) ? transcriptPath : null;\n}\n\n/**\n * Resolve the native Kimi Code CLI wire.jsonl for a kimi-code-harness agent\n * (PAN-1837 wi8a). Fast path: the per-agent kimi-session-id (persisted by\n * wi5's spawnAgent) maps directly to\n * `<kimiHome>/sessions/<workDirKey>/<sessionId>/agents/main/wire.jsonl`.\n * Fallback (no captured id): the newest session directory by wire.jsonl mtime\n * under the workspace's workDirKey bucket — same shape as the codex/pi lazy\n * fallbacks above, since Kimi may not have finished writing its first turn\n * when the id was captured.\n */\nexport async function resolveKimiWirePath(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const workspace = opts.workspaceOverride ?? (await readRecordedState(agentId, opts)).workspace;\n if (!workspace) return null;\n\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const sessionId = (await readOptional(join(agentsRoot, agentId, 'kimi-session-id')))?.trim() || null;\n const kimiHome = opts.kimiHomeOverride ?? join(homedir(), '.kimi-code');\n\n // PAN-1837 review fix (P2): use the async twin here — this resolver runs on\n // the dashboard event loop and Command Deck polling re-resolves the same\n // session repeatedly, so a sync readdirSync/statSync walk would block the\n // loop on every poll as a session's history grows.\n const { findKimiWirePathAsync } = await import('../../../lib/runtimes/kimi-code.js');\n return findKimiWirePathAsync(kimiHome, workspace, sessionId);\n}\n\n/**\n * Resolve the pi/kimi session JSONL (PAN-1908). Pi writes its session transcript\n * as `<iso-ts>_<session-id>.jsonl` either in the agent dir's `sessions/` subdir\n * (conversations) OR in the agent dir root (work agents) — so check both. The dir\n * also holds `cost-events.jsonl` / `activity.jsonl`, which are NOT transcripts.\n * Return the freshest transcript by mtime, or null if pi hasn't written one yet.\n */\nexport async function resolvePiSessionPath(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const agentDir = join(agentsRoot, agentId);\n const NON_TRANSCRIPT = new Set([\n 'acp-session.jsonl',\n 'cost-events.jsonl',\n 'activity.jsonl',\n 'pending-events.jsonl',\n ]);\n let best: { path: string; mtime: number } | null = null;\n for (const dir of [join(agentDir, 'sessions'), agentDir]) {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n continue;\n }\n for (const name of entries) {\n if (!name.endsWith('.jsonl') || NON_TRANSCRIPT.has(name)) continue;\n const p = join(dir, name);\n try {\n const m = (await stat(p)).mtimeMs;\n if (!best || m > best.mtime) best = { path: p, mtime: m };\n } catch { /* unreadable entry — skip */ }\n }\n }\n return best?.path ?? null;\n}\n\n/**\n * Resolve the JSONL transcript for an agent.\n *\n * Codex agents resolve to their rollout JSONL (PAN-1805). For claude-code\n * agents, returns the absolute path if both the claudeSessionId is known AND\n * the corresponding JSONL file exists; otherwise null.\n */\nexport async function resolveJsonlPath(\n agentId: string,\n workspacePath: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n // Dispatch on the recorded harness so a stale session.id from an earlier\n // claude-code run of the same agent id can't shadow the codex transcript.\n const harness = await resolveAgentHarness(agentId, opts);\n const behavior = behaviorForHarness(harness);\n if (behavior.transcriptKind === 'codex-rollout-jsonl') {\n return resolveCodexRolloutPath(agentId, opts);\n }\n if (behavior.transcriptKind === 'ohmypi-jsonl') {\n return resolvePiSessionPath(agentId, opts);\n }\n if (behavior.transcriptKind === 'acp-jsonl') {\n return resolveAcpTranscriptPath(agentId, opts);\n }\n if (behavior.transcriptKind === 'kimi-wire-jsonl') {\n return resolveKimiWirePath(agentId, opts);\n }\n\n const claudeSessionId = await resolveClaudeSessionId(agentId, opts);\n if (!claudeSessionId) {\n logTranscriptResolution(\n agentId,\n `missing-session-id:${harness ?? 'unknown'}`,\n `failed harness=${harness ?? 'unknown'} reason=no-session-id `\n + 'checked=session.id,sessions.json,runtime-state',\n opts,\n );\n return null;\n }\n\n const recordedWorkspace = (await readRecordedState(agentId, opts)).workspace;\n const effectiveWorkspacePath = recordedWorkspace ?? workspacePath;\n const projectsRoot = opts.claudeProjectsDirOverride ?? join(homedir(), '.claude', 'projects');\n const encodedDir = encodeClaudeProjectDir(effectiveWorkspacePath);\n const jsonlPath = join(projectsRoot, encodedDir, `${claudeSessionId}.jsonl`);\n if (await pathExists(jsonlPath)) {\n logTranscriptResolution(\n agentId,\n `resolved:${jsonlPath}`,\n `resolved harness=${harness ?? 'claude-code'} sessionId=${claudeSessionId} path=${jsonlPath}`,\n opts,\n );\n return jsonlPath;\n }\n logTranscriptResolution(\n agentId,\n `missing-jsonl:${jsonlPath}`,\n `failed harness=${harness ?? 'claude-code'} reason=jsonl-missing sessionId=${claudeSessionId} `\n + `workspace=${effectiveWorkspacePath} expectedPath=${jsonlPath}`,\n opts,\n );\n return null;\n}\n","import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';\nimport { join } from 'node:path';\n\nimport { packageRoot } from './paths.js';\n\nexport interface PanCliInvocation {\n command: string;\n args: string[];\n}\n\n/**\n * Invoke Overdeck's bundled CLI without relying on the dashboard process PATH.\n * GUI/service launches often omit the package-manager bin directory that owns\n * the `pan` shim, while the running Node executable and package bundle remain\n * stable and are sufficient to launch the CLI directly.\n */\nexport function panCliInvocation(\n args: readonly string[],\n runtime: { nodePath?: string; root?: string } = {},\n): PanCliInvocation {\n return {\n command: runtime.nodePath ?? process.execPath,\n args: [join(runtime.root ?? packageRoot, 'dist', 'cli', 'index.js'), ...args],\n };\n}\n\nexport function spawnPanCli(args: readonly string[], options: SpawnOptions = {}): ChildProcess {\n const invocation = panCliInvocation(args);\n return spawn(invocation.command, invocation.args, options);\n}\n","import { existsSync } from 'node:fs';\nimport { access } from 'node:fs/promises';\nimport { Data, Effect } from 'effect';\nimport { getAgentStateSync, getAgentState, getAgentRuntimeStateSync, getAgentRuntimeState, getLatestSessionIdSync, getLatestSessionId, normalizeAgentId } from './agents.js';\nimport { hasCompletionMarkerForAgent } from './agents/supervisor-channels.js';\nimport { claudeSessionTranscriptExists } from './paths.js';\nimport { getReviewStatusSync } from './review-status.js';\nimport { sessionExistsSync, sessionExists } from './tmux.js';\n\nexport type WorkAgentOperation = 'start' | 'resume' | 'restart_with_context' | 'reset_session';\nexport type WorkAgentRecommendedAction = 'start' | 'resume' | 'restart_with_context' | 'reset_session' | 'none';\n\nfunction sessionResetRequiredReason(agentId: string, agentOrIssueId: string): string {\n return `Agent ${agentId} has a resumable Claude session. Use 'pan resume ${agentOrIssueId}' to continue it, or run 'pan reset-session ${agentOrIssueId}' before starting a new session.`;\n}\n\nfunction canStartFresh(lifecycle: WorkAgentLifecycleState, fresh: boolean): boolean {\n return lifecycle.canStartFresh || (fresh && lifecycle.requiresSessionResetBeforeFreshStart);\n}\n\n/**\n * PAN-3555: a completion marker stops meaning \"nothing to resume\" the moment the\n * pipeline owes the agent rework — a failed verification, a blocked/failed review,\n * or a failed test after the handoff makes the warm session the rework target\n * (the same condition PAN-2668 uses to clear stoppedByUser for feedback delivery).\n * Without this, the handed-off branch routed `pan start` to a silent fresh session\n * whenever the feedback loop's direct resumeAgent() path failed, abandoning the\n * resumable transcript with no refusal and no logged reason.\n */\nexport function issueOwesReworkSync(issueId: string | undefined): boolean {\n if (!issueId) return false;\n try {\n const row = getReviewStatusSync(issueId);\n if (!row) return false;\n return row.verificationStatus === 'failed'\n || row.reviewStatus === 'blocked'\n || row.reviewStatus === 'failed'\n || row.testStatus === 'failed';\n } catch {\n return false;\n }\n}\n\nexport interface WorkAgentLifecycleState {\n agentId: string;\n hasAgentState: boolean;\n hasLiveTmuxSession: boolean;\n hasSavedSession: boolean;\n hasResumableTranscript: boolean;\n hasWorkspace: boolean;\n isPlaceholder: boolean;\n isOrphaned: boolean;\n isRunning: boolean;\n /** Agent has a live tmux session and running status, but its runtime is idle or\n * suspended — meaning the model stopped producing output (e.g. model errors).\n * The session should be restarted via resume rather than messaged via pan tell. */\n isRunningButStuck: boolean;\n isStopped: boolean;\n isCompleted: boolean;\n isCrashed: boolean;\n /** Agent ran `pan done` and a completion marker exists — its work is handed\n * off. A handed-off agent is never offered a plain resume (PAN-3334): there\n * is nothing to continue, the resume only relaunches a huge transcript that\n * the harness compacts, and the session then strands (continuation nets\n * correctly refuse to re-drive handed-off agents, PAN-2974). The review →\n * feedback loop is unaffected — it resurrects through `resumeAgent()`\n * directly, not through this read door. */\n handedOff: boolean;\n /** PAN-3555: true when the agent handed off but the canonical review row shows the\n * pipeline owes it rework (failed verification, blocked/failed review, or failed\n * test). An owed-rework handoff is resumable again — `handedOff` alone no longer\n * closes the resume doors. */\n owesRework: boolean;\n runtimeState: string;\n agentStatus: string;\n canStartFresh: boolean;\n /** True when the session can be resumed (stopped, crashed, or running-but-stuck).\n * Always false when `isRunning` is true — a live, active session is not resumable. */\n canResumeSession: boolean;\n canRestartWithContext: boolean;\n canResetSession: boolean;\n requiresSessionResetBeforeFreshStart: boolean;\n recommendedAction: WorkAgentRecommendedAction;\n reason?: string;\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getWorkAgentLifecycleStateSync(agentOrIssueId: string): WorkAgentLifecycleState {\n const agentId = normalizeAgentId(agentOrIssueId);\n const agentState = getAgentStateSync(agentId);\n const runtimeState = getAgentRuntimeStateSync(agentId);\n const hasAgentState = !!agentState;\n const sessionId = getLatestSessionIdSync(agentId) ?? null;\n const hasSavedSession = !!sessionId;\n const hasLiveTmuxSession = sessionExistsSync(agentId);\n const hasWorkspace = !!agentState?.workspace && existsSync(agentState.workspace);\n const hasResumableTranscript = !sessionId\n || (!!agentState?.harness && agentState.harness !== 'claude-code')\n || !agentState?.workspace\n || claudeSessionTranscriptExists(agentState.workspace, sessionId);\n const agentStatus = agentState?.status || 'unknown';\n const runtime = runtimeState?.state || 'uninitialized';\n const isCompleted = runtimeState?.resolution === 'completed';\n const isPlaceholder = !!agentState && agentStatus === 'starting' && typeof agentState.model === 'string' && agentState.model.startsWith('pending-');\n const isStopped = agentStatus === 'stopped' || agentStatus === 'error' || isCompleted || runtime === 'stopped' || runtime === 'idle' || runtime === 'suspended';\n const isRunning = (agentStatus === 'running' || isPlaceholder) && hasLiveTmuxSession;\n const isCrashed = (agentStatus === 'running' || isPlaceholder) && !hasLiveTmuxSession;\n // Running-but-stuck: live session + running status, but the runtime is idle or suspended\n // (e.g. the model returned errors and stopped producing output). The tmux session exists but\n // the agent is no longer making progress — it needs a resume, not a message.\n const isRunningButStuck = isRunning && (runtime === 'idle' || runtime === 'suspended');\n const hasResumableBackingState = hasAgentState && hasWorkspace && !isPlaceholder;\n const handedOff = agentState ? hasCompletionMarkerForAgent(agentState) : false;\n const owesRework = handedOff && issueOwesReworkSync(agentState?.issueId);\n const canWarmResumeAfterHandoff = owesRework && !isRunning && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed);\n const isOrphaned = !hasLiveTmuxSession && (\n (hasSavedSession && !hasResumableBackingState)\n || (hasAgentState && (!hasWorkspace || isPlaceholder))\n );\n // A saved resumable session is never discarded by a plain start. The explicit\n // --fresh intent is evaluated by assertCanStartFreshSync before the state wipe.\n const requiresSessionResetBeforeFreshStart = hasSavedSession && hasResumableTranscript && !hasLiveTmuxSession && hasResumableBackingState && (isStopped || isCrashed);\n\n let recommendedAction: WorkAgentRecommendedAction = 'start';\n let reason: string | undefined;\n\n if (isRunningButStuck) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a live session but its runtime is ${runtime} — it is no longer making progress. Use 'pan resume ${agentOrIssueId}' to restart it.`;\n } else if (hasLiveTmuxSession && agentStatus === 'running') {\n recommendedAction = 'none';\n reason = `Agent ${agentId} is already running. Use 'pan tell' to message it.`;\n } else if (canWarmResumeAfterHandoff) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} handed off its work but the pipeline now owes it rework (failed verification, blocked/failed review, or failed test). Use 'pan resume ${agentOrIssueId}' to continue its warm session with the pending feedback (PAN-3555).`;\n } else if (handedOff) {\n // PAN-3334: a handed-off agent has nothing to resume. Offering Resume here\n // only relaunches a finished transcript (which the harness then compacts)\n // and strands the session — continuation nets refuse to re-drive it by\n // design (PAN-2974). The review → feedback loop is unaffected: it\n // resurrects through resumeAgent() directly, not through this read door.\n recommendedAction = 'none';\n reason = `Agent ${agentId} finished and handed off its work (completion marker on disk) — there is nothing to resume. The session is preserved for inspection; message it with 'pan tell ${agentOrIssueId}', or start over with 'pan start ${agentOrIssueId} --fresh'.`;\n } else if (hasLiveTmuxSession && isStopped) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a live tmux session but is stopped. Use 'pan resume ${agentOrIssueId}' to continue or 'pan start ${agentOrIssueId}' will kill the session and start fresh.`;\n } else if (isOrphaned) {\n recommendedAction = 'start';\n reason = hasSavedSession\n ? `Agent ${agentId} has stale/orphaned session metadata without a resumable workspace-backed agent state. Start Agent should create a fresh session.`\n : `Agent ${agentId} is an orphaned placeholder/stale record. Start Agent should create a fresh session.`;\n } else if (requiresSessionResetBeforeFreshStart) {\n recommendedAction = 'resume';\n reason = sessionResetRequiredReason(agentId, agentOrIssueId);\n } else if (hasSavedSession && !hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed)) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has a saved Claude session id but its transcript is missing on disk (jsonl-missing). Start Agent will create a fresh session in the existing workspace.`;\n } else if (hasAgentState && !hasSavedSession && isStopped) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} is stopped and has no saved Claude session. Start Agent will create a fresh session in the existing workspace.`;\n } else if (!hasAgentState && !hasSavedSession) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has no prior resumable session. Start Agent will create a fresh workspace-backed session.`;\n }\n\n return {\n agentId,\n hasAgentState,\n hasLiveTmuxSession,\n hasSavedSession,\n hasResumableTranscript,\n hasWorkspace,\n isPlaceholder,\n isOrphaned,\n isRunning,\n isRunningButStuck,\n isStopped,\n isCompleted,\n isCrashed,\n handedOff,\n owesRework,\n runtimeState: runtime,\n agentStatus,\n canStartFresh: (!hasLiveTmuxSession || (hasLiveTmuxSession && isStopped)) && (!requiresSessionResetBeforeFreshStart || isOrphaned),\n // A live, actively-running agent (isRunning=true, isRunningButStuck=false) is already in\n // session — no resume needed. Stuck agents (isRunning=true, isRunningButStuck=true) must\n // use the dedicated isRunningButStuck flag at call sites; canResumeSession stays false for\n // them so `isRunning` and `canResumeSession` are never simultaneously true.\n canResumeSession: !isRunning && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed) && (!handedOff || owesRework),\n canRestartWithContext: hasAgentState && hasWorkspace,\n canResetSession: hasSavedSession && hasResumableTranscript && hasResumableBackingState,\n requiresSessionResetBeforeFreshStart,\n recommendedAction,\n reason,\n };\n}\n\nasync function getWorkAgentLifecycleStateSnapshot(agentOrIssueId: string): Promise<WorkAgentLifecycleState> {\n const agentId = normalizeAgentId(agentOrIssueId);\n const agentState = await Effect.runPromise(getAgentState(agentId));\n const runtimeState = await Effect.runPromise(getAgentRuntimeState(agentId));\n const hasAgentState = !!agentState;\n const sessionId = await Effect.runPromise(getLatestSessionId(agentId)) ?? null;\n const hasSavedSession = !!sessionId;\n const hasLiveTmuxSession = await Effect.runPromise(sessionExists(agentId));\n const hasWorkspace = !!agentState?.workspace && await pathExists(agentState.workspace);\n const hasResumableTranscript = !sessionId\n || (!!agentState?.harness && agentState.harness !== 'claude-code')\n || !agentState?.workspace\n || claudeSessionTranscriptExists(agentState.workspace, sessionId);\n const agentStatus = agentState?.status || 'unknown';\n const runtime = runtimeState?.state || 'uninitialized';\n const isCompleted = runtimeState?.resolution === 'completed';\n const isPlaceholder = !!agentState && agentStatus === 'starting' && typeof agentState.model === 'string' && agentState.model.startsWith('pending-');\n const isStopped = agentStatus === 'stopped' || agentStatus === 'error' || isCompleted || runtime === 'stopped' || runtime === 'idle' || runtime === 'suspended';\n const isRunning = (agentStatus === 'running' || isPlaceholder) && hasLiveTmuxSession;\n const isCrashed = (agentStatus === 'running' || isPlaceholder) && !hasLiveTmuxSession;\n const isRunningButStuck = isRunning && (runtime === 'idle' || runtime === 'suspended');\n const hasResumableBackingState = hasAgentState && hasWorkspace && !isPlaceholder;\n const handedOff = agentState ? hasCompletionMarkerForAgent(agentState) : false;\n const owesRework = handedOff && issueOwesReworkSync(agentState?.issueId);\n const canWarmResumeAfterHandoff = owesRework && !isRunning && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed);\n const isOrphaned = !hasLiveTmuxSession && (\n (hasSavedSession && !hasResumableBackingState)\n || (hasAgentState && (!hasWorkspace || isPlaceholder))\n );\n // A saved resumable session is never discarded by a plain start. The explicit\n // --fresh intent is evaluated by assertCanStartFreshSync before the state wipe.\n const requiresSessionResetBeforeFreshStart = hasSavedSession && hasResumableTranscript && !hasLiveTmuxSession && hasResumableBackingState && (isStopped || isCrashed);\n\n let recommendedAction: WorkAgentRecommendedAction = 'start';\n let reason: string | undefined;\n\n if (isRunningButStuck) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a live session but its runtime is ${runtime} — it is no longer making progress. Use 'pan resume ${agentOrIssueId}' to restart it.`;\n } else if (hasLiveTmuxSession && agentStatus === 'running') {\n recommendedAction = 'none';\n reason = `Agent ${agentId} is already running. Use 'pan tell' to message it.`;\n } else if (canWarmResumeAfterHandoff) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} handed off its work but the pipeline now owes it rework (failed verification, blocked/failed review, or failed test). Use 'pan resume ${agentOrIssueId}' to continue its warm session with the pending feedback (PAN-3555).`;\n } else if (handedOff) {\n // PAN-3334: a handed-off agent has nothing to resume. Offering Resume here\n // only relaunches a finished transcript (which the harness then compacts)\n // and strands the session — continuation nets refuse to re-drive it by\n // design (PAN-2974). The review → feedback loop is unaffected: it\n // resurrects through resumeAgent() directly, not through this read door.\n recommendedAction = 'none';\n reason = `Agent ${agentId} finished and handed off its work (completion marker on disk) — there is nothing to resume. The session is preserved for inspection; message it with 'pan tell ${agentOrIssueId}', or start over with 'pan start ${agentOrIssueId} --fresh'.`;\n } else if (hasLiveTmuxSession && isStopped) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a live tmux session but is stopped. Use 'pan resume ${agentOrIssueId}' to continue or 'pan start ${agentOrIssueId}' will kill the session and start fresh.`;\n } else if (isOrphaned) {\n recommendedAction = 'start';\n reason = hasSavedSession\n ? `Agent ${agentId} has stale/orphaned session metadata without a resumable workspace-backed agent state. Start Agent should create a fresh session.`\n : `Agent ${agentId} is an orphaned placeholder/stale record. Start Agent should create a fresh session.`;\n } else if (requiresSessionResetBeforeFreshStart) {\n recommendedAction = 'resume';\n reason = sessionResetRequiredReason(agentId, agentOrIssueId);\n } else if (hasSavedSession && !hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed)) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has a saved Claude session id but its transcript is missing on disk (jsonl-missing). Start Agent will create a fresh session in the existing workspace.`;\n } else if (hasAgentState && !hasSavedSession && isStopped) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} is stopped and has no saved Claude session. Start Agent will create a fresh session in the existing workspace.`;\n } else if (!hasAgentState && !hasSavedSession) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has no prior resumable session. Start Agent will create a fresh workspace-backed session.`;\n }\n\n return {\n agentId,\n hasAgentState,\n hasLiveTmuxSession,\n hasSavedSession,\n hasResumableTranscript,\n hasWorkspace,\n isPlaceholder,\n isOrphaned,\n isRunning,\n isRunningButStuck,\n isStopped,\n isCompleted,\n isCrashed,\n handedOff,\n owesRework,\n runtimeState: runtime,\n agentStatus,\n canStartFresh: (!hasLiveTmuxSession || (hasLiveTmuxSession && isStopped)) && (!requiresSessionResetBeforeFreshStart || isOrphaned),\n // A live, actively-running agent (isRunning=true, isRunningButStuck=false) is already in\n // session — no resume needed. Stuck agents (isRunning=true, isRunningButStuck=true) must\n // use the dedicated isRunningButStuck flag at call sites; canResumeSession stays false for\n // them so `isRunning` and `canResumeSession` are never simultaneously true.\n // PAN-2908: the async snapshot used to omit hasSavedSession here — every stopped agent with\n // a workspace looked resumable, so the CTA offered Resume with nothing to resume (PAN-806).\n canResumeSession: !isRunning && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed) && (!handedOff || owesRework),\n canRestartWithContext: hasAgentState && hasWorkspace,\n canResetSession: hasSavedSession && hasResumableTranscript && hasResumableBackingState,\n requiresSessionResetBeforeFreshStart,\n recommendedAction,\n reason,\n };\n}\n\ninterface StartFreshOptions {\n allowPausedForce?: boolean;\n allowLiveSessionReplacement?: boolean;\n /** True only when the caller carries an explicit operator `--fresh` intent.\n * PAN-3555: without it, a handed-off agent that owes rework and has a\n * resumable warm session must be refused a fresh start — plain `pan start`\n * silently abandoning the warm transcript is exactly the bug. Explicit\n * `--fresh` keeps working (the PAN-3543 escape from the reset deadlock). */\n explicitFresh?: boolean;\n}\n\nexport function assertCanStartFreshSync(agentOrIssueId: string, options: StartFreshOptions = {}): WorkAgentLifecycleState {\n const lifecycle = getWorkAgentLifecycleStateSync(agentOrIssueId);\n const pausedForceOverride = options.allowPausedForce === true\n && lifecycle.requiresSessionResetBeforeFreshStart\n && getAgentStateSync(lifecycle.agentId)?.paused === true;\n const liveSessionReplacement = options.allowLiveSessionReplacement === true && lifecycle.isRunning;\n if (liveSessionReplacement && lifecycle.canResetSession) {\n throw new Error(sessionResetRequiredReason(lifecycle.agentId, agentOrIssueId));\n }\n if (options.explicitFresh !== true && lifecycle.owesRework && lifecycle.canResumeSession) {\n throw new Error(\n `Agent ${lifecycle.agentId} handed off its work but the pipeline owes it rework, and its warm session is resumable. `\n + `Use 'pan resume ${agentOrIssueId}' to continue that session with the pending feedback, or pass --fresh to deliberately start a new one (PAN-3555).`,\n );\n }\n if (!canStartFresh(lifecycle, options.explicitFresh === true) && !pausedForceOverride && !liveSessionReplacement) {\n throw new Error(lifecycle.reason || `Cannot start fresh for ${lifecycle.agentId}`);\n }\n return lifecycle;\n}\n\nexport function assertCanResumeSessionSync(agentOrIssueId: string): WorkAgentLifecycleState {\n const lifecycle = getWorkAgentLifecycleStateSync(agentOrIssueId);\n if (!lifecycle.canResumeSession && !lifecycle.isRunningButStuck) {\n throw new Error(lifecycle.reason || `Cannot resume session for ${lifecycle.agentId}`);\n }\n return lifecycle;\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Asserts about agent lifecycle (cannot start fresh / cannot resume) fail in\n * the typed error channel as `WorkAgentLifecycleViolation`.\n */\nexport class WorkAgentLifecycleViolation extends Data.TaggedError('WorkAgentLifecycleViolation')<{\n readonly agentId: string;\n readonly reason: string;\n}> {}\n\nexport const getWorkAgentLifecycleState = (\n agentOrIssueId: string,\n): Effect.Effect<WorkAgentLifecycleState> =>\n Effect.promise(() => getWorkAgentLifecycleStateSnapshot(agentOrIssueId));\n\n/** Assert the agent can start fresh; lifts the synchronous throw to a typed error. */\nexport const assertCanStartFresh = (\n agentOrIssueId: string,\n options: { allowPausedForce?: boolean } = {},\n): Effect.Effect<WorkAgentLifecycleState, WorkAgentLifecycleViolation> =>\n Effect.try({\n try: () => assertCanStartFreshSync(agentOrIssueId, options),\n catch: (cause) =>\n new WorkAgentLifecycleViolation({\n agentId: agentOrIssueId,\n reason: cause instanceof Error ? cause.message : String(cause),\n }),\n });\n\n/** Assert the agent can resume; lifts the synchronous throw to a typed error. */\nexport const assertCanResumeSession = (\n agentOrIssueId: string,\n): Effect.Effect<WorkAgentLifecycleState, WorkAgentLifecycleViolation> =>\n Effect.try({\n try: () => assertCanResumeSessionSync(agentOrIssueId),\n catch: (cause) =>\n new WorkAgentLifecycleViolation({\n agentId: agentOrIssueId,\n reason: cause instanceof Error ? cause.message : String(cause),\n }),\n });\n","/**\n * Git info enricher (PAN-1523)\n *\n * Derives branch + worktree-status for an absolute filesystem path. Used to\n * surface branch/worktree chips on conversation rows, conversation terminals,\n * and agent terminals.\n *\n * Two enricher entrypoints with different semantics:\n * - resolveConversationGitInfo() — best-effort: returns null branch when\n * the path isn't a git repo. Used for conversations whose cwd can be\n * anywhere.\n * - resolveAgentGitInfo() — verifies a work-agent workspace against its\n * expected feature branch. Reports drift and missing-workspace state\n * because both are silent failure modes today.\n *\n * Cache: in-memory, keyed by absolute path and invalidated when the mtime of\n * <path>/.git/HEAD changes. Every read still stats HEAD, so a checkout under a\n * live conversation is picked up immediately without periodically respawning\n * two git processes for every unchanged workspace.\n *\n * All I/O is async (execFile / fs.promises). No execSync, no readFileSync.\n */\n\nimport { execFile } from 'node:child_process';\nimport { readFile, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nconst GIT_INFO_TIMEOUT_MS = 2_000;\n\nexport interface ConversationGitInfo {\n /** Current branch (HEAD), null if cwd is not a git repo. */\n branch: string | null;\n /** True when cwd points at a secondary git worktree (not the primary checkout). */\n isWorktree: boolean;\n}\n\nexport interface AgentGitInfo {\n /** Current branch (HEAD), null if the workspace path is missing or not a git repo. */\n actualBranch: string | null;\n /** True when actualBranch differs from the expected feature branch. */\n branchDrifted: boolean;\n /** True when the workspacePath does not exist on disk, or is no longer a git worktree. */\n workspaceMissing: boolean;\n}\n\ninterface CacheEntry {\n value: ConversationGitInfo;\n headMtimeMs: number;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\n/**\n * Locate the HEAD file for a working tree at `absPath`.\n *\n * In a primary checkout `.git` is a directory and HEAD lives at `.git/HEAD`.\n * In a secondary worktree `.git` is a small text file containing\n * `gitdir: /abs/path/to/.git/worktrees/<name>` and HEAD lives at\n * `<that-gitdir>/HEAD`. We follow the pointer so the mtime check actually\n * sees branch flips in worktrees.\n */\nasync function locateHeadPath(absPath: string): Promise<string | null> {\n const gitMarker = join(absPath, '.git');\n let markerStat;\n try {\n markerStat = await stat(gitMarker);\n } catch {\n return null;\n }\n\n if (markerStat.isDirectory()) {\n return join(gitMarker, 'HEAD');\n }\n\n if (markerStat.isFile()) {\n try {\n const contents = await readFile(gitMarker, 'utf-8');\n const match = contents.match(/^gitdir:\\s*(.+)$/m);\n if (!match) return null;\n return join(match[1].trim(), 'HEAD');\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\nasync function readHeadMtimeMs(absPath: string): Promise<number | null> {\n const headPath = await locateHeadPath(absPath);\n if (headPath === null) return null;\n try {\n const stats = await stat(headPath);\n return stats.mtimeMs;\n } catch {\n return null;\n }\n}\n\nasync function readGitInfoFresh(absPath: string): Promise<ConversationGitInfo> {\n const [branchResult, worktreeResult] = await Promise.all([\n execFileAsync('git', ['-C', absPath, 'rev-parse', '--abbrev-ref', 'HEAD'], {\n encoding: 'utf-8',\n timeout: GIT_INFO_TIMEOUT_MS,\n }).catch(() => null),\n execFileAsync('git', ['-C', absPath, 'rev-parse', '--git-common-dir', '--git-dir'], {\n encoding: 'utf-8',\n timeout: GIT_INFO_TIMEOUT_MS,\n }).catch(() => null),\n ]);\n\n if (!branchResult) {\n return { branch: null, isWorktree: false };\n }\n\n const branch = branchResult.stdout.trim() || null;\n\n // A secondary worktree has `git-dir` !== `git-common-dir` (worktree-specific\n // dir lives at <primary>/.git/worktrees/<name>; common dir is <primary>/.git).\n // The primary checkout has them equal.\n let isWorktree = false;\n if (worktreeResult) {\n const [commonDir, gitDir] = worktreeResult.stdout.trim().split('\\n').map((line) => line.trim());\n if (commonDir && gitDir && commonDir !== gitDir) {\n isWorktree = true;\n }\n }\n\n return { branch, isWorktree };\n}\n\n/**\n * Resolve branch + worktree status for a conversation cwd. Cached until the\n * HEAD mtime changes.\n */\nexport async function resolveConversationGitInfo(absPath: string): Promise<ConversationGitInfo> {\n const headMtimeMs = await readHeadMtimeMs(absPath);\n if (headMtimeMs === null) {\n return { branch: null, isWorktree: false };\n }\n\n const cached = cache.get(absPath);\n if (cached && cached.headMtimeMs === headMtimeMs) {\n return cached.value;\n }\n\n const value = await readGitInfoFresh(absPath);\n cache.set(absPath, {\n value,\n headMtimeMs,\n });\n return value;\n}\n\n/**\n * Resolve drift + missing-workspace state for a work-agent workspace.\n * `expectedBranch` is the deterministic feature/<issue-id> branch from\n * resource-discovery. Reuses the conversation cache for the branch read,\n * then layers drift detection on top.\n */\nexport async function resolveAgentGitInfo(\n workspacePath: string,\n expectedBranch: string,\n): Promise<AgentGitInfo> {\n const headMtimeMs = await readHeadMtimeMs(workspacePath);\n if (headMtimeMs === null) {\n return {\n actualBranch: null,\n branchDrifted: false,\n workspaceMissing: true,\n };\n }\n\n const info = await resolveConversationGitInfo(workspacePath);\n if (info.branch === null) {\n // .git/HEAD exists but rev-parse failed — corrupt or stale worktree.\n return {\n actualBranch: null,\n branchDrifted: false,\n workspaceMissing: true,\n };\n }\n\n return {\n actualBranch: info.branch,\n branchDrifted: info.branch !== expectedBranch,\n workspaceMissing: false,\n };\n}\n\n/** Test-only: reset the cache between unit tests. */\nexport function _resetGitInfoCacheForTests(): void {\n cache.clear();\n}\n","import type { ChildProcess } from 'node:child_process';\n\nimport type { ComposerCommandResult } from '@overdeck/contracts';\nimport { spawnPanCli } from '../pan-cli-invocation.js';\n\nexport const CAPTURED_COMMAND_MAX_OUTPUT_BYTES = 64 * 1024;\nexport const CAPTURED_COMMAND_TIMEOUT_MS = 30_000;\n\nconst TRUNCATION_MESSAGE = '\\n\\nOutput was truncated after 65,536 bytes.';\n\nexport interface CapturedCommandDependencies {\n spawnPanCli?: typeof spawnPanCli;\n timeoutMs?: number;\n}\n\nexport function runCapturedCommand(\n argv: readonly string[],\n dependencies: CapturedCommandDependencies = {},\n): Promise<ComposerCommandResult> {\n const command = `/pan ${argv.join(' ')}`;\n const spawnCommand = dependencies.spawnPanCli ?? spawnPanCli;\n const timeoutMs = dependencies.timeoutMs ?? CAPTURED_COMMAND_TIMEOUT_MS;\n const child = spawnCommand(argv, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n return new Promise(resolve => {\n const chunks: Buffer[] = [];\n let bufferedBytes = 0;\n let settled = false;\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let truncated = false;\n\n const appendOutput = (data: string | Buffer) => {\n if (truncated) return;\n const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);\n const maximumPrefixBytes = CAPTURED_COMMAND_MAX_OUTPUT_BYTES - Buffer.byteLength(TRUNCATION_MESSAGE);\n const remaining = maximumPrefixBytes - bufferedBytes;\n if (buffer.length <= remaining) {\n chunks.push(buffer);\n bufferedBytes += buffer.length;\n return;\n }\n if (remaining > 0) {\n chunks.push(buffer.subarray(0, remaining));\n bufferedBytes += remaining;\n }\n truncated = true;\n };\n\n const output = () => {\n const captured = Buffer.concat(chunks, bufferedBytes).toString('utf8');\n return truncated ? `${captured}${TRUNCATION_MESSAGE}` : captured;\n };\n\n const finish = (result: ComposerCommandResult) => {\n if (settled) return;\n settled = true;\n if (timeout !== undefined) clearTimeout(timeout);\n resolve(result);\n };\n\n child.stdout?.on('data', appendOutput);\n child.stderr?.on('data', appendOutput);\n\n child.once('error', error => {\n finish({\n kind: 'captured',\n status: 'failed',\n command,\n output: `Command ${command} could not start: ${error.message}`,\n truncated: false,\n });\n });\n\n child.once('close', code => {\n const captured = output();\n finish({\n kind: 'captured',\n status: code === 0 ? 'completed' : 'failed',\n command,\n output: captured || `Command ${command} ${code === 0 ? 'completed without output.' : `failed with exit code ${code ?? 'unknown'}.`}`,\n truncated,\n });\n });\n\n timeout = setTimeout(() => {\n stopChild(child);\n finish({\n kind: 'captured',\n status: 'failed',\n command,\n output: `Command ${command} timed out after ${Math.round(timeoutMs / 1000)} seconds and was stopped.`,\n truncated: false,\n });\n }, timeoutMs);\n });\n}\n\nfunction stopChild(child: ChildProcess): void {\n child.kill('SIGTERM');\n}\n","import type { ChildProcess } from 'node:child_process';\nimport { appendFile, mkdir, open } from 'node:fs/promises';\nimport { isAbsolute, join, resolve } from 'node:path';\n\nimport type { ComposerCommandResult } from '@overdeck/contracts';\nimport { emitActivityEntryDurable, type EmitActivityOptions } from '../activity-logger.js';\nimport { getDashboardLoopbackApiUrlSync } from '../config.js';\nimport { parseIssueIdSync } from '../issue-id.js';\nimport { spawnPanCli } from '../pan-cli-invocation.js';\nimport { getOverdeckHome } from '../paths.js';\nimport { CAPTURED_COMMAND_MAX_OUTPUT_BYTES } from './executors.js';\n\nexport interface DetachedPanCommandInput {\n agentSessionName: string;\n issueId: string;\n role: string;\n workspacePath: string;\n args: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n activityId?: string;\n}\n\nexport interface DetachedPanCommandLaunch {\n activityId: string;\n spawnLogPath: string;\n spawnLogStart: number;\n completion: Promise<void>;\n}\n\nexport interface DetachedPanCommandDependencies {\n now?: () => number;\n overdeckHome?: string;\n spawnPanCli?: typeof spawnPanCli;\n emitActivity?: (options: EmitActivityOptions) => Promise<void>;\n}\n\nconst DETACHED_TRUNCATION_MESSAGE = '\\n\\nOutput was truncated after 65,536 bytes.';\n\nfunction resolveAgentDirectory(overdeckHome: string, agentId: string): string {\n if (\n agentId.length === 0 ||\n agentId === '.' ||\n agentId === '..' ||\n agentId.includes('/') ||\n agentId.includes('\\\\') ||\n isAbsolute(agentId)\n ) {\n throw new Error('Agent ID must be a single filesystem-safe path segment.');\n }\n return join(resolve(overdeckHome, 'agents'), agentId);\n}\n\nasync function readBoundedSpawnOutput(spawnLogPath: string, startOffset: number): Promise<string> {\n const maximumPrefixBytes = CAPTURED_COMMAND_MAX_OUTPUT_BYTES - Buffer.byteLength(DETACHED_TRUNCATION_MESSAGE);\n const handle = await open(spawnLogPath, 'r');\n try {\n const stats = await handle.stat();\n const outputBytes = Math.max(0, stats.size - startOffset);\n if (outputBytes === 0) return '';\n const bytesToRead = Math.min(outputBytes, maximumPrefixBytes);\n const buffer = Buffer.alloc(bytesToRead);\n const { bytesRead } = await handle.read(buffer, 0, bytesToRead, startOffset);\n const output = buffer.subarray(0, bytesRead).toString('utf8');\n return outputBytes > maximumPrefixBytes\n ? `${output}${DETACHED_TRUNCATION_MESSAGE}`\n : output;\n } finally {\n await handle.close().catch(() => undefined);\n }\n}\n\nexport async function appendAgentLifecycleLog(\n agentId: string,\n event: string,\n details: Record<string, unknown> = {},\n overdeckHome = getOverdeckHome(),\n): Promise<void> {\n const agentDir = resolveAgentDirectory(overdeckHome, agentId);\n await mkdir(agentDir, { recursive: true });\n const logLine = JSON.stringify({\n ts: new Date().toISOString(),\n event,\n ...details,\n });\n await appendFile(join(agentDir, 'lifecycle.log'), `${logLine}\\n`);\n}\n\nexport async function launchPanCommandDetached(\n input: DetachedPanCommandInput,\n dependencies: DetachedPanCommandDependencies = {},\n): Promise<DetachedPanCommandLaunch> {\n const { agentSessionName, issueId, role, workspacePath, args } = input;\n const cwd = input.cwd ?? workspacePath;\n const overdeckHome = dependencies.overdeckHome ?? getOverdeckHome();\n const activityId = input.activityId ?? `activity-${(dependencies.now ?? Date.now)()}`;\n const agentDir = resolveAgentDirectory(overdeckHome, agentSessionName);\n await mkdir(agentDir, { recursive: true });\n const spawnLogPath = join(agentDir, 'spawn.log');\n const spawnLogHandle = await open(spawnLogPath, 'a');\n const spawnLogStart = (await spawnLogHandle.stat()).size;\n const spawnCommand = dependencies.spawnPanCli ?? spawnPanCli;\n // The server's env carries DASHBOARD_URL as its PUBLIC url (behind Traefik,\n // local TLS). A spawned CLI resolving its API target from that url fails\n // Node's cert check with a bare \"fetch failed\" (PAN-3331) — pin children to\n // the loopback API of the server that spawned them instead.\n const childEnv: NodeJS.ProcessEnv = { ...process.env, ...input.env };\n if (!input.env?.OVERDECK_DASHBOARD_URL) {\n delete childEnv.DASHBOARD_URL;\n childEnv.OVERDECK_DASHBOARD_URL = getDashboardLoopbackApiUrlSync();\n }\n const child = spawnCommand(args, {\n cwd,\n detached: true,\n stdio: ['ignore', spawnLogHandle.fd, spawnLogHandle.fd],\n env: childEnv,\n });\n\n let spawned = false;\n let settleSpawn: (() => void) | undefined;\n let rejectSpawn: ((error: Error) => void) | undefined;\n const spawnPromise = new Promise<void>((resolve, reject) => {\n settleSpawn = resolve;\n rejectSpawn = reject;\n });\n const completion = commandCompletion(child, {\n agentSessionName,\n issueId,\n role,\n workspacePath,\n activityId,\n args,\n cwd,\n spawnLogPath,\n spawnLogStart,\n spawnLogHandle,\n overdeckHome,\n onSpawn: () => {\n spawned = true;\n settleSpawn?.();\n },\n onError: error => {\n if (!spawned) rejectSpawn?.(error);\n },\n });\n void completion.catch(() => undefined);\n\n await spawnPromise;\n child.unref();\n return { activityId, spawnLogPath, spawnLogStart, completion };\n}\n\ninterface CompletionContext {\n agentSessionName: string;\n issueId: string;\n role: string;\n workspacePath: string;\n activityId: string;\n args: string[];\n cwd: string;\n spawnLogPath: string;\n spawnLogStart: number;\n spawnLogHandle: Awaited<ReturnType<typeof open>>;\n overdeckHome: string;\n onSpawn(): void;\n onError(error: Error): void;\n}\n\nfunction commandCompletion(child: ChildProcess, context: CompletionContext): Promise<void> {\n child.once('spawn', () => {\n void appendAgentLifecycleLog(context.agentSessionName, 'agent.work_spawn_process_spawned', {\n issueId: context.issueId,\n role: context.role,\n workspacePath: context.workspacePath,\n activityId: context.activityId,\n pid: child.pid,\n args: context.args,\n cwd: context.cwd,\n spawnLogPath: context.spawnLogPath,\n }, context.overdeckHome)\n .catch(() => undefined)\n .finally(context.onSpawn);\n });\n\n return new Promise((resolve, reject) => {\n let settled = false;\n const finish = async (error?: Error) => {\n if (settled) return;\n settled = true;\n await context.spawnLogHandle.close().catch(() => undefined);\n if (error) reject(error);\n else resolve();\n };\n\n child.once('error', error => {\n void (async () => {\n await appendAgentLifecycleLog(context.agentSessionName, 'agent.work_spawn_process_error', {\n issueId: context.issueId,\n role: context.role,\n workspacePath: context.workspacePath,\n activityId: context.activityId,\n error: error.message,\n args: context.args,\n cwd: context.cwd,\n spawnLogPath: context.spawnLogPath,\n }, context.overdeckHome).catch(() => undefined);\n context.onError(error);\n await finish(error);\n })();\n });\n\n child.once('close', (code, signal) => {\n void (async () => {\n await appendAgentLifecycleLog(context.agentSessionName, 'agent.work_spawn_process_closed', {\n issueId: context.issueId,\n role: context.role,\n workspacePath: context.workspacePath,\n activityId: context.activityId,\n code,\n signal,\n args: context.args,\n cwd: context.cwd,\n spawnLogPath: context.spawnLogPath,\n }, context.overdeckHome).catch(() => undefined);\n if (code === 0) {\n await finish();\n return;\n }\n const output = await readBoundedSpawnOutput(context.spawnLogPath, context.spawnLogStart).catch(() => '');\n const error = new Error(output.trim() || `pan ${context.args.join(' ')} exited with code ${code ?? 'null'}`);\n Object.assign(error, {\n activityId: context.activityId,\n spawnLogPath: context.spawnLogPath,\n code,\n signal,\n output,\n });\n await finish(error);\n })();\n });\n });\n}\n\nexport async function runDetachedCommand(\n argv: string[],\n dependencies: DetachedPanCommandDependencies = {},\n): Promise<ComposerCommandResult> {\n const issueId = argv[1] ?? '';\n const parsedIssueId = parseIssueIdSync(issueId);\n if (!parsedIssueId) {\n return {\n kind: 'terminal-only',\n status: 'rejected',\n message: `/pan ${argv[0] ?? 'command'} requires a canonical issue ID such as PAN-1525.`,\n };\n }\n const role = argv[0] === 'plan' ? 'plan' : 'work';\n const agentSessionName = `${role === 'plan' ? 'planning' : 'agent'}-${parsedIssueId.normalized}`;\n const command = `/pan ${argv.join(' ')}`;\n const activityId = `activity-${(dependencies.now ?? Date.now)()}`;\n const emitActivity = dependencies.emitActivity ?? emitActivityEntryDurable;\n const source = role === 'plan' ? 'planning-agent' : 'work-agent';\n const emitTransition = (\n status: 'accepted' | 'running' | 'completed' | 'failed',\n level: 'info' | 'success' | 'error',\n message: string,\n output?: string,\n ) => emitActivity({\n id: activityId,\n source,\n level,\n status,\n command,\n message,\n details: output,\n output,\n issueId,\n });\n\n await emitTransition(\n 'accepted',\n 'info',\n `Accepted ${command} for ${issueId} as activity ${activityId}.`,\n );\n\n let launch: DetachedPanCommandLaunch;\n try {\n launch = await launchPanCommandDetached({\n agentSessionName,\n issueId,\n role,\n workspacePath: process.cwd(),\n args: argv,\n activityId,\n }, dependencies);\n } catch (error) {\n const output = error instanceof Error ? error.message : String(error);\n await emitTransition(\n 'failed',\n 'error',\n `Activity ${activityId} failed to start ${command} for ${issueId}.`,\n output,\n ).catch(() => undefined);\n throw error;\n }\n\n await emitTransition(\n 'running',\n 'info',\n `Activity ${activityId} is running ${command} for ${issueId}.`,\n ).catch(() => undefined);\n\n void launch.completion.then(async () => {\n const output = await readBoundedSpawnOutput(launch.spawnLogPath, launch.spawnLogStart).catch(() => '');\n await emitTransition(\n 'completed',\n 'success',\n `Activity ${activityId} completed ${command} for ${issueId}.`,\n output || `Command ${command} completed without output.`,\n );\n }, async error => {\n const activityError = error as Error & { output?: string };\n const output = activityError.output?.trim() || activityError.message;\n await emitTransition(\n 'failed',\n 'error',\n `Activity ${activityId} failed ${command} for ${issueId}.`,\n output,\n );\n }).catch(() => undefined);\n\n return {\n kind: 'activity',\n status: 'accepted',\n command,\n activityId: launch.activityId,\n message: `Started ${command} for ${issueId}. Watch activity ${launch.activityId} or the ${issueId} issue view for progress.`,\n };\n}\n","import { exec, execFile } from 'node:child_process';\nimport { timingSafeEqual } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\n\nimport { Effect } from 'effect';\nimport { HttpServerRequest } from 'effect/unstable/http';\nimport type { AgentStatus } from '@overdeck/contracts';\n\nimport { jsonResponse } from '../../http-helpers.js';\nimport { getHeaderFromMap } from '../origin-validation.js';\nimport { claudeSessionTranscriptExists, getOverdeckHome } from '../../../../lib/paths.js';\nimport { resolvePrimaryWorkspaceRepoDirSync } from '../../../../lib/project-repos.js';\nimport {\n appendAgentLifecycleLog,\n launchPanCommandDetached,\n type DetachedPanCommandDependencies,\n} from '../../../../lib/composer-commands/detached.js';\nimport {\n getAgentDir,\n getLatestSessionIdSync,\n normalizeAgentId,\n type AgentRuntimeState,\n type AgentState,\n} from '../../../../lib/agents.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { getGitHubConfig } from '../../services/tracker-config.js';\nimport { getClosedIssueIdsForReadSource } from '../../read-model.js';\nimport { recordFeatureRegistryLifecycle } from '../../../../lib/registry/feature-registry-population.js';\nimport {\n getClaudeProjectDir as getClaudeProjectDirShared,\n getActiveSessionPath as getActiveSessionPathShared,\n getAgentWorkspace as getAgentWorkspaceShared,\n getAgentJsonlPath as getAgentJsonlPathShared,\n getPendingQuestions as getPendingQuestionsShared,\n getAgentPendingQuestions as getAgentPendingQuestionsShared,\n} from '../../../../lib/agent-enrichment.js';\nimport { issueOwesReworkSync, type WorkAgentLifecycleState, type WorkAgentRecommendedAction } from '../../../../lib/work-agent-lifecycle.js';\nimport { hasCompletionMarkerForAgent } from '../../../../lib/agents/supervisor-channels.js';\nimport { emitActivityEntrySync } from '../../../../lib/activity-logger.js';\nimport { getResourceConfig, type HealthLeakedSpecialist, type SystemHealthSnapshot } from '../../services/system-health-service.js';\nimport { classifyMemoryPressure } from '../../../../lib/cloister/memory-governor.js';\nimport { capturePane } from '../../../../lib/tmux.js';\nimport type { RuntimeName } from '../../../../lib/runtimes/types.js';\nimport { normalizeFlywheelRunId } from '../../../../lib/agents/provenance.js';\n\nconst execAsync = promisify(exec);\nconst execFileAsync = promisify(execFile);\n\ntype StartAgentPhase = 'stackHealthGate' | 'guardrails' | 'spawn';\n\nexport function buildPanStartArgs(input: {\n issueId: string;\n model: string;\n harness?: RuntimeName | null;\n allowHost?: boolean;\n offBook?: boolean;\n}): string[] {\n return [\n 'start',\n input.issueId,\n '--local',\n '--model',\n input.model,\n ...(input.harness ? ['--harness', input.harness] : []),\n ...(input.allowHost ? ['--host', '--yes'] : []),\n ...(input.offBook ? ['--off-book'] : []),\n ];\n}\n\n/**\n * PAN-1985: detached-spawn helper for `pan <args>`, shared between the\n * standard work-spawn route and the restart-fresh route. The neutral launcher\n * owns process/log lifecycle; this route-compatible wrapper preserves the\n * original behavior by waiting for a successful child exit.\n */\nconst INTERNAL_STARTED_BY_TOKENS = new Set([\n 'operator:cli:pan-start',\n 'operator:cli:pan-plan',\n 'planning-auto-handoff',\n 'orphan-proposed-reconciler',\n 'workspace-rebuild-recovery',\n 'resume-agent',\n]);\n\nexport function resolveRequestedStartedBy(value: unknown, internalRequest = false): string {\n if (!internalRequest) return 'operator:dashboard';\n const token = typeof value === 'string' ? value.trim() : '';\n const flywheelRunId = token.startsWith('flywheel:') ? normalizeFlywheelRunId(token.slice('flywheel:'.length)) : undefined;\n if (flywheelRunId) return `flywheel:${flywheelRunId}`;\n if (!INTERNAL_STARTED_BY_TOKENS.has(token)) throw new Error('Invalid internal startedBy provenance token.');\n return token;\n}\n\nexport async function isInternalAgentRequest(request: HttpServerRequest.HttpServerRequest): Promise<boolean> {\n const expected = await readInternalTokenForRequest();\n if (!expected) return false;\n const provided = getHeaderFromMap(request.headers as Record<string, string | string[] | undefined>, INTERNAL_TOKEN_HEADER);\n return constantTimeTokenEqual(provided, expected);\n}\n\nexport async function spawnPanCommandDetached(input: {\n agentSessionName: string;\n issueId: string;\n role: string;\n workspacePath: string;\n args: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n}, dependencies: DetachedPanCommandDependencies = {}): Promise<string> {\n const launch = await launchPanCommandDetached(input, dependencies);\n await launch.completion;\n return launch.activityId;\n}\ntype StartAgentPhaseStatus = 'start' | 'success' | 'failure' | 'skipped';\n\nfunction emitStartAgentPhase(\n issueId: string,\n phase: StartAgentPhase,\n status: StartAgentPhaseStatus,\n reason: string,\n details: Record<string, unknown> = {},\n): void {\n const timestamp = new Date().toISOString();\n emitActivityEntrySync({\n source: 'start-agent',\n level: status === 'failure' ? 'error' : status === 'skipped' ? 'warn' : 'info',\n message: `start-agent.phase=${phase}`,\n issueId: issueId.toUpperCase(),\n details: JSON.stringify({ issueId: issueId.toUpperCase(), timestamp, phase, status, reason, ...details }),\n });\n}\n\nconst INTERNAL_TOKEN_HEADER = 'x-overdeck-internal-token';\n\nfunction constantTimeTokenEqual(provided: string | undefined, expected: string): boolean {\n if (!provided) return false;\n const providedBuffer = Buffer.from(provided, 'utf8');\n const expectedBuffer = Buffer.from(expected, 'utf8');\n if (providedBuffer.length !== expectedBuffer.length) {\n return false;\n }\n return timingSafeEqual(providedBuffer, expectedBuffer);\n}\n\nasync function readInternalTokenForRequest(): Promise<string | null> {\n const fromEnv = process.env.OVERDECK_INTERNAL_TOKEN;\n if (fromEnv && fromEnv.length > 0) return fromEnv;\n try {\n const token = (await readFile(join(getOverdeckHome(), 'internal-token'), 'utf8')).trim();\n return token.length > 0 ? token : null;\n } catch {\n return null;\n }\n}\n\nexport async function validateAgentRuntimeEventAuth(\n request: HttpServerRequest.HttpServerRequest,\n) {\n const expected = await readInternalTokenForRequest();\n if (!expected) {\n return {\n ok: false as const,\n response: jsonResponse({ success: false, error: 'internal token not configured' }, { status: 503 }),\n };\n }\n\n const provided = getHeaderFromMap(request.headers as Record<string, string | string[] | undefined>, INTERNAL_TOKEN_HEADER);\n if (constantTimeTokenEqual(provided, expected)) return { ok: true as const };\n return {\n ok: false as const,\n response: jsonResponse({ success: false, error: 'forbidden' }, { status: 403 }),\n };\n}\n\nfunction updateRegistryForAgentStart(issueId: string, workspacePath: string, agentId: string): void {\n void recordFeatureRegistryLifecycle({\n issueId,\n workspacePath,\n agentId,\n status: 'active',\n });\n}\n\n\n// ─── Shared IssueDataService singleton ───────────────────────────────────────\n\nfunction getIssueDataService(): import('../../services/issue-data-service.js').IssueDataService {\n const { getSharedIssueService } = require('../../services/issue-service-singleton.js');\n return getSharedIssueService();\n}\n\n// ─── Cache ────────────────────────────────────────────────────────────────────\n\nconst AGENTS_CACHE_TTL_MS = 5000;\nexport const agentsCache: { data: unknown[] | null; timestamp: number } = { data: null, timestamp: 0 };\n\n/** Invalidate the agents cache so the next request re-reads all agent state. */\nexport function invalidateAgentsCache(): void {\n agentsCache.data = null;\n agentsCache.timestamp = 0;\n}\n\nfunction filterClosedIssueAgents<T>(agents: T[], issues: unknown[]): T[] {\n const closedIssueIds = getClosedIssueIdsForReadSource(issues);\n if (closedIssueIds.size === 0) return agents;\n return agents.filter((agent) => {\n if (!agent || typeof agent !== 'object') return true;\n const issueId = (agent as { issueId?: unknown }).issueId;\n return typeof issueId !== 'string' || !closedIssueIds.has(issueId.toUpperCase());\n });\n}\n\n// ─── Local helpers ────────────────────────────────────────────────────────────\n\n// Read the request body as unknown JSON\nconst readJsonBody = Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const text = yield* request.text;\n try {\n return text ? (JSON.parse(text) ?? {}) : {};\n } catch {\n return {};\n }\n});\n\nfunction toAgentStatusPayload(status: AgentState['status'] | undefined): AgentStatus {\n return status === 'starting' || status === 'running' || status === 'stopped' || status === 'error'\n ? status\n : 'unknown';\n}\n\nfunction buildAgentControlEventPayload(state: AgentState, previousStatus: AgentStatus | undefined, hasLiveTmuxSession: boolean) {\n return {\n agentId: state.id,\n issueId: state.issueId,\n status: toAgentStatusPayload(state.status),\n previousStatus,\n hasLiveTmuxSession,\n stoppedByUser: state.stoppedByUser === true,\n paused: state.paused === true,\n pausedReason: state.pausedReason ?? null,\n pausedAt: state.pausedAt ?? null,\n troubled: state.troubled === true,\n troubledAt: state.troubledAt ?? null,\n consecutiveFailures: state.consecutiveFailures ?? 0,\n firstFailureInRunAt: state.firstFailureInRunAt ?? null,\n lastFailureAt: state.lastFailureAt ?? null,\n lastFailureReason: state.lastFailureReason ?? null,\n lastFailureNextRetryAt: state.lastFailureNextRetryAt ?? null,\n };\n}\n\nfunction buildAgentGateFailureSnapshot(state: Partial<AgentState>) {\n return {\n stoppedByUser: state.stoppedByUser === true,\n paused: state.paused === true,\n pausedReason: state.pausedReason ?? null,\n pausedAt: state.pausedAt ?? null,\n troubled: state.troubled === true,\n troubledAt: state.troubledAt ?? null,\n consecutiveFailures: state.consecutiveFailures ?? 0,\n firstFailureInRunAt: state.firstFailureInRunAt ?? null,\n lastFailureAt: state.lastFailureAt ?? null,\n lastFailureReason: state.lastFailureReason ?? null,\n lastFailureNextRetryAt: state.lastFailureNextRetryAt ?? null,\n };\n}\n\nfunction buildStoppedAgentLifecycle(\n agentOrIssueId: string,\n state: Partial<AgentState>,\n runtimeData: Partial<AgentRuntimeState>,\n): WorkAgentLifecycleState {\n const agentId = normalizeAgentId(agentOrIssueId);\n const hasAgentState = true;\n const hasLiveTmuxSession = false;\n // claudeSessionId only covers claude-code agents — codex agents keep their\n // resumable thread in codex-thread-id, which getLatestSessionIdSync resolves\n // (PAN-1988). Without the fallback the listing reports canResumeSession=false\n // for every stopped codex agent and the UI never offers Resume.\n const sessionId = getLatestSessionIdSync(agentId) ?? runtimeData.claudeSessionId ?? null;\n const hasSavedSession = !!sessionId;\n const hasWorkspace = typeof state.workspace === 'string' && state.workspace.length > 0;\n const hasResumableTranscript = !sessionId\n || (!!state.harness && state.harness !== 'claude-code')\n || !state.workspace\n || claudeSessionTranscriptExists(state.workspace, sessionId);\n const agentStatus = state.status || 'unknown';\n const runtime = runtimeData.state || 'uninitialized';\n const isCompleted = runtimeData.resolution === 'completed';\n const isPlaceholder = agentStatus === 'starting' && typeof state.model === 'string' && state.model.startsWith('pending-');\n const isStopped = agentStatus === 'stopped' || agentStatus === 'error' || isCompleted || runtime === 'stopped' || runtime === 'idle' || runtime === 'suspended';\n const isRunning = false;\n const isCrashed = (agentStatus === 'running' || isPlaceholder) && !hasLiveTmuxSession;\n const isRunningButStuck = false;\n const hasResumableBackingState = hasAgentState && hasWorkspace && !isPlaceholder;\n const handedOff = typeof state.id === 'string' && state.id.length > 0\n ? hasCompletionMarkerForAgent(state as AgentState)\n : false;\n // PAN-3555: an owed-rework handoff is resumable again — mirror the canonical door.\n const owesRework = handedOff && issueOwesReworkSync(state.issueId);\n const canWarmResumeAfterHandoff = owesRework && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed);\n const isOrphaned = !hasLiveTmuxSession && (\n (hasSavedSession && !hasResumableBackingState)\n || (hasAgentState && (!hasWorkspace || isPlaceholder))\n );\n const requiresSessionResetBeforeFreshStart = hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed);\n\n let recommendedAction: WorkAgentRecommendedAction = 'start';\n let reason: string | undefined;\n\n if (isOrphaned) {\n recommendedAction = 'start';\n reason = hasSavedSession\n ? `Agent ${agentId} has stale/orphaned session metadata without a resumable workspace-backed agent state. Start Agent should create a fresh session.`\n : `Agent ${agentId} is an orphaned placeholder/stale record. Start Agent should create a fresh session.`;\n } else if (canWarmResumeAfterHandoff) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} handed off its work but the pipeline now owes it rework (failed verification, blocked/failed review, or failed test). Use 'pan resume ${agentOrIssueId}' to continue its warm session with the pending feedback (PAN-3555).`;\n } else if (handedOff) {\n // PAN-3334: mirror getWorkAgentLifecycleState — a handed-off agent is never\n // offered a plain resume; there is nothing to continue.\n recommendedAction = 'none';\n reason = `Agent ${agentId} finished and handed off its work (completion marker on disk) — there is nothing to resume. The session is preserved for inspection; message it with 'pan tell ${agentOrIssueId}', or start over with 'pan start ${agentOrIssueId} --fresh'.`;\n } else if (requiresSessionResetBeforeFreshStart) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a resumable Claude session. Use 'pan resume ${agentOrIssueId}' to continue it, or run 'pan reset-session ${agentOrIssueId}' before starting a new session.`;\n } else if (hasSavedSession && !hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed)) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has a saved Claude session id but its transcript is missing on disk (jsonl-missing). Start Agent will create a fresh session in the existing workspace.`;\n } else if (hasAgentState && !hasSavedSession && isStopped) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} is stopped and has no saved Claude session. Start Agent will create a fresh session in the existing workspace.`;\n }\n\n return {\n agentId,\n hasAgentState,\n hasLiveTmuxSession,\n hasSavedSession,\n hasResumableTranscript,\n hasWorkspace,\n isPlaceholder,\n isOrphaned,\n isRunning,\n isRunningButStuck,\n isStopped,\n isCompleted,\n isCrashed,\n handedOff,\n owesRework,\n runtimeState: runtime,\n agentStatus,\n canStartFresh: !requiresSessionResetBeforeFreshStart || isOrphaned,\n canResumeSession: hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed) && (!handedOff || owesRework),\n canRestartWithContext: hasAgentState && hasWorkspace,\n canResetSession: hasSavedSession && hasResumableTranscript && hasResumableBackingState,\n requiresSessionResetBeforeFreshStart,\n recommendedAction,\n reason,\n };\n}\n\nasync function readRemoteAgentState(agentId: string): Promise<Record<string, unknown>> {\n const remoteStateFile = join(homedir(), '.overdeck', 'agents', agentId, 'remote-state.json');\n if (!existsSync(remoteStateFile)) return {};\n try {\n return JSON.parse(await readFile(remoteStateFile, 'utf-8')) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nasync function captureAgentOutputBeforeKill(agentId: string): Promise<void> {\n const output = await Effect.runPromise(\n capturePane(agentId, 5000).pipe(Effect.catch(() => Effect.succeed(''))),\n );\n if (!output) return;\n\n const agentDir = getAgentDir(agentId);\n await mkdir(agentDir, { recursive: true });\n await writeFile(join(agentDir, 'output.log'), output);\n}\n\nfunction buildHostOverrideConfirmation(issueId: string): string {\n return `I understand this bypasses workspace isolation for ${issueId.toUpperCase()}`;\n}\n\nfunction getProjectPath(linearProjectId?: string, issuePrefix?: string): string {\n if (issuePrefix) {\n const issueId = `${issuePrefix}-1`;\n const resolved = resolveProjectFromIssueSync(issueId);\n if (resolved) return resolved.projectPath;\n const config = getGitHubConfig();\n if (config) {\n for (const { owner, repo, prefix } of config.repos) {\n const repoPrefix = prefix || repo.toUpperCase().replace(/-CLI$/, '').replace(/-/g, '');\n if (repoPrefix.toUpperCase() === issuePrefix.toUpperCase()) {\n const possiblePaths = [\n join(homedir(), 'Projects', repo),\n join(homedir(), 'Projects', repo.replace(/-cli$/, '')),\n join(homedir(), 'Projects', owner, repo),\n ];\n for (const path of possiblePaths) {\n if (existsSync(path)) return path;\n }\n }\n }\n }\n }\n return join(homedir(), 'Projects');\n}\n\nasync function getWorkspaceLocation(issueId: string): Promise<'local' | 'remote' | undefined> {\n try {\n const workspacesDir = join(homedir(), '.overdeck', 'workspaces');\n const variations = [issueId.toLowerCase(), issueId.toUpperCase(), issueId];\n for (const v of variations) {\n const yamlPath = join(workspacesDir, `${v}.yaml`);\n if (existsSync(yamlPath)) {\n const content = await readFile(yamlPath, 'utf-8');\n if (content.includes('location: remote')) return 'remote';\n return 'local';\n }\n }\n } catch {}\n return undefined;\n}\n\nasync function getGitStatusAsync(issueId: string, workspacePath: string): Promise<{ branch: string; uncommittedFiles: number; latestCommit: string } | null> {\n try {\n if (!existsSync(workspacePath)) return null;\n const repoDir = resolvePrimaryWorkspaceRepoDirSync(issueId, workspacePath);\n const [branchResult, uncommittedResult, commitResult] = await Promise.all([\n execAsync('git rev-parse --abbrev-ref HEAD 2>/dev/null || echo \"\"', { cwd: repoDir }),\n execAsync('git status --porcelain 2>/dev/null | wc -l', { cwd: repoDir }),\n execAsync('git log -1 --pretty=format:\"%s\" 2>/dev/null || echo \"\"', { cwd: repoDir }),\n ]);\n const branch = branchResult.stdout.trim();\n const uncommitted = uncommittedResult.stdout.trim();\n const latestCommit = commitResult.stdout.trim();\n if (!branch) return null;\n return {\n branch,\n uncommittedFiles: parseInt(uncommitted) || 0,\n latestCommit: latestCommit.slice(0, 60) + (latestCommit.length > 60 ? '...' : ''),\n };\n } catch {\n return null;\n }\n}\n\ninterface SpawnGuardrailAdvisory {\n severity: 'warning' | 'critical';\n code: 'memory_pressure' | 'agent_capacity' | 'leaked_specialists' | 'health_snapshot_unavailable';\n message: string;\n}\n\nexport interface SpawnGuardrailDecision {\n blocked: boolean;\n requiresAcknowledgement: boolean;\n status: number;\n error?: string;\n hint?: string;\n warnings: SpawnGuardrailAdvisory[];\n health: Pick<SystemHealthSnapshot, 'severity' | 'summary' | 'reasons' | 'leakedSpecialists' | 'freshness'>;\n}\n\nfunction formatLeakedSpecialistSummary(leaked: HealthLeakedSpecialist[]): string {\n return leaked\n .slice(0, 3)\n .map((item) => `${item.name} (${item.currentIssue})`)\n .join(', ');\n}\n\nfunction resolveAgentCountEnv(varName: string, fallback: number): number {\n const raw = process.env[varName];\n if (raw == null || raw.trim() === '') return fallback;\n const parsed = Number(raw);\n return Number.isFinite(parsed) ? Math.max(1, Math.floor(parsed)) : fallback;\n}\n\nexport interface AgentStartGateDecision {\n success: false;\n blocked: true;\n skipped: true;\n error: string;\n hint: string;\n agentId: string;\n paused: boolean;\n troubled: boolean;\n}\n\nexport function evaluateAgentStartGate(\n agentId: string,\n state: Pick<AgentState, 'paused' | 'pausedReason' | 'troubled' | 'consecutiveFailures'> | null | undefined,\n): AgentStartGateDecision | null {\n if (state?.paused === true) {\n const reason = state.pausedReason ? ` (${state.pausedReason})` : '';\n return {\n success: false,\n blocked: true,\n skipped: true,\n error: `Agent ${agentId} is paused${reason}.`,\n hint: `Run pan unpause ${agentId} before starting it from the dashboard.`,\n agentId,\n paused: true,\n troubled: state.troubled === true,\n };\n }\n\n if (state?.troubled === true) {\n const failures = state.consecutiveFailures ?? 0;\n return {\n success: false,\n blocked: true,\n skipped: true,\n error: `Agent ${agentId} is troubled (${failures} failure${failures === 1 ? '' : 's'}).`,\n hint: `Investigate the crash cause, then run pan untroubled ${agentId} before starting it from the dashboard.`,\n agentId,\n paused: false,\n troubled: true,\n };\n }\n\n return null;\n}\n\nexport function hasActiveAgentGateOrRetry(\n state: Pick<AgentState, 'paused' | 'troubled' | 'lastFailureNextRetryAt'>,\n nowMs: number = Date.now(),\n): boolean {\n if (state.paused === true || state.troubled === true) return true;\n if (!state.lastFailureNextRetryAt) return false;\n const retryAtMs = Date.parse(state.lastFailureNextRetryAt);\n return Number.isFinite(retryAtMs) && retryAtMs > nowMs;\n}\n\nexport function evaluateSpawnGuardrails(health: SystemHealthSnapshot): SpawnGuardrailDecision {\n const warnings: SpawnGuardrailAdvisory[] = [];\n if (health.freshness.status !== 'fresh') {\n const message = health.freshness.status === 'stale'\n ? `System health evidence is stale: ${health.freshness.reason.message}`\n : 'System health is still measuring; fresh admission evidence is not available yet.';\n warnings.push({\n severity: 'critical',\n code: 'health_snapshot_unavailable',\n message,\n });\n return {\n blocked: true,\n requiresAcknowledgement: false,\n status: 503,\n error: message,\n hint: 'Wait for a fresh system health assessment before starting this agent.',\n warnings,\n health: {\n severity: health.severity,\n summary: health.summary,\n reasons: health.reasons,\n leakedSpecialists: health.leakedSpecialists,\n freshness: health.freshness,\n },\n };\n }\n\n const availableGb = Math.round((health.summary.availableMemoryBytes / (1024 ** 3)) * 10) / 10;\n const workAgentCount = health.admission.admittedWorkAgentCount;\n const leakedSpecialists = health.leakedSpecialists;\n const resourceConfig = getResourceConfig();\n const hardWorkAgentLimit = resolveAgentCountEnv('PAN_AGENT_BLOCK_COUNT', resourceConfig.agentBlockCount);\n const warnWorkAgentLimit = resolveAgentCountEnv('PAN_AGENT_WARN_COUNT', resourceConfig.agentWarnCount);\n\n const memoryBand = classifyMemoryPressure(health.summary.availableMemoryBytes, {\n warningBytes: health.thresholds.memoryAvailableWarningBytes,\n criticalBytes: health.thresholds.memoryAvailableCriticalBytes,\n });\n if (memoryBand === 'hard') {\n warnings.push({\n severity: 'critical',\n code: 'memory_pressure',\n message: `Available RAM is critically low (${availableGb} GB).`,\n });\n } else if (memoryBand === 'soft') {\n warnings.push({\n severity: 'warning',\n code: 'memory_pressure',\n message: `Available RAM is tight (${availableGb} GB).`,\n });\n }\n\n if (workAgentCount >= hardWorkAgentLimit) {\n warnings.push({\n severity: 'warning',\n code: 'agent_capacity',\n message: `Work agent count is at the configured ceiling (${workAgentCount}/${hardWorkAgentLimit}).`,\n });\n } else if (workAgentCount >= warnWorkAgentLimit) {\n warnings.push({\n severity: 'warning',\n code: 'agent_capacity',\n message: `Work agent count is high (${workAgentCount}/${hardWorkAgentLimit}).`,\n });\n }\n\n if (leakedSpecialists.length > 0) {\n warnings.push({\n severity: health.summary.availableMemoryBytes < health.thresholds.memoryAvailableCriticalBytes ? 'critical' : 'warning',\n code: 'leaked_specialists',\n message: `Leaked specialist sessions detected: ${formatLeakedSpecialistSummary(leakedSpecialists)}${leakedSpecialists.length > 3 ? `, +${leakedSpecialists.length - 3} more` : ''}.`,\n });\n }\n\n const blockingWarnings = warnings.filter((warning) => warning.severity === 'critical');\n if (blockingWarnings.length > 0) {\n const hasLeakedSpecialists = leakedSpecialists.length > 0;\n return {\n blocked: true,\n requiresAcknowledgement: false,\n status: 429,\n error: blockingWarnings[0]?.message ?? 'System health is blocking new agent spawns.',\n hint: hasLeakedSpecialists\n ? 'Clean up leaked specialist sessions first, then retry the spawn.'\n : 'Reduce memory pressure or active work-agent count before retrying.',\n warnings,\n health: {\n severity: health.severity,\n summary: health.summary,\n reasons: health.reasons,\n leakedSpecialists: health.leakedSpecialists,\n freshness: health.freshness,\n },\n };\n }\n\n return {\n blocked: false,\n requiresAcknowledgement: warnings.length > 0,\n status: warnings.length > 0 ? 409 : 200,\n hint: warnings.length > 0 ? 'Acknowledge the system health warnings before starting this agent.' : undefined,\n warnings,\n health: {\n severity: health.severity,\n summary: health.summary,\n reasons: health.reasons,\n leakedSpecialists: health.leakedSpecialists,\n freshness: health.freshness,\n },\n };\n}\n\n// Shared enrichment utilities (PAN-440) — aliases for readability\nconst getClaudeProjectDir = getClaudeProjectDirShared;\nconst getActiveSessionPath = getActiveSessionPathShared;\nconst getAgentWorkspace = getAgentWorkspaceShared;\nconst getAgentJsonlPath = getAgentJsonlPathShared;\nconst getPendingQuestions = getPendingQuestionsShared;\nconst getAgentPendingQuestions = getAgentPendingQuestionsShared;\n\nfunction flyExecCmd(vmName: string, command: string): string {\n const appName = vmName.replace(/\\/.*$/, ''); // simplified: use vmName as app name\n return `fly ssh console -a ${appName} -C ${JSON.stringify(command)}`;\n}\n\nexport {\n execAsync,\n execFileAsync,\n constantTimeTokenEqual,\n emitStartAgentPhase,\n appendAgentLifecycleLog,\n updateRegistryForAgentStart,\n getIssueDataService,\n AGENTS_CACHE_TTL_MS,\n filterClosedIssueAgents,\n readJsonBody,\n toAgentStatusPayload,\n buildAgentControlEventPayload,\n buildAgentGateFailureSnapshot,\n buildStoppedAgentLifecycle,\n readRemoteAgentState,\n captureAgentOutputBeforeKill,\n buildHostOverrideConfirmation,\n getProjectPath,\n getWorkspaceLocation,\n getGitStatusAsync,\n resolveAgentCountEnv,\n formatLeakedSpecialistSummary,\n getClaudeProjectDir,\n getActiveSessionPath,\n getAgentWorkspace,\n getAgentJsonlPath,\n getPendingQuestions,\n getAgentPendingQuestions,\n flyExecCmd,\n};\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter } from 'effect/unstable/http';\n\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport {\n getAgentState,\n getAgentRuntimeState,\n listAgentStates,\n type AgentState,\n} from '../../../../lib/agents.js';\nimport { getReviewStatusSync } from '../../../../lib/review-status.js';\nimport { computeAgentEnrichment, isBlockedOnPendingInput } from '../../../../lib/agent-enrichment.js';\nimport { normalizeAwaitingInputPrompt } from '../../../../lib/agent-input-detection.js';\nimport { getWorkAgentLifecycleState } from '../../../../lib/work-agent-lifecycle.js';\nimport { resolveAgentGitInfo } from '../../services/git-info.js';\nimport { listSessions, sessionExists } from '../../../../lib/tmux.js';\nimport {\n AGENTS_CACHE_TTL_MS,\n agentsCache,\n buildAgentGateFailureSnapshot,\n buildStoppedAgentLifecycle,\n filterClosedIssueAgents,\n getGitStatusAsync,\n getIssueDataService,\n getWorkspaceLocation,\n hasActiveAgentGateOrRetry,\n readRemoteAgentState,\n} from './shared.js';\n\n/**\n * PAN-3070 — the health status a live agent row reports.\n *\n * A live tmux session used to mean `healthy` unconditionally, so an agent frozen\n * for hours on an unanswered tool-permission prompt was reported\n * `status: healthy` / `resolution: working` while the Decisions surface\n * simultaneously showed it as needing the operator. Two surfaces answering \"is\n * this agent stuck?\" from different evidence, and this one stale.\n *\n * `warning` is the value `src/lib/health.ts` already reports for an agent\n * waiting on a human, and it stays inside the fleet-view status set, so a\n * blocked agent is flagged without disappearing from any existing list.\n */\nexport function liveAgentHealthStatus(enrichment: {\n hasPendingQuestion?: boolean;\n pendingQuestionReason?: string;\n}): 'healthy' | 'warning' {\n return isBlockedOnPendingInput(enrichment) ? 'warning' : 'healthy';\n}\n\n// ─── Route: GET /api/agents ───────────────────────────────────────────────────\n\nexport const getAgentsRoute = HttpRouter.add(\n 'GET',\n '/api/agents',\n httpHandler(Effect.gen(function* () {\n const now = Date.now();\n\n if (agentsCache.data && (now - agentsCache.timestamp) < AGENTS_CACHE_TTL_MS) {\n return jsonResponse(agentsCache.data);\n }\n\n const sessions = yield* listSessions();\n const sessionByName = new Map(sessions.map((session) => [session.name, session]));\n const registeredStates = listAgentStates()\n .filter((state) => state.id.startsWith('agent-') || state.id.startsWith('planning-') || state.id.startsWith('strike-'));\n\n const allAgents = (yield* Effect.promise(() => Promise.all(\n registeredStates.map(async (state) => {\n const name = state.id;\n const isPlanning = name.startsWith('planning-');\n const isStrike = name.startsWith('strike-');\n const issueId = state.issueId?.toUpperCase() ||\n (isPlanning ? name.replace('planning-', '') : isStrike ? name.replace('strike-', '') : name.replace('agent-', '')).toUpperCase();\n const session = sessionByName.get(name);\n const remoteState = await readRemoteAgentState(name);\n const isRemote = remoteState.location === 'remote';\n const runtimeData = await Effect.runPromise(getAgentRuntimeState(name));\n const startedAt = state.startedAt || (session ? new Date(session.created).toISOString() : new Date().toISOString());\n const healthFile = join(homedir(), '.overdeck', 'agents', name, 'health.json');\n let health: any = { killCount: 0 };\n if (existsSync(healthFile)) {\n try { health = { ...health, ...JSON.parse(await readFile(healthFile, 'utf-8')) }; } catch {}\n }\n\n if (state.status === 'stopped') {\n const stoppedTimestamp = state.stoppedAt || runtimeData?.lastActivity || state.lastActivity;\n const stoppedAt = stoppedTimestamp ? new Date(stoppedTimestamp) : null;\n const reviewStatus = getReviewStatusSync(issueId);\n const keepStoppedAgentVisible =\n hasActiveAgentGateOrRetry(state, now) ||\n (\n !!reviewStatus &&\n reviewStatus.mergeStatus !== 'merged' &&\n (\n !!reviewStatus.prUrl ||\n reviewStatus.readyForMerge === true ||\n reviewStatus.reviewStatus !== 'pending' ||\n reviewStatus.testStatus !== 'pending' ||\n reviewStatus.mergeStatus === 'failed'\n )\n );\n if (stoppedAt && (now - stoppedAt.getTime()) > 60 * 60 * 1000 && !keepStoppedAgentVisible) return null;\n const lifecycle = buildStoppedAgentLifecycle(name, state, runtimeData ?? {});\n const needsInput = runtimeData?.resolution === 'needs_input';\n const pendingQuestionPrompt = needsInput\n ? normalizeAwaitingInputPrompt(\n runtimeData?.waitingNotification ||\n 'Agent stopped because it needs human input or hit a blocker',\n )\n : undefined;\n const pendingQuestionReason = needsInput\n ? runtimeData?.waitingReason || 'other'\n : undefined;\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: 'stopped' as const,\n startedAt,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace: state.workspace || null,\n workspaceLocation: isRemote ? 'remote' : 'local',\n // A stopped agent never shells `git` against its workspace (no\n // process cost for a row nobody is actively watching), so this\n // can only report the persisted branch, not live uncommitted/\n // commit detail — better than the unconditional null every\n // stopped agent used to report regardless of what it stored.\n git: state.branch ? { branch: state.branch, uncommittedFiles: 0, latestCommit: '' } : null,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasPendingQuestion: needsInput,\n pendingQuestionCount: 0,\n pendingQuestionPrompt,\n pendingQuestionReason,\n resolution: runtimeData?.resolution || 'working',\n resolutionCount: runtimeData?.resolutionCount || 0,\n hasSession: lifecycle.canResumeSession,\n lifecycle,\n ...(isRemote ? { remote: true, vmName: remoteState.vmName } : {}),\n };\n }\n\n if (state.status === 'starting') {\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: 'starting' as const,\n startedAt,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace: state.workspace || null,\n workspaceLocation: isRemote ? 'remote' : 'local',\n git: null,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasPendingQuestion: false,\n pendingQuestionCount: 0,\n message: (state as { message?: string }).message || 'Starting...',\n ...(isRemote ? { remote: true, vmName: remoteState.vmName } : {}),\n };\n }\n\n if (state.status === 'error') {\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: 'error' as const,\n startedAt,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace: state.workspace || null,\n workspaceLocation: isRemote ? 'remote' : 'local',\n git: null,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasPendingQuestion: false,\n pendingQuestionCount: 0,\n error: state.lastFailureReason || 'Unknown error',\n ...(isRemote ? { remote: true, vmName: remoteState.vmName } : {}),\n };\n }\n\n if (!session && !isRemote) {\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: 'unknown' as const,\n startedAt,\n lastActivity: runtimeData?.lastActivity || state.lastActivity,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace: state.workspace || null,\n workspaceLocation: 'local' as const,\n git: null,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasLiveTmuxSession: false,\n hasPendingQuestion: false,\n pendingQuestionCount: 0,\n lastFailureReason: state.lastFailureReason || 'No live tmux session found for registered agent',\n resolution: runtimeData?.resolution || 'working',\n resolutionCount: runtimeData?.resolutionCount || 0,\n };\n }\n\n const issueReviewStatus = getReviewStatusSync(issueId);\n const hasActiveSpecialist = issueReviewStatus?.reviewStatus === 'reviewing'\n || issueReviewStatus?.testStatus === 'testing'\n || issueReviewStatus?.mergeStatus === 'merging';\n const enrichment = await Effect.runPromise(computeAgentEnrichment(name, startedAt, hasActiveSpecialist));\n const workspaceLocation = isRemote ? 'remote' : await getWorkspaceLocation(issueId);\n const workspace = isRemote && remoteState.vmName\n ? `/workspace (${String(remoteState.vmName)})`\n : state.workspace || null;\n const gitStatus = workspace && !isRemote ? await getGitStatusAsync(issueId, workspace) : null;\n\n let contextPercent: number | null = null;\n let initialContextPercent: number | null = null;\n const agentCtxDir = join(homedir(), '.overdeck', 'agents', name);\n try {\n const ctxFile = join(agentCtxDir, 'context-pct');\n contextPercent = parseInt((await readFile(ctxFile, 'utf-8').catch(() => '')).trim(), 10) || null;\n const initCtxFile = join(agentCtxDir, 'initial-context-pct');\n initialContextPercent = parseInt((await readFile(initCtxFile, 'utf-8').catch(() => '')).trim(), 10) || null;\n } catch {}\n\n const blockedOnPendingInput = isBlockedOnPendingInput(enrichment);\n\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: liveAgentHealthStatus(enrichment),\n startedAt,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace,\n workspaceLocation,\n git: gitStatus,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasLiveTmuxSession: true,\n hasPendingQuestion: enrichment.hasPendingQuestion,\n pendingQuestionCount: enrichment.pendingQuestionCount,\n pendingQuestionPrompt: enrichment.pendingQuestionPrompt,\n pendingQuestionReason: enrichment.pendingQuestionReason,\n pendingInputCount: enrichment.pendingInputCount,\n pendingInputKinds: enrichment.pendingInputKinds,\n pendingAskUserQuestion: enrichment.pendingAskUserQuestion,\n // PAN-3070 — the detection wins over the runtime resolution here\n // too: `runtimeData.resolution` is written by the stop hook and\n // stays at whatever it last was, so a frozen agent reported\n // `working` even once the enrichment knew better.\n resolution: blockedOnPendingInput\n ? 'needs_input'\n : (runtimeData?.resolution || enrichment.resolution || 'working'),\n resolutionCount: runtimeData?.resolutionCount || enrichment.resolutionCount || 0,\n contextPercent,\n initialContextPercent,\n ...(isRemote ? { remote: true, vmName: remoteState.vmName } : {}),\n };\n }),\n ))).filter(Boolean);\n const visibleAgents = filterClosedIssueAgents(allAgents, getIssueDataService().getIssues());\n agentsCache.data = visibleAgents;\n agentsCache.timestamp = now;\n return jsonResponse(visibleAgents);\n })),\n);\n\n// ─── Route: GET /api/agents/:id/git-info ─────────────────────────────────────\n//\n// Branch + worktree status for the agent's workspace (PAN-1523). Used by\n// AgentOutputPanel to render the Local/Worktree/Drifted chip in the panel\n// header. Work agents don't have a conversation row to enrich, so the panel\n// queries this dedicated endpoint instead.\n\n/**\n * True when the agent has a concrete workspace + issue we can evaluate git\n * state for. When false, the git-info route must NOT claim the worktree is\n * missing — an unresolvable session id (e.g. a legacy / JSONL-only \"Planning\n * state\" node) or an agent that never got a workspace is \"unknown\", not\n * \"workspace gone from disk\". Conflating the two made such nodes falsely render\n * \"Worktree missing\" in the SessionPanel chip (PAN-1718).\n */\nexport function agentHasResolvableWorkspace(\n agentState: AgentState | null,\n): agentState is AgentState {\n return Boolean(agentState?.workspace && agentState.issueId);\n}\n\n/**\n * Benign git-info response for a session we cannot resolve to a workspace-bound\n * agent. workspaceMissing is false (not true): we have no path to stat, so we\n * cannot assert the worktree is gone. The frontend chip hides on this shape\n * (showChip = actualBranch || workspaceMissing). The genuine on-disk\n * \"workspace missing\" case is detected separately by resolveAgentGitInfo, which\n * stats the real path. See PAN-1718.\n */\nexport const UNRESOLVABLE_AGENT_GIT_INFO = {\n actualBranch: null,\n branchDrifted: false,\n workspaceMissing: false,\n expectedBranch: null,\n} as const;\n\nexport const getAgentGitInfoRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/git-info',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ error: 'missing agent id' }, { status: 400 });\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentHasResolvableWorkspace(agentState)) {\n // PAN-1718: unknown session id / no workspace bound → \"unknown\", not\n // \"worktree missing\". Return the benign shape so the chip hides instead of\n // flashing a false alarm. Real on-disk absence is caught by\n // resolveAgentGitInfo below.\n return jsonResponse(UNRESOLVABLE_AGENT_GIT_INFO);\n }\n\n const expectedBranch = `feature/${agentState.issueId.toLowerCase()}`;\n const info = yield* Effect.promise(() =>\n resolveAgentGitInfo(agentState.workspace as string, expectedBranch),\n );\n return jsonResponse({\n ...info,\n expectedBranch,\n workspacePath: agentState.workspace,\n });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/tmux-alive ──────────────────────────────────\n\nexport const getAgentTmuxAliveRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/tmux-alive',\n Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const agentId = params['id'] ?? '';\n const alive = yield* sessionExists(agentId);\n return jsonResponse({ alive });\n }),\n);\n\n// ─── Route: GET /api/agents/:id/has-session ─────────────────────────────────\n// Returns whether a stopped agent has a resumable Claude session.\n\nexport const getAgentHasSessionRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/has-session',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const lifecycle = yield* getWorkAgentLifecycleState(id);\n return jsonResponse({\n hasSession: lifecycle.canResumeSession,\n lifecycle,\n });\n })),\n);\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { ConversationResponse } from '@overdeck/contracts';\nimport { Effect, Option } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { encodeClaudeProjectDir } from '../../../../lib/paths.js';\nimport {\n getActivity,\n getAgentState,\n} from '../../../../lib/agents.js';\nimport { capturePane } from '../../../../lib/tmux.js';\nimport { parseEntireConversation } from '../../services/conversation-service.js';\nimport { parsePiConversationMessages } from '../../services/pi-conversation-parser.js';\nimport { parseOhmypiConversationMessages } from '../../services/ohmypi-conversation-parser.js';\nimport { parseCodexConversationMessages } from '../../services/codex-conversation-parser.js';\nimport { parseAcpConversationMessages } from '../../services/acp-conversation-parser.js';\nimport {\n readLauncherPinnedSessionId,\n resolvePiSessionPath,\n resolveCodexRolloutPath,\n resolveAcpTranscriptPath,\n resolveAgentHarness,\n} from '../jsonl-resolver.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport {\n execAsync,\n getAgentJsonlPath,\n getAgentWorkspace,\n} from './shared.js';\n\n// ─── Route: GET /api/agents/:id/output ───────────────────────────────────────\n\nexport const getAgentOutputRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/output',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n const lines = Option.isSome(urlOpt) ? (urlOpt.value.searchParams.get('lines') ?? '100') : '100';\n\n return yield* Effect.promise(async () => {\n try {\n const agentStateDir = join(homedir(), '.overdeck', 'agents', id);\n const remoteStateFile = join(agentStateDir, 'remote-state.json');\n let isRemote = false;\n let vmName = '';\n\n if (existsSync(remoteStateFile)) {\n try {\n const state = JSON.parse(await readFile(remoteStateFile, 'utf-8'));\n if (state.location === 'remote' && state.vmName) {\n isRemote = true;\n vmName = state.vmName;\n }\n } catch {}\n }\n\n let stdout: string;\n if (isRemote && vmName) {\n const { getRemoteAgentOutput } = await import('../../../../lib/remote/remote-agents.js');\n stdout = await getRemoteAgentOutput(id, vmName, parseInt(String(lines), 10) || 100);\n } else {\n stdout = await Effect.runPromise(capturePane(id, parseInt(String(lines), 10) || 100));\n }\n\n if (!stdout || stdout.trim() === '' || stdout.trim() === 'Session not found') {\n const savedLog = join(agentStateDir, 'output.log');\n const logContent = await readFile(savedLog, 'utf-8').catch(() => null);\n if (logContent) {\n const logLines = logContent.split('\\n');\n const numLines = parseInt(String(lines), 10) || 100;\n stdout = logLines.slice(-numLines).join('\\n');\n }\n }\n\n if (stdout?.trim() === 'Session not found') {\n stdout = '';\n }\n\n return jsonResponse({ output: stdout });\n } catch (error: unknown) {\n // Try saved log on error\n try {\n const agentStateDir = join(homedir(), '.overdeck', 'agents', id);\n const savedLog = join(agentStateDir, 'output.log');\n const logContent = await readFile(savedLog, 'utf-8').catch(() => null);\n if (logContent) return jsonResponse({ output: logContent });\n } catch {}\n return jsonResponse({ output: '' });\n }\n })\n })),\n);\n\n// ─── Route: GET /api/agents/:id/conversation ─────────────────────────────────\n\nconst EMPTY_CONVERSATION: ConversationResponse = { messages: [], workLog: [], streaming: false, totalCost: 0, byteOffset: 0 };\n\n/**\n * Resolve and parse an agent's conversation JSONL file.\n * Exported for unit testing — the Effect route layer is not directly unit-testable.\n *\n * Dispatches on harness so Pi and Codex agents get their native parsers (PAN-2012).\n * For claude-code agents, tries the launcher-pinned --session-id first (the exact\n * session the Terminal tab attaches to) before falling back to mtime-based pick\n * (PAN-2011). This makes the Conversation tab match the Terminal tab by construction.\n */\nexport async function buildConversationResponse(id: string): Promise<ConversationResponse> {\n try {\n const harness = await resolveAgentHarness(id);\n\n if (harness === 'ohmypi') {\n const sessionFile = await resolvePiSessionPath(id);\n if (!sessionFile || !existsSync(sessionFile)) return EMPTY_CONVERSATION;\n const result = await parseOhmypiConversationMessages(sessionFile);\n return { ...result, streaming: false };\n }\n\n if (harness === 'pi') {\n const sessionFile = await resolvePiSessionPath(id);\n if (!sessionFile || !existsSync(sessionFile)) return EMPTY_CONVERSATION;\n const result = await parsePiConversationMessages(sessionFile);\n return { ...result, streaming: false };\n }\n\n if (harness === 'codex') {\n const sessionFile = await resolveCodexRolloutPath(id);\n if (!sessionFile || !existsSync(sessionFile)) return EMPTY_CONVERSATION;\n const result = await parseCodexConversationMessages(sessionFile);\n return { ...result, streaming: false };\n }\n\n if (harness === 'acp') {\n const sessionFile = await resolveAcpTranscriptPath(id);\n if (!sessionFile || !existsSync(sessionFile)) return EMPTY_CONVERSATION;\n const result = await parseAcpConversationMessages(sessionFile);\n return {\n ...result,\n messages: result.messages.map((message) => message.role === 'assistant'\n ? {\n ...message,\n completedAt: message.completedAt ?? message.createdAt,\n streaming: false,\n }\n : message),\n streaming: false,\n };\n }\n\n // claude-code (default): try launcher-pinned session ID first (ground truth),\n // then fall back to mtime-based pick.\n let jsonlPath: string | null = null;\n const pinnedSessionId = await readLauncherPinnedSessionId(id);\n if (pinnedSessionId) {\n const workspace = await Effect.runPromise(getAgentWorkspace(id));\n if (workspace) {\n const candidate = join(\n homedir(), '.claude', 'projects',\n encodeClaudeProjectDir(workspace),\n `${pinnedSessionId}.jsonl`,\n );\n if (existsSync(candidate)) jsonlPath = candidate;\n }\n }\n if (!jsonlPath) {\n jsonlPath = await Effect.runPromise(getAgentJsonlPath(id));\n }\n\n if (!jsonlPath || !existsSync(jsonlPath)) return EMPTY_CONVERSATION;\n // parseEntireConversation, not parseConversationMessages: a single parse caps\n // at MAX_READ_BYTES (10 MB) and would drop the most recent turns of a larger\n // transcript (PAN-1989). This one-shot endpoint must return the whole file.\n const result = await parseEntireConversation(jsonlPath);\n // Force streaming: false — tmux session is dead, any \"streaming\" state is stale\n return { ...result, streaming: false };\n } catch (err) {\n console.error('[conversation] failed for', id, err);\n return EMPTY_CONVERSATION;\n }\n}\n\nexport const getAgentConversationRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/conversation',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n return yield* Effect.promise(async () => jsonResponse(await buildConversationResponse(id)));\n })),\n);\n\n// ─── Route: GET /api/agents/:id/activity ─────────────────────────────────────\n\nexport const getAgentActivityRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/activity',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n const limitStr = Option.isSome(urlOpt) ? (urlOpt.value.searchParams.get('limit') ?? '100') : '100';\n const limit = parseInt(limitStr) || 100;\n\n const activity = getActivity(id, limit);\n return jsonResponse({ activity });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/files ────────────────────────────────────────\n\nexport const getAgentFilesRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/files',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const agentState = yield* getAgentState(id);\n if (!agentState?.workspace) {\n return jsonResponse({ files: [] });\n }\n const workspacePath = agentState.workspace;\n if (!existsSync(workspacePath)) {\n return jsonResponse({ files: [] });\n }\n const { stdout } = yield* Effect.promise(() => execAsync(\n 'git diff --name-status HEAD 2>/dev/null || git status --porcelain 2>/dev/null || echo \"\"',\n { cwd: workspacePath, encoding: 'utf-8' }\n ));\n const files = stdout\n .split('\\n')\n .filter(l => l.trim())\n .map(l => {\n const parts = l.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return { status: parts[0], path: parts[parts.length - 1] };\n }\n return { status: '?', path: l.trim() };\n })\n .filter(f => f.path);\n return jsonResponse({ files });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/timeline ─────────────────────────────────────\n\nexport const getAgentTimelineRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/timeline',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n const limitStr = Option.isSome(urlOpt) ? (urlOpt.value.searchParams.get('limit') ?? '50') : '50';\n const limit = parseInt(limitStr) || 50;\n\n const activity = getActivity(id, limit);\n const agentState = yield* getAgentState(id);\n const events = activity.map((a: any) => ({\n timestamp: a.timestamp || new Date().toISOString(),\n type: a.type || 'activity',\n message: a.message || a.content || '',\n }));\n if (agentState?.startedAt) {\n events.unshift({ timestamp: agentState.startedAt, type: 'started', message: 'Agent started' });\n }\n events.sort((a: any, b: any) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());\n return jsonResponse({ timeline: events.slice(0, limit) });\n })),\n);\n","import { randomBytes } from 'node:crypto';\n\nimport type {\n ComposerCommandPolicy,\n ComposerCommandResult,\n ComposerSafety,\n} from '@overdeck/contracts';\n\nexport const COMPOSER_CONFIRMATION_TTL_MS = 5 * 60 * 1000;\n\nexport interface ComposerConfirmationTarget {\n kind: 'conversation' | 'agent';\n id: string;\n}\n\nexport interface ComposerCommandConfirmationInput {\n nonce?: unknown;\n typedText?: unknown;\n}\n\nexport function composerCommandConfirmationFromBody(\n body: Record<string, unknown>,\n): ComposerCommandConfirmationInput | undefined {\n if (!('confirmationNonce' in body) && !('confirmationText' in body)) return undefined;\n return {\n nonce: body['confirmationNonce'],\n typedText: body['confirmationText'],\n };\n}\n\ninterface ComposerConfirmationEntry {\n targetKind: ComposerConfirmationTarget['kind'];\n targetId: string;\n argv: readonly string[];\n safety: ComposerSafety;\n typedText?: string;\n expiresAt: number;\n}\n\nexport class ComposerCommandConfirmationError extends Error {\n readonly code = 'invalid-confirmation';\n\n constructor(message: string) {\n super(message);\n this.name = 'ComposerCommandConfirmationError';\n }\n}\n\nexport interface ComposerConfirmationStore {\n issue(input: {\n target: ComposerConfirmationTarget;\n argv: readonly string[];\n policy: ComposerCommandPolicy;\n display: string;\n }): Extract<ComposerCommandResult, { kind: 'confirmation' }>;\n consume(input: {\n target: ComposerConfirmationTarget;\n argv: readonly string[];\n policy: ComposerCommandPolicy;\n confirmation: ComposerCommandConfirmationInput;\n }): void;\n}\n\nexport interface ComposerConfirmationStoreDependencies {\n now?: () => number;\n createNonce?: () => string;\n}\n\nexport function createComposerConfirmationStore(\n dependencies: ComposerConfirmationStoreDependencies = {},\n): ComposerConfirmationStore {\n const entries = new Map<string, ComposerConfirmationEntry>();\n const now = dependencies.now ?? Date.now;\n const createNonce = dependencies.createNonce\n ?? (() => randomBytes(24).toString('base64url'));\n\n function pruneExpired(): void {\n const currentTime = now();\n for (const [nonce, entry] of entries) {\n if (entry.expiresAt <= currentTime) entries.delete(nonce);\n }\n }\n\n return {\n issue({ target, argv, policy, display }) {\n if (policy.safety === 'destructive' && !policy.typedConfirmation) {\n throw new Error(\n `Destructive composer policy for ${display} must define typedConfirmation.`,\n );\n }\n pruneExpired();\n const nonce = createNonce();\n entries.set(nonce, {\n targetKind: target.kind,\n targetId: target.id,\n argv: [...argv],\n safety: policy.safety,\n typedText: policy.typedConfirmation,\n expiresAt: now() + COMPOSER_CONFIRMATION_TTL_MS,\n });\n return {\n kind: 'confirmation',\n status: 'confirmation_required',\n nonce,\n consequence: policy.confirmationText\n ?? `Running ${display} requires confirmation before Overdeck continues.`,\n ...(policy.typedConfirmation\n ? { typedText: policy.typedConfirmation }\n : {}),\n };\n },\n\n consume({ target, argv, policy, confirmation }) {\n if (typeof confirmation.nonce !== 'string') {\n throw new ComposerCommandConfirmationError(\n 'A valid confirmation nonce is required before this command can run.',\n );\n }\n\n const entry = entries.get(confirmation.nonce);\n entries.delete(confirmation.nonce);\n if (!entry) {\n throw new ComposerCommandConfirmationError(\n 'This command confirmation is missing, invalid, or has already been used. Request a new confirmation and try again.',\n );\n }\n if (entry.expiresAt <= now()) {\n throw new ComposerCommandConfirmationError(\n 'This command confirmation has expired. Request a new confirmation and try again.',\n );\n }\n if (\n entry.targetKind !== target.kind ||\n entry.targetId !== target.id ||\n entry.safety !== policy.safety ||\n !sameArgv(entry.argv, argv)\n ) {\n throw new ComposerCommandConfirmationError(\n 'This command confirmation does not match the current target or command. Request a new confirmation and try again.',\n );\n }\n if (\n policy.safety === 'destructive' &&\n (typeof confirmation.typedText !== 'string' || confirmation.typedText !== entry.typedText)\n ) {\n throw new ComposerCommandConfirmationError(\n `Type \"${entry.typedText ?? ''}\" exactly to confirm this destructive command.`,\n );\n }\n },\n };\n}\n\nfunction sameArgv(left: readonly string[], right: readonly string[]): boolean {\n return left.length === right.length && left.every((value, index) => value === right[index]);\n}\n\nexport const composerConfirmationStore = createComposerConfirmationStore();\n","import {\n COMPOSER_COMMAND_MANIFEST,\n type ComposerCommandManifestEntry,\n type ComposerCommandOption,\n} from '@overdeck/contracts';\n\nexport type ComposerCommandParseErrorCode =\n | 'unknown-command'\n | 'missing-argument'\n | 'unknown-flag'\n | 'forbidden-token';\n\nexport class ComposerCommandParseError extends Error {\n constructor(\n readonly code: ComposerCommandParseErrorCode,\n message: string,\n readonly token: string,\n readonly expected: string,\n ) {\n super(message);\n this.name = 'ComposerCommandParseError';\n }\n}\n\nexport interface ParsedOverdeckComposerCommand {\n entry: ComposerCommandManifestEntry;\n argv: string[];\n}\n\ninterface OptionDefinition {\n option: ComposerCommandOption;\n canonicalFlag: string;\n}\n\nconst FORBIDDEN_TOKEN = /\\$\\(|[;|&<>`]/;\n\nfunction expectedSyntax(entry: ComposerCommandManifestEntry): string {\n const args = entry.args.map(argument => {\n const name = argument.variadic ? `${argument.name}...` : argument.name;\n return argument.required ? `<${name}>` : `[${name}]`;\n });\n const options = entry.options.length > 0 ? ['[options]'] : [];\n return [entry.display, ...args, ...options].join(' ');\n}\n\nfunction tokenize(input: string): string[] {\n const tokens: string[] = [];\n let current = '';\n let quote: '\"' | \"'\" | null = null;\n let escaped = false;\n\n const pushCurrent = () => {\n if (current.length === 0) return;\n tokens.push(current);\n current = '';\n };\n\n for (const character of input) {\n if (escaped) {\n current += character;\n escaped = false;\n continue;\n }\n if (character === '\\\\') {\n escaped = true;\n continue;\n }\n if (quote !== null) {\n if (character === quote) quote = null;\n else current += character;\n continue;\n }\n if (character === '\"' || character === \"'\") {\n quote = character;\n continue;\n }\n if (/\\s/.test(character)) {\n pushCurrent();\n continue;\n }\n current += character;\n }\n\n if (escaped) current += '\\\\';\n if (quote !== null) {\n throw new ComposerCommandParseError(\n 'missing-argument',\n `Unclosed ${quote} quote. Close the quoted value before submitting the command.`,\n quote,\n `a closing ${quote}`,\n );\n }\n pushCurrent();\n return tokens;\n}\n\nfunction aliasesForPath(entry: ComposerCommandManifestEntry): string[][] {\n let candidates: string[][] = [[]];\n for (let index = 0; index < entry.path.length; index += 1) {\n const prefix = entry.path.slice(0, index + 1);\n const prefixEntry = COMPOSER_COMMAND_MANIFEST.find(candidate =>\n candidate.path.length === prefix.length &&\n candidate.path.every((part, partIndex) => part === prefix[partIndex]));\n const names = [entry.path[index], ...(prefixEntry?.aliases ?? [])];\n candidates = candidates.flatMap(candidate => names.map(name => [...candidate, name]));\n }\n return candidates;\n}\n\nfunction resolveEntry(tokens: readonly string[]): { entry: ComposerCommandManifestEntry; pathLength: number } {\n const matches = COMPOSER_COMMAND_MANIFEST.flatMap(entry =>\n aliasesForPath(entry)\n .filter(path => path.every((part, index) => tokens[index] === part))\n .map(path => ({ entry, pathLength: path.length })));\n const match = matches.sort((left, right) => right.pathLength - left.pathLength)[0];\n if (match) return match;\n\n const token = tokens[0] ?? '';\n throw new ComposerCommandParseError(\n 'unknown-command',\n token\n ? `Unknown Overdeck command \"${token}\". Type /pan to list available commands.`\n : 'Missing an Overdeck command after /pan. Type /pan to list available commands.',\n token,\n '/pan <command>',\n );\n}\n\nfunction optionDefinitions(entry: ComposerCommandManifestEntry): Map<string, OptionDefinition> {\n const definitions = new Map<string, OptionDefinition>();\n for (const option of entry.options) {\n const flags = option.flags\n .split(/[ ,|]+/)\n .filter(part => part.startsWith('-'));\n const canonicalFlag = flags.find(flag => flag.startsWith('--')) ?? flags[0];\n for (const flag of flags) definitions.set(flag, { option, canonicalFlag });\n }\n return definitions;\n}\n\nfunction splitFlag(token: string): { flag: string; inlineValue?: string } {\n const separator = token.indexOf('=');\n if (separator === -1) return { flag: token };\n return {\n flag: token.slice(0, separator),\n inlineValue: token.slice(separator + 1),\n };\n}\n\nexport function parseOverdeckComposerCommand(\n message: string,\n): ParsedOverdeckComposerCommand | null {\n if (!/^\\/pan(?:\\s|$)/.test(message)) return null;\n\n const forbidden = message.match(FORBIDDEN_TOKEN)?.[0];\n if (forbidden !== undefined) {\n throw new ComposerCommandParseError(\n 'forbidden-token',\n `The token \"${forbidden}\" is not allowed in /pan commands. Run compound shell syntax in a terminal instead.`,\n forbidden,\n 'plain arguments without shell operators',\n );\n }\n\n const tokens = tokenize(message.slice(4).trim());\n const { entry, pathLength } = resolveEntry(tokens);\n const remaining = tokens.slice(pathLength);\n const definitions = optionDefinitions(entry);\n const positionals: string[] = [];\n const flags: string[] = [];\n\n for (let index = 0; index < remaining.length; index += 1) {\n const token = remaining[index];\n if (!token.startsWith('-')) {\n positionals.push(token);\n continue;\n }\n\n const { flag, inlineValue } = splitFlag(token);\n const definition = definitions.get(flag);\n if (definition === undefined) {\n throw new ComposerCommandParseError(\n 'unknown-flag',\n `Unknown flag \"${flag}\" for ${entry.display}. Expected syntax: ${expectedSyntax(entry)}.`,\n flag,\n expectedSyntax(entry),\n );\n }\n\n flags.push(definition.canonicalFlag);\n if (definition.option.valueHint === null) {\n if (inlineValue !== undefined) {\n throw new ComposerCommandParseError(\n 'unknown-flag',\n `Flag \"${flag}\" does not accept a value. Expected syntax: ${expectedSyntax(entry)}.`,\n token,\n expectedSyntax(entry),\n );\n }\n continue;\n }\n\n if (inlineValue !== undefined) {\n if (inlineValue.length === 0 && definition.option.required) {\n throw new ComposerCommandParseError(\n 'missing-argument',\n `Flag \"${flag}\" requires <${definition.option.valueHint}>. Expected syntax: ${expectedSyntax(entry)}.`,\n flag,\n expectedSyntax(entry),\n );\n }\n if (inlineValue.length > 0) flags.push(inlineValue);\n continue;\n }\n\n const next = remaining[index + 1];\n const nextIsFlag = next?.startsWith('-') === true;\n if (definition.option.required && (next === undefined || nextIsFlag)) {\n throw new ComposerCommandParseError(\n 'missing-argument',\n `Flag \"${flag}\" requires <${definition.option.valueHint}>. Expected syntax: ${expectedSyntax(entry)}.`,\n flag,\n expectedSyntax(entry),\n );\n }\n if (next !== undefined && !nextIsFlag) {\n flags.push(next);\n index += 1;\n }\n }\n\n const variadic = entry.args.at(-1)?.variadic === true;\n if (!variadic && positionals.length > entry.args.length) {\n const token = positionals[entry.args.length];\n throw new ComposerCommandParseError(\n 'unknown-command',\n `Unexpected token \"${token}\" for ${entry.display}. Expected syntax: ${expectedSyntax(entry)}.`,\n token,\n expectedSyntax(entry),\n );\n }\n const missing = entry.args.find((argument, index) => argument.required && positionals[index] === undefined);\n if (missing !== undefined) {\n throw new ComposerCommandParseError(\n 'missing-argument',\n `Missing required argument <${missing.name}> for ${entry.display}. Expected syntax: ${expectedSyntax(entry)}.`,\n missing.name,\n expectedSyntax(entry),\n );\n }\n\n return {\n entry,\n argv: [...entry.path, ...positionals, ...flags],\n };\n}\n","import {\n COMPOSER_COMMAND_MANIFEST,\n type ComposerCommandManifestEntry,\n type ComposerCommandPolicy,\n} from '@overdeck/contracts';\n\nexport const COMPOSER_COMMAND_POLICIES: Readonly<Record<string, ComposerCommandPolicy>> = {\n start: { mode: 'detached', safety: 'safe' },\n plan: { mode: 'detached', safety: 'safe' },\n show: { mode: 'captured', safety: 'safe' },\n status: { mode: 'captured', safety: 'safe' },\n tell: { mode: 'captured', safety: 'safe' },\n handoff: {\n mode: 'ui',\n safety: 'dialog',\n uiAction: 'handoff',\n confirmationText: 'This opens the handoff dialog for the selected conversation. Review the destination and focus before continuing.',\n },\n fork: {\n mode: 'ui',\n safety: 'dialog',\n uiAction: 'fork',\n confirmationText: 'This opens the fork dialog for the selected conversation. Review the fork mode and destination before continuing.',\n },\n};\n\nconst DEFAULT_POLICY: ComposerCommandPolicy = {\n mode: 'terminal-only',\n safety: 'safe',\n};\n\nexport function assertPolicyOverlayIntegrity(\n policies: Readonly<Record<string, ComposerCommandPolicy>> = COMPOSER_COMMAND_POLICIES,\n manifest: readonly ComposerCommandManifestEntry[] = COMPOSER_COMMAND_MANIFEST,\n): void {\n for (const path of Object.keys(policies)) {\n const matches = manifest.filter(entry => entry.path.join(' ') === path);\n if (matches.length !== 1) {\n throw new Error(\n `Composer command policy path \"${path}\" must match exactly one manifest entry; found ${matches.length}.`,\n );\n }\n }\n}\n\nexport function resolvePolicy(path: readonly string[]): ComposerCommandPolicy {\n return COMPOSER_COMMAND_POLICIES[path.join(' ')] ?? DEFAULT_POLICY;\n}\n\nassertPolicyOverlayIntegrity();\n","/**\n * Harness-independent composer control-plane router.\n *\n * Message endpoints call this after target/body validation and before every\n * harness-specific stage: native compact handling, attachment transformation,\n * prompt-memory injection, and delivery. Returning early here guarantees that\n * `/pan` operator commands cannot become harness prompts or transcript rows.\n */\nimport type {\n ComposerCommandPolicy,\n ComposerCommandResult,\n} from '@overdeck/contracts';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport {\n composerConfirmationStore,\n type ComposerCommandConfirmationInput,\n type ComposerConfirmationStore,\n} from './confirmations.js';\nimport { runDetachedCommand } from './detached.js';\nimport { runCapturedCommand } from './executors.js';\nimport {\n parseOverdeckComposerCommand,\n type ParsedOverdeckComposerCommand,\n} from './parser.js';\nimport { resolvePolicy } from './policy.js';\n\nexport interface ComposerCommandTarget {\n kind: 'conversation' | 'agent';\n id: string;\n harness: RuntimeName;\n cwd?: string;\n issueId?: string;\n}\n\nexport interface HandleComposerCommandInput {\n parsed: ParsedOverdeckComposerCommand;\n target: ComposerCommandTarget;\n confirmation?: ComposerCommandConfirmationInput;\n}\n\nexport interface HandleComposerCommandMessageInput {\n message: string;\n target: ComposerCommandTarget;\n confirmation?: ComposerCommandConfirmationInput;\n}\n\nexport interface ComposerCommandRouterDependencies {\n confirmationStore?: ComposerConfirmationStore;\n resolvePolicy?: (path: readonly string[]) => ComposerCommandPolicy;\n}\n\nconst UI_COMMAND_PATTERNS = [\n {\n action: 'handoff' as const,\n pattern: /^\\/(?:handoff|pan-handoff|pan\\s+handoff)(?:\\s+([\\s\\S]+))?$/i,\n },\n {\n action: 'fork' as const,\n pattern: /^\\/pan\\s+fork(?:\\s+([\\s\\S]+))?$/i,\n },\n];\n\nexport function isComposerCommandMessage(message: string): boolean {\n return (\n UI_COMMAND_PATTERNS.some(({ pattern }) => pattern.test(message)) ||\n /^\\/pan(?:\\s|$)/.test(message)\n );\n}\n\nexport async function handleComposerCommandMessage({\n message,\n target,\n confirmation,\n}: HandleComposerCommandMessageInput, dependencies: ComposerCommandRouterDependencies = {}): Promise<ComposerCommandResult | null> {\n for (const { action, pattern } of UI_COMMAND_PATTERNS) {\n const match = message.match(pattern);\n if (match) {\n const parsed = parseOverdeckComposerCommand(`/pan ${action}`)!;\n const focus = match[1]?.trim();\n if (focus) parsed.argv.push(focus);\n return handleComposerCommand(\n { parsed, target, confirmation },\n dependencies,\n );\n }\n }\n\n const parsed = parseOverdeckComposerCommand(message);\n if (parsed === null) return null;\n return handleComposerCommand(\n { parsed, target, confirmation },\n dependencies,\n );\n}\n\nexport async function handleComposerCommand({\n parsed,\n target,\n confirmation,\n}: HandleComposerCommandInput, dependencies: ComposerCommandRouterDependencies = {}): Promise<ComposerCommandResult> {\n const policy = (dependencies.resolvePolicy ?? resolvePolicy)(parsed.entry.path);\n if (policy.safety !== 'safe') {\n const confirmationStore = dependencies.confirmationStore ?? composerConfirmationStore;\n if (confirmation === undefined) {\n return confirmationStore.issue({\n target,\n argv: parsed.argv,\n policy,\n display: parsed.entry.display,\n });\n }\n confirmationStore.consume({\n target,\n argv: parsed.argv,\n policy,\n confirmation,\n });\n }\n\n if (policy.mode === 'captured') {\n return runCapturedCommand(parsed.argv);\n }\n if (policy.mode === 'detached') {\n return runDetachedCommand(parsed.argv);\n }\n if (policy.mode === 'ui' && policy.uiAction) {\n return uiCommandResult(\n policy.uiAction,\n parsed.argv.slice(parsed.entry.path.length).join(' ') || undefined,\n );\n }\n if (policy.mode === 'terminal-only') {\n return {\n kind: 'terminal-only',\n status: 'rejected',\n message: `${parsed.entry.display} must run in a terminal. Composer execution has not been enabled for this command.`,\n };\n }\n\n return missingExecutorResult(parsed.entry.display, policy);\n}\n\nfunction uiCommandResult(\n action: 'handoff' | 'fork',\n focus: string | undefined,\n): ComposerCommandResult {\n return {\n kind: 'ui',\n status: 'requires_ui',\n action,\n args: focus ? { focus } : {},\n };\n}\n\nfunction missingExecutorResult(\n display: string,\n policy: ComposerCommandPolicy,\n): ComposerCommandResult {\n return {\n kind: 'terminal-only',\n status: 'rejected',\n message: `${display} is recognized as a ${policy.mode} command, but its composer executor is not registered yet. Run it in a terminal for now.`,\n };\n}\n\nexport function composerCommandResultHttpStatus(result: ComposerCommandResult): number {\n return result.kind === 'terminal-only' ? 422 : 200;\n}\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { getAgentState, messageAgent } from '../../../../lib/agents.js';\nimport {\n ComposerCommandConfirmationError,\n composerCommandConfirmationFromBody,\n type ComposerCommandConfirmationInput,\n} from '../../../../lib/composer-commands/confirmations.js';\nimport { ComposerCommandParseError } from '../../../../lib/composer-commands/parser.js';\nimport {\n composerCommandResultHttpStatus,\n handleComposerCommandMessage,\n isComposerCommandMessage,\n} from '../../../../lib/composer-commands/router.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { validateOrigin } from '../origin-validation.js';\nimport { readJsonBody } from './shared.js';\n\nasync function sendAgentMessage(id: string, message: string) {\n const agentStateDir = join(homedir(), '.overdeck', 'agents', id);\n const remoteStateFile = join(agentStateDir, 'remote-state.json');\n let isRemote = false;\n\n if (existsSync(remoteStateFile)) {\n try {\n const state = JSON.parse(await readFile(remoteStateFile, 'utf-8'));\n isRemote = state.location === 'remote' && Boolean(state.vmName);\n } catch {}\n }\n\n await messageAgent(id, message, 'dashboard:user-message');\n return isRemote ? { success: true, remote: true } : { success: true };\n}\n\nexport async function handleAgentMessage(\n id: string,\n message: string,\n confirmation?: ComposerCommandConfirmationInput,\n) {\n try {\n if (isComposerCommandMessage(message)) {\n let agentState;\n try {\n agentState = await Effect.runPromise(getAgentState(id));\n } catch {\n return jsonResponse({\n error: `Failed to resolve agent target: ${id}`,\n code: 'agent-resolution-failed',\n }, { status: 503 });\n }\n if (!agentState) {\n return jsonResponse({\n error: `Agent not found: ${id}`,\n code: 'agent-not-found',\n }, { status: 404 });\n }\n if (!agentState.harness) {\n return jsonResponse({\n error: `Agent harness could not be resolved: ${id}`,\n code: 'agent-harness-unresolved',\n }, { status: 503 });\n }\n const result = await handleComposerCommandMessage({\n message,\n confirmation,\n target: {\n kind: 'agent',\n id,\n harness: agentState.harness,\n cwd: agentState.workspace,\n issueId: agentState.issueId,\n },\n });\n if (result !== null) {\n return jsonResponse(result, { status: composerCommandResultHttpStatus(result) });\n }\n }\n } catch (error) {\n if (error instanceof ComposerCommandConfirmationError) {\n return jsonResponse({\n error: error.message,\n code: error.code,\n }, { status: 422 });\n }\n if (!(error instanceof ComposerCommandParseError)) throw error;\n const status = error.code === 'unknown-command' ? 404 : error.code === 'unknown-flag' ? 422 : 400;\n return jsonResponse({\n error: error.message,\n code: error.code,\n token: error.token,\n expected: error.expected,\n }, { status });\n }\n\n return jsonResponse(await sendAgentMessage(id, message));\n}\n\nexport function validateAgentMessageOrigin(request: HttpServerRequest.HttpServerRequest) {\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return {\n ok: false as const,\n response: jsonResponse({ ok: false, error: originCheck.error }, { status: 403 }),\n };\n }\n return { ok: true as const };\n}\n\nfunction postAgentMessageLikeRoute(path: `/${string}`) {\n return HttpRouter.add(\n 'POST',\n path,\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateAgentMessageOrigin(request);\n if (!originCheck.ok) return originCheck.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { message } = body as any;\n if (!message) {\n return jsonResponse({ error: 'Message required' }, { status: 400 });\n }\n\n return yield* Effect.promise(() => handleAgentMessage(\n id,\n message,\n composerCommandConfirmationFromBody(body as Record<string, unknown>),\n ));\n })),\n );\n}\n\n// ─── Route: POST /api/agents/:id/message ─────────────────────────────────────\n\nexport const postAgentMessageRoute = postAgentMessageLikeRoute('/api/agents/:id/message');\n\n// ─── Route: POST /api/agents/:id/tell ────────────────────────────────────────\n\nexport const postAgentTellRoute = postAgentMessageLikeRoute('/api/agents/:id/tell');\n\n// ─── Route: POST /api/agents/:id/poke ────────────────────────────────────────\n\nexport const postAgentPokeRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/poke',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { message } = body as any;\n const defaultPokeMessage =\n \"You seem to have been inactive for a while. If you're stuck:\\n\" +\n '1. Check your current xBRIEF task with `pan task show <issue> <item-id>`\\n' +\n '2. Try an alternative approach if blocked\\n' +\n '3. Ask for help if needed\\n\\n' +\n \"What's your current status?\";\n const pokeMsg = message || defaultPokeMessage;\n yield* Effect.promise(() => messageAgent(id, pokeMsg));\n return jsonResponse({ success: true, message: 'Agent poked successfully' });\n })),\n);\n","import { randomUUID } from 'node:crypto'\nimport { mkdir, rename, writeFile, chmod } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { ohmypiFifoPaths } from './ohmypi-fifo.js'\n\nexport type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'\n\nexport type ControlCommand =\n | {\n id: string\n type: 'prompt' | 'steer' | 'follow_up'\n message: string\n source: 'operator' | 'orchestrator'\n }\n | {\n id: string\n type: 'set_thinking_level'\n level: ThinkingLevel\n }\n | {\n id: string\n type: 'set_model'\n model: string\n }\n | {\n id: string\n type: 'compact'\n }\n\nexport interface ConversationControlPaths {\n agentDir: string\n controlDir: string\n}\n\nexport function conversationControlPaths(agentId: string, home?: string): ConversationControlPaths {\n const { agentDir } = ohmypiFifoPaths(agentId, home)\n return {\n agentDir,\n controlDir: join(agentDir, 'control'),\n }\n}\n\nexport async function writeConversationControlCommand(\n agentId: string,\n command: ControlCommand,\n home?: string,\n): Promise<string> {\n const paths = conversationControlPaths(agentId, home)\n await mkdir(paths.controlDir, { recursive: true, mode: 0o700 })\n await chmod(paths.controlDir, 0o700)\n\n const finalPath = join(paths.controlDir, `${Date.now()}-${command.id}-${randomUUID()}.json`)\n const tempPath = `${finalPath}.tmp`\n await writeFile(tempPath, `${JSON.stringify(command)}\\n`, { encoding: 'utf8', mode: 0o600 })\n await chmod(tempPath, 0o600)\n await rename(tempPath, finalPath)\n await chmod(finalPath, 0o600)\n return finalPath\n}\n","/**\n * oh-my-pi (omp) OAuth helpers for the openai-codex (ChatGPT subscription) provider.\n * Forked from pi-codex-auth.ts (PAN-1520) for the omp migration (PAN-1989).\n *\n * omp stores its general credentials in ~/.omp/agent/agent.db (SQLite). The\n * openai-codex OAuth credential is persisted separately at ~/.omp/agent/auth.json\n * (same JSON format as Pi's ~/.pi/agent/auth.json) so that this helper's\n * read/write logic is unchanged from the Pi side.\n *\n * This module:\n * - reads/writes the openai-codex credential under ~/.omp/agent/auth.json,\n * - reports auth status (for `pan doctor` and the conversation spawn gate),\n * - refreshes an expired-but-recoverable token, and\n * - drives omp's headless device-code login to re-authenticate.\n *\n * The OAuth module is loaded from the installed `omp` package dynamically.\n * A missing or relocated omp OAuth module degrades to 'unavailable' — never throws.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, realpathSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, dirname, delimiter } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nexport const PI_CODEX_PROVIDER = 'openai-codex';\n\n/** Credential as persisted in ~/.omp/agent/auth.json. */\nexport interface OhmypiCodexCredential {\n type: 'oauth';\n access: string;\n refresh: string;\n /** Access-token expiry, epoch milliseconds. */\n expires: number;\n accountId: string;\n}\n\n/** Token bundle returned by omp's login/refresh (pre-persistence, no `type`). */\ninterface OhmypiOAuthTokens {\n access: string;\n refresh: string;\n expires: number;\n accountId: string;\n}\n\nexport interface DeviceCodeInfo {\n userCode: string;\n verificationUri: string;\n intervalSeconds: number;\n expiresInSeconds: number;\n}\n\ninterface OhmypiCodexOAuthModule {\n refreshOpenAICodexToken: (refreshToken: string) => Promise<OhmypiOAuthTokens>;\n loginOpenAICodexDeviceCode: (options: {\n onDeviceCode: (info: DeviceCodeInfo) => void;\n signal?: AbortSignal;\n }) => Promise<OhmypiOAuthTokens>;\n}\n\nexport function getOhmypiAuthPath(): string {\n return join(homedir(), '.omp', 'agent', 'auth.json');\n}\n\nfunction readAuthFile(): Record<string, unknown> {\n const path = getOhmypiAuthPath();\n if (!existsSync(path)) return {};\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf-8'));\n return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\nexport function readOhmypiCodexCredential(): OhmypiCodexCredential | null {\n const cred = readAuthFile()[PI_CODEX_PROVIDER];\n if (\n cred && typeof cred === 'object' &&\n typeof (cred as Record<string, unknown>)['access'] === 'string' &&\n typeof (cred as Record<string, unknown>)['refresh'] === 'string'\n ) {\n return cred as OhmypiCodexCredential;\n }\n return null;\n}\n\n/** Atomically merge a fresh codex credential into auth.json (mode 0600). */\nfunction writeOhmypiCodexCredential(tokens: OhmypiOAuthTokens): OhmypiCodexCredential {\n const path = getOhmypiAuthPath();\n mkdirSync(dirname(path), { recursive: true });\n const auth = readAuthFile();\n const stored: OhmypiCodexCredential = {\n type: 'oauth',\n access: tokens.access,\n refresh: tokens.refresh,\n expires: tokens.expires,\n accountId: tokens.accountId,\n };\n auth[PI_CODEX_PROVIDER] = stored;\n const tmp = `${path}.tmp-${process.pid}`;\n writeFileSync(tmp, JSON.stringify(auth, null, 2), { mode: 0o600 });\n renameSync(tmp, path);\n return stored;\n}\n\n/** Locate the `omp` executable on PATH (no subprocess — event-loop safe). */\nfunction findOmpBinary(): string | null {\n for (const dir of (process.env['PATH'] ?? '').split(delimiter)) {\n if (!dir) continue;\n const candidate = join(dir, 'omp');\n if (existsSync(candidate)) return candidate;\n }\n return null;\n}\n\n/** omp is @oh-my-pi/pi-coding-agent; the oauth submodule path mirrors pi-ai's layout. */\nconst OMP_AI_OAUTH_SUBPATH = join('@oh-my-pi', 'pi-ai', 'dist', 'utils', 'oauth', 'openai-codex.js');\n\n/**\n * Resolve omp's openai-codex OAuth submodule by walking node_modules upward\n * from the real omp binary. Returns null when not found — the caller degrades\n * gracefully to 'unavailable' status.\n */\nfunction resolveOmpCodexOAuthFile(ompBin: string): string | null {\n let dir = dirname(realpathSync(ompBin));\n for (let i = 0; i < 10; i++) {\n const candidate = join(dir, 'node_modules', OMP_AI_OAUTH_SUBPATH);\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nlet cachedModule: Promise<OhmypiCodexOAuthModule | null> | null = null;\n\n/**\n * Dynamically load omp's openai-codex OAuth submodule from the installed `omp`\n * package. Returns null on any failure so callers can degrade gracefully.\n */\nasync function loadOhmypiCodexOAuth(): Promise<OhmypiCodexOAuthModule | null> {\n if (cachedModule) return cachedModule;\n cachedModule = (async () => {\n try {\n const ompBin = findOmpBinary();\n if (!ompBin) return null;\n const oauthFile = resolveOmpCodexOAuthFile(ompBin);\n if (!oauthFile) return null;\n const mod = (await import(pathToFileURL(oauthFile).href)) as Partial<OhmypiCodexOAuthModule>;\n if (\n typeof mod.refreshOpenAICodexToken !== 'function' ||\n typeof mod.loginOpenAICodexDeviceCode !== 'function'\n ) {\n return null;\n }\n return mod as OhmypiCodexOAuthModule;\n } catch {\n return null;\n }\n })();\n return cachedModule;\n}\n\nexport type OhmypiCodexAuthStatus =\n | { status: 'ok'; expiresAt: number }\n | { status: 'missing' }\n | { status: 'expired'; expiresAt: number; refreshFailed: boolean }\n | { status: 'unavailable' };\n\nconst EXPIRY_MARGIN_MS = 60_000;\n\nexport async function getOhmypiCodexAuthStatus(opts?: { refreshIfExpired?: boolean }): Promise<OhmypiCodexAuthStatus> {\n const cred = readOhmypiCodexCredential();\n if (!cred) return { status: 'missing' };\n if (cred.expires > Date.now() + EXPIRY_MARGIN_MS) return { status: 'ok', expiresAt: cred.expires };\n\n if (opts?.refreshIfExpired) {\n const refreshed = await refreshOhmypiCodexAuth();\n if (refreshed) return { status: 'ok', expiresAt: refreshed.expires };\n return { status: 'expired', expiresAt: cred.expires, refreshFailed: true };\n }\n return { status: 'expired', expiresAt: cred.expires, refreshFailed: false };\n}\n\nexport async function refreshOhmypiCodexAuth(): Promise<OhmypiCodexCredential | null> {\n const cred = readOhmypiCodexCredential();\n if (!cred?.refresh) return null;\n const mod = await loadOhmypiCodexOAuth();\n if (!mod) return null;\n try {\n return writeOhmypiCodexCredential(await mod.refreshOpenAICodexToken(cred.refresh));\n } catch {\n return null;\n }\n}\n\nexport async function isOhmypiCodexOAuthAvailable(): Promise<boolean> {\n return (await loadOhmypiCodexOAuth()) !== null;\n}\n\nexport async function loginOhmypiCodexDeviceCode(\n onDeviceCode: (info: DeviceCodeInfo) => void,\n signal?: AbortSignal,\n): Promise<OhmypiCodexCredential> {\n const mod = await loadOhmypiCodexOAuth();\n if (!mod) {\n throw new Error(\n 'Could not load omp\\'s OAuth module. Is `omp` (@oh-my-pi/pi-coding-agent) installed and on PATH?',\n );\n }\n return writeOhmypiCodexCredential(await mod.loginOpenAICodexDeviceCode({ onDeviceCode, signal }));\n}\n","import { existsSync } from 'node:fs';\nimport { createReadStream } from 'node:fs';\nimport { readdir, mkdir, rm, stat, realpath } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { createInterface } from 'node:readline';\n\nimport { getOverdeckHome } from '../../../lib/paths.js';\n\nconst CONVERSATION_ATTACHMENTS_DIR = 'conversation-attachments';\n\n/** Run async tasks in bounded batches to avoid unbounded Promise.all\n * that can exhaust file descriptors or memory under heavy load. */\nexport async function runInBatches<T>(items: T[], batchSize: number, fn: (item: T) => Promise<unknown>): Promise<void> {\n for (let i = 0; i < items.length; i += batchSize) {\n const batch = items.slice(i, i + batchSize);\n await Promise.all(batch.map(fn));\n }\n}\n\n/** Max JSONL line length to process. Lines larger than this are skipped to\n * prevent readline from stalling permanently on oversized input (e.g. a\n * single-line base64 payload >15 MB). */\nconst MAX_JSONL_LINE_LENGTH = 15 * 1024 * 1024;\n\n/** Bounded LRU cache for readSessionAttachmentBasenames to avoid full JSONL\n * rescans when the session file has not changed. Key = sessionFile:mtimeMs. */\nconst SESSION_ATTACHMENT_CACHE_MAX = 100;\nconst sessionAttachmentCache = new Map<string, Set<string>>();\n\nfunction getAttachmentCacheKey(sessionFile: string, mtimeMs: number): string {\n return `${sessionFile}:${mtimeMs}`;\n}\n\nfunction setAttachmentCache(sessionFile: string, mtimeMs: number, basenames: Set<string>): void {\n const key = getAttachmentCacheKey(sessionFile, mtimeMs);\n sessionAttachmentCache.set(key, basenames);\n // Evict oldest entries if over max\n if (sessionAttachmentCache.size > SESSION_ATTACHMENT_CACHE_MAX) {\n const firstKey = sessionAttachmentCache.keys().next().value;\n if (firstKey !== undefined) {\n sessionAttachmentCache.delete(firstKey);\n }\n }\n}\n\nfunction getAttachmentCache(sessionFile: string, mtimeMs: number): Set<string> | undefined {\n const key = getAttachmentCacheKey(sessionFile, mtimeMs);\n return sessionAttachmentCache.get(key);\n}\n\n/** Conversation names are sanitized to [a-zA-Z0-9_-]{1,64} on creation.\n * Re-validate here for defense-in-depth against path traversal. */\nfunction assertSafeName(name: string): void {\n if (!/^[a-zA-Z0-9_-]{1,64}$/.test(name)) {\n throw new Error('Invalid conversation name');\n }\n}\n\nexport function getConversationAttachmentsRoot(): string {\n return join(getOverdeckHome(), CONVERSATION_ATTACHMENTS_DIR);\n}\n\nexport function getConversationAttachmentDir(name: string): string {\n assertSafeName(name);\n return join(getConversationAttachmentsRoot(), name);\n}\n\nexport async function ensureConversationAttachmentDir(name: string): Promise<string> {\n const dir = getConversationAttachmentDir(name);\n await mkdir(dir, { recursive: true });\n return dir;\n}\n\nexport async function cleanupConversationAttachments(name: string): Promise<void> {\n assertSafeName(name);\n await rm(getConversationAttachmentDir(name), { recursive: true, force: true });\n}\n\n/** Safety-net cleanup: remove attachment directories for conversations that no\n * longer exist in the database. Catches orphaned files from abandoned/crashed\n * sessions that the lifecycle poller missed. */\nexport async function cleanupOrphanedConversationAttachments(): Promise<void> {\n try {\n const { listConversations } = await import('../../../lib/overdeck/conversations.js');\n const convNames = new Set(listConversations().map((c) => c.name));\n const root = getConversationAttachmentsRoot();\n const dirs = await readdir(root, { withFileTypes: true });\n const orphaned = dirs\n .filter((entry) => entry.isDirectory() && !convNames.has(entry.name))\n .map((entry) => join(root, entry.name));\n await runInBatches(orphaned, 10, (path) => rm(path, { recursive: true, force: true }).catch(() => {}));\n } catch {\n // ignore — directory may not exist yet\n }\n}\n\nasync function listConversationAttachmentPaths(name: string): Promise<string[]> {\n const dir = getConversationAttachmentDir(name);\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isFile())\n .map((entry) => join(dir, entry.name));\n } catch {\n return [];\n }\n}\n\n/** Synchronous containment check: does the normalized path start with the\n * pre-resolved attachment directory prefix? Uses resolve() (not realpath)\n * so the check is I/O-free — safe because we are only cataloguing references\n * in the JSONL, not accessing the filesystem. */\nfunction isUnderAttachmentDir(resolvedAttachmentDir: string, attachmentPath: string): boolean {\n try {\n const normalized = resolve(attachmentPath);\n return normalized.startsWith(`${resolvedAttachmentDir}/`);\n } catch {\n return false;\n }\n}\n\nasync function readSessionAttachmentBasenames(sessionFile: string, name: string): Promise<Set<string>> {\n try {\n let mtimeMs: number;\n try {\n mtimeMs = (await stat(sessionFile)).mtimeMs;\n } catch {\n return new Set<string>();\n }\n\n const cached = getAttachmentCache(sessionFile, mtimeMs);\n if (cached) {\n return cached;\n }\n\n // Pre-resolve the attachment directory once (I/O outside the loop).\n // realpath follows symlinks for accuracy; resolve() is the fallback if\n // the directory has not been created yet.\n let resolvedAttachmentDir: string;\n try {\n resolvedAttachmentDir = await realpath(getConversationAttachmentDir(name));\n } catch {\n resolvedAttachmentDir = resolve(getConversationAttachmentDir(name));\n }\n\n const referenced = new Set<string>();\n const stream = createReadStream(sessionFile, { encoding: 'utf-8' });\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n try {\n for await (const line of rl) {\n if (!line.trim()) continue;\n // Skip oversized lines that could stall the reader (e.g. huge base64)\n if (line.length > MAX_JSONL_LINE_LENGTH) continue;\n let text: string | undefined;\n let structuredFoundPaths = false;\n try {\n const entry = JSON.parse(line);\n // Extract text from known Claude Code JSONL shapes\n if (typeof entry?.message?.content === 'string') {\n text = entry.message.content;\n } else if (Array.isArray(entry?.message?.content)) {\n text = entry.message.content\n .map((c: unknown) => (typeof c === 'string' ? c : (c as { text?: string })?.text ?? ''))\n .join('');\n } else if (typeof entry?.message?.text === 'string') {\n text = entry.message.text;\n } else if (typeof entry?.text === 'string') {\n text = entry.text;\n }\n } catch {\n // Not valid JSON — fall back to raw line regex\n text = line;\n }\n if (text) {\n for (const attachmentPath of extractConversationAttachmentPaths(text)) {\n if (isUnderAttachmentDir(resolvedAttachmentDir, attachmentPath)) {\n referenced.add(basename(attachmentPath));\n structuredFoundPaths = true;\n }\n }\n }\n // Only run regex fallback on the raw JSON line when structured extraction\n // found nothing. This catches tool-use and other shapes where the path may\n // be in nested fields (e.g. tool_use.input, tool_result.content) that\n // structured extraction above does not reach. We JSON-decode the captured\n // value to handle escaped slashes (\\/) that appear in raw JSON strings.\n if (!structuredFoundPaths) {\n for (const match of line.matchAll(/\"@([^\"]+)\"/g)) {\n const rawValue = match[1];\n // Only consider values that look like attachment paths\n if (!rawValue.startsWith('/') && !rawValue.startsWith('\\\\/')) continue;\n let attachmentPath: string;\n try {\n attachmentPath = JSON.parse(`\"${rawValue}\"`);\n } catch {\n attachmentPath = rawValue;\n }\n if (typeof attachmentPath === 'string' && attachmentPath.startsWith('/')) {\n if (isUnderAttachmentDir(resolvedAttachmentDir, attachmentPath)) {\n referenced.add(basename(attachmentPath));\n }\n }\n }\n }\n }\n } finally {\n rl.close();\n stream.destroy();\n }\n setAttachmentCache(sessionFile, mtimeMs, referenced);\n return referenced;\n } catch {\n return new Set<string>();\n }\n}\n\nexport async function cleanupUnreferencedConversationAttachments(conversation: { name: string; sessionFile: string | null }): Promise<void> {\n const attachmentPaths = await listConversationAttachmentPaths(conversation.name);\n if (attachmentPaths.length === 0) {\n return;\n }\n\n // No session file means nothing can reference attachments — delete them all.\n if (!conversation.sessionFile) {\n await runInBatches(attachmentPaths, 10, async (attachmentPath) => {\n await rm(attachmentPath, { force: true });\n });\n return;\n }\n\n let sessionMtimeMs: number;\n try {\n sessionMtimeMs = (await stat(conversation.sessionFile)).mtimeMs;\n } catch {\n return;\n }\n\n const referencedBasenames = await readSessionAttachmentBasenames(conversation.sessionFile, conversation.name);\n\n // Re-stat the session file to tighten against a /stop race: a JSONL write\n // may have landed while we were reading attachment basenames.\n try {\n const freshStats = await stat(conversation.sessionFile);\n if (freshStats.mtimeMs > sessionMtimeMs) {\n sessionMtimeMs = freshStats.mtimeMs;\n }\n } catch {\n // ignore\n }\n\n await runInBatches(\n attachmentPaths,\n 10,\n async (attachmentPath) => {\n if (referencedBasenames.has(basename(attachmentPath))) {\n return;\n }\n\n try {\n const attachmentMtimeMs = (await stat(attachmentPath)).mtimeMs;\n // >= preserves attachments uploaded in the same mtime tick as the\n // session JSONL write, preventing a race where a just-uploaded file\n // is deleted on stop/archive.\n if (attachmentMtimeMs >= sessionMtimeMs) {\n return;\n }\n } catch {\n return;\n }\n\n await rm(attachmentPath, { force: true });\n },\n );\n}\n\nexport function extractConversationAttachmentPaths(message: string): string[] {\n return Array.from(\n // Use a negative lookbehind (?<!\\S) instead of a consuming group so that\n // adjacent @-paths (e.g. \"@/a @/b\") are matched independently. Strip\n // trailing punctuation that is accidentally captured when a sentence or\n // parenthetical ends immediately after the path.\n message.matchAll(/(?<!\\S)@(\\/[^\\s]+)/g),\n ([, attachmentPath]) => {\n let path = attachmentPath;\n while (path.length > 1 && /[.,;:!?)\\]}+\"']$/.test(path)) {\n path = path.slice(0, -1);\n }\n return path;\n },\n );\n}\n\n/** Resolve a path for containment checking, walking parent directories with\n * realpath to preserve symlink detection even when the target file is missing. */\nasync function resolveForContainment(attachmentPath: string): Promise<string> {\n try {\n return await realpath(attachmentPath);\n } catch {\n // File does not exist — walk up the directory tree and realpath the\n // nearest existing parent, then append the remaining segments.\n let dir = dirname(attachmentPath);\n const remaining = [basename(attachmentPath)];\n while (dir !== '/' && dir !== '.') {\n try {\n const realDir = await realpath(dir);\n return join(realDir, ...remaining);\n } catch {\n remaining.unshift(basename(dir));\n dir = dirname(dir);\n }\n }\n // Nothing in the path exists — fall back to resolve (still safe against ..)\n return resolve(attachmentPath);\n }\n}\n\nexport async function isManagedConversationAttachmentPath(attachmentPath: string): Promise<boolean> {\n try {\n const attachmentsRoot = await realpath(getConversationAttachmentsRoot());\n const candidate = await resolveForContainment(attachmentPath);\n return candidate.startsWith(`${attachmentsRoot}/`);\n } catch {\n return false;\n }\n}\n\nexport async function isConversationAttachmentPath(name: string, attachmentPath: string): Promise<boolean> {\n try {\n const attachmentDir = resolve(getConversationAttachmentDir(name));\n const candidate = await resolveForContainment(attachmentPath);\n return candidate.startsWith(`${attachmentDir}/`);\n } catch {\n return false;\n }\n}\n\nexport async function hasConversationAttachment(name: string, attachmentPath: string): Promise<boolean> {\n try {\n const attachmentDir = await realpath(getConversationAttachmentDir(name));\n const candidate = await resolveForContainment(attachmentPath);\n if (!candidate.startsWith(`${attachmentDir}/`)) {\n return false;\n }\n // Use the resolved path for existence check so dangling symlinks inside\n // the root are handled consistently with the containment check.\n return existsSync(candidate);\n } catch {\n return false;\n }\n}\n\nexport async function removeConversationAttachment(name: string, attachmentPath: string): Promise<boolean> {\n if (!(await isConversationAttachmentPath(name, attachmentPath))) return false;\n await rm(attachmentPath, { force: true });\n return true;\n}\n","import { deliverAgentMessage } from '../agents.js';\nimport { paneHasBlockingChoiceMenu } from '../pane-choice-menu.js';\nimport { capturePaneText } from '../tmux.js';\n\nexport type ResumeContractDeliveryResult = 'delivered' | 'skipped-gated' | 'failed';\n\nexport async function deliverResumeContractUnlessGated(\n tmuxSession: string,\n contract: string,\n caller: string,\n method: Parameters<typeof deliverAgentMessage>[3],\n): Promise<ResumeContractDeliveryResult> {\n const resumeGatePane = await capturePaneText(tmuxSession, 90).catch(() => '');\n if (resumeGatePane && paneHasBlockingChoiceMenu(resumeGatePane)) {\n console.log(`[conversations] resume contract skipped for ${tmuxSession} — the harness is showing a blocking choice menu; the operator answers it`);\n return 'skipped-gated';\n }\n\n try {\n await deliverAgentMessage(tmuxSession, contract, caller, method);\n return 'delivered';\n } catch (err: unknown) {\n console.error(`[conversations] resume contract delivery failed for ${tmuxSession}:`, err instanceof Error ? err.message : String(err));\n return 'failed';\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { exec, execFile } from 'node:child_process';\nimport { existsSync, createReadStream } from 'node:fs';\nimport { mkdir, writeFile, readFile, readdir, stat, realpath, rename, rm } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { createInterface } from 'node:readline';\nimport { promisify } from 'node:util';\nimport { Effect } from 'effect';\nimport { BLANKED_PROVIDER_ENV } from '../child-env.js';\nimport { MODEL_ID_PATTERN } from '../model-validation.js';\nimport { getClaudePermissionFlagsStringSync, resolvePermissionModeSync, BYPASS_PERMISSION_MODE } from '../claude-permissions.js';\nimport { listProjectsAsync, type ProjectConfig } from '../projects.js';\nimport { getDefaultCwd } from '../default-cwd.js';\nimport {\n listConversations,\n getConversationByName,\n createConversation,\n markConversationEnded,\n markConversationActive,\n updateLastAttached,\n setConversationModel,\n setConversationHarness,\n backfillConversationModel,\n archiveConversation,\n removeFavorite,\n updateSpawnError,\n hasOtherActiveConversationOnTmuxSession,\n type LegacyConversation as Conversation,\n} from './conversations.js';\nimport {\n capturePane,\n capturePaneText,\n sessionExists,\n isHarnessProcessAlive,\n killSession,\n createSession,\n setOption,\n exactPaneTarget,\n listSessionNames,\n findManagedServerPidSync,\n} from '../tmux.js';\nimport { deliverAgentMessage, writeChannelsBridgeMcpConfig, dismissDevChannelsDialog, waitForReadySignal, clearReadySignal } from '../agents.js';\nimport {\n getAgentRuntimeBaseCommand,\n getProviderExportsForModel,\n getProviderAuthMode,\n} from '../agents.js';\nimport { writeBridgeTokenSync } from '../bridge-token.js';\nimport { isClaudeCodeChannelsEnabled, loadConfigSync } from '../config-yaml.js';\nimport { writePtyToken } from '../pty-token.js';\nimport { canUseHarnessSync } from '../harness-policy.js';\nimport { resolveHarness } from '../harness-resolve.js';\nimport { prepareHarnessLaunch } from '../harness-binary.js';\nimport { getProviderForModelSync, piProviderForModel, UnknownModelError } from '../providers.js';\nimport { getOhmypiCodexAuthStatus } from '../ohmypi-codex-auth.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport { piFifoPaths } from '../runtimes/pi-fifo.js';\nimport { generateLauncherScriptSync } from '../launcher-generator.js';\nimport { getAcpLauncherFields, waitForAcpHostReady, waitForPromptReady } from '../agents/runtime-command.js';\nimport { workspaceContextFile, piGlobalContextFile } from '../context-layers/layers.js';\nimport { ensureSessionContextBriefingFile } from '../briefing-freshness.js';\nimport { sessionFilePath, getOverdeckHome, resolveOhmypiExtensionPath } from '../paths.js';\nimport { resolvePtySupervisorScriptPath } from '../channels/pty-supervisor-locate.js';\nimport { buildResumeContract } from '../resume-contract.js';\nimport { jsonResponse } from '../../dashboard/server/http-helpers.js';\nimport { getEventStore } from '../../dashboard/server/event-store.js';\nimport { markRespawnPending } from '../../dashboard/server/services/pending-respawn.js';\nimport { cleanupConversationAttachments, cleanupUnreferencedConversationAttachments } from '../../dashboard/server/services/conversation-attachments.js';\nimport { resolveCodexRolloutPath } from '../../dashboard/server/routes/jsonl-resolver.js';\nimport { sendConversationControlCommand, isPiControlChannelHarness, resolveConversationDeliveryMethod } from './conversation-delivery.js';\nimport { deliverResumeContractUnlessGated } from './resume-contract-delivery.js';\nconst execAsync = promisify(exec);\nconst execFileAsync = promisify(execFile);\nconst PROCESS_CLEANUP_GRACE_MS = 750;\nconst PTY_SUPERVISOR_SOCKET_WAIT_MS = 30_000;\ntype ProcessTableRow = {\n pid: number;\n ppid: number;\n args: string;\n};\nfunction parseProcessTable(output: string): ProcessTableRow[] {\n return output\n .split('\\n')\n .map((line) => {\n const match = line.match(/^\\s*(\\d+)\\s+(\\d+)\\s+(.*)$/);\n if (!match) return null;\n return {\n pid: Number(match[1]),\n ppid: Number(match[2]),\n args: match[3] ?? '',\n };\n })\n .filter((row): row is ProcessTableRow => row !== null && Number.isFinite(row.pid) && Number.isFinite(row.ppid));\n}\nasync function readProcessTable(): Promise<ProcessTableRow[]> {\n const { stdout } = await execFileAsync('ps', ['-eo', 'pid=,ppid=,args='], {\n encoding: 'utf-8',\n maxBuffer: 10 * 1024 * 1024,\n });\n return parseProcessTable(stdout);\n}\nfunction collectProcessTree(rootPids: number[], rows: ProcessTableRow[]): number[] {\n const childrenByParent = new Map<number, number[]>();\n for (const row of rows) {\n const children = childrenByParent.get(row.ppid) ?? [];\n children.push(row.pid);\n childrenByParent.set(row.ppid, children);\n }\n const seen = new Set<number>();\n const ordered: number[] = [];\n const visit = (pid: number) => {\n if (seen.has(pid) || pid === process.pid) return;\n seen.add(pid);\n for (const child of childrenByParent.get(pid) ?? []) visit(child);\n ordered.push(pid);\n };\n for (const pid of rootPids) visit(pid);\n return ordered;\n}\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\nasync function terminatePids(pids: number[]): Promise<void> {\n if (pids.length === 0) return;\n for (const pid of pids) {\n try {\n process.kill(pid, 'SIGTERM');\n } catch {\n }\n }\n await new Promise((resolve) => setTimeout(resolve, PROCESS_CLEANUP_GRACE_MS));\n for (const pid of pids) {\n if (!isProcessAlive(pid)) continue;\n try {\n process.kill(pid, 'SIGKILL');\n } catch {\n }\n }\n}\nexport function conversationRuntimeRootPids(conv: Conversation, rows: ProcessTableRow[]): number[] {\n const launcherScript = join(getOverdeckHome(), 'conversations', conv.tmuxSession, 'launcher.sh');\n const sessionId = conv.claudeSessionId?.trim();\n const sessionNeedles = sessionId ? [`--resume ${sessionId}`, `--session-id ${sessionId}`] : [];\n const serverPid = findManagedServerPidSync();\n return rows\n .filter((row) => {\n if (row.pid === process.pid) return false;\n if (serverPid !== undefined && row.pid === serverPid) return false;\n if (row.args.includes(launcherScript)) return true;\n return sessionNeedles.some((needle) => row.args.includes(needle));\n })\n .map((row) => row.pid);\n}\nasync function killConversationRuntimeProcesses(conv: Conversation): Promise<void> {\n const rows = await readProcessTable();\n const rootPids = conversationRuntimeRootPids(conv, rows);\n const pids = collectProcessTree(rootPids, rows);\n await terminatePids(pids);\n}\nexport async function stopConversationRuntime(conv: Conversation, name: string): Promise<void> {\n if (hasOtherActiveConversationOnTmuxSession(conv.tmuxSession, name)) {\n return;\n }\n await Effect.runPromise(killSession(conv.tmuxSession).pipe(Effect.catch(() => Effect.succeed(undefined))));\n try {\n await killConversationRuntimeProcesses(conv);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.warn(`[conversations] failed to cleanup runtime processes for ${name}: ${msg}`);\n }\n}\n/** Quote a string for safe use in a bash script using single-quote wrapping. */\nfunction shellQuote(str: string): string {\n return \"'\" + str.replace(/'/g, \"'\\\"'\\\"'\") + \"'\";\n}\n// Canonical model-id shape lives in model-validation.ts — it permits the\n// square-bracket context suffix (e.g. `k3[1m]`) that a local copy of this\n// pattern once rejected (PAN-2979). Shell safety comes from single-quote\n// wrapping at the launcher, not from this character set.\nconst SAFE_MODEL_PATTERN = MODEL_ID_PATTERN;\nconst SAFE_EFFORT_PATTERN = /^(low|medium|high)$/;\nconst SAFE_ISSUE_ID_PATTERN = /^[A-Z0-9]+-[0-9]+$/;\nconst PI_CONVERSATION_SOURCE_CONTRACT = [\n 'Pi conversation source contract:',\n \"A message marked source:'extension' was injected by the Overdeck orchestrator or another agent, not typed by the human operator.\",\n 'Treat it as coordination guidance; do not attribute it to the human operator.',\n].join(' ');\nexport async function resolveAllowedHarness(requested: unknown, model?: string | null): Promise<RuntimeName> {\n if (!model) return 'claude-code';\n const explicit: RuntimeName | undefined =\n requested === 'ohmypi' || requested === 'claude-code' || requested === 'codex' || requested === 'acp' || requested === 'kimi-code'\n ? requested\n : undefined;\n return resolveHarness({ model, explicit });\n}\nexport async function isInsideGitWorkTree(dir: string): Promise<boolean> {\n try {\n const { stdout } = await execAsync('git rev-parse --is-inside-work-tree', { cwd: dir, encoding: 'utf-8' });\n return stdout.trim() === 'true';\n } catch {\n return false;\n }\n}\nasync function validateCwdContainment(cwd: string): Promise<boolean> {\n if (!cwd.startsWith('/')) return false;\n const segments = cwd.split('/').filter(Boolean);\n if (segments.includes('..')) return false;\n try {\n const resolved = await realpath(cwd);\n const stats = await stat(resolved);\n if (!stats.isDirectory()) return false;\n const home = homedir();\n return resolved.startsWith(`${home}/`) || resolved === home;\n } catch {\n return false;\n }\n}\nasync function waitForClaudeReady(tmuxSession: string): Promise<void> {\n const deadline = Date.now() + 30_000;\n while (Date.now() < deadline) {\n const output = await Effect.runPromise(capturePane(tmuxSession, 200));\n if (output.includes('❯')) {\n console.log(`[conversations] Claude Code ready in ${tmuxSession}`);\n return;\n }\n await new Promise<void>((r) => setTimeout(r, 500));\n }\n console.warn(`[conversations] Timed out waiting for Claude Code prompt in ${tmuxSession}`);\n}\nfunction isPiTuiInputReady(snapshot: string): boolean {\n return /^\\s*[❯›>]\\s/m.test(snapshot)\n || /(?:^|\\s)0(?:\\.\\d+)?%\\s+context\\s+used\\b/i.test(snapshot)\n // Current omp/Pi footer bar, e.g. \"╭── π > ⬢ Qwen3.6 Plus · ◕ high > ...\".\n // It renders within ~1s of spawn — well before MCP servers finish connecting —\n // and is the actual signal that Pi's TUI is accepting input. The two patterns\n // above matched an older Pi build's chrome and no longer appear at all, which\n // is why this check previously ran to its full timeout on every ohmypi spawn.\n || /⬢[^\\n]*◕/.test(snapshot);\n}\nexport async function waitForPiTuiReady(tmuxSession: string, timeoutMs = 60_000): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const snapshot = await Effect.runPromise(\n capturePane(tmuxSession, 40).pipe(Effect.catch(() => Effect.succeed(''))),\n );\n if (isPiTuiInputReady(snapshot)) {\n console.log(`[conversations] Pi TUI ready for ${tmuxSession}`);\n return true;\n }\n await new Promise<void>((r) => setTimeout(r, 250));\n }\n console.warn(`[conversations] Timed out waiting for Pi TUI to render in ${tmuxSession}`);\n return false;\n}\nexport function shouldReportUnresolvedLiveSession(\n conv: Pick<Conversation, 'status' | 'harness'> | null | undefined,\n): boolean {\n if (!conv || conv.status !== 'active') return false;\n return getHarnessBehavior(conv.harness).transcriptKind === 'claude-jsonl';\n}\n\nexport function conversationSessionAliveFromState(\n conv: Pick<Conversation, 'status' | 'forkStatus'>,\n tmuxSessionAlive: boolean,\n): boolean {\n return conv.status === 'active' && !conv.forkStatus && tmuxSessionAlive;\n}\n\nexport function conversationNeedsRunningRepair(\n conv: Pick<Conversation, 'status' | 'forkStatus'>,\n tmuxSessionAlive: boolean,\n harnessProcessAlive: boolean,\n): boolean {\n return conv.status === 'ended' && !conv.forkStatus && tmuxSessionAlive && harnessProcessAlive;\n}\n/** Generate a default conversation name, e.g. 20260404-1234 */\nfunction generateConversationName(): string {\n const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');\n return `${date}-${Math.floor(Math.random() * 9000 + 1000)}`;\n}\n/** Sanitize a user-provided name to be safe for tmux session names */\nfunction sanitizeName(name: string): string {\n return name.replace(/[^a-zA-Z0-9_-]/g, '-').slice(0, 64);\n}\nexport async function tmuxSessionExists(sessionName: string): Promise<boolean> {\n return Effect.runPromise(sessionExists(sessionName));\n}\nexport async function waitForTmuxSession(sessionName: string, timeoutMs = 30000): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (await Effect.runPromise(sessionExists(sessionName))) return;\n await new Promise(r => setTimeout(r, 250));\n }\n throw new Error(`Timed out waiting for tmux session ${sessionName}`);\n}\nexport function shouldUseSupervisorForConversation(\n harness: RuntimeName,\n options: { codexTransport?: 'app-server' | 'tui' } = {},\n): boolean {\n if (harness === 'codex' && options.codexTransport === 'app-server') return false;\n return getHarnessBehavior(harness).supportsPtySupervisor && process.env.OVERDECK_DOCKER_WORKSPACE !== '1' && process.env.PAN_DOCKER !== '1';\n}\nexport async function waitForConversationRuntimeReady(tmuxSession: string, harness: RuntimeName, mode: 'spawn' | 'respawn'): Promise<void> {\n if (harness === 'acp') {\n await waitForAcpHostReady(tmuxSession);\n return;\n }\n const transcriptKind = getHarnessBehavior(harness).transcriptKind;\n if (transcriptKind === 'ohmypi-jsonl') {\n const ready = await waitForPiTuiReady(tmuxSession);\n if (!ready) {\n throw new Error(\n `Pi (ohmypi) did not become interactive in ${tmuxSession} within the startup window — MCP server connections are likely still in progress. Retry once MCP servers finish connecting, or check ~/.claude/mcp.json for a slow/hanging server.`,\n );\n }\n }\n else if (transcriptKind === 'kimi-wire-jsonl') {\n // PAN-1837 review fix: this readiness dispatcher had no Kimi branch, so it\n // fell through to waitForClaudeReady() and waited for Claude Code's own\n // ready markers, which the native kimi binary never produces.\n const ready = await waitForPromptReady(tmuxSession, 'kimi-code', 30);\n if (!ready) {\n throw new Error(`Kimi Code did not become interactive in ${tmuxSession} within the startup window.`);\n }\n }\n else if (transcriptKind !== 'codex-rollout-jsonl' && mode === 'spawn') {\n await waitForClaudeReady(tmuxSession);\n console.log(`[conversations] Claude ready in ${tmuxSession}`);\n } else if (transcriptKind !== 'codex-rollout-jsonl') await waitForReadySignal(tmuxSession, 30);\n}\nexport async function handleConversationSwitchModel(\n name: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) {\n return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n }\n const model = typeof body['model'] === 'string' && body['model'].trim()\n ? body['model'].trim()\n : (conv.model ?? undefined);\n const currentHarness: RuntimeName = conv.harness ?? 'claude-code';\n const requestedHarness = body['harness'];\n let harness: RuntimeName = currentHarness;\n if (requestedHarness === 'ohmypi' || requestedHarness === 'pi' || requestedHarness === 'claude-code' || requestedHarness === 'codex' || requestedHarness === 'acp' || requestedHarness === 'kimi-code') {\n const requestedRuntime: RuntimeName = requestedHarness === 'pi' ? 'ohmypi' : requestedHarness;\n if (requestedRuntime !== currentHarness) {\n const policyModel = model ?? conv.model ?? '';\n const decision = canUseHarnessSync(\n requestedRuntime,\n policyModel,\n await getProviderAuthMode(policyModel),\n );\n if (!decision.allowed) {\n return jsonResponse(\n { error: decision.reason ?? 'Harness not allowed for this model' },\n { status: 400 },\n );\n }\n }\n harness = requestedRuntime;\n }\n const harnessChanged = harness !== currentHarness;\n if (!(await validateCwdContainment(conv.cwd))) {\n return jsonResponse({ error: 'Invalid cwd' }, { status: 400 });\n }\n if (model && !SAFE_MODEL_PATTERN.test(model)) {\n return jsonResponse({ error: 'Invalid model' }, { status: 400 });\n }\n const livePiSwitch = isPiControlChannelHarness(currentHarness) && conv.status !== 'ended';\n if (conv.claudeSessionId && harnessChanged) {\n return jsonResponse(\n { error: 'Conversation harness is locked once a conversation has started' },\n { status: 409 },\n );\n }\n if (conv.claudeSessionId && !livePiSwitch) {\n return jsonResponse(\n { error: 'Conversation model is locked once a conversation has started' },\n { status: 409 },\n );\n }\n if (model) {\n if (livePiSwitch) {\n await sendConversationControlCommand(conv, { type: 'set_model', model });\n }\n setConversationModel(name, model);\n }\n if (harnessChanged) setConversationHarness(name, harness);\n const updated = getConversationByName(name) ?? conv;\n return jsonResponse({\n ...updated,\n model: model ?? updated.model,\n harness,\n sessionAlive: false,\n });\n}\nfunction getPtySupervisorSocketPath(agentId: string): string {\n return join(getOverdeckHome(), 'sockets', `pty-${agentId}.sock`);\n}\n/**\n * Pull the supervisor's own failure out of the tmux pane it died in.\n *\n * A supervisor that cannot load its native node-pty exits instantly with an\n * ERR_MODULE_NOT_FOUND on stderr; all the spawn path sees is the socket that\n * never appeared. Reporting only the timeout hides the actual cause and sent\n * an operator hunting the memory governor instead (PAN-3172).\n */\nexport function extractSupervisorFailure(paneText: string): string | null {\n const lines = paneText.split('\\n').map((line) => line.trim()).filter(Boolean);\n const errors = lines.filter((line) => /error|cannot find|ERR_[A-Z_]+|pty-supervisor:/i.test(line));\n const chosen = (errors.length > 0 ? errors : lines).slice(-3);\n if (chosen.length === 0) return null;\n return chosen.join(' | ').slice(0, 500);\n}\n\nasync function waitForPtySupervisorSocket(agentId: string, timeoutMs = PTY_SUPERVISOR_SOCKET_WAIT_MS): Promise<void> {\n const socketPath = getPtySupervisorSocketPath(agentId);\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const info = await stat(socketPath);\n if ((info.mode & 0o777) === 0o600) return;\n } catch {\n }\n await new Promise(r => setTimeout(r, 250));\n }\n const failure = extractSupervisorFailure(await capturePaneText(agentId, 40));\n throw new Error(\n `Timed out waiting for PTY supervisor socket ${socketPath}`\n + (failure ? ` — supervisor output: ${failure}` : ''),\n );\n}\nasync function extractModelFromSessionFile(sessionFile: string): Promise<string | null> {\n try {\n if (!existsSync(sessionFile)) return null;\n const stream = createReadStream(sessionFile, { encoding: 'utf-8' });\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n try {\n for await (const line of rl) {\n if (!line.trim()) continue;\n const entry = JSON.parse(line);\n if (entry.type === 'assistant' && entry.message?.model) {\n return entry.message.model as string;\n }\n }\n } finally {\n rl.close();\n stream.destroy();\n }\n } catch {\n }\n return null;\n}\nlet backfillRunning = false;\nexport async function backfillConversationModels(resolveSessionFile: (conv: Conversation) => Promise<string | null>): Promise<void> {\n if (backfillRunning) return;\n backfillRunning = true;\n try {\n const convs = listConversations();\n const probe = await Promise.all(\n convs.map(async (conv) => (!conv.model ? await resolveSessionFile(conv) : null)),\n );\n const candidates = convs.filter((_, i) => probe[i] !== null);\n const BATCH_SIZE = 10;\n let backfilled = 0;\n for (let i = 0; i < candidates.length; i += BATCH_SIZE) {\n const batch = candidates.slice(i, i + BATCH_SIZE);\n const results = await Promise.all(\n batch.map(async (conv) => {\n const sessionFile = await resolveSessionFile(conv);\n if (!sessionFile) return false;\n const model = await extractModelFromSessionFile(sessionFile);\n if (model && SAFE_MODEL_PATTERN.test(model)) {\n backfillConversationModel(conv.name, model);\n return true;\n }\n return false;\n }),\n );\n backfilled += results.filter(Boolean).length;\n }\n if (backfilled > 0) {\n console.log(`[conversations] Backfilled model for ${backfilled} conversation(s)`);\n }\n } finally {\n backfillRunning = false;\n }\n}\nexport function startConversationModelBackfill(resolveSessionFile: (conv: Conversation) => Promise<string | null>): void {\n void backfillConversationModels(resolveSessionFile).catch((err: unknown) => {\n console.error('[conversations] Model backfill failed:', err);\n });\n}\nasync function claudeConversationSystemPromptFiles(cwd: string): Promise<string[]> {\n const files: string[] = [];\n const contextFile = workspaceContextFile(cwd);\n try {\n await stat(contextFile);\n files.push(contextFile);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n files.push(await ensureSessionContextBriefingFile());\n return files;\n}\nasync function ensurePiConversationSourceContractFile(): Promise<string> {\n const contextDir = join(getOverdeckHome(), 'context');\n await mkdir(contextDir, { recursive: true });\n const path = join(contextDir, 'pi-conversation-source-contract.md');\n await writeFile(path, `${PI_CONVERSATION_SOURCE_CONTRACT}\\n`, 'utf-8');\n return path;\n}\nexport async function piConversationSystemPromptFiles(cwd: string): Promise<string[]> {\n const files: string[] = [];\n const globalFile = piGlobalContextFile();\n try {\n await stat(globalFile);\n files.push(globalFile);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n files.push(await ensurePiConversationSourceContractFile());\n const contextFile = workspaceContextFile(cwd);\n try {\n await stat(contextFile);\n files.push(contextFile);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n files.push(await ensureSessionContextBriefingFile());\n return files;\n}\nfunction isNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';\n}\nexport async function spawnConversationSession(\n tmuxSession: string,\n cwd: string,\n claudeSessionId: string,\n model?: string,\n effort?: string,\n issueId?: string,\n resume = false,\n harness: RuntimeName = 'claude-code',\n plainFork = false,\n): Promise<void> {\n const behavior = getHarnessBehavior(harness);\n const harnessLaunch = await prepareHarnessLaunch(harness);\n const stateDir = join(getOverdeckHome(), 'conversations', tmuxSession);\n await mkdir(stateDir, { recursive: true });\n clearReadySignal(tmuxSession);\n const launcherScript = join(stateDir, 'launcher.sh');\n const permissionFlags = getClaudePermissionFlagsStringSync();\n let runtimeCommand = `claude ${permissionFlags}`;\n let providerExportsStr = '';\n let piFields: {\n harness: 'ohmypi';\n piMode: 'tui';\n piExtensionPath: string;\n piSessionDir: string;\n resumeSessionId?: string;\n } | undefined;\n let codexFields: {\n harness: 'codex';\n codexMode: 'app-server' | 'tui';\n codexHome: string;\n codexSessionDir: string;\n resumeSessionId?: string;\n } | undefined;\n let acpFields: (ReturnType<typeof getAcpLauncherFields> & { resumeSessionId?: string }) | undefined;\n let kimiCodeFields: { harness: 'kimi-code'; kimiCodeModel: string; kimiCodeYolo: true; resumeSessionId?: string } | undefined;\n let codexTransport: 'app-server' | 'tui' | undefined;\n if (behavior.launchCommandKind === 'acp-host') {\n if (!model) throw new Error('ACP conversation requires a model');\n if (!SAFE_MODEL_PATTERN.test(model)) throw new Error('Invalid model name');\n const sessionIdPath = join(getOverdeckHome(), 'agents', tmuxSession, 'acp-session-id');\n const resumeSessionId = resume\n ? await readFile(sessionIdPath, 'utf-8').then((value) => value.trim() || undefined).catch(() => undefined)\n : undefined;\n await rm(sessionIdPath, { force: true }); // PAN-3357: not a dir removal\n acpFields = {\n ...getAcpLauncherFields(tmuxSession, model, cwd, harnessLaunch.binaryPath, 'work'),\n resumeSessionId,\n };\n runtimeCommand = 'acp-host';\n } else if (model) {\n if (!SAFE_MODEL_PATTERN.test(model)) {\n throw new Error('Invalid model name');\n }\n runtimeCommand = await getAgentRuntimeBaseCommand(model, undefined, undefined, harness);\n const mode = resolvePermissionModeSync();\n if (!runtimeCommand.includes('--permission-mode')) {\n runtimeCommand = `${runtimeCommand} --permission-mode ${mode === 'auto' ? 'auto' : BYPASS_PERMISSION_MODE}`;\n }\n providerExportsStr = (await getProviderExportsForModel(model, harness)).trim();\n if (behavior.transcriptKind === 'ohmypi-jsonl') {\n if (getProviderForModelSync(model).name === 'openai') {\n const auth = await getOhmypiCodexAuthStatus({ refreshIfExpired: true });\n if (auth.status === 'missing' || auth.status === 'expired') {\n throw new Error(\n 'ohmypi ChatGPT/Codex login (openai-codex) has expired and could not be refreshed. ' +\n 'Re-authenticate with `pan pi-auth login`, then retry.',\n );\n }\n }\n const paths = piFifoPaths(tmuxSession);\n const piSessionDir = join(paths.agentDir, 'sessions');\n await mkdir(paths.agentDir, { recursive: true, mode: 0o700 });\n await mkdir(piSessionDir, { recursive: true, mode: 0o700 });\n const storedPiSessionId = resume\n ? (await readFile(join(paths.agentDir, 'session.id'), 'utf-8').then((s) => s.trim()).catch(() => undefined))\n : undefined;\n piFields = {\n harness: 'ohmypi',\n piMode: 'tui',\n piExtensionPath: resolveOhmypiExtensionPath() ?? resolve(process.cwd(), 'packages/ohmypi-extension/dist/index.js'),\n piSessionDir,\n resumeSessionId: storedPiSessionId || undefined,\n };\n } else if (behavior.usesCodexHome) {\n const codexHome = join(getOverdeckHome(), 'agents', tmuxSession, 'codex-home');\n const codexConfig = loadConfigSync().config.codex;\n const codexPermMode = codexConfig?.permissionMode ?? 'workspace';\n codexTransport = codexConfig?.transport ?? 'app-server';\n const codexApprovalPolicy = codexPermMode === 'full-access' ? 'never' : 'on-request';\n const codexSandboxMode =\n codexPermMode === 'full-access' ? 'danger-full-access'\n : codexPermMode === 'read-only' ? 'read-only'\n : 'workspace-write';\n const codexApprovalsReviewer = codexPermMode === 'auto-review' ? 'auto_review' : undefined;\n const { initCodexHome, extractThreadIdFromRollout } = await import('../runtimes/codex.js');\n initCodexHome(codexHome, {\n trustedDir: cwd,\n approvalPolicy: codexApprovalPolicy,\n sandboxMode: codexSandboxMode,\n approvalsReviewer: codexApprovalsReviewer,\n });\n const resumeSessionId = resume\n ? await resolveCodexRolloutPath(tmuxSession, { agentsDirOverride: join(getOverdeckHome(), 'agents') })\n .then((rollout) => rollout ? extractThreadIdFromRollout(rollout) ?? undefined : undefined)\n : undefined;\n codexFields = {\n harness: 'codex',\n codexMode: codexTransport,\n codexHome,\n codexSessionDir: join(codexHome, 'sessions'),\n resumeSessionId,\n };\n } else if (behavior.launchCommandKind === 'kimi-code-tui') {\n // PAN-1837 review fix: read the persisted kimi-session-id when resuming\n // so a stopped Kimi conversation actually resumes its native session\n // (-S <id>) instead of always launching fresh and silently overwriting\n // the pinned transcript pointer with a brand-new session directory.\n // Verify the pinned wire.jsonl still exists on disk before trusting the\n // id — if it was cleaned up, fall through to the fresh-launch path\n // (kimiExistingSessionsBefore capture below) instead of resuming a\n // session Kimi can no longer find.\n let kimiResumeSessionId: string | undefined;\n if (resume) {\n const kimiSessionIdPath = join(getOverdeckHome(), 'agents', tmuxSession, 'kimi-session-id');\n const pinnedId = await readFile(kimiSessionIdPath, 'utf-8').then((value) => value.trim() || undefined).catch(() => undefined);\n if (pinnedId) {\n const { kimiSessionsRoot } = await import('../runtimes/kimi-code.js');\n const candidateWire = join(kimiSessionsRoot(join(homedir(), '.kimi-code'), cwd), pinnedId, 'agents', 'main', 'wire.jsonl');\n const wireExists = await stat(candidateWire).then(() => true, () => false);\n if (wireExists) kimiResumeSessionId = pinnedId;\n }\n }\n kimiCodeFields = {\n harness: 'kimi-code',\n kimiCodeModel: model,\n kimiCodeYolo: true,\n ...(kimiResumeSessionId ? { resumeSessionId: kimiResumeSessionId } : {}),\n };\n }\n }\n let launcherModel = model;\n if (behavior.contextLayerKind === 'pi' && model) {\n const piProvider = piProviderForModel(model);\n if (piProvider) launcherModel = `${piProvider}/${model}`;\n }\n if (effort && !SAFE_EFFORT_PATTERN.test(effort)) {\n throw new Error('Invalid effort level');\n }\n const useSupervisor = shouldUseSupervisorForConversation(harness, { codexTransport });\n let supervisorScriptPath: string | undefined;\n if (useSupervisor) {\n supervisorScriptPath = resolvePtySupervisorScriptPath();\n await writePtyToken(tmuxSession);\n }\n let channelsBridgeMcpConfig: string | undefined;\n if (\n !piFields &&\n !codexFields &&\n !acpFields &&\n !kimiCodeFields &&\n !plainFork &&\n isClaudeCodeChannelsEnabled() &&\n (!model || getProviderForModelSync(model).name === 'anthropic') &&\n process.env.CLAUDE_CODE_USE_BEDROCK !== '1' &&\n process.env.CLAUDE_CODE_USE_VERTEX !== '1' &&\n process.env.CLAUDE_CODE_USE_FOUNDRY !== '1' &&\n process.env.OVERDECK_DOCKER_WORKSPACE !== '1' &&\n process.env.PAN_DOCKER !== '1'\n ) {\n channelsBridgeMcpConfig = join(stateDir, 'agent-mcp.json');\n writeBridgeTokenSync(tmuxSession);\n await writeChannelsBridgeMcpConfig(channelsBridgeMcpConfig, tmuxSession);\n }\n // PAN-1837 review fix: the tmux-create + session-capture sequence below is\n // wrapped per-bucket (withKimiSessionCaptureLock) for kimi-code conversations\n // and genuinely awaited — not fire-and-forget — so two conversations sharing\n // one cwd never snapshot the same \"existing sessions\" set and race for the\n // same newest directory. Non-kimi harnesses run this closure directly,\n // unaffected by the lock.\n const launchTmuxAndCaptureSession = async (): Promise<void> => {\n // Conversations have no AgentState row, so the dashboard can't resolve\n // their wire.jsonl without a conversation-owned captured session id —\n // snapshot the bucket now (before the tmux session exists, and — for\n // kimi-code — inside the per-bucket lock) so the capture below can diff\n // against it instead of guessing from mtime. Skipped when kimiCodeFields\n // already carries a verified resumeSessionId (PAN-1837 review fix): a\n // true resume must not snapshot/capture a replacement id and overwrite\n // the pinned transcript pointer.\n let kimiExistingSessionsBefore: Set<string> | undefined;\n if (kimiCodeFields && !kimiCodeFields.resumeSessionId) {\n try {\n const { kimiSessionsRoot } = await import('../runtimes/kimi-code.js');\n kimiExistingSessionsBefore = new Set(await readdir(kimiSessionsRoot(join(homedir(), '.kimi-code'), cwd)));\n } catch {\n kimiExistingSessionsBefore = new Set();\n }\n }\n\n const launcherTmp = `${launcherScript}.${randomUUID()}.tmp`;\n await writeFile(\n launcherTmp,\n generateLauncherScriptSync({\n role: 'work',\n spawnMode: 'conversation',\n workingDir: cwd,\n setTerminalEnv: true,\n unsetProviderEnv: true,\n overdeckEnv: { ...(issueId ? { issueId } : {}), ...((piFields || codexFields || acpFields || useSupervisor) ? { agentId: tmuxSession } : {}) },\n extraEnvExports: [\n harnessLaunch.pathExport,\n `export OVERDECK_DASHBOARD_URL=\"http://127.0.0.1:${process.env['API_PORT'] ?? process.env['PORT'] ?? '3011'}\"`,\n ],\n providerExports: providerExportsStr || undefined,\n trapHup: true,\n baseCommand: runtimeCommand,\n appendSystemPromptFiles: piFields\n ? await piConversationSystemPromptFiles(cwd)\n : codexFields || acpFields || kimiCodeFields\n ? []\n : await claudeConversationSystemPromptFiles(cwd),\n model: launcherModel,\n ...(piFields ?? codexFields ?? acpFields ?? kimiCodeFields ?? {\n resumeSessionId: resume ? claudeSessionId : undefined,\n sessionId: resume ? undefined : claudeSessionId,\n }),\n extraArgs: !piFields && !acpFields && !kimiCodeFields && effort ? `--effort \"${effort}\"` : undefined,\n keepAlive: true,\n fileMode: 0o700,\n channelsBridgeMcpConfig,\n useSupervisor,\n supervisorScriptPath,\n }),\n { mode: 0o700 },\n );\n await rename(launcherTmp, launcherScript);\n try {\n await Effect.runPromise(killSession(tmuxSession));\n } catch {\n }\n console.log(`[claude-invoke] purpose=conversation-session | model=${model || 'default'} | source=conversations.ts:spawnConversationSession | session=${tmuxSession} | resume=${resume} | command=\"${runtimeCommand}\"`);\n try {\n const { preTrustDirectory } = await import('../workspace-manager.js') as { preTrustDirectory: (dir: string) => void };\n preTrustDirectory(cwd);\n } catch { /* non-fatal */ }\n try {\n await Effect.runPromise(createSession(tmuxSession, cwd, `bash ${shellQuote(launcherScript)}`, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n TERM: 'xterm-256color',\n },\n }));\n } catch (err) {\n if ((err as { code?: string })?.code === 'ENOENT') {\n throw new Error(\n 'tmux is not installed. Install it with: brew install tmux (macOS) or sudo apt-get install tmux (Linux)',\n );\n }\n throw err;\n }\n if (useSupervisor) {\n await waitForPtySupervisorSocket(tmuxSession);\n }\n if (behavior.usesCodexHome && codexFields?.codexHome && codexTransport === 'tui') {\n const codexHomeDir = codexFields.codexHome;\n void (async () => {\n try {\n const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } =\n await import('../runtimes/codex.js');\n const rollout = await waitForCodexRollout(codexHomeDir, 120_000);\n if (rollout) {\n const threadId = extractThreadIdFromRollout(rollout);\n if (threadId) writeThreadId(tmuxSession, threadId);\n }\n } catch {\n }\n })();\n }\n if (kimiCodeFields && kimiExistingSessionsBefore) {\n // Genuinely awaited (not fire-and-forget) so the per-bucket lock below\n // stays held until this conversation's session directory is captured —\n // that's what makes the next queued launch's snapshot see it as\n // \"existing\" rather than racing to claim it too.\n //\n // PAN-1837 review fix (cycle 8): a null capture or a thrown error here\n // used to be silently absorbed, so the conversation was still reported\n // healthy with no kimi-session-id pointer — transcript/cost resolution\n // then falls back to the newest session in the shared cwd bucket, which\n // can render a DIFFERENT conversation's or agent's prompts/transcript\n // under this identity. Require a real captured id or fail the launch,\n // same fail-closed contract as spawnAgent/restartAgent/recoverAgent.\n const { waitForNewKimiSessionAsync, writeKimiSessionId } = await import('../runtimes/kimi-code.js');\n const sessionId = await waitForNewKimiSessionAsync(join(homedir(), '.kimi-code'), cwd, kimiExistingSessionsBefore);\n if (!sessionId) {\n throw new Error(\n `kimi-code session capture timed out for ${tmuxSession} — no new session directory appeared under the workspace bucket`,\n );\n }\n writeKimiSessionId(tmuxSession, sessionId);\n }\n };\n\n if (kimiCodeFields) {\n const { withKimiSessionCaptureLock } = await import('../runtimes/kimi-code.js');\n await withKimiSessionCaptureLock(join(homedir(), '.kimi-code'), cwd, launchTmuxAndCaptureSession);\n } else {\n await launchTmuxAndCaptureSession();\n }\n if (channelsBridgeMcpConfig) {\n void dismissDevChannelsDialog(tmuxSession).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[conversations] dismissDevChannelsDialog failed for ${tmuxSession}: ${msg}`);\n });\n }\n await Effect.runPromise(setOption(tmuxSession, 'destroy-unattached', 'off'));\n await Effect.runPromise(setOption(exactPaneTarget(tmuxSession), 'remain-on-exit', 'on'));\n}\nexport interface ResolvedRegisteredProject {\n key: string;\n config: ProjectConfig;\n}\n\n/** Resolve a project key or display name without blocking the dashboard event loop. */\nexport async function resolveRegisteredProject(\n input: string,\n): Promise<ResolvedRegisteredProject | { error: string }> {\n const projects = await listProjectsAsync();\n const project = projects.find((candidate) => candidate.key === input)\n ?? projects.find((candidate) => candidate.config.name === input);\n return project ?? { error: `Unknown project: ${input}` };\n}\n\n/**\n * Resolve a conversation's cwd from a project identifier.\n *\n * The Command Deck identifies projects by display name, not yaml key\n * (PAN-2590) — accept either, like GET /api/session-trees does.\n */\nexport async function resolveProjectCwd(\n projectIdentifier: string,\n): Promise<{ key: string; cwd: string } | { error: string }> {\n const resolved = await resolveRegisteredProject(projectIdentifier);\n if ('error' in resolved) return resolved;\n const projectPath = resolved.config.path;\n if (!projectPath) {\n return { error: `Project path does not exist: (unset) (project: ${projectIdentifier})` };\n }\n try {\n await stat(projectPath);\n } catch {\n return { error: `Project path does not exist: ${projectPath} (project: ${projectIdentifier})` };\n }\n return { key: resolved.key, cwd: projectPath };\n}\n\nexport interface ConversationCreateRequestBody { [key: string]: unknown }\nexport async function handleConversationCreate(\n body: ConversationCreateRequestBody,\n deps: { generateAiTitle: (name: string, message: string) => Promise<void> },\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const message = typeof body['message'] === 'string' ? body['message'].trim() : '';\n const model = typeof body['model'] === 'string' ? body['model'].trim() || undefined : undefined;\n const effort = typeof body['effort'] === 'string' ? body['effort'].trim() || undefined : undefined;\n const harness = await resolveAllowedHarness(body['harness'], model);\n const issueId = typeof body['issueId'] === 'string' ? body['issueId'] : undefined;\n const projectKey = typeof body['projectKey'] === 'string' ? body['projectKey'].trim() : undefined;\n if (issueId && !SAFE_ISSUE_ID_PATTERN.test(issueId)) return jsonResponse({ error: 'Invalid issueId' }, { status: 400 });\n if (model && !SAFE_MODEL_PATTERN.test(model)) return jsonResponse({ error: 'Invalid model' }, { status: 400 });\n if (effort && !SAFE_EFFORT_PATTERN.test(effort)) return jsonResponse({ error: 'Invalid effort' }, { status: 400 });\n let cwd = getDefaultCwd();\n let canonicalProjectKey: string | undefined;\n if (projectKey) {\n const resolved = await resolveProjectCwd(projectKey);\n if ('error' in resolved) return jsonResponse({ error: resolved.error }, { status: 400 });\n cwd = resolved.cwd;\n canonicalProjectKey = resolved.key;\n }\n if (message && message.length > 50_000) {\n return jsonResponse({ error: 'message exceeds maximum length of 50000 characters' }, { status: 400 });\n }\n let name = generateConversationName();\n for (let i = 0; i < 5 && getConversationByName(name); i++) name = generateConversationName();\n const tmuxSession = `conv-${name}`;\n const claudeSessionId = randomUUID();\n console.log(`[conversations] Creating conversation \"${name}\" with model=${model ?? 'default'} effort=${effort ?? 'default'} cwd=${cwd}`);\n const MAX_TITLE_LEN = 60;\n const title = message ? message.slice(0, MAX_TITLE_LEN) + (message.length > MAX_TITLE_LEN ? '…' : '') : 'New conversation';\n const conv = createConversation({ name, tmuxSession, cwd, issueId, claudeSessionId, title, titleSource: message ? 'auto' : 'default', titleSeed: title, model, effort, harness, projectKey: canonicalProjectKey });\n getEventStore().emitOnly({ type: 'conversation.created', timestamp: new Date().toISOString(), payload: { conversationName: name } });\n void (async () => {\n try {\n await spawnConversationSession(tmuxSession, cwd, claudeSessionId, model, effort, issueId, false, harness);\n console.log(`[conversations] tmux session ${tmuxSession} spawned, sessionId: ${claudeSessionId}`);\n await waitForConversationRuntimeReady(tmuxSession, harness, 'spawn');\n if (message) {\n const delivery = await deliverAgentMessage(tmuxSession, message, 'conversation-message', resolveConversationDeliveryMethod(conv));\n if (harness === 'acp' && !delivery.ok) {\n throw new Error(`ACP initial prompt did not land: ${delivery.failure ?? 'unknown failure'}`);\n }\n }\n } catch (spawnErr: unknown) {\n const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);\n console.error(`[conversations] background spawn failed for ${tmuxSession}: ${msg}`);\n // PAN-1837 review fix: kimi-code needs the same teardown-on-failure as\n // acp — a failed capture must not leave a running tmux session with\n // no owned native identity presented as a healthy conversation.\n if (harness === 'acp' || harness === 'kimi-code') await stopConversationRuntime(conv, name);\n updateSpawnError(name, msg);\n getEventStore().emitOnly({ type: 'conversation.created', timestamp: new Date().toISOString(), payload: { conversationName: name } });\n }\n })();\n if (message) {\n void deps.generateAiTitle(name, message).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[TITLE-GEN-FAILED] AI title generation FAILED for \"${name}\" — NO RETRY, NO FALLBACK:`, msg);\n });\n }\n return jsonResponse({ ...conv, sessionAlive: false }, { status: 201 });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] create conversation failed:', msg);\n return jsonResponse({ error: msg || 'Internal server error' }, { status: error instanceof UnknownModelError ? 400 : 500 });\n }\n}\nexport async function handleConversationStop(name: string, deps: { resolveSessionFileForCleanup?: (conv: Conversation) => string | null }): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n await stopConversationRuntime(conv, name);\n markConversationEnded(name);\n void (async () => {\n await new Promise((r) => setTimeout(r, 500));\n await cleanupUnreferencedConversationAttachments({ name: conv.name, sessionFile: deps.resolveSessionFileForCleanup?.(conv) ?? null });\n })();\n return jsonResponse({ success: true });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] stop conversation failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\nexport async function handleConversationResume(\n name: string,\n body: Record<string, unknown>,\n deps: { resolveSessionFile: (conv: Conversation) => Promise<string | null> },\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n const model = typeof body['model'] === 'string' && body['model'].trim() ? body['model'].trim() : (conv.model ?? undefined);\n const effort = typeof body['effort'] === 'string' && body['effort'].trim() ? body['effort'].trim() : (conv.effort ?? undefined);\n const claudeAlive = await isHarnessProcessAlive(conv.tmuxSession);\n if (claudeAlive) {\n updateLastAttached(name);\n markConversationActive(name);\n return jsonResponse({ ...conv, status: 'active', reattached: true });\n }\n const oldSessionId = conv.claudeSessionId;\n const resumeCause = conv.status === 'ended' ? 'operator' : 'system';\n const harness: RuntimeName = conv.harness ?? 'claude-code';\n const modelChanged = !!model && model !== conv.model;\n if (!(await validateCwdContainment(conv.cwd))) return jsonResponse({ error: 'Invalid cwd' }, { status: 400 });\n if (model && modelChanged && !SAFE_MODEL_PATTERN.test(model)) return jsonResponse({ error: 'Invalid model' }, { status: 400 });\n if (model && modelChanged) setConversationModel(name, model);\n let canResume = !!oldSessionId;\n if (oldSessionId) {\n const resumeFile = await deps.resolveSessionFile(conv);\n if (!resumeFile || !existsSync(resumeFile)) {\n canResume = false;\n console.error(`[conversations] SESSION-LOST ${name} harness=${harness} claudeSessionId=${oldSessionId} resolved=${resumeFile ?? 'null'} — resuming with a fresh session`);\n }\n }\n const respawn = markRespawnPending(conv.tmuxSession);\n try {\n await spawnConversationSession(conv.tmuxSession, conv.cwd, oldSessionId ?? randomUUID(), model, effort, conv.issueId ?? undefined, canResume, harness);\n await waitForTmuxSession(conv.tmuxSession);\n await waitForConversationRuntimeReady(conv.tmuxSession, harness, 'respawn');\n // The harness may open its own blocking gate on resume. The operator owns\n // that answer, so the contract delivery seam skips rather than races it.\n await deliverResumeContractUnlessGated(\n conv.tmuxSession,\n `CONVERSATION RESUME: ${buildResumeContract(resumeCause)}`,\n 'conversation-resume',\n resolveConversationDeliveryMethod(conv),\n );\n markConversationActive(name);\n return jsonResponse({ ...conv, status: 'active', model: model ?? conv.model, harness, reattached: false, sessionAlive: true });\n } catch (error) {\n // PAN-1837 review fix: kimi-code needs the same teardown-on-failure as\n // acp — a failed capture must not leave a running tmux session with no\n // owned native identity presented as a healthy conversation.\n if (harness === 'acp' || harness === 'kimi-code') await stopConversationRuntime(conv, name);\n throw error;\n } finally {\n respawn.done();\n }\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] resume conversation failed:', msg);\n return jsonResponse({ error: msg || 'Internal server error' }, { status: 500 });\n }\n}\nexport async function handleConversationDelete(\n name: string,\n deps: { invalidateFavoritesCache: () => void },\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n await stopConversationRuntime(conv, name);\n markConversationEnded(name);\n archiveConversation(name);\n removeFavorite('conversation', name);\n deps.invalidateFavoritesCache();\n await cleanupConversationAttachments(name);\n return jsonResponse({ success: true });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] delete conversation failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\nexport async function handleConversationRestartAll(\n deps: { resolveSessionFile: (conv: Conversation) => Promise<string | null> },\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const allConvs = listConversations();\n const liveSessionNames = new Set(await Effect.runPromise(listSessionNames()));\n const convs = allConvs.filter((c) => liveSessionNames.has(c.tmuxSession));\n const results: { name: string; model: string | null; status: string }[] = [];\n for (const conv of convs) {\n const respawn = markRespawnPending(conv.tmuxSession);\n let attemptedHarness: RuntimeName = conv.harness ?? 'claude-code';\n try {\n await Effect.runPromise(killSession(conv.tmuxSession).pipe(Effect.catch(() => Effect.succeed(undefined))));\n const oldSessionId = conv.claudeSessionId;\n const sessionFileForResume = await deps.resolveSessionFile(conv);\n const canResume = !!oldSessionId && !!sessionFileForResume && existsSync(sessionFileForResume);\n const harness = await resolveAllowedHarness(conv.harness, conv.model);\n attemptedHarness = harness;\n await spawnConversationSession(conv.tmuxSession, conv.cwd, oldSessionId ?? randomUUID(), conv.model ?? undefined, conv.effort ?? undefined, conv.issueId ?? undefined, canResume, harness);\n if (harness === 'acp') {\n await waitForConversationRuntimeReady(conv.tmuxSession, harness, 'respawn');\n }\n setConversationHarness(conv.name, harness);\n markConversationActive(conv.name);\n results.push({ name: conv.name, model: conv.model, status: 'restarted' });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[conversations] Failed to restart ${conv.name}:`, msg);\n // PAN-1837 review fix: kimi-code needs the same teardown-on-failure as\n // acp — a failed capture must not leave a running tmux session with no\n // owned native identity presented as a healthy conversation.\n if (attemptedHarness === 'acp' || attemptedHarness === 'kimi-code') await stopConversationRuntime(conv, conv.name);\n results.push({ name: conv.name, model: conv.model, status: 'failed' });\n } finally {\n respawn.done();\n }\n }\n console.log(`[conversations] Restarted ${results.filter(r => r.status === 'restarted').length}/${convs.length} conversations`);\n return jsonResponse({ restarted: results.length, results });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] restart conversations failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { request as httpRequest } from 'node:http';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\n\nimport { jsonResponse } from '../../dashboard/server/http-helpers.js';\nimport { validateOriginHeaders, getHeaderFromMap, type HeaderMap } from '../../dashboard/server/routes/origin-validation.js';\nimport { detectAwaitingInputForAgent, parseCodexApprovalPrompt } from '../agent-input-detection.js';\nimport {\n getConversationById,\n getConversationByName,\n setConversationEffort,\n updateConversationDeliveryMethod,\n type LegacyConversation as Conversation,\n} from './conversations.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { loadConfigSync } from '../config-yaml.js';\nimport {\n writeConversationControlCommand,\n type ControlCommand,\n type ThinkingLevel,\n} from '../runtimes/conversation-control.js';\nimport { sendRawKeystroke, sendKeysAsync } from '../tmux.js';\nimport { deliverAgentMessage } from '../agents.js';\nimport { tmuxSessionExists } from './conversation-runtime.js';\nimport type { PendingAskUserQuestionSnapshot, PendingInputKind } from '../agent-enrichment.js';\nimport { getOverdeckHome } from '../paths.js';\nimport { BRIDGE_TOKEN_HEADER } from '../bridge-token.js';\n\nexport const CONTROL_ACK_TIMEOUT_MS = 10_000;\n\nexport interface ConversationControlAck {\n id: string\n ok: boolean\n error?: string\n}\n\ninterface PendingConversationControlAck {\n resolve: () => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\nconst pendingConversationControlAcks = new Map<string, PendingConversationControlAck>();\n\nconst PLAN_ACTION_KEYSTROKES: Record<string, string> = {\n 'approve-auto': '1',\n 'approve-manual': '2',\n 'reject-ultraplan': '3',\n};\n\ninterface CodexAppServerPendingRequest {\n id: string | number;\n method: string;\n params?: unknown;\n}\n\ninterface CodexAppServerStatus {\n pendingRequests?: CodexAppServerPendingRequest[];\n}\n\n/**\n * PAN-1520 (FR-4) — deliver a native plan-menu action to any tmux session\n * (conversation or agent). The plan menu is answered with raw keystrokes\n * (1/2/3/4), not a text message; `reject-feedback` follows the '4' keystroke\n * with the operator's feedback through the normal message pipeline.\n * Returns null on success, or an error string for an invalid action.\n */\nexport async function deliverPlanActionToSession(\n sessionName: string,\n action: string,\n feedback: string,\n deliveryMethod: 'auto' | 'channels' | 'tmux' = 'auto',\n): Promise<string | null> {\n if (action === 'reject-feedback') {\n await Effect.runPromise(sendRawKeystroke(sessionName, '4', 'plan-action-reject'));\n if (feedback) {\n await new Promise(r => setTimeout(r, 300));\n await deliverAgentMessage(sessionName, feedback, 'plan-action-feedback', deliveryMethod);\n }\n return null;\n }\n const keystroke = PLAN_ACTION_KEYSTROKES[action];\n if (!keystroke) return `Invalid action: ${action}`;\n await Effect.runPromise(sendRawKeystroke(sessionName, keystroke, `plan-action-${action}`));\n return null;\n}\n\nexport function registerConversationControlAck(\n commandId: string,\n timeoutMs: number = CONTROL_ACK_TIMEOUT_MS,\n): Promise<void> {\n const existing = pendingConversationControlAcks.get(commandId);\n if (existing) {\n clearTimeout(existing.timer);\n existing.reject(new Error(`Replaced pending conversation control ack ${commandId}`));\n pendingConversationControlAcks.delete(commandId);\n }\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingConversationControlAcks.delete(commandId);\n reject(new Error(`Timed out waiting for conversation control ack ${commandId}`));\n }, timeoutMs);\n pendingConversationControlAcks.set(commandId, {\n resolve: () => {\n clearTimeout(timer);\n pendingConversationControlAcks.delete(commandId);\n resolve();\n },\n reject: (error: Error) => {\n clearTimeout(timer);\n pendingConversationControlAcks.delete(commandId);\n reject(error);\n },\n timer,\n });\n });\n}\n\nexport function resolveConversationControlAck(ack: ConversationControlAck): 'resolved' | 'rejected' | 'unknown' {\n const pending = pendingConversationControlAcks.get(ack.id);\n if (!pending) return 'unknown';\n if (ack.ok) {\n pending.resolve();\n return 'resolved';\n }\n pending.reject(new Error(ack.error || `Conversation control command ${ack.id} failed`));\n return 'rejected';\n}\n\nexport function getPendingConversationControlAckCount(): number {\n return pendingConversationControlAcks.size;\n}\n\nexport function clearPendingConversationControlAcksForTests(): void {\n for (const pending of pendingConversationControlAcks.values()) {\n clearTimeout(pending.timer);\n }\n pendingConversationControlAcks.clear();\n}\n\nexport function handleConversationControlAck(\n body: Record<string, unknown>,\n): { status: number; body: { ok: true; outcome: 'resolved' | 'rejected' | 'unknown' } | { error: string } } {\n const id = typeof body['id'] === 'string' ? body['id'].trim() : '';\n if (!id) return { status: 400, body: { error: 'id is required' } };\n const ok = body['ok'] === true;\n const error = typeof body['error'] === 'string' ? body['error'] : undefined;\n const outcome = resolveConversationControlAck({ id, ok, ...(error !== undefined ? { error } : {}) });\n return { status: 200, body: { ok: true, outcome } };\n}\n\nexport function validateConversationControlAckOrigin(\n headers: HeaderMap,\n method = 'POST',\n): { ok: true } | { ok: false; error: string } {\n const origin = getHeaderFromMap(headers, 'origin');\n const referer = getHeaderFromMap(headers, 'referer');\n if (!origin && !referer) return { ok: true };\n return validateOriginHeaders(headers, method);\n}\n\nexport async function handleConversationCodexApproval(\n rawId: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const optionNumber = Number((body as { optionNumber?: unknown }).optionNumber);\n const requestedToolUseId = typeof body.toolUseId === 'string' ? body.toolUseId : undefined;\n if (!Number.isInteger(optionNumber) || optionNumber < 1 || optionNumber > 9) {\n return jsonResponse({ error: 'optionNumber must be an integer 1-9' }, { status: 400 });\n }\n const numericId = Number(rawId);\n const conv = !Number.isNaN(numericId) && /^\\d+$/.test(rawId)\n ? getConversationById(numericId)\n : getConversationByName(rawId);\n if (!conv) {\n return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n }\n if (getHarnessBehavior(conv.harness).transcriptKind !== 'codex-rollout-jsonl') {\n return jsonResponse({ error: 'Not a Codex conversation' }, { status: 400 });\n }\n if (!(await tmuxSessionExists(conv.tmuxSession))) {\n return jsonResponse({ error: 'Conversation session is not running' }, { status: 409 });\n }\n if (codexUsesAppServerTransport()) {\n const pending = await readFirstAppServerApproval(conv.tmuxSession);\n if (!pending) {\n return jsonResponse({ error: 'No Codex approval prompt is currently pending' }, { status: 409 });\n }\n const expectedToolUseId = `${CODEX_APPROVAL_TOOL_PREFIX}${conv.tmuxSession}:${pending.id}`;\n if (!requestedToolUseId) {\n return jsonResponse({ error: 'Codex approval request id is required' }, { status: 400 });\n }\n if (requestedToolUseId !== expectedToolUseId) {\n return jsonResponse({ error: 'Codex approval request changed; refresh pending input before responding' }, { status: 409 });\n }\n const decision = codexAppServerDecisionForOption(optionNumber);\n if (!decision) {\n return jsonResponse({ error: 'optionNumber out of range (1-2)' }, { status: 400 });\n }\n await postCodexAppServerOp(conv.tmuxSession, { op: 'approval', requestId: pending.id, decision });\n return jsonResponse({ ok: true, optionNumber, decision });\n }\n // Re-detect uncached so we only send keystrokes when the menu is still\n // up, and so we can bound optionNumber to the options actually shown.\n const detection = await Effect.runPromise(\n detectAwaitingInputForAgent(conv.tmuxSession, { isPlanning: false, cache: false }),\n );\n const parsed = detection ? parseCodexApprovalPrompt(detection.prompt) : null;\n if (!parsed) {\n return jsonResponse({ error: 'No Codex approval prompt is currently pending' }, { status: 409 });\n }\n if (optionNumber > parsed.options.length) {\n return jsonResponse({ error: `optionNumber out of range (1-${parsed.options.length})` }, { status: 400 });\n }\n await deliverCodexApprovalChoice(conv.tmuxSession, optionNumber);\n return jsonResponse({ ok: true, optionNumber });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] codex approval failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\n\nexport async function handleConversationPlanAction(\n name: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const conv = getConversationByName(name);\n if (!conv) {\n return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n }\n const action = typeof body['action'] === 'string' ? body['action'] : '';\n const feedback = typeof body['feedback'] === 'string' ? body['feedback'].trim() : '';\n const error = await deliverPlanActionToSession(\n conv.tmuxSession,\n action,\n feedback,\n resolveConversationDeliveryMethod(conv),\n );\n if (error) {\n return jsonResponse({ error }, { status: 400 });\n }\n return jsonResponse({ ok: true });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] plan action failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\n\ntype ConversationControlDeliverAs = Extract<ControlCommand['type'], 'prompt' | 'steer' | 'follow_up'>;\ntype ConversationControlCommandInput = ControlCommand extends infer C\n ? C extends { id: string }\n ? Omit<C, 'id'>\n : never\n : never;\n\nconst THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh'] as const satisfies readonly ThinkingLevel[];\nconst PI_CONVERSATION_ABORT_KEY = 'Escape';\n\nexport function isPiControlChannelHarness(harness: RuntimeName | 'pi'): boolean {\n return harness === 'ohmypi' || harness === 'pi';\n}\n\nexport function parseThinkingLevel(value: unknown): ThinkingLevel | null {\n return typeof value === 'string' && (THINKING_LEVELS as readonly string[]).includes(value)\n ? value as ThinkingLevel\n : null;\n}\n\nexport function pickDeliverAs(bodyDeliverAs: unknown): ConversationControlDeliverAs {\n if (bodyDeliverAs === 'follow_up') return 'follow_up';\n return 'steer';\n}\n\nexport function resolveConversationDeliveryMethod(conv: Pick<Conversation, 'harness' | 'deliveryMethod'>): 'auto' | 'channels' | 'tmux' {\n const harness = conv.harness ?? 'claude-code';\n if (harness === 'acp') return 'auto';\n if (isPiControlChannelHarness(harness)) return 'auto';\n if (harness === 'codex' && loadConfigSync().config.codex?.transport !== 'tui') return 'auto';\n return conv.deliveryMethod ?? (getHarnessBehavior(harness).deliveryKind === 'rpc-fifo' ? 'tmux' : 'auto');\n}\n\nexport async function sendConversationControlCommand(\n conv: Pick<Conversation, 'tmuxSession'>,\n commandInput: ConversationControlCommandInput,\n): Promise<void> {\n const id = randomUUID();\n const ackPromise = registerConversationControlAck(id);\n const command: ControlCommand = { id, ...commandInput };\n\n try {\n await writeConversationControlCommand(conv.tmuxSession, command);\n } catch (err) {\n resolveConversationControlAck({\n id,\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n await ackPromise;\n}\n\nexport async function deliverConversationViaControlChannel(\n conv: Pick<Conversation, 'tmuxSession'>,\n message: string,\n options: {\n source: 'operator' | 'orchestrator'\n deliverAs: ConversationControlDeliverAs\n },\n): Promise<void> {\n await sendConversationControlCommand(conv, {\n type: options.deliverAs,\n message,\n source: options.source,\n });\n}\n\nexport async function handleConversationThinkingLevel(\n name: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n\n const harness: RuntimeName = conv.harness ?? 'claude-code';\n if (!isPiControlChannelHarness(harness)) {\n return jsonResponse({ error: 'Thinking level control is only supported for Pi conversations' }, { status: 400 });\n }\n if (conv.status === 'ended') {\n return jsonResponse({ error: 'Session has ended — start a new run to interact' }, { status: 422 });\n }\n\n const level = parseThinkingLevel(body['level']);\n if (!level) return jsonResponse({ error: 'Invalid thinking level' }, { status: 400 });\n\n await sendConversationControlCommand(conv, { type: 'set_thinking_level', level });\n setConversationEffort(name, level);\n const updated = getConversationByName(name) ?? conv;\n return jsonResponse({ ok: true, effort: updated.effort ?? level });\n}\n\nexport async function handleConversationCompact(name: string): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n\n const harness: RuntimeName = conv.harness ?? 'claude-code';\n if (!isPiControlChannelHarness(harness)) {\n return jsonResponse({ error: 'Compact control endpoint is only supported for Pi conversations' }, { status: 400 });\n }\n if (conv.status === 'ended') {\n return jsonResponse({ error: 'Session has ended — start a new run to interact' }, { status: 422 });\n }\n\n await sendConversationControlCommand(conv, { type: 'compact' });\n return jsonResponse({ ok: true });\n}\n\nexport async function handleConversationAbort(name: string): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n\n const harness: RuntimeName = conv.harness ?? 'claude-code';\n if (!isPiControlChannelHarness(harness)) {\n return jsonResponse({ error: 'Abort control endpoint is only supported for Pi conversations' }, { status: 400 });\n }\n if (conv.status === 'ended') {\n return jsonResponse({ error: 'Session has ended — start a new run to interact' }, { status: 422 });\n }\n\n await sendKeysAsync(conv.tmuxSession, PI_CONVERSATION_ABORT_KEY, 'conversation-abort');\n return jsonResponse({ ok: true, key: PI_CONVERSATION_ABORT_KEY });\n}\n\nexport async function handleConversationDeliveryMethod(\n name: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n const deliveryMethod = body['deliveryMethod'] ?? body['method'];\n if (deliveryMethod !== 'auto' && deliveryMethod !== 'channels' && deliveryMethod !== 'tmux' && deliveryMethod !== null) {\n return jsonResponse({ error: \"deliveryMethod must be 'auto', 'channels', 'tmux', or null\" }, { status: 400 });\n }\n updateConversationDeliveryMethod(name, deliveryMethod as 'auto' | 'channels' | 'tmux' | null);\n return jsonResponse({ ok: true, deliveryMethod });\n}\n\nexport const CODEX_APPROVAL_TOOL_PREFIX = 'codex-approval:';\n\nexport async function codexConversationPendingInput(\n conv: Conversation,\n sessionAlive: boolean,\n askedAt: string,\n): Promise<{ kinds: PendingInputKind[]; approval?: PendingAskUserQuestionSnapshot }> {\n if (!sessionAlive || getHarnessBehavior(conv.harness).transcriptKind !== 'codex-rollout-jsonl') return { kinds: [] };\n try {\n if (codexUsesAppServerTransport()) {\n const pending = await readFirstAppServerApproval(conv.tmuxSession);\n if (!pending) return { kinds: [] };\n return {\n kinds: ['permissionRequest'],\n approval: {\n toolUseId: `${CODEX_APPROVAL_TOOL_PREFIX}${conv.tmuxSession}:${pending.id}`,\n askedAt,\n questions: [{\n question: formatAppServerApprovalQuestion(pending),\n header: 'Codex approval',\n multiSelect: false,\n options: [\n { label: '1. accept' },\n { label: '2. reject' },\n ],\n }],\n },\n };\n }\n const detection = await Effect.runPromise(detectAwaitingInputForAgent(conv.tmuxSession, { isPlanning: false }));\n if (!detection) return { kinds: [] };\n if (detection.reason === 'session_resume') return { kinds: ['sessionResume'] };\n\n const parsed = parseCodexApprovalPrompt(detection.prompt);\n if (parsed) {\n return {\n kinds: ['permissionRequest'],\n approval: {\n toolUseId: `${CODEX_APPROVAL_TOOL_PREFIX}${conv.tmuxSession}`,\n askedAt,\n questions: [{\n question: parsed.detail ? `${parsed.header}\\n\\n${parsed.detail}` : parsed.header,\n header: 'Codex approval',\n multiSelect: false,\n options: parsed.options.map((o) => ({ label: `${o.number}. ${o.label}` })),\n }],\n },\n };\n }\n return { kinds: ['permissionRequest'] };\n } catch {\n return { kinds: [] };\n }\n}\n\nexport async function deliverCodexApprovalChoice(tmuxSession: string, optionNumber: number): Promise<void> {\n for (let i = 1; i < optionNumber; i += 1) {\n await Effect.runPromise(sendRawKeystroke(tmuxSession, 'Down', 'codex-approval'));\n await new Promise((r) => setTimeout(r, 60));\n }\n await Effect.runPromise(sendRawKeystroke(tmuxSession, 'Enter', 'codex-approval'));\n}\n\nfunction codexUsesAppServerTransport(): boolean {\n return loadConfigSync().config.codex?.transport !== 'tui';\n}\n\nfunction codexAppServerDecisionForOption(optionNumber: number): 'accept' | 'reject' | null {\n if (optionNumber === 1) return 'accept';\n if (optionNumber === 2) return 'reject';\n return null;\n}\n\nasync function readFirstAppServerApproval(tmuxSession: string): Promise<CodexAppServerPendingRequest | null> {\n const status = await postCodexAppServerOp<CodexAppServerStatus>(tmuxSession, { op: 'status' });\n return status.pendingRequests?.find(request => /requestApproval/i.test(request.method)) ?? null;\n}\n\nfunction formatAppServerApprovalQuestion(request: CodexAppServerPendingRequest): string {\n const params = asRecord(request.params);\n const command = typeof params.command === 'string' ? params.command : undefined;\n const path = typeof params.path === 'string' ? params.path : undefined;\n if (command) return `Codex requests approval to run:\\n\\n${command}`;\n if (path) return `Codex requests approval for:\\n\\n${path}`;\n return `Codex requests approval for ${request.method}`;\n}\n\nasync function postCodexAppServerOp<T = Record<string, unknown>>(tmuxSession: string, body: Record<string, unknown>): Promise<T> {\n const socketPath = join(getOverdeckHome(), 'sockets', `appserver-${tmuxSession}.sock`);\n const tokenPath = join(getOverdeckHome(), 'agents', tmuxSession, 'appserver-token');\n if (!existsSync(socketPath)) throw new Error(`app-server socket missing for ${tmuxSession}`);\n if (!existsSync(tokenPath)) throw new Error(`app-server token missing for ${tmuxSession}`);\n const token = readFileSync(tokenPath, 'utf-8').trim();\n if (!token) throw new Error(`app-server token missing for ${tmuxSession}`);\n const payload = JSON.stringify(body);\n return new Promise<T>((resolve, reject) => {\n let settled = false;\n const timeout = setTimeout(() => {\n if (settled) return;\n settled = true;\n req.destroy(new Error(`app-server op timed out after 2000ms`));\n reject(new Error(`app-server op timed out after 2000ms`));\n }, 2_000);\n const finishErr = (error: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n reject(error);\n };\n const finishOk = (value: T) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve(value);\n };\n const req = httpRequest(\n {\n socketPath,\n path: '/',\n method: 'POST',\n agent: false,\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(payload),\n [BRIDGE_TOKEN_HEADER]: token,\n },\n },\n (res) => {\n let responseBody = '';\n res.setEncoding('utf-8');\n res.on('data', chunk => { responseBody += chunk; });\n res.on('end', () => {\n const status = res.statusCode ?? 0;\n if (status < 200 || status >= 300) {\n finishErr(new Error(`app-server op returned HTTP ${status}`));\n return;\n }\n try {\n finishOk(JSON.parse(responseBody) as T);\n } catch (error) {\n finishErr(new Error(`app-server op returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`));\n }\n });\n },\n );\n req.on('error', finishErr);\n req.write(payload);\n req.end();\n });\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};\n}\n","import { Effect } from 'effect'\n\nimport {\n buildChoiceKeystrokes,\n paneChoiceMenuSignature,\n parsePaneChoiceMenu,\n type PaneChoiceConfidence,\n} from './pane-choice-menu.js'\nimport { capturePaneText, sendRawKeystroke, sessionExists } from './tmux.js'\n\nconst PANE_CAPTURE_LINES = 90\nconst KEYSTROKE_GAP_MS = 60\nconst DELIVERY_CONFIRM_WAIT_MS = 700\n\nexport interface PendingPaneChoiceOption {\n number: number\n label: string\n recommended: boolean\n}\n\nexport interface PendingPaneChoice {\n /** Stable identity of the parsed menu — answers must match what is on screen. */\n signature: string\n title: string\n contextLines: string[]\n options: PendingPaneChoiceOption[]\n selectedIndex: number\n footerHint: string | null\n confidence: PaneChoiceConfidence\n}\n\nexport interface SessionPaneChoiceDeps {\n capture?: (sessionName: string, lines: number) => Promise<string>\n sendKey?: (sessionName: string, key: string) => Promise<void>\n sleep?: (ms: number) => Promise<void>\n sessionExists?: (sessionName: string) => Promise<boolean>\n}\n\nexport interface SessionPaneChoiceAnswer {\n selectedIndex: number\n signature: string\n}\n\nexport interface SessionPaneChoiceResult {\n body: Record<string, unknown>\n status?: number\n}\n\nexport async function captureSessionPaneChoice(\n sessionName: string,\n capture: (sessionName: string, lines: number) => Promise<string> = capturePaneText,\n): Promise<PendingPaneChoice | null> {\n let pane: string\n try {\n pane = await capture(sessionName, PANE_CAPTURE_LINES)\n } catch {\n return null\n }\n const menu = parsePaneChoiceMenu(pane)\n if (!menu) return null\n return {\n signature: paneChoiceMenuSignature(menu),\n title: menu.title,\n contextLines: menu.contextLines,\n options: menu.options.map((option) => ({\n number: option.number,\n label: option.label,\n recommended: option.recommended,\n })),\n selectedIndex: menu.selectedIndex,\n footerHint: menu.footerHint,\n confidence: menu.confidence,\n }\n}\n\nexport async function answerSessionPaneChoice(\n sessionName: string,\n body: Record<string, unknown> | SessionPaneChoiceAnswer,\n deps: SessionPaneChoiceDeps = {},\n): Promise<SessionPaneChoiceResult> {\n try {\n const selectedIndex = Number(body.selectedIndex)\n const signature = typeof body.signature === 'string' ? body.signature : ''\n if (!Number.isInteger(selectedIndex) || selectedIndex < 0 || selectedIndex > 8) {\n return { body: { error: 'selectedIndex must be an integer 0-8' }, status: 400 }\n }\n if (!signature) {\n return { body: { error: 'signature is required' }, status: 400 }\n }\n\n const isSessionRunning = deps.sessionExists\n ?? ((name: string) => Effect.runPromise(sessionExists(name)))\n if (!(await isSessionRunning(sessionName))) {\n return { body: { error: 'Conversation session is not running' }, status: 409 }\n }\n\n const capture = deps.capture ?? capturePaneText\n const sendKey = deps.sendKey\n ?? ((name: string, key: string) => Effect.runPromise(sendRawKeystroke(name, key, 'pane-choice')))\n const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)))\n\n const pane = await capture(sessionName, PANE_CAPTURE_LINES)\n const menu = parsePaneChoiceMenu(pane)\n if (!menu) {\n return { body: { error: 'The choice menu is no longer on screen', code: 'menu-gone' }, status: 409 }\n }\n if (paneChoiceMenuSignature(menu) !== signature) {\n return { body: { error: 'The choice menu changed since the card was rendered — refresh and re-answer', code: 'menu-changed' }, status: 409 }\n }\n if (selectedIndex >= menu.options.length) {\n return { body: { error: `selectedIndex out of range (0-${menu.options.length - 1})` }, status: 400 }\n }\n\n const keys = buildChoiceKeystrokes(menu, selectedIndex)\n for (const key of keys) {\n await sendKey(sessionName, key)\n await sleep(KEYSTROKE_GAP_MS)\n }\n\n await sleep(DELIVERY_CONFIRM_WAIT_MS)\n const after = parsePaneChoiceMenu(await capture(sessionName, PANE_CAPTURE_LINES))\n if (after && paneChoiceMenuSignature(after) === signature) {\n return { body: { error: 'Keystrokes were sent but the menu is still on screen — answer it from the terminal', code: 'delivery-unconfirmed' }, status: 409 }\n }\n return { body: { ok: true, answeredLabel: menu.options[selectedIndex]!.label } }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error)\n console.error('[sessions] pane-choice answer failed:', message)\n return { body: { error: 'Internal server error' }, status: 500 }\n }\n}\n","import { Data, Effect } from 'effect';\n\nexport const CHANNEL_PERMISSION_LIMITS = {\n requestIdBytes: 128,\n toolNameBytes: 128,\n descriptionBytes: 2 * 1024,\n inputPreviewBytes: 16 * 1024,\n} as const;\n\nexport interface NormalizedChannelPermissionRequestFields {\n requestId: string;\n toolName: string;\n description: string;\n inputPreview: string;\n}\n\n/** A permission request field failed validation. */\nexport class PermissionPayloadValidationError extends Data.TaggedError(\n 'PermissionPayloadValidationError',\n)<{\n readonly field: string;\n readonly message: string;\n}> {}\n\nexport function utf8ByteLength(value: string): number {\n return Buffer.byteLength(value, 'utf8');\n}\n\nexport function normalizePermissionInputPreview(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nexport function normalizeChannelPermissionRequestFields(input: {\n requestId: unknown;\n toolName: unknown;\n description: unknown;\n inputPreview?: unknown;\n}): Effect.Effect<NormalizedChannelPermissionRequestFields, PermissionPayloadValidationError> {\n return Effect.gen(function* () {\n const requestId = typeof input.requestId === 'string' ? input.requestId.trim() : '';\n if (!requestId) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({ field: 'requestId', message: 'requestId is required' }),\n );\n }\n if (utf8ByteLength(requestId) > CHANNEL_PERMISSION_LIMITS.requestIdBytes) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'requestId',\n message: `requestId exceeds ${CHANNEL_PERMISSION_LIMITS.requestIdBytes} bytes`,\n }),\n );\n }\n\n const toolName = typeof input.toolName === 'string' ? input.toolName.trim() : '';\n if (!toolName) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({ field: 'toolName', message: 'toolName is required' }),\n );\n }\n if (utf8ByteLength(toolName) > CHANNEL_PERMISSION_LIMITS.toolNameBytes) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'toolName',\n message: `toolName exceeds ${CHANNEL_PERMISSION_LIMITS.toolNameBytes} bytes`,\n }),\n );\n }\n\n const description = typeof input.description === 'string' ? input.description.trim() : '';\n if (!description) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'description',\n message: 'description is required',\n }),\n );\n }\n if (utf8ByteLength(description) > CHANNEL_PERMISSION_LIMITS.descriptionBytes) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'description',\n message: `description exceeds ${CHANNEL_PERMISSION_LIMITS.descriptionBytes} bytes`,\n }),\n );\n }\n\n const inputPreview = normalizePermissionInputPreview(input.inputPreview);\n if (utf8ByteLength(inputPreview) > CHANNEL_PERMISSION_LIMITS.inputPreviewBytes) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'inputPreview',\n message: `inputPreview exceeds ${CHANNEL_PERMISSION_LIMITS.inputPreviewBytes} bytes`,\n }),\n );\n }\n\n return { requestId, toolName, description, inputPreview };\n });\n}\n","import type { ChannelPermissionRequestSnapshot, ResolvedChannelPermissionDecision as ContractsResolvedChannelPermissionDecision } from '@overdeck/contracts';\nimport { Effect } from 'effect';\nimport * as Result from 'effect/Result';\n\nimport {\n normalizeChannelPermissionRequestFields,\n type NormalizedChannelPermissionRequestFields,\n} from '../../../lib/channels/permission-payload.js';\n\nconst MAX_ACTIVITY_PREVIEW_CHARS = 2048;\n\nexport type ResolvedChannelPermissionDecision = ContractsResolvedChannelPermissionDecision;\n\nexport function buildPermissionWaitingMessage(toolName: string, description: string): string {\n return `Waiting for permission: ${toolName} — ${description}`;\n}\n\nexport function permissionResolutionVerb(behavior: 'allow' | 'deny'): 'allowed' | 'denied' {\n return behavior === 'allow' ? 'allowed' : 'denied';\n}\n\nexport function normalizePermissionRequestBody(body: Record<string, unknown>):\n | { ok: true; value: NormalizedChannelPermissionRequestFields }\n | { ok: false; error: string } {\n const result = Effect.runSync(\n Effect.result(\n normalizeChannelPermissionRequestFields({\n requestId: body['requestId'],\n toolName: body['toolName'],\n description: body['description'],\n inputPreview: body['inputPreview'],\n }),\n ),\n );\n if (Result.isFailure(result)) {\n return { ok: false, error: result.failure.message };\n }\n return { ok: true, value: result.success };\n}\n\nexport function parsePermissionResponseBehavior(body: Record<string, unknown>):\n | { ok: true; value: 'allow' | 'deny' }\n | { ok: false; error: string } {\n const behavior = body['behavior'];\n if (behavior !== 'allow' && behavior !== 'deny') {\n return { ok: false, error: 'behavior must be allow or deny' };\n }\n return { ok: true, value: behavior };\n}\n\nexport function redactPermissionInputPreview(inputPreview: string): string {\n let redacted = inputPreview;\n redacted = redacted.replace(\n /(((?:[\"']?(?:api[_-]?key|token|secret|password|passwd)[\"']?)\\s*[:=]\\s*[\"']?))([^\\s,'\"`]+)([\"']?)/gi,\n (_match, prefix: string, _quotedPrefix: string, _value: string, suffix: string) => `${prefix}[REDACTED]${suffix}`,\n );\n redacted = redacted.replace(\n /(Bearer\\s+)([A-Za-z0-9._~+/=-]+)/g,\n (_match, prefix: string) => `${prefix}[REDACTED]`,\n );\n redacted = redacted.replace(\n /(?:ghp|gho|ghu|ghs|github_pat)_[A-Za-z0-9_]+/g,\n '[REDACTED]',\n );\n redacted = redacted.replace(\n /(?:sk|rk)-[A-Za-z0-9]+/g,\n '[REDACTED]',\n );\n if (redacted.length <= MAX_ACTIVITY_PREVIEW_CHARS) {\n return redacted;\n }\n return `${redacted.slice(0, MAX_ACTIVITY_PREVIEW_CHARS)}…[truncated]`;\n}\n\nexport function buildPermissionActivityDetails(description: string, inputPreview: string): string {\n const safePreview = redactPermissionInputPreview(inputPreview);\n return safePreview.trim().length > 0\n ? `${description}\\n\\nInput preview:\\n${safePreview}`\n : description;\n}\n\nexport interface ProcessPermissionResponseDeps {\n getPendingRequest: (requestId: string) => Promise<ChannelPermissionRequestSnapshot | null>;\n getResolvedDecision: (requestId: string) => Promise<ResolvedChannelPermissionDecision | null>;\n appendResolutionEvents: (\n request: ChannelPermissionRequestSnapshot,\n behavior: 'allow' | 'deny',\n ) => Promise<void>;\n deliverDecision: (\n agentId: string,\n requestId: string,\n behavior: 'allow' | 'deny',\n ) => Promise<void>;\n emitResolvedActivity: (request: ChannelPermissionRequestSnapshot, behavior: 'allow' | 'deny') => void;\n}\n\nexport interface ProcessPermissionResponseResult {\n status: number;\n body: { ok: boolean; error?: string; duplicate?: boolean };\n}\n\nexport async function processPermissionResponse(\n deps: ProcessPermissionResponseDeps,\n args: {\n agentId: string;\n requestId: string;\n behavior: 'allow' | 'deny';\n },\n): Promise<ProcessPermissionResponseResult> {\n const pendingRequest = await deps.getPendingRequest(args.requestId);\n if (pendingRequest) {\n if (pendingRequest.agentId !== args.agentId) {\n return {\n status: 409,\n body: {\n ok: false,\n error: `permission request ${args.requestId} belongs to ${pendingRequest.agentId}`,\n },\n };\n }\n\n try {\n await deps.appendResolutionEvents(pendingRequest, args.behavior);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n status: 500,\n body: { ok: false, error: `failed to persist permission decision: ${msg}` },\n };\n }\n\n try {\n await deps.deliverDecision(args.agentId, args.requestId, args.behavior);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n status: 502,\n body: { ok: false, error: `failed to deliver permission decision: ${msg}` },\n };\n }\n\n deps.emitResolvedActivity(pendingRequest, args.behavior);\n return { status: 200, body: { ok: true } };\n }\n\n const resolvedDecision = await deps.getResolvedDecision(args.requestId);\n if (!resolvedDecision) {\n return {\n status: 404,\n body: { ok: false, error: `permission request ${args.requestId} not found` },\n };\n }\n if (resolvedDecision.agentId !== args.agentId) {\n return {\n status: 409,\n body: {\n ok: false,\n error: `permission request ${args.requestId} belongs to ${resolvedDecision.agentId}`,\n },\n };\n }\n if (resolvedDecision.behavior !== args.behavior) {\n return {\n status: 409,\n body: {\n ok: false,\n error: `permission request ${args.requestId} was already ${resolvedDecision.behavior}`,\n },\n };\n }\n\n try {\n await deps.deliverDecision(args.agentId, args.requestId, args.behavior);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n status: 502,\n body: { ok: false, error: `failed to deliver permission decision: ${msg}` },\n };\n }\n\n return { status: 200, body: { ok: true, duplicate: true } };\n}\n","import { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport {\n deliverAgentMessage,\n deliverAgentPermissionDecision,\n getAgentRuntimeState,\n getAgentState,\n} from '../../../../lib/agents.js';\nimport { getAgentJsonlPath, scanPendingInputsPromise } from '../../../../lib/agent-enrichment.js';\nimport { deliverPlanActionToSession } from '../../../../lib/overdeck/conversation-delivery.js';\nimport {\n answerSessionPaneChoice,\n captureSessionPaneChoice,\n type PendingPaneChoice,\n type SessionPaneChoiceResult,\n} from '../../../../lib/session-pane-choice.js';\nimport { emitActivityEntrySync } from '../../../../lib/activity-logger.js';\nimport { ReadModelService } from '../../read-model.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { validateOrigin } from '../origin-validation.js';\nimport {\n buildPermissionActivityDetails,\n buildPermissionWaitingMessage,\n normalizePermissionRequestBody,\n parsePermissionResponseBehavior,\n permissionResolutionVerb,\n processPermissionResponse,\n} from '../agent-permissions.js';\nimport {\n constantTimeTokenEqual,\n getAgentPendingQuestions,\n readJsonBody,\n} from './shared.js';\n\ninterface AgentPaneChoiceDeps {\n getAgentState?: (agentId: string) => Promise<{ issueId: string } | null>;\n captureSessionPaneChoice?: (sessionName: string) => Promise<PendingPaneChoice | null>;\n answerSessionPaneChoice?: (\n sessionName: string,\n body: Record<string, unknown>,\n ) => Promise<SessionPaneChoiceResult>;\n emitActivityEntry?: typeof emitActivityEntrySync;\n}\n\nexport async function handleGetAgentPaneChoice(\n agentId: string,\n deps: AgentPaneChoiceDeps = {},\n): Promise<SessionPaneChoiceResult> {\n if (!agentId.trim()) return { body: { error: 'missing agent id' }, status: 400 };\n const capture = deps.captureSessionPaneChoice ?? captureSessionPaneChoice;\n const paneChoice = await capture(agentId);\n return paneChoice\n ? { body: { pending: true, ...paneChoice } }\n : { body: { pending: false } };\n}\n\nexport async function handlePostAgentPaneChoice(\n agentId: string,\n body: Record<string, unknown>,\n deps: AgentPaneChoiceDeps = {},\n): Promise<SessionPaneChoiceResult> {\n if (!agentId.trim()) return { body: { error: 'missing agent id' }, status: 400 };\n const readAgentState = deps.getAgentState\n ?? ((id: string) => Effect.runPromise(getAgentState(id)));\n const agentState = await readAgentState(agentId);\n if (!agentState) return { body: { error: `Agent ${agentId} not found` }, status: 404 };\n\n const answer = deps.answerSessionPaneChoice ?? answerSessionPaneChoice;\n const result = await answer(agentId, body);\n const answeredLabel = result.body['answeredLabel'];\n if (result.status === undefined && result.body['ok'] === true && typeof answeredLabel === 'string') {\n (deps.emitActivityEntry ?? emitActivityEntrySync)({\n source: 'dashboard',\n level: 'info',\n issueId: agentState.issueId,\n message: `Operator answered ${agentId}'s pane menu: ${answeredLabel}`,\n });\n }\n return result;\n}\n\nexport const getAgentPaneChoiceRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/pane-choice',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const result = yield* Effect.promise(() => handleGetAgentPaneChoice(params['id'] ?? ''));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nexport const postAgentPaneChoiceRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/pane-choice',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ error: originCheck.error }, { status: 403 });\n }\n const params = yield* HttpRouter.params;\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const result = yield* Effect.promise(() => handlePostAgentPaneChoice(params['id'] ?? '', body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/pending-questions ────────────────────────────\n\nexport const getAgentPendingQuestionsRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/pending-questions',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const questions = yield* getAgentPendingQuestions(id);\n return jsonResponse({ pending: questions.length > 0, questions });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/answer-question (PAN-1520) ──────────────────\n//\n// Operator answer for an AskUserQuestion the agent is blocked on. The Phase 1\n// hook (sync-sources/hooks/ask-user-question-hook) denies the upstream tool\n// call to prevent silent corruption (upstream returns option #1 under\n// --dangerously-skip-permissions), so by the time this endpoint is hit the\n// agent has restated the question as plain text and is waiting on a normal\n// user message. We compose that user message from the chosen option labels\n// and deliver it through the standard message pipeline.\n//\n// Body: { answers: string[] } — one chosen-option label per question.\n\nexport const postAgentAnswerQuestionRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/answer-question',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ error: 'missing agent id' }, { status: 400 });\n }\n const body = (yield* readJsonBody) as Record<string, unknown>;\n\n const answers = body['answers'];\n if (!Array.isArray(answers) || answers.length === 0) {\n return jsonResponse({ error: 'answers array required' }, { status: 400 });\n }\n if (!answers.every((a): a is string => typeof a === 'string' && a.length > 0)) {\n return jsonResponse({ error: 'every answer must be a non-empty string' }, { status: 400 });\n }\n\n const pendingQuestions = yield* getAgentPendingQuestions(id);\n if (pendingQuestions.length === 0) {\n return jsonResponse({ error: 'No pending questions found for this agent' }, { status: 404 });\n }\n\n const questionSet = pendingQuestions[0];\n const questions = questionSet.questions;\n const lines: string[] = [];\n for (let i = 0; i < answers.length && i < questions.length; i++) {\n const q = questions[i].question ?? `Question ${i + 1}`;\n lines.push(`Q: ${q}\\nA: ${answers[i]}`);\n }\n const message = `Operator answered the pending question${answers.length > 1 ? 's' : ''}:\\n\\n${lines.join('\\n\\n')}`;\n\n yield* Effect.promise(() => deliverAgentMessage(id, message, 'ask-user-question-answer'));\n return jsonResponse({ success: true, agentId: id, delivered: answers.length });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/plan-action (PAN-1520 FR-4) ─────────────────\n//\n// Agent-scoped mirror of POST /api/conversations/:name/plan-action. The native\n// ExitPlanMode approval menu is answered with raw keystrokes (1/2/3/4), not a\n// text message, so the conversation delivery door can't serve it — and the\n// existing route is conversation-name-scoped. Guarded on the agent actually\n// having a pending ExitPlanMode so a stale dashboard click can never inject\n// keystrokes into a session that has already moved on.\n//\n// Body: { action: 'approve-auto' | 'approve-manual' | 'reject-ultraplan' | 'reject-feedback', feedback?: string }\n\nexport const postAgentPlanActionRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/plan-action',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ error: 'missing agent id' }, { status: 400 });\n }\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const action = typeof body['action'] === 'string' ? body['action'] : '';\n const feedback = typeof body['feedback'] === 'string' ? body['feedback'].trim() : '';\n\n const agentState = yield* getAgentState(id).pipe(Effect.catch(() => Effect.succeed(null)));\n if (!agentState) {\n return jsonResponse({ error: 'Agent not found' }, { status: 404 });\n }\n\n const jsonlPath = yield* getAgentJsonlPath(id).pipe(Effect.catch(() => Effect.succeed(null)));\n const scan = jsonlPath\n ? yield* Effect.promise(() => scanPendingInputsPromise(jsonlPath))\n : null;\n if (!scan?.exitPlanModePending) {\n return jsonResponse({ error: 'No pending plan approval for this agent' }, { status: 409 });\n }\n\n const error = yield* Effect.promise(() => deliverPlanActionToSession(id, action, feedback));\n if (error) {\n return jsonResponse({ error }, { status: 400 });\n }\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'info',\n issueId: agentState.issueId,\n message: `Operator ${action.startsWith('approve') ? 'approved' : 'requested changes to'} ${id}'s plan from the dashboard`,\n });\n return jsonResponse({ success: true, agentId: id, action });\n })),\n);\n\nexport const postInternalAgentPermissionRequestRoute = HttpRouter.add(\n 'POST',\n '/api/internal/agents/:id/permissions/request',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ ok: false, error: 'missing agent id' }, { status: 400 });\n }\n\n const request = yield* HttpServerRequest.HttpServerRequest;\n const { INTERNAL_TOKEN_HEADER, getInternalTokenSync } = yield* Effect.promise(() =>\n import('../../../../lib/internal-token.js'),\n );\n const expected = getInternalTokenSync();\n if (!expected) {\n return jsonResponse({ ok: false, error: 'internal token not configured' }, { status: 503 });\n }\n const headers = request.headers as Record<string, string | string[] | undefined>;\n const rawHeader = headers[INTERNAL_TOKEN_HEADER];\n const provided = Array.isArray(rawHeader) ? rawHeader[0] : rawHeader;\n if (!constantTimeTokenEqual(provided, expected)) {\n return jsonResponse({ ok: false, error: 'forbidden' }, { status: 403 });\n }\n\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const normalized = normalizePermissionRequestBody(body);\n if (!normalized.ok) {\n return jsonResponse({ ok: false, error: normalized.error }, { status: 400 });\n }\n const { requestId, toolName, description, inputPreview } = normalized.value;\n\n const readModel = yield* ReadModelService;\n const existing = yield* readModel.getChannelPermissionRequest(requestId);\n if (existing) {\n if (existing.agentId !== id) {\n return jsonResponse({ ok: false, error: `request ${requestId} already belongs to ${existing.agentId}` }, { status: 409 });\n }\n return jsonResponse({ ok: true, duplicate: true });\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ ok: false, error: `agent ${id} not found` }, { status: 404 });\n }\n const runtimeState = yield* getAgentRuntimeState(id);\n const issueId = runtimeState?.currentIssue ?? agentState.issueId;\n\n const eventStore = yield* EventStoreService;\n const timestamp = new Date().toISOString();\n yield* eventStore.append({\n type: 'agent.permission_requested',\n timestamp,\n payload: {\n requestId,\n agentId: id,\n issueId,\n toolName,\n description,\n inputPreview,\n createdAt: timestamp,\n },\n } as never);\n yield* eventStore.append({\n type: 'agent.waiting_started',\n timestamp,\n payload: {\n agentId: id,\n reason: 'tool_permission',\n message: buildPermissionWaitingMessage(toolName, description),\n },\n } as never);\n\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'warn',\n message: `Permission requested for ${toolName}`,\n details: buildPermissionActivityDetails(description, inputPreview),\n issueId,\n });\n\n return jsonResponse({ ok: true });\n })),\n);\n\nexport const postAgentPermissionResponseRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/permissions/:requestId/respond',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const requestId = params['requestId'] ?? '';\n if (!id.trim() || !requestId.trim()) {\n return jsonResponse({ ok: false, error: 'missing agent id or request id' }, { status: 400 });\n }\n\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const behaviorResult = parsePermissionResponseBehavior(body);\n if (!behaviorResult.ok) {\n return jsonResponse({ ok: false, error: behaviorResult.error }, { status: 400 });\n }\n const behavior = behaviorResult.value;\n\n const readModel = yield* ReadModelService;\n const eventStore = yield* EventStoreService;\n const result = yield* Effect.promise(() => processPermissionResponse(\n {\n getPendingRequest: (permissionRequestId) =>\n Effect.runPromise(readModel.getChannelPermissionRequest(permissionRequestId)),\n getResolvedDecision: (permissionRequestId) =>\n Effect.runPromise(readModel.getResolvedChannelPermissionDecision(permissionRequestId)),\n appendResolutionEvents: async (pendingRequest, decisionBehavior) => {\n const timestamp = new Date().toISOString();\n await Effect.runPromise(eventStore.append({\n type: 'agent.permission_resolved',\n timestamp,\n payload: {\n requestId: pendingRequest.requestId,\n agentId: pendingRequest.agentId,\n issueId: pendingRequest.issueId,\n behavior: decisionBehavior,\n },\n } as never));\n await Effect.runPromise(eventStore.append({\n type: 'agent.waiting_cleared',\n timestamp,\n payload: {\n agentId: pendingRequest.agentId,\n clearedBy: 'tool_resumed',\n },\n } as never));\n },\n deliverDecision: (agentId, permissionRequestId, decisionBehavior) =>\n deliverAgentPermissionDecision(agentId, permissionRequestId, decisionBehavior),\n emitResolvedActivity: (pendingRequest, decisionBehavior) => {\n emitActivityEntrySync({\n source: 'dashboard',\n level: decisionBehavior === 'allow' ? 'success' : 'warn',\n message: `Permission ${permissionResolutionVerb(decisionBehavior)} for ${pendingRequest.toolName}`,\n details: buildPermissionActivityDetails(\n pendingRequest.description,\n pendingRequest.inputPreview,\n ),\n issueId: pendingRequest.issueId,\n });\n },\n },\n {\n agentId: id,\n requestId,\n behavior,\n },\n ));\n\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n","/**\n * Agent event utilities — shared between HTTP route handlers and background\n * pollers (PAN-1134). Extracted from routes/agents.ts so Pi event polling\n * can reuse bodyToEvent without importing a heavy route module.\n */\n\nimport { Schema } from 'effect';\nimport {\n DomainEvent,\n normalizeChannelReplyPayload,\n} from '@overdeck/contracts';\n\n/**\n * Translate a decoded body into an unsigned DomainEvent (no sequence yet).\n * Returns null if the body doesn't map to a runtime event.\n */\nexport const bodyToEvent = (\n agentId: string,\n body: Record<string, unknown>,\n timestamp: string,\n): Record<string, unknown> | null => {\n const source = body;\n if (typeof source['kind'] !== 'string') return null;\n const kind = source['kind'] as string;\n switch (kind) {\n case 'activity':\n return {\n type: 'agent.activity_changed',\n timestamp,\n payload: {\n agentId,\n activity: source['activity'],\n currentTool: source['tool'] as string | undefined,\n hookName: source['hookName'] as string | undefined,\n },\n };\n case 'hook_fired':\n return {\n type: 'agent.hook_fired',\n timestamp,\n payload: {\n agentId,\n hookName: source['hookName'],\n tool: source['tool'] as string | undefined,\n },\n };\n case 'thinking_start':\n return {\n type: 'agent.thinking_started',\n timestamp,\n payload: {\n agentId,\n lastToolAt: (source['lastToolAt'] as string) ?? timestamp,\n },\n };\n case 'thinking_stop':\n return {\n type: 'agent.thinking_stopped',\n timestamp,\n payload: {\n agentId,\n resolvedBy: source['resolvedBy'] ?? 'tool',\n },\n };\n case 'waiting_start':\n return {\n type: 'agent.waiting_started',\n timestamp,\n payload: {\n agentId,\n reason: source['reason'] ?? 'other',\n message: source['message'] as string | undefined,\n },\n };\n case 'waiting_clear':\n return {\n type: 'agent.waiting_cleared',\n timestamp,\n payload: {\n agentId,\n clearedBy: source['clearedBy'] ?? 'user_response',\n },\n };\n case 'message_received':\n return {\n type: 'agent.message_received',\n timestamp,\n payload: {\n agentId,\n direction: source['direction'] ?? 'to_agent',\n source: source['source'] ?? 'user',\n },\n };\n case 'channel_reply': {\n const reply = normalizeChannelReplyPayload(source['reply'], 'reply');\n return {\n type: 'agent.channel_reply',\n timestamp,\n payload: {\n agentId,\n reply: {\n ...reply,\n reportedAt: timestamp,\n },\n },\n };\n }\n case 'model_set':\n return {\n type: 'agent.model_set',\n timestamp,\n payload: {\n agentId,\n model: source['model'],\n claudeSessionId: source['claudeSessionId'] as string | undefined,\n sessionModel: source['sessionModel'] as string | undefined,\n sessionHarness: source['sessionHarness'] as string | undefined,\n },\n };\n case 'cost-event': {\n const usage = source['usage'] && typeof source['usage'] === 'object'\n ? source['usage'] as Record<string, unknown>\n : {};\n return {\n type: 'cost.event_recorded',\n timestamp,\n payload: {\n agentId,\n issueId: typeof source['issueId'] === 'string' && source['issueId'] ? source['issueId'] : 'UNKNOWN-0',\n cost: typeof source['costUsd'] === 'number' ? source['costUsd'] : 0,\n inputTokens: typeof usage['inputTokens'] === 'number' ? usage['inputTokens'] : 0,\n outputTokens: typeof usage['outputTokens'] === 'number' ? usage['outputTokens'] : 0,\n },\n };\n }\n case 'resolution_set':\n return {\n type: 'agent.resolution_changed',\n timestamp,\n payload: {\n agentId,\n resolution: source['resolution'],\n resolutionCount: Number(source['resolutionCount'] ?? 1),\n },\n };\n case 'current_issue_set':\n return {\n type: 'agent.current_issue_set',\n timestamp,\n payload: {\n agentId,\n currentIssue: source['currentIssue'] as string | undefined,\n },\n };\n case 'context_saturation_changed':\n return {\n type: 'agent.context_saturation_changed',\n timestamp,\n payload: {\n agentId,\n contextSaturatedAt: source['contextSaturatedAt'] as string | undefined,\n },\n };\n case 'linear_mcp_auth_required':\n return {\n type: 'linear_mcp_auth.required',\n timestamp,\n payload: {\n agentId,\n issueId: source['issueId'] ?? null,\n authUrl: source['authUrl'] ?? null,\n expiresAt: source['expiresAt'] ?? null,\n },\n };\n case 'linear_mcp_auth_healthy':\n return {\n type: 'linear_mcp_auth.healthy',\n timestamp,\n payload: {\n agentId,\n issueId: source['issueId'] ?? null,\n source: 'hook',\n },\n };\n default:\n return null;\n }\n};\n\n// Runtime-event decoder. We validate the assembled event (with a placeholder\n// sequence) against the DomainEvent union — bad payloads are rejected rather\n// than silently corrupting the AgentRuntimeSnapshot.\nexport const decodeDomainEvent = Schema.decodeUnknownResult(DomainEvent);\n","import { Effect, Option } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { bodyToEvent, decodeDomainEvent } from '../../services/agent-event-utils.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport {\n readJsonBody,\n validateAgentRuntimeEventAuth,\n} from './shared.js';\n\n// ─── Route: GET /api/agents/:id/health-history ───────────────────────────────\n\nexport const getAgentHealthHistoryRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/health-history',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n const hours = Option.isSome(urlOpt) ? (urlOpt.value.searchParams.get('hours') ?? '24') : '24';\n\n const { getHealthHistory } = yield* Effect.promise(() => import('../../../../lib/overdeck/health-events.js'));\n const endTime = new Date();\n const startTime = new Date(endTime.getTime() - parseInt(hours) * 60 * 60 * 1000);\n const events = getHealthHistory(id, startTime.toISOString(), endTime.toISOString());\n return jsonResponse({\n agentId: id,\n startTime: startTime.toISOString(),\n endTime: endTime.toISOString(),\n events,\n });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/heartbeat (PAN-800 ingestion) ──────────────\n//\n// Typed event ingestion for agent runtime state. Hooks POST a Schema-validated\n// body describing a single runtime transition; the handler translates to an\n// agent.* DomainEvent and hands it to AgentStateService.emit (which durably\n// appendAsyncs via EventStore).\n//\n// Body shape (discriminated by `kind`):\n// {kind: \"activity\", activity, tool?}\n// {kind: \"hook_fired\", hookName, tool?}\n// {kind: \"thinking_start\", lastToolAt}\n// {kind: \"thinking_stop\", resolvedBy}\n// {kind: \"waiting_start\", reason, message?}\n// {kind: \"waiting_clear\", clearedBy}\n// {kind: \"message_received\", direction, source}\n// {kind: \"model_set\", model, claudeSessionId?}\n// {kind: \"resolution_set\", resolution, resolutionCount}\n// {kind: \"current_issue_set\", currentIssue?}\n// {kind: \"context_saturation_changed\", contextSaturatedAt?}\n// {kind: \"linear_mcp_auth_required\", authUrl?, expiresAt?}\n// {kind: \"linear_mcp_auth_healthy\"}\n\nexport function emitAgentRuntimeEvent(id: string, body: Record<string, unknown>, timestamp: string) {\n return Effect.gen(function* () {\n let eventBody = body;\n if (body['kind'] === 'linear_mcp_auth_required' || body['kind'] === 'linear_mcp_auth_healthy') {\n const { AgentStateService } = yield* Effect.promise(\n () => import('../../services/agent-state-service.js'),\n );\n const agentState = yield* AgentStateService;\n const snapshot = yield* agentState.get(id);\n eventBody = { ...body, issueId: snapshot?.currentIssue ?? null };\n }\n\n let raw: Record<string, unknown> | null;\n try {\n raw = bodyToEvent(id, eventBody, timestamp);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'invalid heartbeat payload';\n return { ok: false as const, response: jsonResponse({ success: false, error: message }, { status: 400 }) };\n }\n if (!raw) {\n return { ok: true as const, emitted: false };\n }\n\n const candidate = { ...raw, sequence: 0 };\n const decoded = decodeDomainEvent(candidate);\n if (decoded._tag === 'Failure') {\n return {\n ok: false as const,\n response: jsonResponse(\n { success: false, error: 'invalid event', detail: String(decoded.failure) },\n { status: 400 },\n ),\n };\n }\n\n const { AgentStateService } = yield* Effect.promise(\n () => import('../../services/agent-state-service.js'),\n );\n const agentState = yield* AgentStateService;\n yield* agentState.emit(decoded.success as never);\n return { ok: true as const, emitted: true };\n });\n}\n\nexport const postAgentHeartbeatRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/heartbeat',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ success: false, error: 'missing agent id' }, { status: 400 });\n }\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const timestamp = (body['timestamp'] as string) ?? new Date().toISOString();\n const result = yield* emitAgentRuntimeEvent(id, body, timestamp);\n if (!result.ok) return result.response;\n return jsonResponse({ success: true, emitted: result.emitted });\n })),\n);\n\nexport const postAgentWorkCompleteRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/work-complete',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) return jsonResponse({ success: false, error: 'missing agent id' }, { status: 400 });\n const result = yield* emitAgentRuntimeEvent(id, { kind: 'resolution_set', resolution: 'done', resolutionCount: 1 }, new Date().toISOString());\n if (!result.ok) return result.response;\n return jsonResponse({ success: true, emitted: result.emitted });\n })),\n);\n\nexport const postAgentStuckRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/stuck',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) return jsonResponse({ success: false, error: 'missing agent id' }, { status: 400 });\n const result = yield* emitAgentRuntimeEvent(id, { kind: 'resolution_set', resolution: 'stuck', resolutionCount: 1 }, new Date().toISOString());\n if (!result.ok) return result.response;\n return jsonResponse({ success: true, emitted: result.emitted });\n })),\n);\n\nfunction hasNegatedCompletionOutput(output: string): boolean {\n return /\\b(not|never|no|cannot|can't|blocked|waiting|needs input|not yet|isn't|aren't)\\b.{0,48}\\b(implementation complete|all tasks closed|ready for review|work complete)\\b/i.test(output);\n}\n\nexport const postAgentClassifyCompletionRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/classify-completion',\n httpHandler(Effect.gen(function* () {\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const output = typeof body['output'] === 'string' ? body['output'] : '';\n const verdict = /blocked|needs input|waiting for|not ready for review/i.test(output) || hasNegatedCompletionOutput(output)\n ? 'STOPPED_FOR_INPUT'\n : /Implementation complete|all tasks closed|ready for review|work complete/i.test(output)\n ? 'FORGOT_COMPLETION'\n : 'UNCLEAR';\n return jsonResponse({ success: true, verdict });\n })),\n);\n\nexport const postAgentPlanChecklistRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/plan-checklist',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const { getAgentState } = yield* Effect.promise(() => import('../../../../lib/agents.js'));\n const state = yield* getAgentState(id);\n if (!state?.workspace || !state.issueId) {\n return jsonResponse({ success: false, error: 'agent has no resolvable workspace or issue id' }, { status: 422 });\n }\n\n const { checkIncompletePlanItemsPromise } = yield* Effect.promise(\n () => import('../../../../lib/work/done-preflight.js'),\n );\n const incomplete = yield* Effect.promise(\n () => checkIncompletePlanItemsPromise(state.workspace, state.issueId),\n );\n return jsonResponse({ success: true, complete: incomplete.length === 0, incomplete });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/runtime (PAN-800) ────────────────────────────\n// Exposes AgentRuntimeSnapshot to out-of-process readers (CLI, tests).\n\nexport const getAgentRuntimeRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/runtime',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ success: false, error: 'missing agent id' }, { status: 400 });\n }\n const { AgentStateService } = yield* Effect.promise(\n () => import('../../services/agent-state-service.js'),\n );\n const agentState = yield* AgentStateService;\n const snapshot = yield* agentState.get(id);\n if (!snapshot) {\n return jsonResponse({ success: false, error: 'not found' }, { status: 404 });\n }\n return jsonResponse({ success: true, snapshot });\n })),\n);\n","/**\n * Agent lifecycle projection — write-through transactional boundary (PAN-1908)\n *\n * Every agent lifecycle transition writes the rollback source (state.json)\n * and then commits the authoritative row upsert + event append inside one\n * SQLite transaction. This replaces save-then-append, which a crash could\n * leave out of sync.\n *\n * The transaction runs on the shared overdeck.db connection (PAN-1938). After\n * commit, the persisted event is emitted to the event store's subscribers so\n * the in-memory read model stays current.\n */\n\nimport { Effect } from 'effect';\nimport type { SqliteDatabase } from '../../../lib/database/driver.js';\nimport { getOverdeckDatabaseSync } from '../../../lib/overdeck/infra.js';\nimport { stateToOverdeckParamsForDb, AGENT_COLUMNS_FOR_DB } from '../../../lib/overdeck/agent-state-sync.js';\nimport { getEventStore, type EventStore, type StoredEvent } from '../event-store.js';\nimport { writeAgentStateJsonSync, type AgentState } from '../../../lib/agents.js';\nimport { WORK_LAUNCHER_GRACE_MS } from '../../../lib/cloister/agent-grace.js';\nimport { logAgentLifecycleSync } from '../../../lib/persistent-logger.js';\nimport { getWorkspaceForIssue } from '../../../lib/workspaces/resolver.js';\nimport type { DomainEvent } from '@overdeck/contracts';\n\nexport interface AgentProjectionResult {\n /** Assigned event sequence number. */\n sequence: number;\n}\n\nfunction prepareAgentStateForSave(state: AgentState): AgentState {\n if (state.status === 'running' || state.status === 'starting') {\n delete state.stoppedAt;\n } else if (state.status === 'stopped' && !state.stoppedAt) {\n state.stoppedAt = new Date().toISOString();\n }\n return state;\n}\n\nfunction buildStoredEvent(\n event: Omit<DomainEvent, 'sequence'>,\n sequence: number,\n): StoredEvent {\n const record = event as Record<string, unknown>;\n return {\n sequence,\n type: event.type,\n timestamp: (record['timestamp'] as string) ?? new Date().toISOString(),\n payload: (record['payload'] as Record<string, unknown>) ?? {},\n };\n}\n\n/**\n * Atomically persist an agent state change and its lifecycle event.\n *\n * 1. Prepares the state (stoppedAt stamping).\n * 2. Writes state.json (rollback source) outside the SQLite tx.\n * 3. Begins an overdeck.db transaction, upserts the agents row, inserts the event.\n * 4. Commits and emits the stored event to subscribers.\n *\n * @throws If the SQLite transaction is rolled back, neither the row nor the\n * event is persisted and state.json may be slightly ahead.\n */\nexport function saveAgentStateAndEmitEvent(\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): AgentProjectionResult {\n const db = getOverdeckDatabaseSync();\n const eventStore = getEventStore();\n return saveAgentStateAndEmitEventWithDeps(db, eventStore, state, event);\n}\n\n/**\n * Effect wrapper for server routes. Runs the synchronous projection under\n * Effect.sync so callers in Effect.gen can compose it without blocking.\n */\nexport function saveAgentStateAndEmitEventProgram(\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): Effect.Effect<AgentProjectionResult> {\n return Effect.sync(() => saveAgentStateAndEmitEvent(state, event));\n}\n\n/**\n * Dependency-injected variant for tests.\n */\ntype AgentProjectionEventStore = Pick<EventStore, 'emitStored'>;\n\ntype AgentProjectionRows = AgentProjectionResult & {\n stored: StoredEvent;\n};\n\nfunction writeProjectionRows(\n db: SqliteDatabase,\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): AgentProjectionRows {\n const record = event as Record<string, unknown>;\n const timestamp = (record['timestamp'] as string) ?? new Date().toISOString();\n const timestampMs = new Date(timestamp).getTime();\n // PAN-1990 D-10/WI-11: stamp workspaceId from the projected agent's issueId\n // when the caller didn't already set one — this transactional path bypasses\n // event-store.ts's append()/appendAsync() stamping, so it needs its own.\n const rawPayload = (record['payload'] ?? {}) as Record<string, unknown>;\n const payloadRecord = rawPayload.workspaceId === undefined\n ? { ...rawPayload, workspaceId: getWorkspaceForIssue(state.issueId)?.id }\n : rawPayload;\n const payload = JSON.stringify(payloadRecord);\n const updatedAt = Date.now();\n\n db.prepare(\n `INSERT OR IGNORE INTO issues (id, stage, updated_at) VALUES (?, 'working', ?)`,\n ).run(state.issueId, updatedAt);\n db.prepare(\n `INSERT OR REPLACE INTO agents (${AGENT_COLUMNS_FOR_DB.join(', ')}) VALUES (${AGENT_COLUMNS_FOR_DB.map(() => '?').join(', ')})`,\n ).run(...stateToOverdeckParamsForDb(state, updatedAt));\n db.prepare(\n `INSERT INTO events (type, timestamp, payload) VALUES (?, ?, ?)`,\n ).run(event.type, timestampMs, payload);\n\n const row = db.prepare(`SELECT last_insert_rowid() AS sequence`).get() as\n | { sequence: number }\n | undefined;\n const sequence = row?.sequence ?? 0;\n return { sequence, stored: buildStoredEvent({ ...event, payload: payloadRecord }, sequence) };\n}\n\nfunction emitCommittedProjection(\n eventStore: AgentProjectionEventStore,\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n rows: AgentProjectionRows,\n): AgentProjectionResult {\n eventStore.emitStored(rows.stored);\n logAgentLifecycleSync(\n state.id,\n `projected ${event.type} (seq=${rows.sequence}) for ${state.id}`,\n );\n return { sequence: rows.sequence };\n}\n\nfunction rollbackTransaction(db: SqliteDatabase): void {\n try {\n db.exec('ROLLBACK');\n } catch {\n // Ignore rollback failures; the connection may already be rolled back.\n }\n}\n\nexport function saveAgentStateAndEmitEventWithDeps(\n db: SqliteDatabase,\n eventStore: AgentProjectionEventStore,\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): AgentProjectionResult {\n prepareAgentStateForSave(state);\n\n // Rollback source lives on the filesystem; keep it outside the SQLite tx\n // so a tx failure does not corrupt it.\n writeAgentStateJsonSync(state);\n\n db.exec('BEGIN IMMEDIATE');\n try {\n const rows = writeProjectionRows(db, state, event);\n db.exec('COMMIT');\n return emitCommittedProjection(eventStore, state, event, rows);\n } catch (err) {\n rollbackTransaction(db);\n throw err;\n }\n}\n\nexport type AgentStartPlaceholderClaim =\n | { claimed: true }\n | { claimed: false; reason: 'live-session' | 'active-state' };\n\n/**\n * Claim the single in-flight work-spawn slot and project its starting state.\n * The SQLite write lock makes the status check and placeholder insert one\n * operation, so concurrent dashboard requests cannot both launch `pan start`.\n */\nexport function claimAgentStartPlaceholderWithDeps(\n db: SqliteDatabase,\n eventStore: AgentProjectionEventStore,\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n hasLiveTmuxSession: boolean,\n): AgentStartPlaceholderClaim {\n if (hasLiveTmuxSession) return { claimed: false, reason: 'live-session' };\n\n prepareAgentStateForSave(state);\n db.exec('BEGIN IMMEDIATE');\n try {\n const current = db.prepare(\n `SELECT status, started_at AS startedAt FROM agents WHERE id = ?`,\n ).get(state.id) as { status: string; startedAt: number | null } | undefined;\n const startingWithinGrace = current?.status === 'starting'\n && current.startedAt !== null\n && Date.now() - current.startedAt < WORK_LAUNCHER_GRACE_MS;\n if (current?.status === 'running' || startingWithinGrace) {\n rollbackTransaction(db);\n return { claimed: false, reason: 'active-state' };\n }\n\n writeAgentStateJsonSync(state);\n const rows = writeProjectionRows(db, state, event);\n db.exec('COMMIT');\n emitCommittedProjection(eventStore, state, event, rows);\n return { claimed: true };\n } catch (err) {\n rollbackTransaction(db);\n throw err;\n }\n}\n\nexport function claimAgentStartPlaceholderProgram(\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n hasLiveTmuxSession: boolean,\n): Effect.Effect<AgentStartPlaceholderClaim> {\n return Effect.sync(() => claimAgentStartPlaceholderWithDeps(\n getOverdeckDatabaseSync(),\n getEventStore(),\n state,\n event,\n hasLiveTmuxSession,\n ));\n}\n\n/**\n * Roll back only the placeholder owned by this failed spawn attempt. If the\n * agent has already advanced to a real running state, the compare fails and\n * the successful concurrent launch is left untouched.\n */\nexport function rollbackAgentStartPlaceholderWithDeps(\n db: SqliteDatabase,\n eventStore: AgentProjectionEventStore,\n placeholder: AgentState,\n fallback: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): boolean {\n prepareAgentStateForSave(fallback);\n db.exec('BEGIN IMMEDIATE');\n try {\n const current = db.prepare(\n `SELECT status, model, started_at AS startedAt FROM agents WHERE id = ?`,\n ).get(placeholder.id) as { status: string; model: string | null; startedAt: number | null } | undefined;\n const placeholderStartedAt = new Date(placeholder.startedAt).getTime();\n const ownsPlaceholder = current?.status === 'starting'\n && current.model === 'pending-work-spawn'\n && current.startedAt === placeholderStartedAt;\n if (!ownsPlaceholder) {\n rollbackTransaction(db);\n return false;\n }\n\n writeAgentStateJsonSync(fallback);\n const rows = writeProjectionRows(db, fallback, event);\n db.exec('COMMIT');\n emitCommittedProjection(eventStore, fallback, event, rows);\n return true;\n } catch (err) {\n rollbackTransaction(db);\n throw err;\n }\n}\n\nexport function rollbackAgentStartPlaceholderProgram(\n placeholder: AgentState,\n fallback: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): Effect.Effect<boolean> {\n return Effect.sync(() => rollbackAgentStartPlaceholderWithDeps(\n getOverdeckDatabaseSync(),\n getEventStore(),\n placeholder,\n fallback,\n event,\n ));\n}\n","import { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport {\n clearAgentPaused,\n clearAgentTroubled,\n getAgentState,\n getSessionId,\n markAgentStoppedState,\n saveAgentRuntimeState,\n saveSessionId,\n setAgentPaused,\n stopAgent,\n} from '../../../../lib/agents.js';\nimport { emitActivityEntrySync } from '../../../../lib/activity-logger.js';\nimport { operatorInterventionEvent } from '../../../../lib/operator-interventions.js';\nimport { stopWorkspaceDocker } from '../../../../lib/workspace-manager.js';\nimport { killSession, sessionExists } from '../../../../lib/tmux.js';\nimport { getWorkAgentLifecycleState } from '../../../../lib/work-agent-lifecycle.js';\nimport { saveAgentStateAndEmitEventProgram } from '../../services/agent-projection.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { validateOrigin } from '../origin-validation.js';\nimport {\n appendAgentLifecycleLog,\n buildAgentControlEventPayload,\n captureAgentOutputBeforeKill,\n invalidateAgentsCache,\n readJsonBody,\n toAgentStatusPayload,\n} from './shared.js';\n\nexport function createAgentStopHandler(\n lifecycleEvent: 'agent.delete_requested' | 'agent.stop_requested',\n) {\n return httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const eventStore = yield* EventStoreService;\n\n const stateBeforeStop = yield* getAgentState(id);\n yield* Effect.promise(() => appendAgentLifecycleLog(id, lifecycleEvent));\n yield* stopAgent(id, 'operator');\n\n // PAN-1316/PAN-1326: tear down the workspace Docker stack on user-initiated stop.\n // Without this, dev-server containers (Vite/Webpack) outlive their owning\n // agent and can degrade the host via inotify-fallback polling storms.\n // Internal stops (restart) take a different path and don't reach here.\n //\n // Resolve the workspace from the issue (not from the agent's own state) so\n // killing a specialist (review/test/ship) — whose state.workspace may not\n // point at the work agent's workspace — still tears down the right stack.\n // Mirrors the postMergeLifecycle pattern in merge-agent.ts.\n if (stateBeforeStop?.issueId) {\n yield* Effect.promise(async () => {\n try {\n const { resolveProjectFromIssueSync } = await import('../../../../lib/projects.js');\n const { findWorkspacePath } = await import('../../../../lib/lifecycle/archive-planning.js');\n const issueLower = stateBeforeStop.issueId!.toLowerCase();\n const project = resolveProjectFromIssueSync(stateBeforeStop.issueId!);\n const projectPath = project?.projectPath ?? process.cwd();\n const workspacePath = findWorkspacePath(projectPath, issueLower);\n if (workspacePath) {\n const dockerResult = await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));\n if (dockerResult.containersFound) {\n console.log(`[agents] ✓ Stopped Docker stack for ${id}: ${dockerResult.steps.join('; ')}`);\n }\n }\n } catch (err) {\n console.warn(`[agents] Docker teardown failed for ${id} (non-fatal): ${err instanceof Error ? err.message : String(err)}`);\n }\n });\n }\n\n // PAN-1048 review feedback 004 (C1): AgentStoppedEvent requires both\n // agentId AND issueId on the payload (packages/contracts/src/events.ts:36);\n // ws-rpc drops events that fail Schema validation, so emits without issueId\n // never reach subscribers and the dashboard misses the stop transition.\n //\n // PAN-1908: write-through projection — re-upsert the stopped row and append\n // the lifecycle event in one SQLite transaction. stopAgent already saved\n // state, but repeating the upsert here makes the event append atomic.\n const stateAfterStop = yield* getAgentState(id);\n if (stateAfterStop) {\n yield* saveAgentStateAndEmitEventProgram(stateAfterStop, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: stateAfterStop.issueId || stateBeforeStop?.issueId || '' },\n });\n }\n const issueId = stateBeforeStop?.issueId;\n // PAN-1048: derive label from role; legacy state.phase no longer exists.\n const phaseLabel = stateBeforeStop?.role === 'plan' ? 'planning' : 'work';\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'info',\n message: issueId\n ? `User stopped ${issueId} ${phaseLabel} agent`\n : `User stopped agent ${id}`,\n issueId,\n });\n invalidateAgentsCache();\n return jsonResponse({ success: true });\n }));\n}\n\nfunction agentStopRoute(\n method: 'DELETE' | 'POST',\n path: `/${string}`,\n lifecycleEvent: 'agent.delete_requested' | 'agent.stop_requested',\n) {\n return HttpRouter.add(method, path, createAgentStopHandler(lifecycleEvent));\n}\n\n// ─── Route: DELETE /api/agents/:id ───────────────────────────────────────────\n\nexport const deleteAgentRoute = agentStopRoute('DELETE', '/api/agents/:id', 'agent.delete_requested');\n\n// ─── Route: POST /api/agents/:id/stop ────────────────────────────────────────\n\nexport const postAgentStopRoute = agentStopRoute('POST', '/api/agents/:id/stop', 'agent.stop_requested');\n\n// ─── Route: POST /api/agents/:id/suspend ─────────────────────────────────────\n\nexport const postAgentSuspendRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/suspend',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n\n const { sessionId } = body as any;\n const effectiveSessionId = sessionId || getSessionId(id);\n\n if (!effectiveSessionId) {\n return jsonResponse({ error: 'Session ID required for suspend' }, { status: 400 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.suspend_requested', { sessionId: effectiveSessionId }));\n saveSessionId(id, effectiveSessionId);\n // PAN-1048 review feedback 004 (C1): resolve issueId before kill so we can\n // include it on the agent.stopped payload (the contract requires it).\n const suspendIssueId = (yield* getAgentState(id))?.issueId ?? '';\n yield* killSession(id).pipe(Effect.catch(() => Effect.void));\n saveAgentRuntimeState(id, {\n state: 'suspended',\n lastActivity: new Date().toISOString(),\n claudeSessionId: effectiveSessionId,\n });\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction. Preserve the existing agent-table status\n // (suspend does not flip it to stopped).\n const stateAfterSuspend = yield* getAgentState(id);\n if (stateAfterSuspend) {\n yield* saveAgentStateAndEmitEventProgram(stateAfterSuspend, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: stateAfterSuspend.issueId || suspendIssueId },\n });\n }\n\n invalidateAgentsCache();\n return jsonResponse({ success: true });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/pause ────────────────────────────────────────\n\nexport const postAgentPauseRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/pause',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const reason = (body as { reason?: unknown }).reason;\n\n if (reason !== undefined && typeof reason !== 'string') {\n return jsonResponse({ error: 'reason must be a string' }, { status: 400 });\n }\n\n const stateBeforePause = yield* getAgentState(id);\n if (!stateBeforePause) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n const previousStatus = toAgentStatusPayload(stateBeforePause.status);\n const hasLiveSession = yield* sessionExists(id);\n const stoppedByPause = hasLiveSession || stateBeforePause.status === 'running' || stateBeforePause.status === 'starting';\n let updatedState = yield* setAgentPaused(id, reason, stoppedByPause);\n if (!updatedState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n if (hasLiveSession) {\n yield* Effect.promise(() => captureAgentOutputBeforeKill(id));\n yield* killSession(id);\n }\n\n if (hasLiveSession || updatedState.status === 'running' || updatedState.status === 'starting') {\n const stoppedState = markAgentStoppedState(updatedState, 'operator');\n updatedState = stoppedState;\n yield* Effect.promise(() => saveAgentRuntimeState(id, {\n state: 'stopped',\n lastActivity: new Date().toISOString(),\n }));\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.pause_requested', { reason }));\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: updatedState.issueId || stateBeforePause.issueId || id.replace(/^agent-/, '').toUpperCase(),\n kind: 'pause',\n source: 'dashboard',\n }));\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n // PAN-2633: assert tmux liveness at emission time so the reducer knows whether\n // this stop-shaped transition is idle-alive (preserve pending-input payload).\n const hasLiveTmuxSession = yield* sessionExists(id);\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.status_changed',\n timestamp: new Date().toISOString(),\n payload: buildAgentControlEventPayload(updatedState, previousStatus, hasLiveTmuxSession),\n });\n\n invalidateAgentsCache();\n return jsonResponse({ success: true, agent: updatedState });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/unpause ──────────────────────────────────────\n\nexport const postAgentUnpauseRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/unpause',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const eventStore = yield* EventStoreService;\n\n const stateBeforeUnpause = yield* getAgentState(id);\n if (!stateBeforeUnpause) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n const updatedState = yield* clearAgentPaused(id);\n if (!updatedState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.unpause_requested'));\n if (stateBeforeUnpause.paused === true) {\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: updatedState.issueId || stateBeforeUnpause.issueId || id.replace(/^agent-/, '').toUpperCase(),\n kind: 'unpause',\n source: 'dashboard:agent-unpause',\n }));\n }\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n // PAN-2633: assert tmux liveness at emission time so the reducer knows whether\n // this stop-shaped transition is idle-alive (preserve pending-input payload).\n const hasLiveTmuxSession = yield* sessionExists(id);\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.status_changed',\n timestamp: new Date().toISOString(),\n payload: buildAgentControlEventPayload(updatedState, toAgentStatusPayload(stateBeforeUnpause.status), hasLiveTmuxSession),\n });\n\n invalidateAgentsCache();\n\n // Resume immediately instead of leaving the agent for the deacon's next\n // patrol — clicking unpause means \"go now\". Fire-and-forget: resume has\n // its own readiness gate; the response returns at once and the tree\n // updates via the projection as the agent comes up. Only fires when the\n // lifecycle says there is actually a session to resume — a plain stopped\n // agent with no session is left for the Start button, same as before.\n let resumeTriggered = false;\n const lifecycle = yield* getWorkAgentLifecycleState(id);\n // Troubled agents are quarantined from auto-resume (the deacon skips them\n // too) — firing resumeAgent would just hit its gate and make\n // resumeTriggered a lie. untroubled + start is the path for those.\n if (lifecycle.canResumeSession && updatedState.troubled !== true) {\n resumeTriggered = true;\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.unpause_immediate_resume_triggered'));\n void import('../../../../lib/agents/resume.js')\n .then(({ resumeAgent }) => resumeAgent(id))\n .then((result) => {\n if (!result.success) console.warn(`[agents] immediate resume after unpause failed for ${id}: ${result.error}`);\n })\n .catch((err) => console.warn(`[agents] immediate resume after unpause errored for ${id}:`, err));\n }\n\n return jsonResponse({ success: true, agent: updatedState, resumeTriggered });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/untroubled ───────────────────────────────────\n\nexport const postAgentUntroubledRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/untroubled',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const eventStore = yield* EventStoreService;\n\n const stateBeforeClear = yield* getAgentState(id);\n if (!stateBeforeClear) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n const updatedState = yield* clearAgentTroubled(id);\n if (!updatedState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.untroubled_requested'));\n if (stateBeforeClear.troubled === true || (stateBeforeClear.consecutiveFailures ?? 0) > 0) {\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: updatedState.issueId || stateBeforeClear.issueId || id.replace(/^agent-/, '').toUpperCase(),\n kind: 'untroubled',\n source: 'dashboard:agent-untroubled',\n }));\n }\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n // PAN-2633: assert tmux liveness at emission time so the reducer knows whether\n // this stop-shaped transition is idle-alive (preserve pending-input payload).\n const hasLiveTmuxSession = yield* sessionExists(id);\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.status_changed',\n timestamp: new Date().toISOString(),\n payload: buildAgentControlEventPayload(updatedState, toAgentStatusPayload(stateBeforeClear.status), hasLiveTmuxSession),\n });\n\n invalidateAgentsCache();\n return jsonResponse({ success: true, agent: updatedState });\n })),\n);\n","import { existsSync } from 'node:fs';\nimport { readFile, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join, basename } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter } from 'effect/unstable/http';\nimport type { RuntimeName } from '../../../../lib/runtimes/types.js';\nimport { resolveStaffing } from '../../../../lib/agents/staffing.js';\nimport {\n detectPendingOperatorDecision,\n type PendingOperatorDecision,\n} from '../../../../lib/agents/pending-decision-gate.js';\nimport { findPlanSync, readWorkspacePlanSync } from '../../../../lib/xbrief/io.js';\nimport { resolveTieredExecutionEnabled, resolveTieredExecutionEnabledForIssue } from '../../../../lib/agents/tier-table.js';\nimport { getDispatchableItems } from '../../../../lib/xbrief/dag.js';\nimport { loadConfigSync } from '../../../../lib/config-yaml.js';\nimport { getIssueStageSync, isTerminalIssueStage } from '../../../../lib/overdeck/agents.js';\n\nimport {\n getAgentState,\n getLatestSessionId,\n recoverAgent,\n resumeAgent,\n restartAgent,\n saveAgentStateSync,\n getAgentDir,\n getProviderAuthMode,\n listRunningAgents,\n wipeAgentStateDirs,\n} from '../../../../lib/agents.js';\nimport { canUseHarnessSync } from '../../../../lib/harness-policy.js';\nimport { normalizeModelOverrideSync, requireModelOverrideSync } from '../../../../lib/model-validation.js';\nimport { operatorInterventionEvent } from '../../../../lib/operator-interventions.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { getWorkAgentLifecycleState } from '../../../../lib/work-agent-lifecycle.js';\nimport { killSession } from '../../../../lib/tmux.js';\nimport { saveAgentStateAndEmitEventProgram } from '../../services/agent-projection.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport {\n appendAgentLifecycleLog,\n buildPanStartArgs,\n invalidateAgentsCache,\n readJsonBody,\n spawnPanCommandDetached,\n} from './shared.js';\n\nfunction pendingDecisionError(\n agentId: string,\n issueId: string,\n decision: PendingOperatorDecision,\n): string {\n const reason = decision.reason.replaceAll('_', ' ');\n return `Agent ${agentId} is waiting on an operator decision (${reason}). Answer it with 'pan answer ${issueId}' or open the Decisions panel; pass {force:true} to discard it deliberately.`;\n}\n\n// ─── Route: POST /api/agents/:id/resume ──────────────────────────────────────\n\nexport const postAgentResumeRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/resume',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { message, model, harness, compact } = body as { message?: string; model?: string; harness?: RuntimeName; compact?: boolean };\n let resumeModel: string | undefined;\n try {\n resumeModel = normalizeModelOverrideSync(model);\n } catch (err) {\n console.warn(`[agents/resume] ${id} model validation failed: ${err instanceof Error ? err.message : String(err)}`);\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n // PAN-1985 follow-up: structured log at the route entry so the operator\n // can trace every resume attempt in the server console / pty-supervisor\n // log even when the front-end's toast is missed.\n console.log(`[agents/resume] ${id} requested: model=${resumeModel ?? 'unchanged'} harness=${harness ?? 'unchanged'} hasMessage=${!!message} compact=${compact === true}`);\n const eventStore = yield* EventStoreService;\n // Snapshot lifecycle state BEFORE taking any action so callers can see the\n // temporal context (why was this resume allowed) without recomputing state.\n const lifecycleBefore = yield* getWorkAgentLifecycleState(id);\n console.log(`[agents/resume] ${id} lifecycle: canResume=${lifecycleBefore.canResumeSession} hasSavedSession=${lifecycleBefore.hasSavedSession} hasLiveTmux=${lifecycleBefore.hasLiveTmuxSession} isCrashed=${lifecycleBefore.isCrashed} isStopped=${lifecycleBefore.isStopped}`);\n // PAN-1675: a compact-resume targets a context-wedged agent that is usually\n // still 'running' (a live but stuck session), which the normal gate rejects.\n // Allow it through for compact === true — resumeAgent summarizes the wedged\n // session out-of-band, kills it, and respawns a fresh session seeded with\n // the summary (PAN-1781; its own canResume handles the running case).\n // Non-compact resumes keep the strict gate.\n if (!lifecycleBefore.canResumeSession && !lifecycleBefore.isRunningButStuck && compact !== true) {\n console.warn(`[agents/resume] ${id} rejected: ${lifecycleBefore.reason}`);\n return jsonResponse({\n error: lifecycleBefore.reason || `Cannot resume agent ${lifecycleBefore.agentId}`,\n lifecycle: lifecycleBefore,\n }, { status: 409 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.resume_requested', {\n hasMessage: !!message,\n model: resumeModel || undefined,\n harness: harness || undefined,\n lifecycle: lifecycleBefore,\n }));\n const resumeOpts = resumeModel || harness || compact === true\n ? { ...(resumeModel ? { model: resumeModel } : {}), ...(harness ? { harness } : {}), ...(compact === true ? { compact: true } : {}) }\n : undefined;\n console.log(`[agents/resume] ${id} dispatching resumeAgent() with opts=${JSON.stringify(resumeOpts)}`);\n const result = yield* Effect.promise(() => resumeAgent(id, message, resumeOpts));\n console.log(`[agents/resume] ${id} resumeAgent returned: success=${result.success} messageDelivered=${result.messageDelivered} error=${result.error ?? 'none'}`);\n if (result.success) {\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction so the read model transitions agent\n // status from 'stopped' → 'running' and the frontend updates immediately.\n const agentState = yield* getAgentState(id);\n if (agentState) {\n yield* saveAgentStateAndEmitEventProgram(agentState, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: id,\n issueId: agentState.issueId,\n agent: {\n id,\n issueId: agentState.issueId,\n workspace: agentState.workspace,\n runtime: agentState.harness ?? 'claude-code',\n model: agentState.model,\n status: 'running',\n startedAt: agentState.startedAt,\n lastActivity: new Date().toISOString(),\n role: agentState.role ?? 'work',\n },\n },\n });\n }\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.resume_succeeded', {\n hasMessage: !!message,\n messageDelivered: result.messageDelivered !== false,\n }));\n invalidateAgentsCache();\n // PAN-1985 follow-up: the messageDelivered flag distinguishes \"agent is\n // resumed and your message landed in its composer\" from \"agent is\n // resumed but your message did NOT land in its composer (PTY supervisor\n // echo-confirm timed out, harness/session.id mismatch, etc.)\". The\n // former gets a 'delivered' toast; the latter gets a clear 'queued in\n // mail' warning so the operator can intervene if needed.\n const delivered = result.messageDelivered !== false;\n console.log(`[agents/resume] ${id} returning: success=${true} delivered=${delivered}`);\n return jsonResponse({\n success: true,\n resumed: true,\n messageDelivered: delivered,\n hint: delivered\n ? 'Continue prompt delivered to the agent.'\n : 'The continue prompt was queued in the agent mail/ folder because the live delivery path did not confirm in time. The agent will read it on its next session start.',\n lifecycle: { before: lifecycleBefore, after: yield* getWorkAgentLifecycleState(id) },\n });\n } else {\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.resume_failed', {\n hasMessage: !!message,\n error: result.error,\n }));\n return jsonResponse({\n error: result.error,\n lifecycle: { before: lifecycleBefore, after: yield* getWorkAgentLifecycleState(id) },\n }, { status: 400 });\n }\n })),\n);\n\n// ─── Route: POST /api/agents/:id/recover ──────────────────────────────────────\n\nexport const postAgentRecoverRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/recover',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const { model, force = false } = body as { model?: string; force?: boolean };\n let recoveryModel: string | undefined;\n try {\n recoveryModel = normalizeModelOverrideSync(model);\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n\n const stateBeforeRecover = yield* getAgentState(id);\n if (!stateBeforeRecover) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n if (!force) {\n const pendingDecision = yield* Effect.promise(() => detectPendingOperatorDecision(id));\n if (pendingDecision) {\n return jsonResponse({\n error: pendingDecisionError(id, stateBeforeRecover.issueId, pendingDecision),\n code: 'pending-operator-decision',\n pendingDecision,\n }, { status: 409 });\n }\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.recover_requested', {\n model: recoveryModel || undefined,\n }));\n\n const result = yield* Effect.promise(() => recoverAgent(id, {\n ...(recoveryModel ? { modelOverride: recoveryModel } : {}),\n force,\n }));\n if (!result || result.action === 'already-running') {\n const error = result ? `No recovery performed: agent ${id} already has a live harness runtime` : `Could not recover agent ${id}`;\n yield* Effect.promise(() => appendAgentLifecycleLog(id, result ? 'agent.recover_skipped' : 'agent.recover_failed', { error }));\n return jsonResponse({ success: false, error }, { status: result ? 409 : 400 });\n }\n\n const updatedState = yield* getAgentState(id);\n if (updatedState) {\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: id,\n issueId: updatedState.issueId || stateBeforeRecover.issueId || id.replace('agent-', '').toUpperCase(),\n agent: {\n id,\n issueId: updatedState.issueId || stateBeforeRecover.issueId,\n workspace: updatedState.workspace,\n model: updatedState.model,\n status: 'running',\n startedAt: updatedState.startedAt,\n lastActivity: updatedState.lastActivity,\n role: updatedState.role ?? 'work',\n },\n },\n });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.recover_succeeded'));\n invalidateAgentsCache();\n return jsonResponse({ success: true, recovered: true, agent: updatedState ?? null });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/restart ──────────────────────────────────────\n//\n// Restart an agent with optional model override. Graceful mode sends a 30s\n// warning then restarts; quick mode kills and relaunches immediately.\n// Returns 202 for graceful (async work), 200 for quick.\n\nexport const postAgentRestartRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/restart',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n\n const { model, harness, graceful = true, message, force = false } = body as {\n model?: string;\n harness?: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code';\n graceful?: boolean;\n message?: string;\n force?: boolean;\n };\n let restartModel: string | undefined;\n try {\n restartModel = normalizeModelOverrideSync(model);\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n if (!force) {\n const pendingDecision = yield* Effect.promise(() => detectPendingOperatorDecision(id));\n if (pendingDecision) {\n return jsonResponse({\n error: pendingDecisionError(id, agentState.issueId, pendingDecision),\n code: 'pending-operator-decision',\n pendingDecision,\n }, { status: 409 });\n }\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.restart_requested', {\n model: restartModel || agentState.model,\n harness: harness || undefined,\n graceful,\n hasMessage: !!message,\n }));\n\n if (graceful) {\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: agentState.issueId,\n kind: 'restart',\n source: 'dashboard',\n }));\n // Kick off async restart — don't block the HTTP response for 30s\n (async () => {\n try {\n // PAN-1908: write-through projection — agents-row upsert + lifecycle\n // event append in one SQLite transaction.\n await Effect.runPromise(saveAgentStateAndEmitEventProgram(agentState, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: agentState.issueId },\n }));\n\n const result = await restartAgent(id, { model: restartModel, harness, graceful: true, message, force });\n\n if (result.success || result.code === 'pending-operator-decision') {\n const updatedState = result.success ? await Effect.runPromise(getAgentState(id)) : agentState;\n // PAN-1908: write-through projection — preserve running state when a\n // late operator decision aborts before the destructive stop boundary.\n if (updatedState) {\n await Effect.runPromise(saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: id,\n issueId: updatedState.issueId || agentState.issueId,\n agent: {\n id,\n issueId: updatedState.issueId || agentState.issueId,\n workspace: updatedState.workspace || agentState.workspace,\n // PAN-1048 review feedback 004 (C3): same as quick-restart\n // below — surface the actual harness so Pi agents do not\n // get mis-labelled as Claude Code on graceful restart.\n runtime: updatedState.harness ?? agentState.harness ?? 'claude-code',\n model: restartModel || updatedState.model || agentState.model,\n status: 'running',\n startedAt: updatedState.startedAt || agentState.startedAt,\n lastActivity: new Date().toISOString(),\n role: updatedState.role ?? agentState.role,\n },\n },\n }));\n }\n invalidateAgentsCache();\n }\n await appendAgentLifecycleLog(id, 'agent.restart_completed', {\n success: result.success,\n error: result.error,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[agents] Graceful restart failed for ${id}: ${msg}`);\n await appendAgentLifecycleLog(id, 'agent.restart_failed', { error: msg });\n }\n })();\n\n return jsonResponse({ accepted: true, graceful: true, agentId: id }, { status: 202 });\n }\n\n // Quick restart — synchronous\n const result = yield* Effect.promise(() => restartAgent(id, { model: restartModel, harness, graceful: false, message, force }));\n\n if (result.success) {\n const updatedState = yield* getAgentState(id);\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: updatedState?.issueId || agentState.issueId,\n kind: 'restart',\n source: 'dashboard',\n }));\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction. restartAgent already saved state, but\n // repeating the upsert here makes each lifecycle event atomic.\n if (updatedState) {\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: updatedState.issueId || agentState.issueId },\n });\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: id,\n issueId: updatedState.issueId || agentState.issueId,\n agent: {\n id,\n issueId: updatedState.issueId || agentState.issueId,\n workspace: updatedState.workspace || agentState.workspace,\n // PAN-1048 review feedback 004 (C3): preserve the agent's actual\n // harness instead of hard-coding 'claude'. AgentSnapshot.runtime\n // is what getHarness() reads, so a Pi agent restarted through\n // this path was being mis-labelled as Claude Code.\n runtime: updatedState.harness ?? agentState.harness ?? 'claude-code',\n model: restartModel || updatedState.model || agentState.model,\n status: 'running',\n startedAt: updatedState.startedAt || agentState.startedAt,\n lastActivity: new Date().toISOString(),\n role: updatedState.role ?? agentState.role,\n },\n },\n });\n }\n invalidateAgentsCache();\n return jsonResponse({ success: true, restarted: true, agentId: id, model: restartModel || agentState.model });\n }\n\n if (result.code === 'pending-operator-decision') {\n return jsonResponse({\n error: result.error,\n code: result.code,\n pendingDecision: result.pendingDecision,\n }, { status: 409 });\n }\n return jsonResponse({ error: result.error }, { status: 500 });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/restart-fresh ────────────────────────────────\n//\n// PAN-1985: wipe the work agent's state directory under ~/.overdeck/agents/,\n// then optionally spawn a fresh work agent with a new harness/model. This is\n// the deliberate operator override path — for harness/model switches (the\n// Claude-session JSONL can't be resumed under a different harness) and for\n// \"I want a clean work run\" recovery. The NORMAL review flow continues the\n// same session across re-dispatches (PAN-1862); this route is the escape\n// hatch that pays the re-research cost.\n//\n// Modes (driven by request body):\n// { spawn: true, model, harness } — wipe + respawn a new work agent with\n// the chosen harness/model\n// { spawn: false } — wipe only; user clicks Start afterwards\n// (this is the backend of the new\n// 'completeWorkReset' issue action)\n//\n// Refuses (409) if a live tmux session is alive — same gate as reset-session.\n// Workspace, xBRIEF, tasks, .pan/continue.json, .pan/feedback/, the branch,\n// and the commit history are all left untouched. The new agent (whether\n// auto-spawned or manually started) reads .pan/continue.json + branch state\n// to pick up where the prior run left off.\n\nexport const postAgentRestartFreshRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/restart-fresh',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { spawn: spawnFlag, model: rawModel, harness, force = false } = body as {\n spawn?: boolean;\n model?: string;\n harness?: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code';\n force?: boolean;\n };\n const wantsSpawn = spawnFlag !== false; // default to spawn when omitted (picker path)\n\n let newModel: string | undefined;\n if (wantsSpawn && rawModel) {\n try {\n newModel = requireModelOverrideSync(rawModel);\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n const issueId = agentState.issueId ?? id.replace(/^agent-/, '').toUpperCase();\n const issueStage = getIssueStageSync(issueId);\n if (wantsSpawn && isTerminalIssueStage(issueStage)) {\n return jsonResponse({\n error: `${issueId} is already ${issueStage?.replaceAll('_', ' ')}. Reopen the issue before starting fresh work.`,\n issueStage,\n }, { status: 409 });\n }\n\n // PAN-1837 review fix: validate the explicit harness/model pair BEFORE any\n // mutation. This route used to kill the session and wipe the agent's\n // state directory first, then reject an invalid {harness, model} pair\n // afterward — an invalid selection destroyed the work agent's session\n // pointers and runtime files without spawning a replacement. Resolve\n // spawnModel and check policy here, before killSession/wipeAgentStateDirs.\n const spawnModel = newModel ?? agentState.model ?? 'claude-sonnet-5';\n let effectiveHarness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null = null;\n if (wantsSpawn && harness) {\n const harnessDecision = yield* Effect.promise(async () =>\n canUseHarnessSync(harness, spawnModel, await getProviderAuthMode(spawnModel)),\n );\n if (!harnessDecision.allowed) {\n return jsonResponse({ error: harnessDecision.reason ?? `Harness \"${harness}\" is not allowed for model \"${spawnModel}\".` }, { status: 400 });\n }\n effectiveHarness = harness;\n }\n\n if (!force) {\n const pendingDecision = yield* Effect.promise(() => detectPendingOperatorDecision(id));\n if (pendingDecision) {\n return jsonResponse({\n error: pendingDecisionError(id, issueId, pendingDecision),\n code: 'pending-operator-decision',\n pendingDecision,\n }, { status: 409 });\n }\n }\n\n const lifecycle = yield* getWorkAgentLifecycleState(id);\n if (lifecycle.hasLiveTmuxSession) {\n return jsonResponse({\n error: `Agent ${id} has a live tmux session. Run 'pan kill ${id}' to stop only this work agent, then retry.`,\n lifecycle,\n }, { status: 409 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.restart_fresh_requested', {\n wantsSpawn,\n model: newModel,\n harness,\n issueId,\n }));\n\n // Kill any zombie tmux session (shouldn't happen with the gate above, but\n // belt-and-suspenders) before wiping state.\n yield* killSession(id).pipe(Effect.catch(() => Effect.void));\n\n // Wipe the work agent dir only — leave specialist dirs (review, etc.)\n // alone. The new agent will read .pan/continue.json to pick up.\n const wipeResult = yield* Effect.promise(() => wipeAgentStateDirs(issueId));\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.restart_fresh_wiped', {\n removed: wipeResult.removed,\n path: wipeResult.path,\n }));\n\n if (!wantsSpawn) {\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n spawn: false,\n agentId: id,\n issueId,\n removed: wipeResult.removed,\n hint: 'Agent dir wiped. Click Start agent to respawn with a fresh session.',\n });\n }\n\n // Auto-spawn path: dispatch to the existing /api/agents spawn flow.\n // We don't go through HTTP — we call the spawn primitives directly so\n // the caller gets a single 200 with both wipe and spawn confirmed.\n // (spawnModel/effectiveHarness were already resolved and validated above,\n // before the kill/wipe mutations.)\n const agentSessionName = `agent-${issueId.toLowerCase()}`;\n const projectPath = agentState.workspace\n ? dirname(agentState.workspace)\n : undefined;\n const projectConfig = resolveProjectFromIssueSync(issueId);\n const projectRoot = projectConfig?.projectPath ?? projectPath ?? process.cwd();\n const workspacePath = agentState.workspace ?? join(projectRoot, 'workspaces', `feature-${issueId.toLowerCase()}`);\n\n const args = buildPanStartArgs({\n issueId,\n model: spawnModel,\n harness: effectiveHarness,\n });\n\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.restart_fresh_spawn_requested', {\n args,\n model: spawnModel,\n harness: effectiveHarness,\n }));\n\n // Spawn detached `pan start` — same pattern the existing POST /api/agents\n // route uses, minus the HTTP hop. We deliberately write a placeholder\n // state.json (matching the existing spawn flow) so the dashboard\n // transitions the agent from \"stopped\" to \"starting\" within one refresh.\n saveAgentStateSync({\n id: agentSessionName,\n issueId,\n workspace: workspacePath,\n harness: effectiveHarness ?? 'claude-code',\n role: 'work',\n model: 'pending-work-spawn',\n status: 'starting',\n startedAt: new Date().toISOString(),\n });\n\n try {\n yield* Effect.promise(() => spawnPanCommandDetached({\n agentSessionName,\n issueId,\n role: 'work',\n workspacePath,\n args,\n cwd: workspacePath,\n }));\n } catch (err: any) {\n saveAgentStateSync({\n id: agentSessionName,\n issueId,\n workspace: workspacePath,\n harness: effectiveHarness ?? agentState.harness ?? 'claude-code',\n role: 'work',\n model: spawnModel,\n status: 'stopped',\n startedAt: new Date().toISOString(),\n stoppedAt: new Date().toISOString(),\n });\n const details = err?.message ?? String(err);\n return jsonResponse({\n success: false,\n error: 'The old agent state was cleared, but the fresh agent could not start. Resolve the startup blocker, then try again.',\n details,\n wiped: wipeResult.removed,\n }, { status: 500 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.restart_fresh_spawn_requested_complete', {\n model: spawnModel,\n harness: effectiveHarness,\n }));\n\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n spawn: true,\n agentId: id,\n issueId,\n removed: wipeResult.removed,\n spawnedModel: spawnModel,\n spawnedHarness: effectiveHarness,\n hint: 'Fresh work agent spawned. It will read .pan/continue.json and the branch state to continue.',\n });\n })),\n);\n\n// ─── Route: POST /api/agents/restart-all ──────────────────────────────────────\n//\n// Restart all running workspace agents using restartAgent() directly.\n// Quick mode (no graceful delay) to avoid serializing 30s waits across N agents.\n\nexport const postAgentsRestartAllRoute = HttpRouter.add(\n 'POST',\n '/api/agents/restart-all',\n Effect.gen(function* () {\n const body = yield* readJsonBody;\n const { force = false } = body as { force?: boolean };\n return yield* Effect.promise(async () => {\n try {\n const running = (await Effect.runPromise(listRunningAgents())).filter(a => a.tmuxActive);\n const results: Array<{\n id: string;\n issueId: string;\n model: string;\n status: string;\n pendingReason?: string;\n }> = [];\n\n for (const agent of running) {\n // One-shot roles (review/test convoy members) run once, write their\n // artifact, and exit WITHOUT saving a sessionId — there is nothing\n // to resume, and attempting it only ever yields \"No session ID\n // found\". Report them as skipped, not failed: the operator's\n // \"16 failed\" was 16 completed one-shots mislabeled.\n if (agent.role === 'review' || agent.role === 'test') {\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: 'skipped: one-shot role completes fresh — nothing to resume' });\n continue;\n }\n if (!agent.sessionId) {\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: 'skipped: no resumable session' });\n continue;\n }\n try {\n const result = await restartAgent(agent.id, { graceful: false, force });\n if (result.success) {\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: 'restarted' });\n } else if (result.code === 'pending-operator-decision') {\n results.push({\n id: agent.id,\n issueId: agent.issueId,\n model: agent.model,\n status: 'skipped',\n pendingReason: result.pendingDecision?.reason,\n });\n } else {\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: `failed: ${result.error}` });\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[agents] Failed to restart ${agent.id}:`, msg);\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: `failed: ${msg}` });\n }\n }\n\n const succeeded = results.filter(r => r.status === 'restarted').length;\n const skipped = results\n .filter(r => r.status === 'skipped')\n .map(r => ({ id: r.id, reason: r.pendingReason }));\n console.log(`[agents] Restarted ${succeeded}/${running.length} workspace agents`);\n return jsonResponse({ restarted: succeeded, total: running.length, skipped, results });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return jsonResponse({ error: 'Failed to restart agents: ' + msg }, { status: 500 });\n }\n });\n }),\n);\n\n// ─── Route: POST /api/agents/:id/reset-session ─────────────────────────────\n// Clears saved Claude session tracking so the next start creates a fresh session.\n// Workspace, tasks, and git state are preserved. JSONL files kept for cost history.\n\nexport const postAgentResetSessionRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/reset-session',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const eventStore = yield* EventStoreService;\n\n const lifecycle = yield* getWorkAgentLifecycleState(id);\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: `Agent ${id} not found`, lifecycle }, { status: 404 });\n }\n\n if (lifecycle.hasLiveTmuxSession) {\n return jsonResponse({ error: `Agent ${id} is running. Stop it first.`, lifecycle }, { status: 409 });\n }\n\n const previousSessionId = yield* getLatestSessionId(id);\n // Evidence must match the lifecycle assert's: hasSavedSession reads the\n // agents-table session_id column, so a --fresh wipe that cleared only the\n // state-dir files left reset-session refusing (404) while pan start still\n // refused on the DB pointer — the stranded-pointer deadlock. Accept either\n // evidence source so the route can actually do its job.\n if (!previousSessionId && !agentState.sessionId) {\n return jsonResponse({ error: `Agent ${id} has no saved session to reset`, lifecycle }, { status: 404 });\n }\n\n const agentDir = getAgentDir(id);\n\n // Clear session.id\n yield* Effect.promise(() => rm(join(agentDir, 'session.id'), { force: true })); // PAN-3357: not a dir removal\n\n // Clear sessions.json\n yield* Effect.promise(() => rm(join(agentDir, 'sessions.json'), { force: true })); // PAN-3357: not a dir removal\n\n // Clear claudeSessionId from runtime.json (preserve other fields).\n // Must read/write directly — saveAgentRuntimeState merges with existing file.\n const runtimeFile = join(agentDir, 'runtime.json');\n if (existsSync(runtimeFile)) {\n try {\n const runtimeContent = yield* Effect.promise(() => readFile(runtimeFile, 'utf-8'));\n const runtime = JSON.parse(runtimeContent);\n delete runtime.claudeSessionId;\n yield* Effect.promise(() => writeFile(runtimeFile, JSON.stringify(runtime, null, 2)));\n } catch { /* non-fatal */ }\n }\n\n yield* killSession(id).pipe(Effect.catch(() => Effect.void));\n\n // Emit event so dashboard updates. PAN-1048 review feedback 004 (C1):\n // include issueId — without it AgentStoppedEvent fails Schema validation.\n //\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction. Also clears the agents-table session_id\n // column: a --fresh wipe clears the state-dir files only, and leaving the DB\n // pointer strands the lifecycle assert (requiresSessionResetBeforeFreshStart)\n // refusing every subsequent pan start --fresh with no door that can clear it.\n const clearedState = { ...agentState, sessionId: undefined };\n yield* saveAgentStateAndEmitEventProgram(clearedState, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: agentState.issueId },\n });\n\n const priorSession = previousSessionId ?? agentState.sessionId ?? 'unknown';\n console.log(`[reset-session] Cleared session for ${id} (was: ${priorSession.slice(0, 8)}...)`);\n invalidateAgentsCache();\n return jsonResponse({ success: true, agentId: id, previousSessionId: previousSessionId ?? agentState.sessionId, lifecycle: yield* getWorkAgentLifecycleState(id) });\n })),\n);\n\n// ─── Route: POST /api/agents/restart-with-current-config ────────────────────\n// List all agents with their current vs new staffing, allowing operator to\n// selectively restart with new config\n\nexport interface RestartConfigChangeItem {\n agentId: string;\n issueId: string;\n currentModel: string;\n currentHarness: string;\n newModel: string;\n newHarness: string;\n changed: boolean;\n paused: boolean;\n troubled: boolean;\n status: string;\n}\n\nasync function buildRestartConfigChangeList(): Promise<RestartConfigChangeItem[]> {\n const agents = await Effect.runPromise(listRunningAgents());\n\n const items: RestartConfigChangeItem[] = [];\n\n for (const agent of agents) {\n if (!agent.issueId) continue;\n\n // Only include actually running agents with active tmux sessions\n if (!((agent as any).tmuxActive === true)) {\n continue;\n }\n\n // Skip paused/troubled agents (their gates stand)\n if ((agent as any).paused || (agent as any).troubled) {\n continue;\n }\n\n // Only work agents eligible for restart (skip conversations)\n if (agent.role !== 'work' && agent.role !== undefined) {\n continue;\n }\n\n // Compute new staffing from current config by reading the real xBRIEF work item\n const staffingResult = await resolveCurrentStaffing(agent.id, agent, agent.issueId);\n\n if (staffingResult.error) {\n // Skip agents where staffing cannot be resolved\n console.warn(`[restart-config] Skipping ${agent.id}: ${staffingResult.error}`);\n continue;\n }\n\n const newModel = staffingResult.newModel;\n const newHarness = staffingResult.newHarness;\n\n items.push({\n agentId: agent.id,\n issueId: agent.issueId,\n currentModel: agent.model,\n currentHarness: agent.harness ?? 'claude-code',\n newModel,\n newHarness,\n changed: agent.model !== newModel || (agent.harness ?? 'claude-code') !== newHarness,\n paused: (agent as any).paused ?? false,\n troubled: (agent as any).troubled ?? false,\n status: agent.status,\n });\n }\n\n return items;\n}\n\nexport const getAgentsRestartConfigRoute = HttpRouter.add(\n 'GET',\n '/api/agents/restart-with-current-config',\n httpHandler(Effect.gen(function* () {\n try {\n const items = yield* Effect.promise(() => buildRestartConfigChangeList());\n return jsonResponse({\n success: true,\n items,\n totalAgents: items.length,\n changedAgents: items.filter(i => i.changed).length,\n });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return jsonResponse({ error: `Failed to build restart config list: ${msg}` }, { status: 500 });\n }\n })),\n);\n\n// ─── Route: POST /api/agents/restart-with-current-config ────────────────────\n// Restart selected agents with current config\n\nasync function resolveCurrentStaffing(agentId: string, agentState: any, issueId: string): Promise<{ newModel: string; newHarness: string; error?: string }> {\n const workspacePath = agentState.workspace;\n if (!workspacePath) {\n return { newModel: agentState.model, newHarness: agentState.harness ?? 'claude-code', error: 'No workspace path available' };\n }\n\n try {\n // Read the actual xBRIEF plan\n const plan = readWorkspacePlanSync(workspacePath);\n if (!plan || !plan.plan.items) {\n return { newModel: agentState.model, newHarness: agentState.harness ?? 'claude-code', error: 'Invalid plan structure' };\n }\n\n // Find the work item using the same logic as spawn-prep:\n // For slot agents, use slotItemId; for single-work agents, use first dispatchable item\n let workItem = null;\n if (agentState.slotItemId) {\n // Slot agent: find the item by ID\n workItem = plan.plan.items.find((item: any) => item.id === agentState.slotItemId);\n if (!workItem) {\n return { newModel: agentState.model, newHarness: agentState.harness ?? 'claude-code', error: `Scheduled item ${agentState.slotItemId} not found in plan` };\n }\n } else {\n // Single-work agent: use first dispatchable item (same as spawn-prep)\n const dispatchableItems = getDispatchableItems(plan, new Set());\n workItem = dispatchableItems[0];\n if (!workItem) {\n return { newModel: agentState.model, newHarness: agentState.harness ?? 'claude-code', error: 'No dispatchable items in plan' };\n }\n }\n\n // Use the real work item with plan metadata for staffing resolution\n // This matches the logic in resolveSingleWorkTierSpawnParams\n const config = loadConfigSync().config;\n const tiered = config.tieredExecution;\n const effectiveTieredEnabled = issueId\n ? resolveTieredExecutionEnabledForIssue(tiered, issueId, plan.plan.metadata)\n : resolveTieredExecutionEnabled(tiered, plan.plan.metadata);\n\n const staffing = resolveStaffing(workItem, {\n planMetadata: plan.plan.metadata,\n spawnKey: `work:${issueId.toLowerCase()}`,\n issueId,\n config: { ...config, tieredExecution: { ...tiered, enabled: effectiveTieredEnabled } },\n });\n\n return {\n newModel: staffing.model,\n newHarness: staffing.harness,\n };\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n newModel: agentState.model,\n newHarness: agentState.harness ?? 'claude-code',\n error: `Failed to resolve staffing: ${msg}`,\n };\n }\n}\n\nexport const postAgentsRestartWithConfigRoute = HttpRouter.add(\n 'POST',\n '/api/agents/restart-with-current-config',\n httpHandler(Effect.gen(function* () {\n const body = yield* readJsonBody;\n const { agentIds, force = false } = body as { agentIds?: string[]; force?: boolean };\n\n if (!agentIds || !Array.isArray(agentIds) || agentIds.length === 0) {\n return jsonResponse({ error: 'agentIds array is required and must not be empty' }, { status: 400 });\n }\n\n // Get eligible agents from the same predicate as the GET route\n const eligibleAgents = yield* Effect.promise(() => buildRestartConfigChangeList());\n const eligibleIds = new Set(eligibleAgents.map(a => a.agentId));\n\n const results: { id: string; status: string; newModel?: string; newHarness?: string; error?: string }[] = [];\n\n for (const agentId of agentIds) {\n try {\n // Check if agent is in the eligible set\n if (!eligibleIds.has(agentId)) {\n results.push({ id: agentId, status: 'ineligible', error: 'Agent is not eligible for restart (paused, troubled, stopped, or not a work agent)' });\n continue;\n }\n\n const agentState = yield* getAgentState(agentId);\n if (!agentState) {\n results.push({ id: agentId, status: 'not_found', error: `Agent state not found` });\n continue;\n }\n\n const issueId = agentState.issueId ?? agentId.replace(/^agent-/, '').toUpperCase();\n\n // Resolve new staffing using the real work item\n const staffingResult = yield* Effect.promise(() => resolveCurrentStaffing(agentId, agentState, issueId));\n\n if (staffingResult.error) {\n results.push({\n id: agentId,\n status: 'staffing_error',\n error: staffingResult.error,\n });\n continue;\n }\n\n const newModel = staffingResult.newModel;\n const newHarness = staffingResult.newHarness;\n\n // Restart with new config\n const restartResult = yield* Effect.promise(() => restartAgent(agentId, {\n model: newModel,\n harness: newHarness as any,\n graceful: false,\n force,\n }));\n\n if (restartResult.success) {\n results.push({\n id: agentId,\n status: 'restarted',\n newModel,\n newHarness,\n });\n } else {\n results.push({\n id: agentId,\n status: restartResult.code === 'pending-operator-decision' ? 'skipped' : 'failed',\n error: restartResult.error,\n });\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n results.push({\n id: agentId,\n status: 'error',\n error: msg,\n });\n }\n }\n\n const succeeded = results.filter(r => r.status === 'restarted').length;\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n restarted: succeeded,\n total: agentIds.length,\n results,\n });\n })),\n);\n","import { existsSync } from 'node:fs';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { getAgentHealth } from '../../../../lib/cloister/health.js';\nimport { performHandoff } from '../../../../lib/cloister/handoff.js';\nimport { loadCloisterConfigSync } from '../../../../lib/cloister/config.js';\nimport { getCloisterService } from '../../../../lib/cloister/service.js';\nimport { checkAllTriggers } from '../../../../lib/cloister/triggers.js';\nimport { calculateCostSync, getPricingSync, type TokenUsage } from '../../../../lib/cost.js';\nimport { normalizeModelName } from '../../../../lib/cost-parsers/jsonl-parser.js';\nimport { requireModelOverrideSync } from '../../../../lib/model-validation.js';\nimport { encodeClaudeProjectDir } from '../../../../lib/paths.js';\nimport { getRuntimeForAgent } from '../../../../lib/runtimes/index.js';\nimport {\n getAgentState,\n setAgentDeliveryMethod,\n} from '../../../../lib/agents.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { validateOrigin } from '../origin-validation.js';\nimport { readJsonBody } from './shared.js';\n\n// ─── Route: GET /api/agents/:id/cloister-health ──────────────────────────────\n\nexport const getAgentCloisterHealthRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/cloister-health',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const service = getCloisterService();\n const health = service.getAgentHealth(id);\n if (!health) {\n return jsonResponse({ error: 'Agent not found or runtime not available' }, { status: 404 });\n }\n return jsonResponse(health);\n })),\n);\n\n// ─── Route: GET /api/agents/:id/handoff/suggestion ───────────────────────────\n\nexport const getAgentHandoffSuggestionRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/handoff/suggestion',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: 'Agent not found' }, { status: 404 });\n }\n\n const runtime = getRuntimeForAgent(id);\n if (!runtime) {\n return jsonResponse({ error: 'Runtime not found for agent' }, { status: 404 });\n }\n\n const health = getAgentHealth(id, runtime);\n const triggers = yield* checkAllTriggers(\n id,\n agentState.workspace,\n agentState.issueId,\n agentState.model,\n health,\n loadCloisterConfigSync()\n );\n\n if (triggers.length > 0) {\n const trigger = triggers[0];\n return jsonResponse({\n suggested: true,\n trigger: trigger.type,\n currentModel: agentState.model,\n suggestedModel: trigger.suggestedModel,\n reason: trigger.reason,\n });\n }\n\n return jsonResponse({\n suggested: false,\n trigger: null,\n currentModel: agentState.model,\n suggestedModel: null,\n reason: 'No handoff triggers detected',\n });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/handoff ─────────────────────────────────────\n\nexport const postAgentHandoffRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/handoff',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { toModel, reason } = body as any;\n let targetModel: string;\n try {\n targetModel = requireModelOverrideSync(toModel);\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n\n const result = yield* performHandoff(id, {\n targetModel,\n reason: reason || 'Manual handoff from dashboard',\n });\n\n if (result.success) {\n return jsonResponse({\n success: true,\n newAgentId: result.newAgentId,\n newSessionId: result.newSessionId,\n });\n } else {\n return jsonResponse({ success: false, error: result.error }, { status: 500 });\n }\n })),\n);\n\n\n// ─── Route: GET /api/agents/:id/cost ─────────────────────────────────────────\n\nexport const getAgentCostRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/cost',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: 'Agent not found' }, { status: 404 });\n }\n\n let cost = 0;\n let inputTokens = 0;\n let outputTokens = 0;\n let cacheReadTokens = 0;\n let cacheWriteTokens = 0;\n let detectedModel = agentState.model || '';\n // Claude Code repeats the same `usage` on every JSONL line of one API response\n // (text line, each tool_use line, …). Dedup on requestId/message.id so a multi-block\n // turn is counted once instead of inflating tokens/cost ~2-3×.\n const countedUsageIds = new Set<string>();\n\n const homeDir = process.env.HOME || homedir();\n const claudeProjectsDir = join(homeDir, '.claude', 'projects');\n const workspacePath = agentState.workspace;\n\n if (workspacePath) {\n const projectDirName = encodeClaudeProjectDir(workspacePath);\n const projectDir = join(claudeProjectsDir, projectDirName);\n const sessionsIndexPath = join(projectDir, 'sessions-index.json');\n\n const parseJsonlCost = async (filePath: string) => {\n const jsonlContent = await readFile(filePath, 'utf-8');\n const lines = jsonlContent.split('\\n').filter((l: string) => l.trim());\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n const usage = entry.message?.usage || entry.usage;\n const model = entry.message?.model || entry.model;\n const usageId = entry.requestId ?? entry.message?.id;\n if (usage && (usageId === undefined || !countedUsageIds.has(usageId))) {\n if (usageId !== undefined) countedUsageIds.add(usageId);\n inputTokens += usage.input_tokens || 0;\n outputTokens += usage.output_tokens || 0;\n cacheReadTokens += usage.cache_read_input_tokens || 0;\n cacheWriteTokens += usage.cache_creation_input_tokens || 0;\n }\n if (model && !detectedModel) {\n detectedModel = model;\n }\n } catch {}\n }\n };\n\n if (existsSync(sessionsIndexPath)) {\n try {\n const indexContent = JSON.parse(yield* Effect.promise(() => readFile(sessionsIndexPath, 'utf-8')));\n for (const sessionEntry of (indexContent.entries || [])) {\n if (sessionEntry?.fullPath && existsSync(sessionEntry.fullPath)) {\n yield* Effect.promise(() => parseJsonlCost(sessionEntry.fullPath));\n }\n }\n } catch {}\n }\n\n if (inputTokens === 0 && existsSync(projectDir)) {\n try {\n const files = (yield* Effect.promise(() => readdir(projectDir))).filter(f => f.endsWith('.jsonl'));\n for (const file of files) {\n yield* Effect.promise(() => parseJsonlCost(join(projectDir, file)));\n }\n } catch {}\n }\n }\n\n if (inputTokens > 0 || outputTokens > 0) {\n const modelInfo = normalizeModelName(detectedModel || 'claude-sonnet-4');\n const pricing = getPricingSync(modelInfo.provider, modelInfo.model);\n if (pricing) {\n const usage: TokenUsage = {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheWriteTokens,\n };\n cost = calculateCostSync(usage, pricing);\n }\n }\n\n return jsonResponse({\n agentId: id,\n model: detectedModel || agentState.model,\n tokens: {\n input: inputTokens,\n output: outputTokens,\n cacheRead: cacheReadTokens,\n cacheWrite: cacheWriteTokens,\n },\n cost,\n });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/delivery-method ─────────────────────────────\n// Updates the agent's delivery method (auto | channels | tmux) in state.json.\n\nexport function validateAgentDeliveryMethodOrigin(\n request: HttpServerRequest.HttpServerRequest,\n): { ok: true } | { ok: false; status: 403; body: { error: 'forbidden' } } {\n const originCheck = validateOrigin(request);\n if (originCheck.ok) return { ok: true };\n return { ok: false, status: 403, body: { error: 'forbidden' } };\n}\n\nexport const postAgentDeliveryMethodRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/delivery-method',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originDecision = validateAgentDeliveryMethodOrigin(request);\n if (!originDecision.ok) {\n return jsonResponse(originDecision.body, { status: originDecision.status });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const { deliveryMethod } = body as { deliveryMethod?: 'auto' | 'channels' | 'tmux' };\n\n if (!deliveryMethod || !['auto', 'channels', 'tmux'].includes(deliveryMethod)) {\n return jsonResponse({ error: 'deliveryMethod must be auto, channels, or tmux' }, { status: 400 });\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n yield* Effect.promise(() => setAgentDeliveryMethod(id, deliveryMethod));\n return jsonResponse({ success: true, agentId: id, deliveryMethod });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/switch-model ────────────────────────────────\n// Pipeline agent models are fixed at spawn. Changing a model tears down the\n// live session and discards context, so this route is retained only as a\n// server-side compatibility rejection for older clients.\n\nexport const postAgentSwitchModelRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/switch-model',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n return jsonResponse({\n error: `Agent ${id} model is locked once the agent is spawned`,\n }, { status: 409 });\n })),\n);\n","import type { OrderBook } from '@overdeck/contracts';\n\nimport type { OrderBookProgress } from './types.js';\n\nexport type OrderEligibilityConditionKey =\n | 'book-membership'\n | 'pickup-posture'\n | 'lane-slot'\n | 'prerequisites'\n | 'prd-reverified';\n\nexport interface OrderEligibilityCondition {\n key: OrderEligibilityConditionKey;\n met: boolean;\n detail: string;\n}\n\nexport interface OrderDispatchEligibility {\n eligible: boolean;\n overrideUsed: boolean;\n code?: 'off-book' | 'book-draining' | 'lane-b-busy' | 'lane-a-full' | 'prerequisite-unmet' | 'reverify-required';\n message?: string;\n conditions: OrderEligibilityCondition[];\n}\n\nexport interface EvaluateOrderDispatchInput {\n book: OrderBook;\n progress: OrderBookProgress;\n issueId: string;\n inFlightIssues: ReadonlySet<string>;\n prerequisiteTerminal?: ReadonlyMap<string, boolean>;\n offBook?: boolean;\n}\n\nexport function evaluateOrderDispatchEligibility(input: EvaluateOrderDispatchInput): OrderDispatchEligibility {\n const issueId = input.issueId.toUpperCase();\n const item = input.book.items.find((candidate) => candidate.issue.toUpperCase() === issueId);\n if (!item) {\n const overrideUsed = input.offBook === true;\n return {\n eligible: overrideUsed,\n overrideUsed,\n ...(overrideUsed ? {} : {\n code: 'off-book' as const,\n message: `Issue ${issueId} is not in active order book ${input.book.id}; work-agent dispatch is blocked. Retry with --off-book only for an intentional exception.`,\n }),\n conditions: [{\n key: 'book-membership',\n met: overrideUsed,\n detail: overrideUsed\n ? `Issue ${issueId} is outside the book and the operator supplied --off-book.`\n : `Issue ${issueId} is not a member of ${input.book.id}.`,\n }],\n };\n }\n\n const terminalByIssue = new Map(input.progress.items.map((progress) => [progress.issue.toUpperCase(), progress.terminal]));\n for (const [issue, terminal] of input.prerequisiteTerminal ?? []) {\n if (!terminalByIssue.has(issue.toUpperCase())) terminalByIssue.set(issue.toUpperCase(), terminal);\n }\n const unmetPrereq = item.prereqs.find((prereq) => terminalByIssue.get(prereq.toUpperCase()) !== true);\n const prereqsMet = unmetPrereq === undefined;\n const otherInFlight = input.book.items.filter((candidate) =>\n candidate.issue.toUpperCase() !== issueId\n && input.inFlightIssues.has(candidate.issue.toUpperCase()),\n );\n const laneBBlocker = item.lane === 'B'\n ? otherInFlight.find((candidate) => candidate.lane === 'B')\n : undefined;\n const laneAInFlight = otherInFlight.filter((candidate) => candidate.lane === 'A').length;\n const laneSlotFree = item.lane === 'B'\n ? laneBBlocker === undefined\n : laneAInFlight < input.book.settings.laneAConcurrency;\n\n const postureOpen = input.book.settings.posture === 'open';\n const prdReverified = !item.reVerify;\n const conditions: OrderEligibilityCondition[] = [\n { key: 'book-membership', met: true, detail: `${issueId} is a member of ${input.book.id}.` },\n {\n key: 'pickup-posture',\n met: postureOpen,\n detail: postureOpen ? 'Order-book pickup posture is OPEN.' : 'Order-book pickup posture is DRAIN.',\n },\n {\n key: 'lane-slot',\n met: laneSlotFree,\n detail: item.lane === 'B'\n ? (laneBBlocker ? `Lane B is occupied by ${laneBBlocker.issue}.` : 'Lane B has no issue in flight.')\n : `${laneAInFlight}/${input.book.settings.laneAConcurrency} Lane A slots are in flight.`,\n },\n {\n key: 'prerequisites',\n met: prereqsMet,\n detail: unmetPrereq ? `Prerequisite ${unmetPrereq.toUpperCase()} is not terminal.` : 'Every prerequisite is landed or parked.',\n },\n {\n key: 'prd-reverified',\n met: prdReverified,\n detail: prdReverified ? 'No PRD re-verification is pending.' : `${issueId} requires PRD re-verification before dispatch.`,\n },\n ];\n\n if (!postureOpen) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'book-draining',\n message: `Issue ${issueId} cannot start because order book ${input.book.id} is in DRAIN posture; no new book items may dispatch.`,\n conditions,\n };\n }\n if (unmetPrereq) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'prerequisite-unmet',\n message: `Issue ${issueId} cannot start because prerequisite ${unmetPrereq.toUpperCase()} has not landed or been parked.`,\n conditions,\n };\n }\n if (!prdReverified) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'reverify-required',\n message: `Issue ${issueId} cannot start until its PRD has been re-verified against current main.`,\n conditions,\n };\n }\n if (laneBBlocker) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'lane-b-busy',\n message: `Issue ${issueId} cannot start in Lane B because ${laneBBlocker.issue} is still in flight; Lane B allows one active issue at a time.`,\n conditions,\n };\n }\n if (item.lane === 'A' && !laneSlotFree) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'lane-a-full',\n message: `Issue ${issueId} cannot start in Lane A because ${laneAInFlight} issues are already in flight and laneAConcurrency is ${input.book.settings.laneAConcurrency}.`,\n conditions,\n };\n }\n return { eligible: true, overrideUsed: false, conditions };\n}\n","import { appendFile, mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport {\n getFlywheelRunDir,\n readFlywheelLaunchMetadata,\n resolveLiveFlywheelRunId,\n} from '../../dashboard/server/services/flywheel-run-state.js';\nimport { getReviewStatusSync } from '../review-status.js';\nimport { findProjectByPathSync } from '../projects.js';\nimport { resolveStateReadHomeSync } from '../state-read-home.js';\nimport { getWorkAgentLifecycleStateSync } from '../work-agent-lifecycle.js';\nimport { evaluateOrderDispatchEligibility, type OrderDispatchEligibility } from './eligibility.js';\nimport { computeBookProgress, getBook, liveOrderIssueLookup } from './resolver.js';\nimport type { OrderIssueLookup } from './types.js';\n\ninterface OrdersLaunchMetadata {\n orders?: { bookId?: string };\n}\n\nexport interface ActiveOrderDispatchCheck {\n ordersBound: boolean;\n runId?: string;\n bookId?: string;\n decision: OrderDispatchEligibility;\n}\n\nexport interface ActiveOrderDispatchDeps {\n resolveRunId?: typeof resolveLiveFlywheelRunId;\n readLaunch?: typeof readFlywheelLaunchMetadata;\n stateRoot?: (projectRoot: string) => string;\n getOrderBook?: typeof getBook;\n computeProgress?: typeof computeBookProgress;\n issueLookup?: OrderIssueLookup;\n reservedIssues?: ReadonlySet<string>;\n inFlightIssues?: (bookId: string, issueIds: readonly string[]) => ReadonlySet<string>;\n}\n\nfunction stateRootFor(projectRoot: string): string {\n const project = findProjectByPathSync(projectRoot);\n if (!project) throw new Error(`No configured project contains ${projectRoot}`);\n return resolveStateReadHomeSync(project).root;\n}\n\nfunction defaultInFlightIssues(_bookId: string, issueIds: readonly string[]): ReadonlySet<string> {\n const inFlight = new Set<string>();\n for (const issueId of issueIds) {\n const lifecycle = getWorkAgentLifecycleStateSync(issueId);\n const review = getReviewStatusSync(issueId);\n if (lifecycle.isRunning || (review !== null && review.reviewStatus !== 'pending')) {\n inFlight.add(issueId.toUpperCase());\n }\n }\n return inFlight;\n}\n\nconst booklessDecision: OrderDispatchEligibility = {\n eligible: true,\n overrideUsed: false,\n conditions: [],\n};\n\nexport async function checkActiveOrderDispatch(\n projectRoot: string,\n issueId: string,\n options: { offBook?: boolean } = {},\n deps: ActiveOrderDispatchDeps = {},\n): Promise<ActiveOrderDispatchCheck> {\n const runId = await (deps.resolveRunId ?? resolveLiveFlywheelRunId)();\n if (!runId) return { ordersBound: false, decision: booklessDecision };\n const launch = await (deps.readLaunch ?? readFlywheelLaunchMetadata)(runId) as\n | (Awaited<ReturnType<typeof readFlywheelLaunchMetadata>> & OrdersLaunchMetadata)\n | null;\n const bookId = launch?.orders?.bookId;\n if (!bookId) return { ordersBound: false, runId, decision: booklessDecision };\n\n const stateRoot = (deps.stateRoot ?? stateRootFor)(launch?.workspace ?? projectRoot);\n const book = (deps.getOrderBook ?? getBook)(stateRoot, bookId);\n if (!book) throw new Error(`Active Flywheel run ${runId} references missing order book ${bookId}`);\n const progress = (deps.computeProgress ?? computeBookProgress)(book);\n const prerequisiteIds = [...new Set(book.items.flatMap((item) => item.prereqs.map((prereq) => prereq.toUpperCase())))];\n const prerequisiteState = (deps.issueLookup ?? liveOrderIssueLookup)(prerequisiteIds);\n const prerequisiteTerminal = new Map(prerequisiteIds.map((prereq) => {\n const state = prerequisiteState.get(prereq);\n return [prereq, state ? !state.open || state.parked : false] as const;\n }));\n const inFlightIssues = new Set((deps.inFlightIssues ?? defaultInFlightIssues)(\n book.id,\n book.items.filter((item) => !progress.items.find((entry) => entry.issue === item.issue)?.terminal)\n .map((item) => item.issue),\n ));\n for (const reserved of deps.reservedIssues ?? []) inFlightIssues.add(reserved.toUpperCase());\n return {\n ordersBound: true,\n runId,\n bookId,\n decision: evaluateOrderDispatchEligibility({\n book,\n progress,\n issueId,\n inFlightIssues,\n prerequisiteTerminal,\n offBook: options.offBook,\n }),\n };\n}\n\nexport async function enforceActiveOrderDispatch(\n projectRoot: string,\n issueId: string,\n options: { offBook?: boolean; recordOverride?: boolean } = {},\n deps: ActiveOrderDispatchDeps = {},\n): Promise<ActiveOrderDispatchCheck> {\n const result = await checkActiveOrderDispatch(projectRoot, issueId, options, deps);\n if (!result.decision.eligible) {\n throw new Error(result.decision.message ?? `Order-book dispatch blocked for ${issueId}`);\n }\n if (options.recordOverride && result.decision.overrideUsed && result.runId && result.bookId) {\n await recordOffBookOverride(result.runId, result.bookId, issueId);\n }\n return result;\n}\n\nexport async function recordOffBookOverride(\n runId: string,\n bookId: string,\n issueId: string,\n actor = process.env['OVERDECK_AGENT_ID'] ?? 'operator',\n): Promise<string> {\n const runDir = getFlywheelRunDir(runId);\n const path = join(runDir, 'orders-overrides.jsonl');\n await mkdir(runDir, { recursive: true });\n await appendFile(path, `${JSON.stringify({\n timestamp: new Date().toISOString(),\n runId,\n bookId,\n issueId: issueId.toUpperCase(),\n actor,\n override: 'off-book',\n })}\\n`, 'utf8');\n return path;\n}\n","import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { getFlywheelRunDir } from '../../dashboard/server/services/flywheel-run-state.js';\nimport {\n checkActiveOrderDispatch,\n recordOffBookOverride,\n type ActiveOrderDispatchCheck,\n type ActiveOrderDispatchDeps,\n} from './dispatch-gate.js';\n\ninterface StoredOrderDispatchReservation {\n id: string;\n bookId: string;\n issueId: string;\n reservedAt: string;\n}\n\nexport class OrderDispatchReservationError extends Error {\n constructor(readonly check: ActiveOrderDispatchCheck) {\n super(check.decision.message ?? 'Order-book dispatch is blocked.');\n this.name = 'OrderDispatchReservationError';\n }\n}\n\nexport interface OrderDispatchReservation {\n check: ActiveOrderDispatchCheck;\n release: () => Promise<void>;\n}\n\nexport interface OrderDispatchReservationDeps extends ActiveOrderDispatchDeps {\n reservationRoot?: (runId: string) => string;\n now?: () => Date;\n staleMs?: number;\n recordOverride?: typeof recordOffBookOverride;\n}\n\nconst DEFAULT_STALE_MS = 15 * 60 * 1000;\nconst processLocks = new Map<string, Promise<void>>();\nlet reservationSequence = 0;\n\nasync function withProcessLock<T>(key: string, action: () => Promise<T>): Promise<T> {\n const previous = processLocks.get(key) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => { release = resolve; });\n const tail = previous.then(() => current);\n processLocks.set(key, tail);\n await previous;\n try {\n return await action();\n } finally {\n release();\n if (processLocks.get(key) === tail) processLocks.delete(key);\n }\n}\n\nfunction paths(root: string) {\n return {\n state: join(root, 'orders-dispatch-reservations.json'),\n temp: join(root, `orders-dispatch-reservations.${process.pid}.tmp`),\n lock: join(root, 'orders-dispatch-reservations.lock'),\n };\n}\n\nasync function acquireFileLock(path: string, staleMs: number, now: Date): Promise<void> {\n try {\n await mkdir(path);\n return;\n } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n if (code !== 'EEXIST') throw error;\n }\n\n const age = await stat(path).then((value) => now.getTime() - value.mtimeMs).catch(() => 0);\n if (age <= staleMs) {\n throw new Error('Another order-book dispatch admission is in progress; retry after it finishes.');\n }\n await rm(path, { recursive: true, force: true });\n await mkdir(path);\n}\n\nasync function readReservations(path: string): Promise<StoredOrderDispatchReservation[]> {\n try {\n const parsed = JSON.parse(await readFile(path, 'utf8')) as unknown;\n return Array.isArray(parsed) ? parsed as StoredOrderDispatchReservation[] : [];\n } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n if (code === 'ENOENT') return [];\n throw error;\n }\n}\n\nasync function writeReservations(path: string, tempPath: string, reservations: readonly StoredOrderDispatchReservation[]): Promise<void> {\n await writeFile(tempPath, `${JSON.stringify(reservations, null, 2)}\\n`, { encoding: 'utf8', mode: 0o600 });\n await rename(tempPath, path);\n}\n\nfunction freshReservations(\n reservations: readonly StoredOrderDispatchReservation[],\n now: Date,\n staleMs: number,\n): StoredOrderDispatchReservation[] {\n return reservations.filter((reservation) => {\n const reservedAt = Date.parse(reservation.reservedAt);\n return Number.isFinite(reservedAt) && now.getTime() - reservedAt <= staleMs;\n });\n}\n\nexport async function reserveActiveOrderDispatch(\n projectRoot: string,\n issueId: string,\n options: { offBook?: boolean; recordOverride?: boolean } = {},\n deps: OrderDispatchReservationDeps = {},\n): Promise<OrderDispatchReservation> {\n const initial = await checkActiveOrderDispatch(projectRoot, issueId, options, deps);\n if (!initial.ordersBound || !initial.runId || !initial.bookId || !initial.decision.eligible) {\n return { check: initial, release: async () => {} };\n }\n\n const root = (deps.reservationRoot ?? getFlywheelRunDir)(initial.runId);\n await mkdir(root, { recursive: true });\n const reservationPaths = paths(root);\n const staleMs = deps.staleMs ?? DEFAULT_STALE_MS;\n const now = (deps.now ?? (() => new Date()))();\n const reservationId = `${process.pid}-${now.getTime()}-${reservationSequence++}`;\n let check = initial;\n\n await withProcessLock(reservationPaths.lock, async () => {\n await acquireFileLock(reservationPaths.lock, staleMs, now);\n try {\n const current = freshReservations(await readReservations(reservationPaths.state), now, staleMs);\n check = await checkActiveOrderDispatch(projectRoot, issueId, options, {\n ...deps,\n reservedIssues: new Set(current\n .filter((reservation) => reservation.bookId === initial.bookId)\n .map((reservation) => reservation.issueId)),\n });\n if (!check.decision.eligible) return;\n current.push({\n id: reservationId,\n bookId: initial.bookId!,\n issueId: issueId.toUpperCase(),\n reservedAt: now.toISOString(),\n });\n await writeReservations(reservationPaths.state, reservationPaths.temp, current);\n } finally {\n await rm(reservationPaths.lock, { recursive: true, force: true });\n }\n });\n\n if (!check.decision.eligible) return { check, release: async () => {} };\n if (options.recordOverride && check.decision.overrideUsed) {\n await (deps.recordOverride ?? recordOffBookOverride)(initial.runId, initial.bookId, issueId);\n }\n\n let released = false;\n return {\n check,\n release: async () => {\n if (released) return;\n released = true;\n const releasedAt = (deps.now ?? (() => new Date()))();\n await withProcessLock(reservationPaths.lock, async () => {\n await acquireFileLock(reservationPaths.lock, staleMs, releasedAt);\n try {\n const current = freshReservations(await readReservations(reservationPaths.state), releasedAt, staleMs)\n .filter((reservation) => reservation.id !== reservationId);\n await writeReservations(reservationPaths.state, reservationPaths.temp, current);\n } finally {\n await rm(reservationPaths.lock, { recursive: true, force: true });\n }\n });\n },\n };\n}\n\nexport async function withActiveOrderDispatchReservation<T>(\n projectRoot: string,\n issueId: string,\n options: { offBook?: boolean; recordOverride?: boolean },\n action: () => Promise<T>,\n deps: OrderDispatchReservationDeps = {},\n): Promise<{ check: ActiveOrderDispatchCheck; result?: T }> {\n const reservation = await reserveActiveOrderDispatch(projectRoot, issueId, options, deps);\n if (!reservation.check.decision.eligible) return { check: reservation.check };\n try {\n return { check: reservation.check, result: await action() };\n } finally {\n await reservation.release();\n }\n}\n","import { randomUUID } from 'crypto';\nimport { Effect } from 'effect';\n\nimport type { FsError } from '../errors.js';\nimport { writeSpecForIssue } from '../pan-dir/specs.js';\nimport { generateXBriefFilename } from './lifecycle.js';\nimport type { XBriefDocument, XBriefSubItem } from './types.js';\n\nexport interface AutoSynthesizeIssueInput {\n issueId: string;\n title: string;\n body?: string | null;\n url?: string | null;\n}\n\nexport interface AutoSynthesizeResult {\n document: XBriefDocument;\n workspaceSpecPath: string;\n projectSpecPath: string;\n canonicalFilename: string;\n}\n\nfunction cleanMarkdownLine(line: string): string {\n return line\n .replace(/^[-*+]\\s+\\[[ xX]\\]\\s+/, '')\n .replace(/^[-*+]\\s+/, '')\n .replace(/^#+\\s+/, '')\n .replace(/`([^`]+)`/g, '$1')\n .trim();\n}\n\nexport function extractAcceptanceCriteriaFromIssue(title: string, body?: string | null): string[] {\n const text = body?.trim() ?? '';\n if (!text) return [`Implement ${title}`];\n\n const lines = text.split(/\\r?\\n/);\n // Only a real heading (or standalone bold/label line) counts — the phrase\n // appearing mid-prose must not start a section (PAN-2404).\n const headingIndex = lines.findIndex((line) =>\n /^#{1,6}\\s+.*acceptance(\\s+criteria)?\\b/i.test(line)\n || /^\\s*\\*{0,2}acceptance criteria\\*{0,2}:?\\s*$/i.test(line));\n\n // If an explicit Acceptance Criteria section exists, use only that section\n if (headingIndex >= 0) {\n // Find the end of this section (next heading or end of text)\n const sectionEndIndex = lines.findIndex((line, idx) =>\n idx > headingIndex && /^#+\\s+/.test(line)\n );\n const endIndex = sectionEndIndex >= 0 ? sectionEndIndex : lines.length;\n const sectionLines = lines.slice(headingIndex + 1, endIndex);\n\n const checklist = sectionLines\n .filter((line) => /^\\s*[-*+]\\s+\\[[ xX]\\]\\s+/.test(line))\n .map(cleanMarkdownLine)\n .filter(Boolean);\n if (checklist.length > 0) return checklist;\n\n const bullets = sectionLines\n .filter((line) => /^\\s*[-*+]\\s+/.test(line))\n .map(cleanMarkdownLine)\n .filter(Boolean)\n .slice(0, 8);\n if (bullets.length > 0) return bullets;\n }\n\n // No explicit Acceptance Criteria section: return a single generic AC\n return [`Issue's stated fix implemented with tests`];\n}\n\nexport function synthesizeMinimalXBrief(issue: AutoSynthesizeIssueInput): XBriefDocument {\n const issueId = issue.issueId.toUpperCase();\n const issueLabel = issueId.toLowerCase();\n const now = new Date().toISOString();\n const criteria = extractAcceptanceCriteriaFromIssue(issue.title, issue.body);\n const items: XBriefSubItem[] = criteria.map((criterion, index) => ({\n id: `auto-start.ac${index + 1}`,\n title: criterion,\n status: 'pending',\n created: now,\n metadata: { kind: 'acceptance_criterion' },\n }));\n\n const canonicalFilename = generateXBriefFilename(issueId, issue.title, now);\n\n return {\n xBRIEFInfo: {\n version: '0.8',\n created: now,\n updated: now,\n author: 'overdeck/auto-start',\n description: `Auto-synthesized minimal plan for ${issueId}: ${issue.title}`,\n inspectionPolicy: 'never',\n },\n plan: {\n id: issueLabel,\n title: issue.title,\n status: 'proposed',\n uid: randomUUID(),\n author: 'overdeck/auto-start',\n sequence: 1,\n created: now,\n updated: now,\n references: issue.url ? [{ uri: issue.url, label: issueId, type: 'issue' }] : [],\n tags: ['auto-start'],\n metadata: { canonicalFilename },\n narratives: {\n Problem: issue.body?.trim() || issue.title,\n Proposal: 'Implement the issue directly from the tracker-provided title and body.',\n },\n items: [\n {\n id: 'auto-start',\n title: 'Implement issue',\n status: 'pending',\n priority: 'medium',\n created: now,\n metadata: {\n difficulty: 'simple',\n issueLabel,\n requiresInspection: false,\n inspectionDepth: 'fast',\n },\n narrative: { Action: issue.body?.trim() || issue.title },\n items,\n },\n ],\n edges: [],\n },\n };\n}\n\n/**\n * Synthesize and durably persist a proposed spec through the canonical write\n * door. The returned workspace path is the canonical spec path because\n * workspace-local spec copies are migration compatibility only.\n */\nexport const writeAutoStartXBrief = (\n projectRoot: string,\n _workspacePath: string,\n issue: AutoSynthesizeIssueInput,\n): Effect.Effect<AutoSynthesizeResult, FsError> =>\n Effect.gen(function* () {\n const document = synthesizeMinimalXBrief(issue);\n const canonicalFilename = document.plan.metadata?.canonicalFilename as string;\n const projectDocument: XBriefDocument = {\n ...document,\n plan: { ...document.plan, status: 'proposed' },\n };\n const written = yield* writeSpecForIssue(\n projectRoot,\n projectDocument,\n 'proposed',\n canonicalFilename,\n );\n\n return {\n document,\n workspaceSpecPath: written.path,\n projectSpecPath: written.path,\n canonicalFilename,\n };\n });\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { lstat, mkdir, readFile, rm } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { Role } from '@overdeck/contracts';\nimport { Effect } from 'effect';\n\nimport { buildChildEnvWithoutTmuxSync } from '../../../../lib/child-env.js';\nimport {\n saveAgentState,\n saveAgentStateSync,\n} from '../../../../lib/agents.js';\nimport type { AgentState } from '../../../../lib/agents/agent-state.js';\nimport type { RemoteWorkspaceMetadata } from '../../../../lib/remote/interface.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { saveAgentStateAndEmitEventProgram } from '../../services/agent-projection.js';\nimport {\n appendAgentLifecycleLog,\n buildPanStartArgs,\n emitStartAgentPhase,\n execAsync,\n execFileAsync,\n getIssueDataService,\n invalidateAgentsCache,\n updateRegistryForAgentStart,\n type SpawnGuardrailDecision,\n} from './shared.js';\n\ntype LifecycleTransition = {\n transitionTo: (\n issueId: string,\n status: 'in_progress',\n ) => Effect.Effect<unknown, unknown, never>;\n};\n\ntype EventStoreAppend = {\n append: (event: {\n type: 'issue.statusChanged';\n timestamp: string;\n payload: { issueId: string; status: 'In Progress'; canonicalStatus: 'in_progress' };\n }) => Effect.Effect<unknown, unknown, never>;\n};\n\ntype SpawnPanCommand = (args: string[], cwd?: string) => Promise<string>;\n\ntype PlaceholderHarness = 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null;\n\nexport function buildAgentStartPlaceholder(input: {\n agentSessionName: string;\n issueId: string;\n workspacePath: string;\n role: Role;\n effectiveHarness: PlaceholderHarness;\n startedBy: string;\n allowHost: boolean;\n startedAt: string;\n}) {\n const state: AgentState = {\n id: input.agentSessionName,\n issueId: input.issueId,\n workspace: input.workspacePath,\n role: input.role,\n ...(input.effectiveHarness ? { harness: input.effectiveHarness } : {}),\n model: 'pending-work-spawn',\n status: 'starting',\n startedAt: input.startedAt,\n startedBy: input.startedBy,\n hostOverride: input.allowHost || undefined,\n };\n return {\n state,\n event: {\n type: 'agent.started' as const,\n timestamp: input.startedAt,\n payload: {\n agentId: input.agentSessionName,\n issueId: input.issueId,\n agent: {\n id: input.agentSessionName,\n issueId: input.issueId,\n workspace: input.workspacePath,\n status: 'starting' as const,\n startedAt: input.startedAt,\n role: input.role,\n startedBy: input.startedBy,\n ...(input.effectiveHarness ? { runtime: input.effectiveHarness } : {}),\n },\n },\n },\n };\n}\n\nexport function buildContainerStartState(input: {\n agentSessionName: string;\n issueId: string;\n workspacePath: string;\n role: Role;\n effectiveHarness: PlaceholderHarness;\n startedBy: string;\n allowHost: boolean;\n status: 'starting' | 'error';\n startedAt: string;\n}): AgentState {\n return {\n id: input.agentSessionName,\n issueId: input.issueId,\n ...(input.effectiveHarness ? { harness: input.effectiveHarness } : {}),\n model: 'pending-container-start',\n status: input.status,\n startedAt: input.startedAt,\n workspace: input.workspacePath,\n role: input.role,\n startedBy: input.startedBy,\n hostOverride: input.allowHost || undefined,\n };\n}\n\nexport async function requestWorkStartAfterContainers(input: {\n args: string[];\n workspacePath: string;\n spawnPanCommand: SpawnPanCommand;\n markWorkStartAccepted: () => Promise<void>;\n updateIssueStatus: () => Promise<void>;\n}): Promise<string> {\n const activityId = await input.spawnPanCommand(input.args, input.workspacePath);\n await input.markWorkStartAccepted();\n await input.updateIssueStatus();\n return activityId;\n}\n\nexport function handleRemoteAgentSpawn(input: {\n issueId: string;\n workspacePath: string;\n workspaceMetadata: RemoteWorkspaceMetadata;\n spawnModel: string;\n startedBy: string;\n autoSpawnConsentRequired: boolean;\n projectPath: string;\n spawnGuardrails: SpawnGuardrailDecision;\n lifecycle: LifecycleTransition;\n}) {\n return Effect.gen(function* () {\n const {\n issueId,\n workspacePath,\n workspaceMetadata,\n spawnModel,\n startedBy,\n autoSpawnConsentRequired,\n projectPath,\n spawnGuardrails,\n lifecycle,\n } = input;\n\n const { spawnRemoteAgent, checkRemoteSpendCap } = yield* Effect.promise(() => import('../../../../lib/remote/remote-agents.js'));\n const { createFlyProviderFromConfig } = yield* Effect.promise(() => import('../../../../lib/remote/index.js'));\n const { loadConfigSync: loadPanConfig } = yield* Effect.promise(() => import('../../../../lib/config.js'));\n const panConfig = loadPanConfig();\n const spendCap = checkRemoteSpendCap(panConfig);\n if (!spendCap.allowed) {\n return jsonResponse({ error: spendCap.message }, { status: 429 });\n }\n const fly = createFlyProviderFromConfig(panConfig.remote);\n yield* Effect.promise(() => fly.syncAllCredentials(workspaceMetadata.vmName));\n\n const { buildWorkAgentPrompt, getTrackerContext } = yield* Effect.promise(() => import('../../../../lib/cloister/work-agent-prompt.js'));\n const trackerContext = yield* Effect.promise(() => getTrackerContext(issueId, workspacePath));\n const agentPrompt = yield* Effect.promise(() => buildWorkAgentPrompt({\n issueId,\n env: 'REMOTE',\n workspacePath: '/workspace',\n skipDynamicContext: true,\n trackerContext,\n }));\n\n emitStartAgentPhase(issueId, 'spawn', 'start', 'starting remote work agent', {\n workspacePath,\n vmName: workspaceMetadata.vmName,\n });\n const state = yield* Effect.promise(() => spawnRemoteAgent({\n issueId,\n workspace: workspaceMetadata,\n prompt: agentPrompt,\n model: spawnModel,\n startedBy,\n autoSpawnConsentRequired,\n tier: fly.getResiliencyTier(),\n }));\n\n // Write canonical state.json so activeRoleRunExists() sees this remote\n // work agent as active before we emit the lifecycle transition below.\n // spawnRemoteAgent only writes remote-state.json; without state.json the\n // Cloister duplicate-spawn guard misses the in-flight remote agent and\n // would spawn a second local work run when in_progress is emitted.\n yield* saveAgentState({\n id: state.id,\n issueId: state.issueId,\n workspace: workspacePath,\n role: 'work',\n model: spawnModel,\n status: 'starting',\n startedAt: state.startedAt,\n harness: 'claude-code',\n startedBy,\n });\n updateRegistryForAgentStart(state.issueId, workspacePath, state.id);\n\n // PAN-1048: lifecycle.transitionTo() is the single source of issue.transitioned.\n // The redundant issue.statusChanged emit was racing with reactive Cloister:\n // Cloister mapped 'in_progress' → 'work' role and tried to spawn a second\n // run while activeRoleRunExists() still saw no state.json for the\n // in-flight spawn above. state.json is now written before this emit.\n yield* Effect.promise(() => Effect.runPromise(\n lifecycle.transitionTo(issueId, 'in_progress').pipe(Effect.catch(() => Effect.void))\n ));\n\n // PAN-1048 review feedback 003: emit a contract-compliant agent.started\n // event. The reducer writes event.payload.agent into agentsById keyed by\n // event.payload.agentId — the previous shape ({ agentId: issueId, issueId })\n // omitted .agent and used the issue ID as the key, inserting `undefined`\n // into the read model and breaking dashboard consumers.\n //\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n yield* saveAgentStateAndEmitEventProgram({\n id: state.id,\n issueId: state.issueId,\n workspace: workspacePath,\n role: 'work',\n model: spawnModel,\n status: state.status,\n startedAt: state.startedAt,\n harness: 'claude-code',\n startedBy,\n }, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: state.id,\n issueId: state.issueId,\n agent: {\n id: state.id,\n issueId: state.issueId,\n role: 'work' as const,\n model: spawnModel,\n status: state.status,\n startedAt: state.startedAt,\n lastActivity: state.lastActivity,\n startedBy,\n },\n },\n });\n emitStartAgentPhase(issueId, 'spawn', 'success', 'remote work agent spawn requested', {\n agentId: state.id,\n vmName: workspaceMetadata.vmName,\n });\n try { getIssueDataService().patchIssue(issueId, { status: 'In Progress', canonicalStatus: 'in_progress' }); } catch { /* non-fatal */ }\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n message: `Starting remote agent for ${issueId}`,\n remote: true,\n vmName: workspaceMetadata.vmName,\n agentId: state.id,\n projectPath,\n guardrails: spawnGuardrails,\n });\n });\n}\n\nexport function handleContainerOrchestration(input: {\n issueId: string;\n workspacePath: string;\n devScript: string;\n agentSessionName: string;\n role: Role;\n effectiveHarness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null;\n startedBy: string;\n allowHost: boolean;\n spawnModel: string;\n spawnGuardrails: SpawnGuardrailDecision;\n projectPath: string;\n eventStore: EventStoreAppend;\n spawnPanCommand: SpawnPanCommand;\n markWorkStartAccepted: () => Promise<void>;\n updateIssueStatus: () => Promise<void>;\n}) {\n return Effect.gen(function* () {\n const {\n issueId,\n workspacePath,\n devScript,\n agentSessionName,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n spawnModel,\n spawnGuardrails,\n projectPath,\n eventStore,\n spawnPanCommand,\n markWorkStartAccepted,\n updateIssueStatus,\n } = input;\n\n if (existsSync(workspacePath) && existsSync(devScript)) {\n let dockerRunning = false;\n try {\n yield* Effect.promise(() => execAsync('docker info >/dev/null 2>&1', { encoding: 'utf-8' }));\n dockerRunning = true;\n } catch {}\n\n if (dockerRunning) {\n const getComposeProjectName = async (id: string, wPath: string): Promise<string> => {\n const featureFolder = `feature-${id.toLowerCase()}`;\n const expected = `overdeck-${featureFolder}`;\n const validate = (value: string, devPath: string): string => {\n if (value !== expected) {\n throw new Error(`Invalid COMPOSE_PROJECT_NAME in ${devPath}: expected ${expected}`);\n }\n return value;\n };\n\n const devScriptPaths = [join(wPath, '.devcontainer', 'dev'), join(wPath, 'dev')];\n for (const devPath of devScriptPaths) {\n try {\n if (existsSync(devPath)) {\n const content = await readFile(devPath, 'utf-8');\n const match = content.match(/COMPOSE_PROJECT_NAME=\"([^$\"]*)\\$\\{FEATURE_FOLDER\\}\"/);\n if (match) return validate(`${match[1]}${featureFolder}`, devPath);\n const literalMatch = content.match(/COMPOSE_PROJECT_NAME=\"([^\"]+)\"/);\n if (literalMatch) return validate(literalMatch[1], devPath);\n }\n } catch (error) {\n if (error instanceof Error && error.message.startsWith('Invalid COMPOSE_PROJECT_NAME')) throw error;\n }\n }\n return expected;\n };\n\n let featureName: string;\n try {\n featureName = yield* Effect.promise(() => getComposeProjectName(issueId, workspacePath));\n } catch (error) {\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: error instanceof Error ? error.message : String(error),\n }, { status: 422 });\n }\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_container_check', {\n issueId,\n featureName,\n workspacePath,\n }));\n let containersReady = false;\n\n try {\n const { stdout: existing } = yield* Effect.promise(() => execFileAsync(\n 'docker',\n ['ps', '--filter', `name=${featureName}`, '--format', '{{.Names}}|{{.Status}}'],\n { encoding: 'utf-8' }\n ));\n const runningContainers = existing.trim().split('\\n').filter(Boolean);\n const allHealthy = runningContainers.length > 0 && runningContainers.every(line => {\n const status = line.split('|')[1] || '';\n return status.includes('Up') && (!status.includes('(') || status.includes('(healthy)'));\n });\n if (allHealthy) containersReady = true;\n } catch {}\n\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_container_check_result', {\n issueId,\n featureName,\n containersReady,\n }));\n\n if (!containersReady && !allowHost) {\n const earlyAgentId = agentSessionName;\n const earlyStateDir = join(homedir(), '.overdeck', 'agents', earlyAgentId);\n yield* Effect.promise(() => mkdir(earlyStateDir, { recursive: true }));\n saveAgentStateSync(buildContainerStartState({\n agentSessionName: earlyAgentId,\n issueId,\n workspacePath,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n status: 'starting',\n startedAt: new Date().toISOString(),\n }));\n updateRegistryForAgentStart(issueId, workspacePath, earlyAgentId);\n yield* Effect.promise(() => appendAgentLifecycleLog(earlyAgentId, 'agent.start_waiting_for_containers', {\n issueId,\n featureName,\n workspacePath,\n role,\n }));\n\n const containerActivityId = `containers-${Date.now()}`;\n\n // Start containers in background and spawn agent when ready\n (async () => {\n try {\n const containerUid = process.getuid?.() ?? 1000;\n const containerGid = process.getgid?.() ?? 1000;\n await appendAgentLifecycleLog(earlyAgentId, 'agent.container_start_spawned', {\n issueId,\n featureName,\n workspacePath,\n });\n const containerChild = spawn('./dev', ['all'], {\n cwd: workspacePath,\n stdio: 'ignore',\n env: buildChildEnvWithoutTmuxSync(process.env, { UID: String(containerUid), GID: String(containerGid), DOCKER_USER: `${containerUid}:${containerGid}` }),\n detached: true,\n });\n containerChild.unref();\n\n const maxWaitMs = 3 * 60 * 1000;\n const pollIntervalMs = 3000;\n const startTime = Date.now();\n let healthy = false;\n\n while (Date.now() - startTime < maxWaitMs) {\n try {\n const { stdout } = await execFileAsync(\n 'docker',\n ['ps', '--filter', `name=${featureName}`, '--format', '{{.Names}}|{{.Status}}'],\n { encoding: 'utf-8' }\n );\n const containers = stdout.trim().split('\\n').filter(Boolean);\n const allH = containers.length > 0 && containers.every(line => {\n const status = line.split('|')[1] || '';\n return status.includes('Up') && (!status.includes('(') || status.includes('(healthy)'));\n });\n if (allH) { healthy = true; break; }\n } catch {}\n await new Promise(r => setTimeout(r, pollIntervalMs));\n }\n\n await appendAgentLifecycleLog(earlyAgentId, healthy ? 'agent.container_wait_succeeded' : 'agent.container_wait_timed_out', {\n issueId,\n featureName,\n waitedMs: Date.now() - startTime,\n });\n\n if (!healthy) {\n saveAgentStateSync(buildContainerStartState({\n agentSessionName: earlyAgentId,\n issueId,\n workspacePath,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n status: 'error',\n startedAt: new Date().toISOString(),\n }));\n return;\n }\n\n // Docker named volumes may create root-owned empty node_modules.\n // Remove them — workspace creation runs bun install which creates\n // correct workspace-aware node_modules with proper local package resolution.\n for (const nmDir of [join(workspacePath, 'node_modules'), join(workspacePath, 'src', 'dashboard', 'frontend', 'node_modules')]) {\n try {\n if (existsSync(nmDir)) {\n const stat = await lstat(nmDir);\n if (!stat.isSymbolicLink()) {\n await rm(nmDir, { recursive: true, force: true });\n console.log(`[start-agent] Removed Docker-created ${nmDir}`);\n }\n }\n } catch (nmErr: any) {\n console.warn(`[start-agent] Could not remove ${nmDir}: ${nmErr.message}`);\n }\n }\n\n await appendAgentLifecycleLog(earlyAgentId, 'agent.work_spawn_requested_after_containers', {\n issueId,\n role,\n workspacePath,\n harness: effectiveHarness,\n });\n emitStartAgentPhase(issueId, 'spawn', 'start', 'starting local work agent after containers became healthy', { workspacePath });\n const activityId = await requestWorkStartAfterContainers({\n args: buildPanStartArgs({\n issueId,\n model: spawnModel,\n harness: effectiveHarness,\n allowHost,\n }),\n workspacePath,\n spawnPanCommand,\n markWorkStartAccepted,\n updateIssueStatus,\n });\n emitStartAgentPhase(issueId, 'spawn', 'success', 'local work agent spawn requested after container startup', {\n workspacePath,\n activityId,\n });\n } catch (err: any) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n emitStartAgentPhase(issueId, 'spawn', 'failure', errorMessage, { workspacePath });\n await appendAgentLifecycleLog(earlyAgentId, 'agent.container_start_failed', {\n issueId,\n error: errorMessage,\n }).catch(() => undefined);\n try {\n saveAgentStateSync(buildContainerStartState({\n agentSessionName: earlyAgentId,\n issueId,\n workspacePath,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n status: 'error',\n startedAt: new Date().toISOString(),\n }));\n } catch { /* non-fatal */ }\n console.error(`[start-agent] Background container startup failed for ${issueId}:`, err);\n }\n })();\n\n yield* Effect.promise(() => Effect.runPromise(eventStore.append({\n type: 'issue.statusChanged',\n timestamp: new Date().toISOString(),\n payload: { issueId, status: 'In Progress', canonicalStatus: 'in_progress' },\n })));\n try { getIssueDataService().patchIssue(issueId, { status: 'In Progress', canonicalStatus: 'in_progress' }); } catch { /* non-fatal */ }\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n message: `Starting containers and agent for ${issueId} (this may take a few minutes)`,\n startingContainers: true,\n containerActivityId,\n agentId: earlyAgentId,\n projectPath,\n guardrails: spawnGuardrails,\n });\n }\n }\n }\n\n return null;\n });\n}\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { dirname, join, sep } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport {\n saveAgentState, determineModel, getProviderAuthMode, getAgentState,\n clearAgentPaused, clearAgentTroubled,\n} from '../../../../lib/agents.js';\nimport { resolveIssueWorkModel } from '../../../../lib/agents/staffing.js';\nimport type { AgentState } from '../../../../lib/agents/agent-state.js';\nimport { operatorInterventionEvent } from '../../../../lib/operator-interventions.js';\nimport { buildChildEnvWithoutTmuxSync } from '../../../../lib/child-env.js';\nimport { checkCodexAuthStatus } from '../../../../lib/codex-auth.js';\nimport { canUseHarnessSync } from '../../../../lib/harness-policy.js';\nimport { emitActivityEntrySync } from '../../../../lib/activity-logger.js';\nimport { appendOperatorInterventionEvent } from '../../../../lib/operator-interventions.js';\nimport { extractPrefixSync, parseIssueIdSync } from '../../../../lib/issue-id.js';\nimport { PAN_CONTINUE_FILENAME, PAN_DIRNAME } from '../../../../lib/pan-dir/types.js';\nimport { loadWorkspaceMetadataSync as loadWorkspaceMetadataFn } from '../../../../lib/remote/workspace-metadata.js';\nimport { getWorkAgentLifecycleState } from '../../../../lib/work-agent-lifecycle.js';\nimport { validateProviderHealth } from '../../../../lib/provider-health.js';\nimport { checkActiveOrderDispatch } from '../../../../lib/orders/dispatch-gate.js';\nimport { OrderDispatchReservationError, withActiveOrderDispatchReservation } from '../../../../lib/orders/dispatch-reservation.js';\nimport type { OrderDispatchEligibility } from '../../../../lib/orders/eligibility.js';\nimport { getProjectSync, resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { clearWorkspaceStuck, getReviewStatusSync } from '../../../../lib/review-status.js';\nimport { isStateMigrated } from '../../../../lib/state-home.js';\nimport { shouldCommitLegacyWorkspaceArtifacts } from '../../../../lib/state-read-home.js';\nimport { isGeneratedGitHookPath, isOverdeckWorkspaceRuntimePath, parsePorcelainStatusPaths } from '../../../../lib/state-plane.js';\nimport { assertWorkspaceStackHealthyForSpawn } from '../../../../lib/agents/spawn-prep.js';\nimport { getWorkspaceStackHealth } from '../../../../lib/workspace/stack-health.js';\nimport { writeAutoStartXBrief } from '../../../../lib/xbrief/auto-synthesize.js';\nimport { findPlan, readPlan } from '../../../../lib/xbrief/io.js';\nimport { transitionXBriefOnMain, updatePlanStatus } from '../../../../lib/xbrief/lifecycle-io.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { ReadModelService } from '../../read-model.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport {\n claimAgentStartPlaceholderProgram,\n rollbackAgentStartPlaceholderProgram,\n} from '../../services/agent-projection.js';\nimport { IssueLifecycle } from '../../services/issue-lifecycle.js';\nimport { getSystemHealthSnapshot } from '../../services/system-health-service.js';\nimport { httpHandler } from '../http-handler.js';\nimport { rejectUnsafeDashboardMutationRequest } from '../dashboard-auth.js';\nimport { sessionExists, killSession } from '../../../../lib/tmux.js';\nimport {\n appendAgentLifecycleLog,\n buildHostOverrideConfirmation,\n buildPanStartArgs,\n emitStartAgentPhase,\n evaluateAgentStartGate,\n evaluateSpawnGuardrails,\n execAsync,\n getIssueDataService,\n getProjectPath,\n invalidateAgentsCache,\n isInternalAgentRequest,\n readJsonBody,\n resolveRequestedStartedBy,\n spawnPanCommandDetached,\n updateRegistryForAgentStart,\n type AgentStartGateDecision,\n} from './shared.js';\nimport { buildAgentStartPlaceholder, handleContainerOrchestration, handleRemoteAgentSpawn } from './spawn-helpers.js';\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport function orderDispatchConflict(decision: OrderDispatchEligibility): {\n status: 409;\n body: { error: string; code: string; conditions: OrderDispatchEligibility['conditions'] };\n} | null {\n if (decision.eligible) return null;\n return {\n status: 409,\n body: {\n error: decision.message ?? 'Order-book dispatch is blocked.',\n code: decision.code ?? 'order-dispatch-blocked',\n conditions: decision.conditions,\n },\n };\n}\n\n/**\n * PAN-2386: emit a dashboard activity event when start-agent refuses to spawn\n * because the workspace is dirty. Kept as a pure function so the refusal path\n * can be unit-tested without booting the full Effect route stack.\n */\nexport function emitDirtyWorkspaceRefusalActivity(issueId: string, porcelain: string): void {\n try {\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'warn',\n message: `Workspace dirty — agent start refused for ${issueId}`,\n issueId,\n details: JSON.stringify({\n reason: 'Workspace has uncommitted changes. Commit, discard, or resolve and retry.',\n porcelain: porcelain.split('\\n').slice(0, 5),\n }),\n });\n } catch { /* non-fatal — activity emit should not block the response */ }\n}\n\n/**\n * True when git porcelain contains only Overdeck-owned workspace runtime files\n * (PAN-3042; shared predicate since PAN-3245) or generated git-hook output — the\n * `.husky/_/pre-rebase` guard that worktree creation writes made every fresh\n * workspace read as dirty and 409 the planning auto-handoff (PAN-3266).\n */\nexport function isOnlyOverdeckRuntimeWorkspaceChanges(porcelain: string): boolean {\n return parsePorcelainStatusPaths(porcelain)\n .every((path) => isOverdeckWorkspaceRuntimePath(path) || isGeneratedGitHookPath(path));\n}\n\nexport function spawnGuardrailResourcesHint(hint?: string): string {\n const resourcesHint = 'Open /resources to inspect Machine Room pressure before retrying.';\n return hint ? `${hint} ${resourcesHint}` : resourcesHint;\n}\n\n// ─── Start-agent gate resolution (PAN-2499) ───────────────────────────────────\n\n/**\n * Evaluate the persistent start gate for an agent and, if the request is\n * operator-origin and `clearGates` is set, clear the paused/troubled gates\n * through the same write paths used by `pan unpause` and `pan untroubled`.\n *\n * Returns the gate decision when the agent is still blocked, or `null` when\n * the agent may proceed. Emits `operator.intervention` events when a gate is\n * actually cleared.\n */\nexport function resolveStartAgentGateForRoute(input: {\n agentSessionName: string;\n issueId: string;\n clearGates: boolean;\n originOk: boolean;\n}): Effect.Effect<AgentStartGateDecision | null, never> {\n let gate: AgentStartGateDecision | null = null;\n\n return Effect.gen(function* () {\n const state = yield* getAgentState(input.agentSessionName);\n gate = evaluateAgentStartGate(input.agentSessionName, state);\n if (!gate) return null;\n\n const shouldClear = input.originOk && input.clearGates;\n if (!shouldClear) return gate;\n if (!state) return gate;\n\n let cleared = false;\n if (state.paused === true) {\n yield* clearAgentPaused(input.agentSessionName);\n yield* Effect.promise(() =>\n appendOperatorInterventionEvent({\n issueId: input.issueId,\n kind: 'unpause',\n source: 'dashboard start-agent',\n }),\n );\n cleared = true;\n }\n\n if (state.troubled === true || (state.consecutiveFailures ?? 0) > 0) {\n yield* clearAgentTroubled(input.agentSessionName);\n yield* Effect.promise(() =>\n appendOperatorInterventionEvent({\n issueId: input.issueId,\n kind: 'untroubled',\n source: 'dashboard start-agent',\n }),\n );\n cleared = true;\n }\n\n if (!cleared) return gate;\n\n gate = evaluateAgentStartGate(input.agentSessionName, yield* getAgentState(input.agentSessionName));\n return gate;\n }).pipe(\n Effect.catch((err) => {\n console.error(`[start-agent] Failed to clear gates for ${input.issueId}: ${err instanceof Error ? err.message : String(err)}`);\n return Effect.succeed(gate);\n }),\n );\n}\n\n/**\n * Clear the persistent start gates immediately before spawning, restoring the\n * complete original state if either clear or the spawn itself fails.\n */\nexport async function spawnAfterClearingStartGates<T>(input: {\n agentSessionName: string;\n gate: AgentStartGateDecision | null;\n initialState: AgentState | null;\n spawn: () => Promise<T>;\n isSuccessful?: (result: T) => boolean;\n}): Promise<T> {\n try {\n if (input.gate?.paused) await Effect.runPromise(clearAgentPaused(input.agentSessionName));\n if (input.gate?.troubled) await Effect.runPromise(clearAgentTroubled(input.agentSessionName));\n const result = await input.spawn();\n if (input.isSuccessful && !input.isSuccessful(result) && input.gate && input.initialState) {\n await Effect.runPromise(saveAgentState(input.initialState));\n }\n return result;\n } catch (error) {\n if (input.gate && input.initialState) {\n await Effect.runPromise(saveAgentState(input.initialState));\n }\n throw error;\n }\n}\n// ─── Route: POST /api/agents (start agent) ───────────────────────────────────\n\nexport const postAgentsRoute = HttpRouter.add(\n 'POST',\n '/api/agents',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const lifecycle = yield* IssueLifecycle;\n const readModel = yield* ReadModelService;\n const { issueId, projectId } = body as any;\n const internalRequest = yield* Effect.promise(() => isInternalAgentRequest(request));\n let startedBy: string;\n try { startedBy = resolveRequestedStartedBy((body as any).startedBy, internalRequest); }\n catch (error) { return jsonResponse({ error: error instanceof Error ? error.message : String(error) }, { status: 400 }); }\n const autoStart = (body as any).auto === true;\n const autoSpawnConsentRequired = internalRequest && (body as any).autoSpawnConsentRequired === true;\n const guardrailAcknowledged = (body as any).guardrailAcknowledged === true;\n const offBook = (body as any).offBook === true;\n const requestedHostOverride = (body as any).host === true || (body as any).allowHost === true;\n if (!issueId) {\n return jsonResponse({ error: 'issueId required' }, { status: 400 });\n }\n\n const legacyFields = ['workType', 'phase', 'agentType'].filter((field) => field in (body as Record<string, unknown>));\n if (legacyFields.length > 0) {\n return jsonResponse({\n error: `Legacy start-agent field(s) are no longer accepted: ${legacyFields.join(', ')}. Send role: 'work' instead.`,\n }, { status: 400 });\n }\n\n const role = (body as any).role ?? 'work';\n if (role !== 'work') {\n return jsonResponse({ error: `Unsupported agent role \"${String(role)}\". POST /api/agents only starts role: 'work'.` }, { status: 400 });\n }\n\n // Reject bare numeric IDs (e.g. \"484\") — they have no project prefix, so tracker\n // routing and workspace naming both fail. Require \"PAN-484\" style.\n if (/^\\d+$/.test(String(issueId))) {\n return jsonResponse(\n {\n error: `Invalid issueId \"${issueId}\": bare numeric IDs are not allowed. Use a prefixed ID (e.g. PAN-${issueId}).`,\n hint: 'Issue IDs must include a project prefix (e.g. PAN-484, MIN-123).',\n },\n { status: 422 },\n );\n }\n\n const parsedIssueId = parseIssueIdSync(String(issueId));\n if (!parsedIssueId) {\n return jsonResponse(\n {\n error: `Invalid issueId \"${issueId}\": issue IDs must use a supported project format (e.g. PAN-484, MIN-123).`,\n hint: 'Issue IDs must include a project prefix and numeric identifier.',\n },\n { status: 422 },\n );\n }\n\n const hostOverrideConfirmation = buildHostOverrideConfirmation(String(issueId));\n const allowHost = requestedHostOverride && (body as any).hostOverrideConfirmation === hostOverrideConfirmation;\n if (requestedHostOverride && !allowHost) {\n return jsonResponse({\n success: false,\n error: 'host_override_confirmation_required',\n requiresHostConfirmation: true,\n confirmation: hostOverrideConfirmation,\n hint: `Host override bypasses workspace isolation. Retry only after explicitly confirming: ${hostOverrideConfirmation}`,\n }, { status: 409 });\n }\n\n // Guard: reject starting agents for already-closed issues\n const issueDataService = getIssueDataService();\n const cachedIssues = issueDataService.getIssues();\n const cachedIssue = cachedIssues.find(\n (i: any) => (i.identifier || '').toUpperCase() === issueId.toUpperCase()\n );\n if (cachedIssue && (cachedIssue.canonicalStatus === 'done' || cachedIssue.canonicalStatus === 'canceled')) {\n return jsonResponse(\n {\n error: `Issue ${issueId} is already closed (${cachedIssue.canonicalStatus}). Cannot start an agent for a closed issue.`,\n hint: 'Reopen the issue first if you need to resume work.',\n },\n { status: 422 },\n );\n }\n\n const issueLower = parsedIssueId.normalized;\n const agentSessionName = `agent-${issueLower}`;\n const clearGates = (body as any).clearGates === true;\n const initialAgentState = yield* getAgentState(agentSessionName);\n const startGateBlock = evaluateAgentStartGate(agentSessionName, initialAgentState);\n if (startGateBlock) {\n if (!clearGates) {\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_blocked_gate', {\n issueId,\n paused: startGateBlock.paused,\n troubled: startGateBlock.troubled,\n reason: startGateBlock.error,\n }));\n return jsonResponse(startGateBlock, { status: 409 });\n }\n }\n\n const workspaceMetadata = loadWorkspaceMetadataFn(issueId);\n const isRemote = workspaceMetadata?.location === 'remote';\n\n const issuePrefix = extractPrefixSync(issueId) ?? issueId.split('-')[0];\n const resolvedProject = resolveProjectFromIssueSync(String(issueId));\n const projectConfig = resolvedProject ? getProjectSync(resolvedProject.projectKey) : null;\n const projectPath = projectConfig?.path ?? getProjectPath(projectId, issuePrefix);\n const orderDispatch = yield* Effect.promise(() => checkActiveOrderDispatch(projectPath, issueId, { offBook }));\n const orderConflict = orderDispatchConflict(orderDispatch.decision);\n if (orderConflict) return jsonResponse(orderConflict.body, { status: orderConflict.status });\n\n const workspacePath = join(projectPath, 'workspaces', `feature-${issueLower}`);\n if (!existsSync(workspacePath)) {\n try {\n const nodeDir = dirname(process.execPath);\n yield* Effect.promise(() => execAsync(\n `pan workspace create ${issueId} --local`,\n { cwd: projectPath, encoding: 'utf-8', timeout: 60000, env: buildChildEnvWithoutTmuxSync(process.env, { PATH: `${nodeDir}:${process.env.PATH ?? ''}` }) }\n ));\n } catch (wsErr) {\n return jsonResponse({\n error: `Failed to create workspace for ${issueId}: ${(wsErr as Error).message}`,\n hint: 'Try creating the workspace manually: pan workspace create ' + issueId + ' --local',\n }, { status: 500 });\n }\n }\n\n const workspacePanDir = join(workspacePath, PAN_DIRNAME);\n const workspacePanContinuePath = join(workspacePanDir, PAN_CONTINUE_FILENAME);\n\n let planPath = yield* findPlan(workspacePath);\n if (autoStart && !planPath) {\n const issueTitle = cachedIssue?.title || issueId;\n const issueBody = cachedIssue?.description || '';\n // writeAutoStartXBrief is Effect-returning — yield it directly (PAN-1768).\n yield* writeAutoStartXBrief(projectPath, workspacePath, {\n issueId,\n title: issueTitle,\n body: issueBody,\n url: cachedIssue?.url,\n });\n planPath = yield* findPlan(workspacePath);\n }\n if (!planPath) {\n return jsonResponse({\n error: `No workspace xBRIEF found for ${issueId}. Work agents require a finalized plan.`,\n hint: 'Run planning first, or use auto-start to synthesize a plan before starting the work agent.',\n issueId,\n }, { status: 422 });\n }\n\n const planReadResult = yield* readPlan(planPath).pipe(\n Effect.match({\n onFailure: (planErr) => ({ _tag: 'failure' as const, planErr }),\n onSuccess: (planDoc) => ({ _tag: 'success' as const, planDoc }),\n }),\n );\n if (planReadResult._tag === 'failure') {\n const { planErr } = planReadResult;\n return jsonResponse({\n error: `Could not read workspace xBRIEF for ${issueId}: ${planErr instanceof Error ? planErr.message : String(planErr)}`,\n hint: 'Re-run planning to produce a readable xBRIEF before starting the work agent.',\n issueId,\n }, { status: 422 });\n }\n const { planDoc } = planReadResult;\n\n const planIssueId = planDoc?.plan?.id;\n if (planIssueId && planIssueId.toLowerCase() !== issueLower) {\n return jsonResponse({\n error: `Plan in workspace is for ${planIssueId.toUpperCase()}, not ${issueId}. The workspace contains stale planning artifacts from a different issue.`,\n hint: 'Run planning for this issue first, or clean the workspace planning artifacts.',\n issueId,\n expectedIssue: issueId,\n actualIssue: planIssueId.toUpperCase(),\n }, { status: 422 });\n }\n\n const planItemCount = planDoc?.plan?.items?.length ?? 0;\n if (planItemCount === 0) {\n return jsonResponse({\n error: 'Plan exists but contains no items. Planning may have failed or produced an empty plan.',\n hint: 'Re-run planning to produce a plan with tasks and acceptance criteria.',\n issueId,\n }, { status: 422 });\n }\n\n const health = yield* Effect.promise(() => getSystemHealthSnapshot());\n emitStartAgentPhase(issueId, 'guardrails', 'start', 'evaluating spawn guardrails');\n const spawnGuardrails = evaluateSpawnGuardrails(health);\n if (spawnGuardrails.blocked) {\n emitStartAgentPhase(issueId, 'guardrails', 'failure', spawnGuardrails.error ?? 'guardrails blocked', {\n status: spawnGuardrails.status,\n hint: spawnGuardrails.hint,\n });\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: spawnGuardrails.error,\n hint: spawnGuardrails.hint,\n guardrails: spawnGuardrails,\n }, { status: spawnGuardrails.status });\n }\n if (spawnGuardrails.requiresAcknowledgement && !guardrailAcknowledged) {\n emitStartAgentPhase(issueId, 'guardrails', 'skipped', 'guardrail acknowledgement required', {\n status: spawnGuardrails.status,\n hint: spawnGuardrails.hint,\n });\n return jsonResponse({\n success: false,\n blocked: false,\n skipped: true,\n requiresAcknowledgement: true,\n hint: spawnGuardrailResourcesHint(spawnGuardrails.hint),\n guardrails: spawnGuardrails,\n }, { status: spawnGuardrails.status });\n }\n emitStartAgentPhase(issueId, 'guardrails', 'success', 'spawn guardrails passed');\n\n let spawnModel: string;\n try {\n // PAN-3022: no explicit body model → honor the per-issue work-model\n // override (record.workModel, PAN-2997 issue-override tier) before the\n // role default — same resolution order as `pan start`. Without this the\n // route resolves the role default and the `pan start --model` child then\n // persists it, clobbering the stored override.\n spawnModel = determineModel({\n model: (body as any).model ?? resolveIssueWorkModel(issueId),\n role,\n spawnKey: `${role}:${issueId}`,\n });\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n const providerAuthMode = yield* Effect.promise(() => getProviderAuthMode(spawnModel));\n if (providerAuthMode === 'subscription') {\n const codexAuth = yield* checkCodexAuthStatus();\n if (codexAuth.status === 'expired' || codexAuth.status === 'burned') {\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: `Codex authentication ${codexAuth.status}. GPT subscription agents cannot spawn with expired/burned tokens.`,\n hint: 'Click \"Re-authenticate\" in the Codex auth banner or Settings page to refresh your OpenAI subscription tokens.',\n }, { status: 429 });\n }\n }\n\n // Pre-flight provider health check — detect quota/auth/network errors\n // before spawning the agent into Claude Code's opaque retry loop.\n // validateProviderHealth returns an Effect (typed ProviderHealthError\n // channel) — wrapping it in Effect.promise handed a non-thenable to the\n // runtime and crashed the whole request (PAN-1768).\n const providerHealthCheck = yield* validateProviderHealth(spawnModel).pipe(\n Effect.match({\n onFailure: (err) => ({ _tag: 'failure' as const, err }),\n onSuccess: () => ({ _tag: 'success' as const, err: null }),\n }),\n );\n if (providerHealthCheck._tag === 'failure' && providerHealthCheck.err) {\n const err = providerHealthCheck.err;\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: err.message,\n hint: err.probeResult.kind === 'quota'\n ? 'Top up your credits on the provider dashboard, or switch this agent to a different model.'\n : err.probeResult.kind === 'auth'\n ? 'Check your API key in Settings → Providers.'\n : 'The provider may be temporarily unavailable. Try again later or switch models.',\n providerHealth: {\n provider: err.provider.name,\n model: err.model,\n kind: err.probeResult.kind,\n status: err.probeResult.status,\n },\n }, { status: 429 });\n }\n\n if (!isRemote) {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'start', 'checking workspace docker stack health', { workspacePath });\n let stackHealth = yield* getWorkspaceStackHealth(issueId, { projectConfig, workspacePath });\n if (!stackHealth.healthy && !allowHost) {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'start', 'stack unhealthy — attempting workspace stack rebuild', { workspacePath });\n yield* Effect.promise(() =>\n assertWorkspaceStackHealthyForSpawn(issueId, 'work', false, workspacePath).catch(() => undefined));\n stackHealth = yield* getWorkspaceStackHealth(issueId, { projectConfig, workspacePath });\n }\n if (!stackHealth.healthy) {\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_blocked_stack_unhealthy', {\n issueId,\n reasons: stackHealth.reasons,\n lastObserved: stackHealth.lastObserved,\n }));\n if (!allowHost) {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'failure', stackHealth.reasons.join('; '), {\n workspacePath,\n lastObserved: stackHealth.lastObserved,\n });\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'error',\n issueId: issueId.toUpperCase(),\n message: `agent-spawn-blocked-stack-unhealthy: ${issueId.toUpperCase()}`,\n details: stackHealth.reasons.join('; '),\n });\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: `Workspace docker stack for ${issueId} is not healthy: ${stackHealth.reasons.join('; ')}`,\n hint: `Run 'pan workspace rebuild ${issueId}' or use the CLI break-glass path: pan start ${issueId} --host.`,\n stackHealth,\n }, { status: 422 });\n }\n emitStartAgentPhase(issueId, 'stackHealthGate', 'skipped', 'stack unhealthy but host override confirmed', {\n workspacePath,\n reasons: stackHealth.reasons,\n });\n } else {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'success', 'workspace docker stack healthy', { workspacePath });\n }\n } else {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'skipped', 'remote workspace skips local stack-health gate', { workspacePath });\n }\n\n if (allowHost) {\n // PAN-1556: host-override is a spawn detail, not user-facing activity.\n console.warn(`[agents] agent-spawn-host-override: ${issueId.toUpperCase()} (dashboard-confirmed)`);\n }\n\n const migratedState = projectConfig ? yield* Effect.promise(() => isStateMigrated(projectConfig)) : false;\n if (shouldCommitLegacyWorkspaceArtifacts(migratedState) && (existsSync(workspacePanContinuePath) || existsSync(workspacePanDir))) {\n // Commit workspace orchestration artifacts before handing off to the work agent.\n // The entire block is best-effort — never let git errors abort the agent start.\n yield* Effect.gen(function* () {\n const gitRoot = workspacePath;\n if (existsSync(join(gitRoot, PAN_DIRNAME))) {\n // PAN-1819: use plain git add (never -f) and exclude workspace-state/sync-target paths.\n yield* Effect.promise(() => execAsync(`git add .pan/`, { cwd: gitRoot, encoding: 'utf-8' }));\n yield* Effect.promise(() => execAsync(\n `git reset HEAD -- .pan/kickoff.md .pan/continue.json .pan/handoff-*.md .pan/spec.vbrief.json`,\n { cwd: gitRoot, encoding: 'utf-8' },\n ));\n }\n // git diff --cached --quiet exits 1 when there ARE staged changes (normal).\n // Handle exit-1 in the Promise so it never becomes an Effect failure.\n const diffResult = yield* Effect.promise(() =>\n execAsync(`git diff --cached --quiet`, { cwd: gitRoot, encoding: 'utf-8' })\n .then(() => false)\n .catch(() => true)\n );\n if (diffResult) {\n yield* Effect.promise(() => execAsync(`git commit -m \"chore: planning artifacts for ${issueId} before agent start\"`, { cwd: gitRoot, encoding: 'utf-8' }));\n const pushChild = spawn('git', ['push'], { cwd: gitRoot, detached: true, stdio: 'ignore' });\n pushChild.unref();\n }\n }).pipe(Effect.catch(() => Effect.void));\n }\n\n let gatesCommitted = false;\n const commitClearedGates = async (): Promise<void> => {\n if (!startGateBlock || gatesCommitted) return;\n gatesCommitted = true;\n if (startGateBlock.paused) {\n await Effect.runPromise(eventStore.appendAsync(operatorInterventionEvent({ issueId, kind: 'unpause', source: 'dashboard' })));\n }\n if (startGateBlock.troubled) {\n await Effect.runPromise(eventStore.appendAsync(operatorInterventionEvent({ issueId, kind: 'untroubled', source: 'dashboard' })));\n }\n await appendAgentLifecycleLog(agentSessionName, 'agent.start_gates_cleared', {\n issueId,\n paused: startGateBlock.paused,\n troubled: startGateBlock.troubled,\n });\n };\n let workStartAccepted = false;\n const markWorkStartAccepted = async (): Promise<void> => {\n if (workStartAccepted) return;\n workStartAccepted = true;\n await Effect.runPromise(transitionXBriefOnMain(\n projectPath,\n issueId,\n 'active',\n 'running',\n `chore(state): start ${issueId.toUpperCase()} xBRIEF (status=running)`,\n ).pipe(\n Effect.match({\n onSuccess: (result) => {\n if (result.moved) {\n console.log(`[start-agent] xBRIEF moved ${result.fromDir} → active for ${issueId}`);\n }\n if (result.statusUpdated) {\n console.log(`[start-agent] Set plan.status=running for ${issueId}`);\n }\n if (result.committed) {\n console.log(`[start-agent] Committed running transition for ${issueId}`);\n }\n },\n onFailure: (err) => {\n console.warn(`[start-agent] xBRIEF running transition failed (non-fatal): ${err?.message ?? err}`);\n },\n }),\n ));\n\n if (planPath.startsWith(workspacePath + sep)) {\n try {\n updatePlanStatus(planPath, 'running');\n console.log(`[start-agent] Set plan.status=running for ${issueId}`);\n } catch (planStatusErr: any) {\n console.warn(`[start-agent] Failed to set plan.status=running (non-fatal): ${planStatusErr?.message ?? planStatusErr}`);\n }\n }\n\n try {\n const { appendSessionEntry, getProjectConfigFromWorkspacePath, resolveProjectForIssue } =\n await import('../../../../lib/pan-dir/record.js');\n const recordProject = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(workspacePath);\n await appendSessionEntry(recordProject, issueId, {\n timestamp: new Date().toISOString(),\n reason: 'start',\n agentModel: spawnModel,\n });\n console.log(`[start-agent] Wrote start session entry to record for ${issueId}`);\n } catch (continueErr: any) {\n console.warn(`[start-agent] Failed to write start entry to record (non-fatal): ${continueErr?.message ?? continueErr}`);\n }\n\n const pipelineStatus = getReviewStatusSync(issueId);\n if (pipelineStatus?.stuckReason === 'planning_auto_handoff_failed') {\n clearWorkspaceStuck(issueId);\n }\n };\n if (isRemote && workspaceMetadata) {\n const admitted = yield* Effect.promise(() => withActiveOrderDispatchReservation(\n projectPath,\n issueId,\n { offBook, recordOverride: true },\n () => spawnAfterClearingStartGates({\n agentSessionName,\n gate: startGateBlock,\n initialState: initialAgentState,\n spawn: () => Effect.runPromise(handleRemoteAgentSpawn({\n issueId,\n workspacePath,\n workspaceMetadata,\n spawnModel,\n startedBy,\n autoSpawnConsentRequired,\n projectPath,\n spawnGuardrails,\n lifecycle,\n })),\n isSuccessful: (remoteResponse) => remoteResponse.status >= 200 && remoteResponse.status < 300,\n }),\n ));\n const admittedConflict = orderDispatchConflict(admitted.check.decision);\n if (admittedConflict) return jsonResponse(admittedConflict.body, { status: admittedConflict.status });\n const response = admitted.result!;\n if (response.status < 200 || response.status >= 300) return response;\n yield* Effect.promise(commitClearedGates);\n yield* Effect.promise(markWorkStartAccepted);\n return response;\n }\n // Local workspace\n const devScript = join(workspacePath, 'dev');\n const hasPlanning = existsSync(join(workspacePath, PAN_DIRNAME));\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_requested', {\n issueId,\n workspacePath,\n hasPlanning,\n role,\n }));\n\n const agentLifecycle = yield* getWorkAgentLifecycleState(agentSessionName);\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_lifecycle_evaluated', {\n issueId,\n lifecycle: agentLifecycle,\n }));\n if (!agentLifecycle.canStartFresh) {\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_blocked', {\n issueId,\n reason: agentLifecycle.reason,\n lifecycle: agentLifecycle,\n }));\n return jsonResponse({\n error: agentLifecycle.reason || `Cannot start agent for ${issueId}`,\n lifecycle: agentLifecycle,\n }, { status: 409 });\n }\n\n yield* Effect.gen(function* () {\n const exists = yield* sessionExists(agentSessionName);\n if (exists) yield* killSession(agentSessionName);\n console.log(`[start-agent] Killed stale tmux session ${agentSessionName}`);\n }).pipe(Effect.catch(() => Effect.void));\n\n // PAN-1531: dirty-worktree refusal replaces silent pre-spawn stashing.\n // If the workspace has uncommitted changes the route returns 409 with the\n // diff so the dashboard can present the user three explicit choices:\n // Commit / Discard (typed confirmation required) / Stash as salvageable.\n // Clients that have already resolved the dirtiness MUST pass\n // `acknowledgeDirtyWorkspace: true` to bypass this gate (typically after\n // the user clicked one of the three modal buttons).\n const acknowledgeDirtyWorkspace = (body as any).acknowledgeDirtyWorkspace === true;\n if (!acknowledgeDirtyWorkspace) {\n try {\n const { stdout: statusOut } = yield* Effect.promise(() => execAsync('git status --porcelain --untracked-files=all', {\n cwd: workspacePath,\n encoding: 'utf-8',\n }));\n if (statusOut.trim() && !isOnlyOverdeckRuntimeWorkspaceChanges(statusOut)) {\n const { stdout: diffOut } = yield* Effect.promise(() => execAsync('git diff HEAD --stat', {\n cwd: workspacePath,\n encoding: 'utf-8',\n }).catch(() => ({ stdout: '' })));\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_refused_dirty_workspace', {\n issueId,\n workspacePath,\n porcelain: statusOut.trim(),\n }));\n // PAN-2386: surface the refusal in the dashboard activity feed so the operator\n // sees a toast instead of having to dig through lifecycle.log.\n emitDirtyWorkspaceRefusalActivity(issueId, statusOut.trim());\n return jsonResponse({\n error: `Workspace ${workspacePath} has uncommitted changes. Choose an action and retry start with acknowledgeDirtyWorkspace=true.`,\n code: 'WORKSPACE_DIRTY',\n workspacePath,\n porcelain: statusOut.trim(),\n diffStat: diffOut.trim(),\n actions: ['commit', 'discard', 'stash-salvage'],\n }, { status: 409 });\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.warn(`[start-agent] Failed to check workspace status for ${issueId}: ${message}`);\n }\n }\n\n // PAN-1048 review feedback 003: the route only resolves harness when the\n // dashboard launch panel explicitly chose one. Otherwise pass nothing and\n // let pan start → spawnAgent resolve from roles.work.harness (the new\n // single source of truth for per-role harness). The legacy `phase`\n // variable and the workType/harnessOverrides map are gone — the\n // legacy-field guard above (line 1872) blocks any client still sending\n // them. Note: when bodyHarness is set we still run it through\n // canUseHarness() so we can fail fast on a model+harness incompatibility\n // before spawning the subprocess.\n const bodyHarness = (body as any).harness;\n const userPickedHarness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null =\n bodyHarness === 'ohmypi' || bodyHarness === 'claude-code' || bodyHarness === 'codex' || bodyHarness === 'acp' || bodyHarness === 'kimi-code' ? bodyHarness : null;\n let effectiveHarness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null = null;\n if (userPickedHarness !== null) {\n const harnessDecision = yield* Effect.promise(async () =>\n canUseHarnessSync(userPickedHarness, spawnModel, await getProviderAuthMode(spawnModel))\n );\n // PAN-1837 review fix (NFR-2): an explicitly requested harness that\n // policy denies must fail loudly, not silently substitute claude-code —\n // {harness: 'kimi-code', model: 'claude-sonnet-5'} was previously\n // accepted and spawned claude-code instead of returning the policy\n // reason.\n if (!harnessDecision.allowed) {\n return jsonResponse({ error: harnessDecision.reason ?? `Harness \"${userPickedHarness}\" is not allowed for model \"${spawnModel}\".` }, { status: 400 });\n }\n effectiveHarness = userPickedHarness;\n }\n\n // Spawn pan start command\n const spawnPanCommand = async (args: string[], cwd?: string): Promise<string> => {\n const admitted = await withActiveOrderDispatchReservation(\n projectPath,\n issueId,\n { offBook, recordOverride: false },\n () => spawnAfterClearingStartGates({\n agentSessionName,\n gate: gatesCommitted ? null : startGateBlock,\n initialState: initialAgentState,\n spawn: () => spawnPanCommandDetached({\n agentSessionName,\n issueId,\n role,\n workspacePath,\n args,\n cwd,\n env: {\n OVERDECK_AGENT_STARTED_BY: startedBy,\n OVERDECK_AUTO_SPAWN_CONSENT_REQUIRED: autoSpawnConsentRequired ? '1' : '0',\n },\n }),\n }),\n );\n if (!admitted.check.decision.eligible || !admitted.result) {\n throw new OrderDispatchReservationError(admitted.check);\n }\n await commitClearedGates();\n return admitted.result;\n };\n\n // Use IssueLifecycle service to transition issue to \"In Progress\" (PAN-449)\n const updateIssueStatus = async () => {\n await Effect.runPromise(\n lifecycle.transitionTo(issueId, 'in_progress').pipe(Effect.catch(() => Effect.void))\n );\n };\n\n const containerResponse = yield* handleContainerOrchestration({\n issueId,\n workspacePath,\n devScript,\n agentSessionName,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n spawnModel,\n spawnGuardrails,\n projectPath,\n eventStore,\n spawnPanCommand,\n markWorkStartAccepted,\n updateIssueStatus,\n });\n if (containerResponse) return containerResponse;\n\n // Containers already ready or no containers needed. Claim the spawn before\n // launching `pan start`: two requests can pass the lifecycle read together,\n // but only one may atomically write the starting placeholder.\n const placeholderStartedAt = new Date().toISOString();\n const { state: placeholderState, event: placeholderEvent } = buildAgentStartPlaceholder({\n agentSessionName,\n issueId,\n workspacePath,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n startedAt: placeholderStartedAt,\n });\n const hasLiveTmuxSession = yield* sessionExists(agentSessionName).pipe(\n Effect.catch(() => Effect.succeed(true)),\n );\n const placeholderClaim = yield* claimAgentStartPlaceholderProgram(\n placeholderState,\n placeholderEvent,\n hasLiveTmuxSession,\n );\n if (!placeholderClaim.claimed) {\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_placeholder_blocked', {\n issueId,\n role,\n workspacePath,\n reason: placeholderClaim.reason,\n }));\n return jsonResponse({\n error: `Agent ${agentSessionName} is already starting or running.`,\n code: 'AGENT_START_IN_FLIGHT',\n }, { status: 409 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_placeholder_created', {\n issueId,\n role,\n workspacePath,\n startedAt: placeholderStartedAt,\n }));\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.work_spawn_requested', {\n issueId,\n role,\n workspacePath,\n }));\n\n let activityId: string;\n try {\n emitStartAgentPhase(issueId, 'spawn', 'start', 'starting local work agent', { workspacePath });\n activityId = yield* Effect.promise(() => spawnPanCommand(\n buildPanStartArgs({\n issueId,\n model: spawnModel,\n harness: effectiveHarness,\n allowHost,\n offBook,\n }),\n workspacePath,\n ));\n yield* Effect.promise(markWorkStartAccepted);\n emitStartAgentPhase(issueId, 'spawn', 'success', 'local work agent spawn requested', {\n workspacePath,\n activityId,\n });\n } catch (error: any) {\n const initialStateIsPlaceholder = initialAgentState?.model.startsWith('pending-') === true;\n const fallbackState: AgentState = initialAgentState && !initialStateIsPlaceholder\n ? { ...initialAgentState }\n : {\n ...placeholderState,\n model: spawnModel,\n status: 'stopped',\n stoppedAt: new Date().toISOString(),\n };\n const rolledBack = yield* rollbackAgentStartPlaceholderProgram(placeholderState, fallbackState, {\n type: 'agent.status_changed',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: agentSessionName,\n status: fallbackState.status,\n previousStatus: 'starting',\n hasLiveTmuxSession: false,\n },\n });\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_placeholder_rollback', {\n issueId,\n rolledBack,\n fallbackStatus: fallbackState.status,\n }));\n invalidateAgentsCache();\n\n if (error instanceof OrderDispatchReservationError) {\n const conflict = orderDispatchConflict(error.check.decision)!;\n return jsonResponse(conflict.body, { status: conflict.status });\n }\n const output = String(error?.output ?? error?.message ?? '');\n if (output.includes(`Workspace docker stack for ${issueId}`) && output.includes('is not healthy')) {\n const failedStackHealth = yield* getWorkspaceStackHealth(issueId, { projectConfig, workspacePath });\n emitStartAgentPhase(issueId, 'stackHealthGate', 'failure', failedStackHealth.reasons.length > 0 ? failedStackHealth.reasons.join('; ') : output.trim(), {\n workspacePath,\n activityId: error?.activityId,\n });\n emitStartAgentPhase(issueId, 'spawn', 'failure', output.trim() || `Failed to start agent for ${issueId}`, {\n workspacePath,\n activityId: error?.activityId,\n });\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'error',\n issueId: issueId.toUpperCase(),\n message: `agent-spawn-blocked-stack-unhealthy: ${issueId.toUpperCase()}`,\n details: failedStackHealth.reasons.length > 0 ? failedStackHealth.reasons.join('; ') : output.trim(),\n });\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: failedStackHealth.reasons.length > 0\n ? `Workspace docker stack for ${issueId} is not healthy: ${failedStackHealth.reasons.join('; ')}`\n : output.trim(),\n hint: `Run 'pan workspace rebuild ${issueId}' or use the CLI break-glass path: pan start ${issueId} --host.`,\n stackHealth: failedStackHealth,\n activityId: error?.activityId,\n }, { status: 422 });\n }\n emitStartAgentPhase(issueId, 'spawn', 'failure', output.trim() || `Failed to start agent for ${issueId}`, {\n workspacePath,\n activityId: error?.activityId,\n });\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: output.trim() || `Failed to start agent for ${issueId}`,\n activityId: error?.activityId,\n }, { status: 500 });\n }\n\n updateRegistryForAgentStart(issueId, workspacePath, agentSessionName);\n yield* Effect.promise(() => updateIssueStatus());\n try { getIssueDataService().patchIssue(issueId, { status: 'In Progress', canonicalStatus: 'in_progress' }); } catch { /* non-fatal */ }\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n message: `Starting agent for ${issueId}`,\n activityId,\n projectPath,\n guardrails: spawnGuardrails,\n });\n })),\n);\n","/**\n * Agents route barrel — Effect HttpRouter.Layer (PAN-428 B7)\n *\n * Composes all /api/agents/* route layers and preserves the public helper\n * exports that external callers historically imported from routes/agents.js.\n */\n\nimport { Layer } from 'effect';\n\nimport {\n getAgentsRoute,\n getAgentGitInfoRoute,\n getAgentTmuxAliveRoute,\n getAgentHasSessionRoute,\n} from './agents/listing.js';\nimport {\n getAgentOutputRoute,\n getAgentConversationRoute,\n getAgentActivityRoute,\n getAgentFilesRoute,\n getAgentTimelineRoute,\n} from './agents/conversation.js';\nimport {\n postAgentMessageRoute,\n postAgentTellRoute,\n postAgentPokeRoute,\n} from './agents/messaging.js';\nimport {\n getAgentPendingQuestionsRoute,\n getAgentPaneChoiceRoute,\n postAgentPaneChoiceRoute,\n postAgentAnswerQuestionRoute,\n postAgentPlanActionRoute,\n postInternalAgentPermissionRequestRoute,\n postAgentPermissionResponseRoute,\n} from './agents/permissions.js';\nimport {\n getAgentHealthHistoryRoute,\n postAgentHeartbeatRoute,\n postAgentWorkCompleteRoute,\n postAgentStuckRoute,\n postAgentClassifyCompletionRoute,\n postAgentPlanChecklistRoute,\n getAgentRuntimeRoute,\n} from './agents/runtime-events.js';\nimport {\n deleteAgentRoute,\n postAgentStopRoute,\n postAgentSuspendRoute,\n postAgentPauseRoute,\n postAgentUnpauseRoute,\n postAgentUntroubledRoute,\n} from './agents/lifecycle-stop.js';\nimport {\n postAgentResumeRoute,\n postAgentRecoverRoute,\n postAgentRestartRoute,\n postAgentRestartFreshRoute,\n postAgentsRestartAllRoute,\n postAgentResetSessionRoute,\n getAgentsRestartConfigRoute,\n postAgentsRestartWithConfigRoute,\n} from './agents/lifecycle-restart.js';\nimport {\n getAgentCloisterHealthRoute,\n getAgentHandoffSuggestionRoute,\n postAgentHandoffRoute,\n getAgentCostRoute,\n postAgentDeliveryMethodRoute,\n postAgentSwitchModelRoute,\n} from './agents/control.js';\nimport { postAgentsRoute } from './agents/spawn.js';\n\nexport {\n buildPanStartArgs,\n spawnPanCommandDetached,\n validateAgentRuntimeEventAuth,\n invalidateAgentsCache,\n evaluateAgentStartGate,\n hasActiveAgentGateOrRetry,\n evaluateSpawnGuardrails,\n} from './agents/shared.js';\n\nexport type {\n SpawnGuardrailDecision,\n AgentStartGateDecision,\n} from './agents/shared.js';\n\nexport {\n agentHasResolvableWorkspace,\n UNRESOLVABLE_AGENT_GIT_INFO,\n} from './agents/listing.js';\nexport { buildConversationResponse } from './agents/conversation.js';\nexport { validateAgentMessageOrigin } from './agents/messaging.js';\nexport { createAgentStopHandler } from './agents/lifecycle-stop.js';\nexport { validateAgentDeliveryMethodOrigin } from './agents/control.js';\n\nexport const agentsRouteLayer = Layer.mergeAll(\n getAgentsRoute,\n getAgentOutputRoute,\n getAgentConversationRoute,\n postAgentMessageRoute,\n postAgentTellRoute,\n deleteAgentRoute,\n postAgentStopRoute,\n getAgentHealthHistoryRoute,\n postAgentPokeRoute,\n getAgentPendingQuestionsRoute,\n getAgentPaneChoiceRoute,\n postAgentPaneChoiceRoute,\n postAgentAnswerQuestionRoute,\n postAgentPlanActionRoute,\n postAgentHeartbeatRoute,\n postAgentWorkCompleteRoute,\n postAgentStuckRoute,\n postAgentClassifyCompletionRoute,\n postAgentPlanChecklistRoute,\n postInternalAgentPermissionRequestRoute,\n postAgentPermissionResponseRoute,\n getAgentRuntimeRoute,\n getAgentGitInfoRoute,\n getAgentActivityRoute,\n getAgentFilesRoute,\n getAgentTimelineRoute,\n postAgentSuspendRoute,\n postAgentPauseRoute,\n postAgentUnpauseRoute,\n postAgentUntroubledRoute,\n postAgentResumeRoute,\n postAgentRecoverRoute,\n postAgentRestartRoute,\n getAgentCloisterHealthRoute,\n getAgentHandoffSuggestionRoute,\n postAgentHandoffRoute,\n getAgentCostRoute,\n postAgentsRoute,\n postAgentsRestartAllRoute,\n getAgentTmuxAliveRoute,\n getAgentHasSessionRoute,\n postAgentResetSessionRoute,\n postAgentSwitchModelRoute,\n postAgentRestartFreshRoute,\n postAgentDeliveryMethodRoute,\n getAgentsRestartConfigRoute,\n postAgentsRestartWithConfigRoute,\n);\n\nexport default agentsRouteLayer;\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { decideEscalation, type EscalationAction } from '../../../lib/agents/tier-escalation.js';\nimport { resolveTieredExecutionEnabled } from '../../../lib/agents/tier-table.js';\nimport { loadConfigSync } from '../../../lib/config-yaml.js';\nimport { resolveProjectFromIssueSync } from '../../../lib/projects.js';\nimport { getReviewStatusSync } from '../../../lib/review-status.js';\nimport {\n readTierOverrides,\n readWorkspacePlanSync,\n recordTierPromotion,\n} from '../../../lib/xbrief/io.js';\n\nexport interface TieredInspectFailureEscalationDeps {\n loadConfig?: typeof loadConfigSync;\n resolveProject?: typeof resolveProjectFromIssueSync;\n exists?: typeof existsSync;\n readPlan?: typeof readWorkspacePlanSync;\n readOverrides?: typeof readTierOverrides;\n decide?: typeof decideEscalation;\n recordPromotion?: typeof recordTierPromotion;\n}\n\nexport function handleTieredInspectFailureEscalation(\n issueId: string,\n notes: string | undefined,\n deps: TieredInspectFailureEscalationDeps = {},\n): EscalationAction | null {\n const loadConfig = deps.loadConfig ?? loadConfigSync;\n const resolveProject = deps.resolveProject ?? resolveProjectFromIssueSync;\n const exists = deps.exists ?? existsSync;\n const readPlan = deps.readPlan ?? readWorkspacePlanSync;\n const readOverrides = deps.readOverrides ?? readTierOverrides;\n const decide = deps.decide ?? decideEscalation;\n const recordPromotion = deps.recordPromotion ?? recordTierPromotion;\n\n const tiered = loadConfig().config.tieredExecution;\n if (!tiered.escalation.enabled) return null;\n\n const project = resolveProject(issueId);\n if (!project) return null;\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${issueId.toLowerCase()}`,\n );\n if (!exists(workspacePath)) return null;\n\n const doc = readPlan(workspacePath);\n if (!resolveTieredExecutionEnabled(tiered, doc?.plan.metadata)) return null;\n\n const taskId = notes?.match(/[Bb]ead\\s+(\\S+)/)?.[1];\n if (!doc || !taskId) return null;\n const item = doc.plan.items.find(candidate => candidate.id === taskId);\n if (!item) return null;\n\n const overrides = readOverrides(workspacePath);\n const decision = decide({\n kind: 'supervisor-blocked',\n itemId: taskId,\n sha: getReviewStatusSync(issueId)?.reviewedAtCommit ?? 'unknown',\n attemptsAtCurrentTier: tiered.escalation.retries_at_tier,\n }, item, tiered.escalation, overrides);\n\n if (decision.action === 'promote') {\n recordPromotion(workspacePath, taskId, decision.from, decision.to, decision.reason);\n }\n\n return decision;\n}\n\nexport async function reportTieredInspectFailureEscalation(\n issueId: string,\n notes: string | undefined,\n deps: TieredInspectFailureEscalationDeps = {},\n): Promise<void> {\n try {\n const decision = handleTieredInspectFailureEscalation(issueId, notes, deps);\n if (decision) {\n console.log(`[specialists/done] Tier escalation decision for ${issueId}: ${decision.action}`);\n }\n } catch (err) {\n console.warn(`[specialists/done] Tier escalation handling failed for ${issueId}: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n","import { exec, execFile } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\n\nimport { Effect } from 'effect';\nimport { HttpServerRequest } from 'effect/unstable/http';\n\nimport {\n type AgentRuntimeState,\n type AgentState,\n} from '../../../../lib/agents.js';\nimport { createInFlightGuard } from '../../../../lib/cloister/in-flight-guard.js';\nimport type { VerifiedMergedRepo } from '../../../../lib/cloister/merge-verification.js';\nimport { parseIssueIdSync } from '../../../../lib/issue-id.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\n\nexport const execAsync = promisify(exec);\nexport const execFileAsync = promisify(execFile);\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nexport type SpecialistAgentName = 'merge-agent' | 'review-agent' | 'test-agent' | 'inspect-agent' | 'uat-agent';\nexport type ProjectSpecialistAgentName = 'review-agent' | 'test-agent' | 'merge-agent';\nexport type SpecialistEventRole = 'review' | 'test' | 'ship';\n\nexport function validateSpecialistAgentName(type: string): type is ProjectSpecialistAgentName {\n return type === 'review-agent' || type === 'test-agent' || type === 'merge-agent';\n}\n\nexport function specialistEventRole(name: string): SpecialistEventRole | undefined {\n if (name === 'review' || name === 'review-agent') return 'review';\n if (name === 'test' || name === 'test-agent') return 'test';\n if (name === 'merge' || name === 'merge-agent') return 'ship';\n return undefined;\n}\n\nexport function specialistNameForRole(role: string | undefined): ProjectSpecialistAgentName | null {\n const baseRole = role?.split('.')[0];\n if (baseRole === 'review') return 'review-agent';\n if (baseRole === 'test') return 'test-agent';\n if (baseRole === 'ship') return 'merge-agent';\n return null;\n}\n\nexport type SpecialistAutoCompleteBody = {\n agentId?: string;\n issueId?: string;\n role?: string;\n sessionId?: string | null;\n status?: string;\n notes?: string;\n};\n\nexport function validateSpecialistAutoCompleteMetadata(\n name: string,\n body: SpecialistAutoCompleteBody,\n agentState: AgentState | null,\n runtimeState: AgentRuntimeState | null,\n): { ok: true } | { ok: false; status: number; error: string } {\n if (!validateSpecialistAgentName(name)) {\n return { ok: false, status: 400, error: `Invalid specialist name: ${name}` };\n }\n if (!body.issueId || !body.status) {\n return { ok: false, status: 400, error: 'issueId and status required' };\n }\n if (body.status !== 'passed' && body.status !== 'failed') {\n return { ok: false, status: 400, error: 'status must be passed or failed' };\n }\n if (!body.agentId || !body.role) {\n return { ok: false, status: 400, error: 'agentId and role required' };\n }\n\n const normalizedIssueId = body.issueId.toUpperCase();\n const expectedName = specialistNameForRole(body.role);\n if (expectedName !== name) {\n return { ok: false, status: 403, error: 'role does not match specialist' };\n }\n if (!agentState || agentState.id !== body.agentId) {\n return { ok: false, status: 403, error: 'agent run not found' };\n }\n if (agentState.issueId.toUpperCase() !== normalizedIssueId) {\n return { ok: false, status: 403, error: 'agent issue does not match request' };\n }\n if (specialistNameForRole(agentState.role) !== name) {\n return { ok: false, status: 403, error: 'agent role does not match specialist' };\n }\n if (agentState.status !== 'running' && agentState.status !== 'starting') {\n return { ok: false, status: 409, error: 'agent run is not active' };\n }\n if (runtimeState?.currentIssue && runtimeState.currentIssue.toUpperCase() !== normalizedIssueId) {\n return { ok: false, status: 403, error: 'runtime issue does not match request' };\n }\n if (runtimeState?.claudeSessionId && body.sessionId !== runtimeState.claudeSessionId) {\n return { ok: false, status: 403, error: 'session does not match active run' };\n }\n\n return { ok: true };\n}\n\n// Read the request body as unknown JSON\nexport const readJsonBody = Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const text = yield* request.text;\n try {\n return text ? JSON.parse(text) : {};\n } catch {\n return {};\n }\n});\n\n// ─── Idempotency guard: prevent concurrent postMergeLifecycle re-entry ────────\n// PAN-328: the loop specialists/done → onMergeComplete → postMergeLifecycle →\n// (re-trigger) → specialists/done once burned 24,626 tracker API calls. The\n// in-flight guard makes a second *concurrent* call for the same issue a no-op.\n// The invariant is enforced by tests/unit/lib/cloister/in-flight-guard.test.ts\n// — delete the guard and that suite goes red.\nconst postMergeGuard = createInFlightGuard();\n\n// Track issues where the server is managing the merge lifecycle (polyrepo).\n// Exported so the workspaces route can register/unregister server-managed merges.\nexport const _serverManagedMerges = new Set<string>();\n\n/**\n * Exported for the UAT batch-promote route (PAN-1737): batch promotion fans\n * out the per-member post-merge through THIS guard instance so an issue's\n * lifecycle still runs at most once regardless of which path merged it.\n * Returns false when a run for the issue is already in flight.\n */\nexport interface FirePostMergeLifecycleOptions {\n sourceBranch?: string;\n verifiedMergedRef?: string;\n /**\n * Per-repo merge evidence from a polyrepo batch promotion (PAN-3093). It must\n * be forwarded: a polyrepo member's single-ref evidence is a composite anchor\n * checked against origin/main in the WRAPPER path, which is not a git repo,\n * so dropping this silently refuses every member's post-merge transition even\n * though all repos landed.\n */\n verifiedMergedRepos?: readonly VerifiedMergedRepo[];\n}\n\nexport function firePostMergeLifecycle(issueId: string, options?: FirePostMergeLifecycleOptions): boolean {\n const parsedIssueId = parseIssueIdSync(issueId.trim());\n if (!parsedIssueId) {\n console.error(`[merge] Refusing post-merge lifecycle for invalid issue ID: ${issueId}`);\n return false;\n }\n const normalizedIssueId = parsedIssueId.normalized.toUpperCase();\n\n const started = postMergeGuard.run(\n normalizedIssueId,\n async () => {\n const projectPath = getProjectPathForIssue(parsedIssueId.prefix);\n const { postMergeLifecycle } = await import('../../../../lib/cloister/merge-agent.js');\n const lifecycleOptions = {\n ...(options?.verifiedMergedRef ? { verifiedMergedRef: options.verifiedMergedRef } : {}),\n ...(options?.verifiedMergedRepos?.length ? { verifiedMergedRepos: options.verifiedMergedRepos } : {}),\n };\n await postMergeLifecycle(\n normalizedIssueId,\n projectPath,\n options?.sourceBranch,\n Object.keys(lifecycleOptions).length > 0 ? lifecycleOptions : undefined,\n );\n console.log(`[merge] post-merge lifecycle completed for ${normalizedIssueId}`);\n\n // PAN-1691: roll the merge train — rebase ready siblings onto the new main,\n // re-verify the clean ones, agent-resolve conflicts. No-op unless the\n // flywheel.merge_train_enabled flag is on. Runs inside the in-flight guard,\n // so it cannot re-enter postMergeLifecycle for this issue.\n const { runMergeTrainReconcile } = await import('../../../../lib/cloister/merge-train.js');\n const outcomes = await runMergeTrainReconcile(normalizedIssueId);\n if (outcomes.length > 0) {\n console.log(\n `[merge-train] reconciled ${outcomes.length} sibling(s) after ${normalizedIssueId}: ` +\n outcomes.map((o) => `${o.issueId}=${o.result}`).join(', '),\n );\n }\n },\n (err) => console.error(`[merge] post-merge lifecycle failed for ${normalizedIssueId}:`, err),\n );\n if (!started) {\n console.log(`[merge] firePostMergeLifecycle: skipping ${normalizedIssueId} — already in flight`);\n }\n return started;\n}\n\nexport function getProjectPathForIssue(issuePrefix: string): string {\n const issueId = `${issuePrefix}-1`;\n const resolved = resolveProjectFromIssueSync(issueId);\n if (resolved) return resolved.projectPath;\n return join(homedir(), 'Projects');\n}\n","import { existsSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect, Layer } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { getAgentState, getAgentRuntimeState, messageAgent, saveAgentRuntimeState, transitionIssueToInProgress } from '../../../../lib/agents.js';\nimport { getUnblockedItemsSync } from '../../../../lib/cloister/task-readiness.js';\nimport { recordReviewVerdict } from '../../../../lib/cloister/review-verdict-writer.js';\nimport type { HeadAnchor } from '../../../../lib/git-utils.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { getReviewStatusSync, loadReviewStatuses, setReviewStatusSync as setReviewStatusBase, type ReviewStatus, type ReviewStatusUpdate } from '../../../../lib/review-status.js';\nimport { readWorkspacePlanSync } from '../../../../lib/xbrief/io.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport { validateAgentRuntimeEventAuth } from '../agents.js';\nimport { httpHandler } from '../http-handler.js';\nimport { reportTieredInspectFailureEscalation } from '../tiered-inspect-escalation.js';\nimport { killSession } from '../../../../lib/tmux.js';\nimport {\n _serverManagedMerges,\n execFileAsync,\n firePostMergeLifecycle,\n readJsonBody,\n specialistEventRole,\n validateSpecialistAutoCompleteMetadata,\n type SpecialistAgentName,\n type SpecialistAutoCompleteBody,\n} from './shared.js';\n\n// ─── Route: GET /api/specialists ─────────────────────────────────────────────\n\nconst getSpecialistsRoute = HttpRouter.add(\n 'GET',\n '/api/specialists',\n httpHandler(Effect.gen(function* () {\n const {\n getAllSpecialistStatus,\n getAllProjectSpecialistStatuses,\n } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n\n const legacySpecialists = yield* Effect.promise(() => getAllSpecialistStatus());\n const projectSpecialists = yield* Effect.promise(() => getAllProjectSpecialistStatuses());\n\n return jsonResponse({\n specialists: legacySpecialists,\n projects: projectSpecialists,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/reset-all ───────────────────────────────────\n// NOTE: Must be registered before /:name/reset to avoid \"reset-all\" matching as :name\n\nconst postSpecialistsResetAllRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/reset-all',\n httpHandler(Effect.gen(function* () {\n const {\n getAllSpecialists,\n isRunning,\n getTmuxSessionName,\n } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const { clearHookSync } = yield* Effect.promise(() => import('../../../../lib/hooks.js'));\n\n const specialists = getAllSpecialists();\n const results: { name: string; killed: boolean; sessionCleared: boolean; queueCleared: boolean }[] = [];\n\n for (const specialist of specialists) {\n const name = specialist.name;\n let killed = false;\n\n if (yield* Effect.promise(() => isRunning(name))) {\n const tmuxSession = getTmuxSessionName(name);\n const killResult = yield* killSession(tmuxSession).pipe(\n Effect.as(true),\n Effect.catch(() => Effect.succeed(false)),\n );\n killed = killResult;\n }\n\n clearHookSync(name);\n results.push({ name, killed, sessionCleared: false, queueCleared: true });\n }\n\n // Reset any \"reviewing\" statuses to \"pending\" — use per-issue atomic updates\n // to avoid the read-all/write-all race that saveReviewStatuses() would reintroduce.\n let reviewStatusesReset = 0;\n try {\n const statuses = loadReviewStatuses();\n for (const key of Object.keys(statuses)) {\n if (statuses[key].reviewStatus === 'reviewing') {\n setReviewStatusBase(key, { reviewStatus: 'pending' });\n reviewStatusesReset++;\n }\n }\n } catch (e) {\n console.error('Failed to reset review statuses:', e);\n }\n\n return jsonResponse({\n success: true,\n message: `Reset ${results.length} specialists, reset ${reviewStatusesReset} review statuses`,\n results,\n reviewStatusesReset,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/done ───────────────────────────────────────\n// CRITICAL: This endpoint has idempotency guards — see CLAUDE.md.\n// Must be registered before /:name/* routes to prevent \"done\" matching as :name.\n\nconst postSpecialistsDoneRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/done',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const { specialist, issueId, status, notes, itemId, runId } = body as {\n specialist: string;\n issueId: string;\n status: string;\n notes?: string;\n itemId?: string;\n runId?: string;\n };\n\n // Validate specialist type\n const validSpecialists = ['review', 'test', 'merge', 'inspect', 'uat', 'ship'];\n if (!validSpecialists.includes(specialist)) {\n return jsonResponse(\n { error: `Invalid specialist: ${specialist}. Valid: ${validSpecialists.join(', ')}` },\n { status: 400 },\n );\n }\n\n // Validate status\n if (!status || !['passed', 'failed'].includes(status)) {\n return jsonResponse(\n { error: `Invalid status: ${status}. Must be 'passed' or 'failed'` },\n { status: 400 },\n );\n }\n\n // Validate issueId\n if (!issueId) {\n return jsonResponse({ error: 'issueId is required' }, { status: 400 });\n }\n\n const normalizedIssueId = issueId.toUpperCase();\n\n if (specialist === 'inspect' && status === 'passed') {\n if (!itemId) {\n return jsonResponse({ error: 'itemId is required for a passed inspect verdict' }, { status: 400 });\n }\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n const workspacePath = project && join(project.projectPath, 'workspaces', `feature-${normalizedIssueId.toLowerCase()}`);\n const plan = workspacePath ? readWorkspacePlanSync(workspacePath) : undefined;\n if (!plan?.plan.items.some(item => item.id === itemId)) {\n return jsonResponse({ error: `Item \"${itemId}\" does not exist in the xBRIEF for ${normalizedIssueId}` }, { status: 400 });\n }\n }\n console.log(`[specialists/done] ${specialist} signaling ${status} for ${normalizedIssueId}`);\n\n // Resolve any pending specialist completion waiters (PAN-632: event-driven completion).\n // This replaces polling loops in spawnMergeAgentForBranches / syncMainIntoWorkspace.\n if (specialist === 'merge') {\n const { reportSpecialistCompletion } = yield* Effect.promise(() => import('../../../../lib/cloister/specialist-completion.js'));\n const resolved = yield* reportSpecialistCompletion(normalizedIssueId, {\n status: status as 'passed' | 'failed',\n notes,\n });\n if (resolved) {\n console.log(`[specialists/done] Resolved pending completion waiter for ${normalizedIssueId}`);\n }\n }\n\n // GUARD: If this issue is in a server-managed merge (polyrepo), the server handles\n // the merge lifecycle. Acknowledge the agent's call but do NOT trigger onMergeComplete.\n if (specialist === 'merge' && _serverManagedMerges.has(normalizedIssueId)) {\n console.log(`[specialists/done] ${normalizedIssueId} is server-managed merge — acknowledging without triggering lifecycle`);\n return jsonResponse({\n success: true,\n specialist,\n issueId: normalizedIssueId,\n status,\n notes,\n serverManaged: true,\n });\n }\n\n // Build the update based on specialist type\n const update: ReviewStatusUpdate = {};\n let reviewEvidenceHead: HeadAnchor | undefined;\n\n switch (specialist) {\n case 'review':\n // Passed verdicts need the current workspace head for the write-door check.\n // Blocked verdicts stay durable-first and take their anchor after feedback.\n if (status === 'passed') {\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n const { snapshotWorkspaceHeadsPromise } = await import('../../../../lib/git-utils.js');\n reviewEvidenceHead = await snapshotWorkspaceHeadsPromise(normalizedIssueId, workspacePath);\n }\n }\n } catch {\n // Non-fatal; recordReviewVerdict will proceed without evidenceHead\n }\n });\n }\n update.reviewStatus = status === 'passed' ? 'passed' : 'blocked';\n if (notes) update.reviewNotes = notes;\n break;\n\n case 'test':\n update.testStatus = status as typeof update.testStatus;\n if (notes) update.testNotes = notes;\n break;\n\n case 'merge':\n update.mergeStatus = status === 'passed' ? 'merged' : 'failed';\n break;\n\n case 'inspect':\n update.inspectStatus = status as typeof update.inspectStatus;\n if (notes) update.inspectNotes = notes;\n break;\n\n case 'uat':\n update.uatStatus = status as typeof update.uatStatus;\n if (notes) update.uatNotes = notes;\n if (status === 'passed') {\n update.readyForMerge = true;\n }\n break;\n\n case 'ship':\n if (status === 'passed') {\n update.readyForMerge = true;\n }\n break;\n }\n\n // Apply the update (triggers side effects like idle state, queue processing)\n // For review verdicts, route through recordReviewVerdict (PAN-3512)\n let updatedStatus: ReviewStatus;\n if (specialist === 'review' && (status === 'passed' || status === 'blocked' || status === 'failed')) {\n const verdictOutcome = yield* Effect.promise(() => recordReviewVerdict(normalizedIssueId, {\n verdict: status === 'passed' ? 'passed' : 'blocked',\n notes,\n evidenceHead: reviewEvidenceHead,\n writer: 'coordinator',\n }));\n if (!verdictOutcome.landed) {\n // Verdict was rejected (stale evidence) — return error\n return jsonResponse(\n { error: `Verdict rejected: ${verdictOutcome.reason}` },\n { status: 400 },\n );\n }\n updatedStatus = getReviewStatusSync(normalizedIssueId) || ({} as ReviewStatus);\n } else {\n updatedStatus = setReviewStatusBase(normalizedIssueId, update);\n }\n if (specialist === 'inspect' && status === 'failed') {\n yield* Effect.promise(() => reportTieredInspectFailureEscalation(normalizedIssueId, notes));\n // PAN-3078: a blocked verdict must reach the work agent, or it keeps\n // building on rejected work. itemId comes from the supervisor's\n // `pan admin specialists done --item`; legacy ephemeral inspectors omit\n // it, so fall back to the inspectBeadId stamped when inspection started.\n yield* Effect.promise(async () => {\n try {\n const failedItemId = itemId ?? getReviewStatusSync(normalizedIssueId)?.inspectBeadId ?? undefined;\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project && failedItemId) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n const { onInspectComplete } = await import('../../../../lib/cloister/inspect-agent.js');\n await Effect.runPromise(onInspectComplete(project.projectKey, normalizedIssueId, failedItemId, 'failed', workspacePath, notes));\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Error delivering failed inspect verdict for ${normalizedIssueId}:`, err);\n }\n });\n }\n\n // Set specialist state to idle and clear registry write-scope.\n // CRITICAL: No `await` between the mergeStatus write above and the guard check below.\n yield* Effect.promise(async () => {\n try {\n const { getTmuxSessionName, updateRunMetadata, makeSpecialistRegistryKey } =\n await import('../../../../lib/cloister/specialists.js');\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n const projectKey = project?.projectKey;\n const tmuxSession = projectKey\n ? getTmuxSessionName(`${specialist}-agent` as SpecialistAgentName, projectKey, normalizedIssueId)\n : getTmuxSessionName(`${specialist}-agent` as SpecialistAgentName);\n saveAgentRuntimeState(tmuxSession, {\n state: 'idle',\n lastActivity: new Date().toISOString(),\n });\n console.log(`[specialists/done] Set ${tmuxSession} to idle`);\n\n // PAN-2579 (warm-by-default lifecycle): the verdict is recorded; the session\n // stays ALIVE so the next review/test cycle resumes it with context intact.\n // Warm-idle sessions no longer count against the advancing ceiling\n // (countRunningAgents excludes them) and the memory governor sheds them\n // first under HARD pressure — eviction is the governor's job, never a\n // side effect of recording a verdict. (Supersedes the PAN-846 kill and\n // the PAN-2007 keep-alive flag.)\n console.log(`[specialists/done] Warm lifecycle: verdict recorded, leaving ${tmuxSession} running (PAN-2579)`);\n\n // Clear write-scope lock so the next specialist can claim the workspace\n if (projectKey) {\n const registryKey = makeSpecialistRegistryKey(`${specialist}-agent`, normalizedIssueId);\n updateRunMetadata(projectKey, registryKey, {\n currentRun: null,\n writeScope: undefined,\n workspace: null,\n currentActivity: null,\n });\n console.log(`[specialists/done] Cleared registry lock for ${registryKey} (${projectKey})`);\n }\n\n // Update specialist handoff log so success-rate metrics reflect actual outcome\n const { updateSpecialistHandoffStatus } = await import('../../../../lib/cloister/specialist-handoff-logger.js');\n const updated = await Effect.runPromise(updateSpecialistHandoffStatus(\n normalizedIssueId,\n `${specialist}-agent`,\n status === 'passed' ? 'completed' : 'failed',\n status === 'passed' ? 'success' : 'failure',\n ));\n if (updated) {\n console.log(`[specialists/done] Updated handoff log: ${specialist}-agent ${normalizedIssueId} → ${status}`);\n }\n } catch (err) {\n console.error(`[specialists/done] Error managing specialist state:`, err);\n }\n });\n\n // When review passes, snapshot the current HEAD commit so we can detect\n // if the agent makes new commits before merge (which invalidates the review).\n if (specialist === 'review' && status === 'passed') {\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n const { formatAnchorShort, snapshotWorkspaceHeadsPromise } = await import('../../../../lib/git-utils.js');\n const headAnchor = await snapshotWorkspaceHeadsPromise(normalizedIssueId, workspacePath);\n if (headAnchor) {\n setReviewStatusBase(normalizedIssueId, { reviewedAtCommit: headAnchor });\n console.log(`[specialists/done] Snapshotted reviewedAtCommit=${formatAnchorShort(headAnchor)} for ${normalizedIssueId}`);\n }\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Failed to snapshot reviewedAtCommit for ${normalizedIssueId}:`, err);\n }\n });\n }\n\n // When inspect specialist reports success, save checkpoint\n if (specialist === 'inspect' && status === 'passed') {\n yield* Effect.promise(async () => {\n try {\n const { onInspectComplete } = await import('../../../../lib/cloister/inspect-agent.js');\n // Resolve project to get workspace path\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n await Effect.runPromise(onInspectComplete(project.projectKey, normalizedIssueId, itemId!, 'passed', workspacePath, notes));\n\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Error saving inspect checkpoint:`, err);\n }\n });\n }\n\n // When the test specialist reports success, persist testStatus and emit\n // test.passed so reactive Cloister records the shipping lifecycle phase.\n // PAN-1650 derives readyForMerge from review/test gate state server-side;\n // no ship role is spawned.\n if (specialist === 'test' && status === 'passed') {\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n setReviewStatusBase(normalizedIssueId, { testStatus: 'passed' });\n const { initEventStore } = await import('../../event-store.js');\n const store = await initEventStore();\n await store.appendAsync({\n type: 'test.passed',\n timestamp: new Date().toISOString(),\n payload: { issueId: normalizedIssueId },\n } as any);\n console.log(`[specialists/done] ${normalizedIssueId} emitted test.passed; shipping lifecycle recorded`);\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Error emitting test.passed for ${normalizedIssueId}:`, err);\n }\n });\n }\n\n // When merge specialist reports success, run post-merge lifecycle ONCE.\n // Use firePostMergeLifecycle directly rather than onMergeComplete: onMergeComplete\n // has a guard that checks mergeStatus !== 'merged', but setReviewStatusBase above\n // already set mergeStatus='merged' — that guard would always fire and the lifecycle\n // would never run. firePostMergeLifecycle skips that guard and uses the\n // in-flight guard (postMergeGuard, concurrency) + postMergeLifecycle's\n // _completedPostMerge (defense-in-depth).\n if (specialist === 'merge' && status === 'passed') {\n firePostMergeLifecycle(normalizedIssueId);\n }\n\n // When any specialist reports failure, transition issue back to In Progress\n // (inspect failures don't change Linear status — they're mid-implementation gates).\n if (status === 'failed' && specialist !== 'inspect') {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n const wsPath = existsSync(workspacePath) ? workspacePath : undefined;\n transitionIssueToInProgress(normalizedIssueId, wsPath).catch((err: unknown) => {\n const errMsg = err instanceof Error ? err.message : String(err);\n console.warn(\n `[specialists/done] Could not transition ${normalizedIssueId} back to in_progress: ${errMsg}`,\n );\n });\n console.log(\n `[specialists/done] ${specialist} failed → transitioning ${normalizedIssueId} back to In Progress`,\n );\n }\n } catch (err: unknown) {\n const errMsg = err instanceof Error ? err.message : String(err);\n console.warn(`[specialists/done] Could not transition issue back to in_progress:`, errMsg);\n }\n }\n\n // When merge fails, post a comment on the GitHub PR so the failure is visible\n // outside the dashboard, and send feedback to the work agent.\n if (specialist === 'merge' && status === 'failed') {\n yield* Effect.promise(async () => {\n try {\n // Post comment on the PR\n const reviewStatus = loadReviewStatuses()[normalizedIssueId];\n const prUrl = reviewStatus?.prUrl;\n if (prUrl) {\n // Extract owner/repo#number from PR URL\n const prMatch = prUrl.match(/github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/(\\d+)/);\n if (prMatch) {\n const [, owner, repo, prNumber] = prMatch;\n const commentBody = `## Merge Failed\\n\\n${notes || 'Merge could not be completed.'}\\n\\nThe issue has been moved back to In Progress. The work agent needs to resolve conflicts and resubmit.`;\n await execFileAsync(\n 'gh',\n ['api', `repos/${owner}/${repo}/issues/${prNumber}/comments`, '--field', `body=${commentBody}`],\n { encoding: 'utf-8' },\n );\n console.log(`[specialists/done] Posted merge failure comment on ${prUrl}`);\n }\n }\n } catch (err: any) {\n console.warn(`[specialists/done] Failed to post merge failure comment: ${err.message}`);\n }\n });\n\n // If merge failed due to conflicts, send rebase instructions to the work agent.\n if (notes?.toLowerCase().includes('conflict')) {\n yield* Effect.promise(async () => {\n try {\n const workAgentId = `agent-${normalizedIssueId.toLowerCase()}`;\n const { sessionExists } = await import('../../../../lib/tmux.js');\n const { messageAgent, spawnAgent, getAgentStateSync } = await import('../../../../lib/agents.js');\n\n if (await Effect.runPromise(sessionExists(workAgentId))) {\n // Agent is running — send rebase instructions directly\n const rebaseMsg = `MERGE CONFLICT: The merge-agent could not rebase your branch onto main due to conflicts. Please fix this now:\\n\\n1. git fetch origin main\\n2. git rebase origin/main\\n3. Resolve any conflicts (git add <file> && git rebase --continue)\\n4. git push --force-with-lease\\n5. Resubmit: curl -s -X POST http://localhost:3011/api/review/${normalizedIssueId}/request -H \"Content-Type: application/json\" -d \"{}\"\\n\\nConflict details: ${notes}`;\n await messageAgent(workAgentId, rebaseMsg);\n console.log(`[specialists/done] Sent rebase instructions to ${workAgentId}`);\n } else {\n // Agent is stopped — start fresh (don't resume, sessions may be corrupted: PAN-612)\n console.log(`[specialists/done] Work agent ${workAgentId} not running — will need manual restart or next pan start dispatch`);\n }\n } catch (err: any) {\n console.warn(`[specialists/done] Failed to send rebase feedback to work agent: ${err.message}`);\n }\n });\n }\n }\n\n if (specialist === 'review' && (status === 'failed' || status === 'blocked')) {\n // Only deliver feedback if this is a coordinator verdict (not handled by CLI/fallback)\n // The verdict write door has already validated the evidence head\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n const workspacePath = project\n ? join(project.projectPath, 'workspaces', `feature-${normalizedIssueId.toLowerCase()}`)\n : undefined;\n const { deliverReviewVerdictFeedback } = await import(\n '../../../../lib/cloister/review-verdict-feedback.js'\n );\n const result = await Effect.runPromise(deliverReviewVerdictFeedback({\n issueId: normalizedIssueId,\n verdict: status === 'failed' ? 'failed' : 'blocked',\n notes,\n workspacePath,\n prUrl: updatedStatus.prUrl,\n ...(runId ? { runId } : {}),\n }));\n console.log(\n `[specialists/done] Delivered review verdict feedback for ${normalizedIssueId}` +\n ` (feedback=${result.feedbackPath ?? 'none'}, synthesis=${result.synthesisPath ?? 'none'}, prComment=${result.prCommentPosted})`,\n );\n } catch (err: any) {\n console.warn(`[specialists/done] Failed to deliver review verdict feedback: ${err.message}`);\n }\n });\n }\n\n // PAN-3148: the HTTP review prompt reports changes requested as status=failed,\n // which the durable write above maps to reviewStatus=blocked. Snapshot the\n // reviewed HEAD only after feedback delivery so the verdict remains durable\n // even when the git probe stalls or fails (PAN-2524 ordering).\n if (specialist === 'review' && status === 'failed') {\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n const { formatAnchorShort, snapshotWorkspaceHeadsPromise } = await import('../../../../lib/git-utils.js');\n const reviewedAtCommit = await snapshotWorkspaceHeadsPromise(normalizedIssueId, workspacePath);\n if (reviewedAtCommit) {\n setReviewStatusBase(normalizedIssueId, { reviewedAtCommit });\n console.log(`[specialists/done] Snapshotted blocked reviewedAtCommit=${formatAnchorShort(reviewedAtCommit)} for ${normalizedIssueId}`);\n }\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Failed to snapshot blocked reviewedAtCommit for ${normalizedIssueId}:`, err);\n }\n });\n }\n\n // Emit domain event for role-backed specialist completion/failure.\n const eventRole = specialistEventRole(specialist);\n if (eventRole) {\n if (status === 'passed') {\n yield* eventStore.append({\n type: 'specialist.completed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId: normalizedIssueId },\n });\n } else {\n yield* eventStore.append({\n type: 'specialist.failed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId: normalizedIssueId, error: notes || `${specialist} failed` },\n });\n }\n }\n\n return jsonResponse({\n success: true,\n specialist,\n issueId: normalizedIssueId,\n status,\n notes,\n currentStatus: updatedStatus,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/logs/cleanup-all ────────────────────────────\n// NOTE: Must be registered before /:project/:type routes.\n\nconst postSpecialistsLogsCleanupAllRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/logs/cleanup-all',\n httpHandler(Effect.gen(function* () {\n const { cleanupAllLogsSync } = yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n const results = cleanupAllLogsSync();\n\n return jsonResponse({\n success: true,\n totalDeleted: results.totalDeleted,\n byProject: results.byProject,\n message: `Cleaned up ${results.totalDeleted} old logs`,\n });\n })),\n);\n\n// ─── Route: GET /api/specialists/projects ────────────────────────────────────\n// NOTE: Must be registered before /:name routes.\n\nconst getSpecialistsProjectsRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/projects',\n httpHandler(Effect.gen(function* () {\n const { getAllProjectSpecialistStatuses } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const specialists = yield* Effect.promise(() => getAllProjectSpecialistStatuses());\n return jsonResponse(specialists);\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/wake ─────────────────────────────────\n\nconst postSpecialistWakeRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/wake',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n return jsonResponse(\n { error: `Legacy specialist wake is no longer supported for ${name}; role runs spawn agents directly.` },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/reset ─────────────────────────────────\n\nconst postSpecialistResetRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/reset',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n return jsonResponse(\n { error: `Legacy specialist session reset is no longer supported for ${name}; role agents are managed through the normal agent lifecycle.` },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/init ──────────────────────────────────\n\nconst postSpecialistInitRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/init',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n return jsonResponse(\n { error: `Legacy specialist initialization is no longer supported for ${name}; role flows spawn agents on demand.` },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/report-status ────────────────────────\n\nconst postSpecialistReportStatusRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/report-status',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const { issueId, status, notes } = body as {\n issueId?: string;\n status?: string;\n notes?: string;\n };\n\n if (!issueId || !status) {\n return jsonResponse(\n { error: 'issueId and status required' },\n { status: 400 },\n );\n }\n\n if (!['passed', 'blocked', 'failed', 'in-progress'].includes(status)) {\n return jsonResponse(\n { error: 'status must be: passed, blocked, failed, or in-progress' },\n { status: 400 },\n );\n }\n\n // Write status to specialist's state directory\n const specialistDir = join(homedir(), '.overdeck', 'specialists', name);\n yield* Effect.promise(() => mkdir(specialistDir, { recursive: true }));\n\n const statusFile = join(specialistDir, `${issueId}-status.json`);\n const statusData = {\n issueId,\n specialist: name,\n status,\n notes: notes || '',\n timestamp: new Date().toISOString(),\n };\n\n yield* Effect.promise(() => writeFile(statusFile, JSON.stringify(statusData, null, 2)));\n\n console.log(`[specialists] ${name} reported status for ${issueId}: ${status}`);\n\n // When specialist reports completion (passed/blocked/failed), set state to idle\n if (['passed', 'blocked', 'failed'].includes(status)) {\n const { getTmuxSessionName } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const tmuxSession = getTmuxSessionName(name as SpecialistAgentName);\n saveAgentRuntimeState(tmuxSession, {\n state: 'idle',\n lastActivity: new Date().toISOString(),\n });\n }\n\n // Emit domain event based on status\n const eventRole = specialistEventRole(name);\n if (eventRole && status === 'passed') {\n yield* eventStore.append({\n type: 'specialist.completed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId },\n });\n } else if (eventRole && (status === 'failed' || status === 'blocked')) {\n yield* eventStore.append({\n type: 'specialist.failed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId, error: notes || `${name} reported ${status}` },\n });\n }\n\n return jsonResponse({ success: true });\n })),\n);\n\n// ─── Route: GET /api/specialists/:name/cost ───────────────────────────────────\n\nconst getSpecialistCostRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:name/cost',\n httpHandler(Effect.gen(function* () {\n return jsonResponse({ cost: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, model: '' });\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/auto-complete ────────────────────────\n\nconst postSpecialistAutoCompleteRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/auto-complete',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n const body = (yield* readJsonBody) as SpecialistAutoCompleteBody;\n const eventStore = yield* EventStoreService;\n const { issueId: requestIssueId, status: requestStatus, agentId } = body;\n\n const agentState = agentId ? yield* getAgentState(agentId) : null;\n const runtimeState = agentId ? yield* getAgentRuntimeState(agentId) : null;\n const metadata = validateSpecialistAutoCompleteMetadata(name, body, agentState, runtimeState);\n if (!metadata.ok) {\n return jsonResponse({ error: metadata.error }, { status: metadata.status });\n }\n\n const issueId = requestIssueId!;\n const status = requestStatus!;\n const completingAgentId = agentId!;\n\n console.log(`[specialists] Auto-detected completion for ${name}: ${issueId} -> ${status}`);\n\n yield* Effect.promise(() => saveAgentRuntimeState(completingAgentId, {\n state: 'idle',\n lastActivity: new Date().toISOString(),\n currentIssue: undefined,\n }));\n\n // Update review/test status based on specialist type\n const existingStatus = getReviewStatusSync(issueId);\n\n if (name === 'review-agent') {\n const alreadyReported =\n existingStatus?.reviewNotes &&\n !existingStatus.reviewNotes.startsWith('Auto-detected:');\n if (alreadyReported) {\n console.log(\n `[specialists] Skipping auto-detect for ${name}/${issueId}: specialist already reported (${existingStatus!.reviewStatus})`,\n );\n } else {\n setReviewStatusBase(issueId, {\n reviewStatus: status === 'passed' ? 'passed' : 'blocked',\n reviewNotes: `Auto-detected: ${status}`,\n });\n }\n\n if ((alreadyReported ? existingStatus!.reviewStatus : status === 'passed' ? 'passed' : 'blocked') === 'passed') {\n console.log(`[specialists] ${issueId} review approved; reactive Cloister will dispatch the test role`);\n }\n } else if (name === 'test-agent') {\n const alreadyReported =\n existingStatus?.testNotes && !existingStatus.testNotes.startsWith('Auto-detected:');\n if (alreadyReported) {\n console.log(\n `[specialists] Skipping auto-detect for ${name}/${issueId}: specialist already reported (${existingStatus!.testStatus})`,\n );\n } else {\n const testPassed = status === 'passed';\n setReviewStatusBase(issueId, {\n testStatus: testPassed ? 'passed' : 'failed',\n testNotes: `Auto-detected: ${status}`,\n });\n if (testPassed) {\n // Emit test.passed so reactive Cloister records the shipping\n // lifecycle phase; readyForMerge is derived server-side.\n yield* eventStore.append({\n type: 'test.passed',\n timestamp: new Date().toISOString(),\n payload: { issueId },\n });\n console.log(`[specialists] ${issueId} emitted test.passed after auto-detected test pass`);\n }\n }\n }\n\n const eventRole = specialistEventRole(name);\n if (eventRole) {\n yield* eventStore.append({\n type: 'specialist.completed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId },\n });\n }\n\n return jsonResponse({\n success: true,\n status,\n issueId,\n });\n })),\n);\n\nexport const specialistsLegacyRouteLayer = Layer.mergeAll(\n getSpecialistsRoute,\n getSpecialistsProjectsRoute,\n postSpecialistsResetAllRoute,\n postSpecialistsDoneRoute,\n postSpecialistsLogsCleanupAllRoute,\n postSpecialistWakeRoute,\n postSpecialistResetRoute,\n postSpecialistInitRoute,\n postSpecialistReportStatusRoute,\n getSpecialistCostRoute,\n postSpecialistAutoCompleteRoute,\n);\n","import { existsSync } from 'node:fs';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect, Layer, Option, Stream } from 'effect';\nimport { HttpRouter, HttpServerRequest, HttpServerResponse } from 'effect/unstable/http';\n\nimport { getClaudePermissionFlagsStringSync } from '../../../../lib/claude-permissions.js';\nimport { normalizeModelName } from '../../../../lib/cost-parsers/jsonl-parser.js';\nimport { calculateCostSync, getPricingSync, type TokenUsage } from '../../../../lib/cost.js';\nimport { loadConfigSync, resolveModel } from '../../../../lib/config-yaml.js';\nimport { encodeClaudeProjectDir } from '../../../../lib/paths.js';\nimport { resolvePrimaryWorkspaceRepoDirSync, resolveWorkspaceRepoRootsSync } from '../../../../lib/project-repos.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { getReviewStatusSync } from '../../../../lib/review-status.js';\nimport { getAgentCommandSync } from '../../../../lib/settings.js';\nimport { killSession } from '../../../../lib/tmux.js';\nimport { getAgentStateSync, saveAgentRuntimeState } from '../../../../lib/agents.js';\nimport { REVIEW_SUB_ROLES, type ReviewSubRole } from '../../../../lib/cloister/review-monitor.js';\nimport { resolveReviewParentRunState } from '../../../../lib/cloister/review-run-recovery.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { execAsync, readJsonBody, validateSpecialistAgentName } from './shared.js';\n\n// ─── Route: GET /api/specialists/:project/:issueId/:type/status ───────────────\n//\n// PAN-1048 review feedback 003 (REQ-16): the legacy specialist-status route\n// returned metadata sourced from ~/.overdeck/specialists/registry.json, which\n// the role-primitive refactor explicitly retires. The startup cleanup in\n// service.ts deletes that directory on every boot; preserving the read path\n// would silently recreate it via getRunMetadata() → loadRegistry()/saveRegistry().\n//\n// The route is now a 410 Gone with a pointer to the role-aware status surface.\n// The frontend's only caller (AgentOutputPanel) only fires when\n// parseSpecialistSession(agentId) matches the legacy `specialist-…` session\n// naming, which the new role spawns no longer use, so the dead-letter response\n// is invisible to current dashboards.\n\nconst getProjectSpecialistStatusRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:issueId/:type/status',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n return jsonResponse(\n {\n error: 'specialist-status route retired',\n hint: 'Specialist identity is replaced by the role primitive. Use GET /api/agents and read the role/status fields off the AgentSnapshot for the role-scoped session (e.g. agent-pan-509-review).',\n retiredFor: {\n project: params['project'],\n issueId: params['issueId'],\n type: params['type'],\n },\n },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:issueId/:type/kill ───────────────\n\nconst postProjectSpecialistKillRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:issueId/:type/kill',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const issueId = params['issueId'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { getTmuxSessionName, makeSpecialistRegistryKey, getRunMetadata } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n\n const registryKey = makeSpecialistRegistryKey(type, issueId);\n const tmuxSession = getRunMetadata(project, registryKey).tmuxSession\n ?? getTmuxSessionName(type, project, issueId);\n\n yield* Effect.promise(() => Effect.runPromise(killSession(tmuxSession)).catch(() => {}));\n // Leave Claude JSONL/session artifacts intact; only reset Overdeck runtime state.\n saveAgentRuntimeState(tmuxSession, {\n state: 'idle',\n lastActivity: new Date().toISOString(),\n });\n return jsonResponse({\n success: true,\n message: `Killed ${type} (${project}/${issueId})`,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/spawn ───────────────────────\n//\n// PAN-1048 R1: removed. The legacy /spawn endpoint dispatched arbitrary\n// \"specialist types\" (review-agent, test-agent, merge-agent) by issuing a\n// generic spawnEphemeralSpecialist call. Under the role primitive, review/test\n// dispatch through lifecycle-aware role paths. Shipping is now server-side;\n// `ship` remains only as the merge-specialist identity for model routing and\n// historical activity attribution. The endpoint had no remaining in-tree caller\n// and is replaced by reactive Cloister scheduling on issue state transitions\n// plus the role spawn primitive.\n//\n// Old shape (removed):\n// POST /api/specialists/:project/:type/spawn { issueId, branch, ... }\n//\n// Replacement: drive role spawns via lifecycle.transitionTo() and the\n// reactive scheduler in src/lib/cloister/service.ts.\n\n// ─── Route: GET /api/specialists/:project/:type/runs ──────────────────────────\n\nconst getProjectSpecialistRunsRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/runs',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n\n let limit: number | undefined;\n let offset = 0;\n\n if (Option.isSome(urlOpt)) {\n const sp = urlOpt.value.searchParams;\n const limitParam = sp.get('limit');\n const offsetParam = sp.get('offset');\n if (limitParam) limit = parseInt(limitParam, 10);\n if (offsetParam) offset = parseInt(offsetParam, 10);\n }\n\n const { listRunLogsSync } = yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n const runs = listRunLogsSync(project, type, { limit, offset });\n return jsonResponse(runs);\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/runs/:runId/stream ────────────\n// NOTE: Must be registered before /:project/:type/runs/:runId to avoid route conflict.\n\nconst getProjectSpecialistRunStreamRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/runs/:runId/stream',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n const runId = params['runId'] as string;\n\n const { getRunLogPath, isRunLogActive } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n\n const logPath = getRunLogPath(project, type, runId);\n\n if (!existsSync(logPath)) {\n return jsonResponse({ error: 'Run log not found' }, { status: 404 });\n }\n\n // Build an SSE stream using Effect Stream + Node ReadableStream\n const encoder = new TextEncoder();\n\n const nodeStream = new ReadableStream({\n async start(controller) {\n // Send initial content\n const content = await readFile(logPath, 'utf-8').catch(() => '');\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({ type: 'content', data: content })}\\n\\n`,\n ),\n );\n let lastSize = content.length;\n\n // Poll for updates\n const poll = async () => {\n if (!isRunLogActive(project, type, runId)) {\n // Log completed — send final update and close\n try {\n const finalContent = await readFile(logPath, 'utf-8');\n if (finalContent.length > lastSize) {\n const newContent = finalContent.substring(lastSize);\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({ type: 'append', data: newContent })}\\n\\n`,\n ),\n );\n }\n } catch {}\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify({ type: 'complete' })}\\n\\n`),\n );\n controller.close();\n return;\n }\n\n try {\n const currentContent = await readFile(logPath, 'utf-8');\n if (currentContent.length > lastSize) {\n const newContent = currentContent.substring(lastSize);\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({ type: 'append', data: newContent })}\\n\\n`,\n ),\n );\n lastSize = currentContent.length;\n }\n } catch {}\n\n await new Promise((r) => setTimeout(r, 1000));\n await poll();\n };\n\n await new Promise((r) => setTimeout(r, 1000));\n await poll();\n },\n });\n\n const effectStream = Stream.fromReadableStream<Uint8Array, unknown>({\n evaluate: () => nodeStream,\n onError: (err) => err,\n });\n\n return HttpServerResponse.stream(effectStream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/runs/:runId ───────────────────\n\nconst getProjectSpecialistRunRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/runs/:runId',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n const runId = params['runId'] as string;\n\n const { getRunLogSync, parseLogMetadata } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n const content = getRunLogSync(project, type, runId);\n\n if (!content) {\n return jsonResponse({ error: 'Run log not found' }, { status: 404 });\n }\n\n const metadata = parseLogMetadata(content);\n return jsonResponse({ runId, content, metadata });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/runs/:runId/terminate ────────\n\nconst postProjectSpecialistRunTerminateRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/runs/:runId/terminate',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { terminateSpecialist } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n yield* Effect.promise(() => terminateSpecialist(project, type));\n return jsonResponse({ success: true, message: 'Specialist terminated' });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/grace/pause ──────────────────\n\nconst postProjectSpecialistGracePauseRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/grace/pause',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { pauseGracePeriod } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const success = pauseGracePeriod(project, type);\n\n if (success) {\n return jsonResponse({ success: true, message: 'Grace period paused' });\n } else {\n return jsonResponse(\n { error: 'No active grace period to pause' },\n { status: 400 },\n );\n }\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/grace/resume ─────────────────\n\nconst postProjectSpecialistGraceResumeRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/grace/resume',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { resumeGracePeriod } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const success = resumeGracePeriod(project, type);\n\n if (success) {\n return jsonResponse({ success: true, message: 'Grace period resumed' });\n } else {\n return jsonResponse(\n { error: 'No paused grace period to resume' },\n { status: 400 },\n );\n }\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/grace/exit ───────────────────\n\nconst postProjectSpecialistGraceExitRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/grace/exit',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { exitGracePeriod } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n exitGracePeriod(project, type);\n return jsonResponse({\n success: true,\n message: 'Specialist terminated immediately',\n });\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/grace ────────────────────────\n\nconst getProjectSpecialistGraceRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/grace',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { getGracePeriodState } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const state = getGracePeriodState(project, type);\n\n if (state) {\n return jsonResponse(state);\n } else {\n return jsonResponse({ error: 'No active grace period' }, { status: 404 });\n }\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/context ──────────────────────\n\nconst getProjectSpecialistContextRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/context',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n const { loadContextDigest } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialist-context.js'));\n const digest = loadContextDigest(project, type);\n\n if (digest) {\n return jsonResponse({ digest });\n } else {\n return jsonResponse({ error: 'No context digest found' }, { status: 404 });\n }\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/context/regenerate ───────────\n\nconst postProjectSpecialistContextRegenerateRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/context/regenerate',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n const { regenerateContextDigest } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialist-context.js'));\n const digest = yield* regenerateContextDigest(project, type);\n\n if (digest) {\n return jsonResponse({ digest, message: 'Context digest regenerated' });\n } else {\n return jsonResponse(\n { error: 'Failed to generate context digest' },\n { status: 500 },\n );\n }\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/complete ─────────────────────\n\nconst postProjectSpecialistCompleteRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/complete',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n const body = yield* readJsonBody;\n const { status, notes, issueId } = body as { status?: string; notes?: string; issueId?: string };\n\n if (!status || !['passed', 'failed', 'blocked'].includes(status)) {\n return jsonResponse(\n { error: 'Valid status (passed/failed/blocked) is required' },\n { status: 400 },\n );\n }\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { signalSpecialistCompletion } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n signalSpecialistCompletion(project, type, { status: status as 'passed' | 'failed' | 'blocked', notes }, issueId);\n return jsonResponse({\n success: true,\n message: 'Specialist completion signaled, grace period started',\n });\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/latest-log ───────────────────\n\nconst getProjectSpecialistLatestLogRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/latest-log',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n const runsDir = join(homedir(), '.overdeck', 'specialists', project, type, 'runs');\n if (!existsSync(runsDir)) {\n return jsonResponse({ log: null, message: 'No runs found' });\n }\n\n const files = (yield* Effect.promise(() => readdir(runsDir)))\n .filter((f) => f.endsWith('.log'))\n .sort()\n .reverse();\n\n if (files.length === 0) {\n return jsonResponse({ log: null, message: 'No run logs found' });\n }\n\n const latestLog = yield* Effect.promise(() => readFile(join(runsDir, files[0]), 'utf-8'));\n return jsonResponse({\n log: latestLog,\n file: files[0],\n totalRuns: files.length,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/logs/cleanup ────────────────\n\nconst postProjectSpecialistLogsCleanupRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/logs/cleanup',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n const { cleanupOldLogsSync } = yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n const { getSpecialistRetention } = yield* Effect.promise(() => import('../../../../lib/projects.js'));\n\n const retention = getSpecialistRetention(project);\n const deleted = cleanupOldLogsSync(project, type, { maxDays: retention.max_days, maxRuns: retention.max_runs });\n\n return jsonResponse({\n success: true,\n deleted,\n message: `Cleaned up ${deleted} old logs`,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/projects/:project/:name/reset-session ───────\n// Bumps the session generation so the next dispatch starts a fresh Claude session.\n// Old JSONL files are preserved.\n\nconst postProjectSpecialistResetSessionRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/projects/:project/:name/reset-session',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const projectKey = params['project'] ?? '';\n const name = params['name'] ?? '';\n return jsonResponse(\n { error: `Legacy specialist session rotation is no longer supported for ${projectKey}/${name}.` },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:issueId/review/restart ───────────\n// Stops the review parent and convoy sessions, then resumes the parent through\n// the role primitive. Saved sessions and completed reports stay intact; same-run\n// recovery re-dispatches only reviewer lanes that have not produced a report.\n// A model or harness change still takes the existing fresh-spawn path.\n\nconst postProjectReviewRestartRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:issueId/review/restart',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const issueId = params['issueId'] as string;\n const body = yield* readJsonBody;\n const { model, harness } = body as { model?: string; harness?: 'claude-code' | 'pi' | 'codex' };\n\n const { killAllReviewerSessions } = yield* Effect.promise(\n () => import('../../../../lib/cloister/review-agent.js'),\n );\n const killResult = yield* killAllReviewerSessions(project, issueId);\n\n // PAN-1862: do NOT wipe here. The review session (state.json + saved session id) is preserved\n // so spawnReviewRoleForIssue can RESUME it — keeping the prior review's context so a restart\n // with the same model checks the fix instead of re-researching the whole diff. It wipes +\n // fresh-spawns internally ONLY when the harness/model actually changed.\n\n // Resolve workspace info for re-dispatch\n const projectConfig = resolveProjectFromIssueSync(issueId);\n if (!projectConfig) {\n return jsonResponse({ error: `Cannot resolve project for ${issueId}` }, { status: 404 });\n }\n const workspacePath = join(projectConfig.projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`);\n if (!existsSync(workspacePath)) {\n return jsonResponse({ error: `Workspace not found: ${workspacePath}` }, { status: 404 });\n }\n\n // Detect branch from the primary code repo; fall back to configured source branch.\n const primaryRepo = resolveWorkspaceRepoRootsSync(issueId, workspacePath)[0];\n let branch = primaryRepo.sourceBranch;\n try {\n const { stdout } = yield* Effect.promise(() => execAsync(\n 'git branch --show-current',\n { cwd: resolvePrimaryWorkspaceRepoDirSync(issueId, workspacePath), encoding: 'utf-8', timeout: 5000 },\n ));\n branch = stdout.trim() || primaryRepo.sourceBranch;\n } catch { /* non-fatal */ }\n\n // PAN-1048 R3: review now spawns through the role primitive.\n const { spawnReviewRoleForIssue } = yield* Effect.promise(\n () => import('../../../../lib/cloister/review-agent.js'),\n );\n const prUrl = getReviewStatusSync(issueId)?.prUrl;\n const result = yield* spawnReviewRoleForIssue({\n issueId,\n workspace: workspacePath,\n branch,\n prUrl,\n model,\n harness,\n });\n\n if (result.gated) {\n return jsonResponse({\n success: false,\n gated: true,\n message: result.message,\n killed: killResult.killed,\n wiped: [],\n model: model ?? undefined,\n harness: harness ?? undefined,\n }, { status: 409 });\n }\n\n return jsonResponse({\n success: result.success,\n message: result.message,\n killed: killResult.killed,\n wiped: [],\n model: model ?? undefined,\n harness: harness ?? undefined,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:issueId/reviewer/:role/restart ───\n//\n// PAN-3368: convoy reviewers are independent role sessions again. Restarting one\n// lane preserves completed sibling reports and gives operators the surgical recovery\n// that the CLI's long-advertised --role flag promises.\n\nconst postProjectReviewerRoleRestartRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:issueId/reviewer/:role/restart',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const issueId = (params['issueId'] as string).toUpperCase();\n const role = params['role'] as string;\n if (!(REVIEW_SUB_ROLES as readonly string[]).includes(role)) {\n return jsonResponse(\n { error: `Invalid reviewer role: ${role}`, allowed: REVIEW_SUB_ROLES },\n { status: 400 },\n );\n }\n\n const parentId = `agent-${issueId.toLowerCase()}-review`;\n const reviewerId = `${parentId}-${role}`;\n const parentState = getAgentStateSync(parentId);\n const parent = parentState\n ? yield* Effect.promise(() => resolveReviewParentRunState(parentState))\n : null;\n if (!parent?.reviewRunId || !parent.workspace) {\n return jsonResponse(\n { error: `Active review run not found for ${issueId}` },\n { status: 409 },\n );\n }\n\n const body = yield* readJsonBody;\n const { model } = body as { model?: string };\n const reviewer = getAgentStateSync(reviewerId);\n yield* Effect.promise(() => Effect.runPromise(killSession(reviewerId)).catch(() => undefined));\n\n const { spawnReviewSubRoleForIssue } = yield* Effect.promise(\n () => import('../../../../lib/cloister/review-agent.js'),\n );\n const result = yield* spawnReviewSubRoleForIssue({\n issueId,\n workspace: parent.workspace,\n subRole: role as ReviewSubRole,\n runId: parent.reviewRunId,\n ...(reviewer?.reviewRunId === parent.reviewRunId && reviewer.reviewOutputPath\n ? { outputPath: reviewer.reviewOutputPath }\n : {}),\n contextManifestPath: parent.reviewContextManifestPath,\n synthesisAgentId: parentId,\n ...(model ? { model } : {}),\n allowHost: parent.hostOverride ?? false,\n });\n\n return jsonResponse(\n {\n success: result.success,\n message: result.message,\n error: result.error,\n restarted: role,\n },\n result.success ? undefined : { status: 500 },\n );\n })),\n);\n\n// ─── Route: GET /api/models/resolve ──────────────────────────────────────────\n// Returns the resolved default model for each session/work type.\n\nconst getModelsResolveRoute = HttpRouter.add(\n 'GET',\n '/api/models/resolve',\n httpHandler(Effect.gen(function* () {\n const config = loadConfigSync().config;\n\n const routes = [\n { key: 'role:plan', role: 'plan' },\n { key: 'role:work', role: 'work' },\n { key: 'role:work.inspect', role: 'work', subRole: 'inspect' },\n { key: 'role:work.inspect-deep', role: 'work', subRole: 'inspect-deep' },\n { key: 'role:strike', role: 'strike' },\n { key: 'role:review', role: 'review' },\n { key: 'role:review.correctness', role: 'review', subRole: 'correctness' },\n { key: 'role:review.security', role: 'review', subRole: 'security' },\n { key: 'role:review.performance', role: 'review', subRole: 'performance' },\n { key: 'role:review.requirements', role: 'review', subRole: 'requirements' },\n { key: 'role:test', role: 'test' },\n { key: 'role:ship', role: 'ship' },\n ] as const;\n\n const resolved: Record<string, string | null> = {};\n for (const route of routes) {\n try {\n resolved[route.key] = resolveModel(route.role, (route as { subRole?: string }).subRole, config);\n } catch {\n resolved[route.key] = null;\n }\n }\n\n return jsonResponse(resolved);\n })),\n);\n\nexport const specialistsProjectRouteLayer = Layer.mergeAll(\n // PAN-1048 R1: postProjectSpecialistSpawnRoute removed (see above).\n getProjectSpecialistStatusRoute,\n postProjectSpecialistKillRoute,\n getProjectSpecialistRunsRoute,\n getProjectSpecialistRunStreamRoute, // /runs/:runId/stream — before /runs/:runId\n getProjectSpecialistRunRoute, // /runs/:runId\n postProjectSpecialistRunTerminateRoute, // /runs/:runId/terminate\n postProjectSpecialistGracePauseRoute, // /grace/pause\n postProjectSpecialistGraceResumeRoute, // /grace/resume\n postProjectSpecialistGraceExitRoute, // /grace/exit\n getProjectSpecialistGraceRoute, // /grace\n getProjectSpecialistContextRoute, // /context\n postProjectSpecialistContextRegenerateRoute, // /context/regenerate\n postProjectSpecialistCompleteRoute, // /complete\n getProjectSpecialistLatestLogRoute, // /latest-log\n postProjectSpecialistLogsCleanupRoute, // /logs/cleanup\n postProjectSpecialistResetSessionRoute, // /reset-session\n postProjectReviewRestartRoute, // /:project/:issueId/review/restart\n postProjectReviewerRoleRestartRoute, // /:project/:issueId/reviewer/:role/restart\n getModelsResolveRoute, // /models/resolve\n);\n","/**\n * Specialists route module — Effect HttpRouter.Layer (PAN-428 B9)\n *\n * Implements all /api/specialists/* endpoints from the Express server:\n * GET /api/specialists\n * POST /api/specialists/reset-all\n * POST /api/specialists/done\n * POST /api/specialists/logs/cleanup-all\n * GET /api/specialists/queues\n * GET /api/specialists/projects\n * POST /api/specialists/:name/wake\n * POST /api/specialists/:name/reset\n * POST /api/specialists/:name/init\n * POST /api/specialists/:name/report-status\n * GET /api/specialists/:name/cost\n * GET /api/specialists/:name/queue\n * POST /api/specialists/:name/queue\n * DELETE /api/specialists/:name/queue/:itemId\n * PUT /api/specialists/:name/queue/reorder\n * POST /api/specialists/:name/auto-complete\n * GET /api/specialists/:project/:issueId/:type/status\n * POST /api/specialists/:project/:issueId/:type/kill\n * GET /api/specialists/:project/:type/queue\n * POST /api/specialists/:project/:type/spawn\n * GET /api/specialists/:project/:type/runs\n * GET /api/specialists/:project/:type/runs/:runId\n * GET /api/specialists/:project/:type/runs/:runId/stream\n * POST /api/specialists/:project/:type/runs/:runId/terminate\n * POST /api/specialists/:project/:type/grace/pause\n * POST /api/specialists/:project/:type/grace/resume\n * POST /api/specialists/:project/:type/grace/exit\n * GET /api/specialists/:project/:type/grace\n * GET /api/specialists/:project/:type/context\n * POST /api/specialists/:project/:type/context/regenerate\n * POST /api/specialists/:project/:type/complete\n * GET /api/specialists/:project/:type/latest-log\n * POST /api/specialists/:project/:type/logs/cleanup\n */\n\nimport { Layer } from 'effect';\n\nimport { specialistsLegacyRouteLayer } from './specialists/legacy-routes.js';\nimport { specialistsProjectRouteLayer } from './specialists/project-routes.js';\n\nexport {\n _serverManagedMerges,\n firePostMergeLifecycle,\n validateSpecialistAutoCompleteMetadata,\n} from './specialists/shared.js';\n\nexport const specialistsRouteLayer = Layer.mergeAll(\n specialistsLegacyRouteLayer,\n specialistsProjectRouteLayer,\n);\n\nexport default specialistsRouteLayer;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"specialists-DocAK-7A.js","names":["yaml","constantTimeTokenEqual","createHash","pathExists","getAgentWorkspace","execFileAsync","execAsync","execFileAsync","readJsonBody","getAgentWorkspaceShared","getAgentJsonlPathShared","getAgentPendingQuestionsShared","execAsync","readJsonBody","randomUUID","execAsync","execFileAsync","randomUUID","randomUUID","httpRequest","Result","readJsonBody","getAgentJsonlPath","readJsonBody","readJsonBody","readJsonBody","readJsonBody","execAsync","execFileAsync","readJsonBody","loadWorkspaceMetadataFn","execAsync","setReviewStatusBase"],"sources":["../../src/dashboard/server/http-helpers.ts","../../src/lib/default-cwd.ts","../../src/dashboard/server/routes/http-handler.ts","../../src/dashboard/server/routes/origin-validation.ts","../../src/dashboard/server/services/pending-respawn.ts","../../src/dashboard/server/routes/dashboard-auth.ts","../../src/dashboard/server/routes/jsonl-resolver.ts","../../src/lib/pan-cli-invocation.ts","../../src/lib/work-agent-lifecycle.ts","../../src/dashboard/server/services/git-info.ts","../../src/lib/composer-commands/executors.ts","../../src/lib/composer-commands/detached.ts","../../src/dashboard/server/routes/agents/shared.ts","../../src/dashboard/server/routes/agents/listing.ts","../../src/dashboard/server/routes/agents/conversation.ts","../../src/lib/composer-commands/confirmations.ts","../../src/lib/composer-commands/parser.ts","../../src/lib/composer-commands/policy.ts","../../src/lib/composer-commands/router.ts","../../src/dashboard/server/routes/agents/messaging.ts","../../src/lib/runtimes/conversation-control.ts","../../src/lib/ohmypi-codex-auth.ts","../../src/dashboard/server/services/conversation-attachments.ts","../../src/lib/overdeck/resume-contract-delivery.ts","../../src/lib/overdeck/conversation-runtime.ts","../../src/lib/overdeck/conversation-delivery.ts","../../src/lib/session-pane-choice.ts","../../src/lib/channels/permission-payload.ts","../../src/dashboard/server/routes/agent-permissions.ts","../../src/dashboard/server/routes/agents/permissions.ts","../../src/dashboard/server/services/agent-event-utils.ts","../../src/dashboard/server/routes/agents/runtime-events.ts","../../src/dashboard/server/services/agent-projection.ts","../../src/dashboard/server/routes/agents/lifecycle-stop.ts","../../src/dashboard/server/routes/agents/lifecycle-restart.ts","../../src/dashboard/server/routes/agents/control.ts","../../src/lib/orders/eligibility.ts","../../src/lib/orders/dispatch-gate.ts","../../src/lib/orders/dispatch-reservation.ts","../../src/lib/xbrief/auto-synthesize.ts","../../src/dashboard/server/routes/agents/spawn-helpers.ts","../../src/dashboard/server/routes/agents/spawn.ts","../../src/dashboard/server/routes/agents.ts","../../src/dashboard/server/routes/tiered-inspect-escalation.ts","../../src/dashboard/server/routes/specialists/shared.ts","../../src/dashboard/server/routes/specialists/legacy-routes.ts","../../src/dashboard/server/routes/specialists/project-routes.ts","../../src/dashboard/server/routes/specialists.ts"],"sourcesContent":["/**\n * Workaround for Effect 4.0.0-beta.45 bug where HttpServerResponse.json()\n * creates a response without a proper body._tag, causing NodeHttpServer to crash\n * with \"undefined is not an object (evaluating 'body._tag')\".\n * \n * HttpServerResponse.text() works fine, so we use it with manual JSON.stringify.\n * Remove this workaround when Effect stabilizes HttpServerResponse.json().\n */\nimport { HttpServerResponse } from 'effect/unstable/http';\n\nexport function jsonResponse(\n data: unknown,\n options?: number | { status?: number; headers?: Record<string, string> },\n): HttpServerResponse.HttpServerResponse {\n return jsonStringResponse(JSON.stringify(data), options);\n}\n\nexport function jsonStringResponse(\n json: string,\n options?: number | { status?: number; headers?: Record<string, string> },\n): HttpServerResponse.HttpServerResponse {\n const opts = typeof options === 'number' ? { status: options } : options;\n return HttpServerResponse.text(\n json,\n {\n status: opts?.status ?? 200,\n contentType: 'application/json',\n headers: opts?.headers,\n }\n );\n}\n","import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Resolve the default cwd used when no project is selected — the \"devroot\"\n * fallback. Prefers `~/Projects` if it exists, otherwise the user's home\n * directory.\n *\n * Replaces the deprecated `sync.devroot` config (PAN-1201) for the runtime\n * code paths that still need a sensible default working directory:\n *\n * - New conversations created without a `projectKey` (conversations.ts)\n * - The global editor-launcher in the dashboard top bar (PanOpenInPicker)\n */\nexport function getDefaultCwd(): string {\n const candidate = join(homedir(), 'Projects');\n return existsSync(candidate) ? candidate : homedir();\n}\n","/**\n * httpHandler — typed error-to-HTTP mapping wrapper (PAN-470)\n *\n * Wraps an Effect route body and catches all typed TaggedErrors,\n * mapping them to appropriate HTTP status codes. Unknown/unhandled\n * errors fall through as 500 Internal Server Error.\n *\n * Usage:\n * HttpRouter.add('GET', '/api/foo', httpHandler(Effect.gen(function* () {\n * const result = yield* someService.doThing();\n * return jsonResponse(result);\n * })))\n */\n\nimport { Cause, Effect, Option } from 'effect';\nimport { HttpServerRequest, HttpServerResponse as HttpServerResponseModule } from 'effect/unstable/http';\n\ntype HttpServerResponse = HttpServerResponseModule.HttpServerResponse;\n\nimport { jsonResponse } from '../http-helpers.js';\nimport {\n AgentAlreadyRunning,\n AgentStartError,\n IssueNotFound,\n PlanEmpty,\n RateLimited,\n TasksNotInitialized,\n TrackerApiError,\n TrackerNotConfigured,\n WorkspaceCreateError,\n WorkspaceNotFound,\n} from '../services/typed-errors.js';\n\n// Burst-control for the opaque-defect log: log the first OPAQUE_FULL_BURST\n// occurrences in full, then once per OPAQUE_BURST_INTERVAL_MS, emit a summary\n// with the count of suppressed events. This stops a runaway null-defect source\n// from filling the log + saturating the event loop with formatting work, while\n// preserving full detail for the first few occurrences so diagnosis is possible.\nconst OPAQUE_FULL_BURST = 5;\nconst OPAQUE_BURST_INTERVAL_MS = 30_000;\nlet opaqueLogged = 0;\nlet opaqueSuppressed = 0;\nlet opaqueLastSummaryAt = 0;\n\n/**\n * Wraps a route effect with typed error-to-HTTP status mapping.\n *\n * Error → Status mapping:\n * IssueNotFound, WorkspaceNotFound → 404 Not Found\n * TrackerNotConfigured → 503 Service Unavailable\n * RateLimited → 429 Too Many Requests\n * AgentAlreadyRunning → 409 Conflict\n * PlanEmpty, TasksNotInitialized → 422 Unprocessable Entity\n * TrackerApiError → 502 Bad Gateway\n * WorkspaceCreateError, AgentStartError → 500 Internal Server Error\n * Unknown errors → 500 Internal Server Error\n */\nexport function httpHandler<R, E>(\n effect: Effect.Effect<HttpServerResponse, E, R>\n): Effect.Effect<HttpServerResponse, never, R> {\n // Capture the registration call site (where the route called httpHandler(...))\n // so when a fiber emits a typed Fail with no stack — e.g. Effect.fail(null) —\n // we can still tell which route module the handler belongs to.\n const registeredAt = (new Error('httpHandler registered')).stack ?? '<no stack>';\n type AllKnownErrors =\n | IssueNotFound\n | WorkspaceNotFound\n | TrackerNotConfigured\n | RateLimited\n | AgentAlreadyRunning\n | PlanEmpty\n | TasksNotInitialized\n | TrackerApiError\n | WorkspaceCreateError\n | AgentStartError;\n return (effect as Effect.Effect<HttpServerResponse, AllKnownErrors, R>).pipe(\n Effect.catchTag('IssueNotFound', (err: IssueNotFound) =>\n Effect.succeed(jsonResponse({ error: `Issue not found: ${err.id}` }, { status: 404 }))\n ),\n Effect.catchTag('WorkspaceNotFound', (err: WorkspaceNotFound) =>\n Effect.succeed(jsonResponse({ error: `Workspace not found: ${err.id}` }, { status: 404 }))\n ),\n Effect.catchTag('TrackerNotConfigured', (err: TrackerNotConfigured) =>\n Effect.succeed(jsonResponse({ error: `Tracker not configured: ${err.tracker}` }, { status: 503 }))\n ),\n Effect.catchTag('RateLimited', (err: RateLimited) =>\n Effect.succeed(\n jsonResponse({ error: 'Rate limited', retryAfter: err.retryAfter }, { status: 429 })\n )\n ),\n Effect.catchTag('AgentAlreadyRunning', (err: AgentAlreadyRunning) =>\n Effect.succeed(jsonResponse({ error: `Agent already running for issue: ${err.id}` }, { status: 409 }))\n ),\n Effect.catchTag('PlanEmpty', (err: PlanEmpty) =>\n Effect.succeed(jsonResponse({ error: `Plan is empty for issue: ${err.id}` }, { status: 422 }))\n ),\n Effect.catchTag('TasksNotInitialized', (err: TasksNotInitialized) =>\n Effect.succeed(jsonResponse({ error: `Tasks not initialized for workspace: ${err.workspace}` }, { status: 422 }))\n ),\n Effect.catchTag('TrackerApiError', (err: TrackerApiError) =>\n Effect.succeed(\n jsonResponse({ error: `Tracker API error (${err.tracker}): ${err.message}` }, { status: 502 })\n )\n ),\n Effect.catchTag('WorkspaceCreateError', (err: WorkspaceCreateError) =>\n Effect.succeed(\n jsonResponse({ error: `Workspace creation failed for ${err.id}: ${err.message}` }, { status: 500 })\n )\n ),\n Effect.catchTag('AgentStartError', (err: AgentStartError) =>\n Effect.succeed(\n jsonResponse({ error: `Agent start failed for ${err.id}: ${err.message}` }, { status: 500 })\n )\n ),\n Effect.catchCause((cause) =>\n Effect.gen(function* () {\n // Interrupt-only causes mean the consumer (browser tab, abort signal) cancelled\n // the request. That isn't a server error — silence it instead of spamming logs.\n if (Cause.hasInterruptsOnly(cause)) {\n return jsonResponse({ error: 'Request cancelled' }, { status: 499 });\n }\n const error = Cause.squash(cause);\n const message = error instanceof Error ? error.message : 'Internal server error';\n const reqOption = yield* Effect.serviceOption(HttpServerRequest.HttpServerRequest);\n const route = Option.match(reqOption, {\n onNone: () => 'unknown',\n onSome: (req) => `${req.method} ${req.url}`,\n });\n // When the squashed defect is null/undefined or a non-Error value the pretty\n // printer produces `Error: Unknown error: null` with no stack, which leaves\n // the source impossible to locate. Capture extra context for those cases:\n // the JSON shape of the cause (failures + defects), the typeof, and a stack\n // sampled from this catch site so we can at least see what caller chained\n // into httpHandler.\n const isOpaque = !(error instanceof Error);\n if (isOpaque) {\n if (opaqueLogged < OPAQUE_FULL_BURST) {\n opaqueLogged += 1;\n let causeJson: string;\n try {\n causeJson = JSON.stringify(cause, (_k, v) => (typeof v === 'function' ? '[fn]' : v), 2);\n } catch {\n causeJson = '<cause not serializable>';\n }\n console.error(\n `[httpHandler] Opaque non-Error defect on ${route} (#${opaqueLogged}): typeof=${typeof error} value=${String(error)}\\n` +\n `cause=${causeJson}\\n` +\n `registeredAt=${registeredAt}`,\n );\n } else {\n opaqueSuppressed += 1;\n const now = Date.now();\n if (now - opaqueLastSummaryAt >= OPAQUE_BURST_INTERVAL_MS) {\n opaqueLastSummaryAt = now;\n console.error(\n `[httpHandler] Opaque non-Error defect on ${route} — ${opaqueSuppressed} more suppressed since last summary ` +\n `(set DEBUG_OPAQUE_DEFECTS=1 to see all). value=${String(error)}`,\n );\n opaqueSuppressed = 0;\n }\n }\n } else {\n console.error(`[httpHandler] Unhandled error on ${route}:\\n${Cause.pretty(cause)}`);\n }\n return jsonResponse({ error: message }, { status: 500 });\n }),\n )\n ) as Effect.Effect<HttpServerResponse, never, R>;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport yaml from 'js-yaml';\nimport type { HttpServerRequest } from 'effect/unstable/http';\n\nexport type HeaderMap = Record<string, string | string[] | undefined>;\n\nlet cachedTrustedOrigins: string[] | undefined;\n\nfunction addTrustedOrigin(origins: Set<string>, raw: string | undefined): void {\n if (!raw) return;\n const normalized = normalizeOrigin(raw.trim());\n if (normalized) origins.add(normalized);\n}\n\ninterface TraefikYamlConfig {\n enabled: boolean;\n domain: string;\n}\n\n/**\n * Last-resort origin config for launches that carry no origin env at all\n * (manual `node dist/...`, ad-hoc re-exec). `pan up` builds its launch env from\n * this same config.yaml, so reading it here keeps every launch path trusting the\n * same Traefik domain instead of 403ing /ws/* into a \"Reconnecting\" loop.\n */\nfunction readTraefikConfigFromYaml(): TraefikYamlConfig | null {\n try {\n const home = process.env['OVERDECK_HOME'] || join(homedir(), '.overdeck');\n const configPath = join(home, 'config.yaml');\n if (!existsSync(configPath)) return null;\n const parsed = yaml.load(readFileSync(configPath, 'utf8')) as\n | { traefik?: { enabled?: unknown; domain?: unknown } }\n | null\n | undefined;\n const traefik = parsed?.traefik;\n if (!traefik || typeof traefik !== 'object') return null;\n if (typeof traefik.domain !== 'string' || traefik.domain.trim() === '') return null;\n return { enabled: traefik.enabled === true, domain: traefik.domain.trim() };\n } catch {\n return null; // unreadable/invalid config → env-only behavior, same as before\n }\n}\n\nexport function getTrustedOrigins(): string[] {\n if (cachedTrustedOrigins !== undefined) {\n return cachedTrustedOrigins;\n }\n const port = parseInt(process.env['API_PORT'] ?? process.env['PORT'] ?? '3011', 10);\n const dashboardUrl = process.env['DASHBOARD_URL'] ?? `http://localhost:${port}`;\n const origins = new Set<string>();\n addTrustedOrigin(origins, dashboardUrl);\n addTrustedOrigin(origins, `http://localhost:${port}`);\n addTrustedOrigin(origins, `http://127.0.0.1:${port}`);\n addTrustedOrigin(origins, 'http://localhost:3010');\n addTrustedOrigin(origins, 'http://127.0.0.1:3010');\n\n const trustedOrigins = process.env['OVERDECK_TRUSTED_ORIGINS'];\n for (const origin of trustedOrigins?.split(',') ?? []) {\n addTrustedOrigin(origins, origin);\n }\n\n const traefikDomain = process.env['OVERDECK_TRAEFIK_DOMAIN'] ?? process.env['TRAEFIK_DOMAIN'];\n if (process.env['OVERDECK_TRAEFIK_ENABLED'] === '1' && traefikDomain) {\n addTrustedOrigin(origins, `https://${traefikDomain}`);\n }\n\n // Bare-launch fallback: when the launcher provided NO origin configuration at\n // all, trust the domain from the operator's config.yaml. Any explicit env\n // (even an explicit disable) wins over the file.\n const hasEnvOriginConfig =\n process.env['OVERDECK_TRAEFIK_ENABLED'] !== undefined ||\n traefikDomain !== undefined ||\n process.env['OVERDECK_TRUSTED_ORIGINS'] !== undefined;\n if (!hasEnvOriginConfig) {\n const fromYaml = readTraefikConfigFromYaml();\n if (fromYaml?.enabled) {\n addTrustedOrigin(origins, `https://${fromYaml.domain}`);\n }\n }\n\n if (process.env['NODE_ENV'] === 'development') {\n addTrustedOrigin(origins, 'http://localhost:3000');\n addTrustedOrigin(origins, 'http://127.0.0.1:3000');\n }\n cachedTrustedOrigins = Array.from(origins);\n return cachedTrustedOrigins;\n}\n\nexport function normalizeOrigin(origin: string): string | null {\n try {\n const url = new URL(origin);\n return `${url.protocol}//${url.host}`;\n } catch {\n return null;\n }\n}\n\nexport function getHeaderFromMap(headers: HeaderMap, name: string): string | undefined {\n const direct = headers[name];\n if (Array.isArray(direct)) return direct[0];\n if (direct) return direct;\n\n const lowerName = name.toLowerCase();\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() !== lowerName) continue;\n return Array.isArray(value) ? value[0] : value;\n }\n return undefined;\n}\n\nexport function validateOriginHeaders(\n headers: HeaderMap,\n method: string,\n): { ok: true } | { ok: false; error: string } {\n const origin = getHeaderFromMap(headers, 'origin');\n const referer = getHeaderFromMap(headers, 'referer');\n const trusted = getTrustedOrigins();\n\n if (!origin && !referer) {\n const upperMethod = method.toUpperCase();\n if (upperMethod === 'GET' || upperMethod === 'HEAD') {\n return { ok: true };\n }\n return { ok: false, error: 'Missing origin' };\n }\n\n if (origin) {\n const normalized = normalizeOrigin(origin);\n if (normalized && trusted.includes(normalized)) {\n return { ok: true };\n }\n return { ok: false, error: 'Invalid origin' };\n }\n\n if (!referer) {\n return { ok: false, error: 'Invalid referer' };\n }\n const normalized = normalizeOrigin(referer);\n if (normalized && trusted.includes(normalized)) {\n return { ok: true };\n }\n return { ok: false, error: 'Invalid referer' };\n}\n\nexport function validateOrigin(\n request: HttpServerRequest.HttpServerRequest,\n): { ok: true } | { ok: false; error: string } {\n return validateOriginHeaders(request.headers as HeaderMap, request.method);\n}\n\nexport function _resetTrustedOriginsForTests(): void {\n cachedTrustedOrigins = undefined;\n}\n\n/**\n * Origin trust check used by raw WebSocket upgrade paths (autopreso, voice).\n * Only explicitly configured dashboard origins are trusted; the request Host\n * header is attacker-controlled and must not influence this decision.\n */\nexport function isTrustedOriginForHost(\n origin: string | undefined,\n _host: string | string[] | undefined,\n): boolean {\n if (!origin) return false;\n const normalized = normalizeOrigin(origin);\n if (!normalized) return false;\n return getTrustedOrigins().includes(normalized);\n}\n","/**\n * Tracks tmux sessions that are mid-respawn so that `/ws/terminal`\n * reconnects landing in the kill→spawn gap don't get a fatal 4404.\n *\n * Background: switch-model, resume, and restart-all all do\n *\n * await killSessionAsync(name) // old tmux dies, PTY exits,\n * // ws-terminal closes clients with 1000\n * await spawnConversationSession(...) // new tmux session with same name\n * await waitForTmuxSession(name)\n * await waitForReadySignal(name, 30) // up to 30s\n *\n * The terminal frontend (`XTerminal.tsx`) reconnects on close 1000 with\n * exponential backoff (1s, 2s, 4s, 8s, 16s). Every reconnect re-enters\n * `ws-terminal.ts`, which checks the tmux session list upfront and\n * `close(4404, 'session-not-found')` if the name isn't there yet.\n * 4404 is treated as fatal on the client (no retry — the session is\n * presumed gone), so the panel sticks on \"Could not reconnect\" even\n * after the respawn completes a moment later.\n *\n * This registry lets `ws-terminal.ts` distinguish \"transient absence\n * during respawn\" from \"session is actually gone\". Respawn sites mark\n * the name with `markRespawnPending()` for the duration of their kill+\n * spawn block; ws-terminal calls `waitForSessionRespawn()` instead of\n * 4404-ing immediately when the session is missing AND\n * `isRespawnPending()` is true.\n */\n\nimport { Effect } from 'effect';\nimport { sessionExists } from '../../../lib/tmux.js';\n\nconst pendingRespawns = new Set<string>();\n\n/**\n * Mark a tmux session as mid-respawn. The returned `done()` must run in\n * a `finally` so the marker is cleared even if the respawn throws.\n */\nexport function markRespawnPending(sessionName: string): { done: () => void } {\n pendingRespawns.add(sessionName);\n return {\n done: () => {\n pendingRespawns.delete(sessionName);\n },\n };\n}\n\n/** True if a respawn is currently in progress for this session name. */\nexport function isRespawnPending(sessionName: string): boolean {\n return pendingRespawns.has(sessionName);\n}\n\nconst POLL_INTERVAL_MS = 200;\n\n/**\n * Poll for the session to come back, up to `timeoutMs`. Returns whether\n * it did. Bails early once `pendingRespawns` no longer contains the name\n * (the wrapping respawn block finished, success or failure) and the\n * session still isn't there — at that point the absence is real.\n *\n * `timeoutMs` should comfortably exceed the longest respawn window. The\n * dominant cost is `waitForReadySignal`'s 30s ceiling in the conversation\n * routes, so 35s is the right default for those callers.\n */\nexport async function waitForSessionRespawn(\n sessionName: string,\n timeoutMs: number,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await Effect.runPromise(sessionExists(sessionName))) return true;\n if (!pendingRespawns.has(sessionName)) {\n return Effect.runPromise(sessionExists(sessionName));\n }\n await new Promise<void>((r) => setTimeout(r, POLL_INTERVAL_MS));\n }\n return Effect.runPromise(sessionExists(sessionName));\n}\n","import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { networkInterfaces, type NetworkInterfaceInfo } from 'node:os';\n\nimport { Option } from 'effect';\nimport { HttpServerRequest, HttpServerResponse } from 'effect/unstable/http';\n\nimport { getInternalTokenSync, INTERNAL_TOKEN_HEADER } from '../../../lib/internal-token.js';\nimport { jsonResponse } from '../http-helpers.js';\nimport { getHeaderFromMap, getTrustedOrigins, normalizeOrigin, type HeaderMap } from './origin-validation.js';\n\nexport const DASHBOARD_SESSION_COOKIE = 'overdeck_session';\nexport const DASHBOARD_CSRF_HEADER = 'x-overdeck-csrf-token';\n// Session cookie lifetime. Without Max-Age the cookie was a *session* cookie that\n// died when the browser fully closed — so a reopened tab on a trusted origin had\n// no cookie, its mint 401'd, and every mutation failed with \"CSRF token\n// unavailable\" until the operator re-bootstrapped via the one-time URL token.\n// A 30-day rolling expiry means the operator bootstraps once per browser.\nconst DASHBOARD_SESSION_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;\n\nlet browserSessionToken: string | undefined;\nlet browserCsrfToken: string | undefined;\n\nexport function _resetDashboardSessionTokenForTests(): void {\n browserSessionToken = undefined;\n browserCsrfToken = undefined;\n}\n\nfunction getDashboardSessionToken(): string {\n if (browserSessionToken) return browserSessionToken;\n const override = process.env['OVERDECK_DASHBOARD_SESSION_TOKEN'];\n if (override) {\n browserSessionToken = override;\n return browserSessionToken;\n }\n // Derive a stable session token from the persisted internal token so the\n // browser's session cookie survives dashboard restarts. Without this the\n // token was random per-process, so every restart invalidated open tabs'\n // cookies — and a plain refresh could no longer re-mint, since the one-time\n // bootstrap hash token is already consumed on first load. Falls back to a\n // random token only when no internal token is configured, in which case the\n // auth gate already 503s before the session token is ever consulted.\n const internal = getInternalTokenSync();\n browserSessionToken = internal\n ? createHmac('sha256', internal).update('overdeck-dashboard-session-v1').digest('base64url')\n : randomBytes(32).toString('base64url');\n return browserSessionToken;\n}\n\nexport function dashboardCsrfToken(): string {\n if (browserCsrfToken) return browserCsrfToken;\n const override = process.env['OVERDECK_DASHBOARD_CSRF_TOKEN'];\n if (override) {\n browserCsrfToken = override;\n return browserCsrfToken;\n }\n // Same restart-survival derivation as the session token above: the frontend\n // caches this token once per page load (wsTransport session mint), so a\n // random-per-process value 403'd every mutation from open tabs after each\n // dashboard restart — and the flywheel restarts the dashboard on every\n // post-merge deploy. Distinct context string keeps it independent of the\n // session token.\n const internal = getInternalTokenSync();\n browserCsrfToken = internal\n ? createHmac('sha256', internal).update('overdeck-dashboard-csrf-v1').digest('base64url')\n : randomBytes(32).toString('base64url');\n return browserCsrfToken;\n}\n\nfunction getHeader(\n request: HttpServerRequest.HttpServerRequest,\n name: string,\n): string | undefined {\n return getHeaderFromMap(request.headers as HeaderMap, name);\n}\n\nfunction constantTimeTokenEqual(provided: string | undefined, expected: string): boolean {\n if (!provided) return false;\n const providedHash = createHash('sha256').update(provided).digest();\n const expectedHash = createHash('sha256').update(expected).digest();\n return timingSafeEqual(providedHash, expectedHash);\n}\n\nfunction cookieValue(cookieHeader: string | undefined, name: string): string | undefined {\n if (!cookieHeader) return undefined;\n for (const part of cookieHeader.split(';')) {\n const [rawKey, ...rawValue] = part.trim().split('=');\n if (rawKey !== name) continue;\n try {\n return decodeURIComponent(rawValue.join('='));\n } catch {\n return undefined;\n }\n }\n return undefined;\n}\n\nexport function dashboardSessionCookieHeader(options: { secure?: boolean } = {}): string {\n const secure = options.secure ? '; Secure' : '';\n return `${DASHBOARD_SESSION_COOKIE}=${encodeURIComponent(getDashboardSessionToken())}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${DASHBOARD_SESSION_MAX_AGE_SECONDS}${secure}`;\n}\n\nexport function hasDashboardInternalTokenHeaders(headers: HeaderMap): boolean {\n const expected = getInternalTokenSync();\n if (!expected) return false;\n\n const internalHeader = getHeaderFromMap(headers, INTERNAL_TOKEN_HEADER);\n if (constantTimeTokenEqual(internalHeader, expected)) return true;\n\n const authorization = getHeaderFromMap(headers, 'authorization');\n if (authorization) {\n const [scheme, token] = authorization.split(/\\s+/);\n if (scheme?.toLowerCase() === 'bearer' && constantTimeTokenEqual(token, expected)) return true;\n }\n\n return false;\n}\n\nexport function hasDashboardInternalToken(request: HttpServerRequest.HttpServerRequest): boolean {\n return hasDashboardInternalTokenHeaders(request.headers as HeaderMap);\n}\n\nexport function hasDashboardAuthHeaders(headers: HeaderMap): boolean {\n return hasDashboardInternalTokenHeaders(headers) || constantTimeTokenEqual(cookieValue(getHeaderFromMap(headers, 'cookie'), DASHBOARD_SESSION_COOKIE), getDashboardSessionToken());\n}\n\nexport function hasDashboardAuth(request: HttpServerRequest.HttpServerRequest): boolean {\n return hasDashboardAuthHeaders(request.headers as HeaderMap);\n}\n\nfunction isJsonContentType(headers: HeaderMap): boolean {\n const contentType = getHeaderFromMap(headers, 'content-type');\n if (!contentType) return false;\n const [mime] = contentType.toLowerCase().split(';');\n return mime.trim() === 'application/json';\n}\n\nfunction hasTrustedExactOrigin(headers: HeaderMap): boolean {\n const origin = getHeaderFromMap(headers, 'origin');\n if (!origin) return false;\n const normalized = normalizeOrigin(origin);\n return !!normalized && getTrustedOrigins().includes(normalized);\n}\n\nfunction hasValidCsrfToken(headers: HeaderMap): boolean {\n return constantTimeTokenEqual(getHeaderFromMap(headers, DASHBOARD_CSRF_HEADER), dashboardCsrfToken());\n}\n\nexport function rejectUnsafeDashboardMutationRequest(\n request: HttpServerRequest.HttpServerRequest,\n): HttpServerResponse.HttpServerResponse | null {\n const authError = rejectUnauthorizedDashboardRequest(request);\n if (authError) return authError;\n\n const headers = request.headers as HeaderMap;\n if (!isJsonContentType(headers)) {\n return jsonResponse({ error: 'Content-Type must be application/json' }, { status: 400 });\n }\n\n if (hasDashboardInternalToken(request)) return null;\n\n const origin = getHeaderFromMap(headers, 'origin');\n if (origin && !hasTrustedExactOrigin(headers)) {\n return jsonResponse({ error: 'Invalid origin' }, { status: 403 });\n }\n if (!hasValidCsrfToken(headers)) {\n return jsonResponse({ error: 'Invalid CSRF token' }, { status: 403 });\n }\n return null;\n}\n\nfunction ipv4ToInt(value: string): number | null {\n const parts = value.split('.');\n if (parts.length !== 4) return null;\n let result = 0;\n for (const part of parts) {\n const octet = Number(part);\n if (!Number.isInteger(octet) || octet < 0 || octet > 255) return null;\n result = ((result << 8) | octet) >>> 0;\n }\n return result;\n}\n\n/** RFC1918 private ranges — Docker bridges always fall inside one of these. */\nfunction isRfc1918(ip: number): boolean {\n return (\n (ip & 0xff000000) >>> 0 === 0x0a000000 || // 10.0.0.0/8\n (ip & 0xfff00000) >>> 0 === 0xac100000 || // 172.16.0.0/12\n (ip & 0xffff0000) >>> 0 === 0xc0a80000 // 192.168.0.0/16\n );\n}\n\n/**\n * True when `addr` is the IP of a container on one of THIS host's Docker bridge\n * interfaces (docker0 / br-*). Such a peer can only be a process running on this\n * machine — our own host-local Traefik fronting overdeck.localhost, or another\n * Overdeck container — never a LAN client (a direct LAN hit to the 0.0.0.0\n * API port arrives with a LAN peer that is not inside any Docker bridge subnet).\n *\n * Pure for testability: the interface map is injected. We only ever trust\n * RFC1918 bridge subnets, so a misconfigured public-range br-* cannot widen\n * trust beyond the local Docker fabric.\n */\nexport function peerIsHostLocalDockerBridge(\n addr: string,\n interfaces: NodeJS.Dict<NetworkInterfaceInfo[]>,\n): boolean {\n const peer = ipv4ToInt(addr);\n if (peer === null) return false;\n for (const [name, addrs] of Object.entries(interfaces)) {\n if (name !== 'docker0' && !name.startsWith('br-')) continue;\n for (const info of addrs ?? []) {\n if (info.family !== 'IPv4') continue;\n const ifaceIp = ipv4ToInt(info.address);\n const mask = ipv4ToInt(info.netmask);\n if (ifaceIp === null || mask === null) continue;\n const network = (ifaceIp & mask) >>> 0;\n if (!isRfc1918(network)) continue;\n if (((peer & mask) >>> 0) === network) return true;\n }\n }\n return false;\n}\n\n/**\n * True when `addr` shares an RFC1918 subnet with one of THIS process's own\n * network interfaces. This is the in-container counterpart of\n * peerIsHostLocalDockerBridge: inside a container the Docker bridge is\n * invisible — the container sees only its veth endpoints (eth0/eth1), so the\n * docker0/br-* name filter never matches and the host-local Traefik fronting\n * the stack 401'd every session mint (first hit live on the PAN-1737 UAT\n * stack). A peer inside one of the container's attached Docker-network\n * subnets can only be another container on those networks — the server\n * publishes no host port (Traefik labels only), so no LAN client can ever\n * present such an address. Only consulted when running inside a container.\n */\nexport function peerIsLocalContainerNetwork(\n addr: string,\n interfaces: NodeJS.Dict<NetworkInterfaceInfo[]>,\n): boolean {\n const peer = ipv4ToInt(addr);\n if (peer === null) return false;\n for (const addrs of Object.values(interfaces)) {\n for (const info of addrs ?? []) {\n if (info.family !== 'IPv4' || info.internal) continue;\n const ifaceIp = ipv4ToInt(info.address);\n const mask = ipv4ToInt(info.netmask);\n if (ifaceIp === null || mask === null) continue;\n const network = (ifaceIp & mask) >>> 0;\n if (!isRfc1918(network)) continue;\n if (((peer & mask) >>> 0) === network) return true;\n }\n }\n return false;\n}\n\n/** Docker creates /.dockerenv in every container — stable in-container signal. */\nconst runningInContainer = existsSync('/.dockerenv');\n\n/**\n * A request we trust to mint a session because it originated from this machine —\n * the local browser hitting the dashboard directly (127.0.0.1), or the\n * host-local Traefik that fronts overdeck.localhost. In Docker, that Traefik reaches\n * the host server from a Docker-bridge IP (e.g. 172.18.0.2), NOT 127.0.0.1, so\n * a literal-loopback check alone silently breaks the zero-step bootstrap for\n * every overdeck.localhost user — their session mint 401s and no cookie is ever set.\n *\n * This is the auto-bootstrap that removes the manual one-time #overdeck_token\n * step: any browser on overdeck.localhost gets a session with no user action. It is\n * NOT a security downgrade for the LAN — the raw API ports bind 0.0.0.0, but a\n * direct LAN hit has a non-loopback, non-Docker-bridge peer and is rejected. We\n * read ONLY the real TCP peer (request.remoteAddress), never X-Forwarded-For,\n * which a caller could spoof.\n */\nfunction isLoopbackPeer(request: HttpServerRequest.HttpServerRequest): boolean {\n const remoteAddress = (request as { remoteAddress?: unknown }).remoteAddress;\n const raw = remoteAddress && typeof remoteAddress === 'object' && '_tag' in remoteAddress\n ? Option.getOrElse(remoteAddress as Option.Option<string>, () => '')\n : typeof remoteAddress === 'string'\n ? remoteAddress\n : '';\n if (!raw) return false;\n // Node reports dual-stack IPv4 peers as IPv4-mapped IPv6 (::ffff:a.b.c.d).\n const addr = raw.startsWith('::ffff:') ? raw.slice('::ffff:'.length) : raw;\n if (addr === '127.0.0.1' || addr === '::1') return true;\n if (peerIsHostLocalDockerBridge(addr, networkInterfaces())) return true;\n return runningInContainer && peerIsLocalContainerNetwork(addr, networkInterfaces());\n}\n\nexport function rejectUnauthorizedDashboardSessionMintRequest(\n request: HttpServerRequest.HttpServerRequest,\n): HttpServerResponse.HttpServerResponse | null {\n const expected = getInternalTokenSync();\n if (!expected) {\n return jsonResponse({ error: 'dashboard session token not configured' }, { status: 503 });\n }\n if (!hasDashboardInternalToken(request) && !isLoopbackPeer(request)) {\n return jsonResponse({ error: 'unauthorized' }, { status: 401 });\n }\n return null;\n}\n\nexport function rejectUnauthorizedDashboardRequest(\n request: HttpServerRequest.HttpServerRequest,\n): HttpServerResponse.HttpServerResponse | null {\n const expected = getInternalTokenSync();\n if (!expected) {\n return jsonResponse({ error: 'dashboard session token not configured' }, { status: 503 });\n }\n if (!hasDashboardAuth(request)) {\n return jsonResponse({ error: 'unauthorized' }, { status: 401 });\n }\n return null;\n}\n","/**\n * JSONL transcript resolver for the Command Deck (PAN-830).\n *\n * Maps an agent ID (e.g. `agent-pan-830`, `planning-pan-830`, or a canonical\n * specialist tmux session name) to the agent's JSONL transcript file on disk.\n *\n * Codex agents (PAN-1805) write rollout JSONLs under the per-agent\n * `codex-home/sessions/` tree — resolution dispatches on the harness recorded\n * in state.json (thread-id fast path, then latest-rollout fallback).\n *\n * For claude-code agents the JSONL filename is the *Claude session ID* (a UUID\n * written by Claude Code itself), NOT the agent/tmux name. Lookup order:\n * 1. session.id — UUID pinned before fresh work-agent launch and updated\n * by auto-suspend/resume flows\n * 2. sessions.json — array of UUIDs the agent has used; the heartbeat hook\n * APPENDS new IDs and dedupes, so once a session has been\n * seen its array index never moves. After a `pan resume`\n * brings an older session back as the live one, the array\n * order no longer reflects time-recency. We disambiguate\n * across all listed IDs by JSONL mtime.\n * 3. runtime state — in-process mirror, populated by hooks\n * 4. agents registry — the DB row's session_id recorded at spawn; survives\n * janitor removal of the agent directory\n *\n * Async-only (fs/promises) because this code path runs inside the dashboard\n * server's event loop.\n */\nimport { access, readFile, stat, readdir } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { getAgentRuntimeState, getAgentStateSync } from '../../../lib/agents.js';\nimport { encodeClaudeProjectDir, getOverdeckHome } from '../../../lib/paths.js';\nimport { getAgentWorkspace } from '../../../lib/agent-enrichment.js';\nimport { getHarnessBehavior } from '../../../lib/runtimes/behavior.js';\nimport type { HarnessName } from '../../../lib/runtimes/types.js';\nimport { logAgentLifecycleSync } from '../../../lib/persistent-logger.js';\n\nexport interface ResolveJsonlPathOptions {\n /** Override the ~/.overdeck/agents directory (test hook). */\n agentsDirOverride?: string;\n /** Override the ~/.claude/projects directory (test hook). */\n claudeProjectsDirOverride?: string;\n /** Override the ~/.kimi-code directory (test hook). */\n kimiHomeOverride?: string;\n /**\n * Explicit workspace path for resolveKimiWirePath. Conversation rows have\n * no AgentState (readRecordedState/getAgentStateSync return nothing), so\n * dashboard conversation callers must supply conv.cwd directly instead of\n * relying on the agent-state lookup (PAN-1837 review fix).\n */\n workspaceOverride?: string;\n /** Override the runtime-state lookup (test hook). */\n getRuntimeStateAsync?: (agentId: string) => Promise<{ claudeSessionId?: string } | null>;\n /** Override forensic logging (test hook). */\n logDiagnostic?: (agentId: string, message: string) => void;\n}\n\n// Command Deck polling can resolve the same session repeatedly. Keep forensic\n// logging useful by writing only when the resolution outcome changes.\nconst transcriptResolutionSignatures = new Map<string, string>();\n\nfunction logTranscriptResolution(\n agentId: string,\n signature: string,\n message: string,\n opts: ResolveJsonlPathOptions,\n): void {\n const signatureKey = `${agentId}:${opts.agentsDirOverride ?? 'live'}`;\n if (transcriptResolutionSignatures.get(signatureKey) === signature) return;\n transcriptResolutionSignatures.set(signatureKey, signature);\n const logger = opts.logDiagnostic\n ?? (opts.agentsDirOverride ? undefined : logAgentLifecycleSync);\n logger?.(agentId, `transcript resolution: ${message}`);\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n return access(p).then(() => true, () => false);\n}\n\nasync function readOptional(p: string): Promise<string | null> {\n return readFile(p, 'utf-8').catch(() => null);\n}\n\nfunction behaviorForHarness(harness: string | null | undefined) {\n return getHarnessBehavior(harness as HarnessName | null | undefined);\n}\n\n/**\n * Pick the candidate UUID whose JSONL transcript has the most recent mtime.\n *\n * The heartbeat hook appends to sessions.json and dedupes — so once an ID is\n * recorded, its array index never moves even if a later resume makes it the\n * current session again. Choosing by JSONL mtime is the only reliable signal\n * for \"which session is actually live right now.\"\n *\n * Returns null if no candidate has a corresponding JSONL on disk. Falls back\n * to scanning every project dir if the workspace path can't be resolved\n * (covers specialist sessions whose workspace lives elsewhere).\n */\nasync function pickFreshestSessionId(\n candidates: ReadonlyArray<string>,\n agentId: string,\n opts: ResolveJsonlPathOptions,\n): Promise<string | null> {\n if (candidates.length === 0) return null;\n if (candidates.length === 1) return candidates[0]!;\n\n const projectsRoot = opts.claudeProjectsDirOverride ?? join(homedir(), '.claude', 'projects');\n\n // Fast path: derive the agent's workspace and look only in the matching\n // project dir. Avoids fanning out across every Claude project.\n let candidatePaths: Array<{ id: string; path: string }> = [];\n try {\n const workspace = await Effect.runPromise(getAgentWorkspace(agentId));\n if (workspace) {\n const projectDir = join(projectsRoot, encodeClaudeProjectDir(workspace));\n candidatePaths = candidates.map((id) => ({ id, path: join(projectDir, `${id}.jsonl`) }));\n }\n } catch { /* non-fatal — fall back to project-dir scan */ }\n\n // Slow path: scan all project dirs (specialist agents, multi-workspace cases).\n if (candidatePaths.length === 0) {\n try {\n const dirs = await readdir(projectsRoot);\n const SAFE_DIR = /^[a-zA-Z0-9_.-]+$/;\n for (const id of candidates) {\n for (const dir of dirs) {\n if (!SAFE_DIR.test(dir)) continue;\n candidatePaths.push({ id, path: join(projectsRoot, dir, `${id}.jsonl`) });\n }\n }\n } catch { /* fall through to no-mtime path */ }\n }\n\n // Stat each candidate path; pick the (id, mtime) with the newest mtime.\n let best: { id: string; mtimeMs: number } | null = null;\n for (const { id, path } of candidatePaths) {\n try {\n const s = await stat(path);\n if (!best || s.mtimeMs > best.mtimeMs) {\n best = { id, mtimeMs: s.mtimeMs };\n }\n } catch { /* missing file — skip */ }\n }\n\n // If no candidate has a JSONL on disk, fall back to the historical \"last\n // appended\" heuristic — the previous behavior. Better than returning null\n // for callers that only need a session ID, not necessarily a live JSONL.\n return best ? best.id : (candidates[candidates.length - 1] ?? null);\n}\n\n/** Async equivalent of getLatestSessionId from lib/agents.ts. */\nexport async function resolveClaudeSessionId(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const agentDir = join(agentsRoot, agentId);\n\n // 1. session.id — UUID pinned before fresh work-agent launch and updated by\n // suspend/resume flows. Authoritative when present.\n const sessionIdRaw = await readOptional(join(agentDir, 'session.id'));\n const sessionIdTrimmed = sessionIdRaw?.trim();\n if (sessionIdTrimmed) return sessionIdTrimmed;\n\n // 2. sessions.json — array of UUIDs the agent has ever used. We can't trust\n // array order (see file-level docs), so disambiguate by JSONL mtime.\n const sessionsRaw = await readOptional(join(agentDir, 'sessions.json'));\n if (sessionsRaw) {\n try {\n const parsed: unknown = JSON.parse(sessionsRaw);\n if (Array.isArray(parsed)) {\n const valid = parsed.filter((v): v is string => typeof v === 'string' && v.trim().length > 0);\n const fresh = await pickFreshestSessionId(valid, agentId, opts);\n if (fresh) return fresh;\n }\n } catch { /* non-fatal */ }\n }\n\n // 3. runtime state claudeSessionId (in-process mirror)\n try {\n const lookup = opts.getRuntimeStateAsync ?? ((id: string) => Effect.runPromise(getAgentRuntimeState(id)));\n const runtimeState = await lookup(agentId);\n if (runtimeState?.claudeSessionId) return runtimeState.claudeSessionId;\n } catch { /* non-fatal */ }\n\n // 4. agents registry — the DB row records session_id at spawn (PAN-1908) and\n // survives janitors removing the agent directory, so an ended session's\n // transcript stays resolvable. Skipped under agentsDirOverride (test hook)\n // to keep resolver tests hermetic, mirroring readRecordedState.\n if (!opts.agentsDirOverride) {\n const registrySessionId = getAgentStateSync(agentId)?.sessionId?.trim();\n if (registrySessionId) return registrySessionId;\n }\n\n return null;\n}\n\n/**\n * Read the Claude session id PINNED into an agent/conversation launcher.sh.\n *\n * The launcher is what spawns the live tmux pane: it runs\n * `claude … --session-id <uuid>` (or `--resume <uuid>`), so the pinned id is the\n * EXACT session the Terminal tab attaches to — the only deterministic ground\n * truth for \"which session is live right now.\" Resolving the transcript panel\n * from this id makes the Conversation tab match the Terminal tab by construction,\n * instead of guessing via JSONL mtime (racy: an older session's file gets touched\n * by a compaction summary write-back or a transient relaunch, its mtime jumps\n * ahead of the live session's, and the panel renders the wrong transcript).\n *\n * Checks the conversation launcher dir first, then the agent launcher dir.\n * Returns the uuid, or null when no launcher exists or it pins no session id.\n */\nconst LAUNCHER_UUID =\n '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';\nconst LAUNCHER_SESSION_ID_RE = new RegExp(`--session-id\\\\s+'?(${LAUNCHER_UUID})'?`);\nconst LAUNCHER_RESUME_RE = new RegExp(`--resume\\\\s+'?(${LAUNCHER_UUID})'?`);\n\nexport async function readLauncherPinnedSessionId(\n tmuxSession: string,\n opts: { overdeckHomeOverride?: string } = {},\n): Promise<string | null> {\n const home = opts.overdeckHomeOverride ?? getOverdeckHome();\n const candidates = [\n join(home, 'conversations', tmuxSession, 'launcher.sh'),\n join(home, 'agents', tmuxSession, 'launcher.sh'),\n ];\n for (const path of candidates) {\n const text = await readOptional(path);\n if (!text) continue;\n const match = LAUNCHER_SESSION_ID_RE.exec(text) ?? LAUNCHER_RESUME_RE.exec(text);\n if (match) return match[1]!;\n }\n return null;\n}\n\n/**\n * Read the harness + workspace recorded for an agent, honoring the test\n * override dir. Used by resolveAgentHarness / agentHasClaudeTranscript so the\n * stale-harness self-correction runs identically in prod and under test.\n */\nasync function readRecordedState(\n agentId: string,\n opts: ResolveJsonlPathOptions,\n): Promise<{ harness: string | null; workspace?: string }> {\n if (opts.agentsDirOverride) {\n try {\n const raw = await readFile(join(opts.agentsDirOverride, agentId, 'state.json'), 'utf8');\n const s = JSON.parse(raw) as { harness?: unknown; workspace?: unknown };\n return {\n harness: typeof s.harness === 'string' ? s.harness : null,\n workspace: typeof s.workspace === 'string' ? s.workspace : undefined,\n };\n } catch {\n return { harness: null };\n }\n }\n const st = getAgentStateSync(agentId);\n return { harness: st?.harness ?? null, workspace: st?.workspace };\n}\n\n/**\n * Resolve the harness for transcript routing. Reads state.json, then\n * self-corrects when the recorded value is stale.\n *\n * Trust an explicit non-default harness (codex / pi / ohmypi / acp) — it was chosen\n * by resolveHarness at spawn time and is not the generic fallback. The\n * 'claude-code' default, however, goes stale in one observed case: a\n * wipe-and-respawn that changed the provider-default harness left state.json\n * carrying the pre-respawn 'claude-code' while the fresh launch was\n * codex/ohmypi. The resolver then took the claude-code branch, found no\n * claudeSessionId, and the dashboard showed \"No conversation data available\"\n * for a live agent whose real transcript sat untouched under codex-home.\n *\n * Self-correction (claude-code/null recorded only): if the agent has NO\n * claude-code transcript on disk but DOES have a codex rollout / pi session,\n * the on-disk runtime wins — those artifacts are written only by that runtime,\n * so this cannot surface a wrong transcript. When a claude-code transcript IS\n * present it always wins, so a past codex run's codex-home can't shadow a\n * current claude-code session.\n */\nexport async function resolveAgentHarness(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const recorded = (await readRecordedState(agentId, opts)).harness;\n if (recorded === 'codex' || recorded === 'pi' || recorded === 'ohmypi' || recorded === 'acp' || recorded === 'kimi-code') {\n return recorded;\n }\n // 'claude-code' (or null) is the default that can go stale. Correct it from\n // on-disk artifacts only when no claude-code transcript exists for the agent.\n if (await agentHasClaudeTranscript(agentId, opts)) {\n return recorded ?? 'claude-code';\n }\n if (await resolveAcpTranscriptPath(agentId, opts)) return 'acp';\n if (await resolveCodexRolloutPath(agentId, opts)) return 'codex';\n if (await resolvePiSessionPath(agentId, opts)) return 'ohmypi';\n if (await resolveKimiWirePath(agentId, opts)) return 'kimi-code';\n return recorded;\n}\n\n/**\n * Whether the agent has a resolvable claude-code transcript on disk. Used by\n * resolveAgentHarness to decide if a claude-code recording is still live\n * (vs. a stale default that should be corrected to codex/ohmypi).\n */\nasync function agentHasClaudeTranscript(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<boolean> {\n const sessionId = await resolveClaudeSessionId(agentId, opts);\n if (!sessionId) return false;\n const workspace = (await readRecordedState(agentId, opts)).workspace;\n if (!workspace) return false;\n const projectsRoot = opts.claudeProjectsDirOverride ?? join(homedir(), '.claude', 'projects');\n return pathExists(join(projectsRoot, encodeClaudeProjectDir(workspace), `${sessionId}.jsonl`));\n}\n\n/**\n * Resolve the Codex rollout JSONL for a codex-harness agent (PAN-1805).\n *\n * Fast path: the persisted codex-thread-id maps directly to its rollout file.\n * Lazy fallback (same shape as the conversation panel's PAN-1690 fix): codex\n * writes the rollout only on the first turn, so a spawn-time thread-id capture\n * can miss it — the per-agent CODEX_HOME holds only this agent's rollouts, so\n * the newest one is its current thread.\n */\nexport async function resolveCodexRolloutPath(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const agentDir = join(agentsRoot, agentId);\n const codexHome = join(agentDir, 'codex-home');\n if (!(await pathExists(codexHome))) return null;\n\n const { findRolloutPath, findLatestRollout } = await import('../../../lib/runtimes/codex.js');\n\n const threadId = (await readOptional(join(agentDir, 'codex-thread-id')))?.trim();\n if (threadId) {\n const rollout = findRolloutPath(codexHome, threadId);\n if (rollout) return rollout;\n }\n return findLatestRollout(codexHome);\n}\n\n/** Resolve the normalized transcript written by the persistent ACP host. */\nexport async function resolveAcpTranscriptPath(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const transcriptPath = join(agentsRoot, agentId, 'acp-session.jsonl');\n return await pathExists(transcriptPath) ? transcriptPath : null;\n}\n\n/**\n * Resolve the native Kimi Code CLI wire.jsonl for a kimi-code-harness agent\n * (PAN-1837 wi8a). Fast path: the per-agent kimi-session-id (persisted by\n * wi5's spawnAgent) maps directly to\n * `<kimiHome>/sessions/<workDirKey>/<sessionId>/agents/main/wire.jsonl`.\n * Fallback (no captured id): the newest session directory by wire.jsonl mtime\n * under the workspace's workDirKey bucket — same shape as the codex/pi lazy\n * fallbacks above, since Kimi may not have finished writing its first turn\n * when the id was captured.\n */\nexport async function resolveKimiWirePath(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const workspace = opts.workspaceOverride ?? (await readRecordedState(agentId, opts)).workspace;\n if (!workspace) return null;\n\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const sessionId = (await readOptional(join(agentsRoot, agentId, 'kimi-session-id')))?.trim() || null;\n const kimiHome = opts.kimiHomeOverride ?? join(homedir(), '.kimi-code');\n\n // PAN-1837 review fix (P2): use the async twin here — this resolver runs on\n // the dashboard event loop and Command Deck polling re-resolves the same\n // session repeatedly, so a sync readdirSync/statSync walk would block the\n // loop on every poll as a session's history grows.\n const { findKimiWirePathAsync } = await import('../../../lib/runtimes/kimi-code.js');\n return findKimiWirePathAsync(kimiHome, workspace, sessionId);\n}\n\n/**\n * Resolve the pi/kimi session JSONL (PAN-1908). Pi writes its session transcript\n * as `<iso-ts>_<session-id>.jsonl` either in the agent dir's `sessions/` subdir\n * (conversations) OR in the agent dir root (work agents) — so check both. The dir\n * also holds `cost-events.jsonl` / `activity.jsonl`, which are NOT transcripts.\n * Return the freshest transcript by mtime, or null if pi hasn't written one yet.\n */\nexport async function resolvePiSessionPath(\n agentId: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n const agentsRoot = opts.agentsDirOverride ?? join(getOverdeckHome(), 'agents');\n const agentDir = join(agentsRoot, agentId);\n const NON_TRANSCRIPT = new Set([\n 'acp-session.jsonl',\n 'cost-events.jsonl',\n 'activity.jsonl',\n 'pending-events.jsonl',\n ]);\n let best: { path: string; mtime: number } | null = null;\n for (const dir of [join(agentDir, 'sessions'), agentDir]) {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n continue;\n }\n for (const name of entries) {\n if (!name.endsWith('.jsonl') || NON_TRANSCRIPT.has(name)) continue;\n const p = join(dir, name);\n try {\n const m = (await stat(p)).mtimeMs;\n if (!best || m > best.mtime) best = { path: p, mtime: m };\n } catch { /* unreadable entry — skip */ }\n }\n }\n return best?.path ?? null;\n}\n\n/**\n * Resolve the JSONL transcript for an agent.\n *\n * Codex agents resolve to their rollout JSONL (PAN-1805). For claude-code\n * agents, returns the absolute path if both the claudeSessionId is known AND\n * the corresponding JSONL file exists; otherwise null.\n */\nexport async function resolveJsonlPath(\n agentId: string,\n workspacePath: string,\n opts: ResolveJsonlPathOptions = {},\n): Promise<string | null> {\n // Dispatch on the recorded harness so a stale session.id from an earlier\n // claude-code run of the same agent id can't shadow the codex transcript.\n const harness = await resolveAgentHarness(agentId, opts);\n const behavior = behaviorForHarness(harness);\n if (behavior.transcriptKind === 'codex-rollout-jsonl') {\n return resolveCodexRolloutPath(agentId, opts);\n }\n if (behavior.transcriptKind === 'ohmypi-jsonl') {\n return resolvePiSessionPath(agentId, opts);\n }\n if (behavior.transcriptKind === 'acp-jsonl') {\n return resolveAcpTranscriptPath(agentId, opts);\n }\n if (behavior.transcriptKind === 'kimi-wire-jsonl') {\n return resolveKimiWirePath(agentId, opts);\n }\n\n const claudeSessionId = await resolveClaudeSessionId(agentId, opts);\n if (!claudeSessionId) {\n logTranscriptResolution(\n agentId,\n `missing-session-id:${harness ?? 'unknown'}`,\n `failed harness=${harness ?? 'unknown'} reason=no-session-id `\n + 'checked=session.id,sessions.json,runtime-state',\n opts,\n );\n return null;\n }\n\n const recordedWorkspace = (await readRecordedState(agentId, opts)).workspace;\n const effectiveWorkspacePath = recordedWorkspace ?? workspacePath;\n const projectsRoot = opts.claudeProjectsDirOverride ?? join(homedir(), '.claude', 'projects');\n const encodedDir = encodeClaudeProjectDir(effectiveWorkspacePath);\n const jsonlPath = join(projectsRoot, encodedDir, `${claudeSessionId}.jsonl`);\n if (await pathExists(jsonlPath)) {\n logTranscriptResolution(\n agentId,\n `resolved:${jsonlPath}`,\n `resolved harness=${harness ?? 'claude-code'} sessionId=${claudeSessionId} path=${jsonlPath}`,\n opts,\n );\n return jsonlPath;\n }\n logTranscriptResolution(\n agentId,\n `missing-jsonl:${jsonlPath}`,\n `failed harness=${harness ?? 'claude-code'} reason=jsonl-missing sessionId=${claudeSessionId} `\n + `workspace=${effectiveWorkspacePath} expectedPath=${jsonlPath}`,\n opts,\n );\n return null;\n}\n","import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';\nimport { join } from 'node:path';\n\nimport { packageRoot } from './paths.js';\n\nexport interface PanCliInvocation {\n command: string;\n args: string[];\n}\n\n/**\n * Invoke Overdeck's bundled CLI without relying on the dashboard process PATH.\n * GUI/service launches often omit the package-manager bin directory that owns\n * the `pan` shim, while the running Node executable and package bundle remain\n * stable and are sufficient to launch the CLI directly.\n */\nexport function panCliInvocation(\n args: readonly string[],\n runtime: { nodePath?: string; root?: string } = {},\n): PanCliInvocation {\n return {\n command: runtime.nodePath ?? process.execPath,\n args: [join(runtime.root ?? packageRoot, 'dist', 'cli', 'index.js'), ...args],\n };\n}\n\nexport function spawnPanCli(args: readonly string[], options: SpawnOptions = {}): ChildProcess {\n const invocation = panCliInvocation(args);\n return spawn(invocation.command, invocation.args, options);\n}\n","import { existsSync } from 'node:fs';\nimport { access } from 'node:fs/promises';\nimport { Data, Effect } from 'effect';\nimport { getAgentStateSync, getAgentState, getAgentRuntimeStateSync, getAgentRuntimeState, getLatestSessionIdSync, getLatestSessionId, normalizeAgentId } from './agents.js';\nimport { hasCompletionMarkerForAgent } from './agents/supervisor-channels.js';\nimport { claudeSessionTranscriptExists } from './paths.js';\nimport { getReviewStatusSync } from './review-status.js';\nimport { sessionExistsSync, sessionExists } from './tmux.js';\n\nexport type WorkAgentOperation = 'start' | 'resume' | 'restart_with_context' | 'reset_session';\nexport type WorkAgentRecommendedAction = 'start' | 'resume' | 'restart_with_context' | 'reset_session' | 'none';\n\nfunction sessionResetRequiredReason(agentId: string, agentOrIssueId: string): string {\n return `Agent ${agentId} has a resumable Claude session. Use 'pan resume ${agentOrIssueId}' to continue it, or run 'pan reset-session ${agentOrIssueId}' before starting a new session.`;\n}\n\nfunction canStartFresh(lifecycle: WorkAgentLifecycleState, fresh: boolean): boolean {\n return lifecycle.canStartFresh || (fresh && lifecycle.requiresSessionResetBeforeFreshStart);\n}\n\n/**\n * PAN-3555: a completion marker stops meaning \"nothing to resume\" the moment the\n * pipeline owes the agent rework — a failed verification, a blocked/failed review,\n * or a failed test after the handoff makes the warm session the rework target\n * (the same condition PAN-2668 uses to clear stoppedByUser for feedback delivery).\n * Without this, the handed-off branch routed `pan start` to a silent fresh session\n * whenever the feedback loop's direct resumeAgent() path failed, abandoning the\n * resumable transcript with no refusal and no logged reason.\n */\nexport function issueOwesReworkSync(issueId: string | undefined): boolean {\n if (!issueId) return false;\n try {\n const row = getReviewStatusSync(issueId);\n if (!row) return false;\n return row.verificationStatus === 'failed'\n || row.reviewStatus === 'blocked'\n || row.reviewStatus === 'failed'\n || row.testStatus === 'failed';\n } catch {\n return false;\n }\n}\n\nexport interface WorkAgentLifecycleState {\n agentId: string;\n hasAgentState: boolean;\n hasLiveTmuxSession: boolean;\n hasSavedSession: boolean;\n hasResumableTranscript: boolean;\n hasWorkspace: boolean;\n isPlaceholder: boolean;\n isOrphaned: boolean;\n isRunning: boolean;\n /** Agent has a live tmux session and running status, but its runtime is idle or\n * suspended — meaning the model stopped producing output (e.g. model errors).\n * The session should be restarted via resume rather than messaged via pan tell. */\n isRunningButStuck: boolean;\n isStopped: boolean;\n isCompleted: boolean;\n isCrashed: boolean;\n /** Agent ran `pan done` and a completion marker exists — its work is handed\n * off. A handed-off agent is never offered a plain resume (PAN-3334): there\n * is nothing to continue, the resume only relaunches a huge transcript that\n * the harness compacts, and the session then strands (continuation nets\n * correctly refuse to re-drive handed-off agents, PAN-2974). The review →\n * feedback loop is unaffected — it resurrects through `resumeAgent()`\n * directly, not through this read door. */\n handedOff: boolean;\n /** PAN-3555: true when the agent handed off but the canonical review row shows the\n * pipeline owes it rework (failed verification, blocked/failed review, or failed\n * test). An owed-rework handoff is resumable again — `handedOff` alone no longer\n * closes the resume doors. */\n owesRework: boolean;\n runtimeState: string;\n agentStatus: string;\n canStartFresh: boolean;\n /** True when the session can be resumed (stopped, crashed, or running-but-stuck).\n * Always false when `isRunning` is true — a live, active session is not resumable. */\n canResumeSession: boolean;\n canRestartWithContext: boolean;\n canResetSession: boolean;\n requiresSessionResetBeforeFreshStart: boolean;\n recommendedAction: WorkAgentRecommendedAction;\n reason?: string;\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getWorkAgentLifecycleStateSync(agentOrIssueId: string): WorkAgentLifecycleState {\n const agentId = normalizeAgentId(agentOrIssueId);\n const agentState = getAgentStateSync(agentId);\n const runtimeState = getAgentRuntimeStateSync(agentId);\n const hasAgentState = !!agentState;\n const sessionId = getLatestSessionIdSync(agentId) ?? null;\n const hasSavedSession = !!sessionId;\n const hasLiveTmuxSession = sessionExistsSync(agentId);\n const hasWorkspace = !!agentState?.workspace && existsSync(agentState.workspace);\n const hasResumableTranscript = !sessionId\n || (!!agentState?.harness && agentState.harness !== 'claude-code')\n || !agentState?.workspace\n || claudeSessionTranscriptExists(agentState.workspace, sessionId);\n const agentStatus = agentState?.status || 'unknown';\n const runtime = runtimeState?.state || 'uninitialized';\n const isCompleted = runtimeState?.resolution === 'completed';\n const isPlaceholder = !!agentState && agentStatus === 'starting' && typeof agentState.model === 'string' && agentState.model.startsWith('pending-');\n const isStopped = agentStatus === 'stopped' || agentStatus === 'error' || isCompleted || runtime === 'stopped' || runtime === 'idle' || runtime === 'suspended';\n const isRunning = (agentStatus === 'running' || isPlaceholder) && hasLiveTmuxSession;\n const isCrashed = (agentStatus === 'running' || isPlaceholder) && !hasLiveTmuxSession;\n // Running-but-stuck: live session + running status, but the runtime is idle or suspended\n // (e.g. the model returned errors and stopped producing output). The tmux session exists but\n // the agent is no longer making progress — it needs a resume, not a message.\n const isRunningButStuck = isRunning && (runtime === 'idle' || runtime === 'suspended');\n const hasResumableBackingState = hasAgentState && hasWorkspace && !isPlaceholder;\n const handedOff = agentState ? hasCompletionMarkerForAgent(agentState) : false;\n const owesRework = handedOff && issueOwesReworkSync(agentState?.issueId);\n const canWarmResumeAfterHandoff = owesRework && !isRunning && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed);\n const isOrphaned = !hasLiveTmuxSession && (\n (hasSavedSession && !hasResumableBackingState)\n || (hasAgentState && (!hasWorkspace || isPlaceholder))\n );\n // A saved resumable session is never discarded by a plain start. The explicit\n // --fresh intent is evaluated by assertCanStartFreshSync before the state wipe.\n const requiresSessionResetBeforeFreshStart = hasSavedSession && hasResumableTranscript && !hasLiveTmuxSession && hasResumableBackingState && (isStopped || isCrashed);\n\n let recommendedAction: WorkAgentRecommendedAction = 'start';\n let reason: string | undefined;\n\n if (isRunningButStuck) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a live session but its runtime is ${runtime} — it is no longer making progress. Use 'pan resume ${agentOrIssueId}' to restart it.`;\n } else if (hasLiveTmuxSession && agentStatus === 'running') {\n recommendedAction = 'none';\n reason = `Agent ${agentId} is already running. Use 'pan tell' to message it.`;\n } else if (canWarmResumeAfterHandoff) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} handed off its work but the pipeline now owes it rework (failed verification, blocked/failed review, or failed test). Use 'pan resume ${agentOrIssueId}' to continue its warm session with the pending feedback (PAN-3555).`;\n } else if (handedOff) {\n // PAN-3334: a handed-off agent has nothing to resume. Offering Resume here\n // only relaunches a finished transcript (which the harness then compacts)\n // and strands the session — continuation nets refuse to re-drive it by\n // design (PAN-2974). The review → feedback loop is unaffected: it\n // resurrects through resumeAgent() directly, not through this read door.\n recommendedAction = 'none';\n reason = `Agent ${agentId} finished and handed off its work (completion marker on disk) — there is nothing to resume. The session is preserved for inspection; message it with 'pan tell ${agentOrIssueId}', or start over with 'pan start ${agentOrIssueId} --fresh'.`;\n } else if (hasLiveTmuxSession && isStopped) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a live tmux session but is stopped. Use 'pan resume ${agentOrIssueId}' to continue or 'pan start ${agentOrIssueId}' will kill the session and start fresh.`;\n } else if (isOrphaned) {\n recommendedAction = 'start';\n reason = hasSavedSession\n ? `Agent ${agentId} has stale/orphaned session metadata without a resumable workspace-backed agent state. Start Agent should create a fresh session.`\n : `Agent ${agentId} is an orphaned placeholder/stale record. Start Agent should create a fresh session.`;\n } else if (requiresSessionResetBeforeFreshStart) {\n recommendedAction = 'resume';\n reason = sessionResetRequiredReason(agentId, agentOrIssueId);\n } else if (hasSavedSession && !hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed)) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has a saved Claude session id but its transcript is missing on disk (jsonl-missing). Start Agent will create a fresh session in the existing workspace.`;\n } else if (hasAgentState && !hasSavedSession && isStopped) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} is stopped and has no saved Claude session. Start Agent will create a fresh session in the existing workspace.`;\n } else if (!hasAgentState && !hasSavedSession) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has no prior resumable session. Start Agent will create a fresh workspace-backed session.`;\n }\n\n return {\n agentId,\n hasAgentState,\n hasLiveTmuxSession,\n hasSavedSession,\n hasResumableTranscript,\n hasWorkspace,\n isPlaceholder,\n isOrphaned,\n isRunning,\n isRunningButStuck,\n isStopped,\n isCompleted,\n isCrashed,\n handedOff,\n owesRework,\n runtimeState: runtime,\n agentStatus,\n canStartFresh: (!hasLiveTmuxSession || (hasLiveTmuxSession && isStopped)) && (!requiresSessionResetBeforeFreshStart || isOrphaned),\n // A live, actively-running agent (isRunning=true, isRunningButStuck=false) is already in\n // session — no resume needed. Stuck agents (isRunning=true, isRunningButStuck=true) must\n // use the dedicated isRunningButStuck flag at call sites; canResumeSession stays false for\n // them so `isRunning` and `canResumeSession` are never simultaneously true.\n canResumeSession: !isRunning && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed) && (!handedOff || owesRework),\n canRestartWithContext: hasAgentState && hasWorkspace,\n canResetSession: hasSavedSession && hasResumableTranscript && hasResumableBackingState,\n requiresSessionResetBeforeFreshStart,\n recommendedAction,\n reason,\n };\n}\n\nasync function getWorkAgentLifecycleStateSnapshot(agentOrIssueId: string): Promise<WorkAgentLifecycleState> {\n const agentId = normalizeAgentId(agentOrIssueId);\n const agentState = await Effect.runPromise(getAgentState(agentId));\n const runtimeState = await Effect.runPromise(getAgentRuntimeState(agentId));\n const hasAgentState = !!agentState;\n const sessionId = await Effect.runPromise(getLatestSessionId(agentId)) ?? null;\n const hasSavedSession = !!sessionId;\n const hasLiveTmuxSession = await Effect.runPromise(sessionExists(agentId));\n const hasWorkspace = !!agentState?.workspace && await pathExists(agentState.workspace);\n const hasResumableTranscript = !sessionId\n || (!!agentState?.harness && agentState.harness !== 'claude-code')\n || !agentState?.workspace\n || claudeSessionTranscriptExists(agentState.workspace, sessionId);\n const agentStatus = agentState?.status || 'unknown';\n const runtime = runtimeState?.state || 'uninitialized';\n const isCompleted = runtimeState?.resolution === 'completed';\n const isPlaceholder = !!agentState && agentStatus === 'starting' && typeof agentState.model === 'string' && agentState.model.startsWith('pending-');\n const isStopped = agentStatus === 'stopped' || agentStatus === 'error' || isCompleted || runtime === 'stopped' || runtime === 'idle' || runtime === 'suspended';\n const isRunning = (agentStatus === 'running' || isPlaceholder) && hasLiveTmuxSession;\n const isCrashed = (agentStatus === 'running' || isPlaceholder) && !hasLiveTmuxSession;\n const isRunningButStuck = isRunning && (runtime === 'idle' || runtime === 'suspended');\n const hasResumableBackingState = hasAgentState && hasWorkspace && !isPlaceholder;\n const handedOff = agentState ? hasCompletionMarkerForAgent(agentState) : false;\n const owesRework = handedOff && issueOwesReworkSync(agentState?.issueId);\n const canWarmResumeAfterHandoff = owesRework && !isRunning && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed);\n const isOrphaned = !hasLiveTmuxSession && (\n (hasSavedSession && !hasResumableBackingState)\n || (hasAgentState && (!hasWorkspace || isPlaceholder))\n );\n // A saved resumable session is never discarded by a plain start. The explicit\n // --fresh intent is evaluated by assertCanStartFreshSync before the state wipe.\n const requiresSessionResetBeforeFreshStart = hasSavedSession && hasResumableTranscript && !hasLiveTmuxSession && hasResumableBackingState && (isStopped || isCrashed);\n\n let recommendedAction: WorkAgentRecommendedAction = 'start';\n let reason: string | undefined;\n\n if (isRunningButStuck) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a live session but its runtime is ${runtime} — it is no longer making progress. Use 'pan resume ${agentOrIssueId}' to restart it.`;\n } else if (hasLiveTmuxSession && agentStatus === 'running') {\n recommendedAction = 'none';\n reason = `Agent ${agentId} is already running. Use 'pan tell' to message it.`;\n } else if (canWarmResumeAfterHandoff) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} handed off its work but the pipeline now owes it rework (failed verification, blocked/failed review, or failed test). Use 'pan resume ${agentOrIssueId}' to continue its warm session with the pending feedback (PAN-3555).`;\n } else if (handedOff) {\n // PAN-3334: a handed-off agent has nothing to resume. Offering Resume here\n // only relaunches a finished transcript (which the harness then compacts)\n // and strands the session — continuation nets refuse to re-drive it by\n // design (PAN-2974). The review → feedback loop is unaffected: it\n // resurrects through resumeAgent() directly, not through this read door.\n recommendedAction = 'none';\n reason = `Agent ${agentId} finished and handed off its work (completion marker on disk) — there is nothing to resume. The session is preserved for inspection; message it with 'pan tell ${agentOrIssueId}', or start over with 'pan start ${agentOrIssueId} --fresh'.`;\n } else if (hasLiveTmuxSession && isStopped) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a live tmux session but is stopped. Use 'pan resume ${agentOrIssueId}' to continue or 'pan start ${agentOrIssueId}' will kill the session and start fresh.`;\n } else if (isOrphaned) {\n recommendedAction = 'start';\n reason = hasSavedSession\n ? `Agent ${agentId} has stale/orphaned session metadata without a resumable workspace-backed agent state. Start Agent should create a fresh session.`\n : `Agent ${agentId} is an orphaned placeholder/stale record. Start Agent should create a fresh session.`;\n } else if (requiresSessionResetBeforeFreshStart) {\n recommendedAction = 'resume';\n reason = sessionResetRequiredReason(agentId, agentOrIssueId);\n } else if (hasSavedSession && !hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed)) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has a saved Claude session id but its transcript is missing on disk (jsonl-missing). Start Agent will create a fresh session in the existing workspace.`;\n } else if (hasAgentState && !hasSavedSession && isStopped) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} is stopped and has no saved Claude session. Start Agent will create a fresh session in the existing workspace.`;\n } else if (!hasAgentState && !hasSavedSession) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has no prior resumable session. Start Agent will create a fresh workspace-backed session.`;\n }\n\n return {\n agentId,\n hasAgentState,\n hasLiveTmuxSession,\n hasSavedSession,\n hasResumableTranscript,\n hasWorkspace,\n isPlaceholder,\n isOrphaned,\n isRunning,\n isRunningButStuck,\n isStopped,\n isCompleted,\n isCrashed,\n handedOff,\n owesRework,\n runtimeState: runtime,\n agentStatus,\n canStartFresh: (!hasLiveTmuxSession || (hasLiveTmuxSession && isStopped)) && (!requiresSessionResetBeforeFreshStart || isOrphaned),\n // A live, actively-running agent (isRunning=true, isRunningButStuck=false) is already in\n // session — no resume needed. Stuck agents (isRunning=true, isRunningButStuck=true) must\n // use the dedicated isRunningButStuck flag at call sites; canResumeSession stays false for\n // them so `isRunning` and `canResumeSession` are never simultaneously true.\n // PAN-2908: the async snapshot used to omit hasSavedSession here — every stopped agent with\n // a workspace looked resumable, so the CTA offered Resume with nothing to resume (PAN-806).\n canResumeSession: !isRunning && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed) && (!handedOff || owesRework),\n canRestartWithContext: hasAgentState && hasWorkspace,\n canResetSession: hasSavedSession && hasResumableTranscript && hasResumableBackingState,\n requiresSessionResetBeforeFreshStart,\n recommendedAction,\n reason,\n };\n}\n\ninterface StartFreshOptions {\n allowPausedForce?: boolean;\n allowLiveSessionReplacement?: boolean;\n /** True only when the caller carries an explicit operator `--fresh` intent.\n * PAN-3555: without it, a handed-off agent that owes rework and has a\n * resumable warm session must be refused a fresh start — plain `pan start`\n * silently abandoning the warm transcript is exactly the bug. Explicit\n * `--fresh` keeps working (the PAN-3543 escape from the reset deadlock). */\n explicitFresh?: boolean;\n}\n\nexport function assertCanStartFreshSync(agentOrIssueId: string, options: StartFreshOptions = {}): WorkAgentLifecycleState {\n const lifecycle = getWorkAgentLifecycleStateSync(agentOrIssueId);\n const pausedForceOverride = options.allowPausedForce === true\n && lifecycle.requiresSessionResetBeforeFreshStart\n && getAgentStateSync(lifecycle.agentId)?.paused === true;\n const liveSessionReplacement = options.allowLiveSessionReplacement === true && lifecycle.isRunning;\n if (liveSessionReplacement && lifecycle.canResetSession) {\n throw new Error(sessionResetRequiredReason(lifecycle.agentId, agentOrIssueId));\n }\n if (options.explicitFresh !== true && lifecycle.owesRework && lifecycle.canResumeSession) {\n throw new Error(\n `Agent ${lifecycle.agentId} handed off its work but the pipeline owes it rework, and its warm session is resumable. `\n + `Use 'pan resume ${agentOrIssueId}' to continue that session with the pending feedback, or pass --fresh to deliberately start a new one (PAN-3555).`,\n );\n }\n if (!canStartFresh(lifecycle, options.explicitFresh === true) && !pausedForceOverride && !liveSessionReplacement) {\n throw new Error(lifecycle.reason || `Cannot start fresh for ${lifecycle.agentId}`);\n }\n return lifecycle;\n}\n\nexport function assertCanResumeSessionSync(agentOrIssueId: string): WorkAgentLifecycleState {\n const lifecycle = getWorkAgentLifecycleStateSync(agentOrIssueId);\n if (!lifecycle.canResumeSession && !lifecycle.isRunningButStuck) {\n throw new Error(lifecycle.reason || `Cannot resume session for ${lifecycle.agentId}`);\n }\n return lifecycle;\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Asserts about agent lifecycle (cannot start fresh / cannot resume) fail in\n * the typed error channel as `WorkAgentLifecycleViolation`.\n */\nexport class WorkAgentLifecycleViolation extends Data.TaggedError('WorkAgentLifecycleViolation')<{\n readonly agentId: string;\n readonly reason: string;\n}> {}\n\nexport const getWorkAgentLifecycleState = (\n agentOrIssueId: string,\n): Effect.Effect<WorkAgentLifecycleState> =>\n Effect.promise(() => getWorkAgentLifecycleStateSnapshot(agentOrIssueId));\n\n/** Assert the agent can start fresh; lifts the synchronous throw to a typed error. */\nexport const assertCanStartFresh = (\n agentOrIssueId: string,\n options: { allowPausedForce?: boolean } = {},\n): Effect.Effect<WorkAgentLifecycleState, WorkAgentLifecycleViolation> =>\n Effect.try({\n try: () => assertCanStartFreshSync(agentOrIssueId, options),\n catch: (cause) =>\n new WorkAgentLifecycleViolation({\n agentId: agentOrIssueId,\n reason: cause instanceof Error ? cause.message : String(cause),\n }),\n });\n\n/** Assert the agent can resume; lifts the synchronous throw to a typed error. */\nexport const assertCanResumeSession = (\n agentOrIssueId: string,\n): Effect.Effect<WorkAgentLifecycleState, WorkAgentLifecycleViolation> =>\n Effect.try({\n try: () => assertCanResumeSessionSync(agentOrIssueId),\n catch: (cause) =>\n new WorkAgentLifecycleViolation({\n agentId: agentOrIssueId,\n reason: cause instanceof Error ? cause.message : String(cause),\n }),\n });\n","/**\n * Git info enricher (PAN-1523)\n *\n * Derives branch + worktree-status for an absolute filesystem path. Used to\n * surface branch/worktree chips on conversation rows, conversation terminals,\n * and agent terminals.\n *\n * Two enricher entrypoints with different semantics:\n * - resolveConversationGitInfo() — best-effort: returns null branch when\n * the path isn't a git repo. Used for conversations whose cwd can be\n * anywhere.\n * - resolveAgentGitInfo() — verifies a work-agent workspace against its\n * expected feature branch. Reports drift and missing-workspace state\n * because both are silent failure modes today.\n *\n * Cache: in-memory, keyed by absolute path and invalidated when the mtime of\n * <path>/.git/HEAD changes. Every read still stats HEAD, so a checkout under a\n * live conversation is picked up immediately without periodically respawning\n * two git processes for every unchanged workspace.\n *\n * All I/O is async (execFile / fs.promises). No execSync, no readFileSync.\n */\n\nimport { execFile } from 'node:child_process';\nimport { readFile, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nconst GIT_INFO_TIMEOUT_MS = 2_000;\n\nexport interface ConversationGitInfo {\n /** Current branch (HEAD), null if cwd is not a git repo. */\n branch: string | null;\n /** True when cwd points at a secondary git worktree (not the primary checkout). */\n isWorktree: boolean;\n}\n\nexport interface AgentGitInfo {\n /** Current branch (HEAD), null if the workspace path is missing or not a git repo. */\n actualBranch: string | null;\n /** True when actualBranch differs from the expected feature branch. */\n branchDrifted: boolean;\n /** True when the workspacePath does not exist on disk, or is no longer a git worktree. */\n workspaceMissing: boolean;\n}\n\ninterface CacheEntry {\n value: ConversationGitInfo;\n headMtimeMs: number;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\n/**\n * Locate the HEAD file for a working tree at `absPath`.\n *\n * In a primary checkout `.git` is a directory and HEAD lives at `.git/HEAD`.\n * In a secondary worktree `.git` is a small text file containing\n * `gitdir: /abs/path/to/.git/worktrees/<name>` and HEAD lives at\n * `<that-gitdir>/HEAD`. We follow the pointer so the mtime check actually\n * sees branch flips in worktrees.\n */\nasync function locateHeadPath(absPath: string): Promise<string | null> {\n const gitMarker = join(absPath, '.git');\n let markerStat;\n try {\n markerStat = await stat(gitMarker);\n } catch {\n return null;\n }\n\n if (markerStat.isDirectory()) {\n return join(gitMarker, 'HEAD');\n }\n\n if (markerStat.isFile()) {\n try {\n const contents = await readFile(gitMarker, 'utf-8');\n const match = contents.match(/^gitdir:\\s*(.+)$/m);\n if (!match) return null;\n return join(match[1].trim(), 'HEAD');\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\nasync function readHeadMtimeMs(absPath: string): Promise<number | null> {\n const headPath = await locateHeadPath(absPath);\n if (headPath === null) return null;\n try {\n const stats = await stat(headPath);\n return stats.mtimeMs;\n } catch {\n return null;\n }\n}\n\nasync function readGitInfoFresh(absPath: string): Promise<ConversationGitInfo> {\n const [branchResult, worktreeResult] = await Promise.all([\n execFileAsync('git', ['-C', absPath, 'rev-parse', '--abbrev-ref', 'HEAD'], {\n encoding: 'utf-8',\n timeout: GIT_INFO_TIMEOUT_MS,\n }).catch(() => null),\n execFileAsync('git', ['-C', absPath, 'rev-parse', '--git-common-dir', '--git-dir'], {\n encoding: 'utf-8',\n timeout: GIT_INFO_TIMEOUT_MS,\n }).catch(() => null),\n ]);\n\n if (!branchResult) {\n return { branch: null, isWorktree: false };\n }\n\n const branch = branchResult.stdout.trim() || null;\n\n // A secondary worktree has `git-dir` !== `git-common-dir` (worktree-specific\n // dir lives at <primary>/.git/worktrees/<name>; common dir is <primary>/.git).\n // The primary checkout has them equal.\n let isWorktree = false;\n if (worktreeResult) {\n const [commonDir, gitDir] = worktreeResult.stdout.trim().split('\\n').map((line) => line.trim());\n if (commonDir && gitDir && commonDir !== gitDir) {\n isWorktree = true;\n }\n }\n\n return { branch, isWorktree };\n}\n\n/**\n * Resolve branch + worktree status for a conversation cwd. Cached until the\n * HEAD mtime changes.\n */\nexport async function resolveConversationGitInfo(absPath: string): Promise<ConversationGitInfo> {\n const headMtimeMs = await readHeadMtimeMs(absPath);\n if (headMtimeMs === null) {\n return { branch: null, isWorktree: false };\n }\n\n const cached = cache.get(absPath);\n if (cached && cached.headMtimeMs === headMtimeMs) {\n return cached.value;\n }\n\n const value = await readGitInfoFresh(absPath);\n cache.set(absPath, {\n value,\n headMtimeMs,\n });\n return value;\n}\n\n/**\n * Resolve drift + missing-workspace state for a work-agent workspace.\n * `expectedBranch` is the deterministic feature/<issue-id> branch from\n * resource-discovery. Reuses the conversation cache for the branch read,\n * then layers drift detection on top.\n */\nexport async function resolveAgentGitInfo(\n workspacePath: string,\n expectedBranch: string,\n): Promise<AgentGitInfo> {\n const headMtimeMs = await readHeadMtimeMs(workspacePath);\n if (headMtimeMs === null) {\n return {\n actualBranch: null,\n branchDrifted: false,\n workspaceMissing: true,\n };\n }\n\n const info = await resolveConversationGitInfo(workspacePath);\n if (info.branch === null) {\n // .git/HEAD exists but rev-parse failed — corrupt or stale worktree.\n return {\n actualBranch: null,\n branchDrifted: false,\n workspaceMissing: true,\n };\n }\n\n return {\n actualBranch: info.branch,\n branchDrifted: info.branch !== expectedBranch,\n workspaceMissing: false,\n };\n}\n\n/** Test-only: reset the cache between unit tests. */\nexport function _resetGitInfoCacheForTests(): void {\n cache.clear();\n}\n","import type { ChildProcess } from 'node:child_process';\n\nimport type { ComposerCommandResult } from '@overdeck/contracts';\nimport { spawnPanCli } from '../pan-cli-invocation.js';\n\nexport const CAPTURED_COMMAND_MAX_OUTPUT_BYTES = 64 * 1024;\nexport const CAPTURED_COMMAND_TIMEOUT_MS = 30_000;\n\nconst TRUNCATION_MESSAGE = '\\n\\nOutput was truncated after 65,536 bytes.';\n\nexport interface CapturedCommandDependencies {\n spawnPanCli?: typeof spawnPanCli;\n timeoutMs?: number;\n}\n\nexport function runCapturedCommand(\n argv: readonly string[],\n dependencies: CapturedCommandDependencies = {},\n): Promise<ComposerCommandResult> {\n const command = `/pan ${argv.join(' ')}`;\n const spawnCommand = dependencies.spawnPanCli ?? spawnPanCli;\n const timeoutMs = dependencies.timeoutMs ?? CAPTURED_COMMAND_TIMEOUT_MS;\n const child = spawnCommand(argv, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n return new Promise(resolve => {\n const chunks: Buffer[] = [];\n let bufferedBytes = 0;\n let settled = false;\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let truncated = false;\n\n const appendOutput = (data: string | Buffer) => {\n if (truncated) return;\n const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);\n const maximumPrefixBytes = CAPTURED_COMMAND_MAX_OUTPUT_BYTES - Buffer.byteLength(TRUNCATION_MESSAGE);\n const remaining = maximumPrefixBytes - bufferedBytes;\n if (buffer.length <= remaining) {\n chunks.push(buffer);\n bufferedBytes += buffer.length;\n return;\n }\n if (remaining > 0) {\n chunks.push(buffer.subarray(0, remaining));\n bufferedBytes += remaining;\n }\n truncated = true;\n };\n\n const output = () => {\n const captured = Buffer.concat(chunks, bufferedBytes).toString('utf8');\n return truncated ? `${captured}${TRUNCATION_MESSAGE}` : captured;\n };\n\n const finish = (result: ComposerCommandResult) => {\n if (settled) return;\n settled = true;\n if (timeout !== undefined) clearTimeout(timeout);\n resolve(result);\n };\n\n child.stdout?.on('data', appendOutput);\n child.stderr?.on('data', appendOutput);\n\n child.once('error', error => {\n finish({\n kind: 'captured',\n status: 'failed',\n command,\n output: `Command ${command} could not start: ${error.message}`,\n truncated: false,\n });\n });\n\n child.once('close', code => {\n const captured = output();\n finish({\n kind: 'captured',\n status: code === 0 ? 'completed' : 'failed',\n command,\n output: captured || `Command ${command} ${code === 0 ? 'completed without output.' : `failed with exit code ${code ?? 'unknown'}.`}`,\n truncated,\n });\n });\n\n timeout = setTimeout(() => {\n stopChild(child);\n finish({\n kind: 'captured',\n status: 'failed',\n command,\n output: `Command ${command} timed out after ${Math.round(timeoutMs / 1000)} seconds and was stopped.`,\n truncated: false,\n });\n }, timeoutMs);\n });\n}\n\nfunction stopChild(child: ChildProcess): void {\n child.kill('SIGTERM');\n}\n","import type { ChildProcess } from 'node:child_process';\nimport { appendFile, mkdir, open } from 'node:fs/promises';\nimport { isAbsolute, join, resolve } from 'node:path';\n\nimport type { ComposerCommandResult } from '@overdeck/contracts';\nimport { emitActivityEntryDurable, type EmitActivityOptions } from '../activity-logger.js';\nimport { getDashboardLoopbackApiUrlSync } from '../config.js';\nimport { parseIssueIdSync } from '../issue-id.js';\nimport { spawnPanCli } from '../pan-cli-invocation.js';\nimport { getOverdeckHome } from '../paths.js';\nimport { CAPTURED_COMMAND_MAX_OUTPUT_BYTES } from './executors.js';\n\nexport interface DetachedPanCommandInput {\n agentSessionName: string;\n issueId: string;\n role: string;\n workspacePath: string;\n args: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n activityId?: string;\n}\n\nexport interface DetachedPanCommandLaunch {\n activityId: string;\n spawnLogPath: string;\n spawnLogStart: number;\n completion: Promise<void>;\n}\n\nexport interface DetachedPanCommandDependencies {\n now?: () => number;\n overdeckHome?: string;\n spawnPanCli?: typeof spawnPanCli;\n emitActivity?: (options: EmitActivityOptions) => Promise<void>;\n}\n\nconst DETACHED_TRUNCATION_MESSAGE = '\\n\\nOutput was truncated after 65,536 bytes.';\n\nfunction resolveAgentDirectory(overdeckHome: string, agentId: string): string {\n if (\n agentId.length === 0 ||\n agentId === '.' ||\n agentId === '..' ||\n agentId.includes('/') ||\n agentId.includes('\\\\') ||\n isAbsolute(agentId)\n ) {\n throw new Error('Agent ID must be a single filesystem-safe path segment.');\n }\n return join(resolve(overdeckHome, 'agents'), agentId);\n}\n\nasync function readBoundedSpawnOutput(spawnLogPath: string, startOffset: number): Promise<string> {\n const maximumPrefixBytes = CAPTURED_COMMAND_MAX_OUTPUT_BYTES - Buffer.byteLength(DETACHED_TRUNCATION_MESSAGE);\n const handle = await open(spawnLogPath, 'r');\n try {\n const stats = await handle.stat();\n const outputBytes = Math.max(0, stats.size - startOffset);\n if (outputBytes === 0) return '';\n const bytesToRead = Math.min(outputBytes, maximumPrefixBytes);\n const buffer = Buffer.alloc(bytesToRead);\n const { bytesRead } = await handle.read(buffer, 0, bytesToRead, startOffset);\n const output = buffer.subarray(0, bytesRead).toString('utf8');\n return outputBytes > maximumPrefixBytes\n ? `${output}${DETACHED_TRUNCATION_MESSAGE}`\n : output;\n } finally {\n await handle.close().catch(() => undefined);\n }\n}\n\nexport async function appendAgentLifecycleLog(\n agentId: string,\n event: string,\n details: Record<string, unknown> = {},\n overdeckHome = getOverdeckHome(),\n): Promise<void> {\n const agentDir = resolveAgentDirectory(overdeckHome, agentId);\n await mkdir(agentDir, { recursive: true });\n const logLine = JSON.stringify({\n ts: new Date().toISOString(),\n event,\n ...details,\n });\n await appendFile(join(agentDir, 'lifecycle.log'), `${logLine}\\n`);\n}\n\nexport async function launchPanCommandDetached(\n input: DetachedPanCommandInput,\n dependencies: DetachedPanCommandDependencies = {},\n): Promise<DetachedPanCommandLaunch> {\n const { agentSessionName, issueId, role, workspacePath, args } = input;\n const cwd = input.cwd ?? workspacePath;\n const overdeckHome = dependencies.overdeckHome ?? getOverdeckHome();\n const activityId = input.activityId ?? `activity-${(dependencies.now ?? Date.now)()}`;\n const agentDir = resolveAgentDirectory(overdeckHome, agentSessionName);\n await mkdir(agentDir, { recursive: true });\n const spawnLogPath = join(agentDir, 'spawn.log');\n const spawnLogHandle = await open(spawnLogPath, 'a');\n const spawnLogStart = (await spawnLogHandle.stat()).size;\n const spawnCommand = dependencies.spawnPanCli ?? spawnPanCli;\n // The server's env carries DASHBOARD_URL as its PUBLIC url (behind Traefik,\n // local TLS). A spawned CLI resolving its API target from that url fails\n // Node's cert check with a bare \"fetch failed\" (PAN-3331) — pin children to\n // the loopback API of the server that spawned them instead.\n const childEnv: NodeJS.ProcessEnv = { ...process.env, ...input.env };\n if (!input.env?.OVERDECK_DASHBOARD_URL) {\n delete childEnv.DASHBOARD_URL;\n childEnv.OVERDECK_DASHBOARD_URL = getDashboardLoopbackApiUrlSync();\n }\n const child = spawnCommand(args, {\n cwd,\n detached: true,\n stdio: ['ignore', spawnLogHandle.fd, spawnLogHandle.fd],\n env: childEnv,\n });\n\n let spawned = false;\n let settleSpawn: (() => void) | undefined;\n let rejectSpawn: ((error: Error) => void) | undefined;\n const spawnPromise = new Promise<void>((resolve, reject) => {\n settleSpawn = resolve;\n rejectSpawn = reject;\n });\n const completion = commandCompletion(child, {\n agentSessionName,\n issueId,\n role,\n workspacePath,\n activityId,\n args,\n cwd,\n spawnLogPath,\n spawnLogStart,\n spawnLogHandle,\n overdeckHome,\n onSpawn: () => {\n spawned = true;\n settleSpawn?.();\n },\n onError: error => {\n if (!spawned) rejectSpawn?.(error);\n },\n });\n void completion.catch(() => undefined);\n\n await spawnPromise;\n child.unref();\n return { activityId, spawnLogPath, spawnLogStart, completion };\n}\n\ninterface CompletionContext {\n agentSessionName: string;\n issueId: string;\n role: string;\n workspacePath: string;\n activityId: string;\n args: string[];\n cwd: string;\n spawnLogPath: string;\n spawnLogStart: number;\n spawnLogHandle: Awaited<ReturnType<typeof open>>;\n overdeckHome: string;\n onSpawn(): void;\n onError(error: Error): void;\n}\n\nfunction commandCompletion(child: ChildProcess, context: CompletionContext): Promise<void> {\n child.once('spawn', () => {\n void appendAgentLifecycleLog(context.agentSessionName, 'agent.work_spawn_process_spawned', {\n issueId: context.issueId,\n role: context.role,\n workspacePath: context.workspacePath,\n activityId: context.activityId,\n pid: child.pid,\n args: context.args,\n cwd: context.cwd,\n spawnLogPath: context.spawnLogPath,\n }, context.overdeckHome)\n .catch(() => undefined)\n .finally(context.onSpawn);\n });\n\n return new Promise((resolve, reject) => {\n let settled = false;\n const finish = async (error?: Error) => {\n if (settled) return;\n settled = true;\n await context.spawnLogHandle.close().catch(() => undefined);\n if (error) reject(error);\n else resolve();\n };\n\n child.once('error', error => {\n void (async () => {\n await appendAgentLifecycleLog(context.agentSessionName, 'agent.work_spawn_process_error', {\n issueId: context.issueId,\n role: context.role,\n workspacePath: context.workspacePath,\n activityId: context.activityId,\n error: error.message,\n args: context.args,\n cwd: context.cwd,\n spawnLogPath: context.spawnLogPath,\n }, context.overdeckHome).catch(() => undefined);\n context.onError(error);\n await finish(error);\n })();\n });\n\n child.once('close', (code, signal) => {\n void (async () => {\n await appendAgentLifecycleLog(context.agentSessionName, 'agent.work_spawn_process_closed', {\n issueId: context.issueId,\n role: context.role,\n workspacePath: context.workspacePath,\n activityId: context.activityId,\n code,\n signal,\n args: context.args,\n cwd: context.cwd,\n spawnLogPath: context.spawnLogPath,\n }, context.overdeckHome).catch(() => undefined);\n if (code === 0) {\n await finish();\n return;\n }\n const output = await readBoundedSpawnOutput(context.spawnLogPath, context.spawnLogStart).catch(() => '');\n const error = new Error(output.trim() || `pan ${context.args.join(' ')} exited with code ${code ?? 'null'}`);\n Object.assign(error, {\n activityId: context.activityId,\n spawnLogPath: context.spawnLogPath,\n code,\n signal,\n output,\n });\n await finish(error);\n })();\n });\n });\n}\n\nexport async function runDetachedCommand(\n argv: string[],\n dependencies: DetachedPanCommandDependencies = {},\n): Promise<ComposerCommandResult> {\n const issueId = argv[1] ?? '';\n const parsedIssueId = parseIssueIdSync(issueId);\n if (!parsedIssueId) {\n return {\n kind: 'terminal-only',\n status: 'rejected',\n message: `/pan ${argv[0] ?? 'command'} requires a canonical issue ID such as PAN-1525.`,\n };\n }\n const role = argv[0] === 'plan' ? 'plan' : 'work';\n const agentSessionName = `${role === 'plan' ? 'planning' : 'agent'}-${parsedIssueId.normalized}`;\n const command = `/pan ${argv.join(' ')}`;\n const activityId = `activity-${(dependencies.now ?? Date.now)()}`;\n const emitActivity = dependencies.emitActivity ?? emitActivityEntryDurable;\n const source = role === 'plan' ? 'planning-agent' : 'work-agent';\n const emitTransition = (\n status: 'accepted' | 'running' | 'completed' | 'failed',\n level: 'info' | 'success' | 'error',\n message: string,\n output?: string,\n ) => emitActivity({\n id: activityId,\n source,\n level,\n status,\n command,\n message,\n details: output,\n output,\n issueId,\n });\n\n await emitTransition(\n 'accepted',\n 'info',\n `Accepted ${command} for ${issueId} as activity ${activityId}.`,\n );\n\n let launch: DetachedPanCommandLaunch;\n try {\n launch = await launchPanCommandDetached({\n agentSessionName,\n issueId,\n role,\n workspacePath: process.cwd(),\n args: argv,\n activityId,\n }, dependencies);\n } catch (error) {\n const output = error instanceof Error ? error.message : String(error);\n await emitTransition(\n 'failed',\n 'error',\n `Activity ${activityId} failed to start ${command} for ${issueId}.`,\n output,\n ).catch(() => undefined);\n throw error;\n }\n\n await emitTransition(\n 'running',\n 'info',\n `Activity ${activityId} is running ${command} for ${issueId}.`,\n ).catch(() => undefined);\n\n void launch.completion.then(async () => {\n const output = await readBoundedSpawnOutput(launch.spawnLogPath, launch.spawnLogStart).catch(() => '');\n await emitTransition(\n 'completed',\n 'success',\n `Activity ${activityId} completed ${command} for ${issueId}.`,\n output || `Command ${command} completed without output.`,\n );\n }, async error => {\n const activityError = error as Error & { output?: string };\n const output = activityError.output?.trim() || activityError.message;\n await emitTransition(\n 'failed',\n 'error',\n `Activity ${activityId} failed ${command} for ${issueId}.`,\n output,\n );\n }).catch(() => undefined);\n\n return {\n kind: 'activity',\n status: 'accepted',\n command,\n activityId: launch.activityId,\n message: `Started ${command} for ${issueId}. Watch activity ${launch.activityId} or the ${issueId} issue view for progress.`,\n };\n}\n","import { exec, execFile } from 'node:child_process';\nimport { timingSafeEqual } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\n\nimport { Effect } from 'effect';\nimport { HttpServerRequest } from 'effect/unstable/http';\nimport type { AgentStatus } from '@overdeck/contracts';\n\nimport { jsonResponse } from '../../http-helpers.js';\nimport { getHeaderFromMap } from '../origin-validation.js';\nimport { claudeSessionTranscriptExists, getOverdeckHome } from '../../../../lib/paths.js';\nimport { resolvePrimaryWorkspaceRepoDirSync } from '../../../../lib/project-repos.js';\nimport {\n appendAgentLifecycleLog,\n launchPanCommandDetached,\n type DetachedPanCommandDependencies,\n} from '../../../../lib/composer-commands/detached.js';\nimport {\n getAgentDir,\n getLatestSessionIdSync,\n normalizeAgentId,\n type AgentRuntimeState,\n type AgentState,\n} from '../../../../lib/agents.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { getGitHubConfig } from '../../services/tracker-config.js';\nimport { getClosedIssueIdsForReadSource } from '../../read-model.js';\nimport { recordFeatureRegistryLifecycle } from '../../../../lib/registry/feature-registry-population.js';\nimport {\n getClaudeProjectDir as getClaudeProjectDirShared,\n getActiveSessionPath as getActiveSessionPathShared,\n getAgentWorkspace as getAgentWorkspaceShared,\n getAgentJsonlPath as getAgentJsonlPathShared,\n getPendingQuestions as getPendingQuestionsShared,\n getAgentPendingQuestions as getAgentPendingQuestionsShared,\n} from '../../../../lib/agent-enrichment.js';\nimport { issueOwesReworkSync, type WorkAgentLifecycleState, type WorkAgentRecommendedAction } from '../../../../lib/work-agent-lifecycle.js';\nimport { hasCompletionMarkerForAgent } from '../../../../lib/agents/supervisor-channels.js';\nimport { emitActivityEntrySync } from '../../../../lib/activity-logger.js';\nimport { getResourceConfig, type HealthLeakedSpecialist, type SystemHealthSnapshot } from '../../services/system-health-service.js';\nimport { classifyMemoryPressure } from '../../../../lib/cloister/memory-governor.js';\nimport { capturePane } from '../../../../lib/tmux.js';\nimport type { RuntimeName } from '../../../../lib/runtimes/types.js';\nimport { normalizeFlywheelRunId } from '../../../../lib/agents/provenance.js';\n\nconst execAsync = promisify(exec);\nconst execFileAsync = promisify(execFile);\n\ntype StartAgentPhase = 'stackHealthGate' | 'guardrails' | 'spawn';\n\nexport function buildPanStartArgs(input: {\n issueId: string;\n model: string;\n harness?: RuntimeName | null;\n allowHost?: boolean;\n offBook?: boolean;\n}): string[] {\n return [\n 'start',\n input.issueId,\n '--local',\n '--model',\n input.model,\n ...(input.harness ? ['--harness', input.harness] : []),\n ...(input.allowHost ? ['--host', '--yes'] : []),\n ...(input.offBook ? ['--off-book'] : []),\n ];\n}\n\n/**\n * PAN-1985: detached-spawn helper for `pan <args>`, shared between the\n * standard work-spawn route and the restart-fresh route. The neutral launcher\n * owns process/log lifecycle; this route-compatible wrapper preserves the\n * original behavior by waiting for a successful child exit.\n */\nconst INTERNAL_STARTED_BY_TOKENS = new Set([\n 'operator:cli:pan-start',\n 'operator:cli:pan-plan',\n 'planning-auto-handoff',\n 'orphan-proposed-reconciler',\n 'workspace-rebuild-recovery',\n 'resume-agent',\n]);\n\nexport function resolveRequestedStartedBy(value: unknown, internalRequest = false): string {\n if (!internalRequest) return 'operator:dashboard';\n const token = typeof value === 'string' ? value.trim() : '';\n const flywheelRunId = token.startsWith('flywheel:') ? normalizeFlywheelRunId(token.slice('flywheel:'.length)) : undefined;\n if (flywheelRunId) return `flywheel:${flywheelRunId}`;\n if (!INTERNAL_STARTED_BY_TOKENS.has(token)) throw new Error('Invalid internal startedBy provenance token.');\n return token;\n}\n\nexport async function isInternalAgentRequest(request: HttpServerRequest.HttpServerRequest): Promise<boolean> {\n const expected = await readInternalTokenForRequest();\n if (!expected) return false;\n const provided = getHeaderFromMap(request.headers as Record<string, string | string[] | undefined>, INTERNAL_TOKEN_HEADER);\n return constantTimeTokenEqual(provided, expected);\n}\n\nexport async function spawnPanCommandDetached(input: {\n agentSessionName: string;\n issueId: string;\n role: string;\n workspacePath: string;\n args: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n}, dependencies: DetachedPanCommandDependencies = {}): Promise<string> {\n const launch = await launchPanCommandDetached(input, dependencies);\n await launch.completion;\n return launch.activityId;\n}\ntype StartAgentPhaseStatus = 'start' | 'success' | 'failure' | 'skipped';\n\nfunction emitStartAgentPhase(\n issueId: string,\n phase: StartAgentPhase,\n status: StartAgentPhaseStatus,\n reason: string,\n details: Record<string, unknown> = {},\n): void {\n const timestamp = new Date().toISOString();\n emitActivityEntrySync({\n source: 'start-agent',\n level: status === 'failure' ? 'error' : status === 'skipped' ? 'warn' : 'info',\n message: `start-agent.phase=${phase}`,\n issueId: issueId.toUpperCase(),\n details: JSON.stringify({ issueId: issueId.toUpperCase(), timestamp, phase, status, reason, ...details }),\n });\n}\n\nconst INTERNAL_TOKEN_HEADER = 'x-overdeck-internal-token';\n\nfunction constantTimeTokenEqual(provided: string | undefined, expected: string): boolean {\n if (!provided) return false;\n const providedBuffer = Buffer.from(provided, 'utf8');\n const expectedBuffer = Buffer.from(expected, 'utf8');\n if (providedBuffer.length !== expectedBuffer.length) {\n return false;\n }\n return timingSafeEqual(providedBuffer, expectedBuffer);\n}\n\nasync function readInternalTokenForRequest(): Promise<string | null> {\n const fromEnv = process.env.OVERDECK_INTERNAL_TOKEN;\n if (fromEnv && fromEnv.length > 0) return fromEnv;\n try {\n const token = (await readFile(join(getOverdeckHome(), 'internal-token'), 'utf8')).trim();\n return token.length > 0 ? token : null;\n } catch {\n return null;\n }\n}\n\nexport async function validateAgentRuntimeEventAuth(\n request: HttpServerRequest.HttpServerRequest,\n) {\n const expected = await readInternalTokenForRequest();\n if (!expected) {\n return {\n ok: false as const,\n response: jsonResponse({ success: false, error: 'internal token not configured' }, { status: 503 }),\n };\n }\n\n const provided = getHeaderFromMap(request.headers as Record<string, string | string[] | undefined>, INTERNAL_TOKEN_HEADER);\n if (constantTimeTokenEqual(provided, expected)) return { ok: true as const };\n return {\n ok: false as const,\n response: jsonResponse({ success: false, error: 'forbidden' }, { status: 403 }),\n };\n}\n\nfunction updateRegistryForAgentStart(issueId: string, workspacePath: string, agentId: string): void {\n void recordFeatureRegistryLifecycle({\n issueId,\n workspacePath,\n agentId,\n status: 'active',\n });\n}\n\n\n// ─── Shared IssueDataService singleton ───────────────────────────────────────\n\nfunction getIssueDataService(): import('../../services/issue-data-service.js').IssueDataService {\n const { getSharedIssueService } = require('../../services/issue-service-singleton.js');\n return getSharedIssueService();\n}\n\n// ─── Cache ────────────────────────────────────────────────────────────────────\n\nconst AGENTS_CACHE_TTL_MS = 5000;\nexport const agentsCache: { data: unknown[] | null; timestamp: number } = { data: null, timestamp: 0 };\n\n/** Invalidate the agents cache so the next request re-reads all agent state. */\nexport function invalidateAgentsCache(): void {\n agentsCache.data = null;\n agentsCache.timestamp = 0;\n}\n\nfunction filterClosedIssueAgents<T>(agents: T[], issues: unknown[]): T[] {\n const closedIssueIds = getClosedIssueIdsForReadSource(issues);\n if (closedIssueIds.size === 0) return agents;\n return agents.filter((agent) => {\n if (!agent || typeof agent !== 'object') return true;\n const issueId = (agent as { issueId?: unknown }).issueId;\n return typeof issueId !== 'string' || !closedIssueIds.has(issueId.toUpperCase());\n });\n}\n\n// ─── Local helpers ────────────────────────────────────────────────────────────\n\n// Read the request body as unknown JSON\nconst readJsonBody = Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const text = yield* request.text;\n try {\n return text ? (JSON.parse(text) ?? {}) : {};\n } catch {\n return {};\n }\n});\n\nfunction toAgentStatusPayload(status: AgentState['status'] | undefined): AgentStatus {\n return status === 'starting' || status === 'running' || status === 'stopped' || status === 'error'\n ? status\n : 'unknown';\n}\n\nfunction buildAgentControlEventPayload(state: AgentState, previousStatus: AgentStatus | undefined, hasLiveTmuxSession: boolean) {\n return {\n agentId: state.id,\n issueId: state.issueId,\n status: toAgentStatusPayload(state.status),\n previousStatus,\n hasLiveTmuxSession,\n stoppedByUser: state.stoppedByUser === true,\n paused: state.paused === true,\n pausedReason: state.pausedReason ?? null,\n pausedAt: state.pausedAt ?? null,\n troubled: state.troubled === true,\n troubledAt: state.troubledAt ?? null,\n consecutiveFailures: state.consecutiveFailures ?? 0,\n firstFailureInRunAt: state.firstFailureInRunAt ?? null,\n lastFailureAt: state.lastFailureAt ?? null,\n lastFailureReason: state.lastFailureReason ?? null,\n lastFailureNextRetryAt: state.lastFailureNextRetryAt ?? null,\n };\n}\n\nfunction buildAgentGateFailureSnapshot(state: Partial<AgentState>) {\n return {\n stoppedByUser: state.stoppedByUser === true,\n paused: state.paused === true,\n pausedReason: state.pausedReason ?? null,\n pausedAt: state.pausedAt ?? null,\n troubled: state.troubled === true,\n troubledAt: state.troubledAt ?? null,\n consecutiveFailures: state.consecutiveFailures ?? 0,\n firstFailureInRunAt: state.firstFailureInRunAt ?? null,\n lastFailureAt: state.lastFailureAt ?? null,\n lastFailureReason: state.lastFailureReason ?? null,\n lastFailureNextRetryAt: state.lastFailureNextRetryAt ?? null,\n };\n}\n\nfunction buildStoppedAgentLifecycle(\n agentOrIssueId: string,\n state: Partial<AgentState>,\n runtimeData: Partial<AgentRuntimeState>,\n): WorkAgentLifecycleState {\n const agentId = normalizeAgentId(agentOrIssueId);\n const hasAgentState = true;\n const hasLiveTmuxSession = false;\n // claudeSessionId only covers claude-code agents — codex agents keep their\n // resumable thread in codex-thread-id, which getLatestSessionIdSync resolves\n // (PAN-1988). Without the fallback the listing reports canResumeSession=false\n // for every stopped codex agent and the UI never offers Resume.\n const sessionId = getLatestSessionIdSync(agentId) ?? runtimeData.claudeSessionId ?? null;\n const hasSavedSession = !!sessionId;\n const hasWorkspace = typeof state.workspace === 'string' && state.workspace.length > 0;\n const hasResumableTranscript = !sessionId\n || (!!state.harness && state.harness !== 'claude-code')\n || !state.workspace\n || claudeSessionTranscriptExists(state.workspace, sessionId);\n const agentStatus = state.status || 'unknown';\n const runtime = runtimeData.state || 'uninitialized';\n const isCompleted = runtimeData.resolution === 'completed';\n const isPlaceholder = agentStatus === 'starting' && typeof state.model === 'string' && state.model.startsWith('pending-');\n const isStopped = agentStatus === 'stopped' || agentStatus === 'error' || isCompleted || runtime === 'stopped' || runtime === 'idle' || runtime === 'suspended';\n const isRunning = false;\n const isCrashed = (agentStatus === 'running' || isPlaceholder) && !hasLiveTmuxSession;\n const isRunningButStuck = false;\n const hasResumableBackingState = hasAgentState && hasWorkspace && !isPlaceholder;\n const handedOff = typeof state.id === 'string' && state.id.length > 0\n ? hasCompletionMarkerForAgent(state as AgentState)\n : false;\n // PAN-3555: an owed-rework handoff is resumable again — mirror the canonical door.\n const owesRework = handedOff && issueOwesReworkSync(state.issueId);\n const canWarmResumeAfterHandoff = owesRework && hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed);\n const isOrphaned = !hasLiveTmuxSession && (\n (hasSavedSession && !hasResumableBackingState)\n || (hasAgentState && (!hasWorkspace || isPlaceholder))\n );\n const requiresSessionResetBeforeFreshStart = hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed);\n\n let recommendedAction: WorkAgentRecommendedAction = 'start';\n let reason: string | undefined;\n\n if (isOrphaned) {\n recommendedAction = 'start';\n reason = hasSavedSession\n ? `Agent ${agentId} has stale/orphaned session metadata without a resumable workspace-backed agent state. Start Agent should create a fresh session.`\n : `Agent ${agentId} is an orphaned placeholder/stale record. Start Agent should create a fresh session.`;\n } else if (canWarmResumeAfterHandoff) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} handed off its work but the pipeline now owes it rework (failed verification, blocked/failed review, or failed test). Use 'pan resume ${agentOrIssueId}' to continue its warm session with the pending feedback (PAN-3555).`;\n } else if (handedOff) {\n // PAN-3334: mirror getWorkAgentLifecycleState — a handed-off agent is never\n // offered a plain resume; there is nothing to continue.\n recommendedAction = 'none';\n reason = `Agent ${agentId} finished and handed off its work (completion marker on disk) — there is nothing to resume. The session is preserved for inspection; message it with 'pan tell ${agentOrIssueId}', or start over with 'pan start ${agentOrIssueId} --fresh'.`;\n } else if (requiresSessionResetBeforeFreshStart) {\n recommendedAction = 'resume';\n reason = `Agent ${agentId} has a resumable Claude session. Use 'pan resume ${agentOrIssueId}' to continue it, or run 'pan reset-session ${agentOrIssueId}' before starting a new session.`;\n } else if (hasSavedSession && !hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed)) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} has a saved Claude session id but its transcript is missing on disk (jsonl-missing). Start Agent will create a fresh session in the existing workspace.`;\n } else if (hasAgentState && !hasSavedSession && isStopped) {\n recommendedAction = 'start';\n reason = `Agent ${agentId} is stopped and has no saved Claude session. Start Agent will create a fresh session in the existing workspace.`;\n }\n\n return {\n agentId,\n hasAgentState,\n hasLiveTmuxSession,\n hasSavedSession,\n hasResumableTranscript,\n hasWorkspace,\n isPlaceholder,\n isOrphaned,\n isRunning,\n isRunningButStuck,\n isStopped,\n isCompleted,\n isCrashed,\n handedOff,\n owesRework,\n runtimeState: runtime,\n agentStatus,\n canStartFresh: !requiresSessionResetBeforeFreshStart || isOrphaned,\n canResumeSession: hasSavedSession && hasResumableTranscript && hasResumableBackingState && (isStopped || isCrashed) && (!handedOff || owesRework),\n canRestartWithContext: hasAgentState && hasWorkspace,\n canResetSession: hasSavedSession && hasResumableTranscript && hasResumableBackingState,\n requiresSessionResetBeforeFreshStart,\n recommendedAction,\n reason,\n };\n}\n\nasync function readRemoteAgentState(agentId: string): Promise<Record<string, unknown>> {\n const remoteStateFile = join(homedir(), '.overdeck', 'agents', agentId, 'remote-state.json');\n if (!existsSync(remoteStateFile)) return {};\n try {\n return JSON.parse(await readFile(remoteStateFile, 'utf-8')) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nasync function captureAgentOutputBeforeKill(agentId: string): Promise<void> {\n const output = await Effect.runPromise(\n capturePane(agentId, 5000).pipe(Effect.catch(() => Effect.succeed(''))),\n );\n if (!output) return;\n\n const agentDir = getAgentDir(agentId);\n await mkdir(agentDir, { recursive: true });\n await writeFile(join(agentDir, 'output.log'), output);\n}\n\nfunction buildHostOverrideConfirmation(issueId: string): string {\n return `I understand this bypasses workspace isolation for ${issueId.toUpperCase()}`;\n}\n\nfunction getProjectPath(linearProjectId?: string, issuePrefix?: string): string {\n if (issuePrefix) {\n const issueId = `${issuePrefix}-1`;\n const resolved = resolveProjectFromIssueSync(issueId);\n if (resolved) return resolved.projectPath;\n const config = getGitHubConfig();\n if (config) {\n for (const { owner, repo, prefix } of config.repos) {\n const repoPrefix = prefix || repo.toUpperCase().replace(/-CLI$/, '').replace(/-/g, '');\n if (repoPrefix.toUpperCase() === issuePrefix.toUpperCase()) {\n const possiblePaths = [\n join(homedir(), 'Projects', repo),\n join(homedir(), 'Projects', repo.replace(/-cli$/, '')),\n join(homedir(), 'Projects', owner, repo),\n ];\n for (const path of possiblePaths) {\n if (existsSync(path)) return path;\n }\n }\n }\n }\n }\n return join(homedir(), 'Projects');\n}\n\nasync function getWorkspaceLocation(issueId: string): Promise<'local' | 'remote' | undefined> {\n try {\n const workspacesDir = join(homedir(), '.overdeck', 'workspaces');\n const variations = [issueId.toLowerCase(), issueId.toUpperCase(), issueId];\n for (const v of variations) {\n const yamlPath = join(workspacesDir, `${v}.yaml`);\n if (existsSync(yamlPath)) {\n const content = await readFile(yamlPath, 'utf-8');\n if (content.includes('location: remote')) return 'remote';\n return 'local';\n }\n }\n } catch {}\n return undefined;\n}\n\nasync function getGitStatusAsync(issueId: string, workspacePath: string): Promise<{ branch: string; uncommittedFiles: number; latestCommit: string } | null> {\n try {\n if (!existsSync(workspacePath)) return null;\n const repoDir = resolvePrimaryWorkspaceRepoDirSync(issueId, workspacePath);\n const [branchResult, uncommittedResult, commitResult] = await Promise.all([\n execAsync('git rev-parse --abbrev-ref HEAD 2>/dev/null || echo \"\"', { cwd: repoDir }),\n execAsync('git status --porcelain 2>/dev/null | wc -l', { cwd: repoDir }),\n execAsync('git log -1 --pretty=format:\"%s\" 2>/dev/null || echo \"\"', { cwd: repoDir }),\n ]);\n const branch = branchResult.stdout.trim();\n const uncommitted = uncommittedResult.stdout.trim();\n const latestCommit = commitResult.stdout.trim();\n if (!branch) return null;\n return {\n branch,\n uncommittedFiles: parseInt(uncommitted) || 0,\n latestCommit: latestCommit.slice(0, 60) + (latestCommit.length > 60 ? '...' : ''),\n };\n } catch {\n return null;\n }\n}\n\ninterface SpawnGuardrailAdvisory {\n severity: 'warning' | 'critical';\n code: 'memory_pressure' | 'agent_capacity' | 'leaked_specialists' | 'health_snapshot_unavailable';\n message: string;\n}\n\nexport interface SpawnGuardrailDecision {\n blocked: boolean;\n requiresAcknowledgement: boolean;\n status: number;\n error?: string;\n hint?: string;\n warnings: SpawnGuardrailAdvisory[];\n health: Pick<SystemHealthSnapshot, 'severity' | 'summary' | 'reasons' | 'leakedSpecialists' | 'freshness'>;\n}\n\nfunction formatLeakedSpecialistSummary(leaked: HealthLeakedSpecialist[]): string {\n return leaked\n .slice(0, 3)\n .map((item) => `${item.name} (${item.currentIssue})`)\n .join(', ');\n}\n\nfunction resolveAgentCountEnv(varName: string, fallback: number): number {\n const raw = process.env[varName];\n if (raw == null || raw.trim() === '') return fallback;\n const parsed = Number(raw);\n return Number.isFinite(parsed) ? Math.max(1, Math.floor(parsed)) : fallback;\n}\n\nexport interface AgentStartGateDecision {\n success: false;\n blocked: true;\n skipped: true;\n error: string;\n hint: string;\n agentId: string;\n paused: boolean;\n troubled: boolean;\n}\n\nexport function evaluateAgentStartGate(\n agentId: string,\n state: Pick<AgentState, 'paused' | 'pausedReason' | 'troubled' | 'consecutiveFailures'> | null | undefined,\n): AgentStartGateDecision | null {\n if (state?.paused === true) {\n const reason = state.pausedReason ? ` (${state.pausedReason})` : '';\n return {\n success: false,\n blocked: true,\n skipped: true,\n error: `Agent ${agentId} is paused${reason}.`,\n hint: `Run pan unpause ${agentId} before starting it from the dashboard.`,\n agentId,\n paused: true,\n troubled: state.troubled === true,\n };\n }\n\n if (state?.troubled === true) {\n const failures = state.consecutiveFailures ?? 0;\n return {\n success: false,\n blocked: true,\n skipped: true,\n error: `Agent ${agentId} is troubled (${failures} failure${failures === 1 ? '' : 's'}).`,\n hint: `Investigate the crash cause, then run pan untroubled ${agentId} before starting it from the dashboard.`,\n agentId,\n paused: false,\n troubled: true,\n };\n }\n\n return null;\n}\n\nexport function hasActiveAgentGateOrRetry(\n state: Pick<AgentState, 'paused' | 'troubled' | 'lastFailureNextRetryAt'>,\n nowMs: number = Date.now(),\n): boolean {\n if (state.paused === true || state.troubled === true) return true;\n if (!state.lastFailureNextRetryAt) return false;\n const retryAtMs = Date.parse(state.lastFailureNextRetryAt);\n return Number.isFinite(retryAtMs) && retryAtMs > nowMs;\n}\n\nexport function evaluateSpawnGuardrails(health: SystemHealthSnapshot): SpawnGuardrailDecision {\n const warnings: SpawnGuardrailAdvisory[] = [];\n if (health.freshness.status !== 'fresh') {\n const message = health.freshness.status === 'stale'\n ? `System health evidence is stale: ${health.freshness.reason.message}`\n : 'System health is still measuring; fresh admission evidence is not available yet.';\n warnings.push({\n severity: 'critical',\n code: 'health_snapshot_unavailable',\n message,\n });\n return {\n blocked: true,\n requiresAcknowledgement: false,\n status: 503,\n error: message,\n hint: 'Wait for a fresh system health assessment before starting this agent.',\n warnings,\n health: {\n severity: health.severity,\n summary: health.summary,\n reasons: health.reasons,\n leakedSpecialists: health.leakedSpecialists,\n freshness: health.freshness,\n },\n };\n }\n\n const availableGb = Math.round((health.summary.availableMemoryBytes / (1024 ** 3)) * 10) / 10;\n const workAgentCount = health.admission.admittedWorkAgentCount;\n const leakedSpecialists = health.leakedSpecialists;\n const resourceConfig = getResourceConfig();\n const hardWorkAgentLimit = resolveAgentCountEnv('PAN_AGENT_BLOCK_COUNT', resourceConfig.agentBlockCount);\n const warnWorkAgentLimit = resolveAgentCountEnv('PAN_AGENT_WARN_COUNT', resourceConfig.agentWarnCount);\n\n const memoryBand = classifyMemoryPressure(health.summary.availableMemoryBytes, {\n warningBytes: health.thresholds.memoryAvailableWarningBytes,\n criticalBytes: health.thresholds.memoryAvailableCriticalBytes,\n });\n if (memoryBand === 'hard') {\n warnings.push({\n severity: 'critical',\n code: 'memory_pressure',\n message: `Available RAM is critically low (${availableGb} GB).`,\n });\n } else if (memoryBand === 'soft') {\n warnings.push({\n severity: 'warning',\n code: 'memory_pressure',\n message: `Available RAM is tight (${availableGb} GB).`,\n });\n }\n\n if (workAgentCount >= hardWorkAgentLimit) {\n warnings.push({\n severity: 'warning',\n code: 'agent_capacity',\n message: `Work agent count is at the configured ceiling (${workAgentCount}/${hardWorkAgentLimit}).`,\n });\n } else if (workAgentCount >= warnWorkAgentLimit) {\n warnings.push({\n severity: 'warning',\n code: 'agent_capacity',\n message: `Work agent count is high (${workAgentCount}/${hardWorkAgentLimit}).`,\n });\n }\n\n if (leakedSpecialists.length > 0) {\n warnings.push({\n severity: health.summary.availableMemoryBytes < health.thresholds.memoryAvailableCriticalBytes ? 'critical' : 'warning',\n code: 'leaked_specialists',\n message: `Leaked specialist sessions detected: ${formatLeakedSpecialistSummary(leakedSpecialists)}${leakedSpecialists.length > 3 ? `, +${leakedSpecialists.length - 3} more` : ''}.`,\n });\n }\n\n const blockingWarnings = warnings.filter((warning) => warning.severity === 'critical');\n if (blockingWarnings.length > 0) {\n const hasLeakedSpecialists = leakedSpecialists.length > 0;\n return {\n blocked: true,\n requiresAcknowledgement: false,\n status: 429,\n error: blockingWarnings[0]?.message ?? 'System health is blocking new agent spawns.',\n hint: hasLeakedSpecialists\n ? 'Clean up leaked specialist sessions first, then retry the spawn.'\n : 'Reduce memory pressure or active work-agent count before retrying.',\n warnings,\n health: {\n severity: health.severity,\n summary: health.summary,\n reasons: health.reasons,\n leakedSpecialists: health.leakedSpecialists,\n freshness: health.freshness,\n },\n };\n }\n\n return {\n blocked: false,\n requiresAcknowledgement: warnings.length > 0,\n status: warnings.length > 0 ? 409 : 200,\n hint: warnings.length > 0 ? 'Acknowledge the system health warnings before starting this agent.' : undefined,\n warnings,\n health: {\n severity: health.severity,\n summary: health.summary,\n reasons: health.reasons,\n leakedSpecialists: health.leakedSpecialists,\n freshness: health.freshness,\n },\n };\n}\n\n// Shared enrichment utilities (PAN-440) — aliases for readability\nconst getClaudeProjectDir = getClaudeProjectDirShared;\nconst getActiveSessionPath = getActiveSessionPathShared;\nconst getAgentWorkspace = getAgentWorkspaceShared;\nconst getAgentJsonlPath = getAgentJsonlPathShared;\nconst getPendingQuestions = getPendingQuestionsShared;\nconst getAgentPendingQuestions = getAgentPendingQuestionsShared;\n\nfunction flyExecCmd(vmName: string, command: string): string {\n const appName = vmName.replace(/\\/.*$/, ''); // simplified: use vmName as app name\n return `fly ssh console -a ${appName} -C ${JSON.stringify(command)}`;\n}\n\nexport {\n execAsync,\n execFileAsync,\n constantTimeTokenEqual,\n emitStartAgentPhase,\n appendAgentLifecycleLog,\n updateRegistryForAgentStart,\n getIssueDataService,\n AGENTS_CACHE_TTL_MS,\n filterClosedIssueAgents,\n readJsonBody,\n toAgentStatusPayload,\n buildAgentControlEventPayload,\n buildAgentGateFailureSnapshot,\n buildStoppedAgentLifecycle,\n readRemoteAgentState,\n captureAgentOutputBeforeKill,\n buildHostOverrideConfirmation,\n getProjectPath,\n getWorkspaceLocation,\n getGitStatusAsync,\n resolveAgentCountEnv,\n formatLeakedSpecialistSummary,\n getClaudeProjectDir,\n getActiveSessionPath,\n getAgentWorkspace,\n getAgentJsonlPath,\n getPendingQuestions,\n getAgentPendingQuestions,\n flyExecCmd,\n};\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter } from 'effect/unstable/http';\n\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport {\n getAgentState,\n getAgentRuntimeState,\n listAgentStates,\n type AgentState,\n} from '../../../../lib/agents.js';\nimport { getReviewStatusSync } from '../../../../lib/review-status.js';\nimport { computeAgentEnrichment, isBlockedOnPendingInput } from '../../../../lib/agent-enrichment.js';\nimport { normalizeAwaitingInputPrompt } from '../../../../lib/agent-input-detection.js';\nimport { getWorkAgentLifecycleState } from '../../../../lib/work-agent-lifecycle.js';\nimport { resolveAgentGitInfo } from '../../services/git-info.js';\nimport { listSessions, sessionExists } from '../../../../lib/tmux.js';\nimport {\n AGENTS_CACHE_TTL_MS,\n agentsCache,\n buildAgentGateFailureSnapshot,\n buildStoppedAgentLifecycle,\n filterClosedIssueAgents,\n getGitStatusAsync,\n getIssueDataService,\n getWorkspaceLocation,\n hasActiveAgentGateOrRetry,\n readRemoteAgentState,\n} from './shared.js';\n\n/**\n * PAN-3070 — the health status a live agent row reports.\n *\n * A live tmux session used to mean `healthy` unconditionally, so an agent frozen\n * for hours on an unanswered tool-permission prompt was reported\n * `status: healthy` / `resolution: working` while the Decisions surface\n * simultaneously showed it as needing the operator. Two surfaces answering \"is\n * this agent stuck?\" from different evidence, and this one stale.\n *\n * `warning` is the value `src/lib/health.ts` already reports for an agent\n * waiting on a human, and it stays inside the fleet-view status set, so a\n * blocked agent is flagged without disappearing from any existing list.\n */\nexport function liveAgentHealthStatus(enrichment: {\n hasPendingQuestion?: boolean;\n pendingQuestionReason?: string;\n}): 'healthy' | 'warning' {\n return isBlockedOnPendingInput(enrichment) ? 'warning' : 'healthy';\n}\n\n// ─── Route: GET /api/agents ───────────────────────────────────────────────────\n\nexport const getAgentsRoute = HttpRouter.add(\n 'GET',\n '/api/agents',\n httpHandler(Effect.gen(function* () {\n const now = Date.now();\n\n if (agentsCache.data && (now - agentsCache.timestamp) < AGENTS_CACHE_TTL_MS) {\n return jsonResponse(agentsCache.data);\n }\n\n const sessions = yield* listSessions();\n const sessionByName = new Map(sessions.map((session) => [session.name, session]));\n const registeredStates = listAgentStates()\n .filter((state) => state.id.startsWith('agent-') || state.id.startsWith('planning-') || state.id.startsWith('strike-'));\n\n const allAgents = (yield* Effect.promise(() => Promise.all(\n registeredStates.map(async (state) => {\n const name = state.id;\n const isPlanning = name.startsWith('planning-');\n const isStrike = name.startsWith('strike-');\n const issueId = state.issueId?.toUpperCase() ||\n (isPlanning ? name.replace('planning-', '') : isStrike ? name.replace('strike-', '') : name.replace('agent-', '')).toUpperCase();\n const session = sessionByName.get(name);\n const remoteState = await readRemoteAgentState(name);\n const isRemote = remoteState.location === 'remote';\n const runtimeData = await Effect.runPromise(getAgentRuntimeState(name));\n const startedAt = state.startedAt || (session ? new Date(session.created).toISOString() : new Date().toISOString());\n const healthFile = join(homedir(), '.overdeck', 'agents', name, 'health.json');\n let health: any = { killCount: 0 };\n if (existsSync(healthFile)) {\n try { health = { ...health, ...JSON.parse(await readFile(healthFile, 'utf-8')) }; } catch {}\n }\n\n if (state.status === 'stopped') {\n const stoppedTimestamp = state.stoppedAt || runtimeData?.lastActivity || state.lastActivity;\n const stoppedAt = stoppedTimestamp ? new Date(stoppedTimestamp) : null;\n const reviewStatus = getReviewStatusSync(issueId);\n const keepStoppedAgentVisible =\n hasActiveAgentGateOrRetry(state, now) ||\n (\n !!reviewStatus &&\n reviewStatus.mergeStatus !== 'merged' &&\n (\n !!reviewStatus.prUrl ||\n reviewStatus.readyForMerge === true ||\n reviewStatus.reviewStatus !== 'pending' ||\n reviewStatus.testStatus !== 'pending' ||\n reviewStatus.mergeStatus === 'failed'\n )\n );\n if (stoppedAt && (now - stoppedAt.getTime()) > 60 * 60 * 1000 && !keepStoppedAgentVisible) return null;\n const lifecycle = buildStoppedAgentLifecycle(name, state, runtimeData ?? {});\n const needsInput = runtimeData?.resolution === 'needs_input';\n const pendingQuestionPrompt = needsInput\n ? normalizeAwaitingInputPrompt(\n runtimeData?.waitingNotification ||\n 'Agent stopped because it needs human input or hit a blocker',\n )\n : undefined;\n const pendingQuestionReason = needsInput\n ? runtimeData?.waitingReason || 'other'\n : undefined;\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: 'stopped' as const,\n startedAt,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace: state.workspace || null,\n workspaceLocation: isRemote ? 'remote' : 'local',\n // A stopped agent never shells `git` against its workspace (no\n // process cost for a row nobody is actively watching), so this\n // can only report the persisted branch, not live uncommitted/\n // commit detail — better than the unconditional null every\n // stopped agent used to report regardless of what it stored.\n git: state.branch ? { branch: state.branch, uncommittedFiles: 0, latestCommit: '' } : null,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasPendingQuestion: needsInput,\n pendingQuestionCount: 0,\n pendingQuestionPrompt,\n pendingQuestionReason,\n resolution: runtimeData?.resolution || 'working',\n resolutionCount: runtimeData?.resolutionCount || 0,\n hasSession: lifecycle.canResumeSession,\n lifecycle,\n ...(isRemote ? { remote: true, vmName: remoteState.vmName } : {}),\n };\n }\n\n if (state.status === 'starting') {\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: 'starting' as const,\n startedAt,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace: state.workspace || null,\n workspaceLocation: isRemote ? 'remote' : 'local',\n git: null,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasPendingQuestion: false,\n pendingQuestionCount: 0,\n message: (state as { message?: string }).message || 'Starting...',\n ...(isRemote ? { remote: true, vmName: remoteState.vmName } : {}),\n };\n }\n\n if (state.status === 'error') {\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: 'error' as const,\n startedAt,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace: state.workspace || null,\n workspaceLocation: isRemote ? 'remote' : 'local',\n git: null,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasPendingQuestion: false,\n pendingQuestionCount: 0,\n error: state.lastFailureReason || 'Unknown error',\n ...(isRemote ? { remote: true, vmName: remoteState.vmName } : {}),\n };\n }\n\n if (!session && !isRemote) {\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: 'unknown' as const,\n startedAt,\n lastActivity: runtimeData?.lastActivity || state.lastActivity,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace: state.workspace || null,\n workspaceLocation: 'local' as const,\n git: null,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasLiveTmuxSession: false,\n hasPendingQuestion: false,\n pendingQuestionCount: 0,\n lastFailureReason: state.lastFailureReason || 'No live tmux session found for registered agent',\n resolution: runtimeData?.resolution || 'working',\n resolutionCount: runtimeData?.resolutionCount || 0,\n };\n }\n\n const issueReviewStatus = getReviewStatusSync(issueId);\n const hasActiveSpecialist = issueReviewStatus?.reviewStatus === 'reviewing'\n || issueReviewStatus?.testStatus === 'testing'\n || issueReviewStatus?.mergeStatus === 'merging';\n const enrichment = await Effect.runPromise(computeAgentEnrichment(name, startedAt, hasActiveSpecialist));\n const workspaceLocation = isRemote ? 'remote' : await getWorkspaceLocation(issueId);\n const workspace = isRemote && remoteState.vmName\n ? `/workspace (${String(remoteState.vmName)})`\n : state.workspace || null;\n const gitStatus = workspace && !isRemote ? await getGitStatusAsync(issueId, workspace) : null;\n\n let contextPercent: number | null = null;\n let initialContextPercent: number | null = null;\n const agentCtxDir = join(homedir(), '.overdeck', 'agents', name);\n try {\n const ctxFile = join(agentCtxDir, 'context-pct');\n contextPercent = parseInt((await readFile(ctxFile, 'utf-8').catch(() => '')).trim(), 10) || null;\n const initCtxFile = join(agentCtxDir, 'initial-context-pct');\n initialContextPercent = parseInt((await readFile(initCtxFile, 'utf-8').catch(() => '')).trim(), 10) || null;\n } catch {}\n\n const blockedOnPendingInput = isBlockedOnPendingInput(enrichment);\n\n return {\n id: name,\n issueId,\n runtime: state.harness ?? 'claude-code',\n model: state.model || (isPlanning ? 'opus' : 'sonnet'),\n status: liveAgentHealthStatus(enrichment),\n startedAt,\n ...buildAgentGateFailureSnapshot(state),\n killCount: health.killCount || 0,\n workspace,\n workspaceLocation,\n git: gitStatus,\n type: 'agent',\n role: state.role ?? (isStrike ? 'strike' : isPlanning ? 'plan' : 'work'),\n hasLiveTmuxSession: true,\n hasPendingQuestion: enrichment.hasPendingQuestion,\n pendingQuestionCount: enrichment.pendingQuestionCount,\n pendingQuestionPrompt: enrichment.pendingQuestionPrompt,\n pendingQuestionReason: enrichment.pendingQuestionReason,\n pendingInputCount: enrichment.pendingInputCount,\n pendingInputKinds: enrichment.pendingInputKinds,\n pendingAskUserQuestion: enrichment.pendingAskUserQuestion,\n // PAN-3070 — the detection wins over the runtime resolution here\n // too: `runtimeData.resolution` is written by the stop hook and\n // stays at whatever it last was, so a frozen agent reported\n // `working` even once the enrichment knew better.\n resolution: blockedOnPendingInput\n ? 'needs_input'\n : (runtimeData?.resolution || enrichment.resolution || 'working'),\n resolutionCount: runtimeData?.resolutionCount || enrichment.resolutionCount || 0,\n contextPercent,\n initialContextPercent,\n ...(isRemote ? { remote: true, vmName: remoteState.vmName } : {}),\n };\n }),\n ))).filter(Boolean);\n const visibleAgents = filterClosedIssueAgents(allAgents, getIssueDataService().getIssues());\n agentsCache.data = visibleAgents;\n agentsCache.timestamp = now;\n return jsonResponse(visibleAgents);\n })),\n);\n\n// ─── Route: GET /api/agents/:id/git-info ─────────────────────────────────────\n//\n// Branch + worktree status for the agent's workspace (PAN-1523). Used by\n// AgentOutputPanel to render the Local/Worktree/Drifted chip in the panel\n// header. Work agents don't have a conversation row to enrich, so the panel\n// queries this dedicated endpoint instead.\n\n/**\n * True when the agent has a concrete workspace + issue we can evaluate git\n * state for. When false, the git-info route must NOT claim the worktree is\n * missing — an unresolvable session id (e.g. a legacy / JSONL-only \"Planning\n * state\" node) or an agent that never got a workspace is \"unknown\", not\n * \"workspace gone from disk\". Conflating the two made such nodes falsely render\n * \"Worktree missing\" in the SessionPanel chip (PAN-1718).\n */\nexport function agentHasResolvableWorkspace(\n agentState: AgentState | null,\n): agentState is AgentState {\n return Boolean(agentState?.workspace && agentState.issueId);\n}\n\n/**\n * Benign git-info response for a session we cannot resolve to a workspace-bound\n * agent. workspaceMissing is false (not true): we have no path to stat, so we\n * cannot assert the worktree is gone. The frontend chip hides on this shape\n * (showChip = actualBranch || workspaceMissing). The genuine on-disk\n * \"workspace missing\" case is detected separately by resolveAgentGitInfo, which\n * stats the real path. See PAN-1718.\n */\nexport const UNRESOLVABLE_AGENT_GIT_INFO = {\n actualBranch: null,\n branchDrifted: false,\n workspaceMissing: false,\n expectedBranch: null,\n} as const;\n\nexport const getAgentGitInfoRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/git-info',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ error: 'missing agent id' }, { status: 400 });\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentHasResolvableWorkspace(agentState)) {\n // PAN-1718: unknown session id / no workspace bound → \"unknown\", not\n // \"worktree missing\". Return the benign shape so the chip hides instead of\n // flashing a false alarm. Real on-disk absence is caught by\n // resolveAgentGitInfo below.\n return jsonResponse(UNRESOLVABLE_AGENT_GIT_INFO);\n }\n\n const expectedBranch = `feature/${agentState.issueId.toLowerCase()}`;\n const info = yield* Effect.promise(() =>\n resolveAgentGitInfo(agentState.workspace as string, expectedBranch),\n );\n return jsonResponse({\n ...info,\n expectedBranch,\n workspacePath: agentState.workspace,\n });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/tmux-alive ──────────────────────────────────\n\nexport const getAgentTmuxAliveRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/tmux-alive',\n Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const agentId = params['id'] ?? '';\n const alive = yield* sessionExists(agentId);\n return jsonResponse({ alive });\n }),\n);\n\n// ─── Route: GET /api/agents/:id/has-session ─────────────────────────────────\n// Returns whether a stopped agent has a resumable Claude session.\n\nexport const getAgentHasSessionRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/has-session',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const lifecycle = yield* getWorkAgentLifecycleState(id);\n return jsonResponse({\n hasSession: lifecycle.canResumeSession,\n lifecycle,\n });\n })),\n);\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { ConversationResponse } from '@overdeck/contracts';\nimport { Effect, Option } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { encodeClaudeProjectDir } from '../../../../lib/paths.js';\nimport {\n getActivity,\n getAgentState,\n} from '../../../../lib/agents.js';\nimport { capturePane } from '../../../../lib/tmux.js';\nimport { parseEntireConversation } from '../../services/conversation-service.js';\nimport { parsePiConversationMessages } from '../../services/pi-conversation-parser.js';\nimport { parseOhmypiConversationMessages } from '../../services/ohmypi-conversation-parser.js';\nimport { parseCodexConversationMessages } from '../../services/codex-conversation-parser.js';\nimport { parseAcpConversationMessages } from '../../services/acp-conversation-parser.js';\nimport {\n readLauncherPinnedSessionId,\n resolvePiSessionPath,\n resolveCodexRolloutPath,\n resolveAcpTranscriptPath,\n resolveAgentHarness,\n} from '../jsonl-resolver.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport {\n execAsync,\n getAgentJsonlPath,\n getAgentWorkspace,\n} from './shared.js';\n\n// ─── Route: GET /api/agents/:id/output ───────────────────────────────────────\n\nexport const getAgentOutputRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/output',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n const lines = Option.isSome(urlOpt) ? (urlOpt.value.searchParams.get('lines') ?? '100') : '100';\n\n return yield* Effect.promise(async () => {\n try {\n const agentStateDir = join(homedir(), '.overdeck', 'agents', id);\n const remoteStateFile = join(agentStateDir, 'remote-state.json');\n let isRemote = false;\n let vmName = '';\n\n if (existsSync(remoteStateFile)) {\n try {\n const state = JSON.parse(await readFile(remoteStateFile, 'utf-8'));\n if (state.location === 'remote' && state.vmName) {\n isRemote = true;\n vmName = state.vmName;\n }\n } catch {}\n }\n\n let stdout: string;\n if (isRemote && vmName) {\n const { getRemoteAgentOutput } = await import('../../../../lib/remote/remote-agents.js');\n stdout = await getRemoteAgentOutput(id, vmName, parseInt(String(lines), 10) || 100);\n } else {\n stdout = await Effect.runPromise(capturePane(id, parseInt(String(lines), 10) || 100));\n }\n\n if (!stdout || stdout.trim() === '' || stdout.trim() === 'Session not found') {\n const savedLog = join(agentStateDir, 'output.log');\n const logContent = await readFile(savedLog, 'utf-8').catch(() => null);\n if (logContent) {\n const logLines = logContent.split('\\n');\n const numLines = parseInt(String(lines), 10) || 100;\n stdout = logLines.slice(-numLines).join('\\n');\n }\n }\n\n if (stdout?.trim() === 'Session not found') {\n stdout = '';\n }\n\n return jsonResponse({ output: stdout });\n } catch (error: unknown) {\n // Try saved log on error\n try {\n const agentStateDir = join(homedir(), '.overdeck', 'agents', id);\n const savedLog = join(agentStateDir, 'output.log');\n const logContent = await readFile(savedLog, 'utf-8').catch(() => null);\n if (logContent) return jsonResponse({ output: logContent });\n } catch {}\n return jsonResponse({ output: '' });\n }\n })\n })),\n);\n\n// ─── Route: GET /api/agents/:id/conversation ─────────────────────────────────\n\nconst EMPTY_CONVERSATION: ConversationResponse = { messages: [], workLog: [], streaming: false, totalCost: 0, byteOffset: 0 };\n\n/**\n * Resolve and parse an agent's conversation JSONL file.\n * Exported for unit testing — the Effect route layer is not directly unit-testable.\n *\n * Dispatches on harness so Pi and Codex agents get their native parsers (PAN-2012).\n * For claude-code agents, tries the launcher-pinned --session-id first (the exact\n * session the Terminal tab attaches to) before falling back to mtime-based pick\n * (PAN-2011). This makes the Conversation tab match the Terminal tab by construction.\n */\nexport async function buildConversationResponse(id: string): Promise<ConversationResponse> {\n try {\n const harness = await resolveAgentHarness(id);\n\n if (harness === 'ohmypi') {\n const sessionFile = await resolvePiSessionPath(id);\n if (!sessionFile || !existsSync(sessionFile)) return EMPTY_CONVERSATION;\n const result = await parseOhmypiConversationMessages(sessionFile);\n return { ...result, streaming: false };\n }\n\n if (harness === 'pi') {\n const sessionFile = await resolvePiSessionPath(id);\n if (!sessionFile || !existsSync(sessionFile)) return EMPTY_CONVERSATION;\n const result = await parsePiConversationMessages(sessionFile);\n return { ...result, streaming: false };\n }\n\n if (harness === 'codex') {\n const sessionFile = await resolveCodexRolloutPath(id);\n if (!sessionFile || !existsSync(sessionFile)) return EMPTY_CONVERSATION;\n const result = await parseCodexConversationMessages(sessionFile);\n return { ...result, streaming: false };\n }\n\n if (harness === 'acp') {\n const sessionFile = await resolveAcpTranscriptPath(id);\n if (!sessionFile || !existsSync(sessionFile)) return EMPTY_CONVERSATION;\n const result = await parseAcpConversationMessages(sessionFile);\n return {\n ...result,\n messages: result.messages.map((message) => message.role === 'assistant'\n ? {\n ...message,\n completedAt: message.completedAt ?? message.createdAt,\n streaming: false,\n }\n : message),\n streaming: false,\n };\n }\n\n // claude-code (default): try launcher-pinned session ID first (ground truth),\n // then fall back to mtime-based pick.\n let jsonlPath: string | null = null;\n const pinnedSessionId = await readLauncherPinnedSessionId(id);\n if (pinnedSessionId) {\n const workspace = await Effect.runPromise(getAgentWorkspace(id));\n if (workspace) {\n const candidate = join(\n homedir(), '.claude', 'projects',\n encodeClaudeProjectDir(workspace),\n `${pinnedSessionId}.jsonl`,\n );\n if (existsSync(candidate)) jsonlPath = candidate;\n }\n }\n if (!jsonlPath) {\n jsonlPath = await Effect.runPromise(getAgentJsonlPath(id));\n }\n\n if (!jsonlPath || !existsSync(jsonlPath)) return EMPTY_CONVERSATION;\n // parseEntireConversation, not parseConversationMessages: a single parse caps\n // at MAX_READ_BYTES (10 MB) and would drop the most recent turns of a larger\n // transcript (PAN-1989). This one-shot endpoint must return the whole file.\n const result = await parseEntireConversation(jsonlPath);\n // Force streaming: false — tmux session is dead, any \"streaming\" state is stale\n return { ...result, streaming: false };\n } catch (err) {\n console.error('[conversation] failed for', id, err);\n return EMPTY_CONVERSATION;\n }\n}\n\nexport const getAgentConversationRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/conversation',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n return yield* Effect.promise(async () => jsonResponse(await buildConversationResponse(id)));\n })),\n);\n\n// ─── Route: GET /api/agents/:id/activity ─────────────────────────────────────\n\nexport const getAgentActivityRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/activity',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n const limitStr = Option.isSome(urlOpt) ? (urlOpt.value.searchParams.get('limit') ?? '100') : '100';\n const limit = parseInt(limitStr) || 100;\n\n const activity = getActivity(id, limit);\n return jsonResponse({ activity });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/files ────────────────────────────────────────\n\nexport const getAgentFilesRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/files',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const agentState = yield* getAgentState(id);\n if (!agentState?.workspace) {\n return jsonResponse({ files: [] });\n }\n const workspacePath = agentState.workspace;\n if (!existsSync(workspacePath)) {\n return jsonResponse({ files: [] });\n }\n const { stdout } = yield* Effect.promise(() => execAsync(\n 'git diff --name-status HEAD 2>/dev/null || git status --porcelain 2>/dev/null || echo \"\"',\n { cwd: workspacePath, encoding: 'utf-8' }\n ));\n const files = stdout\n .split('\\n')\n .filter(l => l.trim())\n .map(l => {\n const parts = l.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return { status: parts[0], path: parts[parts.length - 1] };\n }\n return { status: '?', path: l.trim() };\n })\n .filter(f => f.path);\n return jsonResponse({ files });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/timeline ─────────────────────────────────────\n\nexport const getAgentTimelineRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/timeline',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n const limitStr = Option.isSome(urlOpt) ? (urlOpt.value.searchParams.get('limit') ?? '50') : '50';\n const limit = parseInt(limitStr) || 50;\n\n const activity = getActivity(id, limit);\n const agentState = yield* getAgentState(id);\n const events = activity.map((a: any) => ({\n timestamp: a.timestamp || new Date().toISOString(),\n type: a.type || 'activity',\n message: a.message || a.content || '',\n }));\n if (agentState?.startedAt) {\n events.unshift({ timestamp: agentState.startedAt, type: 'started', message: 'Agent started' });\n }\n events.sort((a: any, b: any) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());\n return jsonResponse({ timeline: events.slice(0, limit) });\n })),\n);\n","import { randomBytes } from 'node:crypto';\n\nimport type {\n ComposerCommandPolicy,\n ComposerCommandResult,\n ComposerSafety,\n} from '@overdeck/contracts';\n\nexport const COMPOSER_CONFIRMATION_TTL_MS = 5 * 60 * 1000;\n\nexport interface ComposerConfirmationTarget {\n kind: 'conversation' | 'agent';\n id: string;\n}\n\nexport interface ComposerCommandConfirmationInput {\n nonce?: unknown;\n typedText?: unknown;\n}\n\nexport function composerCommandConfirmationFromBody(\n body: Record<string, unknown>,\n): ComposerCommandConfirmationInput | undefined {\n if (!('confirmationNonce' in body) && !('confirmationText' in body)) return undefined;\n return {\n nonce: body['confirmationNonce'],\n typedText: body['confirmationText'],\n };\n}\n\ninterface ComposerConfirmationEntry {\n targetKind: ComposerConfirmationTarget['kind'];\n targetId: string;\n argv: readonly string[];\n safety: ComposerSafety;\n typedText?: string;\n expiresAt: number;\n}\n\nexport class ComposerCommandConfirmationError extends Error {\n readonly code = 'invalid-confirmation';\n\n constructor(message: string) {\n super(message);\n this.name = 'ComposerCommandConfirmationError';\n }\n}\n\nexport interface ComposerConfirmationStore {\n issue(input: {\n target: ComposerConfirmationTarget;\n argv: readonly string[];\n policy: ComposerCommandPolicy;\n display: string;\n }): Extract<ComposerCommandResult, { kind: 'confirmation' }>;\n consume(input: {\n target: ComposerConfirmationTarget;\n argv: readonly string[];\n policy: ComposerCommandPolicy;\n confirmation: ComposerCommandConfirmationInput;\n }): void;\n}\n\nexport interface ComposerConfirmationStoreDependencies {\n now?: () => number;\n createNonce?: () => string;\n}\n\nexport function createComposerConfirmationStore(\n dependencies: ComposerConfirmationStoreDependencies = {},\n): ComposerConfirmationStore {\n const entries = new Map<string, ComposerConfirmationEntry>();\n const now = dependencies.now ?? Date.now;\n const createNonce = dependencies.createNonce\n ?? (() => randomBytes(24).toString('base64url'));\n\n function pruneExpired(): void {\n const currentTime = now();\n for (const [nonce, entry] of entries) {\n if (entry.expiresAt <= currentTime) entries.delete(nonce);\n }\n }\n\n return {\n issue({ target, argv, policy, display }) {\n if (policy.safety === 'destructive' && !policy.typedConfirmation) {\n throw new Error(\n `Destructive composer policy for ${display} must define typedConfirmation.`,\n );\n }\n pruneExpired();\n const nonce = createNonce();\n entries.set(nonce, {\n targetKind: target.kind,\n targetId: target.id,\n argv: [...argv],\n safety: policy.safety,\n typedText: policy.typedConfirmation,\n expiresAt: now() + COMPOSER_CONFIRMATION_TTL_MS,\n });\n return {\n kind: 'confirmation',\n status: 'confirmation_required',\n nonce,\n consequence: policy.confirmationText\n ?? `Running ${display} requires confirmation before Overdeck continues.`,\n ...(policy.typedConfirmation\n ? { typedText: policy.typedConfirmation }\n : {}),\n };\n },\n\n consume({ target, argv, policy, confirmation }) {\n if (typeof confirmation.nonce !== 'string') {\n throw new ComposerCommandConfirmationError(\n 'A valid confirmation nonce is required before this command can run.',\n );\n }\n\n const entry = entries.get(confirmation.nonce);\n entries.delete(confirmation.nonce);\n if (!entry) {\n throw new ComposerCommandConfirmationError(\n 'This command confirmation is missing, invalid, or has already been used. Request a new confirmation and try again.',\n );\n }\n if (entry.expiresAt <= now()) {\n throw new ComposerCommandConfirmationError(\n 'This command confirmation has expired. Request a new confirmation and try again.',\n );\n }\n if (\n entry.targetKind !== target.kind ||\n entry.targetId !== target.id ||\n entry.safety !== policy.safety ||\n !sameArgv(entry.argv, argv)\n ) {\n throw new ComposerCommandConfirmationError(\n 'This command confirmation does not match the current target or command. Request a new confirmation and try again.',\n );\n }\n if (\n policy.safety === 'destructive' &&\n (typeof confirmation.typedText !== 'string' || confirmation.typedText !== entry.typedText)\n ) {\n throw new ComposerCommandConfirmationError(\n `Type \"${entry.typedText ?? ''}\" exactly to confirm this destructive command.`,\n );\n }\n },\n };\n}\n\nfunction sameArgv(left: readonly string[], right: readonly string[]): boolean {\n return left.length === right.length && left.every((value, index) => value === right[index]);\n}\n\nexport const composerConfirmationStore = createComposerConfirmationStore();\n","import {\n COMPOSER_COMMAND_MANIFEST,\n type ComposerCommandManifestEntry,\n type ComposerCommandOption,\n} from '@overdeck/contracts';\n\nexport type ComposerCommandParseErrorCode =\n | 'unknown-command'\n | 'missing-argument'\n | 'unknown-flag'\n | 'forbidden-token';\n\nexport class ComposerCommandParseError extends Error {\n constructor(\n readonly code: ComposerCommandParseErrorCode,\n message: string,\n readonly token: string,\n readonly expected: string,\n ) {\n super(message);\n this.name = 'ComposerCommandParseError';\n }\n}\n\nexport interface ParsedOverdeckComposerCommand {\n entry: ComposerCommandManifestEntry;\n argv: string[];\n}\n\ninterface OptionDefinition {\n option: ComposerCommandOption;\n canonicalFlag: string;\n}\n\nconst FORBIDDEN_TOKEN = /\\$\\(|[;|&<>`]/;\n\nfunction expectedSyntax(entry: ComposerCommandManifestEntry): string {\n const args = entry.args.map(argument => {\n const name = argument.variadic ? `${argument.name}...` : argument.name;\n return argument.required ? `<${name}>` : `[${name}]`;\n });\n const options = entry.options.length > 0 ? ['[options]'] : [];\n return [entry.display, ...args, ...options].join(' ');\n}\n\nfunction tokenize(input: string): string[] {\n const tokens: string[] = [];\n let current = '';\n let quote: '\"' | \"'\" | null = null;\n let escaped = false;\n\n const pushCurrent = () => {\n if (current.length === 0) return;\n tokens.push(current);\n current = '';\n };\n\n for (const character of input) {\n if (escaped) {\n current += character;\n escaped = false;\n continue;\n }\n if (character === '\\\\') {\n escaped = true;\n continue;\n }\n if (quote !== null) {\n if (character === quote) quote = null;\n else current += character;\n continue;\n }\n if (character === '\"' || character === \"'\") {\n quote = character;\n continue;\n }\n if (/\\s/.test(character)) {\n pushCurrent();\n continue;\n }\n current += character;\n }\n\n if (escaped) current += '\\\\';\n if (quote !== null) {\n throw new ComposerCommandParseError(\n 'missing-argument',\n `Unclosed ${quote} quote. Close the quoted value before submitting the command.`,\n quote,\n `a closing ${quote}`,\n );\n }\n pushCurrent();\n return tokens;\n}\n\nfunction aliasesForPath(entry: ComposerCommandManifestEntry): string[][] {\n let candidates: string[][] = [[]];\n for (let index = 0; index < entry.path.length; index += 1) {\n const prefix = entry.path.slice(0, index + 1);\n const prefixEntry = COMPOSER_COMMAND_MANIFEST.find(candidate =>\n candidate.path.length === prefix.length &&\n candidate.path.every((part, partIndex) => part === prefix[partIndex]));\n const names = [entry.path[index], ...(prefixEntry?.aliases ?? [])];\n candidates = candidates.flatMap(candidate => names.map(name => [...candidate, name]));\n }\n return candidates;\n}\n\nfunction resolveEntry(tokens: readonly string[]): { entry: ComposerCommandManifestEntry; pathLength: number } {\n const matches = COMPOSER_COMMAND_MANIFEST.flatMap(entry =>\n aliasesForPath(entry)\n .filter(path => path.every((part, index) => tokens[index] === part))\n .map(path => ({ entry, pathLength: path.length })));\n const match = matches.sort((left, right) => right.pathLength - left.pathLength)[0];\n if (match) return match;\n\n const token = tokens[0] ?? '';\n throw new ComposerCommandParseError(\n 'unknown-command',\n token\n ? `Unknown Overdeck command \"${token}\". Type /pan to list available commands.`\n : 'Missing an Overdeck command after /pan. Type /pan to list available commands.',\n token,\n '/pan <command>',\n );\n}\n\nfunction optionDefinitions(entry: ComposerCommandManifestEntry): Map<string, OptionDefinition> {\n const definitions = new Map<string, OptionDefinition>();\n for (const option of entry.options) {\n const flags = option.flags\n .split(/[ ,|]+/)\n .filter(part => part.startsWith('-'));\n const canonicalFlag = flags.find(flag => flag.startsWith('--')) ?? flags[0];\n for (const flag of flags) definitions.set(flag, { option, canonicalFlag });\n }\n return definitions;\n}\n\nfunction splitFlag(token: string): { flag: string; inlineValue?: string } {\n const separator = token.indexOf('=');\n if (separator === -1) return { flag: token };\n return {\n flag: token.slice(0, separator),\n inlineValue: token.slice(separator + 1),\n };\n}\n\nexport function parseOverdeckComposerCommand(\n message: string,\n): ParsedOverdeckComposerCommand | null {\n if (!/^\\/pan(?:\\s|$)/.test(message)) return null;\n\n const forbidden = message.match(FORBIDDEN_TOKEN)?.[0];\n if (forbidden !== undefined) {\n throw new ComposerCommandParseError(\n 'forbidden-token',\n `The token \"${forbidden}\" is not allowed in /pan commands. Run compound shell syntax in a terminal instead.`,\n forbidden,\n 'plain arguments without shell operators',\n );\n }\n\n const tokens = tokenize(message.slice(4).trim());\n const { entry, pathLength } = resolveEntry(tokens);\n const remaining = tokens.slice(pathLength);\n const definitions = optionDefinitions(entry);\n const positionals: string[] = [];\n const flags: string[] = [];\n\n for (let index = 0; index < remaining.length; index += 1) {\n const token = remaining[index];\n if (!token.startsWith('-')) {\n positionals.push(token);\n continue;\n }\n\n const { flag, inlineValue } = splitFlag(token);\n const definition = definitions.get(flag);\n if (definition === undefined) {\n throw new ComposerCommandParseError(\n 'unknown-flag',\n `Unknown flag \"${flag}\" for ${entry.display}. Expected syntax: ${expectedSyntax(entry)}.`,\n flag,\n expectedSyntax(entry),\n );\n }\n\n flags.push(definition.canonicalFlag);\n if (definition.option.valueHint === null) {\n if (inlineValue !== undefined) {\n throw new ComposerCommandParseError(\n 'unknown-flag',\n `Flag \"${flag}\" does not accept a value. Expected syntax: ${expectedSyntax(entry)}.`,\n token,\n expectedSyntax(entry),\n );\n }\n continue;\n }\n\n if (inlineValue !== undefined) {\n if (inlineValue.length === 0 && definition.option.required) {\n throw new ComposerCommandParseError(\n 'missing-argument',\n `Flag \"${flag}\" requires <${definition.option.valueHint}>. Expected syntax: ${expectedSyntax(entry)}.`,\n flag,\n expectedSyntax(entry),\n );\n }\n if (inlineValue.length > 0) flags.push(inlineValue);\n continue;\n }\n\n const next = remaining[index + 1];\n const nextIsFlag = next?.startsWith('-') === true;\n if (definition.option.required && (next === undefined || nextIsFlag)) {\n throw new ComposerCommandParseError(\n 'missing-argument',\n `Flag \"${flag}\" requires <${definition.option.valueHint}>. Expected syntax: ${expectedSyntax(entry)}.`,\n flag,\n expectedSyntax(entry),\n );\n }\n if (next !== undefined && !nextIsFlag) {\n flags.push(next);\n index += 1;\n }\n }\n\n const variadic = entry.args.at(-1)?.variadic === true;\n if (!variadic && positionals.length > entry.args.length) {\n const token = positionals[entry.args.length];\n throw new ComposerCommandParseError(\n 'unknown-command',\n `Unexpected token \"${token}\" for ${entry.display}. Expected syntax: ${expectedSyntax(entry)}.`,\n token,\n expectedSyntax(entry),\n );\n }\n const missing = entry.args.find((argument, index) => argument.required && positionals[index] === undefined);\n if (missing !== undefined) {\n throw new ComposerCommandParseError(\n 'missing-argument',\n `Missing required argument <${missing.name}> for ${entry.display}. Expected syntax: ${expectedSyntax(entry)}.`,\n missing.name,\n expectedSyntax(entry),\n );\n }\n\n return {\n entry,\n argv: [...entry.path, ...positionals, ...flags],\n };\n}\n","import {\n COMPOSER_COMMAND_MANIFEST,\n type ComposerCommandManifestEntry,\n type ComposerCommandPolicy,\n} from '@overdeck/contracts';\n\nexport const COMPOSER_COMMAND_POLICIES: Readonly<Record<string, ComposerCommandPolicy>> = {\n start: { mode: 'detached', safety: 'safe' },\n plan: { mode: 'detached', safety: 'safe' },\n show: { mode: 'captured', safety: 'safe' },\n status: { mode: 'captured', safety: 'safe' },\n tell: { mode: 'captured', safety: 'safe' },\n handoff: {\n mode: 'ui',\n safety: 'dialog',\n uiAction: 'handoff',\n confirmationText: 'This opens the handoff dialog for the selected conversation. Review the destination and focus before continuing.',\n },\n fork: {\n mode: 'ui',\n safety: 'dialog',\n uiAction: 'fork',\n confirmationText: 'This opens the fork dialog for the selected conversation. Review the fork mode and destination before continuing.',\n },\n};\n\nconst DEFAULT_POLICY: ComposerCommandPolicy = {\n mode: 'terminal-only',\n safety: 'safe',\n};\n\nexport function assertPolicyOverlayIntegrity(\n policies: Readonly<Record<string, ComposerCommandPolicy>> = COMPOSER_COMMAND_POLICIES,\n manifest: readonly ComposerCommandManifestEntry[] = COMPOSER_COMMAND_MANIFEST,\n): void {\n for (const path of Object.keys(policies)) {\n const matches = manifest.filter(entry => entry.path.join(' ') === path);\n if (matches.length !== 1) {\n throw new Error(\n `Composer command policy path \"${path}\" must match exactly one manifest entry; found ${matches.length}.`,\n );\n }\n }\n}\n\nexport function resolvePolicy(path: readonly string[]): ComposerCommandPolicy {\n return COMPOSER_COMMAND_POLICIES[path.join(' ')] ?? DEFAULT_POLICY;\n}\n\nassertPolicyOverlayIntegrity();\n","/**\n * Harness-independent composer control-plane router.\n *\n * Message endpoints call this after target/body validation and before every\n * harness-specific stage: native compact handling, attachment transformation,\n * prompt-memory injection, and delivery. Returning early here guarantees that\n * `/pan` operator commands cannot become harness prompts or transcript rows.\n */\nimport type {\n ComposerCommandPolicy,\n ComposerCommandResult,\n} from '@overdeck/contracts';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport {\n composerConfirmationStore,\n type ComposerCommandConfirmationInput,\n type ComposerConfirmationStore,\n} from './confirmations.js';\nimport { runDetachedCommand } from './detached.js';\nimport { runCapturedCommand } from './executors.js';\nimport {\n parseOverdeckComposerCommand,\n type ParsedOverdeckComposerCommand,\n} from './parser.js';\nimport { resolvePolicy } from './policy.js';\n\nexport interface ComposerCommandTarget {\n kind: 'conversation' | 'agent';\n id: string;\n harness: RuntimeName;\n cwd?: string;\n issueId?: string;\n}\n\nexport interface HandleComposerCommandInput {\n parsed: ParsedOverdeckComposerCommand;\n target: ComposerCommandTarget;\n confirmation?: ComposerCommandConfirmationInput;\n}\n\nexport interface HandleComposerCommandMessageInput {\n message: string;\n target: ComposerCommandTarget;\n confirmation?: ComposerCommandConfirmationInput;\n}\n\nexport interface ComposerCommandRouterDependencies {\n confirmationStore?: ComposerConfirmationStore;\n resolvePolicy?: (path: readonly string[]) => ComposerCommandPolicy;\n}\n\nconst UI_COMMAND_PATTERNS = [\n {\n action: 'handoff' as const,\n pattern: /^\\/(?:handoff|pan-handoff|pan\\s+handoff)(?:\\s+([\\s\\S]+))?$/i,\n },\n {\n action: 'fork' as const,\n pattern: /^\\/pan\\s+fork(?:\\s+([\\s\\S]+))?$/i,\n },\n];\n\nexport function isComposerCommandMessage(message: string): boolean {\n return (\n UI_COMMAND_PATTERNS.some(({ pattern }) => pattern.test(message)) ||\n /^\\/pan(?:\\s|$)/.test(message)\n );\n}\n\nexport async function handleComposerCommandMessage({\n message,\n target,\n confirmation,\n}: HandleComposerCommandMessageInput, dependencies: ComposerCommandRouterDependencies = {}): Promise<ComposerCommandResult | null> {\n for (const { action, pattern } of UI_COMMAND_PATTERNS) {\n const match = message.match(pattern);\n if (match) {\n const parsed = parseOverdeckComposerCommand(`/pan ${action}`)!;\n const focus = match[1]?.trim();\n if (focus) parsed.argv.push(focus);\n return handleComposerCommand(\n { parsed, target, confirmation },\n dependencies,\n );\n }\n }\n\n const parsed = parseOverdeckComposerCommand(message);\n if (parsed === null) return null;\n return handleComposerCommand(\n { parsed, target, confirmation },\n dependencies,\n );\n}\n\nexport async function handleComposerCommand({\n parsed,\n target,\n confirmation,\n}: HandleComposerCommandInput, dependencies: ComposerCommandRouterDependencies = {}): Promise<ComposerCommandResult> {\n const policy = (dependencies.resolvePolicy ?? resolvePolicy)(parsed.entry.path);\n if (policy.safety !== 'safe') {\n const confirmationStore = dependencies.confirmationStore ?? composerConfirmationStore;\n if (confirmation === undefined) {\n return confirmationStore.issue({\n target,\n argv: parsed.argv,\n policy,\n display: parsed.entry.display,\n });\n }\n confirmationStore.consume({\n target,\n argv: parsed.argv,\n policy,\n confirmation,\n });\n }\n\n if (policy.mode === 'captured') {\n return runCapturedCommand(parsed.argv);\n }\n if (policy.mode === 'detached') {\n return runDetachedCommand(parsed.argv);\n }\n if (policy.mode === 'ui' && policy.uiAction) {\n return uiCommandResult(\n policy.uiAction,\n parsed.argv.slice(parsed.entry.path.length).join(' ') || undefined,\n );\n }\n if (policy.mode === 'terminal-only') {\n return {\n kind: 'terminal-only',\n status: 'rejected',\n message: `${parsed.entry.display} must run in a terminal. Composer execution has not been enabled for this command.`,\n };\n }\n\n return missingExecutorResult(parsed.entry.display, policy);\n}\n\nfunction uiCommandResult(\n action: 'handoff' | 'fork',\n focus: string | undefined,\n): ComposerCommandResult {\n return {\n kind: 'ui',\n status: 'requires_ui',\n action,\n args: focus ? { focus } : {},\n };\n}\n\nfunction missingExecutorResult(\n display: string,\n policy: ComposerCommandPolicy,\n): ComposerCommandResult {\n return {\n kind: 'terminal-only',\n status: 'rejected',\n message: `${display} is recognized as a ${policy.mode} command, but its composer executor is not registered yet. Run it in a terminal for now.`,\n };\n}\n\nexport function composerCommandResultHttpStatus(result: ComposerCommandResult): number {\n return result.kind === 'terminal-only' ? 422 : 200;\n}\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { getAgentState, messageAgent } from '../../../../lib/agents.js';\nimport {\n ComposerCommandConfirmationError,\n composerCommandConfirmationFromBody,\n type ComposerCommandConfirmationInput,\n} from '../../../../lib/composer-commands/confirmations.js';\nimport { ComposerCommandParseError } from '../../../../lib/composer-commands/parser.js';\nimport {\n composerCommandResultHttpStatus,\n handleComposerCommandMessage,\n isComposerCommandMessage,\n} from '../../../../lib/composer-commands/router.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { validateOrigin } from '../origin-validation.js';\nimport { readJsonBody } from './shared.js';\n\nasync function sendAgentMessage(id: string, message: string) {\n const agentStateDir = join(homedir(), '.overdeck', 'agents', id);\n const remoteStateFile = join(agentStateDir, 'remote-state.json');\n let isRemote = false;\n\n if (existsSync(remoteStateFile)) {\n try {\n const state = JSON.parse(await readFile(remoteStateFile, 'utf-8'));\n isRemote = state.location === 'remote' && Boolean(state.vmName);\n } catch {}\n }\n\n await messageAgent(id, message, 'dashboard:user-message');\n return isRemote ? { success: true, remote: true } : { success: true };\n}\n\nexport async function handleAgentMessage(\n id: string,\n message: string,\n confirmation?: ComposerCommandConfirmationInput,\n) {\n try {\n if (isComposerCommandMessage(message)) {\n let agentState;\n try {\n agentState = await Effect.runPromise(getAgentState(id));\n } catch {\n return jsonResponse({\n error: `Failed to resolve agent target: ${id}`,\n code: 'agent-resolution-failed',\n }, { status: 503 });\n }\n if (!agentState) {\n return jsonResponse({\n error: `Agent not found: ${id}`,\n code: 'agent-not-found',\n }, { status: 404 });\n }\n if (!agentState.harness) {\n return jsonResponse({\n error: `Agent harness could not be resolved: ${id}`,\n code: 'agent-harness-unresolved',\n }, { status: 503 });\n }\n const result = await handleComposerCommandMessage({\n message,\n confirmation,\n target: {\n kind: 'agent',\n id,\n harness: agentState.harness,\n cwd: agentState.workspace,\n issueId: agentState.issueId,\n },\n });\n if (result !== null) {\n return jsonResponse(result, { status: composerCommandResultHttpStatus(result) });\n }\n }\n } catch (error) {\n if (error instanceof ComposerCommandConfirmationError) {\n return jsonResponse({\n error: error.message,\n code: error.code,\n }, { status: 422 });\n }\n if (!(error instanceof ComposerCommandParseError)) throw error;\n const status = error.code === 'unknown-command' ? 404 : error.code === 'unknown-flag' ? 422 : 400;\n return jsonResponse({\n error: error.message,\n code: error.code,\n token: error.token,\n expected: error.expected,\n }, { status });\n }\n\n return jsonResponse(await sendAgentMessage(id, message));\n}\n\nexport function validateAgentMessageOrigin(request: HttpServerRequest.HttpServerRequest) {\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return {\n ok: false as const,\n response: jsonResponse({ ok: false, error: originCheck.error }, { status: 403 }),\n };\n }\n return { ok: true as const };\n}\n\nfunction postAgentMessageLikeRoute(path: `/${string}`) {\n return HttpRouter.add(\n 'POST',\n path,\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateAgentMessageOrigin(request);\n if (!originCheck.ok) return originCheck.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { message } = body as any;\n if (!message) {\n return jsonResponse({ error: 'Message required' }, { status: 400 });\n }\n\n return yield* Effect.promise(() => handleAgentMessage(\n id,\n message,\n composerCommandConfirmationFromBody(body as Record<string, unknown>),\n ));\n })),\n );\n}\n\n// ─── Route: POST /api/agents/:id/message ─────────────────────────────────────\n\nexport const postAgentMessageRoute = postAgentMessageLikeRoute('/api/agents/:id/message');\n\n// ─── Route: POST /api/agents/:id/tell ────────────────────────────────────────\n\nexport const postAgentTellRoute = postAgentMessageLikeRoute('/api/agents/:id/tell');\n\n// ─── Route: POST /api/agents/:id/poke ────────────────────────────────────────\n\nexport const postAgentPokeRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/poke',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { message } = body as any;\n const defaultPokeMessage =\n \"You seem to have been inactive for a while. If you're stuck:\\n\" +\n '1. Check your current xBRIEF task with `pan task show <issue> <item-id>`\\n' +\n '2. Try an alternative approach if blocked\\n' +\n '3. Ask for help if needed\\n\\n' +\n \"What's your current status?\";\n const pokeMsg = message || defaultPokeMessage;\n yield* Effect.promise(() => messageAgent(id, pokeMsg));\n return jsonResponse({ success: true, message: 'Agent poked successfully' });\n })),\n);\n","import { randomUUID } from 'node:crypto'\nimport { mkdir, rename, writeFile, chmod } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { ohmypiFifoPaths } from './ohmypi-fifo.js'\n\nexport type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'\n\nexport type ControlCommand =\n | {\n id: string\n type: 'prompt' | 'steer' | 'follow_up'\n message: string\n source: 'operator' | 'orchestrator'\n }\n | {\n id: string\n type: 'set_thinking_level'\n level: ThinkingLevel\n }\n | {\n id: string\n type: 'set_model'\n model: string\n }\n | {\n id: string\n type: 'compact'\n }\n\nexport interface ConversationControlPaths {\n agentDir: string\n controlDir: string\n}\n\nexport function conversationControlPaths(agentId: string, home?: string): ConversationControlPaths {\n const { agentDir } = ohmypiFifoPaths(agentId, home)\n return {\n agentDir,\n controlDir: join(agentDir, 'control'),\n }\n}\n\nexport async function writeConversationControlCommand(\n agentId: string,\n command: ControlCommand,\n home?: string,\n): Promise<string> {\n const paths = conversationControlPaths(agentId, home)\n await mkdir(paths.controlDir, { recursive: true, mode: 0o700 })\n await chmod(paths.controlDir, 0o700)\n\n const finalPath = join(paths.controlDir, `${Date.now()}-${command.id}-${randomUUID()}.json`)\n const tempPath = `${finalPath}.tmp`\n await writeFile(tempPath, `${JSON.stringify(command)}\\n`, { encoding: 'utf8', mode: 0o600 })\n await chmod(tempPath, 0o600)\n await rename(tempPath, finalPath)\n await chmod(finalPath, 0o600)\n return finalPath\n}\n","/**\n * oh-my-pi (omp) OAuth helpers for the openai-codex (ChatGPT subscription) provider.\n * Forked from pi-codex-auth.ts (PAN-1520) for the omp migration (PAN-1989).\n *\n * omp stores its general credentials in ~/.omp/agent/agent.db (SQLite). The\n * openai-codex OAuth credential is persisted separately at ~/.omp/agent/auth.json\n * (same JSON format as Pi's ~/.pi/agent/auth.json) so that this helper's\n * read/write logic is unchanged from the Pi side.\n *\n * This module:\n * - reads/writes the openai-codex credential under ~/.omp/agent/auth.json,\n * - reports auth status (for `pan doctor` and the conversation spawn gate),\n * - refreshes an expired-but-recoverable token, and\n * - drives omp's headless device-code login to re-authenticate.\n *\n * The OAuth module is loaded from the installed `omp` package dynamically.\n * A missing or relocated omp OAuth module degrades to 'unavailable' — never throws.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, realpathSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, dirname, delimiter } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nexport const PI_CODEX_PROVIDER = 'openai-codex';\n\n/** Credential as persisted in ~/.omp/agent/auth.json. */\nexport interface OhmypiCodexCredential {\n type: 'oauth';\n access: string;\n refresh: string;\n /** Access-token expiry, epoch milliseconds. */\n expires: number;\n accountId: string;\n}\n\n/** Token bundle returned by omp's login/refresh (pre-persistence, no `type`). */\ninterface OhmypiOAuthTokens {\n access: string;\n refresh: string;\n expires: number;\n accountId: string;\n}\n\nexport interface DeviceCodeInfo {\n userCode: string;\n verificationUri: string;\n intervalSeconds: number;\n expiresInSeconds: number;\n}\n\ninterface OhmypiCodexOAuthModule {\n refreshOpenAICodexToken: (refreshToken: string) => Promise<OhmypiOAuthTokens>;\n loginOpenAICodexDeviceCode: (options: {\n onDeviceCode: (info: DeviceCodeInfo) => void;\n signal?: AbortSignal;\n }) => Promise<OhmypiOAuthTokens>;\n}\n\nexport function getOhmypiAuthPath(): string {\n return join(homedir(), '.omp', 'agent', 'auth.json');\n}\n\nfunction readAuthFile(): Record<string, unknown> {\n const path = getOhmypiAuthPath();\n if (!existsSync(path)) return {};\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf-8'));\n return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\nexport function readOhmypiCodexCredential(): OhmypiCodexCredential | null {\n const cred = readAuthFile()[PI_CODEX_PROVIDER];\n if (\n cred && typeof cred === 'object' &&\n typeof (cred as Record<string, unknown>)['access'] === 'string' &&\n typeof (cred as Record<string, unknown>)['refresh'] === 'string'\n ) {\n return cred as OhmypiCodexCredential;\n }\n return null;\n}\n\n/** Atomically merge a fresh codex credential into auth.json (mode 0600). */\nfunction writeOhmypiCodexCredential(tokens: OhmypiOAuthTokens): OhmypiCodexCredential {\n const path = getOhmypiAuthPath();\n mkdirSync(dirname(path), { recursive: true });\n const auth = readAuthFile();\n const stored: OhmypiCodexCredential = {\n type: 'oauth',\n access: tokens.access,\n refresh: tokens.refresh,\n expires: tokens.expires,\n accountId: tokens.accountId,\n };\n auth[PI_CODEX_PROVIDER] = stored;\n const tmp = `${path}.tmp-${process.pid}`;\n writeFileSync(tmp, JSON.stringify(auth, null, 2), { mode: 0o600 });\n renameSync(tmp, path);\n return stored;\n}\n\n/** Locate the `omp` executable on PATH (no subprocess — event-loop safe). */\nfunction findOmpBinary(): string | null {\n for (const dir of (process.env['PATH'] ?? '').split(delimiter)) {\n if (!dir) continue;\n const candidate = join(dir, 'omp');\n if (existsSync(candidate)) return candidate;\n }\n return null;\n}\n\n/** omp is @oh-my-pi/pi-coding-agent; the oauth submodule path mirrors pi-ai's layout. */\nconst OMP_AI_OAUTH_SUBPATH = join('@oh-my-pi', 'pi-ai', 'dist', 'utils', 'oauth', 'openai-codex.js');\n\n/**\n * Resolve omp's openai-codex OAuth submodule by walking node_modules upward\n * from the real omp binary. Returns null when not found — the caller degrades\n * gracefully to 'unavailable' status.\n */\nfunction resolveOmpCodexOAuthFile(ompBin: string): string | null {\n let dir = dirname(realpathSync(ompBin));\n for (let i = 0; i < 10; i++) {\n const candidate = join(dir, 'node_modules', OMP_AI_OAUTH_SUBPATH);\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nlet cachedModule: Promise<OhmypiCodexOAuthModule | null> | null = null;\n\n/**\n * Dynamically load omp's openai-codex OAuth submodule from the installed `omp`\n * package. Returns null on any failure so callers can degrade gracefully.\n */\nasync function loadOhmypiCodexOAuth(): Promise<OhmypiCodexOAuthModule | null> {\n if (cachedModule) return cachedModule;\n cachedModule = (async () => {\n try {\n const ompBin = findOmpBinary();\n if (!ompBin) return null;\n const oauthFile = resolveOmpCodexOAuthFile(ompBin);\n if (!oauthFile) return null;\n const mod = (await import(pathToFileURL(oauthFile).href)) as Partial<OhmypiCodexOAuthModule>;\n if (\n typeof mod.refreshOpenAICodexToken !== 'function' ||\n typeof mod.loginOpenAICodexDeviceCode !== 'function'\n ) {\n return null;\n }\n return mod as OhmypiCodexOAuthModule;\n } catch {\n return null;\n }\n })();\n return cachedModule;\n}\n\nexport type OhmypiCodexAuthStatus =\n | { status: 'ok'; expiresAt: number }\n | { status: 'missing' }\n | { status: 'expired'; expiresAt: number; refreshFailed: boolean }\n | { status: 'unavailable' };\n\nconst EXPIRY_MARGIN_MS = 60_000;\n\nexport async function getOhmypiCodexAuthStatus(opts?: { refreshIfExpired?: boolean }): Promise<OhmypiCodexAuthStatus> {\n const cred = readOhmypiCodexCredential();\n if (!cred) return { status: 'missing' };\n if (cred.expires > Date.now() + EXPIRY_MARGIN_MS) return { status: 'ok', expiresAt: cred.expires };\n\n if (opts?.refreshIfExpired) {\n const refreshed = await refreshOhmypiCodexAuth();\n if (refreshed) return { status: 'ok', expiresAt: refreshed.expires };\n return { status: 'expired', expiresAt: cred.expires, refreshFailed: true };\n }\n return { status: 'expired', expiresAt: cred.expires, refreshFailed: false };\n}\n\nexport async function refreshOhmypiCodexAuth(): Promise<OhmypiCodexCredential | null> {\n const cred = readOhmypiCodexCredential();\n if (!cred?.refresh) return null;\n const mod = await loadOhmypiCodexOAuth();\n if (!mod) return null;\n try {\n return writeOhmypiCodexCredential(await mod.refreshOpenAICodexToken(cred.refresh));\n } catch {\n return null;\n }\n}\n\nexport async function isOhmypiCodexOAuthAvailable(): Promise<boolean> {\n return (await loadOhmypiCodexOAuth()) !== null;\n}\n\nexport async function loginOhmypiCodexDeviceCode(\n onDeviceCode: (info: DeviceCodeInfo) => void,\n signal?: AbortSignal,\n): Promise<OhmypiCodexCredential> {\n const mod = await loadOhmypiCodexOAuth();\n if (!mod) {\n throw new Error(\n 'Could not load omp\\'s OAuth module. Is `omp` (@oh-my-pi/pi-coding-agent) installed and on PATH?',\n );\n }\n return writeOhmypiCodexCredential(await mod.loginOpenAICodexDeviceCode({ onDeviceCode, signal }));\n}\n","import { existsSync } from 'node:fs';\nimport { createReadStream } from 'node:fs';\nimport { readdir, mkdir, rm, stat, realpath } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { createInterface } from 'node:readline';\n\nimport { getOverdeckHome } from '../../../lib/paths.js';\n\nconst CONVERSATION_ATTACHMENTS_DIR = 'conversation-attachments';\n\n/** Run async tasks in bounded batches to avoid unbounded Promise.all\n * that can exhaust file descriptors or memory under heavy load. */\nexport async function runInBatches<T>(items: T[], batchSize: number, fn: (item: T) => Promise<unknown>): Promise<void> {\n for (let i = 0; i < items.length; i += batchSize) {\n const batch = items.slice(i, i + batchSize);\n await Promise.all(batch.map(fn));\n }\n}\n\n/** Max JSONL line length to process. Lines larger than this are skipped to\n * prevent readline from stalling permanently on oversized input (e.g. a\n * single-line base64 payload >15 MB). */\nconst MAX_JSONL_LINE_LENGTH = 15 * 1024 * 1024;\n\n/** Bounded LRU cache for readSessionAttachmentBasenames to avoid full JSONL\n * rescans when the session file has not changed. Key = sessionFile:mtimeMs. */\nconst SESSION_ATTACHMENT_CACHE_MAX = 100;\nconst sessionAttachmentCache = new Map<string, Set<string>>();\n\nfunction getAttachmentCacheKey(sessionFile: string, mtimeMs: number): string {\n return `${sessionFile}:${mtimeMs}`;\n}\n\nfunction setAttachmentCache(sessionFile: string, mtimeMs: number, basenames: Set<string>): void {\n const key = getAttachmentCacheKey(sessionFile, mtimeMs);\n sessionAttachmentCache.set(key, basenames);\n // Evict oldest entries if over max\n if (sessionAttachmentCache.size > SESSION_ATTACHMENT_CACHE_MAX) {\n const firstKey = sessionAttachmentCache.keys().next().value;\n if (firstKey !== undefined) {\n sessionAttachmentCache.delete(firstKey);\n }\n }\n}\n\nfunction getAttachmentCache(sessionFile: string, mtimeMs: number): Set<string> | undefined {\n const key = getAttachmentCacheKey(sessionFile, mtimeMs);\n return sessionAttachmentCache.get(key);\n}\n\n/** Conversation names are sanitized to [a-zA-Z0-9_-]{1,64} on creation.\n * Re-validate here for defense-in-depth against path traversal. */\nfunction assertSafeName(name: string): void {\n if (!/^[a-zA-Z0-9_-]{1,64}$/.test(name)) {\n throw new Error('Invalid conversation name');\n }\n}\n\nexport function getConversationAttachmentsRoot(): string {\n return join(getOverdeckHome(), CONVERSATION_ATTACHMENTS_DIR);\n}\n\nexport function getConversationAttachmentDir(name: string): string {\n assertSafeName(name);\n return join(getConversationAttachmentsRoot(), name);\n}\n\nexport async function ensureConversationAttachmentDir(name: string): Promise<string> {\n const dir = getConversationAttachmentDir(name);\n await mkdir(dir, { recursive: true });\n return dir;\n}\n\nexport async function cleanupConversationAttachments(name: string): Promise<void> {\n assertSafeName(name);\n await rm(getConversationAttachmentDir(name), { recursive: true, force: true });\n}\n\n/** Safety-net cleanup: remove attachment directories for conversations that no\n * longer exist in the database. Catches orphaned files from abandoned/crashed\n * sessions that the lifecycle poller missed. */\nexport async function cleanupOrphanedConversationAttachments(): Promise<void> {\n try {\n const { listConversations } = await import('../../../lib/overdeck/conversations.js');\n const convNames = new Set(listConversations().map((c) => c.name));\n const root = getConversationAttachmentsRoot();\n const dirs = await readdir(root, { withFileTypes: true });\n const orphaned = dirs\n .filter((entry) => entry.isDirectory() && !convNames.has(entry.name))\n .map((entry) => join(root, entry.name));\n await runInBatches(orphaned, 10, (path) => rm(path, { recursive: true, force: true }).catch(() => {}));\n } catch {\n // ignore — directory may not exist yet\n }\n}\n\nasync function listConversationAttachmentPaths(name: string): Promise<string[]> {\n const dir = getConversationAttachmentDir(name);\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isFile())\n .map((entry) => join(dir, entry.name));\n } catch {\n return [];\n }\n}\n\n/** Synchronous containment check: does the normalized path start with the\n * pre-resolved attachment directory prefix? Uses resolve() (not realpath)\n * so the check is I/O-free — safe because we are only cataloguing references\n * in the JSONL, not accessing the filesystem. */\nfunction isUnderAttachmentDir(resolvedAttachmentDir: string, attachmentPath: string): boolean {\n try {\n const normalized = resolve(attachmentPath);\n return normalized.startsWith(`${resolvedAttachmentDir}/`);\n } catch {\n return false;\n }\n}\n\nasync function readSessionAttachmentBasenames(sessionFile: string, name: string): Promise<Set<string>> {\n try {\n let mtimeMs: number;\n try {\n mtimeMs = (await stat(sessionFile)).mtimeMs;\n } catch {\n return new Set<string>();\n }\n\n const cached = getAttachmentCache(sessionFile, mtimeMs);\n if (cached) {\n return cached;\n }\n\n // Pre-resolve the attachment directory once (I/O outside the loop).\n // realpath follows symlinks for accuracy; resolve() is the fallback if\n // the directory has not been created yet.\n let resolvedAttachmentDir: string;\n try {\n resolvedAttachmentDir = await realpath(getConversationAttachmentDir(name));\n } catch {\n resolvedAttachmentDir = resolve(getConversationAttachmentDir(name));\n }\n\n const referenced = new Set<string>();\n const stream = createReadStream(sessionFile, { encoding: 'utf-8' });\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n try {\n for await (const line of rl) {\n if (!line.trim()) continue;\n // Skip oversized lines that could stall the reader (e.g. huge base64)\n if (line.length > MAX_JSONL_LINE_LENGTH) continue;\n let text: string | undefined;\n let structuredFoundPaths = false;\n try {\n const entry = JSON.parse(line);\n // Extract text from known Claude Code JSONL shapes\n if (typeof entry?.message?.content === 'string') {\n text = entry.message.content;\n } else if (Array.isArray(entry?.message?.content)) {\n text = entry.message.content\n .map((c: unknown) => (typeof c === 'string' ? c : (c as { text?: string })?.text ?? ''))\n .join('');\n } else if (typeof entry?.message?.text === 'string') {\n text = entry.message.text;\n } else if (typeof entry?.text === 'string') {\n text = entry.text;\n }\n } catch {\n // Not valid JSON — fall back to raw line regex\n text = line;\n }\n if (text) {\n for (const attachmentPath of extractConversationAttachmentPaths(text)) {\n if (isUnderAttachmentDir(resolvedAttachmentDir, attachmentPath)) {\n referenced.add(basename(attachmentPath));\n structuredFoundPaths = true;\n }\n }\n }\n // Only run regex fallback on the raw JSON line when structured extraction\n // found nothing. This catches tool-use and other shapes where the path may\n // be in nested fields (e.g. tool_use.input, tool_result.content) that\n // structured extraction above does not reach. We JSON-decode the captured\n // value to handle escaped slashes (\\/) that appear in raw JSON strings.\n if (!structuredFoundPaths) {\n for (const match of line.matchAll(/\"@([^\"]+)\"/g)) {\n const rawValue = match[1];\n // Only consider values that look like attachment paths\n if (!rawValue.startsWith('/') && !rawValue.startsWith('\\\\/')) continue;\n let attachmentPath: string;\n try {\n attachmentPath = JSON.parse(`\"${rawValue}\"`);\n } catch {\n attachmentPath = rawValue;\n }\n if (typeof attachmentPath === 'string' && attachmentPath.startsWith('/')) {\n if (isUnderAttachmentDir(resolvedAttachmentDir, attachmentPath)) {\n referenced.add(basename(attachmentPath));\n }\n }\n }\n }\n }\n } finally {\n rl.close();\n stream.destroy();\n }\n setAttachmentCache(sessionFile, mtimeMs, referenced);\n return referenced;\n } catch {\n return new Set<string>();\n }\n}\n\nexport async function cleanupUnreferencedConversationAttachments(conversation: { name: string; sessionFile: string | null }): Promise<void> {\n const attachmentPaths = await listConversationAttachmentPaths(conversation.name);\n if (attachmentPaths.length === 0) {\n return;\n }\n\n // No session file means nothing can reference attachments — delete them all.\n if (!conversation.sessionFile) {\n await runInBatches(attachmentPaths, 10, async (attachmentPath) => {\n await rm(attachmentPath, { force: true });\n });\n return;\n }\n\n let sessionMtimeMs: number;\n try {\n sessionMtimeMs = (await stat(conversation.sessionFile)).mtimeMs;\n } catch {\n return;\n }\n\n const referencedBasenames = await readSessionAttachmentBasenames(conversation.sessionFile, conversation.name);\n\n // Re-stat the session file to tighten against a /stop race: a JSONL write\n // may have landed while we were reading attachment basenames.\n try {\n const freshStats = await stat(conversation.sessionFile);\n if (freshStats.mtimeMs > sessionMtimeMs) {\n sessionMtimeMs = freshStats.mtimeMs;\n }\n } catch {\n // ignore\n }\n\n await runInBatches(\n attachmentPaths,\n 10,\n async (attachmentPath) => {\n if (referencedBasenames.has(basename(attachmentPath))) {\n return;\n }\n\n try {\n const attachmentMtimeMs = (await stat(attachmentPath)).mtimeMs;\n // >= preserves attachments uploaded in the same mtime tick as the\n // session JSONL write, preventing a race where a just-uploaded file\n // is deleted on stop/archive.\n if (attachmentMtimeMs >= sessionMtimeMs) {\n return;\n }\n } catch {\n return;\n }\n\n await rm(attachmentPath, { force: true });\n },\n );\n}\n\nexport function extractConversationAttachmentPaths(message: string): string[] {\n return Array.from(\n // Use a negative lookbehind (?<!\\S) instead of a consuming group so that\n // adjacent @-paths (e.g. \"@/a @/b\") are matched independently. Strip\n // trailing punctuation that is accidentally captured when a sentence or\n // parenthetical ends immediately after the path.\n message.matchAll(/(?<!\\S)@(\\/[^\\s]+)/g),\n ([, attachmentPath]) => {\n let path = attachmentPath;\n while (path.length > 1 && /[.,;:!?)\\]}+\"']$/.test(path)) {\n path = path.slice(0, -1);\n }\n return path;\n },\n );\n}\n\n/** Resolve a path for containment checking, walking parent directories with\n * realpath to preserve symlink detection even when the target file is missing. */\nasync function resolveForContainment(attachmentPath: string): Promise<string> {\n try {\n return await realpath(attachmentPath);\n } catch {\n // File does not exist — walk up the directory tree and realpath the\n // nearest existing parent, then append the remaining segments.\n let dir = dirname(attachmentPath);\n const remaining = [basename(attachmentPath)];\n while (dir !== '/' && dir !== '.') {\n try {\n const realDir = await realpath(dir);\n return join(realDir, ...remaining);\n } catch {\n remaining.unshift(basename(dir));\n dir = dirname(dir);\n }\n }\n // Nothing in the path exists — fall back to resolve (still safe against ..)\n return resolve(attachmentPath);\n }\n}\n\nexport async function isManagedConversationAttachmentPath(attachmentPath: string): Promise<boolean> {\n try {\n const attachmentsRoot = await realpath(getConversationAttachmentsRoot());\n const candidate = await resolveForContainment(attachmentPath);\n return candidate.startsWith(`${attachmentsRoot}/`);\n } catch {\n return false;\n }\n}\n\nexport async function isConversationAttachmentPath(name: string, attachmentPath: string): Promise<boolean> {\n try {\n const attachmentDir = resolve(getConversationAttachmentDir(name));\n const candidate = await resolveForContainment(attachmentPath);\n return candidate.startsWith(`${attachmentDir}/`);\n } catch {\n return false;\n }\n}\n\nexport async function hasConversationAttachment(name: string, attachmentPath: string): Promise<boolean> {\n try {\n const attachmentDir = await realpath(getConversationAttachmentDir(name));\n const candidate = await resolveForContainment(attachmentPath);\n if (!candidate.startsWith(`${attachmentDir}/`)) {\n return false;\n }\n // Use the resolved path for existence check so dangling symlinks inside\n // the root are handled consistently with the containment check.\n return existsSync(candidate);\n } catch {\n return false;\n }\n}\n\nexport async function removeConversationAttachment(name: string, attachmentPath: string): Promise<boolean> {\n if (!(await isConversationAttachmentPath(name, attachmentPath))) return false;\n await rm(attachmentPath, { force: true });\n return true;\n}\n","import { deliverAgentMessage } from '../agents.js';\nimport { paneHasBlockingChoiceMenu } from '../pane-choice-menu.js';\nimport { capturePaneText } from '../tmux.js';\n\nexport type ResumeContractDeliveryResult = 'delivered' | 'skipped-gated' | 'failed';\n\nexport async function deliverResumeContractUnlessGated(\n tmuxSession: string,\n contract: string,\n caller: string,\n method: Parameters<typeof deliverAgentMessage>[3],\n): Promise<ResumeContractDeliveryResult> {\n const resumeGatePane = await capturePaneText(tmuxSession, 90).catch(() => '');\n if (resumeGatePane && paneHasBlockingChoiceMenu(resumeGatePane)) {\n console.log(`[conversations] resume contract skipped for ${tmuxSession} — the harness is showing a blocking choice menu; the operator answers it`);\n return 'skipped-gated';\n }\n\n try {\n await deliverAgentMessage(tmuxSession, contract, caller, method);\n return 'delivered';\n } catch (err: unknown) {\n console.error(`[conversations] resume contract delivery failed for ${tmuxSession}:`, err instanceof Error ? err.message : String(err));\n return 'failed';\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { exec, execFile } from 'node:child_process';\nimport { existsSync, createReadStream } from 'node:fs';\nimport { mkdir, writeFile, readFile, readdir, stat, realpath, rename, rm } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { createInterface } from 'node:readline';\nimport { promisify } from 'node:util';\nimport { Effect } from 'effect';\nimport { BLANKED_PROVIDER_ENV } from '../child-env.js';\nimport { MODEL_ID_PATTERN } from '../model-validation.js';\nimport { getClaudePermissionFlagsStringSync, resolvePermissionModeSync, BYPASS_PERMISSION_MODE } from '../claude-permissions.js';\nimport { listProjectsAsync, type ProjectConfig } from '../projects.js';\nimport { getDefaultCwd } from '../default-cwd.js';\nimport {\n listConversations,\n getConversationByName,\n createConversation,\n markConversationEnded,\n markConversationActive,\n updateLastAttached,\n setConversationModel,\n setConversationHarness,\n backfillConversationModel,\n archiveConversation,\n removeFavorite,\n updateSpawnError,\n hasOtherActiveConversationOnTmuxSession,\n type LegacyConversation as Conversation,\n} from './conversations.js';\nimport {\n capturePane,\n capturePaneText,\n sessionExists,\n isHarnessProcessAlive,\n killSession,\n createSession,\n setOption,\n exactPaneTarget,\n listSessionNames,\n findManagedServerPidSync,\n} from '../tmux.js';\nimport { deliverAgentMessage, writeChannelsBridgeMcpConfig, dismissDevChannelsDialog, waitForReadySignal, clearReadySignal } from '../agents.js';\nimport {\n getAgentRuntimeBaseCommand,\n getProviderExportsForModel,\n getProviderAuthMode,\n} from '../agents.js';\nimport { writeBridgeTokenSync } from '../bridge-token.js';\nimport { isClaudeCodeChannelsEnabled, loadConfigSync } from '../config-yaml.js';\nimport { writePtyToken } from '../pty-token.js';\nimport { canUseHarnessSync } from '../harness-policy.js';\nimport { resolveHarness } from '../harness-resolve.js';\nimport { prepareHarnessLaunch } from '../harness-binary.js';\nimport { getProviderForModelSync, piProviderForModel, UnknownModelError } from '../providers.js';\nimport { getOhmypiCodexAuthStatus } from '../ohmypi-codex-auth.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport { piFifoPaths } from '../runtimes/pi-fifo.js';\nimport { generateLauncherScriptSync } from '../launcher-generator.js';\nimport { getAcpLauncherFields, waitForAcpHostReady, waitForPromptReady } from '../agents/runtime-command.js';\nimport { workspaceContextFile, piGlobalContextFile } from '../context-layers/layers.js';\nimport { ensureSessionContextBriefingFile } from '../briefing-freshness.js';\nimport { sessionFilePath, getOverdeckHome, resolveOhmypiExtensionPath } from '../paths.js';\nimport { resolvePtySupervisorScriptPath } from '../channels/pty-supervisor-locate.js';\nimport { buildResumeContract } from '../resume-contract.js';\nimport { jsonResponse } from '../../dashboard/server/http-helpers.js';\nimport { getEventStore } from '../../dashboard/server/event-store.js';\nimport { markRespawnPending } from '../../dashboard/server/services/pending-respawn.js';\nimport { cleanupConversationAttachments, cleanupUnreferencedConversationAttachments } from '../../dashboard/server/services/conversation-attachments.js';\nimport { resolveCodexRolloutPath } from '../../dashboard/server/routes/jsonl-resolver.js';\nimport { sendConversationControlCommand, isPiControlChannelHarness, resolveConversationDeliveryMethod } from './conversation-delivery.js';\nimport { deliverResumeContractUnlessGated } from './resume-contract-delivery.js';\nconst execAsync = promisify(exec);\nconst execFileAsync = promisify(execFile);\nconst PROCESS_CLEANUP_GRACE_MS = 750;\nconst PTY_SUPERVISOR_SOCKET_WAIT_MS = 30_000;\ntype ProcessTableRow = {\n pid: number;\n ppid: number;\n args: string;\n};\nfunction parseProcessTable(output: string): ProcessTableRow[] {\n return output\n .split('\\n')\n .map((line) => {\n const match = line.match(/^\\s*(\\d+)\\s+(\\d+)\\s+(.*)$/);\n if (!match) return null;\n return {\n pid: Number(match[1]),\n ppid: Number(match[2]),\n args: match[3] ?? '',\n };\n })\n .filter((row): row is ProcessTableRow => row !== null && Number.isFinite(row.pid) && Number.isFinite(row.ppid));\n}\nasync function readProcessTable(): Promise<ProcessTableRow[]> {\n const { stdout } = await execFileAsync('ps', ['-eo', 'pid=,ppid=,args='], {\n encoding: 'utf-8',\n maxBuffer: 10 * 1024 * 1024,\n });\n return parseProcessTable(stdout);\n}\nfunction collectProcessTree(rootPids: number[], rows: ProcessTableRow[]): number[] {\n const childrenByParent = new Map<number, number[]>();\n for (const row of rows) {\n const children = childrenByParent.get(row.ppid) ?? [];\n children.push(row.pid);\n childrenByParent.set(row.ppid, children);\n }\n const seen = new Set<number>();\n const ordered: number[] = [];\n const visit = (pid: number) => {\n if (seen.has(pid) || pid === process.pid) return;\n seen.add(pid);\n for (const child of childrenByParent.get(pid) ?? []) visit(child);\n ordered.push(pid);\n };\n for (const pid of rootPids) visit(pid);\n return ordered;\n}\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\nasync function terminatePids(pids: number[]): Promise<void> {\n if (pids.length === 0) return;\n for (const pid of pids) {\n try {\n process.kill(pid, 'SIGTERM');\n } catch {\n }\n }\n await new Promise((resolve) => setTimeout(resolve, PROCESS_CLEANUP_GRACE_MS));\n for (const pid of pids) {\n if (!isProcessAlive(pid)) continue;\n try {\n process.kill(pid, 'SIGKILL');\n } catch {\n }\n }\n}\nexport function conversationRuntimeRootPids(conv: Conversation, rows: ProcessTableRow[]): number[] {\n const launcherScript = join(getOverdeckHome(), 'conversations', conv.tmuxSession, 'launcher.sh');\n const sessionId = conv.claudeSessionId?.trim();\n const sessionNeedles = sessionId ? [`--resume ${sessionId}`, `--session-id ${sessionId}`] : [];\n const serverPid = findManagedServerPidSync();\n return rows\n .filter((row) => {\n if (row.pid === process.pid) return false;\n if (serverPid !== undefined && row.pid === serverPid) return false;\n if (row.args.includes(launcherScript)) return true;\n return sessionNeedles.some((needle) => row.args.includes(needle));\n })\n .map((row) => row.pid);\n}\nasync function killConversationRuntimeProcesses(conv: Conversation): Promise<void> {\n const rows = await readProcessTable();\n const rootPids = conversationRuntimeRootPids(conv, rows);\n const pids = collectProcessTree(rootPids, rows);\n await terminatePids(pids);\n}\nexport async function stopConversationRuntime(conv: Conversation, name: string): Promise<void> {\n if (hasOtherActiveConversationOnTmuxSession(conv.tmuxSession, name)) {\n return;\n }\n await Effect.runPromise(killSession(conv.tmuxSession).pipe(Effect.catch(() => Effect.succeed(undefined))));\n try {\n await killConversationRuntimeProcesses(conv);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.warn(`[conversations] failed to cleanup runtime processes for ${name}: ${msg}`);\n }\n}\n/** Quote a string for safe use in a bash script using single-quote wrapping. */\nfunction shellQuote(str: string): string {\n return \"'\" + str.replace(/'/g, \"'\\\"'\\\"'\") + \"'\";\n}\n// Canonical model-id shape lives in model-validation.ts — it permits the\n// square-bracket context suffix (e.g. `k3[1m]`) that a local copy of this\n// pattern once rejected (PAN-2979). Shell safety comes from single-quote\n// wrapping at the launcher, not from this character set.\nconst SAFE_MODEL_PATTERN = MODEL_ID_PATTERN;\nconst SAFE_EFFORT_PATTERN = /^(low|medium|high)$/;\nconst SAFE_ISSUE_ID_PATTERN = /^[A-Z0-9]+-[0-9]+$/;\nconst PI_CONVERSATION_SOURCE_CONTRACT = [\n 'Pi conversation source contract:',\n \"A message marked source:'extension' was injected by the Overdeck orchestrator or another agent, not typed by the human operator.\",\n 'Treat it as coordination guidance; do not attribute it to the human operator.',\n].join(' ');\nexport async function resolveAllowedHarness(requested: unknown, model?: string | null): Promise<RuntimeName> {\n if (!model) return 'claude-code';\n const explicit: RuntimeName | undefined =\n requested === 'ohmypi' || requested === 'claude-code' || requested === 'codex' || requested === 'acp' || requested === 'kimi-code'\n ? requested\n : undefined;\n return resolveHarness({ model, explicit });\n}\nexport async function isInsideGitWorkTree(dir: string): Promise<boolean> {\n try {\n const { stdout } = await execAsync('git rev-parse --is-inside-work-tree', { cwd: dir, encoding: 'utf-8' });\n return stdout.trim() === 'true';\n } catch {\n return false;\n }\n}\nasync function validateCwdContainment(cwd: string): Promise<boolean> {\n if (!cwd.startsWith('/')) return false;\n const segments = cwd.split('/').filter(Boolean);\n if (segments.includes('..')) return false;\n try {\n const resolved = await realpath(cwd);\n const stats = await stat(resolved);\n if (!stats.isDirectory()) return false;\n const home = homedir();\n return resolved.startsWith(`${home}/`) || resolved === home;\n } catch {\n return false;\n }\n}\nasync function waitForClaudeReady(tmuxSession: string): Promise<void> {\n const deadline = Date.now() + 30_000;\n while (Date.now() < deadline) {\n const output = await Effect.runPromise(capturePane(tmuxSession, 200));\n if (output.includes('❯')) {\n console.log(`[conversations] Claude Code ready in ${tmuxSession}`);\n return;\n }\n await new Promise<void>((r) => setTimeout(r, 500));\n }\n console.warn(`[conversations] Timed out waiting for Claude Code prompt in ${tmuxSession}`);\n}\nfunction isPiTuiInputReady(snapshot: string): boolean {\n return /^\\s*[❯›>]\\s/m.test(snapshot)\n || /(?:^|\\s)0(?:\\.\\d+)?%\\s+context\\s+used\\b/i.test(snapshot)\n // Current omp/Pi footer bar, e.g. \"╭── π > ⬢ Qwen3.6 Plus · ◕ high > ...\".\n // It renders within ~1s of spawn — well before MCP servers finish connecting —\n // and is the actual signal that Pi's TUI is accepting input. The two patterns\n // above matched an older Pi build's chrome and no longer appear at all, which\n // is why this check previously ran to its full timeout on every ohmypi spawn.\n || /⬢[^\\n]*◕/.test(snapshot);\n}\nexport async function waitForPiTuiReady(tmuxSession: string, timeoutMs = 60_000): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const snapshot = await Effect.runPromise(\n capturePane(tmuxSession, 40).pipe(Effect.catch(() => Effect.succeed(''))),\n );\n if (isPiTuiInputReady(snapshot)) {\n console.log(`[conversations] Pi TUI ready for ${tmuxSession}`);\n return true;\n }\n await new Promise<void>((r) => setTimeout(r, 250));\n }\n console.warn(`[conversations] Timed out waiting for Pi TUI to render in ${tmuxSession}`);\n return false;\n}\nexport function shouldReportUnresolvedLiveSession(\n conv: Pick<Conversation, 'status' | 'harness'> | null | undefined,\n): boolean {\n if (!conv || conv.status !== 'active') return false;\n return getHarnessBehavior(conv.harness).transcriptKind === 'claude-jsonl';\n}\n\nexport function conversationSessionAliveFromState(\n conv: Pick<Conversation, 'status' | 'forkStatus'>,\n tmuxSessionAlive: boolean,\n): boolean {\n return conv.status === 'active' && !conv.forkStatus && tmuxSessionAlive;\n}\n\nexport function conversationNeedsRunningRepair(\n conv: Pick<Conversation, 'status' | 'forkStatus'>,\n tmuxSessionAlive: boolean,\n harnessProcessAlive: boolean,\n): boolean {\n return conv.status === 'ended' && !conv.forkStatus && tmuxSessionAlive && harnessProcessAlive;\n}\n/** Generate a default conversation name, e.g. 20260404-1234 */\nfunction generateConversationName(): string {\n const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');\n return `${date}-${Math.floor(Math.random() * 9000 + 1000)}`;\n}\n/** Sanitize a user-provided name to be safe for tmux session names */\nfunction sanitizeName(name: string): string {\n return name.replace(/[^a-zA-Z0-9_-]/g, '-').slice(0, 64);\n}\nexport async function tmuxSessionExists(sessionName: string): Promise<boolean> {\n return Effect.runPromise(sessionExists(sessionName));\n}\nexport async function waitForTmuxSession(sessionName: string, timeoutMs = 30000): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (await Effect.runPromise(sessionExists(sessionName))) return;\n await new Promise(r => setTimeout(r, 250));\n }\n throw new Error(`Timed out waiting for tmux session ${sessionName}`);\n}\nexport function shouldUseSupervisorForConversation(\n harness: RuntimeName,\n options: { codexTransport?: 'app-server' | 'tui' } = {},\n): boolean {\n if (harness === 'codex' && options.codexTransport === 'app-server') return false;\n return getHarnessBehavior(harness).supportsPtySupervisor && process.env.OVERDECK_DOCKER_WORKSPACE !== '1' && process.env.PAN_DOCKER !== '1';\n}\nexport async function waitForConversationRuntimeReady(tmuxSession: string, harness: RuntimeName, mode: 'spawn' | 'respawn'): Promise<void> {\n if (harness === 'acp') {\n await waitForAcpHostReady(tmuxSession);\n return;\n }\n const transcriptKind = getHarnessBehavior(harness).transcriptKind;\n if (transcriptKind === 'ohmypi-jsonl') {\n const ready = await waitForPiTuiReady(tmuxSession);\n if (!ready) {\n throw new Error(\n `Pi (ohmypi) did not become interactive in ${tmuxSession} within the startup window — MCP server connections are likely still in progress. Retry once MCP servers finish connecting, or check ~/.claude/mcp.json for a slow/hanging server.`,\n );\n }\n }\n else if (transcriptKind === 'kimi-wire-jsonl') {\n // PAN-1837 review fix: this readiness dispatcher had no Kimi branch, so it\n // fell through to waitForClaudeReady() and waited for Claude Code's own\n // ready markers, which the native kimi binary never produces.\n const ready = await waitForPromptReady(tmuxSession, 'kimi-code', 30);\n if (!ready) {\n throw new Error(`Kimi Code did not become interactive in ${tmuxSession} within the startup window.`);\n }\n }\n else if (transcriptKind !== 'codex-rollout-jsonl' && mode === 'spawn') {\n await waitForClaudeReady(tmuxSession);\n console.log(`[conversations] Claude ready in ${tmuxSession}`);\n } else if (transcriptKind !== 'codex-rollout-jsonl') await waitForReadySignal(tmuxSession, 30);\n}\nexport async function handleConversationSwitchModel(\n name: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) {\n return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n }\n const model = typeof body['model'] === 'string' && body['model'].trim()\n ? body['model'].trim()\n : (conv.model ?? undefined);\n const currentHarness: RuntimeName = conv.harness ?? 'claude-code';\n const requestedHarness = body['harness'];\n let harness: RuntimeName = currentHarness;\n if (requestedHarness === 'ohmypi' || requestedHarness === 'pi' || requestedHarness === 'claude-code' || requestedHarness === 'codex' || requestedHarness === 'acp' || requestedHarness === 'kimi-code') {\n const requestedRuntime: RuntimeName = requestedHarness === 'pi' ? 'ohmypi' : requestedHarness;\n if (requestedRuntime !== currentHarness) {\n const policyModel = model ?? conv.model ?? '';\n const decision = canUseHarnessSync(\n requestedRuntime,\n policyModel,\n await getProviderAuthMode(policyModel),\n );\n if (!decision.allowed) {\n return jsonResponse(\n { error: decision.reason ?? 'Harness not allowed for this model' },\n { status: 400 },\n );\n }\n }\n harness = requestedRuntime;\n }\n const harnessChanged = harness !== currentHarness;\n if (!(await validateCwdContainment(conv.cwd))) {\n return jsonResponse({ error: 'Invalid cwd' }, { status: 400 });\n }\n if (model && !SAFE_MODEL_PATTERN.test(model)) {\n return jsonResponse({ error: 'Invalid model' }, { status: 400 });\n }\n const livePiSwitch = isPiControlChannelHarness(currentHarness) && conv.status !== 'ended';\n if (conv.claudeSessionId && harnessChanged) {\n return jsonResponse(\n { error: 'Conversation harness is locked once a conversation has started' },\n { status: 409 },\n );\n }\n if (conv.claudeSessionId && !livePiSwitch) {\n return jsonResponse(\n { error: 'Conversation model is locked once a conversation has started' },\n { status: 409 },\n );\n }\n if (model) {\n if (livePiSwitch) {\n await sendConversationControlCommand(conv, { type: 'set_model', model });\n }\n setConversationModel(name, model);\n }\n if (harnessChanged) setConversationHarness(name, harness);\n const updated = getConversationByName(name) ?? conv;\n return jsonResponse({\n ...updated,\n model: model ?? updated.model,\n harness,\n sessionAlive: false,\n });\n}\nfunction getPtySupervisorSocketPath(agentId: string): string {\n return join(getOverdeckHome(), 'sockets', `pty-${agentId}.sock`);\n}\n/**\n * Pull the supervisor's own failure out of the tmux pane it died in.\n *\n * A supervisor that cannot load its native node-pty exits instantly with an\n * ERR_MODULE_NOT_FOUND on stderr; all the spawn path sees is the socket that\n * never appeared. Reporting only the timeout hides the actual cause and sent\n * an operator hunting the memory governor instead (PAN-3172).\n */\nexport function extractSupervisorFailure(paneText: string): string | null {\n const lines = paneText.split('\\n').map((line) => line.trim()).filter(Boolean);\n const errors = lines.filter((line) => /error|cannot find|ERR_[A-Z_]+|pty-supervisor:/i.test(line));\n const chosen = (errors.length > 0 ? errors : lines).slice(-3);\n if (chosen.length === 0) return null;\n return chosen.join(' | ').slice(0, 500);\n}\n\nasync function waitForPtySupervisorSocket(agentId: string, timeoutMs = PTY_SUPERVISOR_SOCKET_WAIT_MS): Promise<void> {\n const socketPath = getPtySupervisorSocketPath(agentId);\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const info = await stat(socketPath);\n if ((info.mode & 0o777) === 0o600) return;\n } catch {\n }\n await new Promise(r => setTimeout(r, 250));\n }\n const failure = extractSupervisorFailure(await capturePaneText(agentId, 40));\n throw new Error(\n `Timed out waiting for PTY supervisor socket ${socketPath}`\n + (failure ? ` — supervisor output: ${failure}` : ''),\n );\n}\nasync function extractModelFromSessionFile(sessionFile: string): Promise<string | null> {\n try {\n if (!existsSync(sessionFile)) return null;\n const stream = createReadStream(sessionFile, { encoding: 'utf-8' });\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n try {\n for await (const line of rl) {\n if (!line.trim()) continue;\n const entry = JSON.parse(line);\n if (entry.type === 'assistant' && entry.message?.model) {\n return entry.message.model as string;\n }\n }\n } finally {\n rl.close();\n stream.destroy();\n }\n } catch {\n }\n return null;\n}\nlet backfillRunning = false;\nexport async function backfillConversationModels(resolveSessionFile: (conv: Conversation) => Promise<string | null>): Promise<void> {\n if (backfillRunning) return;\n backfillRunning = true;\n try {\n const convs = listConversations();\n const probe = await Promise.all(\n convs.map(async (conv) => (!conv.model ? await resolveSessionFile(conv) : null)),\n );\n const candidates = convs.filter((_, i) => probe[i] !== null);\n const BATCH_SIZE = 10;\n let backfilled = 0;\n for (let i = 0; i < candidates.length; i += BATCH_SIZE) {\n const batch = candidates.slice(i, i + BATCH_SIZE);\n const results = await Promise.all(\n batch.map(async (conv) => {\n const sessionFile = await resolveSessionFile(conv);\n if (!sessionFile) return false;\n const model = await extractModelFromSessionFile(sessionFile);\n if (model && SAFE_MODEL_PATTERN.test(model)) {\n backfillConversationModel(conv.name, model);\n return true;\n }\n return false;\n }),\n );\n backfilled += results.filter(Boolean).length;\n }\n if (backfilled > 0) {\n console.log(`[conversations] Backfilled model for ${backfilled} conversation(s)`);\n }\n } finally {\n backfillRunning = false;\n }\n}\nexport function startConversationModelBackfill(resolveSessionFile: (conv: Conversation) => Promise<string | null>): void {\n void backfillConversationModels(resolveSessionFile).catch((err: unknown) => {\n console.error('[conversations] Model backfill failed:', err);\n });\n}\nasync function claudeConversationSystemPromptFiles(cwd: string): Promise<string[]> {\n const files: string[] = [];\n const contextFile = workspaceContextFile(cwd);\n try {\n await stat(contextFile);\n files.push(contextFile);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n files.push(await ensureSessionContextBriefingFile());\n return files;\n}\nasync function ensurePiConversationSourceContractFile(): Promise<string> {\n const contextDir = join(getOverdeckHome(), 'context');\n await mkdir(contextDir, { recursive: true });\n const path = join(contextDir, 'pi-conversation-source-contract.md');\n await writeFile(path, `${PI_CONVERSATION_SOURCE_CONTRACT}\\n`, 'utf-8');\n return path;\n}\nexport async function piConversationSystemPromptFiles(cwd: string): Promise<string[]> {\n const files: string[] = [];\n const globalFile = piGlobalContextFile();\n try {\n await stat(globalFile);\n files.push(globalFile);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n files.push(await ensurePiConversationSourceContractFile());\n const contextFile = workspaceContextFile(cwd);\n try {\n await stat(contextFile);\n files.push(contextFile);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n files.push(await ensureSessionContextBriefingFile());\n return files;\n}\nfunction isNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';\n}\nexport async function spawnConversationSession(\n tmuxSession: string,\n cwd: string,\n claudeSessionId: string,\n model?: string,\n effort?: string,\n issueId?: string,\n resume = false,\n harness: RuntimeName = 'claude-code',\n plainFork = false,\n): Promise<void> {\n const behavior = getHarnessBehavior(harness);\n const harnessLaunch = await prepareHarnessLaunch(harness);\n const stateDir = join(getOverdeckHome(), 'conversations', tmuxSession);\n await mkdir(stateDir, { recursive: true });\n clearReadySignal(tmuxSession);\n const launcherScript = join(stateDir, 'launcher.sh');\n const permissionFlags = getClaudePermissionFlagsStringSync();\n let runtimeCommand = `claude ${permissionFlags}`;\n let providerExportsStr = '';\n let piFields: {\n harness: 'ohmypi';\n piMode: 'tui';\n piExtensionPath: string;\n piSessionDir: string;\n resumeSessionId?: string;\n } | undefined;\n let codexFields: {\n harness: 'codex';\n codexMode: 'app-server' | 'tui';\n codexHome: string;\n codexSessionDir: string;\n resumeSessionId?: string;\n } | undefined;\n let acpFields: (ReturnType<typeof getAcpLauncherFields> & { resumeSessionId?: string }) | undefined;\n let kimiCodeFields: { harness: 'kimi-code'; kimiCodeModel: string; kimiCodeYolo: true; resumeSessionId?: string } | undefined;\n let codexTransport: 'app-server' | 'tui' | undefined;\n if (behavior.launchCommandKind === 'acp-host') {\n if (!model) throw new Error('ACP conversation requires a model');\n if (!SAFE_MODEL_PATTERN.test(model)) throw new Error('Invalid model name');\n const sessionIdPath = join(getOverdeckHome(), 'agents', tmuxSession, 'acp-session-id');\n const resumeSessionId = resume\n ? await readFile(sessionIdPath, 'utf-8').then((value) => value.trim() || undefined).catch(() => undefined)\n : undefined;\n await rm(sessionIdPath, { force: true }); // PAN-3357: not a dir removal\n acpFields = {\n ...getAcpLauncherFields(tmuxSession, model, cwd, harnessLaunch.binaryPath, 'work'),\n resumeSessionId,\n };\n runtimeCommand = 'acp-host';\n } else if (model) {\n if (!SAFE_MODEL_PATTERN.test(model)) {\n throw new Error('Invalid model name');\n }\n runtimeCommand = await getAgentRuntimeBaseCommand(model, undefined, undefined, harness);\n const mode = resolvePermissionModeSync();\n if (!runtimeCommand.includes('--permission-mode')) {\n runtimeCommand = `${runtimeCommand} --permission-mode ${mode === 'auto' ? 'auto' : BYPASS_PERMISSION_MODE}`;\n }\n providerExportsStr = (await getProviderExportsForModel(model, harness)).trim();\n if (behavior.transcriptKind === 'ohmypi-jsonl') {\n if (getProviderForModelSync(model).name === 'openai') {\n const auth = await getOhmypiCodexAuthStatus({ refreshIfExpired: true });\n if (auth.status === 'missing' || auth.status === 'expired') {\n throw new Error(\n 'ohmypi ChatGPT/Codex login (openai-codex) has expired and could not be refreshed. ' +\n 'Re-authenticate with `pan pi-auth login`, then retry.',\n );\n }\n }\n const paths = piFifoPaths(tmuxSession);\n const piSessionDir = join(paths.agentDir, 'sessions');\n await mkdir(paths.agentDir, { recursive: true, mode: 0o700 });\n await mkdir(piSessionDir, { recursive: true, mode: 0o700 });\n const storedPiSessionId = resume\n ? (await readFile(join(paths.agentDir, 'session.id'), 'utf-8').then((s) => s.trim()).catch(() => undefined))\n : undefined;\n piFields = {\n harness: 'ohmypi',\n piMode: 'tui',\n piExtensionPath: resolveOhmypiExtensionPath() ?? resolve(process.cwd(), 'packages/ohmypi-extension/dist/index.js'),\n piSessionDir,\n resumeSessionId: storedPiSessionId || undefined,\n };\n } else if (behavior.usesCodexHome) {\n const codexHome = join(getOverdeckHome(), 'agents', tmuxSession, 'codex-home');\n const codexConfig = loadConfigSync().config.codex;\n const codexPermMode = codexConfig?.permissionMode ?? 'workspace';\n codexTransport = codexConfig?.transport ?? 'app-server';\n const codexApprovalPolicy = codexPermMode === 'full-access' ? 'never' : 'on-request';\n const codexSandboxMode =\n codexPermMode === 'full-access' ? 'danger-full-access'\n : codexPermMode === 'read-only' ? 'read-only'\n : 'workspace-write';\n const codexApprovalsReviewer = codexPermMode === 'auto-review' ? 'auto_review' : undefined;\n const { initCodexHome, extractThreadIdFromRollout } = await import('../runtimes/codex.js');\n initCodexHome(codexHome, {\n trustedDir: cwd,\n approvalPolicy: codexApprovalPolicy,\n sandboxMode: codexSandboxMode,\n approvalsReviewer: codexApprovalsReviewer,\n });\n const resumeSessionId = resume\n ? await resolveCodexRolloutPath(tmuxSession, { agentsDirOverride: join(getOverdeckHome(), 'agents') })\n .then((rollout) => rollout ? extractThreadIdFromRollout(rollout) ?? undefined : undefined)\n : undefined;\n codexFields = {\n harness: 'codex',\n codexMode: codexTransport,\n codexHome,\n codexSessionDir: join(codexHome, 'sessions'),\n resumeSessionId,\n };\n } else if (behavior.launchCommandKind === 'kimi-code-tui') {\n // PAN-1837 review fix: read the persisted kimi-session-id when resuming\n // so a stopped Kimi conversation actually resumes its native session\n // (-S <id>) instead of always launching fresh and silently overwriting\n // the pinned transcript pointer with a brand-new session directory.\n // Verify the pinned wire.jsonl still exists on disk before trusting the\n // id — if it was cleaned up, fall through to the fresh-launch path\n // (kimiExistingSessionsBefore capture below) instead of resuming a\n // session Kimi can no longer find.\n let kimiResumeSessionId: string | undefined;\n if (resume) {\n const kimiSessionIdPath = join(getOverdeckHome(), 'agents', tmuxSession, 'kimi-session-id');\n const pinnedId = await readFile(kimiSessionIdPath, 'utf-8').then((value) => value.trim() || undefined).catch(() => undefined);\n if (pinnedId) {\n const { kimiSessionsRoot } = await import('../runtimes/kimi-code.js');\n const candidateWire = join(kimiSessionsRoot(join(homedir(), '.kimi-code'), cwd), pinnedId, 'agents', 'main', 'wire.jsonl');\n const wireExists = await stat(candidateWire).then(() => true, () => false);\n if (wireExists) kimiResumeSessionId = pinnedId;\n }\n }\n kimiCodeFields = {\n harness: 'kimi-code',\n kimiCodeModel: model,\n kimiCodeYolo: true,\n ...(kimiResumeSessionId ? { resumeSessionId: kimiResumeSessionId } : {}),\n };\n }\n }\n let launcherModel = model;\n if (behavior.contextLayerKind === 'pi' && model) {\n const piProvider = piProviderForModel(model);\n if (piProvider) launcherModel = `${piProvider}/${model}`;\n }\n if (effort && !SAFE_EFFORT_PATTERN.test(effort)) {\n throw new Error('Invalid effort level');\n }\n const useSupervisor = shouldUseSupervisorForConversation(harness, { codexTransport });\n let supervisorScriptPath: string | undefined;\n if (useSupervisor) {\n supervisorScriptPath = resolvePtySupervisorScriptPath();\n await writePtyToken(tmuxSession);\n }\n let channelsBridgeMcpConfig: string | undefined;\n if (\n !piFields &&\n !codexFields &&\n !acpFields &&\n !kimiCodeFields &&\n !plainFork &&\n isClaudeCodeChannelsEnabled() &&\n (!model || getProviderForModelSync(model).name === 'anthropic') &&\n process.env.CLAUDE_CODE_USE_BEDROCK !== '1' &&\n process.env.CLAUDE_CODE_USE_VERTEX !== '1' &&\n process.env.CLAUDE_CODE_USE_FOUNDRY !== '1' &&\n process.env.OVERDECK_DOCKER_WORKSPACE !== '1' &&\n process.env.PAN_DOCKER !== '1'\n ) {\n channelsBridgeMcpConfig = join(stateDir, 'agent-mcp.json');\n writeBridgeTokenSync(tmuxSession);\n await writeChannelsBridgeMcpConfig(channelsBridgeMcpConfig, tmuxSession);\n }\n // PAN-1837 review fix: the tmux-create + session-capture sequence below is\n // wrapped per-bucket (withKimiSessionCaptureLock) for kimi-code conversations\n // and genuinely awaited — not fire-and-forget — so two conversations sharing\n // one cwd never snapshot the same \"existing sessions\" set and race for the\n // same newest directory. Non-kimi harnesses run this closure directly,\n // unaffected by the lock.\n const launchTmuxAndCaptureSession = async (): Promise<void> => {\n // Conversations have no AgentState row, so the dashboard can't resolve\n // their wire.jsonl without a conversation-owned captured session id —\n // snapshot the bucket now (before the tmux session exists, and — for\n // kimi-code — inside the per-bucket lock) so the capture below can diff\n // against it instead of guessing from mtime. Skipped when kimiCodeFields\n // already carries a verified resumeSessionId (PAN-1837 review fix): a\n // true resume must not snapshot/capture a replacement id and overwrite\n // the pinned transcript pointer.\n let kimiExistingSessionsBefore: Set<string> | undefined;\n if (kimiCodeFields && !kimiCodeFields.resumeSessionId) {\n try {\n const { kimiSessionsRoot } = await import('../runtimes/kimi-code.js');\n kimiExistingSessionsBefore = new Set(await readdir(kimiSessionsRoot(join(homedir(), '.kimi-code'), cwd)));\n } catch {\n kimiExistingSessionsBefore = new Set();\n }\n }\n\n const launcherTmp = `${launcherScript}.${randomUUID()}.tmp`;\n await writeFile(\n launcherTmp,\n generateLauncherScriptSync({\n role: 'work',\n spawnMode: 'conversation',\n workingDir: cwd,\n setTerminalEnv: true,\n unsetProviderEnv: true,\n overdeckEnv: { ...(issueId ? { issueId } : {}), ...((piFields || codexFields || acpFields || useSupervisor) ? { agentId: tmuxSession } : {}) },\n extraEnvExports: [\n harnessLaunch.pathExport,\n `export OVERDECK_DASHBOARD_URL=\"http://127.0.0.1:${process.env['API_PORT'] ?? process.env['PORT'] ?? '3011'}\"`,\n ],\n providerExports: providerExportsStr || undefined,\n trapHup: true,\n baseCommand: runtimeCommand,\n appendSystemPromptFiles: piFields\n ? await piConversationSystemPromptFiles(cwd)\n : codexFields || acpFields || kimiCodeFields\n ? []\n : await claudeConversationSystemPromptFiles(cwd),\n model: launcherModel,\n ...(piFields ?? codexFields ?? acpFields ?? kimiCodeFields ?? {\n resumeSessionId: resume ? claudeSessionId : undefined,\n sessionId: resume ? undefined : claudeSessionId,\n }),\n extraArgs: !piFields && !acpFields && !kimiCodeFields && effort ? `--effort \"${effort}\"` : undefined,\n keepAlive: true,\n fileMode: 0o700,\n channelsBridgeMcpConfig,\n useSupervisor,\n supervisorScriptPath,\n }),\n { mode: 0o700 },\n );\n await rename(launcherTmp, launcherScript);\n try {\n await Effect.runPromise(killSession(tmuxSession));\n } catch {\n }\n console.log(`[claude-invoke] purpose=conversation-session | model=${model || 'default'} | source=conversations.ts:spawnConversationSession | session=${tmuxSession} | resume=${resume} | command=\"${runtimeCommand}\"`);\n try {\n const { preTrustDirectory } = await import('../workspace-manager.js') as { preTrustDirectory: (dir: string) => void };\n preTrustDirectory(cwd);\n } catch { /* non-fatal */ }\n try {\n await Effect.runPromise(createSession(tmuxSession, cwd, `bash ${shellQuote(launcherScript)}`, {\n env: {\n ...BLANKED_PROVIDER_ENV,\n TERM: 'xterm-256color',\n },\n }));\n } catch (err) {\n if ((err as { code?: string })?.code === 'ENOENT') {\n throw new Error(\n 'tmux is not installed. Install it with: brew install tmux (macOS) or sudo apt-get install tmux (Linux)',\n );\n }\n throw err;\n }\n if (useSupervisor) {\n await waitForPtySupervisorSocket(tmuxSession);\n }\n if (behavior.usesCodexHome && codexFields?.codexHome && codexTransport === 'tui') {\n const codexHomeDir = codexFields.codexHome;\n void (async () => {\n try {\n const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } =\n await import('../runtimes/codex.js');\n const rollout = await waitForCodexRollout(codexHomeDir, 120_000);\n if (rollout) {\n const threadId = extractThreadIdFromRollout(rollout);\n if (threadId) writeThreadId(tmuxSession, threadId);\n }\n } catch {\n }\n })();\n }\n if (kimiCodeFields && kimiExistingSessionsBefore) {\n // Genuinely awaited (not fire-and-forget) so the per-bucket lock below\n // stays held until this conversation's session directory is captured —\n // that's what makes the next queued launch's snapshot see it as\n // \"existing\" rather than racing to claim it too.\n //\n // PAN-1837 review fix (cycle 8): a null capture or a thrown error here\n // used to be silently absorbed, so the conversation was still reported\n // healthy with no kimi-session-id pointer — transcript/cost resolution\n // then falls back to the newest session in the shared cwd bucket, which\n // can render a DIFFERENT conversation's or agent's prompts/transcript\n // under this identity. Require a real captured id or fail the launch,\n // same fail-closed contract as spawnAgent/restartAgent/recoverAgent.\n const { waitForNewKimiSessionAsync, writeKimiSessionId } = await import('../runtimes/kimi-code.js');\n const sessionId = await waitForNewKimiSessionAsync(join(homedir(), '.kimi-code'), cwd, kimiExistingSessionsBefore);\n if (!sessionId) {\n throw new Error(\n `kimi-code session capture timed out for ${tmuxSession} — no new session directory appeared under the workspace bucket`,\n );\n }\n writeKimiSessionId(tmuxSession, sessionId);\n }\n };\n\n if (kimiCodeFields) {\n const { withKimiSessionCaptureLock } = await import('../runtimes/kimi-code.js');\n await withKimiSessionCaptureLock(join(homedir(), '.kimi-code'), cwd, launchTmuxAndCaptureSession);\n } else {\n await launchTmuxAndCaptureSession();\n }\n if (channelsBridgeMcpConfig) {\n void dismissDevChannelsDialog(tmuxSession).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[conversations] dismissDevChannelsDialog failed for ${tmuxSession}: ${msg}`);\n });\n }\n await Effect.runPromise(setOption(tmuxSession, 'destroy-unattached', 'off'));\n await Effect.runPromise(setOption(exactPaneTarget(tmuxSession), 'remain-on-exit', 'on'));\n}\nexport interface ResolvedRegisteredProject {\n key: string;\n config: ProjectConfig;\n}\n\n/** Resolve a project key or display name without blocking the dashboard event loop. */\nexport async function resolveRegisteredProject(\n input: string,\n): Promise<ResolvedRegisteredProject | { error: string }> {\n const projects = await listProjectsAsync();\n const project = projects.find((candidate) => candidate.key === input)\n ?? projects.find((candidate) => candidate.config.name === input);\n return project ?? { error: `Unknown project: ${input}` };\n}\n\n/**\n * Resolve a conversation's cwd from a project identifier.\n *\n * The Command Deck identifies projects by display name, not yaml key\n * (PAN-2590) — accept either, like GET /api/session-trees does.\n */\nexport async function resolveProjectCwd(\n projectIdentifier: string,\n): Promise<{ key: string; cwd: string } | { error: string }> {\n const resolved = await resolveRegisteredProject(projectIdentifier);\n if ('error' in resolved) return resolved;\n const projectPath = resolved.config.path;\n if (!projectPath) {\n return { error: `Project path does not exist: (unset) (project: ${projectIdentifier})` };\n }\n try {\n await stat(projectPath);\n } catch {\n return { error: `Project path does not exist: ${projectPath} (project: ${projectIdentifier})` };\n }\n return { key: resolved.key, cwd: projectPath };\n}\n\nexport interface ConversationCreateRequestBody { [key: string]: unknown }\nexport async function handleConversationCreate(\n body: ConversationCreateRequestBody,\n deps: { generateAiTitle: (name: string, message: string) => Promise<void> },\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const message = typeof body['message'] === 'string' ? body['message'].trim() : '';\n const model = typeof body['model'] === 'string' ? body['model'].trim() || undefined : undefined;\n const effort = typeof body['effort'] === 'string' ? body['effort'].trim() || undefined : undefined;\n const harness = await resolveAllowedHarness(body['harness'], model);\n const issueId = typeof body['issueId'] === 'string' ? body['issueId'] : undefined;\n const projectKey = typeof body['projectKey'] === 'string' ? body['projectKey'].trim() : undefined;\n if (issueId && !SAFE_ISSUE_ID_PATTERN.test(issueId)) return jsonResponse({ error: 'Invalid issueId' }, { status: 400 });\n if (model && !SAFE_MODEL_PATTERN.test(model)) return jsonResponse({ error: 'Invalid model' }, { status: 400 });\n if (effort && !SAFE_EFFORT_PATTERN.test(effort)) return jsonResponse({ error: 'Invalid effort' }, { status: 400 });\n let cwd = getDefaultCwd();\n let canonicalProjectKey: string | undefined;\n if (projectKey) {\n const resolved = await resolveProjectCwd(projectKey);\n if ('error' in resolved) return jsonResponse({ error: resolved.error }, { status: 400 });\n cwd = resolved.cwd;\n canonicalProjectKey = resolved.key;\n }\n if (message && message.length > 50_000) {\n return jsonResponse({ error: 'message exceeds maximum length of 50000 characters' }, { status: 400 });\n }\n let name = generateConversationName();\n for (let i = 0; i < 5 && getConversationByName(name); i++) name = generateConversationName();\n const tmuxSession = `conv-${name}`;\n const claudeSessionId = randomUUID();\n console.log(`[conversations] Creating conversation \"${name}\" with model=${model ?? 'default'} effort=${effort ?? 'default'} cwd=${cwd}`);\n const MAX_TITLE_LEN = 60;\n const title = message ? message.slice(0, MAX_TITLE_LEN) + (message.length > MAX_TITLE_LEN ? '…' : '') : 'New conversation';\n const conv = createConversation({ name, tmuxSession, cwd, issueId, claudeSessionId, title, titleSource: message ? 'auto' : 'default', titleSeed: title, model, effort, harness, projectKey: canonicalProjectKey });\n getEventStore().emitOnly({ type: 'conversation.created', timestamp: new Date().toISOString(), payload: { conversationName: name } });\n void (async () => {\n try {\n await spawnConversationSession(tmuxSession, cwd, claudeSessionId, model, effort, issueId, false, harness);\n console.log(`[conversations] tmux session ${tmuxSession} spawned, sessionId: ${claudeSessionId}`);\n await waitForConversationRuntimeReady(tmuxSession, harness, 'spawn');\n if (message) {\n const delivery = await deliverAgentMessage(tmuxSession, message, 'conversation-message', resolveConversationDeliveryMethod(conv));\n if (harness === 'acp' && !delivery.ok) {\n throw new Error(`ACP initial prompt did not land: ${delivery.failure ?? 'unknown failure'}`);\n }\n }\n } catch (spawnErr: unknown) {\n const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);\n console.error(`[conversations] background spawn failed for ${tmuxSession}: ${msg}`);\n // PAN-1837 review fix: kimi-code needs the same teardown-on-failure as\n // acp — a failed capture must not leave a running tmux session with\n // no owned native identity presented as a healthy conversation.\n if (harness === 'acp' || harness === 'kimi-code') await stopConversationRuntime(conv, name);\n updateSpawnError(name, msg);\n getEventStore().emitOnly({ type: 'conversation.created', timestamp: new Date().toISOString(), payload: { conversationName: name } });\n }\n })();\n if (message) {\n void deps.generateAiTitle(name, message).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[TITLE-GEN-FAILED] AI title generation FAILED for \"${name}\" — NO RETRY, NO FALLBACK:`, msg);\n });\n }\n return jsonResponse({ ...conv, sessionAlive: false }, { status: 201 });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] create conversation failed:', msg);\n return jsonResponse({ error: msg || 'Internal server error' }, { status: error instanceof UnknownModelError ? 400 : 500 });\n }\n}\nexport async function handleConversationStop(name: string, deps: { resolveSessionFileForCleanup?: (conv: Conversation) => string | null }): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n await stopConversationRuntime(conv, name);\n markConversationEnded(name);\n void (async () => {\n await new Promise((r) => setTimeout(r, 500));\n await cleanupUnreferencedConversationAttachments({ name: conv.name, sessionFile: deps.resolveSessionFileForCleanup?.(conv) ?? null });\n })();\n return jsonResponse({ success: true });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] stop conversation failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\nexport async function handleConversationResume(\n name: string,\n body: Record<string, unknown>,\n deps: { resolveSessionFile: (conv: Conversation) => Promise<string | null> },\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n const model = typeof body['model'] === 'string' && body['model'].trim() ? body['model'].trim() : (conv.model ?? undefined);\n const effort = typeof body['effort'] === 'string' && body['effort'].trim() ? body['effort'].trim() : (conv.effort ?? undefined);\n const claudeAlive = await isHarnessProcessAlive(conv.tmuxSession);\n if (claudeAlive) {\n updateLastAttached(name);\n markConversationActive(name);\n return jsonResponse({ ...conv, status: 'active', reattached: true });\n }\n const oldSessionId = conv.claudeSessionId;\n const resumeCause = conv.status === 'ended' ? 'operator' : 'system';\n const harness: RuntimeName = conv.harness ?? 'claude-code';\n const modelChanged = !!model && model !== conv.model;\n if (!(await validateCwdContainment(conv.cwd))) return jsonResponse({ error: 'Invalid cwd' }, { status: 400 });\n if (model && modelChanged && !SAFE_MODEL_PATTERN.test(model)) return jsonResponse({ error: 'Invalid model' }, { status: 400 });\n if (model && modelChanged) setConversationModel(name, model);\n let canResume = !!oldSessionId;\n if (oldSessionId) {\n const resumeFile = await deps.resolveSessionFile(conv);\n if (!resumeFile || !existsSync(resumeFile)) {\n canResume = false;\n console.error(`[conversations] SESSION-LOST ${name} harness=${harness} claudeSessionId=${oldSessionId} resolved=${resumeFile ?? 'null'} — resuming with a fresh session`);\n }\n }\n const respawn = markRespawnPending(conv.tmuxSession);\n try {\n await spawnConversationSession(conv.tmuxSession, conv.cwd, oldSessionId ?? randomUUID(), model, effort, conv.issueId ?? undefined, canResume, harness);\n await waitForTmuxSession(conv.tmuxSession);\n await waitForConversationRuntimeReady(conv.tmuxSession, harness, 'respawn');\n // The harness may open its own blocking gate on resume. The operator owns\n // that answer, so the contract delivery seam skips rather than races it.\n await deliverResumeContractUnlessGated(\n conv.tmuxSession,\n `CONVERSATION RESUME: ${buildResumeContract(resumeCause)}`,\n 'conversation-resume',\n resolveConversationDeliveryMethod(conv),\n );\n markConversationActive(name);\n return jsonResponse({ ...conv, status: 'active', model: model ?? conv.model, harness, reattached: false, sessionAlive: true });\n } catch (error) {\n // PAN-1837 review fix: kimi-code needs the same teardown-on-failure as\n // acp — a failed capture must not leave a running tmux session with no\n // owned native identity presented as a healthy conversation.\n if (harness === 'acp' || harness === 'kimi-code') await stopConversationRuntime(conv, name);\n throw error;\n } finally {\n respawn.done();\n }\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] resume conversation failed:', msg);\n return jsonResponse({ error: msg || 'Internal server error' }, { status: 500 });\n }\n}\nexport async function handleConversationDelete(\n name: string,\n deps: { invalidateFavoritesCache: () => void },\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n await stopConversationRuntime(conv, name);\n markConversationEnded(name);\n archiveConversation(name);\n removeFavorite('conversation', name);\n deps.invalidateFavoritesCache();\n await cleanupConversationAttachments(name);\n return jsonResponse({ success: true });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] delete conversation failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\nexport async function handleConversationRestartAll(\n deps: { resolveSessionFile: (conv: Conversation) => Promise<string | null> },\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const allConvs = listConversations();\n const liveSessionNames = new Set(await Effect.runPromise(listSessionNames()));\n const convs = allConvs.filter((c) => liveSessionNames.has(c.tmuxSession));\n const results: { name: string; model: string | null; status: string }[] = [];\n for (const conv of convs) {\n const respawn = markRespawnPending(conv.tmuxSession);\n let attemptedHarness: RuntimeName = conv.harness ?? 'claude-code';\n try {\n await Effect.runPromise(killSession(conv.tmuxSession).pipe(Effect.catch(() => Effect.succeed(undefined))));\n const oldSessionId = conv.claudeSessionId;\n const sessionFileForResume = await deps.resolveSessionFile(conv);\n const canResume = !!oldSessionId && !!sessionFileForResume && existsSync(sessionFileForResume);\n const harness = await resolveAllowedHarness(conv.harness, conv.model);\n attemptedHarness = harness;\n await spawnConversationSession(conv.tmuxSession, conv.cwd, oldSessionId ?? randomUUID(), conv.model ?? undefined, conv.effort ?? undefined, conv.issueId ?? undefined, canResume, harness);\n if (harness === 'acp') {\n await waitForConversationRuntimeReady(conv.tmuxSession, harness, 'respawn');\n }\n setConversationHarness(conv.name, harness);\n markConversationActive(conv.name);\n results.push({ name: conv.name, model: conv.model, status: 'restarted' });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[conversations] Failed to restart ${conv.name}:`, msg);\n // PAN-1837 review fix: kimi-code needs the same teardown-on-failure as\n // acp — a failed capture must not leave a running tmux session with no\n // owned native identity presented as a healthy conversation.\n if (attemptedHarness === 'acp' || attemptedHarness === 'kimi-code') await stopConversationRuntime(conv, conv.name);\n results.push({ name: conv.name, model: conv.model, status: 'failed' });\n } finally {\n respawn.done();\n }\n }\n console.log(`[conversations] Restarted ${results.filter(r => r.status === 'restarted').length}/${convs.length} conversations`);\n return jsonResponse({ restarted: results.length, results });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] restart conversations failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { request as httpRequest } from 'node:http';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\n\nimport { jsonResponse } from '../../dashboard/server/http-helpers.js';\nimport { validateOriginHeaders, getHeaderFromMap, type HeaderMap } from '../../dashboard/server/routes/origin-validation.js';\nimport { detectAwaitingInputForAgent, parseCodexApprovalPrompt } from '../agent-input-detection.js';\nimport {\n getConversationById,\n getConversationByName,\n setConversationEffort,\n updateConversationDeliveryMethod,\n type LegacyConversation as Conversation,\n} from './conversations.js';\nimport { getHarnessBehavior } from '../runtimes/behavior.js';\nimport type { RuntimeName } from '../runtimes/types.js';\nimport { loadConfigSync } from '../config-yaml.js';\nimport {\n writeConversationControlCommand,\n type ControlCommand,\n type ThinkingLevel,\n} from '../runtimes/conversation-control.js';\nimport { sendRawKeystroke, sendKeysAsync } from '../tmux.js';\nimport { deliverAgentMessage } from '../agents.js';\nimport { tmuxSessionExists } from './conversation-runtime.js';\nimport type { PendingAskUserQuestionSnapshot, PendingInputKind } from '../agent-enrichment.js';\nimport { getOverdeckHome } from '../paths.js';\nimport { BRIDGE_TOKEN_HEADER } from '../bridge-token.js';\n\nexport const CONTROL_ACK_TIMEOUT_MS = 10_000;\n\nexport interface ConversationControlAck {\n id: string\n ok: boolean\n error?: string\n}\n\ninterface PendingConversationControlAck {\n resolve: () => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\nconst pendingConversationControlAcks = new Map<string, PendingConversationControlAck>();\n\nconst PLAN_ACTION_KEYSTROKES: Record<string, string> = {\n 'approve-auto': '1',\n 'approve-manual': '2',\n 'reject-ultraplan': '3',\n};\n\ninterface CodexAppServerPendingRequest {\n id: string | number;\n method: string;\n params?: unknown;\n}\n\ninterface CodexAppServerStatus {\n pendingRequests?: CodexAppServerPendingRequest[];\n}\n\n/**\n * PAN-1520 (FR-4) — deliver a native plan-menu action to any tmux session\n * (conversation or agent). The plan menu is answered with raw keystrokes\n * (1/2/3/4), not a text message; `reject-feedback` follows the '4' keystroke\n * with the operator's feedback through the normal message pipeline.\n * Returns null on success, or an error string for an invalid action.\n */\nexport async function deliverPlanActionToSession(\n sessionName: string,\n action: string,\n feedback: string,\n deliveryMethod: 'auto' | 'channels' | 'tmux' = 'auto',\n): Promise<string | null> {\n if (action === 'reject-feedback') {\n await Effect.runPromise(sendRawKeystroke(sessionName, '4', 'plan-action-reject'));\n if (feedback) {\n await new Promise(r => setTimeout(r, 300));\n await deliverAgentMessage(sessionName, feedback, 'plan-action-feedback', deliveryMethod);\n }\n return null;\n }\n const keystroke = PLAN_ACTION_KEYSTROKES[action];\n if (!keystroke) return `Invalid action: ${action}`;\n await Effect.runPromise(sendRawKeystroke(sessionName, keystroke, `plan-action-${action}`));\n return null;\n}\n\nexport function registerConversationControlAck(\n commandId: string,\n timeoutMs: number = CONTROL_ACK_TIMEOUT_MS,\n): Promise<void> {\n const existing = pendingConversationControlAcks.get(commandId);\n if (existing) {\n clearTimeout(existing.timer);\n existing.reject(new Error(`Replaced pending conversation control ack ${commandId}`));\n pendingConversationControlAcks.delete(commandId);\n }\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingConversationControlAcks.delete(commandId);\n reject(new Error(`Timed out waiting for conversation control ack ${commandId}`));\n }, timeoutMs);\n pendingConversationControlAcks.set(commandId, {\n resolve: () => {\n clearTimeout(timer);\n pendingConversationControlAcks.delete(commandId);\n resolve();\n },\n reject: (error: Error) => {\n clearTimeout(timer);\n pendingConversationControlAcks.delete(commandId);\n reject(error);\n },\n timer,\n });\n });\n}\n\nexport function resolveConversationControlAck(ack: ConversationControlAck): 'resolved' | 'rejected' | 'unknown' {\n const pending = pendingConversationControlAcks.get(ack.id);\n if (!pending) return 'unknown';\n if (ack.ok) {\n pending.resolve();\n return 'resolved';\n }\n pending.reject(new Error(ack.error || `Conversation control command ${ack.id} failed`));\n return 'rejected';\n}\n\nexport function getPendingConversationControlAckCount(): number {\n return pendingConversationControlAcks.size;\n}\n\nexport function clearPendingConversationControlAcksForTests(): void {\n for (const pending of pendingConversationControlAcks.values()) {\n clearTimeout(pending.timer);\n }\n pendingConversationControlAcks.clear();\n}\n\nexport function handleConversationControlAck(\n body: Record<string, unknown>,\n): { status: number; body: { ok: true; outcome: 'resolved' | 'rejected' | 'unknown' } | { error: string } } {\n const id = typeof body['id'] === 'string' ? body['id'].trim() : '';\n if (!id) return { status: 400, body: { error: 'id is required' } };\n const ok = body['ok'] === true;\n const error = typeof body['error'] === 'string' ? body['error'] : undefined;\n const outcome = resolveConversationControlAck({ id, ok, ...(error !== undefined ? { error } : {}) });\n return { status: 200, body: { ok: true, outcome } };\n}\n\nexport function validateConversationControlAckOrigin(\n headers: HeaderMap,\n method = 'POST',\n): { ok: true } | { ok: false; error: string } {\n const origin = getHeaderFromMap(headers, 'origin');\n const referer = getHeaderFromMap(headers, 'referer');\n if (!origin && !referer) return { ok: true };\n return validateOriginHeaders(headers, method);\n}\n\nexport async function handleConversationCodexApproval(\n rawId: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const optionNumber = Number((body as { optionNumber?: unknown }).optionNumber);\n const requestedToolUseId = typeof body.toolUseId === 'string' ? body.toolUseId : undefined;\n if (!Number.isInteger(optionNumber) || optionNumber < 1 || optionNumber > 9) {\n return jsonResponse({ error: 'optionNumber must be an integer 1-9' }, { status: 400 });\n }\n const numericId = Number(rawId);\n const conv = !Number.isNaN(numericId) && /^\\d+$/.test(rawId)\n ? getConversationById(numericId)\n : getConversationByName(rawId);\n if (!conv) {\n return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n }\n if (getHarnessBehavior(conv.harness).transcriptKind !== 'codex-rollout-jsonl') {\n return jsonResponse({ error: 'Not a Codex conversation' }, { status: 400 });\n }\n if (!(await tmuxSessionExists(conv.tmuxSession))) {\n return jsonResponse({ error: 'Conversation session is not running' }, { status: 409 });\n }\n if (codexUsesAppServerTransport()) {\n const pending = await readFirstAppServerApproval(conv.tmuxSession);\n if (!pending) {\n return jsonResponse({ error: 'No Codex approval prompt is currently pending' }, { status: 409 });\n }\n const expectedToolUseId = `${CODEX_APPROVAL_TOOL_PREFIX}${conv.tmuxSession}:${pending.id}`;\n if (!requestedToolUseId) {\n return jsonResponse({ error: 'Codex approval request id is required' }, { status: 400 });\n }\n if (requestedToolUseId !== expectedToolUseId) {\n return jsonResponse({ error: 'Codex approval request changed; refresh pending input before responding' }, { status: 409 });\n }\n const decision = codexAppServerDecisionForOption(optionNumber);\n if (!decision) {\n return jsonResponse({ error: 'optionNumber out of range (1-2)' }, { status: 400 });\n }\n await postCodexAppServerOp(conv.tmuxSession, { op: 'approval', requestId: pending.id, decision });\n return jsonResponse({ ok: true, optionNumber, decision });\n }\n // Re-detect uncached so we only send keystrokes when the menu is still\n // up, and so we can bound optionNumber to the options actually shown.\n const detection = await Effect.runPromise(\n detectAwaitingInputForAgent(conv.tmuxSession, { isPlanning: false, cache: false }),\n );\n const parsed = detection ? parseCodexApprovalPrompt(detection.prompt) : null;\n if (!parsed) {\n return jsonResponse({ error: 'No Codex approval prompt is currently pending' }, { status: 409 });\n }\n if (optionNumber > parsed.options.length) {\n return jsonResponse({ error: `optionNumber out of range (1-${parsed.options.length})` }, { status: 400 });\n }\n await deliverCodexApprovalChoice(conv.tmuxSession, optionNumber);\n return jsonResponse({ ok: true, optionNumber });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] codex approval failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\n\nexport async function handleConversationPlanAction(\n name: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n try {\n const conv = getConversationByName(name);\n if (!conv) {\n return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n }\n const action = typeof body['action'] === 'string' ? body['action'] : '';\n const feedback = typeof body['feedback'] === 'string' ? body['feedback'].trim() : '';\n const error = await deliverPlanActionToSession(\n conv.tmuxSession,\n action,\n feedback,\n resolveConversationDeliveryMethod(conv),\n );\n if (error) {\n return jsonResponse({ error }, { status: 400 });\n }\n return jsonResponse({ ok: true });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error('[conversations] plan action failed:', msg);\n return jsonResponse({ error: 'Internal server error' }, { status: 500 });\n }\n}\n\ntype ConversationControlDeliverAs = Extract<ControlCommand['type'], 'prompt' | 'steer' | 'follow_up'>;\ntype ConversationControlCommandInput = ControlCommand extends infer C\n ? C extends { id: string }\n ? Omit<C, 'id'>\n : never\n : never;\n\nconst THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh'] as const satisfies readonly ThinkingLevel[];\nconst PI_CONVERSATION_ABORT_KEY = 'Escape';\n\nexport function isPiControlChannelHarness(harness: RuntimeName | 'pi'): boolean {\n return harness === 'ohmypi' || harness === 'pi';\n}\n\nexport function parseThinkingLevel(value: unknown): ThinkingLevel | null {\n return typeof value === 'string' && (THINKING_LEVELS as readonly string[]).includes(value)\n ? value as ThinkingLevel\n : null;\n}\n\nexport function pickDeliverAs(bodyDeliverAs: unknown): ConversationControlDeliverAs {\n if (bodyDeliverAs === 'follow_up') return 'follow_up';\n return 'steer';\n}\n\nexport function resolveConversationDeliveryMethod(conv: Pick<Conversation, 'harness' | 'deliveryMethod'>): 'auto' | 'channels' | 'tmux' {\n const harness = conv.harness ?? 'claude-code';\n if (harness === 'acp') return 'auto';\n if (isPiControlChannelHarness(harness)) return 'auto';\n if (harness === 'codex' && loadConfigSync().config.codex?.transport !== 'tui') return 'auto';\n return conv.deliveryMethod ?? (getHarnessBehavior(harness).deliveryKind === 'rpc-fifo' ? 'tmux' : 'auto');\n}\n\nexport async function sendConversationControlCommand(\n conv: Pick<Conversation, 'tmuxSession'>,\n commandInput: ConversationControlCommandInput,\n): Promise<void> {\n const id = randomUUID();\n const ackPromise = registerConversationControlAck(id);\n const command: ControlCommand = { id, ...commandInput };\n\n try {\n await writeConversationControlCommand(conv.tmuxSession, command);\n } catch (err) {\n resolveConversationControlAck({\n id,\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n await ackPromise;\n}\n\nexport async function deliverConversationViaControlChannel(\n conv: Pick<Conversation, 'tmuxSession'>,\n message: string,\n options: {\n source: 'operator' | 'orchestrator'\n deliverAs: ConversationControlDeliverAs\n },\n): Promise<void> {\n await sendConversationControlCommand(conv, {\n type: options.deliverAs,\n message,\n source: options.source,\n });\n}\n\nexport async function handleConversationThinkingLevel(\n name: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n\n const harness: RuntimeName = conv.harness ?? 'claude-code';\n if (!isPiControlChannelHarness(harness)) {\n return jsonResponse({ error: 'Thinking level control is only supported for Pi conversations' }, { status: 400 });\n }\n if (conv.status === 'ended') {\n return jsonResponse({ error: 'Session has ended — start a new run to interact' }, { status: 422 });\n }\n\n const level = parseThinkingLevel(body['level']);\n if (!level) return jsonResponse({ error: 'Invalid thinking level' }, { status: 400 });\n\n await sendConversationControlCommand(conv, { type: 'set_thinking_level', level });\n setConversationEffort(name, level);\n const updated = getConversationByName(name) ?? conv;\n return jsonResponse({ ok: true, effort: updated.effort ?? level });\n}\n\nexport async function handleConversationCompact(name: string): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n\n const harness: RuntimeName = conv.harness ?? 'claude-code';\n if (!isPiControlChannelHarness(harness)) {\n return jsonResponse({ error: 'Compact control endpoint is only supported for Pi conversations' }, { status: 400 });\n }\n if (conv.status === 'ended') {\n return jsonResponse({ error: 'Session has ended — start a new run to interact' }, { status: 422 });\n }\n\n await sendConversationControlCommand(conv, { type: 'compact' });\n return jsonResponse({ ok: true });\n}\n\nexport async function handleConversationAbort(name: string): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n\n const harness: RuntimeName = conv.harness ?? 'claude-code';\n if (!isPiControlChannelHarness(harness)) {\n return jsonResponse({ error: 'Abort control endpoint is only supported for Pi conversations' }, { status: 400 });\n }\n if (conv.status === 'ended') {\n return jsonResponse({ error: 'Session has ended — start a new run to interact' }, { status: 422 });\n }\n\n await sendKeysAsync(conv.tmuxSession, PI_CONVERSATION_ABORT_KEY, 'conversation-abort');\n return jsonResponse({ ok: true, key: PI_CONVERSATION_ABORT_KEY });\n}\n\nexport async function handleConversationDeliveryMethod(\n name: string,\n body: Record<string, unknown>,\n): Promise<ReturnType<typeof jsonResponse>> {\n const conv = getConversationByName(name);\n if (!conv) return jsonResponse({ error: 'Conversation not found' }, { status: 404 });\n const deliveryMethod = body['deliveryMethod'] ?? body['method'];\n if (deliveryMethod !== 'auto' && deliveryMethod !== 'channels' && deliveryMethod !== 'tmux' && deliveryMethod !== null) {\n return jsonResponse({ error: \"deliveryMethod must be 'auto', 'channels', 'tmux', or null\" }, { status: 400 });\n }\n updateConversationDeliveryMethod(name, deliveryMethod as 'auto' | 'channels' | 'tmux' | null);\n return jsonResponse({ ok: true, deliveryMethod });\n}\n\nexport const CODEX_APPROVAL_TOOL_PREFIX = 'codex-approval:';\n\nexport async function codexConversationPendingInput(\n conv: Conversation,\n sessionAlive: boolean,\n askedAt: string,\n): Promise<{ kinds: PendingInputKind[]; approval?: PendingAskUserQuestionSnapshot }> {\n if (!sessionAlive || getHarnessBehavior(conv.harness).transcriptKind !== 'codex-rollout-jsonl') return { kinds: [] };\n try {\n if (codexUsesAppServerTransport()) {\n const pending = await readFirstAppServerApproval(conv.tmuxSession);\n if (!pending) return { kinds: [] };\n return {\n kinds: ['permissionRequest'],\n approval: {\n toolUseId: `${CODEX_APPROVAL_TOOL_PREFIX}${conv.tmuxSession}:${pending.id}`,\n askedAt,\n questions: [{\n question: formatAppServerApprovalQuestion(pending),\n header: 'Codex approval',\n multiSelect: false,\n options: [\n { label: '1. accept' },\n { label: '2. reject' },\n ],\n }],\n },\n };\n }\n const detection = await Effect.runPromise(detectAwaitingInputForAgent(conv.tmuxSession, { isPlanning: false }));\n if (!detection) return { kinds: [] };\n if (detection.reason === 'session_resume') return { kinds: ['sessionResume'] };\n\n const parsed = parseCodexApprovalPrompt(detection.prompt);\n if (parsed) {\n return {\n kinds: ['permissionRequest'],\n approval: {\n toolUseId: `${CODEX_APPROVAL_TOOL_PREFIX}${conv.tmuxSession}`,\n askedAt,\n questions: [{\n question: parsed.detail ? `${parsed.header}\\n\\n${parsed.detail}` : parsed.header,\n header: 'Codex approval',\n multiSelect: false,\n options: parsed.options.map((o) => ({ label: `${o.number}. ${o.label}` })),\n }],\n },\n };\n }\n return { kinds: ['permissionRequest'] };\n } catch {\n return { kinds: [] };\n }\n}\n\nexport async function deliverCodexApprovalChoice(tmuxSession: string, optionNumber: number): Promise<void> {\n for (let i = 1; i < optionNumber; i += 1) {\n await Effect.runPromise(sendRawKeystroke(tmuxSession, 'Down', 'codex-approval'));\n await new Promise((r) => setTimeout(r, 60));\n }\n await Effect.runPromise(sendRawKeystroke(tmuxSession, 'Enter', 'codex-approval'));\n}\n\nfunction codexUsesAppServerTransport(): boolean {\n return loadConfigSync().config.codex?.transport !== 'tui';\n}\n\nfunction codexAppServerDecisionForOption(optionNumber: number): 'accept' | 'reject' | null {\n if (optionNumber === 1) return 'accept';\n if (optionNumber === 2) return 'reject';\n return null;\n}\n\nasync function readFirstAppServerApproval(tmuxSession: string): Promise<CodexAppServerPendingRequest | null> {\n const status = await postCodexAppServerOp<CodexAppServerStatus>(tmuxSession, { op: 'status' });\n return status.pendingRequests?.find(request => /requestApproval/i.test(request.method)) ?? null;\n}\n\nfunction formatAppServerApprovalQuestion(request: CodexAppServerPendingRequest): string {\n const params = asRecord(request.params);\n const command = typeof params.command === 'string' ? params.command : undefined;\n const path = typeof params.path === 'string' ? params.path : undefined;\n if (command) return `Codex requests approval to run:\\n\\n${command}`;\n if (path) return `Codex requests approval for:\\n\\n${path}`;\n return `Codex requests approval for ${request.method}`;\n}\n\nasync function postCodexAppServerOp<T = Record<string, unknown>>(tmuxSession: string, body: Record<string, unknown>): Promise<T> {\n const socketPath = join(getOverdeckHome(), 'sockets', `appserver-${tmuxSession}.sock`);\n const tokenPath = join(getOverdeckHome(), 'agents', tmuxSession, 'appserver-token');\n if (!existsSync(socketPath)) throw new Error(`app-server socket missing for ${tmuxSession}`);\n if (!existsSync(tokenPath)) throw new Error(`app-server token missing for ${tmuxSession}`);\n const token = readFileSync(tokenPath, 'utf-8').trim();\n if (!token) throw new Error(`app-server token missing for ${tmuxSession}`);\n const payload = JSON.stringify(body);\n return new Promise<T>((resolve, reject) => {\n let settled = false;\n const timeout = setTimeout(() => {\n if (settled) return;\n settled = true;\n req.destroy(new Error(`app-server op timed out after 2000ms`));\n reject(new Error(`app-server op timed out after 2000ms`));\n }, 2_000);\n const finishErr = (error: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n reject(error);\n };\n const finishOk = (value: T) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve(value);\n };\n const req = httpRequest(\n {\n socketPath,\n path: '/',\n method: 'POST',\n agent: false,\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(payload),\n [BRIDGE_TOKEN_HEADER]: token,\n },\n },\n (res) => {\n let responseBody = '';\n res.setEncoding('utf-8');\n res.on('data', chunk => { responseBody += chunk; });\n res.on('end', () => {\n const status = res.statusCode ?? 0;\n if (status < 200 || status >= 300) {\n finishErr(new Error(`app-server op returned HTTP ${status}`));\n return;\n }\n try {\n finishOk(JSON.parse(responseBody) as T);\n } catch (error) {\n finishErr(new Error(`app-server op returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`));\n }\n });\n },\n );\n req.on('error', finishErr);\n req.write(payload);\n req.end();\n });\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};\n}\n","import { Effect } from 'effect'\n\nimport {\n buildChoiceKeystrokes,\n paneChoiceMenuSignature,\n parsePaneChoiceMenu,\n type PaneChoiceConfidence,\n} from './pane-choice-menu.js'\nimport { capturePaneText, sendRawKeystroke, sessionExists } from './tmux.js'\n\nconst PANE_CAPTURE_LINES = 90\nconst KEYSTROKE_GAP_MS = 60\nconst DELIVERY_CONFIRM_WAIT_MS = 700\n\nexport interface PendingPaneChoiceOption {\n number: number\n label: string\n recommended: boolean\n}\n\nexport interface PendingPaneChoice {\n /** Stable identity of the parsed menu — answers must match what is on screen. */\n signature: string\n title: string\n contextLines: string[]\n options: PendingPaneChoiceOption[]\n selectedIndex: number\n footerHint: string | null\n confidence: PaneChoiceConfidence\n}\n\nexport interface SessionPaneChoiceDeps {\n capture?: (sessionName: string, lines: number) => Promise<string>\n sendKey?: (sessionName: string, key: string) => Promise<void>\n sleep?: (ms: number) => Promise<void>\n sessionExists?: (sessionName: string) => Promise<boolean>\n}\n\nexport interface SessionPaneChoiceAnswer {\n selectedIndex: number\n signature: string\n}\n\nexport interface SessionPaneChoiceResult {\n body: Record<string, unknown>\n status?: number\n}\n\nexport async function captureSessionPaneChoice(\n sessionName: string,\n capture: (sessionName: string, lines: number) => Promise<string> = capturePaneText,\n): Promise<PendingPaneChoice | null> {\n let pane: string\n try {\n pane = await capture(sessionName, PANE_CAPTURE_LINES)\n } catch {\n return null\n }\n const menu = parsePaneChoiceMenu(pane)\n if (!menu) return null\n return {\n signature: paneChoiceMenuSignature(menu),\n title: menu.title,\n contextLines: menu.contextLines,\n options: menu.options.map((option) => ({\n number: option.number,\n label: option.label,\n recommended: option.recommended,\n })),\n selectedIndex: menu.selectedIndex,\n footerHint: menu.footerHint,\n confidence: menu.confidence,\n }\n}\n\nexport async function answerSessionPaneChoice(\n sessionName: string,\n body: Record<string, unknown> | SessionPaneChoiceAnswer,\n deps: SessionPaneChoiceDeps = {},\n): Promise<SessionPaneChoiceResult> {\n try {\n const selectedIndex = Number(body.selectedIndex)\n const signature = typeof body.signature === 'string' ? body.signature : ''\n if (!Number.isInteger(selectedIndex) || selectedIndex < 0 || selectedIndex > 8) {\n return { body: { error: 'selectedIndex must be an integer 0-8' }, status: 400 }\n }\n if (!signature) {\n return { body: { error: 'signature is required' }, status: 400 }\n }\n\n const isSessionRunning = deps.sessionExists\n ?? ((name: string) => Effect.runPromise(sessionExists(name)))\n if (!(await isSessionRunning(sessionName))) {\n return { body: { error: 'Conversation session is not running' }, status: 409 }\n }\n\n const capture = deps.capture ?? capturePaneText\n const sendKey = deps.sendKey\n ?? ((name: string, key: string) => Effect.runPromise(sendRawKeystroke(name, key, 'pane-choice')))\n const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)))\n\n const pane = await capture(sessionName, PANE_CAPTURE_LINES)\n const menu = parsePaneChoiceMenu(pane)\n if (!menu) {\n return { body: { error: 'The choice menu is no longer on screen', code: 'menu-gone' }, status: 409 }\n }\n if (paneChoiceMenuSignature(menu) !== signature) {\n return { body: { error: 'The choice menu changed since the card was rendered — refresh and re-answer', code: 'menu-changed' }, status: 409 }\n }\n if (selectedIndex >= menu.options.length) {\n return { body: { error: `selectedIndex out of range (0-${menu.options.length - 1})` }, status: 400 }\n }\n\n const keys = buildChoiceKeystrokes(menu, selectedIndex)\n for (const key of keys) {\n await sendKey(sessionName, key)\n await sleep(KEYSTROKE_GAP_MS)\n }\n\n await sleep(DELIVERY_CONFIRM_WAIT_MS)\n const after = parsePaneChoiceMenu(await capture(sessionName, PANE_CAPTURE_LINES))\n if (after && paneChoiceMenuSignature(after) === signature) {\n return { body: { error: 'Keystrokes were sent but the menu is still on screen — answer it from the terminal', code: 'delivery-unconfirmed' }, status: 409 }\n }\n return { body: { ok: true, answeredLabel: menu.options[selectedIndex]!.label } }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error)\n console.error('[sessions] pane-choice answer failed:', message)\n return { body: { error: 'Internal server error' }, status: 500 }\n }\n}\n","import { Data, Effect } from 'effect';\n\nexport const CHANNEL_PERMISSION_LIMITS = {\n requestIdBytes: 128,\n toolNameBytes: 128,\n descriptionBytes: 2 * 1024,\n inputPreviewBytes: 16 * 1024,\n} as const;\n\nexport interface NormalizedChannelPermissionRequestFields {\n requestId: string;\n toolName: string;\n description: string;\n inputPreview: string;\n}\n\n/** A permission request field failed validation. */\nexport class PermissionPayloadValidationError extends Data.TaggedError(\n 'PermissionPayloadValidationError',\n)<{\n readonly field: string;\n readonly message: string;\n}> {}\n\nexport function utf8ByteLength(value: string): number {\n return Buffer.byteLength(value, 'utf8');\n}\n\nexport function normalizePermissionInputPreview(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nexport function normalizeChannelPermissionRequestFields(input: {\n requestId: unknown;\n toolName: unknown;\n description: unknown;\n inputPreview?: unknown;\n}): Effect.Effect<NormalizedChannelPermissionRequestFields, PermissionPayloadValidationError> {\n return Effect.gen(function* () {\n const requestId = typeof input.requestId === 'string' ? input.requestId.trim() : '';\n if (!requestId) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({ field: 'requestId', message: 'requestId is required' }),\n );\n }\n if (utf8ByteLength(requestId) > CHANNEL_PERMISSION_LIMITS.requestIdBytes) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'requestId',\n message: `requestId exceeds ${CHANNEL_PERMISSION_LIMITS.requestIdBytes} bytes`,\n }),\n );\n }\n\n const toolName = typeof input.toolName === 'string' ? input.toolName.trim() : '';\n if (!toolName) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({ field: 'toolName', message: 'toolName is required' }),\n );\n }\n if (utf8ByteLength(toolName) > CHANNEL_PERMISSION_LIMITS.toolNameBytes) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'toolName',\n message: `toolName exceeds ${CHANNEL_PERMISSION_LIMITS.toolNameBytes} bytes`,\n }),\n );\n }\n\n const description = typeof input.description === 'string' ? input.description.trim() : '';\n if (!description) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'description',\n message: 'description is required',\n }),\n );\n }\n if (utf8ByteLength(description) > CHANNEL_PERMISSION_LIMITS.descriptionBytes) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'description',\n message: `description exceeds ${CHANNEL_PERMISSION_LIMITS.descriptionBytes} bytes`,\n }),\n );\n }\n\n const inputPreview = normalizePermissionInputPreview(input.inputPreview);\n if (utf8ByteLength(inputPreview) > CHANNEL_PERMISSION_LIMITS.inputPreviewBytes) {\n return yield* Effect.fail(\n new PermissionPayloadValidationError({\n field: 'inputPreview',\n message: `inputPreview exceeds ${CHANNEL_PERMISSION_LIMITS.inputPreviewBytes} bytes`,\n }),\n );\n }\n\n return { requestId, toolName, description, inputPreview };\n });\n}\n","import type { ChannelPermissionRequestSnapshot, ResolvedChannelPermissionDecision as ContractsResolvedChannelPermissionDecision } from '@overdeck/contracts';\nimport { Effect } from 'effect';\nimport * as Result from 'effect/Result';\n\nimport {\n normalizeChannelPermissionRequestFields,\n type NormalizedChannelPermissionRequestFields,\n} from '../../../lib/channels/permission-payload.js';\n\nconst MAX_ACTIVITY_PREVIEW_CHARS = 2048;\n\nexport type ResolvedChannelPermissionDecision = ContractsResolvedChannelPermissionDecision;\n\nexport function buildPermissionWaitingMessage(toolName: string, description: string): string {\n return `Waiting for permission: ${toolName} — ${description}`;\n}\n\nexport function permissionResolutionVerb(behavior: 'allow' | 'deny'): 'allowed' | 'denied' {\n return behavior === 'allow' ? 'allowed' : 'denied';\n}\n\nexport function normalizePermissionRequestBody(body: Record<string, unknown>):\n | { ok: true; value: NormalizedChannelPermissionRequestFields }\n | { ok: false; error: string } {\n const result = Effect.runSync(\n Effect.result(\n normalizeChannelPermissionRequestFields({\n requestId: body['requestId'],\n toolName: body['toolName'],\n description: body['description'],\n inputPreview: body['inputPreview'],\n }),\n ),\n );\n if (Result.isFailure(result)) {\n return { ok: false, error: result.failure.message };\n }\n return { ok: true, value: result.success };\n}\n\nexport function parsePermissionResponseBehavior(body: Record<string, unknown>):\n | { ok: true; value: 'allow' | 'deny' }\n | { ok: false; error: string } {\n const behavior = body['behavior'];\n if (behavior !== 'allow' && behavior !== 'deny') {\n return { ok: false, error: 'behavior must be allow or deny' };\n }\n return { ok: true, value: behavior };\n}\n\nexport function redactPermissionInputPreview(inputPreview: string): string {\n let redacted = inputPreview;\n redacted = redacted.replace(\n /(((?:[\"']?(?:api[_-]?key|token|secret|password|passwd)[\"']?)\\s*[:=]\\s*[\"']?))([^\\s,'\"`]+)([\"']?)/gi,\n (_match, prefix: string, _quotedPrefix: string, _value: string, suffix: string) => `${prefix}[REDACTED]${suffix}`,\n );\n redacted = redacted.replace(\n /(Bearer\\s+)([A-Za-z0-9._~+/=-]+)/g,\n (_match, prefix: string) => `${prefix}[REDACTED]`,\n );\n redacted = redacted.replace(\n /(?:ghp|gho|ghu|ghs|github_pat)_[A-Za-z0-9_]+/g,\n '[REDACTED]',\n );\n redacted = redacted.replace(\n /(?:sk|rk)-[A-Za-z0-9]+/g,\n '[REDACTED]',\n );\n if (redacted.length <= MAX_ACTIVITY_PREVIEW_CHARS) {\n return redacted;\n }\n return `${redacted.slice(0, MAX_ACTIVITY_PREVIEW_CHARS)}…[truncated]`;\n}\n\nexport function buildPermissionActivityDetails(description: string, inputPreview: string): string {\n const safePreview = redactPermissionInputPreview(inputPreview);\n return safePreview.trim().length > 0\n ? `${description}\\n\\nInput preview:\\n${safePreview}`\n : description;\n}\n\nexport interface ProcessPermissionResponseDeps {\n getPendingRequest: (requestId: string) => Promise<ChannelPermissionRequestSnapshot | null>;\n getResolvedDecision: (requestId: string) => Promise<ResolvedChannelPermissionDecision | null>;\n appendResolutionEvents: (\n request: ChannelPermissionRequestSnapshot,\n behavior: 'allow' | 'deny',\n ) => Promise<void>;\n deliverDecision: (\n agentId: string,\n requestId: string,\n behavior: 'allow' | 'deny',\n ) => Promise<void>;\n emitResolvedActivity: (request: ChannelPermissionRequestSnapshot, behavior: 'allow' | 'deny') => void;\n}\n\nexport interface ProcessPermissionResponseResult {\n status: number;\n body: { ok: boolean; error?: string; duplicate?: boolean };\n}\n\nexport async function processPermissionResponse(\n deps: ProcessPermissionResponseDeps,\n args: {\n agentId: string;\n requestId: string;\n behavior: 'allow' | 'deny';\n },\n): Promise<ProcessPermissionResponseResult> {\n const pendingRequest = await deps.getPendingRequest(args.requestId);\n if (pendingRequest) {\n if (pendingRequest.agentId !== args.agentId) {\n return {\n status: 409,\n body: {\n ok: false,\n error: `permission request ${args.requestId} belongs to ${pendingRequest.agentId}`,\n },\n };\n }\n\n try {\n await deps.appendResolutionEvents(pendingRequest, args.behavior);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n status: 500,\n body: { ok: false, error: `failed to persist permission decision: ${msg}` },\n };\n }\n\n try {\n await deps.deliverDecision(args.agentId, args.requestId, args.behavior);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n status: 502,\n body: { ok: false, error: `failed to deliver permission decision: ${msg}` },\n };\n }\n\n deps.emitResolvedActivity(pendingRequest, args.behavior);\n return { status: 200, body: { ok: true } };\n }\n\n const resolvedDecision = await deps.getResolvedDecision(args.requestId);\n if (!resolvedDecision) {\n return {\n status: 404,\n body: { ok: false, error: `permission request ${args.requestId} not found` },\n };\n }\n if (resolvedDecision.agentId !== args.agentId) {\n return {\n status: 409,\n body: {\n ok: false,\n error: `permission request ${args.requestId} belongs to ${resolvedDecision.agentId}`,\n },\n };\n }\n if (resolvedDecision.behavior !== args.behavior) {\n return {\n status: 409,\n body: {\n ok: false,\n error: `permission request ${args.requestId} was already ${resolvedDecision.behavior}`,\n },\n };\n }\n\n try {\n await deps.deliverDecision(args.agentId, args.requestId, args.behavior);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n status: 502,\n body: { ok: false, error: `failed to deliver permission decision: ${msg}` },\n };\n }\n\n return { status: 200, body: { ok: true, duplicate: true } };\n}\n","import { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport {\n deliverAgentMessage,\n deliverAgentPermissionDecision,\n getAgentRuntimeState,\n getAgentState,\n} from '../../../../lib/agents.js';\nimport { getAgentJsonlPath, scanPendingInputsPromise } from '../../../../lib/agent-enrichment.js';\nimport { deliverPlanActionToSession } from '../../../../lib/overdeck/conversation-delivery.js';\nimport {\n answerSessionPaneChoice,\n captureSessionPaneChoice,\n type PendingPaneChoice,\n type SessionPaneChoiceResult,\n} from '../../../../lib/session-pane-choice.js';\nimport { emitActivityEntrySync } from '../../../../lib/activity-logger.js';\nimport { ReadModelService } from '../../read-model.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { validateOrigin } from '../origin-validation.js';\nimport {\n buildPermissionActivityDetails,\n buildPermissionWaitingMessage,\n normalizePermissionRequestBody,\n parsePermissionResponseBehavior,\n permissionResolutionVerb,\n processPermissionResponse,\n} from '../agent-permissions.js';\nimport {\n constantTimeTokenEqual,\n getAgentPendingQuestions,\n readJsonBody,\n} from './shared.js';\n\ninterface AgentPaneChoiceDeps {\n getAgentState?: (agentId: string) => Promise<{ issueId: string } | null>;\n captureSessionPaneChoice?: (sessionName: string) => Promise<PendingPaneChoice | null>;\n answerSessionPaneChoice?: (\n sessionName: string,\n body: Record<string, unknown>,\n ) => Promise<SessionPaneChoiceResult>;\n emitActivityEntry?: typeof emitActivityEntrySync;\n}\n\nexport async function handleGetAgentPaneChoice(\n agentId: string,\n deps: AgentPaneChoiceDeps = {},\n): Promise<SessionPaneChoiceResult> {\n if (!agentId.trim()) return { body: { error: 'missing agent id' }, status: 400 };\n const capture = deps.captureSessionPaneChoice ?? captureSessionPaneChoice;\n const paneChoice = await capture(agentId);\n return paneChoice\n ? { body: { pending: true, ...paneChoice } }\n : { body: { pending: false } };\n}\n\nexport async function handlePostAgentPaneChoice(\n agentId: string,\n body: Record<string, unknown>,\n deps: AgentPaneChoiceDeps = {},\n): Promise<SessionPaneChoiceResult> {\n if (!agentId.trim()) return { body: { error: 'missing agent id' }, status: 400 };\n const readAgentState = deps.getAgentState\n ?? ((id: string) => Effect.runPromise(getAgentState(id)));\n const agentState = await readAgentState(agentId);\n if (!agentState) return { body: { error: `Agent ${agentId} not found` }, status: 404 };\n\n const answer = deps.answerSessionPaneChoice ?? answerSessionPaneChoice;\n const result = await answer(agentId, body);\n const answeredLabel = result.body['answeredLabel'];\n if (result.status === undefined && result.body['ok'] === true && typeof answeredLabel === 'string') {\n (deps.emitActivityEntry ?? emitActivityEntrySync)({\n source: 'dashboard',\n level: 'info',\n issueId: agentState.issueId,\n message: `Operator answered ${agentId}'s pane menu: ${answeredLabel}`,\n });\n }\n return result;\n}\n\nexport const getAgentPaneChoiceRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/pane-choice',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const result = yield* Effect.promise(() => handleGetAgentPaneChoice(params['id'] ?? ''));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\nexport const postAgentPaneChoiceRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/pane-choice',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ error: originCheck.error }, { status: 403 });\n }\n const params = yield* HttpRouter.params;\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const result = yield* Effect.promise(() => handlePostAgentPaneChoice(params['id'] ?? '', body));\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/pending-questions ────────────────────────────\n\nexport const getAgentPendingQuestionsRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/pending-questions',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const questions = yield* getAgentPendingQuestions(id);\n return jsonResponse({ pending: questions.length > 0, questions });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/answer-question (PAN-1520) ──────────────────\n//\n// Operator answer for an AskUserQuestion the agent is blocked on. The Phase 1\n// hook (sync-sources/hooks/ask-user-question-hook) denies the upstream tool\n// call to prevent silent corruption (upstream returns option #1 under\n// --dangerously-skip-permissions), so by the time this endpoint is hit the\n// agent has restated the question as plain text and is waiting on a normal\n// user message. We compose that user message from the chosen option labels\n// and deliver it through the standard message pipeline.\n//\n// Body: { answers: string[] } — one chosen-option label per question.\n\nexport const postAgentAnswerQuestionRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/answer-question',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ error: 'missing agent id' }, { status: 400 });\n }\n const body = (yield* readJsonBody) as Record<string, unknown>;\n\n const answers = body['answers'];\n if (!Array.isArray(answers) || answers.length === 0) {\n return jsonResponse({ error: 'answers array required' }, { status: 400 });\n }\n if (!answers.every((a): a is string => typeof a === 'string' && a.length > 0)) {\n return jsonResponse({ error: 'every answer must be a non-empty string' }, { status: 400 });\n }\n\n const pendingQuestions = yield* getAgentPendingQuestions(id);\n if (pendingQuestions.length === 0) {\n return jsonResponse({ error: 'No pending questions found for this agent' }, { status: 404 });\n }\n\n const questionSet = pendingQuestions[0];\n const questions = questionSet.questions;\n const lines: string[] = [];\n for (let i = 0; i < answers.length && i < questions.length; i++) {\n const q = questions[i].question ?? `Question ${i + 1}`;\n lines.push(`Q: ${q}\\nA: ${answers[i]}`);\n }\n const message = `Operator answered the pending question${answers.length > 1 ? 's' : ''}:\\n\\n${lines.join('\\n\\n')}`;\n\n yield* Effect.promise(() => deliverAgentMessage(id, message, 'ask-user-question-answer'));\n return jsonResponse({ success: true, agentId: id, delivered: answers.length });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/plan-action (PAN-1520 FR-4) ─────────────────\n//\n// Agent-scoped mirror of POST /api/conversations/:name/plan-action. The native\n// ExitPlanMode approval menu is answered with raw keystrokes (1/2/3/4), not a\n// text message, so the conversation delivery door can't serve it — and the\n// existing route is conversation-name-scoped. Guarded on the agent actually\n// having a pending ExitPlanMode so a stale dashboard click can never inject\n// keystrokes into a session that has already moved on.\n//\n// Body: { action: 'approve-auto' | 'approve-manual' | 'reject-ultraplan' | 'reject-feedback', feedback?: string }\n\nexport const postAgentPlanActionRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/plan-action',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ error: 'missing agent id' }, { status: 400 });\n }\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const action = typeof body['action'] === 'string' ? body['action'] : '';\n const feedback = typeof body['feedback'] === 'string' ? body['feedback'].trim() : '';\n\n const agentState = yield* getAgentState(id).pipe(Effect.catch(() => Effect.succeed(null)));\n if (!agentState) {\n return jsonResponse({ error: 'Agent not found' }, { status: 404 });\n }\n\n const jsonlPath = yield* getAgentJsonlPath(id).pipe(Effect.catch(() => Effect.succeed(null)));\n const scan = jsonlPath\n ? yield* Effect.promise(() => scanPendingInputsPromise(jsonlPath))\n : null;\n if (!scan?.exitPlanModePending) {\n return jsonResponse({ error: 'No pending plan approval for this agent' }, { status: 409 });\n }\n\n const error = yield* Effect.promise(() => deliverPlanActionToSession(id, action, feedback));\n if (error) {\n return jsonResponse({ error }, { status: 400 });\n }\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'info',\n issueId: agentState.issueId,\n message: `Operator ${action.startsWith('approve') ? 'approved' : 'requested changes to'} ${id}'s plan from the dashboard`,\n });\n return jsonResponse({ success: true, agentId: id, action });\n })),\n);\n\nexport const postInternalAgentPermissionRequestRoute = HttpRouter.add(\n 'POST',\n '/api/internal/agents/:id/permissions/request',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ ok: false, error: 'missing agent id' }, { status: 400 });\n }\n\n const request = yield* HttpServerRequest.HttpServerRequest;\n const { INTERNAL_TOKEN_HEADER, getInternalTokenSync } = yield* Effect.promise(() =>\n import('../../../../lib/internal-token.js'),\n );\n const expected = getInternalTokenSync();\n if (!expected) {\n return jsonResponse({ ok: false, error: 'internal token not configured' }, { status: 503 });\n }\n const headers = request.headers as Record<string, string | string[] | undefined>;\n const rawHeader = headers[INTERNAL_TOKEN_HEADER];\n const provided = Array.isArray(rawHeader) ? rawHeader[0] : rawHeader;\n if (!constantTimeTokenEqual(provided, expected)) {\n return jsonResponse({ ok: false, error: 'forbidden' }, { status: 403 });\n }\n\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const normalized = normalizePermissionRequestBody(body);\n if (!normalized.ok) {\n return jsonResponse({ ok: false, error: normalized.error }, { status: 400 });\n }\n const { requestId, toolName, description, inputPreview } = normalized.value;\n\n const readModel = yield* ReadModelService;\n const existing = yield* readModel.getChannelPermissionRequest(requestId);\n if (existing) {\n if (existing.agentId !== id) {\n return jsonResponse({ ok: false, error: `request ${requestId} already belongs to ${existing.agentId}` }, { status: 409 });\n }\n return jsonResponse({ ok: true, duplicate: true });\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ ok: false, error: `agent ${id} not found` }, { status: 404 });\n }\n const runtimeState = yield* getAgentRuntimeState(id);\n const issueId = runtimeState?.currentIssue ?? agentState.issueId;\n\n const eventStore = yield* EventStoreService;\n const timestamp = new Date().toISOString();\n yield* eventStore.append({\n type: 'agent.permission_requested',\n timestamp,\n payload: {\n requestId,\n agentId: id,\n issueId,\n toolName,\n description,\n inputPreview,\n createdAt: timestamp,\n },\n } as never);\n yield* eventStore.append({\n type: 'agent.waiting_started',\n timestamp,\n payload: {\n agentId: id,\n reason: 'tool_permission',\n message: buildPermissionWaitingMessage(toolName, description),\n },\n } as never);\n\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'warn',\n message: `Permission requested for ${toolName}`,\n details: buildPermissionActivityDetails(description, inputPreview),\n issueId,\n });\n\n return jsonResponse({ ok: true });\n })),\n);\n\nexport const postAgentPermissionResponseRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/permissions/:requestId/respond',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const requestId = params['requestId'] ?? '';\n if (!id.trim() || !requestId.trim()) {\n return jsonResponse({ ok: false, error: 'missing agent id or request id' }, { status: 400 });\n }\n\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const behaviorResult = parsePermissionResponseBehavior(body);\n if (!behaviorResult.ok) {\n return jsonResponse({ ok: false, error: behaviorResult.error }, { status: 400 });\n }\n const behavior = behaviorResult.value;\n\n const readModel = yield* ReadModelService;\n const eventStore = yield* EventStoreService;\n const result = yield* Effect.promise(() => processPermissionResponse(\n {\n getPendingRequest: (permissionRequestId) =>\n Effect.runPromise(readModel.getChannelPermissionRequest(permissionRequestId)),\n getResolvedDecision: (permissionRequestId) =>\n Effect.runPromise(readModel.getResolvedChannelPermissionDecision(permissionRequestId)),\n appendResolutionEvents: async (pendingRequest, decisionBehavior) => {\n const timestamp = new Date().toISOString();\n await Effect.runPromise(eventStore.append({\n type: 'agent.permission_resolved',\n timestamp,\n payload: {\n requestId: pendingRequest.requestId,\n agentId: pendingRequest.agentId,\n issueId: pendingRequest.issueId,\n behavior: decisionBehavior,\n },\n } as never));\n await Effect.runPromise(eventStore.append({\n type: 'agent.waiting_cleared',\n timestamp,\n payload: {\n agentId: pendingRequest.agentId,\n clearedBy: 'tool_resumed',\n },\n } as never));\n },\n deliverDecision: (agentId, permissionRequestId, decisionBehavior) =>\n deliverAgentPermissionDecision(agentId, permissionRequestId, decisionBehavior),\n emitResolvedActivity: (pendingRequest, decisionBehavior) => {\n emitActivityEntrySync({\n source: 'dashboard',\n level: decisionBehavior === 'allow' ? 'success' : 'warn',\n message: `Permission ${permissionResolutionVerb(decisionBehavior)} for ${pendingRequest.toolName}`,\n details: buildPermissionActivityDetails(\n pendingRequest.description,\n pendingRequest.inputPreview,\n ),\n issueId: pendingRequest.issueId,\n });\n },\n },\n {\n agentId: id,\n requestId,\n behavior,\n },\n ));\n\n return jsonResponse(result.body, { status: result.status });\n })),\n);\n","/**\n * Agent event utilities — shared between HTTP route handlers and background\n * pollers (PAN-1134). Extracted from routes/agents.ts so Pi event polling\n * can reuse bodyToEvent without importing a heavy route module.\n */\n\nimport { Schema } from 'effect';\nimport {\n DomainEvent,\n normalizeChannelReplyPayload,\n} from '@overdeck/contracts';\n\n/**\n * Translate a decoded body into an unsigned DomainEvent (no sequence yet).\n * Returns null if the body doesn't map to a runtime event.\n */\nexport const bodyToEvent = (\n agentId: string,\n body: Record<string, unknown>,\n timestamp: string,\n): Record<string, unknown> | null => {\n const source = body;\n if (typeof source['kind'] !== 'string') return null;\n const kind = source['kind'] as string;\n switch (kind) {\n case 'activity':\n return {\n type: 'agent.activity_changed',\n timestamp,\n payload: {\n agentId,\n activity: source['activity'],\n currentTool: source['tool'] as string | undefined,\n hookName: source['hookName'] as string | undefined,\n },\n };\n case 'hook_fired':\n return {\n type: 'agent.hook_fired',\n timestamp,\n payload: {\n agentId,\n hookName: source['hookName'],\n tool: source['tool'] as string | undefined,\n },\n };\n case 'thinking_start':\n return {\n type: 'agent.thinking_started',\n timestamp,\n payload: {\n agentId,\n lastToolAt: (source['lastToolAt'] as string) ?? timestamp,\n },\n };\n case 'thinking_stop':\n return {\n type: 'agent.thinking_stopped',\n timestamp,\n payload: {\n agentId,\n resolvedBy: source['resolvedBy'] ?? 'tool',\n },\n };\n case 'waiting_start':\n return {\n type: 'agent.waiting_started',\n timestamp,\n payload: {\n agentId,\n reason: source['reason'] ?? 'other',\n message: source['message'] as string | undefined,\n },\n };\n case 'waiting_clear':\n return {\n type: 'agent.waiting_cleared',\n timestamp,\n payload: {\n agentId,\n clearedBy: source['clearedBy'] ?? 'user_response',\n },\n };\n case 'message_received':\n return {\n type: 'agent.message_received',\n timestamp,\n payload: {\n agentId,\n direction: source['direction'] ?? 'to_agent',\n source: source['source'] ?? 'user',\n },\n };\n case 'channel_reply': {\n const reply = normalizeChannelReplyPayload(source['reply'], 'reply');\n return {\n type: 'agent.channel_reply',\n timestamp,\n payload: {\n agentId,\n reply: {\n ...reply,\n reportedAt: timestamp,\n },\n },\n };\n }\n case 'model_set':\n return {\n type: 'agent.model_set',\n timestamp,\n payload: {\n agentId,\n model: source['model'],\n claudeSessionId: source['claudeSessionId'] as string | undefined,\n sessionModel: source['sessionModel'] as string | undefined,\n sessionHarness: source['sessionHarness'] as string | undefined,\n },\n };\n case 'cost-event': {\n const usage = source['usage'] && typeof source['usage'] === 'object'\n ? source['usage'] as Record<string, unknown>\n : {};\n return {\n type: 'cost.event_recorded',\n timestamp,\n payload: {\n agentId,\n issueId: typeof source['issueId'] === 'string' && source['issueId'] ? source['issueId'] : 'UNKNOWN-0',\n cost: typeof source['costUsd'] === 'number' ? source['costUsd'] : 0,\n inputTokens: typeof usage['inputTokens'] === 'number' ? usage['inputTokens'] : 0,\n outputTokens: typeof usage['outputTokens'] === 'number' ? usage['outputTokens'] : 0,\n },\n };\n }\n case 'resolution_set':\n return {\n type: 'agent.resolution_changed',\n timestamp,\n payload: {\n agentId,\n resolution: source['resolution'],\n resolutionCount: Number(source['resolutionCount'] ?? 1),\n },\n };\n case 'current_issue_set':\n return {\n type: 'agent.current_issue_set',\n timestamp,\n payload: {\n agentId,\n currentIssue: source['currentIssue'] as string | undefined,\n },\n };\n case 'context_saturation_changed':\n return {\n type: 'agent.context_saturation_changed',\n timestamp,\n payload: {\n agentId,\n contextSaturatedAt: source['contextSaturatedAt'] as string | undefined,\n },\n };\n case 'linear_mcp_auth_required':\n return {\n type: 'linear_mcp_auth.required',\n timestamp,\n payload: {\n agentId,\n issueId: source['issueId'] ?? null,\n authUrl: source['authUrl'] ?? null,\n expiresAt: source['expiresAt'] ?? null,\n },\n };\n case 'linear_mcp_auth_healthy':\n return {\n type: 'linear_mcp_auth.healthy',\n timestamp,\n payload: {\n agentId,\n issueId: source['issueId'] ?? null,\n source: 'hook',\n },\n };\n default:\n return null;\n }\n};\n\n// Runtime-event decoder. We validate the assembled event (with a placeholder\n// sequence) against the DomainEvent union — bad payloads are rejected rather\n// than silently corrupting the AgentRuntimeSnapshot.\nexport const decodeDomainEvent = Schema.decodeUnknownResult(DomainEvent);\n","import { Effect, Option } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { bodyToEvent, decodeDomainEvent } from '../../services/agent-event-utils.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport {\n readJsonBody,\n validateAgentRuntimeEventAuth,\n} from './shared.js';\n\n// ─── Route: GET /api/agents/:id/health-history ───────────────────────────────\n\nexport const getAgentHealthHistoryRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/health-history',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n const hours = Option.isSome(urlOpt) ? (urlOpt.value.searchParams.get('hours') ?? '24') : '24';\n\n const { getHealthHistory } = yield* Effect.promise(() => import('../../../../lib/overdeck/health-events.js'));\n const endTime = new Date();\n const startTime = new Date(endTime.getTime() - parseInt(hours) * 60 * 60 * 1000);\n const events = getHealthHistory(id, startTime.toISOString(), endTime.toISOString());\n return jsonResponse({\n agentId: id,\n startTime: startTime.toISOString(),\n endTime: endTime.toISOString(),\n events,\n });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/heartbeat (PAN-800 ingestion) ──────────────\n//\n// Typed event ingestion for agent runtime state. Hooks POST a Schema-validated\n// body describing a single runtime transition; the handler translates to an\n// agent.* DomainEvent and hands it to AgentStateService.emit (which durably\n// appendAsyncs via EventStore).\n//\n// Body shape (discriminated by `kind`):\n// {kind: \"activity\", activity, tool?}\n// {kind: \"hook_fired\", hookName, tool?}\n// {kind: \"thinking_start\", lastToolAt}\n// {kind: \"thinking_stop\", resolvedBy}\n// {kind: \"waiting_start\", reason, message?}\n// {kind: \"waiting_clear\", clearedBy}\n// {kind: \"message_received\", direction, source}\n// {kind: \"model_set\", model, claudeSessionId?}\n// {kind: \"resolution_set\", resolution, resolutionCount}\n// {kind: \"current_issue_set\", currentIssue?}\n// {kind: \"context_saturation_changed\", contextSaturatedAt?}\n// {kind: \"linear_mcp_auth_required\", authUrl?, expiresAt?}\n// {kind: \"linear_mcp_auth_healthy\"}\n\nexport function emitAgentRuntimeEvent(id: string, body: Record<string, unknown>, timestamp: string) {\n return Effect.gen(function* () {\n let eventBody = body;\n if (body['kind'] === 'linear_mcp_auth_required' || body['kind'] === 'linear_mcp_auth_healthy') {\n const { AgentStateService } = yield* Effect.promise(\n () => import('../../services/agent-state-service.js'),\n );\n const agentState = yield* AgentStateService;\n const snapshot = yield* agentState.get(id);\n eventBody = { ...body, issueId: snapshot?.currentIssue ?? null };\n }\n\n let raw: Record<string, unknown> | null;\n try {\n raw = bodyToEvent(id, eventBody, timestamp);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'invalid heartbeat payload';\n return { ok: false as const, response: jsonResponse({ success: false, error: message }, { status: 400 }) };\n }\n if (!raw) {\n return { ok: true as const, emitted: false };\n }\n\n const candidate = { ...raw, sequence: 0 };\n const decoded = decodeDomainEvent(candidate);\n if (decoded._tag === 'Failure') {\n return {\n ok: false as const,\n response: jsonResponse(\n { success: false, error: 'invalid event', detail: String(decoded.failure) },\n { status: 400 },\n ),\n };\n }\n\n const { AgentStateService } = yield* Effect.promise(\n () => import('../../services/agent-state-service.js'),\n );\n const agentState = yield* AgentStateService;\n yield* agentState.emit(decoded.success as never);\n return { ok: true as const, emitted: true };\n });\n}\n\nexport const postAgentHeartbeatRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/heartbeat',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ success: false, error: 'missing agent id' }, { status: 400 });\n }\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const timestamp = (body['timestamp'] as string) ?? new Date().toISOString();\n const result = yield* emitAgentRuntimeEvent(id, body, timestamp);\n if (!result.ok) return result.response;\n return jsonResponse({ success: true, emitted: result.emitted });\n })),\n);\n\nexport const postAgentWorkCompleteRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/work-complete',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) return jsonResponse({ success: false, error: 'missing agent id' }, { status: 400 });\n const result = yield* emitAgentRuntimeEvent(id, { kind: 'resolution_set', resolution: 'done', resolutionCount: 1 }, new Date().toISOString());\n if (!result.ok) return result.response;\n return jsonResponse({ success: true, emitted: result.emitted });\n })),\n);\n\nexport const postAgentStuckRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/stuck',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) return jsonResponse({ success: false, error: 'missing agent id' }, { status: 400 });\n const result = yield* emitAgentRuntimeEvent(id, { kind: 'resolution_set', resolution: 'stuck', resolutionCount: 1 }, new Date().toISOString());\n if (!result.ok) return result.response;\n return jsonResponse({ success: true, emitted: result.emitted });\n })),\n);\n\nfunction hasNegatedCompletionOutput(output: string): boolean {\n return /\\b(not|never|no|cannot|can't|blocked|waiting|needs input|not yet|isn't|aren't)\\b.{0,48}\\b(implementation complete|all tasks closed|ready for review|work complete)\\b/i.test(output);\n}\n\nexport const postAgentClassifyCompletionRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/classify-completion',\n httpHandler(Effect.gen(function* () {\n const body = (yield* readJsonBody) as Record<string, unknown>;\n const output = typeof body['output'] === 'string' ? body['output'] : '';\n const verdict = /blocked|needs input|waiting for|not ready for review/i.test(output) || hasNegatedCompletionOutput(output)\n ? 'STOPPED_FOR_INPUT'\n : /Implementation complete|all tasks closed|ready for review|work complete/i.test(output)\n ? 'FORGOT_COMPLETION'\n : 'UNCLEAR';\n return jsonResponse({ success: true, verdict });\n })),\n);\n\nexport const postAgentPlanChecklistRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/plan-checklist',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const { getAgentState } = yield* Effect.promise(() => import('../../../../lib/agents.js'));\n const state = yield* getAgentState(id);\n if (!state?.workspace || !state.issueId) {\n return jsonResponse({ success: false, error: 'agent has no resolvable workspace or issue id' }, { status: 422 });\n }\n\n const { checkIncompletePlanItemsPromise } = yield* Effect.promise(\n () => import('../../../../lib/work/done-preflight.js'),\n );\n const incomplete = yield* Effect.promise(\n () => checkIncompletePlanItemsPromise(state.workspace, state.issueId),\n );\n return jsonResponse({ success: true, complete: incomplete.length === 0, incomplete });\n })),\n);\n\n// ─── Route: GET /api/agents/:id/runtime (PAN-800) ────────────────────────────\n// Exposes AgentRuntimeSnapshot to out-of-process readers (CLI, tests).\n\nexport const getAgentRuntimeRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/runtime',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n if (!id.trim()) {\n return jsonResponse({ success: false, error: 'missing agent id' }, { status: 400 });\n }\n const { AgentStateService } = yield* Effect.promise(\n () => import('../../services/agent-state-service.js'),\n );\n const agentState = yield* AgentStateService;\n const snapshot = yield* agentState.get(id);\n if (!snapshot) {\n return jsonResponse({ success: false, error: 'not found' }, { status: 404 });\n }\n return jsonResponse({ success: true, snapshot });\n })),\n);\n","/**\n * Agent lifecycle projection — write-through transactional boundary (PAN-1908)\n *\n * Every agent lifecycle transition writes the rollback source (state.json)\n * and then commits the authoritative row upsert + event append inside one\n * SQLite transaction. This replaces save-then-append, which a crash could\n * leave out of sync.\n *\n * The transaction runs on the shared overdeck.db connection (PAN-1938). After\n * commit, the persisted event is emitted to the event store's subscribers so\n * the in-memory read model stays current.\n */\n\nimport { Effect } from 'effect';\nimport type { SqliteDatabase } from '../../../lib/database/driver.js';\nimport { getOverdeckDatabaseSync } from '../../../lib/overdeck/infra.js';\nimport { stateToOverdeckParamsForDb, AGENT_COLUMNS_FOR_DB } from '../../../lib/overdeck/agent-state-sync.js';\nimport { getEventStore, type EventStore, type StoredEvent } from '../event-store.js';\nimport { writeAgentStateJsonSync, type AgentState } from '../../../lib/agents.js';\nimport { WORK_LAUNCHER_GRACE_MS } from '../../../lib/cloister/agent-grace.js';\nimport { logAgentLifecycleSync } from '../../../lib/persistent-logger.js';\nimport { getWorkspaceForIssue } from '../../../lib/workspaces/resolver.js';\nimport type { DomainEvent } from '@overdeck/contracts';\n\nexport interface AgentProjectionResult {\n /** Assigned event sequence number. */\n sequence: number;\n}\n\nfunction prepareAgentStateForSave(state: AgentState): AgentState {\n if (state.status === 'running' || state.status === 'starting') {\n delete state.stoppedAt;\n } else if (state.status === 'stopped' && !state.stoppedAt) {\n state.stoppedAt = new Date().toISOString();\n }\n return state;\n}\n\nfunction buildStoredEvent(\n event: Omit<DomainEvent, 'sequence'>,\n sequence: number,\n): StoredEvent {\n const record = event as Record<string, unknown>;\n return {\n sequence,\n type: event.type,\n timestamp: (record['timestamp'] as string) ?? new Date().toISOString(),\n payload: (record['payload'] as Record<string, unknown>) ?? {},\n };\n}\n\n/**\n * Atomically persist an agent state change and its lifecycle event.\n *\n * 1. Prepares the state (stoppedAt stamping).\n * 2. Writes state.json (rollback source) outside the SQLite tx.\n * 3. Begins an overdeck.db transaction, upserts the agents row, inserts the event.\n * 4. Commits and emits the stored event to subscribers.\n *\n * @throws If the SQLite transaction is rolled back, neither the row nor the\n * event is persisted and state.json may be slightly ahead.\n */\nexport function saveAgentStateAndEmitEvent(\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): AgentProjectionResult {\n const db = getOverdeckDatabaseSync();\n const eventStore = getEventStore();\n return saveAgentStateAndEmitEventWithDeps(db, eventStore, state, event);\n}\n\n/**\n * Effect wrapper for server routes. Runs the synchronous projection under\n * Effect.sync so callers in Effect.gen can compose it without blocking.\n */\nexport function saveAgentStateAndEmitEventProgram(\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): Effect.Effect<AgentProjectionResult> {\n return Effect.sync(() => saveAgentStateAndEmitEvent(state, event));\n}\n\n/**\n * Dependency-injected variant for tests.\n */\ntype AgentProjectionEventStore = Pick<EventStore, 'emitStored'>;\n\ntype AgentProjectionRows = AgentProjectionResult & {\n stored: StoredEvent;\n};\n\nfunction writeProjectionRows(\n db: SqliteDatabase,\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): AgentProjectionRows {\n const record = event as Record<string, unknown>;\n const timestamp = (record['timestamp'] as string) ?? new Date().toISOString();\n const timestampMs = new Date(timestamp).getTime();\n // PAN-1990 D-10/WI-11: stamp workspaceId from the projected agent's issueId\n // when the caller didn't already set one — this transactional path bypasses\n // event-store.ts's append()/appendAsync() stamping, so it needs its own.\n const rawPayload = (record['payload'] ?? {}) as Record<string, unknown>;\n const payloadRecord = rawPayload.workspaceId === undefined\n ? { ...rawPayload, workspaceId: getWorkspaceForIssue(state.issueId)?.id }\n : rawPayload;\n const payload = JSON.stringify(payloadRecord);\n const updatedAt = Date.now();\n\n db.prepare(\n `INSERT OR IGNORE INTO issues (id, stage, updated_at) VALUES (?, 'working', ?)`,\n ).run(state.issueId, updatedAt);\n db.prepare(\n `INSERT OR REPLACE INTO agents (${AGENT_COLUMNS_FOR_DB.join(', ')}) VALUES (${AGENT_COLUMNS_FOR_DB.map(() => '?').join(', ')})`,\n ).run(...stateToOverdeckParamsForDb(state, updatedAt));\n db.prepare(\n `INSERT INTO events (type, timestamp, payload) VALUES (?, ?, ?)`,\n ).run(event.type, timestampMs, payload);\n\n const row = db.prepare(`SELECT last_insert_rowid() AS sequence`).get() as\n | { sequence: number }\n | undefined;\n const sequence = row?.sequence ?? 0;\n return { sequence, stored: buildStoredEvent({ ...event, payload: payloadRecord }, sequence) };\n}\n\nfunction emitCommittedProjection(\n eventStore: AgentProjectionEventStore,\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n rows: AgentProjectionRows,\n): AgentProjectionResult {\n eventStore.emitStored(rows.stored);\n logAgentLifecycleSync(\n state.id,\n `projected ${event.type} (seq=${rows.sequence}) for ${state.id}`,\n );\n return { sequence: rows.sequence };\n}\n\nfunction rollbackTransaction(db: SqliteDatabase): void {\n try {\n db.exec('ROLLBACK');\n } catch {\n // Ignore rollback failures; the connection may already be rolled back.\n }\n}\n\nexport function saveAgentStateAndEmitEventWithDeps(\n db: SqliteDatabase,\n eventStore: AgentProjectionEventStore,\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): AgentProjectionResult {\n prepareAgentStateForSave(state);\n\n // Rollback source lives on the filesystem; keep it outside the SQLite tx\n // so a tx failure does not corrupt it.\n writeAgentStateJsonSync(state);\n\n db.exec('BEGIN IMMEDIATE');\n try {\n const rows = writeProjectionRows(db, state, event);\n db.exec('COMMIT');\n return emitCommittedProjection(eventStore, state, event, rows);\n } catch (err) {\n rollbackTransaction(db);\n throw err;\n }\n}\n\nexport type AgentStartPlaceholderClaim =\n | { claimed: true }\n | { claimed: false; reason: 'live-session' | 'active-state' };\n\n/**\n * Claim the single in-flight work-spawn slot and project its starting state.\n * The SQLite write lock makes the status check and placeholder insert one\n * operation, so concurrent dashboard requests cannot both launch `pan start`.\n */\nexport function claimAgentStartPlaceholderWithDeps(\n db: SqliteDatabase,\n eventStore: AgentProjectionEventStore,\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n hasLiveTmuxSession: boolean,\n): AgentStartPlaceholderClaim {\n if (hasLiveTmuxSession) return { claimed: false, reason: 'live-session' };\n\n prepareAgentStateForSave(state);\n db.exec('BEGIN IMMEDIATE');\n try {\n const current = db.prepare(\n `SELECT status, started_at AS startedAt FROM agents WHERE id = ?`,\n ).get(state.id) as { status: string; startedAt: number | null } | undefined;\n const startingWithinGrace = current?.status === 'starting'\n && current.startedAt !== null\n && Date.now() - current.startedAt < WORK_LAUNCHER_GRACE_MS;\n if (current?.status === 'running' || startingWithinGrace) {\n rollbackTransaction(db);\n return { claimed: false, reason: 'active-state' };\n }\n\n writeAgentStateJsonSync(state);\n const rows = writeProjectionRows(db, state, event);\n db.exec('COMMIT');\n emitCommittedProjection(eventStore, state, event, rows);\n return { claimed: true };\n } catch (err) {\n rollbackTransaction(db);\n throw err;\n }\n}\n\nexport function claimAgentStartPlaceholderProgram(\n state: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n hasLiveTmuxSession: boolean,\n): Effect.Effect<AgentStartPlaceholderClaim> {\n return Effect.sync(() => claimAgentStartPlaceholderWithDeps(\n getOverdeckDatabaseSync(),\n getEventStore(),\n state,\n event,\n hasLiveTmuxSession,\n ));\n}\n\n/**\n * Roll back only the placeholder owned by this failed spawn attempt. If the\n * agent has already advanced to a real running state, the compare fails and\n * the successful concurrent launch is left untouched.\n */\nexport function rollbackAgentStartPlaceholderWithDeps(\n db: SqliteDatabase,\n eventStore: AgentProjectionEventStore,\n placeholder: AgentState,\n fallback: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): boolean {\n prepareAgentStateForSave(fallback);\n db.exec('BEGIN IMMEDIATE');\n try {\n const current = db.prepare(\n `SELECT status, model, started_at AS startedAt FROM agents WHERE id = ?`,\n ).get(placeholder.id) as { status: string; model: string | null; startedAt: number | null } | undefined;\n const placeholderStartedAt = new Date(placeholder.startedAt).getTime();\n const ownsPlaceholder = current?.status === 'starting'\n && current.model === 'pending-work-spawn'\n && current.startedAt === placeholderStartedAt;\n if (!ownsPlaceholder) {\n rollbackTransaction(db);\n return false;\n }\n\n writeAgentStateJsonSync(fallback);\n const rows = writeProjectionRows(db, fallback, event);\n db.exec('COMMIT');\n emitCommittedProjection(eventStore, fallback, event, rows);\n return true;\n } catch (err) {\n rollbackTransaction(db);\n throw err;\n }\n}\n\nexport function rollbackAgentStartPlaceholderProgram(\n placeholder: AgentState,\n fallback: AgentState,\n event: Omit<DomainEvent, 'sequence'>,\n): Effect.Effect<boolean> {\n return Effect.sync(() => rollbackAgentStartPlaceholderWithDeps(\n getOverdeckDatabaseSync(),\n getEventStore(),\n placeholder,\n fallback,\n event,\n ));\n}\n","import { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport {\n clearAgentPaused,\n clearAgentTroubled,\n getAgentState,\n getSessionId,\n markAgentStoppedState,\n saveAgentRuntimeState,\n saveSessionId,\n setAgentPaused,\n stopAgent,\n} from '../../../../lib/agents.js';\nimport { emitActivityEntrySync } from '../../../../lib/activity-logger.js';\nimport { operatorInterventionEvent } from '../../../../lib/operator-interventions.js';\nimport { stopWorkspaceDocker } from '../../../../lib/workspace-manager.js';\nimport { killSession, sessionExists } from '../../../../lib/tmux.js';\nimport { getWorkAgentLifecycleState } from '../../../../lib/work-agent-lifecycle.js';\nimport { saveAgentStateAndEmitEventProgram } from '../../services/agent-projection.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { validateOrigin } from '../origin-validation.js';\nimport {\n appendAgentLifecycleLog,\n buildAgentControlEventPayload,\n captureAgentOutputBeforeKill,\n invalidateAgentsCache,\n readJsonBody,\n toAgentStatusPayload,\n} from './shared.js';\n\nexport function createAgentStopHandler(\n lifecycleEvent: 'agent.delete_requested' | 'agent.stop_requested',\n) {\n return httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const eventStore = yield* EventStoreService;\n\n const stateBeforeStop = yield* getAgentState(id);\n yield* Effect.promise(() => appendAgentLifecycleLog(id, lifecycleEvent));\n yield* stopAgent(id, 'operator');\n\n // PAN-1316/PAN-1326: tear down the workspace Docker stack on user-initiated stop.\n // Without this, dev-server containers (Vite/Webpack) outlive their owning\n // agent and can degrade the host via inotify-fallback polling storms.\n // Internal stops (restart) take a different path and don't reach here.\n //\n // Resolve the workspace from the issue (not from the agent's own state) so\n // killing a specialist (review/test/ship) — whose state.workspace may not\n // point at the work agent's workspace — still tears down the right stack.\n // Mirrors the postMergeLifecycle pattern in merge-agent.ts.\n if (stateBeforeStop?.issueId) {\n yield* Effect.promise(async () => {\n try {\n const { resolveProjectFromIssueSync } = await import('../../../../lib/projects.js');\n const { findWorkspacePath } = await import('../../../../lib/lifecycle/archive-planning.js');\n const issueLower = stateBeforeStop.issueId!.toLowerCase();\n const project = resolveProjectFromIssueSync(stateBeforeStop.issueId!);\n const projectPath = project?.projectPath ?? process.cwd();\n const workspacePath = findWorkspacePath(projectPath, issueLower);\n if (workspacePath) {\n const dockerResult = await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));\n if (dockerResult.containersFound) {\n console.log(`[agents] ✓ Stopped Docker stack for ${id}: ${dockerResult.steps.join('; ')}`);\n }\n }\n } catch (err) {\n console.warn(`[agents] Docker teardown failed for ${id} (non-fatal): ${err instanceof Error ? err.message : String(err)}`);\n }\n });\n }\n\n // PAN-1048 review feedback 004 (C1): AgentStoppedEvent requires both\n // agentId AND issueId on the payload (packages/contracts/src/events.ts:36);\n // ws-rpc drops events that fail Schema validation, so emits without issueId\n // never reach subscribers and the dashboard misses the stop transition.\n //\n // PAN-1908: write-through projection — re-upsert the stopped row and append\n // the lifecycle event in one SQLite transaction. stopAgent already saved\n // state, but repeating the upsert here makes the event append atomic.\n const stateAfterStop = yield* getAgentState(id);\n if (stateAfterStop) {\n yield* saveAgentStateAndEmitEventProgram(stateAfterStop, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: stateAfterStop.issueId || stateBeforeStop?.issueId || '' },\n });\n }\n const issueId = stateBeforeStop?.issueId;\n // PAN-1048: derive label from role; legacy state.phase no longer exists.\n const phaseLabel = stateBeforeStop?.role === 'plan' ? 'planning' : 'work';\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'info',\n message: issueId\n ? `User stopped ${issueId} ${phaseLabel} agent`\n : `User stopped agent ${id}`,\n issueId,\n });\n invalidateAgentsCache();\n return jsonResponse({ success: true });\n }));\n}\n\nfunction agentStopRoute(\n method: 'DELETE' | 'POST',\n path: `/${string}`,\n lifecycleEvent: 'agent.delete_requested' | 'agent.stop_requested',\n) {\n return HttpRouter.add(method, path, createAgentStopHandler(lifecycleEvent));\n}\n\n// ─── Route: DELETE /api/agents/:id ───────────────────────────────────────────\n\nexport const deleteAgentRoute = agentStopRoute('DELETE', '/api/agents/:id', 'agent.delete_requested');\n\n// ─── Route: POST /api/agents/:id/stop ────────────────────────────────────────\n\nexport const postAgentStopRoute = agentStopRoute('POST', '/api/agents/:id/stop', 'agent.stop_requested');\n\n// ─── Route: POST /api/agents/:id/suspend ─────────────────────────────────────\n\nexport const postAgentSuspendRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/suspend',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n\n const { sessionId } = body as any;\n const effectiveSessionId = sessionId || getSessionId(id);\n\n if (!effectiveSessionId) {\n return jsonResponse({ error: 'Session ID required for suspend' }, { status: 400 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.suspend_requested', { sessionId: effectiveSessionId }));\n saveSessionId(id, effectiveSessionId);\n // PAN-1048 review feedback 004 (C1): resolve issueId before kill so we can\n // include it on the agent.stopped payload (the contract requires it).\n const suspendIssueId = (yield* getAgentState(id))?.issueId ?? '';\n yield* killSession(id).pipe(Effect.catch(() => Effect.void));\n saveAgentRuntimeState(id, {\n state: 'suspended',\n lastActivity: new Date().toISOString(),\n claudeSessionId: effectiveSessionId,\n });\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction. Preserve the existing agent-table status\n // (suspend does not flip it to stopped).\n const stateAfterSuspend = yield* getAgentState(id);\n if (stateAfterSuspend) {\n yield* saveAgentStateAndEmitEventProgram(stateAfterSuspend, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: stateAfterSuspend.issueId || suspendIssueId },\n });\n }\n\n invalidateAgentsCache();\n return jsonResponse({ success: true });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/pause ────────────────────────────────────────\n\nexport const postAgentPauseRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/pause',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const reason = (body as { reason?: unknown }).reason;\n\n if (reason !== undefined && typeof reason !== 'string') {\n return jsonResponse({ error: 'reason must be a string' }, { status: 400 });\n }\n\n const stateBeforePause = yield* getAgentState(id);\n if (!stateBeforePause) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n const previousStatus = toAgentStatusPayload(stateBeforePause.status);\n const hasLiveSession = yield* sessionExists(id);\n const stoppedByPause = hasLiveSession || stateBeforePause.status === 'running' || stateBeforePause.status === 'starting';\n let updatedState = yield* setAgentPaused(id, reason, stoppedByPause);\n if (!updatedState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n if (hasLiveSession) {\n yield* Effect.promise(() => captureAgentOutputBeforeKill(id));\n yield* killSession(id);\n }\n\n if (hasLiveSession || updatedState.status === 'running' || updatedState.status === 'starting') {\n const stoppedState = markAgentStoppedState(updatedState, 'operator');\n updatedState = stoppedState;\n yield* Effect.promise(() => saveAgentRuntimeState(id, {\n state: 'stopped',\n lastActivity: new Date().toISOString(),\n }));\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.pause_requested', { reason }));\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: updatedState.issueId || stateBeforePause.issueId || id.replace(/^agent-/, '').toUpperCase(),\n kind: 'pause',\n source: 'dashboard',\n }));\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n // PAN-2633: assert tmux liveness at emission time so the reducer knows whether\n // this stop-shaped transition is idle-alive (preserve pending-input payload).\n const hasLiveTmuxSession = yield* sessionExists(id);\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.status_changed',\n timestamp: new Date().toISOString(),\n payload: buildAgentControlEventPayload(updatedState, previousStatus, hasLiveTmuxSession),\n });\n\n invalidateAgentsCache();\n return jsonResponse({ success: true, agent: updatedState });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/unpause ──────────────────────────────────────\n\nexport const postAgentUnpauseRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/unpause',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const eventStore = yield* EventStoreService;\n\n const stateBeforeUnpause = yield* getAgentState(id);\n if (!stateBeforeUnpause) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n const updatedState = yield* clearAgentPaused(id);\n if (!updatedState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.unpause_requested'));\n if (stateBeforeUnpause.paused === true) {\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: updatedState.issueId || stateBeforeUnpause.issueId || id.replace(/^agent-/, '').toUpperCase(),\n kind: 'unpause',\n source: 'dashboard:agent-unpause',\n }));\n }\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n // PAN-2633: assert tmux liveness at emission time so the reducer knows whether\n // this stop-shaped transition is idle-alive (preserve pending-input payload).\n const hasLiveTmuxSession = yield* sessionExists(id);\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.status_changed',\n timestamp: new Date().toISOString(),\n payload: buildAgentControlEventPayload(updatedState, toAgentStatusPayload(stateBeforeUnpause.status), hasLiveTmuxSession),\n });\n\n invalidateAgentsCache();\n\n // Resume immediately instead of leaving the agent for the deacon's next\n // patrol — clicking unpause means \"go now\". Fire-and-forget: resume has\n // its own readiness gate; the response returns at once and the tree\n // updates via the projection as the agent comes up. Only fires when the\n // lifecycle says there is actually a session to resume — a plain stopped\n // agent with no session is left for the Start button, same as before.\n let resumeTriggered = false;\n const lifecycle = yield* getWorkAgentLifecycleState(id);\n // Troubled agents are quarantined from auto-resume (the deacon skips them\n // too) — firing resumeAgent would just hit its gate and make\n // resumeTriggered a lie. untroubled + start is the path for those.\n if (lifecycle.canResumeSession && updatedState.troubled !== true) {\n resumeTriggered = true;\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.unpause_immediate_resume_triggered'));\n void import('../../../../lib/agents/resume.js')\n .then(({ resumeAgent }) => resumeAgent(id))\n .then((result) => {\n if (!result.success) console.warn(`[agents] immediate resume after unpause failed for ${id}: ${result.error}`);\n })\n .catch((err) => console.warn(`[agents] immediate resume after unpause errored for ${id}:`, err));\n }\n\n return jsonResponse({ success: true, agent: updatedState, resumeTriggered });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/untroubled ───────────────────────────────────\n\nexport const postAgentUntroubledRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/untroubled',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originCheck = validateOrigin(request);\n if (!originCheck.ok) {\n return jsonResponse({ ok: false, error: originCheck.error }, { status: 403 });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const eventStore = yield* EventStoreService;\n\n const stateBeforeClear = yield* getAgentState(id);\n if (!stateBeforeClear) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n const updatedState = yield* clearAgentTroubled(id);\n if (!updatedState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.untroubled_requested'));\n if (stateBeforeClear.troubled === true || (stateBeforeClear.consecutiveFailures ?? 0) > 0) {\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: updatedState.issueId || stateBeforeClear.issueId || id.replace(/^agent-/, '').toUpperCase(),\n kind: 'untroubled',\n source: 'dashboard:agent-untroubled',\n }));\n }\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n // PAN-2633: assert tmux liveness at emission time so the reducer knows whether\n // this stop-shaped transition is idle-alive (preserve pending-input payload).\n const hasLiveTmuxSession = yield* sessionExists(id);\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.status_changed',\n timestamp: new Date().toISOString(),\n payload: buildAgentControlEventPayload(updatedState, toAgentStatusPayload(stateBeforeClear.status), hasLiveTmuxSession),\n });\n\n invalidateAgentsCache();\n return jsonResponse({ success: true, agent: updatedState });\n })),\n);\n","import { existsSync } from 'node:fs';\nimport { readFile, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join, basename } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter } from 'effect/unstable/http';\nimport type { RuntimeName } from '../../../../lib/runtimes/types.js';\nimport { resolveStaffing } from '../../../../lib/agents/staffing.js';\nimport {\n detectPendingOperatorDecision,\n type PendingOperatorDecision,\n} from '../../../../lib/agents/pending-decision-gate.js';\nimport { findPlanSync, readWorkspacePlanSync } from '../../../../lib/xbrief/io.js';\nimport { resolveTieredExecutionEnabled, resolveTieredExecutionEnabledForIssue } from '../../../../lib/agents/tier-table.js';\nimport { getDispatchableItems } from '../../../../lib/xbrief/dag.js';\nimport { loadConfigSync } from '../../../../lib/config-yaml.js';\nimport { getIssueStageSync, isTerminalIssueStage } from '../../../../lib/overdeck/agents.js';\n\nimport {\n getAgentState,\n getLatestSessionId,\n recoverAgent,\n resumeAgent,\n restartAgent,\n saveAgentStateSync,\n getAgentDir,\n getProviderAuthMode,\n listRunningAgents,\n wipeAgentStateDirs,\n} from '../../../../lib/agents.js';\nimport { canUseHarnessSync } from '../../../../lib/harness-policy.js';\nimport { normalizeModelOverrideSync, requireModelOverrideSync } from '../../../../lib/model-validation.js';\nimport { operatorInterventionEvent } from '../../../../lib/operator-interventions.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { getWorkAgentLifecycleState } from '../../../../lib/work-agent-lifecycle.js';\nimport { killSession } from '../../../../lib/tmux.js';\nimport { saveAgentStateAndEmitEventProgram } from '../../services/agent-projection.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport {\n appendAgentLifecycleLog,\n buildPanStartArgs,\n invalidateAgentsCache,\n readJsonBody,\n spawnPanCommandDetached,\n} from './shared.js';\n\nfunction pendingDecisionError(\n agentId: string,\n issueId: string,\n decision: PendingOperatorDecision,\n): string {\n const reason = decision.reason.replaceAll('_', ' ');\n return `Agent ${agentId} is waiting on an operator decision (${reason}). Answer it with 'pan answer ${issueId}' or open the Decisions panel; pass {force:true} to discard it deliberately.`;\n}\n\n// ─── Route: POST /api/agents/:id/resume ──────────────────────────────────────\n\nexport const postAgentResumeRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/resume',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { message, model, harness, compact } = body as { message?: string; model?: string; harness?: RuntimeName; compact?: boolean };\n let resumeModel: string | undefined;\n try {\n resumeModel = normalizeModelOverrideSync(model);\n } catch (err) {\n console.warn(`[agents/resume] ${id} model validation failed: ${err instanceof Error ? err.message : String(err)}`);\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n // PAN-1985 follow-up: structured log at the route entry so the operator\n // can trace every resume attempt in the server console / pty-supervisor\n // log even when the front-end's toast is missed.\n console.log(`[agents/resume] ${id} requested: model=${resumeModel ?? 'unchanged'} harness=${harness ?? 'unchanged'} hasMessage=${!!message} compact=${compact === true}`);\n const eventStore = yield* EventStoreService;\n // Snapshot lifecycle state BEFORE taking any action so callers can see the\n // temporal context (why was this resume allowed) without recomputing state.\n const lifecycleBefore = yield* getWorkAgentLifecycleState(id);\n console.log(`[agents/resume] ${id} lifecycle: canResume=${lifecycleBefore.canResumeSession} hasSavedSession=${lifecycleBefore.hasSavedSession} hasLiveTmux=${lifecycleBefore.hasLiveTmuxSession} isCrashed=${lifecycleBefore.isCrashed} isStopped=${lifecycleBefore.isStopped}`);\n // PAN-1675: a compact-resume targets a context-wedged agent that is usually\n // still 'running' (a live but stuck session), which the normal gate rejects.\n // Allow it through for compact === true — resumeAgent summarizes the wedged\n // session out-of-band, kills it, and respawns a fresh session seeded with\n // the summary (PAN-1781; its own canResume handles the running case).\n // Non-compact resumes keep the strict gate.\n if (!lifecycleBefore.canResumeSession && !lifecycleBefore.isRunningButStuck && compact !== true) {\n console.warn(`[agents/resume] ${id} rejected: ${lifecycleBefore.reason}`);\n return jsonResponse({\n error: lifecycleBefore.reason || `Cannot resume agent ${lifecycleBefore.agentId}`,\n lifecycle: lifecycleBefore,\n }, { status: 409 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.resume_requested', {\n hasMessage: !!message,\n model: resumeModel || undefined,\n harness: harness || undefined,\n lifecycle: lifecycleBefore,\n }));\n const resumeOpts = resumeModel || harness || compact === true\n ? { ...(resumeModel ? { model: resumeModel } : {}), ...(harness ? { harness } : {}), ...(compact === true ? { compact: true } : {}) }\n : undefined;\n console.log(`[agents/resume] ${id} dispatching resumeAgent() with opts=${JSON.stringify(resumeOpts)}`);\n const result = yield* Effect.promise(() => resumeAgent(id, message, resumeOpts));\n console.log(`[agents/resume] ${id} resumeAgent returned: success=${result.success} messageDelivered=${result.messageDelivered} error=${result.error ?? 'none'}`);\n if (result.success) {\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction so the read model transitions agent\n // status from 'stopped' → 'running' and the frontend updates immediately.\n const agentState = yield* getAgentState(id);\n if (agentState) {\n yield* saveAgentStateAndEmitEventProgram(agentState, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: id,\n issueId: agentState.issueId,\n agent: {\n id,\n issueId: agentState.issueId,\n workspace: agentState.workspace,\n runtime: agentState.harness ?? 'claude-code',\n model: agentState.model,\n status: 'running',\n startedAt: agentState.startedAt,\n lastActivity: new Date().toISOString(),\n role: agentState.role ?? 'work',\n },\n },\n });\n }\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.resume_succeeded', {\n hasMessage: !!message,\n messageDelivered: result.messageDelivered !== false,\n }));\n invalidateAgentsCache();\n // PAN-1985 follow-up: the messageDelivered flag distinguishes \"agent is\n // resumed and your message landed in its composer\" from \"agent is\n // resumed but your message did NOT land in its composer (PTY supervisor\n // echo-confirm timed out, harness/session.id mismatch, etc.)\". The\n // former gets a 'delivered' toast; the latter gets a clear 'queued in\n // mail' warning so the operator can intervene if needed.\n const delivered = result.messageDelivered !== false;\n console.log(`[agents/resume] ${id} returning: success=${true} delivered=${delivered}`);\n return jsonResponse({\n success: true,\n resumed: true,\n messageDelivered: delivered,\n hint: delivered\n ? 'Continue prompt delivered to the agent.'\n : 'The continue prompt was queued in the agent mail/ folder because the live delivery path did not confirm in time. The agent will read it on its next session start.',\n lifecycle: { before: lifecycleBefore, after: yield* getWorkAgentLifecycleState(id) },\n });\n } else {\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.resume_failed', {\n hasMessage: !!message,\n error: result.error,\n }));\n return jsonResponse({\n error: result.error,\n lifecycle: { before: lifecycleBefore, after: yield* getWorkAgentLifecycleState(id) },\n }, { status: 400 });\n }\n })),\n);\n\n// ─── Route: POST /api/agents/:id/recover ──────────────────────────────────────\n\nexport const postAgentRecoverRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/recover',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const { model, force = false } = body as { model?: string; force?: boolean };\n let recoveryModel: string | undefined;\n try {\n recoveryModel = normalizeModelOverrideSync(model);\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n\n const stateBeforeRecover = yield* getAgentState(id);\n if (!stateBeforeRecover) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n if (!force) {\n const pendingDecision = yield* Effect.promise(() => detectPendingOperatorDecision(id));\n if (pendingDecision) {\n return jsonResponse({\n error: pendingDecisionError(id, stateBeforeRecover.issueId, pendingDecision),\n code: 'pending-operator-decision',\n pendingDecision,\n }, { status: 409 });\n }\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.recover_requested', {\n model: recoveryModel || undefined,\n }));\n\n const result = yield* Effect.promise(() => recoverAgent(id, {\n ...(recoveryModel ? { modelOverride: recoveryModel } : {}),\n force,\n }));\n if (!result || result.action === 'already-running') {\n const error = result ? `No recovery performed: agent ${id} already has a live harness runtime` : `Could not recover agent ${id}`;\n yield* Effect.promise(() => appendAgentLifecycleLog(id, result ? 'agent.recover_skipped' : 'agent.recover_failed', { error }));\n return jsonResponse({ success: false, error }, { status: result ? 409 : 400 });\n }\n\n const updatedState = yield* getAgentState(id);\n if (updatedState) {\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: id,\n issueId: updatedState.issueId || stateBeforeRecover.issueId || id.replace('agent-', '').toUpperCase(),\n agent: {\n id,\n issueId: updatedState.issueId || stateBeforeRecover.issueId,\n workspace: updatedState.workspace,\n model: updatedState.model,\n status: 'running',\n startedAt: updatedState.startedAt,\n lastActivity: updatedState.lastActivity,\n role: updatedState.role ?? 'work',\n },\n },\n });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.recover_succeeded'));\n invalidateAgentsCache();\n return jsonResponse({ success: true, recovered: true, agent: updatedState ?? null });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/restart ──────────────────────────────────────\n//\n// Restart an agent with optional model override. Graceful mode sends a 30s\n// warning then restarts; quick mode kills and relaunches immediately.\n// Returns 202 for graceful (async work), 200 for quick.\n\nexport const postAgentRestartRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/restart',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n\n const { model, harness, graceful = true, message, force = false } = body as {\n model?: string;\n harness?: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code';\n graceful?: boolean;\n message?: string;\n force?: boolean;\n };\n let restartModel: string | undefined;\n try {\n restartModel = normalizeModelOverrideSync(model);\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n if (!force) {\n const pendingDecision = yield* Effect.promise(() => detectPendingOperatorDecision(id));\n if (pendingDecision) {\n return jsonResponse({\n error: pendingDecisionError(id, agentState.issueId, pendingDecision),\n code: 'pending-operator-decision',\n pendingDecision,\n }, { status: 409 });\n }\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.restart_requested', {\n model: restartModel || agentState.model,\n harness: harness || undefined,\n graceful,\n hasMessage: !!message,\n }));\n\n if (graceful) {\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: agentState.issueId,\n kind: 'restart',\n source: 'dashboard',\n }));\n // Kick off async restart — don't block the HTTP response for 30s\n (async () => {\n try {\n // PAN-1908: write-through projection — agents-row upsert + lifecycle\n // event append in one SQLite transaction.\n await Effect.runPromise(saveAgentStateAndEmitEventProgram(agentState, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: agentState.issueId },\n }));\n\n const result = await restartAgent(id, { model: restartModel, harness, graceful: true, message, force });\n\n if (result.success || result.code === 'pending-operator-decision') {\n const updatedState = result.success ? await Effect.runPromise(getAgentState(id)) : agentState;\n // PAN-1908: write-through projection — preserve running state when a\n // late operator decision aborts before the destructive stop boundary.\n if (updatedState) {\n await Effect.runPromise(saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: id,\n issueId: updatedState.issueId || agentState.issueId,\n agent: {\n id,\n issueId: updatedState.issueId || agentState.issueId,\n workspace: updatedState.workspace || agentState.workspace,\n // PAN-1048 review feedback 004 (C3): same as quick-restart\n // below — surface the actual harness so Pi agents do not\n // get mis-labelled as Claude Code on graceful restart.\n runtime: updatedState.harness ?? agentState.harness ?? 'claude-code',\n model: restartModel || updatedState.model || agentState.model,\n status: 'running',\n startedAt: updatedState.startedAt || agentState.startedAt,\n lastActivity: new Date().toISOString(),\n role: updatedState.role ?? agentState.role,\n },\n },\n }));\n }\n invalidateAgentsCache();\n }\n await appendAgentLifecycleLog(id, 'agent.restart_completed', {\n success: result.success,\n error: result.error,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[agents] Graceful restart failed for ${id}: ${msg}`);\n await appendAgentLifecycleLog(id, 'agent.restart_failed', { error: msg });\n }\n })();\n\n return jsonResponse({ accepted: true, graceful: true, agentId: id }, { status: 202 });\n }\n\n // Quick restart — synchronous\n const result = yield* Effect.promise(() => restartAgent(id, { model: restartModel, harness, graceful: false, message, force }));\n\n if (result.success) {\n const updatedState = yield* getAgentState(id);\n yield* eventStore.appendAsync(operatorInterventionEvent({\n issueId: updatedState?.issueId || agentState.issueId,\n kind: 'restart',\n source: 'dashboard',\n }));\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction. restartAgent already saved state, but\n // repeating the upsert here makes each lifecycle event atomic.\n if (updatedState) {\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: updatedState.issueId || agentState.issueId },\n });\n yield* saveAgentStateAndEmitEventProgram(updatedState, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: id,\n issueId: updatedState.issueId || agentState.issueId,\n agent: {\n id,\n issueId: updatedState.issueId || agentState.issueId,\n workspace: updatedState.workspace || agentState.workspace,\n // PAN-1048 review feedback 004 (C3): preserve the agent's actual\n // harness instead of hard-coding 'claude'. AgentSnapshot.runtime\n // is what getHarness() reads, so a Pi agent restarted through\n // this path was being mis-labelled as Claude Code.\n runtime: updatedState.harness ?? agentState.harness ?? 'claude-code',\n model: restartModel || updatedState.model || agentState.model,\n status: 'running',\n startedAt: updatedState.startedAt || agentState.startedAt,\n lastActivity: new Date().toISOString(),\n role: updatedState.role ?? agentState.role,\n },\n },\n });\n }\n invalidateAgentsCache();\n return jsonResponse({ success: true, restarted: true, agentId: id, model: restartModel || agentState.model });\n }\n\n if (result.code === 'pending-operator-decision') {\n return jsonResponse({\n error: result.error,\n code: result.code,\n pendingDecision: result.pendingDecision,\n }, { status: 409 });\n }\n return jsonResponse({ error: result.error }, { status: 500 });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/restart-fresh ────────────────────────────────\n//\n// PAN-1985: wipe the work agent's state directory under ~/.overdeck/agents/,\n// then optionally spawn a fresh work agent with a new harness/model. This is\n// the deliberate operator override path — for harness/model switches (the\n// Claude-session JSONL can't be resumed under a different harness) and for\n// \"I want a clean work run\" recovery. The NORMAL review flow continues the\n// same session across re-dispatches (PAN-1862); this route is the escape\n// hatch that pays the re-research cost.\n//\n// Modes (driven by request body):\n// { spawn: true, model, harness } — wipe + respawn a new work agent with\n// the chosen harness/model\n// { spawn: false } — wipe only; user clicks Start afterwards\n// (this is the backend of the new\n// 'completeWorkReset' issue action)\n//\n// Refuses (409) if a live tmux session is alive — same gate as reset-session.\n// Workspace, xBRIEF, tasks, .pan/continue.json, .pan/feedback/, the branch,\n// and the commit history are all left untouched. The new agent (whether\n// auto-spawned or manually started) reads .pan/continue.json + branch state\n// to pick up where the prior run left off.\n\nexport const postAgentRestartFreshRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/restart-fresh',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { spawn: spawnFlag, model: rawModel, harness, force = false } = body as {\n spawn?: boolean;\n model?: string;\n harness?: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code';\n force?: boolean;\n };\n const wantsSpawn = spawnFlag !== false; // default to spawn when omitted (picker path)\n\n let newModel: string | undefined;\n if (wantsSpawn && rawModel) {\n try {\n newModel = requireModelOverrideSync(rawModel);\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n const issueId = agentState.issueId ?? id.replace(/^agent-/, '').toUpperCase();\n const issueStage = getIssueStageSync(issueId);\n if (wantsSpawn && isTerminalIssueStage(issueStage)) {\n return jsonResponse({\n error: `${issueId} is already ${issueStage?.replaceAll('_', ' ')}. Reopen the issue before starting fresh work.`,\n issueStage,\n }, { status: 409 });\n }\n\n // PAN-1837 review fix: validate the explicit harness/model pair BEFORE any\n // mutation. This route used to kill the session and wipe the agent's\n // state directory first, then reject an invalid {harness, model} pair\n // afterward — an invalid selection destroyed the work agent's session\n // pointers and runtime files without spawning a replacement. Resolve\n // spawnModel and check policy here, before killSession/wipeAgentStateDirs.\n const spawnModel = newModel ?? agentState.model ?? 'claude-sonnet-5';\n let effectiveHarness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null = null;\n if (wantsSpawn && harness) {\n const harnessDecision = yield* Effect.promise(async () =>\n canUseHarnessSync(harness, spawnModel, await getProviderAuthMode(spawnModel)),\n );\n if (!harnessDecision.allowed) {\n return jsonResponse({ error: harnessDecision.reason ?? `Harness \"${harness}\" is not allowed for model \"${spawnModel}\".` }, { status: 400 });\n }\n effectiveHarness = harness;\n }\n\n if (!force) {\n const pendingDecision = yield* Effect.promise(() => detectPendingOperatorDecision(id));\n if (pendingDecision) {\n return jsonResponse({\n error: pendingDecisionError(id, issueId, pendingDecision),\n code: 'pending-operator-decision',\n pendingDecision,\n }, { status: 409 });\n }\n }\n\n const lifecycle = yield* getWorkAgentLifecycleState(id);\n if (lifecycle.hasLiveTmuxSession) {\n return jsonResponse({\n error: `Agent ${id} has a live tmux session. Run 'pan kill ${id}' to stop only this work agent, then retry.`,\n lifecycle,\n }, { status: 409 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.restart_fresh_requested', {\n wantsSpawn,\n model: newModel,\n harness,\n issueId,\n }));\n\n // Kill any zombie tmux session (shouldn't happen with the gate above, but\n // belt-and-suspenders) before wiping state.\n yield* killSession(id).pipe(Effect.catch(() => Effect.void));\n\n // Wipe the work agent dir only — leave specialist dirs (review, etc.)\n // alone. The new agent will read .pan/continue.json to pick up.\n const wipeResult = yield* Effect.promise(() => wipeAgentStateDirs(issueId));\n yield* Effect.promise(() => appendAgentLifecycleLog(id, 'agent.restart_fresh_wiped', {\n removed: wipeResult.removed,\n path: wipeResult.path,\n }));\n\n if (!wantsSpawn) {\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n spawn: false,\n agentId: id,\n issueId,\n removed: wipeResult.removed,\n hint: 'Agent dir wiped. Click Start agent to respawn with a fresh session.',\n });\n }\n\n // Auto-spawn path: dispatch to the existing /api/agents spawn flow.\n // We don't go through HTTP — we call the spawn primitives directly so\n // the caller gets a single 200 with both wipe and spawn confirmed.\n // (spawnModel/effectiveHarness were already resolved and validated above,\n // before the kill/wipe mutations.)\n const agentSessionName = `agent-${issueId.toLowerCase()}`;\n const projectPath = agentState.workspace\n ? dirname(agentState.workspace)\n : undefined;\n const projectConfig = resolveProjectFromIssueSync(issueId);\n const projectRoot = projectConfig?.projectPath ?? projectPath ?? process.cwd();\n const workspacePath = agentState.workspace ?? join(projectRoot, 'workspaces', `feature-${issueId.toLowerCase()}`);\n\n const args = buildPanStartArgs({\n issueId,\n model: spawnModel,\n harness: effectiveHarness,\n });\n\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.restart_fresh_spawn_requested', {\n args,\n model: spawnModel,\n harness: effectiveHarness,\n }));\n\n // Spawn detached `pan start` — same pattern the existing POST /api/agents\n // route uses, minus the HTTP hop. We deliberately write a placeholder\n // state.json (matching the existing spawn flow) so the dashboard\n // transitions the agent from \"stopped\" to \"starting\" within one refresh.\n saveAgentStateSync({\n id: agentSessionName,\n issueId,\n workspace: workspacePath,\n harness: effectiveHarness ?? 'claude-code',\n role: 'work',\n model: 'pending-work-spawn',\n status: 'starting',\n startedAt: new Date().toISOString(),\n });\n\n try {\n yield* Effect.promise(() => spawnPanCommandDetached({\n agentSessionName,\n issueId,\n role: 'work',\n workspacePath,\n args,\n cwd: workspacePath,\n }));\n } catch (err: any) {\n saveAgentStateSync({\n id: agentSessionName,\n issueId,\n workspace: workspacePath,\n harness: effectiveHarness ?? agentState.harness ?? 'claude-code',\n role: 'work',\n model: spawnModel,\n status: 'stopped',\n startedAt: new Date().toISOString(),\n stoppedAt: new Date().toISOString(),\n });\n const details = err?.message ?? String(err);\n return jsonResponse({\n success: false,\n error: 'The old agent state was cleared, but the fresh agent could not start. Resolve the startup blocker, then try again.',\n details,\n wiped: wipeResult.removed,\n }, { status: 500 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.restart_fresh_spawn_requested_complete', {\n model: spawnModel,\n harness: effectiveHarness,\n }));\n\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n spawn: true,\n agentId: id,\n issueId,\n removed: wipeResult.removed,\n spawnedModel: spawnModel,\n spawnedHarness: effectiveHarness,\n hint: 'Fresh work agent spawned. It will read .pan/continue.json and the branch state to continue.',\n });\n })),\n);\n\n// ─── Route: POST /api/agents/restart-all ──────────────────────────────────────\n//\n// Restart all running workspace agents using restartAgent() directly.\n// Quick mode (no graceful delay) to avoid serializing 30s waits across N agents.\n\nexport const postAgentsRestartAllRoute = HttpRouter.add(\n 'POST',\n '/api/agents/restart-all',\n Effect.gen(function* () {\n const body = yield* readJsonBody;\n const { force = false } = body as { force?: boolean };\n return yield* Effect.promise(async () => {\n try {\n const running = (await Effect.runPromise(listRunningAgents())).filter(a => a.tmuxActive);\n const results: Array<{\n id: string;\n issueId: string;\n model: string;\n status: string;\n pendingReason?: string;\n }> = [];\n\n for (const agent of running) {\n // One-shot roles (review/test convoy members) run once, write their\n // artifact, and exit WITHOUT saving a sessionId — there is nothing\n // to resume, and attempting it only ever yields \"No session ID\n // found\". Report them as skipped, not failed: the operator's\n // \"16 failed\" was 16 completed one-shots mislabeled.\n if (agent.role === 'review' || agent.role === 'test') {\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: 'skipped: one-shot role completes fresh — nothing to resume' });\n continue;\n }\n if (!agent.sessionId) {\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: 'skipped: no resumable session' });\n continue;\n }\n try {\n const result = await restartAgent(agent.id, { graceful: false, force });\n if (result.success) {\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: 'restarted' });\n } else if (result.code === 'pending-operator-decision') {\n results.push({\n id: agent.id,\n issueId: agent.issueId,\n model: agent.model,\n status: 'skipped',\n pendingReason: result.pendingDecision?.reason,\n });\n } else {\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: `failed: ${result.error}` });\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[agents] Failed to restart ${agent.id}:`, msg);\n results.push({ id: agent.id, issueId: agent.issueId, model: agent.model, status: `failed: ${msg}` });\n }\n }\n\n const succeeded = results.filter(r => r.status === 'restarted').length;\n const skipped = results\n .filter(r => r.status === 'skipped')\n .map(r => ({ id: r.id, reason: r.pendingReason }));\n console.log(`[agents] Restarted ${succeeded}/${running.length} workspace agents`);\n return jsonResponse({ restarted: succeeded, total: running.length, skipped, results });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return jsonResponse({ error: 'Failed to restart agents: ' + msg }, { status: 500 });\n }\n });\n }),\n);\n\n// ─── Route: POST /api/agents/:id/reset-session ─────────────────────────────\n// Clears saved Claude session tracking so the next start creates a fresh session.\n// Workspace, tasks, and git state are preserved. JSONL files kept for cost history.\n\nexport const postAgentResetSessionRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/reset-session',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const eventStore = yield* EventStoreService;\n\n const lifecycle = yield* getWorkAgentLifecycleState(id);\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: `Agent ${id} not found`, lifecycle }, { status: 404 });\n }\n\n if (lifecycle.hasLiveTmuxSession) {\n return jsonResponse({ error: `Agent ${id} is running. Stop it first.`, lifecycle }, { status: 409 });\n }\n\n const previousSessionId = yield* getLatestSessionId(id);\n // Evidence must match the lifecycle assert's: hasSavedSession reads the\n // agents-table session_id column, so a --fresh wipe that cleared only the\n // state-dir files left reset-session refusing (404) while pan start still\n // refused on the DB pointer — the stranded-pointer deadlock. Accept either\n // evidence source so the route can actually do its job.\n if (!previousSessionId && !agentState.sessionId) {\n return jsonResponse({ error: `Agent ${id} has no saved session to reset`, lifecycle }, { status: 404 });\n }\n\n const agentDir = getAgentDir(id);\n\n // Clear session.id\n yield* Effect.promise(() => rm(join(agentDir, 'session.id'), { force: true })); // PAN-3357: not a dir removal\n\n // Clear sessions.json\n yield* Effect.promise(() => rm(join(agentDir, 'sessions.json'), { force: true })); // PAN-3357: not a dir removal\n\n // Clear claudeSessionId from runtime.json (preserve other fields).\n // Must read/write directly — saveAgentRuntimeState merges with existing file.\n const runtimeFile = join(agentDir, 'runtime.json');\n if (existsSync(runtimeFile)) {\n try {\n const runtimeContent = yield* Effect.promise(() => readFile(runtimeFile, 'utf-8'));\n const runtime = JSON.parse(runtimeContent);\n delete runtime.claudeSessionId;\n yield* Effect.promise(() => writeFile(runtimeFile, JSON.stringify(runtime, null, 2)));\n } catch { /* non-fatal */ }\n }\n\n yield* killSession(id).pipe(Effect.catch(() => Effect.void));\n\n // Emit event so dashboard updates. PAN-1048 review feedback 004 (C1):\n // include issueId — without it AgentStoppedEvent fails Schema validation.\n //\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction. Also clears the agents-table session_id\n // column: a --fresh wipe clears the state-dir files only, and leaving the DB\n // pointer strands the lifecycle assert (requiresSessionResetBeforeFreshStart)\n // refusing every subsequent pan start --fresh with no door that can clear it.\n const clearedState = { ...agentState, sessionId: undefined };\n yield* saveAgentStateAndEmitEventProgram(clearedState, {\n type: 'agent.stopped',\n timestamp: new Date().toISOString(),\n payload: { agentId: id, issueId: agentState.issueId },\n });\n\n const priorSession = previousSessionId ?? agentState.sessionId ?? 'unknown';\n console.log(`[reset-session] Cleared session for ${id} (was: ${priorSession.slice(0, 8)}...)`);\n invalidateAgentsCache();\n return jsonResponse({ success: true, agentId: id, previousSessionId: previousSessionId ?? agentState.sessionId, lifecycle: yield* getWorkAgentLifecycleState(id) });\n })),\n);\n\n// ─── Route: POST /api/agents/restart-with-current-config ────────────────────\n// List all agents with their current vs new staffing, allowing operator to\n// selectively restart with new config\n\nexport interface RestartConfigChangeItem {\n agentId: string;\n issueId: string;\n currentModel: string;\n currentHarness: string;\n newModel: string;\n newHarness: string;\n changed: boolean;\n paused: boolean;\n troubled: boolean;\n status: string;\n}\n\nasync function buildRestartConfigChangeList(): Promise<RestartConfigChangeItem[]> {\n const agents = await Effect.runPromise(listRunningAgents());\n\n const items: RestartConfigChangeItem[] = [];\n\n for (const agent of agents) {\n if (!agent.issueId) continue;\n\n // Only include actually running agents with active tmux sessions\n if (!((agent as any).tmuxActive === true)) {\n continue;\n }\n\n // Skip paused/troubled agents (their gates stand)\n if ((agent as any).paused || (agent as any).troubled) {\n continue;\n }\n\n // Only work agents eligible for restart (skip conversations)\n if (agent.role !== 'work' && agent.role !== undefined) {\n continue;\n }\n\n // Compute new staffing from current config by reading the real xBRIEF work item\n const staffingResult = await resolveCurrentStaffing(agent.id, agent, agent.issueId);\n\n if (staffingResult.error) {\n // Skip agents where staffing cannot be resolved\n console.warn(`[restart-config] Skipping ${agent.id}: ${staffingResult.error}`);\n continue;\n }\n\n const newModel = staffingResult.newModel;\n const newHarness = staffingResult.newHarness;\n\n items.push({\n agentId: agent.id,\n issueId: agent.issueId,\n currentModel: agent.model,\n currentHarness: agent.harness ?? 'claude-code',\n newModel,\n newHarness,\n changed: agent.model !== newModel || (agent.harness ?? 'claude-code') !== newHarness,\n paused: (agent as any).paused ?? false,\n troubled: (agent as any).troubled ?? false,\n status: agent.status,\n });\n }\n\n return items;\n}\n\nexport const getAgentsRestartConfigRoute = HttpRouter.add(\n 'GET',\n '/api/agents/restart-with-current-config',\n httpHandler(Effect.gen(function* () {\n try {\n const items = yield* Effect.promise(() => buildRestartConfigChangeList());\n return jsonResponse({\n success: true,\n items,\n totalAgents: items.length,\n changedAgents: items.filter(i => i.changed).length,\n });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return jsonResponse({ error: `Failed to build restart config list: ${msg}` }, { status: 500 });\n }\n })),\n);\n\n// ─── Route: POST /api/agents/restart-with-current-config ────────────────────\n// Restart selected agents with current config\n\nasync function resolveCurrentStaffing(agentId: string, agentState: any, issueId: string): Promise<{ newModel: string; newHarness: string; error?: string }> {\n const workspacePath = agentState.workspace;\n if (!workspacePath) {\n return { newModel: agentState.model, newHarness: agentState.harness ?? 'claude-code', error: 'No workspace path available' };\n }\n\n try {\n // Read the actual xBRIEF plan\n const plan = readWorkspacePlanSync(workspacePath);\n if (!plan || !plan.plan.items) {\n return { newModel: agentState.model, newHarness: agentState.harness ?? 'claude-code', error: 'Invalid plan structure' };\n }\n\n // Find the work item using the same logic as spawn-prep:\n // For slot agents, use slotItemId; for single-work agents, use first dispatchable item\n let workItem = null;\n if (agentState.slotItemId) {\n // Slot agent: find the item by ID\n workItem = plan.plan.items.find((item: any) => item.id === agentState.slotItemId);\n if (!workItem) {\n return { newModel: agentState.model, newHarness: agentState.harness ?? 'claude-code', error: `Scheduled item ${agentState.slotItemId} not found in plan` };\n }\n } else {\n // Single-work agent: use first dispatchable item (same as spawn-prep)\n const dispatchableItems = getDispatchableItems(plan, new Set());\n workItem = dispatchableItems[0];\n if (!workItem) {\n return { newModel: agentState.model, newHarness: agentState.harness ?? 'claude-code', error: 'No dispatchable items in plan' };\n }\n }\n\n // Use the real work item with plan metadata for staffing resolution\n // This matches the logic in resolveSingleWorkTierSpawnParams\n const config = loadConfigSync().config;\n const tiered = config.tieredExecution;\n const effectiveTieredEnabled = issueId\n ? resolveTieredExecutionEnabledForIssue(tiered, issueId, plan.plan.metadata)\n : resolveTieredExecutionEnabled(tiered, plan.plan.metadata);\n\n const staffing = resolveStaffing(workItem, {\n planMetadata: plan.plan.metadata,\n spawnKey: `work:${issueId.toLowerCase()}`,\n issueId,\n config: { ...config, tieredExecution: { ...tiered, enabled: effectiveTieredEnabled } },\n });\n\n return {\n newModel: staffing.model,\n newHarness: staffing.harness,\n };\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n newModel: agentState.model,\n newHarness: agentState.harness ?? 'claude-code',\n error: `Failed to resolve staffing: ${msg}`,\n };\n }\n}\n\nexport const postAgentsRestartWithConfigRoute = HttpRouter.add(\n 'POST',\n '/api/agents/restart-with-current-config',\n httpHandler(Effect.gen(function* () {\n const body = yield* readJsonBody;\n const { agentIds, force = false } = body as { agentIds?: string[]; force?: boolean };\n\n if (!agentIds || !Array.isArray(agentIds) || agentIds.length === 0) {\n return jsonResponse({ error: 'agentIds array is required and must not be empty' }, { status: 400 });\n }\n\n // Get eligible agents from the same predicate as the GET route\n const eligibleAgents = yield* Effect.promise(() => buildRestartConfigChangeList());\n const eligibleIds = new Set(eligibleAgents.map(a => a.agentId));\n\n const results: { id: string; status: string; newModel?: string; newHarness?: string; error?: string }[] = [];\n\n for (const agentId of agentIds) {\n try {\n // Check if agent is in the eligible set\n if (!eligibleIds.has(agentId)) {\n results.push({ id: agentId, status: 'ineligible', error: 'Agent is not eligible for restart (paused, troubled, stopped, or not a work agent)' });\n continue;\n }\n\n const agentState = yield* getAgentState(agentId);\n if (!agentState) {\n results.push({ id: agentId, status: 'not_found', error: `Agent state not found` });\n continue;\n }\n\n const issueId = agentState.issueId ?? agentId.replace(/^agent-/, '').toUpperCase();\n\n // Resolve new staffing using the real work item\n const staffingResult = yield* Effect.promise(() => resolveCurrentStaffing(agentId, agentState, issueId));\n\n if (staffingResult.error) {\n results.push({\n id: agentId,\n status: 'staffing_error',\n error: staffingResult.error,\n });\n continue;\n }\n\n const newModel = staffingResult.newModel;\n const newHarness = staffingResult.newHarness;\n\n // Restart with new config\n const restartResult = yield* Effect.promise(() => restartAgent(agentId, {\n model: newModel,\n harness: newHarness as any,\n graceful: false,\n force,\n }));\n\n if (restartResult.success) {\n results.push({\n id: agentId,\n status: 'restarted',\n newModel,\n newHarness,\n });\n } else {\n results.push({\n id: agentId,\n status: restartResult.code === 'pending-operator-decision' ? 'skipped' : 'failed',\n error: restartResult.error,\n });\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n results.push({\n id: agentId,\n status: 'error',\n error: msg,\n });\n }\n }\n\n const succeeded = results.filter(r => r.status === 'restarted').length;\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n restarted: succeeded,\n total: agentIds.length,\n results,\n });\n })),\n);\n","import { existsSync } from 'node:fs';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { getAgentHealth } from '../../../../lib/cloister/health.js';\nimport { performHandoff } from '../../../../lib/cloister/handoff.js';\nimport { loadCloisterConfigSync } from '../../../../lib/cloister/config.js';\nimport { getCloisterService } from '../../../../lib/cloister/service.js';\nimport { checkAllTriggers } from '../../../../lib/cloister/triggers.js';\nimport { calculateCostSync, getPricingSync, type TokenUsage } from '../../../../lib/cost.js';\nimport { normalizeModelName } from '../../../../lib/cost-parsers/jsonl-parser.js';\nimport { requireModelOverrideSync } from '../../../../lib/model-validation.js';\nimport { encodeClaudeProjectDir } from '../../../../lib/paths.js';\nimport { getRuntimeForAgent } from '../../../../lib/runtimes/index.js';\nimport {\n getAgentState,\n setAgentDeliveryMethod,\n} from '../../../../lib/agents.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { validateOrigin } from '../origin-validation.js';\nimport { readJsonBody } from './shared.js';\n\n// ─── Route: GET /api/agents/:id/cloister-health ──────────────────────────────\n\nexport const getAgentCloisterHealthRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/cloister-health',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const service = getCloisterService();\n const health = service.getAgentHealth(id);\n if (!health) {\n return jsonResponse({ error: 'Agent not found or runtime not available' }, { status: 404 });\n }\n return jsonResponse(health);\n })),\n);\n\n// ─── Route: GET /api/agents/:id/handoff/suggestion ───────────────────────────\n\nexport const getAgentHandoffSuggestionRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/handoff/suggestion',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: 'Agent not found' }, { status: 404 });\n }\n\n const runtime = getRuntimeForAgent(id);\n if (!runtime) {\n return jsonResponse({ error: 'Runtime not found for agent' }, { status: 404 });\n }\n\n const health = getAgentHealth(id, runtime);\n const triggers = yield* checkAllTriggers(\n id,\n agentState.workspace,\n agentState.issueId,\n agentState.model,\n health,\n loadCloisterConfigSync()\n );\n\n if (triggers.length > 0) {\n const trigger = triggers[0];\n return jsonResponse({\n suggested: true,\n trigger: trigger.type,\n currentModel: agentState.model,\n suggestedModel: trigger.suggestedModel,\n reason: trigger.reason,\n });\n }\n\n return jsonResponse({\n suggested: false,\n trigger: null,\n currentModel: agentState.model,\n suggestedModel: null,\n reason: 'No handoff triggers detected',\n });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/handoff ─────────────────────────────────────\n\nexport const postAgentHandoffRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/handoff',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n\n const { toModel, reason } = body as any;\n let targetModel: string;\n try {\n targetModel = requireModelOverrideSync(toModel);\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n\n const result = yield* performHandoff(id, {\n targetModel,\n reason: reason || 'Manual handoff from dashboard',\n });\n\n if (result.success) {\n return jsonResponse({\n success: true,\n newAgentId: result.newAgentId,\n newSessionId: result.newSessionId,\n });\n } else {\n return jsonResponse({ success: false, error: result.error }, { status: 500 });\n }\n })),\n);\n\n\n// ─── Route: GET /api/agents/:id/cost ─────────────────────────────────────────\n\nexport const getAgentCostRoute = HttpRouter.add(\n 'GET',\n '/api/agents/:id/cost',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: 'Agent not found' }, { status: 404 });\n }\n\n let cost = 0;\n let inputTokens = 0;\n let outputTokens = 0;\n let cacheReadTokens = 0;\n let cacheWriteTokens = 0;\n let detectedModel = agentState.model || '';\n // Claude Code repeats the same `usage` on every JSONL line of one API response\n // (text line, each tool_use line, …). Dedup on requestId/message.id so a multi-block\n // turn is counted once instead of inflating tokens/cost ~2-3×.\n const countedUsageIds = new Set<string>();\n\n const homeDir = process.env.HOME || homedir();\n const claudeProjectsDir = join(homeDir, '.claude', 'projects');\n const workspacePath = agentState.workspace;\n\n if (workspacePath) {\n const projectDirName = encodeClaudeProjectDir(workspacePath);\n const projectDir = join(claudeProjectsDir, projectDirName);\n const sessionsIndexPath = join(projectDir, 'sessions-index.json');\n\n const parseJsonlCost = async (filePath: string) => {\n const jsonlContent = await readFile(filePath, 'utf-8');\n const lines = jsonlContent.split('\\n').filter((l: string) => l.trim());\n for (const line of lines) {\n try {\n const entry = JSON.parse(line);\n const usage = entry.message?.usage || entry.usage;\n const model = entry.message?.model || entry.model;\n const usageId = entry.requestId ?? entry.message?.id;\n if (usage && (usageId === undefined || !countedUsageIds.has(usageId))) {\n if (usageId !== undefined) countedUsageIds.add(usageId);\n inputTokens += usage.input_tokens || 0;\n outputTokens += usage.output_tokens || 0;\n cacheReadTokens += usage.cache_read_input_tokens || 0;\n cacheWriteTokens += usage.cache_creation_input_tokens || 0;\n }\n if (model && !detectedModel) {\n detectedModel = model;\n }\n } catch {}\n }\n };\n\n if (existsSync(sessionsIndexPath)) {\n try {\n const indexContent = JSON.parse(yield* Effect.promise(() => readFile(sessionsIndexPath, 'utf-8')));\n for (const sessionEntry of (indexContent.entries || [])) {\n if (sessionEntry?.fullPath && existsSync(sessionEntry.fullPath)) {\n yield* Effect.promise(() => parseJsonlCost(sessionEntry.fullPath));\n }\n }\n } catch {}\n }\n\n if (inputTokens === 0 && existsSync(projectDir)) {\n try {\n const files = (yield* Effect.promise(() => readdir(projectDir))).filter(f => f.endsWith('.jsonl'));\n for (const file of files) {\n yield* Effect.promise(() => parseJsonlCost(join(projectDir, file)));\n }\n } catch {}\n }\n }\n\n if (inputTokens > 0 || outputTokens > 0) {\n const modelInfo = normalizeModelName(detectedModel || 'claude-sonnet-4');\n const pricing = getPricingSync(modelInfo.provider, modelInfo.model);\n if (pricing) {\n const usage: TokenUsage = {\n inputTokens,\n outputTokens,\n cacheReadTokens,\n cacheWriteTokens,\n };\n cost = calculateCostSync(usage, pricing);\n }\n }\n\n return jsonResponse({\n agentId: id,\n model: detectedModel || agentState.model,\n tokens: {\n input: inputTokens,\n output: outputTokens,\n cacheRead: cacheReadTokens,\n cacheWrite: cacheWriteTokens,\n },\n cost,\n });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/delivery-method ─────────────────────────────\n// Updates the agent's delivery method (auto | channels | tmux) in state.json.\n\nexport function validateAgentDeliveryMethodOrigin(\n request: HttpServerRequest.HttpServerRequest,\n): { ok: true } | { ok: false; status: 403; body: { error: 'forbidden' } } {\n const originCheck = validateOrigin(request);\n if (originCheck.ok) return { ok: true };\n return { ok: false, status: 403, body: { error: 'forbidden' } };\n}\n\nexport const postAgentDeliveryMethodRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/delivery-method',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const originDecision = validateAgentDeliveryMethodOrigin(request);\n if (!originDecision.ok) {\n return jsonResponse(originDecision.body, { status: originDecision.status });\n }\n\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n const body = yield* readJsonBody;\n const { deliveryMethod } = body as { deliveryMethod?: 'auto' | 'channels' | 'tmux' };\n\n if (!deliveryMethod || !['auto', 'channels', 'tmux'].includes(deliveryMethod)) {\n return jsonResponse({ error: 'deliveryMethod must be auto, channels, or tmux' }, { status: 400 });\n }\n\n const agentState = yield* getAgentState(id);\n if (!agentState) {\n return jsonResponse({ error: `Agent ${id} not found` }, { status: 404 });\n }\n\n yield* Effect.promise(() => setAgentDeliveryMethod(id, deliveryMethod));\n return jsonResponse({ success: true, agentId: id, deliveryMethod });\n })),\n);\n\n// ─── Route: POST /api/agents/:id/switch-model ────────────────────────────────\n// Pipeline agent models are fixed at spawn. Changing a model tears down the\n// live session and discards context, so this route is retained only as a\n// server-side compatibility rejection for older clients.\n\nexport const postAgentSwitchModelRoute = HttpRouter.add(\n 'POST',\n '/api/agents/:id/switch-model',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const id = params['id'] ?? '';\n return jsonResponse({\n error: `Agent ${id} model is locked once the agent is spawned`,\n }, { status: 409 });\n })),\n);\n","import type { OrderBook } from '@overdeck/contracts';\n\nimport type { OrderBookProgress } from './types.js';\n\nexport type OrderEligibilityConditionKey =\n | 'book-membership'\n | 'pickup-posture'\n | 'lane-slot'\n | 'prerequisites'\n | 'prd-reverified';\n\nexport interface OrderEligibilityCondition {\n key: OrderEligibilityConditionKey;\n met: boolean;\n detail: string;\n}\n\nexport interface OrderDispatchEligibility {\n eligible: boolean;\n overrideUsed: boolean;\n code?: 'off-book' | 'book-draining' | 'lane-b-busy' | 'lane-a-full' | 'prerequisite-unmet' | 'reverify-required';\n message?: string;\n conditions: OrderEligibilityCondition[];\n}\n\nexport interface EvaluateOrderDispatchInput {\n book: OrderBook;\n progress: OrderBookProgress;\n issueId: string;\n inFlightIssues: ReadonlySet<string>;\n prerequisiteTerminal?: ReadonlyMap<string, boolean>;\n offBook?: boolean;\n}\n\nexport function evaluateOrderDispatchEligibility(input: EvaluateOrderDispatchInput): OrderDispatchEligibility {\n const issueId = input.issueId.toUpperCase();\n const item = input.book.items.find((candidate) => candidate.issue.toUpperCase() === issueId);\n if (!item) {\n const overrideUsed = input.offBook === true;\n return {\n eligible: overrideUsed,\n overrideUsed,\n ...(overrideUsed ? {} : {\n code: 'off-book' as const,\n message: `Issue ${issueId} is not in active order book ${input.book.id}; work-agent dispatch is blocked. Retry with --off-book only for an intentional exception.`,\n }),\n conditions: [{\n key: 'book-membership',\n met: overrideUsed,\n detail: overrideUsed\n ? `Issue ${issueId} is outside the book and the operator supplied --off-book.`\n : `Issue ${issueId} is not a member of ${input.book.id}.`,\n }],\n };\n }\n\n const terminalByIssue = new Map(input.progress.items.map((progress) => [progress.issue.toUpperCase(), progress.terminal]));\n for (const [issue, terminal] of input.prerequisiteTerminal ?? []) {\n if (!terminalByIssue.has(issue.toUpperCase())) terminalByIssue.set(issue.toUpperCase(), terminal);\n }\n const unmetPrereq = item.prereqs.find((prereq) => terminalByIssue.get(prereq.toUpperCase()) !== true);\n const prereqsMet = unmetPrereq === undefined;\n const otherInFlight = input.book.items.filter((candidate) =>\n candidate.issue.toUpperCase() !== issueId\n && input.inFlightIssues.has(candidate.issue.toUpperCase()),\n );\n const laneBBlocker = item.lane === 'B'\n ? otherInFlight.find((candidate) => candidate.lane === 'B')\n : undefined;\n const laneAInFlight = otherInFlight.filter((candidate) => candidate.lane === 'A').length;\n const laneSlotFree = item.lane === 'B'\n ? laneBBlocker === undefined\n : laneAInFlight < input.book.settings.laneAConcurrency;\n\n const postureOpen = input.book.settings.posture === 'open';\n const prdReverified = !item.reVerify;\n const conditions: OrderEligibilityCondition[] = [\n { key: 'book-membership', met: true, detail: `${issueId} is a member of ${input.book.id}.` },\n {\n key: 'pickup-posture',\n met: postureOpen,\n detail: postureOpen ? 'Order-book pickup posture is OPEN.' : 'Order-book pickup posture is DRAIN.',\n },\n {\n key: 'lane-slot',\n met: laneSlotFree,\n detail: item.lane === 'B'\n ? (laneBBlocker ? `Lane B is occupied by ${laneBBlocker.issue}.` : 'Lane B has no issue in flight.')\n : `${laneAInFlight}/${input.book.settings.laneAConcurrency} Lane A slots are in flight.`,\n },\n {\n key: 'prerequisites',\n met: prereqsMet,\n detail: unmetPrereq ? `Prerequisite ${unmetPrereq.toUpperCase()} is not terminal.` : 'Every prerequisite is landed or parked.',\n },\n {\n key: 'prd-reverified',\n met: prdReverified,\n detail: prdReverified ? 'No PRD re-verification is pending.' : `${issueId} requires PRD re-verification before dispatch.`,\n },\n ];\n\n if (!postureOpen) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'book-draining',\n message: `Issue ${issueId} cannot start because order book ${input.book.id} is in DRAIN posture; no new book items may dispatch.`,\n conditions,\n };\n }\n if (unmetPrereq) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'prerequisite-unmet',\n message: `Issue ${issueId} cannot start because prerequisite ${unmetPrereq.toUpperCase()} has not landed or been parked.`,\n conditions,\n };\n }\n if (!prdReverified) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'reverify-required',\n message: `Issue ${issueId} cannot start until its PRD has been re-verified against current main.`,\n conditions,\n };\n }\n if (laneBBlocker) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'lane-b-busy',\n message: `Issue ${issueId} cannot start in Lane B because ${laneBBlocker.issue} is still in flight; Lane B allows one active issue at a time.`,\n conditions,\n };\n }\n if (item.lane === 'A' && !laneSlotFree) {\n return {\n eligible: false,\n overrideUsed: false,\n code: 'lane-a-full',\n message: `Issue ${issueId} cannot start in Lane A because ${laneAInFlight} issues are already in flight and laneAConcurrency is ${input.book.settings.laneAConcurrency}.`,\n conditions,\n };\n }\n return { eligible: true, overrideUsed: false, conditions };\n}\n","import { appendFile, mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport {\n getFlywheelRunDir,\n readFlywheelLaunchMetadata,\n resolveLiveFlywheelRunId,\n} from '../../dashboard/server/services/flywheel-run-state.js';\nimport { getReviewStatusSync } from '../review-status.js';\nimport { findProjectByPathSync } from '../projects.js';\nimport { resolveStateReadHomeSync } from '../state-read-home.js';\nimport { getWorkAgentLifecycleStateSync } from '../work-agent-lifecycle.js';\nimport { evaluateOrderDispatchEligibility, type OrderDispatchEligibility } from './eligibility.js';\nimport { computeBookProgress, getBook, liveOrderIssueLookup } from './resolver.js';\nimport type { OrderIssueLookup } from './types.js';\n\ninterface OrdersLaunchMetadata {\n orders?: { bookId?: string };\n}\n\nexport interface ActiveOrderDispatchCheck {\n ordersBound: boolean;\n runId?: string;\n bookId?: string;\n decision: OrderDispatchEligibility;\n}\n\nexport interface ActiveOrderDispatchDeps {\n resolveRunId?: typeof resolveLiveFlywheelRunId;\n readLaunch?: typeof readFlywheelLaunchMetadata;\n stateRoot?: (projectRoot: string) => string;\n getOrderBook?: typeof getBook;\n computeProgress?: typeof computeBookProgress;\n issueLookup?: OrderIssueLookup;\n reservedIssues?: ReadonlySet<string>;\n inFlightIssues?: (bookId: string, issueIds: readonly string[]) => ReadonlySet<string>;\n}\n\nfunction stateRootFor(projectRoot: string): string {\n const project = findProjectByPathSync(projectRoot);\n if (!project) throw new Error(`No configured project contains ${projectRoot}`);\n return resolveStateReadHomeSync(project).root;\n}\n\nfunction defaultInFlightIssues(_bookId: string, issueIds: readonly string[]): ReadonlySet<string> {\n const inFlight = new Set<string>();\n for (const issueId of issueIds) {\n const lifecycle = getWorkAgentLifecycleStateSync(issueId);\n const review = getReviewStatusSync(issueId);\n if (lifecycle.isRunning || (review !== null && review.reviewStatus !== 'pending')) {\n inFlight.add(issueId.toUpperCase());\n }\n }\n return inFlight;\n}\n\nconst booklessDecision: OrderDispatchEligibility = {\n eligible: true,\n overrideUsed: false,\n conditions: [],\n};\n\nexport async function checkActiveOrderDispatch(\n projectRoot: string,\n issueId: string,\n options: { offBook?: boolean } = {},\n deps: ActiveOrderDispatchDeps = {},\n): Promise<ActiveOrderDispatchCheck> {\n const runId = await (deps.resolveRunId ?? resolveLiveFlywheelRunId)();\n if (!runId) return { ordersBound: false, decision: booklessDecision };\n const launch = await (deps.readLaunch ?? readFlywheelLaunchMetadata)(runId) as\n | (Awaited<ReturnType<typeof readFlywheelLaunchMetadata>> & OrdersLaunchMetadata)\n | null;\n const bookId = launch?.orders?.bookId;\n if (!bookId) return { ordersBound: false, runId, decision: booklessDecision };\n\n const stateRoot = (deps.stateRoot ?? stateRootFor)(launch?.workspace ?? projectRoot);\n const book = (deps.getOrderBook ?? getBook)(stateRoot, bookId);\n if (!book) throw new Error(`Active Flywheel run ${runId} references missing order book ${bookId}`);\n const progress = (deps.computeProgress ?? computeBookProgress)(book);\n const prerequisiteIds = [...new Set(book.items.flatMap((item) => item.prereqs.map((prereq) => prereq.toUpperCase())))];\n const prerequisiteState = (deps.issueLookup ?? liveOrderIssueLookup)(prerequisiteIds);\n const prerequisiteTerminal = new Map(prerequisiteIds.map((prereq) => {\n const state = prerequisiteState.get(prereq);\n return [prereq, state ? !state.open || state.parked : false] as const;\n }));\n const inFlightIssues = new Set((deps.inFlightIssues ?? defaultInFlightIssues)(\n book.id,\n book.items.filter((item) => !progress.items.find((entry) => entry.issue === item.issue)?.terminal)\n .map((item) => item.issue),\n ));\n for (const reserved of deps.reservedIssues ?? []) inFlightIssues.add(reserved.toUpperCase());\n return {\n ordersBound: true,\n runId,\n bookId,\n decision: evaluateOrderDispatchEligibility({\n book,\n progress,\n issueId,\n inFlightIssues,\n prerequisiteTerminal,\n offBook: options.offBook,\n }),\n };\n}\n\nexport async function enforceActiveOrderDispatch(\n projectRoot: string,\n issueId: string,\n options: { offBook?: boolean; recordOverride?: boolean } = {},\n deps: ActiveOrderDispatchDeps = {},\n): Promise<ActiveOrderDispatchCheck> {\n const result = await checkActiveOrderDispatch(projectRoot, issueId, options, deps);\n if (!result.decision.eligible) {\n throw new Error(result.decision.message ?? `Order-book dispatch blocked for ${issueId}`);\n }\n if (options.recordOverride && result.decision.overrideUsed && result.runId && result.bookId) {\n await recordOffBookOverride(result.runId, result.bookId, issueId);\n }\n return result;\n}\n\nexport async function recordOffBookOverride(\n runId: string,\n bookId: string,\n issueId: string,\n actor = process.env['OVERDECK_AGENT_ID'] ?? 'operator',\n): Promise<string> {\n const runDir = getFlywheelRunDir(runId);\n const path = join(runDir, 'orders-overrides.jsonl');\n await mkdir(runDir, { recursive: true });\n await appendFile(path, `${JSON.stringify({\n timestamp: new Date().toISOString(),\n runId,\n bookId,\n issueId: issueId.toUpperCase(),\n actor,\n override: 'off-book',\n })}\\n`, 'utf8');\n return path;\n}\n","import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { getFlywheelRunDir } from '../../dashboard/server/services/flywheel-run-state.js';\nimport {\n checkActiveOrderDispatch,\n recordOffBookOverride,\n type ActiveOrderDispatchCheck,\n type ActiveOrderDispatchDeps,\n} from './dispatch-gate.js';\n\ninterface StoredOrderDispatchReservation {\n id: string;\n bookId: string;\n issueId: string;\n reservedAt: string;\n}\n\nexport class OrderDispatchReservationError extends Error {\n constructor(readonly check: ActiveOrderDispatchCheck) {\n super(check.decision.message ?? 'Order-book dispatch is blocked.');\n this.name = 'OrderDispatchReservationError';\n }\n}\n\nexport interface OrderDispatchReservation {\n check: ActiveOrderDispatchCheck;\n release: () => Promise<void>;\n}\n\nexport interface OrderDispatchReservationDeps extends ActiveOrderDispatchDeps {\n reservationRoot?: (runId: string) => string;\n now?: () => Date;\n staleMs?: number;\n recordOverride?: typeof recordOffBookOverride;\n}\n\nconst DEFAULT_STALE_MS = 15 * 60 * 1000;\nconst processLocks = new Map<string, Promise<void>>();\nlet reservationSequence = 0;\n\nasync function withProcessLock<T>(key: string, action: () => Promise<T>): Promise<T> {\n const previous = processLocks.get(key) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => { release = resolve; });\n const tail = previous.then(() => current);\n processLocks.set(key, tail);\n await previous;\n try {\n return await action();\n } finally {\n release();\n if (processLocks.get(key) === tail) processLocks.delete(key);\n }\n}\n\nfunction paths(root: string) {\n return {\n state: join(root, 'orders-dispatch-reservations.json'),\n temp: join(root, `orders-dispatch-reservations.${process.pid}.tmp`),\n lock: join(root, 'orders-dispatch-reservations.lock'),\n };\n}\n\nasync function acquireFileLock(path: string, staleMs: number, now: Date): Promise<void> {\n try {\n await mkdir(path);\n return;\n } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n if (code !== 'EEXIST') throw error;\n }\n\n const age = await stat(path).then((value) => now.getTime() - value.mtimeMs).catch(() => 0);\n if (age <= staleMs) {\n throw new Error('Another order-book dispatch admission is in progress; retry after it finishes.');\n }\n await rm(path, { recursive: true, force: true });\n await mkdir(path);\n}\n\nasync function readReservations(path: string): Promise<StoredOrderDispatchReservation[]> {\n try {\n const parsed = JSON.parse(await readFile(path, 'utf8')) as unknown;\n return Array.isArray(parsed) ? parsed as StoredOrderDispatchReservation[] : [];\n } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n if (code === 'ENOENT') return [];\n throw error;\n }\n}\n\nasync function writeReservations(path: string, tempPath: string, reservations: readonly StoredOrderDispatchReservation[]): Promise<void> {\n await writeFile(tempPath, `${JSON.stringify(reservations, null, 2)}\\n`, { encoding: 'utf8', mode: 0o600 });\n await rename(tempPath, path);\n}\n\nfunction freshReservations(\n reservations: readonly StoredOrderDispatchReservation[],\n now: Date,\n staleMs: number,\n): StoredOrderDispatchReservation[] {\n return reservations.filter((reservation) => {\n const reservedAt = Date.parse(reservation.reservedAt);\n return Number.isFinite(reservedAt) && now.getTime() - reservedAt <= staleMs;\n });\n}\n\nexport async function reserveActiveOrderDispatch(\n projectRoot: string,\n issueId: string,\n options: { offBook?: boolean; recordOverride?: boolean } = {},\n deps: OrderDispatchReservationDeps = {},\n): Promise<OrderDispatchReservation> {\n const initial = await checkActiveOrderDispatch(projectRoot, issueId, options, deps);\n if (!initial.ordersBound || !initial.runId || !initial.bookId || !initial.decision.eligible) {\n return { check: initial, release: async () => {} };\n }\n\n const root = (deps.reservationRoot ?? getFlywheelRunDir)(initial.runId);\n await mkdir(root, { recursive: true });\n const reservationPaths = paths(root);\n const staleMs = deps.staleMs ?? DEFAULT_STALE_MS;\n const now = (deps.now ?? (() => new Date()))();\n const reservationId = `${process.pid}-${now.getTime()}-${reservationSequence++}`;\n let check = initial;\n\n await withProcessLock(reservationPaths.lock, async () => {\n await acquireFileLock(reservationPaths.lock, staleMs, now);\n try {\n const current = freshReservations(await readReservations(reservationPaths.state), now, staleMs);\n check = await checkActiveOrderDispatch(projectRoot, issueId, options, {\n ...deps,\n reservedIssues: new Set(current\n .filter((reservation) => reservation.bookId === initial.bookId)\n .map((reservation) => reservation.issueId)),\n });\n if (!check.decision.eligible) return;\n current.push({\n id: reservationId,\n bookId: initial.bookId!,\n issueId: issueId.toUpperCase(),\n reservedAt: now.toISOString(),\n });\n await writeReservations(reservationPaths.state, reservationPaths.temp, current);\n } finally {\n await rm(reservationPaths.lock, { recursive: true, force: true });\n }\n });\n\n if (!check.decision.eligible) return { check, release: async () => {} };\n if (options.recordOverride && check.decision.overrideUsed) {\n await (deps.recordOverride ?? recordOffBookOverride)(initial.runId, initial.bookId, issueId);\n }\n\n let released = false;\n return {\n check,\n release: async () => {\n if (released) return;\n released = true;\n const releasedAt = (deps.now ?? (() => new Date()))();\n await withProcessLock(reservationPaths.lock, async () => {\n await acquireFileLock(reservationPaths.lock, staleMs, releasedAt);\n try {\n const current = freshReservations(await readReservations(reservationPaths.state), releasedAt, staleMs)\n .filter((reservation) => reservation.id !== reservationId);\n await writeReservations(reservationPaths.state, reservationPaths.temp, current);\n } finally {\n await rm(reservationPaths.lock, { recursive: true, force: true });\n }\n });\n },\n };\n}\n\nexport async function withActiveOrderDispatchReservation<T>(\n projectRoot: string,\n issueId: string,\n options: { offBook?: boolean; recordOverride?: boolean },\n action: () => Promise<T>,\n deps: OrderDispatchReservationDeps = {},\n): Promise<{ check: ActiveOrderDispatchCheck; result?: T }> {\n const reservation = await reserveActiveOrderDispatch(projectRoot, issueId, options, deps);\n if (!reservation.check.decision.eligible) return { check: reservation.check };\n try {\n return { check: reservation.check, result: await action() };\n } finally {\n await reservation.release();\n }\n}\n","import { randomUUID } from 'crypto';\nimport { Effect } from 'effect';\n\nimport type { FsError } from '../errors.js';\nimport { writeSpecForIssue } from '../pan-dir/specs.js';\nimport { generateXBriefFilename } from './lifecycle.js';\nimport type { XBriefDocument, XBriefSubItem } from './types.js';\n\nexport interface AutoSynthesizeIssueInput {\n issueId: string;\n title: string;\n body?: string | null;\n url?: string | null;\n}\n\nexport interface AutoSynthesizeResult {\n document: XBriefDocument;\n workspaceSpecPath: string;\n projectSpecPath: string;\n canonicalFilename: string;\n}\n\nfunction cleanMarkdownLine(line: string): string {\n return line\n .replace(/^[-*+]\\s+\\[[ xX]\\]\\s+/, '')\n .replace(/^[-*+]\\s+/, '')\n .replace(/^#+\\s+/, '')\n .replace(/`([^`]+)`/g, '$1')\n .trim();\n}\n\nexport function extractAcceptanceCriteriaFromIssue(title: string, body?: string | null): string[] {\n const text = body?.trim() ?? '';\n if (!text) return [`Implement ${title}`];\n\n const lines = text.split(/\\r?\\n/);\n // Only a real heading (or standalone bold/label line) counts — the phrase\n // appearing mid-prose must not start a section (PAN-2404).\n const headingIndex = lines.findIndex((line) =>\n /^#{1,6}\\s+.*acceptance(\\s+criteria)?\\b/i.test(line)\n || /^\\s*\\*{0,2}acceptance criteria\\*{0,2}:?\\s*$/i.test(line));\n\n // If an explicit Acceptance Criteria section exists, use only that section\n if (headingIndex >= 0) {\n // Find the end of this section (next heading or end of text)\n const sectionEndIndex = lines.findIndex((line, idx) =>\n idx > headingIndex && /^#+\\s+/.test(line)\n );\n const endIndex = sectionEndIndex >= 0 ? sectionEndIndex : lines.length;\n const sectionLines = lines.slice(headingIndex + 1, endIndex);\n\n const checklist = sectionLines\n .filter((line) => /^\\s*[-*+]\\s+\\[[ xX]\\]\\s+/.test(line))\n .map(cleanMarkdownLine)\n .filter(Boolean);\n if (checklist.length > 0) return checklist;\n\n const bullets = sectionLines\n .filter((line) => /^\\s*[-*+]\\s+/.test(line))\n .map(cleanMarkdownLine)\n .filter(Boolean)\n .slice(0, 8);\n if (bullets.length > 0) return bullets;\n }\n\n // No explicit Acceptance Criteria section: return a single generic AC\n return [`Issue's stated fix implemented with tests`];\n}\n\nexport function synthesizeMinimalXBrief(issue: AutoSynthesizeIssueInput): XBriefDocument {\n const issueId = issue.issueId.toUpperCase();\n const issueLabel = issueId.toLowerCase();\n const now = new Date().toISOString();\n const criteria = extractAcceptanceCriteriaFromIssue(issue.title, issue.body);\n const items: XBriefSubItem[] = criteria.map((criterion, index) => ({\n id: `auto-start.ac${index + 1}`,\n title: criterion,\n status: 'pending',\n created: now,\n metadata: { kind: 'acceptance_criterion' },\n }));\n\n const canonicalFilename = generateXBriefFilename(issueId, issue.title, now);\n\n return {\n xBRIEFInfo: {\n version: '0.8',\n created: now,\n updated: now,\n author: 'overdeck/auto-start',\n description: `Auto-synthesized minimal plan for ${issueId}: ${issue.title}`,\n inspectionPolicy: 'never',\n },\n plan: {\n id: issueLabel,\n title: issue.title,\n status: 'proposed',\n uid: randomUUID(),\n author: 'overdeck/auto-start',\n sequence: 1,\n created: now,\n updated: now,\n references: issue.url ? [{ uri: issue.url, label: issueId, type: 'issue' }] : [],\n tags: ['auto-start'],\n metadata: { canonicalFilename },\n narratives: {\n Problem: issue.body?.trim() || issue.title,\n Proposal: 'Implement the issue directly from the tracker-provided title and body.',\n },\n items: [\n {\n id: 'auto-start',\n title: 'Implement issue',\n status: 'pending',\n priority: 'medium',\n created: now,\n metadata: {\n difficulty: 'simple',\n issueLabel,\n requiresInspection: false,\n inspectionDepth: 'fast',\n },\n narrative: { Action: issue.body?.trim() || issue.title },\n items,\n },\n ],\n edges: [],\n },\n };\n}\n\n/**\n * Synthesize and durably persist a proposed spec through the canonical write\n * door. The returned workspace path is the canonical spec path because\n * workspace-local spec copies are migration compatibility only.\n */\nexport const writeAutoStartXBrief = (\n projectRoot: string,\n _workspacePath: string,\n issue: AutoSynthesizeIssueInput,\n): Effect.Effect<AutoSynthesizeResult, FsError> =>\n Effect.gen(function* () {\n const document = synthesizeMinimalXBrief(issue);\n const canonicalFilename = document.plan.metadata?.canonicalFilename as string;\n const projectDocument: XBriefDocument = {\n ...document,\n plan: { ...document.plan, status: 'proposed' },\n };\n const written = yield* writeSpecForIssue(\n projectRoot,\n projectDocument,\n 'proposed',\n canonicalFilename,\n );\n\n return {\n document,\n workspaceSpecPath: written.path,\n projectSpecPath: written.path,\n canonicalFilename,\n };\n });\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { lstat, mkdir, readFile, rm } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { Role } from '@overdeck/contracts';\nimport { Effect } from 'effect';\n\nimport { buildChildEnvWithoutTmuxSync } from '../../../../lib/child-env.js';\nimport {\n saveAgentState,\n saveAgentStateSync,\n} from '../../../../lib/agents.js';\nimport type { AgentState } from '../../../../lib/agents/agent-state.js';\nimport type { RemoteWorkspaceMetadata } from '../../../../lib/remote/interface.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { saveAgentStateAndEmitEventProgram } from '../../services/agent-projection.js';\nimport {\n appendAgentLifecycleLog,\n buildPanStartArgs,\n emitStartAgentPhase,\n execAsync,\n execFileAsync,\n getIssueDataService,\n invalidateAgentsCache,\n updateRegistryForAgentStart,\n type SpawnGuardrailDecision,\n} from './shared.js';\n\ntype LifecycleTransition = {\n transitionTo: (\n issueId: string,\n status: 'in_progress',\n ) => Effect.Effect<unknown, unknown, never>;\n};\n\ntype EventStoreAppend = {\n append: (event: {\n type: 'issue.statusChanged';\n timestamp: string;\n payload: { issueId: string; status: 'In Progress'; canonicalStatus: 'in_progress' };\n }) => Effect.Effect<unknown, unknown, never>;\n};\n\ntype SpawnPanCommand = (args: string[], cwd?: string) => Promise<string>;\n\ntype PlaceholderHarness = 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null;\n\nexport function buildAgentStartPlaceholder(input: {\n agentSessionName: string;\n issueId: string;\n workspacePath: string;\n role: Role;\n effectiveHarness: PlaceholderHarness;\n startedBy: string;\n allowHost: boolean;\n startedAt: string;\n}) {\n const state: AgentState = {\n id: input.agentSessionName,\n issueId: input.issueId,\n workspace: input.workspacePath,\n role: input.role,\n ...(input.effectiveHarness ? { harness: input.effectiveHarness } : {}),\n model: 'pending-work-spawn',\n status: 'starting',\n startedAt: input.startedAt,\n startedBy: input.startedBy,\n hostOverride: input.allowHost || undefined,\n };\n return {\n state,\n event: {\n type: 'agent.started' as const,\n timestamp: input.startedAt,\n payload: {\n agentId: input.agentSessionName,\n issueId: input.issueId,\n agent: {\n id: input.agentSessionName,\n issueId: input.issueId,\n workspace: input.workspacePath,\n status: 'starting' as const,\n startedAt: input.startedAt,\n role: input.role,\n startedBy: input.startedBy,\n ...(input.effectiveHarness ? { runtime: input.effectiveHarness } : {}),\n },\n },\n },\n };\n}\n\nexport function buildContainerStartState(input: {\n agentSessionName: string;\n issueId: string;\n workspacePath: string;\n role: Role;\n effectiveHarness: PlaceholderHarness;\n startedBy: string;\n allowHost: boolean;\n status: 'starting' | 'error';\n startedAt: string;\n}): AgentState {\n return {\n id: input.agentSessionName,\n issueId: input.issueId,\n ...(input.effectiveHarness ? { harness: input.effectiveHarness } : {}),\n model: 'pending-container-start',\n status: input.status,\n startedAt: input.startedAt,\n workspace: input.workspacePath,\n role: input.role,\n startedBy: input.startedBy,\n hostOverride: input.allowHost || undefined,\n };\n}\n\nexport async function requestWorkStartAfterContainers(input: {\n args: string[];\n workspacePath: string;\n spawnPanCommand: SpawnPanCommand;\n markWorkStartAccepted: () => Promise<void>;\n updateIssueStatus: () => Promise<void>;\n}): Promise<string> {\n const activityId = await input.spawnPanCommand(input.args, input.workspacePath);\n await input.markWorkStartAccepted();\n await input.updateIssueStatus();\n return activityId;\n}\n\nexport function handleRemoteAgentSpawn(input: {\n issueId: string;\n workspacePath: string;\n workspaceMetadata: RemoteWorkspaceMetadata;\n spawnModel: string;\n startedBy: string;\n autoSpawnConsentRequired: boolean;\n projectPath: string;\n spawnGuardrails: SpawnGuardrailDecision;\n lifecycle: LifecycleTransition;\n}) {\n return Effect.gen(function* () {\n const {\n issueId,\n workspacePath,\n workspaceMetadata,\n spawnModel,\n startedBy,\n autoSpawnConsentRequired,\n projectPath,\n spawnGuardrails,\n lifecycle,\n } = input;\n\n const { spawnRemoteAgent, checkRemoteSpendCap } = yield* Effect.promise(() => import('../../../../lib/remote/remote-agents.js'));\n const { createFlyProviderFromConfig } = yield* Effect.promise(() => import('../../../../lib/remote/index.js'));\n const { loadConfigSync: loadPanConfig } = yield* Effect.promise(() => import('../../../../lib/config.js'));\n const panConfig = loadPanConfig();\n const spendCap = checkRemoteSpendCap(panConfig);\n if (!spendCap.allowed) {\n return jsonResponse({ error: spendCap.message }, { status: 429 });\n }\n const fly = createFlyProviderFromConfig(panConfig.remote);\n yield* Effect.promise(() => fly.syncAllCredentials(workspaceMetadata.vmName));\n\n const { buildWorkAgentPrompt, getTrackerContext } = yield* Effect.promise(() => import('../../../../lib/cloister/work-agent-prompt.js'));\n const trackerContext = yield* Effect.promise(() => getTrackerContext(issueId, workspacePath));\n const agentPrompt = yield* Effect.promise(() => buildWorkAgentPrompt({\n issueId,\n env: 'REMOTE',\n workspacePath: '/workspace',\n skipDynamicContext: true,\n trackerContext,\n }));\n\n emitStartAgentPhase(issueId, 'spawn', 'start', 'starting remote work agent', {\n workspacePath,\n vmName: workspaceMetadata.vmName,\n });\n const state = yield* Effect.promise(() => spawnRemoteAgent({\n issueId,\n workspace: workspaceMetadata,\n prompt: agentPrompt,\n model: spawnModel,\n startedBy,\n autoSpawnConsentRequired,\n tier: fly.getResiliencyTier(),\n }));\n\n // Write canonical state.json so activeRoleRunExists() sees this remote\n // work agent as active before we emit the lifecycle transition below.\n // spawnRemoteAgent only writes remote-state.json; without state.json the\n // Cloister duplicate-spawn guard misses the in-flight remote agent and\n // would spawn a second local work run when in_progress is emitted.\n yield* saveAgentState({\n id: state.id,\n issueId: state.issueId,\n workspace: workspacePath,\n role: 'work',\n model: spawnModel,\n status: 'starting',\n startedAt: state.startedAt,\n harness: 'claude-code',\n startedBy,\n });\n updateRegistryForAgentStart(state.issueId, workspacePath, state.id);\n\n // PAN-1048: lifecycle.transitionTo() is the single source of issue.transitioned.\n // The redundant issue.statusChanged emit was racing with reactive Cloister:\n // Cloister mapped 'in_progress' → 'work' role and tried to spawn a second\n // run while activeRoleRunExists() still saw no state.json for the\n // in-flight spawn above. state.json is now written before this emit.\n yield* Effect.promise(() => Effect.runPromise(\n lifecycle.transitionTo(issueId, 'in_progress').pipe(Effect.catch(() => Effect.void))\n ));\n\n // PAN-1048 review feedback 003: emit a contract-compliant agent.started\n // event. The reducer writes event.payload.agent into agentsById keyed by\n // event.payload.agentId — the previous shape ({ agentId: issueId, issueId })\n // omitted .agent and used the issue ID as the key, inserting `undefined`\n // into the read model and breaking dashboard consumers.\n //\n // PAN-1908: write-through projection — agents-row upsert + lifecycle event\n // append in one SQLite transaction.\n yield* saveAgentStateAndEmitEventProgram({\n id: state.id,\n issueId: state.issueId,\n workspace: workspacePath,\n role: 'work',\n model: spawnModel,\n status: state.status,\n startedAt: state.startedAt,\n harness: 'claude-code',\n startedBy,\n }, {\n type: 'agent.started',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: state.id,\n issueId: state.issueId,\n agent: {\n id: state.id,\n issueId: state.issueId,\n role: 'work' as const,\n model: spawnModel,\n status: state.status,\n startedAt: state.startedAt,\n lastActivity: state.lastActivity,\n startedBy,\n },\n },\n });\n emitStartAgentPhase(issueId, 'spawn', 'success', 'remote work agent spawn requested', {\n agentId: state.id,\n vmName: workspaceMetadata.vmName,\n });\n try { getIssueDataService().patchIssue(issueId, { status: 'In Progress', canonicalStatus: 'in_progress' }); } catch { /* non-fatal */ }\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n message: `Starting remote agent for ${issueId}`,\n remote: true,\n vmName: workspaceMetadata.vmName,\n agentId: state.id,\n projectPath,\n guardrails: spawnGuardrails,\n });\n });\n}\n\nexport function handleContainerOrchestration(input: {\n issueId: string;\n workspacePath: string;\n devScript: string;\n agentSessionName: string;\n role: Role;\n effectiveHarness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null;\n startedBy: string;\n allowHost: boolean;\n spawnModel: string;\n spawnGuardrails: SpawnGuardrailDecision;\n projectPath: string;\n eventStore: EventStoreAppend;\n spawnPanCommand: SpawnPanCommand;\n markWorkStartAccepted: () => Promise<void>;\n updateIssueStatus: () => Promise<void>;\n}) {\n return Effect.gen(function* () {\n const {\n issueId,\n workspacePath,\n devScript,\n agentSessionName,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n spawnModel,\n spawnGuardrails,\n projectPath,\n eventStore,\n spawnPanCommand,\n markWorkStartAccepted,\n updateIssueStatus,\n } = input;\n\n if (existsSync(workspacePath) && existsSync(devScript)) {\n let dockerRunning = false;\n try {\n yield* Effect.promise(() => execAsync('docker info >/dev/null 2>&1', { encoding: 'utf-8' }));\n dockerRunning = true;\n } catch {}\n\n if (dockerRunning) {\n const getComposeProjectName = async (id: string, wPath: string): Promise<string> => {\n const featureFolder = `feature-${id.toLowerCase()}`;\n const expected = `overdeck-${featureFolder}`;\n const validate = (value: string, devPath: string): string => {\n if (value !== expected) {\n throw new Error(`Invalid COMPOSE_PROJECT_NAME in ${devPath}: expected ${expected}`);\n }\n return value;\n };\n\n const devScriptPaths = [join(wPath, '.devcontainer', 'dev'), join(wPath, 'dev')];\n for (const devPath of devScriptPaths) {\n try {\n if (existsSync(devPath)) {\n const content = await readFile(devPath, 'utf-8');\n const match = content.match(/COMPOSE_PROJECT_NAME=\"([^$\"]*)\\$\\{FEATURE_FOLDER\\}\"/);\n if (match) return validate(`${match[1]}${featureFolder}`, devPath);\n const literalMatch = content.match(/COMPOSE_PROJECT_NAME=\"([^\"]+)\"/);\n if (literalMatch) return validate(literalMatch[1], devPath);\n }\n } catch (error) {\n if (error instanceof Error && error.message.startsWith('Invalid COMPOSE_PROJECT_NAME')) throw error;\n }\n }\n return expected;\n };\n\n let featureName: string;\n try {\n featureName = yield* Effect.promise(() => getComposeProjectName(issueId, workspacePath));\n } catch (error) {\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: error instanceof Error ? error.message : String(error),\n }, { status: 422 });\n }\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_container_check', {\n issueId,\n featureName,\n workspacePath,\n }));\n let containersReady = false;\n\n try {\n const { stdout: existing } = yield* Effect.promise(() => execFileAsync(\n 'docker',\n ['ps', '--filter', `name=${featureName}`, '--format', '{{.Names}}|{{.Status}}'],\n { encoding: 'utf-8' }\n ));\n const runningContainers = existing.trim().split('\\n').filter(Boolean);\n const allHealthy = runningContainers.length > 0 && runningContainers.every(line => {\n const status = line.split('|')[1] || '';\n return status.includes('Up') && (!status.includes('(') || status.includes('(healthy)'));\n });\n if (allHealthy) containersReady = true;\n } catch {}\n\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_container_check_result', {\n issueId,\n featureName,\n containersReady,\n }));\n\n if (!containersReady && !allowHost) {\n const earlyAgentId = agentSessionName;\n const earlyStateDir = join(homedir(), '.overdeck', 'agents', earlyAgentId);\n yield* Effect.promise(() => mkdir(earlyStateDir, { recursive: true }));\n saveAgentStateSync(buildContainerStartState({\n agentSessionName: earlyAgentId,\n issueId,\n workspacePath,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n status: 'starting',\n startedAt: new Date().toISOString(),\n }));\n updateRegistryForAgentStart(issueId, workspacePath, earlyAgentId);\n yield* Effect.promise(() => appendAgentLifecycleLog(earlyAgentId, 'agent.start_waiting_for_containers', {\n issueId,\n featureName,\n workspacePath,\n role,\n }));\n\n const containerActivityId = `containers-${Date.now()}`;\n\n // Start containers in background and spawn agent when ready\n (async () => {\n try {\n const containerUid = process.getuid?.() ?? 1000;\n const containerGid = process.getgid?.() ?? 1000;\n await appendAgentLifecycleLog(earlyAgentId, 'agent.container_start_spawned', {\n issueId,\n featureName,\n workspacePath,\n });\n const containerChild = spawn('./dev', ['all'], {\n cwd: workspacePath,\n stdio: 'ignore',\n env: buildChildEnvWithoutTmuxSync(process.env, { UID: String(containerUid), GID: String(containerGid), DOCKER_USER: `${containerUid}:${containerGid}` }),\n detached: true,\n });\n containerChild.unref();\n\n const maxWaitMs = 3 * 60 * 1000;\n const pollIntervalMs = 3000;\n const startTime = Date.now();\n let healthy = false;\n\n while (Date.now() - startTime < maxWaitMs) {\n try {\n const { stdout } = await execFileAsync(\n 'docker',\n ['ps', '--filter', `name=${featureName}`, '--format', '{{.Names}}|{{.Status}}'],\n { encoding: 'utf-8' }\n );\n const containers = stdout.trim().split('\\n').filter(Boolean);\n const allH = containers.length > 0 && containers.every(line => {\n const status = line.split('|')[1] || '';\n return status.includes('Up') && (!status.includes('(') || status.includes('(healthy)'));\n });\n if (allH) { healthy = true; break; }\n } catch {}\n await new Promise(r => setTimeout(r, pollIntervalMs));\n }\n\n await appendAgentLifecycleLog(earlyAgentId, healthy ? 'agent.container_wait_succeeded' : 'agent.container_wait_timed_out', {\n issueId,\n featureName,\n waitedMs: Date.now() - startTime,\n });\n\n if (!healthy) {\n saveAgentStateSync(buildContainerStartState({\n agentSessionName: earlyAgentId,\n issueId,\n workspacePath,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n status: 'error',\n startedAt: new Date().toISOString(),\n }));\n return;\n }\n\n // Docker named volumes may create root-owned empty node_modules.\n // Remove them — workspace creation runs bun install which creates\n // correct workspace-aware node_modules with proper local package resolution.\n for (const nmDir of [join(workspacePath, 'node_modules'), join(workspacePath, 'src', 'dashboard', 'frontend', 'node_modules')]) {\n try {\n if (existsSync(nmDir)) {\n const stat = await lstat(nmDir);\n if (!stat.isSymbolicLink()) {\n await rm(nmDir, { recursive: true, force: true });\n console.log(`[start-agent] Removed Docker-created ${nmDir}`);\n }\n }\n } catch (nmErr: any) {\n console.warn(`[start-agent] Could not remove ${nmDir}: ${nmErr.message}`);\n }\n }\n\n await appendAgentLifecycleLog(earlyAgentId, 'agent.work_spawn_requested_after_containers', {\n issueId,\n role,\n workspacePath,\n harness: effectiveHarness,\n });\n emitStartAgentPhase(issueId, 'spawn', 'start', 'starting local work agent after containers became healthy', { workspacePath });\n const activityId = await requestWorkStartAfterContainers({\n args: buildPanStartArgs({\n issueId,\n model: spawnModel,\n harness: effectiveHarness,\n allowHost,\n }),\n workspacePath,\n spawnPanCommand,\n markWorkStartAccepted,\n updateIssueStatus,\n });\n emitStartAgentPhase(issueId, 'spawn', 'success', 'local work agent spawn requested after container startup', {\n workspacePath,\n activityId,\n });\n } catch (err: any) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n emitStartAgentPhase(issueId, 'spawn', 'failure', errorMessage, { workspacePath });\n await appendAgentLifecycleLog(earlyAgentId, 'agent.container_start_failed', {\n issueId,\n error: errorMessage,\n }).catch(() => undefined);\n try {\n saveAgentStateSync(buildContainerStartState({\n agentSessionName: earlyAgentId,\n issueId,\n workspacePath,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n status: 'error',\n startedAt: new Date().toISOString(),\n }));\n } catch { /* non-fatal */ }\n console.error(`[start-agent] Background container startup failed for ${issueId}:`, err);\n }\n })();\n\n yield* Effect.promise(() => Effect.runPromise(eventStore.append({\n type: 'issue.statusChanged',\n timestamp: new Date().toISOString(),\n payload: { issueId, status: 'In Progress', canonicalStatus: 'in_progress' },\n })));\n try { getIssueDataService().patchIssue(issueId, { status: 'In Progress', canonicalStatus: 'in_progress' }); } catch { /* non-fatal */ }\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n message: `Starting containers and agent for ${issueId} (this may take a few minutes)`,\n startingContainers: true,\n containerActivityId,\n agentId: earlyAgentId,\n projectPath,\n guardrails: spawnGuardrails,\n });\n }\n }\n }\n\n return null;\n });\n}\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { dirname, join, sep } from 'node:path';\n\nimport { Effect } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport {\n saveAgentState, determineModel, getProviderAuthMode, getAgentState,\n clearAgentPaused, clearAgentTroubled,\n} from '../../../../lib/agents.js';\nimport { resolveIssueWorkModel } from '../../../../lib/agents/staffing.js';\nimport type { AgentState } from '../../../../lib/agents/agent-state.js';\nimport { operatorInterventionEvent } from '../../../../lib/operator-interventions.js';\nimport { buildChildEnvWithoutTmuxSync } from '../../../../lib/child-env.js';\nimport { checkCodexAuthStatus } from '../../../../lib/codex-auth.js';\nimport { canUseHarnessSync } from '../../../../lib/harness-policy.js';\nimport { emitActivityEntrySync } from '../../../../lib/activity-logger.js';\nimport { appendOperatorInterventionEvent } from '../../../../lib/operator-interventions.js';\nimport { extractPrefixSync, parseIssueIdSync } from '../../../../lib/issue-id.js';\nimport { PAN_CONTINUE_FILENAME, PAN_DIRNAME } from '../../../../lib/pan-dir/types.js';\nimport { loadWorkspaceMetadataSync as loadWorkspaceMetadataFn } from '../../../../lib/remote/workspace-metadata.js';\nimport { getWorkAgentLifecycleState } from '../../../../lib/work-agent-lifecycle.js';\nimport { validateProviderHealth } from '../../../../lib/provider-health.js';\nimport { checkActiveOrderDispatch } from '../../../../lib/orders/dispatch-gate.js';\nimport { OrderDispatchReservationError, withActiveOrderDispatchReservation } from '../../../../lib/orders/dispatch-reservation.js';\nimport type { OrderDispatchEligibility } from '../../../../lib/orders/eligibility.js';\nimport { getProjectSync, resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { clearWorkspaceStuck, getReviewStatusSync } from '../../../../lib/review-status.js';\nimport { isStateMigrated } from '../../../../lib/state-home.js';\nimport { shouldCommitLegacyWorkspaceArtifacts } from '../../../../lib/state-read-home.js';\nimport { isGeneratedGitHookPath, isOverdeckWorkspaceRuntimePath, parsePorcelainStatusPaths } from '../../../../lib/state-plane.js';\nimport { assertWorkspaceStackHealthyForSpawn } from '../../../../lib/agents/spawn-prep.js';\nimport { getWorkspaceStackHealth } from '../../../../lib/workspace/stack-health.js';\nimport { writeAutoStartXBrief } from '../../../../lib/xbrief/auto-synthesize.js';\nimport { findPlan, readPlan } from '../../../../lib/xbrief/io.js';\nimport { transitionXBriefOnMain, updatePlanStatus } from '../../../../lib/xbrief/lifecycle-io.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { ReadModelService } from '../../read-model.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport {\n claimAgentStartPlaceholderProgram,\n rollbackAgentStartPlaceholderProgram,\n} from '../../services/agent-projection.js';\nimport { IssueLifecycle } from '../../services/issue-lifecycle.js';\nimport { getSystemHealthSnapshot } from '../../services/system-health-service.js';\nimport { httpHandler } from '../http-handler.js';\nimport { rejectUnsafeDashboardMutationRequest } from '../dashboard-auth.js';\nimport { sessionExists, killSession } from '../../../../lib/tmux.js';\nimport {\n appendAgentLifecycleLog,\n buildHostOverrideConfirmation,\n buildPanStartArgs,\n emitStartAgentPhase,\n evaluateAgentStartGate,\n evaluateSpawnGuardrails,\n execAsync,\n getIssueDataService,\n getProjectPath,\n invalidateAgentsCache,\n isInternalAgentRequest,\n readJsonBody,\n resolveRequestedStartedBy,\n spawnPanCommandDetached,\n updateRegistryForAgentStart,\n type AgentStartGateDecision,\n} from './shared.js';\nimport { buildAgentStartPlaceholder, handleContainerOrchestration, handleRemoteAgentSpawn } from './spawn-helpers.js';\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport function orderDispatchConflict(decision: OrderDispatchEligibility): {\n status: 409;\n body: { error: string; code: string; conditions: OrderDispatchEligibility['conditions'] };\n} | null {\n if (decision.eligible) return null;\n return {\n status: 409,\n body: {\n error: decision.message ?? 'Order-book dispatch is blocked.',\n code: decision.code ?? 'order-dispatch-blocked',\n conditions: decision.conditions,\n },\n };\n}\n\n/**\n * PAN-2386: emit a dashboard activity event when start-agent refuses to spawn\n * because the workspace is dirty. Kept as a pure function so the refusal path\n * can be unit-tested without booting the full Effect route stack.\n */\nexport function emitDirtyWorkspaceRefusalActivity(issueId: string, porcelain: string): void {\n try {\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'warn',\n message: `Workspace dirty — agent start refused for ${issueId}`,\n issueId,\n details: JSON.stringify({\n reason: 'Workspace has uncommitted changes. Commit, discard, or resolve and retry.',\n porcelain: porcelain.split('\\n').slice(0, 5),\n }),\n });\n } catch { /* non-fatal — activity emit should not block the response */ }\n}\n\n/**\n * True when git porcelain contains only Overdeck-owned workspace runtime files\n * (PAN-3042; shared predicate since PAN-3245) or generated git-hook output — the\n * `.husky/_/pre-rebase` guard that worktree creation writes made every fresh\n * workspace read as dirty and 409 the planning auto-handoff (PAN-3266).\n */\nexport function isOnlyOverdeckRuntimeWorkspaceChanges(porcelain: string): boolean {\n return parsePorcelainStatusPaths(porcelain)\n .every((path) => isOverdeckWorkspaceRuntimePath(path) || isGeneratedGitHookPath(path));\n}\n\nexport function spawnGuardrailResourcesHint(hint?: string): string {\n const resourcesHint = 'Open /resources to inspect Machine Room pressure before retrying.';\n return hint ? `${hint} ${resourcesHint}` : resourcesHint;\n}\n\n// ─── Start-agent gate resolution (PAN-2499) ───────────────────────────────────\n\n/**\n * Evaluate the persistent start gate for an agent and, if the request is\n * operator-origin and `clearGates` is set, clear the paused/troubled gates\n * through the same write paths used by `pan unpause` and `pan untroubled`.\n *\n * Returns the gate decision when the agent is still blocked, or `null` when\n * the agent may proceed. Emits `operator.intervention` events when a gate is\n * actually cleared.\n */\nexport function resolveStartAgentGateForRoute(input: {\n agentSessionName: string;\n issueId: string;\n clearGates: boolean;\n originOk: boolean;\n}): Effect.Effect<AgentStartGateDecision | null, never> {\n let gate: AgentStartGateDecision | null = null;\n\n return Effect.gen(function* () {\n const state = yield* getAgentState(input.agentSessionName);\n gate = evaluateAgentStartGate(input.agentSessionName, state);\n if (!gate) return null;\n\n const shouldClear = input.originOk && input.clearGates;\n if (!shouldClear) return gate;\n if (!state) return gate;\n\n let cleared = false;\n if (state.paused === true) {\n yield* clearAgentPaused(input.agentSessionName);\n yield* Effect.promise(() =>\n appendOperatorInterventionEvent({\n issueId: input.issueId,\n kind: 'unpause',\n source: 'dashboard start-agent',\n }),\n );\n cleared = true;\n }\n\n if (state.troubled === true || (state.consecutiveFailures ?? 0) > 0) {\n yield* clearAgentTroubled(input.agentSessionName);\n yield* Effect.promise(() =>\n appendOperatorInterventionEvent({\n issueId: input.issueId,\n kind: 'untroubled',\n source: 'dashboard start-agent',\n }),\n );\n cleared = true;\n }\n\n if (!cleared) return gate;\n\n gate = evaluateAgentStartGate(input.agentSessionName, yield* getAgentState(input.agentSessionName));\n return gate;\n }).pipe(\n Effect.catch((err) => {\n console.error(`[start-agent] Failed to clear gates for ${input.issueId}: ${err instanceof Error ? err.message : String(err)}`);\n return Effect.succeed(gate);\n }),\n );\n}\n\n/**\n * Clear the persistent start gates immediately before spawning, restoring the\n * complete original state if either clear or the spawn itself fails.\n */\nexport async function spawnAfterClearingStartGates<T>(input: {\n agentSessionName: string;\n gate: AgentStartGateDecision | null;\n initialState: AgentState | null;\n spawn: () => Promise<T>;\n isSuccessful?: (result: T) => boolean;\n}): Promise<T> {\n try {\n if (input.gate?.paused) await Effect.runPromise(clearAgentPaused(input.agentSessionName));\n if (input.gate?.troubled) await Effect.runPromise(clearAgentTroubled(input.agentSessionName));\n const result = await input.spawn();\n if (input.isSuccessful && !input.isSuccessful(result) && input.gate && input.initialState) {\n await Effect.runPromise(saveAgentState(input.initialState));\n }\n return result;\n } catch (error) {\n if (input.gate && input.initialState) {\n await Effect.runPromise(saveAgentState(input.initialState));\n }\n throw error;\n }\n}\n// ─── Route: POST /api/agents (start agent) ───────────────────────────────────\n\nexport const postAgentsRoute = HttpRouter.add(\n 'POST',\n '/api/agents',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const authError = rejectUnsafeDashboardMutationRequest(request);\n if (authError) return authError;\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const lifecycle = yield* IssueLifecycle;\n const readModel = yield* ReadModelService;\n const { issueId, projectId } = body as any;\n const internalRequest = yield* Effect.promise(() => isInternalAgentRequest(request));\n let startedBy: string;\n try { startedBy = resolveRequestedStartedBy((body as any).startedBy, internalRequest); }\n catch (error) { return jsonResponse({ error: error instanceof Error ? error.message : String(error) }, { status: 400 }); }\n const autoStart = (body as any).auto === true;\n const autoSpawnConsentRequired = internalRequest && (body as any).autoSpawnConsentRequired === true;\n const guardrailAcknowledged = (body as any).guardrailAcknowledged === true;\n const offBook = (body as any).offBook === true;\n const requestedHostOverride = (body as any).host === true || (body as any).allowHost === true;\n if (!issueId) {\n return jsonResponse({ error: 'issueId required' }, { status: 400 });\n }\n\n const legacyFields = ['workType', 'phase', 'agentType'].filter((field) => field in (body as Record<string, unknown>));\n if (legacyFields.length > 0) {\n return jsonResponse({\n error: `Legacy start-agent field(s) are no longer accepted: ${legacyFields.join(', ')}. Send role: 'work' instead.`,\n }, { status: 400 });\n }\n\n const role = (body as any).role ?? 'work';\n if (role !== 'work') {\n return jsonResponse({ error: `Unsupported agent role \"${String(role)}\". POST /api/agents only starts role: 'work'.` }, { status: 400 });\n }\n\n // Reject bare numeric IDs (e.g. \"484\") — they have no project prefix, so tracker\n // routing and workspace naming both fail. Require \"PAN-484\" style.\n if (/^\\d+$/.test(String(issueId))) {\n return jsonResponse(\n {\n error: `Invalid issueId \"${issueId}\": bare numeric IDs are not allowed. Use a prefixed ID (e.g. PAN-${issueId}).`,\n hint: 'Issue IDs must include a project prefix (e.g. PAN-484, MIN-123).',\n },\n { status: 422 },\n );\n }\n\n const parsedIssueId = parseIssueIdSync(String(issueId));\n if (!parsedIssueId) {\n return jsonResponse(\n {\n error: `Invalid issueId \"${issueId}\": issue IDs must use a supported project format (e.g. PAN-484, MIN-123).`,\n hint: 'Issue IDs must include a project prefix and numeric identifier.',\n },\n { status: 422 },\n );\n }\n\n const hostOverrideConfirmation = buildHostOverrideConfirmation(String(issueId));\n const allowHost = requestedHostOverride && (body as any).hostOverrideConfirmation === hostOverrideConfirmation;\n if (requestedHostOverride && !allowHost) {\n return jsonResponse({\n success: false,\n error: 'host_override_confirmation_required',\n requiresHostConfirmation: true,\n confirmation: hostOverrideConfirmation,\n hint: `Host override bypasses workspace isolation. Retry only after explicitly confirming: ${hostOverrideConfirmation}`,\n }, { status: 409 });\n }\n\n // Guard: reject starting agents for already-closed issues\n const issueDataService = getIssueDataService();\n const cachedIssues = issueDataService.getIssues();\n const cachedIssue = cachedIssues.find(\n (i: any) => (i.identifier || '').toUpperCase() === issueId.toUpperCase()\n );\n if (cachedIssue && (cachedIssue.canonicalStatus === 'done' || cachedIssue.canonicalStatus === 'canceled')) {\n return jsonResponse(\n {\n error: `Issue ${issueId} is already closed (${cachedIssue.canonicalStatus}). Cannot start an agent for a closed issue.`,\n hint: 'Reopen the issue first if you need to resume work.',\n },\n { status: 422 },\n );\n }\n\n const issueLower = parsedIssueId.normalized;\n const agentSessionName = `agent-${issueLower}`;\n const clearGates = (body as any).clearGates === true;\n const initialAgentState = yield* getAgentState(agentSessionName);\n const startGateBlock = evaluateAgentStartGate(agentSessionName, initialAgentState);\n if (startGateBlock) {\n if (!clearGates) {\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_blocked_gate', {\n issueId,\n paused: startGateBlock.paused,\n troubled: startGateBlock.troubled,\n reason: startGateBlock.error,\n }));\n return jsonResponse(startGateBlock, { status: 409 });\n }\n }\n\n const workspaceMetadata = loadWorkspaceMetadataFn(issueId);\n const isRemote = workspaceMetadata?.location === 'remote';\n\n const issuePrefix = extractPrefixSync(issueId) ?? issueId.split('-')[0];\n const resolvedProject = resolveProjectFromIssueSync(String(issueId));\n const projectConfig = resolvedProject ? getProjectSync(resolvedProject.projectKey) : null;\n const projectPath = projectConfig?.path ?? getProjectPath(projectId, issuePrefix);\n const orderDispatch = yield* Effect.promise(() => checkActiveOrderDispatch(projectPath, issueId, { offBook }));\n const orderConflict = orderDispatchConflict(orderDispatch.decision);\n if (orderConflict) return jsonResponse(orderConflict.body, { status: orderConflict.status });\n\n const workspacePath = join(projectPath, 'workspaces', `feature-${issueLower}`);\n if (!existsSync(workspacePath)) {\n try {\n const nodeDir = dirname(process.execPath);\n yield* Effect.promise(() => execAsync(\n `pan workspace create ${issueId} --local`,\n { cwd: projectPath, encoding: 'utf-8', timeout: 60000, env: buildChildEnvWithoutTmuxSync(process.env, { PATH: `${nodeDir}:${process.env.PATH ?? ''}` }) }\n ));\n } catch (wsErr) {\n return jsonResponse({\n error: `Failed to create workspace for ${issueId}: ${(wsErr as Error).message}`,\n hint: 'Try creating the workspace manually: pan workspace create ' + issueId + ' --local',\n }, { status: 500 });\n }\n }\n\n const workspacePanDir = join(workspacePath, PAN_DIRNAME);\n const workspacePanContinuePath = join(workspacePanDir, PAN_CONTINUE_FILENAME);\n\n let planPath = yield* findPlan(workspacePath);\n if (autoStart && !planPath) {\n const issueTitle = cachedIssue?.title || issueId;\n const issueBody = cachedIssue?.description || '';\n // writeAutoStartXBrief is Effect-returning — yield it directly (PAN-1768).\n yield* writeAutoStartXBrief(projectPath, workspacePath, {\n issueId,\n title: issueTitle,\n body: issueBody,\n url: cachedIssue?.url,\n });\n planPath = yield* findPlan(workspacePath);\n }\n if (!planPath) {\n return jsonResponse({\n error: `No workspace xBRIEF found for ${issueId}. Work agents require a finalized plan.`,\n hint: 'Run planning first, or use auto-start to synthesize a plan before starting the work agent.',\n issueId,\n }, { status: 422 });\n }\n\n const planReadResult = yield* readPlan(planPath).pipe(\n Effect.match({\n onFailure: (planErr) => ({ _tag: 'failure' as const, planErr }),\n onSuccess: (planDoc) => ({ _tag: 'success' as const, planDoc }),\n }),\n );\n if (planReadResult._tag === 'failure') {\n const { planErr } = planReadResult;\n return jsonResponse({\n error: `Could not read workspace xBRIEF for ${issueId}: ${planErr instanceof Error ? planErr.message : String(planErr)}`,\n hint: 'Re-run planning to produce a readable xBRIEF before starting the work agent.',\n issueId,\n }, { status: 422 });\n }\n const { planDoc } = planReadResult;\n\n const planIssueId = planDoc?.plan?.id;\n if (planIssueId && planIssueId.toLowerCase() !== issueLower) {\n return jsonResponse({\n error: `Plan in workspace is for ${planIssueId.toUpperCase()}, not ${issueId}. The workspace contains stale planning artifacts from a different issue.`,\n hint: 'Run planning for this issue first, or clean the workspace planning artifacts.',\n issueId,\n expectedIssue: issueId,\n actualIssue: planIssueId.toUpperCase(),\n }, { status: 422 });\n }\n\n const planItemCount = planDoc?.plan?.items?.length ?? 0;\n if (planItemCount === 0) {\n return jsonResponse({\n error: 'Plan exists but contains no items. Planning may have failed or produced an empty plan.',\n hint: 'Re-run planning to produce a plan with tasks and acceptance criteria.',\n issueId,\n }, { status: 422 });\n }\n\n const health = yield* Effect.promise(() => getSystemHealthSnapshot());\n emitStartAgentPhase(issueId, 'guardrails', 'start', 'evaluating spawn guardrails');\n const spawnGuardrails = evaluateSpawnGuardrails(health);\n if (spawnGuardrails.blocked) {\n emitStartAgentPhase(issueId, 'guardrails', 'failure', spawnGuardrails.error ?? 'guardrails blocked', {\n status: spawnGuardrails.status,\n hint: spawnGuardrails.hint,\n });\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: spawnGuardrails.error,\n hint: spawnGuardrails.hint,\n guardrails: spawnGuardrails,\n }, { status: spawnGuardrails.status });\n }\n if (spawnGuardrails.requiresAcknowledgement && !guardrailAcknowledged) {\n emitStartAgentPhase(issueId, 'guardrails', 'skipped', 'guardrail acknowledgement required', {\n status: spawnGuardrails.status,\n hint: spawnGuardrails.hint,\n });\n return jsonResponse({\n success: false,\n blocked: false,\n skipped: true,\n requiresAcknowledgement: true,\n hint: spawnGuardrailResourcesHint(spawnGuardrails.hint),\n guardrails: spawnGuardrails,\n }, { status: spawnGuardrails.status });\n }\n emitStartAgentPhase(issueId, 'guardrails', 'success', 'spawn guardrails passed');\n\n let spawnModel: string;\n try {\n // PAN-3022: no explicit body model → honor the per-issue work-model\n // override (record.workModel, PAN-2997 issue-override tier) before the\n // role default — same resolution order as `pan start`. Without this the\n // route resolves the role default and the `pan start --model` child then\n // persists it, clobbering the stored override.\n spawnModel = determineModel({\n model: (body as any).model ?? resolveIssueWorkModel(issueId),\n role,\n spawnKey: `${role}:${issueId}`,\n });\n } catch (err) {\n return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });\n }\n const providerAuthMode = yield* Effect.promise(() => getProviderAuthMode(spawnModel));\n if (providerAuthMode === 'subscription') {\n const codexAuth = yield* checkCodexAuthStatus();\n if (codexAuth.status === 'expired' || codexAuth.status === 'burned') {\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: `Codex authentication ${codexAuth.status}. GPT subscription agents cannot spawn with expired/burned tokens.`,\n hint: 'Click \"Re-authenticate\" in the Codex auth banner or Settings page to refresh your OpenAI subscription tokens.',\n }, { status: 429 });\n }\n }\n\n // Pre-flight provider health check — detect quota/auth/network errors\n // before spawning the agent into Claude Code's opaque retry loop.\n // validateProviderHealth returns an Effect (typed ProviderHealthError\n // channel) — wrapping it in Effect.promise handed a non-thenable to the\n // runtime and crashed the whole request (PAN-1768).\n const providerHealthCheck = yield* validateProviderHealth(spawnModel).pipe(\n Effect.match({\n onFailure: (err) => ({ _tag: 'failure' as const, err }),\n onSuccess: () => ({ _tag: 'success' as const, err: null }),\n }),\n );\n if (providerHealthCheck._tag === 'failure' && providerHealthCheck.err) {\n const err = providerHealthCheck.err;\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: err.message,\n hint: err.probeResult.kind === 'quota'\n ? 'Top up your credits on the provider dashboard, or switch this agent to a different model.'\n : err.probeResult.kind === 'auth'\n ? 'Check your API key in Settings → Providers.'\n : 'The provider may be temporarily unavailable. Try again later or switch models.',\n providerHealth: {\n provider: err.provider.name,\n model: err.model,\n kind: err.probeResult.kind,\n status: err.probeResult.status,\n },\n }, { status: 429 });\n }\n\n if (!isRemote) {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'start', 'checking workspace docker stack health', { workspacePath });\n let stackHealth = yield* getWorkspaceStackHealth(issueId, { projectConfig, workspacePath });\n if (!stackHealth.healthy && !allowHost) {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'start', 'stack unhealthy — attempting workspace stack rebuild', { workspacePath });\n yield* Effect.promise(() =>\n assertWorkspaceStackHealthyForSpawn(issueId, 'work', false, workspacePath).catch(() => undefined));\n stackHealth = yield* getWorkspaceStackHealth(issueId, { projectConfig, workspacePath });\n }\n if (!stackHealth.healthy) {\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_blocked_stack_unhealthy', {\n issueId,\n reasons: stackHealth.reasons,\n lastObserved: stackHealth.lastObserved,\n }));\n if (!allowHost) {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'failure', stackHealth.reasons.join('; '), {\n workspacePath,\n lastObserved: stackHealth.lastObserved,\n });\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'error',\n issueId: issueId.toUpperCase(),\n message: `agent-spawn-blocked-stack-unhealthy: ${issueId.toUpperCase()}`,\n details: stackHealth.reasons.join('; '),\n });\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: `Workspace docker stack for ${issueId} is not healthy: ${stackHealth.reasons.join('; ')}`,\n hint: `Run 'pan workspace rebuild ${issueId}' or use the CLI break-glass path: pan start ${issueId} --host.`,\n stackHealth,\n }, { status: 422 });\n }\n emitStartAgentPhase(issueId, 'stackHealthGate', 'skipped', 'stack unhealthy but host override confirmed', {\n workspacePath,\n reasons: stackHealth.reasons,\n });\n } else {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'success', 'workspace docker stack healthy', { workspacePath });\n }\n } else {\n emitStartAgentPhase(issueId, 'stackHealthGate', 'skipped', 'remote workspace skips local stack-health gate', { workspacePath });\n }\n\n if (allowHost) {\n // PAN-1556: host-override is a spawn detail, not user-facing activity.\n console.warn(`[agents] agent-spawn-host-override: ${issueId.toUpperCase()} (dashboard-confirmed)`);\n }\n\n const migratedState = projectConfig ? yield* Effect.promise(() => isStateMigrated(projectConfig)) : false;\n if (shouldCommitLegacyWorkspaceArtifacts(migratedState) && (existsSync(workspacePanContinuePath) || existsSync(workspacePanDir))) {\n // Commit workspace orchestration artifacts before handing off to the work agent.\n // The entire block is best-effort — never let git errors abort the agent start.\n yield* Effect.gen(function* () {\n const gitRoot = workspacePath;\n if (existsSync(join(gitRoot, PAN_DIRNAME))) {\n // PAN-1819: use plain git add (never -f) and exclude workspace-state/sync-target paths.\n yield* Effect.promise(() => execAsync(`git add .pan/`, { cwd: gitRoot, encoding: 'utf-8' }));\n yield* Effect.promise(() => execAsync(\n `git reset HEAD -- .pan/kickoff.md .pan/continue.json .pan/handoff-*.md .pan/spec.vbrief.json`,\n { cwd: gitRoot, encoding: 'utf-8' },\n ));\n }\n // git diff --cached --quiet exits 1 when there ARE staged changes (normal).\n // Handle exit-1 in the Promise so it never becomes an Effect failure.\n const diffResult = yield* Effect.promise(() =>\n execAsync(`git diff --cached --quiet`, { cwd: gitRoot, encoding: 'utf-8' })\n .then(() => false)\n .catch(() => true)\n );\n if (diffResult) {\n yield* Effect.promise(() => execAsync(`git commit -m \"chore: planning artifacts for ${issueId} before agent start\"`, { cwd: gitRoot, encoding: 'utf-8' }));\n const pushChild = spawn('git', ['push'], { cwd: gitRoot, detached: true, stdio: 'ignore' });\n pushChild.unref();\n }\n }).pipe(Effect.catch(() => Effect.void));\n }\n\n let gatesCommitted = false;\n const commitClearedGates = async (): Promise<void> => {\n if (!startGateBlock || gatesCommitted) return;\n gatesCommitted = true;\n if (startGateBlock.paused) {\n await Effect.runPromise(eventStore.appendAsync(operatorInterventionEvent({ issueId, kind: 'unpause', source: 'dashboard' })));\n }\n if (startGateBlock.troubled) {\n await Effect.runPromise(eventStore.appendAsync(operatorInterventionEvent({ issueId, kind: 'untroubled', source: 'dashboard' })));\n }\n await appendAgentLifecycleLog(agentSessionName, 'agent.start_gates_cleared', {\n issueId,\n paused: startGateBlock.paused,\n troubled: startGateBlock.troubled,\n });\n };\n let workStartAccepted = false;\n const markWorkStartAccepted = async (): Promise<void> => {\n if (workStartAccepted) return;\n workStartAccepted = true;\n await Effect.runPromise(transitionXBriefOnMain(\n projectPath,\n issueId,\n 'active',\n 'running',\n `chore(state): start ${issueId.toUpperCase()} xBRIEF (status=running)`,\n ).pipe(\n Effect.match({\n onSuccess: (result) => {\n if (result.moved) {\n console.log(`[start-agent] xBRIEF moved ${result.fromDir} → active for ${issueId}`);\n }\n if (result.statusUpdated) {\n console.log(`[start-agent] Set plan.status=running for ${issueId}`);\n }\n if (result.committed) {\n console.log(`[start-agent] Committed running transition for ${issueId}`);\n }\n },\n onFailure: (err) => {\n console.warn(`[start-agent] xBRIEF running transition failed (non-fatal): ${err?.message ?? err}`);\n },\n }),\n ));\n\n if (planPath.startsWith(workspacePath + sep)) {\n try {\n updatePlanStatus(planPath, 'running');\n console.log(`[start-agent] Set plan.status=running for ${issueId}`);\n } catch (planStatusErr: any) {\n console.warn(`[start-agent] Failed to set plan.status=running (non-fatal): ${planStatusErr?.message ?? planStatusErr}`);\n }\n }\n\n try {\n const { appendSessionEntry, getProjectConfigFromWorkspacePath, resolveProjectForIssue } =\n await import('../../../../lib/pan-dir/record.js');\n const recordProject = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(workspacePath);\n await appendSessionEntry(recordProject, issueId, {\n timestamp: new Date().toISOString(),\n reason: 'start',\n agentModel: spawnModel,\n });\n console.log(`[start-agent] Wrote start session entry to record for ${issueId}`);\n } catch (continueErr: any) {\n console.warn(`[start-agent] Failed to write start entry to record (non-fatal): ${continueErr?.message ?? continueErr}`);\n }\n\n const pipelineStatus = getReviewStatusSync(issueId);\n if (pipelineStatus?.stuckReason === 'planning_auto_handoff_failed') {\n clearWorkspaceStuck(issueId);\n }\n };\n if (isRemote && workspaceMetadata) {\n const admitted = yield* Effect.promise(() => withActiveOrderDispatchReservation(\n projectPath,\n issueId,\n { offBook, recordOverride: true },\n () => spawnAfterClearingStartGates({\n agentSessionName,\n gate: startGateBlock,\n initialState: initialAgentState,\n spawn: () => Effect.runPromise(handleRemoteAgentSpawn({\n issueId,\n workspacePath,\n workspaceMetadata,\n spawnModel,\n startedBy,\n autoSpawnConsentRequired,\n projectPath,\n spawnGuardrails,\n lifecycle,\n })),\n isSuccessful: (remoteResponse) => remoteResponse.status >= 200 && remoteResponse.status < 300,\n }),\n ));\n const admittedConflict = orderDispatchConflict(admitted.check.decision);\n if (admittedConflict) return jsonResponse(admittedConflict.body, { status: admittedConflict.status });\n const response = admitted.result!;\n if (response.status < 200 || response.status >= 300) return response;\n yield* Effect.promise(commitClearedGates);\n yield* Effect.promise(markWorkStartAccepted);\n return response;\n }\n // Local workspace\n const devScript = join(workspacePath, 'dev');\n const hasPlanning = existsSync(join(workspacePath, PAN_DIRNAME));\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_requested', {\n issueId,\n workspacePath,\n hasPlanning,\n role,\n }));\n\n const agentLifecycle = yield* getWorkAgentLifecycleState(agentSessionName);\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_lifecycle_evaluated', {\n issueId,\n lifecycle: agentLifecycle,\n }));\n if (!agentLifecycle.canStartFresh) {\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_blocked', {\n issueId,\n reason: agentLifecycle.reason,\n lifecycle: agentLifecycle,\n }));\n return jsonResponse({\n error: agentLifecycle.reason || `Cannot start agent for ${issueId}`,\n lifecycle: agentLifecycle,\n }, { status: 409 });\n }\n\n yield* Effect.gen(function* () {\n const exists = yield* sessionExists(agentSessionName);\n if (exists) yield* killSession(agentSessionName);\n console.log(`[start-agent] Killed stale tmux session ${agentSessionName}`);\n }).pipe(Effect.catch(() => Effect.void));\n\n // PAN-1531: dirty-worktree refusal replaces silent pre-spawn stashing.\n // If the workspace has uncommitted changes the route returns 409 with the\n // diff so the dashboard can present the user three explicit choices:\n // Commit / Discard (typed confirmation required) / Stash as salvageable.\n // Clients that have already resolved the dirtiness MUST pass\n // `acknowledgeDirtyWorkspace: true` to bypass this gate (typically after\n // the user clicked one of the three modal buttons).\n const acknowledgeDirtyWorkspace = (body as any).acknowledgeDirtyWorkspace === true;\n if (!acknowledgeDirtyWorkspace) {\n try {\n const { stdout: statusOut } = yield* Effect.promise(() => execAsync('git status --porcelain --untracked-files=all', {\n cwd: workspacePath,\n encoding: 'utf-8',\n }));\n if (statusOut.trim() && !isOnlyOverdeckRuntimeWorkspaceChanges(statusOut)) {\n const { stdout: diffOut } = yield* Effect.promise(() => execAsync('git diff HEAD --stat', {\n cwd: workspacePath,\n encoding: 'utf-8',\n }).catch(() => ({ stdout: '' })));\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_refused_dirty_workspace', {\n issueId,\n workspacePath,\n porcelain: statusOut.trim(),\n }));\n // PAN-2386: surface the refusal in the dashboard activity feed so the operator\n // sees a toast instead of having to dig through lifecycle.log.\n emitDirtyWorkspaceRefusalActivity(issueId, statusOut.trim());\n return jsonResponse({\n error: `Workspace ${workspacePath} has uncommitted changes. Choose an action and retry start with acknowledgeDirtyWorkspace=true.`,\n code: 'WORKSPACE_DIRTY',\n workspacePath,\n porcelain: statusOut.trim(),\n diffStat: diffOut.trim(),\n actions: ['commit', 'discard', 'stash-salvage'],\n }, { status: 409 });\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.warn(`[start-agent] Failed to check workspace status for ${issueId}: ${message}`);\n }\n }\n\n // PAN-1048 review feedback 003: the route only resolves harness when the\n // dashboard launch panel explicitly chose one. Otherwise pass nothing and\n // let pan start → spawnAgent resolve from roles.work.harness (the new\n // single source of truth for per-role harness). The legacy `phase`\n // variable and the workType/harnessOverrides map are gone — the\n // legacy-field guard above (line 1872) blocks any client still sending\n // them. Note: when bodyHarness is set we still run it through\n // canUseHarness() so we can fail fast on a model+harness incompatibility\n // before spawning the subprocess.\n const bodyHarness = (body as any).harness;\n const userPickedHarness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null =\n bodyHarness === 'ohmypi' || bodyHarness === 'claude-code' || bodyHarness === 'codex' || bodyHarness === 'acp' || bodyHarness === 'kimi-code' ? bodyHarness : null;\n let effectiveHarness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | null = null;\n if (userPickedHarness !== null) {\n const harnessDecision = yield* Effect.promise(async () =>\n canUseHarnessSync(userPickedHarness, spawnModel, await getProviderAuthMode(spawnModel))\n );\n // PAN-1837 review fix (NFR-2): an explicitly requested harness that\n // policy denies must fail loudly, not silently substitute claude-code —\n // {harness: 'kimi-code', model: 'claude-sonnet-5'} was previously\n // accepted and spawned claude-code instead of returning the policy\n // reason.\n if (!harnessDecision.allowed) {\n return jsonResponse({ error: harnessDecision.reason ?? `Harness \"${userPickedHarness}\" is not allowed for model \"${spawnModel}\".` }, { status: 400 });\n }\n effectiveHarness = userPickedHarness;\n }\n\n // Spawn pan start command\n const spawnPanCommand = async (args: string[], cwd?: string): Promise<string> => {\n const admitted = await withActiveOrderDispatchReservation(\n projectPath,\n issueId,\n { offBook, recordOverride: false },\n () => spawnAfterClearingStartGates({\n agentSessionName,\n gate: gatesCommitted ? null : startGateBlock,\n initialState: initialAgentState,\n spawn: () => spawnPanCommandDetached({\n agentSessionName,\n issueId,\n role,\n workspacePath,\n args,\n cwd,\n env: {\n OVERDECK_AGENT_STARTED_BY: startedBy,\n OVERDECK_AUTO_SPAWN_CONSENT_REQUIRED: autoSpawnConsentRequired ? '1' : '0',\n },\n }),\n }),\n );\n if (!admitted.check.decision.eligible || !admitted.result) {\n throw new OrderDispatchReservationError(admitted.check);\n }\n await commitClearedGates();\n return admitted.result;\n };\n\n // Use IssueLifecycle service to transition issue to \"In Progress\" (PAN-449)\n const updateIssueStatus = async () => {\n await Effect.runPromise(\n lifecycle.transitionTo(issueId, 'in_progress').pipe(Effect.catch(() => Effect.void))\n );\n };\n\n const containerResponse = yield* handleContainerOrchestration({\n issueId,\n workspacePath,\n devScript,\n agentSessionName,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n spawnModel,\n spawnGuardrails,\n projectPath,\n eventStore,\n spawnPanCommand,\n markWorkStartAccepted,\n updateIssueStatus,\n });\n if (containerResponse) return containerResponse;\n\n // Containers already ready or no containers needed. Claim the spawn before\n // launching `pan start`: two requests can pass the lifecycle read together,\n // but only one may atomically write the starting placeholder.\n const placeholderStartedAt = new Date().toISOString();\n const { state: placeholderState, event: placeholderEvent } = buildAgentStartPlaceholder({\n agentSessionName,\n issueId,\n workspacePath,\n role,\n effectiveHarness,\n startedBy,\n allowHost,\n startedAt: placeholderStartedAt,\n });\n const hasLiveTmuxSession = yield* sessionExists(agentSessionName).pipe(\n Effect.catch(() => Effect.succeed(true)),\n );\n const placeholderClaim = yield* claimAgentStartPlaceholderProgram(\n placeholderState,\n placeholderEvent,\n hasLiveTmuxSession,\n );\n if (!placeholderClaim.claimed) {\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_placeholder_blocked', {\n issueId,\n role,\n workspacePath,\n reason: placeholderClaim.reason,\n }));\n return jsonResponse({\n error: `Agent ${agentSessionName} is already starting or running.`,\n code: 'AGENT_START_IN_FLIGHT',\n }, { status: 409 });\n }\n\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_placeholder_created', {\n issueId,\n role,\n workspacePath,\n startedAt: placeholderStartedAt,\n }));\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.work_spawn_requested', {\n issueId,\n role,\n workspacePath,\n }));\n\n let activityId: string;\n try {\n emitStartAgentPhase(issueId, 'spawn', 'start', 'starting local work agent', { workspacePath });\n activityId = yield* Effect.promise(() => spawnPanCommand(\n buildPanStartArgs({\n issueId,\n model: spawnModel,\n harness: effectiveHarness,\n allowHost,\n offBook,\n }),\n workspacePath,\n ));\n yield* Effect.promise(markWorkStartAccepted);\n emitStartAgentPhase(issueId, 'spawn', 'success', 'local work agent spawn requested', {\n workspacePath,\n activityId,\n });\n } catch (error: any) {\n const initialStateIsPlaceholder = initialAgentState?.model.startsWith('pending-') === true;\n const fallbackState: AgentState = initialAgentState && !initialStateIsPlaceholder\n ? { ...initialAgentState }\n : {\n ...placeholderState,\n model: spawnModel,\n status: 'stopped',\n stoppedAt: new Date().toISOString(),\n };\n const rolledBack = yield* rollbackAgentStartPlaceholderProgram(placeholderState, fallbackState, {\n type: 'agent.status_changed',\n timestamp: new Date().toISOString(),\n payload: {\n agentId: agentSessionName,\n status: fallbackState.status,\n previousStatus: 'starting',\n hasLiveTmuxSession: false,\n },\n });\n yield* Effect.promise(() => appendAgentLifecycleLog(agentSessionName, 'agent.start_placeholder_rollback', {\n issueId,\n rolledBack,\n fallbackStatus: fallbackState.status,\n }));\n invalidateAgentsCache();\n\n if (error instanceof OrderDispatchReservationError) {\n const conflict = orderDispatchConflict(error.check.decision)!;\n return jsonResponse(conflict.body, { status: conflict.status });\n }\n const output = String(error?.output ?? error?.message ?? '');\n if (output.includes(`Workspace docker stack for ${issueId}`) && output.includes('is not healthy')) {\n const failedStackHealth = yield* getWorkspaceStackHealth(issueId, { projectConfig, workspacePath });\n emitStartAgentPhase(issueId, 'stackHealthGate', 'failure', failedStackHealth.reasons.length > 0 ? failedStackHealth.reasons.join('; ') : output.trim(), {\n workspacePath,\n activityId: error?.activityId,\n });\n emitStartAgentPhase(issueId, 'spawn', 'failure', output.trim() || `Failed to start agent for ${issueId}`, {\n workspacePath,\n activityId: error?.activityId,\n });\n emitActivityEntrySync({\n source: 'dashboard',\n level: 'error',\n issueId: issueId.toUpperCase(),\n message: `agent-spawn-blocked-stack-unhealthy: ${issueId.toUpperCase()}`,\n details: failedStackHealth.reasons.length > 0 ? failedStackHealth.reasons.join('; ') : output.trim(),\n });\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: failedStackHealth.reasons.length > 0\n ? `Workspace docker stack for ${issueId} is not healthy: ${failedStackHealth.reasons.join('; ')}`\n : output.trim(),\n hint: `Run 'pan workspace rebuild ${issueId}' or use the CLI break-glass path: pan start ${issueId} --host.`,\n stackHealth: failedStackHealth,\n activityId: error?.activityId,\n }, { status: 422 });\n }\n emitStartAgentPhase(issueId, 'spawn', 'failure', output.trim() || `Failed to start agent for ${issueId}`, {\n workspacePath,\n activityId: error?.activityId,\n });\n return jsonResponse({\n success: false,\n blocked: true,\n skipped: true,\n error: output.trim() || `Failed to start agent for ${issueId}`,\n activityId: error?.activityId,\n }, { status: 500 });\n }\n\n updateRegistryForAgentStart(issueId, workspacePath, agentSessionName);\n yield* Effect.promise(() => updateIssueStatus());\n try { getIssueDataService().patchIssue(issueId, { status: 'In Progress', canonicalStatus: 'in_progress' }); } catch { /* non-fatal */ }\n invalidateAgentsCache();\n return jsonResponse({\n success: true,\n message: `Starting agent for ${issueId}`,\n activityId,\n projectPath,\n guardrails: spawnGuardrails,\n });\n })),\n);\n","/**\n * Agents route barrel — Effect HttpRouter.Layer (PAN-428 B7)\n *\n * Composes all /api/agents/* route layers and preserves the public helper\n * exports that external callers historically imported from routes/agents.js.\n */\n\nimport { Layer } from 'effect';\n\nimport {\n getAgentsRoute,\n getAgentGitInfoRoute,\n getAgentTmuxAliveRoute,\n getAgentHasSessionRoute,\n} from './agents/listing.js';\nimport {\n getAgentOutputRoute,\n getAgentConversationRoute,\n getAgentActivityRoute,\n getAgentFilesRoute,\n getAgentTimelineRoute,\n} from './agents/conversation.js';\nimport {\n postAgentMessageRoute,\n postAgentTellRoute,\n postAgentPokeRoute,\n} from './agents/messaging.js';\nimport {\n getAgentPendingQuestionsRoute,\n getAgentPaneChoiceRoute,\n postAgentPaneChoiceRoute,\n postAgentAnswerQuestionRoute,\n postAgentPlanActionRoute,\n postInternalAgentPermissionRequestRoute,\n postAgentPermissionResponseRoute,\n} from './agents/permissions.js';\nimport {\n getAgentHealthHistoryRoute,\n postAgentHeartbeatRoute,\n postAgentWorkCompleteRoute,\n postAgentStuckRoute,\n postAgentClassifyCompletionRoute,\n postAgentPlanChecklistRoute,\n getAgentRuntimeRoute,\n} from './agents/runtime-events.js';\nimport {\n deleteAgentRoute,\n postAgentStopRoute,\n postAgentSuspendRoute,\n postAgentPauseRoute,\n postAgentUnpauseRoute,\n postAgentUntroubledRoute,\n} from './agents/lifecycle-stop.js';\nimport {\n postAgentResumeRoute,\n postAgentRecoverRoute,\n postAgentRestartRoute,\n postAgentRestartFreshRoute,\n postAgentsRestartAllRoute,\n postAgentResetSessionRoute,\n getAgentsRestartConfigRoute,\n postAgentsRestartWithConfigRoute,\n} from './agents/lifecycle-restart.js';\nimport {\n getAgentCloisterHealthRoute,\n getAgentHandoffSuggestionRoute,\n postAgentHandoffRoute,\n getAgentCostRoute,\n postAgentDeliveryMethodRoute,\n postAgentSwitchModelRoute,\n} from './agents/control.js';\nimport { postAgentsRoute } from './agents/spawn.js';\n\nexport {\n buildPanStartArgs,\n spawnPanCommandDetached,\n validateAgentRuntimeEventAuth,\n invalidateAgentsCache,\n evaluateAgentStartGate,\n hasActiveAgentGateOrRetry,\n evaluateSpawnGuardrails,\n} from './agents/shared.js';\n\nexport type {\n SpawnGuardrailDecision,\n AgentStartGateDecision,\n} from './agents/shared.js';\n\nexport {\n agentHasResolvableWorkspace,\n UNRESOLVABLE_AGENT_GIT_INFO,\n} from './agents/listing.js';\nexport { buildConversationResponse } from './agents/conversation.js';\nexport { validateAgentMessageOrigin } from './agents/messaging.js';\nexport { createAgentStopHandler } from './agents/lifecycle-stop.js';\nexport { validateAgentDeliveryMethodOrigin } from './agents/control.js';\n\nexport const agentsRouteLayer = Layer.mergeAll(\n getAgentsRoute,\n getAgentOutputRoute,\n getAgentConversationRoute,\n postAgentMessageRoute,\n postAgentTellRoute,\n deleteAgentRoute,\n postAgentStopRoute,\n getAgentHealthHistoryRoute,\n postAgentPokeRoute,\n getAgentPendingQuestionsRoute,\n getAgentPaneChoiceRoute,\n postAgentPaneChoiceRoute,\n postAgentAnswerQuestionRoute,\n postAgentPlanActionRoute,\n postAgentHeartbeatRoute,\n postAgentWorkCompleteRoute,\n postAgentStuckRoute,\n postAgentClassifyCompletionRoute,\n postAgentPlanChecklistRoute,\n postInternalAgentPermissionRequestRoute,\n postAgentPermissionResponseRoute,\n getAgentRuntimeRoute,\n getAgentGitInfoRoute,\n getAgentActivityRoute,\n getAgentFilesRoute,\n getAgentTimelineRoute,\n postAgentSuspendRoute,\n postAgentPauseRoute,\n postAgentUnpauseRoute,\n postAgentUntroubledRoute,\n postAgentResumeRoute,\n postAgentRecoverRoute,\n postAgentRestartRoute,\n getAgentCloisterHealthRoute,\n getAgentHandoffSuggestionRoute,\n postAgentHandoffRoute,\n getAgentCostRoute,\n postAgentsRoute,\n postAgentsRestartAllRoute,\n getAgentTmuxAliveRoute,\n getAgentHasSessionRoute,\n postAgentResetSessionRoute,\n postAgentSwitchModelRoute,\n postAgentRestartFreshRoute,\n postAgentDeliveryMethodRoute,\n getAgentsRestartConfigRoute,\n postAgentsRestartWithConfigRoute,\n);\n\nexport default agentsRouteLayer;\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { decideEscalation, type EscalationAction } from '../../../lib/agents/tier-escalation.js';\nimport { resolveTieredExecutionEnabled } from '../../../lib/agents/tier-table.js';\nimport { loadConfigSync } from '../../../lib/config-yaml.js';\nimport { resolveProjectFromIssueSync } from '../../../lib/projects.js';\nimport { getReviewStatusSync } from '../../../lib/review-status.js';\nimport {\n readTierOverrides,\n readWorkspacePlanSync,\n recordTierPromotion,\n} from '../../../lib/xbrief/io.js';\n\nexport interface TieredInspectFailureEscalationDeps {\n loadConfig?: typeof loadConfigSync;\n resolveProject?: typeof resolveProjectFromIssueSync;\n exists?: typeof existsSync;\n readPlan?: typeof readWorkspacePlanSync;\n readOverrides?: typeof readTierOverrides;\n decide?: typeof decideEscalation;\n recordPromotion?: typeof recordTierPromotion;\n}\n\nexport function handleTieredInspectFailureEscalation(\n issueId: string,\n notes: string | undefined,\n deps: TieredInspectFailureEscalationDeps = {},\n): EscalationAction | null {\n const loadConfig = deps.loadConfig ?? loadConfigSync;\n const resolveProject = deps.resolveProject ?? resolveProjectFromIssueSync;\n const exists = deps.exists ?? existsSync;\n const readPlan = deps.readPlan ?? readWorkspacePlanSync;\n const readOverrides = deps.readOverrides ?? readTierOverrides;\n const decide = deps.decide ?? decideEscalation;\n const recordPromotion = deps.recordPromotion ?? recordTierPromotion;\n\n const tiered = loadConfig().config.tieredExecution;\n if (!tiered.escalation.enabled) return null;\n\n const project = resolveProject(issueId);\n if (!project) return null;\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${issueId.toLowerCase()}`,\n );\n if (!exists(workspacePath)) return null;\n\n const doc = readPlan(workspacePath);\n if (!resolveTieredExecutionEnabled(tiered, doc?.plan.metadata)) return null;\n\n const taskId = notes?.match(/[Bb]ead\\s+(\\S+)/)?.[1];\n if (!doc || !taskId) return null;\n const item = doc.plan.items.find(candidate => candidate.id === taskId);\n if (!item) return null;\n\n const overrides = readOverrides(workspacePath);\n const decision = decide({\n kind: 'supervisor-blocked',\n itemId: taskId,\n sha: getReviewStatusSync(issueId)?.reviewedAtCommit ?? 'unknown',\n attemptsAtCurrentTier: tiered.escalation.retries_at_tier,\n }, item, tiered.escalation, overrides);\n\n if (decision.action === 'promote') {\n recordPromotion(workspacePath, taskId, decision.from, decision.to, decision.reason);\n }\n\n return decision;\n}\n\nexport async function reportTieredInspectFailureEscalation(\n issueId: string,\n notes: string | undefined,\n deps: TieredInspectFailureEscalationDeps = {},\n): Promise<void> {\n try {\n const decision = handleTieredInspectFailureEscalation(issueId, notes, deps);\n if (decision) {\n console.log(`[specialists/done] Tier escalation decision for ${issueId}: ${decision.action}`);\n }\n } catch (err) {\n console.warn(`[specialists/done] Tier escalation handling failed for ${issueId}: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n","import { exec, execFile } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\n\nimport { Effect } from 'effect';\nimport { HttpServerRequest } from 'effect/unstable/http';\n\nimport {\n type AgentRuntimeState,\n type AgentState,\n} from '../../../../lib/agents.js';\nimport { createInFlightGuard } from '../../../../lib/cloister/in-flight-guard.js';\nimport type { VerifiedMergedRepo } from '../../../../lib/cloister/merge-verification.js';\nimport { parseIssueIdSync } from '../../../../lib/issue-id.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\n\nexport const execAsync = promisify(exec);\nexport const execFileAsync = promisify(execFile);\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nexport type SpecialistAgentName = 'merge-agent' | 'review-agent' | 'test-agent' | 'inspect-agent' | 'uat-agent';\nexport type ProjectSpecialistAgentName = 'review-agent' | 'test-agent' | 'merge-agent';\nexport type SpecialistEventRole = 'review' | 'test' | 'ship';\n\nexport function validateSpecialistAgentName(type: string): type is ProjectSpecialistAgentName {\n return type === 'review-agent' || type === 'test-agent' || type === 'merge-agent';\n}\n\nexport function specialistEventRole(name: string): SpecialistEventRole | undefined {\n if (name === 'review' || name === 'review-agent') return 'review';\n if (name === 'test' || name === 'test-agent') return 'test';\n if (name === 'merge' || name === 'merge-agent') return 'ship';\n return undefined;\n}\n\nexport function specialistNameForRole(role: string | undefined): ProjectSpecialistAgentName | null {\n const baseRole = role?.split('.')[0];\n if (baseRole === 'review') return 'review-agent';\n if (baseRole === 'test') return 'test-agent';\n if (baseRole === 'ship') return 'merge-agent';\n return null;\n}\n\nexport type SpecialistAutoCompleteBody = {\n agentId?: string;\n issueId?: string;\n role?: string;\n sessionId?: string | null;\n status?: string;\n notes?: string;\n};\n\nexport function validateSpecialistAutoCompleteMetadata(\n name: string,\n body: SpecialistAutoCompleteBody,\n agentState: AgentState | null,\n runtimeState: AgentRuntimeState | null,\n): { ok: true } | { ok: false; status: number; error: string } {\n if (!validateSpecialistAgentName(name)) {\n return { ok: false, status: 400, error: `Invalid specialist name: ${name}` };\n }\n if (!body.issueId || !body.status) {\n return { ok: false, status: 400, error: 'issueId and status required' };\n }\n if (body.status !== 'passed' && body.status !== 'failed') {\n return { ok: false, status: 400, error: 'status must be passed or failed' };\n }\n if (!body.agentId || !body.role) {\n return { ok: false, status: 400, error: 'agentId and role required' };\n }\n\n const normalizedIssueId = body.issueId.toUpperCase();\n const expectedName = specialistNameForRole(body.role);\n if (expectedName !== name) {\n return { ok: false, status: 403, error: 'role does not match specialist' };\n }\n if (!agentState || agentState.id !== body.agentId) {\n return { ok: false, status: 403, error: 'agent run not found' };\n }\n if (agentState.issueId.toUpperCase() !== normalizedIssueId) {\n return { ok: false, status: 403, error: 'agent issue does not match request' };\n }\n if (specialistNameForRole(agentState.role) !== name) {\n return { ok: false, status: 403, error: 'agent role does not match specialist' };\n }\n if (agentState.status !== 'running' && agentState.status !== 'starting') {\n return { ok: false, status: 409, error: 'agent run is not active' };\n }\n if (runtimeState?.currentIssue && runtimeState.currentIssue.toUpperCase() !== normalizedIssueId) {\n return { ok: false, status: 403, error: 'runtime issue does not match request' };\n }\n if (runtimeState?.claudeSessionId && body.sessionId !== runtimeState.claudeSessionId) {\n return { ok: false, status: 403, error: 'session does not match active run' };\n }\n\n return { ok: true };\n}\n\n// Read the request body as unknown JSON\nexport const readJsonBody = Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const text = yield* request.text;\n try {\n return text ? JSON.parse(text) : {};\n } catch {\n return {};\n }\n});\n\n// ─── Idempotency guard: prevent concurrent postMergeLifecycle re-entry ────────\n// PAN-328: the loop specialists/done → onMergeComplete → postMergeLifecycle →\n// (re-trigger) → specialists/done once burned 24,626 tracker API calls. The\n// in-flight guard makes a second *concurrent* call for the same issue a no-op.\n// The invariant is enforced by tests/unit/lib/cloister/in-flight-guard.test.ts\n// — delete the guard and that suite goes red.\nconst postMergeGuard = createInFlightGuard();\n\n// Track issues where the server is managing the merge lifecycle (polyrepo).\n// Exported so the workspaces route can register/unregister server-managed merges.\nexport const _serverManagedMerges = new Set<string>();\n\n/**\n * Exported for the UAT batch-promote route (PAN-1737): batch promotion fans\n * out the per-member post-merge through THIS guard instance so an issue's\n * lifecycle still runs at most once regardless of which path merged it.\n * Returns false when a run for the issue is already in flight.\n */\nexport interface FirePostMergeLifecycleOptions {\n sourceBranch?: string;\n verifiedMergedRef?: string;\n /**\n * Per-repo merge evidence from a polyrepo batch promotion (PAN-3093). It must\n * be forwarded: a polyrepo member's single-ref evidence is a composite anchor\n * checked against origin/main in the WRAPPER path, which is not a git repo,\n * so dropping this silently refuses every member's post-merge transition even\n * though all repos landed.\n */\n verifiedMergedRepos?: readonly VerifiedMergedRepo[];\n}\n\nexport function firePostMergeLifecycle(issueId: string, options?: FirePostMergeLifecycleOptions): boolean {\n const parsedIssueId = parseIssueIdSync(issueId.trim());\n if (!parsedIssueId) {\n console.error(`[merge] Refusing post-merge lifecycle for invalid issue ID: ${issueId}`);\n return false;\n }\n const normalizedIssueId = parsedIssueId.normalized.toUpperCase();\n\n const started = postMergeGuard.run(\n normalizedIssueId,\n async () => {\n const projectPath = getProjectPathForIssue(parsedIssueId.prefix);\n const { postMergeLifecycle } = await import('../../../../lib/cloister/merge-agent.js');\n const lifecycleOptions = {\n ...(options?.verifiedMergedRef ? { verifiedMergedRef: options.verifiedMergedRef } : {}),\n ...(options?.verifiedMergedRepos?.length ? { verifiedMergedRepos: options.verifiedMergedRepos } : {}),\n };\n await postMergeLifecycle(\n normalizedIssueId,\n projectPath,\n options?.sourceBranch,\n Object.keys(lifecycleOptions).length > 0 ? lifecycleOptions : undefined,\n );\n console.log(`[merge] post-merge lifecycle completed for ${normalizedIssueId}`);\n\n // PAN-1691: roll the merge train — rebase ready siblings onto the new main,\n // re-verify the clean ones, agent-resolve conflicts. No-op unless the\n // flywheel.merge_train_enabled flag is on. Runs inside the in-flight guard,\n // so it cannot re-enter postMergeLifecycle for this issue.\n const { runMergeTrainReconcile } = await import('../../../../lib/cloister/merge-train.js');\n const outcomes = await runMergeTrainReconcile(normalizedIssueId);\n if (outcomes.length > 0) {\n console.log(\n `[merge-train] reconciled ${outcomes.length} sibling(s) after ${normalizedIssueId}: ` +\n outcomes.map((o) => `${o.issueId}=${o.result}`).join(', '),\n );\n }\n },\n (err) => console.error(`[merge] post-merge lifecycle failed for ${normalizedIssueId}:`, err),\n );\n if (!started) {\n console.log(`[merge] firePostMergeLifecycle: skipping ${normalizedIssueId} — already in flight`);\n }\n return started;\n}\n\nexport function getProjectPathForIssue(issuePrefix: string): string {\n const issueId = `${issuePrefix}-1`;\n const resolved = resolveProjectFromIssueSync(issueId);\n if (resolved) return resolved.projectPath;\n return join(homedir(), 'Projects');\n}\n","import { existsSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect, Layer } from 'effect';\nimport { HttpRouter, HttpServerRequest } from 'effect/unstable/http';\n\nimport { getAgentState, getAgentRuntimeState, messageAgent, saveAgentRuntimeState, transitionIssueToInProgress } from '../../../../lib/agents.js';\nimport { getUnblockedItemsSync } from '../../../../lib/cloister/task-readiness.js';\nimport { recordReviewVerdict } from '../../../../lib/cloister/review-verdict-writer.js';\nimport type { HeadAnchor } from '../../../../lib/git-utils.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { getReviewStatusSync, loadReviewStatuses, setReviewStatusSync as setReviewStatusBase, type ReviewStatus, type ReviewStatusUpdate } from '../../../../lib/review-status.js';\nimport { readWorkspacePlanSync } from '../../../../lib/xbrief/io.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { EventStoreService } from '../../services/domain-services.js';\nimport { validateAgentRuntimeEventAuth } from '../agents.js';\nimport { httpHandler } from '../http-handler.js';\nimport { reportTieredInspectFailureEscalation } from '../tiered-inspect-escalation.js';\nimport { killSession } from '../../../../lib/tmux.js';\nimport {\n _serverManagedMerges,\n execFileAsync,\n firePostMergeLifecycle,\n readJsonBody,\n specialistEventRole,\n validateSpecialistAutoCompleteMetadata,\n type SpecialistAgentName,\n type SpecialistAutoCompleteBody,\n} from './shared.js';\n\n// ─── Route: GET /api/specialists ─────────────────────────────────────────────\n\nconst getSpecialistsRoute = HttpRouter.add(\n 'GET',\n '/api/specialists',\n httpHandler(Effect.gen(function* () {\n const {\n getAllSpecialistStatus,\n getAllProjectSpecialistStatuses,\n } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n\n const legacySpecialists = yield* Effect.promise(() => getAllSpecialistStatus());\n const projectSpecialists = yield* Effect.promise(() => getAllProjectSpecialistStatuses());\n\n return jsonResponse({\n specialists: legacySpecialists,\n projects: projectSpecialists,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/reset-all ───────────────────────────────────\n// NOTE: Must be registered before /:name/reset to avoid \"reset-all\" matching as :name\n\nconst postSpecialistsResetAllRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/reset-all',\n httpHandler(Effect.gen(function* () {\n const {\n getAllSpecialists,\n isRunning,\n getTmuxSessionName,\n } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const { clearHookSync } = yield* Effect.promise(() => import('../../../../lib/hooks.js'));\n\n const specialists = getAllSpecialists();\n const results: { name: string; killed: boolean; sessionCleared: boolean; queueCleared: boolean }[] = [];\n\n for (const specialist of specialists) {\n const name = specialist.name;\n let killed = false;\n\n if (yield* Effect.promise(() => isRunning(name))) {\n const tmuxSession = getTmuxSessionName(name);\n const killResult = yield* killSession(tmuxSession).pipe(\n Effect.as(true),\n Effect.catch(() => Effect.succeed(false)),\n );\n killed = killResult;\n }\n\n clearHookSync(name);\n results.push({ name, killed, sessionCleared: false, queueCleared: true });\n }\n\n // Reset any \"reviewing\" statuses to \"pending\" — use per-issue atomic updates\n // to avoid the read-all/write-all race that saveReviewStatuses() would reintroduce.\n let reviewStatusesReset = 0;\n try {\n const statuses = loadReviewStatuses();\n for (const key of Object.keys(statuses)) {\n if (statuses[key].reviewStatus === 'reviewing') {\n setReviewStatusBase(key, { reviewStatus: 'pending' });\n reviewStatusesReset++;\n }\n }\n } catch (e) {\n console.error('Failed to reset review statuses:', e);\n }\n\n return jsonResponse({\n success: true,\n message: `Reset ${results.length} specialists, reset ${reviewStatusesReset} review statuses`,\n results,\n reviewStatusesReset,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/done ───────────────────────────────────────\n// CRITICAL: This endpoint has idempotency guards — see CLAUDE.md.\n// Must be registered before /:name/* routes to prevent \"done\" matching as :name.\n\nconst postSpecialistsDoneRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/done',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const { specialist, issueId, status, notes, itemId, runId } = body as {\n specialist: string;\n issueId: string;\n status: string;\n notes?: string;\n itemId?: string;\n runId?: string;\n };\n\n // Validate specialist type\n const validSpecialists = ['review', 'test', 'merge', 'inspect', 'uat', 'ship'];\n if (!validSpecialists.includes(specialist)) {\n return jsonResponse(\n { error: `Invalid specialist: ${specialist}. Valid: ${validSpecialists.join(', ')}` },\n { status: 400 },\n );\n }\n\n // Validate status\n if (!status || !['passed', 'failed'].includes(status)) {\n return jsonResponse(\n { error: `Invalid status: ${status}. Must be 'passed' or 'failed'` },\n { status: 400 },\n );\n }\n\n // Validate issueId\n if (!issueId) {\n return jsonResponse({ error: 'issueId is required' }, { status: 400 });\n }\n\n const normalizedIssueId = issueId.toUpperCase();\n\n if (specialist === 'inspect' && status === 'passed') {\n if (!itemId) {\n return jsonResponse({ error: 'itemId is required for a passed inspect verdict' }, { status: 400 });\n }\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n const workspacePath = project && join(project.projectPath, 'workspaces', `feature-${normalizedIssueId.toLowerCase()}`);\n const plan = workspacePath ? readWorkspacePlanSync(workspacePath) : undefined;\n if (!plan?.plan.items.some(item => item.id === itemId)) {\n return jsonResponse({ error: `Item \"${itemId}\" does not exist in the xBRIEF for ${normalizedIssueId}` }, { status: 400 });\n }\n }\n console.log(`[specialists/done] ${specialist} signaling ${status} for ${normalizedIssueId}`);\n\n // Resolve any pending specialist completion waiters (PAN-632: event-driven completion).\n // This replaces polling loops in spawnMergeAgentForBranches / syncMainIntoWorkspace.\n if (specialist === 'merge') {\n const { reportSpecialistCompletion } = yield* Effect.promise(() => import('../../../../lib/cloister/specialist-completion.js'));\n const resolved = yield* reportSpecialistCompletion(normalizedIssueId, {\n status: status as 'passed' | 'failed',\n notes,\n });\n if (resolved) {\n console.log(`[specialists/done] Resolved pending completion waiter for ${normalizedIssueId}`);\n }\n }\n\n // GUARD: If this issue is in a server-managed merge (polyrepo), the server handles\n // the merge lifecycle. Acknowledge the agent's call but do NOT trigger onMergeComplete.\n if (specialist === 'merge' && _serverManagedMerges.has(normalizedIssueId)) {\n console.log(`[specialists/done] ${normalizedIssueId} is server-managed merge — acknowledging without triggering lifecycle`);\n return jsonResponse({\n success: true,\n specialist,\n issueId: normalizedIssueId,\n status,\n notes,\n serverManaged: true,\n });\n }\n\n // Build the update based on specialist type\n const update: ReviewStatusUpdate = {};\n let reviewEvidenceHead: HeadAnchor | undefined;\n\n switch (specialist) {\n case 'review':\n // Passed verdicts need the current workspace head for the write-door check.\n // Blocked verdicts stay durable-first and take their anchor after feedback.\n if (status === 'passed') {\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n const { snapshotWorkspaceHeadsPromise } = await import('../../../../lib/git-utils.js');\n reviewEvidenceHead = await snapshotWorkspaceHeadsPromise(normalizedIssueId, workspacePath);\n }\n }\n } catch {\n // Non-fatal; recordReviewVerdict will proceed without evidenceHead\n }\n });\n }\n update.reviewStatus = status === 'passed' ? 'passed' : 'blocked';\n if (notes) update.reviewNotes = notes;\n break;\n\n case 'test':\n update.testStatus = status as typeof update.testStatus;\n if (notes) update.testNotes = notes;\n break;\n\n case 'merge':\n update.mergeStatus = status === 'passed' ? 'merged' : 'failed';\n break;\n\n case 'inspect':\n update.inspectStatus = status as typeof update.inspectStatus;\n if (notes) update.inspectNotes = notes;\n break;\n\n case 'uat':\n update.uatStatus = status as typeof update.uatStatus;\n if (notes) update.uatNotes = notes;\n if (status === 'passed') {\n update.readyForMerge = true;\n }\n break;\n\n case 'ship':\n if (status === 'passed') {\n update.readyForMerge = true;\n }\n break;\n }\n\n // Apply the update (triggers side effects like idle state, queue processing)\n // For review verdicts, route through recordReviewVerdict (PAN-3512)\n let updatedStatus: ReviewStatus;\n if (specialist === 'review' && (status === 'passed' || status === 'blocked' || status === 'failed')) {\n const verdictOutcome = yield* Effect.promise(() => recordReviewVerdict(normalizedIssueId, {\n verdict: status === 'passed' ? 'passed' : 'blocked',\n notes,\n evidenceHead: reviewEvidenceHead,\n writer: 'coordinator',\n }));\n if (!verdictOutcome.landed) {\n // Verdict was rejected (stale evidence) — return error\n return jsonResponse(\n { error: `Verdict rejected: ${verdictOutcome.reason}` },\n { status: 400 },\n );\n }\n updatedStatus = getReviewStatusSync(normalizedIssueId) || ({} as ReviewStatus);\n } else {\n updatedStatus = setReviewStatusBase(normalizedIssueId, update);\n }\n if (specialist === 'inspect' && status === 'failed') {\n yield* Effect.promise(() => reportTieredInspectFailureEscalation(normalizedIssueId, notes));\n // PAN-3078: a blocked verdict must reach the work agent, or it keeps\n // building on rejected work. itemId comes from the supervisor's\n // `pan admin specialists done --item`; legacy ephemeral inspectors omit\n // it, so fall back to the inspectBeadId stamped when inspection started.\n yield* Effect.promise(async () => {\n try {\n const failedItemId = itemId ?? getReviewStatusSync(normalizedIssueId)?.inspectBeadId ?? undefined;\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project && failedItemId) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n const { onInspectComplete } = await import('../../../../lib/cloister/inspect-agent.js');\n await Effect.runPromise(onInspectComplete(project.projectKey, normalizedIssueId, failedItemId, 'failed', workspacePath, notes));\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Error delivering failed inspect verdict for ${normalizedIssueId}:`, err);\n }\n });\n }\n\n // Set specialist state to idle and clear registry write-scope.\n // CRITICAL: No `await` between the mergeStatus write above and the guard check below.\n yield* Effect.promise(async () => {\n try {\n const { getTmuxSessionName, updateRunMetadata, makeSpecialistRegistryKey } =\n await import('../../../../lib/cloister/specialists.js');\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n const projectKey = project?.projectKey;\n const tmuxSession = projectKey\n ? getTmuxSessionName(`${specialist}-agent` as SpecialistAgentName, projectKey, normalizedIssueId)\n : getTmuxSessionName(`${specialist}-agent` as SpecialistAgentName);\n saveAgentRuntimeState(tmuxSession, {\n state: 'idle',\n lastActivity: new Date().toISOString(),\n });\n console.log(`[specialists/done] Set ${tmuxSession} to idle`);\n\n // PAN-2579 (warm-by-default lifecycle): the verdict is recorded; the session\n // stays ALIVE so the next review/test cycle resumes it with context intact.\n // Warm-idle sessions no longer count against the advancing ceiling\n // (countRunningAgents excludes them) and the memory governor sheds them\n // first under HARD pressure — eviction is the governor's job, never a\n // side effect of recording a verdict. (Supersedes the PAN-846 kill and\n // the PAN-2007 keep-alive flag.)\n console.log(`[specialists/done] Warm lifecycle: verdict recorded, leaving ${tmuxSession} running (PAN-2579)`);\n\n // Clear write-scope lock so the next specialist can claim the workspace\n if (projectKey) {\n const registryKey = makeSpecialistRegistryKey(`${specialist}-agent`, normalizedIssueId);\n updateRunMetadata(projectKey, registryKey, {\n currentRun: null,\n writeScope: undefined,\n workspace: null,\n currentActivity: null,\n });\n console.log(`[specialists/done] Cleared registry lock for ${registryKey} (${projectKey})`);\n }\n\n // Update specialist handoff log so success-rate metrics reflect actual outcome\n const { updateSpecialistHandoffStatus } = await import('../../../../lib/cloister/specialist-handoff-logger.js');\n const updated = await Effect.runPromise(updateSpecialistHandoffStatus(\n normalizedIssueId,\n `${specialist}-agent`,\n status === 'passed' ? 'completed' : 'failed',\n status === 'passed' ? 'success' : 'failure',\n ));\n if (updated) {\n console.log(`[specialists/done] Updated handoff log: ${specialist}-agent ${normalizedIssueId} → ${status}`);\n }\n } catch (err) {\n console.error(`[specialists/done] Error managing specialist state:`, err);\n }\n });\n\n // When review passes, snapshot the current HEAD commit so we can detect\n // if the agent makes new commits before merge (which invalidates the review).\n if (specialist === 'review' && status === 'passed') {\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n const { formatAnchorShort, snapshotWorkspaceHeadsPromise } = await import('../../../../lib/git-utils.js');\n const headAnchor = await snapshotWorkspaceHeadsPromise(normalizedIssueId, workspacePath);\n if (headAnchor) {\n setReviewStatusBase(normalizedIssueId, { reviewedAtCommit: headAnchor });\n console.log(`[specialists/done] Snapshotted reviewedAtCommit=${formatAnchorShort(headAnchor)} for ${normalizedIssueId}`);\n }\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Failed to snapshot reviewedAtCommit for ${normalizedIssueId}:`, err);\n }\n });\n }\n\n // When inspect specialist reports success, save checkpoint\n if (specialist === 'inspect' && status === 'passed') {\n yield* Effect.promise(async () => {\n try {\n const { onInspectComplete } = await import('../../../../lib/cloister/inspect-agent.js');\n // Resolve project to get workspace path\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n await Effect.runPromise(onInspectComplete(project.projectKey, normalizedIssueId, itemId!, 'passed', workspacePath, notes));\n\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Error saving inspect checkpoint:`, err);\n }\n });\n }\n\n // When the test specialist reports success, persist testStatus and emit\n // test.passed so reactive Cloister records the shipping lifecycle phase.\n // PAN-1650 derives readyForMerge from review/test gate state server-side;\n // no ship role is spawned.\n if (specialist === 'test' && status === 'passed') {\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n setReviewStatusBase(normalizedIssueId, { testStatus: 'passed' });\n const { initEventStore } = await import('../../event-store.js');\n const store = await initEventStore();\n await store.appendAsync({\n type: 'test.passed',\n timestamp: new Date().toISOString(),\n payload: { issueId: normalizedIssueId },\n } as any);\n console.log(`[specialists/done] ${normalizedIssueId} emitted test.passed; shipping lifecycle recorded`);\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Error emitting test.passed for ${normalizedIssueId}:`, err);\n }\n });\n }\n\n // When merge specialist reports success, run post-merge lifecycle ONCE.\n // Use firePostMergeLifecycle directly rather than onMergeComplete: onMergeComplete\n // has a guard that checks mergeStatus !== 'merged', but setReviewStatusBase above\n // already set mergeStatus='merged' — that guard would always fire and the lifecycle\n // would never run. firePostMergeLifecycle skips that guard and uses the\n // in-flight guard (postMergeGuard, concurrency) + postMergeLifecycle's\n // _completedPostMerge (defense-in-depth).\n if (specialist === 'merge' && status === 'passed') {\n firePostMergeLifecycle(normalizedIssueId);\n }\n\n // When any specialist reports failure, transition issue back to In Progress\n // (inspect failures don't change Linear status — they're mid-implementation gates).\n if (status === 'failed' && specialist !== 'inspect') {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n const wsPath = existsSync(workspacePath) ? workspacePath : undefined;\n transitionIssueToInProgress(normalizedIssueId, wsPath).catch((err: unknown) => {\n const errMsg = err instanceof Error ? err.message : String(err);\n console.warn(\n `[specialists/done] Could not transition ${normalizedIssueId} back to in_progress: ${errMsg}`,\n );\n });\n console.log(\n `[specialists/done] ${specialist} failed → transitioning ${normalizedIssueId} back to In Progress`,\n );\n }\n } catch (err: unknown) {\n const errMsg = err instanceof Error ? err.message : String(err);\n console.warn(`[specialists/done] Could not transition issue back to in_progress:`, errMsg);\n }\n }\n\n // When merge fails, post a comment on the GitHub PR so the failure is visible\n // outside the dashboard, and send feedback to the work agent.\n if (specialist === 'merge' && status === 'failed') {\n yield* Effect.promise(async () => {\n try {\n // Post comment on the PR\n const reviewStatus = loadReviewStatuses()[normalizedIssueId];\n const prUrl = reviewStatus?.prUrl;\n if (prUrl) {\n // Extract owner/repo#number from PR URL\n const prMatch = prUrl.match(/github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/(\\d+)/);\n if (prMatch) {\n const [, owner, repo, prNumber] = prMatch;\n const commentBody = `## Merge Failed\\n\\n${notes || 'Merge could not be completed.'}\\n\\nThe issue has been moved back to In Progress. The work agent needs to resolve conflicts and resubmit.`;\n await execFileAsync(\n 'gh',\n ['api', `repos/${owner}/${repo}/issues/${prNumber}/comments`, '--field', `body=${commentBody}`],\n { encoding: 'utf-8' },\n );\n console.log(`[specialists/done] Posted merge failure comment on ${prUrl}`);\n }\n }\n } catch (err: any) {\n console.warn(`[specialists/done] Failed to post merge failure comment: ${err.message}`);\n }\n });\n\n // If merge failed due to conflicts, send rebase instructions to the work agent.\n if (notes?.toLowerCase().includes('conflict')) {\n yield* Effect.promise(async () => {\n try {\n const workAgentId = `agent-${normalizedIssueId.toLowerCase()}`;\n const { sessionExists } = await import('../../../../lib/tmux.js');\n const { messageAgent, spawnAgent, getAgentStateSync } = await import('../../../../lib/agents.js');\n\n if (await Effect.runPromise(sessionExists(workAgentId))) {\n // Agent is running — send rebase instructions directly\n const rebaseMsg = `MERGE CONFLICT: The merge-agent could not rebase your branch onto main due to conflicts. Please fix this now:\\n\\n1. git fetch origin main\\n2. git rebase origin/main\\n3. Resolve any conflicts (git add <file> && git rebase --continue)\\n4. git push --force-with-lease\\n5. Resubmit: curl -s -X POST http://localhost:3011/api/review/${normalizedIssueId}/request -H \"Content-Type: application/json\" -d \"{}\"\\n\\nConflict details: ${notes}`;\n await messageAgent(workAgentId, rebaseMsg);\n console.log(`[specialists/done] Sent rebase instructions to ${workAgentId}`);\n } else {\n // Agent is stopped — start fresh (don't resume, sessions may be corrupted: PAN-612)\n console.log(`[specialists/done] Work agent ${workAgentId} not running — will need manual restart or next pan start dispatch`);\n }\n } catch (err: any) {\n console.warn(`[specialists/done] Failed to send rebase feedback to work agent: ${err.message}`);\n }\n });\n }\n }\n\n if (specialist === 'review' && (status === 'failed' || status === 'blocked')) {\n // Only deliver feedback if this is a coordinator verdict (not handled by CLI/fallback)\n // The verdict write door has already validated the evidence head\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n const workspacePath = project\n ? join(project.projectPath, 'workspaces', `feature-${normalizedIssueId.toLowerCase()}`)\n : undefined;\n const { deliverReviewVerdictFeedback } = await import(\n '../../../../lib/cloister/review-verdict-feedback.js'\n );\n const result = await Effect.runPromise(deliverReviewVerdictFeedback({\n issueId: normalizedIssueId,\n verdict: status === 'failed' ? 'failed' : 'blocked',\n notes,\n workspacePath,\n prUrl: updatedStatus.prUrl,\n ...(runId ? { runId } : {}),\n }));\n console.log(\n `[specialists/done] Delivered review verdict feedback for ${normalizedIssueId}` +\n ` (feedback=${result.feedbackPath ?? 'none'}, synthesis=${result.synthesisPath ?? 'none'}, prComment=${result.prCommentPosted})`,\n );\n } catch (err: any) {\n console.warn(`[specialists/done] Failed to deliver review verdict feedback: ${err.message}`);\n }\n });\n }\n\n // PAN-3148: the HTTP review prompt reports changes requested as status=failed,\n // which the durable write above maps to reviewStatus=blocked. Snapshot the\n // reviewed HEAD only after feedback delivery so the verdict remains durable\n // even when the git probe stalls or fails (PAN-2524 ordering).\n if (specialist === 'review' && status === 'failed') {\n yield* Effect.promise(async () => {\n try {\n const project = resolveProjectFromIssueSync(normalizedIssueId);\n if (project) {\n const workspacePath = join(\n project.projectPath,\n 'workspaces',\n `feature-${normalizedIssueId.toLowerCase()}`,\n );\n if (existsSync(workspacePath)) {\n const { formatAnchorShort, snapshotWorkspaceHeadsPromise } = await import('../../../../lib/git-utils.js');\n const reviewedAtCommit = await snapshotWorkspaceHeadsPromise(normalizedIssueId, workspacePath);\n if (reviewedAtCommit) {\n setReviewStatusBase(normalizedIssueId, { reviewedAtCommit });\n console.log(`[specialists/done] Snapshotted blocked reviewedAtCommit=${formatAnchorShort(reviewedAtCommit)} for ${normalizedIssueId}`);\n }\n }\n }\n } catch (err) {\n console.error(`[specialists/done] Failed to snapshot blocked reviewedAtCommit for ${normalizedIssueId}:`, err);\n }\n });\n }\n\n // Emit domain event for role-backed specialist completion/failure.\n const eventRole = specialistEventRole(specialist);\n if (eventRole) {\n if (status === 'passed') {\n yield* eventStore.append({\n type: 'specialist.completed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId: normalizedIssueId },\n });\n } else {\n yield* eventStore.append({\n type: 'specialist.failed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId: normalizedIssueId, error: notes || `${specialist} failed` },\n });\n }\n }\n\n return jsonResponse({\n success: true,\n specialist,\n issueId: normalizedIssueId,\n status,\n notes,\n currentStatus: updatedStatus,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/logs/cleanup-all ────────────────────────────\n// NOTE: Must be registered before /:project/:type routes.\n\nconst postSpecialistsLogsCleanupAllRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/logs/cleanup-all',\n httpHandler(Effect.gen(function* () {\n const { cleanupAllLogsSync } = yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n const results = cleanupAllLogsSync();\n\n return jsonResponse({\n success: true,\n totalDeleted: results.totalDeleted,\n byProject: results.byProject,\n message: `Cleaned up ${results.totalDeleted} old logs`,\n });\n })),\n);\n\n// ─── Route: GET /api/specialists/projects ────────────────────────────────────\n// NOTE: Must be registered before /:name routes.\n\nconst getSpecialistsProjectsRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/projects',\n httpHandler(Effect.gen(function* () {\n const { getAllProjectSpecialistStatuses } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const specialists = yield* Effect.promise(() => getAllProjectSpecialistStatuses());\n return jsonResponse(specialists);\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/wake ─────────────────────────────────\n\nconst postSpecialistWakeRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/wake',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n return jsonResponse(\n { error: `Legacy specialist wake is no longer supported for ${name}; role runs spawn agents directly.` },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/reset ─────────────────────────────────\n\nconst postSpecialistResetRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/reset',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n return jsonResponse(\n { error: `Legacy specialist session reset is no longer supported for ${name}; role agents are managed through the normal agent lifecycle.` },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/init ──────────────────────────────────\n\nconst postSpecialistInitRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/init',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n return jsonResponse(\n { error: `Legacy specialist initialization is no longer supported for ${name}; role flows spawn agents on demand.` },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/report-status ────────────────────────\n\nconst postSpecialistReportStatusRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/report-status',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n const body = yield* readJsonBody;\n const eventStore = yield* EventStoreService;\n const { issueId, status, notes } = body as {\n issueId?: string;\n status?: string;\n notes?: string;\n };\n\n if (!issueId || !status) {\n return jsonResponse(\n { error: 'issueId and status required' },\n { status: 400 },\n );\n }\n\n if (!['passed', 'blocked', 'failed', 'in-progress'].includes(status)) {\n return jsonResponse(\n { error: 'status must be: passed, blocked, failed, or in-progress' },\n { status: 400 },\n );\n }\n\n // Write status to specialist's state directory\n const specialistDir = join(homedir(), '.overdeck', 'specialists', name);\n yield* Effect.promise(() => mkdir(specialistDir, { recursive: true }));\n\n const statusFile = join(specialistDir, `${issueId}-status.json`);\n const statusData = {\n issueId,\n specialist: name,\n status,\n notes: notes || '',\n timestamp: new Date().toISOString(),\n };\n\n yield* Effect.promise(() => writeFile(statusFile, JSON.stringify(statusData, null, 2)));\n\n console.log(`[specialists] ${name} reported status for ${issueId}: ${status}`);\n\n // When specialist reports completion (passed/blocked/failed), set state to idle\n if (['passed', 'blocked', 'failed'].includes(status)) {\n const { getTmuxSessionName } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const tmuxSession = getTmuxSessionName(name as SpecialistAgentName);\n saveAgentRuntimeState(tmuxSession, {\n state: 'idle',\n lastActivity: new Date().toISOString(),\n });\n }\n\n // Emit domain event based on status\n const eventRole = specialistEventRole(name);\n if (eventRole && status === 'passed') {\n yield* eventStore.append({\n type: 'specialist.completed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId },\n });\n } else if (eventRole && (status === 'failed' || status === 'blocked')) {\n yield* eventStore.append({\n type: 'specialist.failed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId, error: notes || `${name} reported ${status}` },\n });\n }\n\n return jsonResponse({ success: true });\n })),\n);\n\n// ─── Route: GET /api/specialists/:name/cost ───────────────────────────────────\n\nconst getSpecialistCostRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:name/cost',\n httpHandler(Effect.gen(function* () {\n return jsonResponse({ cost: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, model: '' });\n })),\n);\n\n// ─── Route: POST /api/specialists/:name/auto-complete ────────────────────────\n\nconst postSpecialistAutoCompleteRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:name/auto-complete',\n httpHandler(Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n const auth = yield* Effect.promise(() => validateAgentRuntimeEventAuth(request));\n if (!auth.ok) return auth.response;\n\n const params = yield* HttpRouter.params;\n const name = params['name'] as string;\n const body = (yield* readJsonBody) as SpecialistAutoCompleteBody;\n const eventStore = yield* EventStoreService;\n const { issueId: requestIssueId, status: requestStatus, agentId } = body;\n\n const agentState = agentId ? yield* getAgentState(agentId) : null;\n const runtimeState = agentId ? yield* getAgentRuntimeState(agentId) : null;\n const metadata = validateSpecialistAutoCompleteMetadata(name, body, agentState, runtimeState);\n if (!metadata.ok) {\n return jsonResponse({ error: metadata.error }, { status: metadata.status });\n }\n\n const issueId = requestIssueId!;\n const status = requestStatus!;\n const completingAgentId = agentId!;\n\n console.log(`[specialists] Auto-detected completion for ${name}: ${issueId} -> ${status}`);\n\n yield* Effect.promise(() => saveAgentRuntimeState(completingAgentId, {\n state: 'idle',\n lastActivity: new Date().toISOString(),\n currentIssue: undefined,\n }));\n\n // Update review/test status based on specialist type\n const existingStatus = getReviewStatusSync(issueId);\n\n if (name === 'review-agent') {\n const alreadyReported =\n existingStatus?.reviewNotes &&\n !existingStatus.reviewNotes.startsWith('Auto-detected:');\n if (alreadyReported) {\n console.log(\n `[specialists] Skipping auto-detect for ${name}/${issueId}: specialist already reported (${existingStatus!.reviewStatus})`,\n );\n } else {\n setReviewStatusBase(issueId, {\n reviewStatus: status === 'passed' ? 'passed' : 'blocked',\n reviewNotes: `Auto-detected: ${status}`,\n });\n }\n\n if ((alreadyReported ? existingStatus!.reviewStatus : status === 'passed' ? 'passed' : 'blocked') === 'passed') {\n console.log(`[specialists] ${issueId} review approved; reactive Cloister will dispatch the test role`);\n }\n } else if (name === 'test-agent') {\n const alreadyReported =\n existingStatus?.testNotes && !existingStatus.testNotes.startsWith('Auto-detected:');\n if (alreadyReported) {\n console.log(\n `[specialists] Skipping auto-detect for ${name}/${issueId}: specialist already reported (${existingStatus!.testStatus})`,\n );\n } else {\n const testPassed = status === 'passed';\n setReviewStatusBase(issueId, {\n testStatus: testPassed ? 'passed' : 'failed',\n testNotes: `Auto-detected: ${status}`,\n });\n if (testPassed) {\n // Emit test.passed so reactive Cloister records the shipping\n // lifecycle phase; readyForMerge is derived server-side.\n yield* eventStore.append({\n type: 'test.passed',\n timestamp: new Date().toISOString(),\n payload: { issueId },\n });\n console.log(`[specialists] ${issueId} emitted test.passed after auto-detected test pass`);\n }\n }\n }\n\n const eventRole = specialistEventRole(name);\n if (eventRole) {\n yield* eventStore.append({\n type: 'specialist.completed',\n timestamp: new Date().toISOString(),\n payload: { name: eventRole, issueId },\n });\n }\n\n return jsonResponse({\n success: true,\n status,\n issueId,\n });\n })),\n);\n\nexport const specialistsLegacyRouteLayer = Layer.mergeAll(\n getSpecialistsRoute,\n getSpecialistsProjectsRoute,\n postSpecialistsResetAllRoute,\n postSpecialistsDoneRoute,\n postSpecialistsLogsCleanupAllRoute,\n postSpecialistWakeRoute,\n postSpecialistResetRoute,\n postSpecialistInitRoute,\n postSpecialistReportStatusRoute,\n getSpecialistCostRoute,\n postSpecialistAutoCompleteRoute,\n);\n","import { existsSync } from 'node:fs';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { Effect, Layer, Option, Stream } from 'effect';\nimport { HttpRouter, HttpServerRequest, HttpServerResponse } from 'effect/unstable/http';\n\nimport { getClaudePermissionFlagsStringSync } from '../../../../lib/claude-permissions.js';\nimport { normalizeModelName } from '../../../../lib/cost-parsers/jsonl-parser.js';\nimport { calculateCostSync, getPricingSync, type TokenUsage } from '../../../../lib/cost.js';\nimport { loadConfigSync, resolveModel } from '../../../../lib/config-yaml.js';\nimport { encodeClaudeProjectDir } from '../../../../lib/paths.js';\nimport { resolvePrimaryWorkspaceRepoDirSync, resolveWorkspaceRepoRootsSync } from '../../../../lib/project-repos.js';\nimport { resolveProjectFromIssueSync } from '../../../../lib/projects.js';\nimport { getReviewStatusSync } from '../../../../lib/review-status.js';\nimport { getAgentCommandSync } from '../../../../lib/settings.js';\nimport { killSession } from '../../../../lib/tmux.js';\nimport { getAgentStateSync, saveAgentRuntimeState } from '../../../../lib/agents.js';\nimport { REVIEW_SUB_ROLES, type ReviewSubRole } from '../../../../lib/cloister/review-monitor.js';\nimport { resolveReviewParentRunState } from '../../../../lib/cloister/review-run-recovery.js';\nimport { jsonResponse } from '../../http-helpers.js';\nimport { httpHandler } from '../http-handler.js';\nimport { execAsync, readJsonBody, validateSpecialistAgentName } from './shared.js';\n\n// ─── Route: GET /api/specialists/:project/:issueId/:type/status ───────────────\n//\n// PAN-1048 review feedback 003 (REQ-16): the legacy specialist-status route\n// returned metadata sourced from ~/.overdeck/specialists/registry.json, which\n// the role-primitive refactor explicitly retires. The startup cleanup in\n// service.ts deletes that directory on every boot; preserving the read path\n// would silently recreate it via getRunMetadata() → loadRegistry()/saveRegistry().\n//\n// The route is now a 410 Gone with a pointer to the role-aware status surface.\n// The frontend's only caller (AgentOutputPanel) only fires when\n// parseSpecialistSession(agentId) matches the legacy `specialist-…` session\n// naming, which the new role spawns no longer use, so the dead-letter response\n// is invisible to current dashboards.\n\nconst getProjectSpecialistStatusRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:issueId/:type/status',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n return jsonResponse(\n {\n error: 'specialist-status route retired',\n hint: 'Specialist identity is replaced by the role primitive. Use GET /api/agents and read the role/status fields off the AgentSnapshot for the role-scoped session (e.g. agent-pan-509-review).',\n retiredFor: {\n project: params['project'],\n issueId: params['issueId'],\n type: params['type'],\n },\n },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:issueId/:type/kill ───────────────\n\nconst postProjectSpecialistKillRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:issueId/:type/kill',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const issueId = params['issueId'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { getTmuxSessionName, makeSpecialistRegistryKey, getRunMetadata } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n\n const registryKey = makeSpecialistRegistryKey(type, issueId);\n const tmuxSession = getRunMetadata(project, registryKey).tmuxSession\n ?? getTmuxSessionName(type, project, issueId);\n\n yield* Effect.promise(() => Effect.runPromise(killSession(tmuxSession)).catch(() => {}));\n // Leave Claude JSONL/session artifacts intact; only reset Overdeck runtime state.\n saveAgentRuntimeState(tmuxSession, {\n state: 'idle',\n lastActivity: new Date().toISOString(),\n });\n return jsonResponse({\n success: true,\n message: `Killed ${type} (${project}/${issueId})`,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/spawn ───────────────────────\n//\n// PAN-1048 R1: removed. The legacy /spawn endpoint dispatched arbitrary\n// \"specialist types\" (review-agent, test-agent, merge-agent) by issuing a\n// generic spawnEphemeralSpecialist call. Under the role primitive, review/test\n// dispatch through lifecycle-aware role paths. Shipping is now server-side;\n// `ship` remains only as the merge-specialist identity for model routing and\n// historical activity attribution. The endpoint had no remaining in-tree caller\n// and is replaced by reactive Cloister scheduling on issue state transitions\n// plus the role spawn primitive.\n//\n// Old shape (removed):\n// POST /api/specialists/:project/:type/spawn { issueId, branch, ... }\n//\n// Replacement: drive role spawns via lifecycle.transitionTo() and the\n// reactive scheduler in src/lib/cloister/service.ts.\n\n// ─── Route: GET /api/specialists/:project/:type/runs ──────────────────────────\n\nconst getProjectSpecialistRunsRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/runs',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n const request = yield* HttpServerRequest.HttpServerRequest;\n const urlOpt = HttpServerRequest.toURL(request);\n\n let limit: number | undefined;\n let offset = 0;\n\n if (Option.isSome(urlOpt)) {\n const sp = urlOpt.value.searchParams;\n const limitParam = sp.get('limit');\n const offsetParam = sp.get('offset');\n if (limitParam) limit = parseInt(limitParam, 10);\n if (offsetParam) offset = parseInt(offsetParam, 10);\n }\n\n const { listRunLogsSync } = yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n const runs = listRunLogsSync(project, type, { limit, offset });\n return jsonResponse(runs);\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/runs/:runId/stream ────────────\n// NOTE: Must be registered before /:project/:type/runs/:runId to avoid route conflict.\n\nconst getProjectSpecialistRunStreamRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/runs/:runId/stream',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n const runId = params['runId'] as string;\n\n const { getRunLogPath, isRunLogActive } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n\n const logPath = getRunLogPath(project, type, runId);\n\n if (!existsSync(logPath)) {\n return jsonResponse({ error: 'Run log not found' }, { status: 404 });\n }\n\n // Build an SSE stream using Effect Stream + Node ReadableStream\n const encoder = new TextEncoder();\n\n const nodeStream = new ReadableStream({\n async start(controller) {\n // Send initial content\n const content = await readFile(logPath, 'utf-8').catch(() => '');\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({ type: 'content', data: content })}\\n\\n`,\n ),\n );\n let lastSize = content.length;\n\n // Poll for updates\n const poll = async () => {\n if (!isRunLogActive(project, type, runId)) {\n // Log completed — send final update and close\n try {\n const finalContent = await readFile(logPath, 'utf-8');\n if (finalContent.length > lastSize) {\n const newContent = finalContent.substring(lastSize);\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({ type: 'append', data: newContent })}\\n\\n`,\n ),\n );\n }\n } catch {}\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify({ type: 'complete' })}\\n\\n`),\n );\n controller.close();\n return;\n }\n\n try {\n const currentContent = await readFile(logPath, 'utf-8');\n if (currentContent.length > lastSize) {\n const newContent = currentContent.substring(lastSize);\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({ type: 'append', data: newContent })}\\n\\n`,\n ),\n );\n lastSize = currentContent.length;\n }\n } catch {}\n\n await new Promise((r) => setTimeout(r, 1000));\n await poll();\n };\n\n await new Promise((r) => setTimeout(r, 1000));\n await poll();\n },\n });\n\n const effectStream = Stream.fromReadableStream<Uint8Array, unknown>({\n evaluate: () => nodeStream,\n onError: (err) => err,\n });\n\n return HttpServerResponse.stream(effectStream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/runs/:runId ───────────────────\n\nconst getProjectSpecialistRunRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/runs/:runId',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n const runId = params['runId'] as string;\n\n const { getRunLogSync, parseLogMetadata } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n const content = getRunLogSync(project, type, runId);\n\n if (!content) {\n return jsonResponse({ error: 'Run log not found' }, { status: 404 });\n }\n\n const metadata = parseLogMetadata(content);\n return jsonResponse({ runId, content, metadata });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/runs/:runId/terminate ────────\n\nconst postProjectSpecialistRunTerminateRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/runs/:runId/terminate',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { terminateSpecialist } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n yield* Effect.promise(() => terminateSpecialist(project, type));\n return jsonResponse({ success: true, message: 'Specialist terminated' });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/grace/pause ──────────────────\n\nconst postProjectSpecialistGracePauseRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/grace/pause',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { pauseGracePeriod } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const success = pauseGracePeriod(project, type);\n\n if (success) {\n return jsonResponse({ success: true, message: 'Grace period paused' });\n } else {\n return jsonResponse(\n { error: 'No active grace period to pause' },\n { status: 400 },\n );\n }\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/grace/resume ─────────────────\n\nconst postProjectSpecialistGraceResumeRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/grace/resume',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { resumeGracePeriod } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const success = resumeGracePeriod(project, type);\n\n if (success) {\n return jsonResponse({ success: true, message: 'Grace period resumed' });\n } else {\n return jsonResponse(\n { error: 'No paused grace period to resume' },\n { status: 400 },\n );\n }\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/grace/exit ───────────────────\n\nconst postProjectSpecialistGraceExitRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/grace/exit',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { exitGracePeriod } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n exitGracePeriod(project, type);\n return jsonResponse({\n success: true,\n message: 'Specialist terminated immediately',\n });\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/grace ────────────────────────\n\nconst getProjectSpecialistGraceRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/grace',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { getGracePeriodState } = yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n const state = getGracePeriodState(project, type);\n\n if (state) {\n return jsonResponse(state);\n } else {\n return jsonResponse({ error: 'No active grace period' }, { status: 404 });\n }\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/context ──────────────────────\n\nconst getProjectSpecialistContextRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/context',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n const { loadContextDigest } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialist-context.js'));\n const digest = loadContextDigest(project, type);\n\n if (digest) {\n return jsonResponse({ digest });\n } else {\n return jsonResponse({ error: 'No context digest found' }, { status: 404 });\n }\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/context/regenerate ───────────\n\nconst postProjectSpecialistContextRegenerateRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/context/regenerate',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n const { regenerateContextDigest } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialist-context.js'));\n const digest = yield* regenerateContextDigest(project, type);\n\n if (digest) {\n return jsonResponse({ digest, message: 'Context digest regenerated' });\n } else {\n return jsonResponse(\n { error: 'Failed to generate context digest' },\n { status: 500 },\n );\n }\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/complete ─────────────────────\n\nconst postProjectSpecialistCompleteRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/complete',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n const body = yield* readJsonBody;\n const { status, notes, issueId } = body as { status?: string; notes?: string; issueId?: string };\n\n if (!status || !['passed', 'failed', 'blocked'].includes(status)) {\n return jsonResponse(\n { error: 'Valid status (passed/failed/blocked) is required' },\n { status: 400 },\n );\n }\n\n if (!validateSpecialistAgentName(type)) {\n return jsonResponse(\n { error: 'Invalid specialist type. Must be review-agent, test-agent, or merge-agent' },\n { status: 400 },\n );\n }\n\n const { signalSpecialistCompletion } =\n yield* Effect.promise(() => import('../../../../lib/cloister/specialists.js'));\n signalSpecialistCompletion(project, type, { status: status as 'passed' | 'failed' | 'blocked', notes }, issueId);\n return jsonResponse({\n success: true,\n message: 'Specialist completion signaled, grace period started',\n });\n })),\n);\n\n// ─── Route: GET /api/specialists/:project/:type/latest-log ───────────────────\n\nconst getProjectSpecialistLatestLogRoute = HttpRouter.add(\n 'GET',\n '/api/specialists/:project/:type/latest-log',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n const runsDir = join(homedir(), '.overdeck', 'specialists', project, type, 'runs');\n if (!existsSync(runsDir)) {\n return jsonResponse({ log: null, message: 'No runs found' });\n }\n\n const files = (yield* Effect.promise(() => readdir(runsDir)))\n .filter((f) => f.endsWith('.log'))\n .sort()\n .reverse();\n\n if (files.length === 0) {\n return jsonResponse({ log: null, message: 'No run logs found' });\n }\n\n const latestLog = yield* Effect.promise(() => readFile(join(runsDir, files[0]), 'utf-8'));\n return jsonResponse({\n log: latestLog,\n file: files[0],\n totalRuns: files.length,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:type/logs/cleanup ────────────────\n\nconst postProjectSpecialistLogsCleanupRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:type/logs/cleanup',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const type = params['type'] as string;\n\n const { cleanupOldLogsSync } = yield* Effect.promise(() => import('../../../../lib/cloister/specialist-logs.js'));\n const { getSpecialistRetention } = yield* Effect.promise(() => import('../../../../lib/projects.js'));\n\n const retention = getSpecialistRetention(project);\n const deleted = cleanupOldLogsSync(project, type, { maxDays: retention.max_days, maxRuns: retention.max_runs });\n\n return jsonResponse({\n success: true,\n deleted,\n message: `Cleaned up ${deleted} old logs`,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/projects/:project/:name/reset-session ───────\n// Bumps the session generation so the next dispatch starts a fresh Claude session.\n// Old JSONL files are preserved.\n\nconst postProjectSpecialistResetSessionRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/projects/:project/:name/reset-session',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const projectKey = params['project'] ?? '';\n const name = params['name'] ?? '';\n return jsonResponse(\n { error: `Legacy specialist session rotation is no longer supported for ${projectKey}/${name}.` },\n { status: 410 },\n );\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:issueId/review/restart ───────────\n// Stops the review parent and convoy sessions, then resumes the parent through\n// the role primitive. Saved sessions and completed reports stay intact; same-run\n// recovery re-dispatches only reviewer lanes that have not produced a report.\n// A model or harness change still takes the existing fresh-spawn path.\n\nconst postProjectReviewRestartRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:issueId/review/restart',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const project = params['project'] as string;\n const issueId = params['issueId'] as string;\n const body = yield* readJsonBody;\n const { model, harness } = body as { model?: string; harness?: 'claude-code' | 'pi' | 'codex' };\n\n const { killAllReviewerSessions } = yield* Effect.promise(\n () => import('../../../../lib/cloister/review-agent.js'),\n );\n const killResult = yield* killAllReviewerSessions(project, issueId);\n\n // PAN-1862: do NOT wipe here. The review session (state.json + saved session id) is preserved\n // so spawnReviewRoleForIssue can RESUME it — keeping the prior review's context so a restart\n // with the same model checks the fix instead of re-researching the whole diff. It wipes +\n // fresh-spawns internally ONLY when the harness/model actually changed.\n\n // Resolve workspace info for re-dispatch\n const projectConfig = resolveProjectFromIssueSync(issueId);\n if (!projectConfig) {\n return jsonResponse({ error: `Cannot resolve project for ${issueId}` }, { status: 404 });\n }\n const workspacePath = join(projectConfig.projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`);\n if (!existsSync(workspacePath)) {\n return jsonResponse({ error: `Workspace not found: ${workspacePath}` }, { status: 404 });\n }\n\n // Detect branch from the primary code repo; fall back to configured source branch.\n const primaryRepo = resolveWorkspaceRepoRootsSync(issueId, workspacePath)[0];\n let branch = primaryRepo.sourceBranch;\n try {\n const { stdout } = yield* Effect.promise(() => execAsync(\n 'git branch --show-current',\n { cwd: resolvePrimaryWorkspaceRepoDirSync(issueId, workspacePath), encoding: 'utf-8', timeout: 5000 },\n ));\n branch = stdout.trim() || primaryRepo.sourceBranch;\n } catch { /* non-fatal */ }\n\n // PAN-1048 R3: review now spawns through the role primitive.\n const { spawnReviewRoleForIssue } = yield* Effect.promise(\n () => import('../../../../lib/cloister/review-agent.js'),\n );\n const prUrl = getReviewStatusSync(issueId)?.prUrl;\n const result = yield* spawnReviewRoleForIssue({\n issueId,\n workspace: workspacePath,\n branch,\n prUrl,\n model,\n harness,\n });\n\n if (result.gated) {\n return jsonResponse({\n success: false,\n gated: true,\n message: result.message,\n killed: killResult.killed,\n wiped: [],\n model: model ?? undefined,\n harness: harness ?? undefined,\n }, { status: 409 });\n }\n\n return jsonResponse({\n success: result.success,\n message: result.message,\n killed: killResult.killed,\n wiped: [],\n model: model ?? undefined,\n harness: harness ?? undefined,\n });\n })),\n);\n\n// ─── Route: POST /api/specialists/:project/:issueId/reviewer/:role/restart ───\n//\n// PAN-3368: convoy reviewers are independent role sessions again. Restarting one\n// lane preserves completed sibling reports and gives operators the surgical recovery\n// that the CLI's long-advertised --role flag promises.\n\nconst postProjectReviewerRoleRestartRoute = HttpRouter.add(\n 'POST',\n '/api/specialists/:project/:issueId/reviewer/:role/restart',\n httpHandler(Effect.gen(function* () {\n const params = yield* HttpRouter.params;\n const issueId = (params['issueId'] as string).toUpperCase();\n const role = params['role'] as string;\n if (!(REVIEW_SUB_ROLES as readonly string[]).includes(role)) {\n return jsonResponse(\n { error: `Invalid reviewer role: ${role}`, allowed: REVIEW_SUB_ROLES },\n { status: 400 },\n );\n }\n\n const parentId = `agent-${issueId.toLowerCase()}-review`;\n const reviewerId = `${parentId}-${role}`;\n const parentState = getAgentStateSync(parentId);\n const parent = parentState\n ? yield* Effect.promise(() => resolveReviewParentRunState(parentState))\n : null;\n if (!parent?.reviewRunId || !parent.workspace) {\n return jsonResponse(\n { error: `Active review run not found for ${issueId}` },\n { status: 409 },\n );\n }\n\n const body = yield* readJsonBody;\n const { model } = body as { model?: string };\n const reviewer = getAgentStateSync(reviewerId);\n yield* Effect.promise(() => Effect.runPromise(killSession(reviewerId)).catch(() => undefined));\n\n const { spawnReviewSubRoleForIssue } = yield* Effect.promise(\n () => import('../../../../lib/cloister/review-agent.js'),\n );\n const result = yield* spawnReviewSubRoleForIssue({\n issueId,\n workspace: parent.workspace,\n subRole: role as ReviewSubRole,\n runId: parent.reviewRunId,\n ...(reviewer?.reviewRunId === parent.reviewRunId && reviewer.reviewOutputPath\n ? { outputPath: reviewer.reviewOutputPath }\n : {}),\n contextManifestPath: parent.reviewContextManifestPath,\n synthesisAgentId: parentId,\n ...(model ? { model } : {}),\n allowHost: parent.hostOverride ?? false,\n });\n\n return jsonResponse(\n {\n success: result.success,\n message: result.message,\n error: result.error,\n restarted: role,\n },\n result.success ? undefined : { status: 500 },\n );\n })),\n);\n\n// ─── Route: GET /api/models/resolve ──────────────────────────────────────────\n// Returns the resolved default model for each session/work type.\n\nconst getModelsResolveRoute = HttpRouter.add(\n 'GET',\n '/api/models/resolve',\n httpHandler(Effect.gen(function* () {\n const config = loadConfigSync().config;\n\n const routes = [\n { key: 'role:plan', role: 'plan' },\n { key: 'role:work', role: 'work' },\n { key: 'role:work.inspect', role: 'work', subRole: 'inspect' },\n { key: 'role:work.inspect-deep', role: 'work', subRole: 'inspect-deep' },\n { key: 'role:strike', role: 'strike' },\n { key: 'role:review', role: 'review' },\n { key: 'role:review.correctness', role: 'review', subRole: 'correctness' },\n { key: 'role:review.security', role: 'review', subRole: 'security' },\n { key: 'role:review.performance', role: 'review', subRole: 'performance' },\n { key: 'role:review.requirements', role: 'review', subRole: 'requirements' },\n { key: 'role:test', role: 'test' },\n { key: 'role:ship', role: 'ship' },\n ] as const;\n\n const resolved: Record<string, string | null> = {};\n for (const route of routes) {\n try {\n resolved[route.key] = resolveModel(route.role, (route as { subRole?: string }).subRole, config);\n } catch {\n resolved[route.key] = null;\n }\n }\n\n return jsonResponse(resolved);\n })),\n);\n\nexport const specialistsProjectRouteLayer = Layer.mergeAll(\n // PAN-1048 R1: postProjectSpecialistSpawnRoute removed (see above).\n getProjectSpecialistStatusRoute,\n postProjectSpecialistKillRoute,\n getProjectSpecialistRunsRoute,\n getProjectSpecialistRunStreamRoute, // /runs/:runId/stream — before /runs/:runId\n getProjectSpecialistRunRoute, // /runs/:runId\n postProjectSpecialistRunTerminateRoute, // /runs/:runId/terminate\n postProjectSpecialistGracePauseRoute, // /grace/pause\n postProjectSpecialistGraceResumeRoute, // /grace/resume\n postProjectSpecialistGraceExitRoute, // /grace/exit\n getProjectSpecialistGraceRoute, // /grace\n getProjectSpecialistContextRoute, // /context\n postProjectSpecialistContextRegenerateRoute, // /context/regenerate\n postProjectSpecialistCompleteRoute, // /complete\n getProjectSpecialistLatestLogRoute, // /latest-log\n postProjectSpecialistLogsCleanupRoute, // /logs/cleanup\n postProjectSpecialistResetSessionRoute, // /reset-session\n postProjectReviewRestartRoute, // /:project/:issueId/review/restart\n postProjectReviewerRoleRestartRoute, // /:project/:issueId/reviewer/:role/restart\n getModelsResolveRoute, // /models/resolve\n);\n","/**\n * Specialists route module — Effect HttpRouter.Layer (PAN-428 B9)\n *\n * Implements all /api/specialists/* endpoints from the Express server:\n * GET /api/specialists\n * POST /api/specialists/reset-all\n * POST /api/specialists/done\n * POST /api/specialists/logs/cleanup-all\n * GET /api/specialists/queues\n * GET /api/specialists/projects\n * POST /api/specialists/:name/wake\n * POST /api/specialists/:name/reset\n * POST /api/specialists/:name/init\n * POST /api/specialists/:name/report-status\n * GET /api/specialists/:name/cost\n * GET /api/specialists/:name/queue\n * POST /api/specialists/:name/queue\n * DELETE /api/specialists/:name/queue/:itemId\n * PUT /api/specialists/:name/queue/reorder\n * POST /api/specialists/:name/auto-complete\n * GET /api/specialists/:project/:issueId/:type/status\n * POST /api/specialists/:project/:issueId/:type/kill\n * GET /api/specialists/:project/:type/queue\n * POST /api/specialists/:project/:type/spawn\n * GET /api/specialists/:project/:type/runs\n * GET /api/specialists/:project/:type/runs/:runId\n * GET /api/specialists/:project/:type/runs/:runId/stream\n * POST /api/specialists/:project/:type/runs/:runId/terminate\n * POST /api/specialists/:project/:type/grace/pause\n * POST /api/specialists/:project/:type/grace/resume\n * POST /api/specialists/:project/:type/grace/exit\n * GET /api/specialists/:project/:type/grace\n * GET /api/specialists/:project/:type/context\n * POST /api/specialists/:project/:type/context/regenerate\n * POST /api/specialists/:project/:type/complete\n * GET /api/specialists/:project/:type/latest-log\n * POST /api/specialists/:project/:type/logs/cleanup\n */\n\nimport { Layer } from 'effect';\n\nimport { specialistsLegacyRouteLayer } from './specialists/legacy-routes.js';\nimport { specialistsProjectRouteLayer } from './specialists/project-routes.js';\n\nexport {\n _serverManagedMerges,\n firePostMergeLifecycle,\n validateSpecialistAutoCompleteMetadata,\n} from './specialists/shared.js';\n\nexport const specialistsRouteLayer = Layer.mergeAll(\n specialistsLegacyRouteLayer,\n specialistsProjectRouteLayer,\n);\n\nexport default specialistsRouteLayer;\n"],"mappings":"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"}
|