panopticon-cli 0.4.30 → 0.4.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-ZG4JIPO2.js → agents-BDFHF4T3.js} +4 -3
- package/dist/{chunk-42O6XJWZ.js → chunk-2NIAOCIC.js} +14 -14
- package/dist/{chunk-SIAUVHVO.js → chunk-3XAB4IXF.js} +4 -2
- package/dist/{chunk-SIAUVHVO.js.map → chunk-3XAB4IXF.js.map} +1 -1
- package/dist/chunk-ELK6Q7QI.js +545 -0
- package/dist/chunk-ELK6Q7QI.js.map +1 -0
- package/dist/chunk-LYSBSZYV.js +1523 -0
- package/dist/chunk-LYSBSZYV.js.map +1 -0
- package/dist/{chunk-VTMXR7JF.js → chunk-VU4FLXV5.js} +47 -40
- package/dist/{chunk-VTMXR7JF.js.map → chunk-VU4FLXV5.js.map} +1 -1
- package/dist/{chunk-VH27COUW.js → chunk-XP2DXWYP.js} +49 -10
- package/dist/chunk-XP2DXWYP.js.map +1 -0
- package/dist/cli/index.js +52 -29
- package/dist/cli/index.js.map +1 -1
- package/dist/{config-QWTS63TU.js → config-BOAMSKTF.js} +4 -2
- package/dist/dashboard/public/assets/{index-Dhwz2I7n.js → index-ClYqpcAJ.js} +84 -84
- package/dist/dashboard/public/index.html +1 -1
- package/dist/dashboard/server.js +93512 -23114
- package/dist/index.js +16 -14
- package/dist/index.js.map +1 -1
- package/dist/{remote-workspace-FNXLMNBG.js → remote-workspace-2G6V2KNP.js} +7 -5
- package/dist/{remote-workspace-FNXLMNBG.js.map → remote-workspace-2G6V2KNP.js.map} +1 -1
- package/dist/{specialist-context-QVRSHPYN.js → specialist-context-N32QBNNQ.js} +2 -2
- package/dist/{specialist-logs-7TQMHCUN.js → specialist-logs-GF3YV4KL.js} +2 -2
- package/dist/{specialists-UUXB5KFV.js → specialists-JBIW6MP4.js} +2 -2
- package/dist/{traefik-7OLLXUD7.js → traefik-CUJM6K5Z.js} +3 -3
- package/package.json +3 -2
- package/scripts/record-cost-event.js +243 -79
- package/scripts/record-cost-event.ts +128 -68
- package/dist/chunk-J3J32DIR.js +0 -279
- package/dist/chunk-J3J32DIR.js.map +0 -1
- package/dist/chunk-SUMIHS2B.js +0 -1714
- package/dist/chunk-SUMIHS2B.js.map +0 -1
- package/dist/chunk-VH27COUW.js.map +0 -1
- /package/dist/{agents-ZG4JIPO2.js.map → agents-BDFHF4T3.js.map} +0 -0
- /package/dist/{chunk-42O6XJWZ.js.map → chunk-2NIAOCIC.js.map} +0 -0
- /package/dist/{config-QWTS63TU.js.map → config-BOAMSKTF.js.map} +0 -0
- /package/dist/{specialist-context-QVRSHPYN.js.map → specialist-context-N32QBNNQ.js.map} +0 -0
- /package/dist/{specialist-logs-7TQMHCUN.js.map → specialist-logs-GF3YV4KL.js.map} +0 -0
- /package/dist/{specialists-UUXB5KFV.js.map → specialists-JBIW6MP4.js.map} +0 -0
- /package/dist/{traefik-7OLLXUD7.js.map → traefik-CUJM6K5Z.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/cv.ts","../src/lib/cloister/config.ts","../src/lib/agents.ts"],"sourcesContent":["/**\n * Agent CV (Work History) System\n *\n * Tracks agent performance over time to enable capability-based routing.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport { AGENTS_DIR } from './paths.js';\n\nexport interface WorkEntry {\n issueId: string;\n startedAt: string;\n completedAt?: string;\n outcome: 'success' | 'failed' | 'abandoned' | 'in_progress';\n duration?: number; // minutes\n skills?: string[];\n failureReason?: string;\n commits?: number;\n linesChanged?: number;\n}\n\nexport interface AgentCV {\n agentId: string;\n createdAt: string;\n lastActive: string;\n runtime: string;\n model: string;\n stats: {\n totalIssues: number;\n successCount: number;\n failureCount: number;\n abandonedCount: number;\n avgDuration: number; // minutes\n successRate: number; // 0-1\n };\n skillsUsed: string[];\n recentWork: WorkEntry[];\n}\n\nfunction getCVFile(agentId: string): string {\n return join(AGENTS_DIR, agentId, 'cv.json');\n}\n\n/**\n * Get or create an agent's CV\n */\nexport function getAgentCV(agentId: string): AgentCV {\n const cvFile = getCVFile(agentId);\n\n if (existsSync(cvFile)) {\n try {\n return JSON.parse(readFileSync(cvFile, 'utf-8'));\n } catch {}\n }\n\n // Create new CV\n const cv: AgentCV = {\n agentId,\n createdAt: new Date().toISOString(),\n lastActive: new Date().toISOString(),\n runtime: 'claude',\n model: 'sonnet',\n stats: {\n totalIssues: 0,\n successCount: 0,\n failureCount: 0,\n abandonedCount: 0,\n avgDuration: 0,\n successRate: 0,\n },\n skillsUsed: [],\n recentWork: [],\n };\n\n saveAgentCV(cv);\n return cv;\n}\n\n/**\n * Save an agent's CV\n */\nexport function saveAgentCV(cv: AgentCV): void {\n const dir = join(AGENTS_DIR, cv.agentId);\n mkdirSync(dir, { recursive: true });\n writeFileSync(getCVFile(cv.agentId), JSON.stringify(cv, null, 2));\n}\n\n/**\n * Start tracking work for an agent\n */\nexport function startWork(agentId: string, issueId: string, skills?: string[]): void {\n const cv = getAgentCV(agentId);\n\n const entry: WorkEntry = {\n issueId,\n startedAt: new Date().toISOString(),\n outcome: 'in_progress',\n skills,\n };\n\n cv.recentWork.unshift(entry);\n cv.stats.totalIssues++;\n cv.lastActive = new Date().toISOString();\n\n // Track skills\n if (skills) {\n for (const skill of skills) {\n if (!cv.skillsUsed.includes(skill)) {\n cv.skillsUsed.push(skill);\n }\n }\n }\n\n // Keep only last 50 entries\n if (cv.recentWork.length > 50) {\n cv.recentWork = cv.recentWork.slice(0, 50);\n }\n\n saveAgentCV(cv);\n}\n\n/**\n * Complete work for an agent\n */\nexport function completeWork(\n agentId: string,\n issueId: string,\n outcome: 'success' | 'failed' | 'abandoned',\n details?: { commits?: number; linesChanged?: number; failureReason?: string }\n): void {\n const cv = getAgentCV(agentId);\n\n // Find the work entry\n const entry = cv.recentWork.find(\n (w) => w.issueId === issueId && w.outcome === 'in_progress'\n );\n\n if (entry) {\n entry.outcome = outcome;\n entry.completedAt = new Date().toISOString();\n entry.duration = Math.round(\n (new Date().getTime() - new Date(entry.startedAt).getTime()) / (1000 * 60)\n );\n if (details?.commits) entry.commits = details.commits;\n if (details?.linesChanged) entry.linesChanged = details.linesChanged;\n if (details?.failureReason) entry.failureReason = details.failureReason;\n }\n\n // Update stats\n if (outcome === 'success') {\n cv.stats.successCount++;\n } else if (outcome === 'failed') {\n cv.stats.failureCount++;\n } else if (outcome === 'abandoned') {\n cv.stats.abandonedCount++;\n }\n\n // Calculate success rate\n const completed = cv.stats.successCount + cv.stats.failureCount + cv.stats.abandonedCount;\n cv.stats.successRate = completed > 0 ? cv.stats.successCount / completed : 0;\n\n // Calculate average duration (only from completed work)\n const completedEntries = cv.recentWork.filter(\n (w) => w.duration !== undefined && w.outcome !== 'in_progress'\n );\n if (completedEntries.length > 0) {\n const totalDuration = completedEntries.reduce((sum, w) => sum + (w.duration || 0), 0);\n cv.stats.avgDuration = Math.round(totalDuration / completedEntries.length);\n }\n\n cv.lastActive = new Date().toISOString();\n saveAgentCV(cv);\n}\n\n/**\n * Get agent rankings by success rate\n */\nexport function getAgentRankings(): Array<{\n agentId: string;\n successRate: number;\n totalIssues: number;\n avgDuration: number;\n}> {\n const rankings: Array<{\n agentId: string;\n successRate: number;\n totalIssues: number;\n avgDuration: number;\n }> = [];\n\n if (!existsSync(AGENTS_DIR)) return rankings;\n\n const dirs = readdirSync(AGENTS_DIR, { withFileTypes: true }).filter(\n (d) => d.isDirectory()\n );\n\n for (const dir of dirs) {\n const cv = getAgentCV(dir.name);\n if (cv.stats.totalIssues > 0) {\n rankings.push({\n agentId: dir.name,\n successRate: cv.stats.successRate,\n totalIssues: cv.stats.totalIssues,\n avgDuration: cv.stats.avgDuration,\n });\n }\n }\n\n // Sort by success rate, then by total issues\n rankings.sort((a, b) => {\n if (b.successRate !== a.successRate) {\n return b.successRate - a.successRate;\n }\n return b.totalIssues - a.totalIssues;\n });\n\n return rankings;\n}\n\n/**\n * Format CV for display\n */\nexport function formatCV(cv: AgentCV): string {\n const lines: string[] = [\n `# Agent CV: ${cv.agentId}`,\n '',\n `Runtime: ${cv.runtime} (${cv.model})`,\n `Created: ${cv.createdAt}`,\n `Last Active: ${cv.lastActive}`,\n '',\n '## Statistics',\n '',\n `- Total Issues: ${cv.stats.totalIssues}`,\n `- Success Rate: ${(cv.stats.successRate * 100).toFixed(1)}%`,\n `- Successes: ${cv.stats.successCount}`,\n `- Failures: ${cv.stats.failureCount}`,\n `- Abandoned: ${cv.stats.abandonedCount}`,\n `- Avg Duration: ${cv.stats.avgDuration} minutes`,\n '',\n ];\n\n if (cv.skillsUsed.length > 0) {\n lines.push('## Skills Used');\n lines.push('');\n lines.push(cv.skillsUsed.join(', '));\n lines.push('');\n }\n\n if (cv.recentWork.length > 0) {\n lines.push('## Recent Work');\n lines.push('');\n\n for (const work of cv.recentWork.slice(0, 10)) {\n const statusIcon = {\n success: '✓',\n failed: '✗',\n abandoned: '⊘',\n in_progress: '●',\n }[work.outcome];\n\n const duration = work.duration ? ` (${work.duration}m)` : '';\n lines.push(`${statusIcon} ${work.issueId}${duration}`);\n\n if (work.failureReason) {\n lines.push(` Reason: ${work.failureReason}`);\n }\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n","/**\n * Cloister Configuration\n *\n * Loads and manages Cloister configuration from ~/.panopticon/cloister.toml\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { parse, stringify } from '@iarna/toml';\nimport { join } from 'path';\nimport { PANOPTICON_HOME } from '../paths.js';\n\nconst CLOISTER_CONFIG_FILE = join(PANOPTICON_HOME, 'cloister.toml');\n\n/**\n * Health threshold configuration (in minutes)\n */\nexport interface HealthThresholds {\n stale: number;\n warning: number;\n stuck: number;\n}\n\n/**\n * Automatic action configuration\n */\nexport interface AutoActions {\n poke_on_warning: boolean;\n kill_on_stuck: boolean;\n restart_on_kill: boolean;\n}\n\n/**\n * Monitoring configuration\n */\nexport interface MonitoringConfig {\n check_interval: number; // seconds between health checks\n heartbeat_sources: ('jsonl_mtime' | 'tmux_activity' | 'git_activity' | 'active_heartbeat')[];\n}\n\n/**\n * Startup configuration\n */\nexport interface StartupConfig {\n auto_start: boolean; // Start Cloister when dashboard starts\n}\n\n/**\n * Notification configuration (future feature)\n */\nexport interface NotificationConfig {\n slack_webhook?: string;\n email?: string;\n}\n\n/**\n * Specialist agent configuration\n */\nexport interface SpecialistConfig {\n enabled: boolean;\n auto_wake: boolean;\n}\n\n/**\n * Test agent specific configuration\n */\nexport interface TestAgentConfig extends SpecialistConfig {\n test_command?: string; // Optional test command override (e.g., \"npm test\", \"pytest\", etc.)\n}\n\n/**\n * All specialist agents configuration\n */\nexport interface SpecialistsConfig {\n merge_agent?: SpecialistConfig;\n review_agent?: SpecialistConfig;\n test_agent?: TestAgentConfig;\n}\n\n/**\n * Model selection configuration\n */\nexport interface ModelSelectionConfig {\n default_model: 'opus' | 'sonnet' | 'haiku';\n complexity_routing: {\n trivial: 'opus' | 'sonnet' | 'haiku';\n simple: 'opus' | 'sonnet' | 'haiku';\n medium: 'opus' | 'sonnet' | 'haiku';\n complex: 'opus' | 'sonnet' | 'haiku';\n expert: 'opus' | 'sonnet' | 'haiku';\n };\n specialist_models: {\n merge_agent: 'opus' | 'sonnet' | 'haiku';\n review_agent: 'opus' | 'sonnet' | 'haiku';\n test_agent: 'opus' | 'sonnet' | 'haiku';\n planning_agent: 'opus' | 'sonnet' | 'haiku';\n };\n}\n\n/**\n * Handoff trigger configuration\n */\nexport interface HandoffTriggersConfig {\n planning_complete?: {\n enabled: boolean;\n from_model: 'opus' | 'sonnet' | 'haiku';\n to_model: 'opus' | 'sonnet' | 'haiku';\n };\n stuck_escalation?: {\n enabled: boolean;\n haiku_to_sonnet_minutes: number;\n sonnet_to_opus_minutes: number;\n };\n test_failure?: {\n enabled: boolean;\n from_model: 'opus' | 'sonnet' | 'haiku';\n to_model: 'opus' | 'sonnet' | 'haiku';\n trigger_on: 'any_failure' | '2_consecutive';\n };\n implementation_complete?: {\n enabled: boolean;\n to_specialist: string; // e.g., 'test-agent'\n };\n}\n\n/**\n * Handoff configuration\n */\nexport interface HandoffConfig {\n auto_triggers: HandoffTriggersConfig;\n}\n\n/**\n * Cost tracking configuration\n */\nexport interface CostTrackingConfig {\n display_enabled: boolean;\n log_to_jsonl: boolean;\n}\n\n/**\n * Auto-restart configuration\n */\nexport interface AutoRestartConfig {\n enabled: boolean;\n max_retries: number;\n backoff_seconds: number[]; // Array of backoff delays (e.g., [30, 60, 120])\n}\n\n/**\n * Cost limits configuration\n */\nexport interface CostLimitsConfig {\n per_agent_usd: number;\n per_issue_usd: number;\n daily_total_usd: number;\n alert_threshold: number; // Fraction (0.0-1.0) at which to start alerting\n}\n\n/**\n * Retention policy configuration\n */\nexport interface RetentionConfig {\n agent_state_days: number; // Days to keep agent state dirs (default: 30)\n health_staleness_hours: number; // Hours before hiding stale agents in health API (default: 24)\n}\n\n/**\n * Complete Cloister configuration\n */\nexport interface CloisterConfig {\n startup: StartupConfig;\n thresholds: HealthThresholds;\n auto_actions: AutoActions;\n monitoring: MonitoringConfig;\n notifications?: NotificationConfig;\n specialists?: SpecialistsConfig;\n model_selection?: ModelSelectionConfig;\n handoffs?: HandoffConfig;\n cost_tracking?: CostTrackingConfig;\n auto_restart?: AutoRestartConfig;\n cost_limits?: CostLimitsConfig;\n retention?: RetentionConfig;\n}\n\n/**\n * Default Cloister configuration\n */\nexport const DEFAULT_CLOISTER_CONFIG: CloisterConfig = {\n startup: {\n auto_start: true,\n },\n thresholds: {\n stale: 5,\n warning: 15,\n stuck: 30,\n },\n auto_actions: {\n poke_on_warning: true,\n kill_on_stuck: false, // Manual by default for safety\n restart_on_kill: false,\n },\n monitoring: {\n check_interval: 60, // 1 minute\n heartbeat_sources: ['jsonl_mtime', 'tmux_activity', 'git_activity'],\n },\n notifications: {\n slack_webhook: undefined,\n email: undefined,\n },\n specialists: {\n merge_agent: {\n enabled: true,\n auto_wake: false, // Only wake on explicit \"Approve & Merge\" click\n },\n review_agent: {\n enabled: true,\n auto_wake: false, // Only wake on explicit request\n },\n test_agent: {\n enabled: false, // Not yet implemented\n auto_wake: false,\n },\n },\n model_selection: {\n default_model: 'sonnet',\n complexity_routing: {\n trivial: 'haiku',\n simple: 'haiku',\n medium: 'sonnet',\n complex: 'sonnet',\n expert: 'opus',\n },\n specialist_models: {\n merge_agent: 'sonnet',\n review_agent: 'sonnet',\n test_agent: 'haiku',\n planning_agent: 'opus',\n },\n },\n handoffs: {\n auto_triggers: {\n planning_complete: {\n enabled: true,\n from_model: 'opus',\n to_model: 'sonnet',\n },\n stuck_escalation: {\n enabled: true,\n haiku_to_sonnet_minutes: 10,\n sonnet_to_opus_minutes: 20,\n },\n test_failure: {\n enabled: true,\n from_model: 'haiku',\n to_model: 'sonnet',\n trigger_on: 'any_failure',\n },\n implementation_complete: {\n enabled: true, // Auto-handoff to test-agent when implementation done\n to_specialist: 'test-agent',\n },\n },\n },\n cost_tracking: {\n display_enabled: true,\n log_to_jsonl: true,\n },\n auto_restart: {\n enabled: true,\n max_retries: 3,\n backoff_seconds: [30, 60, 120], // 30s, 1m, 2m\n },\n cost_limits: {\n per_agent_usd: 10.0,\n per_issue_usd: 25.0,\n daily_total_usd: 100.0,\n alert_threshold: 0.8, // Alert at 80%\n },\n retention: {\n agent_state_days: 30,\n health_staleness_hours: 24,\n },\n};\n\n/**\n * Deep merge utility that recursively merges objects.\n * - Recursively merges nested objects\n * - Arrays in overrides replace defaults (not concatenated)\n * - User values take precedence over defaults\n */\nfunction deepMerge<T extends object>(defaults: T, overrides: Partial<T>): T {\n const result = { ...defaults };\n\n for (const key of Object.keys(overrides) as (keyof T)[]) {\n const defaultVal = defaults[key];\n const overrideVal = overrides[key];\n\n // Skip undefined values in overrides\n if (overrideVal === undefined) continue;\n\n // Deep merge if both values are non-array objects\n if (\n typeof defaultVal === 'object' &&\n defaultVal !== null &&\n !Array.isArray(defaultVal) &&\n typeof overrideVal === 'object' &&\n overrideVal !== null &&\n !Array.isArray(overrideVal)\n ) {\n result[key] = deepMerge(defaultVal as any, overrideVal as any);\n } else {\n // Direct override for primitives and arrays\n result[key] = overrideVal as T[keyof T];\n }\n }\n\n return result;\n}\n\n/**\n * Load Cloister configuration\n *\n * Reads from ~/.panopticon/cloister.toml and merges with defaults.\n * Creates default config file if it doesn't exist.\n */\nexport function loadCloisterConfig(): CloisterConfig {\n // Ensure panopticon home exists\n if (!existsSync(PANOPTICON_HOME)) {\n mkdirSync(PANOPTICON_HOME, { recursive: true });\n }\n\n // If config file doesn't exist, create it with defaults\n if (!existsSync(CLOISTER_CONFIG_FILE)) {\n saveCloisterConfig(DEFAULT_CLOISTER_CONFIG);\n return DEFAULT_CLOISTER_CONFIG;\n }\n\n try {\n const content = readFileSync(CLOISTER_CONFIG_FILE, 'utf-8');\n const parsed = parse(content) as unknown as Partial<CloisterConfig>;\n\n // Deep merge with defaults\n return deepMerge(DEFAULT_CLOISTER_CONFIG, parsed);\n } catch (error) {\n console.error('Failed to load Cloister config:', error);\n console.error('Using default configuration');\n return DEFAULT_CLOISTER_CONFIG;\n }\n}\n\n/**\n * Save Cloister configuration\n *\n * Writes configuration to ~/.panopticon/cloister.toml\n */\nexport function saveCloisterConfig(config: CloisterConfig): void {\n // Ensure panopticon home exists\n if (!existsSync(PANOPTICON_HOME)) {\n mkdirSync(PANOPTICON_HOME, { recursive: true });\n }\n\n try {\n const content = stringify(config as any);\n writeFileSync(CLOISTER_CONFIG_FILE, content, 'utf-8');\n } catch (error) {\n console.error('Failed to save Cloister config:', error);\n throw error;\n }\n}\n\n/**\n * Update Cloister configuration\n *\n * Merges partial config updates with existing config.\n */\nexport function updateCloisterConfig(updates: Partial<CloisterConfig>): CloisterConfig {\n const current = loadCloisterConfig();\n const updated = deepMerge(current, updates);\n saveCloisterConfig(updated);\n return updated;\n}\n\n/**\n * Get the path to the Cloister config file\n */\nexport function getCloisterConfigPath(): string {\n return CLOISTER_CONFIG_FILE;\n}\n\n/**\n * Check if Cloister should auto-start\n */\nexport function shouldAutoStart(): boolean {\n const config = loadCloisterConfig();\n return config.startup.auto_start;\n}\n\n/**\n * Get health thresholds in milliseconds\n */\nexport function getHealthThresholdsMs(): {\n stale: number;\n warning: number;\n stuck: number;\n} {\n const config = loadCloisterConfig();\n return {\n stale: config.thresholds.stale * 60 * 1000,\n warning: config.thresholds.warning * 60 * 1000,\n stuck: config.thresholds.stuck * 60 * 1000,\n };\n}\n","import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, appendFileSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { AGENTS_DIR } from './paths.js';\nimport { createSession, killSession, sendKeys, sendKeysAsync, sessionExists, getAgentSessions, capturePane } from './tmux.js';\nimport { initHook, checkHook, generateFixedPointPrompt } from './hooks.js';\nimport { startWork, completeWork, getAgentCV } from './cv.js';\nimport type { ComplexityLevel } from './cloister/complexity.js';\nimport { loadCloisterConfig } from './cloister/config.js';\nimport { loadSettings, type ModelId } from './settings.js';\nimport { getModelId, WorkTypeId } from './work-type-router.js';\nimport { getProviderForModel, getProviderEnv, requiresRouter } from './providers.js';\nimport { loadConfig } from './config.js';\nimport { createTrackerFromConfig } from './tracker/factory.js';\nimport type { TrackerType } from './tracker/interface.js';\n\nconst execAsync = promisify(exec);\n\n/** Known agent ID prefixes — IDs with these prefixes are already normalized */\nconst AGENT_PREFIXES = ['agent-', 'planning-'];\n\n/** Normalize agent ID: preserve known prefixes, add 'agent-' for bare issue IDs */\nfunction normalizeAgentId(agentId: string): string {\n if (AGENT_PREFIXES.some(p => agentId.startsWith(p))) {\n return agentId;\n }\n return `agent-${agentId.toLowerCase()}`;\n}\n\n/**\n * Get provider-specific env vars (BASE_URL, AUTH_TOKEN) for a model.\n * Reads the current API key from settings so resumed/recovered agents\n * always use the latest key.\n */\nfunction getProviderEnvForModel(model: string): Record<string, string> {\n const provider = getProviderForModel(model as ModelId);\n if (provider.name === 'anthropic') return {};\n\n const settings = loadSettings();\n const apiKey = settings.api_keys?.[provider.name as keyof typeof settings.api_keys];\n if (apiKey) {\n return getProviderEnv(provider, apiKey);\n }\n console.warn(`Warning: No API key configured for ${provider.displayName}, falling back to Anthropic`);\n return {};\n}\n\n// ============================================================================\n// Ready Signal Management (PAN-87)\n// ============================================================================\n\n/**\n * Get path to agent's ready signal file (written by SessionStart hook)\n */\nfunction getReadySignalPath(agentId: string): string {\n return join(getAgentDir(agentId), 'ready.json');\n}\n\n/**\n * Clear ready signal before spawning (clean slate)\n */\nfunction clearReadySignal(agentId: string): void {\n const readyPath = getReadySignalPath(agentId);\n if (existsSync(readyPath)) {\n try {\n unlinkSync(readyPath);\n } catch {\n // Ignore errors - non-critical\n }\n }\n}\n\n/**\n * Wait for SessionStart hook to signal ready (async - non-blocking)\n * Returns true if ready signal received, false if timeout\n */\nasync function waitForReadySignal(agentId: string, timeoutSeconds = 30): Promise<boolean> {\n const readyPath = getReadySignalPath(agentId);\n\n for (let i = 0; i < timeoutSeconds; i++) {\n await new Promise(resolve => setTimeout(resolve, 1000)); // Non-blocking sleep\n\n if (existsSync(readyPath)) {\n try {\n const content = readFileSync(readyPath, 'utf-8');\n const signal = JSON.parse(content);\n if (signal.ready === true) {\n return true;\n }\n } catch {\n // File exists but invalid - keep waiting\n }\n }\n }\n\n return false;\n}\n\nexport interface AgentState {\n id: string;\n issueId: string;\n workspace: string;\n runtime: string;\n model: string;\n status: 'starting' | 'running' | 'stopped' | 'error';\n startedAt: string;\n lastActivity?: string;\n branch?: string; // Git branch name for this agent\n\n // Model routing & handoffs (Phase 4)\n complexity?: ComplexityLevel;\n handoffCount?: number;\n costSoFar?: number;\n sessionId?: string; // For resuming sessions after handoff\n\n // Work type system (PAN-118)\n phase?: 'exploration' | 'planning' | 'implementation' | 'testing' | 'documentation' | 'review-response';\n workType?: WorkTypeId; // Current work type ID\n}\n\nexport function getAgentDir(agentId: string): string {\n return join(AGENTS_DIR, agentId);\n}\n\nexport function getAgentState(agentId: string): AgentState | null {\n const stateFile = join(getAgentDir(agentId), 'state.json');\n if (!existsSync(stateFile)) return null;\n\n const content = readFileSync(stateFile, 'utf8');\n return JSON.parse(content);\n}\n\nexport function saveAgentState(state: AgentState): void {\n const dir = getAgentDir(state.id);\n mkdirSync(dir, { recursive: true });\n\n writeFileSync(\n join(dir, 'state.json'),\n JSON.stringify(state, null, 2)\n );\n}\n\n// ============================================================================\n// Hook-based State Management (PAN-80)\n// ============================================================================\n\n/**\n * Agent runtime state (hook-based tracking)\n */\nexport interface AgentRuntimeState {\n state: 'active' | 'idle' | 'suspended' | 'uninitialized';\n lastActivity: string;\n currentTool?: string;\n sessionId?: string;\n suspendedAt?: string;\n resumedAt?: string;\n currentIssue?: string; // Issue ID the agent is currently working on\n}\n\n/**\n * Activity log entry\n */\nexport interface ActivityEntry {\n ts: string;\n tool: string;\n action?: string;\n state?: 'active' | 'idle';\n}\n\n/**\n * Get agent runtime state (from hooks)\n */\nexport function getAgentRuntimeState(agentId: string): AgentRuntimeState | null {\n const stateFile = join(getAgentDir(agentId), 'state.json');\n\n // If file doesn't exist, agent is uninitialized\n if (!existsSync(stateFile)) {\n return {\n state: 'uninitialized',\n lastActivity: new Date().toISOString(),\n };\n }\n\n try {\n const content = readFileSync(stateFile, 'utf8');\n return JSON.parse(content) as AgentRuntimeState;\n } catch {\n return null;\n }\n}\n\n/**\n * Save agent runtime state\n */\nexport function saveAgentRuntimeState(agentId: string, state: Partial<AgentRuntimeState>): void {\n const dir = getAgentDir(agentId);\n mkdirSync(dir, { recursive: true });\n\n // Merge with existing state\n const existing = getAgentRuntimeState(agentId);\n const merged: AgentRuntimeState = {\n ...(existing || { state: 'uninitialized', lastActivity: new Date().toISOString() }),\n ...state,\n };\n\n writeFileSync(\n join(dir, 'state.json'),\n JSON.stringify(merged, null, 2)\n );\n}\n\n/**\n * Append to activity log with automatic pruning to 100 entries\n */\nexport function appendActivity(agentId: string, entry: ActivityEntry): void {\n const dir = getAgentDir(agentId);\n mkdirSync(dir, { recursive: true });\n\n const activityFile = join(dir, 'activity.jsonl');\n\n // Append entry\n appendFileSync(activityFile, JSON.stringify(entry) + '\\n');\n\n // Prune to last 100 entries\n if (existsSync(activityFile)) {\n try {\n const lines = readFileSync(activityFile, 'utf8').trim().split('\\n');\n if (lines.length > 100) {\n const trimmed = lines.slice(-100);\n writeFileSync(activityFile, trimmed.join('\\n') + '\\n');\n }\n } catch (error) {\n // Ignore pruning errors - activity log is non-critical\n }\n }\n}\n\n/**\n * Read activity log (last N entries)\n */\nexport function getActivity(agentId: string, limit = 100): ActivityEntry[] {\n const activityFile = join(getAgentDir(agentId), 'activity.jsonl');\n\n if (!existsSync(activityFile)) {\n return [];\n }\n\n try {\n const lines = readFileSync(activityFile, 'utf8').trim().split('\\n');\n const entries = lines\n .filter(line => line.trim())\n .map(line => JSON.parse(line) as ActivityEntry)\n .slice(-limit);\n\n return entries;\n } catch {\n return [];\n }\n}\n\n/**\n * Save Claude session ID for later resume\n */\nexport function saveSessionId(agentId: string, sessionId: string): void {\n const dir = getAgentDir(agentId);\n mkdirSync(dir, { recursive: true });\n\n writeFileSync(join(dir, 'session.id'), sessionId);\n}\n\n/**\n * Get saved Claude session ID\n */\nexport function getSessionId(agentId: string): string | null {\n const sessionFile = join(getAgentDir(agentId), 'session.id');\n\n if (!existsSync(sessionFile)) {\n return null;\n }\n\n try {\n return readFileSync(sessionFile, 'utf8').trim();\n } catch {\n return null;\n }\n}\n\nexport interface SpawnOptions {\n issueId: string;\n workspace: string;\n runtime?: string;\n model?: string;\n prompt?: string;\n difficulty?: ComplexityLevel;\n agentType?: 'review-agent' | 'test-agent' | 'merge-agent' | 'planning-agent' | 'work-agent';\n\n // Work type system (PAN-118)\n phase?: 'exploration' | 'planning' | 'implementation' | 'testing' | 'documentation' | 'review-response';\n workType?: WorkTypeId; // Explicit work type ID (overrides phase-based detection)\n}\n\n/**\n * Determine which model to use for an agent based on configuration\n *\n * New Priority (PAN-118):\n * 1. Explicitly provided model (options.model)\n * 2. Explicit work type ID (options.workType)\n * 3. Work type from phase (options.phase → issue-agent:{phase})\n * 4. Specialist work type (options.agentType → specialist-{type})\n * 5. Complexity-based routing (LEGACY - deprecated)\n * 6. Default fallback (claude-sonnet-4-5)\n */\nfunction determineModel(options: SpawnOptions): string {\n console.log(`[DEBUG] determineModel called with:`, { model: options.model, workType: options.workType, phase: options.phase, agentType: options.agentType, difficulty: options.difficulty });\n\n // Explicit model always wins\n if (options.model) {\n console.log(`[DEBUG] Using explicit model: ${options.model}`);\n return options.model;\n }\n\n try {\n // Use work type router if work type or phase specified\n if (options.workType) {\n return getModelId(options.workType);\n }\n\n // Map phase to work type ID\n if (options.phase) {\n const workType: WorkTypeId = `issue-agent:${options.phase}` as WorkTypeId;\n return getModelId(workType);\n }\n\n // Map specialist agent type to work type ID\n if (options.agentType && options.agentType !== 'work-agent') {\n if (options.agentType === 'planning-agent') {\n return getModelId('planning-agent');\n }\n // Specialists: review-agent, test-agent, merge-agent\n const workType: WorkTypeId = `specialist-${options.agentType}` as WorkTypeId;\n return getModelId(workType);\n }\n\n // LEGACY: Complexity-based routing (deprecated but kept for backward compat)\n if (options.difficulty) {\n const settings = loadSettings();\n if (settings.models.complexity[options.difficulty]) {\n console.warn(`Using legacy complexity-based routing for ${options.difficulty}. Consider migrating to work types.`);\n return settings.models.complexity[options.difficulty];\n }\n }\n\n // Fall back to default model from Cloister config or claude-sonnet-4-5\n try {\n const cloisterConfig = loadCloisterConfig();\n const defaultModel = cloisterConfig.model_selection?.default_model || 'sonnet';\n const modelMap: Record<string, string> = {\n 'opus': 'claude-opus-4-6',\n 'sonnet': 'claude-sonnet-4-5',\n 'haiku': 'claude-haiku-4-5',\n };\n return modelMap[defaultModel] || 'claude-sonnet-4-5';\n } catch {\n return 'claude-sonnet-4-5';\n }\n } catch (error) {\n // If work type router fails, fall back to default\n console.warn('Warning: Could not resolve model using work type router, using default');\n return options.model || 'claude-sonnet-4-5';\n }\n}\n\n/**\n * Transition an issue to \"in_progress\" in its tracker.\n * Tries each configured tracker until one succeeds.\n */\nasync function transitionIssueToInProgress(issueId: string): Promise<void> {\n const config = loadConfig();\n const trackersConfig = config.trackers;\n\n // Try primary tracker first, then secondary\n const trackerTypes: TrackerType[] = [trackersConfig.primary];\n if (trackersConfig.secondary) {\n trackerTypes.push(trackersConfig.secondary);\n }\n\n for (const trackerType of trackerTypes) {\n try {\n const tracker = createTrackerFromConfig(trackersConfig, trackerType);\n await tracker.transitionIssue(issueId, 'in_progress');\n console.log(`[agents] Transitioned ${issueId} to in_progress via ${trackerType}`);\n return;\n } catch {\n // Issue not found in this tracker or transition failed, try next\n }\n }\n}\n\nexport async function spawnAgent(options: SpawnOptions): Promise<AgentState> {\n const agentId = `agent-${options.issueId.toLowerCase()}`;\n\n // Check if already running\n if (sessionExists(agentId)) {\n throw new Error(`Agent ${agentId} already running. Use 'pan work tell' to message it.`);\n }\n\n // Initialize hook for this agent (FPP support)\n initHook(agentId);\n\n // Determine model based on configuration\n const selectedModel = determineModel(options);\n console.log(`[DEBUG] Selected model: ${selectedModel}`);\n\n // Create state\n const state: AgentState = {\n id: agentId,\n issueId: options.issueId,\n workspace: options.workspace,\n runtime: options.runtime || 'claude',\n model: selectedModel,\n status: 'starting',\n startedAt: new Date().toISOString(),\n // Initialize Phase 4 fields (legacy)\n complexity: options.difficulty,\n handoffCount: 0,\n costSoFar: 0,\n // Work type system (PAN-118)\n phase: options.phase,\n workType: options.workType,\n };\n\n saveAgentState(state);\n\n // Build prompt with FPP work if available\n let prompt = options.prompt || '';\n\n // FPP: Check for pending work on hook\n const { hasWork, items } = checkHook(agentId);\n if (hasWork) {\n const fixedPointPrompt = generateFixedPointPrompt(agentId);\n if (fixedPointPrompt) {\n prompt = fixedPointPrompt + '\\n\\n---\\n\\n' + prompt;\n }\n }\n\n // Write prompt to file for complex prompts (avoids shell escaping issues)\n const promptFile = join(getAgentDir(agentId), 'initial-prompt.md');\n if (prompt) {\n writeFileSync(promptFile, prompt);\n }\n\n // Auto-setup hooks if not configured\n checkAndSetupHooks();\n\n // Write initial task cache for heartbeat hook\n writeTaskCache(agentId, options.issueId);\n\n // Clear ready signal before spawning (clean slate for PAN-87 fix)\n clearReadySignal(agentId);\n\n // Get provider-specific environment variables (BASE_URL, AUTH_TOKEN)\n const providerEnv = getProviderEnvForModel(selectedModel);\n\n // Create tmux session and start claude\n // For prompts with special shell characters, use a launcher script to safely pass the prompt\n // The script reads the file into a variable, which bash then safely expands\n let claudeCmd: string;\n if (prompt) {\n const launcherScript = join(getAgentDir(agentId), 'launcher.sh');\n const launcherContent = `#!/bin/bash\nprompt=$(cat \"${promptFile}\")\nexec claude --dangerously-skip-permissions --model ${state.model} \"\\$prompt\"\n`;\n writeFileSync(launcherScript, launcherContent, { mode: 0o755 });\n claudeCmd = `bash \"${launcherScript}\"`;\n } else {\n claudeCmd = `claude --dangerously-skip-permissions --model ${state.model}`;\n }\n\n createSession(agentId, options.workspace, claudeCmd, {\n env: {\n PANOPTICON_AGENT_ID: agentId,\n PANOPTICON_ISSUE_ID: options.issueId,\n PANOPTICON_SESSION_TYPE: options.phase || 'implementation',\n ...providerEnv // Add provider-specific env vars (BASE_URL, AUTH_TOKEN, etc.)\n }\n });\n\n // Update status\n state.status = 'running';\n saveAgentState(state);\n\n // Track work in CV\n startWork(agentId, options.issueId);\n\n // Transition issue tracker to \"in progress\" (best-effort, don't block agent spawn)\n // Only for work agents, not planning/specialist agents\n if (!options.agentType || options.agentType === 'work-agent') {\n transitionIssueToInProgress(options.issueId).catch((err) => {\n console.warn(`[agents] Could not transition ${options.issueId} to in_progress: ${err.message}`);\n });\n }\n\n return state;\n}\n\nexport function listRunningAgents(): (AgentState & { tmuxActive: boolean })[] {\n const tmuxSessions = getAgentSessions();\n const tmuxNames = new Set(tmuxSessions.map(s => s.name));\n\n const agents: (AgentState & { tmuxActive: boolean })[] = [];\n\n // Read all agent states\n if (!existsSync(AGENTS_DIR)) return agents;\n\n const dirs = readdirSync(AGENTS_DIR, { withFileTypes: true })\n .filter(d => d.isDirectory());\n\n for (const dir of dirs) {\n const state = getAgentState(dir.name);\n if (state) {\n agents.push({\n ...state,\n tmuxActive: tmuxNames.has(state.id),\n });\n }\n }\n\n return agents;\n}\n\nexport function stopAgent(agentId: string): void {\n const normalizedId = normalizeAgentId(agentId);\n\n if (sessionExists(normalizedId)) {\n // Capture tmux output before killing so logs remain viewable after stop\n try {\n const output = capturePane(normalizedId, 5000);\n if (output) {\n const agentDir = getAgentDir(normalizedId);\n mkdirSync(agentDir, { recursive: true });\n writeFileSync(join(agentDir, 'output.log'), output);\n }\n } catch {\n // Non-fatal — best effort log capture\n }\n\n killSession(normalizedId);\n }\n\n const state = getAgentState(normalizedId);\n if (state) {\n // Ensure id is set — runtime state files may lack it (PAN-150)\n if (!state.id) state.id = normalizedId;\n state.status = 'stopped';\n saveAgentState(state);\n }\n}\n\nexport async function messageAgent(agentId: string, message: string): Promise<void> {\n const normalizedId = normalizeAgentId(agentId);\n\n // Check if agent is suspended - auto-resume if so (PAN-80)\n const runtimeState = getAgentRuntimeState(normalizedId);\n if (runtimeState?.state === 'suspended') {\n console.log(`[agents] Auto-resuming suspended agent ${normalizedId} to deliver message`);\n const result = await resumeAgent(normalizedId, message);\n if (!result.success) {\n throw new Error(`Failed to auto-resume agent: ${result.error}`);\n }\n // Message already sent during resume\n return;\n }\n\n // Check if this is a remote agent\n const { loadRemoteAgentState, sendToRemoteAgent } = await import('./remote/remote-agents.js');\n const remoteState = loadRemoteAgentState(normalizedId);\n if (remoteState && remoteState.vmName) {\n console.log(`[agents] Sending message to remote agent ${normalizedId} on ${remoteState.vmName}`);\n await sendToRemoteAgent(normalizedId, remoteState.vmName, message);\n\n // Also save to mail queue for persistence\n const mailDir = join(getAgentDir(normalizedId), 'mail');\n mkdirSync(mailDir, { recursive: true });\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n writeFileSync(\n join(mailDir, `${timestamp}.md`),\n `# Message\\n\\n${message}\\n`\n );\n return;\n }\n\n if (!sessionExists(normalizedId)) {\n throw new Error(`Agent ${normalizedId} not running`);\n }\n\n await sendKeysAsync(normalizedId, message);\n\n // Also save to mail queue\n const mailDir = join(getAgentDir(normalizedId), 'mail');\n mkdirSync(mailDir, { recursive: true });\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n writeFileSync(\n join(mailDir, `${timestamp}.md`),\n `# Message\\n\\n${message}\\n`\n );\n}\n\n/**\n * Resume a suspended agent (PAN-80)\n *\n * Reads saved session ID and creates new tmux session with --resume flag.\n * Optionally sends a message after resuming.\n *\n * Auto-resume triggers:\n * - Specialists: When queued work arrives\n * - Work agents: When message is sent via /work-tell\n */\nexport async function resumeAgent(agentId: string, message?: string): Promise<{ success: boolean; error?: string }> {\n const normalizedId = normalizeAgentId(agentId);\n\n // Check runtime state\n const runtimeState = getAgentRuntimeState(normalizedId);\n if (!runtimeState || runtimeState.state !== 'suspended') {\n return {\n success: false,\n error: `Cannot resume agent in state: ${runtimeState?.state || 'unknown'}`\n };\n }\n\n // Get saved session ID\n const sessionId = getSessionId(normalizedId);\n if (!sessionId) {\n return {\n success: false,\n error: 'No saved session ID found'\n };\n }\n\n // Get agent state for workspace info\n const agentState = getAgentState(normalizedId);\n if (!agentState) {\n return {\n success: false,\n error: 'Agent state not found'\n };\n }\n\n // Check if session already exists (shouldn't happen for suspended agents)\n if (sessionExists(normalizedId)) {\n return {\n success: false,\n error: 'Agent session already exists'\n };\n }\n\n try {\n // Clear ready signal before resuming (clean slate for PAN-87 fix)\n clearReadySignal(normalizedId);\n\n // Get provider env for the agent's model (reads latest API key from settings)\n const providerEnv = agentState.model ? getProviderEnvForModel(agentState.model) : {};\n\n // Create new tmux session with resume command\n const claudeCmd = `claude --resume \"${sessionId}\" --dangerously-skip-permissions`;\n createSession(normalizedId, agentState.workspace, claudeCmd, {\n env: {\n PANOPTICON_AGENT_ID: normalizedId,\n ...providerEnv\n }\n });\n\n // If there's a message, wait for ready signal then send\n if (message) {\n // Wait for SessionStart hook to signal ready (PAN-87: reliable message delivery)\n const ready = await waitForReadySignal(normalizedId, 30);\n\n if (ready) {\n // Send message\n await sendKeysAsync(normalizedId, message);\n } else {\n console.error('Claude SessionStart hook did not fire during resume, message not sent');\n }\n }\n\n // Update runtime state\n saveAgentRuntimeState(normalizedId, {\n state: 'active',\n resumedAt: new Date().toISOString(),\n });\n\n // Update agent state\n if (agentState) {\n agentState.status = 'running';\n agentState.lastActivity = new Date().toISOString();\n saveAgentState(agentState);\n }\n\n return { success: true };\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: `Failed to resume agent: ${msg}`\n };\n }\n}\n\n/**\n * Detect crashed agents (state shows running but tmux session is gone)\n */\nexport function detectCrashedAgents(): AgentState[] {\n const agents = listRunningAgents();\n return agents.filter(\n (agent) => agent.status === 'running' && !agent.tmuxActive\n );\n}\n\n/**\n * Recover a crashed agent by restarting it with context\n */\nexport function recoverAgent(agentId: string): AgentState | null {\n const normalizedId = normalizeAgentId(agentId);\n const state = getAgentState(normalizedId);\n\n if (!state) {\n return null;\n }\n\n // Runtime state files may lack required fields (PAN-150)\n if (!state.id) state.id = normalizedId;\n if (!state.workspace || !state.model) {\n console.error(`[agents] Cannot recover ${normalizedId}: state.json missing workspace or model`);\n return null;\n }\n\n // Check if already running\n if (sessionExists(normalizedId)) {\n return state;\n }\n\n // Update crash count in health file\n const healthFile = join(getAgentDir(normalizedId), 'health.json');\n let health = { consecutiveFailures: 0, killCount: 0, recoveryCount: 0 };\n if (existsSync(healthFile)) {\n try {\n health = { ...health, ...JSON.parse(readFileSync(healthFile, 'utf-8')) };\n } catch {}\n }\n health.recoveryCount = (health.recoveryCount || 0) + 1;\n writeFileSync(healthFile, JSON.stringify(health, null, 2));\n\n // Build recovery prompt\n const recoveryPrompt = generateRecoveryPrompt(state);\n\n // Get provider env for the agent's model (reads latest API key from settings)\n const providerEnv = state.model ? getProviderEnvForModel(state.model) : {};\n\n // Restart the agent with recovery context (YOLO mode - skip permissions)\n const claudeCmd = `claude --dangerously-skip-permissions --model ${state.model} \"${recoveryPrompt.replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n')}\"`;\n createSession(normalizedId, state.workspace, claudeCmd, {\n env: {\n PANOPTICON_AGENT_ID: normalizedId,\n ...providerEnv\n }\n });\n\n // Update state\n state.status = 'running';\n state.lastActivity = new Date().toISOString();\n saveAgentState(state);\n\n return state;\n}\n\n/**\n * Generate a recovery prompt for a crashed agent\n */\nfunction generateRecoveryPrompt(state: AgentState): string {\n const lines: string[] = [\n '# Agent Recovery',\n '',\n '⚠️ This agent session was recovered after a crash.',\n '',\n '## Previous Context',\n `- Issue: ${state.issueId}`,\n `- Workspace: ${state.workspace}`,\n `- Started: ${state.startedAt}`,\n '',\n '## Recovery Steps',\n '1. Check beads for context: `bd show ' + state.issueId + '`',\n '2. Review recent git commits: `git log --oneline -10`',\n '3. Check hook for pending work: `pan work hook check`',\n '4. Resume from last known state',\n '',\n '## FPP Reminder',\n '> \"Any runnable action is a fixed point and must resolve before the system can rest.\"',\n '',\n ];\n\n // Add FPP work if available\n const { hasWork } = checkHook(state.id);\n if (hasWork) {\n const fixedPointPrompt = generateFixedPointPrompt(state.id);\n if (fixedPointPrompt) {\n lines.push('---');\n lines.push('');\n lines.push(fixedPointPrompt);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Auto-recover all crashed agents\n */\nexport function autoRecoverAgents(): { recovered: string[]; failed: string[] } {\n const crashed = detectCrashedAgents();\n const recovered: string[] = [];\n const failed: string[] = [];\n\n for (const agent of crashed) {\n try {\n const result = recoverAgent(agent.id);\n if (result) {\n recovered.push(agent.id);\n } else {\n failed.push(agent.id);\n }\n } catch (error) {\n failed.push(agent.id);\n }\n }\n\n return { recovered, failed };\n}\n\n/**\n * Check if Panopticon hooks are configured, and auto-setup if not\n */\nfunction checkAndSetupHooks(): void {\n const settingsPath = join(homedir(), '.claude', 'settings.json');\n const hookPath = join(homedir(), '.panopticon', 'bin', 'heartbeat-hook');\n\n // Check if settings.json exists and has heartbeat hook configured\n if (existsSync(settingsPath)) {\n try {\n const settingsContent = readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(settingsContent);\n const postToolUse = settings?.hooks?.PostToolUse || [];\n\n const hookConfigured = postToolUse.some((hookConfig: any) =>\n hookConfig.hooks?.some((hook: any) =>\n hook.command === hookPath ||\n hook.command?.includes('panopticon') ||\n hook.command?.includes('heartbeat-hook')\n )\n );\n\n if (hookConfigured) {\n return; // Already configured\n }\n } catch {\n // Ignore errors, will attempt setup\n }\n }\n\n // Hooks not configured - run setup silently\n try {\n console.log('Configuring Panopticon heartbeat hooks...');\n // Note: This runs during spawn which is now async, so we can use execAsync\n // But this is called from a sync context in checkAndSetupHooks, so we use fire-and-forget\n exec('pan setup hooks', (error: Error | null) => {\n if (error) {\n console.warn('⚠ Failed to auto-configure hooks. Run `pan setup hooks` manually.');\n } else {\n console.log('✓ Heartbeat hooks configured');\n }\n });\n } catch (error) {\n console.warn('⚠ Failed to auto-configure hooks. Run `pan setup hooks` manually.');\n }\n}\n\n/**\n * Write task cache for heartbeat hook to use\n */\nfunction writeTaskCache(agentId: string, issueId: string): void {\n const cacheDir = join(getAgentDir(agentId));\n mkdirSync(cacheDir, { recursive: true });\n\n const cacheFile = join(cacheDir, 'current-task.json');\n writeFileSync(\n cacheFile,\n JSON.stringify({\n id: issueId,\n title: `Working on ${issueId}`,\n updated_at: new Date().toISOString()\n }, null, 2)\n );\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","existsSync","mkdirSync","join","init_config","existsSync","mkdirSync","writeFileSync","readFileSync","readdirSync","join","mailDir","timestamp","init_config"]}
|
package/dist/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
cleanupTemplateFiles,
|
|
4
4
|
generatePanopticonTraefikConfig
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-3XAB4IXF.js";
|
|
6
6
|
import {
|
|
7
7
|
checkSpecialistQueue,
|
|
8
8
|
clearSessionId,
|
|
@@ -25,12 +25,11 @@ import {
|
|
|
25
25
|
recordWake,
|
|
26
26
|
wakeSpecialistOrQueue,
|
|
27
27
|
wakeSpecialistWithTask
|
|
28
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-2NIAOCIC.js";
|
|
29
29
|
import {
|
|
30
30
|
addAlias,
|
|
31
31
|
cleanOldBackups,
|
|
32
32
|
createBackup,
|
|
33
|
-
createTracker,
|
|
34
33
|
detectShell,
|
|
35
34
|
executeSync,
|
|
36
35
|
getAliasInstructions,
|
|
@@ -40,7 +39,7 @@ import {
|
|
|
40
39
|
planSync,
|
|
41
40
|
restoreBackup,
|
|
42
41
|
syncHooks
|
|
43
|
-
} from "../chunk-
|
|
42
|
+
} from "../chunk-ELK6Q7QI.js";
|
|
44
43
|
import {
|
|
45
44
|
init_remote_agents,
|
|
46
45
|
isRemoteAgentRunning,
|
|
@@ -57,7 +56,7 @@ import {
|
|
|
57
56
|
getAgentState,
|
|
58
57
|
getHealthThresholdsMs,
|
|
59
58
|
init_agents,
|
|
60
|
-
init_config,
|
|
59
|
+
init_config as init_config2,
|
|
61
60
|
init_cv,
|
|
62
61
|
listRunningAgents,
|
|
63
62
|
loadCloisterConfig,
|
|
@@ -68,7 +67,7 @@ import {
|
|
|
68
67
|
saveSessionId,
|
|
69
68
|
spawnAgent,
|
|
70
69
|
stopAgent
|
|
71
|
-
} from "../chunk-
|
|
70
|
+
} from "../chunk-XP2DXWYP.js";
|
|
72
71
|
import {
|
|
73
72
|
checkHook,
|
|
74
73
|
clearHook,
|
|
@@ -87,9 +86,10 @@ import {
|
|
|
87
86
|
sessionExists
|
|
88
87
|
} from "../chunk-VIWUCJ4V.js";
|
|
89
88
|
import {
|
|
89
|
+
createTracker,
|
|
90
90
|
init_settings,
|
|
91
91
|
loadSettings
|
|
92
|
-
} from "../chunk-
|
|
92
|
+
} from "../chunk-LYSBSZYV.js";
|
|
93
93
|
import {
|
|
94
94
|
init_config_yaml,
|
|
95
95
|
loadConfig as loadConfig2
|
|
@@ -101,12 +101,6 @@ import {
|
|
|
101
101
|
loadWorkspaceMetadata,
|
|
102
102
|
saveWorkspaceMetadata
|
|
103
103
|
} from "../chunk-44EOY2ZL.js";
|
|
104
|
-
import {
|
|
105
|
-
getDashboardApiUrl,
|
|
106
|
-
getDefaultConfig,
|
|
107
|
-
loadConfig,
|
|
108
|
-
saveConfig
|
|
109
|
-
} from "../chunk-VTMXR7JF.js";
|
|
110
104
|
import {
|
|
111
105
|
PROJECTS_CONFIG_FILE,
|
|
112
106
|
extractTeamPrefix,
|
|
@@ -125,6 +119,13 @@ import {
|
|
|
125
119
|
createExeProvider,
|
|
126
120
|
init_exe_provider
|
|
127
121
|
} from "../chunk-JM6V62LT.js";
|
|
122
|
+
import {
|
|
123
|
+
getDashboardApiUrl,
|
|
124
|
+
getDefaultConfig,
|
|
125
|
+
init_config,
|
|
126
|
+
loadConfig,
|
|
127
|
+
saveConfig
|
|
128
|
+
} from "../chunk-VU4FLXV5.js";
|
|
128
129
|
import {
|
|
129
130
|
AGENTS_DIR,
|
|
130
131
|
CERTS_DIR,
|
|
@@ -167,6 +168,7 @@ import chalk56 from "chalk";
|
|
|
167
168
|
// src/cli/commands/init.ts
|
|
168
169
|
init_esm_shims();
|
|
169
170
|
init_paths();
|
|
171
|
+
init_config();
|
|
170
172
|
import { existsSync, mkdirSync, readdirSync, cpSync } from "fs";
|
|
171
173
|
import { join, dirname } from "path";
|
|
172
174
|
import { fileURLToPath } from "url";
|
|
@@ -276,6 +278,7 @@ async function initCommand() {
|
|
|
276
278
|
|
|
277
279
|
// src/cli/commands/sync.ts
|
|
278
280
|
init_esm_shims();
|
|
281
|
+
init_config();
|
|
279
282
|
import chalk2 from "chalk";
|
|
280
283
|
import ora2 from "ora";
|
|
281
284
|
import { execSync } from "child_process";
|
|
@@ -502,6 +505,7 @@ init_esm_shims();
|
|
|
502
505
|
import chalk3 from "chalk";
|
|
503
506
|
import ora3 from "ora";
|
|
504
507
|
import inquirer from "inquirer";
|
|
508
|
+
init_config();
|
|
505
509
|
init_paths();
|
|
506
510
|
async function restoreCommand(timestamp) {
|
|
507
511
|
const backups = listBackups();
|
|
@@ -869,6 +873,9 @@ function shouldSkipTrackerUpdate(issueId, cliFlag, trackerType = "linear") {
|
|
|
869
873
|
});
|
|
870
874
|
}
|
|
871
875
|
|
|
876
|
+
// src/cli/commands/work/issue.ts
|
|
877
|
+
init_config();
|
|
878
|
+
|
|
872
879
|
// src/lib/remote/index.ts
|
|
873
880
|
init_esm_shims();
|
|
874
881
|
init_exe_provider();
|
|
@@ -1315,7 +1322,7 @@ async function handleRemoteWorkspace(issueId, options, spinner) {
|
|
|
1315
1322
|
if (!remoteMetadata) {
|
|
1316
1323
|
spinner.text = "Remote workspace not found, creating...";
|
|
1317
1324
|
try {
|
|
1318
|
-
const { createRemoteWorkspace: createRemoteWorkspace2 } = await import("../remote-workspace-
|
|
1325
|
+
const { createRemoteWorkspace: createRemoteWorkspace2 } = await import("../remote-workspace-2G6V2KNP.js");
|
|
1319
1326
|
remoteMetadata = await createRemoteWorkspace2(issueId, { spinner });
|
|
1320
1327
|
} catch (error) {
|
|
1321
1328
|
spinner.fail(`Failed to create remote workspace: ${error.message}`);
|
|
@@ -2098,7 +2105,7 @@ async function doneCommand(id, options = {}) {
|
|
|
2098
2105
|
console.log(chalk12.dim(` pan work approve ${issueId}`));
|
|
2099
2106
|
console.log("");
|
|
2100
2107
|
try {
|
|
2101
|
-
const { getDashboardApiUrl: getDashboardApiUrl2 } = await import("../config-
|
|
2108
|
+
const { getDashboardApiUrl: getDashboardApiUrl2 } = await import("../config-BOAMSKTF.js");
|
|
2102
2109
|
const dashboardUrl = getDashboardApiUrl2();
|
|
2103
2110
|
const http = await import("http");
|
|
2104
2111
|
const checkDashboard = () => new Promise((resolve2) => {
|
|
@@ -2774,6 +2781,7 @@ async function planCommand(id, options = {}) {
|
|
|
2774
2781
|
|
|
2775
2782
|
// src/cli/commands/work/list.ts
|
|
2776
2783
|
init_esm_shims();
|
|
2784
|
+
init_config();
|
|
2777
2785
|
import chalk14 from "chalk";
|
|
2778
2786
|
import ora8 from "ora";
|
|
2779
2787
|
var PRIORITY_LABELS = {
|
|
@@ -2942,6 +2950,7 @@ ${tracker.toUpperCase()} (${issues.length} shadowed issues)
|
|
|
2942
2950
|
|
|
2943
2951
|
// src/cli/commands/work/triage.ts
|
|
2944
2952
|
init_esm_shims();
|
|
2953
|
+
init_config();
|
|
2945
2954
|
import chalk15 from "chalk";
|
|
2946
2955
|
import ora9 from "ora";
|
|
2947
2956
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, existsSync as existsSync12, mkdirSync as mkdirSync6 } from "fs";
|
|
@@ -4315,6 +4324,7 @@ Previous state: ${issue.state}`
|
|
|
4315
4324
|
|
|
4316
4325
|
// src/cli/commands/work/request-review.ts
|
|
4317
4326
|
init_esm_shims();
|
|
4327
|
+
init_config();
|
|
4318
4328
|
import chalk22 from "chalk";
|
|
4319
4329
|
var DASHBOARD_URL = getDashboardApiUrl();
|
|
4320
4330
|
async function requestReviewCommand(id, options) {
|
|
@@ -5572,7 +5582,12 @@ async function createWorkspace(options) {
|
|
|
5572
5582
|
await execAsync4(`docker compose -f "${traefikPath}" up -d`, { cwd: projectConfig.path });
|
|
5573
5583
|
result.steps.push("Started Traefik");
|
|
5574
5584
|
} catch (error) {
|
|
5575
|
-
|
|
5585
|
+
const msg = error?.message || String(error);
|
|
5586
|
+
if (msg.includes("port is already allocated") || msg.includes("address already in use")) {
|
|
5587
|
+
result.steps.push("Traefik already running (port in use)");
|
|
5588
|
+
} else {
|
|
5589
|
+
result.errors.push(`Failed to start Traefik: ${error}`);
|
|
5590
|
+
}
|
|
5576
5591
|
}
|
|
5577
5592
|
}
|
|
5578
5593
|
}
|
|
@@ -5717,6 +5732,7 @@ async function removeWorkspace(options) {
|
|
|
5717
5732
|
}
|
|
5718
5733
|
|
|
5719
5734
|
// src/cli/commands/workspace.ts
|
|
5735
|
+
init_config();
|
|
5720
5736
|
import { exec as exec5 } from "child_process";
|
|
5721
5737
|
import { promisify as promisify5 } from "util";
|
|
5722
5738
|
import { homedir as homedir12 } from "os";
|
|
@@ -7008,6 +7024,7 @@ ${config2.name || key}`));
|
|
|
7008
7024
|
// src/cli/commands/install.ts
|
|
7009
7025
|
init_esm_shims();
|
|
7010
7026
|
init_paths();
|
|
7027
|
+
init_config();
|
|
7011
7028
|
import chalk30 from "chalk";
|
|
7012
7029
|
import ora17 from "ora";
|
|
7013
7030
|
import inquirer5 from "inquirer";
|
|
@@ -7471,11 +7488,11 @@ import chalk31 from "chalk";
|
|
|
7471
7488
|
|
|
7472
7489
|
// src/lib/cloister/service.ts
|
|
7473
7490
|
init_esm_shims();
|
|
7474
|
-
|
|
7491
|
+
init_config2();
|
|
7475
7492
|
|
|
7476
7493
|
// src/lib/cloister/health.ts
|
|
7477
7494
|
init_esm_shims();
|
|
7478
|
-
|
|
7495
|
+
init_config2();
|
|
7479
7496
|
function evaluateHealthState(timeSinceActivityMs, thresholds) {
|
|
7480
7497
|
if (timeSinceActivityMs < thresholds.stale) {
|
|
7481
7498
|
return "active";
|
|
@@ -8044,7 +8061,7 @@ init_agents();
|
|
|
8044
8061
|
|
|
8045
8062
|
// src/lib/cloister/triggers.ts
|
|
8046
8063
|
init_esm_shims();
|
|
8047
|
-
|
|
8064
|
+
init_config2();
|
|
8048
8065
|
import { existsSync as existsSync27 } from "fs";
|
|
8049
8066
|
import { join as join26 } from "path";
|
|
8050
8067
|
import { exec as exec7 } from "child_process";
|
|
@@ -8792,7 +8809,7 @@ function clearOldViolations(hoursOld = 24) {
|
|
|
8792
8809
|
// src/lib/cloister/cost-monitor.ts
|
|
8793
8810
|
init_esm_shims();
|
|
8794
8811
|
init_paths();
|
|
8795
|
-
|
|
8812
|
+
init_config2();
|
|
8796
8813
|
import { readFileSync as readFileSync26, existsSync as existsSync32, writeFileSync as writeFileSync18, mkdirSync as mkdirSync21, unlinkSync as unlinkSync3 } from "fs";
|
|
8797
8814
|
import { join as join31, dirname as dirname10 } from "path";
|
|
8798
8815
|
var COST_DATA_FILE = join31(PANOPTICON_HOME, "cost-data.json");
|
|
@@ -9145,7 +9162,7 @@ async function checkAndRotateIfNeeded(specialistName, workingDir) {
|
|
|
9145
9162
|
// src/lib/cloister/deacon.ts
|
|
9146
9163
|
init_esm_shims();
|
|
9147
9164
|
init_paths();
|
|
9148
|
-
|
|
9165
|
+
init_config2();
|
|
9149
9166
|
init_specialists();
|
|
9150
9167
|
init_agents();
|
|
9151
9168
|
init_tmux();
|
|
@@ -9746,7 +9763,7 @@ async function runPatrol() {
|
|
|
9746
9763
|
if (nextTask) {
|
|
9747
9764
|
console.log(`[deacon] Auto-resuming suspended ${specialist.name} for queued task: ${nextTask.payload.issueId}`);
|
|
9748
9765
|
try {
|
|
9749
|
-
const { resumeAgent } = await import("../agents-
|
|
9766
|
+
const { resumeAgent } = await import("../agents-BDFHF4T3.js");
|
|
9750
9767
|
const message = `# Queued Work
|
|
9751
9768
|
|
|
9752
9769
|
Processing queued task: ${nextTask.payload.issueId}`;
|
|
@@ -11571,7 +11588,7 @@ import { promisify as promisify12 } from "util";
|
|
|
11571
11588
|
var execAsync12 = promisify12(exec12);
|
|
11572
11589
|
async function listLogsCommand(project2, type, options) {
|
|
11573
11590
|
try {
|
|
11574
|
-
const { listRunLogs } = await import("../specialist-logs-
|
|
11591
|
+
const { listRunLogs } = await import("../specialist-logs-GF3YV4KL.js");
|
|
11575
11592
|
const limit = options.limit ? parseInt(options.limit) : 10;
|
|
11576
11593
|
const runs = listRunLogs(project2, type, { limit });
|
|
11577
11594
|
if (options.json) {
|
|
@@ -11616,7 +11633,7 @@ View a specific run: pan specialists logs ${project2} ${type} <runId>
|
|
|
11616
11633
|
}
|
|
11617
11634
|
async function viewLogCommand(project2, type, runId, options) {
|
|
11618
11635
|
try {
|
|
11619
|
-
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-
|
|
11636
|
+
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-GF3YV4KL.js");
|
|
11620
11637
|
const content = getRunLog(project2, type, runId);
|
|
11621
11638
|
if (!content) {
|
|
11622
11639
|
console.error(`\u274C Run log not found: ${runId}`);
|
|
@@ -11640,8 +11657,8 @@ async function viewLogCommand(project2, type, runId, options) {
|
|
|
11640
11657
|
}
|
|
11641
11658
|
async function tailLogCommand(project2, type) {
|
|
11642
11659
|
try {
|
|
11643
|
-
const { getRunLogPath } = await import("../specialist-logs-
|
|
11644
|
-
const { getProjectSpecialistMetadata } = await import("../specialists-
|
|
11660
|
+
const { getRunLogPath } = await import("../specialist-logs-GF3YV4KL.js");
|
|
11661
|
+
const { getProjectSpecialistMetadata } = await import("../specialists-JBIW6MP4.js");
|
|
11645
11662
|
const metadata = getProjectSpecialistMetadata(project2, type);
|
|
11646
11663
|
if (!metadata.currentRun) {
|
|
11647
11664
|
console.error(`\u274C No active run for ${project2}/${type}`);
|
|
@@ -11710,7 +11727,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
11710
11727
|
console.log(" Use --force to confirm.");
|
|
11711
11728
|
process.exit(1);
|
|
11712
11729
|
}
|
|
11713
|
-
const { cleanupAllLogs } = await import("../specialist-logs-
|
|
11730
|
+
const { cleanupAllLogs } = await import("../specialist-logs-GF3YV4KL.js");
|
|
11714
11731
|
console.log("\u{1F9F9} Cleaning up old logs for all projects...\n");
|
|
11715
11732
|
const results = cleanupAllLogs();
|
|
11716
11733
|
console.log(`
|
|
@@ -11737,7 +11754,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
11737
11754
|
console.log(" Use --force to confirm.");
|
|
11738
11755
|
process.exit(1);
|
|
11739
11756
|
}
|
|
11740
|
-
const { cleanupOldLogs } = await import("../specialist-logs-
|
|
11757
|
+
const { cleanupOldLogs } = await import("../specialist-logs-GF3YV4KL.js");
|
|
11741
11758
|
const { getSpecialistRetention } = await import("../projects-VXRUCMLM.js");
|
|
11742
11759
|
const retention = getSpecialistRetention(projectOrAll);
|
|
11743
11760
|
console.log(`\u{1F9F9} Cleaning up old logs for ${projectOrAll}/${type}...`);
|
|
@@ -12859,6 +12876,7 @@ async function doctorCommand() {
|
|
|
12859
12876
|
|
|
12860
12877
|
// src/cli/commands/update.ts
|
|
12861
12878
|
init_esm_shims();
|
|
12879
|
+
init_config();
|
|
12862
12880
|
import { execSync as execSync9 } from "child_process";
|
|
12863
12881
|
import chalk47 from "chalk";
|
|
12864
12882
|
import { readFileSync as readFileSync37 } from "fs";
|
|
@@ -13859,6 +13877,7 @@ init_esm_shims();
|
|
|
13859
13877
|
|
|
13860
13878
|
// src/cli/commands/remote/status.ts
|
|
13861
13879
|
init_esm_shims();
|
|
13880
|
+
init_config();
|
|
13862
13881
|
import chalk51 from "chalk";
|
|
13863
13882
|
import ora23 from "ora";
|
|
13864
13883
|
async function statusCommand5(options) {
|
|
@@ -13938,6 +13957,7 @@ async function statusCommand5(options) {
|
|
|
13938
13957
|
|
|
13939
13958
|
// src/cli/commands/remote/init.ts
|
|
13940
13959
|
init_esm_shims();
|
|
13960
|
+
init_config();
|
|
13941
13961
|
import chalk52 from "chalk";
|
|
13942
13962
|
import ora24 from "ora";
|
|
13943
13963
|
async function initCommand2(options) {
|
|
@@ -14099,6 +14119,7 @@ COMPOSE_EOF`);
|
|
|
14099
14119
|
|
|
14100
14120
|
// src/cli/commands/remote/resources.ts
|
|
14101
14121
|
init_esm_shims();
|
|
14122
|
+
init_config();
|
|
14102
14123
|
import chalk53 from "chalk";
|
|
14103
14124
|
import ora25 from "ora";
|
|
14104
14125
|
async function resourcesCommand(options) {
|
|
@@ -14227,6 +14248,7 @@ async function resourcesCommand(options) {
|
|
|
14227
14248
|
|
|
14228
14249
|
// src/cli/commands/remote/setup.ts
|
|
14229
14250
|
init_esm_shims();
|
|
14251
|
+
init_config();
|
|
14230
14252
|
import chalk54 from "chalk";
|
|
14231
14253
|
import ora26 from "ora";
|
|
14232
14254
|
import { exec as exec16 } from "child_process";
|
|
@@ -14377,6 +14399,7 @@ function registerRemoteCommands(program2) {
|
|
|
14377
14399
|
|
|
14378
14400
|
// src/cli/commands/config.ts
|
|
14379
14401
|
init_esm_shims();
|
|
14402
|
+
init_config();
|
|
14380
14403
|
import chalk55 from "chalk";
|
|
14381
14404
|
|
|
14382
14405
|
// src/lib/env-loader.ts
|
|
@@ -14549,7 +14572,7 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
|
|
|
14549
14572
|
console.log(chalk56.bold("Starting Panopticon...\n"));
|
|
14550
14573
|
if (traefikEnabled && !options.skipTraefik) {
|
|
14551
14574
|
try {
|
|
14552
|
-
const { generatePanopticonTraefikConfig: generatePanopticonTraefikConfig2 } = await import("../traefik-
|
|
14575
|
+
const { generatePanopticonTraefikConfig: generatePanopticonTraefikConfig2 } = await import("../traefik-CUJM6K5Z.js");
|
|
14553
14576
|
if (generatePanopticonTraefikConfig2()) {
|
|
14554
14577
|
console.log(chalk56.dim(" Regenerated Traefik config from template"));
|
|
14555
14578
|
}
|