pi-deck 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../packages/server/src/plan-service.ts", "../packages/server/src/job-service.ts", "../packages/server/src/index.ts", "../packages/server/src/config.ts", "../packages/server/src/directory-browser.ts", "../packages/server/src/workspace-manager.ts", "../packages/server/src/session-orchestrator.ts", "../packages/server/src/pi-session.ts", "../packages/server/src/git-info.ts", "../packages/server/src/web-tui-components.ts", "../packages/server/src/web-extension-ui.ts", "../packages/server/src/ui-state.ts", "../packages/server/src/sync/SyncState.ts", "../packages/server/src/sync/SQLiteStore.ts", "../packages/server/src/sync/SyncManager.ts", "../packages/server/src/sync/SyncIntegration.ts", "../packages/server/src/sync/FileWatcher.ts", "../packages/server/src/sync/PlanJobWatcher.ts"],
4
+ "sourcesContent": ["import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync, watch } from 'fs';\nimport { join, basename, resolve } from 'path';\nimport { homedir } from 'os';\nimport type { PlanInfo, PlanTask, PlanFrontmatter, PlanStatus, ActivePlanState } from '@pi-deck/shared';\n\n// ============================================================================\n// Plan Parsing\n// ============================================================================\n\n/**\n * Parse YAML-ish frontmatter from a markdown string.\n * Handles the simple key: value format used in plan files.\n */\nexport function parseFrontmatter(content: string): { frontmatter: PlanFrontmatter; bodyStart: number } {\n const frontmatter: PlanFrontmatter = {};\n \n if (!content.startsWith('---')) {\n return { frontmatter, bodyStart: 0 };\n }\n \n const endIndex = content.indexOf('\\n---', 3);\n if (endIndex === -1) {\n return { frontmatter, bodyStart: 0 };\n }\n \n const fmBlock = content.slice(4, endIndex); // skip opening '---\\n'\n const lines = fmBlock.split('\\n');\n \n for (const line of lines) {\n const match = line.match(/^(\\w+)\\s*:\\s*(.*)$/);\n if (!match) continue;\n const [, key, rawValue] = match;\n const value = rawValue.trim();\n \n switch (key) {\n case 'title':\n frontmatter.title = value;\n break;\n case 'status':\n if (['draft', 'active', 'complete'].includes(value)) {\n frontmatter.status = value as PlanStatus;\n }\n break;\n case 'created':\n frontmatter.created = value;\n break;\n case 'completed':\n frontmatter.completed = value;\n break;\n case 'summary':\n frontmatter.summary = value;\n break;\n }\n }\n \n // bodyStart is the character after the closing '---\\n'\n const bodyStart = endIndex + 4; // skip '\\n---'\n return { frontmatter, bodyStart: Math.min(bodyStart, content.length) };\n}\n\n/**\n * Parse tasks (checkboxes) from markdown content.\n */\nexport function parseTasks(content: string): PlanTask[] {\n const tasks: PlanTask[] = [];\n const lines = content.split('\\n');\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n // Match lines like \"- [ ] Task text\" or \" - [x] Sub-task\"\n const match = line.match(/^(\\s*)- \\[([ xX])\\]\\s+(.*)$/);\n if (match) {\n const [, indent, checkChar, text] = match;\n const depth = Math.floor(indent.length / 2);\n tasks.push({\n text,\n done: checkChar.toLowerCase() === 'x',\n depth,\n line: i,\n });\n }\n }\n \n return tasks;\n}\n\n/**\n * Parse a plan file into a PlanInfo object.\n */\nexport function parsePlan(filePath: string, content: string): PlanInfo {\n const { frontmatter } = parseFrontmatter(content);\n const tasks = parseTasks(content);\n const fileName = basename(filePath, '.md');\n \n // Extract title from first H1 heading if not in frontmatter\n let title = frontmatter.title;\n if (!title) {\n const h1Match = content.match(/^#\\s+(.+)$/m);\n title = h1Match ? h1Match[1] : fileName;\n }\n \n const doneCount = tasks.filter(t => t.done).length;\n \n return {\n path: filePath,\n fileName,\n title,\n status: frontmatter.status || 'draft',\n frontmatter,\n tasks,\n taskCount: tasks.length,\n doneCount,\n };\n}\n\n/**\n * Update a task's checked state in plan content.\n * Returns the updated content string.\n */\nexport function updateTaskInContent(content: string, lineNumber: number, done: boolean): string {\n const lines = content.split('\\n');\n if (lineNumber < 0 || lineNumber >= lines.length) {\n return content;\n }\n \n const line = lines[lineNumber];\n if (done) {\n lines[lineNumber] = line.replace(/- \\[ \\]/, '- [x]');\n } else {\n lines[lineNumber] = line.replace(/- \\[[xX]\\]/, '- [ ]');\n }\n \n return lines.join('\\n');\n}\n\n/**\n * Update the frontmatter status in plan content.\n */\nexport function updateFrontmatterStatus(\n content: string,\n status: PlanStatus,\n extra?: { completed?: string; summary?: string }\n): string {\n const { frontmatter } = parseFrontmatter(content);\n \n if (!content.startsWith('---')) {\n // No frontmatter \u2014 prepend it\n const lines = ['---', `status: ${status}`];\n if (extra?.completed) lines.push(`completed: ${extra.completed}`);\n if (extra?.summary) lines.push(`summary: ${extra.summary}`);\n lines.push('---', '');\n return lines.join('\\n') + content;\n }\n \n const endIndex = content.indexOf('\\n---', 3);\n if (endIndex === -1) return content;\n \n let fmBlock = content.slice(4, endIndex);\n \n // Update or add status\n if (fmBlock.match(/^status\\s*:/m)) {\n fmBlock = fmBlock.replace(/^status\\s*:.*$/m, `status: ${status}`);\n } else {\n fmBlock += `\\nstatus: ${status}`;\n }\n \n // Add completed datetime\n if (extra?.completed) {\n if (fmBlock.match(/^completed\\s*:/m)) {\n fmBlock = fmBlock.replace(/^completed\\s*:.*$/m, `completed: ${extra.completed}`);\n } else {\n fmBlock += `\\ncompleted: ${extra.completed}`;\n }\n }\n \n // Add summary\n if (extra?.summary) {\n if (fmBlock.match(/^summary\\s*:/m)) {\n fmBlock = fmBlock.replace(/^summary\\s*:.*$/m, `summary: ${extra.summary}`);\n } else {\n fmBlock += `\\nsummary: ${extra.summary}`;\n }\n }\n \n return `---\\n${fmBlock}\\n---${content.slice(endIndex + 4)}`;\n}\n\n// ============================================================================\n// Plan Discovery\n// ============================================================================\n\n/**\n * Get the plan directories for a workspace.\n * Returns both ~/plans/<workspace-name>/ and <workspace>/.pi/plans/\n */\nexport function getPlanDirectories(workspacePath: string): string[] {\n const workspaceName = basename(workspacePath);\n const dirs: string[] = [];\n \n // Global: ~/plans/<workspace-name>/\n const globalDir = join(homedir(), 'plans', workspaceName);\n dirs.push(globalDir);\n \n // Local: <workspace>/.pi/plans/\n const localDir = join(workspacePath, '.pi', 'plans');\n dirs.push(localDir);\n \n return dirs;\n}\n\n/**\n * Discover all plan files for a workspace.\n */\nexport function discoverPlans(workspacePath: string): PlanInfo[] {\n const dirs = getPlanDirectories(workspacePath);\n const plans: PlanInfo[] = [];\n \n for (const dir of dirs) {\n if (!existsSync(dir)) continue;\n \n try {\n const files = readdirSync(dir).filter(f => f.endsWith('.md'));\n for (const file of files) {\n const filePath = resolve(join(dir, file));\n try {\n const content = readFileSync(filePath, 'utf-8');\n plans.push(parsePlan(filePath, content));\n } catch (err) {\n console.warn(`[PlanService] Failed to parse plan file: ${filePath}`, err);\n }\n }\n } catch (err) {\n console.warn(`[PlanService] Failed to read plan directory: ${dir}`, err);\n }\n }\n \n // Sort: active first, then by filename descending (newest first)\n plans.sort((a, b) => {\n if (a.status === 'active' && b.status !== 'active') return -1;\n if (b.status === 'active' && a.status !== 'active') return 1;\n return b.fileName.localeCompare(a.fileName);\n });\n \n return plans;\n}\n\n/**\n * Read a plan file and parse it.\n */\nexport function readPlan(planPath: string): { content: string; plan: PlanInfo } {\n const content = readFileSync(planPath, 'utf-8');\n const plan = parsePlan(planPath, content);\n return { content, plan };\n}\n\n/**\n * Write plan content to a file.\n */\nexport function writePlan(planPath: string, content: string): PlanInfo {\n writeFileSync(planPath, content, 'utf-8');\n return parsePlan(planPath, content);\n}\n\n// ============================================================================\n// Active Plan Management\n// ============================================================================\n\n/**\n * Build the system prompt prefix for an active plan.\n */\nexport function buildActivePlanPrompt(planPath: string): string {\n return `<active_plan>\nYou have an active plan at: ${planPath}\nRead this plan and work through the tasks. As you complete each task, update the plan file by checking off the corresponding checkbox (change \\`- [ ]\\` to \\`- [x]\\`).\nWhen all tasks are complete, let the user know the plan is finished.\n</active_plan>`;\n}\n\n/**\n * Get active plan state from a plan file.\n */\nexport function getActivePlanState(planPath: string): ActivePlanState | null {\n if (!existsSync(planPath)) return null;\n \n try {\n const content = readFileSync(planPath, 'utf-8');\n const plan = parsePlan(planPath, content);\n return {\n planPath: plan.path,\n title: plan.title,\n tasks: plan.tasks,\n taskCount: plan.taskCount,\n doneCount: plan.doneCount,\n };\n } catch {\n return null;\n }\n}\n", "import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync, renameSync } from 'fs';\nimport { join, basename, resolve, dirname } from 'path';\nimport { homedir } from 'os';\nimport YAML from 'yaml';\nimport type { JobPhase, JobFrontmatter, JobInfo, JobTask, PlanStatus } from '@pi-deck/shared';\n\n// Re-export parseTasks from plan-service (same format)\nexport { parseTasks } from './plan-service.js';\nimport { parseTasks } from './plan-service.js';\n\n// ============================================================================\n// Phase Helpers\n// ============================================================================\n\nconst PHASE_ORDER: JobPhase[] = ['backlog', 'planning', 'ready', 'executing', 'review', 'complete'];\n\n/** Map legacy plan `status` to job `phase` */\nfunction statusToPhase(status: PlanStatus): JobPhase {\n switch (status) {\n case 'draft': return 'backlog';\n case 'active': return 'executing';\n case 'complete': return 'complete';\n default: return 'backlog';\n }\n}\n\nexport function getNextPhase(current: JobPhase): JobPhase | null {\n const idx = PHASE_ORDER.indexOf(current);\n if (idx < 0 || idx >= PHASE_ORDER.length - 1) return null;\n return PHASE_ORDER[idx + 1];\n}\n\nexport function getPreviousPhase(current: JobPhase): JobPhase | null {\n const idx = PHASE_ORDER.indexOf(current);\n if (idx <= 0) return null;\n return PHASE_ORDER[idx - 1];\n}\n\n// ============================================================================\n// Frontmatter Parsing (uses `yaml` library)\n// ============================================================================\n\nfunction normalizeTags(tags: unknown): string[] {\n if (!Array.isArray(tags)) return [];\n\n const normalized: string[] = [];\n const seen = new Set<string>();\n\n for (const raw of tags) {\n const tag = String(raw).trim();\n if (!tag) continue;\n\n const key = tag.toLowerCase();\n if (seen.has(key)) continue;\n\n seen.add(key);\n normalized.push(tag);\n }\n\n return normalized;\n}\n\n/**\n * Extract the raw YAML block and body start position from markdown with frontmatter.\n * Returns null if no valid frontmatter delimiters found.\n */\nfunction extractFrontmatterBlock(content: string): { yamlBlock: string; bodyStart: number } | null {\n if (!content.startsWith('---')) return null;\n\n const endIndex = content.indexOf('\\n---', 3);\n if (endIndex === -1) return null;\n\n const yamlBlock = content.slice(4, endIndex); // skip opening '---\\n'\n const bodyStart = Math.min(endIndex + 4, content.length); // skip '\\n---'\n return { yamlBlock, bodyStart };\n}\n\nexport function parseJobFrontmatter(content: string): { frontmatter: JobFrontmatter; bodyStart: number } {\n const frontmatter: JobFrontmatter = {};\n\n const block = extractFrontmatterBlock(content);\n if (!block) return { frontmatter, bodyStart: 0 };\n\n let parsed: Record<string, unknown>;\n try {\n parsed = YAML.parse(block.yamlBlock) ?? {};\n } catch {\n // Malformed YAML \u2014 treat as no frontmatter\n return { frontmatter, bodyStart: 0 };\n }\n\n if (typeof parsed !== 'object' || parsed === null) {\n return { frontmatter, bodyStart: 0 };\n }\n\n const str = (v: unknown): string | undefined =>\n v != null ? String(v) : undefined;\n\n if (parsed.title != null) frontmatter.title = str(parsed.title);\n if (typeof parsed.phase === 'string' && PHASE_ORDER.includes(parsed.phase as JobPhase)) {\n frontmatter.phase = parsed.phase as JobPhase;\n }\n frontmatter.tags = normalizeTags(parsed.tags);\n if (typeof parsed.status === 'string' && ['draft', 'active', 'complete'].includes(parsed.status)) {\n frontmatter.status = parsed.status as PlanStatus;\n }\n if (parsed.created != null) frontmatter.created = str(parsed.created);\n if (parsed.updated != null) frontmatter.updated = str(parsed.updated);\n if (parsed.completedAt != null) frontmatter.completedAt = str(parsed.completedAt);\n // Legacy field\n if (parsed.completed != null && frontmatter.completedAt == null) {\n frontmatter.completedAt = str(parsed.completed);\n }\n if (parsed.planningSessionId != null) frontmatter.planningSessionId = str(parsed.planningSessionId);\n if (parsed.executionSessionId != null) frontmatter.executionSessionId = str(parsed.executionSessionId);\n if (parsed.reviewSessionId != null) frontmatter.reviewSessionId = str(parsed.reviewSessionId);\n\n return { frontmatter, bodyStart: block.bodyStart };\n}\n\n// ============================================================================\n// Job Parsing\n// ============================================================================\n\nexport function parseJob(filePath: string, content: string): JobInfo {\n const { frontmatter } = parseJobFrontmatter(content);\n const tasks = parseTasks(content);\n const fileName = basename(filePath, '.md');\n\n // Determine title: frontmatter > first H1 > filename\n let title = frontmatter.title;\n if (!title) {\n const h1Match = content.match(/^#\\s+(.+)$/m);\n title = h1Match ? h1Match[1] : fileName;\n }\n\n // Determine phase: explicit phase > mapped status > backlog\n let phase: JobPhase = 'backlog';\n if (frontmatter.phase) {\n phase = frontmatter.phase;\n } else if (frontmatter.status) {\n phase = statusToPhase(frontmatter.status);\n }\n\n const tags = normalizeTags(frontmatter.tags);\n const doneCount = tasks.filter(t => t.done).length;\n const updatedAt = frontmatter.updated || frontmatter.created || new Date().toISOString();\n\n return {\n path: filePath,\n fileName,\n title,\n phase,\n tags,\n frontmatter: {\n ...frontmatter,\n tags,\n },\n tasks,\n taskCount: tasks.length,\n doneCount,\n updatedAt,\n };\n}\n\n// ============================================================================\n// Frontmatter Update\n// ============================================================================\n\n/**\n * Update or set fields in the YAML frontmatter of a job file.\n * Creates frontmatter if it doesn't exist.\n * Uses the `yaml` library for correct parsing and serialization.\n */\nexport function updateJobFrontmatter(\n content: string,\n updates: Record<string, string | string[] | undefined>,\n): string {\n const block = extractFrontmatterBlock(content);\n\n let existing: Record<string, unknown> = {};\n let body = content;\n\n if (block) {\n try {\n existing = YAML.parse(block.yamlBlock) ?? {};\n } catch {\n existing = {};\n }\n body = content.slice(block.bodyStart);\n }\n\n for (const [key, value] of Object.entries(updates)) {\n if (value === undefined) continue;\n existing[key] = value;\n }\n\n const yamlStr = YAML.stringify(existing).trimEnd();\n return `---\\n${yamlStr}\\n---${body}`;\n}\n\n// ============================================================================\n// Task Update (reuse from plan-service)\n// ============================================================================\n\nexport function updateTaskInContent(content: string, lineNumber: number, done: boolean): string {\n const lines = content.split('\\n');\n if (lineNumber < 0 || lineNumber >= lines.length) return content;\n\n const line = lines[lineNumber];\n if (done) {\n lines[lineNumber] = line.replace(/- \\[ \\]/, '- [x]');\n } else {\n lines[lineNumber] = line.replace(/- \\[[xX]\\]/, '- [ ]');\n }\n\n return lines.join('\\n');\n}\n\n// ============================================================================\n// Job Discovery\n// ============================================================================\n\nexport function getJobDirectories(workspacePath: string): string[] {\n const workspaceName = basename(workspacePath);\n const dirs: string[] = [];\n\n // Primary: ~/.pi/agent/jobs/<workspace-name>/\n dirs.push(join(homedir(), '.pi', 'agent', 'jobs', workspaceName));\n\n // Local: <workspace>/.pi/jobs/\n dirs.push(join(workspacePath, '.pi', 'jobs'));\n\n return dirs;\n}\n\nexport function discoverJobs(workspacePath: string): JobInfo[] {\n const dirs = getJobDirectories(workspacePath);\n const jobs: JobInfo[] = [];\n const seenPaths = new Set<string>();\n\n for (const dir of dirs) {\n if (!existsSync(dir)) continue;\n\n try {\n const files = readdirSync(dir).filter(f => f.endsWith('.md'));\n for (const file of files) {\n const filePath = resolve(join(dir, file));\n if (seenPaths.has(filePath)) continue;\n seenPaths.add(filePath);\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n jobs.push(parseJob(filePath, content));\n } catch (err) {\n console.warn(`[JobService] Failed to parse job file: ${filePath}`, err);\n }\n }\n } catch (err) {\n console.warn(`[JobService] Failed to read job directory: ${dir}`, err);\n }\n }\n\n // Sort: active phases first (executing, planning), then by updated descending\n const phaseWeight: Record<JobPhase, number> = {\n executing: 0,\n planning: 1,\n review: 2,\n ready: 3,\n backlog: 4,\n complete: 5,\n };\n\n jobs.sort((a, b) => {\n const pw = phaseWeight[a.phase] - phaseWeight[b.phase];\n if (pw !== 0) return pw;\n return b.updatedAt.localeCompare(a.updatedAt);\n });\n\n return jobs;\n}\n\n// ============================================================================\n// Job CRUD\n// ============================================================================\n\nexport function readJob(jobPath: string): { content: string; job: JobInfo } {\n const content = readFileSync(jobPath, 'utf-8');\n const job = parseJob(jobPath, content);\n return { content, job };\n}\n\nexport function writeJob(jobPath: string, content: string): JobInfo {\n writeFileSync(jobPath, content, 'utf-8');\n return parseJob(jobPath, content);\n}\n\n/**\n * Create a new job file in the primary jobs directory.\n */\nexport function createJob(workspacePath: string, title: string, description: string, tags?: string[]): { path: string; content: string; job: JobInfo } {\n const workspaceName = basename(workspacePath);\n const jobsDir = join(homedir(), '.pi', 'agent', 'jobs', workspaceName);\n\n // Ensure directory exists\n if (!existsSync(jobsDir)) {\n mkdirSync(jobsDir, { recursive: true });\n }\n\n // Generate filename: YYYYMMDD-<slug>.md\n const now = new Date();\n const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60);\n \n let filePath = join(jobsDir, `${dateStr}-${slug}.md`);\n \n // Handle collision\n let counter = 1;\n while (existsSync(filePath)) {\n filePath = join(jobsDir, `${dateStr}-${slug}-${counter}.md`);\n counter++;\n }\n\n const isoNow = now.toISOString();\n const normalizedTags = normalizeTags(tags);\n const fmObj: Record<string, unknown> = {\n title,\n phase: 'backlog',\n tags: normalizedTags,\n created: isoNow,\n updated: isoNow,\n };\n const yamlStr = YAML.stringify(fmObj).trimEnd();\n const content = [\n `---`,\n yamlStr,\n '---',\n '',\n `# ${title}`,\n '',\n '## Description',\n description,\n '',\n '## Review',\n '',\n ].join('\\n');\n\n writeFileSync(filePath, content, 'utf-8');\n const job = parseJob(filePath, content);\n return { path: filePath, content, job };\n}\n\n/**\n * Promote a job to the next (or specified) phase.\n * Returns the updated content and job info.\n */\nexport function promoteJob(\n jobPath: string,\n toPhase?: JobPhase,\n): { content: string; job: JobInfo } {\n const { content, job } = readJob(jobPath);\n const targetPhase = toPhase || getNextPhase(job.phase);\n\n if (!targetPhase) {\n throw new Error(`Cannot promote job from phase \"${job.phase}\" \u2014 already at final phase`);\n }\n\n const now = new Date().toISOString();\n const updates: Record<string, string | undefined> = {\n phase: targetPhase,\n updated: now,\n };\n\n if (targetPhase === 'complete') {\n updates.completedAt = now;\n }\n\n const updatedContent = updateJobFrontmatter(content, updates);\n const updatedJob = writeJob(jobPath, updatedContent);\n return { content: updatedContent, job: updatedJob };\n}\n\n/**\n * Demote a job to a previous (or specified) phase.\n */\nexport function demoteJob(\n jobPath: string,\n toPhase?: JobPhase,\n): { content: string; job: JobInfo } {\n const { content, job } = readJob(jobPath);\n const targetPhase = toPhase || getPreviousPhase(job.phase);\n\n if (!targetPhase) {\n throw new Error(`Cannot demote job from phase \"${job.phase}\" \u2014 already at first phase`);\n }\n\n const now = new Date().toISOString();\n const updates: Record<string, string | undefined> = {\n phase: targetPhase,\n updated: now,\n };\n\n const updatedContent = updateJobFrontmatter(content, updates);\n const updatedJob = writeJob(jobPath, updatedContent);\n return { content: updatedContent, job: updatedJob };\n}\n\n/**\n * Store a session ID in the job frontmatter.\n */\nexport function setJobSessionId(\n jobPath: string,\n field: 'planningSessionId' | 'executionSessionId' | 'reviewSessionId',\n sessionId: string,\n): void {\n const { content } = readJob(jobPath);\n const updatedContent = updateJobFrontmatter(content, {\n [field]: sessionId,\n updated: new Date().toISOString(),\n });\n writeFileSync(jobPath, updatedContent, 'utf-8');\n}\n\n// ============================================================================\n// Archive Helpers\n// ============================================================================\n\n/**\n * Get the archived subdirectory for a given job directory.\n */\nfunction getArchivedDir(jobDir: string): string {\n return join(jobDir, 'archived');\n}\n\n/**\n * Archive a job by moving its file into the archived/ subdirectory.\n * Returns the new path.\n */\nexport function archiveJob(jobPath: string): string {\n const dir = dirname(jobPath);\n const file = basename(jobPath);\n const archivedDir = getArchivedDir(dir);\n\n if (!existsSync(archivedDir)) {\n mkdirSync(archivedDir, { recursive: true });\n }\n\n const newPath = join(archivedDir, file);\n renameSync(jobPath, newPath);\n return newPath;\n}\n\n/**\n * Unarchive a job by moving it back from the archived/ subdirectory.\n * Returns the new path.\n */\nexport function unarchiveJob(jobPath: string): string {\n const archivedDir = dirname(jobPath);\n const parentDir = dirname(archivedDir);\n const file = basename(jobPath);\n\n const newPath = join(parentDir, file);\n renameSync(jobPath, newPath);\n return newPath;\n}\n\n/**\n * Discover archived jobs across all job directories for a workspace.\n */\nexport function discoverArchivedJobs(workspacePath: string): JobInfo[] {\n const dirs = getJobDirectories(workspacePath);\n const jobs: JobInfo[] = [];\n\n for (const dir of dirs) {\n const archivedDir = getArchivedDir(dir);\n if (!existsSync(archivedDir)) continue;\n\n try {\n const files = readdirSync(archivedDir).filter(f => f.endsWith('.md'));\n for (const file of files) {\n const filePath = resolve(join(archivedDir, file));\n try {\n const content = readFileSync(filePath, 'utf-8');\n jobs.push(parseJob(filePath, content));\n } catch (err) {\n console.warn(`[JobService] Failed to parse archived job: ${filePath}`, err);\n }\n }\n } catch (err) {\n console.warn(`[JobService] Failed to read archived directory: ${archivedDir}`, err);\n }\n }\n\n jobs.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));\n return jobs;\n}\n\n// ============================================================================\n// Active Job Helpers\n// ============================================================================\n\n/**\n * Build a system context block describing the job system for any session.\n * Injected into the first prompt of a new session so the agent knows how to manage jobs.\n */\nexport function buildJobSystemContext(workspacePath: string): string | null {\n const dirs = getJobDirectories(workspacePath);\n const primaryDir = dirs[0]; // ~/.pi/agent/jobs/<workspace>/\n const jobs = discoverJobs(workspacePath);\n\n // Build a brief listing of current jobs\n const jobLines = jobs.map(j => ` - [${j.phase}] \"${j.title}\" \u2192 ${j.path}`).join('\\n');\n const jobListing = jobs.length > 0\n ? `\\nCurrent jobs:\\n${jobLines}`\n : '\\nNo jobs exist yet.';\n\n return `<job_system>\nYou have access to a job management system. Jobs are markdown files with YAML frontmatter stored in: ${primaryDir}\n\n## Job File Format\n\\`\\`\\`markdown\n---\ntitle: \"Job Title\"\nphase: backlog # backlog \u2192 planning \u2192 ready \u2192 executing \u2192 review \u2192 complete\ntags:\n - feature\n - frontend\ncreated: 2026-01-15T10:00:00.000Z\nupdated: 2026-01-15T10:00:00.000Z\n---\n\n# Job Title\n\n## Description\nWhat needs to be done.\n\n## Plan\n- [ ] Task 1\n- [ ] Task 2\n- [x] Completed task\n\n## Review\n- Run /skill:code-review on all changed files\n\\`\\`\\`\n\n## Managing Jobs\n- **Create a job**: Write a new .md file to ${primaryDir} with the frontmatter format above. Use filename format: YYYYMMDD-slug.md\n- **List jobs**: Read files from ${primaryDir}\n- **Update phase**: Edit the \\`phase\\` field in frontmatter. Update \\`updated\\` timestamp.\n- **Check off tasks**: Change \\`- [ ]\\` to \\`- [x]\\` in the job file.\n- **Complete a job**: Set phase to \"complete\" and add \\`completedAt\\` to frontmatter.\n\n## Phase Lifecycle\nbacklog \u2192 planning \u2192 ready \u2192 executing \u2192 review \u2192 complete\n\n${jobListing}\n</job_system>`;\n}\n\n/**\n * Build the system prompt for a planning conversation.\n */\nexport function buildPlanningPrompt(jobPath: string): string {\n return `<active_job phase=\"planning\">\nYou have a job to plan at: ${jobPath}\nRead the job file. It contains a title and description.\nYour goal is to create a detailed implementation plan. Ask the user clarifying questions if needed, then write a concrete plan with \\`- [ ]\\` checkbox tasks back into the job file under a \"## Plan\" section.\nGroup tasks under \\`### Phase\\` headings. Keep tasks concise and actionable (start with a verb).\nWhen you're done writing the plan, let the user know so they can review and iterate or mark it as ready.\n</active_job>`;\n}\n\n/**\n * Build the system prompt for an execution conversation.\n */\nexport function buildExecutionPrompt(jobPath: string): string {\n return `<active_job phase=\"executing\">\nYou have a job to execute at: ${jobPath}\nRead the job file. It contains a plan with \\`- [ ]\\` checkbox tasks.\nWork through each task systematically. As you complete each one, update the job file by checking off the corresponding checkbox (change \\`- [ ]\\` to \\`- [x]\\`).\nWhen all tasks are complete, let the user know the job is ready for review.\n</active_job>`;\n}\n\n/**\n * Extract the `## Review` section from a job file's content.\n * Returns the raw text of the review section (after the heading, up to the next ## heading or EOF),\n * or null if no review section exists.\n */\nexport function extractReviewSection(content: string): string | null {\n // Match \"## Review\" heading (case-insensitive)\n const reviewMatch = content.match(/^## Review\\s*$/im);\n if (!reviewMatch || reviewMatch.index === undefined) return null;\n\n const startIndex = reviewMatch.index + reviewMatch[0].length;\n const rest = content.slice(startIndex);\n\n // Find the next ## heading (end of review section)\n const nextHeading = rest.match(/^## /m);\n const sectionText = nextHeading && nextHeading.index !== undefined\n ? rest.slice(0, nextHeading.index)\n : rest;\n\n const trimmed = sectionText.trim();\n return trimmed.length > 0 ? trimmed : null;\n}\n\n/**\n * Build the system prompt for a review conversation.\n * Reads the job file and extracts the ## Review section to tell the agent what to do.\n */\nexport function buildReviewPrompt(jobPath: string): string {\n const content = readFileSync(jobPath, 'utf-8');\n const reviewSection = extractReviewSection(content);\n\n if (!reviewSection) {\n return `<active_job phase=\"review\">\nYou have a job to review at: ${jobPath}\nRead the job file and perform a general review of the completed work.\nWhen the review is complete, let the user know.\n</active_job>`;\n }\n\n return `<active_job phase=\"review\">\nYou have a job to review at: ${jobPath}\nRead the job file first to understand the full context.\n\nThen execute the following review steps:\n\n${reviewSection}\n\nWork through each review step. When all review steps are complete, let the user know the review is done.\n</active_job>`;\n}\n\n/**\n * Build the finalize prompt \u2014 sent after review completes.\n * Asks the agent to update the job doc with final remarks, links, and artifacts.\n */\nexport function buildFinalizePrompt(jobPath: string): string {\n return `<active_job phase=\"finalize\">\nThe review for this job is complete. Now finalize the job at: ${jobPath}\n\nPlease update the job file with a ## Summary section at the end containing:\n- A brief summary of what was accomplished\n- Links to any pull requests created\n- Links to any other important artifacts (docs, configs, etc.)\n- Any notes for future reference\n\nThen mark all remaining tasks as done if they aren't already.\nUpdate the \\`updated\\` timestamp in the frontmatter.\n</active_job>`;\n}\n\n/**\n * Get active job states for a workspace (jobs in planning, executing, or review phase).\n */\nexport function getActiveJobStates(workspacePath: string): import('@pi-deck/shared').ActiveJobState[] {\n const jobs = discoverJobs(workspacePath);\n const activePhases: import('@pi-deck/shared').JobPhase[] = ['planning', 'executing', 'review'];\n\n return jobs\n .filter(j => activePhases.includes(j.phase))\n .map(j => ({\n jobPath: j.path,\n title: j.title,\n phase: j.phase,\n tasks: j.tasks,\n taskCount: j.taskCount,\n doneCount: j.doneCount,\n sessionSlotId: j.phase === 'planning'\n ? j.frontmatter.planningSessionId\n : j.phase === 'review'\n ? j.frontmatter.reviewSessionId\n : j.frontmatter.executionSessionId,\n }));\n}\n", "import express from 'express';\nimport cors from 'cors';\nimport { createServer } from 'http';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { fileURLToPath } from 'url';\nimport { dirname, join, resolve, sep } from 'path';\nimport { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from 'fs';\nimport { unlink } from 'fs/promises';\nimport { spawn } from 'child_process';\nimport { homedir } from 'os';\nimport { SessionManager } from '@mariozechner/pi-coding-agent';\nimport { loadConfig } from './config.js';\nimport { DirectoryBrowser } from './directory-browser.js';\nimport { getWorkspaceManager } from './workspace-manager.js';\nimport { getUIStateStore } from './ui-state.js';\nimport { getGitChangedFiles, getGitChangedDirectories, getFileDiff, getGitBranch, getGitWorktree } from './git-info.js';\nimport { discoverPlans, readPlan, writePlan, parsePlan, updateTaskInContent, getActivePlanState, buildActivePlanPrompt, updateFrontmatterStatus } from './plan-service.js';\nimport {\n discoverJobs, readJob, writeJob, createJob, promoteJob, demoteJob,\n updateTaskInContent as updateJobTaskInContent, setJobSessionId,\n updateJobFrontmatter,\n buildPlanningPrompt, buildExecutionPrompt, buildReviewPrompt, buildFinalizePrompt,\n buildJobSystemContext,\n getActiveJobStates, parseJob, extractReviewSection,\n archiveJob, unarchiveJob, discoverArchivedJobs,\n} from './job-service.js';\nimport type { SessionOrchestrator } from './session-orchestrator.js';\nimport type { WsClientMessage, WsServerEvent, ActivePlanState, ActiveJobState } from '@pi-deck/shared';\nimport { SyncIntegration } from './sync/index.js';\n\n// Load configuration\nconst config = loadConfig();\n\n// Initialize sync system\nconst syncDbPath = join(homedir(), '.pi', 'pi-deck-sync.db');\nconst syncIntegration = new SyncIntegration(syncDbPath);\nconsole.log(`[Sync] Initialized sync database at ${syncDbPath}`);\nconst PORT = config.port;\n\nconst app = express();\napp.use(cors());\napp.use(express.json());\n\n// Serve static files in production\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n// When run via the CLI bundle, PI_DECK_CLIENT_DIST is set; otherwise fall back to monorepo layout.\nconst clientDistPath = process.env.PI_DECK_CLIENT_DIST || join(__dirname, '../../client/dist');\n\nif (existsSync(clientDistPath)) {\n console.log(`[Server] Serving static files from ${clientDistPath}`);\n app.use(express.static(clientDistPath));\n}\n\nconst server = createServer(app);\nconst wss = new WebSocketServer({ server, path: '/ws' });\n\n// Create shared services (singletons)\nconst directoryBrowser = new DirectoryBrowser(config.allowedDirectories);\nconst uiStateStore = getUIStateStore();\nconst workspaceManager = getWorkspaceManager(config.allowedDirectories);\n\n// Wire up sync integration\nworkspaceManager.setSyncIntegration(syncIntegration);\n\n// Track which workspaces each WebSocket is attached to\nconst clientWorkspaces = new Map<WebSocket, Set<string>>();\n\n// Route questionnaire responses robustly by toolCallId (survives client/workspace races)\nconst pendingQuestionnaireRoutes = new Map<string, { workspaceId: string; slotId: string }>();\n\n/**\n * Broadcast an event to all clients attached to a specific workspace\n */\nfunction broadcastToWorkspace(workspaceId: string, event: WsServerEvent): void {\n for (const [ws, workspaceIds] of clientWorkspaces.entries()) {\n if (workspaceIds.has(workspaceId) && ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(event));\n }\n }\n}\n\n// Forward workspace manager events to connected clients\nworkspaceManager.on('event', (event: WsServerEvent) => {\n // Track questionnaire routing by toolCallId\n if (event.type === 'questionnaireRequest') {\n pendingQuestionnaireRoutes.set(event.toolCallId, {\n workspaceId: event.workspaceId,\n slotId: event.sessionSlotId || 'default',\n });\n }\n\n // Broadcast to all clients that are attached to this workspace\n if ('workspaceId' in event && event.workspaceId) {\n const workspaceId = event.workspaceId;\n for (const [ws, workspaceIds] of clientWorkspaces.entries()) {\n if (workspaceIds.has(workspaceId) && ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(event));\n }\n }\n }\n});\n\n// Log buffered events (optional - for debugging)\nworkspaceManager.on('bufferedEvent', (event: WsServerEvent) => {\n if ('workspaceId' in event) {\n console.log(`[WorkspaceManager] Buffering event for disconnected workspace: ${event.type}`);\n }\n});\n\n// ============================================================================\n// Auto-promote jobs when agent sessions end\n// ============================================================================\n\n// Track review sessions that have already received the finalize nudge\nconst finalizedSessions = new Set<string>();\n\nworkspaceManager.on('event', (event: WsServerEvent) => {\n if (event.type !== 'agentEnd' || !('workspaceId' in event)) return;\n\n const { workspaceId, sessionSlotId } = event as { workspaceId: string; sessionSlotId?: string };\n if (!sessionSlotId) return;\n\n // Run async logic outside the synchronous handler\n setImmediate(async () => {\n try {\n const workspace = workspaceManager.getWorkspace(workspaceId);\n if (!workspace) return;\n\n // Find jobs whose session slot matches the one that just ended\n const jobs = discoverJobs(workspace.path);\n const matchingJob = jobs.find(j =>\n (j.phase === 'executing' && j.frontmatter.executionSessionId === sessionSlotId) ||\n (j.phase === 'review' && j.frontmatter.reviewSessionId === sessionSlotId)\n );\n if (!matchingJob) return;\n\n if (matchingJob.phase === 'executing') {\n // Check if the job has a ## Review section\n const { content } = readJob(matchingJob.path);\n const reviewSection = extractReviewSection(content);\n\n if (reviewSection) {\n console.log(`[Jobs] Auto-promoting job \"${matchingJob.title}\" from executing \u2192 review`);\n const { job } = promoteJob(matchingJob.path);\n\n // Spin up review agent session\n const orchestrator = workspaceManager.getOrchestrator(workspaceId);\n const reviewSlotId = `job-review-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;\n const slotResult = await orchestrator.createSlot(reviewSlotId);\n\n // Apply stored thinking level preference\n const uiState = uiStateStore.loadState();\n const storedThinkingLevel = uiState.thinkingLevels[workspace.path];\n if (storedThinkingLevel) {\n orchestrator.setThinkingLevel(reviewSlotId, storedThinkingLevel);\n slotResult.state = await orchestrator.getState(reviewSlotId);\n }\n\n // Store review session ID in frontmatter\n setJobSessionId(matchingJob.path, 'reviewSessionId', reviewSlotId);\n\n // Broadcast slot created + job promoted\n broadcastToWorkspace(workspaceId, {\n type: 'sessionSlotCreated',\n workspaceId,\n sessionSlotId: reviewSlotId,\n state: slotResult.state,\n messages: slotResult.messages,\n });\n\n syncIntegration.createSlot(workspaceId, reviewSlotId);\n syncIntegration.setQueuedMessages(workspaceId, reviewSlotId, { steering: [], followUp: [] });\n\n broadcastToWorkspace(workspaceId, {\n type: 'jobPromoted',\n workspaceId,\n jobPath: matchingJob.path,\n job,\n sessionSlotId: reviewSlotId,\n });\n\n // Refresh jobs list + active jobs\n const updatedJobs = discoverJobs(workspace.path);\n broadcastToWorkspace(workspaceId, { type: 'jobsList', workspaceId, jobs: updatedJobs });\n syncIntegration.setJobs(workspaceId, updatedJobs);\n\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(workspaceId, { type: 'activeJob', workspaceId, activeJobs });\n syncIntegration.setActiveJobs(workspaceId, activeJobs);\n\n // Send the review prompt\n const reviewPrompt = buildReviewPrompt(matchingJob.path);\n const initialMessage = `${reviewPrompt}\\n\\nPlease read the job file and execute the review steps.`;\n await orchestrator.prompt(reviewSlotId, initialMessage);\n } else {\n // No review section \u2014 auto-promote executing \u2192 complete (skip review)\n console.log(`[Jobs] Auto-promoting job \"${matchingJob.title}\" from executing \u2192 complete (no review section)`);\n const { job } = promoteJob(matchingJob.path, 'complete');\n\n broadcastToWorkspace(workspaceId, {\n type: 'jobPromoted',\n workspaceId,\n jobPath: matchingJob.path,\n job,\n });\n\n const updatedJobs = discoverJobs(workspace.path);\n broadcastToWorkspace(workspaceId, { type: 'jobsList', workspaceId, jobs: updatedJobs });\n syncIntegration.setJobs(workspaceId, updatedJobs);\n\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(workspaceId, { type: 'activeJob', workspaceId, activeJobs });\n syncIntegration.setActiveJobs(workspaceId, activeJobs);\n }\n } else if (matchingJob.phase === 'review') {\n const reviewSlotId = matchingJob.frontmatter.reviewSessionId;\n const alreadyFinalized = reviewSlotId && finalizedSessions.has(reviewSlotId);\n\n if (!alreadyFinalized && reviewSlotId) {\n // First agentEnd after review \u2014 send finalize nudge\n console.log(`[Jobs] Sending finalize nudge for job \"${matchingJob.title}\"`);\n finalizedSessions.add(reviewSlotId);\n\n const orchestrator = workspaceManager.getOrchestrator(workspaceId);\n const finalizePrompt = buildFinalizePrompt(matchingJob.path);\n await orchestrator.prompt(reviewSlotId, finalizePrompt);\n } else {\n // Second agentEnd (after finalize) \u2014 promote to complete\n console.log(`[Jobs] Auto-promoting job \"${matchingJob.title}\" from review \u2192 complete`);\n if (reviewSlotId) finalizedSessions.delete(reviewSlotId);\n\n const { job } = promoteJob(matchingJob.path);\n\n broadcastToWorkspace(workspaceId, {\n type: 'jobPromoted',\n workspaceId,\n jobPath: matchingJob.path,\n job,\n });\n\n // Refresh jobs list + active jobs\n const updatedJobs = discoverJobs(workspace.path);\n broadcastToWorkspace(workspaceId, { type: 'jobsList', workspaceId, jobs: updatedJobs });\n syncIntegration.setJobs(workspaceId, updatedJobs);\n\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(workspaceId, { type: 'activeJob', workspaceId, activeJobs });\n syncIntegration.setActiveJobs(workspaceId, activeJobs);\n }\n }\n } catch (err) {\n console.error(`[Jobs] Auto-promote failed:`, err);\n }\n });\n});\n\n// Health check endpoint\napp.get('/health', (_req: express.Request, res: express.Response) => {\n res.json({\n status: 'ok',\n allowedDirectories: config.allowedDirectories,\n activeWorkspaces: workspaceManager.listWorkspaces().length,\n });\n});\n\n// WebSocket connection handler\nwss.on('connection', async (ws) => {\n console.log('[WS] Client connected');\n\n // Track workspaces this client is attached to\n clientWorkspaces.set(ws, new Set());\n\n // Send initial connected event with persisted UI state\n const uiState = uiStateStore.loadState();\n \n // Also send list of existing workspaces (sessions that are still running)\n const existingWorkspaces = workspaceManager.listWorkspaces();\n \n send(ws, {\n type: 'connected',\n workspaces: existingWorkspaces,\n allowedRoots: config.allowedDirectories,\n homeDirectory: homedir(),\n uiState,\n });\n\n // Handle incoming messages\n ws.on('message', async (data) => {\n try {\n const message: WsClientMessage = JSON.parse(data.toString());\n await handleMessage(ws, message);\n } catch (error) {\n console.error('[WS] Error handling message:', error);\n send(ws, {\n type: 'error',\n message: error instanceof Error ? error.message : 'Unknown error',\n });\n }\n });\n\n // Clean up on disconnect - detach from all workspaces but don't close them\n ws.on('close', () => {\n console.log('[WS] Client disconnected');\n \n // Detach from all workspaces this client was attached to\n const workspaceIds = clientWorkspaces.get(ws);\n if (workspaceIds) {\n for (const workspaceId of workspaceIds) {\n workspaceManager.detachFromWorkspace(workspaceId);\n }\n }\n clientWorkspaces.delete(ws);\n });\n\n ws.on('error', (error) => {\n console.error('[WS] WebSocket error:', error);\n });\n});\n\n/**\n * Helper to get the session slot ID from a message, defaulting to 'default'\n */\nfunction getSlotId(message: { sessionSlotId?: string }): string {\n return message.sessionSlotId || 'default';\n}\n\nasync function handleMessage(\n ws: WebSocket,\n message: WsClientMessage\n) {\n // Sync protocol messages are handled by SyncIntegration's WebSocket listener.\n // Ignore them here to avoid noisy \"Unknown message type\" logs.\n const rawType = (message as { type?: string }).type;\n if (rawType === 'ack' || rawType === 'sync' || rawType === 'mutate') {\n return;\n }\n\n switch (message.type) {\n // ========================================================================\n // Workspace management\n // ========================================================================\n case 'openWorkspace': {\n const result = await workspaceManager.openWorkspace(message.path);\n \n // Track that this client is attached to this workspace\n clientWorkspaces.get(ws)?.add(result.workspace.id);\n \n // Register with sync system\n const syncClientId = syncIntegration.registerClient(ws, result.workspace.id);\n console.log(`[Sync] Client ${syncClientId} registered for workspace ${result.workspace.id}`);\n \n // Get startup info from the orchestrator\n const orchestrator = workspaceManager.getOrchestrator(result.workspace.id);\n const startupInfo = await orchestrator.getStartupInfo();\n\n // Ensure workspace exists in sync state.\n syncIntegration.createWorkspace(result.workspace.id, message.path);\n\n // Seed sessions in sync state early so snapshot/delta can drive sidebar state.\n try {\n const sessions = await orchestrator.listSessions();\n syncIntegration.setSessions(result.workspace.id, sessions);\n } catch {\n // Ignore session list failures during attach/open; client can refresh later.\n }\n\n // Ensure all current slots exist in sync state and seed queued state.\n for (const slot of orchestrator.listSlots()) {\n syncIntegration.createSlot(result.workspace.id, slot.slotId);\n try {\n const queued = orchestrator.getQueuedMessages(slot.slotId);\n syncIntegration.setQueuedMessages(result.workspace.id, slot.slotId, queued);\n } catch {\n // Slot may disappear during reconnect races; ignore.\n }\n }\n \n // Apply stored thinking level preference if one exists for this workspace\n // Only apply if this is a newly created workspace (not existing)\n if (!result.isExisting) {\n const uiState = uiStateStore.loadState();\n const storedThinkingLevel = uiState.thinkingLevels[message.path];\n if (storedThinkingLevel) {\n orchestrator.setThinkingLevel('default', storedThinkingLevel);\n // Update the state to reflect the applied thinking level\n result.state = await orchestrator.getState('default');\n }\n }\n \n send(ws, {\n type: 'workspaceOpened',\n workspace: result.workspace,\n state: result.state,\n messages: result.messages,\n startupInfo,\n });\n \n // If there are buffered events (from when no client was connected), replay them\n if (result.bufferedEvents.length > 0) {\n console.log(`[WS] Replaying ${result.bufferedEvents.length} buffered events`);\n for (const event of result.bufferedEvents) {\n send(ws, event);\n }\n }\n \n // If this was an existing workspace that was already running, log it\n if (result.isExisting) {\n console.log(`[WS] Client attached to existing workspace: ${result.workspace.path}`);\n }\n \n // Seed sync workspace UI state (right pane + tab layouts) for this workspace\n const currentUiState = uiStateStore.loadState();\n syncIntegration.setWorkspaceUI(\n result.workspace.id,\n result.workspace.path,\n currentUiState.rightPaneByWorkspace[result.workspace.path] ?? false,\n currentUiState.paneTabsByWorkspace[result.workspace.path] ?? [],\n currentUiState.activePaneTabByWorkspace[result.workspace.path] ?? null,\n );\n\n // Seed sync state with current plans/jobs snapshot for this workspace\n const plans = discoverPlans(message.path);\n syncIntegration.setPlans(result.workspace.id, plans);\n const jobs = discoverJobs(message.path);\n syncIntegration.setJobs(result.workspace.id, jobs);\n\n // Send and sync active plan state\n const activePlanPath = uiStateStore.getActivePlan(message.path);\n const activePlanState = activePlanPath ? getActivePlanState(activePlanPath) : null;\n send(ws, {\n type: 'activePlan',\n workspaceId: result.workspace.id,\n activePlan: activePlanState,\n });\n syncIntegration.setActivePlan(result.workspace.id, activePlanState);\n\n // Send and sync active job states (jobs in planning/executing phase)\n try {\n const activeJobs = getActiveJobStates(message.path);\n send(ws, {\n type: 'activeJob',\n workspaceId: result.workspace.id,\n activeJobs,\n });\n syncIntegration.setActiveJobs(result.workspace.id, activeJobs);\n } catch {\n // Jobs directory may not exist yet; still clear active jobs in sync state\n syncIntegration.setActiveJobs(result.workspace.id, []);\n }\n break;\n }\n\n case 'closeWorkspace': {\n // Broadcast close event to ALL clients attached to this workspace BEFORE closing\n // This ensures all clients (including other browser tabs) are notified\n const closeEvent: WsServerEvent = {\n type: 'workspaceClosed',\n workspaceId: message.workspaceId,\n };\n broadcastToWorkspace(message.workspaceId, closeEvent);\n \n // Detach ALL clients from this workspace (not just the requesting one)\n for (const [client, workspaceIds] of clientWorkspaces.entries()) {\n workspaceIds.delete(message.workspaceId);\n }\n \n // Actually close and dispose the workspace\n workspaceManager.closeWorkspace(message.workspaceId);\n syncIntegration.closeWorkspace(message.workspaceId);\n syncIntegration.stopFileWatching(message.workspaceId);\n break;\n }\n\n case 'listWorkspaces': {\n send(ws, {\n type: 'workspacesList',\n workspaces: workspaceManager.listWorkspaces(),\n });\n break;\n }\n\n case 'browseDirectory': {\n if (message.path) {\n const entries = directoryBrowser.browse(message.path);\n send(ws, {\n type: 'directoryList',\n path: message.path,\n entries,\n });\n } else {\n // Return allowed roots\n send(ws, {\n type: 'directoryList',\n path: '/',\n entries: directoryBrowser.listRoots(),\n allowedRoots: directoryBrowser.getAllowedDirectories(),\n });\n }\n break;\n }\n\n // ========================================================================\n // Session slot management\n // ========================================================================\n case 'createSessionSlot': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const result = await orchestrator.createSlot(message.slotId);\n \n // Apply stored thinking level preference for the workspace to the new slot\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (workspace) {\n const uiState = uiStateStore.loadState();\n const storedThinkingLevel = uiState.thinkingLevels[workspace.path];\n if (storedThinkingLevel) {\n orchestrator.setThinkingLevel(result.slotId, storedThinkingLevel);\n // Update the state to reflect the applied thinking level\n result.state = await orchestrator.getState(result.slotId);\n }\n }\n \n syncIntegration.createSlot(message.workspaceId, result.slotId);\n syncIntegration.setQueuedMessages(message.workspaceId, result.slotId, { steering: [], followUp: [] });\n\n send(ws, {\n type: 'sessionSlotCreated',\n workspaceId: message.workspaceId,\n sessionSlotId: result.slotId,\n state: result.state,\n messages: result.messages,\n });\n break;\n }\n\n case 'closeSessionSlot': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n orchestrator.closeSlot(message.sessionSlotId);\n syncIntegration.deleteSlot(message.workspaceId, message.sessionSlotId);\n send(ws, {\n type: 'sessionSlotClosed',\n workspaceId: message.workspaceId,\n sessionSlotId: message.sessionSlotId,\n });\n break;\n }\n\n case 'listSessionSlots': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n send(ws, {\n type: 'sessionSlotsList',\n workspaceId: message.workspaceId,\n slots: orchestrator.listSlots(),\n });\n break;\n }\n\n // ========================================================================\n // UI State persistence\n // ========================================================================\n case 'getUIState': {\n send(ws, {\n type: 'uiState',\n state: uiStateStore.loadState(),\n });\n break;\n }\n\n case 'saveUIState': {\n const updated = uiStateStore.updateState(message.state);\n send(ws, {\n type: 'uiState',\n state: updated,\n });\n\n // Keep sync state aligned with workspace-scoped UI layout state.\n for (const workspace of workspaceManager.listWorkspaces()) {\n syncIntegration.setWorkspaceUI(\n workspace.id,\n workspace.path,\n updated.rightPaneByWorkspace[workspace.path] ?? false,\n updated.paneTabsByWorkspace[workspace.path] ?? [],\n updated.activePaneTabByWorkspace[workspace.path] ?? null,\n );\n }\n break;\n }\n\n case 'setTheme': {\n uiStateStore.setThemeId(message.themeId);\n break;\n }\n\n case 'setSidebarWidth': {\n uiStateStore.setSidebarWidth(message.width);\n break;\n }\n\n case 'setDraftInput': {\n uiStateStore.setDraftInput(message.workspacePath, message.value);\n break;\n }\n\n case 'setActiveSession': {\n uiStateStore.setActiveSession(message.workspacePath, message.sessionId);\n break;\n }\n\n case 'setActiveModel': {\n uiStateStore.setActiveModel(message.workspacePath, message.provider, message.modelId);\n break;\n }\n\n case 'setThinkingLevelPref': {\n uiStateStore.setThinkingLevel(message.workspacePath, message.level);\n break;\n }\n\n // ========================================================================\n // Session-slot-scoped operations (via orchestrator)\n // ========================================================================\n case 'prompt': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n\n // Inject job system context on the first prompt of a new session\n let promptMessage = message.message;\n if (workspace) {\n const messages = orchestrator.getMessages(slotId);\n if (messages.length === 0) {\n const jobContext = buildJobSystemContext(workspace.path);\n if (jobContext) {\n promptMessage = `${jobContext}\\n\\n${promptMessage}`;\n }\n }\n }\n\n await orchestrator.prompt(slotId, promptMessage, message.images);\n break;\n }\n\n case 'steer': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n await orchestrator.steer(slotId, message.message, message.images);\n\n // Broadcast updated queue state so all clients stay in sync.\n const steerQueue = orchestrator.getQueuedMessages(slotId);\n syncIntegration.setQueuedMessages(message.workspaceId, slotId, steerQueue);\n broadcastToWorkspace(message.workspaceId, {\n type: 'queuedMessages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n steering: steerQueue.steering,\n followUp: steerQueue.followUp,\n });\n break;\n }\n\n case 'followUp': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n await orchestrator.followUp(slotId, message.message);\n\n // Broadcast updated queue state so all clients stay in sync.\n const followQueue = orchestrator.getQueuedMessages(slotId);\n syncIntegration.setQueuedMessages(message.workspaceId, slotId, followQueue);\n broadcastToWorkspace(message.workspaceId, {\n type: 'queuedMessages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n steering: followQueue.steering,\n followUp: followQueue.followUp,\n });\n break;\n }\n\n case 'abort': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n await orchestrator.abort(slotId);\n break;\n }\n\n case 'setModel': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n await orchestrator.setModel(slotId, message.provider, message.modelId);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n break;\n }\n\n case 'setThinkingLevel': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n orchestrator.setThinkingLevel(slotId, message.level);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n break;\n }\n\n case 'newSession': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n await orchestrator.newSession(slotId);\n // Send updated state\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n // Send empty messages for new session\n send(ws, {\n type: 'messages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n messages: orchestrator.getMessages(slotId),\n });\n // Refresh sessions list to include the new session (async to avoid blocking)\n scheduleSessionsRefresh(ws, message.workspaceId, orchestrator);\n break;\n }\n\n case 'switchSession': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n let sessionPath = message.sessionId;\n const looksLikePath = sessionPath.includes('/') || sessionPath.includes('\\\\') || sessionPath.endsWith('.jsonl');\n if (!looksLikePath) {\n const sessions = await orchestrator.listSessions();\n const match = sessions.find((session) => session.id === sessionPath);\n if (match?.path) {\n sessionPath = match.path;\n } else {\n console.warn(`[WS] switchSession: session path not found for id ${sessionPath}`);\n break;\n }\n }\n await orchestrator.switchSession(slotId, sessionPath);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n send(ws, {\n type: 'messages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n messages: orchestrator.getMessages(slotId),\n });\n break;\n }\n\n case 'compact': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n // If there's an active plan, include it in compaction instructions\n // so the plan reference survives compaction\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n let compactInstructions = message.customInstructions;\n if (workspace) {\n const planPath = uiStateStore.getActivePlan(workspace.path);\n if (planPath) {\n const planNote = `IMPORTANT: There is an active plan at ${planPath}. Preserve this plan reference in the summary so the agent continues working on it after compaction.`;\n compactInstructions = compactInstructions\n ? `${compactInstructions}\\n\\n${planNote}`\n : planNote;\n }\n }\n await orchestrator.compact(slotId, compactInstructions);\n break;\n }\n\n case 'getState': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n break;\n }\n\n case 'getMessages': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n send(ws, {\n type: 'messages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n messages: orchestrator.getMessages(slotId),\n });\n break;\n }\n\n case 'getSessions': {\n // Sessions list is workspace-wide\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const sessions = await orchestrator.listSessions();\n send(ws, {\n type: 'sessions',\n workspaceId: message.workspaceId,\n sessions,\n });\n syncIntegration.setSessions(message.workspaceId, sessions);\n break;\n }\n\n case 'getModels': {\n // Models list is workspace-wide\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n send(ws, {\n type: 'models',\n workspaceId: message.workspaceId,\n models: await orchestrator.getAvailableModels(),\n });\n break;\n }\n\n case 'getCommands': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n send(ws, {\n type: 'commands',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n commands: orchestrator.getCommands(slotId),\n });\n break;\n }\n\n // ========================================================================\n // Session operations\n // ========================================================================\n case 'fork': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n try {\n const result = await orchestrator.fork(slotId, message.entryId);\n send(ws, {\n type: 'forkResult',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n success: true,\n text: result.text,\n });\n // Refresh state and messages after fork\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n send(ws, {\n type: 'messages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n messages: orchestrator.getMessages(slotId),\n });\n } catch (error) {\n send(ws, {\n type: 'forkResult',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n success: false,\n error: error instanceof Error ? error.message : 'Fork failed',\n });\n }\n break;\n }\n\n case 'getForkMessages': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n send(ws, {\n type: 'forkMessages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n messages: orchestrator.getForkMessages(slotId),\n });\n break;\n }\n\n case 'setSessionName': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n orchestrator.setSessionName(slotId, message.name);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n // Also refresh sessions list to show new name (async to avoid blocking)\n scheduleSessionsRefresh(ws, message.workspaceId, orchestrator);\n break;\n }\n\n case 'renameSession': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const sessionInfo = await resolveSessionInfo(orchestrator, message.sessionId, message.sessionPath);\n if (!sessionInfo) {\n send(ws, {\n type: 'error',\n workspaceId: message.workspaceId,\n message: 'Session not found for rename.',\n });\n break;\n }\n const trimmedName = message.name.trim();\n if (!trimmedName) {\n send(ws, {\n type: 'error',\n workspaceId: message.workspaceId,\n message: 'Session name cannot be empty.',\n });\n break;\n }\n const slotStates = await getSlotStates(orchestrator);\n const matchingSlots = slotStates.filter(({ state }) => (\n state.sessionId === sessionInfo.id || state.sessionFile === sessionInfo.path\n ));\n if (matchingSlots.length > 0) {\n for (const { slotId } of matchingSlots) {\n orchestrator.setSessionName(slotId, trimmedName);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n }\n } else {\n const sessionManager = SessionManager.open(sessionInfo.path);\n sessionManager.appendSessionInfo(trimmedName);\n }\n scheduleSessionsRefresh(ws, message.workspaceId, orchestrator);\n break;\n }\n\n case 'deleteSession': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const sessionInfo = await resolveSessionInfo(orchestrator, message.sessionId, message.sessionPath);\n if (!sessionInfo) {\n send(ws, {\n type: 'error',\n workspaceId: message.workspaceId,\n message: 'Session not found for deletion.',\n });\n break;\n }\n const slotStates = await getSlotStates(orchestrator);\n const matchingSlots = slotStates.filter(({ state }) => (\n state.sessionId === sessionInfo.id || state.sessionFile === sessionInfo.path\n ));\n if (matchingSlots.length > 0) {\n for (const { slotId, state } of matchingSlots) {\n if (state.isStreaming) {\n await orchestrator.abort(slotId);\n }\n await orchestrator.newSession(slotId);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n send(ws, {\n type: 'messages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n messages: orchestrator.getMessages(slotId),\n });\n }\n }\n try {\n if (existsSync(sessionInfo.path)) {\n await unlink(sessionInfo.path);\n }\n } catch (error) {\n send(ws, {\n type: 'error',\n workspaceId: message.workspaceId,\n message: error instanceof Error ? error.message : 'Failed to delete session file.',\n });\n } finally {\n scheduleSessionsRefresh(ws, message.workspaceId, orchestrator);\n }\n break;\n }\n\n case 'exportHtml': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n try {\n const path = await orchestrator.exportHtml(slotId, message.outputPath);\n send(ws, {\n type: 'exportHtmlResult',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n success: true,\n path,\n });\n } catch (error) {\n send(ws, {\n type: 'exportHtmlResult',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n success: false,\n error: error instanceof Error ? error.message : 'Export failed',\n });\n }\n break;\n }\n\n // ========================================================================\n // Model/Thinking cycling\n // ========================================================================\n case 'cycleModel': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n await orchestrator.cycleModel(slotId, message.direction);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n break;\n }\n\n case 'cycleThinkingLevel': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n orchestrator.cycleThinkingLevel(slotId);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n break;\n }\n\n // ========================================================================\n // Mode settings\n // ========================================================================\n case 'setSteeringMode': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n orchestrator.setSteeringMode(slotId, message.mode);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n break;\n }\n\n case 'setFollowUpMode': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n orchestrator.setFollowUpMode(slotId, message.mode);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n break;\n }\n\n case 'setAutoCompaction': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n orchestrator.setAutoCompaction(slotId, message.enabled);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n break;\n }\n\n case 'setAutoRetry': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n orchestrator.setAutoRetry(slotId, message.enabled);\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n break;\n }\n\n case 'abortRetry': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n orchestrator.abortRetry(slotId);\n break;\n }\n\n // ========================================================================\n // Bash execution\n // ========================================================================\n case 'bash': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n const excludeFromContext = message.excludeFromContext ?? false;\n send(ws, {\n type: 'bashStart',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n command: message.command,\n excludeFromContext,\n });\n try {\n const result = await orchestrator.executeBash(slotId, message.command, (chunk) => {\n send(ws, {\n type: 'bashOutput',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n chunk,\n });\n }, excludeFromContext);\n send(ws, {\n type: 'bashEnd',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n result,\n });\n } catch (error) {\n send(ws, {\n type: 'bashEnd',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n result: {\n stdout: '',\n stderr: error instanceof Error ? error.message : 'Bash execution failed',\n exitCode: 1,\n signal: null,\n timedOut: false,\n truncated: false,\n },\n });\n }\n break;\n }\n\n case 'abortBash': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n orchestrator.abortBash(slotId);\n break;\n }\n\n // ========================================================================\n // Stats\n // ========================================================================\n case 'getSessionStats': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n send(ws, {\n type: 'sessionStats',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n stats: orchestrator.getSessionStats(slotId),\n });\n break;\n }\n\n case 'getLastAssistantText': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n send(ws, {\n type: 'lastAssistantText',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n text: orchestrator.getLastAssistantText(slotId),\n });\n break;\n }\n\n // ========================================================================\n // Server management\n // ========================================================================\n case 'deploy': {\n // Get project root (2 levels up from dist/index.js)\n const projectRoot = join(__dirname, '../..');\n \n send(ws, {\n type: 'deployStatus',\n status: 'building',\n message: 'Building project...',\n });\n\n console.log('[Deploy] Starting build...');\n \n // Run npm build\n const buildProcess = spawn('npm', ['run', 'build'], {\n cwd: projectRoot,\n shell: true,\n });\n\n let buildOutput = '';\n buildProcess.stdout?.on('data', (data) => {\n buildOutput += data.toString();\n });\n buildProcess.stderr?.on('data', (data) => {\n buildOutput += data.toString();\n });\n\n buildProcess.on('close', (code) => {\n if (code !== 0) {\n console.error('[Deploy] Build failed:', buildOutput);\n send(ws, {\n type: 'deployStatus',\n status: 'error',\n message: `Build failed with code ${code}`,\n });\n return;\n }\n\n console.log('[Deploy] Build complete, restarting...');\n send(ws, {\n type: 'deployStatus',\n status: 'restarting',\n message: 'Build complete. Restarting server...',\n });\n\n // Give the message time to send, then exit\n // launchctl will restart us due to KeepAlive\n setTimeout(() => {\n console.log('[Deploy] Exiting for restart...');\n process.exit(0);\n }, 500);\n });\n break;\n }\n\n case 'updateAllowedRoots': {\n const { roots } = message;\n console.log('[Config] Updating allowed roots:', roots);\n \n // Update config file\n const configPath = join(homedir(), '.pi-deck.json');\n let fileConfig: Record<string, unknown> = {};\n try {\n if (existsSync(configPath)) {\n fileConfig = JSON.parse(readFileSync(configPath, 'utf-8'));\n }\n } catch {\n // Ignore parse errors\n }\n \n fileConfig.allowedDirectories = roots;\n writeFileSync(configPath, JSON.stringify(fileConfig, null, 2));\n console.log('[Config] Saved config to', configPath);\n \n // Note: Requires server restart to take effect\n send(ws, {\n type: 'allowedRootsUpdated',\n roots,\n });\n break;\n }\n\n // ========================================================================\n // Session Tree Navigation\n // ========================================================================\n case 'getSessionTree': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n const { tree, currentLeafId } = orchestrator.getSessionTree(slotId);\n send(ws, {\n type: 'sessionTree',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n tree,\n currentLeafId,\n });\n break;\n }\n\n case 'navigateTree': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n const result = await orchestrator.navigateTree(slotId, message.targetId, message.summarize);\n send(ws, {\n type: 'navigateTreeResult',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n success: result.success,\n editorText: result.editorText,\n error: result.error,\n });\n // Refresh state and messages after navigation\n if (result.success) {\n send(ws, {\n type: 'state',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: await orchestrator.getState(slotId),\n });\n send(ws, {\n type: 'messages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n messages: orchestrator.getMessages(slotId),\n });\n }\n break;\n }\n\n // ========================================================================\n // Copy Last Assistant Text\n // ========================================================================\n case 'copyLastAssistant': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n const text = orchestrator.getLastAssistantText(slotId);\n send(ws, {\n type: 'copyResult',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n success: text !== null,\n text: text ?? undefined,\n error: text === null ? 'No assistant message to copy' : undefined,\n });\n break;\n }\n\n // ========================================================================\n // Queued Messages\n // ========================================================================\n case 'getQueuedMessages': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n const { steering, followUp } = orchestrator.getQueuedMessages(slotId);\n syncIntegration.setQueuedMessages(message.workspaceId, slotId, { steering, followUp });\n send(ws, {\n type: 'queuedMessages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n steering,\n followUp,\n });\n break;\n }\n\n case 'clearQueue': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n const { steering, followUp } = orchestrator.clearQueue(slotId);\n syncIntegration.setQueuedMessages(message.workspaceId, slotId, { steering, followUp });\n broadcastToWorkspace(message.workspaceId, {\n type: 'queuedMessages',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n steering,\n followUp,\n });\n break;\n }\n\n // ========================================================================\n // Scoped Models\n // ========================================================================\n case 'getScopedModels': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n const models = await orchestrator.getScopedModels(slotId);\n send(ws, {\n type: 'scopedModels',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n models,\n });\n break;\n }\n\n case 'setScopedModels': {\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const slotId = getSlotId(message);\n await orchestrator.setScopedModels(slotId, message.models);\n // Return updated scoped models\n const models = await orchestrator.getScopedModels(slotId);\n send(ws, {\n type: 'scopedModels',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n models,\n });\n break;\n }\n\n // ========================================================================\n // Workspace directory listing (for file tree)\n // ========================================================================\n case 'listWorkspaceEntries': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n send(ws, {\n type: 'workspaceEntries',\n workspaceId: message.workspaceId,\n path: message.path || '',\n entries: [],\n requestId: message.requestId,\n });\n break;\n }\n\n const rootPath = resolve(workspace.path);\n const relativePath = (message.path || '').replace(/^\\/+/, '');\n const targetPath = resolve(rootPath, relativePath);\n\n if (targetPath !== rootPath && !targetPath.startsWith(rootPath + sep)) {\n send(ws, {\n type: 'workspaceEntries',\n workspaceId: message.workspaceId,\n path: relativePath,\n entries: [],\n requestId: message.requestId,\n });\n break;\n }\n\n const skipEntries = new Set(['.git', '.pi', 'node_modules', 'dist', 'build', 'coverage']);\n\n // Get git status for the workspace\n const gitChangedFiles = getGitChangedFiles(rootPath);\n const gitChangedDirs = getGitChangedDirectories(rootPath);\n\n try {\n const entries = readdirSync(targetPath, { withFileTypes: true })\n .filter((entry) => !skipEntries.has(entry.name))\n .map((entry) => {\n const entryPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;\n const isDir = entry.isDirectory();\n return {\n name: entry.name,\n path: entryPath,\n isDirectory: isDir,\n gitStatus: isDir ? undefined : gitChangedFiles.get(entryPath),\n hasChanges: isDir ? gitChangedDirs.has(entryPath) : undefined,\n };\n })\n .sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) {\n return a.isDirectory ? -1 : 1;\n }\n return a.name.localeCompare(b.name);\n });\n\n send(ws, {\n type: 'workspaceEntries',\n workspaceId: message.workspaceId,\n path: relativePath,\n entries,\n requestId: message.requestId,\n });\n } catch {\n send(ws, {\n type: 'workspaceEntries',\n workspaceId: message.workspaceId,\n path: relativePath,\n entries: [],\n requestId: message.requestId,\n });\n }\n break;\n }\n\n // ========================================================================\n // Directory watching for file tree (Phase 1 of file watcher)\n // ========================================================================\n case 'watchDirectory': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n\n const rootPath = resolve(workspace.path);\n const relativePath = message.path.replace(/^\\/+/, '');\n const targetPath = resolve(rootPath, relativePath);\n\n // Security check\n if (!targetPath.startsWith(rootPath + sep) && targetPath !== rootPath) {\n break;\n }\n\n // Start watching via sync integration\n syncIntegration.watchDirectory(message.workspaceId, targetPath);\n break;\n }\n\n case 'unwatchDirectory': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n\n const rootPath = resolve(workspace.path);\n const relativePath = message.path.replace(/^\\/+/, '');\n const targetPath = resolve(rootPath, relativePath);\n\n syncIntegration.unwatchDirectory(message.workspaceId, targetPath);\n break;\n }\n\n // ========================================================================\n // Workspace file read (for file preview)\n // ========================================================================\n case 'readWorkspaceFile': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n send(ws, {\n type: 'workspaceFile',\n workspaceId: message.workspaceId,\n path: message.path,\n content: '',\n truncated: false,\n requestId: message.requestId,\n });\n break;\n }\n\n const rootPath = resolve(workspace.path);\n const rawPath = message.path || '';\n // Expand ~/ to the user's home directory\n const expandedPath = rawPath.startsWith('~/') ? join(homedir(), rawPath.slice(2)) : rawPath;\n const isAbsolute = expandedPath.startsWith('/');\n let targetPath: string;\n let displayPath: string;\n\n if (isAbsolute) {\n // Absolute path \u2014 allow if within workspace or allowed directories\n targetPath = resolve(expandedPath);\n displayPath = rawPath;\n const inWorkspace = targetPath.startsWith(rootPath + sep) || targetPath === rootPath;\n const inAllowed = config.allowedDirectories.some(\n (dir: string) => targetPath.startsWith(resolve(dir) + sep) || targetPath === resolve(dir)\n );\n if (!inWorkspace && !inAllowed) {\n send(ws, {\n type: 'workspaceFile',\n workspaceId: message.workspaceId,\n path: displayPath,\n content: '',\n truncated: false,\n requestId: message.requestId,\n });\n break;\n }\n } else {\n // Relative path \u2014 resolve within workspace\n const relativePath = rawPath.replace(/^\\/+/, '');\n targetPath = resolve(rootPath, relativePath);\n displayPath = relativePath;\n if (!relativePath || (targetPath !== rootPath && !targetPath.startsWith(rootPath + sep))) {\n send(ws, {\n type: 'workspaceFile',\n workspaceId: message.workspaceId,\n path: displayPath,\n content: '',\n truncated: false,\n requestId: message.requestId,\n });\n break;\n }\n }\n\n try {\n if (!existsSync(targetPath)) {\n throw new Error('File not found');\n }\n const stat = statSync(targetPath);\n if (stat.isDirectory()) {\n throw new Error('Path is a directory');\n }\n\n const maxBytes = 200 * 1024;\n const raw = readFileSync(targetPath, 'utf-8');\n const truncated = raw.length > maxBytes;\n const content = truncated ? raw.slice(0, maxBytes) : raw;\n\n send(ws, {\n type: 'workspaceFile',\n workspaceId: message.workspaceId,\n path: displayPath,\n content,\n truncated,\n requestId: message.requestId,\n });\n } catch {\n send(ws, {\n type: 'workspaceFile',\n workspaceId: message.workspaceId,\n path: displayPath,\n content: '',\n truncated: false,\n requestId: message.requestId,\n });\n }\n break;\n }\n\n // ========================================================================\n // Git Status (for Git tab in file pane)\n // ========================================================================\n case 'getGitStatus': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n send(ws, {\n type: 'gitStatus',\n workspaceId: message.workspaceId,\n files: [],\n requestId: message.requestId,\n });\n break;\n }\n\n const gitChanges = getGitChangedFiles(workspace.path);\n const files = Array.from(gitChanges.entries()).map(([path, status]) => ({\n path,\n status,\n }));\n const branch = getGitBranch(workspace.path);\n const worktree = getGitWorktree(workspace.path);\n\n send(ws, {\n type: 'gitStatus',\n workspaceId: message.workspaceId,\n files,\n branch,\n worktree,\n requestId: message.requestId,\n });\n break;\n }\n\n case 'getFileDiff': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n send(ws, {\n type: 'fileDiff',\n workspaceId: message.workspaceId,\n path: message.path,\n diff: '',\n requestId: message.requestId,\n });\n break;\n }\n\n const diff = getFileDiff(workspace.path, message.path);\n\n send(ws, {\n type: 'fileDiff',\n workspaceId: message.workspaceId,\n path: message.path,\n diff,\n requestId: message.requestId,\n });\n break;\n }\n\n // ========================================================================\n // File Listing (for @ reference)\n // ========================================================================\n case 'listFiles': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n send(ws, {\n type: 'fileList',\n workspaceId: message.workspaceId,\n files: [],\n requestId: message.requestId,\n });\n break;\n }\n \n // Use find to list files in the workspace\n const { execSync } = await import('child_process');\n const limit = message.limit || 100;\n const query = message.query || '';\n \n try {\n // Find files, excluding common directories\n let cmd = `find . -type f \\\\( -name \"*.ts\" -o -name \"*.tsx\" -o -name \"*.js\" -o -name \"*.jsx\" -o -name \"*.json\" -o -name \"*.md\" -o -name \"*.py\" -o -name \"*.go\" -o -name \"*.rs\" -o -name \"*.java\" -o -name \"*.c\" -o -name \"*.cpp\" -o -name \"*.h\" -o -name \"*.css\" -o -name \"*.html\" -o -name \"*.yml\" -o -name \"*.yaml\" \\\\) ! -path \"*/node_modules/*\" ! -path \"*/.git/*\" ! -path \"*/dist/*\" ! -path \"*/build/*\" | head -${limit * 2}`;\n \n const output = execSync(cmd, {\n cwd: workspace.path,\n encoding: 'utf-8',\n maxBuffer: 10 * 1024 * 1024,\n timeout: 5000,\n });\n \n let files = output.split('\\\\n')\n .filter(Boolean)\n .map(f => f.replace(/^\\\\.\\//, ''))\n .map(path => ({\n path,\n name: path.split('/').pop() || path,\n isDirectory: false,\n }));\n \n // Filter by query if provided\n if (query) {\n const lowerQuery = query.toLowerCase();\n files = files.filter(f => \n f.path.toLowerCase().includes(lowerQuery) ||\n f.name.toLowerCase().includes(lowerQuery)\n );\n }\n \n // Sort: exact name matches first, then path matches\n files.sort((a, b) => {\n if (query) {\n const lowerQuery = query.toLowerCase();\n const aNameMatch = a.name.toLowerCase().startsWith(lowerQuery);\n const bNameMatch = b.name.toLowerCase().startsWith(lowerQuery);\n if (aNameMatch && !bNameMatch) return -1;\n if (!aNameMatch && bNameMatch) return 1;\n }\n return a.path.localeCompare(b.path);\n });\n \n send(ws, {\n type: 'fileList',\n workspaceId: message.workspaceId,\n files: files.slice(0, limit),\n requestId: message.requestId,\n });\n } catch {\n send(ws, {\n type: 'fileList',\n workspaceId: message.workspaceId,\n files: [],\n requestId: message.requestId,\n });\n }\n break;\n }\n\n // ========================================================================\n // Share Session (GitHub Gist) - Not yet implemented\n // ========================================================================\n case 'shareSession': {\n // Share to gist requires GitHub auth - not implementing in this PR\n send(ws, {\n type: 'shareResult',\n workspaceId: message.workspaceId,\n sessionSlotId: getSlotId(message),\n success: false,\n error: 'Share to GitHub Gist is not yet implemented in the web UI',\n });\n break;\n }\n\n // ========================================================================\n // Auth (Login/Logout) - Basic support\n // ========================================================================\n case 'login': {\n // OAuth login requires opening browser - basic implementation\n send(ws, {\n type: 'loginStatus',\n provider: message.provider,\n status: 'error',\n message: 'OAuth login requires browser interaction. Please use the Pi CLI for OAuth login, or set API keys via environment variables.',\n });\n break;\n }\n\n case 'logout': {\n // Logout would clear stored OAuth tokens\n send(ws, {\n type: 'loginStatus',\n provider: message.provider,\n status: 'error',\n message: 'Logout is not yet implemented in the web UI. Please use the Pi CLI.',\n });\n break;\n }\n\n case 'getAuthProviders': {\n // Return list of providers - basic for now\n send(ws, {\n type: 'authProviders',\n providers: [\n { id: 'anthropic', name: 'Anthropic', supportsOAuth: true },\n { id: 'openai', name: 'OpenAI', supportsOAuth: true },\n { id: 'google', name: 'Google', supportsOAuth: true },\n { id: 'github-copilot', name: 'GitHub Copilot', supportsOAuth: true },\n ],\n authenticated: [], // Would need to check AuthStorage\n });\n break;\n }\n\n case 'extensionUIResponse': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n console.warn(`[WS] Workspace not found for extensionUIResponse: ${message.workspaceId}`);\n break;\n }\n const slotId = message.sessionSlotId || 'default';\n workspace.orchestrator.handleExtensionUIResponse(slotId, message.response);\n break;\n }\n\n case 'customUIInput': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n console.warn(`[WS] Workspace not found for customUIInput: ${message.workspaceId}`);\n break;\n }\n const slotId = message.sessionSlotId || 'default';\n workspace.orchestrator.handleCustomUIInput(slotId, message.input);\n break;\n }\n\n case 'questionnaireResponse': {\n // Route by toolCallId first (source of truth), fallback to message workspace/slot.\n const route = pendingQuestionnaireRoutes.get(message.toolCallId);\n const workspaceId = route?.workspaceId || message.workspaceId;\n const slotId = route?.slotId || message.sessionSlotId || 'default';\n\n const workspace = workspaceManager.getWorkspace(workspaceId);\n if (!workspace) {\n console.warn(`[WS] Workspace not found for questionnaireResponse: ${workspaceId}`);\n break;\n }\n\n // Ignore stale/duplicate responses that no longer have a pending resolver.\n if (!workspace.orchestrator.hasPendingQuestionnaire(slotId, message.toolCallId)) {\n console.warn(`[WS] Ignoring stale questionnaireResponse for ${message.toolCallId}`);\n // Drop stale routing entry if present.\n pendingQuestionnaireRoutes.delete(message.toolCallId);\n break;\n }\n\n workspace.orchestrator.handleQuestionnaireResponse(slotId, {\n toolCallId: message.toolCallId,\n answers: message.answers,\n cancelled: message.cancelled,\n });\n pendingQuestionnaireRoutes.delete(message.toolCallId);\n\n // Clear pending UI in sync state only when response was valid.\n syncIntegration.clearPendingUI(workspaceId, slotId);\n console.log(`[Sync] Cleared pending questionnaire for ${workspaceId}/${slotId}`);\n break;\n }\n\n // ========================================================================\n // Plans\n // ========================================================================\n case 'getPlans': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n send(ws, { type: 'plansList', workspaceId: message.workspaceId, plans: [] });\n syncIntegration.setPlans(message.workspaceId, []);\n break;\n }\n const plans = discoverPlans(workspace.path);\n send(ws, { type: 'plansList', workspaceId: message.workspaceId, plans });\n syncIntegration.setPlans(message.workspaceId, plans);\n break;\n }\n\n case 'getPlanContent': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const { content, plan } = readPlan(message.planPath);\n send(ws, {\n type: 'planContent',\n workspaceId: message.workspaceId,\n planPath: message.planPath,\n content,\n plan,\n });\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to read plan: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'savePlan': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const plan = writePlan(message.planPath, message.content);\n broadcastToWorkspace(message.workspaceId, {\n type: 'planSaved',\n workspaceId: message.workspaceId,\n planPath: message.planPath,\n plan,\n });\n\n const plans = discoverPlans(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'plansList', workspaceId: message.workspaceId, plans });\n syncIntegration.setPlans(message.workspaceId, plans);\n\n // If this is the active plan, also send updated active plan state\n const activePlanPath = uiStateStore.getActivePlan(workspace.path);\n if (activePlanPath === message.planPath) {\n const activePlanState = getActivePlanState(message.planPath);\n broadcastToWorkspace(message.workspaceId, {\n type: 'activePlan',\n workspaceId: message.workspaceId,\n activePlan: activePlanState,\n });\n syncIntegration.setActivePlan(message.workspaceId, activePlanState);\n }\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to save plan: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'activatePlan': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n // Persist active plan\n uiStateStore.setActivePlan(workspace.path, message.planPath);\n \n // Update frontmatter to active\n const { content } = readPlan(message.planPath);\n const updatedContent = updateFrontmatterStatus(content, 'active');\n const plan = writePlan(message.planPath, updatedContent);\n \n // Send active plan state\n const activePlanState = getActivePlanState(message.planPath);\n broadcastToWorkspace(message.workspaceId, {\n type: 'activePlan',\n workspaceId: message.workspaceId,\n activePlan: activePlanState,\n });\n syncIntegration.setActivePlan(message.workspaceId, activePlanState);\n \n // Create a new session slot for the plan execution\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n const planSlotId = `plan-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n const slotResult = await orchestrator.createSlot(planSlotId);\n syncIntegration.createSlot(message.workspaceId, planSlotId);\n syncIntegration.setQueuedMessages(message.workspaceId, planSlotId, { steering: [], followUp: [] });\n \n // Apply stored thinking level preference\n const uiState = uiStateStore.loadState();\n const storedThinkingLevel = uiState.thinkingLevels[workspace.path];\n if (storedThinkingLevel) {\n orchestrator.setThinkingLevel(planSlotId, storedThinkingLevel);\n slotResult.state = await orchestrator.getState(planSlotId);\n }\n \n // Send slot created event so client can wire up a new tab\n send(ws, {\n type: 'sessionSlotCreated',\n workspaceId: message.workspaceId,\n sessionSlotId: planSlotId,\n state: slotResult.state,\n messages: slotResult.messages,\n });\n \n // Send the initial prompt with the plan context\n const planPrompt = buildActivePlanPrompt(message.planPath);\n const initialMessage = `${planPrompt}\\n\\nPlease read the plan file and begin working through the tasks.`;\n await orchestrator.prompt(planSlotId, initialMessage);\n \n // Send updated plans list\n const plans = discoverPlans(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'plansList', workspaceId: message.workspaceId, plans });\n syncIntegration.setPlans(message.workspaceId, plans);\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to activate plan: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'deactivatePlan': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n \n const activePlanPath = uiStateStore.getActivePlan(workspace.path);\n if (activePlanPath) {\n try {\n // Update frontmatter to complete\n const { content } = readPlan(activePlanPath);\n const updatedContent = updateFrontmatterStatus(content, 'complete', {\n completed: new Date().toISOString(),\n });\n writePlan(activePlanPath, updatedContent);\n } catch (err) {\n console.warn(`[Plans] Failed to update plan frontmatter: ${err}`);\n }\n }\n \n // Clear active plan\n uiStateStore.setActivePlan(workspace.path, null);\n \n broadcastToWorkspace(message.workspaceId, {\n type: 'activePlan',\n workspaceId: message.workspaceId,\n activePlan: null,\n });\n syncIntegration.setActivePlan(message.workspaceId, null);\n \n // Refresh plans list\n const plans = discoverPlans(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'plansList', workspaceId: message.workspaceId, plans });\n syncIntegration.setPlans(message.workspaceId, plans);\n break;\n }\n\n case 'updatePlanTask': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const { content } = readPlan(message.planPath);\n const updatedContent = updateTaskInContent(content, message.line, message.done);\n const plan = writePlan(message.planPath, updatedContent);\n \n broadcastToWorkspace(message.workspaceId, {\n type: 'planTaskUpdated',\n workspaceId: message.workspaceId,\n planPath: message.planPath,\n plan,\n });\n\n const plans = discoverPlans(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'plansList', workspaceId: message.workspaceId, plans });\n syncIntegration.setPlans(message.workspaceId, plans);\n \n // If this is the active plan, also update active plan state\n const activePlanPath = uiStateStore.getActivePlan(workspace.path);\n if (activePlanPath === message.planPath) {\n const activePlanState = getActivePlanState(message.planPath);\n broadcastToWorkspace(message.workspaceId, {\n type: 'activePlan',\n workspaceId: message.workspaceId,\n activePlan: activePlanState,\n });\n syncIntegration.setActivePlan(message.workspaceId, activePlanState);\n \n // Auto-complete: if all tasks are done, mark plan as complete\n if (activePlanState && activePlanState.taskCount > 0 && activePlanState.doneCount === activePlanState.taskCount) {\n try {\n const { content: currentContent } = readPlan(message.planPath);\n const completedContent = updateFrontmatterStatus(currentContent, 'complete', {\n completed: new Date().toISOString(),\n });\n writePlan(message.planPath, completedContent);\n \n // Deactivate the plan\n uiStateStore.clearActivePlan(workspace.path);\n broadcastToWorkspace(message.workspaceId, {\n type: 'activePlan',\n workspaceId: message.workspaceId,\n activePlan: null,\n });\n syncIntegration.setActivePlan(message.workspaceId, null);\n \n // Refresh plans list to show completed status\n const plansAfterComplete = discoverPlans(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'plansList', workspaceId: message.workspaceId, plans: plansAfterComplete });\n syncIntegration.setPlans(message.workspaceId, plansAfterComplete);\n } catch (completeErr) {\n console.warn(`[Plans] Failed to auto-complete plan: ${completeErr}`);\n }\n }\n }\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to update task: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n // ========================================================================\n // Jobs\n // ========================================================================\n case 'getJobs': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n send(ws, { type: 'jobsList', workspaceId: message.workspaceId, jobs: [] });\n syncIntegration.setJobs(message.workspaceId, []);\n break;\n }\n const jobs = discoverJobs(workspace.path);\n send(ws, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n break;\n }\n\n case 'getJobContent': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const { content, job } = readJob(message.jobPath);\n send(ws, {\n type: 'jobContent',\n workspaceId: message.workspaceId,\n jobPath: message.jobPath,\n content,\n job,\n });\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to read job: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'createJob': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const { path: jobPath, job } = createJob(workspace.path, message.title, message.description, message.tags);\n broadcastToWorkspace(message.workspaceId, {\n type: 'jobSaved',\n workspaceId: message.workspaceId,\n jobPath,\n job,\n });\n // Refresh jobs list\n const jobs = discoverJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(message.workspaceId, {\n type: 'activeJob',\n workspaceId: message.workspaceId,\n activeJobs,\n });\n syncIntegration.setActiveJobs(message.workspaceId, activeJobs);\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to create job: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'saveJob': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const job = writeJob(message.jobPath, message.content);\n broadcastToWorkspace(message.workspaceId, {\n type: 'jobSaved',\n workspaceId: message.workspaceId,\n jobPath: message.jobPath,\n job,\n });\n\n const jobs = discoverJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(message.workspaceId, {\n type: 'activeJob',\n workspaceId: message.workspaceId,\n activeJobs,\n });\n syncIntegration.setActiveJobs(message.workspaceId, activeJobs);\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to save job: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'promoteJob': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const { job } = promoteJob(message.jobPath, message.toPhase);\n let sessionSlotId: string | undefined;\n const orchestrator = workspaceManager.getOrchestrator(message.workspaceId);\n\n // If promoted to planning, executing, or review, spin up a conversation\n if (job.phase === 'planning' || job.phase === 'executing' || job.phase === 'review') {\n\n if (job.phase === 'executing' && job.frontmatter.executionSessionId) {\n // Reuse existing execution session (e.g., demoted from review, now re-promoted)\n sessionSlotId = job.frontmatter.executionSessionId;\n } else if (job.phase === 'review' && job.frontmatter.reviewSessionId) {\n // Reuse existing review session\n sessionSlotId = job.frontmatter.reviewSessionId;\n } else {\n // Create a new session slot\n const slotId = `job-${job.phase}-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;\n const slotResult = await orchestrator.createSlot(slotId);\n sessionSlotId = slotId;\n\n // Apply stored thinking level preference\n const uiState = uiStateStore.loadState();\n const storedThinkingLevel = uiState.thinkingLevels[workspace.path];\n if (storedThinkingLevel) {\n orchestrator.setThinkingLevel(slotId, storedThinkingLevel);\n slotResult.state = await orchestrator.getState(slotId);\n }\n\n // Send slot created event so client can wire up a new tab\n send(ws, {\n type: 'sessionSlotCreated',\n workspaceId: message.workspaceId,\n sessionSlotId: slotId,\n state: slotResult.state,\n messages: slotResult.messages,\n });\n\n // Store the session ID in frontmatter\n const sessionField = job.phase === 'planning'\n ? 'planningSessionId'\n : job.phase === 'review'\n ? 'reviewSessionId'\n : 'executionSessionId';\n setJobSessionId(message.jobPath, sessionField, slotId);\n\n // Send the initial prompt\n const prompt = job.phase === 'planning'\n ? buildPlanningPrompt(message.jobPath)\n : job.phase === 'review'\n ? buildReviewPrompt(message.jobPath)\n : buildExecutionPrompt(message.jobPath);\n const initialMessage = job.phase === 'planning'\n ? `${prompt}\\n\\nPlease read the job file and help me create a plan.`\n : job.phase === 'review'\n ? `${prompt}\\n\\nPlease read the job file and execute the review steps.`\n : `${prompt}\\n\\nPlease read the job file and begin working through the tasks.`;\n await orchestrator.prompt(slotId, initialMessage);\n }\n }\n\n if (sessionSlotId) {\n syncIntegration.createSlot(message.workspaceId, sessionSlotId);\n try {\n const queued = orchestrator.getQueuedMessages(sessionSlotId);\n syncIntegration.setQueuedMessages(message.workspaceId, sessionSlotId, queued);\n } catch {\n syncIntegration.setQueuedMessages(message.workspaceId, sessionSlotId, { steering: [], followUp: [] });\n }\n }\n\n broadcastToWorkspace(message.workspaceId, {\n type: 'jobPromoted',\n workspaceId: message.workspaceId,\n jobPath: message.jobPath,\n job,\n sessionSlotId,\n });\n\n // Refresh jobs list\n const jobs = discoverJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n\n // Send active job states\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(message.workspaceId, {\n type: 'activeJob',\n workspaceId: message.workspaceId,\n activeJobs,\n });\n syncIntegration.setActiveJobs(message.workspaceId, activeJobs);\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to promote job: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'demoteJob': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const { job } = demoteJob(message.jobPath, message.toPhase);\n\n broadcastToWorkspace(message.workspaceId, {\n type: 'jobPromoted', // reuse same event \u2014 it's a phase change\n workspaceId: message.workspaceId,\n jobPath: message.jobPath,\n job,\n // If demoting to executing, provide the existing session slot\n sessionSlotId: job.phase === 'executing' ? job.frontmatter.executionSessionId : undefined,\n });\n\n // Refresh jobs list\n const jobs = discoverJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n\n // Send active job states\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(message.workspaceId, {\n type: 'activeJob',\n workspaceId: message.workspaceId,\n activeJobs,\n });\n syncIntegration.setActiveJobs(message.workspaceId, activeJobs);\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to demote job: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'updateJobTask': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const { content } = readJob(message.jobPath);\n const updatedContent = updateJobTaskInContent(content, message.line, message.done);\n const job = writeJob(message.jobPath, updatedContent);\n\n broadcastToWorkspace(message.workspaceId, {\n type: 'jobTaskUpdated',\n workspaceId: message.workspaceId,\n jobPath: message.jobPath,\n job,\n });\n\n const jobs = discoverJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n\n // Send updated active job states (progress may have changed)\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(message.workspaceId, {\n type: 'activeJob',\n workspaceId: message.workspaceId,\n activeJobs,\n });\n syncIntegration.setActiveJobs(message.workspaceId, activeJobs);\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to update job task: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'deleteJob': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n await unlink(message.jobPath);\n\n // Refresh jobs list + active jobs\n const jobs = discoverJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'activeJob', workspaceId: message.workspaceId, activeJobs });\n syncIntegration.setActiveJobs(message.workspaceId, activeJobs);\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to delete job: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'renameJob': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n const { content } = readJob(message.jobPath);\n const updatedContent = updateJobFrontmatter(content, {\n title: message.newTitle,\n updated: new Date().toISOString(),\n });\n const job = writeJob(message.jobPath, updatedContent);\n\n broadcastToWorkspace(message.workspaceId, {\n type: 'jobSaved',\n workspaceId: message.workspaceId,\n jobPath: message.jobPath,\n job,\n });\n\n // Refresh jobs list + active jobs\n const jobs = discoverJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'activeJob', workspaceId: message.workspaceId, activeJobs });\n syncIntegration.setActiveJobs(message.workspaceId, activeJobs);\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to rename job: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'archiveJob': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n archiveJob(message.jobPath);\n\n // Refresh jobs list + active jobs\n const jobs = discoverJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'activeJob', workspaceId: message.workspaceId, activeJobs });\n syncIntegration.setActiveJobs(message.workspaceId, activeJobs);\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to archive job: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'unarchiveJob': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) break;\n try {\n unarchiveJob(message.jobPath);\n\n // Refresh jobs list + active jobs\n const jobs = discoverJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'jobsList', workspaceId: message.workspaceId, jobs });\n syncIntegration.setJobs(message.workspaceId, jobs);\n\n const activeJobs = getActiveJobStates(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'activeJob', workspaceId: message.workspaceId, activeJobs });\n syncIntegration.setActiveJobs(message.workspaceId, activeJobs);\n\n // Also refresh archived list\n const archivedJobs = discoverArchivedJobs(workspace.path);\n broadcastToWorkspace(message.workspaceId, { type: 'archivedJobsList', workspaceId: message.workspaceId, jobs: archivedJobs });\n } catch (err) {\n send(ws, {\n type: 'error',\n message: `Failed to unarchive job: ${err instanceof Error ? err.message : 'Unknown error'}`,\n workspaceId: message.workspaceId,\n });\n }\n break;\n }\n\n case 'getArchivedJobs': {\n const workspace = workspaceManager.getWorkspace(message.workspaceId);\n if (!workspace) {\n send(ws, { type: 'archivedJobsList', workspaceId: message.workspaceId, jobs: [] });\n break;\n }\n const archivedJobs = discoverArchivedJobs(workspace.path);\n send(ws, { type: 'archivedJobsList', workspaceId: message.workspaceId, jobs: archivedJobs });\n break;\n }\n\n default:\n console.warn('[WS] Unknown message type:', (message as { type: string }).type);\n }\n}\n\nfunction scheduleSessionsRefresh(\n _ws: WebSocket,\n workspaceId: string,\n orchestrator: SessionOrchestrator\n): void {\n setImmediate(async () => {\n try {\n const sessions = await orchestrator.listSessions();\n // Broadcast to all clients in this workspace so sidebars stay in sync\n broadcastToWorkspace(workspaceId, { type: 'sessions', workspaceId, sessions });\n syncIntegration.setSessions(workspaceId, sessions);\n } catch (error) {\n console.error(`[WS] Failed to refresh sessions for ${workspaceId}:`, error);\n }\n });\n}\n\nasync function resolveSessionInfo(\n orchestrator: SessionOrchestrator,\n sessionId?: string,\n sessionPath?: string\n): Promise<{ id: string; path: string } | null> {\n const sessions = await orchestrator.listSessions();\n if (sessionPath) {\n const match = sessions.find((session) => session.path === sessionPath);\n if (match) return { id: match.id, path: match.path };\n }\n if (sessionId) {\n const matchById = sessions.find((session) => session.id === sessionId);\n if (matchById) return { id: matchById.id, path: matchById.path };\n }\n const slotStates = await getSlotStates(orchestrator);\n const slotMatch = slotStates.find(({ state }) => (\n (sessionPath && state.sessionFile === sessionPath) || (sessionId && state.sessionId === sessionId)\n ));\n if (slotMatch?.state.sessionFile) {\n return { id: slotMatch.state.sessionId, path: slotMatch.state.sessionFile };\n }\n return null;\n}\n\nasync function getSlotStates(orchestrator: SessionOrchestrator) {\n const slots = orchestrator.listSlots();\n return Promise.all(slots.map(async (slot) => ({\n slotId: slot.slotId,\n state: await orchestrator.getState(slot.slotId),\n })));\n}\n\nfunction send(ws: WebSocket, event: WsServerEvent) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(event));\n }\n}\n\n// SPA fallback - serve index.html for unmatched routes\nif (existsSync(clientDistPath)) {\n app.get('*', (_req: express.Request, res: express.Response) => {\n res.sendFile(join(clientDistPath, 'index.html'));\n });\n}\n\nserver.listen(PORT, config.host, () => {\n console.log(`[Server] Pi-Deck server running on http://${config.host}:${PORT}`);\n console.log(`[Server] Allowed directories: ${config.allowedDirectories.join(', ')}`);\n console.log(`[Server] WebSocket endpoint: ws://${config.host}:${PORT}/ws`);\n});\n", "import { existsSync, readFileSync, realpathSync } from 'fs';\nimport { homedir } from 'os';\nimport { resolve, dirname, join } from 'path';\n\nexport interface ServerConfig {\n port: number;\n host: string;\n allowedDirectories: string[];\n}\n\n/**\n * Find project root by looking for .git, package.json with workspaces, or .pi directory\n */\nfunction findProjectRoot(startDir: string): string {\n let dir = resolve(startDir);\n const root = resolve('/');\n \n while (dir !== root) {\n // Check for .git (most reliable project root indicator)\n if (existsSync(join(dir, '.git'))) {\n return dir;\n }\n // Check for package.json with workspaces (monorepo root)\n const pkgPath = join(dir, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n if (pkg.workspaces) {\n return dir;\n }\n } catch {\n // Ignore parse errors\n }\n }\n // Check for .pi directory (Pi session root)\n if (existsSync(join(dir, '.pi'))) {\n return dir;\n }\n \n dir = dirname(dir);\n }\n \n // Fallback to original directory\n return startDir;\n}\n\nconst projectRoot = findProjectRoot(process.cwd());\n\nconst DEFAULT_CONFIG: ServerConfig = {\n port: 9741,\n host: '0.0.0.0',\n // Default to detected project root, then home as fallback\n allowedDirectories: [projectRoot, homedir()],\n};\n\nconst CONFIG_PATHS = [\n resolve(process.cwd(), 'pi-deck.config.json'),\n resolve(homedir(), '.config', 'pi-deck', 'config.json'),\n resolve(homedir(), '.pi-deck.config.json'),\n];\n\nfunction loadConfigFromFile(): Partial<ServerConfig> {\n for (const configPath of CONFIG_PATHS) {\n if (existsSync(configPath)) {\n try {\n const content = readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(content);\n console.log(`[Config] Loaded from ${configPath}`);\n return parsed;\n } catch (error) {\n console.warn(`[Config] Failed to parse ${configPath}:`, error);\n }\n }\n }\n return {};\n}\n\nfunction loadConfigFromEnv(): Partial<ServerConfig> {\n const config: Partial<ServerConfig> = {};\n\n if (process.env.PORT) {\n config.port = parseInt(process.env.PORT, 10);\n }\n\n if (process.env.HOST) {\n config.host = process.env.HOST;\n }\n\n if (process.env.PI_ALLOWED_DIRS) {\n config.allowedDirectories = process.env.PI_ALLOWED_DIRS.split(':').map((d) =>\n resolve(d.replace(/^~/, homedir()))\n );\n }\n\n return config;\n}\n\nconst TILDE_PREFIX = /^~(?=\\/|$)/;\n\nexport function canonicalizePath(input: string): string {\n const normalized = resolve(input.replace(TILDE_PREFIX, homedir()));\n try {\n return realpathSync(normalized);\n } catch {\n return normalized;\n }\n}\n\nfunction normalizeDirectories(dirs: string[]): string[] {\n return dirs.map((d) => canonicalizePath(d));\n}\n\nexport function loadConfig(): ServerConfig {\n const fileConfig = loadConfigFromFile();\n const envConfig = loadConfigFromEnv();\n\n const config: ServerConfig = {\n port: envConfig.port ?? fileConfig.port ?? DEFAULT_CONFIG.port,\n host: envConfig.host ?? fileConfig.host ?? DEFAULT_CONFIG.host,\n allowedDirectories: normalizeDirectories(\n envConfig.allowedDirectories ?? fileConfig.allowedDirectories ?? DEFAULT_CONFIG.allowedDirectories\n ),\n };\n\n console.log('[Config] Allowed directories:', config.allowedDirectories);\n return config;\n}\n\nexport function isPathAllowed(path: string, allowedDirectories: string[]): boolean {\n const normalizedPath = canonicalizePath(path);\n return allowedDirectories.some((allowed) => {\n const normalizedAllowed = canonicalizePath(allowed);\n return normalizedPath === normalizedAllowed || normalizedPath.startsWith(normalizedAllowed + '/');\n });\n}\n", "import { readdirSync, statSync, existsSync } from 'fs';\nimport { basename, join } from 'path';\nimport type { DirectoryEntry } from '@pi-deck/shared';\nimport { canonicalizePath, isPathAllowed } from './config.js';\n\nexport class DirectoryBrowser {\n constructor(private allowedDirectories: string[]) {}\n\n /**\n * List the allowed root directories\n */\n listRoots(): DirectoryEntry[] {\n return this.allowedDirectories.map((dir) => ({\n name: basename(dir) || dir,\n path: dir,\n hasPiSessions: this.checkForPiSessions(dir),\n }));\n }\n\n /**\n * Browse a directory and return its subdirectories\n */\n browse(path: string): DirectoryEntry[] {\n const normalizedPath = canonicalizePath(path);\n\n // Security check: ensure path is within allowed directories\n if (!isPathAllowed(normalizedPath, this.allowedDirectories)) {\n throw new Error(`Access denied: ${path} is not within allowed directories`);\n }\n\n // Check if path exists and is a directory\n if (!existsSync(normalizedPath)) {\n throw new Error(`Directory not found: ${path}`);\n }\n\n const stat = statSync(normalizedPath);\n if (!stat.isDirectory()) {\n throw new Error(`Not a directory: ${path}`);\n }\n\n // Read directory contents\n const entries: DirectoryEntry[] = [];\n\n try {\n const items = readdirSync(normalizedPath);\n\n for (const item of items) {\n // Skip hidden files/directories\n if (item.startsWith('.')) {\n continue;\n }\n\n const itemPath = join(normalizedPath, item);\n\n try {\n const itemStat = statSync(itemPath);\n if (itemStat.isDirectory()) {\n entries.push({\n name: item,\n path: itemPath,\n hasPiSessions: this.checkForPiSessions(itemPath),\n });\n }\n } catch {\n // Skip items we can't stat (permission issues, etc.)\n }\n }\n } catch (error) {\n throw new Error(`Cannot read directory: ${path}`);\n }\n\n // Sort alphabetically, case-insensitive\n entries.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));\n\n return entries;\n }\n\n /**\n * Check if a directory has existing Pi sessions\n */\n private checkForPiSessions(dirPath: string): boolean {\n const piSessionsPath = join(dirPath, '.pi', 'sessions');\n return existsSync(piSessionsPath);\n }\n\n /**\n * Get the allowed directories\n */\n getAllowedDirectories(): string[] {\n return [...this.allowedDirectories];\n }\n}\n", "import { EventEmitter } from 'events';\nimport { basename } from 'path';\nimport { SessionOrchestrator } from './session-orchestrator.js';\nimport { canonicalizePath, isPathAllowed } from './config.js';\nimport type { SyncIntegration } from './sync/index.js';\nimport type {\n WorkspaceInfo,\n SessionState,\n ChatMessage,\n SessionEvent,\n WsServerEvent,\n ExtensionUIRequest,\n QuestionnaireQuestion,\n} from '@pi-deck/shared';\n\ninterface Workspace {\n id: string;\n path: string;\n name: string;\n orchestrator: SessionOrchestrator;\n unsubscribe: () => void;\n /** Connected client count */\n clientCount: number;\n /** Events buffered while no clients connected (during active agent loop) */\n bufferedEvents: WsServerEvent[];\n /** Max events to buffer (prevent memory issues) */\n maxBufferedEvents: number;\n}\n\n/**\n * Global workspace manager that persists sessions across WebSocket connections.\n * \n * Sessions continue running even when all clients disconnect.\n * Clients can reconnect and receive buffered events + current state.\n * \n * Each workspace contains a SessionOrchestrator that manages multiple\n * concurrent session \"slots\" (one per pane in the UI).\n */\nexport class WorkspaceManager extends EventEmitter {\n private workspaces = new Map<string, Workspace>();\n private nextWorkspaceId = 1;\n private syncIntegration: SyncIntegration | null = null;\n private openingPaths = new Set<string>();\n\n constructor(private allowedDirectories: string[]) {\n super();\n }\n\n /**\n * Set the sync integration for state tracking\n */\n setSyncIntegration(sync: SyncIntegration): void {\n this.syncIntegration = sync;\n }\n\n /**\n * Open or attach to a workspace.\n * If a workspace already exists for this path, attach to it.\n * Otherwise, create a new one.\n */\n async openWorkspace(path: string): Promise<{\n workspace: WorkspaceInfo;\n state: SessionState;\n messages: ChatMessage[];\n bufferedEvents: WsServerEvent[];\n isExisting: boolean;\n }> {\n const normalizedPath = canonicalizePath(path);\n\n // Security check\n if (!isPathAllowed(normalizedPath, this.allowedDirectories)) {\n throw new Error(`Access denied: ${normalizedPath} is not within allowed directories`);\n }\n\n // If another request is currently opening this exact path, wait and attach.\n while (this.openingPaths.has(normalizedPath)) {\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n\n // Check if workspace already exists for this path\n const existing = this.findWorkspaceByPath(normalizedPath);\n if (existing) {\n existing.clientCount++;\n\n // Ensure orchestrator has workspace ID and sync integration\n existing.orchestrator.setWorkspaceId(existing.id);\n if (this.syncIntegration) {\n existing.orchestrator.setSyncIntegration(this.syncIntegration);\n }\n\n // Return buffered events and clear buffer\n const bufferedEvents = [...existing.bufferedEvents];\n existing.bufferedEvents = [];\n\n // Get state from default slot\n const defaultSlot = await existing.orchestrator.getDefaultSlot();\n const state = await defaultSlot.session.getState();\n const messages = defaultSlot.session.getMessages();\n\n // Check for pending questionnaire request in the slot (for reconnects)\n if (defaultSlot.pendingQuestionnaireRequest) {\n bufferedEvents.push({\n type: 'questionnaireRequest',\n workspaceId: existing.id,\n sessionSlotId: 'default',\n toolCallId: defaultSlot.pendingQuestionnaireRequest.toolCallId,\n questions: defaultSlot.pendingQuestionnaireRequest.questions,\n });\n }\n\n return {\n workspace: this.toWorkspaceInfo(existing.id, existing),\n state,\n messages,\n bufferedEvents,\n isExisting: true,\n };\n }\n\n // Create new workspace (guarded by openingPaths to prevent duplicates)\n this.openingPaths.add(normalizedPath);\n try {\n const id = `workspace-${this.nextWorkspaceId++}`;\n const orchestrator = new SessionOrchestrator(normalizedPath);\n orchestrator.setWorkspaceId(id);\n if (this.syncIntegration) {\n orchestrator.setSyncIntegration(this.syncIntegration);\n }\n\n // Subscribe to orchestrator events\n const unsubscribe = this.subscribeToOrchestrator(id, orchestrator);\n\n const workspace: Workspace = {\n id,\n path: normalizedPath,\n name: basename(normalizedPath) || normalizedPath,\n orchestrator,\n unsubscribe,\n clientCount: 1,\n bufferedEvents: [],\n maxBufferedEvents: 1000,\n };\n\n // Register early so concurrent callers attach to this workspace\n this.workspaces.set(id, workspace);\n\n // Initialize the default slot\n const { state, messages } = await orchestrator.createSlot('default');\n\n return {\n workspace: this.toWorkspaceInfo(id, workspace),\n state,\n messages,\n bufferedEvents: [],\n isExisting: false,\n };\n } finally {\n this.openingPaths.delete(normalizedPath);\n }\n }\n\n /**\n * Detach a client from a workspace.\n * The workspace continues to exist and run.\n */\n detachFromWorkspace(workspaceId: string): void {\n const workspace = this.workspaces.get(workspaceId);\n if (!workspace) {\n return; // Already gone\n }\n\n workspace.clientCount = Math.max(0, workspace.clientCount - 1);\n \n // Note: We do NOT dispose the workspace here.\n // It continues running even with no clients.\n console.log(`[WorkspaceManager] Client detached from ${workspaceId}, ${workspace.clientCount} clients remaining, buffered events: ${workspace.bufferedEvents.length}`);\n }\n\n /**\n * Explicitly close and dispose a workspace.\n * Use this when the user explicitly closes a workspace tab.\n */\n closeWorkspace(workspaceId: string): void {\n const workspace = this.workspaces.get(workspaceId);\n if (!workspace) {\n throw new Error(`Workspace not found: ${workspaceId}`);\n }\n\n workspace.unsubscribe();\n workspace.orchestrator.dispose();\n this.workspaces.delete(workspaceId);\n console.log(`[WorkspaceManager] Workspace ${workspaceId} closed and disposed`);\n }\n\n /**\n * Get a workspace by ID\n */\n getWorkspace(workspaceId: string): Workspace | undefined {\n return this.workspaces.get(workspaceId);\n }\n\n /**\n * Find a workspace by path\n */\n findWorkspaceByPath(path: string): Workspace | undefined {\n for (const workspace of this.workspaces.values()) {\n if (workspace.path === path) {\n return workspace;\n }\n }\n return undefined;\n }\n\n /**\n * Get the orchestrator for a workspace\n */\n getOrchestrator(workspaceId: string): SessionOrchestrator {\n const workspace = this.workspaces.get(workspaceId);\n if (!workspace) {\n throw new Error(`Workspace not found: ${workspaceId}`);\n }\n return workspace.orchestrator;\n }\n\n /**\n * List all open workspaces\n */\n listWorkspaces(): WorkspaceInfo[] {\n return Array.from(this.workspaces.entries()).map(([id, ws]) =>\n this.toWorkspaceInfo(id, ws)\n );\n }\n\n /**\n * Get workspaces that are currently active (any slot running)\n */\n getActiveWorkspaces(): WorkspaceInfo[] {\n return Array.from(this.workspaces.entries())\n .filter(([_, ws]) => ws.orchestrator.isAnySlotActive())\n .map(([id, ws]) => this.toWorkspaceInfo(id, ws));\n }\n\n /**\n * Dispose all workspaces (for server shutdown)\n */\n dispose(): void {\n for (const workspace of this.workspaces.values()) {\n workspace.unsubscribe();\n workspace.orchestrator.dispose();\n }\n this.workspaces.clear();\n }\n\n private subscribeToOrchestrator(workspaceId: string, orchestrator: SessionOrchestrator): () => void {\n const handler = (event: SessionEvent & { sessionSlotId?: string }) => {\n // Add workspaceId to convert SessionEvent to WsServerEvent\n const scopedEvent: WsServerEvent = { \n ...event, \n workspaceId,\n sessionSlotId: event.sessionSlotId,\n } as WsServerEvent;\n \n const workspace = this.workspaces.get(workspaceId);\n if (workspace) {\n if (workspace.clientCount > 0) {\n // Clients connected - emit the event\n this.emit('event', scopedEvent);\n } else {\n // No clients - buffer the event\n if (workspace.bufferedEvents.length < workspace.maxBufferedEvents) {\n workspace.bufferedEvents.push(scopedEvent);\n }\n // Also emit so server can log if needed\n this.emit('bufferedEvent', scopedEvent);\n }\n }\n };\n\n // Listen for slot closed events too\n const slotHandler = (data: { slotId: string }) => {\n const event: WsServerEvent = {\n type: 'sessionSlotClosed',\n workspaceId,\n sessionSlotId: data.slotId,\n };\n this.emit('event', event);\n };\n\n // Listen for extension UI requests\n const extensionUIHandler = (data: { request: ExtensionUIRequest; sessionSlotId: string }) => {\n const event: WsServerEvent = {\n type: 'extensionUIRequest',\n workspaceId,\n sessionSlotId: data.sessionSlotId,\n request: data.request,\n };\n // Extension UI requests should always be sent (even if buffered events would be dropped)\n // because they need immediate user interaction\n this.emit('event', event);\n };\n\n // Listen for extension notifications\n const notificationHandler = (data: { message: string; type: 'info' | 'warning' | 'error'; sessionSlotId: string }) => {\n // Could emit as a special event or just log for now\n console.log(`[Extension Notification] [${data.type}] ${data.message}`);\n // TODO: Could add a WsExtensionNotificationEvent type\n };\n\n // Listen for custom UI events (ctx.ui.custom())\n const customUIStartHandler = (data: { state: import('@pi-deck/shared').CustomUIState; sessionSlotId: string }) => {\n const event: WsServerEvent = {\n type: 'customUIStart',\n workspaceId,\n sessionSlotId: data.sessionSlotId,\n state: data.state,\n };\n this.emit('event', event);\n };\n\n const customUIUpdateHandler = (data: { sessionId: string; root: import('@pi-deck/shared').CustomUINode; sessionSlotId: string }) => {\n const event: WsServerEvent = {\n type: 'customUIUpdate',\n workspaceId,\n sessionSlotId: data.sessionSlotId,\n sessionId: data.sessionId,\n root: data.root,\n };\n this.emit('event', event);\n };\n\n const customUICloseHandler = (data: { sessionId: string; sessionSlotId: string }) => {\n const event: WsServerEvent = {\n type: 'customUIClose',\n workspaceId,\n sessionSlotId: data.sessionSlotId,\n sessionId: data.sessionId,\n };\n this.emit('event', event);\n };\n\n // Listen for native questionnaire requests\n const questionnaireRequestHandler = (data: { toolCallId: string; questions: QuestionnaireQuestion[]; sessionSlotId: string }) => {\n const event: WsServerEvent = {\n type: 'questionnaireRequest',\n workspaceId,\n sessionSlotId: data.sessionSlotId,\n toolCallId: data.toolCallId,\n questions: data.questions,\n };\n const workspace = this.workspaces.get(workspaceId);\n if (workspace) {\n // Always emit the event for immediate delivery\n this.emit('event', event);\n\n // Also buffer for reconnects if no clients\n if (workspace.clientCount === 0) {\n if (workspace.bufferedEvents.length < workspace.maxBufferedEvents) {\n workspace.bufferedEvents.push(event);\n }\n }\n\n // Track in sync state for durable reconnects\n if (this.syncIntegration) {\n this.syncIntegration.setPendingQuestionnaire(workspaceId, data.sessionSlotId, data.toolCallId, data.questions);\n }\n }\n };\n\n orchestrator.on('event', handler);\n orchestrator.on('slotClosed', slotHandler);\n orchestrator.on('extensionUIRequest', extensionUIHandler);\n orchestrator.on('extensionNotification', notificationHandler);\n orchestrator.on('customUIStart', customUIStartHandler);\n orchestrator.on('customUIUpdate', customUIUpdateHandler);\n orchestrator.on('customUIClose', customUICloseHandler);\n orchestrator.on('questionnaireRequest', questionnaireRequestHandler);\n \n return () => {\n orchestrator.off('event', handler);\n orchestrator.off('slotClosed', slotHandler);\n orchestrator.off('extensionUIRequest', extensionUIHandler);\n orchestrator.off('extensionNotification', notificationHandler);\n orchestrator.off('customUIStart', customUIStartHandler);\n orchestrator.off('customUIUpdate', customUIUpdateHandler);\n orchestrator.off('customUIClose', customUICloseHandler);\n orchestrator.off('questionnaireRequest', questionnaireRequestHandler);\n };\n }\n\n private toWorkspaceInfo(id: string, workspace: Workspace): WorkspaceInfo {\n return {\n id,\n path: workspace.path,\n name: workspace.name,\n isActive: workspace.orchestrator.isAnySlotActive(),\n state: null, // State is fetched separately when needed\n };\n }\n}\n\n// Singleton instance\nlet workspaceManager: WorkspaceManager | null = null;\n\nexport function getWorkspaceManager(allowedDirectories: string[]): WorkspaceManager {\n if (!workspaceManager) {\n workspaceManager = new WorkspaceManager(allowedDirectories);\n }\n return workspaceManager;\n}\n", "import { EventEmitter } from 'events';\nimport { PiSession } from './pi-session.js';\nimport type {\n ChatMessage,\n SessionState,\n SessionEvent,\n SessionInfo,\n ModelInfo,\n SlashCommand,\n StartupInfo,\n ThinkingLevel,\n ImageAttachment,\n SessionStats,\n BashResult,\n SessionTreeNode,\n ScopedModelInfo,\n ExtensionUIRequest,\n ExtensionUIResponse,\n QuestionnaireQuestion,\n QuestionnaireResponse,\n} from '@pi-deck/shared';\n\n/**\n * Represents a session slot - a \"pane\" that can hold a session\n */\ninterface SessionSlot {\n id: string;\n session: PiSession;\n unsubscribe: () => void;\n /** The session file/ID currently loaded in this slot (if any) */\n loadedSessionId: string | null;\n /** Pending questionnaire request, if any (for reconnects) */\n pendingQuestionnaireRequest?: { toolCallId: string; questions: QuestionnaireQuestion[] };\n /** Slot initialization promise (used to avoid races on reconnect/create) */\n initializationPromise: Promise<void> | null;\n}\n\n/**\n * Orchestrates multiple concurrent PiSession instances within a single workspace.\n * \n * Each \"slot\" represents a pane in the UI that can independently:\n * - Load different sessions\n * - Run agent prompts concurrently\n * - Have its own streaming state\n * \n * Events are scoped with sessionSlotId so the UI knows which pane to update.\n */\nexport class SessionOrchestrator extends EventEmitter {\n private cwd: string;\n private slots = new Map<string, SessionSlot>();\n private nextSlotId = 1;\n private workspaceId: string | null = null;\n private syncIntegration: import('./sync/index.js').SyncIntegration | null = null;\n\n constructor(cwd: string) {\n super();\n this.cwd = cwd;\n }\n\n /**\n * Set the workspace ID for this orchestrator\n */\n setWorkspaceId(id: string): void {\n this.workspaceId = id;\n }\n\n /**\n * Set the sync integration for state tracking\n */\n setSyncIntegration(sync: import('./sync/index.js').SyncIntegration): void {\n this.syncIntegration = sync;\n }\n\n /**\n * Create a new session slot (pane)\n */\n async createSlot(slotId?: string): Promise<{\n slotId: string;\n state: SessionState;\n messages: ChatMessage[];\n }> {\n const id = slotId || `slot-${this.nextSlotId++}`;\n\n // Check if slot already exists\n if (this.slots.has(id)) {\n const slot = this.slots.get(id)!;\n if (slot.initializationPromise) {\n await slot.initializationPromise;\n }\n const state = await slot.session.getState();\n const messages = slot.session.getMessages();\n // Re-send pending questionnaire request if any (for client reconnects),\n // but only if the underlying resolver is still pending in the session.\n if (slot.pendingQuestionnaireRequest) {\n const pending = slot.session.hasPendingQuestionnaire(slot.pendingQuestionnaireRequest.toolCallId);\n if (pending) {\n setTimeout(() => {\n this.emit('questionnaireRequest', {\n ...slot.pendingQuestionnaireRequest!,\n sessionSlotId: id,\n });\n }, 100);\n } else {\n // Drop stale pending questionnaire entries\n delete slot.pendingQuestionnaireRequest;\n }\n }\n return { slotId: id, state, messages };\n }\n\n // Create a new PiSession for this slot\n const session = new PiSession(this.cwd);\n\n // Subscribe to session events, adding slotId\n const unsubscribe = this.subscribeToSession(id, session);\n\n const initializationPromise = session.initialize();\n\n const slot: SessionSlot = {\n id,\n session,\n unsubscribe,\n loadedSessionId: null,\n initializationPromise,\n };\n\n this.slots.set(id, slot);\n\n try {\n await initializationPromise;\n slot.initializationPromise = null;\n\n const state = await session.getState();\n const messages = session.getMessages();\n\n return { slotId: id, state, messages };\n } catch (error) {\n slot.unsubscribe();\n slot.session.dispose();\n this.slots.delete(id);\n throw error;\n }\n }\n\n /**\n * Get the default (first) slot, creating it if needed\n */\n async getDefaultSlot(): Promise<SessionSlot> {\n if (this.slots.size === 0) {\n await this.createSlot('default');\n }\n const slot = this.slots.values().next().value!;\n if (slot.initializationPromise) {\n await slot.initializationPromise;\n }\n return slot;\n }\n\n /**\n * Get a specific slot by ID\n */\n getSlot(slotId: string): SessionSlot | undefined {\n return this.slots.get(slotId);\n }\n\n /**\n * Get the session for a specific slot\n */\n getSession(slotId: string): PiSession {\n const slot = this.slots.get(slotId);\n if (!slot) {\n throw new Error(`Session slot not found: ${slotId}`);\n }\n return slot.session;\n }\n\n /**\n * Close a session slot\n */\n closeSlot(slotId: string): void {\n const slot = this.slots.get(slotId);\n if (!slot) {\n return;\n }\n\n slot.unsubscribe();\n slot.session.dispose();\n this.slots.delete(slotId);\n\n this.emit('slotClosed', { slotId });\n }\n\n /**\n * List all active slots\n */\n listSlots(): Array<{\n slotId: string;\n loadedSessionId: string | null;\n isActive: boolean;\n }> {\n return Array.from(this.slots.values()).map(slot => ({\n slotId: slot.id,\n loadedSessionId: slot.loadedSessionId,\n isActive: slot.session.isActive(),\n }));\n }\n\n /**\n * Check if any slot is currently active (streaming)\n */\n isAnySlotActive(): boolean {\n for (const slot of this.slots.values()) {\n if (slot.session.isActive()) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Dispose all slots\n */\n dispose(): void {\n for (const slot of this.slots.values()) {\n slot.unsubscribe();\n slot.session.dispose();\n }\n this.slots.clear();\n }\n\n // ============================================================================\n // Proxy methods for session operations (all require slotId)\n // ============================================================================\n\n async getState(slotId: string): Promise<SessionState> {\n return this.getSession(slotId).getState();\n }\n\n getMessages(slotId: string): ChatMessage[] {\n return this.getSession(slotId).getMessages();\n }\n\n async prompt(slotId: string, message: string, images?: ImageAttachment[]): Promise<void> {\n return this.getSession(slotId).prompt(message, images);\n }\n\n async steer(slotId: string, message: string, images?: ImageAttachment[]): Promise<void> {\n return this.getSession(slotId).steer(message, images);\n }\n\n async followUp(slotId: string, message: string): Promise<void> {\n return this.getSession(slotId).followUp(message);\n }\n\n async abort(slotId: string): Promise<void> {\n return this.getSession(slotId).abort();\n }\n\n async setModel(slotId: string, provider: string, modelId: string): Promise<void> {\n return this.getSession(slotId).setModel(provider, modelId);\n }\n\n setThinkingLevel(slotId: string, level: ThinkingLevel): void {\n return this.getSession(slotId).setThinkingLevel(level);\n }\n\n async newSession(slotId: string): Promise<void> {\n const slot = this.slots.get(slotId);\n if (!slot) {\n throw new Error(`Session slot not found: ${slotId}`);\n }\n await slot.session.newSession();\n slot.loadedSessionId = null;\n }\n\n async switchSession(slotId: string, sessionPath: string): Promise<void> {\n const slot = this.slots.get(slotId);\n if (!slot) {\n throw new Error(`Session slot not found: ${slotId}`);\n }\n await slot.session.switchSession(sessionPath);\n slot.loadedSessionId = sessionPath;\n }\n\n async compact(slotId: string, customInstructions?: string): Promise<void> {\n return this.getSession(slotId).compact(customInstructions);\n }\n\n async listSessions(): Promise<SessionInfo[]> {\n // Sessions list is shared across all slots (they can all load any session)\n const defaultSlot = await this.getDefaultSlot();\n const sessions = await defaultSlot.session.listSessions();\n\n // Hide stale empty sessions, but keep empty sessions currently open in a slot.\n const activeSessionFiles = new Set<string>();\n for (const slot of this.slots.values()) {\n if (slot.initializationPromise) {\n await slot.initializationPromise;\n }\n const sessionFile = slot.session.getSessionFile();\n if (sessionFile) {\n activeSessionFiles.add(sessionFile);\n }\n }\n\n return sessions.filter((session) => session.messageCount > 0 || activeSessionFiles.has(session.path));\n }\n\n async getAvailableModels(): Promise<ModelInfo[]> {\n // Models list is shared across all slots\n const defaultSlot = await this.getDefaultSlot();\n return defaultSlot.session.getAvailableModels();\n }\n\n getCommands(slotId: string): SlashCommand[] {\n return this.getSession(slotId).getCommands();\n }\n\n // Session operations\n async fork(slotId: string, entryId: string): Promise<{ text: string; cancelled: boolean }> {\n return this.getSession(slotId).fork(entryId);\n }\n\n getForkMessages(slotId: string): Array<{ entryId: string; text: string }> {\n return this.getSession(slotId).getForkMessages();\n }\n\n setSessionName(slotId: string, name: string): void {\n return this.getSession(slotId).setSessionName(name);\n }\n\n async exportHtml(slotId: string, outputPath?: string): Promise<string> {\n return this.getSession(slotId).exportHtml(outputPath);\n }\n\n // Model/Thinking cycling\n async cycleModel(slotId: string, direction?: 'forward' | 'backward') {\n return this.getSession(slotId).cycleModel(direction);\n }\n\n cycleThinkingLevel(slotId: string): ThinkingLevel | null {\n return this.getSession(slotId).cycleThinkingLevel();\n }\n\n // Mode settings\n setSteeringMode(slotId: string, mode: 'all' | 'one-at-a-time'): void {\n return this.getSession(slotId).setSteeringMode(mode);\n }\n\n setFollowUpMode(slotId: string, mode: 'all' | 'one-at-a-time'): void {\n return this.getSession(slotId).setFollowUpMode(mode);\n }\n\n setAutoCompaction(slotId: string, enabled: boolean): void {\n return this.getSession(slotId).setAutoCompaction(enabled);\n }\n\n setAutoRetry(slotId: string, enabled: boolean): void {\n return this.getSession(slotId).setAutoRetry(enabled);\n }\n\n abortRetry(slotId: string): void {\n return this.getSession(slotId).abortRetry();\n }\n\n // Bash execution\n async executeBash(slotId: string, command: string, onChunk?: (chunk: string) => void, excludeFromContext = false): Promise<BashResult> {\n return this.getSession(slotId).executeBash(command, onChunk, excludeFromContext);\n }\n\n abortBash(slotId: string): void {\n return this.getSession(slotId).abortBash();\n }\n\n // Stats\n getSessionStats(slotId: string): SessionStats {\n return this.getSession(slotId).getSessionStats();\n }\n\n getLastAssistantText(slotId: string): string | null {\n return this.getSession(slotId).getLastAssistantText();\n }\n\n // Startup info (shared across slots - uses default slot)\n async getStartupInfo(): Promise<StartupInfo> {\n const slot = await this.getDefaultSlot();\n return slot.session.getStartupInfo();\n }\n\n // ============================================================================\n // Session Tree Navigation\n // ============================================================================\n\n getSessionTree(slotId: string): { tree: SessionTreeNode[]; currentLeafId: string | null } {\n return this.getSession(slotId).getSessionTree();\n }\n\n async navigateTree(slotId: string, targetId: string, summarize?: boolean): Promise<{ success: boolean; editorText?: string; error?: string }> {\n return this.getSession(slotId).navigateTree(targetId, summarize);\n }\n\n // ============================================================================\n // Queued Messages\n // ============================================================================\n\n getQueuedMessages(slotId: string): { steering: string[]; followUp: string[] } {\n return this.getSession(slotId).getQueuedMessages();\n }\n\n clearQueue(slotId: string): { steering: string[]; followUp: string[] } {\n return this.getSession(slotId).clearQueue();\n }\n\n // ============================================================================\n // Scoped Models\n // ============================================================================\n\n async getScopedModels(slotId: string): Promise<ScopedModelInfo[]> {\n return this.getSession(slotId).getScopedModels();\n }\n\n async setScopedModels(slotId: string, models: Array<{ provider: string; modelId: string; thinkingLevel: ThinkingLevel }>): Promise<void> {\n return this.getSession(slotId).setScopedModels(models);\n }\n\n // ============================================================================\n // Extension UI\n // ============================================================================\n\n /**\n * Handle an extension UI response from the client.\n */\n handleExtensionUIResponse(slotId: string, response: ExtensionUIResponse): void {\n return this.getSession(slotId).handleExtensionUIResponse(response);\n }\n\n /**\n * Update the stored editor text (for extension UI context).\n */\n setEditorTextFromClient(slotId: string, text: string): void {\n return this.getSession(slotId).setEditorTextFromClient(text);\n }\n\n /**\n * Handle custom UI input from the client.\n */\n handleCustomUIInput(slotId: string, input: import('@pi-deck/shared').CustomUIInputEvent): void {\n return this.getSession(slotId).handleCustomUIInput(input);\n }\n\n /**\n * Check if a questionnaire tool call is still pending for a slot.\n */\n hasPendingQuestionnaire(slotId: string, toolCallId: string): boolean {\n return this.getSession(slotId).hasPendingQuestionnaire(toolCallId);\n }\n\n /**\n * Handle a questionnaire response from the client.\n */\n handleQuestionnaireResponse(slotId: string, response: QuestionnaireResponse): void {\n const slot = this.slots.get(slotId);\n if (slot) {\n // Clear pending questionnaire request\n delete slot.pendingQuestionnaireRequest;\n }\n return this.getSession(slotId).handleQuestionnaireResponse(response);\n }\n\n // ============================================================================\n // Private\n // ============================================================================\n\n private subscribeToSession(slotId: string, session: PiSession): () => void {\n const eventHandler = (event: SessionEvent) => {\n // Emit event with slotId attached (for backward compatibility)\n this.emit('event', { ...event, sessionSlotId: slotId });\n\n // Forward to sync system for durable state tracking\n if (this.workspaceId && this.syncIntegration) {\n this.syncIntegration.handleSessionEvent(this.workspaceId, slotId, event);\n }\n\n // When a user message starts, the SDK may have consumed a steering message\n // from the queue. Send updated queue state so the UI clears stale entries.\n if (event.type === 'messageStart' && event.message.role === 'user') {\n const queueState = session.getQueuedMessages();\n this.emit('event', {\n type: 'queuedMessages',\n sessionSlotId: slotId,\n steering: queueState.steering,\n followUp: queueState.followUp,\n });\n }\n };\n\n // Handler for extension UI requests\n const extensionUIHandler = (request: ExtensionUIRequest) => {\n this.emit('extensionUIRequest', { request, sessionSlotId: slotId });\n };\n\n // Handler for extension notifications\n const notificationHandler = (notification: { message: string; type: 'info' | 'warning' | 'error' }) => {\n this.emit('extensionNotification', { ...notification, sessionSlotId: slotId });\n };\n\n // Handler for editor text changes from extensions\n const editorTextHandler = (text: string) => {\n this.emit('extensionEditorText', { text, sessionSlotId: slotId });\n };\n\n // Handlers for custom UI events (ctx.ui.custom())\n const customUIStartHandler = (state: import('@pi-deck/shared').CustomUIState) => {\n this.emit('customUIStart', { state, sessionSlotId: slotId });\n };\n const customUIUpdateHandler = (update: { sessionId: string; root: import('@pi-deck/shared').CustomUINode }) => {\n this.emit('customUIUpdate', { ...update, sessionSlotId: slotId });\n };\n const customUICloseHandler = (close: { sessionId: string }) => {\n this.emit('customUIClose', { ...close, sessionSlotId: slotId });\n };\n\n // Handler for native questionnaire requests\n const questionnaireRequestHandler = (request: { toolCallId: string; questions: QuestionnaireQuestion[] }) => {\n // Store for reconnects\n const slot = this.slots.get(slotId);\n if (slot) {\n slot.pendingQuestionnaireRequest = request;\n }\n this.emit('questionnaireRequest', { ...request, sessionSlotId: slotId });\n };\n\n session.on('event', eventHandler);\n session.on('extensionUIRequest', extensionUIHandler);\n session.on('extensionNotification', notificationHandler);\n session.on('editorTextChange', editorTextHandler);\n session.on('customUIStart', customUIStartHandler);\n session.on('customUIUpdate', customUIUpdateHandler);\n session.on('customUIClose', customUICloseHandler);\n session.on('questionnaireRequest', questionnaireRequestHandler);\n\n return () => {\n session.off('event', eventHandler);\n session.off('extensionUIRequest', extensionUIHandler);\n session.off('extensionNotification', notificationHandler);\n session.off('editorTextChange', editorTextHandler);\n session.off('customUIStart', customUIStartHandler);\n session.off('customUIUpdate', customUIUpdateHandler);\n session.off('customUIClose', customUICloseHandler);\n session.off('questionnaireRequest', questionnaireRequestHandler);\n };\n }\n}\n", "import { EventEmitter } from 'events';\nimport { existsSync, unlinkSync } from 'fs';\nimport {\n createAgentSession,\n AuthStorage,\n ModelRegistry,\n SessionManager,\n VERSION,\n type AgentSession,\n type AgentSessionEvent,\n} from '@mariozechner/pi-coding-agent';\nimport type { ImageContent, TextContent } from '@mariozechner/pi-ai';\nimport type {\n BashResult,\n ChatMessage,\n ImageAttachment,\n MessageContent,\n ModelInfo,\n SessionInfo,\n SessionState,\n SessionStats,\n SessionEvent,\n SlashCommand,\n StartupInfo,\n StartupResourceInfo,\n ThinkingLevel,\n TokenUsage,\n SessionTreeNode,\n ScopedModelInfo,\n FileInfo,\n ExtensionUIRequest,\n ExtensionUIResponse,\n QuestionnaireQuestion,\n QuestionnaireResponse,\n} from '@pi-deck/shared';\nimport { getGitInfo } from './git-info.js';\nimport { WebExtensionUIContext } from './web-extension-ui.js';\n\nexport class PiSession extends EventEmitter {\n private session: AgentSession | null = null;\n private authStorage: AuthStorage;\n private modelRegistry: ModelRegistry;\n private cwd: string;\n private messageMap = new Map<string, ChatMessage>();\n private unsubscribe: (() => void) | null = null;\n private extensionUIContext: WebExtensionUIContext | null = null;\n /** Stored editor text for extensions (since there's no real editor in web UI) */\n private editorText = '';\n\n constructor(cwd: string) {\n super();\n this.cwd = cwd;\n this.authStorage = new AuthStorage();\n this.modelRegistry = new ModelRegistry(this.authStorage);\n }\n\n async initialize(): Promise<void> {\n const { session } = await createAgentSession({\n cwd: this.cwd,\n authStorage: this.authStorage,\n modelRegistry: this.modelRegistry,\n sessionManager: SessionManager.create(this.cwd),\n });\n\n this.session = session;\n this.unsubscribe = session.subscribe((event) => this.handleEvent(event));\n\n // Create and bind the extension UI context\n this.extensionUIContext = new WebExtensionUIContext({\n sendRequest: (request: ExtensionUIRequest) => {\n this.emit('extensionUIRequest', request);\n },\n sendNotification: (message: string, type: 'info' | 'warning' | 'error') => {\n // Emit as a notification event (could be handled by client)\n this.emit('extensionNotification', { message, type });\n },\n setEditorText: (text: string) => {\n this.editorText = text;\n this.emit('editorTextChange', text);\n },\n getEditorText: () => {\n return this.editorText;\n },\n // Custom UI callbacks (for ctx.ui.custom())\n sendCustomUIStart: (state) => {\n this.emit('customUIStart', state);\n },\n sendCustomUIUpdate: (update) => {\n this.emit('customUIUpdate', update);\n },\n sendCustomUIClose: (close) => {\n this.emit('customUIClose', close);\n },\n // Questionnaire callback (native web questionnaire UI)\n sendQuestionnaireRequest: (request) => {\n this.emit('questionnaireRequest', request);\n },\n });\n\n // Bind extensions with UI context\n await session.bindExtensions({\n uiContext: this.extensionUIContext,\n // TODO: Could also bind commandContextActions for navigateTree, etc.\n });\n }\n\n dispose(): void {\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = null;\n }\n if (this.extensionUIContext) {\n this.extensionUIContext.cancelAllPending();\n this.extensionUIContext = null;\n }\n if (this.session) {\n this.deleteSessionFileIfEmpty(this.session.sessionFile, this.session.messages.length);\n this.session.dispose();\n this.session = null;\n }\n }\n\n private deleteSessionFileIfEmpty(sessionFile: string | undefined, messageCount: number): void {\n if (!sessionFile || messageCount !== 0) {\n return;\n }\n\n try {\n if (existsSync(sessionFile)) {\n unlinkSync(sessionFile);\n }\n } catch {\n // Best effort cleanup only.\n }\n }\n\n /**\n * Handle an extension UI response from the client.\n */\n handleExtensionUIResponse(response: ExtensionUIResponse): void {\n if (this.extensionUIContext) {\n this.extensionUIContext.handleResponse(response);\n }\n }\n\n /**\n * Handle a questionnaire response from the client.\n */\n handleQuestionnaireResponse(response: QuestionnaireResponse): void {\n if (this.extensionUIContext) {\n this.extensionUIContext.handleQuestionnaireResponse(response);\n }\n }\n\n /**\n * Check whether a questionnaire tool call is still pending in this session.\n */\n hasPendingQuestionnaire(toolCallId: string): boolean {\n return this.extensionUIContext?.hasPendingQuestionnaire(toolCallId) ?? false;\n }\n\n /**\n * Handle custom UI input from the client.\n */\n handleCustomUIInput(input: import('@pi-deck/shared').CustomUIInputEvent): void {\n if (this.extensionUIContext) {\n this.extensionUIContext.handleCustomUIInput(input);\n }\n }\n\n /**\n * Set the editor text (for extensions that call ctx.ui.setEditorText)\n */\n setEditorTextFromClient(text: string): void {\n this.editorText = text;\n }\n\n private handleEvent(event: AgentSessionEvent): void {\n switch (event.type) {\n case 'agent_start':\n this.emit('event', { type: 'agentStart' } satisfies SessionEvent);\n break;\n\n case 'agent_end':\n this.emit('event', { type: 'agentEnd' } satisfies SessionEvent);\n break;\n\n case 'message_start': {\n const startMsg = this.convertMessage(event.message);\n this.messageMap.set(startMsg.id, startMsg);\n this.emit('event', { type: 'messageStart', message: startMsg } satisfies SessionEvent);\n break;\n }\n\n case 'message_update':\n this.handleMessageUpdate(event);\n break;\n\n case 'message_end': {\n const endMsg = this.convertMessage(event.message);\n this.messageMap.set(endMsg.id, endMsg);\n this.emit('event', { type: 'messageEnd', message: endMsg } satisfies SessionEvent);\n break;\n }\n\n case 'tool_execution_start':\n // Detect questionnaire tool and set up native web UI interception.\n // This must happen synchronously before the tool's execute() calls ctx.ui.custom(),\n // so that custom() sees the flag and routes to the native QuestionnaireUI.\n if (event.toolName === 'questionnaire' && this.extensionUIContext) {\n const args = event.args as { questions?: QuestionnaireQuestion[] };\n if (args.questions && args.questions.length > 0) {\n this.extensionUIContext.setQuestionnaireMode(event.toolCallId, args.questions);\n }\n }\n this.emit('event', {\n type: 'toolStart',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n args: event.args as Record<string, unknown>,\n } satisfies SessionEvent);\n break;\n\n case 'tool_execution_update': {\n const partialText = this.extractTextFromContent(event.partialResult?.content);\n const updateDetails = event.partialResult?.details;\n this.emit('event', {\n type: 'toolUpdate',\n toolCallId: event.toolCallId,\n partialResult: partialText,\n ...(updateDetails !== undefined && { details: updateDetails }),\n } satisfies SessionEvent);\n break;\n }\n\n case 'tool_execution_end': {\n const resultText = this.extractTextFromContent(event.result?.content);\n const endDetails = event.result?.details;\n this.emit('event', {\n type: 'toolEnd',\n toolCallId: event.toolCallId,\n result: resultText,\n isError: event.isError,\n ...(endDetails !== undefined && { details: endDetails }),\n } satisfies SessionEvent);\n break;\n }\n\n case 'auto_compaction_start':\n this.emit('event', { type: 'compactionStart' } satisfies SessionEvent);\n break;\n\n case 'auto_compaction_end':\n this.emit('event', {\n type: 'compactionEnd',\n summary: event.result?.summary || '',\n } satisfies SessionEvent);\n break;\n }\n }\n\n private extractTextFromContent(content: (TextContent | ImageContent)[] | undefined): string {\n if (!content) return '';\n return content\n .filter((c): c is TextContent => c.type === 'text')\n .map((c) => c.text)\n .join('');\n }\n\n private handleMessageUpdate(event: Extract<AgentSessionEvent, { type: 'message_update' }>): void {\n const { assistantMessageEvent } = event;\n const messageId = event.message.timestamp?.toString() || Date.now().toString();\n\n switch (assistantMessageEvent.type) {\n case 'text_delta':\n this.emit('event', {\n type: 'messageUpdate',\n messageId,\n update: {\n type: 'textDelta',\n delta: assistantMessageEvent.delta,\n contentIndex: assistantMessageEvent.contentIndex,\n },\n } satisfies SessionEvent);\n break;\n\n case 'thinking_delta':\n this.emit('event', {\n type: 'messageUpdate',\n messageId,\n update: {\n type: 'thinkingDelta',\n delta: assistantMessageEvent.delta,\n contentIndex: assistantMessageEvent.contentIndex,\n },\n } satisfies SessionEvent);\n break;\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private convertMessage(msg: any): ChatMessage {\n const id = msg.timestamp?.toString() || Date.now().toString();\n const timestamp = msg.timestamp || Date.now();\n\n if (msg.role === 'user') {\n return {\n id,\n role: 'user',\n timestamp,\n content: this.convertContent(msg.content),\n };\n }\n\n if (msg.role === 'toolResult') {\n return {\n id,\n role: 'toolResult',\n timestamp,\n content: this.convertContent(msg.content),\n toolCallId: msg.toolCallId,\n toolName: msg.toolName,\n isError: msg.isError,\n };\n }\n\n if (msg.role === 'bashExecution') {\n const exitCode = msg.exitCode ?? null;\n return {\n id,\n role: 'bashExecution',\n timestamp,\n content: [],\n command: msg.command,\n output: msg.output || '',\n exitCode,\n cancelled: msg.cancelled || false,\n truncated: msg.truncated || false,\n fullOutputPath: msg.fullOutputPath,\n excludeFromContext: msg.excludeFromContext || false,\n isError: (exitCode !== null && exitCode !== 0) || Boolean(msg.cancelled),\n };\n }\n\n // Assistant message\n return {\n id,\n role: 'assistant',\n timestamp,\n content: this.convertContent(msg.content),\n model: msg.model,\n provider: msg.provider,\n usage: msg.usage ? {\n input: msg.usage.input || 0,\n output: msg.usage.output || 0,\n cacheRead: msg.usage.cacheRead || 0,\n cacheWrite: msg.usage.cacheWrite || 0,\n total: (msg.usage.input || 0) + (msg.usage.output || 0),\n } : undefined,\n };\n }\n\n private convertContent(content: unknown): MessageContent[] {\n if (typeof content === 'string') {\n return [{ type: 'text', text: content }];\n }\n\n if (!Array.isArray(content)) {\n return [];\n }\n\n return content.map((block: { type: string; text?: string; thinking?: string; id?: string; name?: string; arguments?: Record<string, unknown>; data?: string; mimeType?: string }) => {\n if (block.type === 'text') {\n return { type: 'text' as const, text: block.text || '' };\n }\n if (block.type === 'thinking') {\n return { type: 'thinking' as const, thinking: block.thinking || '' };\n }\n if (block.type === 'toolCall') {\n return {\n type: 'toolCall' as const,\n id: block.id || '',\n name: block.name || '',\n arguments: block.arguments || {},\n status: 'complete' as const,\n };\n }\n if (block.type === 'image') {\n return {\n type: 'image' as const,\n source: {\n type: 'base64' as const,\n mediaType: block.mimeType || 'image/png',\n data: block.data || '',\n },\n };\n }\n return { type: 'text' as const, text: JSON.stringify(block) };\n });\n }\n\n async getState(): Promise<SessionState> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n\n const session = this.session;\n const model = session.model;\n const messages = session.messages;\n\n // Calculate token usage from messages\n const tokens: TokenUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };\n\n for (const msg of messages) {\n // Check if this is an assistant message with usage info\n if (msg.role === 'assistant' && 'usage' in msg && msg.usage) {\n const usage = msg.usage as { input?: number; output?: number; cacheRead?: number; cacheWrite?: number };\n tokens.input += usage.input || 0;\n tokens.output += usage.output || 0;\n tokens.cacheRead += usage.cacheRead || 0;\n tokens.cacheWrite += usage.cacheWrite || 0;\n }\n }\n tokens.total = tokens.input + tokens.output + tokens.cacheRead + tokens.cacheWrite;\n\n // Get context window usage from Pi SDK\n const contextUsage = session.getContextUsage();\n const contextWindowPercent = contextUsage?.percent ?? 0;\n\n // Get git info for this workspace\n const git = getGitInfo(this.cwd);\n\n return {\n sessionId: session.sessionId,\n sessionFile: session.sessionFile,\n model: model ? {\n id: model.id,\n name: model.name,\n provider: model.provider,\n reasoning: model.reasoning || false,\n contextWindow: model.contextWindow || 0,\n } : null,\n thinkingLevel: session.thinkingLevel as ThinkingLevel,\n isStreaming: session.isStreaming,\n isCompacting: session.isCompacting,\n autoCompactionEnabled: session.autoCompactionEnabled,\n autoRetryEnabled: session.autoRetryEnabled,\n steeringMode: session.steeringMode,\n followUpMode: session.followUpMode,\n messageCount: messages.length,\n tokens,\n contextWindowPercent,\n git,\n // Include pending questionnaire request for reconnects\n questionnaireRequest: this.extensionUIContext?.getPendingQuestionnaireRequest(),\n };\n }\n\n getMessages(): ChatMessage[] {\n if (!this.session) {\n return [];\n }\n\n return this.session.messages.map((msg) => this.convertMessage(msg));\n }\n\n async prompt(message: string, images?: ImageAttachment[]): Promise<void> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n\n // Convert ImageAttachment to Pi SDK's ImageContent format\n const imageContents: ImageContent[] | undefined = images?.map((img) => ({\n type: 'image' as const,\n data: img.source.data,\n mimeType: img.source.mediaType,\n }));\n\n await this.session.prompt(message, { images: imageContents });\n }\n\n async steer(message: string, images?: ImageAttachment[]): Promise<void> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n\n if (images && images.length > 0) {\n // Use sendUserMessage with deliverAs: 'steer' when there are images\n const imageContents: ImageContent[] = images.map((img) => ({\n type: 'image' as const,\n data: img.source.data,\n mimeType: img.source.mediaType,\n }));\n\n const content: (TextContent | ImageContent)[] = [\n { type: 'text' as const, text: message },\n ...imageContents,\n ];\n\n await this.session.sendUserMessage(content, { deliverAs: 'steer' });\n } else {\n // Use simple steer() for text-only\n await this.session.steer(message);\n }\n }\n\n async followUp(message: string): Promise<void> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n await this.session.followUp(message);\n }\n\n async abort(): Promise<void> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n await this.session.abort();\n }\n\n async setModel(provider: string, modelId: string): Promise<void> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n\n const model = this.modelRegistry.find(provider, modelId);\n if (!model) {\n throw new Error(`Model not found: ${provider}/${modelId}`);\n }\n\n await this.session.setModel(model);\n }\n\n setThinkingLevel(level: ThinkingLevel): void {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n this.session.setThinkingLevel(level);\n }\n\n async newSession(): Promise<void> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n\n const previousSessionFile = this.session.sessionFile;\n const previousMessageCount = this.session.messages.length;\n\n await this.session.newSession();\n\n if (previousSessionFile !== this.session.sessionFile) {\n this.deleteSessionFileIfEmpty(previousSessionFile, previousMessageCount);\n }\n }\n\n async switchSession(sessionPath: string): Promise<void> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n\n const previousSessionFile = this.session.sessionFile;\n const previousMessageCount = this.session.messages.length;\n\n await this.session.switchSession(sessionPath);\n\n if (previousSessionFile !== this.session.sessionFile) {\n this.deleteSessionFileIfEmpty(previousSessionFile, previousMessageCount);\n }\n }\n\n async compact(customInstructions?: string): Promise<void> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n await this.session.compact(customInstructions);\n }\n\n async listSessions(): Promise<SessionInfo[]> {\n const sessions = await SessionManager.list(this.cwd);\n return sessions.map((s) => ({\n id: s.id,\n path: s.path,\n name: s.name,\n firstMessage: s.firstMessage,\n messageCount: s.messageCount,\n updatedAt: s.modified.getTime(),\n cwd: s.cwd,\n }));\n }\n\n async getAvailableModels(): Promise<ModelInfo[]> {\n const models = await this.modelRegistry.getAvailable();\n return models.map((m) => ({\n id: m.id,\n name: m.name,\n provider: m.provider,\n reasoning: m.reasoning || false,\n contextWindow: m.contextWindow || 0,\n }));\n }\n\n /**\n * Get available slash commands (prompt templates, skills, extension commands)\n */\n getCommands(): SlashCommand[] {\n if (!this.session) {\n return [];\n }\n\n const commands: SlashCommand[] = [];\n\n // Add prompt templates\n for (const template of this.session.promptTemplates) {\n commands.push({\n name: template.name,\n description: template.description,\n source: 'template',\n path: template.filePath,\n });\n }\n\n // Add skills\n const { skills } = this.session.resourceLoader.getSkills();\n for (const skill of skills) {\n commands.push({\n name: `skill:${skill.name}`,\n description: skill.description,\n source: 'skill',\n path: skill.filePath,\n });\n }\n\n // Add extension commands\n const extensionCommands = this.session.extensionRunner?.getRegisteredCommands() ?? [];\n for (const cmd of extensionCommands) {\n commands.push({\n name: cmd.name,\n description: cmd.description ?? '(extension command)',\n source: 'extension',\n });\n }\n\n return commands;\n }\n\n /**\n * Check if the session is currently streaming/active\n */\n isActive(): boolean {\n return this.session?.isStreaming ?? false;\n }\n\n getSessionFile(): string | null {\n return this.session?.sessionFile ?? null;\n }\n\n // ============================================================================\n // Session Operations\n // ============================================================================\n\n /**\n * Fork the conversation at a specific message entry\n */\n async fork(entryId: string): Promise<{ text: string; cancelled: boolean }> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n const result = await this.session.fork(entryId);\n return { text: result.selectedText, cancelled: result.cancelled };\n }\n\n /**\n * Get messages available for forking\n */\n getForkMessages(): Array<{ entryId: string; text: string }> {\n if (!this.session) {\n return [];\n }\n return this.session.getUserMessagesForForking();\n }\n\n /**\n * Set the session name\n */\n setSessionName(name: string): void {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n this.session.setSessionName(name);\n }\n\n /**\n * Export the session to HTML\n */\n async exportHtml(outputPath?: string): Promise<string> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n return await this.session.exportToHtml(outputPath);\n }\n\n // ============================================================================\n // Model/Thinking Cycling\n // ============================================================================\n\n /**\n * Cycle to the next/previous model\n */\n async cycleModel(direction: 'forward' | 'backward' = 'forward'): Promise<{ model: ModelInfo; thinkingLevel: ThinkingLevel; isScoped: boolean } | null> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n const result = await this.session.cycleModel(direction);\n if (!result) return null;\n return {\n model: {\n id: result.model.id,\n name: result.model.name,\n provider: result.model.provider,\n reasoning: result.model.reasoning || false,\n contextWindow: result.model.contextWindow || 0,\n },\n thinkingLevel: result.thinkingLevel as ThinkingLevel,\n isScoped: result.isScoped,\n };\n }\n\n /**\n * Cycle to the next thinking level\n */\n cycleThinkingLevel(): ThinkingLevel | null {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n const level = this.session.cycleThinkingLevel();\n return level ? (level as ThinkingLevel) : null;\n }\n\n // ============================================================================\n // Mode Settings\n // ============================================================================\n\n /**\n * Set steering mode (how steering messages are delivered)\n */\n setSteeringMode(mode: 'all' | 'one-at-a-time'): void {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n this.session.setSteeringMode(mode);\n }\n\n /**\n * Set follow-up mode (how follow-up messages are delivered)\n */\n setFollowUpMode(mode: 'all' | 'one-at-a-time'): void {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n this.session.setFollowUpMode(mode);\n }\n\n /**\n * Enable/disable auto-compaction\n */\n setAutoCompaction(enabled: boolean): void {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n this.session.setAutoCompactionEnabled(enabled);\n }\n\n /**\n * Enable/disable auto-retry on errors\n */\n setAutoRetry(enabled: boolean): void {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n this.session.setAutoRetryEnabled(enabled);\n }\n\n /**\n * Abort an ongoing retry attempt\n */\n abortRetry(): void {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n this.session.abortRetry();\n }\n\n // ============================================================================\n // Bash Execution\n // ============================================================================\n\n /**\n * Execute a bash command\n * @param excludeFromContext If true, output won't be sent to LLM (!! prefix)\n */\n async executeBash(command: string, onChunk?: (chunk: string) => void, excludeFromContext = false): Promise<BashResult> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n const result = await this.session.executeBash(command, onChunk, { excludeFromContext });\n return {\n stdout: result.output,\n stderr: '', // Pi SDK combines stdout/stderr into output\n exitCode: result.exitCode ?? null,\n signal: result.cancelled ? 'SIGTERM' : null,\n timedOut: false, // Pi SDK uses cancelled for this\n truncated: result.truncated,\n };\n }\n\n /**\n * Abort a running bash command\n */\n abortBash(): void {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n this.session.abortBash();\n }\n\n // ============================================================================\n // Stats\n // ============================================================================\n\n /**\n * Get detailed session statistics\n */\n getSessionStats(): SessionStats {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n return this.session.getSessionStats();\n }\n\n /**\n * Get the last assistant response text\n */\n getLastAssistantText(): string | null {\n if (!this.session) {\n return null;\n }\n return this.session.getLastAssistantText() ?? null;\n }\n\n // ============================================================================\n // Startup Info\n // ============================================================================\n\n /**\n * Get startup info for display (version, context, skills, extensions, themes)\n */\n getStartupInfo(): StartupInfo {\n if (!this.session) {\n return {\n version: VERSION,\n contextFiles: [],\n skills: [],\n extensions: [],\n themes: [],\n shortcuts: [],\n };\n }\n\n const resourceLoader = this.session.resourceLoader;\n const pathMetadata = resourceLoader.getPathMetadata();\n\n // Helper to determine scope from path\n const getScope = (filePath: string): 'user' | 'project' => {\n const meta = pathMetadata.get(filePath);\n if (meta) {\n return meta.scope as 'user' | 'project';\n }\n // Fallback: check if path contains home directory pattern\n return filePath.includes('/.pi/') ? 'user' : 'project';\n };\n\n // Get context files (AGENTS.md) - extract just the paths\n const { agentsFiles } = resourceLoader.getAgentsFiles();\n const contextFilePaths = agentsFiles.map(f => f.path);\n \n // Get skills\n const { skills } = resourceLoader.getSkills();\n const skillInfos: StartupResourceInfo[] = skills.map(skill => ({\n name: skill.name,\n path: skill.filePath,\n description: skill.description,\n scope: getScope(skill.filePath),\n }));\n\n // Get extensions\n const extensionsResult = resourceLoader.getExtensions();\n const extensionInfos: StartupResourceInfo[] = extensionsResult.extensions.map(ext => ({\n name: ext.path.split('/').pop() || ext.path,\n path: ext.resolvedPath,\n scope: getScope(ext.resolvedPath),\n }));\n\n // Get themes - Theme class has name and sourcePath properties\n const { themes } = resourceLoader.getThemes();\n const themeInfos: StartupResourceInfo[] = themes\n .filter(theme => theme.name && theme.sourcePath) // Filter out built-in themes without paths\n .map(theme => ({\n name: theme.name!,\n path: theme.sourcePath!,\n scope: getScope(theme.sourcePath!),\n }));\n\n // Web UI shortcuts (different from TUI)\n const shortcuts = [\n { key: '\u2318O', description: 'Open directory' },\n { key: '\u2318,', description: 'Settings' },\n { key: '\u2318\\\\', description: 'Split pane' },\n { key: '\u2318.', description: 'Stop agent' },\n { key: 'Ctrl+L', description: 'Select model' },\n { key: 'Shift+Tab', description: 'Cycle thinking' },\n { key: 'Ctrl+P', description: 'Cycle models' },\n { key: 'Alt+Enter', description: 'Queue follow-up' },\n { key: '/', description: 'Commands' },\n ];\n\n return {\n version: VERSION,\n contextFiles: contextFilePaths,\n skills: skillInfos,\n extensions: extensionInfos,\n themes: themeInfos,\n shortcuts,\n };\n }\n\n // ============================================================================\n // Session Tree Navigation\n // ============================================================================\n\n /**\n * Get the session tree structure for navigation\n */\n getSessionTree(): { tree: SessionTreeNode[]; currentLeafId: string | null } {\n if (!this.session) {\n return { tree: [], currentLeafId: null };\n }\n\n const sessionManager = this.session.sessionManager;\n const sdkTree = sessionManager.getTree();\n const currentLeafId = sessionManager.getLeafId();\n\n // Convert SDK tree to our format\n const convertNode = (node: { entry: { id: string; parentId: string | null; type: string; timestamp: string; message?: { role?: string; content?: unknown } }; children: typeof sdkTree; label?: string }): SessionTreeNode => {\n let role: 'user' | 'assistant' | 'toolResult' | undefined;\n let text = '';\n\n if (node.entry.type === 'message' && node.entry.message) {\n const msg = node.entry.message;\n role = msg.role as 'user' | 'assistant' | 'toolResult' | undefined;\n // Extract text preview from message content\n const content = msg.content;\n if (typeof content === 'string') {\n text = content.slice(0, 100);\n } else if (Array.isArray(content)) {\n // Try text blocks first\n const textBlock = content.find((c: { type: string }) => c.type === 'text');\n if (textBlock && 'text' in textBlock) {\n text = (textBlock.text as string).slice(0, 100);\n }\n // If no text block, try to summarize tool_use blocks\n if (!text) {\n const toolUseBlocks = content.filter((c: { type: string }) => c.type === 'tool_use');\n if (toolUseBlocks.length > 0) {\n const toolNames = toolUseBlocks.map((t: { name?: string }) => t.name || 'tool').join(', ');\n text = `[${toolNames}]`;\n }\n }\n // If no text and no tools, try tool_result blocks\n if (!text) {\n const toolResultBlocks = content.filter((c: { type: string }) => c.type === 'tool_result');\n if (toolResultBlocks.length > 0) {\n const resultNames = toolResultBlocks.map((t: { name?: string; tool_use_id?: string }) => t.name || 'result').join(', ');\n text = `[${resultNames}]`;\n }\n }\n }\n // Final fallback for messages with no extractable text\n if (!text && role) {\n text = role === 'user' ? '[user message]' :\n role === 'assistant' ? '[assistant]' :\n role === 'toolResult' ? '[tool result]' : '[message]';\n }\n } else if (node.entry.type === 'compaction') {\n text = '[Compaction]';\n } else if (node.entry.type === 'branch_summary') {\n text = '[Branch Summary]';\n } else if (node.entry.type === 'model_change') {\n text = '[Model Change]';\n } else if (node.entry.type === 'thinking_level_change') {\n text = '[Thinking Level Change]';\n }\n\n return {\n id: node.entry.id,\n parentId: node.entry.parentId,\n type: node.entry.type === 'message' ? 'message' :\n node.entry.type === 'compaction' ? 'compaction' :\n node.entry.type === 'branch_summary' ? 'branch_summary' :\n node.entry.type === 'model_change' ? 'model_change' :\n node.entry.type === 'thinking_level_change' ? 'thinking_level_change' : 'other',\n role,\n text,\n label: node.label,\n timestamp: new Date(node.entry.timestamp).getTime(),\n children: node.children.map(convertNode),\n };\n };\n\n return {\n tree: sdkTree.map(convertNode),\n currentLeafId,\n };\n }\n\n /**\n * Navigate to a different point in the session tree\n */\n async navigateTree(targetId: string, summarize = false): Promise<{ success: boolean; editorText?: string; error?: string }> {\n if (!this.session) {\n return { success: false, error: 'Session not initialized' };\n }\n\n try {\n const result = await this.session.navigateTree(targetId, { summarize });\n return {\n success: !result.cancelled && !result.aborted,\n editorText: result.editorText,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Navigation failed',\n };\n }\n }\n\n // ============================================================================\n // Queued Messages\n // ============================================================================\n\n /**\n * Get pending queued messages\n */\n getQueuedMessages(): { steering: string[]; followUp: string[] } {\n if (!this.session) {\n return { steering: [], followUp: [] };\n }\n return {\n steering: [...this.session.getSteeringMessages()],\n followUp: [...this.session.getFollowUpMessages()],\n };\n }\n\n /**\n * Clear queued messages and return them\n */\n clearQueue(): { steering: string[]; followUp: string[] } {\n if (!this.session) {\n return { steering: [], followUp: [] };\n }\n return this.session.clearQueue();\n }\n\n // ============================================================================\n // Scoped Models\n // ============================================================================\n\n /**\n * Get scoped models (for Ctrl+P cycling)\n */\n async getScopedModels(): Promise<ScopedModelInfo[]> {\n if (!this.session) {\n return [];\n }\n\n const scopedModels = this.session.scopedModels;\n const allModels = await this.modelRegistry.getAvailable();\n\n // If scoped models are set, return them with enabled=true\n if (scopedModels.length > 0) {\n return scopedModels.map(sm => ({\n provider: sm.model.provider,\n modelId: sm.model.id,\n modelName: sm.model.name,\n thinkingLevel: sm.thinkingLevel as ThinkingLevel,\n enabled: true,\n }));\n }\n\n // Otherwise return all models with enabled=false (using session defaults)\n return allModels.map(m => ({\n provider: m.provider,\n modelId: m.id,\n modelName: m.name,\n thinkingLevel: 'off' as ThinkingLevel,\n enabled: false,\n }));\n }\n\n /**\n * Set scoped models for cycling\n */\n async setScopedModels(models: Array<{ provider: string; modelId: string; thinkingLevel: ThinkingLevel }>): Promise<void> {\n if (!this.session) {\n throw new Error('Session not initialized');\n }\n\n const scopedModels = [];\n for (const m of models) {\n const model = this.modelRegistry.find(m.provider, m.modelId);\n if (model) {\n scopedModels.push({ model, thinkingLevel: m.thinkingLevel });\n }\n }\n\n this.session.setScopedModels(scopedModels);\n }\n}\n", "import { execSync } from 'child_process';\n\nexport interface GitInfo {\n branch: string | null;\n changedFiles: number;\n}\n\nexport type GitFileStatus = 'modified' | 'added' | 'deleted' | 'renamed' | 'untracked' | 'conflicted';\n\nexport function getGitInfo(cwd: string): GitInfo {\n try {\n // Get current branch\n const branch = execSync('git rev-parse --abbrev-ref HEAD', {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n // Get number of changed files (staged + unstaged + untracked)\n const status = execSync('git status --porcelain', {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n \n const changedFiles = status.split('\\n').filter((line) => line.trim()).length;\n\n return { branch, changedFiles };\n } catch {\n // Not a git repo or git not available\n return { branch: null, changedFiles: 0 };\n }\n}\n\n/**\n * Get the current git branch name.\n * Returns null if not a git repo.\n */\nexport function getGitBranch(cwd: string): string | null {\n try {\n return execSync('git rev-parse --abbrev-ref HEAD', {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Get the worktree name if the cwd is inside a git worktree.\n * Returns null if it's the main working tree or not a git repo.\n * Detects worktrees by comparing --git-dir and --git-common-dir.\n */\nexport function getGitWorktree(cwd: string): string | null {\n try {\n const gitDir = execSync('git rev-parse --git-dir', {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n const commonDir = execSync('git rev-parse --git-common-dir', {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n // If they differ, we're in a worktree (not the main working tree)\n if (gitDir !== commonDir && gitDir !== '.git') {\n // The worktree name is the last segment of the gitDir path\n // e.g. /repo/.git/worktrees/my-worktree -> my-worktree\n const parts = gitDir.split('/');\n return parts[parts.length - 1] || null;\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get a map of changed files with their git status.\n * Keys are relative paths from the repo root.\n */\nexport function getGitChangedFiles(cwd: string): Map<string, GitFileStatus> {\n try {\n const status = execSync('git status --porcelain', {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n const changes = new Map<string, GitFileStatus>();\n \n for (const line of status.split('\\n')) {\n if (!line.trim()) continue;\n \n const statusCode = line.slice(0, 2);\n let filePath = line.slice(3);\n \n // Handle renamed files: \"R old -> new\"\n if (filePath.includes(' -> ')) {\n filePath = filePath.split(' -> ')[1];\n }\n \n // Parse git status codes (XY format where X=staged, Y=unstaged)\n if (statusCode.includes('?')) {\n changes.set(filePath, 'untracked');\n } else if (statusCode.includes('A')) {\n changes.set(filePath, 'added');\n } else if (statusCode.includes('D')) {\n changes.set(filePath, 'deleted');\n } else if (statusCode.includes('R')) {\n changes.set(filePath, 'renamed');\n } else if (statusCode.includes('U') || statusCode === 'AA' || statusCode === 'DD') {\n changes.set(filePath, 'conflicted');\n } else if (statusCode.includes('M') || statusCode.trim()) {\n changes.set(filePath, 'modified');\n }\n }\n \n return changes;\n } catch {\n // Not a git repo or git not available\n return new Map();\n }\n}\n\n/**\n * Get git status for directories (has changes inside).\n * Returns a set of directory paths that contain changed files.\n */\nexport function getGitChangedDirectories(cwd: string): Set<string> {\n const changedFiles = getGitChangedFiles(cwd);\n const changedDirs = new Set<string>();\n \n for (const filePath of changedFiles.keys()) {\n // Add all parent directories\n const parts = filePath.split('/');\n for (let i = 1; i < parts.length; i++) {\n changedDirs.add(parts.slice(0, i).join('/'));\n }\n }\n \n return changedDirs;\n}\n\n/**\n * Get the git diff for a specific file.\n * Returns unified diff output.\n */\nexport function getFileDiff(cwd: string, filePath: string): string {\n try {\n // First check if the file is untracked\n const status = execSync(`git status --porcelain -- \"${filePath}\"`, {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n if (status.startsWith('??')) {\n // Untracked file - show the entire file as added\n try {\n const content = execSync(`cat \"${filePath}\"`, {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n maxBuffer: 1024 * 1024, // 1MB\n });\n // Format as a diff with all lines added\n const lines = content.split('\\n');\n const header = `diff --git a/${filePath} b/${filePath}\nnew file mode 100644\n--- /dev/null\n+++ b/${filePath}\n@@ -0,0 +1,${lines.length} @@\n`;\n return header + lines.map(line => `+${line}`).join('\\n');\n } catch {\n return `Unable to read untracked file: ${filePath}`;\n }\n }\n\n // For tracked files, get the diff (staged + unstaged)\n // First try to get combined diff\n let diff = execSync(`git diff HEAD -- \"${filePath}\"`, {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n maxBuffer: 1024 * 1024, // 1MB\n });\n\n // If no diff from HEAD, try unstaged changes\n if (!diff.trim()) {\n diff = execSync(`git diff -- \"${filePath}\"`, {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n maxBuffer: 1024 * 1024,\n });\n }\n\n // If still no diff, try staged changes\n if (!diff.trim()) {\n diff = execSync(`git diff --cached -- \"${filePath}\"`, {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n maxBuffer: 1024 * 1024,\n });\n }\n\n return diff || 'No changes detected';\n } catch (error) {\n return `Error getting diff: ${error instanceof Error ? error.message : 'Unknown error'}`;\n }\n}\n", "/**\n * Mock TUI components for web mode.\n * \n * These components mirror the pi-tui API but instead of rendering ANSI strings,\n * they build a serializable component tree that can be sent to the web client\n * and rendered with React components.\n * \n * The approach:\n * 1. Extension calls ctx.ui.custom(factory)\n * 2. factory receives mock TUI, theme, keybindings, done\n * 3. factory creates components (MockContainer, MockSelectList, etc.)\n * 4. We extract the component tree via toNode()\n * 5. Tree is serialized and sent to client\n * 6. Client renders with React equivalents\n * 7. Client input events are routed to handleInput()\n * 8. Tree is re-extracted and sent as update\n */\n\nimport type {\n CustomUINode,\n CustomUIContainerNode,\n CustomUITextNode,\n CustomUIBorderNode,\n CustomUISelectListNode,\n CustomUILoaderNode,\n CustomUISelectItem,\n} from '@pi-deck/shared';\n\n// ============================================================================\n// Component Interface\n// ============================================================================\n\n/** Interface for mock components that can convert to serializable nodes */\nexport interface MockComponent {\n /** Convert to serializable node */\n toNode(): CustomUINode;\n /** Stub render method (returns empty - actual rendering is done client-side) */\n render(width: number): string[];\n /** Stub invalidate method */\n invalidate(): void;\n /** Handle input (for interactive components) */\n handleInput?(data: string): void;\n}\n\n// ID counter for generating unique node IDs\nlet nodeIdCounter = 0;\nfunction generateNodeId(): string {\n return `node-${++nodeIdCounter}`;\n}\n\n/** Reset node ID counter (for testing) */\nexport function resetNodeIdCounter(): void {\n nodeIdCounter = 0;\n}\n\n// ============================================================================\n// MockContainer\n// ============================================================================\n\nexport class MockContainer implements MockComponent {\n private id = generateNodeId();\n private children: MockComponent[] = [];\n\n addChild(child: MockComponent): void {\n this.children.push(child);\n }\n\n toNode(): CustomUIContainerNode {\n return {\n id: this.id,\n type: 'container',\n children: this.children.map((c) => c.toNode()),\n };\n }\n\n render(_width: number): string[] {\n return [];\n }\n\n invalidate(): void {\n // No-op in mock\n }\n}\n\n// ============================================================================\n// MockText\n// ============================================================================\n\nexport class MockText implements MockComponent {\n private id = generateNodeId();\n\n constructor(\n private content: string,\n private style?: 'normal' | 'accent' | 'muted' | 'dim' | 'warning' | 'error',\n private bold?: boolean\n ) {}\n\n toNode(): CustomUITextNode {\n return {\n id: this.id,\n type: 'text',\n content: this.content,\n style: this.style,\n bold: this.bold,\n };\n }\n\n render(_width: number): string[] {\n return [];\n }\n\n invalidate(): void {\n // No-op in mock\n }\n}\n\n// ============================================================================\n// MockSelectList\n// ============================================================================\n\nexport class MockSelectList implements MockComponent {\n private id = generateNodeId();\n private selectedIndex = 0;\n private filter = '';\n private filteredIndices: number[] | undefined;\n\n /** Callback when item is selected */\n onSelect?: (item: CustomUISelectItem) => void;\n /** Callback when cancelled */\n onCancel?: () => void;\n /** Callback when selection changes */\n onSelectionChange?: (item: CustomUISelectItem) => void;\n /** Whether search/filter is enabled */\n searchable = false;\n\n constructor(\n private items: CustomUISelectItem[],\n private maxVisible: number,\n _theme?: any // Theme is ignored in mock - styling is done client-side\n ) {}\n\n toNode(): CustomUISelectListNode {\n return {\n id: this.id,\n type: 'selectList',\n items: this.items,\n selectedIndex: this.selectedIndex,\n maxVisible: this.maxVisible,\n searchable: this.searchable,\n filter: this.filter || undefined,\n filteredIndices: this.filteredIndices,\n };\n }\n\n /** Get currently selected item */\n getSelectedItem(): CustomUISelectItem | null {\n if (this.items.length === 0) return null;\n \n if (this.filteredIndices) {\n const actualIndex = this.filteredIndices[this.selectedIndex];\n return this.items[actualIndex] ?? null;\n }\n \n return this.items[this.selectedIndex] ?? null;\n }\n\n /** Get the list of items currently visible (filtered or all) */\n private getVisibleItems(): CustomUISelectItem[] {\n if (this.filteredIndices) {\n return this.filteredIndices.map((i) => this.items[i]);\n }\n return this.items;\n }\n\n /** Update filter and recalculate filtered indices */\n private updateFilter(newFilter: string): void {\n this.filter = newFilter;\n \n if (!newFilter) {\n this.filteredIndices = undefined;\n return;\n }\n\n const lowerFilter = newFilter.toLowerCase();\n this.filteredIndices = this.items\n .map((item, index) => ({ item, index }))\n .filter(({ item }) => item.label.toLowerCase().includes(lowerFilter))\n .map(({ index }) => index);\n \n // Reset selection to first filtered item\n this.selectedIndex = 0;\n }\n\n handleInput(data: string): void {\n const visibleItems = this.getVisibleItems();\n const maxIndex = Math.max(0, visibleItems.length - 1);\n\n // Arrow keys / j/k navigation\n if (data === '\\x1b[B' || data === 'j') {\n // Arrow Down or j\n this.selectedIndex = Math.min(this.selectedIndex + 1, maxIndex);\n this.notifySelectionChange();\n return;\n }\n\n if (data === '\\x1b[A' || data === 'k') {\n // Arrow Up or k\n this.selectedIndex = Math.max(this.selectedIndex - 1, 0);\n this.notifySelectionChange();\n return;\n }\n\n // Enter - select current item\n if (data === '\\r' || data === '\\n') {\n const item = this.getSelectedItem();\n if (item && this.onSelect) {\n this.onSelect(item);\n }\n return;\n }\n\n // Escape - cancel\n if (data === '\\x1b' && !data.startsWith('\\x1b[')) {\n // Plain escape (not an escape sequence)\n if (this.onCancel) {\n this.onCancel();\n }\n return;\n }\n\n // If searchable, handle typing\n if (this.searchable && data.length === 1 && data >= ' ') {\n this.updateFilter(this.filter + data);\n return;\n }\n\n // Backspace in search mode\n if (this.searchable && (data === '\\x7f' || data === '\\b')) {\n if (this.filter.length > 0) {\n this.updateFilter(this.filter.slice(0, -1));\n }\n return;\n }\n }\n\n private notifySelectionChange(): void {\n if (this.onSelectionChange) {\n const item = this.getSelectedItem();\n if (item) {\n this.onSelectionChange(item);\n }\n }\n }\n\n render(_width: number): string[] {\n return [];\n }\n\n invalidate(): void {\n // No-op in mock\n }\n}\n\n// ============================================================================\n// MockBorder (DynamicBorder equivalent)\n// ============================================================================\n\nexport class MockBorder implements MockComponent {\n private id = generateNodeId();\n\n constructor(\n private style?: 'accent' | 'muted' | 'dim'\n ) {}\n\n toNode(): CustomUIBorderNode {\n return {\n id: this.id,\n type: 'border',\n style: this.style,\n };\n }\n\n render(_width: number): string[] {\n return [];\n }\n\n invalidate(): void {\n // No-op in mock\n }\n}\n\n// ============================================================================\n// MockLoader\n// ============================================================================\n\nexport class MockLoader implements MockComponent {\n private id = generateNodeId();\n\n /** Callback when aborted */\n onAbort?: () => void;\n\n constructor(\n private message: string,\n private bordered = false\n ) {}\n\n toNode(): CustomUILoaderNode {\n return {\n id: this.id,\n type: 'loader',\n message: this.message,\n bordered: this.bordered,\n };\n }\n\n handleInput(data: string): void {\n // Escape or Ctrl+C to abort\n if (data === '\\x1b' || data === '\\x03') {\n if (this.onAbort) {\n this.onAbort();\n }\n }\n }\n\n render(_width: number): string[] {\n return [];\n }\n\n invalidate(): void {\n // No-op in mock\n }\n}\n\n// ============================================================================\n// MockTUI\n// ============================================================================\n\n/** Mock TUI instance passed to custom() factory */\nexport class MockTUI {\n requestRender(): void {\n // No-op in mock - client handles rendering\n }\n}\n\n// ============================================================================\n// MockTheme\n// ============================================================================\n\n/**\n * Mock theme that captures styling information.\n * \n * When theme.fg('accent', 'text') is called, we capture both the text\n * and the style so it can be applied client-side.\n */\nexport class MockTheme {\n /** Callback to capture created text nodes */\n private captureText?: (text: MockText) => void;\n\n constructor(captureText?: (text: MockText) => void) {\n this.captureText = captureText;\n }\n\n /**\n * Apply foreground color style.\n * Returns the text as-is but captures the style information.\n */\n fg(color: string, text: string): string {\n const style = this.colorToStyle(color);\n const mockText = new MockText(text, style);\n this.captureText?.(mockText);\n return text;\n }\n\n /**\n * Apply bold style.\n * Returns the text as-is but captures the bold flag.\n */\n bold(text: string): string {\n const mockText = new MockText(text, undefined, true);\n this.captureText?.(mockText);\n return text;\n }\n\n /** Convert color name to style enum */\n private colorToStyle(color: string): CustomUITextNode['style'] {\n switch (color) {\n case 'accent':\n return 'accent';\n case 'muted':\n return 'muted';\n case 'dim':\n return 'dim';\n case 'warning':\n return 'warning';\n case 'error':\n return 'error';\n default:\n return 'normal';\n }\n }\n}\n\n// ============================================================================\n// Mock Keybindings Manager\n// ============================================================================\n\n/** Mock keybindings manager */\nexport class MockKeybindingsManager {\n // Stub implementation - keybindings are handled client-side\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Build a serializable component tree from a mock component.\n */\nexport function buildComponentTree(component: MockComponent): CustomUINode {\n if (typeof component.toNode === 'function') {\n return component.toNode();\n }\n \n // Fallback for components without toNode\n return {\n id: generateNodeId(),\n type: 'container',\n children: [],\n };\n}\n\n/**\n * Find a component by ID in the tree (for routing input events).\n */\nexport function findComponentById(\n root: MockComponent,\n targetId: string,\n visited = new Set<MockComponent>()\n): MockComponent | null {\n if (visited.has(root)) return null;\n visited.add(root);\n\n const node = root.toNode();\n if (node.id === targetId) {\n return root;\n }\n\n // Check children if container\n if (root instanceof MockContainer) {\n const containerNode = node as CustomUIContainerNode;\n // We need to iterate the actual children, not the serialized ones\n // This requires access to the children array\n // For now, we'll store a reference to find children\n }\n\n return null;\n}\n", "/**\n * Web-based implementation of ExtensionUIContext.\n * \n * Sends UI requests to the client via callbacks and waits for responses.\n * This enables extension commands like /review to show interactive UI\n * in the web client.\n */\n\nimport type { ExtensionUIContext, ExtensionUIDialogOptions, WidgetPlacement, ExtensionWidgetOptions } from '@mariozechner/pi-coding-agent';\nimport type { ExtensionUIRequest, ExtensionUIResponse, CustomUIState, CustomUINode, CustomUIInputEvent, QuestionnaireQuestion, QuestionnaireResponse } from '@pi-deck/shared';\nimport { MockTUI, MockTheme, MockKeybindingsManager, buildComponentTree, type MockComponent } from './web-tui-components.js';\n\n// Generate unique request IDs\nlet requestIdCounter = 0;\nfunction generateRequestId(): string {\n return `ext-ui-${Date.now()}-${++requestIdCounter}`;\n}\n\n// Generate unique custom UI session IDs\nlet customUISessionCounter = 0;\nfunction generateCustomUISessionId(): string {\n return `custom-ui-${Date.now()}-${++customUISessionCounter}`;\n}\n\n/** Callback to send UI request to client */\nexport type SendUIRequestCallback = (request: ExtensionUIRequest) => void;\n\n/** Callback to send notification */\nexport type SendNotificationCallback = (message: string, type: 'info' | 'warning' | 'error') => void;\n\n/** Callback to set editor text */\nexport type SetEditorTextCallback = (text: string) => void;\n\n/** Callback to get editor text */\nexport type GetEditorTextCallback = () => string;\n\n/** Callback to start a custom UI session */\nexport type SendCustomUIStartCallback = (state: CustomUIState) => void;\n\n/** Callback to update custom UI state */\nexport type SendCustomUIUpdateCallback = (update: { sessionId: string; root: CustomUINode }) => void;\n\n/** Callback to close a custom UI session */\nexport type SendCustomUICloseCallback = (close: { sessionId: string }) => void;\n\n/** Callback to send a questionnaire request to the client */\nexport type SendQuestionnaireRequestCallback = (request: { toolCallId: string; questions: QuestionnaireQuestion[] }) => void;\n\nexport interface WebExtensionUIContextOptions {\n /** Callback to send UI requests to the client */\n sendRequest: SendUIRequestCallback;\n /** Callback to send notifications */\n sendNotification: SendNotificationCallback;\n /** Callback to set editor text */\n setEditorText?: SetEditorTextCallback;\n /** Callback to get editor text */\n getEditorText?: GetEditorTextCallback;\n /** Callback to start a custom UI session */\n sendCustomUIStart?: SendCustomUIStartCallback;\n /** Callback to update custom UI state */\n sendCustomUIUpdate?: SendCustomUIUpdateCallback;\n /** Callback to close a custom UI session */\n sendCustomUIClose?: SendCustomUICloseCallback;\n /** Callback to send a native questionnaire request (bypasses custom UI) */\n sendQuestionnaireRequest?: SendQuestionnaireRequestCallback;\n}\n\n/**\n * Web-based ExtensionUIContext implementation.\n * \n * For interactive methods (select, confirm, input, editor), this sends\n * a request to the client and waits for a response via the pending\n * requests map.\n * \n * For non-interactive methods (notify, setStatus, setWidget, etc.),\n * these are either forwarded to the client or are no-ops in web mode.\n */\n/** Active custom UI session */\ninterface CustomUISession {\n sessionId: string;\n component: MockComponent & { handleInput?(data: string): void };\n resolve: (value: any) => void;\n}\n\nexport class WebExtensionUIContext implements ExtensionUIContext {\n private sendRequest: SendUIRequestCallback;\n private sendNotification: SendNotificationCallback;\n private _setEditorText?: SetEditorTextCallback;\n private _getEditorText?: GetEditorTextCallback;\n private _sendCustomUIStart?: SendCustomUIStartCallback;\n private _sendCustomUIUpdate?: SendCustomUIUpdateCallback;\n private _sendCustomUIClose?: SendCustomUICloseCallback;\n private _sendQuestionnaireRequest?: SendQuestionnaireRequestCallback;\n \n /** Pending requests waiting for client responses */\n private pendingRequests = new Map<string, {\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n }>();\n\n /** Active custom UI sessions */\n private customUISessions = new Map<string, CustomUISession>();\n\n /** Status text values (stored locally, not sent to client yet) */\n private statusValues = new Map<string, string>();\n\n /** Tool output expansion state */\n private toolsExpanded = true;\n\n // Questionnaire interception state\n /** Set when a questionnaire tool starts; consumed by next custom() call */\n private _questionnaireToolCallId: string | null = null;\n private _questionnaireQuestions: QuestionnaireQuestion[] | null = null;\n private _questionnaireSetTime: number = 0;\n /** Pending questionnaire resolvers keyed by toolCallId */\n private pendingQuestionnaireResolvers = new Map<string, {\n resolve: (value: any) => void;\n questions: QuestionnaireQuestion[];\n }>();\n /** Queue of recent questionnaire tool calls (for race condition handling) */\n private recentQuestionnaireCalls: Array<{ toolCallId: string; questions: QuestionnaireQuestion[]; time: number }> = [];\n\n constructor(options: WebExtensionUIContextOptions) {\n this.sendRequest = options.sendRequest;\n this.sendNotification = options.sendNotification;\n this._setEditorText = options.setEditorText;\n this._getEditorText = options.getEditorText;\n this._sendCustomUIStart = options.sendCustomUIStart;\n this._sendCustomUIUpdate = options.sendCustomUIUpdate;\n this._sendCustomUIClose = options.sendCustomUIClose;\n this._sendQuestionnaireRequest = options.sendQuestionnaireRequest;\n }\n\n /**\n * Set questionnaire mode. Called when a questionnaire tool_execution_start\n * is detected. The next custom() call will use the native questionnaire UI\n * instead of mock TUI components.\n */\n setQuestionnaireMode(toolCallId: string, questions: QuestionnaireQuestion[]): void {\n this._questionnaireToolCallId = toolCallId;\n this._questionnaireQuestions = questions;\n this._questionnaireSetTime = Date.now();\n // Also add to queue for race condition handling\n this.recentQuestionnaireCalls.push({ toolCallId, questions, time: Date.now() });\n // Clean old entries (older than 5 seconds)\n this.recentQuestionnaireCalls = this.recentQuestionnaireCalls.filter(c => Date.now() - c.time < 5000);\n }\n\n /** Whether a questionnaire with this toolCallId is still pending resolution. */\n hasPendingQuestionnaire(toolCallId: string): boolean {\n return this.pendingQuestionnaireResolvers.has(toolCallId);\n }\n\n /**\n * Handle a questionnaire response from the client.\n * Resolves the pending custom() call with the converted result.\n */\n handleQuestionnaireResponse(response: QuestionnaireResponse): void {\n const pending = this.pendingQuestionnaireResolvers.get(response.toolCallId);\n if (!pending) {\n console.warn(`[WebExtensionUIContext] No pending questionnaire for toolCallId: ${response.toolCallId}`);\n return;\n }\n\n this.pendingQuestionnaireResolvers.delete(response.toolCallId);\n\n // Convert QuestionnaireResponse to QuestionnaireResult (the format the tool expects)\n const result = {\n questions: pending.questions,\n answers: response.answers,\n cancelled: response.cancelled,\n };\n pending.resolve(result);\n }\n\n /**\n * Handle a response from the client.\n * Call this when receiving a WsExtensionUIResponseMessage.\n */\n handleResponse(response: ExtensionUIResponse): void {\n const pending = this.pendingRequests.get(response.requestId);\n if (!pending) {\n console.warn(`No pending request for response: ${response.requestId}`);\n return;\n }\n\n // Clear timeout if set\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n }\n\n this.pendingRequests.delete(response.requestId);\n\n if (response.cancelled) {\n pending.resolve(undefined);\n } else {\n pending.resolve(response.value);\n }\n }\n\n /**\n * Cancel all pending requests (e.g., when session is disposed).\n */\n cancelAllPending(): void {\n for (const [requestId, pending] of this.pendingRequests) {\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n }\n pending.resolve(undefined);\n }\n this.pendingRequests.clear();\n\n // Also cancel custom UI sessions\n for (const [sessionId, session] of this.customUISessions) {\n session.resolve(undefined);\n if (this._sendCustomUIClose) {\n this._sendCustomUIClose({ sessionId });\n }\n }\n this.customUISessions.clear();\n\n // Cancel pending questionnaire resolvers\n for (const [toolCallId, pending] of this.pendingQuestionnaireResolvers) {\n pending.resolve({ questions: pending.questions, answers: [], cancelled: true });\n }\n this.pendingQuestionnaireResolvers.clear();\n this._questionnaireToolCallId = null;\n this._questionnaireQuestions = null;\n this._questionnaireSetTime = 0;\n this.recentQuestionnaireCalls = [];\n }\n\n /**\n * Get the pending questionnaire request, if any.\n * Used for reconnects to restore UI state.\n */\n getPendingQuestionnaireRequest(): { toolCallId: string; questions: QuestionnaireQuestion[] } | undefined {\n // Check current active questionnaire\n if (this._questionnaireToolCallId && this._questionnaireQuestions) {\n return {\n toolCallId: this._questionnaireToolCallId,\n questions: this._questionnaireQuestions,\n };\n }\n // Check pending resolvers (questionnaire already sent to client, waiting for response)\n for (const [toolCallId, pending] of this.pendingQuestionnaireResolvers) {\n return { toolCallId, questions: pending.questions };\n }\n // Check recent calls queue\n if (this.recentQuestionnaireCalls.length > 0) {\n const recent = this.recentQuestionnaireCalls[this.recentQuestionnaireCalls.length - 1];\n return { toolCallId: recent.toolCallId, questions: recent.questions };\n }\n return undefined;\n }\n\n // ============================================================================\n // Interactive UI Methods (send request, wait for response)\n // ============================================================================\n\n async select(title: string, options: string[], opts?: ExtensionUIDialogOptions): Promise<string | undefined> {\n const requestId = generateRequestId();\n \n return new Promise((resolve, reject) => {\n // Set up timeout if specified\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n if (opts?.timeout) {\n timeoutId = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n resolve(undefined);\n }, opts.timeout);\n }\n\n // Handle abort signal\n if (opts?.signal) {\n opts.signal.addEventListener('abort', () => {\n if (timeoutId) clearTimeout(timeoutId);\n this.pendingRequests.delete(requestId);\n resolve(undefined);\n });\n }\n\n this.pendingRequests.set(requestId, { resolve, reject, timeoutId });\n\n this.sendRequest({\n method: 'select',\n requestId,\n title,\n options,\n timeout: opts?.timeout,\n });\n });\n }\n\n async confirm(title: string, message: string, opts?: ExtensionUIDialogOptions): Promise<boolean> {\n const requestId = generateRequestId();\n \n return new Promise((resolve, reject) => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n if (opts?.timeout) {\n timeoutId = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n resolve(false);\n }, opts.timeout);\n }\n\n if (opts?.signal) {\n opts.signal.addEventListener('abort', () => {\n if (timeoutId) clearTimeout(timeoutId);\n this.pendingRequests.delete(requestId);\n resolve(false);\n });\n }\n\n this.pendingRequests.set(requestId, { resolve, reject, timeoutId });\n\n this.sendRequest({\n method: 'confirm',\n requestId,\n title,\n message,\n timeout: opts?.timeout,\n });\n });\n }\n\n async input(title: string, placeholder?: string, opts?: ExtensionUIDialogOptions): Promise<string | undefined> {\n const requestId = generateRequestId();\n \n return new Promise((resolve, reject) => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n if (opts?.timeout) {\n timeoutId = setTimeout(() => {\n this.pendingRequests.delete(requestId);\n resolve(undefined);\n }, opts.timeout);\n }\n\n if (opts?.signal) {\n opts.signal.addEventListener('abort', () => {\n if (timeoutId) clearTimeout(timeoutId);\n this.pendingRequests.delete(requestId);\n resolve(undefined);\n });\n }\n\n this.pendingRequests.set(requestId, { resolve, reject, timeoutId });\n\n this.sendRequest({\n method: 'input',\n requestId,\n title,\n placeholder,\n timeout: opts?.timeout,\n });\n });\n }\n\n async editor(title: string, prefill?: string): Promise<string | undefined> {\n const requestId = generateRequestId();\n \n return new Promise((resolve, reject) => {\n this.pendingRequests.set(requestId, { resolve, reject });\n\n this.sendRequest({\n method: 'editor',\n requestId,\n title,\n prefill,\n });\n });\n }\n\n // ============================================================================\n // Non-interactive methods\n // ============================================================================\n\n notify(message: string, type?: 'info' | 'warning' | 'error'): void {\n this.sendNotification(message, type || 'info');\n }\n\n setStatus(key: string, text: string | undefined): void {\n if (text === undefined) {\n this.statusValues.delete(key);\n } else {\n this.statusValues.set(key, text);\n }\n // TODO: Could send status updates to client if needed\n }\n\n setWorkingMessage(message?: string): void {\n // Not implemented for web UI - could show in status bar\n }\n\n setWidget(key: string, content: any, options?: ExtensionWidgetOptions): void {\n // Widgets are TUI-specific, not supported in web UI\n // Could potentially render simple text widgets in future\n }\n\n setFooter(factory: any): void {\n // Footer is TUI-specific\n }\n\n setHeader(factory: any): void {\n // Header is TUI-specific\n }\n\n setTitle(title: string): void {\n // Could potentially update browser tab title\n }\n\n /**\n * Handle custom UI with mock TUI components.\n * \n * This creates mock versions of TUI, theme, etc. and calls the factory.\n * The resulting component tree is serialized and sent to the client for rendering.\n * \n * If questionnaire mode is active (set via setQuestionnaireMode), this bypasses\n * mock TUI rendering and uses the native web QuestionnaireUI component instead.\n */\n async custom<T>(factory: any, options?: any): Promise<T> {\n // Race guard: tool execute may call custom() before tool_execution_start is observed.\n // Wait briefly for setQuestionnaireMode() to populate questionnaire metadata.\n if (!this._questionnaireToolCallId) {\n for (let i = 0; i < 12; i++) {\n if (this._questionnaireToolCallId) break;\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n }\n\n // Fallback to recent queue if mode arrived slightly out of order\n if (!this._questionnaireToolCallId && this.recentQuestionnaireCalls.length > 0) {\n const recent = this.recentQuestionnaireCalls[this.recentQuestionnaireCalls.length - 1];\n this._questionnaireToolCallId = recent.toolCallId;\n this._questionnaireQuestions = recent.questions;\n this.recentQuestionnaireCalls = this.recentQuestionnaireCalls.filter(c => c.toolCallId !== recent.toolCallId);\n }\n\n // Intercept questionnaire tool: use native web UI instead of mock TUI\n if (this._questionnaireToolCallId && this._questionnaireQuestions && this._sendQuestionnaireRequest) {\n const toolCallId = this._questionnaireToolCallId;\n const questions = this._questionnaireQuestions;\n this._questionnaireToolCallId = null;\n this._questionnaireQuestions = null;\n return new Promise<T>((resolve) => {\n this.pendingQuestionnaireResolvers.set(toolCallId, { resolve, questions });\n this._sendQuestionnaireRequest!({ toolCallId, questions });\n });\n }\n\n // If no custom UI callbacks are set, fall back to returning undefined\n if (!this._sendCustomUIStart) {\n console.warn('[WebExtensionUIContext] custom() called but sendCustomUIStart not configured');\n return undefined as T;\n }\n\n return new Promise<T>((resolve) => {\n try {\n const sessionId = generateCustomUISessionId();\n \n // Create mock TUI and theme\n const mockTui = new MockTUI();\n const mockTheme = new MockTheme();\n const mockKeybindings = new MockKeybindingsManager();\n \n // The done callback that the factory will call when finished\n const done = (result: T) => {\n // Clean up session\n this.customUISessions.delete(sessionId);\n \n // Send close event\n if (this._sendCustomUIClose) {\n this._sendCustomUIClose({ sessionId });\n }\n \n resolve(result);\n };\n \n // Call the factory\n const component = factory(mockTui, mockTheme, mockKeybindings, done);\n \n // Handle null/undefined component\n if (!component) {\n resolve(undefined as T);\n return;\n }\n \n // Store the session\n this.customUISessions.set(sessionId, {\n sessionId,\n component,\n resolve,\n });\n \n // Build and send the initial component tree\n const root = buildComponentTree(component);\n \n this._sendCustomUIStart!({\n sessionId,\n root,\n });\n } catch (error) {\n console.error('[WebExtensionUIContext] custom() factory threw:', error);\n resolve(undefined as T);\n }\n });\n }\n\n /**\n * Handle input from the client for a custom UI session.\n */\n handleCustomUIInput(input: CustomUIInputEvent): void {\n const session = this.customUISessions.get(input.sessionId);\n if (!session) {\n console.warn(`[WebExtensionUIContext] No custom UI session for: ${input.sessionId}`);\n return;\n }\n\n // Route input to the component\n if (session.component.handleInput) {\n // Convert input event to key string\n let keyData = input.key || '';\n \n // Handle special keys\n if (input.inputType === 'key') {\n switch (input.key) {\n case 'ArrowDown':\n keyData = '\\x1b[B';\n break;\n case 'ArrowUp':\n keyData = '\\x1b[A';\n break;\n case 'ArrowLeft':\n keyData = '\\x1b[D';\n break;\n case 'ArrowRight':\n keyData = '\\x1b[C';\n break;\n case 'Enter':\n keyData = '\\r';\n break;\n case 'Escape':\n keyData = '\\x1b';\n break;\n case 'Backspace':\n keyData = '\\x7f';\n break;\n default:\n // Use the key as-is for single characters\n keyData = input.key || '';\n }\n }\n \n session.component.handleInput(keyData);\n \n // Rebuild and send updated tree\n if (this._sendCustomUIUpdate) {\n const root = buildComponentTree(session.component);\n this._sendCustomUIUpdate({\n sessionId: input.sessionId,\n root,\n });\n }\n }\n }\n\n pasteToEditor(text: string): void {\n // In web UI, paste is equivalent to setEditorText\n if (this._setEditorText) {\n this._setEditorText(text);\n }\n }\n\n setEditorText(text: string): void {\n if (this._setEditorText) {\n this._setEditorText(text);\n }\n }\n\n getEditorText(): string {\n if (this._getEditorText) {\n return this._getEditorText();\n }\n return '';\n }\n\n setEditorComponent(factory: any): void {\n // Custom editor components are TUI-specific\n }\n\n // Theme-related methods\n get theme(): any {\n // Return a minimal theme object for web UI\n // Extensions shouldn't rely on theme in web mode\n return {\n fg: (color: string, text: string) => text,\n bold: (text: string) => text,\n };\n }\n\n getAllThemes(): { name: string; path: string | undefined }[] {\n return [];\n }\n\n getTheme(name: string): any {\n return undefined;\n }\n\n setTheme(theme: string | any): { success: boolean; error?: string } {\n return { success: false, error: 'Theme switching not supported in web UI' };\n }\n\n getToolsExpanded(): boolean {\n return this.toolsExpanded;\n }\n\n setToolsExpanded(expanded: boolean): void {\n this.toolsExpanded = expanded;\n }\n}\n", "import Database from 'better-sqlite3';\nimport { existsSync, mkdirSync } from 'fs';\nimport { homedir } from 'os';\nimport { dirname, join } from 'path';\n\nexport type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';\n\nexport interface UIState {\n openWorkspaces: string[];\n activeWorkspacePath: string | null;\n draftInputs: Record<string, string>;\n sidebarWidth: number;\n themeId: string | null;\n /** Maps workspace path to active session ID */\n activeSessions: Record<string, string>;\n /** Maps workspace path to selected model */\n activeModels: Record<string, { provider: string; modelId: string }>;\n /** Maps workspace path to thinking level */\n thinkingLevels: Record<string, ThinkingLevel>;\n /** Maps workspace path to right pane visibility */\n rightPaneByWorkspace: Record<string, boolean>;\n /** Maps workspace path to saved tab pages */\n paneTabsByWorkspace: Record<string, import('@pi-deck/shared').PaneTabPageState[]>;\n /** Maps workspace path to active tab ID */\n activePaneTabByWorkspace: Record<string, string>;\n /** Maps workspace path to active plan file path */\n activePlanByWorkspace: Record<string, string>;\n}\n\nconst DEFAULT_STATE: UIState = {\n openWorkspaces: [],\n activeWorkspacePath: null,\n draftInputs: {},\n sidebarWidth: 224,\n themeId: null,\n activeSessions: {},\n activeModels: {},\n thinkingLevels: {},\n rightPaneByWorkspace: {},\n paneTabsByWorkspace: {},\n activePaneTabByWorkspace: {},\n activePlanByWorkspace: {},\n};\n\n/**\n * Manages UI state persistence with SQLite.\n * Single-user, no authentication needed.\n */\nexport class UIStateStore {\n private db: Database.Database;\n\n constructor(dbPath?: string) {\n const path = dbPath || join(homedir(), '.config', 'pi-deck', 'ui-state.db');\n \n // Ensure directory exists\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n this.db = new Database(path);\n this.init();\n }\n\n private init(): void {\n // Create table if not exists\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS ui_state (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n )\n `);\n\n // Initialize default values if table is empty\n const count = this.db.prepare('SELECT COUNT(*) as count FROM ui_state').get() as { count: number };\n if (count.count === 0) {\n this.saveState(DEFAULT_STATE);\n }\n }\n\n private getValue(key: string): string | null {\n const row = this.db.prepare('SELECT value FROM ui_state WHERE key = ?').get(key) as { value: string } | undefined;\n return row?.value ?? null;\n }\n\n private setValue(key: string, value: string): void {\n this.db.prepare(`\n INSERT INTO ui_state (key, value) VALUES (?, ?)\n ON CONFLICT(key) DO UPDATE SET value = excluded.value\n `).run(key, value);\n }\n\n /**\n * Load the full UI state\n */\n loadState(): UIState {\n const openWorkspacesRaw = this.getValue('openWorkspaces');\n const draftInputsRaw = this.getValue('draftInputs');\n const activeSessionsRaw = this.getValue('activeSessions');\n const activeModelsRaw = this.getValue('activeModels');\n const thinkingLevelsRaw = this.getValue('thinkingLevels');\n const rightPaneByWorkspaceRaw = this.getValue('rightPaneByWorkspace');\n const paneTabsByWorkspaceRaw = this.getValue('paneTabsByWorkspace');\n const activePaneTabByWorkspaceRaw = this.getValue('activePaneTabByWorkspace');\n const activePlanByWorkspaceRaw = this.getValue('activePlanByWorkspace');\n \n return {\n openWorkspaces: openWorkspacesRaw ? JSON.parse(openWorkspacesRaw) : DEFAULT_STATE.openWorkspaces,\n activeWorkspacePath: this.getValue('activeWorkspacePath') || null,\n draftInputs: draftInputsRaw ? JSON.parse(draftInputsRaw) : DEFAULT_STATE.draftInputs,\n sidebarWidth: parseInt(this.getValue('sidebarWidth') || String(DEFAULT_STATE.sidebarWidth), 10),\n themeId: this.getValue('themeId') || null,\n activeSessions: activeSessionsRaw ? JSON.parse(activeSessionsRaw) : DEFAULT_STATE.activeSessions,\n activeModels: activeModelsRaw ? JSON.parse(activeModelsRaw) : DEFAULT_STATE.activeModels,\n thinkingLevels: thinkingLevelsRaw ? JSON.parse(thinkingLevelsRaw) : DEFAULT_STATE.thinkingLevels,\n rightPaneByWorkspace: rightPaneByWorkspaceRaw ? JSON.parse(rightPaneByWorkspaceRaw) : DEFAULT_STATE.rightPaneByWorkspace,\n paneTabsByWorkspace: paneTabsByWorkspaceRaw ? JSON.parse(paneTabsByWorkspaceRaw) : DEFAULT_STATE.paneTabsByWorkspace,\n activePaneTabByWorkspace: activePaneTabByWorkspaceRaw ? JSON.parse(activePaneTabByWorkspaceRaw) : DEFAULT_STATE.activePaneTabByWorkspace,\n activePlanByWorkspace: activePlanByWorkspaceRaw ? JSON.parse(activePlanByWorkspaceRaw) : DEFAULT_STATE.activePlanByWorkspace,\n };\n }\n\n /**\n * Save the full UI state\n */\n saveState(state: UIState): void {\n this.setValue('openWorkspaces', JSON.stringify(state.openWorkspaces));\n this.setValue('activeWorkspacePath', state.activeWorkspacePath || '');\n this.setValue('draftInputs', JSON.stringify(state.draftInputs));\n this.setValue('sidebarWidth', String(state.sidebarWidth));\n this.setValue('themeId', state.themeId || '');\n this.setValue('activeSessions', JSON.stringify(state.activeSessions));\n this.setValue('activeModels', JSON.stringify(state.activeModels));\n this.setValue('thinkingLevels', JSON.stringify(state.thinkingLevels));\n this.setValue('rightPaneByWorkspace', JSON.stringify(state.rightPaneByWorkspace));\n this.setValue('paneTabsByWorkspace', JSON.stringify(state.paneTabsByWorkspace));\n this.setValue('activePaneTabByWorkspace', JSON.stringify(state.activePaneTabByWorkspace));\n this.setValue('activePlanByWorkspace', JSON.stringify(state.activePlanByWorkspace));\n }\n\n /**\n * Update specific fields of UI state\n */\n updateState(updates: Partial<UIState>): UIState {\n const current = this.loadState();\n const updated = { ...current, ...updates };\n this.saveState(updated);\n return updated;\n }\n\n /**\n * Update just the open workspaces list\n */\n setOpenWorkspaces(paths: string[]): void {\n this.setValue('openWorkspaces', JSON.stringify(paths));\n }\n\n /**\n * Update just the active workspace\n */\n setActiveWorkspace(path: string | null): void {\n this.setValue('activeWorkspacePath', path || '');\n }\n\n /**\n * Update a single draft input\n */\n setDraftInput(workspacePath: string, value: string): void {\n const draftInputsRaw = this.getValue('draftInputs');\n const draftInputs = draftInputsRaw ? JSON.parse(draftInputsRaw) : {};\n \n if (value) {\n draftInputs[workspacePath] = value;\n } else {\n delete draftInputs[workspacePath];\n }\n \n this.setValue('draftInputs', JSON.stringify(draftInputs));\n }\n\n /**\n * Update sidebar width\n */\n setSidebarWidth(width: number): void {\n this.setValue('sidebarWidth', String(width));\n }\n\n /**\n * Update theme\n */\n setThemeId(themeId: string | null): void {\n this.setValue('themeId', themeId || '');\n }\n\n /**\n * Update active session for a workspace\n */\n setActiveSession(workspacePath: string, sessionId: string): void {\n const activeSessionsRaw = this.getValue('activeSessions');\n const activeSessions = activeSessionsRaw ? JSON.parse(activeSessionsRaw) : {};\n \n if (sessionId) {\n activeSessions[workspacePath] = sessionId;\n } else {\n delete activeSessions[workspacePath];\n }\n \n this.setValue('activeSessions', JSON.stringify(activeSessions));\n }\n\n /**\n * Get active session for a workspace\n */\n getActiveSession(workspacePath: string): string | null {\n const activeSessionsRaw = this.getValue('activeSessions');\n const activeSessions = activeSessionsRaw ? JSON.parse(activeSessionsRaw) : {};\n return activeSessions[workspacePath] || null;\n }\n\n /**\n * Update active model for a workspace\n */\n setActiveModel(workspacePath: string, provider: string, modelId: string): void {\n const activeModelsRaw = this.getValue('activeModels');\n const activeModels = activeModelsRaw ? JSON.parse(activeModelsRaw) : {};\n \n activeModels[workspacePath] = { provider, modelId };\n this.setValue('activeModels', JSON.stringify(activeModels));\n }\n\n /**\n * Update thinking level for a workspace\n */\n setThinkingLevel(workspacePath: string, level: ThinkingLevel): void {\n const thinkingLevelsRaw = this.getValue('thinkingLevels');\n const thinkingLevels = thinkingLevelsRaw ? JSON.parse(thinkingLevelsRaw) : {};\n \n thinkingLevels[workspacePath] = level;\n this.setValue('thinkingLevels', JSON.stringify(thinkingLevels));\n }\n\n /**\n * Set or clear the active plan for a workspace\n */\n setActivePlan(workspacePath: string, planPath: string | null): void {\n const raw = this.getValue('activePlanByWorkspace');\n const activePlans = raw ? JSON.parse(raw) : {};\n \n if (planPath) {\n activePlans[workspacePath] = planPath;\n } else {\n delete activePlans[workspacePath];\n }\n \n this.setValue('activePlanByWorkspace', JSON.stringify(activePlans));\n }\n\n /**\n * Clear the active plan for a workspace.\n */\n clearActivePlan(workspacePath: string): void {\n this.setActivePlan(workspacePath, null);\n }\n\n /**\n * Get the active plan path for a workspace\n */\n getActivePlan(workspacePath: string): string | null {\n const raw = this.getValue('activePlanByWorkspace');\n const activePlans = raw ? JSON.parse(raw) : {};\n return activePlans[workspacePath] || null;\n }\n\n /**\n * Close the database connection\n */\n close(): void {\n this.db.close();\n }\n}\n\n// Singleton instance\nlet storeInstance: UIStateStore | null = null;\n\nexport function getUIStateStore(): UIStateStore {\n if (!storeInstance) {\n storeInstance = new UIStateStore();\n }\n return storeInstance;\n}\n", "/**\n * Core sync state engine.\n * \n * This is the single source of truth for all workspace state.\n * All mutations go through here, are persisted, and broadcast to clients.\n */\n\nimport { EventEmitter } from 'events';\nimport type { ActiveJobState, ActivePlanState, DirectoryEntry, JobInfo, PaneTabPageState, PlanInfo } from '@pi-deck/shared';\nimport { SQLiteStore } from './SQLiteStore.js';\n\n// State types (will expand as we implement)\nexport interface GlobalState {\n version: number;\n lastModified: number;\n workspaces: Map<string, WorkspaceState>;\n}\n\nexport interface SessionInfo {\n id: string;\n path: string;\n name?: string;\n firstMessage?: string;\n messageCount: number;\n updatedAt: number;\n cwd?: string;\n}\n\nexport interface WorkspaceState {\n id: string;\n path: string;\n active: boolean;\n slots: Map<string, SlotState>;\n panes: PaneState;\n sessions: SessionInfo[];\n plans: PlanInfo[];\n jobs: JobInfo[];\n activePlan: ActivePlanState | null;\n activeJobs: ActiveJobState[];\n rightPaneOpen: boolean;\n paneTabs: PaneTabPageState[];\n activePaneTab: string | null;\n /** Directory entries for watched paths (file tree) */\n directoryEntries: Map<string, DirectoryEntry[]>;\n /** Paths currently being watched */\n watchedDirectories: Set<string>;\n /** File watcher statistics */\n fileWatcherStats: { watchedCount: number; maxWatched: number; isAtLimit: boolean } | null;\n createdAt: number;\n lastModified: number;\n}\n\nexport interface SlotState {\n id: string;\n sessionId: string | null;\n sessionFile: string | null;\n messages: unknown[]; // Will be ChatMessage[]\n isStreaming: boolean;\n isCompacting: boolean;\n pendingUI: PendingUI | null;\n activeTools: ToolExecution[];\n queuedMessages: { steering: string[]; followUp: string[] };\n lastModified: number;\n}\n\nexport interface PaneState {\n tabs: string[]; // slotIds\n activeTab: string | null;\n splitView: boolean;\n}\n\nexport interface PendingUI {\n type: 'questionnaire' | 'extensionDialog' | 'customUI';\n id: string;\n data: unknown;\n createdAt: number;\n}\n\nexport interface ToolExecution {\n toolCallId: string;\n toolName: string;\n status: 'running' | 'completed' | 'error';\n args: Record<string, unknown>;\n result?: unknown;\n startedAt: number;\n endedAt?: number;\n}\n\n// Mutation types\nexport type StateMutation =\n | { type: 'workspaceCreate'; workspaceId: string; path: string }\n | { type: 'workspaceClose'; workspaceId: string }\n | { type: 'slotCreate'; workspaceId: string; slotId: string }\n | { type: 'slotUpdate'; workspaceId: string; slotId: string; updates: Partial<SlotState> }\n | { type: 'slotDelete'; workspaceId: string; slotId: string }\n | { type: 'messagesAppend'; workspaceId: string; slotId: string; messages: unknown[] }\n | { type: 'pendingUISet'; workspaceId: string; slotId: string; pendingUI: PendingUI | null }\n | { type: 'toolExecutionStart'; workspaceId: string; slotId: string; execution: ToolExecution }\n | { type: 'toolExecutionEnd'; workspaceId: string; slotId: string; toolCallId: string; result: unknown; error?: boolean }\n | { type: 'paneUpdate'; workspaceId: string; updates: Partial<PaneState> }\n | { type: 'sessionsUpdate'; workspaceId: string; sessions: SessionInfo[] }\n | { type: 'plansUpdate'; workspaceId: string; plans: PlanInfo[] }\n | { type: 'jobsUpdate'; workspaceId: string; jobs: JobInfo[] }\n | { type: 'activePlanUpdate'; workspaceId: string; activePlan: ActivePlanState | null }\n | { type: 'activeJobsUpdate'; workspaceId: string; activeJobs: ActiveJobState[] }\n | {\n type: 'workspaceUIUpdate';\n workspaceId: string;\n workspacePath: string;\n rightPaneOpen: boolean;\n paneTabs: PaneTabPageState[];\n activePaneTab: string | null;\n }\n | {\n type: 'queuedMessagesUpdate';\n workspaceId: string;\n slotId: string;\n queuedMessages: { steering: string[]; followUp: string[] };\n }\n | {\n type: 'directoryEntriesUpdate';\n workspaceId: string;\n directoryPath: string;\n entries: DirectoryEntry[];\n }\n | {\n type: 'directoryWatchError';\n workspaceId: string;\n directoryPath: string;\n error: string;\n }\n | {\n type: 'watchedDirectoryAdd';\n workspaceId: string;\n directoryPath: string;\n }\n | {\n type: 'watchedDirectoryRemove';\n workspaceId: string;\n directoryPath: string;\n }\n | {\n type: 'fileWatcherStatsUpdate';\n workspaceId: string;\n stats: { watchedCount: number; maxWatched: number; isAtLimit: boolean };\n };\n\n// Events emitted by SyncState\nexport interface SyncStateEvents {\n 'stateChanged': { workspaceId: string; version: number; mutation: StateMutation };\n 'workspaceCreated': { workspaceId: string; state: WorkspaceState };\n 'workspaceClosed': { workspaceId: string };\n 'clientStale': { clientId: string; currentVersion: number; clientVersion: number };\n}\n\nexport class SyncState extends EventEmitter {\n private store: SQLiteStore;\n private inMemoryState: GlobalState;\n private vacuumInterval: NodeJS.Timeout | null = null;\n\n constructor(dbPath: string) {\n super();\n this.store = new SQLiteStore(dbPath);\n this.inMemoryState = {\n version: 0,\n lastModified: Date.now(),\n workspaces: new Map(),\n };\n \n // Periodically vacuum old deltas\n this.vacuumInterval = setInterval(() => {\n this.store.vacuumDeltas();\n }, 5 * 60 * 1000); // Every 5 minutes\n }\n\n /**\n * Apply a mutation to state, persist it, and return the new version\n */\n mutate(mutation: StateMutation): number {\n const newVersion = this.inMemoryState.version + 1;\n \n // Apply to in-memory state\n this.applyMutation(mutation, newVersion);\n \n // Persist delta\n const delta = Buffer.from(JSON.stringify(mutation));\n this.store.storeDelta(\n this.getWorkspaceIdFromMutation(mutation),\n newVersion,\n this.inMemoryState.version,\n delta\n );\n \n // Update global version\n this.inMemoryState.version = newVersion;\n this.inMemoryState.lastModified = Date.now();\n \n // Emit event for broadcasting\n const workspaceId = this.getWorkspaceIdFromMutation(mutation);\n this.emit('stateChanged', { \n workspaceId, \n version: newVersion, \n mutation \n });\n \n // Occasionally create snapshot (every 100 versions)\n if (newVersion % 100 === 0) {\n this.createSnapshot(workspaceId);\n }\n \n return newVersion;\n }\n\n /**\n * Get current state for a workspace\n */\n getWorkspaceState(workspaceId: string): WorkspaceState | undefined {\n return this.inMemoryState.workspaces.get(workspaceId);\n }\n\n /**\n * Get full global state (for debugging/admin)\n */\n getGlobalState(): GlobalState {\n return {\n version: this.inMemoryState.version,\n lastModified: this.inMemoryState.lastModified,\n workspaces: new Map(this.inMemoryState.workspaces),\n };\n }\n\n /**\n * Get state delta since a specific version\n */\n getDeltaSince(workspaceId: string, sinceVersion: number): StateMutation[] {\n const deltas = this.store.getDeltasSince(workspaceId, sinceVersion);\n return deltas.map(d => JSON.parse(d.changes.toString()) as StateMutation);\n }\n\n /**\n * Get snapshot at or before a version\n */\n async getSnapshotAtVersion(workspaceId: string, version?: number): Promise<WorkspaceState | null> {\n // If version not specified, get latest\n if (version === undefined) {\n const state = this.getWorkspaceState(workspaceId);\n return state ? this.cloneWorkspaceState(state) : null;\n }\n \n // Try to find snapshot\n const snapshot = this.store.getSnapshotAtVersion(workspaceId, version);\n if (snapshot) {\n return JSON.parse(snapshot.state.toString()) as WorkspaceState;\n }\n \n // Reconstruct from earlier snapshot + deltas\n const latestSnapshot = this.store.getLatestSnapshot(workspaceId);\n if (!latestSnapshot) {\n return null;\n }\n \n let state = JSON.parse(latestSnapshot.state.toString()) as WorkspaceState;\n const deltas = this.store.getDeltasSince(workspaceId, latestSnapshot.version);\n \n for (const delta of deltas) {\n if (delta.version <= version) {\n const mutation = JSON.parse(delta.changes.toString()) as StateMutation;\n state = this.applyMutationToWorkspace(state, mutation);\n }\n }\n \n return state;\n }\n\n /**\n * Register a client connection\n */\n registerClient(clientId: string, workspaceId: string): void {\n this.store.registerClient(clientId, workspaceId);\n }\n\n /**\n * Update client's acknowledged version\n */\n clientAck(clientId: string, version: number): void {\n this.store.updateClientAck(clientId, version);\n }\n\n /**\n * Remove a client\n */\n removeClient(clientId: string): void {\n this.store.removeClient(clientId);\n }\n\n /**\n * Get clients that need updates\n */\n getStaleClients(workspaceId: string, currentVersion: number): Array<{ clientId: string; lastAckVersion: number }> {\n const clients = this.store.getWorkspaceClients(workspaceId);\n return clients\n .filter(c => c.last_ack_version < currentVersion)\n .map(c => ({ clientId: c.client_id, lastAckVersion: c.last_ack_version }));\n }\n\n /**\n * Dispose and cleanup\n */\n dispose(): void {\n if (this.vacuumInterval) {\n clearInterval(this.vacuumInterval);\n }\n this.store.close();\n this.removeAllListeners();\n }\n\n // ============ Private methods ============\n\n private applyMutation(mutation: StateMutation, version: number): void {\n switch (mutation.type) {\n case 'workspaceCreate': {\n const existing = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (existing) {\n existing.active = true;\n existing.path = mutation.path;\n existing.lastModified = Date.now();\n this.emit('workspaceCreated', { workspaceId: mutation.workspaceId, state: existing });\n break;\n }\n\n const workspace: WorkspaceState = {\n id: mutation.workspaceId,\n path: mutation.path,\n active: true,\n slots: new Map(),\n panes: { tabs: [], activeTab: null, splitView: false },\n sessions: [],\n plans: [],\n jobs: [],\n activePlan: null,\n activeJobs: [],\n rightPaneOpen: false,\n paneTabs: [],\n activePaneTab: null,\n directoryEntries: new Map(),\n watchedDirectories: new Set(),\n fileWatcherStats: null,\n createdAt: Date.now(),\n lastModified: Date.now(),\n };\n this.inMemoryState.workspaces.set(mutation.workspaceId, workspace);\n this.emit('workspaceCreated', { workspaceId: mutation.workspaceId, state: workspace });\n break;\n }\n \n case 'workspaceClose': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.active = false;\n workspace.lastModified = Date.now();\n }\n this.emit('workspaceClosed', { workspaceId: mutation.workspaceId });\n break;\n }\n \n case 'slotCreate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n if (!workspace.slots.has(mutation.slotId)) {\n const slot: SlotState = {\n id: mutation.slotId,\n sessionId: null,\n sessionFile: null,\n messages: [],\n isStreaming: false,\n isCompacting: false,\n pendingUI: null,\n activeTools: [],\n queuedMessages: { steering: [], followUp: [] },\n lastModified: Date.now(),\n };\n workspace.slots.set(mutation.slotId, slot);\n }\n workspace.lastModified = Date.now();\n }\n break;\n }\n \n case 'slotUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n const slot = workspace?.slots.get(mutation.slotId);\n if (slot) {\n Object.assign(slot, mutation.updates, { lastModified: Date.now() });\n if (workspace) {\n workspace.lastModified = Date.now();\n }\n }\n break;\n }\n \n case 'slotDelete': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.slots.delete(mutation.slotId);\n workspace.lastModified = Date.now();\n }\n break;\n }\n \n case 'messagesAppend': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n const slot = workspace?.slots.get(mutation.slotId);\n if (slot) {\n slot.messages.push(...mutation.messages);\n slot.lastModified = Date.now();\n if (workspace) {\n workspace.lastModified = Date.now();\n }\n }\n break;\n }\n \n case 'pendingUISet': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n const slot = workspace?.slots.get(mutation.slotId);\n if (slot) {\n slot.pendingUI = mutation.pendingUI;\n slot.lastModified = Date.now();\n if (workspace) {\n workspace.lastModified = Date.now();\n }\n }\n break;\n }\n \n case 'toolExecutionStart': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n const slot = workspace?.slots.get(mutation.slotId);\n if (slot) {\n slot.activeTools.push(mutation.execution);\n slot.lastModified = Date.now();\n if (workspace) {\n workspace.lastModified = Date.now();\n }\n }\n break;\n }\n \n case 'toolExecutionEnd': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n const slot = workspace?.slots.get(mutation.slotId);\n if (slot) {\n const tool = slot.activeTools.find(t => t.toolCallId === mutation.toolCallId);\n if (tool) {\n tool.status = mutation.error ? 'error' : 'completed';\n tool.result = mutation.result;\n tool.endedAt = Date.now();\n }\n slot.lastModified = Date.now();\n if (workspace) {\n workspace.lastModified = Date.now();\n }\n }\n break;\n }\n \n case 'paneUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n Object.assign(workspace.panes, mutation.updates);\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'sessionsUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.sessions = mutation.sessions;\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'plansUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.plans = mutation.plans;\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'jobsUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.jobs = mutation.jobs;\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'activePlanUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.activePlan = mutation.activePlan;\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'activeJobsUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.activeJobs = mutation.activeJobs;\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'workspaceUIUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.rightPaneOpen = mutation.rightPaneOpen;\n workspace.paneTabs = mutation.paneTabs;\n workspace.activePaneTab = mutation.activePaneTab;\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'queuedMessagesUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n const slot = workspace?.slots.get(mutation.slotId);\n if (slot) {\n slot.queuedMessages = mutation.queuedMessages;\n slot.lastModified = Date.now();\n if (workspace) {\n workspace.lastModified = Date.now();\n }\n }\n break;\n }\n\n case 'directoryEntriesUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.directoryEntries.set(mutation.directoryPath, mutation.entries);\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'directoryWatchError': {\n // Error is logged and emitted, but state doesn't change\n break;\n }\n\n case 'watchedDirectoryAdd': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.watchedDirectories.add(mutation.directoryPath);\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'watchedDirectoryRemove': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.watchedDirectories.delete(mutation.directoryPath);\n workspace.directoryEntries.delete(mutation.directoryPath);\n workspace.lastModified = Date.now();\n }\n break;\n }\n\n case 'fileWatcherStatsUpdate': {\n const workspace = this.inMemoryState.workspaces.get(mutation.workspaceId);\n if (workspace) {\n workspace.fileWatcherStats = mutation.stats;\n workspace.lastModified = Date.now();\n }\n break;\n }\n }\n }\n\n private getWorkspaceIdFromMutation(mutation: StateMutation): string {\n return mutation.workspaceId;\n }\n\n private createSnapshot(workspaceId: string): void {\n const state = this.getWorkspaceState(workspaceId);\n if (state) {\n const serialized = Buffer.from(JSON.stringify(state));\n this.store.storeSnapshot(workspaceId, this.inMemoryState.version, serialized);\n }\n }\n\n private cloneWorkspaceState(state: WorkspaceState): WorkspaceState {\n return {\n ...state,\n slots: new Map(state.slots),\n sessions: [...state.sessions],\n plans: [...state.plans],\n jobs: [...state.jobs],\n activeJobs: [...state.activeJobs],\n paneTabs: [...state.paneTabs],\n directoryEntries: new Map(state.directoryEntries),\n watchedDirectories: new Set(state.watchedDirectories),\n fileWatcherStats: state.fileWatcherStats,\n };\n }\n\n private applyMutationToWorkspace(state: WorkspaceState, mutation: StateMutation): WorkspaceState {\n // Simplified - in reality would need to properly reconstruct\n // For now, just return the mutation-applied state\n this.applyMutation(mutation, 0);\n return this.getWorkspaceState(state.id) || state;\n }\n}\n", "/**\n * SQLite persistence layer for sync state.\n * Stores snapshots, deltas, and client registry.\n */\n\nimport Database from 'better-sqlite3';\nimport { mkdirSync } from 'fs';\nimport { dirname } from 'path';\n\nexport interface StoredSnapshot {\n id: number;\n workspace_id: string;\n version: number;\n state: Buffer;\n timestamp: number;\n}\n\nexport interface StoredDelta {\n id: number;\n workspace_id: string;\n version: number;\n base_version: number;\n changes: Buffer;\n timestamp: number;\n}\n\nexport interface StoredClient {\n client_id: string;\n workspace_id: string;\n last_ack_version: number;\n connected_at: number;\n last_seen_at: number;\n}\n\nexport class SQLiteStore {\n private db: Database.Database;\n\n constructor(dbPath: string) {\n // Ensure directory exists\n mkdirSync(dirname(dbPath), { recursive: true });\n \n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('foreign_keys = ON');\n \n this.initTables();\n }\n\n private initTables(): void {\n // Snapshots - full state blobs\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n workspace_id TEXT NOT NULL,\n version INTEGER NOT NULL,\n state BLOB NOT NULL,\n timestamp INTEGER NOT NULL,\n UNIQUE(workspace_id, version)\n );\n CREATE INDEX IF NOT EXISTS idx_snapshots_workspace ON snapshots(workspace_id, version);\n `);\n\n // Deltas - incremental changes\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS deltas (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n workspace_id TEXT NOT NULL,\n version INTEGER NOT NULL,\n base_version INTEGER NOT NULL,\n changes BLOB NOT NULL,\n timestamp INTEGER NOT NULL,\n UNIQUE(workspace_id, version)\n );\n CREATE INDEX IF NOT EXISTS idx_deltas_workspace ON deltas(workspace_id, version);\n CREATE INDEX IF NOT EXISTS idx_deltas_timestamp ON deltas(timestamp);\n `);\n\n // Clients - connected client registry\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS clients (\n client_id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n last_ack_version INTEGER NOT NULL DEFAULT 0,\n connected_at INTEGER NOT NULL,\n last_seen_at INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_clients_workspace ON clients(workspace_id);\n `);\n }\n\n /**\n * Store a full state snapshot\n */\n storeSnapshot(workspaceId: string, version: number, state: Buffer): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO snapshots (workspace_id, version, state, timestamp)\n VALUES (?, ?, ?, ?)\n `);\n stmt.run(workspaceId, version, state, Date.now());\n }\n\n /**\n * Get the latest snapshot for a workspace\n */\n getLatestSnapshot(workspaceId: string): StoredSnapshot | null {\n const stmt = this.db.prepare(`\n SELECT * FROM snapshots \n WHERE workspace_id = ? \n ORDER BY version DESC \n LIMIT 1\n `);\n return stmt.get(workspaceId) as StoredSnapshot | null;\n }\n\n /**\n * Get snapshot at specific version\n */\n getSnapshotAtVersion(workspaceId: string, version: number): StoredSnapshot | null {\n const stmt = this.db.prepare(`\n SELECT * FROM snapshots \n WHERE workspace_id = ? AND version = ?\n `);\n return stmt.get(workspaceId, version) as StoredSnapshot | null;\n }\n\n /**\n * Store a delta (incremental change)\n */\n storeDelta(workspaceId: string, version: number, baseVersion: number, changes: Buffer): void {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO deltas (workspace_id, version, base_version, changes, timestamp)\n VALUES (?, ?, ?, ?, ?)\n `);\n stmt.run(workspaceId, version, baseVersion, changes, Date.now());\n }\n\n /**\n * Get deltas for a workspace since a specific version\n */\n getDeltasSince(workspaceId: string, sinceVersion: number): StoredDelta[] {\n const stmt = this.db.prepare(`\n SELECT * FROM deltas \n WHERE workspace_id = ? AND version > ?\n ORDER BY version ASC\n `);\n return stmt.all(workspaceId, sinceVersion) as StoredDelta[];\n }\n\n /**\n * Register or update a client\n */\n registerClient(clientId: string, workspaceId: string): void {\n const now = Date.now();\n const stmt = this.db.prepare(`\n INSERT INTO clients (client_id, workspace_id, last_ack_version, connected_at, last_seen_at)\n VALUES (?, ?, 0, ?, ?)\n ON CONFLICT(client_id) DO UPDATE SET\n workspace_id = excluded.workspace_id,\n last_seen_at = excluded.last_seen_at\n `);\n stmt.run(clientId, workspaceId, now, now);\n }\n\n /**\n * Update client's last acknowledged version\n */\n updateClientAck(clientId: string, version: number): void {\n const stmt = this.db.prepare(`\n UPDATE clients \n SET last_ack_version = ?, last_seen_at = ?\n WHERE client_id = ?\n `);\n stmt.run(version, Date.now(), clientId);\n }\n\n /**\n * Remove a client\n */\n removeClient(clientId: string): void {\n const stmt = this.db.prepare(`DELETE FROM clients WHERE client_id = ?`);\n stmt.run(clientId);\n }\n\n /**\n * Get clients for a workspace\n */\n getWorkspaceClients(workspaceId: string): StoredClient[] {\n const stmt = this.db.prepare(`\n SELECT * FROM clients WHERE workspace_id = ?\n `);\n return stmt.all(workspaceId) as StoredClient[];\n }\n\n /**\n * Clean up old deltas (keep last N or time window)\n */\n vacuumDeltas(keepCount: number = 1000, maxAgeMs: number = 24 * 60 * 60 * 1000): void {\n const cutoffTime = Date.now() - maxAgeMs;\n \n // Delete old deltas beyond the keep count\n this.db.exec(`\n DELETE FROM deltas \n WHERE id IN (\n SELECT id FROM deltas \n WHERE timestamp < ${cutoffTime}\n ORDER BY version ASC \n LIMIT -1 OFFSET ${keepCount}\n )\n `);\n }\n\n /**\n * Get the current version for a workspace\n */\n getCurrentVersion(workspaceId: string): number {\n const stmt = this.db.prepare(`\n SELECT MAX(version) as version FROM deltas WHERE workspace_id = ?\n `);\n const result = stmt.get(workspaceId) as { version: number | null };\n return result.version ?? 0;\n }\n\n close(): void {\n this.db.close();\n }\n}\n", "/**\n * SyncManager - Bridges SyncState with WebSocket clients\n * \n * Handles:\n * - Client connections/disconnections\n * - Broadcasting state changes\n * - Handling client mutations\n * - Reconnection sync (deltas vs snapshots)\n */\n\nimport { EventEmitter } from 'events';\nimport type { WebSocket } from 'ws';\nimport { SyncState, type StateMutation, type WorkspaceState } from './SyncState.js';\n\nexport interface SyncClient {\n id: string;\n ws: WebSocket;\n workspaceId: string;\n lastAckVersion: number;\n connectedAt: number;\n}\n\nexport interface SyncMessage {\n type: 'sync' | 'mutate' | 'ack' | 'snapshot' | 'delta';\n clientId?: string;\n workspaceId?: string;\n version?: number;\n sinceVersion?: number;\n mutation?: StateMutation;\n state?: unknown;\n deltas?: StateMutation[];\n error?: string;\n}\n\nexport class SyncManager extends EventEmitter {\n private syncState: SyncState;\n private clients = new Map<string, SyncClient>();\n private clientIdCounter = 0;\n\n constructor(dbPath: string) {\n super();\n this.syncState = new SyncState(dbPath);\n \n // Listen for state changes and broadcast to clients\n this.syncState.on('stateChanged', ({ workspaceId, version, mutation }) => {\n this.broadcastDelta(workspaceId, version, mutation);\n });\n }\n\n /**\n * Register a new WebSocket client\n */\n registerClient(ws: WebSocket, workspaceId: string): string {\n const clientId = `client-${++this.clientIdCounter}`;\n \n const client: SyncClient = {\n id: clientId,\n ws,\n workspaceId,\n lastAckVersion: 0,\n connectedAt: Date.now(),\n };\n \n this.clients.set(clientId, client);\n this.syncState.registerClient(clientId, workspaceId);\n \n // Handle messages from client\n ws.on('message', (data) => {\n try {\n const message = JSON.parse(data.toString()) as SyncMessage;\n this.handleClientMessage(clientId, message);\n } catch (err) {\n console.error('[SyncManager] Failed to parse client message:', err);\n }\n });\n \n // Handle disconnect\n ws.on('close', () => {\n this.unregisterClient(clientId);\n });\n \n ws.on('error', (err) => {\n console.error(`[SyncManager] Client ${clientId} error:`, err);\n this.unregisterClient(clientId);\n });\n \n return clientId;\n }\n\n /**\n * Unregister a client\n */\n unregisterClient(clientId: string): void {\n const client = this.clients.get(clientId);\n if (client) {\n this.clients.delete(clientId);\n this.syncState.removeClient(clientId);\n }\n }\n\n /**\n * Send initial sync to a client (full state or deltas)\n */\n async sendInitialSync(clientId: string, sinceVersion?: number): Promise<void> {\n const client = this.clients.get(clientId);\n if (!client) return;\n \n const workspaceState = this.syncState.getWorkspaceState(client.workspaceId);\n if (!workspaceState) {\n this.sendToClient(clientId, { type: 'snapshot', state: null });\n return;\n }\n \n // If no sinceVersion or very old, send full snapshot\n const currentVersion = this.syncState.getGlobalState().version;\n const shouldSendSnapshot = !sinceVersion || (currentVersion - (sinceVersion || 0)) > 50;\n \n if (shouldSendSnapshot) {\n this.sendToClient(clientId, {\n type: 'snapshot',\n version: currentVersion,\n state: this.serializeWorkspaceState(workspaceState),\n });\n } else {\n // Send deltas\n const deltas = this.syncState.getDeltaSince(client.workspaceId, sinceVersion || 0);\n this.sendToClient(clientId, {\n type: 'delta',\n version: currentVersion,\n sinceVersion: sinceVersion || 0,\n deltas,\n });\n }\n \n // Update client's ack version\n client.lastAckVersion = currentVersion;\n this.syncState.clientAck(clientId, currentVersion);\n }\n\n /**\n * Apply a mutation (from server-side code)\n */\n mutate(mutation: StateMutation): number {\n return this.syncState.mutate(mutation);\n }\n\n /**\n * Get current workspace state\n */\n getWorkspaceState(workspaceId: string) {\n return this.syncState.getWorkspaceState(workspaceId);\n }\n\n /**\n * Dispose\n */\n dispose(): void {\n this.syncState.dispose();\n this.clients.clear();\n this.removeAllListeners();\n }\n\n // ============ Private methods ============\n\n private handleClientMessage(clientId: string, message: SyncMessage): void {\n const client = this.clients.get(clientId);\n if (!client) return;\n \n switch (message.type) {\n case 'mutate':\n if (message.mutation) {\n this.syncState.mutate(message.mutation);\n }\n break;\n \n case 'ack':\n if (message.version !== undefined) {\n client.lastAckVersion = message.version;\n this.syncState.clientAck(clientId, message.version);\n }\n break;\n \n case 'sync':\n this.sendInitialSync(clientId, message.sinceVersion);\n break;\n }\n }\n\n private broadcastDelta(workspaceId: string, version: number, mutation: StateMutation): void {\n const staleClients = this.syncState.getStaleClients(workspaceId, version);\n \n for (const { clientId } of staleClients) {\n const client = this.clients.get(clientId);\n if (client && client.workspaceId === workspaceId) {\n this.sendToClient(clientId, {\n type: 'delta',\n version,\n deltas: [mutation],\n });\n }\n }\n }\n\n private sendToClient(clientId: string, message: SyncMessage): void {\n const client = this.clients.get(clientId);\n if (!client) return;\n \n if (client.ws.readyState === 1) { // WebSocket.OPEN\n client.ws.send(JSON.stringify(message));\n }\n }\n\n private serializeWorkspaceState(state: WorkspaceState): unknown {\n const slots: Record<string, unknown> = {};\n for (const [slotId, slot] of state.slots.entries()) {\n slots[slotId] = {\n ...slot,\n queuedMessages: {\n steering: [...slot.queuedMessages.steering],\n followUp: [...slot.queuedMessages.followUp],\n },\n activeTools: [...slot.activeTools],\n messages: [...slot.messages],\n };\n }\n\n // Convert Maps to objects for serialization\n const directoryEntries: Record<string, unknown[]> = {};\n for (const [path, entries] of state.directoryEntries.entries()) {\n directoryEntries[path] = entries;\n }\n\n return {\n ...state,\n slots,\n sessions: [...state.sessions],\n plans: [...state.plans],\n jobs: [...state.jobs],\n activeJobs: [...state.activeJobs],\n paneTabs: [...state.paneTabs],\n directoryEntries,\n watchedDirectories: [...state.watchedDirectories],\n };\n }\n}\n", "/**\n * Sync Integration Layer\n *\n * Bridges the existing event-based architecture with the new sync-based state.\n * This allows gradual migration without rewriting everything at once.\n */\n\nimport { EventEmitter } from 'events';\nimport type { WebSocket } from 'ws';\nimport { SyncManager, type SyncMessage } from './SyncManager.js';\nimport type { SessionInfo as SyncSessionInfo } from './SyncState.js';\nimport type { ActiveJobState, ActivePlanState, JobInfo, PaneTabPageState, PlanInfo, SessionEvent } from '@pi-deck/shared';\nimport { ScopedFileWatcher } from './FileWatcher.js';\nimport { PlanJobWatcher } from './PlanJobWatcher.js';\n\nexport class SyncIntegration extends EventEmitter {\n private syncManager: SyncManager;\n private clientWsMap = new Map<string, WebSocket>();\n private fileWatchers = new Map<string, ScopedFileWatcher>(); // workspaceId -> watcher\n private planJobWatcher: PlanJobWatcher;\n\n constructor(dbPath: string) {\n super();\n this.syncManager = new SyncManager(dbPath);\n this.planJobWatcher = new PlanJobWatcher({ debounceMs: 500 });\n\n // Forward sync events to WebSocket clients\n this.syncManager.on('stateChanged', ({ workspaceId, version, mutation }) => {\n this.emit('syncStateChanged', { workspaceId, version, mutation });\n });\n\n // Listen for plan/job changes from watcher\n this.planJobWatcher.on('plansChanged', ({ workspaceId, plans }) => {\n this.syncManager.mutate({\n type: 'plansUpdate',\n workspaceId,\n plans,\n });\n });\n\n this.planJobWatcher.on('jobsChanged', ({ workspaceId, jobs }) => {\n this.syncManager.mutate({\n type: 'jobsUpdate',\n workspaceId,\n jobs,\n });\n });\n }\n\n /**\n * Register a WebSocket client for sync\n */\n registerClient(ws: WebSocket, workspaceId: string, clientId?: string): string {\n const id = clientId || this.syncManager.registerClient(ws, workspaceId);\n this.clientWsMap.set(id, ws);\n \n // Handle sync messages from client\n ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString()) as SyncMessage & { type: string };\n if (msg.type === 'sync' || msg.type === 'mutate' || msg.type === 'ack') {\n // Forward to sync manager\n this.handleClientSyncMessage(id, msg);\n }\n } catch {\n // Not a sync message, ignore\n }\n });\n\n // Send initial sync\n this.syncManager.sendInitialSync(id);\n \n return id;\n }\n\n /**\n * Convert a session event to a state mutation\n */\n handleSessionEvent(workspaceId: string, slotId: string, event: SessionEvent): void {\n switch (event.type) {\n case 'messageStart': {\n this.syncManager.mutate({\n type: 'messagesAppend',\n workspaceId,\n slotId,\n messages: [event.message],\n });\n break;\n }\n \n case 'messageUpdate': {\n // Update streaming text - could be optimized\n break;\n }\n \n case 'messageEnd': {\n // Message complete - update with final content\n break;\n }\n \n case 'toolStart': {\n this.syncManager.mutate({\n type: 'toolExecutionStart',\n workspaceId,\n slotId,\n execution: {\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n status: 'running',\n args: event.args,\n startedAt: Date.now(),\n },\n });\n break;\n }\n \n case 'toolEnd': {\n this.syncManager.mutate({\n type: 'toolExecutionEnd',\n workspaceId,\n slotId,\n toolCallId: event.toolCallId,\n result: event.result,\n error: event.isError,\n });\n break;\n }\n \n case 'agentStart': {\n this.syncManager.mutate({\n type: 'slotUpdate',\n workspaceId,\n slotId,\n updates: { isStreaming: true },\n });\n break;\n }\n \n case 'agentEnd': {\n this.syncManager.mutate({\n type: 'slotUpdate',\n workspaceId,\n slotId,\n updates: { isStreaming: false },\n });\n break;\n }\n \n case 'compactionStart': {\n this.syncManager.mutate({\n type: 'slotUpdate',\n workspaceId,\n slotId,\n updates: { isCompacting: true },\n });\n break;\n }\n \n case 'compactionEnd': {\n this.syncManager.mutate({\n type: 'slotUpdate',\n workspaceId,\n slotId,\n updates: { isCompacting: false },\n });\n break;\n }\n }\n }\n\n /**\n * Set pending questionnaire UI\n */\n setPendingQuestionnaire(workspaceId: string, slotId: string, toolCallId: string, questions: unknown[]): void {\n this.syncManager.mutate({\n type: 'pendingUISet',\n workspaceId,\n slotId,\n pendingUI: {\n type: 'questionnaire',\n id: toolCallId,\n data: { toolCallId, questions },\n createdAt: Date.now(),\n },\n });\n }\n\n /**\n * Clear pending UI (questionnaire answered)\n */\n clearPendingUI(workspaceId: string, slotId: string): void {\n this.syncManager.mutate({\n type: 'pendingUISet',\n workspaceId,\n slotId,\n pendingUI: null,\n });\n }\n\n /**\n * Sync sessions list for a workspace.\n */\n setSessions(workspaceId: string, sessions: SyncSessionInfo[]): void {\n this.syncManager.mutate({\n type: 'sessionsUpdate',\n workspaceId,\n sessions,\n });\n }\n\n /**\n * Sync plans list for a workspace.\n */\n setPlans(workspaceId: string, plans: PlanInfo[]): void {\n this.syncManager.mutate({\n type: 'plansUpdate',\n workspaceId,\n plans,\n });\n }\n\n /**\n * Sync jobs list for a workspace.\n */\n setJobs(workspaceId: string, jobs: JobInfo[]): void {\n this.syncManager.mutate({\n type: 'jobsUpdate',\n workspaceId,\n jobs,\n });\n }\n\n /**\n * Sync active plan state for a workspace.\n */\n setActivePlan(workspaceId: string, activePlan: ActivePlanState | null): void {\n this.syncManager.mutate({\n type: 'activePlanUpdate',\n workspaceId,\n activePlan,\n });\n }\n\n /**\n * Sync active job states for a workspace.\n */\n setActiveJobs(workspaceId: string, activeJobs: ActiveJobState[]): void {\n this.syncManager.mutate({\n type: 'activeJobsUpdate',\n workspaceId,\n activeJobs,\n });\n }\n\n /**\n * Get workspace state from sync\n */\n getWorkspaceState(workspaceId: string) {\n return this.syncManager.getWorkspaceState(workspaceId);\n }\n\n /**\n * Create workspace in sync state and start watching plans/jobs.\n */\n createWorkspace(workspaceId: string, path: string): void {\n this.syncManager.mutate({\n type: 'workspaceCreate',\n workspaceId,\n path,\n });\n\n // Start watching plan/job directories for this workspace\n this.planJobWatcher.watchWorkspace(workspaceId, path);\n }\n\n /**\n * Create slot in sync state\n */\n createSlot(workspaceId: string, slotId: string): void {\n this.syncManager.mutate({\n type: 'slotCreate',\n workspaceId,\n slotId,\n });\n }\n\n /**\n * Delete slot in sync state.\n */\n deleteSlot(workspaceId: string, slotId: string): void {\n this.syncManager.mutate({\n type: 'slotDelete',\n workspaceId,\n slotId,\n });\n }\n\n /**\n * Sync workspace UI state used by multi-tab web UI.\n */\n setWorkspaceUI(\n workspaceId: string,\n workspacePath: string,\n rightPaneOpen: boolean,\n paneTabs: PaneTabPageState[],\n activePaneTab: string | null,\n ): void {\n this.syncManager.mutate({\n type: 'workspaceUIUpdate',\n workspaceId,\n workspacePath,\n rightPaneOpen,\n paneTabs,\n activePaneTab,\n });\n }\n\n /**\n * Sync queued steering/follow-up messages for a slot.\n */\n setQueuedMessages(\n workspaceId: string,\n slotId: string,\n queuedMessages: { steering: string[]; followUp: string[] },\n ): void {\n this.syncManager.mutate({\n type: 'queuedMessagesUpdate',\n workspaceId,\n slotId,\n queuedMessages,\n });\n }\n\n /**\n * Mark workspace closed in sync state and stop watching plans/jobs.\n */\n closeWorkspace(workspaceId: string): void {\n this.syncManager.mutate({\n type: 'workspaceClose',\n workspaceId,\n });\n\n // Stop watching plan/job directories\n this.planJobWatcher.unwatchWorkspace(workspaceId);\n }\n\n private handleClientSyncMessage(clientId: string, message: SyncMessage): void {\n // Handle sync-related messages\n if (message.type === 'sync') {\n this.syncManager.sendInitialSync(clientId, message.sinceVersion);\n }\n }\n\n /**\n * Start watching a directory for file changes.\n * Called when client expands a folder in the file tree.\n */\n watchDirectory(workspaceId: string, dirPath: string): void {\n let watcher = this.fileWatchers.get(workspaceId);\n if (!watcher) {\n watcher = new ScopedFileWatcher({ debounceMs: 100, maxWatchedDirs: 50 });\n this.setupFileWatcherListeners(workspaceId, watcher);\n this.fileWatchers.set(workspaceId, watcher);\n }\n\n // Watch the directory and emit initial state\n const entries = watcher.watchDirectory(dirPath);\n if (entries !== null) {\n // Emit mutation to add to watched directories\n this.syncManager.mutate({\n type: 'watchedDirectoryAdd',\n workspaceId,\n directoryPath: dirPath,\n });\n\n // Emit mutation with current entries\n this.syncManager.mutate({\n type: 'directoryEntriesUpdate',\n workspaceId,\n directoryPath: dirPath,\n entries,\n });\n\n // Update stats\n this.syncManager.mutate({\n type: 'fileWatcherStatsUpdate',\n workspaceId,\n stats: watcher.getStats(),\n });\n } else {\n // Directory doesn't exist or can't be watched\n this.syncManager.mutate({\n type: 'directoryWatchError',\n workspaceId,\n directoryPath: dirPath,\n error: 'Failed to watch directory - may not exist or insufficient permissions',\n });\n }\n }\n\n /**\n * Stop watching a directory.\n * Called when client collapses a folder in the file tree.\n */\n unwatchDirectory(workspaceId: string, dirPath: string): void {\n const watcher = this.fileWatchers.get(workspaceId);\n if (watcher) {\n watcher.unwatchDirectory(dirPath);\n\n // Emit mutation to remove from watched directories\n this.syncManager.mutate({\n type: 'watchedDirectoryRemove',\n workspaceId,\n directoryPath: dirPath,\n });\n\n // Update stats\n const stats = watcher.getStats();\n this.syncManager.mutate({\n type: 'fileWatcherStatsUpdate',\n workspaceId,\n stats,\n });\n\n // Clean up if no more watched directories\n if (watcher.getWatchedPaths().length === 0) {\n watcher.unwatchAll();\n this.fileWatchers.delete(workspaceId);\n // Clear stats when no longer watching\n this.syncManager.mutate({\n type: 'fileWatcherStatsUpdate',\n workspaceId,\n stats: { watchedCount: 0, maxWatched: 50, isAtLimit: false },\n });\n }\n }\n }\n\n /**\n * Get entries for a directory (without starting a watch).\n * Used for initial load before client requests watching.\n */\n getDirectoryEntries(workspaceId: string, dirPath: string): import('@pi-deck/shared').DirectoryEntry[] | null {\n const watcher = this.fileWatchers.get(workspaceId);\n if (watcher) {\n return watcher.getEntries(dirPath);\n }\n return null;\n }\n\n /**\n * Get file watcher stats for a workspace.\n */\n getFileWatcherStats(workspaceId: string): { watchedCount: number; maxWatched: number; isAtLimit: boolean } | null {\n const watcher = this.fileWatchers.get(workspaceId);\n if (watcher) {\n return watcher.getStats();\n }\n return null;\n }\n\n /**\n * Stop all file watching for a workspace.\n * Called when workspace is closed.\n */\n stopFileWatching(workspaceId: string): void {\n const watcher = this.fileWatchers.get(workspaceId);\n if (watcher) {\n watcher.unwatchAll();\n this.fileWatchers.delete(workspaceId);\n }\n }\n\n private setupFileWatcherListeners(workspaceId: string, watcher: ScopedFileWatcher): void {\n watcher.on('change', (event) => {\n // Find which watched directory this change belongs to\n const watchedPaths = watcher.getWatchedPaths();\n for (const dirPath of watchedPaths) {\n if (event.path.startsWith(dirPath + '/') || event.path === dirPath) {\n // Get updated entries for this directory\n const entries = watcher.getEntries(dirPath);\n if (entries !== null) {\n this.syncManager.mutate({\n type: 'directoryEntriesUpdate',\n workspaceId,\n directoryPath: dirPath,\n entries,\n });\n }\n break;\n }\n }\n });\n\n watcher.on('error', ({ path, error }) => {\n console.error(`[SyncIntegration] File watcher error for ${path}:`, error);\n this.syncManager.mutate({\n type: 'directoryWatchError',\n workspaceId,\n directoryPath: path,\n error: error instanceof Error ? error.message : String(error),\n });\n });\n\n watcher.on('evicted', ({ path }) => {\n // Directory was evicted due to LRU limit\n this.syncManager.mutate({\n type: 'watchedDirectoryRemove',\n workspaceId,\n directoryPath: path,\n });\n // Update stats\n const stats = watcher.getStats();\n this.syncManager.mutate({\n type: 'fileWatcherStatsUpdate',\n workspaceId,\n stats,\n });\n });\n\n watcher.on('deleted', ({ path }) => {\n // Directory was deleted - remove from sync state\n console.log(`[SyncIntegration] Directory deleted: ${path}`);\n this.syncManager.mutate({\n type: 'watchedDirectoryRemove',\n workspaceId,\n directoryPath: path,\n });\n // Clear the entries since directory no longer exists\n this.syncManager.mutate({\n type: 'directoryEntriesUpdate',\n workspaceId,\n directoryPath: path,\n entries: [],\n });\n });\n }\n\n dispose(): void {\n // Clean up all file watchers\n for (const [workspaceId, watcher] of this.fileWatchers) {\n watcher.unwatchAll();\n }\n this.fileWatchers.clear();\n\n // Clean up plan/job watcher\n this.planJobWatcher.unwatchAll();\n\n this.syncManager.dispose();\n this.clientWsMap.clear();\n this.removeAllListeners();\n }\n}\n", "/**\n * ScopedFileWatcher - Watches only user-expanded directories in the file tree.\n *\n * Uses non-recursive fs.watch() to monitor specific directories.\n * Emits CRDT mutations through SyncState when files change.\n */\n\nimport { FSWatcher, watch } from 'fs';\nimport { readdirSync, statSync, existsSync } from 'fs';\nimport { join, basename } from 'path';\nimport { EventEmitter } from 'events';\nimport type { DirectoryEntry } from '@pi-deck/shared';\n\nexport interface FileWatcherEvent {\n type: 'add' | 'remove' | 'change' | 'deleted';\n path: string;\n entry?: DirectoryEntry;\n}\n\ninterface WatchedDirectory {\n path: string;\n watcher: FSWatcher;\n lastAccessed: number; // For LRU eviction\n entries: Map<string, DirectoryEntry>; // Current known entries\n}\n\nexport interface FileWatcherOptions {\n debounceMs?: number;\n maxWatchedDirs?: number;\n}\n\nconst DEFAULT_OPTIONS: Required<FileWatcherOptions> = {\n debounceMs: 100,\n maxWatchedDirs: 50,\n};\n\nexport class ScopedFileWatcher extends EventEmitter {\n private watchedDirs = new Map<string, WatchedDirectory>();\n private debounceTimers = new Map<string, NodeJS.Timeout>();\n private options: Required<FileWatcherOptions>;\n\n constructor(options: FileWatcherOptions = {}) {\n super();\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Start watching a directory. Returns the current entries.\n * If already watching, updates lastAccessed time and returns current entries.\n */\n watchDirectory(dirPath: string): DirectoryEntry[] | null {\n // Normalize path\n const normalizedPath = dirPath.replace(/\\\\/g, '/');\n\n // Check if already watching\n const existing = this.watchedDirs.get(normalizedPath);\n if (existing) {\n existing.lastAccessed = Date.now();\n return Array.from(existing.entries.values());\n }\n\n // Check if directory exists\n if (!existsSync(normalizedPath)) {\n return null;\n }\n\n // Evict oldest if at limit\n if (this.watchedDirs.size >= this.options.maxWatchedDirs) {\n this.evictOldest();\n }\n\n try {\n // Get initial entries\n const entries = this.scanDirectory(normalizedPath);\n const entriesMap = new Map<string, DirectoryEntry>();\n for (const entry of entries) {\n entriesMap.set(entry.name, entry);\n }\n\n // Start watcher (non-recursive)\n const watcher = watch(normalizedPath, { recursive: false }, (eventType, filename) => {\n if (!filename) return;\n this.handleFsEvent(normalizedPath, filename, eventType);\n });\n\n // Handle watcher errors\n watcher.on('error', (error) => {\n console.error(`[FileWatcher] Error watching ${normalizedPath}:`, error);\n this.emit('error', { path: normalizedPath, error });\n this.unwatchDirectory(normalizedPath);\n });\n\n // Check if directory still exists after a short delay (catches rapid delete after creation)\n setTimeout(() => {\n if (!existsSync(normalizedPath)) {\n console.log(`[FileWatcher] Directory deleted shortly after watching: ${normalizedPath}`);\n this.emit('deleted', { path: normalizedPath });\n this.unwatchDirectory(normalizedPath);\n }\n }, 100);\n\n this.watchedDirs.set(normalizedPath, {\n path: normalizedPath,\n watcher,\n lastAccessed: Date.now(),\n entries: entriesMap,\n });\n\n console.log(`[FileWatcher] Started watching ${normalizedPath} (${entries.length} entries)`);\n this.emit('watchStarted', { path: normalizedPath, entries });\n\n return entries;\n } catch (error) {\n console.error(`[FileWatcher] Failed to watch ${normalizedPath}:`, error);\n this.emit('error', { path: normalizedPath, error });\n return null;\n }\n }\n\n /**\n * Stop watching a directory.\n */\n unwatchDirectory(dirPath: string): void {\n const normalizedPath = dirPath.replace(/\\\\/g, '/');\n const watched = this.watchedDirs.get(normalizedPath);\n if (!watched) return;\n\n watched.watcher.close();\n this.watchedDirs.delete(normalizedPath);\n console.log(`[FileWatcher] Stopped watching ${normalizedPath}`);\n this.emit('watchStopped', { path: normalizedPath });\n }\n\n /**\n * Stop all watchers.\n */\n unwatchAll(): void {\n for (const [path, watched] of this.watchedDirs) {\n watched.watcher.close();\n console.log(`[FileWatcher] Stopped watching ${path}`);\n }\n this.watchedDirs.clear();\n this.emit('watchStopped', { path: '*' });\n }\n\n /**\n * Get current entries for a watched directory.\n */\n getEntries(dirPath: string): DirectoryEntry[] | null {\n const normalizedPath = dirPath.replace(/\\\\/g, '/');\n const watched = this.watchedDirs.get(normalizedPath);\n if (!watched) return null;\n watched.lastAccessed = Date.now();\n return Array.from(watched.entries.values());\n }\n\n /**\n * Check if a directory is being watched.\n */\n isWatching(dirPath: string): boolean {\n return this.watchedDirs.has(dirPath.replace(/\\\\/g, '/'));\n }\n\n /**\n * Get list of all watched directories.\n */\n getWatchedPaths(): string[] {\n return Array.from(this.watchedDirs.keys());\n }\n\n /**\n * Get watcher statistics.\n */\n getStats(): { watchedCount: number; maxWatched: number; isAtLimit: boolean } {\n return {\n watchedCount: this.watchedDirs.size,\n maxWatched: this.options.maxWatchedDirs,\n isAtLimit: this.watchedDirs.size >= this.options.maxWatchedDirs,\n };\n }\n\n /**\n * Force a rescan of a watched directory and emit updates.\n */\n rescan(dirPath: string): DirectoryEntry[] | null {\n const normalizedPath = dirPath.replace(/\\\\/g, '/');\n const watched = this.watchedDirs.get(normalizedPath);\n if (!watched) return null;\n\n const newEntries = this.scanDirectory(normalizedPath);\n const newEntriesMap = new Map<string, DirectoryEntry>();\n for (const entry of newEntries) {\n newEntriesMap.set(entry.name, entry);\n }\n\n // Compare and emit changes\n this.detectChanges(watched, newEntriesMap);\n\n watched.entries = newEntriesMap;\n watched.lastAccessed = Date.now();\n return newEntries;\n }\n\n private scanDirectory(dirPath: string): DirectoryEntry[] {\n const entries: DirectoryEntry[] = [];\n\n try {\n const items = readdirSync(dirPath);\n\n for (const item of items) {\n // Skip hidden files/directories\n if (item.startsWith('.')) continue;\n\n const itemPath = join(dirPath, item);\n try {\n const stat = statSync(itemPath);\n if (stat.isDirectory()) {\n entries.push({\n name: item,\n path: itemPath,\n hasPiSessions: this.checkForPiSessions(itemPath),\n });\n }\n } catch {\n // Skip items we can't stat\n }\n }\n } catch {\n // Directory may have been deleted\n }\n\n // Sort alphabetically, directories first\n entries.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));\n return entries;\n }\n\n private checkForPiSessions(dirPath: string): boolean {\n try {\n const piSessionsPath = join(dirPath, '.pi', 'sessions');\n return existsSync(piSessionsPath);\n } catch {\n return false;\n }\n }\n\n private handleFsEvent(dirPath: string, filename: string, eventType: string): void {\n // Debounce events for this directory\n const key = dirPath;\n const existingTimer = this.debounceTimers.get(key);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n this.debounceTimers.set(key, setTimeout(() => {\n this.debounceTimers.delete(key);\n this.processFsEvent(dirPath, filename, eventType);\n }, this.options.debounceMs));\n }\n\n private processFsEvent(dirPath: string, filename: string, _eventType: string): void {\n const watched = this.watchedDirs.get(dirPath);\n if (!watched) return;\n\n // Skip hidden files\n if (filename.startsWith('.')) return;\n\n const fullPath = join(dirPath, filename);\n\n // Check if file/directory exists now\n let exists = false;\n let isDirectory = false;\n let hasPiSessions = false;\n\n try {\n const stat = statSync(fullPath);\n exists = true;\n isDirectory = stat.isDirectory();\n if (isDirectory) {\n hasPiSessions = this.checkForPiSessions(fullPath);\n }\n } catch {\n // File doesn't exist (was deleted)\n }\n\n const existingEntry = watched.entries.get(filename);\n\n if (exists) {\n const entry: DirectoryEntry = {\n name: filename,\n path: fullPath,\n hasPiSessions,\n };\n\n if (!existingEntry) {\n // New file/directory added\n watched.entries.set(filename, entry);\n this.emit('change', {\n type: 'add',\n path: fullPath,\n entry,\n } as FileWatcherEvent);\n } else {\n // Existing entry changed (metadata like hasPiSessions might have changed)\n watched.entries.set(filename, entry);\n this.emit('change', {\n type: 'change',\n path: fullPath,\n entry,\n } as FileWatcherEvent);\n }\n } else if (existingEntry) {\n // File/directory was removed\n watched.entries.delete(filename);\n this.emit('change', {\n type: 'remove',\n path: fullPath,\n entry: existingEntry,\n } as FileWatcherEvent);\n }\n\n watched.lastAccessed = Date.now();\n }\n\n private detectChanges(watched: WatchedDirectory, newEntries: Map<string, DirectoryEntry>): void {\n const oldEntries = watched.entries;\n\n // Find added entries\n for (const [name, entry] of newEntries) {\n if (!oldEntries.has(name)) {\n this.emit('change', {\n type: 'add',\n path: entry.path,\n entry,\n } as FileWatcherEvent);\n }\n }\n\n // Find removed entries\n for (const [name, entry] of oldEntries) {\n if (!newEntries.has(name)) {\n this.emit('change', {\n type: 'remove',\n path: entry.path,\n entry,\n } as FileWatcherEvent);\n }\n }\n }\n\n private evictOldest(): void {\n let oldest: WatchedDirectory | null = null;\n let oldestPath = '';\n\n for (const [path, watched] of this.watchedDirs) {\n if (!oldest || watched.lastAccessed < oldest.lastAccessed) {\n oldest = watched;\n oldestPath = path;\n }\n }\n\n if (oldest && oldestPath) {\n console.log(`[FileWatcher] LRU evicting ${oldestPath}`);\n this.unwatchDirectory(oldestPath);\n this.emit('evicted', { path: oldestPath });\n }\n }\n}\n", "/**\n * PlanJobWatcher - Watches .pi/plans/ and .pi/jobs/ directories for changes.\n *\n * Uses fs.watch() recursively on these bounded directories.\n * Debounces changes and emits plansUpdate/jobsUpdate mutations.\n */\n\nimport { FSWatcher, watch } from 'fs';\nimport { existsSync, statSync } from 'fs';\nimport { join, basename } from 'path';\nimport { EventEmitter } from 'events';\nimport { discoverPlans, parsePlan } from '../plan-service.js';\nimport { discoverJobs, parseJob } from '../job-service.js';\nimport type { PlanInfo, JobInfo } from '@pi-deck/shared';\n\nexport interface PlanJobWatcherEvent {\n type: 'plansChanged' | 'jobsChanged';\n workspaceId: string;\n plans?: PlanInfo[];\n jobs?: JobInfo[];\n}\n\ninterface WatchedWorkspace {\n workspaceId: string;\n workspacePath: string;\n planWatchers: Map<string, FSWatcher>; // dirPath -> watcher\n jobWatchers: Map<string, FSWatcher>; // dirPath -> watcher\n lastPlanUpdate: number;\n lastJobUpdate: number;\n}\n\nexport interface PlanJobWatcherOptions {\n debounceMs?: number;\n}\n\nconst DEFAULT_OPTIONS: Required<PlanJobWatcherOptions> = {\n debounceMs: 500, // Longer debounce for agent writes\n};\n\nexport class PlanJobWatcher extends EventEmitter {\n private watchedWorkspaces = new Map<string, WatchedWorkspace>();\n private debounceTimers = new Map<string, NodeJS.Timeout>();\n private options: Required<PlanJobWatcherOptions>;\n\n constructor(options: PlanJobWatcherOptions = {}) {\n super();\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n /**\n * Start watching plan/job directories for a workspace.\n */\n watchWorkspace(workspaceId: string, workspacePath: string): void {\n if (this.watchedWorkspaces.has(workspaceId)) {\n return; // Already watching\n }\n\n const watched: WatchedWorkspace = {\n workspaceId,\n workspacePath,\n planWatchers: new Map(),\n jobWatchers: new Map(),\n lastPlanUpdate: 0,\n lastJobUpdate: 0,\n };\n\n this.watchedWorkspaces.set(workspaceId, watched);\n\n // Watch plan directories\n this.watchPlanDirectories(watched);\n\n // Watch job directories\n this.watchJobDirectories(watched);\n\n console.log(`[PlanJobWatcher] Started watching workspace ${workspaceId}`);\n }\n\n /**\n * Stop watching plan/job directories for a workspace.\n */\n unwatchWorkspace(workspaceId: string): void {\n const watched = this.watchedWorkspaces.get(workspaceId);\n if (!watched) return;\n\n // Close all plan watchers\n for (const [path, watcher] of watched.planWatchers) {\n watcher.close();\n console.log(`[PlanJobWatcher] Stopped watching plans: ${path}`);\n }\n\n // Close all job watchers\n for (const [path, watcher] of watched.jobWatchers) {\n watcher.close();\n console.log(`[PlanJobWatcher] Stopped watching jobs: ${path}`);\n }\n\n // Clear debounce timers\n for (const key of this.debounceTimers.keys()) {\n if (key.startsWith(`${workspaceId}:`)) {\n const timer = this.debounceTimers.get(key);\n if (timer) clearTimeout(timer);\n this.debounceTimers.delete(key);\n }\n }\n\n this.watchedWorkspaces.delete(workspaceId);\n console.log(`[PlanJobWatcher] Stopped watching workspace ${workspaceId}`);\n }\n\n /**\n * Force a rescan and emit updates for a workspace.\n */\n rescan(workspaceId: string): { plans: PlanInfo[]; jobs: JobInfo[] } | null {\n const watched = this.watchedWorkspaces.get(workspaceId);\n if (!watched) return null;\n\n const plans = discoverPlans(watched.workspacePath);\n const jobs = discoverJobs(watched.workspacePath);\n\n this.emit('plansChanged', { workspaceId, plans });\n this.emit('jobsChanged', { workspaceId, jobs });\n\n return { plans, jobs };\n }\n\n /**\n * Stop all watchers.\n */\n unwatchAll(): void {\n for (const workspaceId of this.watchedWorkspaces.keys()) {\n this.unwatchWorkspace(workspaceId);\n }\n }\n\n private watchPlanDirectories(watched: WatchedWorkspace): void {\n // Import plan-service functions dynamically\n import('../plan-service.js').then(({ getPlanDirectories }) => {\n const dirs = getPlanDirectories(watched.workspacePath);\n\n for (const dir of dirs) {\n if (!existsSync(dir)) {\n // Directory doesn't exist yet - we'll watch it when it's created\n continue;\n }\n\n try {\n const watcher = this.createPlanWatcher(watched.workspaceId, dir);\n watched.planWatchers.set(dir, watcher);\n console.log(`[PlanJobWatcher] Watching plans: ${dir}`);\n } catch (error) {\n console.error(`[PlanJobWatcher] Failed to watch plans dir ${dir}:`, error);\n }\n }\n });\n }\n\n private watchJobDirectories(watched: WatchedWorkspace): void {\n // Import job-service functions dynamically\n import('../job-service.js').then(({ getJobDirectories }) => {\n const dirs = getJobDirectories(watched.workspacePath);\n\n for (const dir of dirs) {\n if (!existsSync(dir)) {\n continue;\n }\n\n try {\n const watcher = this.createJobWatcher(watched.workspaceId, dir);\n watched.jobWatchers.set(dir, watcher);\n console.log(`[PlanJobWatcher] Watching jobs: ${dir}`);\n } catch (error) {\n console.error(`[PlanJobWatcher] Failed to watch jobs dir ${dir}:`, error);\n }\n }\n });\n }\n\n private createPlanWatcher(workspaceId: string, dirPath: string): FSWatcher {\n const watcher = watch(dirPath, { recursive: true }, (eventType, filename) => {\n if (!filename) return;\n if (!filename.endsWith('.md')) return; // Only care about markdown files\n\n this.handlePlanChange(workspaceId, dirPath, filename);\n });\n\n watcher.on('error', (error) => {\n console.error(`[PlanJobWatcher] Plan watcher error for ${dirPath}:`, error);\n });\n\n return watcher;\n }\n\n private createJobWatcher(workspaceId: string, dirPath: string): FSWatcher {\n const watcher = watch(dirPath, { recursive: true }, (eventType, filename) => {\n if (!filename) return;\n if (!filename.endsWith('.md')) return;\n\n this.handleJobChange(workspaceId, dirPath, filename);\n });\n\n watcher.on('error', (error) => {\n console.error(`[PlanJobWatcher] Job watcher error for ${dirPath}:`, error);\n });\n\n return watcher;\n }\n\n private handlePlanChange(workspaceId: string, _dirPath: string, filename: string): void {\n const key = `${workspaceId}:plans`;\n\n // Debounce plan updates\n const existingTimer = this.debounceTimers.get(key);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n this.debounceTimers.set(key, setTimeout(() => {\n this.debounceTimers.delete(key);\n this.emitPlanUpdate(workspaceId);\n }, this.options.debounceMs));\n }\n\n private handleJobChange(workspaceId: string, _dirPath: string, filename: string): void {\n const key = `${workspaceId}:jobs`;\n\n // Debounce job updates\n const existingTimer = this.debounceTimers.get(key);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n this.debounceTimers.set(key, setTimeout(() => {\n this.debounceTimers.delete(key);\n this.emitJobUpdate(workspaceId);\n }, this.options.debounceMs));\n }\n\n private emitPlanUpdate(workspaceId: string): void {\n const watched = this.watchedWorkspaces.get(workspaceId);\n if (!watched) return;\n\n try {\n const plans = discoverPlans(watched.workspacePath);\n watched.lastPlanUpdate = Date.now();\n this.emit('plansChanged', { workspaceId, plans });\n console.log(`[PlanJobWatcher] Plans updated for ${workspaceId}: ${plans.length} plans`);\n } catch (error) {\n console.error(`[PlanJobWatcher] Failed to discover plans for ${workspaceId}:`, error);\n }\n }\n\n private emitJobUpdate(workspaceId: string): void {\n const watched = this.watchedWorkspaces.get(workspaceId);\n if (!watched) return;\n\n try {\n const jobs = discoverJobs(watched.workspacePath);\n watched.lastJobUpdate = Date.now();\n this.emit('jobsChanged', { workspaceId, jobs });\n console.log(`[PlanJobWatcher] Jobs updated for ${workspaceId}: ${jobs.length} jobs`);\n } catch (error) {\n console.error(`[PlanJobWatcher] Failed to discover jobs for ${workspaceId}:`, error);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["existsSync", "readFileSync", "readdirSync", "join", "basename", "resolve", "homedir", "updateTaskInContent", "existsSync", "readFileSync", "writeFileSync", "readdirSync", "mkdirSync", "join", "basename", "resolve", "dirname", "homedir", "dirname", "join", "resolve", "existsSync", "readFileSync", "writeFileSync", "readdirSync", "statSync", "homedir", "SessionManager", "config", "existsSync", "join", "existsSync", "join", "EventEmitter", "basename", "EventEmitter", "existsSync", "resolve", "existsSync", "EventEmitter", "slot", "EventEmitter", "resolve", "basename", "existsSync", "homedir", "dirname", "join", "EventEmitter", "Database", "mkdirSync", "dirname", "EventEmitter", "state", "EventEmitter", "EventEmitter", "EventEmitter", "readdirSync", "statSync", "existsSync", "join", "EventEmitter", "watch", "existsSync", "EventEmitter", "DEFAULT_OPTIONS", "getPlanDirectories", "getJobDirectories", "EventEmitter", "join", "homedir", "dirname", "existsSync", "workspaceManager", "SessionManager", "projectRoot", "readFileSync", "writeFileSync", "resolve", "readdirSync", "statSync", "execSync", "updateTaskInContent"]
7
+ }