@wrongstack/webui 0.257.2 → 0.260.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.
- package/dist/assets/index-6rPVh7TJ.css +2 -0
- package/dist/assets/index-CfIQObXO.js +165 -0
- package/dist/assets/{vendor-BXyxPv8C.js → vendor-BRkhRU94.js} +225 -225
- package/dist/index.html +3 -3
- package/dist/index.js +4220 -4782
- package/dist/index.js.map +1 -1
- package/dist/server/entry.js +1 -1
- package/dist/server/entry.js.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/package.json +5 -5
- package/dist/assets/index-C19_lY6u.css +0 -2
- package/dist/assets/index-DiFYgNpK.js +0 -163
package/dist/server/entry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/index.ts","../../src/server/http-server.ts","../../src/server/ws-auth.ts","../../src/server/file-handlers.ts","../../src/server/file-picker.ts","../../src/server/ws-utils.ts","../../src/server/memory-handlers.ts","../../../runtime/src/container.ts","../../src/server/boot.ts","../../src/server/autophase-ws-handler.ts","../../src/server/collaboration-ws-handler.ts","../../src/server/worktree-ws-handler.ts","../../src/server/mailbox-handlers.ts","../../src/server/lifecycle.ts","../../src/server/instance-registry.ts","../../src/server/port-utils.ts","../../src/server/open-browser.ts","../../src/server/usage-cost.ts","../../src/server/provider-config-io.ts","../../src/server/provider-keys.ts","../../src/server/provider-handlers.ts","../../src/server/setup-events.ts","../../src/server/custom-context-modes.ts","../../src/server/token-estimator.ts","../../src/server/eternal-iteration-broadcast.ts","../../src/server/shell-open.ts","../../src/server/entry.ts"],"sourcesContent":["import { expectDefined, GlobalMailbox, projectSlug, getSessionRegistry, AgentStatusTracker } from '@wrongstack/core';\nimport { makeMailboxTool, makeMailSendTool, makeMailInboxTool, mailboxSessionTag } from '@wrongstack/core';\nimport {\n BrainMonitor,\n DefaultBrainArbiter,\n ObservableBrainArbiter,\n createAutonomyBrain,\n createTieredBrainArbiter,\n type BrainArbiter,\n type BrainAutoRisk,\n} from '@wrongstack/core';\nimport * as fs from 'node:fs/promises';\nimport * as http from 'node:http';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { createHttpServer } from './http-server.js';\nimport {\n handleFilesTree,\n handleFilesRead,\n handleFilesWrite,\n handleFilesList,\n} from './file-handlers.js';\nimport {\n handleMemoryList,\n handleMemoryRemember,\n handleMemoryForget,\n} from './memory-handlers.js';\nimport {\n Agent,\n AutoCompactionMiddleware,\n Context,\n DefaultMemoryStore,\n DefaultModeStore,\n DefaultModelsRegistry,\n DefaultSessionReader,\n DefaultSessionStore,\n DefaultSkillLoader,\n DefaultSystemPromptBuilder,\n DefaultTokenCounter,\n AnnotationsStore,\n CollaborationBus,\n collabPauseMiddleware,\n collabInjectMiddleware,\n estimateRequestTokensCalibrated,\n EventBus,\n createStrategyCompactor,\n type ProviderConfig,\n type Provider,\n ProviderRegistry,\n TOKENS,\n ToolRegistry,\n atomicWrite,\n createDefaultPipelines,\n createSessionEventBridge,\n resolveSessionLoggingConfig,\n DEFAULT_CONTEXT_WINDOW_MODE_ID,\n DEFAULT_SESSION_PRUNE_DAYS,\n DEFAULT_TOOLS_CONFIG,\n listContextWindowModes,\n repairToolUseAdjacency,\n resolveContextWindowPolicy,\n enhanceUserPrompt,\n recentTextTurns,\n} from '@wrongstack/core';\nimport { ToolExecutor } from '@wrongstack/core/execution';\nimport { decryptConfigSecrets, encryptConfigSecrets } from '@wrongstack/core/security';\nimport { buildProviderFactoriesFromRegistry, makeProviderFromConfig } from '@wrongstack/providers';\nimport { builtinToolsPack, forgetTool, rememberTool, searchMemoryTool, relatedMemoryTool } from '@wrongstack/tools';\nimport { type WebSocket, WebSocketServer } from 'ws';\nimport { createDefaultContainer } from '../../../runtime/src/container.js';\nimport { bootConfig, patchConfig } from './boot.js';\nimport { AutoPhaseWebSocketHandler } from './autophase-ws-handler.js';\nimport { CollaborationWebSocketHandler } from './collaboration-ws-handler.js';\nimport { WorktreeWebSocketHandler } from './worktree-ws-handler.js';\nimport { handleMailboxMessages, handleMailboxAgents, handleMailboxClear } from './mailbox-handlers.js';\nimport { verifyClient as verifyWsClient } from './ws-auth.js';\nimport { registerShutdownHandlers } from './lifecycle.js';\nimport { registerInstance, unregisterInstance } from './instance-registry.js';\nimport { findFreePort } from './port-utils.js';\nimport { openBrowser } from './open-browser.js';\nimport { computeUsageCost, getCostRates } from './usage-cost.js';\nimport { createProviderHandlers } from './provider-handlers.js';\nimport { setupEvents } from './setup-events.js';\nimport { createCustomModeStore } from './custom-context-modes.js';\nimport { maskedKey, normalizeKeys } from './provider-keys.js';\nimport { send, broadcast, sendResult, errMessage, generateAuthToken } from './ws-utils.js';\nimport { estimateContextBreakdown } from './token-estimator.js';\nimport { createEternalSubscription } from './eternal-iteration-broadcast.js';\nimport { handleShellOpen, type ShellOpenRequest, type ShellOpenResult } from './shell-open.js';\n// Re-export types — shared message shapes and options used by both the\n// standalone server and the CLI's `--webui` embedded mode.\nexport type { WebUIOptions, BackendServices } from './types.js';\nexport type { WSServerMessage, WSClientMessage, ConnectedClient } from './types.js';\n\n// Re-export the static-serve + multi-instance building blocks so other packages\n// (the CLI's `--webui` mode) can serve the same React frontend, inject the live\n// WS port, pick free ports, and register in the shared instance registry —\n// without duplicating any of that logic.\nexport { createHttpServer, buildCspHeader, injectWsPort } from './http-server.js';\nexport type { CreateHttpServerOptions } from './http-server.js';\nexport { findFreePort, isPortFree } from './port-utils.js';\nexport { openBrowser, browserOpenCommand } from './open-browser.js';\n// Token estimator primitives — exposed for the CLI's embedded webui\n// (which historically inlined its own copy and let it drift). Now\n// there's exactly one definition. See\n// packages/cli/src/webui-server.ts Phase 2 of the refactor plan.\nexport {\n estimateTokens,\n messageTokens,\n messagePreview,\n stringifyContent,\n type ContextBreakdown,\n type MessageTokenEntry,\n type ToolTokenEntry,\n} from './token-estimator.js';\nexport {\n registerInstance,\n unregisterInstance,\n listInstances,\n formatInstances,\n registryPath,\n defaultBaseDir,\n type WebUIInstanceRecord,\n} from './instance-registry.js';\n\n// WebSocket utilities shared with CLI\nexport {\n createEternalSubscription,\n type EternalSubscribe,\n type EternalBroadcast,\n type EternalSubscription,\n} from './eternal-iteration-broadcast.js';\nexport {\n handleShellOpen,\n type ShellOpenRequest,\n type ShellOpenResult,\n type ShellOpenTarget,\n} from './shell-open.js';\nexport {\n send,\n broadcast,\n sendResult,\n errMessage,\n generateAuthToken,\n} from './ws-utils.js';\n\n// File operation handlers shared with CLI (files.tree, files.read, files.write, files.list)\nexport {\n handleFilesTree,\n handleFilesRead,\n handleFilesWrite,\n handleFilesList,\n} from './file-handlers.js';\n\n// Memory operation handlers shared with CLI (memory.list, memory.remember, memory.forget)\nexport {\n handleMemoryList,\n handleMemoryRemember,\n handleMemoryForget,\n} from './memory-handlers.js';\n\n// Custom context-mode store shared with the CLI's embedded server\n// (context.mode.create/update/delete + custom-aware list/switch).\nexport {\n createCustomModeStore,\n type CustomModeStore,\n type CustomContextMode,\n} from './custom-context-modes.js';\n\n// WS auth — pure functions for verifying WebSocket connections\nexport {\n verifyClient,\n isLoopbackHostname,\n isLoopbackBind,\n tokenMatches,\n extractToken,\n hostHeaderOk,\n type VerifyClientInput,\n} from './ws-auth.js';\n\n// Provider/API-key record transforms (pure functions, testable without I/O)\nexport {\n normalizeKeys,\n writeKeysBack,\n maskedKey,\n upsertKey,\n deleteKey,\n setActiveKey,\n addProvider,\n removeProvider,\n type KeyOpResult,\n type ProvidersRecord,\n} from './provider-keys.js';\n\n// Provider config load/save (decrypt from / encrypt to global config)\nexport {\n loadSavedProviders,\n saveProviders,\n createProviderConfigIO,\n} from './provider-config-io.js';\n\n// AutoPhase WebSocket handler — manages AutoPhase lifecycle via WS messages.\n// Exported so the CLI's embedded webui-server can also handle autophase.*\n// messages when running in --webui mode.\nexport { AutoPhaseWebSocketHandler } from './autophase-ws-handler.js';\n\n// Message + client shapes now live in ./types.ts (shared with the CLI's\n// embedded server). Imported here for internal use; re-exported above for\n// external consumers. The previous local copies shadowed these and made the\n// `Map<WebSocket, ConnectedClient>` passed to the extracted ws-utils helpers\n// nominally distinct, which TS rejected.\nimport type { ConnectedClient, WSClientMessage, WSServerMessage, WebUIOptions } from './types.js';\n\nexport async function startWebUI(\n opts: WebUIOptions & {\n wsPort?: number | undefined;\n wsHost?: string | undefined;\n open?: boolean | undefined;\n } = {},\n): Promise<void> {\n const requestedWsPort = opts.wsPort ?? 3457;\n // Bind to loopback IP by default (not the string \"localhost\", which on some\n // hosts resolves to IPv6 ::1 and surprises older WS clients). Set WS_HOST or\n // pass opts.wsHost to override (e.g. \"0.0.0.0\" for LAN access).\n const wsHost = opts.wsHost ?? '127.0.0.1';\n const requestedHttpPort = Number.parseInt(process.env['PORT'] ?? '3456', 10);\n\n // Port resolution. Unless WEBUI_STRICT_PORT is set, auto-advance past any port\n // already taken by another instance so running `webui` several times \"just\n // works\" — the real ports are then stamped into the served HTML and the\n // instance registry. Strict mode keeps the requested ports and lets bind fail\n // loudly (useful behind a reverse proxy that expects fixed ports).\n const strictPort =\n process.env['WEBUI_STRICT_PORT'] === '1' || process.env['WEBUI_STRICT_PORT'] === 'true';\n let wsPort = requestedWsPort;\n let httpPort = requestedHttpPort;\n if (!strictPort) {\n // Resolve HTTP first, then WS excluding it, so successive instances land on\n // tidy adjacent pairs (3456/3457, 3458/3459, …) instead of interleaving.\n httpPort = await findFreePort(wsHost, requestedHttpPort);\n wsPort = await findFreePort(wsHost, requestedWsPort, { exclude: new Set([httpPort]) });\n if (httpPort !== requestedHttpPort) {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.port_reassigned',\n protocol: 'HTTP',\n requested: requestedHttpPort,\n assigned: httpPort,\n timestamp: new Date().toISOString(),\n }));\n }\n if (wsPort !== requestedWsPort) {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.port_reassigned',\n protocol: 'WS',\n requested: requestedWsPort,\n assigned: wsPort,\n timestamp: new Date().toISOString(),\n }));\n }\n }\n\n console.log('[WebUI] Starting backend services...');\n\n // Boot configuration\n const boot = await bootConfig();\n const { config: baseConfig, globalConfigPath, wpaths, logger } = boot;\n // PR 5 of Phase 2: when the caller (typically the CLI) supplies a\n // pre-built `BackendServices`, prefer its `vault` over the one the\n // default boot would construct. This lets `runWebUI` keep owning the\n // vault lifecycle (so it can decrypt/encrypt its own config writes\n // in lockstep with the rest of the CLI session) instead of having\n // the webui build a parallel vault it can never see.\n const vault = opts.services?.vault ?? boot.vault;\n let config = baseConfig;\n\n /** Mutable project root — updated on `projects.select`. File handlers,\n * sessionStartPayload, and session store use this value. */\n let projectRoot = boot.projectRoot;\n /** Mutable working directory — starts at projectRoot, changeable via\n * `working_dir.set` WS message. Must always stay inside projectRoot. */\n let workingDir = projectRoot;\n\n // Serialize concurrent config writes to prevent races between model.switch\n // and key.add/key.update handlers that both read-modify-write globalConfigPath.\n let configWriteLock: Promise<void> = Promise.resolve();\n\n console.log('[WebUI] Config loaded:', config.provider ?? '(none)', '/', config.model ?? '(none)');\n\n // If no active provider is set but there are saved providers, pick the first one.\n // This handles configs written in older formats or by external tools.\n // Guard against config.providers being a string or other non-object value\n // (e.g., from a corrupted config or YAML parser misreading the value).\n if (\n !config.provider &&\n config.providers &&\n typeof config.providers === 'object' &&\n config.providers !== null &&\n !Array.isArray(config.providers) &&\n Object.keys(config.providers).length > 0\n ) {\n const firstKey = expectDefined(Object.keys(config.providers)[0]);\n config = patchConfig(config, { provider: firstKey });\n console.log('[WebUI] No active provider — auto-selected:', firstKey);\n }\n\n // If still no provider, the frontend will show a no-provider welcome screen.\n // We still start the HTTP/WS servers so the user can configure via the UI.\n const needsProvider = !config.provider || !config.model;\n\n // ModelsRegistry — use injected one if `services.modelsRegistry` was passed,\n // otherwise build a fresh one. The injected path lets the CLI's `runWebUI`\n // share a single registry across its own runtime and the webui surface.\n const modelsRegistry =\n opts.services?.modelsRegistry ??\n new DefaultModelsRegistry({\n cacheFile: wpaths.modelsCache,\n ttlSeconds: 24 * 3600,\n });\n\n // Container via shared factory\n const container = createDefaultContainer({ config, wpaths, logger, modelsRegistry });\n // PR 5 of Phase 2: when the caller (typically the CLI) supplies a\n // pre-built `BackendServices`, prefer its `configStore` over the one\n // the default container would resolve. This is the read+write\n // counterpart of the `vault` injection above: together they let\n // `runWebUI` own the global config lifecycle and have the webui\n // operate on the *same* in-memory store, so a `provider.switch`\n // from the webui is visible to the CLI's next call without a disk\n // round-trip in between.\n const configStore = opts.services?.configStore ?? container.resolve(TOKENS.ConfigStore);\n\n // Provider registry\n const providerRegistry = new ProviderRegistry();\n try {\n const factories = await buildProviderFactoriesFromRegistry({\n registry: modelsRegistry,\n log: logger,\n });\n for (const f of factories) providerRegistry.register(f);\n console.log('[WebUI] Provider registry loaded:', providerRegistry.list().length, 'providers');\n } catch (err) {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.provider_registry_load_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n }\n\n // Tool registry — use injected one if `services.toolRegistry` was passed.\n // When injected, the caller has already registered the tools they want\n // (the CLI's runWebUI registers its own runtime tools); startWebUI just\n // uses the registry as-is.\n const toolRegistry =\n opts.services?.toolRegistry ??\n (() => {\n const r = new ToolRegistry();\n r.registerAllOrThrow([...(builtinToolsPack.tools ?? [])], builtinToolsPack.name);\n return r;\n })();\n\n // Memory tools\n const memoryStore = new DefaultMemoryStore({ paths: wpaths });\n if (config.features.memory) {\n toolRegistry.register(rememberTool(memoryStore));\n toolRegistry.register(forgetTool(memoryStore));\n toolRegistry.register(searchMemoryTool(memoryStore));\n toolRegistry.register(relatedMemoryTool(memoryStore));\n }\n\n // Event bus — use injected one if `services.events` was passed. The CLI's\n // runWebUI owns the agent's EventBus so it can wire sub-agents onto the\n // same bus the webui dashboard reads from. When injected, we just\n // attach the logger and reuse the existing instance.\n const events = opts.services?.events ?? new EventBus();\n events.setLogger(logger);\n\n // Inter-agent mailbox tools — same project-level GlobalMailbox the CLI\n // registers, keyed by wpaths.projectDir so WebUI agents and terminal\n // agents on the same project share one inbox and can chat/broadcast.\n // mail_send/mail_inbox are the high-affordance thin wrappers.\n toolRegistry.register(makeMailboxTool({ projectDir: wpaths.projectDir, events }));\n toolRegistry.register(makeMailSendTool({ projectDir: wpaths.projectDir, events }));\n toolRegistry.register(makeMailInboxTool({ projectDir: wpaths.projectDir, events }));\n console.log('[WebUI] Tool registry loaded:', toolRegistry.list().length, 'tools');\n\n // Session store — mutable so projects.select can swap it to the new project's dir.\n // Use the injected one if `services.session` was passed. The CLI's\n // runWebUI already has its own session store pointing at the\n // right per-project dir; we reuse it here so the webui reads\n // the same history the CLI is writing.\n let sessionStore = opts.services?.session ?? new DefaultSessionStore({ dir: wpaths.projectSessions });\n // Prune old sessions on server start (non-blocking). Skipped when\n // an injected store is in use — the CLI's eternal loop is\n // responsible for its own lifecycle and pruning an in-use store\n // would race with the CLI's own prune policy.\n if (!opts.services?.session) {\n sessionStore\n .prune(DEFAULT_SESSION_PRUNE_DAYS)\n .then((count) => {\n if (count > 0) logger.info(`Pruned ${count} old session${count === 1 ? '' : 's'}.`);\n })\n .catch(() => undefined);\n }\n // Session reader — same on-disk store, read-only access. Used by the\n // collaboration handler to replay the last N events to late-joining\n // observers (Phase 1.5 of idea #13).\n const sessionReader = new DefaultSessionReader({ store: sessionStore });\n // Annotations store — sidecar files for collaboration notes (Phase 2\n // of idea #13). Living under `projectSessions` so all per-session\n // data is colocated and travels with the project.\n const annotationsStore = new AnnotationsStore({ dir: wpaths.projectSessions });\n let session = await sessionStore.create({\n id: '',\n title: '',\n model: config.model,\n provider: config.provider,\n });\n // Wall-clock when the *current* session started. Updated on /new and on\n // /resume so /stats can report accurate elapsed time per the active\n // session, not the daemon process uptime.\n let sessionStartedAt = Date.now();\n console.log('[WebUI] Session created:', session.id);\n\n // ── Cross-surface discovery ──────────────────────────────────────────\n // (1) Register/refresh this project in ~/.wrongstack/projects.json so\n // pickers and other surfaces see it regardless of which interface\n // opened it first. (2) Register this session in the cross-process\n // SessionRegistry so terminals' `/sessions status` lists this WebUI\n // (and vice versa). Both best-effort — discovery must not block boot.\n try {\n await touchProjectEntry(projectRoot, workingDir);\n } catch { /* best-effort */ }\n let statusTracker: AgentStatusTracker | undefined;\n try {\n const registry = getSessionRegistry(wpaths.globalRoot);\n await registry.register({\n sessionId: session.id,\n projectSlug: wpaths.projectSlug,\n projectRoot,\n projectName: path.basename(projectRoot),\n workingDir,\n pid: process.pid,\n startedAt: new Date().toISOString(),\n });\n statusTracker = new AgentStatusTracker({ events, registry });\n statusTracker.start();\n const stopTracking = async () => {\n try {\n await registry.markClosing();\n statusTracker?.stop();\n } catch { /* ignore */ }\n };\n process.once('beforeExit', () => { void stopTracking(); });\n process.once('SIGINT', () => { void stopTracking(); });\n process.once('SIGTERM', () => { void stopTracking(); });\n } catch { /* best-effort — discovery degrades gracefully */ }\n\n // Token counter\n const tokenCounter = new DefaultTokenCounter({\n registry: modelsRegistry,\n providerId: config.provider,\n });\n\n // Mode store\n const modeStore = new DefaultModeStore({ directory: wpaths.configDir });\n const activeMode = await modeStore.getActiveMode();\n let modeId = activeMode?.id ?? 'default';\n const modePrompt = activeMode?.prompt ?? '';\n\n // Custom context modes store — user-defined presets persisted to disk.\n // Loaded once on startup; merges with built-in modes in the list handler.\n const customModeStore = createCustomModeStore(wpaths.configDir);\n await customModeStore.load();\n console.log(\n '[WebUI] Custom context modes loaded:',\n customModeStore.list().filter((m) => (m as { custom?: boolean }).custom).length,\n 'custom',\n );\n\n // System prompt builder\n const resolvedModel = await modelsRegistry.getModel(config.provider, config.model);\n const modelCapabilities = resolvedModel?.capabilities\n ? {\n maxContextTokens: resolvedModel.capabilities.maxContext,\n supportsTools: resolvedModel.capabilities.tools,\n supportsVision: resolvedModel.capabilities.vision,\n supportsReasoning: resolvedModel.capabilities.reasoning,\n }\n : undefined;\n\n const skillLoader = config.features.skills\n ? new DefaultSkillLoader({ paths: wpaths })\n : undefined;\n const systemPromptBuilder = new DefaultSystemPromptBuilder({\n memoryStore,\n skillLoader,\n modeStore,\n modeId,\n modePrompt,\n modelCapabilities,\n });\n\n // Fetch online agents from the shared mailbox to include in system prompt\n let onlineAgents: import('@wrongstack/core').MailboxAgentStatus[] = [];\n try {\n const systemMailbox = new GlobalMailbox(wpaths.projectDir);\n onlineAgents = await systemMailbox.getAgentStatuses();\n } catch {\n // Non-fatal — mailbox errors should not block prompt building\n }\n\n const systemPrompt = await systemPromptBuilder.build({\n cwd: projectRoot,\n projectRoot,\n tools: toolRegistry.list(),\n provider: config.provider,\n model: config.model,\n onlineAgents,\n });\n\n // Build provider (only if provider is configured)\n let provider: ReturnType<ProviderRegistry['create']>;\n if (!needsProvider) {\n const providerConfig = config.providers?.[config.provider] ?? {\n type: config.provider,\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n };\n try {\n const cfgWithType = { ...providerConfig, type: config.provider };\n if (config.features.modelsRegistry && providerRegistry.has(config.provider)) {\n provider = providerRegistry.create(cfgWithType);\n } else {\n provider = makeProviderFromConfig(config.provider, cfgWithType);\n }\n } catch (err) {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.provider_create_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n throw err;\n }\n } else {\n // No provider is actively selected, but saved providers exist.\n // Re-read the config to find one with a usable encrypted API key\n // and create a real provider from it (the vault is already initialized).\n const savedProviders = config.providers ?? {};\n const firstKey = Object.keys(savedProviders)[0];\n if (firstKey) {\n const firstProvider = expectDefined(savedProviders[firstKey]);\n try {\n provider = makeProviderFromConfig(firstKey, {\n ...firstProvider,\n type: firstKey,\n family: firstProvider.family,\n apiKey: firstProvider.apiKey,\n });\n console.log('[WebUI] Using saved provider:', firstKey);\n } catch (err) {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.provider_stub_create_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n throw err;\n }\n } else {\n throw new Error(\n 'No provider configured. Run `wrongstack init` first, or configure via the WebUI.',\n );\n }\n }\n\n // Context\n const context = new Context({\n systemPrompt,\n provider,\n session,\n signal: new AbortController().signal,\n tokenCounter,\n cwd: workingDir,\n projectRoot,\n model: config.model,\n });\n const initialContextPolicy = resolveContextWindowPolicy(config.context);\n context.meta['contextWindowMode'] = initialContextPolicy.id;\n context.meta['contextWindowPolicy'] = initialContextPolicy;\n\n // ── Seed runtime prefs from config ──────────────────────────────────────\n // The settings panel reads prefs via `prefs.get` → context.meta. Without\n // this seed the snapshot is empty and every browser shows localStorage\n // defaults (autonomy \"off\", etc.) regardless of what config.json says.\n // Mirrors the CLI's getSettings() mapping so TUI and WebUI agree.\n {\n const autonomyCfg = (config.autonomy ?? {}) as Record<string, unknown>;\n const rawMode = autonomyCfg['defaultMode'];\n context.meta['autonomy'] =\n rawMode === 'suggest' || rawMode === 'auto' ? rawMode : 'off';\n context.meta['autonomyDelayMs'] = (autonomyCfg['autoProceedDelayMs'] as number) ?? 45_000;\n context.meta['autoProceedMaxIterations'] =\n (autonomyCfg['autoProceedMaxIterations'] as number) ?? 50;\n context.meta['yolo'] = (autonomyCfg['yolo'] as boolean) ?? config.yolo ?? false;\n context.meta['chime'] = (autonomyCfg['chime'] as boolean) ?? false;\n context.meta['confirmExit'] = autonomyCfg['confirmExit'] !== false;\n context.meta['streamFleet'] = autonomyCfg['streamFleet'] !== false;\n context.meta['enhanceEnabled'] = (autonomyCfg['enhance'] as boolean) ?? true;\n context.meta['enhanceDelayMs'] = (autonomyCfg['enhanceDelayMs'] as number) ?? 60_000;\n context.meta['enhanceLanguage'] = (autonomyCfg['enhanceLanguage'] as string) ?? 'original';\n context.meta['nextPrediction'] = config.nextPrediction ?? false;\n context.meta['featureMcp'] = config.features.mcp !== false;\n context.meta['featurePlugins'] = config.features.plugins !== false;\n context.meta['featureMemory'] = config.features.memory !== false;\n context.meta['featureSkills'] = config.features.skills !== false;\n context.meta['featureModelsRegistry'] = config.features.modelsRegistry !== false;\n context.meta['indexOnStart'] = config.indexing?.onSessionStart !== false;\n context.meta['contextAutoCompact'] = config.context?.autoCompact !== false;\n context.meta['contextStrategy'] = config.context?.strategy ?? 'hybrid';\n context.meta['logLevel'] = config.log?.level ?? 'info';\n context.meta['auditLevel'] = config.session?.auditLevel ?? 'standard';\n context.meta['maxIterations'] = config.tools?.maxIterations ?? 500;\n }\n\n /** Pref keys exposed to the settings panel via prefs.get / prefs.updated. */\n const PREF_KEYS = [\n 'autonomy', 'autonomyDelayMs', 'autoProceedMaxIterations', 'yolo', 'maxIterations',\n 'chime', 'confirmExit', 'streamFleet', 'nextPrediction',\n 'enhanceEnabled', 'enhanceDelayMs', 'enhanceLanguage',\n 'featureMcp', 'featurePlugins', 'featureMemory', 'featureSkills',\n 'featureModelsRegistry', 'indexOnStart',\n 'contextAutoCompact', 'contextStrategy', 'logLevel', 'auditLevel',\n ] as const;\n\n const prefSnapshot = (): Record<string, unknown> => {\n const snapshot: Record<string, unknown> = {};\n for (const k of PREF_KEYS) {\n if (k in context.meta) snapshot[k] = context.meta[k];\n }\n return snapshot;\n };\n\n /**\n * Persist pref changes into the global config.json — the SAME keys the\n * TUI settings picker writes — so a toggle made in the browser survives\n * restarts and is visible to the CLI/TUI (and vice versa on next boot).\n * Best-effort and serialized behind configWriteLock (shared with the\n * provider/key handlers); failures log but never break the WS reply.\n */\n const persistPrefsToConfig = async (payload: Record<string, unknown>): Promise<void> => {\n const write = async (): Promise<void> => {\n let raw: string;\n try {\n raw = await fs.readFile(globalConfigPath, 'utf8');\n } catch {\n raw = '{}';\n }\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // Refuse to overwrite a corrupt-but-existing config.\n logger.warn(`prefs: refusing to overwrite corrupt config at ${globalConfigPath}`);\n return;\n }\n const decrypted = decryptConfigSecrets(parsed, vault) as Record<string, unknown>;\n\n const autonomyCfg = (decrypted.autonomy as Record<string, unknown>) ?? {};\n let autonomyTouched = false;\n const setAutonomy = (key: string, val: unknown): void => {\n autonomyCfg[key] = val;\n autonomyTouched = true;\n };\n if (\n typeof payload['autonomy'] === 'string' &&\n ['off', 'suggest', 'auto'].includes(payload['autonomy'])\n ) {\n setAutonomy('defaultMode', payload['autonomy']);\n }\n if (typeof payload['autonomyDelayMs'] === 'number') setAutonomy('autoProceedDelayMs', payload['autonomyDelayMs']);\n if (typeof payload['autoProceedMaxIterations'] === 'number') setAutonomy('autoProceedMaxIterations', payload['autoProceedMaxIterations']);\n if (typeof payload['yolo'] === 'boolean') setAutonomy('yolo', payload['yolo']);\n if (typeof payload['chime'] === 'boolean') setAutonomy('chime', payload['chime']);\n if (typeof payload['confirmExit'] === 'boolean') setAutonomy('confirmExit', payload['confirmExit']);\n if (typeof payload['streamFleet'] === 'boolean') setAutonomy('streamFleet', payload['streamFleet']);\n if (typeof payload['enhanceEnabled'] === 'boolean') setAutonomy('enhance', payload['enhanceEnabled']);\n if (typeof payload['enhanceDelayMs'] === 'number') setAutonomy('enhanceDelayMs', payload['enhanceDelayMs']);\n if (typeof payload['enhanceLanguage'] === 'string') setAutonomy('enhanceLanguage', payload['enhanceLanguage']);\n if (autonomyTouched) decrypted.autonomy = autonomyCfg;\n\n if (typeof payload['nextPrediction'] === 'boolean') decrypted.nextPrediction = payload['nextPrediction'];\n\n const FEATURE_MAP: Record<string, string> = {\n featureMcp: 'mcp',\n featurePlugins: 'plugins',\n featureMemory: 'memory',\n featureSkills: 'skills',\n featureModelsRegistry: 'modelsRegistry',\n };\n for (const [prefKey, cfgKey] of Object.entries(FEATURE_MAP)) {\n if (typeof payload[prefKey] === 'boolean') {\n const feats = (decrypted.features as Record<string, unknown>) ?? {};\n feats[cfgKey] = payload[prefKey];\n decrypted.features = feats;\n }\n }\n\n if (typeof payload['contextAutoCompact'] === 'boolean' || typeof payload['contextStrategy'] === 'string') {\n const ctxCfg = (decrypted.context as Record<string, unknown>) ?? {};\n if (typeof payload['contextAutoCompact'] === 'boolean') ctxCfg.autoCompact = payload['contextAutoCompact'];\n if (typeof payload['contextStrategy'] === 'string') ctxCfg.strategy = payload['contextStrategy'];\n decrypted.context = ctxCfg;\n }\n if (typeof payload['logLevel'] === 'string') {\n const logCfg = (decrypted.log as Record<string, unknown>) ?? {};\n logCfg.level = payload['logLevel'];\n decrypted.log = logCfg;\n }\n if (typeof payload['auditLevel'] === 'string') {\n const sessionCfg = (decrypted.session as Record<string, unknown>) ?? {};\n sessionCfg.auditLevel = payload['auditLevel'];\n decrypted.session = sessionCfg;\n }\n if (typeof payload['indexOnStart'] === 'boolean') {\n const indexingCfg = (decrypted.indexing as Record<string, unknown>) ?? {};\n indexingCfg.onSessionStart = payload['indexOnStart'];\n decrypted.indexing = indexingCfg;\n }\n if (typeof payload['maxIterations'] === 'number') {\n const toolsCfg = (decrypted.tools as Record<string, unknown>) ?? {};\n toolsCfg.maxIterations = payload['maxIterations'];\n decrypted.tools = toolsCfg;\n }\n\n const encrypted = encryptConfigSecrets(decrypted, vault);\n await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 0o600 });\n };\n const next = configWriteLock.then(write);\n configWriteLock = next.then(\n () => undefined,\n () => undefined,\n );\n try {\n await next;\n } catch (err) {\n logger.warn(`prefs: failed to persist to config: ${errMessage(err)}`);\n }\n };\n\n // Pipelines\n const pipelines = createDefaultPipelines();\n // Collaboration bus — process-singleton pause/resume signal. The\n // middleware below hooks it into the toolCall pipeline so a\n // `controller` participant can halt the agent before the next tool\n // call (Phase 3 of idea #13). The same bus instance is shared with\n // the CollaborationWebSocketHandler so client pause/resume requests\n // are routed to the kernel.\n const collabBus = new CollaborationBus();\n // prepend (not use) — the pause check must run first, before any\n // permission/retry middleware that would otherwise proceed.\n const collabPause = collabPauseMiddleware(collabBus, { logger });\n Object.defineProperty(collabPause, 'name', { value: 'collab-pause' });\n pipelines.toolCall.prepend(collabPause as never);\n // Phase 4 — collab-inject. Installed AFTER collab-pause so the\n // controller can pause + inject before the next tool runs. The\n // middleware checks the bus's injection queue and splices a\n // synthetic tool_result when a controller has queued one for\n // the current toolUse.id.\n const collabInject = collabInjectMiddleware(collabBus, { logger });\n Object.defineProperty(collabInject, 'name', { value: 'collab-inject' });\n pipelines.toolCall.prepend(collabInject as never);\n // Compactor — honors config.context.strategy ('hybrid' default, lossless\n // rules; 'intelligent'/'selective' resolve their provider from ctx at\n // compact()-time). eliseThreshold is a TOKEN COUNT (not a fraction).\n const compactor = createStrategyCompactor({\n strategy: config.context?.strategy,\n preserveK: config.context?.preserveK ?? 10,\n eliseThreshold: config.context?.eliseThreshold ?? 2000,\n summarizerModel: config.context?.summarizerModel,\n llmSelector: config.context?.llmSelector,\n });\n\n // Auto-compaction\n let autoCompactor: AutoCompactionMiddleware | undefined;\n if (config.context?.autoCompact !== false) {\n // Priority: explicit override → models.dev per-model window → family default.\n // The catalog lookup matters for openai-compatible providers (OpenRouter,\n // Groq, …) whose family default is 0; without it auto-compaction would be\n // disabled even though the model has a real published window. Mirrors\n // updateAutoCompactionMaxContext below.\n let effectiveMaxContext = config.context?.effectiveMaxContext ?? 0;\n if (!effectiveMaxContext) {\n try {\n const m = await modelsRegistry.getModel(provider.id, context.model);\n effectiveMaxContext = m?.capabilities?.maxContext ?? 0;\n } catch {\n // best-effort: fall through to provider capability\n }\n }\n if (!effectiveMaxContext) effectiveMaxContext = provider.capabilities.maxContext;\n autoCompactor = new AutoCompactionMiddleware(\n compactor,\n effectiveMaxContext,\n (ctx) =>\n estimateRequestTokensCalibrated(\n ctx.messages,\n ctx.systemPrompt,\n ctx.tools ?? [],\n `${ctx.provider?.id ?? 'unknown'}/${ctx.model}`,\n ).total,\n {\n warn: initialContextPolicy.thresholds.warn,\n soft: initialContextPolicy.thresholds.soft,\n hard: initialContextPolicy.thresholds.hard,\n },\n {\n events,\n aggressiveOn: initialContextPolicy.aggressiveOn,\n policyProvider: (ctx) => {\n const policy = ctx.meta['contextWindowPolicy'];\n return policy && typeof policy === 'object'\n ? (policy as ReturnType<typeof resolveContextWindowPolicy>)\n : initialContextPolicy;\n },\n },\n );\n pipelines.contextWindow.use({ name: 'AutoCompaction', handler: autoCompactor.handler() });\n }\n\n /** Refresh AutoCompactionMiddleware denominator when the active model changes. */\n async function updateAutoCompactionMaxContext(newProvider: Provider): Promise<void> {\n if (!autoCompactor) return;\n let newMaxContext = config.context?.effectiveMaxContext ?? newProvider.capabilities.maxContext;\n try {\n const m = await modelsRegistry.getModel(newProvider.id, context.model);\n newMaxContext = m?.capabilities?.maxContext ?? newMaxContext;\n } catch {\n // best-effort: use provider capability\n }\n autoCompactor.setMaxContext(newMaxContext);\n }\n\n // Agent\n const secretScrubber = container.resolve(TOKENS.SecretScrubber);\n const renderer = container.has(TOKENS.Renderer) ? container.resolve(TOKENS.Renderer) : undefined;\n const permissionPolicy = container.resolve(TOKENS.PermissionPolicy);\n const toolExecutor = new ToolExecutor(toolRegistry, {\n permissionPolicy,\n secretScrubber,\n renderer,\n events,\n confirmAwaiter: undefined,\n iterationTimeoutMs: config.tools?.iterationTimeoutMs ?? DEFAULT_TOOLS_CONFIG.iterationTimeoutMs,\n perIterationOutputCapBytes:\n config.tools?.perIterationOutputCapBytes ?? DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,\n tracer: undefined,\n });\n\n const agent = new Agent({\n container,\n tools: toolRegistry,\n providers: providerRegistry,\n events,\n pipelines,\n context,\n maxIterations: config.tools?.maxIterations ?? DEFAULT_TOOLS_CONFIG.maxIterations,\n iterationTimeoutMs: config.tools?.iterationTimeoutMs ?? DEFAULT_TOOLS_CONFIG.iterationTimeoutMs,\n executionStrategy:\n config.tools?.defaultExecutionStrategy ?? DEFAULT_TOOLS_CONFIG.defaultExecutionStrategy,\n perIterationOutputCapBytes:\n config.tools?.perIterationOutputCapBytes ?? DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,\n confirmAwaiter: undefined,\n toolExecutor,\n });\n console.log('[WebUI] Agent initialized');\n\n // ── Brain — policy → LLM tiered decision layer ─────────────────────────\n // Same positioning as the CLI: one Brain per process at\n // TOKENS.BrainArbiter. The WebUI has no human-escalation prompt yet, so\n // the chain stops at the LLM tier — `ask_human` decisions surface to the\n // browser as `brain.event` WS messages and the caller's fallback applies.\n const brainSettings: { maxAutoRisk: BrainAutoRisk } = { maxAutoRisk: 'medium' };\n // Lazy wrapper so the LLM tier always sees the LIVE provider/model —\n // both are swapped at runtime via the settings panel.\n const autonomousBrain: BrainArbiter = {\n decide: (request) =>\n createAutonomyBrain({\n provider,\n model: context.model,\n maxAutoRisk: 'all', // the tiered ceiling gates risk — keep inner permissive\n }).decide(request),\n };\n const brain = new ObservableBrainArbiter(\n createTieredBrainArbiter({\n policy: new DefaultBrainArbiter(),\n autonomous: autonomousBrain,\n getMaxAutoRisk: () => brainSettings.maxAutoRisk,\n }),\n events,\n );\n container.bind(TOKENS.BrainArbiter, () => brain);\n\n // Self-activation: watch for tool-failure streaks / error storms and\n // steer this session's leader via the shared project mailbox. `session`\n // is mutable (swapped on /new and resume) — read it at send time so the\n // steer always targets the LIVE session's leader identity.\n const brainMailbox = new GlobalMailbox(wpaths.projectDir, events);\n const brainMonitor = new BrainMonitor({\n events,\n brain,\n intervene: async ({ subject, body }) => {\n const tag = mailboxSessionTag(session.id);\n await brainMailbox.send({\n from: `brain@${tag}`,\n to: `leader@${tag}`,\n type: 'steer',\n subject,\n body,\n priority: 'high',\n });\n },\n });\n brainMonitor.start();\n console.log('[WebUI] Brain initialized (tiered policy → LLM, monitor active)');\n\n // Decision log for the /brain command — last 20 decisions, newest last.\n const brainLog: Array<{ at: number; kind: string; question: string; outcome: string }> = [];\n const pushBrainLog = (entry: (typeof brainLog)[number]) => {\n brainLog.push(entry);\n if (brainLog.length > 20) brainLog.shift();\n };\n events.on('brain.decision_answered', (e) =>\n pushBrainLog({\n at: e.at,\n kind: 'answered',\n question: e.request.question,\n outcome: e.decision.type === 'answer' ? (e.decision.optionId ?? e.decision.text) : '',\n }),\n );\n events.on('brain.decision_ask_human', (e) =>\n pushBrainLog({ at: e.at, kind: 'ask_human', question: e.request.question, outcome: 'needs human judgement' }),\n );\n events.on('brain.decision_denied', (e) =>\n pushBrainLog({\n at: e.at,\n kind: 'denied',\n question: e.request.question,\n outcome: e.decision.type === 'deny' ? e.decision.reason : '',\n }),\n );\n events.on('brain.intervention', (e) =>\n pushBrainLog({\n at: e.at,\n kind: 'intervention',\n question: e.request.question,\n outcome: e.intervened ? 'steered the agent' : 'observed (no action)',\n }),\n );\n\n // AutoPhase handler — manages AutoPhaseRunner lifecycle via WS messages.\n // Stored under the per-project autophase dir (not the shared SDD task-graphs).\n const autoPhaseHandler = new AutoPhaseWebSocketHandler(\n agent,\n context,\n logger,\n wpaths.projectAutophase,\n events,\n projectRoot,\n );\n\n // Worktree handler — subscribes to the shared EventBus `worktree.*` events\n // and streams live swim-lane / DAG state to connected clients.\n const worktreeHandler = new WorktreeWebSocketHandler(events, logger);\n\n // Collaboration handler — Phase 1 of idea #13. Lets a second client\n // (e.g. a senior dev) join an active agent run as a read-only\n // observer and watch a live mirror of kernel events. Annotated and\n // controller roles land in Phase 2/3. The session reader enables\n // replay-on-join for late observers.\n const collabHandler = new CollaborationWebSocketHandler(\n events,\n logger,\n sessionReader,\n annotationsStore,\n collabBus,\n );\n\n // Helper: build the rich session.start payload from current runtime state.\n // Centralised so initial connect, post-/new, and post-model.switch all\n // broadcast the same shape — frontend treats this as the single source of\n // truth for everything in the status bar (model, context window, project).\n async function sessionStartPayload(): Promise<{\n sessionId: string;\n model: string;\n provider: string;\n maxContext: number;\n /** USD per 1M input tokens (0 if unknown / free). */\n inputCost: number;\n /** USD per 1M output tokens. */\n outputCost: number;\n /** USD per 1M cache-read tokens. */\n cacheReadCost: number;\n projectName: string;\n projectRoot: string;\n cwd: string;\n mode: string;\n contextMode: string;\n }> {\n let maxContext = 0;\n let inputCost = 0;\n let outputCost = 0;\n let cacheReadCost = 0;\n try {\n const m = await modelsRegistry.getModel(config.provider, config.model);\n maxContext = m?.capabilities?.maxContext ?? 0;\n // Fall back to the provider's raw model data from the registry when the\n // resolved model has no maxContext (e.g. a user-defined or API-proxied\n // model that wasn't in the models.dev catalog). DefaultModelsRegistry\n // exposes getProvider() which gives us the model's limit.context directly.\n if (!maxContext) {\n try {\n const provider = await (\n modelsRegistry as { getProvider(id: string): Promise<{ models: Array<{ id: string; limit?: { context?: number } }> } | undefined> }\n ).getProvider(config.provider);\n const rawModel = provider?.models.find((mod) => mod.id === config.model);\n maxContext = rawModel?.limit?.context ?? 0;\n } catch {\n /* best-effort — leave maxContext at whatever the registry set it */\n }\n }\n // models.dev pricing is dollars per 1M tokens; some providers omit the\n // field for free/unmetered plans (e.g. minimax-coding-plan) — in that\n // case we report 0 and the cost chip just stays at $0.\n const rates = getCostRates(m);\n inputCost = rates.input;\n outputCost = rates.output;\n cacheReadCost = rates.cacheRead;\n } catch {\n // best-effort\n }\n return {\n sessionId: session.id,\n model: config.model,\n provider: config.provider,\n maxContext,\n inputCost,\n outputCost,\n cacheReadCost,\n projectName: path.basename(projectRoot) || projectRoot,\n projectRoot,\n cwd: workingDir,\n mode: modeId,\n contextMode: String(context.meta['contextWindowMode'] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID),\n };\n }\n\n // WebSocket server(s).\n //\n // When the user keeps the default loopback bind (127.0.0.1), we ALSO open a\n // second listener on ::1 (IPv6 loopback). Reason: Chrome/Edge on Windows\n // resolve `localhost` to `[::1]` before `127.0.0.1`, so a single v4-only\n // bind causes \"ws disconnect hep\" — clients hammer the v6 socket, get\n // ECONNREFUSED, fall back to v4 inconsistently. Listening on both v4 and v6\n // loopback keeps the connection scope \"this machine only\" while removing\n // the resolution-order coin flip.\n //\n // When the user explicitly sets WS_HOST (e.g. 0.0.0.0 or a LAN IP), we\n // respect that choice exactly and don't add a second listener.\n // Generate a random WS auth token so only callers that know the token\n // can connect. Printed to console on startup; the frontend reads it from\n // the URL query param `?token=...`. Without a token, any client on the\n // network can connect and send `user_message`/`key.add`/`model.switch`.\n const wsToken = generateAuthToken();\n // Token is sent to clients via session.start payload — log without any\n // token characters to prevent search-space reduction for brute-force attacks.\n console.log('[WebUI] WS auth token generated (redacted from logs)');\n\n // CSWSH guard + token auth: when the user exposes the socket beyond loopback,\n // require the shared token; loopback connections bootstrap without one. The\n // policy (DNS-rebinding Host guard, constant-time token compare, loopback\n // bootstrap) lives in ./ws-auth.ts as pure functions — this closure just\n // pulls the relevant fields off the incoming request and delegates.\n const verifyClient = (info: {\n origin: string;\n secure: boolean;\n req: import('node:http').IncomingMessage;\n }) =>\n verifyWsClient({\n origin: info.origin,\n url: info.req.url ?? '',\n hostHeader: info.req.headers.host,\n remoteAddress: info.req.socket.remoteAddress,\n // C-2 fix: accept the token via the HttpOnly cookie set by\n // `/ws-auth` (preferred) OR the URL query param (non-browser\n // fallback). The cookie path closes the C-598 query-string\n // exposure class.\n cookieHeader: info.req.headers.cookie,\n wsHost,\n expectedToken: wsToken,\n });\n // Cap inbound frame size (8 MiB) so a single oversized message can't exhaust\n // memory. Agent messages are small; large pastes/attachments stay well under.\n const WS_MAX_PAYLOAD = 8 * 1024 * 1024;\n const wssPrimary = new WebSocketServer({\n port: wsPort,\n host: wsHost,\n verifyClient,\n maxPayload: WS_MAX_PAYLOAD,\n } as ConstructorParameters<typeof WebSocketServer>[0]);\n const wssSecondary =\n wsHost === '127.0.0.1'\n ? new WebSocketServer({\n port: wsPort,\n host: '::1',\n verifyClient,\n maxPayload: WS_MAX_PAYLOAD,\n } as ConstructorParameters<typeof WebSocketServer>[0])\n : null;\n const clients = new Map<WebSocket, ConnectedClient>();\n\n // ── Subscribe to working directory changes from the CLI ──────────────\n // When ctx.setWorkingDir() is called from the CLI (e.g. /wd, /cd, or\n // the set_working_dir tool), update the server's workingDir reference\n // and broadcast to all connected WebUI clients so the file explorer\n // and the WorkingDirChip UI stay in sync.\n context.onWorkingDirChanged((newDir) => {\n workingDir = newDir;\n broadcast(clients, {\n type: 'working_dir.changed',\n payload: { cwd: newDir, projectRoot },\n });\n });\n\n // ── Eternal-autonomy iteration broadcast (PR 4 of Phase 2) ─────────\n // When the CLI passes `opts.subscribeEternalIteration`, hook the\n // returned observer into a WS broadcast so every connected client\n // gets a live stream of `JournalEntry` items as the engine ticks.\n // The disposer is captured and invoked on shutdown() so the CLI's\n // engine subscription is properly torn down with the webui.\n let eternalSubscription: { dispose: () => void } | null = null;\n if (opts.subscribeEternalIteration) {\n eternalSubscription = createEternalSubscription(\n opts.subscribeEternalIteration,\n broadcast,\n () => clients,\n );\n }\n\n // Per-connection message rate limiting: 60 messages per 60-second window.\n // Exceeding clients are temporarily blocked to prevent flooding.\n // Uses sessionId as the key once connected, falling back to ws for\n // pre-auth messages — prevents connection-reuse bypass.\n // Rate limit OFF by default (counted pings/list calls too and tripped during\n // normal use). Opt in via WEBUI_RATE_LIMIT=<messages-per-60s> for LAN exposure.\n const RATE_LIMIT_MESSAGES = Number.parseInt(process.env['WEBUI_RATE_LIMIT'] ?? '0', 10);\n const RATE_LIMIT_WINDOW_MS = 60_000;\n const rateLimits = new Map<string, { count: number; resetAt: number }>();\n\n function checkRateLimit(ws: WebSocket, client: ConnectedClient): boolean {\n if (RATE_LIMIT_MESSAGES <= 0) return true; // disabled\n const now = Date.now();\n // Prefer the per-client authenticated sessionId; fall back to the\n // WebSocket identity for pre-auth messages before session.start.\n const key = client.sessionId ?? String(ws);\n const limit = rateLimits.get(key);\n if (!limit || now > limit.resetAt) {\n rateLimits.set(key, { count: 1, resetAt: now + RATE_LIMIT_WINDOW_MS });\n return true;\n }\n if (limit.count >= RATE_LIMIT_MESSAGES) return false;\n limit.count++;\n return true;\n }\n\n /** Holds the AbortController for the currently in-flight agent.run().\n * Non-null while the agent is running; guarded at the user_message\n * handler to prevent concurrent runs that would corrupt shared state\n * (context, agent, tokenCounter). A second user_message while running\n * is answered with an inline error instead of being queued — the\n * caller should wait for run.result. */\n let runLock: AbortController | null = null;\n\n console.log(\n `[WebUI] WebSocket server running on ws://${wsHost}:${wsPort}` +\n (wssSecondary ? ` (and ws://[::1]:${wsPort})` : ''),\n );\n\n // Pending permission confirmations. When the agent emits\n // tool.confirm_needed, we store the resolve function here keyed by\n // toolUseId. When the client sends tool.confirm_result back, we look\n // it up and resolve — unblocking the agent loop.\n const pendingConfirms = new Map<string, (d: 'yes' | 'no' | 'always' | 'deny') => void>();\n\n const handleConnection = (ws: WebSocket): void => {\n const client: ConnectedClient = { ws, sessionId: session.id, connectedAt: Date.now() };\n clients.set(ws, client);\n\n // sessionStartPayload handles errors internally; no explicit catch needed.\n // Adding a catch would be defensive but sessionStartPayload already has try-catch.\n void sessionStartPayload()\n .then((payload) => {\n send(ws, { type: 'session.start', payload });\n })\n .catch((err) => {\n // Log at warn level since sessionStartPayload should rarely fail.\n // This prevents silent failures if internal error handling changes.\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.session_start_payload_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n });\n\n // Register this client with the AutoPhase handler so it receives phase events\n autoPhaseHandler.addClient(ws);\n // …and the worktree handler for live isolation lanes.\n worktreeHandler.addClient(ws);\n // …and the collaboration handler for read-only session observation.\n collabHandler.addClient(ws);\n\n ws.on('message', async (data) => {\n if (!checkRateLimit(ws, client)) {\n send(ws, {\n type: 'error',\n payload: {\n phase: 'rate_limit',\n message: 'Too many messages. Please wait before sending more.',\n },\n });\n return;\n }\n try {\n // Prototype pollution guard: reject messages whose root-level payload\n // contains __proto__, constructor, or prototype keys. These could\n // cause prototype pollution via Object.assign({}, payload) or\n // spread {...payload}. The top-level check below catches the\n // dangerous keys; nested payload sub-objects are low-risk since\n // handlers don't do deep property merges.\n const rawObj = JSON.parse(data.toString());\n if (typeof rawObj === 'object' && rawObj !== null) {\n const obj = rawObj as Record<string, unknown>;\n if ('__proto__' in obj || 'constructor' in obj || 'prototype' in obj) {\n send(ws, {\n type: 'error',\n payload: { phase: 'parse', message: 'Invalid message object' },\n });\n } else {\n await handleMessage(ws, client, rawObj as WSClientMessage);\n }\n } else {\n // Non-object JSON (array, string, number…) — pass through\n await handleMessage(ws, client, rawObj as unknown as WSClientMessage);\n }\n } catch (err) {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.ws_message_parse_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n }\n });\n\n ws.on('close', () => {\n clients.delete(ws);\n rateLimits.delete(String(ws));\n // If the client disconnects while a permission prompt is pending,\n // resolve all pending confirms with 'no' so the agent loop doesn't\n // hang forever waiting for a response that will never come.\n if (pendingConfirms.size > 0) {\n for (const [id, resolve] of pendingConfirms) {\n resolve('no');\n pendingConfirms.delete(id);\n }\n }\n });\n\n ws.on('error', (err) => {\n // Without this handler an errored socket would crash the process.\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.client_socket_error',\n message: err.message,\n timestamp: new Date().toISOString(),\n }));\n });\n };\n\n // Audit-level-aware session log bridge — persists tool/error/provider\n // events to the session JSONL with the same contract as the CLI. The\n // getter form resolves the CURRENT writer on every append so events\n // follow session.new / session.resume / projects.select swaps.\n const sessionLogging = resolveSessionLoggingConfig(\n config as unknown as Parameters<typeof resolveSessionLoggingConfig>[0],\n );\n const sessionBridge = createSessionEventBridge(\n () => context.session ?? session,\n sessionLogging.auditLevel,\n { sampling: sessionLogging.sampling },\n );\n\n let eventsArmed = false;\n const armOnce = (label: string): void => {\n if (eventsArmed) return;\n eventsArmed = true;\n console.log(`[WebUI] Backend ready (${label})`);\n setupEvents({ events, broadcast, clients, config, context, pendingConfirms, globalConfigPath, sessionBridge });\n };\n\n wssPrimary.on('listening', () => armOnce(`${wsHost}:${wsPort}`));\n wssPrimary.on('connection', handleConnection);\n wssPrimary.on('error', (err) => {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.ws_server_error',\n host: wsHost,\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n });\n\n if (wssSecondary) {\n wssSecondary.on('listening', () => armOnce(`::1:${wsPort}`));\n wssSecondary.on('connection', handleConnection);\n wssSecondary.on('error', (err: NodeJS.ErrnoException) => {\n // Best-effort secondary: if IPv6 loopback isn't available on this host\n // (e.g. disabled in OS), just log and continue. Primary v4 is enough.\n if (err.code === 'EAFNOSUPPORT' || err.code === 'EADDRNOTAVAIL') {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.ipv6_unavailable',\n code: err.code,\n message: err.message,\n timestamp: new Date().toISOString(),\n }));\n } else {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.ws_server_error',\n host: '::1',\n message: err.message,\n timestamp: new Date().toISOString(),\n }));\n }\n });\n }\n\n // ── Project manifest helpers ──────────────────────────────────────────\n\n interface ProjectEntry {\n name: string;\n root: string;\n slug: string;\n lastSeen?: string | undefined;\n createdAt?: string | undefined;\n /** Working directory of the most recent session (may differ from root). */\n lastWorkingDir?: string | undefined;\n }\n\n interface ProjectsManifest {\n projects: ProjectEntry[];\n }\n\n /**\n * Idempotent manifest registration (mirrors the CLI's\n * touchProjectInManifest): create the projects.json entry when missing,\n * refresh lastSeen/lastWorkingDir when present.\n */\n async function touchProjectEntry(root: string, workDir?: string): Promise<void> {\n const resolved = path.resolve(root);\n const manifest = await loadManifest(globalConfigPath);\n const now = new Date().toISOString();\n const existing = manifest.projects.find((p) => path.resolve(p.root) === resolved);\n if (existing) {\n existing.lastSeen = now;\n if (workDir) existing.lastWorkingDir = path.resolve(workDir);\n } else {\n manifest.projects.push({\n name: path.basename(resolved),\n root: resolved,\n slug: generateProjectSlug(resolved),\n createdAt: now,\n lastSeen: now,\n lastWorkingDir: workDir ? path.resolve(workDir) : undefined,\n });\n }\n await saveManifest(manifest, globalConfigPath);\n await ensureProjectDataDir(generateProjectSlug(resolved), globalConfigPath);\n }\n\n function projectsJsonPath(globalConfigPath: string): string {\n const base = path.dirname(globalConfigPath);\n return path.join(base, 'projects.json');\n }\n\n async function loadManifest(globalConfigPath: string): Promise<ProjectsManifest> {\n try {\n const raw = await fs.readFile(projectsJsonPath(globalConfigPath), 'utf8');\n const parsed = JSON.parse(raw) as ProjectsManifest;\n return { projects: parsed.projects ?? [] };\n } catch {\n return { projects: [] };\n }\n }\n\n async function saveManifest(manifest: ProjectsManifest, globalConfigPath: string): Promise<void> {\n const file = projectsJsonPath(globalConfigPath);\n await fs.mkdir(path.dirname(file), { recursive: true });\n await fs.writeFile(file, JSON.stringify(manifest, null, 2), 'utf8');\n }\n\n function generateProjectSlug(rootPath: string): string {\n // Canonical derivation — must match wstack-paths/projectSlug exactly or\n // the WebUI and CLI would key the same project under different dirs.\n return projectSlug(rootPath);\n }\n\n async function ensureProjectDataDir(slug: string, globalConfigPath: string): Promise<string> {\n const base = path.dirname(globalConfigPath);\n const dir = path.join(base, 'projects', slug);\n await fs.mkdir(dir, { recursive: true });\n return dir;\n }\n\n async function handleMessage(\n ws: WebSocket,\n _client: ConnectedClient,\n msg: WSClientMessage,\n ): Promise<void> {\n switch (msg.type) {\n // Collaboration messages short-circuit the user/agent flow.\n // They don't touch runLock, the agent loop, or the message queue —\n // they're pure transport for the live observer mirror.\n case 'collab.join':\n case 'collab.leave':\n case 'collab.annotate':\n case 'collab.resolve': {\n collabHandler.handleMessage(ws, msg as { type: string; payload?: unknown | undefined });\n return;\n }\n case 'user_message': {\n const content = (msg as { payload: { content: string } }).payload.content;\n\n // Guard against concurrent agent runs — a second user_message while\n // the agent is already processing would kick off two agent.run()\n // calls on the same shared context/agent, leading to corrupted\n // state (duplicate tool bubbles, mixed text_delta streams, token\n // counter undercount). Reject with an inline error; the frontend\n // should wait for run.result before sending the next message.\n if (runLock) {\n send(ws, {\n type: 'error',\n payload: {\n phase: 'user_message',\n message: 'Agent is already processing a request. Wait for the current run to finish.',\n },\n });\n break;\n }\n\n runLock = new AbortController();\n // Capture so the finally block only clears its own lock — a\n // second race could set a new runLock between await and finally.\n const thisRun = runLock;\n\n try {\n // Read maxIterations from context.meta so the webui settings\n // panel can adjust the cap dynamically without restarting.\n const maxIt = typeof context.meta['maxIterations'] === 'number'\n ? context.meta['maxIterations']\n : undefined;\n const result = await agent.run(content, { signal: thisRun.signal, maxIterations: maxIt });\n send(ws, {\n type: 'run.result',\n payload: {\n status: result.status,\n iterations: result.iterations,\n finalText: result.finalText,\n error: result.error\n ? {\n code: result.error.code,\n message: result.error.message,\n recoverable: result.error.recoverable,\n }\n : undefined,\n },\n });\n } catch (err) {\n send(ws, {\n type: 'error',\n payload: {\n phase: 'agent.run',\n message: errMessage(err),\n },\n });\n } finally {\n // Only clear runLock if it's still ours — otherwise we'd wipe a\n // newer run's controller set after we returned.\n if (runLock === thisRun) {\n runLock = null;\n }\n }\n break;\n }\n\n case 'tool.confirm_result': {\n const { id, decision } = (\n msg as { payload: { id: string; decision: 'yes' | 'no' | 'always' | 'deny' } }\n ).payload;\n const resolve = pendingConfirms.get(id);\n if (resolve) {\n pendingConfirms.delete(id);\n resolve(decision);\n }\n break;\n }\n\n case 'abort':\n runLock?.abort();\n broadcast(clients, { type: 'error', payload: { phase: 'abort', message: 'User aborted' } });\n break;\n\n case 'ping':\n send(ws, { type: 'pong', payload: {} });\n break;\n\n case 'session.new': {\n // Truly fresh chat: new on-disk session AND reset every piece of\n // in-memory state that survived (messages history, todos, read-file\n // tracking, token totals). Otherwise the model still sees the prior\n // turns even though the UI looks empty — that's the \"ghost context\"\n // bug. After this, the next user message goes out as turn 1 with no\n // prior history.\n //\n // Finalize the writer we are leaving (session_end + close) — same\n // pattern as projects.select/shutdown. Without it the old JSONL\n // never ends cleanly, the summary sidecar/index entry are never\n // written, and the file handle leaks for the daemon's lifetime.\n try {\n await session.append({\n type: 'session_end',\n ts: new Date().toISOString(),\n usage: tokenCounter.total(),\n });\n await session.close();\n } catch {\n // best-effort\n }\n session = await sessionStore.create({\n id: '',\n title: '',\n model: config.model,\n provider: config.provider,\n });\n context.session = session;\n context.state.replaceMessages([]);\n context.state.replaceTodos([]);\n context.readFiles.clear();\n context.fileMtimes.clear();\n tokenCounter.reset();\n sessionStartedAt = Date.now();\n broadcast(clients, { type: 'session.start', payload: await sessionStartPayload() });\n break;\n }\n\n case 'context.clear': {\n // Same in-memory wipe as session.new, but reuses the current session\n // file (so the JSONL still has the history for audit / replay). The\n // user wants a clean slate on the model side; the disk record stays.\n context.state.replaceMessages([]);\n context.state.replaceTodos([]);\n context.readFiles.clear();\n context.fileMtimes.clear();\n tokenCounter.reset();\n sendResult(ws, true, 'Context cleared');\n broadcast(clients, {\n type: 'session.start',\n payload: { ...(await sessionStartPayload()), reset: true },\n });\n break;\n }\n\n case 'context.debug': {\n // Per-section token estimate so users can see what's actually eating\n // the context window. The breakdown maths lives in ./token-estimator.ts\n // (4-chars-per-token heuristic); we layer the active mode/policy on top.\n const breakdown = estimateContextBreakdown({\n systemPrompt: context.systemPrompt,\n tools: toolRegistry.list(),\n messages: context.messages,\n });\n send(ws, {\n type: 'context.debug',\n payload: {\n ...breakdown,\n mode: context.meta['contextWindowMode'] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID,\n policy: context.meta['contextWindowPolicy'],\n },\n });\n break;\n }\n\n case 'context.compact': {\n const aggressive = !!(msg as { payload?: { aggressive?: boolean | undefined } }).payload\n ?.aggressive;\n try {\n const report = await compactor.compact(context, { aggressive });\n send(ws, {\n type: 'context.compacted',\n payload: {\n before: report.before,\n after: report.after,\n saved: Math.max(0, report.before - report.after),\n reductions: report.reductions,\n repaired: report.repaired,\n },\n });\n sendResult(\n ws,\n true,\n `Compacted: ${report.before} → ${report.after} tokens (saved ~${Math.max(0, report.before - report.after)})`,\n );\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'context.repair': {\n const beforeMessages = context.messages.length;\n const repaired = repairToolUseAdjacency(context.messages);\n if (repaired.report.changed) {\n context.state.replaceMessages(repaired.messages);\n }\n const payload = {\n removedToolUses: repaired.report.removedToolUses,\n removedToolResults: repaired.report.removedToolResults,\n removedMessages: repaired.report.removedMessages,\n beforeMessages,\n afterMessages: context.messages.length,\n };\n broadcast(clients, { type: 'context.repaired', payload });\n const removed =\n payload.removedToolUses.length +\n payload.removedToolResults.length +\n payload.removedMessages;\n sendResult(\n ws,\n true,\n removed > 0\n ? `Context repaired: removed ${removed} orphan protocol item(s)`\n : 'Context repair found no orphan protocol blocks',\n );\n break;\n }\n\n case 'context.modes.list': {\n const active = String(context.meta['contextWindowMode'] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID);\n const allModes = customModeStore.list().map((m) => ({\n id: m.id,\n name: m.name,\n description: m.description,\n isActive: m.id === active,\n thresholds: m.thresholds,\n preserveK: m.preserveK,\n eliseThreshold: m.eliseThreshold,\n custom: (m as { custom?: boolean }).custom === true,\n }));\n send(ws, {\n type: 'context.modes.list',\n payload: { activeId: active, modes: allModes },\n });\n break;\n }\n\n case 'context.mode.switch': {\n const { id } = (msg as { payload: { id: string } }).payload;\n // Try built-in first, then custom\n let policy = resolveContextWindowPolicy({}, id);\n if (policy.id !== id) {\n // Check custom modes\n const customModes = customModeStore.list().filter(\n (m) => (m as { custom?: boolean }).custom === true,\n );\n const custom = customModes.find((m) => m.id === id);\n if (!custom) {\n sendResult(ws, false, `Unknown context mode \"${id}\"`);\n break;\n }\n // Create a policy from the custom mode\n policy = custom as unknown as typeof policy;\n }\n context.meta['contextWindowMode'] = policy.id;\n context.meta['contextWindowPolicy'] = policy;\n sendResult(ws, true, `Context mode switched to ${policy.id}`);\n broadcast(clients, {\n type: 'context.mode.changed',\n payload: { id: policy.id, name: policy.name, policy },\n });\n break;\n }\n\n case 'context.mode.create': {\n const payload = (msg as { payload: { id: string; name: string; description: string; thresholds: { warn: number; soft: number; hard: number }; preserveK: number; eliseThreshold: number } }).payload;\n const result = customModeStore.create({\n id: payload.id,\n name: payload.name,\n description: payload.description,\n thresholds: payload.thresholds,\n preserveK: payload.preserveK,\n eliseThreshold: payload.eliseThreshold,\n custom: true,\n aggressiveOn: 'soft',\n targetLoad: 0.65,\n });\n sendResult(ws, result.ok, result.error ?? `Mode \"${payload.id}\" created`);\n break;\n }\n\n case 'context.mode.update': {\n const payload = (msg as { payload: { id: string; name?: string | undefined; description?: string | undefined; thresholds?: { warn?: number | undefined; soft?: number | undefined; hard?: number | undefined } | undefined; preserveK?: number | undefined; eliseThreshold?: number | undefined } }).payload;\n const result = customModeStore.update(payload.id, {\n name: payload.name,\n description: payload.description,\n thresholds: payload.thresholds ? {\n warn: payload.thresholds.warn ?? 0.6,\n soft: payload.thresholds.soft ?? 0.75,\n hard: payload.thresholds.hard ?? 0.9,\n } : undefined,\n preserveK: payload.preserveK,\n eliseThreshold: payload.eliseThreshold,\n });\n sendResult(ws, result.ok, result.error ?? `Mode \"${payload.id}\" updated`);\n break;\n }\n\n case 'context.mode.delete': {\n const { id } = (msg as { payload: { id: string } }).payload;\n // If the active mode is being deleted, reset to default\n if (String(context.meta['contextWindowMode'] ?? '') === id) {\n context.meta['contextWindowMode'] = DEFAULT_CONTEXT_WINDOW_MODE_ID;\n context.meta['contextWindowPolicy'] = resolveContextWindowPolicy({}, DEFAULT_CONTEXT_WINDOW_MODE_ID);\n }\n const result = customModeStore.remove(id);\n sendResult(ws, result.ok, result.error ?? `Mode \"${id}\" deleted`);\n break;\n }\n\n case 'providers.list': {\n const providers = await modelsRegistry.listProviders();\n // \"Configured\" should mean *any* working credential, not just env vars.\n // Users register keys with `wstack auth`, which writes apiKey/apiKeys\n // into config.providers[<id>] — those are decrypted in memory here.\n const savedIds = new Set(Object.keys(config.providers ?? {}));\n send(ws, {\n type: 'provider.catalog',\n payload: {\n providers: providers.map((p) => ({\n id: p.id,\n name: p.name,\n family: p.family,\n apiBase: p.apiBase,\n envVars: p.envVars,\n modelCount: p.models.length,\n hasApiKey: savedIds.has(p.id) || p.envVars.some((v) => !!process.env[v]),\n })),\n },\n });\n break;\n }\n\n case 'providers.saved': {\n const saved = await providerHandlers.loadConfigProviders();\n send(ws, {\n type: 'providers.saved',\n payload: {\n providers: Object.entries(saved).map(([id, cfg]) => {\n const keys = normalizeKeys(cfg);\n return {\n id,\n family: cfg.family ?? id,\n baseUrl: cfg.baseUrl,\n apiKeys: keys.map((k) => ({\n label: k.label,\n maskedKey: maskedKey(k.apiKey),\n isActive: k.label === cfg.activeKey,\n createdAt: k.createdAt,\n })),\n };\n }),\n },\n });\n break;\n }\n\n case 'provider.models': {\n const providerId = (msg as { payload: { providerId: string } }).payload.providerId;\n const provider = await modelsRegistry.getProvider(providerId);\n if (provider) {\n send(ws, {\n type: 'provider.models',\n payload: {\n provider: providerId,\n models: provider.models.map((m) => ({\n id: m.id,\n name: m.name,\n releaseDate: (m as { release_date?: string | undefined }).release_date,\n contextWindow: (m as { limit?: { context?: number | undefined } }).limit?.context,\n inputCost: (m as { cost?: { input?: number | undefined } }).cost?.input,\n outputCost: (m as { cost?: { output?: number | undefined } }).cost?.output,\n capabilities: [\n ...((m as { tool_call?: boolean | undefined }).tool_call ? ['tools'] : []),\n ...((m as { reasoning?: boolean | undefined }).reasoning ? ['reasoning'] : []),\n ],\n })),\n },\n });\n }\n break;\n }\n\n case 'model.switch': {\n const { provider: newProvider, model: newModel } = (\n msg as { payload: { provider: string; model: string } }\n ).payload;\n try {\n // Update config\n config = patchConfig(config, { provider: newProvider, model: newModel });\n configStore.update({ provider: newProvider, model: newModel });\n context.model = newModel;\n\n // Create new provider instance — fail loudly if the user picks a\n // provider with no creds rather than silently keeping the old one.\n const providerCfg = config.providers?.[newProvider] ?? { type: newProvider };\n const newProv = providerRegistry.has(newProvider)\n ? providerRegistry.create({ ...providerCfg, type: newProvider })\n : makeProviderFromConfig(newProvider, providerCfg);\n context.provider = newProv;\n\n // Update AutoCompactionMiddleware with the new model's maxContext so\n // backend threshold triggers (warn/soft/hard) use the correct denominator.\n // sessionStartPayload is called below (after this block) and uses\n // the new provider for its modelsRegistry lookup.\n updateAutoCompactionMaxContext?.(newProv);\n\n // Persist to global config file\n try {\n configWriteLock = configWriteLock.then(async () => {\n const raw = await fs.readFile(globalConfigPath, 'utf8');\n const parsed = JSON.parse(raw);\n parsed.provider = newProvider;\n parsed.model = newModel;\n await atomicWrite(globalConfigPath, JSON.stringify(parsed, null, 2));\n });\n await configWriteLock;\n } catch (err) {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.config_save_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n }\n\n // Toast for the SettingsPanel\n send(ws, {\n type: 'key.operation_result',\n payload: { success: true, message: `Switched to ${newProvider} / ${newModel}` },\n });\n } catch (err) {\n send(ws, {\n type: 'key.operation_result',\n payload: {\n success: false,\n message: `Switch failed: ${errMessage(err)}`,\n },\n });\n break;\n }\n\n broadcast(clients, { type: 'session.start', payload: await sessionStartPayload() });\n break;\n }\n\n case 'model.refine': {\n const { text } = (msg as { payload: { text: string } }).payload;\n if (!text?.trim()) {\n send(ws, {\n type: 'model.refine_result',\n payload: { refined: '', english: '', error: 'Empty text' },\n });\n break;\n }\n try {\n const history = recentTextTurns(context.messages);\n const result = await enhanceUserPrompt({\n provider: context.provider,\n model: context.model,\n text,\n history,\n timeoutMs: 90000,\n onError: (reason) => {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'model.refine_failed',\n reason,\n timestamp: new Date().toISOString(),\n }));\n },\n });\n if (result) {\n send(ws, {\n type: 'model.refine_result',\n payload: { refined: result.refined, english: result.english },\n });\n } else {\n send(ws, {\n type: 'model.refine_result',\n payload: { refined: text, english: text, error: 'Refinement returned no result' },\n });\n }\n } catch (err) {\n console.error(JSON.stringify({\n level: 'error',\n event: 'model.refine.error',\n error: errMessage(err),\n timestamp: new Date().toISOString(),\n }));\n send(ws, {\n type: 'model.refine_result',\n payload: { refined: text, english: text, error: errMessage(err) },\n });\n }\n break;\n }\n\n case 'key.add':\n case 'key.update': {\n const { providerId, label, apiKey } = (\n msg as { payload: { providerId: string; label: string; apiKey: string } }\n ).payload;\n await providerHandlers.handleKeyUpsert(ws, providerId, label, apiKey);\n break;\n }\n\n case 'key.delete': {\n const { providerId, label } = (msg as { payload: { providerId: string; label: string } })\n .payload;\n await providerHandlers.handleKeyDelete(ws, providerId, label);\n break;\n }\n\n case 'key.set_active': {\n const { providerId, label } = (msg as { payload: { providerId: string; label: string } })\n .payload;\n await providerHandlers.handleKeySetActive(ws, providerId, label);\n break;\n }\n\n case 'provider.add': {\n const p = (\n msg as {\n payload: {\n id: string;\n family: string;\n baseUrl?: string | undefined;\n apiKey?: string | undefined;\n };\n }\n ).payload;\n await providerHandlers.handleProviderAdd(ws, p);\n break;\n }\n\n case 'provider.remove': {\n const { providerId } = (msg as { payload: { providerId: string } }).payload;\n await providerHandlers.handleProviderRemove(ws, providerId);\n break;\n }\n\n case 'sessions.list': {\n // Per-project history. Sessions live under .wrongstack/sessions/ for\n // this project; we never enumerate cross-project state.\n const limit = (msg as { payload?: { limit?: number | undefined } }).payload?.limit ?? 50;\n try {\n const list = await sessionStore.list(limit);\n send(ws, {\n type: 'sessions.list',\n payload: {\n sessions: list.map((s) => ({\n id: s.id,\n title: s.title,\n startedAt: s.startedAt,\n model: s.model,\n provider: s.provider,\n tokenTotal: s.tokenTotal,\n isCurrent: s.id === session.id,\n })),\n },\n });\n } catch (err) {\n send(ws, {\n type: 'sessions.list',\n payload: { sessions: [], error: errMessage(err) },\n });\n }\n break;\n }\n\n case 'session.delete': {\n const { id } = (msg as { payload: { id: string } }).payload;\n try {\n if (id === session.id) {\n sendResult(ws, false, 'Cannot delete the active session');\n break;\n }\n await sessionStore.delete(id);\n sendResult(ws, true, `Session ${id} deleted`);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'session.resume': {\n // Load a past session's messages + usage, swap the active session\n // writer, hydrate the live Context, then broadcast a session.start\n // payload tagged with the replayed transcript so the UI can render\n // the chat history.\n const { id } = (msg as { payload: { id: string } }).payload;\n try {\n if (id === session.id) {\n sendResult(ws, false, 'Session is already active');\n break;\n }\n const resumed = await sessionStore.resume(id);\n // Finalize the prior writer (session_end + close) best-effort;\n // swallow errors so we don't block the resume on a crashed file\n // handle. The end marker is what makes the session we are leaving\n // read as cleanly completed (summary outcome, endedAt).\n try {\n await session.append({\n type: 'session_end',\n ts: new Date().toISOString(),\n usage: tokenCounter.total(),\n });\n await session.close();\n } catch {\n /* noop */\n }\n session = resumed.writer;\n context.session = session;\n context.state.replaceMessages(resumed.data.messages);\n context.readFiles.clear();\n context.fileMtimes.clear();\n tokenCounter.reset();\n // Replay usage so the topbar shows accurate totals after resume.\n tokenCounter.account(resumed.data.usage, config.model);\n sessionStartedAt = Date.now();\n broadcast(clients, {\n type: 'session.start',\n payload: {\n ...(await sessionStartPayload()),\n reset: true,\n replayMessages: resumed.data.messages,\n replayUsage: resumed.data.usage,\n },\n });\n sendResult(ws, true, `Resumed session ${id}`);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'session.save': {\n // SessionWriter already flushes after every event; this is mostly a\n // no-op marker so the user gets confirmation. Useful for habit\n // parity with the CLI /save command.\n sendResult(ws, true, `Session ${session.id} is auto-saved`);\n break;\n }\n\n case 'tools.list': {\n // Full tool registry dump for the /tools inspect view. We surface\n // name, description, and schema-derived param names so the user\n // can tell at a glance which tools the model can call right now.\n const list = toolRegistry.list().map((t) => {\n const schema =\n (t as { inputSchema?: { properties?: Record<string, unknown> } }).inputSchema ?? {};\n const params = schema.properties ? Object.keys(schema.properties) : [];\n return {\n name: t.name,\n description: (t as { description?: string | undefined }).description ?? '',\n params,\n };\n });\n send(ws, { type: 'tools.list', payload: { tools: list } });\n break;\n }\n\n // ── Memory operations — delegated to shared handlers (memory-handlers.ts) ──\n case 'memory.list':\n return handleMemoryList(ws, memoryStore);\n case 'memory.remember':\n return handleMemoryRemember(ws, msg, memoryStore);\n case 'memory.forget':\n return handleMemoryForget(ws, msg, memoryStore);\n\n case 'skills.list': {\n if (!skillLoader) {\n send(ws, { type: 'skills.list', payload: { skills: [], enabled: false } });\n break;\n }\n try {\n const manifests = await skillLoader.list();\n const entries = await skillLoader.listEntries();\n const byName = new Map(entries.map((e) => [e.name, e]));\n send(ws, {\n type: 'skills.list',\n payload: {\n enabled: true,\n skills: manifests.map((m) => ({\n name: m.name,\n description: m.description,\n version: m.version ?? '',\n source: m.source,\n path: m.path,\n trigger: byName.get(m.name)?.trigger ?? '',\n scope: byName.get(m.name)?.scope ?? [],\n })),\n },\n });\n } catch (err) {\n send(ws, {\n type: 'skills.list',\n payload: {\n skills: [],\n enabled: true,\n error: errMessage(err),\n },\n });\n }\n break;\n }\n\n case 'diag.get': {\n // Snapshot of the moving parts so the user can debug \"why is X\n // not working?\" without diving into the server logs.\n const usage = tokenCounter.total();\n send(ws, {\n type: 'diag.get',\n payload: {\n provider: config.provider,\n model: config.model,\n cwd: projectRoot,\n sessionId: session.id,\n tools: {\n count: toolRegistry.list().length,\n names: toolRegistry.list().map((t) => t.name),\n },\n features: {\n memory: !!config.features?.memory,\n skills: !!config.features?.skills,\n modelsRegistry: !!config.features?.modelsRegistry,\n },\n mode: modeId ?? 'default',\n usage,\n messages: context.messages.length,\n todos: context.todos.length,\n },\n });\n break;\n }\n\n case 'todos.get': {\n // On-demand snapshot — used when a UI surface first mounts and\n // needs to render the live todo list without waiting for the next\n // tool.executed to broadcast.\n send(ws, {\n type: 'todos.updated',\n payload: { todos: [...context.todos] },\n });\n break;\n }\n\n case 'todos.clear': {\n // Manual override — the agent normally curates this list via\n // TodoWrite, but the user might want a clean slate without losing\n // the rest of the context. Use state.replaceTodos so observers\n // (checkpoint writer) stay in sync.\n context.state.replaceTodos([]);\n sendResult(ws, true, 'Todos cleared');\n broadcast(clients, { type: 'todos.updated', payload: { todos: [] } });\n break;\n }\n\n case 'todos.remove': {\n // Remove a single todo item by id or 1-based index.\n const payload = msg.payload as\n | { id?: string | undefined; index?: number | undefined }\n | undefined;\n if (!payload) {\n sendResult(ws, false, 'Missing id or index');\n break;\n }\n const { id, index } = payload;\n let targetIdx = -1;\n if (typeof id === 'string') {\n targetIdx = context.todos.findIndex((t) => t.id === id);\n } else if (typeof index === 'number' && index > 0) {\n targetIdx = index - 1;\n }\n if (targetIdx < 0 || !context.todos[targetIdx]) {\n sendResult(ws, false, 'Todo not found');\n break;\n }\n const removed = expectDefined(context.todos[targetIdx]);\n const next = [...context.todos.slice(0, targetIdx), ...context.todos.slice(targetIdx + 1)];\n context.state.replaceTodos(next);\n sendResult(ws, true, `Removed: ${removed.content}`);\n broadcast(clients, { type: 'todos.updated', payload: { todos: next } });\n break;\n }\n\n case 'tasks.get': {\n // On-demand task snapshot — loads from <sessionId>.tasks.json\n const taskPath = (context.meta as Record<string, unknown>)['task.path'];\n if (typeof taskPath === 'string' && taskPath) {\n try {\n const { loadTasks } = await import('@wrongstack/core');\n const file = await loadTasks(taskPath);\n send(ws, {\n type: 'tasks.updated',\n payload: { tasks: file?.tasks ?? [] },\n });\n } catch {\n send(ws, { type: 'tasks.updated', payload: { tasks: [] } });\n }\n } else {\n send(ws, { type: 'tasks.updated', payload: { tasks: [], error: 'Task storage not configured.' } });\n }\n break;\n }\n\n case 'plan.get': {\n // On-demand plan snapshot — used when a UI surface first mounts\n // and needs to render the live plan without waiting for the next\n // tool.executed to broadcast.\n const planPath = (context.meta as Record<string, unknown>)['plan.path'];\n if (typeof planPath === 'string' && planPath) {\n try {\n const { loadPlan } = await import('@wrongstack/core');\n const plan = await loadPlan(planPath);\n send(ws, {\n type: 'plan.updated',\n payload: {\n plan: plan ?? {\n version: 1,\n sessionId: session.id,\n updatedAt: new Date().toISOString(),\n items: [],\n },\n },\n });\n } catch {\n send(ws, {\n type: 'plan.updated',\n payload: {\n plan: {\n version: 1,\n sessionId: session.id,\n updatedAt: new Date().toISOString(),\n items: [],\n },\n },\n });\n }\n } else {\n send(ws, {\n type: 'plan.updated',\n payload: { plan: null, error: 'Plan storage is not configured for this session.' },\n });\n }\n break;\n }\n\n case 'plan.template_use': {\n const { template } = (msg as { payload: { template: string } }).payload;\n const planPath = (context.meta as Record<string, unknown>)['plan.path'];\n if (typeof planPath !== 'string' || !planPath) {\n sendResult(ws, false, 'Plan storage is not configured for this session.');\n break;\n }\n try {\n const { getPlanTemplate, loadPlan, savePlan, emptyPlan, addPlanItem } = await import(\n '@wrongstack/core'\n );\n const tpl = getPlanTemplate(template);\n if (!tpl) {\n sendResult(ws, false, `Unknown template \"${template}\".`);\n break;\n }\n let plan = (await loadPlan(planPath)) ?? emptyPlan(session.id);\n for (const item of tpl.items) {\n ({ plan } = addPlanItem(plan, item.title, item.details));\n }\n await savePlan(planPath, plan);\n sendResult(ws, true, `Applied template \"${tpl.name}\" — ${tpl.items.length} items added.`);\n broadcast(clients, {\n type: 'plan.updated',\n payload: { plan },\n });\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n // ── File operations — delegated to shared handlers (file-handlers.ts) ──\n // These handlers are also used by the CLI's webui-server.ts. When\n // adding or modifying file-operation WebSocket messages, update\n // file-handlers.ts — NOT these case blocks individually.\n case 'files.list':\n return handleFilesList(ws, msg, projectRoot);\n case 'files.tree':\n return handleFilesTree(ws, msg, projectRoot);\n case 'files.read':\n return handleFilesRead(ws, msg, projectRoot);\n case 'files.write':\n return handleFilesWrite(ws, msg, projectRoot);\n\n case 'modes.list': {\n try {\n const modes = await modeStore.listModes();\n const active = await modeStore.getActiveMode();\n send(ws, {\n type: 'modes.list',\n payload: {\n modes: modes.map((m) => ({\n id: m.id,\n name: m.name,\n description: m.description,\n isActive: m.id === (active?.id ?? 'default'),\n })),\n activeId: active?.id ?? 'default',\n },\n });\n } catch (err) {\n send(ws, {\n type: 'modes.list',\n payload: {\n modes: [],\n activeId: 'default',\n error: errMessage(err),\n },\n });\n }\n break;\n }\n\n case 'mode.switch': {\n const { id } = (msg as { payload: { id: string } }).payload;\n try {\n // 'default' is the implicit no-mode state — persisting null\n // clears the override. Anything else has to exist in the store.\n if (id === 'default') {\n await modeStore.setActiveMode(null);\n } else {\n const found = await modeStore.getMode(id);\n if (!found) throw new Error(`Unknown mode \"${id}\"`);\n await modeStore.setActiveMode(id);\n }\n modeId = id;\n // Rebuild the system prompt so the next turn picks up the new\n // mode's instructions. The builder caches the environment block\n // per projectRoot (including the modeId), so we clear the cache\n // and rebuild. The `buildMode()` method reads this.opts.modePrompt\n // which is set on the builder constructor — we construct a fresh\n // builder with the updated mode. This is cheap (no fs/net IO in\n // the constructor; the real work happens in build()).\n const modePrompt = id === 'default' ? '' : ((await modeStore.getMode(id))?.prompt ?? '');\n const freshBuilder = new DefaultSystemPromptBuilder({\n memoryStore,\n skillLoader,\n modeStore,\n modeId: id,\n modePrompt,\n modelCapabilities,\n });\n context.systemPrompt = await freshBuilder.build({\n cwd: projectRoot,\n projectRoot,\n tools: toolRegistry.list(),\n provider: config.provider,\n model: config.model,\n });\n sendResult(ws, true, `Switched to mode \"${id}\"`);\n broadcast(clients, {\n type: 'session.start',\n payload: { ...(await sessionStartPayload()) },\n });\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'stats.get': {\n // Mirror of the CLI's /stats: detailed session report.\n const usage = tokenCounter.total();\n const cacheStats = tokenCounter.cacheStats();\n const m = await modelsRegistry.getModel(config.provider, config.model).catch(() => null);\n const cost = computeUsageCost(usage, getCostRates(m));\n send(ws, {\n type: 'stats.get',\n payload: {\n sessionId: session.id,\n provider: config.provider,\n model: config.model,\n usage,\n cache: cacheStats,\n cost,\n messages: context.messages.length,\n readFiles: context.readFiles.size,\n tools: toolRegistry.list().length,\n elapsedMs: Date.now() - sessionStartedAt,\n },\n });\n break;\n }\n\n case 'process.list': {\n // Return tracked process list from the process registry.\n try {\n const { getProcessRegistry } = await import('@wrongstack/tools');\n const procs = getProcessRegistry().list();\n send(ws, {\n type: 'process.list',\n payload: {\n processes: procs.map((p) => ({\n pid: p.pid,\n command: p.command,\n tool: p.name,\n startedAt: p.startedAt,\n status: p.killed ? ('killed' as const) : ('running' as const),\n protected: p.protected,\n })),\n },\n });\n } catch {\n send(ws, { type: 'process.list', payload: { processes: [] } });\n }\n break;\n }\n\n case 'process.kill': {\n const { pid } = (msg as { payload: { pid: number } }).payload;\n try {\n const { getProcessRegistry } = await import('@wrongstack/tools');\n const proc = getProcessRegistry().get(pid);\n if (proc?.protected) {\n sendResult(ws, false, `Cannot kill protected process (PID ${pid})`);\n break;\n }\n getProcessRegistry().kill(pid);\n sendResult(ws, true, `Killed PID ${pid}`);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'process.killAll': {\n try {\n const { getProcessRegistry } = await import('@wrongstack/tools');\n getProcessRegistry().killAll();\n sendResult(ws, true, 'All processes killed');\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'goal.get': {\n // Read goal.json from disk and broadcast to all clients so every\n // connected browser sees the same goal state. The file is polled\n // by the frontend every 10s — we serve the latest snapshot here.\n try {\n const goalPath = path.join(projectRoot, '.wrongstack', 'goal.json');\n const raw = await fs.readFile(goalPath, 'utf8');\n const goal = JSON.parse(raw);\n broadcast(clients, { type: 'goal.updated', payload: goal });\n } catch {\n // No goal file yet or parse error — broadcast null so the\n // frontend clears any stale goal state.\n broadcast(clients, { type: 'goal.updated', payload: null });\n }\n break;\n }\n\n case 'autonomy.switch': {\n // Autonomy mode switch — forwarded to the agent context.\n // The mode is stored in context.meta for the permission policy to read.\n const { mode } = (msg as { payload: { mode: string } }).payload;\n context.meta['autonomy'] = mode;\n sendResult(ws, true, `Autonomy mode set to \"${mode}\"`);\n // Keep every browser tab + the settings panel in sync, and persist\n // the durable modes (eternal/eternal-parallel are session-level).\n broadcast(clients, { type: 'prefs.updated', payload: { autonomy: mode } });\n void persistPrefsToConfig({ autonomy: mode });\n break;\n }\n\n case 'prefs.update': {\n // Batch preference update from the webui. Merges arbitrary key/value\n // pairs into context.meta so the runtime can read them immediately,\n // broadcasts the full pref snapshot to every connected client so all\n // browser tabs stay in sync, and persists the durable keys to\n // config.json (same keys the TUI settings picker writes).\n const payload = (msg as { payload: Record<string, unknown> }).payload;\n // Write each pref into context.meta\n for (const [key, val] of Object.entries(payload)) {\n context.meta[key] = val;\n }\n void persistPrefsToConfig(payload);\n // YOLO mode: toggle the permission policy so tool confirmations\n // are auto-approved instead of prompting the user. Uses the live\n // reference resolved from the container at startup.\n if (typeof payload['yolo'] === 'boolean') {\n permissionPolicy.setYolo?.(payload['yolo']);\n }\n // Also update config.features for feature flags that affect tool/skill\n // initialisation (these were read at startup but can be changed at runtime\n // by the agent's permission middleware or tool guards).\n if (typeof payload['featureMcp'] === 'boolean')\n config.features.mcp = payload['featureMcp'];\n if (typeof payload['featurePlugins'] === 'boolean')\n config.features.plugins = payload['featurePlugins'];\n if (typeof payload['featureMemory'] === 'boolean')\n config.features.memory = payload['featureMemory'];\n if (typeof payload['featureSkills'] === 'boolean')\n config.features.skills = payload['featureSkills'];\n if (typeof payload['featureModelsRegistry'] === 'boolean')\n config.features.modelsRegistry = payload['featureModelsRegistry'];\n\n // Runtime effects: apply prefs that change server behaviour immediately.\n\n // contextAutoCompact — toggle AutoCompactionMiddleware in/out of the\n // contextWindow pipeline. When off, the pipeline skips the compaction\n // step entirely (zero overhead). When on, re-adds the middleware.\n if (typeof payload['contextAutoCompact'] === 'boolean') {\n if (payload['contextAutoCompact'] && autoCompactor) {\n // Re-add: remove first (idempotent via optional), then insert.\n pipelines.contextWindow.remove('AutoCompaction', { optional: true });\n pipelines.contextWindow.use({ name: 'AutoCompaction', handler: autoCompactor.handler() });\n } else {\n pipelines.contextWindow.remove('AutoCompaction', { optional: true });\n }\n }\n\n // logLevel — the DefaultLogger.level property is a public mutable\n // field. Setting it at runtime changes the log threshold immediately\n // (the log() method checks LEVEL_RANK on every call).\n if (typeof payload['logLevel'] === 'string') {\n const valid = ['debug', 'info', 'warn', 'error'] as const;\n if ((valid as readonly string[]).includes(payload['logLevel'])) {\n logger.level = payload['logLevel'] as typeof valid[number];\n }\n }\n\n // auditLevel — stored in context.meta by the generic loop above.\n // Consumed by the session audit log system at session-close time.\n\n // Broadcast the full current prefs snapshot to ALL clients.\n broadcast(clients, { type: 'prefs.updated', payload: prefSnapshot() });\n break;\n }\n\n case 'prefs.get': {\n // Return the current pref snapshot so a freshly-connected client\n // can seed its local-prefs store from the server's truth.\n send(ws, { type: 'prefs.updated', payload: prefSnapshot() });\n break;\n }\n\n case 'session.checkpoints': {\n // Return session checkpoints for the rewind timeline.\n try {\n const { DefaultSessionRewinder } = await import('@wrongstack/core');\n const rewinder = new DefaultSessionRewinder(\n path.join(projectRoot, '.wrongstack', 'sessions'),\n projectRoot,\n );\n const checkpoints = await rewinder.listCheckpoints(session.id);\n send(ws, {\n type: 'session.checkpoints',\n payload: { checkpoints },\n });\n } catch (err) {\n send(ws, {\n type: 'session.checkpoints',\n payload: { checkpoints: [] },\n });\n }\n break;\n }\n\n case 'session.rewind': {\n const { checkpointIndex } = (msg as { payload: { checkpointIndex: number } }).payload;\n try {\n const { DefaultSessionRewinder } = await import('@wrongstack/core');\n const rewinder = new DefaultSessionRewinder(\n path.join(projectRoot, '.wrongstack', 'sessions'),\n projectRoot,\n );\n await rewinder.rewindToCheckpoint(session.id, checkpointIndex);\n await context.session.truncateToCheckpoint(checkpointIndex);\n sendResult(ws, true, `Rewound to checkpoint ${checkpointIndex}`);\n broadcast(clients, {\n type: 'session.start',\n payload: { ...(await sessionStartPayload()), reset: true },\n });\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n // ── Project management ────────────────────────────────────────────\n\n case 'projects.list': {\n try {\n const manifest = await loadManifest(globalConfigPath);\n send(ws, {\n type: 'projects.list',\n payload: { projects: manifest.projects },\n });\n } catch (err) {\n send(ws, {\n type: 'projects.list',\n payload: { projects: [], error: errMessage(err) },\n });\n }\n break;\n }\n\n case 'projects.add': {\n const { root: addRoot, name: displayName } = (\n msg as { payload: { root: string; name?: string | undefined } }\n ).payload;\n try {\n const resolved = path.resolve(addRoot);\n await fs.access(resolved);\n const stat = await fs.stat(resolved);\n if (!stat.isDirectory()) throw new Error(`Not a directory: ${resolved}`);\n\n const manifest = await loadManifest(globalConfigPath);\n const existing = manifest.projects.find((p) => p.root === resolved);\n if (existing) {\n send(ws, {\n type: 'projects.added',\n payload: {\n name: existing.name,\n root: existing.root,\n slug: existing.slug,\n message: `Already registered as \"${existing.name}\"`,\n },\n });\n break;\n }\n\n const name = displayName?.trim() || path.basename(resolved);\n const slug = generateProjectSlug(resolved);\n await ensureProjectDataDir(slug, globalConfigPath);\n const now = new Date().toISOString();\n manifest.projects.push({ name, root: resolved, slug, lastSeen: now, createdAt: now });\n await saveManifest(manifest, globalConfigPath);\n\n send(ws, {\n type: 'projects.added',\n payload: {\n name,\n root: resolved,\n slug,\n message: `Registered project \"${name}\"`,\n },\n });\n } catch (err) {\n send(ws, {\n type: 'projects.added',\n payload: {\n name: path.basename(addRoot),\n root: addRoot,\n slug: '',\n message: errMessage(err),\n },\n });\n }\n break;\n }\n\n case 'projects.select': {\n const { root: selRoot, name: selName } = (\n msg as { payload: { root: string; name?: string | undefined } }\n ).payload;\n try {\n const resolved = path.resolve(selRoot);\n\n // Validate the directory exists\n try {\n await fs.access(resolved);\n const stat = await fs.stat(resolved);\n if (!stat.isDirectory()) throw new Error(`Not a directory: ${resolved}`);\n } catch (err) {\n send(ws, {\n type: 'projects.selected',\n payload: {\n root: selRoot,\n name: selName || path.basename(selRoot),\n message: `Cannot switch: ${errMessage(err)}`,\n },\n });\n break;\n }\n\n // Update lastSeen in manifest\n const manifest = await loadManifest(globalConfigPath);\n const entry = manifest.projects.find((p) => p.root === resolved);\n if (entry) {\n entry.lastSeen = new Date().toISOString();\n entry.lastWorkingDir = resolved;\n } else {\n // Auto-register if not in manifest\n const name = selName?.trim() || path.basename(resolved);\n const slug = generateProjectSlug(resolved);\n manifest.projects.push({\n name,\n root: resolved,\n slug,\n lastSeen: new Date().toISOString(),\n createdAt: new Date().toISOString(),\n lastWorkingDir: resolved,\n });\n await ensureProjectDataDir(slug, globalConfigPath);\n }\n await saveManifest(manifest, globalConfigPath);\n\n // ── Hot-swap the project root + working dir ─────────────────\n // Abort any in-flight agent run before switching — the agent's\n // context (cwd, projectRoot, session) is about to change and\n // continuing would cause inconsistent state.\n if (runLock) {\n runLock.abort();\n runLock = null;\n }\n\n projectRoot = resolved;\n workingDir = resolved;\n\n // Update the live context so tools use the new directory\n context.cwd = workingDir;\n context.projectRoot = projectRoot;\n\n const switchSlug = entry?.slug ?? generateProjectSlug(resolved);\n\n // Rebuild the system prompt for the NEW project. The environment\n // block (project root, git status, detected languages) is baked into\n // the prompt at boot and cached by projectRoot; without this rebuild\n // the agent keeps the launch-directory environment and tries to work\n // in the old folder until tool errors force a correction. Mirrors the\n // mode.switch rebuild; best-effort so a failure here leaves the prior\n // (stale-but-usable) prompt rather than breaking the switch.\n try {\n const switchMode =\n modeId === 'default' ? undefined : await modeStore.getMode(modeId);\n const switchBuilder = new DefaultSystemPromptBuilder({\n memoryStore,\n skillLoader,\n modeStore,\n modeId,\n modePrompt: switchMode?.prompt ?? '',\n modelCapabilities,\n });\n context.systemPrompt = await switchBuilder.build({\n cwd: workingDir,\n projectRoot,\n tools: toolRegistry.list(),\n provider: config.provider,\n model: config.model,\n });\n } catch {\n /* best-effort — keep the prior system prompt if rebuild fails */\n }\n\n // Create a new session store for the new project's sessions dir\n const newSessionsDir = path.join(\n path.dirname(globalConfigPath),\n 'projects',\n switchSlug,\n 'sessions',\n );\n await fs.mkdir(newSessionsDir, { recursive: true });\n const newSessionStore = new DefaultSessionStore({ dir: newSessionsDir });\n\n // Switch the session store for the new project.\n // Close the old session gracefully\n const oldSessionId = session.id;\n try {\n await session.append({\n type: 'session_end',\n ts: new Date().toISOString(),\n usage: tokenCounter.total(),\n });\n await session.close();\n } catch {\n // best-effort\n }\n\n // Create a fresh session in the new project\n sessionStore = newSessionStore;\n session = await sessionStore.create({\n id: '',\n title: '',\n model: config.model,\n provider: config.provider,\n });\n context.session = session;\n context.state.replaceMessages([]);\n context.state.replaceTodos([]);\n context.readFiles.clear();\n context.fileMtimes.clear();\n tokenCounter.reset();\n sessionStartedAt = Date.now();\n\n // Re-point the cross-process SessionRegistry at the new project +\n // session id. Without this, `/sessions status`, the WebUI sessions\n // dashboard, and the 5s status poll keep listing this process under\n // the launch project's root/workingDir — the WebUI looks like it is\n // still \"in\" the old folder after the switch. register() is now\n // re-entrant (drops the old entry, restarts a single heartbeat).\n try {\n const registry = getSessionRegistry(wpaths.globalRoot);\n await registry.register({\n sessionId: session.id,\n projectSlug: switchSlug,\n projectRoot,\n projectName: path.basename(projectRoot),\n workingDir,\n pid: process.pid,\n startedAt: new Date().toISOString(),\n });\n } catch {\n /* best-effort — discovery degrades gracefully */\n }\n\n send(ws, {\n type: 'projects.selected',\n payload: {\n root: resolved,\n name: selName || path.basename(resolved),\n message: `Switched to ${selName || path.basename(resolved)}`,\n },\n });\n\n // Broadcast old-session subagents as stopped so the frontend\n // fleet store cleans them via its normal event pipeline.\n broadcast(clients, {\n type: 'subagent.event',\n payload: {\n kind: 'session_stopped',\n sessionId: oldSessionId,\n },\n });\n\n // Broadcast updated project info to ALL clients so file\n // explorer / context bar pick up the new root.\n broadcast(clients, {\n type: 'session.start',\n payload: {\n ...(await sessionStartPayload()),\n reset: true,\n clearedSessionId: oldSessionId,\n },\n });\n } catch (err) {\n send(ws, {\n type: 'projects.selected',\n payload: {\n root: selRoot,\n name: selName || path.basename(selRoot),\n message: errMessage(err),\n },\n });\n }\n break;\n }\n\n // ── Working directory (within current project) ───────────────────\n\n case 'working_dir.set': {\n const { path: newPath } = (msg as { payload: { path: string } }).payload;\n try {\n const resolved = path.resolve(projectRoot, newPath);\n\n // Guard: must stay inside projectRoot\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n sendResult(ws, false, `Path must stay inside the project root: ${projectRoot}`);\n break;\n }\n\n try {\n await fs.access(resolved);\n const stat = await fs.stat(resolved);\n if (!stat.isDirectory()) throw new Error('Not a directory');\n } catch {\n sendResult(ws, false, `Directory not found or not accessible: ${resolved}`);\n break;\n }\n\n workingDir = resolved;\n context.cwd = resolved;\n\n // Notify all clients so the file explorer and context bar update\n broadcast(clients, {\n type: 'working_dir.changed',\n payload: { cwd: resolved, projectRoot },\n });\n\n sendResult(ws, true, `Working directory set to ${resolved}`);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n // ── Shell open — spawn terminal or file manager at a path ─────────\n\n case 'shell.open': {\n // Logic lives in `shell-open.ts` so the CLI's `runWebUI` can\n // share the same metacharacter guard + cross-platform spawn\n // chain. See the docstring in shell-open.ts for the security\n // rationale and the fallback chain.\n const result: ShellOpenResult = await handleShellOpen(\n msg.payload as ShellOpenRequest,\n logger,\n );\n sendResult(ws, result.success, result.message);\n break;\n }\n\n // ── Mailbox operations — project-level inter-agent messaging ────\n case 'mailbox.messages':\n return handleMailboxMessages(\n ws,\n { projectRoot, globalRoot: path.dirname(globalConfigPath) },\n (msg as { payload?: { limit?: number; agentId?: string; unreadOnly?: boolean } }).payload,\n );\n case 'mailbox.agents':\n return handleMailboxAgents(\n ws,\n { projectRoot, globalRoot: path.dirname(globalConfigPath) },\n (msg as { payload?: { onlineOnly?: boolean } }).payload,\n );\n case 'mailbox.clear':\n return handleMailboxClear(\n ws,\n { projectRoot, globalRoot: path.dirname(globalConfigPath) },\n );\n\n // ── Brain — status, autonomy ceiling, direct decision support ───\n case 'brain.status':\n send(ws, {\n type: 'brain.status',\n payload: { maxAutoRisk: brainSettings.maxAutoRisk, log: brainLog },\n });\n break;\n case 'brain.risk': {\n const level = (msg as { payload?: { level?: string } }).payload?.level ?? '';\n const valid = ['off', 'low', 'medium', 'high', 'all'];\n if (!valid.includes(level)) {\n sendResult(ws, false, `Unknown risk level \"${level}\". Use: ${valid.join(', ')}.`);\n break;\n }\n brainSettings.maxAutoRisk = level as BrainAutoRisk;\n send(ws, {\n type: 'brain.status',\n payload: { maxAutoRisk: brainSettings.maxAutoRisk, log: brainLog },\n });\n break;\n }\n case 'brain.ask': {\n const question = (msg as { payload?: { question?: string } }).payload?.question?.trim();\n if (!question) {\n sendResult(ws, false, 'Usage: /brain ask <question>');\n break;\n }\n try {\n const decision = await brain.decide({\n id: `brain-ask-${Date.now().toString(36)}`,\n source: 'user',\n question,\n risk: 'medium',\n fallback: 'ask_human',\n });\n send(ws, { type: 'brain.answer', payload: { question, decision } });\n } catch (err) {\n sendResult(ws, false, `Brain consultation failed: ${errMessage(err)}`);\n }\n break;\n }\n\n default:\n if (msg.type.startsWith('autophase.')) {\n // Delegate all AutoPhase lifecycle messages to the handler\n await autoPhaseHandler.handleMessage(\n msg as { type: string; payload?: Record<string, unknown> },\n );\n } else {\n send(ws, {\n type: 'error',\n payload: { phase: 'handleMessage', message: `Unknown message type: ${msg.type}` },\n });\n }\n }\n }\n\n // ---- Provider/Key management helpers (extracted to provider-handlers.ts) ----\n const providerHandlers = createProviderHandlers({\n globalConfigPath,\n vault,\n getConfigWriteLock: () => configWriteLock,\n setConfigWriteLock: (p) => {\n configWriteLock = p;\n },\n broadcast,\n clients,\n });\n\n // HTTP server for the React frontend (port 3456) — see `http-server.ts`\n // for the static-serve, MIME matching, path-traversal guard, and CSP\n // header logic. Constructed here, listen()d below alongside the WS server.\n // `globalRoot` powers the /api/sessions and /api/sessions/:id/agents\n // handlers (read the cross-process SessionRegistry); `apiToken` is the\n // shared auth token the HTTP API requires when bound to a non-loopback\n // host (LAN exposure). Loopback binds skip the token check, mirroring\n // the WS verifyClient loopback-bootstrap policy.\n const httpServer = createHttpServer({\n host: wsHost,\n distDir: path.resolve(import.meta.dirname, '../../dist'),\n wsPort,\n globalRoot: wpaths.globalRoot,\n apiToken: wsToken,\n });\n // httpPort/wsPort were resolved (and possibly auto-advanced) at the top.\n // Base dir for the running-instance registry — keep it next to the rest of\n // the wstack home state (config.json lives here too).\n const registryBaseDir = path.dirname(globalConfigPath);\n httpServer.listen(httpPort, wsHost, () => {\n const openUrl = `http://${wsHost}:${httpPort}`;\n console.log(`[WebUI] HTTP server running on ${openUrl}`);\n // Optionally pop the browser open (best-effort; the URL is always printed).\n if (opts.open) openBrowser(openUrl);\n // Record this instance so `webui --list` (and `~/.wrongstack/\n // webui-instances.json`) show which ports are open for which project.\n // Best-effort: a registry write failure must not affect serving.\n void registerInstance(\n {\n pid: process.pid,\n httpPort,\n wsPort,\n host: wsHost,\n projectRoot,\n projectName: path.basename(projectRoot) || projectRoot,\n startedAt: new Date().toISOString(),\n url: `http://${wsHost}:${httpPort}`,\n },\n registryBaseDir,\n ).catch((err) => console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.instance_record_failed',\n message: errMessage(err),\n timestamp: new Date().toISOString(),\n })));\n });\n\n // Graceful shutdown on SIGINT/SIGTERM — see `lifecycle.ts`. The session\n // flush (session_end + close) is passed as a thunk so lifecycle stays\n // decoupled from the session/tokenCounter types.\n registerShutdownHandlers({\n flushSession: async () => {\n await session.append({\n type: 'session_end',\n ts: new Date().toISOString(),\n usage: tokenCounter.total(),\n });\n await session.close();\n },\n clients: () => clients.keys(),\n servers: [httpServer, wssPrimary, wssSecondary],\n // Drop this instance from the registry on a clean exit so the file reflects\n // reality. Crash exits are healed by the next register()/list() prune pass.\n onShutdown: () => {\n brainMonitor.stop();\n if (eternalSubscription) {\n eternalSubscription.dispose();\n eternalSubscription = null;\n }\n return unregisterInstance(process.pid, registryBaseDir);\n },\n });\n}\n","/**\n * Static-file HTTP server for the WebUI React frontend.\n *\n * - Serves files from `distDir` (typically `<webui>/dist`).\n * - Returns `index.html` for any unknown path so client-side routing works\n * (SPA fallback) — and applies the same Content-Security-Policy to that\n * fallback as to a direct `.html` response, so deep-linked routes are\n * not unprotected.\n * - **Path-traversal guard**: `path.join` alone does NOT prevent\n * `%2e%2e%2f` escapes (the `URL` constructor decodes percent-encoding\n * before we see the path). We re-`resolve` the candidate and verify it\n * stays under `distDir`.\n * - **CSP**: `connect-src` uses explicit loopback addresses for the WS\n * server (not bare `ws:` / `wss:`) so a malicious page script cannot\n * dial an attacker-controlled WebSocket. Combined with the\n * cookie-based WS auth delivery (`/ws-auth` → `Set-Cookie: ws_token=\n * …; HttpOnly; SameSite=Strict; Path=/`), this prevents cross-origin\n * WS abuse.\n * - **API auth**: the `/api/sessions` and `/api/sessions/:id/agents`\n * endpoints accept the same shared token as the WS upgrade, via the\n * `X-WS-Token` header. Without it, those endpoints return 401. This\n * closes the LAN-attacker enumeration vector when `wsHost` is\n * non-loopback (e.g. `WS_HOST=0.0.0.0`).\n *\n * Extracted from `index.ts` so the static-serve concern can be tested\n * with a tiny fake `distDir` and asserted on path-traversal, MIME\n * matching, and CSP header presence.\n */\nimport * as fs from 'node:fs/promises';\nimport * as http from 'node:http';\nimport * as path from 'node:path';\nimport { isLoopbackBind, tokenMatches } from './ws-auth.js';\n\nexport interface CreateHttpServerOptions {\n /** Port to listen on. Defaults to 3456 (or the `PORT` env var). */\n port?: number | undefined;\n /** Host/interface to bind. Typically the loopback for the WebUI. */\n host: string;\n /** Resolved path to the directory containing the built React assets. */\n distDir: string;\n /**\n * WS port — appears in the CSP `connect-src` directive so the browser\n * is allowed to open a WebSocket back to the local server.\n */\n wsPort: number;\n /**\n * Path to the global WrongStack root (~/.wrongstack). Used by the\n * /api/sessions and /api/sessions/:id/agents endpoints to read the\n * cross-process SessionRegistry.\n */\n globalRoot?: string | undefined;\n /**\n * Shared auth token for `/api/*` endpoints. Required for non-loopback\n * binds (LAN exposure). Loopback binds accept any local origin without\n * a token (the WS path's loopback-bootstrap policy — see ws-auth.ts).\n */\n apiToken?: string | undefined;\n /**\n * If true, the `/ws-auth` endpoint exchanges a `?token=` query param (or\n * `X-WS-Token` header) for an `HttpOnly` auth cookie. The cookie is then\n * sent automatically on the WS upgrade, closing the C-598 query-string\n * token exposure class. Default: true. Set to false to keep the legacy\n * URL-token-only flow (e.g. in tests that don't want cookie state).\n */\n enableWsCookie?: boolean | undefined;\n}\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n};\n\n/**\n * Inject the live WS port into the served HTML so the frontend connects to\n * THIS instance's backend instead of a hardcoded default. Enables running\n * several WebUI instances simultaneously on different PORT/WS_PORT pairs\n * (e.g. one per project) — each instance serves HTML stamped with its own\n * WS port.\n *\n * A `<meta>` tag is used deliberately rather than an inline `<script>`: the\n * CSP sets `script-src 'self'`, which would block an inline script, but meta\n * tags are not subject to script-src. The frontend reads\n * `meta[name=\"wrongstack-ws-port\"]` (see ws-client.ts `defaultWsUrl`).\n */\nexport function injectWsPort(html: string, wsPort: number): string {\n const tag = `<meta name=\"wrongstack-ws-port\" content=\"${wsPort}\" />`;\n // Idempotent: never inject twice if the source HTML already carries one.\n if (html.includes('name=\"wrongstack-ws-port\"')) return html;\n if (html.includes('</head>')) {\n return html.replace('</head>', ` ${tag}\\n </head>`);\n }\n // No <head> (unexpected) — prepend so the tag is still in the document.\n return `${tag}\\n${html}`;\n}\n\n/** Build the Content-Security-Policy value for the given WS port. */\nexport function buildCspHeader(wsPort: number): string {\n return (\n `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; ` +\n `connect-src 'self' ws://127.0.0.1:${wsPort} wss://127.0.0.1:${wsPort} ` +\n `ws://[::1]:${wsPort} wss://[::1]:${wsPort}; ` +\n `img-src 'self' data:; font-src 'self' data:; worker-src 'self' blob:; object-src 'none'; ` +\n `base-uri 'self'; frame-ancestors 'none'; form-action 'self'`\n );\n}\n\n/**\n * Returns true when `candidate` (a fully-resolved absolute path) lies\n * strictly inside `distDir` (or equals it). Used to reject path-traversal\n * attempts after `path.resolve` has normalised any `..` segments.\n *\n * Exported so tests can assert the guard's contract without having to\n * also defeat the WHATWG URL normaliser (which strips `..` from the\n * path string *before* the request even reaches the server, making a\n * black-box test via fetch impossible).\n */\nexport function isInsideDist(candidate: string, distDir: string): boolean {\n const root = path.resolve(distDir);\n const resolved = path.resolve(candidate);\n return resolved === root || resolved.startsWith(root + path.sep);\n}\n\n/**\n * Create the static-file HTTP server. Returns the `http.Server` (not\n * listening yet) so the caller can attach to a `shutdown()` hook and\n * coordinate the listen() with the WebSocket bootstrap.\n */\nexport function createHttpServer(opts: CreateHttpServerOptions): http.Server {\n const port = opts.port ?? Number.parseInt(process.env['PORT'] ?? '3456', 10);\n const distDir = path.resolve(opts.distDir);\n const wsPort = opts.wsPort;\n // Loopback bind: no API token required (mirrors WS loopback-bootstrap).\n // LAN bind: caller MUST supply a token; we reject any /api request that\n // doesn't present it via `X-WS-Token` (constant-time compared).\n const requireApiToken = !isLoopbackBind(opts.host) && Boolean(opts.apiToken);\n\n return http.createServer(async (req, res) => {\n try {\n const url = new URL(req.url ?? '/', `http://127.0.0.1:${port}`);\n\n // ── API routes ──────────────────────────────────────────────────\n // /ws-auth — exchange a one-shot token (header or query) for an\n // HttpOnly cookie. The browser then sends the cookie on the WS\n // upgrade automatically, closing C-598 (token-in-URL). Disabled\n // when `enableWsCookie: false` (tests, or operators who prefer\n // the URL-token flow for explicit dev).\n if (url.pathname === '/ws-auth' && req.method === 'GET' && (opts.enableWsCookie ?? true)) {\n // Accept the token from `?token=` query (browser navigation\n // from the server-printed URL) OR the `X-WS-Token` header\n // (scripted client).\n const provided =\n url.searchParams.get('token') ?? (req.headers['x-ws-token'] as string | undefined);\n if (!provided || !opts.apiToken || !tokenMatches(provided, opts.apiToken)) {\n res.writeHead(401, { 'Content-Type': 'text/plain' });\n res.end('Unauthorized');\n return;\n }\n // HttpOnly + SameSite=Strict + Path=/ — the cookie is immune to\n // XSS exfiltration (no JS access), cross-origin Referer leakage\n // (Strict blocks cross-site), and is scoped to this origin only.\n // No `Secure` flag: the dev server is plain HTTP on loopback,\n // and a Secure cookie over HTTP would not be sent by the browser.\n res.writeHead(200, {\n 'Content-Type': 'text/plain',\n 'Set-Cookie': `ws_token=${encodeURIComponent(opts.apiToken)}; HttpOnly; SameSite=Strict; Path=/; Max-Age=3600`,\n // Belt-and-braces: tell any caches the cookie response itself\n // is sensitive.\n 'Cache-Control': 'no-store',\n });\n res.end('ok');\n return;\n }\n\n if (url.pathname === '/api/sessions' && req.method === 'GET') {\n // `req.headers['x-ws-token']` is typed as `string | string[] | undefined`\n // because some Node http clients send repeated headers as an array.\n // Pick the first value for the constant-time compare.\n const headerToken = req.headers['x-ws-token'];\n const provided = Array.isArray(headerToken) ? headerToken[0] : headerToken;\n if (requireApiToken && !tokenMatches(provided, opts.apiToken ?? '')) {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Unauthorized' }));\n return;\n }\n await handleApiSessions(res, opts.globalRoot);\n return;\n }\n\n const agentsMatch = url.pathname.match(/^\\/api\\/sessions\\/([^/]+)\\/agents$/);\n if (agentsMatch && req.method === 'GET') {\n const headerToken = req.headers['x-ws-token'];\n const provided = Array.isArray(headerToken) ? headerToken[0] : headerToken;\n if (requireApiToken && !tokenMatches(provided, opts.apiToken ?? '')) {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Unauthorized' }));\n return;\n }\n await handleApiSessionAgents(res, opts.globalRoot, agentsMatch[1]!);\n return;\n }\n\n let filePath: string;\n\n if (url.pathname === '/' || url.pathname === '') {\n filePath = path.join(distDir, 'index.html');\n } else if (url.pathname.startsWith('/assets/')) {\n filePath = path.join(distDir, url.pathname);\n } else if (url.pathname.startsWith('/')) {\n filePath = path.join(distDir, url.pathname);\n } else {\n filePath = path.join(distDir, 'index.html');\n }\n\n // Path traversal guard: the resolved path must stay inside distDir.\n // WHATWG URL leaves percent-encoding alone in `url.pathname` (it\n // does not decode `%2e%2e` to `..`), so percent-encoded escapes\n // are *not* a concern here — but unencoded `..` segments are\n // normalised by `path.resolve` and would walk the candidate up\n // out of distDir. `isInsideDist` catches that.\n const resolvedPath = path.resolve(filePath);\n if (!isInsideDist(resolvedPath, distDir)) {\n res.writeHead(403, { 'Content-Type': 'text/plain' });\n res.end('Forbidden');\n return;\n }\n\n const ext = path.extname(resolvedPath);\n const contentType = MIME_TYPES[ext] ?? 'application/octet-stream';\n res.setHeader('Content-Type', contentType);\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'DENY');\n res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');\n\n if (ext === '.html') {\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Content-Security-Policy', buildCspHeader(wsPort));\n // Stamp the live WS port into the HTML so the frontend dials this\n // instance's backend (not the hardcoded default) — required for\n // running multiple WebUI instances on different ports.\n const html = await fs.readFile(resolvedPath, 'utf8');\n res.writeHead(200);\n res.end(injectWsPort(html, wsPort));\n return;\n }\n\n const fileContent = await fs.readFile(resolvedPath);\n res.writeHead(200);\n res.end(fileContent);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n // SPA fallback: serve index.html so client-side routing still works.\n try {\n const html = await fs.readFile(path.join(distDir, 'index.html'), 'utf8');\n res.writeHead(200, {\n 'Content-Type': 'text/html',\n 'X-Content-Type-Options': 'nosniff',\n 'X-Frame-Options': 'DENY',\n 'Referrer-Policy': 'strict-origin-when-cross-origin',\n 'Content-Security-Policy': buildCspHeader(wsPort),\n });\n res.end(injectWsPort(html, wsPort));\n } catch {\n res.writeHead(404);\n res.end('Not found');\n }\n } else {\n res.writeHead(500);\n res.end('Server error');\n }\n }\n });\n}\n\n// ── API handlers ─────────────────────────────────────────────────────────\n\nasync function handleApiSessions(\n res: http.ServerResponse,\n globalRoot: string | undefined,\n): Promise<void> {\n if (!globalRoot) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'SessionRegistry not available' }));\n return;\n }\n\n try {\n const { SessionRegistry } = await import('@wrongstack/core');\n const registry = new SessionRegistry(globalRoot);\n const sessions = await registry.list();\n\n const result = sessions.map((s) => ({\n sessionId: s.sessionId,\n projectSlug: s.projectSlug,\n projectName: s.projectName,\n projectRoot: s.projectRoot,\n workingDir: s.workingDir,\n status: s.status,\n pid: s.pid,\n startedAt: s.startedAt,\n lastHeartbeatAt: s.lastHeartbeatAt,\n agentCount: s.agentCount,\n agents: s.agents.map((a) => ({\n id: a.id,\n name: a.name,\n status: a.status,\n currentTool: a.currentTool,\n iterations: a.iterations,\n toolCalls: a.toolCalls,\n lastActivityAt: a.lastActivityAt,\n })),\n }));\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(result));\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(err) }));\n }\n}\n\nasync function handleApiSessionAgents(\n res: http.ServerResponse,\n globalRoot: string | undefined,\n sessionId: string,\n): Promise<void> {\n if (!globalRoot) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'SessionRegistry not available' }));\n return;\n }\n\n try {\n const { SessionRegistry } = await import('@wrongstack/core');\n const registry = new SessionRegistry(globalRoot);\n const entry = await registry.get(sessionId);\n\n if (!entry) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Session not found' }));\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({\n sessionId: entry.sessionId,\n projectName: entry.projectName,\n status: entry.status,\n agents: entry.agents.map((a) => ({\n id: a.id,\n name: a.name,\n status: a.status,\n currentTool: a.currentTool,\n iterations: a.iterations,\n toolCalls: a.toolCalls,\n lastActivityAt: a.lastActivityAt,\n })),\n }));\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(err) }));\n }\n}\n","/**\n * WebSocket connection authentication for the WebUI server.\n *\n * Three layered defenses, all enforced in {@link verifyClient}:\n * 1. **DNS-rebinding guard** ({@link hostHeaderOk}) — on a loopback bind the\n * `Host` header must itself be a loopback name, so a rebound attacker page\n * (`Host: evil.com`) is rejected even though its TCP peer is 127.0.0.1.\n * 2. **Shared-token auth** ({@link tokenMatches}, constant-time) — required for\n * any non-loopback origin and for non-browser clients reaching a publicly\n * bound socket. Tokens are accepted via `Cookie: ws_token=…` (preferred;\n * set by the `/ws-auth` HTTP endpoint with HttpOnly+SameSite=Strict) OR\n * `?token=…` URL query param (non-browser fallback).\n * 3. **Loopback bootstrap** — same-machine browser origins are allowed without\n * a token; the Host-header guard above already blocks cross-site pages.\n *\n * Browser clients (those that send an `Origin` header) MAY use either the\n * cookie path (preferred; set via `/ws-auth`) or the URL-token path\n * (legacy, still accepted for backward compat while the frontend migrates).\n * Once the frontend is updated to call `/ws-auth` on startup, the URL\n * token path can be removed entirely, closing the C-598\n * (Information Exposure Through Query String) class. Non-browser\n * clients always use the URL-token path for ergonomics (curl, scripts,\n * tests).\n *\n * Extracted from `index.ts` as pure functions so the auth contract can be unit\n * tested without standing up a real `http.Server`/`WebSocketServer`. `index.ts`\n * builds a thin closure that pulls the fields below off the incoming request.\n */\nimport { Buffer } from 'node:buffer';\nimport { timingSafeEqual } from 'node:crypto';\n\n/** A hostname that refers to the local machine. */\nexport function isLoopbackHostname(hostname: string): boolean {\n return (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '::1' ||\n hostname === '[::1]'\n );\n}\n\n/**\n * Check if an origin is a trusted loopback browser origin.\n * Defense-in-depth: when wsHost=0.0.0.0, only accept explicit localhost origins,\n * not arbitrary loopback hostnames that could be spoofed by local malware.\n */\nfunction isTrustedLoopbackOrigin(origin: string): boolean {\n try {\n const url = new URL(origin);\n // Only allow http(s)://localhost(:PORT) and http(s)://127.0.0.1(:PORT)\n // Reject file://, data://, and other schemes even on loopback.\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return false;\n // Require explicit localhost or 127.0.0.1 (not just any loopback like ::1)\n return url.hostname === 'localhost' || url.hostname === '127.0.0.1';\n } catch {\n return false;\n }\n}\n\n/** True when the server is bound to a loopback interface (vs. LAN/0.0.0.0). */\nexport function isLoopbackBind(wsHost: string): boolean {\n return wsHost === '127.0.0.1' || wsHost === '::1' || wsHost === 'localhost';\n}\n\n/**\n * Constant-time comparison of a provided token against the expected one.\n * A length mismatch short-circuits (lengths aren't secret); equal-length\n * inputs are compared with `timingSafeEqual` so the token can't be recovered\n * byte-by-byte via response timing.\n */\nexport function tokenMatches(provided: string | undefined, expected: string): boolean {\n if (!provided) return false;\n const a = Buffer.from(provided);\n const b = Buffer.from(expected);\n if (a.length !== b.length) return false;\n return timingSafeEqual(a, b);\n}\n\n/** Pull the `token` query param out of a request URL (`/?token=…`). */\nexport function extractToken(url: string): string | undefined {\n const match = url.match(/[?&]token=([^&]+)/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Pull the `ws_token` value out of a Cookie header (`Cookie: ws_token=…`).\n * The WebUI's auth-token cookie is set via `Set-Cookie: ws_token=<token>;\n * HttpOnly; SameSite=Strict; Path=/` from the `/ws-auth` HTTP endpoint. The\n * browser then sends it back automatically on the WS upgrade request —\n * closing the C-598 (Information Exposure Through Query String) class\n * because the token never appears in the URL, browser history, or\n * reverse-proxy access logs.\n *\n * Returns `undefined` if the cookie header is absent or malformed.\n */\nexport function extractTokenFromCookie(cookieHeader: string | string[] | undefined): string | undefined {\n if (!cookieHeader) return undefined;\n const raw = Array.isArray(cookieHeader) ? cookieHeader.join('; ') : cookieHeader;\n for (const part of raw.split(';')) {\n const eq = part.indexOf('=');\n if (eq < 0) continue;\n const name = part.slice(0, eq).trim();\n if (name === 'ws_token') {\n // Cookie values are url-encoded in spec; decode for the constant-time\n // compare downstream. Trim trailing whitespace defensively.\n try {\n return decodeURIComponent(part.slice(eq + 1).trim());\n } catch {\n return part.slice(eq + 1).trim();\n }\n }\n }\n return undefined;\n}\n\n/**\n * DNS-rebinding defense. On a loopback bind, the `Host` header must resolve to\n * a loopback name. When the operator deliberately exposes the socket (wsHost is\n * a LAN/0.0.0.0 address) the Host is legitimately non-loopback, so the guard is\n * skipped and connection auth falls to the token check.\n */\nexport function hostHeaderOk(input: { hostHeader: string | undefined; wsHost: string }): boolean {\n if (!isLoopbackBind(input.wsHost)) return true; // operator opted into wider exposure\n const hostHeader = (input.hostHeader ?? '').trim();\n if (!hostHeader) return false;\n // Strip the port (handle bare host, host:port, and [::1]:port).\n let hostname: string;\n try {\n hostname = new URL(`http://${hostHeader}`).hostname;\n } catch {\n return false;\n }\n return isLoopbackHostname(hostname);\n}\n\nexport interface VerifyClientInput {\n /** Browser `Origin` header, or undefined for non-browser clients. */\n origin?: string | undefined;\n /** Request URL (`req.url`) — carries the `?token=…` query param. */\n url: string;\n /** `Host` header (`req.headers.host`). */\n hostHeader?: string | undefined;\n /** Peer address (`req.socket.remoteAddress`). */\n remoteAddress?: string | undefined;\n /** `Cookie` header (`req.headers.cookie`). Carries `ws_token=…` when the\n * browser went through `/ws-auth` to set the HttpOnly auth cookie. */\n cookieHeader?: string | string[] | undefined;\n /** Host/interface the WS server is bound to. */\n wsHost: string;\n /** The server's generated auth token. */\n expectedToken: string;\n}\n\n/**\n * Decide whether to accept an incoming WebSocket handshake. Pure mirror of the\n * closure previously inlined in `index.ts`; see the module doc for the layered\n * policy. Returns `true` to accept, `false` to reject.\n *\n * Token sources, in priority order:\n * 1. `Cookie: ws_token=…` (browser clients that went through `/ws-auth`)\n * 2. `?token=…` URL query param (non-browser clients: curl, scripts)\n *\n * Browser clients (with an `Origin` header) are restricted to the cookie path —\n * URL token is rejected for them, closing the C-598 query-string token\n * exposure class. Non-browser clients keep the URL-token fallback so curl\n * and tests continue to work.\n */\nexport function verifyClient(input: VerifyClientInput): boolean {\n const { origin, url, hostHeader, remoteAddress, cookieHeader, wsHost, expectedToken } = input;\n const urlToken = extractToken(url ?? '');\n const cookieToken = extractTokenFromCookie(cookieHeader);\n const tokenOk = tokenMatches(urlToken, expectedToken) || tokenMatches(cookieToken, expectedToken);\n\n // DNS-rebinding guard runs first on a loopback bind — independent of token\n // and Origin. Blocks a rebound attacker page (Host = attacker domain) even\n // though the TCP peer is 127.0.0.1.\n if (!hostHeaderOk({ hostHeader, wsHost })) return false;\n\n if (!origin) {\n // Non-browser clients (curl, scripts): require token unless on loopback.\n // When wsHost=0.0.0.0 the server accepts connections from any network\n // interface — a non-loopback peer is denied outright.\n const remoteIp = remoteAddress ?? '';\n const isRemoteLoopback = remoteIp === '127.0.0.1' || remoteIp === '::1';\n if (!isRemoteLoopback && wsHost === '0.0.0.0') return false; // LAN exposure = deny\n return tokenOk || isLoopbackBind(wsHost);\n }\n try {\n const { hostname } = new URL(origin);\n // Loopback browser origins: allow without token only if the origin is\n // explicitly http://localhost or http://127.0.0.1 (defense-in-depth).\n // Reject file://, data://, and other schemes even on loopback.\n if (isLoopbackHostname(hostname)) {\n // For stricter security on 0.0.0.0 binds, require a trusted origin scheme\n if (wsHost === '0.0.0.0' && !isTrustedLoopbackOrigin(origin)) {\n return false;\n }\n return true;\n }\n // Non-loopback browser origin: token is mandatory. Both the cookie\n // path (C-2 fix, preferred — set via /ws-auth) and the URL-token\n // path (legacy, still accepted for backward compat) authenticate\n // the connection. The token in the URL is no longer required —\n // once the frontend is updated to call /ws-auth on startup, it can\n // drop the `?token=` from the WS URL entirely.\n return tokenOk;\n } catch {\n return false;\n }\n}\n","/**\n * Shared file-operation WebSocket handlers for both the standalone WebUI\n * server and the CLI's `--webui` embedded server. Extracted from the\n * duplicated switch cases in `index.ts` and `cli/src/webui-server.ts`.\n *\n * Each function handles the full request→response cycle for one message\n * type. Callers drop them into their switch statement:\n *\n * case 'files.tree': return handleFilesTree(ws, msg, projectRoot);\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { WebSocket } from 'ws';\nimport { atomicWrite } from '@wrongstack/core';\nimport { SKIP_DIRS, isHiddenEntry, rankFiles } from './file-picker.js';\nimport { send, errMessage } from './ws-utils.js';\n\n// ── Type helpers (inlined, no dependence on types.ts) ──\n\ninterface FilesListPayload {\n query?: string | undefined;\n limit?: number | undefined;\n /** Optional directory root for the file list (relative to projectRoot).\n * When set, only files under this directory are returned. */\n path?: string | undefined;\n}\n\ninterface FilesReadPayload {\n filePath: string;\n}\n\ninterface FilesWritePayload {\n filePath: string;\n content: string;\n}\n\n// ── Shared handlers ───────────────────────────────────────────────────\n\n/**\n * Build and send a nested directory tree for the File Explorer.\n *\n * Walks `projectRoot` to depth 10 max, skipping heavyweight dirs\n * (node_modules, .git, dist, …) and dot-entries. Responds with\n * `{ type: 'files.tree', payload: { root, tree } }`.\n */\nexport async function handleFilesTree(\n ws: WebSocket,\n msg: unknown,\n projectRoot: string,\n): Promise<void> {\n interface TreeNode {\n name: string;\n path: string;\n type: 'file' | 'directory';\n children?: TreeNode[];\n }\n\n // Use the optional `path` from the message payload as the tree root.\n // When absent, empty, or \".\", fall back to projectRoot (backward compatible).\n const payload = (msg as { payload?: { path?: string | undefined } }).payload;\n const rawPath = payload?.path?.trim();\n const treeRoot = rawPath && rawPath !== '.'\n ? path.resolve(projectRoot, rawPath)\n : projectRoot;\n\n // Guard: treeRoot must stay inside projectRoot.\n if (!treeRoot.startsWith(projectRoot + path.sep) && treeRoot !== projectRoot) {\n send(ws, {\n type: 'files.tree',\n payload: { root: projectRoot, tree: [], error: 'Path outside project root' },\n });\n return;\n }\n\n // Compute the path prefix so tree paths are always relative to\n // projectRoot (not treeRoot). This ensures double-clicking a file in\n // the explorer sends the correct path to files.read/files.write.\n const pathPrefix = treeRoot === projectRoot\n ? ''\n : (path.relative(projectRoot, treeRoot) + '/').replace(/\\\\/g, '/');\n\n async function buildTree(dir: string, rel: string, depth: number): Promise<TreeNode[]> {\n if (depth > 10) return [];\n let entries: import('node:fs').Dirent[] = [];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n entries.sort((a, b) => {\n if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n const nodes: TreeNode[] = [];\n for (const e of entries) {\n if (isHiddenEntry(e.name)) continue;\n const childRel = rel ? `${rel}/${e.name}` : e.name;\n const childAbs = path.join(dir, e.name);\n // Prepend the workingDir prefix so the path is projectRoot-relative\n const childPath = pathPrefix + childRel;\n if (e.isDirectory()) {\n if (SKIP_DIRS.has(e.name)) continue;\n const children = await buildTree(childAbs, childRel, depth + 1);\n nodes.push({ name: e.name, path: childPath, type: 'directory', children });\n } else if (e.isFile()) {\n nodes.push({ name: e.name, path: childPath, type: 'file' });\n }\n }\n return nodes;\n }\n\n try {\n const tree = await buildTree(treeRoot, '', 0);\n const rootLabel = treeRoot === projectRoot\n ? projectRoot\n : path.relative(projectRoot, treeRoot) || '.';\n send(ws, { type: 'files.tree', payload: { root: rootLabel, tree } });\n } catch (err) {\n const rootLabel = treeRoot === projectRoot\n ? projectRoot\n : path.relative(projectRoot, treeRoot) || '.';\n send(ws, {\n type: 'files.tree',\n payload: { root: rootLabel, tree: [], error: errMessage(err) },\n });\n }\n}\n\n/**\n * Read a file's content for the Monaco editor.\n *\n * Guards against path traversal (`../` escapes). Responds with\n * `{ type: 'files.read', payload: { filePath, content } }`.\n */\nexport async function handleFilesRead(\n ws: WebSocket,\n msg: unknown,\n projectRoot: string,\n): Promise<void> {\n const { filePath } = (msg as { payload: FilesReadPayload }).payload;\n\n // Path traversal guard: resolve and verify the file stays inside projectRoot.\n const resolved = path.resolve(projectRoot, filePath);\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n send(ws, { type: 'files.read', payload: { filePath, content: '', error: 'Forbidden' } });\n return;\n }\n\n try {\n const content = await fs.readFile(resolved, 'utf8');\n send(ws, { type: 'files.read', payload: { filePath, content } });\n } catch (err) {\n send(ws, {\n type: 'files.read',\n payload: { filePath, content: '', error: errMessage(err) },\n });\n }\n}\n\n/**\n * Write file content back to disk (atomic write via tmp + rename).\n *\n * Guards against path traversal. Responds with\n * `{ type: 'files.written', payload: { filePath, success } }`.\n */\nexport async function handleFilesWrite(\n ws: WebSocket,\n msg: unknown,\n projectRoot: string,\n): Promise<void> {\n const { filePath, content } = (msg as { payload: FilesWritePayload }).payload;\n\n // Path traversal guard.\n const resolved = path.resolve(projectRoot, filePath);\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n send(ws, { type: 'files.written', payload: { filePath, success: false, error: 'Forbidden' } });\n return;\n }\n\n try {\n await atomicWrite(resolved, content);\n send(ws, { type: 'files.written', payload: { filePath, success: true } });\n } catch (err) {\n send(ws, {\n type: 'files.written',\n payload: { filePath, success: false, error: errMessage(err) },\n });\n }\n}\n\n/**\n * Lightweight project file picker for the chat `@` mention popup.\n *\n * Walks `projectRoot` (max depth 8), skipping hidden and heavyweight\n * dirs, then fuzzy-ranks results against `query`. Responds with\n * `{ type: 'files.list', payload: { files } }`.\n */\nexport async function handleFilesList(\n ws: WebSocket,\n msg: unknown,\n projectRoot: string,\n): Promise<void> {\n const payload = (msg as { payload?: FilesListPayload }).payload ?? {};\n const limit = payload.limit ?? 50;\n const listRoot = payload.path\n ? path.resolve(projectRoot, payload.path)\n : projectRoot;\n\n // Guard: listRoot must stay inside projectRoot.\n if (!listRoot.startsWith(projectRoot + path.sep) && listRoot !== projectRoot) {\n send(ws, { type: 'files.list', payload: { files: [] } });\n return;\n }\n\n const results: string[] = [];\n\n async function walk(dir: string, rel: string, depth: number): Promise<void> {\n if (depth > 8 || results.length >= 600) return;\n let entries: import('node:fs').Dirent[] = [];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (results.length >= 600) return;\n if (isHiddenEntry(e.name)) continue;\n const childRel = rel ? `${rel}/${e.name}` : e.name;\n if (e.isDirectory()) {\n if (SKIP_DIRS.has(e.name)) continue;\n await walk(path.join(dir, e.name), childRel, depth + 1);\n } else if (e.isFile()) {\n results.push(childRel);\n }\n }\n }\n\n await walk(listRoot, '', 0);\n send(ws, {\n type: 'files.list',\n payload: { files: rankFiles(results, payload.query ?? '', limit) },\n });\n}\n","/**\n * Pure filtering + ranking for the `files.list` project file picker (the chat\n * `@`-mention popup). The directory *walk* stays in index.ts (it's I/O), but\n * the two decisions that shape the result — which entries to hide and how to\n * rank matches — are pure and live here so the scoring weights, depth penalty,\n * and tie-break order can be unit tested. A silently-flipped weight would make\n * the picker feel subtly wrong with nothing to catch it.\n */\n/** Heavyweight build/vcs/dependency dirs the picker never descends into. */\nexport const SKIP_DIRS: ReadonlySet<string> = new Set([\n '.git',\n 'node_modules',\n 'dist',\n 'build',\n '.next',\n '.turbo',\n '.cache',\n 'target',\n 'coverage',\n '.nyc_output',\n 'out',\n '.pnpm-store',\n '.parcel-cache',\n]);\n\n/** Dotfiles/dirs kept despite the hide-dotfiles-by-default rule. */\nconst KEEP_DOTFILES: ReadonlySet<string> = new Set([\n '.wrongstack',\n '.env.example',\n '.gitignore',\n '.eslintrc',\n '.prettierrc',\n]);\n\n/**\n * Whether a directory entry should be hidden from the picker by its name.\n * Dotfiles are hidden by default, except a few commonly-wanted ones.\n */\nexport function isHiddenEntry(name: string): boolean {\n return name.startsWith('.') && !KEEP_DOTFILES.has(name);\n}\n\n/**\n * Rank `paths` against `query` and return up to `limit` paths, best first.\n *\n * Scoring (cheap heuristic, good enough for a picker): exact basename match\n * (100) > basename prefix (60) > path substring (20); non-matches are dropped.\n * Each match is penalized by its path depth so root files sort first. Ties\n * break by lexicographic path. An empty query keeps every path (score 0), so\n * the result is the paths sorted lexicographically, capped to `limit`.\n */\nexport function rankFiles(paths: readonly string[], query: string, limit: number): string[] {\n const q = query.toLowerCase();\n const scored: Array<{ path: string; score: number }> = [];\n for (const p of paths) {\n if (!q) {\n scored.push({ path: p, score: 0 });\n continue;\n }\n const lower = p.toLowerCase();\n const base = lower.split('/').pop() ?? lower;\n let score = 0;\n if (base === q) score = 100;\n else if (base.startsWith(q)) score = 60;\n else if (lower.includes(q)) score = 20;\n else continue;\n // Penalise depth so root files come first.\n score -= p.split('/').length;\n scored.push({ path: p, score });\n }\n scored.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));\n return scored.slice(0, limit).map((s) => s.path);\n}\n","/**\n * Shared WebSocket utilities for both the standalone WebUI server and the\n * CLI's `--webui` embedded server. Extracted from the duplicated `send` /\n * `broadcast` / `sendResult` / `generateAuthToken` patterns that were\n * copy-pasted between `packages/webui/src/server/index.ts` and\n * `packages/cli/src/webui-server.ts`.\n */\nimport { randomBytes } from 'node:crypto';\n// Value import (not `import type`): we reference `WebSocket.OPEN` below, which\n// is a runtime value, not just a type.\nimport { WebSocket } from 'ws';\nimport type { ConnectedClient, WSServerMessage } from './types.js';\n\n/**\n * Send a JSON message to a single WebSocket client.\n * No-op when the socket is not in OPEN state (disconnected / closing).\n */\nexport function send(ws: WebSocket, msg: WSServerMessage): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n}\n\n/**\n * Broadcast a JSON message to every connected client.\n * Swallows per-socket send errors — a client that disconnected between the\n * readyState check and `ws.send()` is cleaned up by its own `close` handler.\n */\nexport function broadcast(\n clients: Map<WebSocket, ConnectedClient>,\n msg: WSServerMessage,\n): void {\n const data = JSON.stringify(msg);\n for (const [ws] of clients) {\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(data);\n } catch {\n // Client disconnected between the readyState check and the send —\n // let the 'close' handler remove it from the map naturally.\n }\n }\n }\n}\n\n/**\n * Send a success/failure result message (used by key.* and provider.* handlers).\n * The frontend expects `key.operation_result` with `{ success, message }`.\n */\nexport function sendResult(ws: WebSocket, success: boolean, message: string): void {\n send(ws, { type: 'key.operation_result', payload: { success, message } });\n}\n\n/**\n * Extract a human-readable message from an unknown thrown value.\n */\nexport function errMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Generate a cryptographically random WebSocket auth token (hex string).\n * Shared between standalone and CLI-embedded WebUI servers.\n */\nexport function generateAuthToken(): string {\n return randomBytes(16).toString('hex');\n}\n","/**\n * Shared memory-operation WebSocket handlers for both the standalone WebUI\n * server and the CLI's `--webui` embedded server. Extracted from the\n * duplicated switch cases in `index.ts` and `cli/src/webui-server.ts`.\n *\n * Each function handles the full request→response cycle for one message\n * type. Callers drop them into their switch statement:\n *\n * case 'memory.list': return handleMemoryList(ws, memoryStore);\n */\n\nimport type { WebSocket } from 'ws';\nimport type { MemoryStore } from '@wrongstack/core';\nimport { send, sendResult, errMessage } from './ws-utils.js';\n\n// ── Shared handlers ───────────────────────────────────────────────────\n\n/**\n * List all memory entries across all scopes.\n * Responds with `{ type: 'memory.list', payload: { text } }`.\n */\nexport async function handleMemoryList(\n ws: WebSocket,\n memoryStore: MemoryStore,\n): Promise<void> {\n try {\n const text = await memoryStore.readAll();\n send(ws, { type: 'memory.list', payload: { text } });\n } catch (err) {\n send(ws, {\n type: 'memory.list',\n payload: { text: '', error: errMessage(err) },\n });\n }\n}\n\n/**\n * Persist a new memory entry.\n * Responds with `{ type: 'key.operation_result', payload: { success, message } }`.\n */\nexport async function handleMemoryRemember(\n ws: WebSocket,\n msg: unknown,\n memoryStore: MemoryStore,\n): Promise<void> {\n const { text, scope } = (\n msg as {\n payload: {\n text: string;\n scope?: 'project-agents' | 'project-memory' | 'user-memory' | undefined;\n };\n }\n ).payload;\n try {\n await memoryStore.remember(text, scope ?? 'project-memory');\n sendResult(ws, true, 'Saved to memory');\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n}\n\n/**\n * Remove memory entries matching the given text.\n * Responds with `{ type: 'key.operation_result', payload: { success, message } }`.\n */\nexport async function handleMemoryForget(\n ws: WebSocket,\n msg: unknown,\n memoryStore: MemoryStore,\n): Promise<void> {\n const { text, scope } = (\n msg as {\n payload: {\n text: string;\n scope?: 'project-agents' | 'project-memory' | 'user-memory' | undefined;\n };\n }\n ).payload;\n try {\n const removed = await memoryStore.forget(text, scope ?? 'project-memory');\n sendResult(\n ws,\n removed > 0,\n removed > 0\n ? `Removed ${removed} entr${removed === 1 ? 'y' : 'ies'}`\n : 'No matching entries',\n );\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n}\n","import {\n type Config,\n Container,\n DefaultConfigStore,\n DefaultErrorHandler,\n DefaultMemoryStore,\n DefaultModeStore,\n DefaultPermissionPolicy,\n DefaultRetryPolicy,\n DefaultSecretScrubber,\n DefaultSessionStore,\n DefaultSkillLoader,\n DefaultSystemPromptBuilder,\n DefaultTokenCounter,\n type EventBus,\n createStrategyCompactor,\n buildRecoveryStrategies,\n type Logger,\n type ModelsRegistry,\n TOKENS,\n type Tool,\n type WstackPaths,\n} from '@wrongstack/core';\nimport type { DefaultSystemPromptBuilderOptions } from '@wrongstack/core';\n\nexport interface CreateContainerOptions {\n config: Config;\n wpaths: WstackPaths;\n logger: Logger;\n modelsRegistry: ModelsRegistry;\n /**\n * Optional event bus — passed to DefaultMemoryStore so plugins and\n * subsystems can react to memory mutations in real time.\n */\n events?: EventBus | undefined;\n permission?: {\n yolo?: boolean | undefined;\n yoloDestructive?: boolean | undefined;\n /** @deprecated Use `yoloDestructive`. */\n forceAllYolo?: boolean | undefined;\n /** When true, destructive ops prompt even in YOLO mode. */\n confirmDestructive?: boolean | undefined;\n promptDelegate?: (\n tool: Tool,\n input: unknown,\n suggestedPattern: string,\n ) => Promise<'yes' | 'no' | 'always' | 'deny'>;\n };\n compactor?: { preserveK?: number | undefined; eliseThreshold?: number | undefined };\n systemPrompt?: Partial<DefaultSystemPromptBuilderOptions> | undefined;\n /** Bundled skills directory path (resolved at boot time). */\n bundledSkillsDir?: string | undefined;\n}\n\n/**\n * Create a Container pre-bound with all default service implementations.\n * Both CLI and WebUI use this factory so container wiring stays in one place.\n */\nexport function createDefaultContainer(opts: CreateContainerOptions): Container {\n const { config, wpaths, logger, modelsRegistry } = opts;\n const container = new Container();\n\n const configStore = new DefaultConfigStore(config);\n container.bind(TOKENS.ConfigStore, () => configStore);\n container.bind(TOKENS.Logger, () => logger);\n container.bind(TOKENS.SecretScrubber, () => new DefaultSecretScrubber());\n container.bind(TOKENS.RetryPolicy, () => new DefaultRetryPolicy());\n // Wire the compactor into the recovery chain so a 413 / context-overflow\n // response can shed tokens and retry. Without an explicit compactor the\n // default `context_overflow_reduce` strategy is a no-op. The Compactor\n // binding is resolved lazily (it is registered further below).\n container.bind(\n TOKENS.ErrorHandler,\n () =>\n new DefaultErrorHandler(\n buildRecoveryStrategies({\n compactor: container.resolve(TOKENS.Compactor),\n modelsRegistry,\n }),\n ),\n );\n container.bind(TOKENS.ModelsRegistry, () => modelsRegistry);\n container.bind(\n TOKENS.TokenCounter,\n () => new DefaultTokenCounter({ registry: modelsRegistry, providerId: config.provider }),\n );\n\n const modeStore = new DefaultModeStore({ directory: wpaths.configDir });\n container.bind(TOKENS.ModeStore, () => modeStore);\n container.bind(\n TOKENS.SessionStore,\n () =>\n new DefaultSessionStore({\n dir: wpaths.projectSessions,\n // Scrub secrets out of persisted user/model turns (F-06). Tool output\n // is already scrubbed by the executor.\n secretScrubber: container.resolve(TOKENS.SecretScrubber),\n }),\n );\n\n const memoryStore = new DefaultMemoryStore({ paths: wpaths, events: opts.events });\n container.bind(TOKENS.MemoryStore, () => memoryStore);\n\n const skillLoader = new DefaultSkillLoader({ paths: wpaths, bundledDir: opts.bundledSkillsDir });\n container.bind(TOKENS.SkillLoader, () => skillLoader);\n\n if (opts.systemPrompt) {\n container.bind(\n TOKENS.SystemPromptBuilder,\n () => new DefaultSystemPromptBuilder(opts.systemPrompt as DefaultSystemPromptBuilderOptions),\n );\n }\n\n container.bind(\n TOKENS.PermissionPolicy,\n () => {\n const policyOptions: ConstructorParameters<typeof DefaultPermissionPolicy>[0] = {\n trustFile: wpaths.projectTrust,\n yolo: opts.permission?.yolo ?? false,\n yoloDestructive: opts.permission?.yoloDestructive ?? opts.permission?.forceAllYolo ?? false,\n confirmDestructive: opts.permission?.confirmDestructive ?? false,\n };\n if (opts.permission?.promptDelegate !== undefined) {\n policyOptions.promptDelegate = opts.permission.promptDelegate;\n }\n return new DefaultPermissionPolicy(policyOptions);\n },\n );\n\n container.bind(\n TOKENS.Compactor,\n () =>\n // Strategy comes from config.context.strategy: 'hybrid' (default, lossless\n // rules, no LLM), 'intelligent' (LLM summarization), or 'selective'\n // (LLM-driven selection). The LLM strategies resolve their provider from\n // ctx at compact()-time, so binding here (before context.provider exists)\n // is safe. preserveK / eliseThreshold are class-level fallbacks; the active\n // ContextWindowPolicy in ctx.meta normally overrides both at runtime.\n // eliseThreshold is a TOKEN COUNT — a previous value of 0.7 elided\n // essentially every tool_result (anything > 1 token).\n createStrategyCompactor({\n strategy: config.context?.strategy,\n preserveK: opts.compactor?.preserveK ?? 10,\n eliseThreshold: opts.compactor?.eliseThreshold ?? 2000,\n smart: true,\n summarizerModel: config.context?.summarizerModel,\n llmSelector: config.context?.llmSelector,\n }),\n );\n\n return container;\n}\n","import {\n type Config,\n type DefaultLogger,\n type DefaultSecretVault,\n type WstackPaths,\n bootConfig as coreBootConfig,\n} from '@wrongstack/core';\n\nexport interface BootResult {\n config: Config;\n vault: DefaultSecretVault;\n globalConfigPath: string;\n projectRoot: string;\n wpaths: WstackPaths;\n logger: InstanceType<typeof DefaultLogger>;\n}\n\n/**\n * Thin WebUI wrapper over the canonical `bootConfig` in `@wrongstack/core`\n * (mirrors packages/cli/src/boot-config.ts). All real boot behavior — wstack\n * path resolution, the AES-GCM `DefaultSecretVault`, plaintext-secret\n * migration, and config load/merge — lives in core so the WebUI server and the\n * CLI can't drift. Only the secret-migration notice label (`WebUI`) differs.\n */\nexport async function bootConfig(): Promise<BootResult> {\n const { config, vault, globalConfigPath, projectRoot, wpaths, logger } = await coreBootConfig({\n appLabel: 'WebUI',\n });\n return { config, vault, globalConfigPath, projectRoot, wpaths, logger };\n}\n\nexport function patchConfig(config: Config, updates: Partial<Config>): Config {\n return Object.freeze({ ...config, ...updates });\n}\n","import { spawnSync } from 'node:child_process';\nimport type { WebSocket } from 'ws';\nimport {\n AutoPhasePlanner,\n PhaseGraphBuilder,\n PhaseOrchestrator,\n PhaseStore,\n WorktreeManager,\n type PhaseGraph,\n type PhaseTemplate,\n} from '@wrongstack/core';\nimport type { Agent, Context, EventBus, Logger } from '@wrongstack/core';\n\nfunction isGitRepo(cwd: string): boolean {\n try {\n const r = spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd, encoding: 'utf8', windowsHide: true });\n return r.status === 0 && r.stdout.trim() === 'true';\n } catch {\n return false;\n }\n}\n\ninterface WSClient {\n ws: WebSocket;\n id: string;\n}\n\ninterface AutoPhaseWSMessage {\n type: string;\n payload?: Record<string, unknown>;\n}\n\n/**\n * AutoPhaseWebSocketHandler — WebSocket-based AutoPhase control.\n *\n * Message types:\n * autophase.start → { title, phases?, autonomous? }\n * autophase.pause → {}\n * autophase.resume → {}\n * autophase.stop → {}\n * autophase.status → {}\n * autophase.selectPhase → { phaseId }\n * autophase.taskStatus → { taskId, status }\n */\nexport class AutoPhaseWebSocketHandler {\n private orchestrator: PhaseOrchestrator | null = null;\n private graph: PhaseGraph | null = null;\n private store: PhaseStore;\n private clients = new Set<WSClient>();\n private broadcastInterval: ReturnType<typeof setInterval> | null = null;\n /** Aborts in-flight task agents when the run is stopped. */\n private abort: AbortController | null = null;\n /** Optional per-phase git-worktree isolation (lazily created at start). */\n private worktrees: WorktreeManager | null = null;\n\n constructor(\n private agent: Agent,\n private context: Context,\n private logger: Logger,\n storeDir: string,\n private events?: EventBus | undefined,\n private projectRoot?: string | undefined,\n ) {\n this.store = new PhaseStore({ baseDir: storeDir });\n }\n\n addClient(ws: WebSocket): void {\n const client: WSClient = { ws, id: crypto.randomUUID() };\n this.clients.add(client);\n\n ws.on('close', () => this.clients.delete(client));\n ws.on('error', () => this.clients.delete(client));\n\n // Send current state\n this.sendState(client);\n }\n\n async handleMessage(msg: AutoPhaseWSMessage): Promise<void> {\n switch (msg.type) {\n case 'autophase.start':\n await this.handleStart(msg.payload);\n break;\n case 'autophase.pause':\n this.orchestrator?.pause();\n this.broadcast({ type: 'autophase.paused', payload: {} });\n break;\n case 'autophase.resume':\n this.orchestrator?.resume();\n this.broadcast({ type: 'autophase.resumed', payload: {} });\n break;\n case 'autophase.stop':\n this.abort?.abort();\n this.orchestrator?.stop();\n this.stopBroadcast();\n if (this.graph) void this.store.save(this.graph);\n this.broadcast({ type: 'autophase.stopped', payload: {} });\n break;\n case 'autophase.status':\n this.broadcastState();\n break;\n case 'autophase.selectPhase': {\n const phaseId = msg.payload?.phaseId as string;\n if (phaseId && this.graph) {\n this.broadcastState(phaseId);\n }\n break;\n }\n case 'autophase.taskStatus': {\n const { taskId, status } = msg.payload as { taskId: string; status: string };\n await this.handleTaskStatusChange(taskId, status);\n break;\n }\n case 'autophase.toggleAutonomous': {\n const autonomous = (msg.payload?.autonomous as boolean) ?? !this.graph?.autonomous;\n if (this.graph) {\n this.graph.autonomous = autonomous;\n await this.store.save(this.graph);\n this.broadcast({ type: 'autophase.state', payload: this.buildState() });\n }\n break;\n }\n case 'autophase.save': {\n if (this.graph) {\n await this.store.save(this.graph);\n this.broadcast({ type: 'autophase.saved', payload: { graphId: this.graph.id } });\n }\n break;\n }\n case 'autophase.list': {\n const graphs = await this.store.list();\n this.broadcast({ type: 'autophase.list', payload: { graphs } });\n break;\n }\n case 'autophase.load': {\n const graphId = msg.payload?.graphId as string | undefined;\n if (graphId) {\n const graph = await this.store.load(graphId);\n if (graph) {\n this.graph = graph;\n this.broadcast({ type: 'autophase.state', payload: this.buildState() });\n } else {\n this.broadcast({ type: 'autophase.error', payload: { message: `Graph not found: ${graphId}` } });\n }\n }\n break;\n }\n }\n }\n\n private async handleStart(payload?: Record<string, unknown>): Promise<void> {\n const title = (payload?.goal as string) || (payload?.title as string) || 'Untitled Project';\n const autonomous = (payload?.autonomous as boolean) ?? true;\n\n // Phase plan resolution:\n // 1. explicit phases in the payload win (caller override);\n // 2. otherwise the LLM plans phases+todos for the goal;\n // 3. failing that, fall back to the generic default phases.\n const phases = Array.isArray(payload?.phases)\n ? (payload.phases as PhaseTemplate[])\n : await this.planPhases(title);\n\n this.logger.info(`[AutoPhase] Starting: ${title}`);\n\n // Build the graph up-front so we have a reference for live broadcasts and\n // persistence *before* the (long-running) build begins.\n const graph = await new PhaseGraphBuilder({ title, phases, autonomous }).build();\n this.graph = graph;\n this.abort = new AbortController();\n await this.store.save(graph);\n\n // Per-phase git-worktree isolation, when enabled and inside a git repo.\n // The shared agent/context means we can't run phases in parallel here\n // (we swap a single context.cwd per task), so phases stay sequential —\n // but each phase still commits + squash-merges back through its own\n // worktree, and the lifecycle events drive the live swim-lane/DAG view.\n if (\n !this.worktrees &&\n this.events &&\n this.projectRoot &&\n process.env['WRONGSTACK_AUTOPHASE_WORKTREES'] !== '0' &&\n isGitRepo(this.projectRoot)\n ) {\n this.worktrees = new WorktreeManager({ projectRoot: this.projectRoot, events: this.events });\n }\n\n this.orchestrator = new PhaseOrchestrator({\n graph,\n ctx: {\n executeTask: async (task, phaseId, env) => {\n this.logger.info(`[AutoPhase] [${phaseId}] Executing: ${task.title}`);\n const result = await this.executeTaskWithAgent(task, phaseId, env);\n this.logger.info(`[AutoPhase] [${phaseId}] Completed: ${task.title}`);\n return result;\n },\n onPhaseComplete: (phase) => {\n this.logger.info(`[AutoPhase] Phase completed: ${phase.name}`);\n void this.store.save(graph);\n this.broadcastState();\n },\n onPhaseFail: (phase, error) => {\n this.logger.error(`[AutoPhase] Phase failed: ${phase.name} — ${error.message}`);\n void this.store.save(graph);\n this.broadcastState();\n },\n },\n worktrees: this.worktrees ?? undefined,\n autonomous,\n // Must stay 1: phase tasks run on the single shared context whose cwd we\n // swap per phase, so parallel phases would race on context.cwd.\n maxConcurrentPhases: 1,\n // Sequential within a phase: each todo is a full-tool agent editing the\n // phase worktree, so running two at once risks concurrent writes.\n maxConcurrentTasks: 1,\n });\n\n // Start the live broadcast immediately, then run the orchestrator in the\n // background. Awaiting start() would block until the *entire* build\n // finishes — the periodic broadcast (below) reads the mutating graph, so\n // clients see live progress while it runs.\n this.startBroadcast();\n this.broadcastState();\n\n void this.orchestrator\n .start()\n .then(() => {\n this.orchestrator?.stop(); // clear the autonomous tick interval\n void this.store.save(graph);\n this.stopBroadcast();\n const failed = graph.failedPhaseIds.length > 0;\n this.broadcast(\n failed\n ? { type: 'autophase.failed', payload: { title } }\n : { type: 'autophase.completed', payload: { title } },\n );\n this.broadcastState();\n })\n .catch((err: unknown) => {\n this.logger.error(`[AutoPhase] Aborted: ${err instanceof Error ? err.message : String(err)}`);\n this.stopBroadcast();\n this.broadcast({ type: 'autophase.failed', payload: { title, error: String(err) } });\n });\n }\n\n /** Generic fallback phases when the LLM planner produces nothing usable. */\n private defaultPhases(): PhaseTemplate[] {\n return [\n { name: 'Discovery', description: 'Requirements gathering', priority: 'high', estimateHours: 2, parallelizable: false },\n { name: 'Design', description: 'Architecture and design', priority: 'critical', estimateHours: 4, parallelizable: false },\n { name: 'Implementation', description: 'Core development', priority: 'critical', estimateHours: 12, parallelizable: false },\n { name: 'Testing', description: 'Unit and integration tests', priority: 'high', estimateHours: 6, parallelizable: true },\n { name: 'Deployment', description: 'Deploy to production', priority: 'medium', estimateHours: 2, parallelizable: false },\n ];\n }\n\n /** Plan phases+todos for the goal via the LLM; fall back to defaults on failure. */\n private async planPhases(goal: string): Promise<PhaseTemplate[]> {\n try {\n const planner = new AutoPhasePlanner({\n goal,\n runOnce: async (prompt) => {\n const result = (await this.agent.run(prompt, { signal: new AbortController().signal })) as {\n status: string;\n finalText?: string | undefined;\n };\n return result.status === 'done' ? (result.finalText ?? '') : '';\n },\n });\n const { phases, parseFailed } = await planner.plan();\n if (!parseFailed && phases.length > 0) {\n const todos = phases.reduce((n, p) => n + (p.taskTemplates?.length ?? 0), 0);\n this.logger.info(`[AutoPhase] Planned ${phases.length} phases / ${todos} todos for: ${goal}`);\n return phases;\n }\n this.logger.info(`[AutoPhase] Planner produced no phases; using defaults for: ${goal}`);\n } catch (err) {\n this.logger.error(`[AutoPhase] Planning failed, using defaults: ${err instanceof Error ? err.message : String(err)}`);\n }\n return this.defaultPhases();\n }\n\n private async executeTaskWithAgent(\n task: import('@wrongstack/core').TaskNode,\n phaseId: string,\n env?: { cwd?: string | undefined; branch?: string | undefined },\n ): Promise<unknown> {\n // Execute task with agent\n const prompt = `Execute task: ${task.title}\\n\\nDescription: ${task.description}\\nPhase: ${phaseId}\\nPriority: ${task.priority}\\nType: ${task.type}`;\n const signal = this.abort?.signal ?? new AbortController().signal;\n // Redirect the shared context's cwd at the phase worktree for the duration\n // of this task. Safe because phases/tasks run strictly sequentially here;\n // tools read `ctx.cwd` live, so the agent operates inside the worktree.\n const prevCwd = this.context.cwd;\n if (env?.cwd) this.context.cwd = env.cwd;\n try {\n return await this.agent.run(prompt, { signal });\n } finally {\n this.context.cwd = prevCwd;\n }\n }\n\n private async handleTaskStatusChange(taskId: string, status: string): Promise<void> {\n if (!this.graph) return;\n\n for (const phase of this.graph.phases.values()) {\n const task = phase.taskGraph.nodes.get(taskId);\n if (task) {\n task.status = status as import('@wrongstack/core').TaskStatus;\n task.updatedAt = Date.now();\n this.broadcastState();\n return;\n }\n }\n }\n\n private startBroadcast(): void {\n if (this.broadcastInterval) return;\n this.broadcastInterval = setInterval(() => {\n const progress = this.orchestrator?.getProgress();\n if (progress) this.broadcast({ type: 'autophase.progress', payload: progress });\n this.broadcastState();\n }, 2000);\n }\n\n private stopBroadcast(): void {\n if (this.broadcastInterval) {\n clearInterval(this.broadcastInterval);\n this.broadcastInterval = null;\n }\n }\n\n private broadcastState(activePhaseId?: string): void {\n if (!this.graph) return;\n\n const state = this.buildState(activePhaseId);\n this.broadcast({ type: 'autophase.state', payload: state });\n }\n\n private buildState(activePhaseId?: string): Record<string, unknown> {\n if (!this.graph) {\n return { phases: [], tasks: [], overallPercent: 0, autonomous: true, title: '' };\n }\n\n const phases = Array.from(this.graph.phases.values());\n const currentActiveId = activePhaseId || phases.find((p) => p.status === 'running')?.id || phases[0]?.id || '';\n const activePhase = this.graph.phases.get(currentActiveId);\n\n const totalTasks = phases.reduce((sum, p) => sum + p.taskGraph.nodes.size, 0);\n const completedTasks = phases.reduce(\n (sum, p) => sum + Array.from(p.taskGraph.nodes.values()).filter((t) => t.status === 'completed').length,\n 0,\n );\n\n const phaseItems = phases.map((p) => ({\n id: p.id,\n name: p.name,\n description: p.description,\n status: p.status,\n priority: p.priority,\n estimateHours: p.estimateHours,\n actualDurationMs: p.actualDurationMs,\n startedAt: p.startedAt,\n completedAt: p.completedAt,\n progressPercent: p.taskGraph.nodes.size > 0\n ? Math.round((Array.from(p.taskGraph.nodes.values()).filter((t) => t.status === 'completed').length / p.taskGraph.nodes.size) * 100)\n : 0,\n taskCount: p.taskGraph.nodes.size,\n completedTasks: Array.from(p.taskGraph.nodes.values()).filter((t) => t.status === 'completed').length,\n assignedAgents: p.assignedAgents,\n isActive: p.id === currentActiveId,\n }));\n\n const taskItems = activePhase\n ? Array.from(activePhase.taskGraph.nodes.values()).map((t) => ({\n id: t.id,\n title: t.title,\n description: t.description,\n status: t.status,\n priority: t.priority,\n type: t.type,\n estimateHours: t.estimateHours,\n actualHours: t.actualHours,\n assignee: t.assignee,\n tags: t.tags || [],\n startedAt: t.startedAt,\n completedAt: t.completedAt,\n }))\n : [];\n\n const completedPhases = phases.filter((p) => p.status === 'completed').length;\n\n return {\n title: this.graph.title,\n phases: phaseItems,\n tasks: taskItems,\n activePhaseId: currentActiveId,\n overallPercent: phases.length > 0 ? Math.round((completedPhases / phases.length) * 100) : 0,\n autonomous: this.graph.autonomous,\n totalTasks,\n completedTasks,\n };\n }\n\n private sendState(client: WSClient): void {\n if (!this.graph) return;\n const state = this.buildState();\n this.send(client, { type: 'autophase.state', payload: state });\n }\n\n private broadcast(msg: { type: string; payload: unknown }): void {\n const data = JSON.stringify(msg);\n for (const client of this.clients) {\n if (client.ws.readyState === 1) { // OPEN\n client.ws.send(data);\n }\n }\n }\n\n private send(client: WSClient, msg: { type: string; payload: unknown }): void {\n if (client.ws.readyState === 1) {\n client.ws.send(JSON.stringify(msg));\n }\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport type { WebSocket } from 'ws';\nimport type { CollaborationBus, EventBus, Logger } from '@wrongstack/core';\nimport type { AnnotationsStore, SessionReader } from '@wrongstack/core/storage';\nimport type {\n CollabRole,\n WSCollabParticipantJoined,\n WSCollabParticipantLeft,\n WSCollabState,\n WSServerMessage,\n} from '../types.js';\n\n/** How many historical events to replay to a late-joining observer. */\nconst REPLAY_LIMIT = 50;\n\n/** How long the middleware waits before auto-resuming (mirrors the middleware default). */\nconst PAUSE_TIMEOUT_MS = 60_000;\n\n/**\n * CollaborationWebSocketHandler — passive read-only session observer (Phase 1\n * of idea #13 from IDEAS.md). Mirrors `WorktreeWebSocketHandler` and\n * `AutoPhaseWebSocketHandler`.\n *\n * Capabilities in this phase:\n * - A second human (or any client) joins an active agent run as an\n * `observer` and receives a live mirror of the kernel's iteration /\n * tool / subagent events.\n * - The observer declares a `sessionId` on join (used for state scoping\n * and future replay-on-join). Live event routing is session-agnostic\n * for now — see the limitation note below.\n * - The observer can leave at any time; cleanup runs on WS close/error.\n * - The observer CANNOT modify the agent's state, pause it, or inject\n * tool calls. Those capabilities land in Phase 2/3.\n *\n * Limitation (documented, acceptable for Phase 1):\n * The webui server multiplexes every active session onto a single\n * EventBus, and most event payloads (`tool.started`, `iteration.*`,\n * `subagent.*`) do NOT carry a `sessionId` field. The webui's primary\n * WS path works because it is the only consumer and assumes one\n * active session at a time. We mirror that assumption here. When a\n * future multi-session \"session router\" lands, this handler will be\n * upgraded to filter by sessionId.\n *\n * Protocol additions (see `packages/webui/src/types.ts`):\n * client → server: collab.join { sessionId, role: 'observer' }\n * collab.leave { sessionId }\n * server → client: collab.state (initial + 2s periodic)\n * collab.participant.joined\n * collab.participant.left\n * collab.event (live kernel event mirror)\n */\nexport class CollaborationWebSocketHandler {\n private readonly clients = new Set<WebSocket>();\n /** sessionId → participants currently watching it. */\n private readonly bySession = new Map<string, Set<Participant>>();\n private broadcastInterval: ReturnType<typeof setInterval> | null = null;\n private readonly offs: Array<() => void> = [];\n\n constructor(\n private readonly events: EventBus,\n private readonly logger: Logger,\n /**\n * Optional reader over the on-disk session log. When provided, late\n * joiners receive the last `REPLAY_LIMIT` events of the joined\n * session before live mirroring begins. Without a reader, joining\n * is still allowed — the observer simply starts from \"now\" with no\n * historical context.\n */\n private readonly reader?: SessionReader | undefined,\n /**\n * Optional sidecar store for collaboration annotations. Required\n * for the `annotator` role — without it, `collab.annotate` messages\n * are rejected with an error.\n */\n private readonly annotations?: AnnotationsStore | undefined,\n /**\n * Optional kernel-level pause/resume bus. Required for the\n * `controller` role — without it, `collab.request_pause` is rejected\n * with an error. Wired to the agent's `toolCall` pipeline via\n * `collabPauseMiddleware` in the webui server boot.\n */\n private readonly bus?: CollaborationBus | undefined,\n ) {\n this.subscribe();\n }\n\n // ── Public API (called by server/index.ts per WS connection) ───────────\n\n addClient(ws: WebSocket): void {\n this.clients.add(ws);\n this.ensureBroadcast();\n ws.on('close', () => this.handleDisconnect(ws));\n ws.on('error', () => this.handleDisconnect(ws));\n }\n\n dispose(): void {\n for (const off of this.offs) off();\n this.offs.length = 0;\n this.stopBroadcast();\n }\n\n // ── Inbound client messages ────────────────────────────────────────────\n\n /**\n * Dispatch a parsed client message. Returns true when the message was\n * recognized and handled; false when the caller should ignore / log.\n * Phase 1 only knows `collab.join` and `collab.leave`; unknown types\n * return false so the upstream router can decide.\n */\n handleMessage(\n ws: WebSocket,\n msg: { type: string; payload?: unknown | undefined },\n ): boolean {\n if (msg.type === 'collab.join') {\n const payload = msg.payload as { sessionId?: string | undefined; role?: CollabRole | undefined } | undefined;\n if (!payload?.sessionId) {\n this.send(ws, this.errorMessage('collab.join requires sessionId'));\n return true;\n }\n // The `role` field is accepted on the wire for forward-compat;\n // 'controller' (Phase 3) is not yet wired and is rejected here.\n this.join(ws, payload.sessionId, payload.role ?? 'observer');\n return true;\n }\n if (msg.type === 'collab.leave') {\n this.leave(ws);\n return true;\n }\n if (msg.type === 'collab.annotate') {\n void this.handleAnnotate(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.resolve') {\n void this.handleResolve(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.request_pause') {\n void this.handleRequestPause(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.resume') {\n void this.handleResume(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.grant_control') {\n void this.handleGrantControl(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.inject_tool') {\n void this.handleInjectTool(ws, msg.payload);\n return true;\n }\n return false;\n }\n\n // ── Join / leave flow ──────────────────────────────────────────────────\n\n private join(ws: WebSocket, sessionId: string, role: CollabRole): void {\n if (role === 'controller' && !this.bus) {\n this.send(\n ws,\n this.errorMessage(\n `role 'controller' is not available: server has no CollaborationBus`,\n ),\n );\n return;\n }\n if (role === 'annotator' && !this.annotations) {\n this.send(\n ws,\n this.errorMessage(\n `role 'annotator' is not available: server has no annotations store`,\n ),\n );\n return;\n }\n const participant: Participant = {\n participantId: randomUUID(),\n ws,\n sessionId,\n role,\n joinedAt: new Date().toISOString(),\n };\n let bucket = this.bySession.get(sessionId);\n if (!bucket) {\n bucket = new Set();\n this.bySession.set(sessionId, bucket);\n }\n bucket.add(participant);\n\n // Per-participant hello: send the current state snapshot immediately\n // so the new observer knows who else is watching. Then broadcast the\n // join event AND a fresh state to every participant (including the\n // newcomer) so existing observers see the updated count without\n // waiting for the 2s timer.\n this.send(ws, this.stateMessage(sessionId));\n this.broadcast(sessionId, {\n type: 'collab.participant.joined',\n payload: {\n participantId: participant.participantId,\n sessionId,\n role,\n joinedAt: participant.joinedAt,\n },\n });\n this.broadcast(sessionId, this.stateMessage(sessionId));\n\n // Replay last N events to give the late joiner historical context.\n // Best-effort: failures are logged and silently ignored — the live\n // mirror continues regardless.\n if (this.reader) {\n this.replayHistory(ws, sessionId).catch((err) => {\n this.logger.debug?.(\n `collab: replay failed for ${sessionId}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n });\n }\n this.logger.debug?.(\n `collab: participant ${participant.participantId} joined ${sessionId}`,\n );\n }\n\n private leave(ws: WebSocket): void {\n this.handleDisconnect(ws);\n }\n\n private handleDisconnect(ws: WebSocket): void {\n this.clients.delete(ws);\n // Remove from every session bucket the WS may have joined (a single\n // WS is in at most one bucket in Phase 1, but the loop is cheap and\n // future-proofs multi-session observers).\n //\n // Order matters:\n // 1. Send `participant.left` to the leaving ws so they get a\n // confirmation that their leave registered.\n // 2. Delete from bucket.\n // 3. Broadcast the fresh state to remaining observers so they\n // see the updated count without waiting for the 2s timer.\n for (const [sessionId, bucket] of this.bySession) {\n for (const p of bucket) {\n if (p.ws === ws) {\n const leftEvent = {\n type: 'collab.participant.left' as const,\n payload: { participantId: p.participantId, sessionId },\n };\n // Send directly to the leaving ws first so they get an\n // immediate confirmation, then broadcast to the rest of the\n // bucket (which is still inclusive of the leaving ws here —\n // the per-iteration below strips it out).\n this.send(ws, leftEvent);\n bucket.delete(p);\n if (bucket.size === 0) {\n this.bySession.delete(sessionId);\n } else {\n this.broadcast(sessionId, leftEvent);\n this.broadcast(sessionId, this.stateMessage(sessionId));\n }\n break;\n }\n }\n }\n if (this.bySession.size === 0) this.stopBroadcast();\n }\n\n // ── Annotation flow (Phase 2) ───────────────────────────────────────────\n\n /**\n * Look up the participant record for a given WS across all sessions.\n * Returns null when the WS hasn't joined (e.g. the client sent a\n * `collab.annotate` before `collab.join`).\n */\n private findParticipant(ws: WebSocket): Participant | null {\n for (const bucket of this.bySession.values()) {\n for (const p of bucket) {\n if (p.ws === ws) return p;\n }\n }\n return null;\n }\n\n private async handleAnnotate(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.annotations) {\n this.send(ws, this.errorMessage('annotations store is not configured'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('annotate requires an active join'));\n return;\n }\n if (participant.role !== 'annotator') {\n this.send(\n ws,\n this.errorMessage(\n `annotate requires the 'annotator' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as\n | { sessionId?: string | undefined; atEventIndex?: number | undefined; text?: string | undefined }\n | undefined;\n if (\n !payload?.sessionId ||\n typeof payload.atEventIndex !== 'number' ||\n typeof payload.text !== 'string'\n ) {\n this.send(\n ws,\n this.errorMessage('annotate requires { sessionId, atEventIndex, text }'),\n );\n return;\n }\n if (payload.sessionId !== participant.sessionId) {\n this.send(\n ws,\n this.errorMessage(\n `annotate sessionId mismatch (joined: ${participant.sessionId})`,\n ),\n );\n return;\n }\n try {\n const annotation = await this.annotations.add({\n sessionId: payload.sessionId,\n atEventIndex: payload.atEventIndex,\n authorId: participant.participantId,\n text: payload.text,\n });\n this.broadcast(payload.sessionId, {\n type: 'collab.annotation.added',\n payload: {\n sessionId: payload.sessionId,\n annotation: {\n id: annotation.id,\n atEventIndex: annotation.atEventIndex,\n authorId: annotation.authorId,\n authorRole: annotation.authorRole,\n text: annotation.text,\n createdAt: annotation.createdAt,\n resolved: annotation.resolved,\n },\n },\n });\n } catch (err) {\n this.send(\n ws,\n this.errorMessage(\n `annotation rejected: ${\n err instanceof Error ? err.message : String(err)\n }`,\n ),\n );\n }\n }\n\n private async handleResolve(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.annotations) {\n this.send(ws, this.errorMessage('annotations store is not configured'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('resolve requires an active join'));\n return;\n }\n if (participant.role !== 'annotator') {\n this.send(\n ws,\n this.errorMessage(\n `resolve requires the 'annotator' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as\n | { sessionId?: string | undefined; annotationId?: string | undefined }\n | undefined;\n if (!payload?.sessionId || !payload.annotationId) {\n this.send(\n ws,\n this.errorMessage('resolve requires { sessionId, annotationId }'),\n );\n return;\n }\n if (payload.sessionId !== participant.sessionId) {\n this.send(\n ws,\n this.errorMessage(\n `resolve sessionId mismatch (joined: ${participant.sessionId})`,\n ),\n );\n return;\n }\n try {\n const updated = await this.annotations.resolve({\n sessionId: payload.sessionId,\n annotationId: payload.annotationId,\n resolvedBy: participant.participantId,\n });\n if (!updated) {\n this.send(\n ws,\n this.errorMessage(`annotation not found: ${payload.annotationId}`),\n );\n return;\n }\n this.broadcast(payload.sessionId, {\n type: 'collab.annotation.resolved',\n payload: {\n sessionId: payload.sessionId,\n annotationId: updated.id,\n resolvedBy: updated.resolvedBy ?? participant.participantId,\n resolvedAt: updated.resolvedAt ?? new Date().toISOString(),\n },\n });\n } catch (err) {\n this.send(\n ws,\n this.errorMessage(\n `resolve failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n ),\n );\n }\n }\n\n // ── Event subscription (live mirror) ───────────────────────────────────\n\n private subscribe(): void {\n // Same trick as WorktreeWebSocketHandler: bind a single typed-on helper\n // to a string-keyed signature so we can register many handlers.\n const on = this.events.on.bind(this.events) as unknown as (\n ev: string,\n fn: (p: unknown) => void,\n ) => () => void;\n\n // Mirror every event an observer would care about. Each is forwarded\n // to all joined participants as a generic `collab.event` envelope so\n // the client can render a flowing activity strip. Filtering /\n // denormalization happens on the client.\n const forwarded: Array<[string, string]> = [\n ['iteration.started', 'iteration.started'],\n ['iteration.completed', 'iteration.completed'],\n ['tool.started', 'tool.started'],\n ['tool.progress', 'tool.progress'],\n ['tool.executed', 'tool.executed'],\n ['tool.confirm_needed', 'tool.confirm_needed'],\n ['subagent.spawned', 'subagent.spawned'],\n ['subagent.task_started', 'subagent.task_started'],\n ['subagent.iteration_summary', 'subagent.iteration_summary'],\n ['subagent.task_completed', 'subagent.task_completed'],\n ['subagent.done', 'subagent.done'],\n ];\n for (const [kernelEvent, kind] of forwarded) {\n this.offs.push(\n on(kernelEvent, (raw) => {\n // Best-effort payload shape: we don't deeply validate, but we\n // make sure it's serializable. Observers must never receive\n // non-serializable objects (Functions, circular refs).\n let payload: unknown = raw;\n try {\n payload = JSON.parse(JSON.stringify(raw));\n } catch {\n // Skip unserializable payloads — better to drop than to crash\n // the broadcast loop.\n return;\n }\n this.broadcastEvent(kind, payload);\n }),\n );\n }\n }\n\n private broadcastEvent(kind: string, payload: unknown): void {\n if (this.bySession.size === 0) return; // nobody watching — no-op\n const msg: WSServerMessage = {\n type: 'collab.event',\n payload: { kind, payload, at: new Date().toISOString() },\n };\n const data = JSON.stringify(msg);\n for (const bucket of this.bySession.values()) {\n for (const p of bucket) {\n try {\n if (p.ws.readyState === 1) p.ws.send(data);\n } catch (err) {\n this.logger.debug?.(\n `collab broadcast failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n }\n }\n\n /**\n * Replay the last `REPLAY_LIMIT` events from the on-disk session log\n * to a single observer (the late joiner). Each event is forwarded as\n * a `collab.event` with `replay: true` so the client can distinguish\n * history from the live stream.\n *\n * The session log stores typed `SessionEvent`s (`user_input`,\n * `llm_response`, `tool_result`, etc.) — different from the kernel's\n * bus events. We translate the most useful subset (`tool.*` and\n * `iteration.*`-shaped ones) into the same `kind` namespace the live\n * mirror uses, so the client can render a single activity strip.\n */\n private async replayHistory(ws: WebSocket, sessionId: string): Promise<void> {\n if (!this.reader) return;\n const all: unknown[] = [];\n try {\n for await (const ev of this.reader.replay(sessionId)) {\n all.push(ev);\n }\n } catch (err) {\n this.logger.debug?.(\n `collab: session reader rejected ${sessionId}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n return;\n }\n const tail = all.slice(-REPLAY_LIMIT);\n if (tail.length === 0) return; // nothing to replay\n for (const raw of tail) {\n const ev = raw as { type?: string | undefined; ts?: string | undefined; [k: string]: unknown };\n const kind = this.historyEventToKind(ev);\n if (!kind) continue; // skip events we don't know how to mirror\n this.send(ws, {\n type: 'collab.event',\n payload: {\n kind,\n payload: ev,\n at: ev.ts ?? new Date().toISOString(),\n replay: true,\n },\n });\n }\n }\n\n /**\n * Map a stored `SessionEvent` to a `collab.event.kind` so the live\n * strip and the history strip can share a single rendering path.\n * Returns null for events that don't have a meaningful live analog\n * (e.g. `session_start`, file-snapshot bookkeeping, rewind markers).\n */\n private historyEventToKind(ev: { type?: string | undefined }): string | null {\n switch (ev.type) {\n case 'user_input':\n return 'user_input';\n case 'llm_response':\n return 'llm_response';\n case 'tool_result':\n return 'tool.executed';\n case 'compaction':\n return 'compaction';\n case 'error':\n return 'error';\n default:\n return null;\n }\n }\n\n // ── State snapshot + periodic broadcast ────────────────────────────────\n\n private stateMessage(sessionId: string): WSCollabState {\n const bucket = this.bySession.get(sessionId);\n return {\n type: 'collab.state',\n payload: {\n sessionId,\n participants: bucket\n ? [...bucket].map((p) => ({\n participantId: p.participantId,\n role: p.role,\n joinedAt: p.joinedAt,\n }))\n : [],\n },\n };\n }\n\n private ensureBroadcast(): void {\n if (this.broadcastInterval) return;\n this.broadcastInterval = setInterval(() => {\n for (const sessionId of this.bySession.keys()) {\n this.broadcast(sessionId, this.stateMessage(sessionId));\n }\n }, 2000);\n }\n\n private stopBroadcast(): void {\n if (this.broadcastInterval) {\n clearInterval(this.broadcastInterval);\n this.broadcastInterval = null;\n }\n }\n\n private broadcast(sessionId: string, msg: WSServerMessage): void {\n const data = JSON.stringify(msg);\n const bucket = this.bySession.get(sessionId);\n if (!bucket) return;\n for (const p of bucket) {\n try {\n if (p.ws.readyState === 1) p.ws.send(data);\n } catch (err) {\n this.logger.debug?.(\n `collab broadcast failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n }\n\n private send(ws: WebSocket, msg: WSServerMessage): void {\n try {\n if (ws.readyState === 1) ws.send(JSON.stringify(msg));\n } catch {\n /* client gone */\n }\n }\n\n private errorMessage(detail: string): WSServerMessage {\n return { type: 'error', payload: { phase: 'collab', message: detail } };\n }\n\n // ── Controller flow (Phase 3) ───────────────────────────────────────────\n\n private async handleRequestPause(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.bus) {\n this.send(ws, this.errorMessage('pause requires a CollaborationBus'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('pause requires an active join'));\n return;\n }\n if (participant.role !== 'controller') {\n this.send(\n ws,\n this.errorMessage(\n `pause requires the 'controller' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as { sessionId?: string | undefined } | undefined;\n if (!payload?.sessionId || payload.sessionId !== participant.sessionId) {\n this.send(ws, this.errorMessage('pause sessionId mismatch'));\n return;\n }\n const transitioned = this.bus.requestPause(participant.participantId);\n if (!transitioned) {\n // Already paused — surface the current state to the requester.\n const s = this.bus.getState();\n this.send(ws, {\n type: 'error',\n payload: {\n phase: 'collab',\n message: `bus already paused by ${s.pausedBy ?? '?'} at ${s.pausedAt ?? '?'}`,\n },\n });\n return;\n }\n const s = this.bus.getState();\n this.broadcast(payload.sessionId, {\n type: 'collab.pause.granted',\n payload: {\n sessionId: payload.sessionId,\n pausedBy: s.pausedBy ?? participant.participantId,\n pausedAt: s.pausedAt ?? new Date().toISOString(),\n autoResumeInMs: PAUSE_TIMEOUT_MS,\n },\n });\n }\n\n private async handleResume(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.bus) {\n this.send(ws, this.errorMessage('resume requires a CollaborationBus'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('resume requires an active join'));\n return;\n }\n // Permission: controller OR the original pauser. We do a simple\n // \"any controller can release\" check — fine for Phase 3, can be\n // tightened to \"only the pauser\" later.\n if (participant.role !== 'controller') {\n this.send(\n ws,\n this.errorMessage(\n `resume requires the 'controller' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as { sessionId?: string | undefined } | undefined;\n if (!payload?.sessionId || payload.sessionId !== participant.sessionId) {\n this.send(ws, this.errorMessage('resume sessionId mismatch'));\n return;\n }\n const transitioned = this.bus.resume();\n if (!transitioned) {\n this.send(ws, this.errorMessage('bus is not currently paused'));\n return;\n }\n this.broadcast(payload.sessionId, {\n type: 'collab.pause.released',\n payload: {\n sessionId: payload.sessionId,\n reason: 'controller',\n at: new Date().toISOString(),\n },\n });\n }\n\n private async handleGrantControl(ws: WebSocket, raw: unknown): Promise<void> {\n // Phase 3 metadata-only: record the grant in the log; the\n // existing controller's effective permissions do not change.\n // A future iteration can wire this to a per-participant RBAC\n // table that the `handleRequestPause`/`handleResume` checks read.\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('grant_control requires an active join'));\n return;\n }\n const payload = raw as\n | { sessionId?: string | undefined; toParticipant?: string | undefined }\n | undefined;\n if (\n !payload?.sessionId ||\n !payload.toParticipant ||\n payload.sessionId !== participant.sessionId\n ) {\n this.send(ws, this.errorMessage('grant_control requires { sessionId, toParticipant }'));\n return;\n }\n this.logger.debug?.(\n `collab: control granted from ${participant.participantId} to ${payload.toParticipant} in ${payload.sessionId}`,\n );\n }\n\n /**\n * Phase 4 — handle a controller's manual tool-call injection.\n * Validates the payload, queues it on the bus, and broadcasts\n * the grant so observers see what just happened. The actual\n * splice into the agent's pipeline is performed by the\n * `collabInjectMiddleware` on the next tool call.\n */\n private async handleInjectTool(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.bus) {\n this.send(ws, this.errorMessage('inject_tool requires a CollaborationBus'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('inject_tool requires an active join'));\n return;\n }\n if (participant.role !== 'controller') {\n this.send(\n ws,\n this.errorMessage(\n `inject_tool requires the 'controller' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as\n | {\n sessionId?: string | undefined;\n toolUseId?: string | undefined;\n content?: unknown | undefined;\n isError?: boolean | undefined;\n reason?: string | undefined;\n }\n | undefined;\n if (\n !payload?.sessionId ||\n !payload.toolUseId ||\n typeof payload.isError !== 'boolean' ||\n typeof payload.reason !== 'string' ||\n payload.content === undefined\n ) {\n this.send(\n ws,\n this.errorMessage(\n 'inject_tool requires { sessionId, toolUseId, content, isError, reason }',\n ),\n );\n return;\n }\n if (payload.sessionId !== participant.sessionId) {\n this.send(\n ws,\n this.errorMessage(\n `inject_tool sessionId mismatch (joined: ${participant.sessionId})`,\n ),\n );\n return;\n }\n const queued = this.bus.injectToolResult({\n toolUseId: payload.toolUseId,\n content: payload.content,\n isError: payload.isError,\n reason: payload.reason,\n authorId: participant.participantId,\n });\n if (!queued) {\n this.send(\n ws,\n this.errorMessage(\n `an injection for toolUseId ${payload.toolUseId} is already queued`,\n ),\n );\n return;\n }\n this.broadcast(payload.sessionId, {\n type: 'collab.injection.granted',\n payload: {\n sessionId: payload.sessionId,\n toolUseId: payload.toolUseId,\n // The tool name is unknown here (the injection is queued\n // before the model produces the tool call). We surface a\n // placeholder; the middleware will emit a `consumed` event\n // with the real name on match.\n toolName: '(pending match)',\n authorId: participant.participantId,\n reason: payload.reason,\n isError: payload.isError,\n phase: 'queued',\n at: new Date().toISOString(),\n },\n });\n }\n}\n\ninterface Participant {\n participantId: string;\n ws: WebSocket;\n sessionId: string;\n role: CollabRole;\n joinedAt: string;\n}\n","import type { WebSocket } from 'ws';\nimport type { EventBus, Logger } from '@wrongstack/core';\nimport type { WorktreeHandleView, WSServerMessage } from '../types.js';\n\nconst MAX_ACTIVITY = 6;\n\n/**\n * WorktreeWebSocketHandler — mirrors AutoPhaseWebSocketHandler. Subscribes to\n * the shared EventBus `worktree.*` lifecycle events, keeps a live snapshot of\n * every worktree, and broadcasts:\n * - `worktree.event` incrementally (drives the flowing activity strip)\n * - `worktree.state` on connect + on a 2s timer (drives swim-lanes/DAG)\n */\nexport class WorktreeWebSocketHandler {\n private readonly clients = new Set<WebSocket>();\n private readonly handles = new Map<string, WorktreeHandleView>();\n private baseBranch = '';\n private broadcastInterval: ReturnType<typeof setInterval> | null = null;\n private readonly offs: Array<() => void> = [];\n\n constructor(\n private readonly events: EventBus,\n private readonly logger: Logger,\n ) {\n this.subscribe();\n }\n\n addClient(ws: WebSocket): void {\n this.clients.add(ws);\n ws.on('close', () => this.clients.delete(ws));\n ws.on('error', () => this.clients.delete(ws));\n this.send(ws, this.stateMessage());\n }\n\n dispose(): void {\n for (const off of this.offs) off();\n this.offs.length = 0;\n this.stopBroadcast();\n }\n\n // ── internals ───────────────────────────────────────────────────────────\n\n private subscribe(): void {\n const on = this.events.on.bind(this.events) as unknown as (\n ev: string,\n fn: (p: unknown) => void,\n ) => () => void;\n\n this.offs.push(\n on('worktree.allocated', (p) => {\n const e = p as { handleId: string; ownerId: string; ownerLabel: string; branch: string; baseBranch: string };\n this.baseBranch = e.baseBranch || this.baseBranch;\n this.upsert(e.handleId, {\n handleId: e.handleId,\n ownerId: e.ownerId,\n ownerLabel: e.ownerLabel,\n branch: e.branch,\n baseBranch: e.baseBranch,\n status: 'active',\n insertions: 0,\n deletions: 0,\n files: 0,\n allocatedAt: Date.now(),\n lastEventAt: Date.now(),\n recentActivity: [],\n });\n this.activity(e.handleId, 'allocated', `branch ${e.branch}`);\n this.ensureBroadcast();\n }),\n on('worktree.committed', (p) => {\n const e = p as { handleId: string; insertions: number; deletions: number; files: number; committed: boolean };\n this.patch(e.handleId, { status: 'committing', insertions: e.insertions, deletions: e.deletions, files: e.files });\n if (e.committed) this.activity(e.handleId, 'committed', `+${e.insertions}/-${e.deletions} (${e.files}f)`);\n this.broadcastState();\n }),\n on('worktree.merged', (p) => {\n const e = p as { handleId: string; baseBranch: string };\n this.patch(e.handleId, { status: 'merged' });\n this.activity(e.handleId, 'merged', `→ ${e.baseBranch}`);\n this.broadcastState();\n }),\n on('worktree.conflict', (p) => {\n const e = p as { handleId: string; conflictFiles: string[] };\n this.patch(e.handleId, { status: 'needs-review', conflictFiles: e.conflictFiles });\n this.activity(e.handleId, 'conflict', e.conflictFiles.join(', '));\n this.broadcastState();\n }),\n on('worktree.failed', (p) => {\n const e = p as { handleId: string; error: string };\n this.patch(e.handleId, { status: 'failed' });\n this.activity(e.handleId, 'failed', e.error);\n this.broadcastState();\n }),\n on('worktree.released', (p) => {\n const e = p as { handleId: string; kept: boolean };\n if (!e.kept) this.handles.delete(e.handleId);\n this.activity(e.handleId, 'released', e.kept ? 'kept for review' : 'removed');\n if (this.handles.size === 0) this.stopBroadcast();\n else this.broadcastState();\n }),\n );\n }\n\n private upsert(id: string, view: WorktreeHandleView): void {\n this.handles.set(id, view);\n }\n\n private patch(id: string, patch: Partial<WorktreeHandleView>): void {\n const cur = this.handles.get(id);\n if (!cur) return;\n this.handles.set(id, { ...cur, ...patch, lastEventAt: Date.now() });\n }\n\n private activity(id: string, kind: string, text: string): void {\n const cur = this.handles.get(id);\n if (cur) {\n const recentActivity = [...cur.recentActivity, { kind, text, at: Date.now() }].slice(-MAX_ACTIVITY);\n this.handles.set(id, { ...cur, recentActivity });\n }\n this.broadcast({ type: 'worktree.event', payload: { kind, handleId: id, text, at: Date.now() } });\n }\n\n private stateMessage(): WSServerMessage {\n return {\n type: 'worktree.state',\n payload: { worktrees: [...this.handles.values()], baseBranch: this.baseBranch },\n };\n }\n\n private broadcastState(): void {\n this.broadcast(this.stateMessage());\n }\n\n private ensureBroadcast(): void {\n this.broadcast(this.stateMessage());\n if (this.broadcastInterval) return;\n this.broadcastInterval = setInterval(() => this.broadcast(this.stateMessage()), 2000);\n }\n\n private stopBroadcast(): void {\n this.broadcast(this.stateMessage());\n if (this.broadcastInterval) {\n clearInterval(this.broadcastInterval);\n this.broadcastInterval = null;\n }\n }\n\n private broadcast(msg: WSServerMessage): void {\n const data = JSON.stringify(msg);\n for (const ws of this.clients) {\n try {\n if (ws.readyState === 1) ws.send(data);\n } catch (err) {\n this.logger.debug?.(`worktree broadcast failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n\n private send(ws: WebSocket, msg: WSServerMessage): void {\n try {\n if (ws.readyState === 1) ws.send(JSON.stringify(msg));\n } catch {\n /* client gone */\n }\n }\n}\n","/**\n * Mailbox WebSocket handlers for the WebUI.\n *\n * Handles `mailbox.messages` and `mailbox.agents` message types.\n * The frontend sends these to populate the mailbox panel; the server\n * reads from the project-level GlobalMailbox and responds.\n */\n\nimport * as path from 'node:path';\nimport type { WebSocket } from 'ws';\nimport { GlobalMailbox } from '@wrongstack/core';\nimport { send, errMessage } from './ws-utils.js';\n\n// ── Helpers ───────────────────────────────────────────────────────────\n\nfunction resolveProjectDir(projectRoot: string, globalRoot: string): string {\n const { createHash } = require('node:crypto');\n const hash = createHash('sha256')\n .update(path.resolve(projectRoot))\n .digest('hex')\n .slice(0, 6);\n const slug = path\n .basename(projectRoot)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .slice(0, 40) || 'project';\n return path.join(globalRoot, 'projects', `${slug}-${hash}`);\n}\n\nexport interface MailboxHandlerDeps {\n /** Absolute project root. */\n projectRoot: string;\n /** Global WrongStack root (~/.wrongstack). */\n globalRoot: string;\n}\n\n// ── Handlers ──────────────────────────────────────────────────────────\n\n/**\n * List recent mailbox messages. Frontend sends:\n * { type: 'mailbox.messages', limit?: number, agentId?: string }\n */\nexport async function handleMailboxMessages(\n ws: WebSocket,\n deps: MailboxHandlerDeps,\n payload: { limit?: number; agentId?: string; unreadOnly?: boolean } | undefined,\n): Promise<void> {\n try {\n const dir = resolveProjectDir(deps.projectRoot, deps.globalRoot);\n const mb = new GlobalMailbox(dir);\n const messages = await mb.query({\n limit: payload?.limit ?? 30,\n to: payload?.agentId,\n unreadBy: payload?.unreadOnly ? payload.agentId : undefined,\n });\n send(ws, {\n type: 'mailbox.messages',\n payload: {\n messages: messages.map((m) => ({\n id: m.id, from: m.from, to: m.to, type: m.type,\n subject: m.subject, body: m.body, priority: m.priority,\n readBy: m.readBy, readByCount: Object.keys(m.readBy).length,\n completed: m.completed, completedBy: m.completedBy,\n outcome: m.outcome, timestamp: m.timestamp,\n replyTo: m.replyTo, senderSessionId: m.senderSessionId,\n })),\n },\n });\n } catch (err) {\n send(ws, { type: 'mailbox.messages', payload: { messages: [], error: errMessage(err) } });\n }\n}\n\n/**\n * List registered agents. Frontend sends:\n * { type: 'mailbox.agents', onlineOnly?: boolean }\n */\nexport async function handleMailboxAgents(\n ws: WebSocket,\n deps: MailboxHandlerDeps,\n payload: { onlineOnly?: boolean } | undefined,\n): Promise<void> {\n try {\n const dir = resolveProjectDir(deps.projectRoot, deps.globalRoot);\n const mb = new GlobalMailbox(dir);\n const agents = payload?.onlineOnly\n ? await mb.getOnlineAgents()\n : await mb.getAgentStatuses();\n send(ws, {\n type: 'mailbox.agents',\n payload: {\n agents: agents.map((a) => ({\n agentId: a.agentId, name: a.name, role: a.role,\n sessionId: a.sessionId, status: a.status,\n currentTool: a.currentTool, currentTask: a.currentTask,\n iterations: a.iterations, toolCalls: a.toolCalls,\n lastSeenAt: a.lastSeenAt, online: a.online,\n pid: a.pid, source: a.source,\n })),\n },\n });\n } catch (err) {\n send(ws, { type: 'mailbox.agents', payload: { agents: [], error: errMessage(err) } });\n }\n}\n\n/**\n * Delete all messages from the mailbox. Frontend sends:\n * { type: 'mailbox.clear' }\n * Server responds with 'mailbox.cleared'.\n */\nexport async function handleMailboxClear(\n ws: WebSocket,\n deps: MailboxHandlerDeps,\n): Promise<void> {\n try {\n const dir = resolveProjectDir(deps.projectRoot, deps.globalRoot);\n const mb = new GlobalMailbox(dir);\n await mb.clearAll();\n send(ws, { type: 'mailbox.cleared', payload: {} });\n } catch (err) {\n send(ws, { type: 'mailbox.cleared', payload: { error: errMessage(err) } });\n }\n}\n","/**\n * Process lifecycle for the WebUI server: graceful shutdown and the\n * SIGINT/SIGTERM wiring that triggers it.\n *\n * On a termination signal we (best-effort) flush + close the active session,\n * close every connected WebSocket, stop the HTTP and WS servers, then exit.\n * A re-entrancy guard makes a second signal during shutdown a no-op (rapid\n * double Ctrl+C no longer runs the teardown twice).\n *\n * Extracted from `index.ts` as a parameterized factory so the teardown\n * sequence can be unit tested without a real process signal, server, or\n * `process.exit` — `log` and `exit` are injectable seams.\n */\n\nexport interface LifecycleResources {\n /** Persist + close the active session (best-effort; errors are logged). */\n flushSession: () => Promise<void>;\n /**\n * Returns the currently-connected client sockets to close. A thunk (not a\n * snapshot) so shutdown closes whoever is connected *at signal time*, not\n * whoever was connected when the handler was registered.\n */\n clients: () => Iterable<{ close: () => void }>;\n /** Servers to stop (HTTP + WS). `null`/`undefined` entries are skipped. */\n servers: Array<{ close: () => void } | null | undefined>;\n /**\n * Optional best-effort cleanup run after the session flush and before exit\n * (e.g. removing this process from the running-instance registry). Errors are\n * logged, never thrown — cleanup must not block a clean shutdown.\n */\n onShutdown?: (() => Promise<void> | void) | undefined;\n /** Output sink. Defaults to `console.log`. */\n log?: ((msg: string) => void) | undefined;\n /** Process exit. Defaults to `process.exit`. Injectable for tests. */\n exit?: ((code: number) => void) | undefined;\n}\n\n/**\n * Build the graceful-shutdown handler. Returns an idempotent async function:\n * the first call runs the teardown, subsequent calls (e.g. a second SIGINT)\n * return immediately.\n */\nexport function createShutdown(res: LifecycleResources): () => Promise<void> {\n const log = res.log ?? ((m: string) => console.log(m));\n const exit = res.exit ?? ((code: number) => process.exit(code));\n let shuttingDown = false;\n\n return async () => {\n if (shuttingDown) return; // a second signal during teardown is a no-op\n shuttingDown = true;\n\n log('[WebUI] Shutting down...');\n try {\n await res.flushSession();\n } catch (e) {\n log(`[WebUI] Error closing session: ${e instanceof Error ? e.message : String(e)}`);\n }\n for (const ws of res.clients()) ws.close();\n for (const server of res.servers) server?.close();\n if (res.onShutdown) {\n try {\n await res.onShutdown();\n } catch (e) {\n log(`[WebUI] Error during shutdown cleanup: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n exit(0);\n };\n}\n\n/**\n * Register the shutdown handler on SIGINT and SIGTERM. Returns an unregister\n * function that detaches both listeners (useful for tests and clean restarts).\n */\nexport function registerShutdownHandlers(res: LifecycleResources): () => void {\n const shutdown = createShutdown(res);\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n return () => {\n process.off('SIGINT', shutdown);\n process.off('SIGTERM', shutdown);\n };\n}\n","/**\n * Running-instance registry for the standalone WebUI server.\n *\n * Every live `webui` process records itself in a single JSON file under the\n * wstack home dir (`~/.wrongstack/webui-instances.json`) so a user running\n * several instances (one per project, or several per project on different\n * ports) can see at a glance which ports are open for which path.\n *\n * Design notes:\n * - **Self-healing**: every register/unregister/list prunes entries whose PID\n * is no longer alive (`process.kill(pid, 0)`), so a crashed instance that\n * never got to unregister doesn't leave a ghost behind.\n * - **Atomic writes**: the file is rewritten via `atomicWrite` (tmp + rename),\n * so a concurrent reader never sees a half-written file. Two instances\n * starting at the *exact* same millisecond could still race the\n * read-modify-write — acceptable for a best-effort tracking file, and the\n * next register() heals any dropped entry.\n * - **Best-effort**: a failure to read/write the registry must NEVER take the\n * server down. Callers wrap these in `.catch()`.\n */\n\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport { atomicWrite } from '@wrongstack/core';\n\n/** One running WebUI process. */\nexport interface WebUIInstanceRecord {\n /** OS process id — also the liveness key. */\n pid: number;\n /** HTTP port serving the React frontend. */\n httpPort: number;\n /** WebSocket port for the agent backend. */\n wsPort: number;\n /** Bind host (e.g. 127.0.0.1 or 0.0.0.0). */\n host: string;\n /** Absolute project root the instance booted against. */\n projectRoot: string;\n /** Display name (basename of projectRoot). */\n projectName: string;\n /** ISO timestamp when the instance registered. */\n startedAt: string;\n /** Convenience open-in-browser URL. */\n url: string;\n}\n\ninterface RegistryFile {\n version: 1;\n instances: WebUIInstanceRecord[];\n}\n\n/** Default wstack home dir (`~/.wrongstack`). Callers may override the base. */\nexport function defaultBaseDir(): string {\n return path.join(os.homedir(), '.wrongstack');\n}\n\n/** Resolve the registry file path for a given base dir. */\nexport function registryPath(baseDir: string = defaultBaseDir()): string {\n return path.join(baseDir, 'webui-instances.json');\n}\n\n/**\n * Liveness probe. `process.kill(pid, 0)` sends no signal — it only checks the\n * process exists. ESRCH ⇒ dead; EPERM ⇒ alive but owned by another user (still\n * counts as alive). Any other error is treated conservatively as \"alive\" so we\n * never prune an instance we simply failed to probe.\n */\nexport function isPidAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n}\n\nasync function load(file: string): Promise<RegistryFile> {\n try {\n const raw = await fs.readFile(file, 'utf8');\n const parsed = JSON.parse(raw) as RegistryFile;\n if (parsed?.version === 1 && Array.isArray(parsed.instances)) {\n return parsed;\n }\n } catch {\n // Missing or corrupt → start fresh.\n }\n return { version: 1, instances: [] };\n}\n\nasync function save(file: string, instances: WebUIInstanceRecord[]): Promise<void> {\n await atomicWrite(file, `${JSON.stringify({ version: 1, instances }, null, 2)}\\n`, {\n mode: 0o600,\n });\n}\n\n/** Drop dead processes and (optionally) one specific pid. */\nfunction prune(instances: WebUIInstanceRecord[], excludePid?: number): WebUIInstanceRecord[] {\n return instances.filter((i) => i.pid !== excludePid && isPidAlive(i.pid));\n}\n\n/**\n * Register (or refresh) this instance. Prunes dead entries and any stale entry\n * for our own PID before adding the current record. Best-effort — rejects only\n * on a hard fs error, which callers swallow.\n */\nexport async function registerInstance(\n record: WebUIInstanceRecord,\n baseDir: string = defaultBaseDir(),\n): Promise<void> {\n const file = registryPath(baseDir);\n const data = await load(file);\n const instances = prune(data.instances, record.pid);\n instances.push(record);\n await save(file, instances);\n}\n\n/** Remove this instance (called on graceful shutdown). Also prunes dead pids. */\nexport async function unregisterInstance(\n pid: number,\n baseDir: string = defaultBaseDir(),\n): Promise<void> {\n const file = registryPath(baseDir);\n const data = await load(file);\n const instances = prune(data.instances, pid);\n await save(file, instances);\n}\n\n/** List live instances, pruning any dead entries encountered. */\nexport async function listInstances(\n baseDir: string = defaultBaseDir(),\n): Promise<WebUIInstanceRecord[]> {\n const file = registryPath(baseDir);\n const data = await load(file);\n const live = prune(data.instances);\n // Persist the pruned view so `cat`-ing the file also shows reality, but never\n // fail the list on a write error.\n if (live.length !== data.instances.length) {\n await save(file, live).catch(() => {});\n }\n return live;\n}\n\n/** Human-readable table of running instances for `webui --list`. */\nexport function formatInstances(instances: WebUIInstanceRecord[]): string {\n if (instances.length === 0) {\n return 'No WebUI instances are currently running.';\n }\n const lines = [`Running WebUI instances (${instances.length}):`, ''];\n for (const i of instances) {\n lines.push(\n ` • ${i.url} · ws:${i.wsPort} · pid ${i.pid}`,\n ` project: ${i.projectName} (${i.projectRoot})`,\n ` since: ${i.startedAt}`,\n );\n }\n return lines.join('\\n');\n}\n","/**\n * Free-port discovery for the standalone WebUI server.\n *\n * When a user runs several instances, the default ports (HTTP 3456 / WS 3457)\n * are taken by the first one. Rather than make the user hand-pick `PORT` /\n * `WS_PORT` for every extra instance, the server probes upward from the\n * requested port and binds the first free one — then stamps that real port into\n * the served HTML and the instance registry so everything stays consistent.\n *\n * The probe binds a throwaway `net.Server`, then closes it, so there is a tiny\n * TOCTOU window between \"found free\" and \"the real server binds it\". For local\n * single-user multi-instance use that race is negligible; if it ever loses, the\n * real bind fails loudly with EADDRINUSE exactly as before.\n */\n\nimport * as net from 'node:net';\n\n/** Resolve true when `port` can be bound on `host`, false on EADDRINUSE/EACCES. */\nexport function isPortFree(host: string, port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const srv = net.createServer();\n srv.once('error', () => resolve(false));\n srv.once('listening', () => {\n srv.close(() => resolve(true));\n });\n try {\n srv.listen(port, host);\n } catch {\n resolve(false);\n }\n });\n}\n\nexport interface FindFreePortOptions {\n /** Ports to skip even if free (e.g. one already chosen for the sibling server). */\n exclude?: Set<number> | undefined;\n /** How many consecutive ports to try before giving up. Default 200. */\n maxTries?: number | undefined;\n}\n\n/**\n * Find the first free port at or above `startPort` on `host`, skipping any in\n * `exclude`. Throws if nothing is free within `maxTries` steps.\n */\nexport async function findFreePort(\n host: string,\n startPort: number,\n opts: FindFreePortOptions = {},\n): Promise<number> {\n const exclude = opts.exclude ?? new Set<number>();\n const maxTries = opts.maxTries ?? 200;\n let port = startPort;\n for (let i = 0; i < maxTries; i++) {\n // Stay inside the valid TCP range; wrap into the high ephemeral band if a\n // pathological startPort pushes us past the ceiling.\n if (port > 65535) port = 1024 + (port % 50000);\n if (!exclude.has(port) && (await isPortFree(host, port))) {\n return port;\n }\n port++;\n }\n throw new Error(\n `No free port found near ${startPort} on ${host} after ${maxTries} attempts.`,\n );\n}\n","/**\n * Best-effort \"open this URL in the default browser\" for `--webui --open`.\n *\n * Cross-platform via the OS opener (`start` / `open` / `xdg-open`). Fully\n * fire-and-forget: a missing opener, a headless box, or a spawn failure must\n * NEVER take the server down — the URL is always also printed to the console.\n */\n\nimport { spawn } from 'node:child_process';\n\n/** Resolve the platform's URL-opener command + args. */\nexport function browserOpenCommand(\n url: string,\n platform: NodeJS.Platform = process.platform,\n): { command: string; args: string[] } {\n if (platform === 'win32') {\n // `start` is a cmd builtin; the empty \"\" is the window title slot so URLs\n // containing `&` / spaces are passed through intact.\n return { command: 'cmd', args: ['/c', 'start', '', url] };\n }\n if (platform === 'darwin') {\n return { command: 'open', args: [url] };\n }\n return { command: 'xdg-open', args: [url] };\n}\n\n/** Spawn the OS browser-opener for `url` and register it as a protected\n * process so it survives kill/killAll. Never throws. */\nexport function openBrowser(url: string, platform: NodeJS.Platform = process.platform): void {\n try {\n const { command, args } = browserOpenCommand(url, platform);\n const child = spawn(command, args, { stdio: 'ignore', detached: true, windowsHide: true });\n // A missing opener (e.g. xdg-open absent on a headless box) surfaces as an\n // async 'error' event — swallow it so it doesn't crash the process.\n child.on('error', () => {});\n child.unref();\n\n // Register the browser process as protected so process.kill / killAll\n // never accidentally terminates it — that would crash the webui session.\n // The registry is imported lazily to avoid a circular dependency with\n // @wrongstack/tools (which the webui server does not directly depend on).\n if (child.pid) {\n try {\n // Dynamic import to avoid hard dependency on @wrongstack/tools from\n // this module (the webui server may not have tools installed).\n import('@wrongstack/tools').then(({ getProcessRegistry }) => {\n getProcessRegistry().register({\n // biome-ignore lint/style/noNonNullAssertion: pid always present after spawn\n pid: child.pid!,\n name: 'browser',\n command: `${command} ${args.join(' ')}`,\n startedAt: Date.now(),\n child,\n protected: true,\n });\n // Auto-unregister on exit so the process list stays accurate.\n child.on('exit', () => {\n // biome-ignore lint/style/noNonNullAssertion: pid guaranteed after spawn\n getProcessRegistry().unregister(child.pid!);\n });\n }).catch(() => {\n // @wrongstack/tools may not be available — silently skip registration.\n });\n } catch {\n // Module resolution failure — silently skip.\n }\n }\n } catch {\n // Synchronous spawn failure — best-effort, ignore.\n }\n}\n","/**\n * Token-usage cost math for the WebUI server.\n *\n * models.dev pricing is expressed in **dollars per 1,000,000 tokens**, and\n * providers omit the field entirely for free/unmetered plans. Both the\n * `session.start` payload (which ships the per-token rates to the client) and\n * `stats.get` (which reports an actual dollar figure) repeated the same\n * \"read `model.cost.*` with a `?? 0` fallback, then divide by 1e6\" logic\n * inline. Pulling it here keeps the rate normalization and the cost formula in\n * one tested place — a wrong field name or a missing `/ 1e6` silently produces\n * a plausible-but-wrong number, which is exactly what a unit test should pin.\n */\n\n/** Per-1,000,000-token pricing, normalized to numbers (0 when unpriced). */\nexport interface CostRates {\n /** $ per 1M input tokens. */\n input: number;\n /** $ per 1M output tokens. */\n output: number;\n /** $ per 1M cache-read tokens. */\n cacheRead: number;\n}\n\n/** Token counts for a turn/session. `cacheRead` is optional (older counters). */\nexport interface TokenUsage {\n input: number;\n output: number;\n cacheRead?: number | undefined;\n}\n\n/**\n * Normalize a models.dev model object's pricing into {@link CostRates}.\n * Missing model, missing `cost`, or missing individual fields all yield 0 —\n * free/unmetered plans report `$0` rather than crashing.\n */\nexport function getCostRates(model: unknown): CostRates {\n const cost = (\n model as { cost?: { input?: number | undefined; output?: number | undefined; cache_read?: number | undefined } } | null | undefined\n )?.cost;\n return {\n input: cost?.input ?? 0,\n output: cost?.output ?? 0,\n cacheRead: cost?.cache_read ?? 0,\n };\n}\n\n/**\n * Dollar cost of `usage` at the given per-1M-token `rates`. Returns 0 when all\n * rates are 0 (unpriced plan).\n */\nexport function computeUsageCost(usage: TokenUsage, rates: CostRates): number {\n return (\n (usage.input * rates.input +\n usage.output * rates.output +\n (usage.cacheRead ?? 0) * rates.cacheRead) /\n 1_000_000\n );\n}\n","/**\n * Shared config I/O helpers for the `providers` map inside the global config.\n *\n * Extracted from both `packages/webui/src/server/index.ts` and\n * `packages/cli/src/webui-server.ts` so the CLI's `--webui` mode doesn't\n * duplicate the read-merge-decrypt / encrypt-write cycle. Callers supply\n * their own vault (already booted) and config path — this module is pure I/O\n * with no side-channel state.\n */\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { type ProviderConfig, type SecretVault, atomicWrite } from '@wrongstack/core';\nimport { decryptConfigSecrets, encryptConfigSecrets } from '@wrongstack/core/security';\n\n/**\n * Read the `providers` section from the global config, decrypting\n * secret-bearing fields. Returns an empty record when the config file\n * doesn't exist or has no `providers` key.\n */\nexport async function loadSavedProviders(\n configPath: string,\n vault: SecretVault,\n): Promise<Record<string, ProviderConfig>> {\n let raw: string;\n try {\n raw = await fs.readFile(configPath, 'utf8');\n } catch {\n return {};\n }\n let parsed: { providers?: Record<string, ProviderConfig> } = {};\n try {\n parsed = JSON.parse(raw) as { providers?: Record<string, ProviderConfig> };\n } catch {\n return {};\n }\n if (!parsed.providers) return {};\n return decryptConfigSecrets(parsed.providers, vault);\n}\n\n/**\n * Write `providers` back into the global config, encrypting secrets first.\n * Refuses to overwrite a corrupt-but-existing config file (the operator\n * should fix it manually). When the config file is missing (ENOENT), starts\n * from an empty object.\n */\nexport async function saveProviders(\n configPath: string,\n vault: SecretVault,\n providers: Record<string, ProviderConfig>,\n): Promise<void> {\n let raw: string;\n let fileExists = true;\n try {\n raw = await fs.readFile(configPath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new Error(\n `Refusing to mutate ${configPath}: ${(err as Error).message}`,\n { cause: err },\n );\n }\n fileExists = false;\n raw = '{}';\n }\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n if (fileExists) {\n throw new Error(\n `Refusing to overwrite corrupt config at ${configPath} ` +\n `(${(err as Error).message}). Fix or move the file aside before retrying.`,\n { cause: err },\n );\n }\n parsed = {};\n }\n parsed.providers = providers;\n const encrypted = encryptConfigSecrets(parsed, vault);\n await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 0o600 });\n}\n\n// ---------------------------------------------------------------------------\n// Standalone WebUI server helpers (boot phase — not WS-connected)\n// ---------------------------------------------------------------------------\n\nimport { DefaultSecretVault } from '@wrongstack/core';\n\n/**\n * Small helper for the standalone WebUI entry point: create a\n * `{ load, save }` pair from a config path alone (uses the\n * config-directory-relative `.key` file for the vault). The `--webui`\n * CLI mode and the standalone server both need to read/write the\n * `providers` map identically.\n */\nexport function createProviderConfigIO(configPath: string) {\n const keyFile = path.join(path.dirname(configPath), '.key');\n const vault = new DefaultSecretVault({ keyFile });\n\n return {\n load: () => loadSavedProviders(configPath, vault),\n save: (providers: Record<string, ProviderConfig>) =>\n saveProviders(configPath, vault, providers),\n };\n}\n","import { expectDefined } from '@wrongstack/core';\n/**\n * Pure provider/API-key record transforms for the WebUI server's `key.*` and\n * `provider.*` WebSocket handlers.\n *\n * These operate on an in-memory `providers` record (the decrypted\n * `config.providers` map) and return a `{ ok, message }` result mirroring the\n * status string the handler sends back to the client. All persistence\n * (load/decrypt, encrypt/atomic-write) and WS messaging stays in `index.ts` —\n * keeping this layer pure means the security-sensitive key bookkeeping (which\n * key is active, when a provider is dropped, how legacy single-key configs are\n * normalized) is unit-testable without a vault or a socket.\n *\n * Extracted from `index.ts`; transforms mutate the passed record in place, the\n * same way the original handlers did before calling `saveProviders`.\n */\nimport type { ProviderApiKey, ProviderConfig } from '@wrongstack/core';\nexport type ProvidersRecord = Record<string, ProviderConfig>;\n\nexport interface KeyOpResult {\n ok: boolean;\n message: string;\n}\n\n/**\n * Normalize a provider's keys to the array form, upgrading a legacy single\n * `apiKey` string to a one-element `[{ label: 'default', ... }]` list. Returns\n * fresh copies so callers can mutate without aliasing the stored config.\n */\nexport function normalizeKeys(cfg: ProviderConfig): ProviderApiKey[] {\n if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {\n return cfg.apiKeys.map((k) => ({ ...k }));\n }\n if (typeof cfg.apiKey === 'string' && cfg.apiKey.length > 0) {\n return [{ label: 'default', apiKey: cfg.apiKey, createdAt: '' }];\n }\n return [];\n}\n\n/**\n * Write a normalized key list back onto a provider config: drop all key fields\n * when empty, otherwise sync `apiKeys`, the legacy `apiKey` mirror (the active\n * key), and re-point `activeKey` if it no longer names a present key.\n */\nexport function writeKeysBack(cfg: ProviderConfig, keys: ProviderApiKey[]): void {\n if (keys.length === 0) {\n delete cfg.apiKeys;\n delete cfg.apiKey;\n delete cfg.activeKey;\n return;\n }\n cfg.apiKeys = keys;\n const active = keys.find((k) => k.label === cfg.activeKey) ?? expectDefined(keys[0]);\n cfg.apiKey = active.apiKey;\n if (!cfg.activeKey || !keys.some((k) => k.label === cfg.activeKey)) {\n cfg.activeKey = active.label;\n }\n}\n\n/** Mask a secret for display: `••••` for short keys, `abcd…wxyz` otherwise. */\nexport function maskedKey(key: string | undefined): string {\n if (!key) return '—';\n if (key.length <= 8) return '•'.repeat(key.length);\n return `${key.slice(0, 4)}…${key.slice(-4)}`;\n}\n\n/** Add or replace a labeled key for a provider, creating the provider if new. */\nexport function upsertKey(\n providers: ProvidersRecord,\n providerId: string,\n label: string,\n apiKey: string,\n nowIso: string,\n): KeyOpResult {\n const existing: ProviderConfig = providers[providerId] ?? { type: providerId };\n const keys = normalizeKeys(existing);\n const idx = keys.findIndex((k) => k.label === label);\n if (idx >= 0) {\n keys[idx] = { ...expectDefined(keys[idx]), apiKey, createdAt: nowIso };\n } else {\n keys.push({ label, apiKey, createdAt: nowIso });\n }\n writeKeysBack(existing, keys);\n if (!existing.activeKey) existing.activeKey = label;\n providers[providerId] = existing;\n return { ok: true, message: `Key \"${label}\" saved for ${providerId}` };\n}\n\n/** Remove a labeled key; drops the provider entirely when its last key goes. */\nexport function deleteKey(\n providers: ProvidersRecord,\n providerId: string,\n label: string,\n): KeyOpResult {\n const existing = providers[providerId];\n if (!existing) {\n return { ok: false, message: `Provider \"${providerId}\" not found` };\n }\n const keys = normalizeKeys(existing).filter((k) => k.label !== label);\n if (keys.length === 0) {\n delete providers[providerId];\n } else {\n writeKeysBack(existing, keys);\n if (existing.activeKey === label) existing.activeKey = keys[0]?.label;\n providers[providerId] = existing;\n }\n return { ok: true, message: `Key \"${label}\" deleted from ${providerId}` };\n}\n\n/** Point a provider's active key at the given label. */\nexport function setActiveKey(\n providers: ProvidersRecord,\n providerId: string,\n label: string,\n): KeyOpResult {\n const existing = providers[providerId];\n if (!existing) {\n return { ok: false, message: `Provider \"${providerId}\" not found` };\n }\n existing.activeKey = label;\n writeKeysBack(existing, normalizeKeys(existing));\n providers[providerId] = existing;\n return { ok: true, message: `Active key for ${providerId} set to \"${label}\"` };\n}\n\n/** Register a brand-new provider (optionally with an initial `default` key). */\nexport function addProvider(\n providers: ProvidersRecord,\n payload: { id: string; family: string; baseUrl?: string | undefined; apiKey?: string | undefined },\n nowIso: string,\n): KeyOpResult {\n if (providers[payload.id]) {\n return {\n ok: false,\n message: `Provider \"${payload.id}\" already exists. Use key.add to add a key.`,\n };\n }\n const newProv: ProviderConfig = {\n type: payload.id,\n family: payload.family as ProviderConfig['family'],\n baseUrl: payload.baseUrl,\n };\n if (payload.apiKey) {\n newProv.apiKeys = [{ label: 'default', apiKey: payload.apiKey, createdAt: nowIso }];\n newProv.activeKey = 'default';\n }\n providers[payload.id] = newProv;\n return { ok: true, message: `Provider \"${payload.id}\" added` };\n}\n\n/** Remove an entire provider and all its keys. */\nexport function removeProvider(providers: ProvidersRecord, providerId: string): KeyOpResult {\n if (!providers[providerId]) {\n return { ok: false, message: `Provider \"${providerId}\" not found` };\n }\n delete providers[providerId];\n return { ok: true, message: `Provider \"${providerId}\" removed` };\n}\n","import type { WebSocket } from 'ws';\nimport type { ProviderConfig } from '@wrongstack/core';\nimport { loadSavedProviders, saveProviders } from './provider-config-io.js';\nimport {\n upsertKey as upsertKeyRecord,\n deleteKey as deleteKeyRecord,\n setActiveKey as setActiveKeyRecord,\n addProvider as addProviderRecord,\n removeProvider as removeProviderRecord,\n maskedKey,\n normalizeKeys,\n} from './provider-keys.js';\nimport type { ConnectedClient, WSServerMessage } from './types.js';\nimport { sendResult, errMessage } from './ws-utils.js';\n\nexport interface ProviderHandlerDeps {\n globalConfigPath: string;\n vault: import('@wrongstack/core').SecretVault;\n /** Shared config write lock — serialized via chained promises */\n setConfigWriteLock: (lock: Promise<void>) => void;\n getConfigWriteLock: () => Promise<void>;\n /** Broadcast a message to all connected WebUI clients */\n broadcast: (clients: Map<WebSocket, ConnectedClient>, msg: WSServerMessage) => void;\n /** Connected WebUI clients map */\n clients: Map<WebSocket, ConnectedClient>;\n}\n\nexport function createProviderHandlers(deps: ProviderHandlerDeps) {\n const { globalConfigPath, vault, broadcast, clients } = deps;\n let configWriteLock = deps.getConfigWriteLock();\n\n async function loadConfigProviders(): Promise<Record<string, ProviderConfig>> {\n return loadSavedProviders(globalConfigPath, vault);\n }\n\n async function saveConfigProviders(providers: Record<string, ProviderConfig>): Promise<void> {\n const next = configWriteLock\n .then(() => saveProviders(globalConfigPath, vault, providers))\n .catch((err) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.provider_save_failed',\n message: msg,\n timestamp: new Date().toISOString(),\n }));\n });\n configWriteLock = next;\n deps.setConfigWriteLock(next);\n await next;\n }\n\n async function handleKeyUpsert(ws: WebSocket, providerId: string, label: string, apiKey: string): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = upsertKeyRecord(providers, providerId, label, apiKey, new Date().toISOString());\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n async function handleKeyDelete(ws: WebSocket, providerId: string, label: string): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = deleteKeyRecord(providers, providerId, label);\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n async function handleKeySetActive(ws: WebSocket, providerId: string, label: string): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = setActiveKeyRecord(providers, providerId, label);\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n async function handleProviderAdd(ws: WebSocket, payload: { id: string; family: string; baseUrl?: string | undefined; apiKey?: string | undefined }): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = addProviderRecord(providers, payload, new Date().toISOString());\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n if (result.ok) {\n console.log(`[WebUI] Provider \"${payload.id}\" added via provider.add`);\n broadcast(clients, {\n type: 'providers.saved',\n payload: {\n providers: Object.entries(providers).map(([id, cfg]) => {\n const keys = normalizeKeys(cfg);\n return {\n id,\n family: cfg.family ?? id,\n baseUrl: cfg.baseUrl,\n apiKeys: keys.map((k) => ({\n label: k.label,\n maskedKey: maskedKey(k.apiKey),\n isActive: k.label === cfg.activeKey,\n createdAt: k.createdAt,\n })),\n };\n }),\n },\n });\n }\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n async function handleProviderRemove(ws: WebSocket, providerId: string): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = removeProviderRecord(providers, providerId);\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n return { handleKeyUpsert, handleKeyDelete, handleKeySetActive, handleProviderAdd, handleProviderRemove, loadConfigProviders };\n}\n","import type { EventBus, Context, SessionEventBridge } from '@wrongstack/core';\nimport type { WebSocket } from 'ws';\nimport type { ConnectedClient, WSServerMessage } from './types.js';\n\nimport * as path from 'node:path';\n\nexport interface SetupEventsDeps {\n events: EventBus;\n broadcast: (clients: Map<WebSocket, ConnectedClient>, msg: WSServerMessage) => void;\n clients: Map<WebSocket, ConnectedClient>;\n config: { tools?: { maxIterations?: number | undefined } };\n context: Context;\n pendingConfirms: Map<string, (d: 'yes' | 'no' | 'always' | 'deny') => void>;\n /** Optional global config dir (~/.wrongstack) — enables SessionRegistry poll for fleet view. */\n globalConfigPath?: string | undefined;\n /**\n * Audit-level-aware session log bridge. When provided, tool/error/provider\n * events are persisted to the session JSONL (same contract as the CLI) —\n * without it, standalone-WebUI sessions carry no audit events and resume\n * with no tool history.\n */\n sessionBridge?: SessionEventBridge | undefined;\n}\n\nexport function setupEvents(deps: SetupEventsDeps): void {\n const { events, broadcast, clients, config, context, pendingConfirms, globalConfigPath, sessionBridge } = deps;\n\n events.on('iteration.started', (e) => {\n // Read maxIterations from context.meta so the UI reflects the\n // webui setting, falling back to the startup config default.\n const maxIt = typeof context.meta['maxIterations'] === 'number'\n ? context.meta['maxIterations']\n : config.tools?.maxIterations ?? 100;\n broadcast(clients, {\n type: 'iteration.started',\n payload: { index: e.index, maxIterations: maxIt },\n });\n });\n\n events.on('provider.text_delta', (e) => {\n broadcast(clients, { type: 'provider.text_delta', payload: { text: e.text, messageId: 'current' } });\n });\n\n events.on('provider.thinking_delta', (e) => {\n broadcast(clients, { type: 'provider.thinking_delta', payload: { text: e.text } });\n });\n\n events.on('tool.started', (e) => {\n broadcast(clients, {\n type: 'tool.started',\n payload: { id: e.id, name: e.name, input: e.input, messageId: `tool_${e.id}` },\n });\n // Persist for audit + resume tool history (respects auditLevel).\n sessionBridge\n ?.append({\n type: 'tool_call_start',\n ts: new Date().toISOString(),\n name: e.name,\n id: e.id,\n input: e.input,\n })\n .catch(() => { /* best-effort */ });\n });\n\n events.on('tool.progress', (e) => {\n broadcast(clients, {\n type: 'tool.progress',\n payload: { id: e.id, name: e.name, eventType: e.event.type, text: e.event.text },\n });\n sessionBridge\n ?.append({\n type: 'tool_progress',\n ts: new Date().toISOString(),\n name: e.name,\n id: e.id,\n event: { type: e.event.type, text: e.event.text, data: e.event.data },\n })\n .catch(() => { /* best-effort */ });\n });\n\n events.on('tool.executed', (e) => {\n broadcast(clients, {\n type: 'tool.executed',\n payload: { id: e.id, name: e.name, durationMs: e.durationMs, ok: e.ok, input: e.input, output: e.output },\n });\n sessionBridge\n ?.append({\n type: 'tool_call_end',\n ts: new Date().toISOString(),\n name: e.name,\n id: e.id ?? '',\n durationMs: e.durationMs,\n outputSize: e.outputBytes ?? 0,\n ok: e.ok,\n outputBytes: e.outputBytes,\n outputTokens: e.outputTokens,\n outputLines: e.outputLines,\n })\n .catch(() => { /* best-effort */ });\n broadcast(clients, { type: 'todos.updated', payload: { todos: [...context.todos] } });\n\n // Broadcast task/plan updates after task/plan/todo tool executions.\n if (e.name === 'task' || e.name === 'plan' || e.name === 'todo') {\n void (async () => {\n try {\n const taskPath = (context.meta as Record<string, unknown>)['task.path'];\n if (typeof taskPath === 'string' && taskPath) {\n const { loadTasks } = await import('@wrongstack/core');\n const file = await loadTasks(taskPath);\n broadcast(clients, { type: 'tasks.updated', payload: { tasks: file?.tasks ?? [] } });\n }\n } catch { /* best-effort */ }\n try {\n const planPath = (context.meta as Record<string, unknown>)['plan.path'];\n if (typeof planPath === 'string' && planPath) {\n const { loadPlan } = await import('@wrongstack/core');\n const plan = await loadPlan(planPath);\n broadcast(clients, { type: 'plan.updated', payload: { plan: plan ?? { version: 1, sessionId: context.session?.id ?? '', updatedAt: new Date().toISOString(), items: [] } } });\n }\n } catch { /* best-effort */ }\n })();\n }\n });\n\n events.on('provider.response', (e) => {\n broadcast(clients, { type: 'provider.response', payload: { usage: e.usage, stopReason: e.stopReason, messageId: 'current' } });\n });\n\n events.on('context.repaired', (e) => {\n broadcast(clients, { type: 'context.repaired', payload: { removedToolUses: e.removedToolUses, removedToolResults: e.removedToolResults, removedMessages: e.removedMessages } });\n });\n\n events.on('tool.confirm_needed', (e) => {\n const id = e.toolUseId ?? `confirm_${Date.now()}`;\n pendingConfirms.set(id, e.resolve);\n broadcast(clients, { type: 'tool.confirm_needed', payload: { id, toolName: e.tool?.name ?? 'unknown', input: e.input, suggestedPattern: e.suggestedPattern } });\n });\n\n events.on('error', (e) => {\n broadcast(clients, { type: 'error', payload: { phase: e.phase, message: e.err instanceof Error ? e.err.message : String(e.err) } });\n sessionBridge\n ?.append({\n type: 'error',\n ts: new Date().toISOString(),\n message: e.err instanceof Error ? e.err.message : String(e.err),\n phase: e.phase,\n })\n .catch(() => { /* best-effort */ });\n });\n\n // Provider visibility — retry storms and provider failures in the JSONL\n // for forensics, mirroring the CLI's bridge wiring.\n events.on('provider.retry', (e) => {\n sessionBridge\n ?.append({\n type: 'provider_retry',\n ts: new Date().toISOString(),\n providerId: e.providerId,\n attempt: e.attempt,\n delayMs: e.delayMs,\n status: e.status,\n description: e.description,\n })\n .catch(() => { /* best-effort */ });\n });\n\n events.on('provider.error', (e) => {\n sessionBridge\n ?.append({\n type: 'provider_error',\n ts: new Date().toISOString(),\n providerId: e.providerId,\n status: e.status,\n description: e.description,\n retryable: e.retryable,\n })\n .catch(() => { /* best-effort */ });\n });\n\n // ── Inter-agent mailbox visibility ───────────────────────────────────\n // Forward cross-session mailbox activity (messages received by this\n // process's agents, new agent registrations on the project) to the\n // browser so the user sees multi-terminal/multi-surface chatter live.\n // These events are emitted via emit() with untyped names (GlobalMailbox\n // + mailbox-loop), so subscribe by pattern like the TUI does.\n events.onPattern('mailbox.received', (_e, payload) => {\n broadcast(clients, { type: 'mailbox.received', payload } as unknown as WSServerMessage);\n });\n events.onPattern('mailbox.agent_registered', (_e, payload) => {\n broadcast(clients, { type: 'mailbox.agent_registered', payload } as unknown as WSServerMessage);\n });\n\n // Subagent fleet lifecycle\n const forwardSubagent = (kind: string, payload: Record<string, unknown>) =>\n broadcast(clients, { type: 'subagent.event', payload: { kind, sessionId: context.session.id, ...payload } });\n\n events.on('subagent.spawned', (e) => forwardSubagent('spawned', { subagentId: e.subagentId, taskId: e.taskId, name: e.name, provider: e.provider, model: e.model, description: e.description }));\n events.on('subagent.task_started', (e) => forwardSubagent('task_started', { subagentId: e.subagentId, taskId: e.taskId, description: e.description }));\n events.on('subagent.tool_executed', (e) => forwardSubagent('tool_executed', { subagentId: e.subagentId, toolName: e.name, durationMs: e.durationMs, ok: e.ok }));\n events.on('subagent.iteration_summary', (e) => forwardSubagent('iteration_summary', { subagentId: e.subagentId, iteration: e.iteration, toolCalls: e.toolCalls, costUsd: e.costUsd, currentTool: e.currentTool, partialText: e.partialText }));\n events.on('subagent.budget_extended', (e) => forwardSubagent('budget_extended', { subagentId: e.subagentId, totalExtensions: e.totalExtensions }));\n events.on('subagent.ctx_pct', (e) => forwardSubagent('ctx_pct', { subagentId: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext }));\n events.on('subagent.task_completed', (e) => forwardSubagent('task_completed', { subagentId: e.subagentId, status: e.status, iterations: e.iterations, toolCalls: e.toolCalls, finalText: (e as Record<string, unknown>).finalText as string | undefined, error: e.error ? { kind: e.error.kind, message: e.error.message } : undefined }));\n\n // ── Leader (main session) events — forwarded as subagent.event with subagentId 'leader' ──\n // These give the AgentsPage a live leader row with real-time tool tracking,\n // context pressure — matching the TUI's leader entry.\n // Iteration counts, cost, and overall status come from the sessionStore on the frontend.\n\n // Leader spawned: sent on first iteration so the frontend creates the leader row.\n let leaderSpawned = false;\n events.on('iteration.started', () => {\n if (!leaderSpawned) {\n leaderSpawned = true;\n const provider = (context.provider as { id?: string } | undefined)?.id ?? 'unknown';\n forwardSubagent('spawned', {\n subagentId: 'leader',\n name: 'LEADER',\n provider,\n model: context.model,\n description: `Main agent session (${context.session.id})`,\n });\n }\n });\n\n // Leader tool execution: emitted on every tool.executed in the main session.\n events.on('tool.executed', (e) => {\n forwardSubagent('tool_executed', {\n subagentId: 'leader',\n toolName: e.name,\n durationMs: e.durationMs,\n ok: e.ok,\n });\n });\n\n // Leader context pressure + cost: emitted on every provider response.\n events.on('provider.response', (e) => {\n if (e.usage?.input != null) {\n const maxCtx = context.provider.capabilities.maxContext;\n const pct = maxCtx > 0 ? e.usage.input / maxCtx : 0;\n const costUsd = context.tokenCounter.estimateCost().total;\n forwardSubagent('ctx_pct', {\n subagentId: 'leader',\n load: pct,\n tokens: e.usage.input,\n maxContext: maxCtx,\n costUsd,\n });\n }\n });\n\n // Leader iteration updates: we already track iteration started above.\n // The frontend uses sessionStore for accurate cost/iteration counts.\n // When the run completes, the frontend's run.result handler resets isLoading,\n // making the leader go idle. We reset leader state on iteration.started.\n events.on('iteration.completed', () => {\n // Respawn leader if it was cleared (e.g., on session resume).\n if (!leaderSpawned) {\n leaderSpawned = true;\n const provider = (context.provider as { id?: string } | undefined)?.id ?? 'unknown';\n forwardSubagent('spawned', {\n subagentId: 'leader',\n name: 'LEADER',\n provider,\n model: context.model,\n description: `Main agent session (${context.session.id})`,\n });\n }\n });\n\n // ── Mailbox events — broadcast to WebUI for real-time per-project visibility ──\n events.onPattern('mailbox.*', (eventName, payload) => {\n broadcast(clients, { type: 'mailbox.event', payload: { event: eventName, ...payload as Record<string, unknown> } });\n });\n\n // ── Brain events — decisions + proactive interventions, live in the browser ──\n events.onPattern('brain.*', (eventName, payload) => {\n broadcast(clients, { type: 'brain.event', payload: { event: eventName, ...payload as Record<string, unknown> } } as unknown as WSServerMessage);\n });\n\n // ── Cross-process session / fleet status poll ──\n // Periodically read the SessionRegistry and broadcast live session+agent status\n // to all connected clients. Gives the AgentFlowViz a project-level overview of\n // how many sessions are active, what agents are doing, costs, and context usage.\n const globalRoot = globalConfigPath ? path.dirname(globalConfigPath) : undefined;\n if (globalRoot) {\n const statusInterval = setInterval(async () => {\n try {\n const { SessionRegistry } = await import('@wrongstack/core');\n const registry = new SessionRegistry(globalRoot);\n const sessions = await registry.list();\n const live = sessions\n .filter((s) => s.status !== 'stale')\n .map((s) => ({\n sessionId: s.sessionId,\n projectName: s.projectName,\n projectSlug: s.projectSlug,\n projectRoot: s.projectRoot,\n workingDir: s.workingDir,\n gitBranch: s.gitBranch,\n status: s.status,\n pid: s.pid,\n startedAt: s.startedAt,\n agentCount: s.agentCount,\n agents: (s.agents ?? []).map((a) => ({\n id: a.id,\n name: a.name,\n status: a.status,\n currentTool: a.currentTool,\n iterations: a.iterations,\n toolCalls: a.toolCalls,\n lastActivityAt: a.lastActivityAt,\n })),\n }));\n broadcast(clients, { type: 'sessions.status_update', payload: { sessions: live } });\n } catch {\n // Best-effort — never crash for status polling errors\n }\n }, 5_000);\n if (statusInterval.unref) statusInterval.unref();\n }\n}\n","import { listContextWindowModes, atomicWrite } from '@wrongstack/core';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\n/**\n * Custom context modes — user-defined presets that are loaded from disk,\n * merged with the built-in modes, and managed via WebSocket CRUD handlers.\n *\n * Stored in: ~/.wrongstack/custom-context-modes.json\n * Format: { \"modes\": ContextWindowMode[] }\n */\n\nexport interface CustomContextMode {\n id: string;\n name: string;\n description: string;\n thresholds: { warn: number; soft: number; hard: number };\n aggressiveOn: string;\n preserveK: number;\n eliseThreshold: number;\n targetLoad: number;\n /** Whether this is a user-defined (custom) or built-in mode. */\n custom: boolean;\n}\n\nexport interface CustomModeStore {\n modes: Map<string, CustomContextMode>;\n load: () => Promise<void>;\n save: () => Promise<void>;\n create: (mode: CustomContextMode) => { ok: boolean; error?: string | undefined };\n update: (id: string, patch: Partial<CustomContextMode>) => { ok: boolean; error?: string | undefined };\n remove: (id: string) => { ok: boolean; error?: string | undefined };\n list: () => CustomContextMode[];\n}\n\nconst STORE_FILENAME = 'custom-context-modes.json';\n\nfunction storePath(wrongstackDir: string): string {\n return path.join(wrongstackDir, STORE_FILENAME);\n}\n\nconst BUILTIN_IDS = new Set(['balanced', 'frugal', 'deep', 'archival']);\n\nexport function createCustomModeStore(wrongstackDir: string): CustomModeStore {\n const modes = new Map<string, CustomContextMode>();\n\n const load = async (): Promise<void> => {\n modes.clear();\n try {\n const raw = await fs.readFile(storePath(wrongstackDir), 'utf8');\n const parsed = JSON.parse(raw) as { modes?: CustomContextMode[] };\n if (Array.isArray(parsed.modes)) {\n for (const m of parsed.modes) {\n if (m.id && !BUILTIN_IDS.has(m.id)) {\n modes.set(m.id, { ...m, custom: true });\n }\n }\n }\n } catch {\n // File missing or corrupt — start with empty custom modes.\n }\n };\n\n const save = async (): Promise<void> => {\n const arr = [...modes.values()];\n const json = JSON.stringify({ modes: arr }, null, 2);\n await atomicWrite(storePath(wrongstackDir), json);\n };\n\n const create = (\n mode: CustomContextMode,\n ): { ok: boolean; error?: string | undefined } => {\n if (!mode.id || typeof mode.id !== 'string') {\n return { ok: false, error: 'id is required' };\n }\n if (BUILTIN_IDS.has(mode.id)) {\n return { ok: false, error: `Cannot override built-in mode \"${mode.id}\"` };\n }\n if (modes.has(mode.id)) {\n return { ok: false, error: `Mode \"${mode.id}\" already exists` };\n }\n if (!mode.name) {\n return { ok: false, error: 'name is required' };\n }\n const entry: CustomContextMode = {\n id: mode.id,\n name: mode.name,\n description: mode.description || '',\n thresholds: {\n warn: mode.thresholds?.warn ?? 0.6,\n soft: mode.thresholds?.soft ?? 0.75,\n hard: mode.thresholds?.hard ?? 0.9,\n },\n aggressiveOn: mode.aggressiveOn || 'soft',\n preserveK: mode.preserveK ?? 10,\n eliseThreshold: mode.eliseThreshold ?? 2000,\n targetLoad: mode.targetLoad ?? 0.65,\n custom: true,\n };\n modes.set(mode.id, entry);\n void save();\n return { ok: true };\n };\n\n const update = (\n id: string,\n patch: Partial<CustomContextMode>,\n ): { ok: boolean; error?: string | undefined } => {\n if (BUILTIN_IDS.has(id)) {\n return { ok: false, error: `Cannot modify built-in mode \"${id}\"` };\n }\n const existing = modes.get(id);\n if (!existing) {\n return { ok: false, error: `Mode \"${id}\" not found` };\n }\n const next: CustomContextMode = { ...existing };\n if (patch.name !== undefined) next.name = patch.name;\n if (patch.description !== undefined) next.description = patch.description;\n if (patch.thresholds) {\n next.thresholds = {\n warn: patch.thresholds.warn ?? existing.thresholds.warn,\n soft: patch.thresholds.soft ?? existing.thresholds.soft,\n hard: patch.thresholds.hard ?? existing.thresholds.hard,\n };\n }\n if (patch.preserveK !== undefined) next.preserveK = patch.preserveK;\n if (patch.eliseThreshold !== undefined) next.eliseThreshold = patch.eliseThreshold;\n if (patch.targetLoad !== undefined) next.targetLoad = patch.targetLoad;\n if (patch.aggressiveOn !== undefined) next.aggressiveOn = patch.aggressiveOn;\n modes.set(id, next);\n void save();\n return { ok: true };\n };\n\n const remove = (\n id: string,\n ): { ok: boolean; error?: string | undefined } => {\n if (BUILTIN_IDS.has(id)) {\n return { ok: false, error: `Cannot delete built-in mode \"${id}\"` };\n }\n if (!modes.delete(id)) {\n return { ok: false, error: `Mode \"${id}\" not found` };\n }\n void save();\n return { ok: true };\n };\n\n const list = (): CustomContextMode[] => {\n const builtins = listContextWindowModes().map((m) => ({\n id: m.id as string,\n name: m.name,\n description: m.description,\n thresholds: { ...m.thresholds },\n aggressiveOn: m.aggressiveOn as string,\n preserveK: m.preserveK,\n eliseThreshold: m.eliseThreshold,\n targetLoad: m.targetLoad,\n custom: false as const,\n }));\n const custom = [...modes.values()];\n return [...builtins, ...custom];\n };\n\n return { modes, load, save, create, update, remove, list };\n}\n","/**\n * Per-section context-window token estimate for the `context.debug` command.\n *\n * Uses the simple 4-chars-per-token heuristic — not exact, but close enough to\n * spot which section (system prompt, tool schemas, or message history) is\n * eating the context window. Tool schemas in particular are easy to overlook:\n * each tool ships its full JSON schema to the model every turn, so 20+ builtins\n * can cost 10-20k tokens on their own.\n *\n * Extracted from `index.ts` as a pure function so the breakdown maths can be\n * unit tested without standing up a Context/ToolRegistry.\n */\n\n/** 4-chars-per-token heuristic estimate for a string. */\nexport function estimateTokens(s: string): number {\n return Math.ceil(s.length / 4);\n}\n\n/** Stringify arbitrary content for length estimation (JSON, with fallbacks). */\nexport function stringifyContent(c: unknown): string {\n if (typeof c === 'string') return c;\n try {\n return JSON.stringify(c);\n } catch {\n return String(c);\n }\n}\n\ninterface PromptBlock {\n text?: string | undefined;\n}\ninterface ToolLike {\n name: string;\n inputSchema?: unknown | undefined;\n description?: string | undefined;\n}\ninterface ContentBlock {\n type?: string | undefined;\n text?: string | undefined;\n input?: unknown | undefined;\n content?: unknown | undefined;\n name?: string | undefined;\n}\ninterface MessageLike {\n role: string;\n content: unknown;\n}\n\nexport interface ToolTokenEntry {\n name: string;\n tokens: number;\n}\nexport interface MessageTokenEntry {\n index: number;\n role: string;\n tokens: number;\n preview: string;\n}\n\nexport interface ContextBreakdown {\n total: number;\n systemPrompt: number;\n tools: { total: number; count: number; breakdown: ToolTokenEntry[] };\n messages: { total: number; count: number; breakdown: MessageTokenEntry[] };\n}\n\nexport function messageTokens(content: unknown): number {\n if (typeof content === 'string') return estimateTokens(content);\n if (!Array.isArray(content)) return 0;\n let tk = 0;\n for (const b of content as ContentBlock[]) {\n if (b.type === 'text') tk += estimateTokens(b.text ?? '');\n else if (b.type === 'tool_use') tk += estimateTokens(stringifyContent(b.input));\n else if (b.type === 'tool_result') tk += estimateTokens(stringifyContent(b.content));\n else tk += estimateTokens(stringifyContent(b));\n }\n return tk;\n}\n\nexport function messagePreview(content: unknown): string {\n if (typeof content === 'string') return content.slice(0, 60);\n if (!Array.isArray(content)) return '';\n return (content as ContentBlock[])\n .map((b) =>\n b.type === 'text'\n ? (b.text ?? '').slice(0, 40)\n : b.type === 'tool_use'\n ? `[tool_use: ${b.name}]`\n : b.type === 'tool_result'\n ? '[tool_result]'\n : `[${b.type}]`,\n )\n .join(' ')\n .slice(0, 60);\n}\n\n/**\n * Compute the per-section token breakdown for the active context. Mirrors the\n * shape the `context.debug` WS reply expects (minus the `mode`/`policy` fields,\n * which the caller layers on from `context.meta`).\n */\nexport function estimateContextBreakdown(input: {\n systemPrompt: ReadonlyArray<PromptBlock>;\n tools: ReadonlyArray<ToolLike>;\n messages: ReadonlyArray<MessageLike>;\n}): ContextBreakdown {\n const sysTokens = input.systemPrompt.reduce((acc, b) => acc + estimateTokens(b.text ?? ''), 0);\n\n const toolBreakdown: ToolTokenEntry[] = input.tools.map((t) => {\n const schema = t.inputSchema ?? {};\n const desc = t.description ?? '';\n return {\n name: t.name,\n tokens:\n estimateTokens(t.name) + estimateTokens(desc) + estimateTokens(stringifyContent(schema)),\n };\n });\n const toolTokens = toolBreakdown.reduce((a, b) => a + b.tokens, 0);\n\n const messageBreakdown: MessageTokenEntry[] = input.messages.map((m, i) => ({\n index: i,\n role: m.role,\n tokens: messageTokens(m.content),\n preview: messagePreview(m.content),\n }));\n const msgTokens = messageBreakdown.reduce((a, b) => a + b.tokens, 0);\n\n return {\n total: sysTokens + toolTokens + msgTokens,\n systemPrompt: sysTokens,\n tools: { total: toolTokens, count: input.tools.length, breakdown: toolBreakdown },\n messages: { total: msgTokens, count: input.messages.length, breakdown: messageBreakdown },\n };\n}\n","// Eternal-autonomy iteration broadcast wiring.\n//\n// The CLI's `runWebUI` owns the eternal-autonomy engine and exposes a\n// `subscribeEternalIteration` callback that lets the webui hook into\n// the journal-entry stream. This module extracts the wiring in\n// isolation so it can be unit-tested without spinning up the full\n// server (clients, WS, HTTP bring-up).\n//\n// `createEternalSubscription` is the helper that:\n// 1. Calls the caller-supplied `subscribe` function with a broadcast\n// closure bound to the current `clients` Map.\n// 2. Captures the returned disposer so `tearDown` can invoke it on\n// server shutdown.\n//\n// Both the disposer and the `JournalEntry` are projected by the caller\n// — this module intentionally knows nothing about the engine itself.\n\nimport type { WebSocket } from 'ws';\nimport type { WSServerMessage } from './types.js';\nimport type { JournalEntry } from '@wrongstack/core';\n\nexport type EternalSubscribe = (\n fn: (entry: JournalEntry) => void,\n) => () => void;\n\n// `clients` is generic so callers that use a structurally-similar\n// `ConnectedClient` (the CLI's own Map<WebSocket, { ws; sessionId }>,\n// for example) don't have to alias their type to webui's\n// `ConnectedClient`. The helper doesn't read the value at all —\n// `broadcast` gets to decide whether to use the map or not — so we\n// only need the key type (WebSocket) to be present, which is\n// enforced by the constraint.\nexport type EternalBroadcast<C> = (clients: Map<WebSocket, C>, msg: WSServerMessage) => void;\n\nexport interface EternalSubscription {\n /** Tear down the underlying engine subscription. Idempotent. */\n dispose: () => void;\n}\n\nexport function createEternalSubscription<C>(\n subscribe: EternalSubscribe,\n broadcast: EternalBroadcast<C>,\n clientsRef: () => Map<WebSocket, C>,\n): EternalSubscription {\n let disposed = false;\n const dispose = subscribe((entry) => {\n if (disposed) return;\n broadcast(clientsRef(), {\n type: 'eternal.iteration',\n payload: { entry },\n });\n });\n return {\n dispose() {\n if (disposed) return;\n disposed = true;\n dispose();\n },\n };\n}\n","// Open the OS file manager or a terminal at a given path (the\n// `shell.open` WS message handler).\n//\n// Both the standalone `startWebUI` and the CLI's `runWebUI` needed\n// the same logic \\u2014 metacharacter guard, `path.resolve` to fold\n// any `..` traversal, and a cross-platform `spawn()` for the\n// platform's file manager (`explorer`/`open`/`xdg-open`) or\n// terminal (`cmd /c start cmd /k` / `open -a Terminal` /\n// `x-terminal-emulator` \\u2192 `gnome-terminal` \\u2192 `xterm` fallback chain).\n// Phase 1.2 added the `spawn`-based version on the CLI side; the\n// standalone got it shortly after. The two implementations have\n// drifted slightly (CLI lacks the `logger.warn` on spawn failure),\n// which is exactly the class of bug extraction prevents.\n//\n// This module returns a `ShellOpenResult` so the caller (the WS\n// router in either entry point) can pipe it through `sendResult`\n// with the same `success`/`message` shape both sides already use.\n// Spawn is async + detached + unref'd so a missing terminal\n// emulator (which fires `error` async) never crashes the server\n// \\u2014 the fallback chain tries the next one, the file-manager\n// branch just logs.\n//\n// SECURITY: the path arrives over the WebSocket. The\n// metacharacter guard (`/[&|<>^\\\"'`\\n\\r]/`) closes the cmd.exe\n// re-parsing injection class (`\"foo\" && calc.exe`,\n// `'$(...)'`, backticks, redirections) before anything reaches\n// the argv array. The `path.resolve` then folds any\n// `..` traversal, and `fs.access(resolved)` enforces that the\n// target exists \\u2014 callers can't ask us to open a non-existent\n// path. Defense in depth: the spawn below uses an argv array\n// (no string concatenation), and `windowsHide` keeps the\n// launcher console out of the way.\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport type { Logger } from '@wrongstack/core';\n\nexport type ShellOpenTarget = 'terminal' | 'file-manager';\n\nexport interface ShellOpenRequest {\n path: string;\n target: ShellOpenTarget;\n}\n\nexport interface ShellOpenResult {\n success: boolean;\n message: string;\n}\n\n/** Metacharacters that would let a path slip past an argv-array\n * spawn and into a shell re-parse on Windows. Real directory\n * paths virtually never contain these. */\nconst METACHAR_REGEX = /[&|<>^\"'`\\n\\r]/;\n\nexport async function handleShellOpen(\n req: ShellOpenRequest,\n logger: Logger,\n): Promise<ShellOpenResult> {\n try {\n const resolved = path.resolve(req.path);\n await fs.access(resolved);\n if (METACHAR_REGEX.test(resolved)) {\n return { success: false, message: 'Path contains unsupported characters.' };\n }\n\n const platform = process.platform;\n const launch = (cmd: string, args: string[], onError?: () => void) => {\n const child = spawn(cmd, args, {\n detached: true,\n stdio: 'ignore',\n windowsHide: true,\n });\n // `error` fires when the binary is missing (e.g. xterm not\n // installed) \\u2014 log it, but never block the WS caller. The\n // fallback chain in the terminal branch uses onError to try\n // the next emulator.\n child.on('error', (err) => {\n logger.warn(`shell.open spawn failed: ${err.message}`);\n onError?.();\n });\n child.unref();\n };\n\n if (req.target === 'file-manager') {\n if (platform === 'win32') launch('explorer', [resolved]);\n else if (platform === 'darwin') launch('open', [resolved]);\n else launch('xdg-open', [resolved]);\n } else if (req.target === 'terminal') {\n if (platform === 'win32') {\n // `start` is a cmd builtin; each token is a separate argv\n // entry (Node quotes them individually \\u2014 no string\n // concatenation). This replaces the previous\n // `start cmd /k cd /d \"...\"` exec() call that was\n // shell-injectable.\n launch('cmd', ['/c', 'start', 'cmd', '/k', 'cd', '/d', resolved]);\n } else if (platform === 'darwin') {\n launch('open', ['-a', 'Terminal', resolved]);\n } else {\n // Try several terminal emulators\n launch('x-terminal-emulator', [`--working-directory=${resolved}`], () =>\n launch('gnome-terminal', [`--working-directory=${resolved}`], () =>\n launch('xterm', ['-e', `cd '${resolved}' && ${process.env['SHELL'] ?? 'sh'}`]),\n ),\n );\n }\n } else {\n return { success: false, message: `Unknown shell.open target: ${String(req.target)}` };\n }\n return { success: true, message: `Opened ${req.target} at ${resolved}` };\n } catch (err) {\n return { success: false, message: err instanceof Error ? err.message : String(err) };\n }\n}\n","// Server entry point for standalone WebUI.\n// Bind defaults: 127.0.0.1:3457 (loopback only). Override with WS_HOST / WS_PORT.\n// HTTP frontend defaults to 3456 (override with PORT). Run several instances on\n// different PORT/WS_PORT pairs — `webui --list` shows which are open for which\n// project (registry: ~/.wrongstack/webui-instances.json).\nimport { startWebUI } from './index.js';\nimport { formatInstances, listInstances } from './instance-registry.js';\n\nconst argv = process.argv.slice(2);\n\n// `webui --list` / `webui ls` — print running instances and exit. Cheap,\n// side-effect-free (it only prunes dead pids), so it never boots a server.\nif (argv.includes('--list') || argv.includes('-l') || argv[0] === 'ls') {\n listInstances()\n .then((instances) => {\n console.log(formatInstances(instances));\n process.exit(0);\n })\n .catch((err) => {\n console.error(JSON.stringify({\n level: 'fatal',\n event: 'webui.instance_registry_read_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n process.exit(1);\n });\n} else {\n const wsPort = Number.parseInt(process.env['WS_PORT'] ?? '3457', 10);\n const wsHost = process.env['WS_HOST'] ?? '127.0.0.1';\n const open =\n argv.includes('--open') || argv.includes('-o') || process.env['WEBUI_OPEN'] === '1';\n\n console.log(`[WebUI] Starting standalone server on ${wsHost}:${wsPort}...`);\n\n startWebUI({ wsPort, wsHost, open }).catch((err) => {\n console.error(JSON.stringify({\n level: 'fatal',\n event: 'webui.startup_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n process.exit(1);\n });\n}\n"],"mappings":"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","names":["expectDefined","GlobalMailbox","fs","path","Buffer","fs","path","DefaultMemoryStore","DefaultModeStore","DefaultSessionStore","DefaultSkillLoader","DefaultSystemPromptBuilder","DefaultTokenCounter","createStrategyCompactor","TOKENS","atomicWrite","decryptConfigSecrets","encryptConfigSecrets","s","path","path","fs","atomicWrite","resolve","fs","path","atomicWrite","broadcast","path","broadcast","atomicWrite","fs","path","load","save","broadcast","fs","path","spawn","expectDefined","TOKENS","DefaultMemoryStore","DefaultSessionStore","DefaultTokenCounter","DefaultModeStore","DefaultSkillLoader","DefaultSystemPromptBuilder","GlobalMailbox","decryptConfigSecrets","encryptConfigSecrets","atomicWrite","createStrategyCompactor","provider","verifyClient","resolve","globalConfigPath","modePrompt","stat"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/index.ts","../../src/server/http-server.ts","../../src/server/ws-auth.ts","../../src/server/file-handlers.ts","../../src/server/file-picker.ts","../../src/server/ws-utils.ts","../../src/server/memory-handlers.ts","../../../runtime/src/container.ts","../../src/server/boot.ts","../../src/server/autophase-ws-handler.ts","../../src/server/collaboration-ws-handler.ts","../../src/server/worktree-ws-handler.ts","../../src/server/mailbox-handlers.ts","../../src/server/lifecycle.ts","../../src/server/instance-registry.ts","../../src/server/port-utils.ts","../../src/server/open-browser.ts","../../src/server/usage-cost.ts","../../src/server/provider-config-io.ts","../../src/server/provider-keys.ts","../../src/server/provider-handlers.ts","../../src/server/setup-events.ts","../../src/server/custom-context-modes.ts","../../src/server/token-estimator.ts","../../src/server/eternal-iteration-broadcast.ts","../../src/server/shell-open.ts","../../src/server/entry.ts"],"sourcesContent":["import { expectDefined, GlobalMailbox, projectSlug, getSessionRegistry, AgentStatusTracker } from '@wrongstack/core';\nimport { makeMailboxTool, makeMailSendTool, makeMailInboxTool, mailboxSessionTag } from '@wrongstack/core';\nimport {\n BrainMonitor,\n DefaultBrainArbiter,\n ObservableBrainArbiter,\n createAutonomyBrain,\n createTieredBrainArbiter,\n type BrainArbiter,\n type BrainAutoRisk,\n} from '@wrongstack/core';\nimport * as fs from 'node:fs/promises';\nimport * as http from 'node:http';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { createHttpServer } from './http-server.js';\nimport {\n handleFilesTree,\n handleFilesRead,\n handleFilesWrite,\n handleFilesList,\n} from './file-handlers.js';\nimport {\n handleMemoryList,\n handleMemoryRemember,\n handleMemoryForget,\n} from './memory-handlers.js';\nimport {\n Agent,\n AutoCompactionMiddleware,\n Context,\n DefaultMemoryStore,\n DefaultModeStore,\n DefaultModelsRegistry,\n DefaultSessionReader,\n DefaultSessionStore,\n DefaultSkillLoader,\n DefaultSystemPromptBuilder,\n DefaultTokenCounter,\n AnnotationsStore,\n CollaborationBus,\n collabPauseMiddleware,\n collabInjectMiddleware,\n estimateRequestTokensCalibrated,\n EventBus,\n createStrategyCompactor,\n type ProviderConfig,\n type Provider,\n ProviderRegistry,\n TOKENS,\n ToolRegistry,\n atomicWrite,\n createDefaultPipelines,\n createSessionEventBridge,\n resolveSessionLoggingConfig,\n DEFAULT_CONTEXT_WINDOW_MODE_ID,\n DEFAULT_SESSION_PRUNE_DAYS,\n DEFAULT_TOOLS_CONFIG,\n listContextWindowModes,\n repairToolUseAdjacency,\n resolveContextWindowPolicy,\n enhanceUserPrompt,\n recentTextTurns,\n} from '@wrongstack/core';\nimport { ToolExecutor } from '@wrongstack/core/execution';\nimport { decryptConfigSecrets, encryptConfigSecrets } from '@wrongstack/core/security';\nimport { buildProviderFactoriesFromRegistry, makeProviderFromConfig } from '@wrongstack/providers';\nimport { builtinToolsPack, forgetTool, rememberTool, searchMemoryTool, relatedMemoryTool } from '@wrongstack/tools';\nimport { type WebSocket, WebSocketServer } from 'ws';\nimport { createDefaultContainer } from '../../../runtime/src/container.js';\nimport { bootConfig, patchConfig } from './boot.js';\nimport { AutoPhaseWebSocketHandler } from './autophase-ws-handler.js';\nimport { CollaborationWebSocketHandler } from './collaboration-ws-handler.js';\nimport { WorktreeWebSocketHandler } from './worktree-ws-handler.js';\nimport { handleMailboxMessages, handleMailboxAgents, handleMailboxClear } from './mailbox-handlers.js';\nimport { verifyClient as verifyWsClient } from './ws-auth.js';\nimport { registerShutdownHandlers } from './lifecycle.js';\nimport { registerInstance, unregisterInstance } from './instance-registry.js';\nimport { findFreePort } from './port-utils.js';\nimport { openBrowser } from './open-browser.js';\nimport { computeUsageCost, getCostRates } from './usage-cost.js';\nimport { createProviderHandlers } from './provider-handlers.js';\nimport { setupEvents } from './setup-events.js';\nimport { createCustomModeStore } from './custom-context-modes.js';\nimport { maskedKey, normalizeKeys } from './provider-keys.js';\nimport { send, broadcast, sendResult, errMessage, generateAuthToken } from './ws-utils.js';\nimport { estimateContextBreakdown } from './token-estimator.js';\nimport { createEternalSubscription } from './eternal-iteration-broadcast.js';\nimport { handleShellOpen, type ShellOpenRequest, type ShellOpenResult } from './shell-open.js';\n// Re-export types — shared message shapes and options used by both the\n// standalone server and the CLI's `--webui` embedded mode.\nexport type { WebUIOptions, BackendServices } from './types.js';\nexport type { WSServerMessage, WSClientMessage, ConnectedClient } from './types.js';\n\n// Re-export the static-serve + multi-instance building blocks so other packages\n// (the CLI's `--webui` mode) can serve the same React frontend, inject the live\n// WS port, pick free ports, and register in the shared instance registry —\n// without duplicating any of that logic.\nexport { createHttpServer, buildCspHeader, injectWsPort } from './http-server.js';\nexport type { CreateHttpServerOptions } from './http-server.js';\nexport { findFreePort, isPortFree } from './port-utils.js';\nexport { openBrowser, browserOpenCommand } from './open-browser.js';\n// Token estimator primitives — exposed for the CLI's embedded webui\n// (which historically inlined its own copy and let it drift). Now\n// there's exactly one definition. See\n// packages/cli/src/webui-server.ts Phase 2 of the refactor plan.\nexport {\n estimateTokens,\n messageTokens,\n messagePreview,\n stringifyContent,\n type ContextBreakdown,\n type MessageTokenEntry,\n type ToolTokenEntry,\n} from './token-estimator.js';\nexport {\n registerInstance,\n unregisterInstance,\n listInstances,\n formatInstances,\n registryPath,\n defaultBaseDir,\n type WebUIInstanceRecord,\n} from './instance-registry.js';\n\n// WebSocket utilities shared with CLI\nexport {\n createEternalSubscription,\n type EternalSubscribe,\n type EternalBroadcast,\n type EternalSubscription,\n} from './eternal-iteration-broadcast.js';\nexport {\n handleShellOpen,\n type ShellOpenRequest,\n type ShellOpenResult,\n type ShellOpenTarget,\n} from './shell-open.js';\nexport {\n send,\n broadcast,\n sendResult,\n errMessage,\n generateAuthToken,\n} from './ws-utils.js';\n\n// File operation handlers shared with CLI (files.tree, files.read, files.write, files.list)\nexport {\n handleFilesTree,\n handleFilesRead,\n handleFilesWrite,\n handleFilesList,\n} from './file-handlers.js';\n\n// Memory operation handlers shared with CLI (memory.list, memory.remember, memory.forget)\nexport {\n handleMemoryList,\n handleMemoryRemember,\n handleMemoryForget,\n} from './memory-handlers.js';\n\n// Custom context-mode store shared with the CLI's embedded server\n// (context.mode.create/update/delete + custom-aware list/switch).\nexport {\n createCustomModeStore,\n type CustomModeStore,\n type CustomContextMode,\n} from './custom-context-modes.js';\n\n// WS auth — pure functions for verifying WebSocket connections\nexport {\n verifyClient,\n isLoopbackHostname,\n isLoopbackBind,\n tokenMatches,\n extractToken,\n hostHeaderOk,\n type VerifyClientInput,\n} from './ws-auth.js';\n\n// Provider/API-key record transforms (pure functions, testable without I/O)\nexport {\n normalizeKeys,\n writeKeysBack,\n maskedKey,\n upsertKey,\n deleteKey,\n setActiveKey,\n addProvider,\n removeProvider,\n type KeyOpResult,\n type ProvidersRecord,\n} from './provider-keys.js';\n\n// Provider config load/save (decrypt from / encrypt to global config)\nexport {\n loadSavedProviders,\n saveProviders,\n createProviderConfigIO,\n} from './provider-config-io.js';\n\n// AutoPhase WebSocket handler — manages AutoPhase lifecycle via WS messages.\n// Exported so the CLI's embedded webui-server can also handle autophase.*\n// messages when running in --webui mode.\nexport { AutoPhaseWebSocketHandler } from './autophase-ws-handler.js';\n\n// Message + client shapes now live in ./types.ts (shared with the CLI's\n// embedded server). Imported here for internal use; re-exported above for\n// external consumers. The previous local copies shadowed these and made the\n// `Map<WebSocket, ConnectedClient>` passed to the extracted ws-utils helpers\n// nominally distinct, which TS rejected.\nimport type { ConnectedClient, WSClientMessage, WSServerMessage, WebUIOptions } from './types.js';\n\nexport async function startWebUI(\n opts: WebUIOptions & {\n wsPort?: number | undefined;\n wsHost?: string | undefined;\n open?: boolean | undefined;\n } = {},\n): Promise<void> {\n const requestedWsPort = opts.wsPort ?? 3457;\n // Bind to loopback IP by default (not the string \"localhost\", which on some\n // hosts resolves to IPv6 ::1 and surprises older WS clients). Set WS_HOST or\n // pass opts.wsHost to override (e.g. \"0.0.0.0\" for LAN access).\n const wsHost = opts.wsHost ?? '127.0.0.1';\n const requestedHttpPort = Number.parseInt(process.env['PORT'] ?? '3456', 10);\n\n // Port resolution. Unless WEBUI_STRICT_PORT is set, auto-advance past any port\n // already taken by another instance so running `webui` several times \"just\n // works\" — the real ports are then stamped into the served HTML and the\n // instance registry. Strict mode keeps the requested ports and lets bind fail\n // loudly (useful behind a reverse proxy that expects fixed ports).\n const strictPort =\n process.env['WEBUI_STRICT_PORT'] === '1' || process.env['WEBUI_STRICT_PORT'] === 'true';\n let wsPort = requestedWsPort;\n let httpPort = requestedHttpPort;\n if (!strictPort) {\n // Resolve HTTP first, then WS excluding it, so successive instances land on\n // tidy adjacent pairs (3456/3457, 3458/3459, …) instead of interleaving.\n httpPort = await findFreePort(wsHost, requestedHttpPort);\n wsPort = await findFreePort(wsHost, requestedWsPort, { exclude: new Set([httpPort]) });\n if (httpPort !== requestedHttpPort) {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.port_reassigned',\n protocol: 'HTTP',\n requested: requestedHttpPort,\n assigned: httpPort,\n timestamp: new Date().toISOString(),\n }));\n }\n if (wsPort !== requestedWsPort) {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.port_reassigned',\n protocol: 'WS',\n requested: requestedWsPort,\n assigned: wsPort,\n timestamp: new Date().toISOString(),\n }));\n }\n }\n\n console.log('[WebUI] Starting backend services...');\n\n // Boot configuration\n const boot = await bootConfig();\n const { config: baseConfig, globalConfigPath, wpaths, logger } = boot;\n // PR 5 of Phase 2: when the caller (typically the CLI) supplies a\n // pre-built `BackendServices`, prefer its `vault` over the one the\n // default boot would construct. This lets `runWebUI` keep owning the\n // vault lifecycle (so it can decrypt/encrypt its own config writes\n // in lockstep with the rest of the CLI session) instead of having\n // the webui build a parallel vault it can never see.\n const vault = opts.services?.vault ?? boot.vault;\n let config = baseConfig;\n\n /** Mutable project root — updated on `projects.select`. File handlers,\n * sessionStartPayload, and session store use this value. */\n let projectRoot = boot.projectRoot;\n /** Mutable working directory — starts at projectRoot, changeable via\n * `working_dir.set` WS message. Must always stay inside projectRoot. */\n let workingDir = projectRoot;\n\n // Serialize concurrent config writes to prevent races between model.switch\n // and key.add/key.update handlers that both read-modify-write globalConfigPath.\n let configWriteLock: Promise<void> = Promise.resolve();\n\n console.log('[WebUI] Config loaded:', config.provider ?? '(none)', '/', config.model ?? '(none)');\n\n // If no active provider is set but there are saved providers, pick the first one.\n // This handles configs written in older formats or by external tools.\n // Guard against config.providers being a string or other non-object value\n // (e.g., from a corrupted config or YAML parser misreading the value).\n if (\n !config.provider &&\n config.providers &&\n typeof config.providers === 'object' &&\n config.providers !== null &&\n !Array.isArray(config.providers) &&\n Object.keys(config.providers).length > 0\n ) {\n const firstKey = expectDefined(Object.keys(config.providers)[0]);\n config = patchConfig(config, { provider: firstKey });\n console.log('[WebUI] No active provider — auto-selected:', firstKey);\n }\n\n // If still no provider, the frontend will show a no-provider welcome screen.\n // We still start the HTTP/WS servers so the user can configure via the UI.\n const needsProvider = !config.provider || !config.model;\n\n // ModelsRegistry — use injected one if `services.modelsRegistry` was passed,\n // otherwise build a fresh one. The injected path lets the CLI's `runWebUI`\n // share a single registry across its own runtime and the webui surface.\n const modelsRegistry =\n opts.services?.modelsRegistry ??\n new DefaultModelsRegistry({\n cacheFile: wpaths.modelsCache,\n ttlSeconds: 24 * 3600,\n });\n\n // Container via shared factory\n const container = createDefaultContainer({ config, wpaths, logger, modelsRegistry });\n // PR 5 of Phase 2: when the caller (typically the CLI) supplies a\n // pre-built `BackendServices`, prefer its `configStore` over the one\n // the default container would resolve. This is the read+write\n // counterpart of the `vault` injection above: together they let\n // `runWebUI` own the global config lifecycle and have the webui\n // operate on the *same* in-memory store, so a `provider.switch`\n // from the webui is visible to the CLI's next call without a disk\n // round-trip in between.\n const configStore = opts.services?.configStore ?? container.resolve(TOKENS.ConfigStore);\n\n // Provider registry\n const providerRegistry = new ProviderRegistry();\n try {\n const factories = await buildProviderFactoriesFromRegistry({\n registry: modelsRegistry,\n log: logger,\n });\n for (const f of factories) providerRegistry.register(f);\n console.log('[WebUI] Provider registry loaded:', providerRegistry.list().length, 'providers');\n } catch (err) {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.provider_registry_load_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n }\n\n // Tool registry — use injected one if `services.toolRegistry` was passed.\n // When injected, the caller has already registered the tools they want\n // (the CLI's runWebUI registers its own runtime tools); startWebUI just\n // uses the registry as-is.\n const toolRegistry =\n opts.services?.toolRegistry ??\n (() => {\n const r = new ToolRegistry();\n r.registerAllOrThrow([...(builtinToolsPack.tools ?? [])], builtinToolsPack.name);\n return r;\n })();\n\n // Memory tools\n const memoryStore = new DefaultMemoryStore({ paths: wpaths });\n if (config.features.memory) {\n toolRegistry.register(rememberTool(memoryStore));\n toolRegistry.register(forgetTool(memoryStore));\n toolRegistry.register(searchMemoryTool(memoryStore));\n toolRegistry.register(relatedMemoryTool(memoryStore));\n }\n\n // Event bus — use injected one if `services.events` was passed. The CLI's\n // runWebUI owns the agent's EventBus so it can wire sub-agents onto the\n // same bus the webui dashboard reads from. When injected, we just\n // attach the logger and reuse the existing instance.\n const events = opts.services?.events ?? new EventBus();\n events.setLogger(logger);\n\n // Inter-agent mailbox tools — same project-level GlobalMailbox the CLI\n // registers, keyed by wpaths.projectDir so WebUI agents and terminal\n // agents on the same project share one inbox and can chat/broadcast.\n // mail_send/mail_inbox are the high-affordance thin wrappers.\n toolRegistry.register(makeMailboxTool({ projectDir: wpaths.projectDir, events }));\n toolRegistry.register(makeMailSendTool({ projectDir: wpaths.projectDir, events }));\n toolRegistry.register(makeMailInboxTool({ projectDir: wpaths.projectDir, events }));\n console.log('[WebUI] Tool registry loaded:', toolRegistry.list().length, 'tools');\n\n // Session store — mutable so projects.select can swap it to the new project's dir.\n // Use the injected one if `services.session` was passed. The CLI's\n // runWebUI already has its own session store pointing at the\n // right per-project dir; we reuse it here so the webui reads\n // the same history the CLI is writing.\n let sessionStore = opts.services?.session ?? new DefaultSessionStore({ dir: wpaths.projectSessions });\n // Prune old sessions on server start (non-blocking). Skipped when\n // an injected store is in use — the CLI's eternal loop is\n // responsible for its own lifecycle and pruning an in-use store\n // would race with the CLI's own prune policy.\n if (!opts.services?.session) {\n sessionStore\n .prune(DEFAULT_SESSION_PRUNE_DAYS)\n .then((count) => {\n if (count > 0) logger.info(`Pruned ${count} old session${count === 1 ? '' : 's'}.`);\n })\n .catch(() => undefined);\n }\n // Session reader — same on-disk store, read-only access. Used by the\n // collaboration handler to replay the last N events to late-joining\n // observers (Phase 1.5 of idea #13).\n const sessionReader = new DefaultSessionReader({ store: sessionStore });\n // Annotations store — sidecar files for collaboration notes (Phase 2\n // of idea #13). Living under `projectSessions` so all per-session\n // data is colocated and travels with the project.\n const annotationsStore = new AnnotationsStore({ dir: wpaths.projectSessions, events });\n let session = await sessionStore.create({\n id: '',\n title: '',\n model: config.model,\n provider: config.provider,\n });\n // Wall-clock when the *current* session started. Updated on /new and on\n // /resume so /stats can report accurate elapsed time per the active\n // session, not the daemon process uptime.\n let sessionStartedAt = Date.now();\n console.log('[WebUI] Session created:', session.id);\n\n // ── Cross-surface discovery ──────────────────────────────────────────\n // (1) Register/refresh this project in ~/.wrongstack/projects.json so\n // pickers and other surfaces see it regardless of which interface\n // opened it first. (2) Register this session in the cross-process\n // SessionRegistry so terminals' `/sessions status` lists this WebUI\n // (and vice versa). Both best-effort — discovery must not block boot.\n try {\n await touchProjectEntry(projectRoot, workingDir);\n } catch { /* best-effort */ }\n let statusTracker: AgentStatusTracker | undefined;\n try {\n const registry = getSessionRegistry(wpaths.globalRoot);\n await registry.register({\n sessionId: session.id,\n projectSlug: wpaths.projectSlug,\n projectRoot,\n projectName: path.basename(projectRoot),\n workingDir,\n pid: process.pid,\n startedAt: new Date().toISOString(),\n });\n statusTracker = new AgentStatusTracker({ events, registry });\n statusTracker.start();\n const stopTracking = async () => {\n try {\n await registry.markClosing();\n statusTracker?.stop();\n } catch { /* ignore */ }\n };\n process.once('beforeExit', () => { void stopTracking(); });\n process.once('SIGINT', () => { void stopTracking(); });\n process.once('SIGTERM', () => { void stopTracking(); });\n } catch { /* best-effort — discovery degrades gracefully */ }\n\n // Token counter\n const tokenCounter = new DefaultTokenCounter({\n registry: modelsRegistry,\n providerId: config.provider,\n });\n\n // Mode store\n const modeStore = new DefaultModeStore({ directory: wpaths.configDir });\n const activeMode = await modeStore.getActiveMode();\n let modeId = activeMode?.id ?? 'default';\n const modePrompt = activeMode?.prompt ?? '';\n\n // Custom context modes store — user-defined presets persisted to disk.\n // Loaded once on startup; merges with built-in modes in the list handler.\n const customModeStore = createCustomModeStore(wpaths.configDir);\n await customModeStore.load();\n console.log(\n '[WebUI] Custom context modes loaded:',\n customModeStore.list().filter((m) => (m as { custom?: boolean }).custom).length,\n 'custom',\n );\n\n // System prompt builder\n const resolvedModel = await modelsRegistry.getModel(config.provider, config.model);\n const modelCapabilities = resolvedModel?.capabilities\n ? {\n maxContextTokens: resolvedModel.capabilities.maxContext,\n supportsTools: resolvedModel.capabilities.tools,\n supportsVision: resolvedModel.capabilities.vision,\n supportsReasoning: resolvedModel.capabilities.reasoning,\n }\n : undefined;\n\n const skillLoader = config.features.skills\n ? new DefaultSkillLoader({ paths: wpaths })\n : undefined;\n const systemPromptBuilder = new DefaultSystemPromptBuilder({\n memoryStore,\n skillLoader,\n modeStore,\n modeId,\n modePrompt,\n modelCapabilities,\n });\n\n // Fetch online agents from the shared mailbox to include in system prompt\n let onlineAgents: import('@wrongstack/core').MailboxAgentStatus[] = [];\n try {\n const systemMailbox = new GlobalMailbox(wpaths.projectDir);\n onlineAgents = await systemMailbox.getAgentStatuses();\n } catch {\n // Non-fatal — mailbox errors should not block prompt building\n }\n\n const systemPrompt = await systemPromptBuilder.build({\n cwd: projectRoot,\n projectRoot,\n tools: toolRegistry.list(),\n provider: config.provider,\n model: config.model,\n onlineAgents,\n });\n\n // Build provider (only if provider is configured)\n let provider: ReturnType<ProviderRegistry['create']>;\n if (!needsProvider) {\n const providerConfig = config.providers?.[config.provider] ?? {\n type: config.provider,\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n };\n try {\n const cfgWithType = { ...providerConfig, type: config.provider };\n if (config.features.modelsRegistry && providerRegistry.has(config.provider)) {\n provider = providerRegistry.create(cfgWithType);\n } else {\n provider = makeProviderFromConfig(config.provider, cfgWithType);\n }\n } catch (err) {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.provider_create_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n throw err;\n }\n } else {\n // No provider is actively selected, but saved providers exist.\n // Re-read the config to find one with a usable encrypted API key\n // and create a real provider from it (the vault is already initialized).\n const savedProviders = config.providers ?? {};\n const firstKey = Object.keys(savedProviders)[0];\n if (firstKey) {\n const firstProvider = expectDefined(savedProviders[firstKey]);\n try {\n provider = makeProviderFromConfig(firstKey, {\n ...firstProvider,\n type: firstKey,\n family: firstProvider.family,\n apiKey: firstProvider.apiKey,\n });\n console.log('[WebUI] Using saved provider:', firstKey);\n } catch (err) {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.provider_stub_create_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n throw err;\n }\n } else {\n throw new Error(\n 'No provider configured. Run `wrongstack init` first, or configure via the WebUI.',\n );\n }\n }\n\n // Context\n const context = new Context({\n systemPrompt,\n provider,\n session,\n signal: new AbortController().signal,\n tokenCounter,\n cwd: workingDir,\n projectRoot,\n model: config.model,\n });\n const initialContextPolicy = resolveContextWindowPolicy(config.context);\n context.meta['contextWindowMode'] = initialContextPolicy.id;\n context.meta['contextWindowPolicy'] = initialContextPolicy;\n\n // ── Seed runtime prefs from config ──────────────────────────────────────\n // The settings panel reads prefs via `prefs.get` → context.meta. Without\n // this seed the snapshot is empty and every browser shows localStorage\n // defaults (autonomy \"off\", etc.) regardless of what config.json says.\n // Mirrors the CLI's getSettings() mapping so TUI and WebUI agree.\n {\n const autonomyCfg = (config.autonomy ?? {}) as Record<string, unknown>;\n const rawMode = autonomyCfg['defaultMode'];\n context.meta['autonomy'] =\n rawMode === 'suggest' || rawMode === 'auto' ? rawMode : 'off';\n context.meta['autonomyDelayMs'] = (autonomyCfg['autoProceedDelayMs'] as number) ?? 45_000;\n context.meta['autoProceedMaxIterations'] =\n (autonomyCfg['autoProceedMaxIterations'] as number) ?? 50;\n context.meta['yolo'] = (autonomyCfg['yolo'] as boolean) ?? config.yolo ?? false;\n context.meta['chime'] = (autonomyCfg['chime'] as boolean) ?? false;\n context.meta['confirmExit'] = autonomyCfg['confirmExit'] !== false;\n context.meta['streamFleet'] = autonomyCfg['streamFleet'] !== false;\n context.meta['enhanceEnabled'] = (autonomyCfg['enhance'] as boolean) ?? true;\n context.meta['enhanceDelayMs'] = (autonomyCfg['enhanceDelayMs'] as number) ?? 60_000;\n context.meta['enhanceLanguage'] = (autonomyCfg['enhanceLanguage'] as string) ?? 'original';\n context.meta['nextPrediction'] = config.nextPrediction ?? false;\n context.meta['featureMcp'] = config.features.mcp !== false;\n context.meta['featurePlugins'] = config.features.plugins !== false;\n context.meta['featureMemory'] = config.features.memory !== false;\n context.meta['featureSkills'] = config.features.skills !== false;\n context.meta['featureModelsRegistry'] = config.features.modelsRegistry !== false;\n context.meta['indexOnStart'] = config.indexing?.onSessionStart !== false;\n context.meta['contextAutoCompact'] = config.context?.autoCompact !== false;\n context.meta['contextStrategy'] = config.context?.strategy ?? 'hybrid';\n context.meta['logLevel'] = config.log?.level ?? 'info';\n context.meta['auditLevel'] = config.session?.auditLevel ?? 'standard';\n context.meta['maxIterations'] = config.tools?.maxIterations ?? 500;\n }\n\n /** Pref keys exposed to the settings panel via prefs.get / prefs.updated. */\n const PREF_KEYS = [\n 'autonomy', 'autonomyDelayMs', 'autoProceedMaxIterations', 'yolo', 'maxIterations',\n 'chime', 'confirmExit', 'streamFleet', 'nextPrediction',\n 'enhanceEnabled', 'enhanceDelayMs', 'enhanceLanguage',\n 'featureMcp', 'featurePlugins', 'featureMemory', 'featureSkills',\n 'featureModelsRegistry', 'indexOnStart',\n 'contextAutoCompact', 'contextStrategy', 'logLevel', 'auditLevel',\n ] as const;\n\n const prefSnapshot = (): Record<string, unknown> => {\n const snapshot: Record<string, unknown> = {};\n for (const k of PREF_KEYS) {\n if (k in context.meta) snapshot[k] = context.meta[k];\n }\n return snapshot;\n };\n\n /**\n * Persist pref changes into the global config.json — the SAME keys the\n * TUI settings picker writes — so a toggle made in the browser survives\n * restarts and is visible to the CLI/TUI (and vice versa on next boot).\n * Best-effort and serialized behind configWriteLock (shared with the\n * provider/key handlers); failures log but never break the WS reply.\n */\n const persistPrefsToConfig = async (payload: Record<string, unknown>): Promise<void> => {\n const write = async (): Promise<void> => {\n let raw: string;\n try {\n raw = await fs.readFile(globalConfigPath, 'utf8');\n } catch {\n raw = '{}';\n }\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // Refuse to overwrite a corrupt-but-existing config.\n logger.warn(`prefs: refusing to overwrite corrupt config at ${globalConfigPath}`);\n return;\n }\n const decrypted = decryptConfigSecrets(parsed, vault) as Record<string, unknown>;\n\n const autonomyCfg = (decrypted.autonomy as Record<string, unknown>) ?? {};\n let autonomyTouched = false;\n const setAutonomy = (key: string, val: unknown): void => {\n autonomyCfg[key] = val;\n autonomyTouched = true;\n };\n if (\n typeof payload['autonomy'] === 'string' &&\n ['off', 'suggest', 'auto'].includes(payload['autonomy'])\n ) {\n setAutonomy('defaultMode', payload['autonomy']);\n }\n if (typeof payload['autonomyDelayMs'] === 'number') setAutonomy('autoProceedDelayMs', payload['autonomyDelayMs']);\n if (typeof payload['autoProceedMaxIterations'] === 'number') setAutonomy('autoProceedMaxIterations', payload['autoProceedMaxIterations']);\n if (typeof payload['yolo'] === 'boolean') setAutonomy('yolo', payload['yolo']);\n if (typeof payload['chime'] === 'boolean') setAutonomy('chime', payload['chime']);\n if (typeof payload['confirmExit'] === 'boolean') setAutonomy('confirmExit', payload['confirmExit']);\n if (typeof payload['streamFleet'] === 'boolean') setAutonomy('streamFleet', payload['streamFleet']);\n if (typeof payload['enhanceEnabled'] === 'boolean') setAutonomy('enhance', payload['enhanceEnabled']);\n if (typeof payload['enhanceDelayMs'] === 'number') setAutonomy('enhanceDelayMs', payload['enhanceDelayMs']);\n if (typeof payload['enhanceLanguage'] === 'string') setAutonomy('enhanceLanguage', payload['enhanceLanguage']);\n if (autonomyTouched) decrypted.autonomy = autonomyCfg;\n\n if (typeof payload['nextPrediction'] === 'boolean') decrypted.nextPrediction = payload['nextPrediction'];\n\n const FEATURE_MAP: Record<string, string> = {\n featureMcp: 'mcp',\n featurePlugins: 'plugins',\n featureMemory: 'memory',\n featureSkills: 'skills',\n featureModelsRegistry: 'modelsRegistry',\n };\n for (const [prefKey, cfgKey] of Object.entries(FEATURE_MAP)) {\n if (typeof payload[prefKey] === 'boolean') {\n const feats = (decrypted.features as Record<string, unknown>) ?? {};\n feats[cfgKey] = payload[prefKey];\n decrypted.features = feats;\n }\n }\n\n if (typeof payload['contextAutoCompact'] === 'boolean' || typeof payload['contextStrategy'] === 'string') {\n const ctxCfg = (decrypted.context as Record<string, unknown>) ?? {};\n if (typeof payload['contextAutoCompact'] === 'boolean') ctxCfg.autoCompact = payload['contextAutoCompact'];\n if (typeof payload['contextStrategy'] === 'string') ctxCfg.strategy = payload['contextStrategy'];\n decrypted.context = ctxCfg;\n }\n if (typeof payload['logLevel'] === 'string') {\n const logCfg = (decrypted.log as Record<string, unknown>) ?? {};\n logCfg.level = payload['logLevel'];\n decrypted.log = logCfg;\n }\n if (typeof payload['auditLevel'] === 'string') {\n const sessionCfg = (decrypted.session as Record<string, unknown>) ?? {};\n sessionCfg.auditLevel = payload['auditLevel'];\n decrypted.session = sessionCfg;\n }\n if (typeof payload['indexOnStart'] === 'boolean') {\n const indexingCfg = (decrypted.indexing as Record<string, unknown>) ?? {};\n indexingCfg.onSessionStart = payload['indexOnStart'];\n decrypted.indexing = indexingCfg;\n }\n if (typeof payload['maxIterations'] === 'number') {\n const toolsCfg = (decrypted.tools as Record<string, unknown>) ?? {};\n toolsCfg.maxIterations = payload['maxIterations'];\n decrypted.tools = toolsCfg;\n }\n\n const encrypted = encryptConfigSecrets(decrypted, vault);\n await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 0o600 });\n };\n const next = configWriteLock.then(write);\n configWriteLock = next.then(\n () => undefined,\n () => undefined,\n );\n try {\n await next;\n } catch (err) {\n logger.warn(`prefs: failed to persist to config: ${errMessage(err)}`);\n }\n };\n\n // Pipelines\n const pipelines = createDefaultPipelines();\n // Collaboration bus — process-singleton pause/resume signal. The\n // middleware below hooks it into the toolCall pipeline so a\n // `controller` participant can halt the agent before the next tool\n // call (Phase 3 of idea #13). The same bus instance is shared with\n // the CollaborationWebSocketHandler so client pause/resume requests\n // are routed to the kernel.\n const collabBus = new CollaborationBus();\n // prepend (not use) — the pause check must run first, before any\n // permission/retry middleware that would otherwise proceed.\n const collabPause = collabPauseMiddleware(collabBus, { logger });\n Object.defineProperty(collabPause, 'name', { value: 'collab-pause' });\n pipelines.toolCall.prepend(collabPause as never);\n // Phase 4 — collab-inject. Installed AFTER collab-pause so the\n // controller can pause + inject before the next tool runs. The\n // middleware checks the bus's injection queue and splices a\n // synthetic tool_result when a controller has queued one for\n // the current toolUse.id.\n const collabInject = collabInjectMiddleware(collabBus, { logger });\n Object.defineProperty(collabInject, 'name', { value: 'collab-inject' });\n pipelines.toolCall.prepend(collabInject as never);\n // Compactor — honors config.context.strategy ('hybrid' default, lossless\n // rules; 'intelligent'/'selective' resolve their provider from ctx at\n // compact()-time). eliseThreshold is a TOKEN COUNT (not a fraction).\n const compactor = createStrategyCompactor({\n strategy: config.context?.strategy,\n preserveK: config.context?.preserveK ?? 10,\n eliseThreshold: config.context?.eliseThreshold ?? 2000,\n summarizerModel: config.context?.summarizerModel,\n llmSelector: config.context?.llmSelector,\n });\n\n // Auto-compaction\n let autoCompactor: AutoCompactionMiddleware | undefined;\n if (config.context?.autoCompact !== false) {\n // Priority: explicit override → models.dev per-model window → family default.\n // The catalog lookup matters for openai-compatible providers (OpenRouter,\n // Groq, …) whose family default is 0; without it auto-compaction would be\n // disabled even though the model has a real published window. Mirrors\n // updateAutoCompactionMaxContext below.\n let effectiveMaxContext = config.context?.effectiveMaxContext ?? 0;\n if (!effectiveMaxContext) {\n try {\n const m = await modelsRegistry.getModel(provider.id, context.model);\n effectiveMaxContext = m?.capabilities?.maxContext ?? 0;\n } catch {\n // best-effort: fall through to provider capability\n }\n }\n if (!effectiveMaxContext) effectiveMaxContext = provider.capabilities.maxContext;\n autoCompactor = new AutoCompactionMiddleware(\n compactor,\n effectiveMaxContext,\n (ctx) =>\n estimateRequestTokensCalibrated(\n ctx.messages,\n ctx.systemPrompt,\n ctx.tools ?? [],\n `${ctx.provider?.id ?? 'unknown'}/${ctx.model}`,\n ).total,\n {\n warn: initialContextPolicy.thresholds.warn,\n soft: initialContextPolicy.thresholds.soft,\n hard: initialContextPolicy.thresholds.hard,\n },\n {\n events,\n aggressiveOn: initialContextPolicy.aggressiveOn,\n policyProvider: (ctx) => {\n const policy = ctx.meta['contextWindowPolicy'];\n return policy && typeof policy === 'object'\n ? (policy as ReturnType<typeof resolveContextWindowPolicy>)\n : initialContextPolicy;\n },\n },\n );\n pipelines.contextWindow.use({ name: 'AutoCompaction', handler: autoCompactor.handler() });\n }\n\n /** Refresh AutoCompactionMiddleware denominator when the active model changes. */\n async function updateAutoCompactionMaxContext(newProvider: Provider): Promise<void> {\n if (!autoCompactor) return;\n let newMaxContext = config.context?.effectiveMaxContext ?? newProvider.capabilities.maxContext;\n try {\n const m = await modelsRegistry.getModel(newProvider.id, context.model);\n newMaxContext = m?.capabilities?.maxContext ?? newMaxContext;\n } catch {\n // best-effort: use provider capability\n }\n autoCompactor.setMaxContext(newMaxContext);\n }\n\n // Agent\n const secretScrubber = container.resolve(TOKENS.SecretScrubber);\n const renderer = container.has(TOKENS.Renderer) ? container.resolve(TOKENS.Renderer) : undefined;\n const permissionPolicy = container.resolve(TOKENS.PermissionPolicy);\n const toolExecutor = new ToolExecutor(toolRegistry, {\n permissionPolicy,\n secretScrubber,\n renderer,\n events,\n confirmAwaiter: undefined,\n iterationTimeoutMs: config.tools?.iterationTimeoutMs ?? DEFAULT_TOOLS_CONFIG.iterationTimeoutMs,\n perIterationOutputCapBytes:\n config.tools?.perIterationOutputCapBytes ?? DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,\n tracer: undefined,\n });\n\n const agent = new Agent({\n container,\n tools: toolRegistry,\n providers: providerRegistry,\n events,\n pipelines,\n context,\n maxIterations: config.tools?.maxIterations ?? DEFAULT_TOOLS_CONFIG.maxIterations,\n iterationTimeoutMs: config.tools?.iterationTimeoutMs ?? DEFAULT_TOOLS_CONFIG.iterationTimeoutMs,\n executionStrategy:\n config.tools?.defaultExecutionStrategy ?? DEFAULT_TOOLS_CONFIG.defaultExecutionStrategy,\n perIterationOutputCapBytes:\n config.tools?.perIterationOutputCapBytes ?? DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,\n confirmAwaiter: undefined,\n toolExecutor,\n });\n console.log('[WebUI] Agent initialized');\n\n // ── Brain — policy → LLM tiered decision layer ─────────────────────────\n // Same positioning as the CLI: one Brain per process at\n // TOKENS.BrainArbiter. The WebUI has no human-escalation prompt yet, so\n // the chain stops at the LLM tier — `ask_human` decisions surface to the\n // browser as `brain.event` WS messages and the caller's fallback applies.\n const brainSettings: { maxAutoRisk: BrainAutoRisk } = { maxAutoRisk: 'medium' };\n // Lazy wrapper so the LLM tier always sees the LIVE provider/model —\n // both are swapped at runtime via the settings panel.\n const autonomousBrain: BrainArbiter = {\n decide: (request) =>\n createAutonomyBrain({\n provider,\n model: context.model,\n maxAutoRisk: 'all', // the tiered ceiling gates risk — keep inner permissive\n }).decide(request),\n };\n const brain = new ObservableBrainArbiter(\n createTieredBrainArbiter({\n policy: new DefaultBrainArbiter(),\n autonomous: autonomousBrain,\n getMaxAutoRisk: () => brainSettings.maxAutoRisk,\n }),\n events,\n );\n container.bind(TOKENS.BrainArbiter, () => brain);\n\n // Self-activation: watch for tool-failure streaks / error storms and\n // steer this session's leader via the shared project mailbox. `session`\n // is mutable (swapped on /new and resume) — read it at send time so the\n // steer always targets the LIVE session's leader identity.\n const brainMailbox = new GlobalMailbox(wpaths.projectDir, events);\n const brainMonitor = new BrainMonitor({\n events,\n brain,\n intervene: async ({ subject, body }) => {\n const tag = mailboxSessionTag(session.id);\n await brainMailbox.send({\n from: `brain@${tag}`,\n to: `leader@${tag}`,\n type: 'steer',\n subject,\n body,\n priority: 'high',\n });\n },\n });\n brainMonitor.start();\n console.log('[WebUI] Brain initialized (tiered policy → LLM, monitor active)');\n\n // Decision log for the /brain command — last 20 decisions, newest last.\n const brainLog: Array<{ at: number; kind: string; question: string; outcome: string }> = [];\n const pushBrainLog = (entry: (typeof brainLog)[number]) => {\n brainLog.push(entry);\n if (brainLog.length > 20) brainLog.shift();\n };\n events.on('brain.decision_answered', (e) =>\n pushBrainLog({\n at: e.at,\n kind: 'answered',\n question: e.request.question,\n outcome: e.decision.type === 'answer' ? (e.decision.optionId ?? e.decision.text) : '',\n }),\n );\n events.on('brain.decision_ask_human', (e) =>\n pushBrainLog({ at: e.at, kind: 'ask_human', question: e.request.question, outcome: 'needs human judgement' }),\n );\n events.on('brain.decision_denied', (e) =>\n pushBrainLog({\n at: e.at,\n kind: 'denied',\n question: e.request.question,\n outcome: e.decision.type === 'deny' ? e.decision.reason : '',\n }),\n );\n events.on('brain.intervention', (e) =>\n pushBrainLog({\n at: e.at,\n kind: 'intervention',\n question: e.request.question,\n outcome: e.intervened ? 'steered the agent' : 'observed (no action)',\n }),\n );\n\n // AutoPhase handler — manages AutoPhaseRunner lifecycle via WS messages.\n // Stored under the per-project autophase dir (not the shared SDD task-graphs).\n const autoPhaseHandler = new AutoPhaseWebSocketHandler(\n agent,\n context,\n logger,\n wpaths.projectAutophase,\n events,\n projectRoot,\n );\n\n // Worktree handler — subscribes to the shared EventBus `worktree.*` events\n // and streams live swim-lane / DAG state to connected clients.\n const worktreeHandler = new WorktreeWebSocketHandler(events, logger);\n\n // Collaboration handler — Phase 1 of idea #13. Lets a second client\n // (e.g. a senior dev) join an active agent run as a read-only\n // observer and watch a live mirror of kernel events. Annotated and\n // controller roles land in Phase 2/3. The session reader enables\n // replay-on-join for late observers.\n const collabHandler = new CollaborationWebSocketHandler(\n events,\n logger,\n sessionReader,\n annotationsStore,\n collabBus,\n );\n\n // Helper: build the rich session.start payload from current runtime state.\n // Centralised so initial connect, post-/new, and post-model.switch all\n // broadcast the same shape — frontend treats this as the single source of\n // truth for everything in the status bar (model, context window, project).\n async function sessionStartPayload(): Promise<{\n sessionId: string;\n model: string;\n provider: string;\n maxContext: number;\n /** USD per 1M input tokens (0 if unknown / free). */\n inputCost: number;\n /** USD per 1M output tokens. */\n outputCost: number;\n /** USD per 1M cache-read tokens. */\n cacheReadCost: number;\n projectName: string;\n projectRoot: string;\n cwd: string;\n mode: string;\n contextMode: string;\n }> {\n let maxContext = 0;\n let inputCost = 0;\n let outputCost = 0;\n let cacheReadCost = 0;\n try {\n const m = await modelsRegistry.getModel(config.provider, config.model);\n maxContext = m?.capabilities?.maxContext ?? 0;\n // Fall back to the provider's raw model data from the registry when the\n // resolved model has no maxContext (e.g. a user-defined or API-proxied\n // model that wasn't in the models.dev catalog). DefaultModelsRegistry\n // exposes getProvider() which gives us the model's limit.context directly.\n if (!maxContext) {\n try {\n const provider = await (\n modelsRegistry as { getProvider(id: string): Promise<{ models: Array<{ id: string; limit?: { context?: number } }> } | undefined> }\n ).getProvider(config.provider);\n const rawModel = provider?.models.find((mod) => mod.id === config.model);\n maxContext = rawModel?.limit?.context ?? 0;\n } catch {\n /* best-effort — leave maxContext at whatever the registry set it */\n }\n }\n // models.dev pricing is dollars per 1M tokens; some providers omit the\n // field for free/unmetered plans (e.g. minimax-coding-plan) — in that\n // case we report 0 and the cost chip just stays at $0.\n const rates = getCostRates(m);\n inputCost = rates.input;\n outputCost = rates.output;\n cacheReadCost = rates.cacheRead;\n } catch {\n // best-effort\n }\n return {\n sessionId: session.id,\n model: config.model,\n provider: config.provider,\n maxContext,\n inputCost,\n outputCost,\n cacheReadCost,\n projectName: path.basename(projectRoot) || projectRoot,\n projectRoot,\n cwd: workingDir,\n mode: modeId,\n contextMode: String(context.meta['contextWindowMode'] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID),\n };\n }\n\n // WebSocket server(s).\n //\n // When the user keeps the default loopback bind (127.0.0.1), we ALSO open a\n // second listener on ::1 (IPv6 loopback). Reason: Chrome/Edge on Windows\n // resolve `localhost` to `[::1]` before `127.0.0.1`, so a single v4-only\n // bind causes \"ws disconnect hep\" — clients hammer the v6 socket, get\n // ECONNREFUSED, fall back to v4 inconsistently. Listening on both v4 and v6\n // loopback keeps the connection scope \"this machine only\" while removing\n // the resolution-order coin flip.\n //\n // When the user explicitly sets WS_HOST (e.g. 0.0.0.0 or a LAN IP), we\n // respect that choice exactly and don't add a second listener.\n // Generate a random WS auth token so only callers that know the token\n // can connect. Printed to console on startup; the frontend reads it from\n // the URL query param `?token=...`. Without a token, any client on the\n // network can connect and send `user_message`/`key.add`/`model.switch`.\n const wsToken = generateAuthToken();\n // Token is sent to clients via session.start payload — log without any\n // token characters to prevent search-space reduction for brute-force attacks.\n console.log('[WebUI] WS auth token generated (redacted from logs)');\n\n // CSWSH guard + token auth: when the user exposes the socket beyond loopback,\n // require the shared token; loopback connections bootstrap without one. The\n // policy (DNS-rebinding Host guard, constant-time token compare, loopback\n // bootstrap) lives in ./ws-auth.ts as pure functions — this closure just\n // pulls the relevant fields off the incoming request and delegates.\n const verifyClient = (info: {\n origin: string;\n secure: boolean;\n req: import('node:http').IncomingMessage;\n }) =>\n verifyWsClient({\n origin: info.origin,\n url: info.req.url ?? '',\n hostHeader: info.req.headers.host,\n remoteAddress: info.req.socket.remoteAddress,\n // C-2 fix: accept the token via the HttpOnly cookie set by\n // `/ws-auth` (preferred) OR the URL query param (non-browser\n // fallback). The cookie path closes the C-598 query-string\n // exposure class.\n cookieHeader: info.req.headers.cookie,\n wsHost,\n expectedToken: wsToken,\n });\n // Cap inbound frame size (8 MiB) so a single oversized message can't exhaust\n // memory. Agent messages are small; large pastes/attachments stay well under.\n const WS_MAX_PAYLOAD = 8 * 1024 * 1024;\n const wssPrimary = new WebSocketServer({\n port: wsPort,\n host: wsHost,\n verifyClient,\n maxPayload: WS_MAX_PAYLOAD,\n } as ConstructorParameters<typeof WebSocketServer>[0]);\n const wssSecondary =\n wsHost === '127.0.0.1'\n ? new WebSocketServer({\n port: wsPort,\n host: '::1',\n verifyClient,\n maxPayload: WS_MAX_PAYLOAD,\n } as ConstructorParameters<typeof WebSocketServer>[0])\n : null;\n const clients = new Map<WebSocket, ConnectedClient>();\n\n // ── Subscribe to working directory changes from the CLI ──────────────\n // When ctx.setWorkingDir() is called from the CLI (e.g. /wd, /cd, or\n // the set_working_dir tool), update the server's workingDir reference\n // and broadcast to all connected WebUI clients so the file explorer\n // and the WorkingDirChip UI stay in sync.\n context.onWorkingDirChanged((newDir) => {\n workingDir = newDir;\n broadcast(clients, {\n type: 'working_dir.changed',\n payload: { cwd: newDir, projectRoot },\n });\n });\n\n // ── Eternal-autonomy iteration broadcast (PR 4 of Phase 2) ─────────\n // When the CLI passes `opts.subscribeEternalIteration`, hook the\n // returned observer into a WS broadcast so every connected client\n // gets a live stream of `JournalEntry` items as the engine ticks.\n // The disposer is captured and invoked on shutdown() so the CLI's\n // engine subscription is properly torn down with the webui.\n let eternalSubscription: { dispose: () => void } | null = null;\n if (opts.subscribeEternalIteration) {\n eternalSubscription = createEternalSubscription(\n opts.subscribeEternalIteration,\n broadcast,\n () => clients,\n );\n }\n\n // Per-connection message rate limiting: 60 messages per 60-second window.\n // Exceeding clients are temporarily blocked to prevent flooding.\n // Uses sessionId as the key once connected, falling back to ws for\n // pre-auth messages — prevents connection-reuse bypass.\n // Rate limit OFF by default (counted pings/list calls too and tripped during\n // normal use). Opt in via WEBUI_RATE_LIMIT=<messages-per-60s> for LAN exposure.\n const RATE_LIMIT_MESSAGES = Number.parseInt(process.env['WEBUI_RATE_LIMIT'] ?? '0', 10);\n const RATE_LIMIT_WINDOW_MS = 60_000;\n const rateLimits = new Map<string, { count: number; resetAt: number }>();\n\n function checkRateLimit(ws: WebSocket, client: ConnectedClient): boolean {\n if (RATE_LIMIT_MESSAGES <= 0) return true; // disabled\n const now = Date.now();\n // Prefer the per-client authenticated sessionId; fall back to the\n // WebSocket identity for pre-auth messages before session.start.\n const key = client.sessionId ?? String(ws);\n const limit = rateLimits.get(key);\n if (!limit || now > limit.resetAt) {\n rateLimits.set(key, { count: 1, resetAt: now + RATE_LIMIT_WINDOW_MS });\n return true;\n }\n if (limit.count >= RATE_LIMIT_MESSAGES) return false;\n limit.count++;\n return true;\n }\n\n /** Holds the AbortController for the currently in-flight agent.run().\n * Non-null while the agent is running; guarded at the user_message\n * handler to prevent concurrent runs that would corrupt shared state\n * (context, agent, tokenCounter). A second user_message while running\n * is answered with an inline error instead of being queued — the\n * caller should wait for run.result. */\n let runLock: AbortController | null = null;\n\n console.log(\n `[WebUI] WebSocket server running on ws://${wsHost}:${wsPort}` +\n (wssSecondary ? ` (and ws://[::1]:${wsPort})` : ''),\n );\n\n // Pending permission confirmations. When the agent emits\n // tool.confirm_needed, we store the resolve function here keyed by\n // toolUseId. When the client sends tool.confirm_result back, we look\n // it up and resolve — unblocking the agent loop.\n const pendingConfirms = new Map<string, (d: 'yes' | 'no' | 'always' | 'deny') => void>();\n\n const handleConnection = (ws: WebSocket): void => {\n const client: ConnectedClient = { ws, sessionId: session.id, connectedAt: Date.now() };\n clients.set(ws, client);\n\n // sessionStartPayload handles errors internally; no explicit catch needed.\n // Adding a catch would be defensive but sessionStartPayload already has try-catch.\n void sessionStartPayload()\n .then((payload) => {\n send(ws, { type: 'session.start', payload });\n })\n .catch((err) => {\n // Log at warn level since sessionStartPayload should rarely fail.\n // This prevents silent failures if internal error handling changes.\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.session_start_payload_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n });\n\n // Register this client with the AutoPhase handler so it receives phase events\n autoPhaseHandler.addClient(ws);\n // …and the worktree handler for live isolation lanes.\n worktreeHandler.addClient(ws);\n // …and the collaboration handler for read-only session observation.\n collabHandler.addClient(ws);\n\n ws.on('message', async (data) => {\n if (!checkRateLimit(ws, client)) {\n send(ws, {\n type: 'error',\n payload: {\n phase: 'rate_limit',\n message: 'Too many messages. Please wait before sending more.',\n },\n });\n return;\n }\n try {\n // Prototype pollution guard: reject messages whose root-level payload\n // contains __proto__, constructor, or prototype keys. These could\n // cause prototype pollution via Object.assign({}, payload) or\n // spread {...payload}. The top-level check below catches the\n // dangerous keys; nested payload sub-objects are low-risk since\n // handlers don't do deep property merges.\n const rawObj = JSON.parse(data.toString());\n if (typeof rawObj === 'object' && rawObj !== null) {\n const obj = rawObj as Record<string, unknown>;\n if ('__proto__' in obj || 'constructor' in obj || 'prototype' in obj) {\n send(ws, {\n type: 'error',\n payload: { phase: 'parse', message: 'Invalid message object' },\n });\n } else {\n await handleMessage(ws, client, rawObj as WSClientMessage);\n }\n } else {\n // Non-object JSON (array, string, number…) — pass through\n await handleMessage(ws, client, rawObj as unknown as WSClientMessage);\n }\n } catch (err) {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.ws_message_parse_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n }\n });\n\n ws.on('close', () => {\n clients.delete(ws);\n rateLimits.delete(String(ws));\n // If the client disconnects while a permission prompt is pending,\n // resolve all pending confirms with 'no' so the agent loop doesn't\n // hang forever waiting for a response that will never come.\n if (pendingConfirms.size > 0) {\n for (const [id, resolve] of pendingConfirms) {\n resolve('no');\n pendingConfirms.delete(id);\n }\n }\n });\n\n ws.on('error', (err) => {\n // Without this handler an errored socket would crash the process.\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.client_socket_error',\n message: err.message,\n timestamp: new Date().toISOString(),\n }));\n });\n };\n\n // Audit-level-aware session log bridge — persists tool/error/provider\n // events to the session JSONL with the same contract as the CLI. The\n // getter form resolves the CURRENT writer on every append so events\n // follow session.new / session.resume / projects.select swaps.\n const sessionLogging = resolveSessionLoggingConfig(\n config as unknown as Parameters<typeof resolveSessionLoggingConfig>[0],\n );\n const sessionBridge = createSessionEventBridge(\n () => context.session ?? session,\n sessionLogging.auditLevel,\n { sampling: sessionLogging.sampling },\n );\n\n let eventsArmed = false;\n const armOnce = (label: string): void => {\n if (eventsArmed) return;\n eventsArmed = true;\n console.log(`[WebUI] Backend ready (${label})`);\n setupEvents({ events, broadcast, clients, config, context, pendingConfirms, globalConfigPath, sessionBridge });\n };\n\n wssPrimary.on('listening', () => armOnce(`${wsHost}:${wsPort}`));\n wssPrimary.on('connection', handleConnection);\n wssPrimary.on('error', (err) => {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.ws_server_error',\n host: wsHost,\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n });\n\n if (wssSecondary) {\n wssSecondary.on('listening', () => armOnce(`::1:${wsPort}`));\n wssSecondary.on('connection', handleConnection);\n wssSecondary.on('error', (err: NodeJS.ErrnoException) => {\n // Best-effort secondary: if IPv6 loopback isn't available on this host\n // (e.g. disabled in OS), just log and continue. Primary v4 is enough.\n if (err.code === 'EAFNOSUPPORT' || err.code === 'EADDRNOTAVAIL') {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.ipv6_unavailable',\n code: err.code,\n message: err.message,\n timestamp: new Date().toISOString(),\n }));\n } else {\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.ws_server_error',\n host: '::1',\n message: err.message,\n timestamp: new Date().toISOString(),\n }));\n }\n });\n }\n\n // ── Project manifest helpers ──────────────────────────────────────────\n\n interface ProjectEntry {\n name: string;\n root: string;\n slug: string;\n lastSeen?: string | undefined;\n createdAt?: string | undefined;\n /** Working directory of the most recent session (may differ from root). */\n lastWorkingDir?: string | undefined;\n }\n\n interface ProjectsManifest {\n projects: ProjectEntry[];\n }\n\n /**\n * Idempotent manifest registration (mirrors the CLI's\n * touchProjectInManifest): create the projects.json entry when missing,\n * refresh lastSeen/lastWorkingDir when present.\n */\n async function touchProjectEntry(root: string, workDir?: string): Promise<void> {\n const resolved = path.resolve(root);\n const manifest = await loadManifest(globalConfigPath);\n const now = new Date().toISOString();\n const existing = manifest.projects.find((p) => path.resolve(p.root) === resolved);\n if (existing) {\n existing.lastSeen = now;\n if (workDir) existing.lastWorkingDir = path.resolve(workDir);\n } else {\n manifest.projects.push({\n name: path.basename(resolved),\n root: resolved,\n slug: generateProjectSlug(resolved),\n createdAt: now,\n lastSeen: now,\n lastWorkingDir: workDir ? path.resolve(workDir) : undefined,\n });\n }\n await saveManifest(manifest, globalConfigPath);\n await ensureProjectDataDir(generateProjectSlug(resolved), globalConfigPath);\n }\n\n function projectsJsonPath(globalConfigPath: string): string {\n const base = path.dirname(globalConfigPath);\n return path.join(base, 'projects.json');\n }\n\n async function loadManifest(globalConfigPath: string): Promise<ProjectsManifest> {\n try {\n const raw = await fs.readFile(projectsJsonPath(globalConfigPath), 'utf8');\n const parsed = JSON.parse(raw) as ProjectsManifest;\n return { projects: parsed.projects ?? [] };\n } catch {\n return { projects: [] };\n }\n }\n\n async function saveManifest(manifest: ProjectsManifest, globalConfigPath: string): Promise<void> {\n const file = projectsJsonPath(globalConfigPath);\n await fs.mkdir(path.dirname(file), { recursive: true });\n await fs.writeFile(file, JSON.stringify(manifest, null, 2), 'utf8');\n }\n\n function generateProjectSlug(rootPath: string): string {\n // Canonical derivation — must match wstack-paths/projectSlug exactly or\n // the WebUI and CLI would key the same project under different dirs.\n return projectSlug(rootPath);\n }\n\n async function ensureProjectDataDir(slug: string, globalConfigPath: string): Promise<string> {\n const base = path.dirname(globalConfigPath);\n const dir = path.join(base, 'projects', slug);\n await fs.mkdir(dir, { recursive: true });\n return dir;\n }\n\n async function handleMessage(\n ws: WebSocket,\n _client: ConnectedClient,\n msg: WSClientMessage,\n ): Promise<void> {\n switch (msg.type) {\n // Collaboration messages short-circuit the user/agent flow.\n // They don't touch runLock, the agent loop, or the message queue —\n // they're pure transport for the live observer mirror.\n case 'collab.join':\n case 'collab.leave':\n case 'collab.annotate':\n case 'collab.resolve': {\n collabHandler.handleMessage(ws, msg as { type: string; payload?: unknown | undefined });\n return;\n }\n case 'user_message': {\n const content = (msg as { payload: { content: string } }).payload.content;\n\n // Guard against concurrent agent runs — a second user_message while\n // the agent is already processing would kick off two agent.run()\n // calls on the same shared context/agent, leading to corrupted\n // state (duplicate tool bubbles, mixed text_delta streams, token\n // counter undercount). Reject with an inline error; the frontend\n // should wait for run.result before sending the next message.\n if (runLock) {\n send(ws, {\n type: 'error',\n payload: {\n phase: 'user_message',\n message: 'Agent is already processing a request. Wait for the current run to finish.',\n },\n });\n break;\n }\n\n runLock = new AbortController();\n // Capture so the finally block only clears its own lock — a\n // second race could set a new runLock between await and finally.\n const thisRun = runLock;\n\n try {\n // Read maxIterations from context.meta so the webui settings\n // panel can adjust the cap dynamically without restarting.\n const maxIt = typeof context.meta['maxIterations'] === 'number'\n ? context.meta['maxIterations']\n : undefined;\n const result = await agent.run(content, { signal: thisRun.signal, maxIterations: maxIt });\n send(ws, {\n type: 'run.result',\n payload: {\n status: result.status,\n iterations: result.iterations,\n finalText: result.finalText,\n error: result.error\n ? {\n code: result.error.code,\n message: result.error.message,\n recoverable: result.error.recoverable,\n }\n : undefined,\n },\n });\n } catch (err) {\n send(ws, {\n type: 'error',\n payload: {\n phase: 'agent.run',\n message: errMessage(err),\n },\n });\n } finally {\n // Only clear runLock if it's still ours — otherwise we'd wipe a\n // newer run's controller set after we returned.\n if (runLock === thisRun) {\n runLock = null;\n }\n }\n break;\n }\n\n case 'tool.confirm_result': {\n const { id, decision } = (\n msg as { payload: { id: string; decision: 'yes' | 'no' | 'always' | 'deny' } }\n ).payload;\n const resolve = pendingConfirms.get(id);\n if (resolve) {\n pendingConfirms.delete(id);\n resolve(decision);\n }\n break;\n }\n\n case 'abort':\n runLock?.abort();\n broadcast(clients, { type: 'error', payload: { phase: 'abort', message: 'User aborted' } });\n break;\n\n case 'ping':\n send(ws, { type: 'pong', payload: {} });\n break;\n\n case 'session.new': {\n // Truly fresh chat: new on-disk session AND reset every piece of\n // in-memory state that survived (messages history, todos, read-file\n // tracking, token totals). Otherwise the model still sees the prior\n // turns even though the UI looks empty — that's the \"ghost context\"\n // bug. After this, the next user message goes out as turn 1 with no\n // prior history.\n //\n // Finalize the writer we are leaving (session_end + close) — same\n // pattern as projects.select/shutdown. Without it the old JSONL\n // never ends cleanly, the summary sidecar/index entry are never\n // written, and the file handle leaks for the daemon's lifetime.\n try {\n await session.append({\n type: 'session_end',\n ts: new Date().toISOString(),\n usage: tokenCounter.total(),\n });\n await session.close();\n } catch {\n // best-effort\n }\n session = await sessionStore.create({\n id: '',\n title: '',\n model: config.model,\n provider: config.provider,\n });\n context.session = session;\n context.state.replaceMessages([]);\n context.state.replaceTodos([]);\n context.readFiles.clear();\n context.fileMtimes.clear();\n tokenCounter.reset();\n sessionStartedAt = Date.now();\n broadcast(clients, { type: 'session.start', payload: await sessionStartPayload() });\n break;\n }\n\n case 'context.clear': {\n // Same in-memory wipe as session.new, but reuses the current session\n // file (so the JSONL still has the history for audit / replay). The\n // user wants a clean slate on the model side; the disk record stays.\n context.state.replaceMessages([]);\n context.state.replaceTodos([]);\n context.readFiles.clear();\n context.fileMtimes.clear();\n tokenCounter.reset();\n sendResult(ws, true, 'Context cleared');\n broadcast(clients, {\n type: 'session.start',\n payload: { ...(await sessionStartPayload()), reset: true },\n });\n break;\n }\n\n case 'context.debug': {\n // Per-section token estimate so users can see what's actually eating\n // the context window. The breakdown maths lives in ./token-estimator.ts\n // (4-chars-per-token heuristic); we layer the active mode/policy on top.\n const breakdown = estimateContextBreakdown({\n systemPrompt: context.systemPrompt,\n tools: toolRegistry.list(),\n messages: context.messages,\n });\n send(ws, {\n type: 'context.debug',\n payload: {\n ...breakdown,\n mode: context.meta['contextWindowMode'] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID,\n policy: context.meta['contextWindowPolicy'],\n },\n });\n break;\n }\n\n case 'context.compact': {\n const aggressive = !!(msg as { payload?: { aggressive?: boolean | undefined } }).payload\n ?.aggressive;\n try {\n const report = await compactor.compact(context, { aggressive });\n send(ws, {\n type: 'context.compacted',\n payload: {\n before: report.before,\n after: report.after,\n saved: Math.max(0, report.before - report.after),\n reductions: report.reductions,\n repaired: report.repaired,\n },\n });\n sendResult(\n ws,\n true,\n `Compacted: ${report.before} → ${report.after} tokens (saved ~${Math.max(0, report.before - report.after)})`,\n );\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'context.repair': {\n const beforeMessages = context.messages.length;\n const repaired = repairToolUseAdjacency(context.messages);\n if (repaired.report.changed) {\n context.state.replaceMessages(repaired.messages);\n }\n const payload = {\n removedToolUses: repaired.report.removedToolUses,\n removedToolResults: repaired.report.removedToolResults,\n removedMessages: repaired.report.removedMessages,\n beforeMessages,\n afterMessages: context.messages.length,\n };\n broadcast(clients, { type: 'context.repaired', payload });\n const removed =\n payload.removedToolUses.length +\n payload.removedToolResults.length +\n payload.removedMessages;\n sendResult(\n ws,\n true,\n removed > 0\n ? `Context repaired: removed ${removed} orphan protocol item(s)`\n : 'Context repair found no orphan protocol blocks',\n );\n break;\n }\n\n case 'context.modes.list': {\n const active = String(context.meta['contextWindowMode'] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID);\n const allModes = customModeStore.list().map((m) => ({\n id: m.id,\n name: m.name,\n description: m.description,\n isActive: m.id === active,\n thresholds: m.thresholds,\n preserveK: m.preserveK,\n eliseThreshold: m.eliseThreshold,\n custom: (m as { custom?: boolean }).custom === true,\n }));\n send(ws, {\n type: 'context.modes.list',\n payload: { activeId: active, modes: allModes },\n });\n break;\n }\n\n case 'context.mode.switch': {\n const { id } = (msg as { payload: { id: string } }).payload;\n // Try built-in first, then custom\n let policy = resolveContextWindowPolicy({}, id);\n if (policy.id !== id) {\n // Check custom modes\n const customModes = customModeStore.list().filter(\n (m) => (m as { custom?: boolean }).custom === true,\n );\n const custom = customModes.find((m) => m.id === id);\n if (!custom) {\n sendResult(ws, false, `Unknown context mode \"${id}\"`);\n break;\n }\n // Create a policy from the custom mode\n policy = custom as unknown as typeof policy;\n }\n context.meta['contextWindowMode'] = policy.id;\n context.meta['contextWindowPolicy'] = policy;\n sendResult(ws, true, `Context mode switched to ${policy.id}`);\n broadcast(clients, {\n type: 'context.mode.changed',\n payload: { id: policy.id, name: policy.name, policy },\n });\n break;\n }\n\n case 'context.mode.create': {\n const payload = (msg as { payload: { id: string; name: string; description: string; thresholds: { warn: number; soft: number; hard: number }; preserveK: number; eliseThreshold: number } }).payload;\n const result = customModeStore.create({\n id: payload.id,\n name: payload.name,\n description: payload.description,\n thresholds: payload.thresholds,\n preserveK: payload.preserveK,\n eliseThreshold: payload.eliseThreshold,\n custom: true,\n aggressiveOn: 'soft',\n targetLoad: 0.65,\n });\n sendResult(ws, result.ok, result.error ?? `Mode \"${payload.id}\" created`);\n break;\n }\n\n case 'context.mode.update': {\n const payload = (msg as { payload: { id: string; name?: string | undefined; description?: string | undefined; thresholds?: { warn?: number | undefined; soft?: number | undefined; hard?: number | undefined } | undefined; preserveK?: number | undefined; eliseThreshold?: number | undefined } }).payload;\n const result = customModeStore.update(payload.id, {\n name: payload.name,\n description: payload.description,\n thresholds: payload.thresholds ? {\n warn: payload.thresholds.warn ?? 0.6,\n soft: payload.thresholds.soft ?? 0.75,\n hard: payload.thresholds.hard ?? 0.9,\n } : undefined,\n preserveK: payload.preserveK,\n eliseThreshold: payload.eliseThreshold,\n });\n sendResult(ws, result.ok, result.error ?? `Mode \"${payload.id}\" updated`);\n break;\n }\n\n case 'context.mode.delete': {\n const { id } = (msg as { payload: { id: string } }).payload;\n // If the active mode is being deleted, reset to default\n if (String(context.meta['contextWindowMode'] ?? '') === id) {\n context.meta['contextWindowMode'] = DEFAULT_CONTEXT_WINDOW_MODE_ID;\n context.meta['contextWindowPolicy'] = resolveContextWindowPolicy({}, DEFAULT_CONTEXT_WINDOW_MODE_ID);\n }\n const result = customModeStore.remove(id);\n sendResult(ws, result.ok, result.error ?? `Mode \"${id}\" deleted`);\n break;\n }\n\n case 'providers.list': {\n const providers = await modelsRegistry.listProviders();\n // \"Configured\" should mean *any* working credential, not just env vars.\n // Users register keys with `wstack auth`, which writes apiKey/apiKeys\n // into config.providers[<id>] — those are decrypted in memory here.\n const savedIds = new Set(Object.keys(config.providers ?? {}));\n send(ws, {\n type: 'provider.catalog',\n payload: {\n providers: providers.map((p) => ({\n id: p.id,\n name: p.name,\n family: p.family,\n apiBase: p.apiBase,\n envVars: p.envVars,\n modelCount: p.models.length,\n hasApiKey: savedIds.has(p.id) || p.envVars.some((v) => !!process.env[v]),\n })),\n },\n });\n break;\n }\n\n case 'providers.saved': {\n const saved = await providerHandlers.loadConfigProviders();\n send(ws, {\n type: 'providers.saved',\n payload: {\n providers: Object.entries(saved).map(([id, cfg]) => {\n const keys = normalizeKeys(cfg);\n return {\n id,\n family: cfg.family ?? id,\n baseUrl: cfg.baseUrl,\n apiKeys: keys.map((k) => ({\n label: k.label,\n maskedKey: maskedKey(k.apiKey),\n isActive: k.label === cfg.activeKey,\n createdAt: k.createdAt,\n })),\n };\n }),\n },\n });\n break;\n }\n\n case 'provider.models': {\n const providerId = (msg as { payload: { providerId: string } }).payload.providerId;\n const provider = await modelsRegistry.getProvider(providerId);\n if (provider) {\n send(ws, {\n type: 'provider.models',\n payload: {\n provider: providerId,\n models: provider.models.map((m) => ({\n id: m.id,\n name: m.name,\n releaseDate: (m as { release_date?: string | undefined }).release_date,\n contextWindow: (m as { limit?: { context?: number | undefined } }).limit?.context,\n inputCost: (m as { cost?: { input?: number | undefined } }).cost?.input,\n outputCost: (m as { cost?: { output?: number | undefined } }).cost?.output,\n capabilities: [\n ...((m as { tool_call?: boolean | undefined }).tool_call ? ['tools'] : []),\n ...((m as { reasoning?: boolean | undefined }).reasoning ? ['reasoning'] : []),\n ],\n })),\n },\n });\n }\n break;\n }\n\n case 'model.switch': {\n const { provider: newProvider, model: newModel } = (\n msg as { payload: { provider: string; model: string } }\n ).payload;\n try {\n // Update config\n config = patchConfig(config, { provider: newProvider, model: newModel });\n configStore.update({ provider: newProvider, model: newModel });\n context.model = newModel;\n\n // Create new provider instance — fail loudly if the user picks a\n // provider with no creds rather than silently keeping the old one.\n const providerCfg = config.providers?.[newProvider] ?? { type: newProvider };\n const newProv = providerRegistry.has(newProvider)\n ? providerRegistry.create({ ...providerCfg, type: newProvider })\n : makeProviderFromConfig(newProvider, providerCfg);\n context.provider = newProv;\n\n // Update AutoCompactionMiddleware with the new model's maxContext so\n // backend threshold triggers (warn/soft/hard) use the correct denominator.\n // sessionStartPayload is called below (after this block) and uses\n // the new provider for its modelsRegistry lookup.\n updateAutoCompactionMaxContext?.(newProv);\n\n // Persist to global config file\n try {\n configWriteLock = configWriteLock.then(async () => {\n const raw = await fs.readFile(globalConfigPath, 'utf8');\n const parsed = JSON.parse(raw);\n parsed.provider = newProvider;\n parsed.model = newModel;\n await atomicWrite(globalConfigPath, JSON.stringify(parsed, null, 2));\n });\n await configWriteLock;\n } catch (err) {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.config_save_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n }\n\n // Toast for the SettingsPanel\n send(ws, {\n type: 'key.operation_result',\n payload: { success: true, message: `Switched to ${newProvider} / ${newModel}` },\n });\n } catch (err) {\n send(ws, {\n type: 'key.operation_result',\n payload: {\n success: false,\n message: `Switch failed: ${errMessage(err)}`,\n },\n });\n break;\n }\n\n broadcast(clients, { type: 'session.start', payload: await sessionStartPayload() });\n break;\n }\n\n case 'model.refine': {\n const { text } = (msg as { payload: { text: string } }).payload;\n if (!text?.trim()) {\n send(ws, {\n type: 'model.refine_result',\n payload: { refined: '', english: '', error: 'Empty text' },\n });\n break;\n }\n try {\n const history = recentTextTurns(context.messages);\n const result = await enhanceUserPrompt({\n provider: context.provider,\n model: context.model,\n text,\n history,\n timeoutMs: 90000,\n onError: (reason) => {\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'model.refine_failed',\n reason,\n timestamp: new Date().toISOString(),\n }));\n },\n });\n if (result) {\n send(ws, {\n type: 'model.refine_result',\n payload: { refined: result.refined, english: result.english },\n });\n } else {\n send(ws, {\n type: 'model.refine_result',\n payload: { refined: text, english: text, error: 'Refinement returned no result' },\n });\n }\n } catch (err) {\n console.error(JSON.stringify({\n level: 'error',\n event: 'model.refine.error',\n error: errMessage(err),\n timestamp: new Date().toISOString(),\n }));\n send(ws, {\n type: 'model.refine_result',\n payload: { refined: text, english: text, error: errMessage(err) },\n });\n }\n break;\n }\n\n case 'key.add':\n case 'key.update': {\n const { providerId, label, apiKey } = (\n msg as { payload: { providerId: string; label: string; apiKey: string } }\n ).payload;\n await providerHandlers.handleKeyUpsert(ws, providerId, label, apiKey);\n break;\n }\n\n case 'key.delete': {\n const { providerId, label } = (msg as { payload: { providerId: string; label: string } })\n .payload;\n await providerHandlers.handleKeyDelete(ws, providerId, label);\n break;\n }\n\n case 'key.set_active': {\n const { providerId, label } = (msg as { payload: { providerId: string; label: string } })\n .payload;\n await providerHandlers.handleKeySetActive(ws, providerId, label);\n break;\n }\n\n case 'provider.add': {\n const p = (\n msg as {\n payload: {\n id: string;\n family: string;\n baseUrl?: string | undefined;\n apiKey?: string | undefined;\n };\n }\n ).payload;\n await providerHandlers.handleProviderAdd(ws, p);\n break;\n }\n\n case 'provider.remove': {\n const { providerId } = (msg as { payload: { providerId: string } }).payload;\n await providerHandlers.handleProviderRemove(ws, providerId);\n break;\n }\n\n case 'sessions.list': {\n // Per-project history. Sessions live under .wrongstack/sessions/ for\n // this project; we never enumerate cross-project state.\n const limit = (msg as { payload?: { limit?: number | undefined } }).payload?.limit ?? 50;\n try {\n const list = await sessionStore.list(limit);\n send(ws, {\n type: 'sessions.list',\n payload: {\n sessions: list.map((s) => ({\n id: s.id,\n title: s.title,\n startedAt: s.startedAt,\n model: s.model,\n provider: s.provider,\n tokenTotal: s.tokenTotal,\n isCurrent: s.id === session.id,\n })),\n },\n });\n } catch (err) {\n send(ws, {\n type: 'sessions.list',\n payload: { sessions: [], error: errMessage(err) },\n });\n }\n break;\n }\n\n case 'session.delete': {\n const { id } = (msg as { payload: { id: string } }).payload;\n try {\n if (id === session.id) {\n sendResult(ws, false, 'Cannot delete the active session');\n break;\n }\n await sessionStore.delete(id);\n sendResult(ws, true, `Session ${id} deleted`);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'session.resume': {\n // Load a past session's messages + usage, swap the active session\n // writer, hydrate the live Context, then broadcast a session.start\n // payload tagged with the replayed transcript so the UI can render\n // the chat history.\n const { id } = (msg as { payload: { id: string } }).payload;\n try {\n if (id === session.id) {\n sendResult(ws, false, 'Session is already active');\n break;\n }\n const resumed = await sessionStore.resume(id);\n // Finalize the prior writer (session_end + close) best-effort;\n // swallow errors so we don't block the resume on a crashed file\n // handle. The end marker is what makes the session we are leaving\n // read as cleanly completed (summary outcome, endedAt).\n try {\n await session.append({\n type: 'session_end',\n ts: new Date().toISOString(),\n usage: tokenCounter.total(),\n });\n await session.close();\n } catch {\n /* noop */\n }\n session = resumed.writer;\n context.session = session;\n context.state.replaceMessages(resumed.data.messages);\n context.readFiles.clear();\n context.fileMtimes.clear();\n tokenCounter.reset();\n // Replay usage so the topbar shows accurate totals after resume.\n tokenCounter.account(resumed.data.usage, config.model);\n sessionStartedAt = Date.now();\n broadcast(clients, {\n type: 'session.start',\n payload: {\n ...(await sessionStartPayload()),\n reset: true,\n replayMessages: resumed.data.messages,\n replayUsage: resumed.data.usage,\n },\n });\n sendResult(ws, true, `Resumed session ${id}`);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'session.save': {\n // SessionWriter already flushes after every event; this is mostly a\n // no-op marker so the user gets confirmation. Useful for habit\n // parity with the CLI /save command.\n sendResult(ws, true, `Session ${session.id} is auto-saved`);\n break;\n }\n\n case 'tools.list': {\n // Full tool registry dump for the /tools inspect view. We surface\n // name, description, and schema-derived param names so the user\n // can tell at a glance which tools the model can call right now.\n const list = toolRegistry.list().map((t) => {\n const schema =\n (t as { inputSchema?: { properties?: Record<string, unknown> } }).inputSchema ?? {};\n const params = schema.properties ? Object.keys(schema.properties) : [];\n return {\n name: t.name,\n description: (t as { description?: string | undefined }).description ?? '',\n params,\n };\n });\n send(ws, { type: 'tools.list', payload: { tools: list } });\n break;\n }\n\n // ── Memory operations — delegated to shared handlers (memory-handlers.ts) ──\n case 'memory.list':\n return handleMemoryList(ws, memoryStore);\n case 'memory.remember':\n return handleMemoryRemember(ws, msg, memoryStore);\n case 'memory.forget':\n return handleMemoryForget(ws, msg, memoryStore);\n\n case 'skills.list': {\n if (!skillLoader) {\n send(ws, { type: 'skills.list', payload: { skills: [], enabled: false } });\n break;\n }\n try {\n const manifests = await skillLoader.list();\n const entries = await skillLoader.listEntries();\n const byName = new Map(entries.map((e) => [e.name, e]));\n send(ws, {\n type: 'skills.list',\n payload: {\n enabled: true,\n skills: manifests.map((m) => ({\n name: m.name,\n description: m.description,\n version: m.version ?? '',\n source: m.source,\n path: m.path,\n trigger: byName.get(m.name)?.trigger ?? '',\n scope: byName.get(m.name)?.scope ?? [],\n })),\n },\n });\n } catch (err) {\n send(ws, {\n type: 'skills.list',\n payload: {\n skills: [],\n enabled: true,\n error: errMessage(err),\n },\n });\n }\n break;\n }\n\n case 'diag.get': {\n // Snapshot of the moving parts so the user can debug \"why is X\n // not working?\" without diving into the server logs.\n const usage = tokenCounter.total();\n send(ws, {\n type: 'diag.get',\n payload: {\n provider: config.provider,\n model: config.model,\n cwd: projectRoot,\n sessionId: session.id,\n tools: {\n count: toolRegistry.list().length,\n names: toolRegistry.list().map((t) => t.name),\n },\n features: {\n memory: !!config.features?.memory,\n skills: !!config.features?.skills,\n modelsRegistry: !!config.features?.modelsRegistry,\n },\n mode: modeId ?? 'default',\n usage,\n messages: context.messages.length,\n todos: context.todos.length,\n },\n });\n break;\n }\n\n case 'todos.get': {\n // On-demand snapshot — used when a UI surface first mounts and\n // needs to render the live todo list without waiting for the next\n // tool.executed to broadcast.\n send(ws, {\n type: 'todos.updated',\n payload: { todos: [...context.todos] },\n });\n break;\n }\n\n case 'todos.clear': {\n // Manual override — the agent normally curates this list via\n // TodoWrite, but the user might want a clean slate without losing\n // the rest of the context. Use state.replaceTodos so observers\n // (checkpoint writer) stay in sync.\n context.state.replaceTodos([]);\n sendResult(ws, true, 'Todos cleared');\n broadcast(clients, { type: 'todos.updated', payload: { todos: [] } });\n break;\n }\n\n case 'todos.remove': {\n // Remove a single todo item by id or 1-based index.\n const payload = msg.payload as\n | { id?: string | undefined; index?: number | undefined }\n | undefined;\n if (!payload) {\n sendResult(ws, false, 'Missing id or index');\n break;\n }\n const { id, index } = payload;\n let targetIdx = -1;\n if (typeof id === 'string') {\n targetIdx = context.todos.findIndex((t) => t.id === id);\n } else if (typeof index === 'number' && index > 0) {\n targetIdx = index - 1;\n }\n if (targetIdx < 0 || !context.todos[targetIdx]) {\n sendResult(ws, false, 'Todo not found');\n break;\n }\n const removed = expectDefined(context.todos[targetIdx]);\n const next = [...context.todos.slice(0, targetIdx), ...context.todos.slice(targetIdx + 1)];\n context.state.replaceTodos(next);\n sendResult(ws, true, `Removed: ${removed.content}`);\n broadcast(clients, { type: 'todos.updated', payload: { todos: next } });\n break;\n }\n\n case 'tasks.get': {\n // On-demand task snapshot — loads from <sessionId>.tasks.json\n const taskPath = (context.meta as Record<string, unknown>)['task.path'];\n if (typeof taskPath === 'string' && taskPath) {\n try {\n const { loadTasks } = await import('@wrongstack/core');\n const file = await loadTasks(taskPath);\n send(ws, {\n type: 'tasks.updated',\n payload: { tasks: file?.tasks ?? [] },\n });\n } catch {\n send(ws, { type: 'tasks.updated', payload: { tasks: [] } });\n }\n } else {\n send(ws, { type: 'tasks.updated', payload: { tasks: [], error: 'Task storage not configured.' } });\n }\n break;\n }\n\n case 'plan.get': {\n // On-demand plan snapshot — used when a UI surface first mounts\n // and needs to render the live plan without waiting for the next\n // tool.executed to broadcast.\n const planPath = (context.meta as Record<string, unknown>)['plan.path'];\n if (typeof planPath === 'string' && planPath) {\n try {\n const { loadPlan } = await import('@wrongstack/core');\n const plan = await loadPlan(planPath);\n send(ws, {\n type: 'plan.updated',\n payload: {\n plan: plan ?? {\n version: 1,\n sessionId: session.id,\n updatedAt: new Date().toISOString(),\n items: [],\n },\n },\n });\n } catch {\n send(ws, {\n type: 'plan.updated',\n payload: {\n plan: {\n version: 1,\n sessionId: session.id,\n updatedAt: new Date().toISOString(),\n items: [],\n },\n },\n });\n }\n } else {\n send(ws, {\n type: 'plan.updated',\n payload: { plan: null, error: 'Plan storage is not configured for this session.' },\n });\n }\n break;\n }\n\n case 'plan.template_use': {\n const { template } = (msg as { payload: { template: string } }).payload;\n const planPath = (context.meta as Record<string, unknown>)['plan.path'];\n if (typeof planPath !== 'string' || !planPath) {\n sendResult(ws, false, 'Plan storage is not configured for this session.');\n break;\n }\n try {\n const { getPlanTemplate, loadPlan, savePlan, emptyPlan, addPlanItem } = await import(\n '@wrongstack/core'\n );\n const tpl = getPlanTemplate(template);\n if (!tpl) {\n sendResult(ws, false, `Unknown template \"${template}\".`);\n break;\n }\n let plan = (await loadPlan(planPath)) ?? emptyPlan(session.id);\n for (const item of tpl.items) {\n ({ plan } = addPlanItem(plan, item.title, item.details));\n }\n await savePlan(planPath, plan);\n sendResult(ws, true, `Applied template \"${tpl.name}\" — ${tpl.items.length} items added.`);\n broadcast(clients, {\n type: 'plan.updated',\n payload: { plan },\n });\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n // ── File operations — delegated to shared handlers (file-handlers.ts) ──\n // These handlers are also used by the CLI's webui-server.ts. When\n // adding or modifying file-operation WebSocket messages, update\n // file-handlers.ts — NOT these case blocks individually.\n case 'files.list':\n return handleFilesList(ws, msg, projectRoot);\n case 'files.tree':\n return handleFilesTree(ws, msg, projectRoot);\n case 'files.read':\n return handleFilesRead(ws, msg, projectRoot);\n case 'files.write':\n return handleFilesWrite(ws, msg, projectRoot);\n\n case 'modes.list': {\n try {\n const modes = await modeStore.listModes();\n const active = await modeStore.getActiveMode();\n send(ws, {\n type: 'modes.list',\n payload: {\n modes: modes.map((m) => ({\n id: m.id,\n name: m.name,\n description: m.description,\n isActive: m.id === (active?.id ?? 'default'),\n })),\n activeId: active?.id ?? 'default',\n },\n });\n } catch (err) {\n send(ws, {\n type: 'modes.list',\n payload: {\n modes: [],\n activeId: 'default',\n error: errMessage(err),\n },\n });\n }\n break;\n }\n\n case 'mode.switch': {\n const { id } = (msg as { payload: { id: string } }).payload;\n try {\n // 'default' is the implicit no-mode state — persisting null\n // clears the override. Anything else has to exist in the store.\n if (id === 'default') {\n await modeStore.setActiveMode(null);\n } else {\n const found = await modeStore.getMode(id);\n if (!found) throw new Error(`Unknown mode \"${id}\"`);\n await modeStore.setActiveMode(id);\n }\n modeId = id;\n // Rebuild the system prompt so the next turn picks up the new\n // mode's instructions. The builder caches the environment block\n // per projectRoot (including the modeId), so we clear the cache\n // and rebuild. The `buildMode()` method reads this.opts.modePrompt\n // which is set on the builder constructor — we construct a fresh\n // builder with the updated mode. This is cheap (no fs/net IO in\n // the constructor; the real work happens in build()).\n const modePrompt = id === 'default' ? '' : ((await modeStore.getMode(id))?.prompt ?? '');\n const freshBuilder = new DefaultSystemPromptBuilder({\n memoryStore,\n skillLoader,\n modeStore,\n modeId: id,\n modePrompt,\n modelCapabilities,\n });\n context.systemPrompt = await freshBuilder.build({\n cwd: projectRoot,\n projectRoot,\n tools: toolRegistry.list(),\n provider: config.provider,\n model: config.model,\n });\n sendResult(ws, true, `Switched to mode \"${id}\"`);\n broadcast(clients, {\n type: 'session.start',\n payload: { ...(await sessionStartPayload()) },\n });\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'stats.get': {\n // Mirror of the CLI's /stats: detailed session report.\n const usage = tokenCounter.total();\n const cacheStats = tokenCounter.cacheStats();\n const m = await modelsRegistry.getModel(config.provider, config.model).catch(() => null);\n const cost = computeUsageCost(usage, getCostRates(m));\n send(ws, {\n type: 'stats.get',\n payload: {\n sessionId: session.id,\n provider: config.provider,\n model: config.model,\n usage,\n cache: cacheStats,\n cost,\n messages: context.messages.length,\n readFiles: context.readFiles.size,\n tools: toolRegistry.list().length,\n elapsedMs: Date.now() - sessionStartedAt,\n },\n });\n break;\n }\n\n case 'process.list': {\n // Return tracked process list from the process registry.\n try {\n const { getProcessRegistry } = await import('@wrongstack/tools');\n const procs = getProcessRegistry().list();\n send(ws, {\n type: 'process.list',\n payload: {\n processes: procs.map((p) => ({\n pid: p.pid,\n command: p.command,\n tool: p.name,\n startedAt: p.startedAt,\n status: p.killed ? ('killed' as const) : ('running' as const),\n protected: p.protected,\n })),\n },\n });\n } catch {\n send(ws, { type: 'process.list', payload: { processes: [] } });\n }\n break;\n }\n\n case 'process.kill': {\n const { pid } = (msg as { payload: { pid: number } }).payload;\n try {\n const { getProcessRegistry } = await import('@wrongstack/tools');\n const proc = getProcessRegistry().get(pid);\n if (proc?.protected) {\n sendResult(ws, false, `Cannot kill protected process (PID ${pid})`);\n break;\n }\n getProcessRegistry().kill(pid);\n sendResult(ws, true, `Killed PID ${pid}`);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'process.killAll': {\n try {\n const { getProcessRegistry } = await import('@wrongstack/tools');\n getProcessRegistry().killAll();\n sendResult(ws, true, 'All processes killed');\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n case 'goal.get': {\n // Read goal.json from disk and broadcast to all clients so every\n // connected browser sees the same goal state. The file is polled\n // by the frontend every 10s — we serve the latest snapshot here.\n try {\n const goalPath = path.join(projectRoot, '.wrongstack', 'goal.json');\n const raw = await fs.readFile(goalPath, 'utf8');\n const goal = JSON.parse(raw);\n broadcast(clients, { type: 'goal.updated', payload: goal });\n } catch {\n // No goal file yet or parse error — broadcast null so the\n // frontend clears any stale goal state.\n broadcast(clients, { type: 'goal.updated', payload: null });\n }\n break;\n }\n\n case 'autonomy.switch': {\n // Autonomy mode switch — forwarded to the agent context.\n // The mode is stored in context.meta for the permission policy to read.\n const { mode } = (msg as { payload: { mode: string } }).payload;\n context.meta['autonomy'] = mode;\n sendResult(ws, true, `Autonomy mode set to \"${mode}\"`);\n // Keep every browser tab + the settings panel in sync, and persist\n // the durable modes (eternal/eternal-parallel are session-level).\n broadcast(clients, { type: 'prefs.updated', payload: { autonomy: mode } });\n void persistPrefsToConfig({ autonomy: mode });\n break;\n }\n\n case 'prefs.update': {\n // Batch preference update from the webui. Merges arbitrary key/value\n // pairs into context.meta so the runtime can read them immediately,\n // broadcasts the full pref snapshot to every connected client so all\n // browser tabs stay in sync, and persists the durable keys to\n // config.json (same keys the TUI settings picker writes).\n const payload = (msg as { payload: Record<string, unknown> }).payload;\n // Write each pref into context.meta\n for (const [key, val] of Object.entries(payload)) {\n context.meta[key] = val;\n }\n void persistPrefsToConfig(payload);\n // YOLO mode: toggle the permission policy so tool confirmations\n // are auto-approved instead of prompting the user. Uses the live\n // reference resolved from the container at startup.\n if (typeof payload['yolo'] === 'boolean') {\n permissionPolicy.setYolo?.(payload['yolo']);\n }\n // Also update config.features for feature flags that affect tool/skill\n // initialisation (these were read at startup but can be changed at runtime\n // by the agent's permission middleware or tool guards).\n if (typeof payload['featureMcp'] === 'boolean')\n config.features.mcp = payload['featureMcp'];\n if (typeof payload['featurePlugins'] === 'boolean')\n config.features.plugins = payload['featurePlugins'];\n if (typeof payload['featureMemory'] === 'boolean')\n config.features.memory = payload['featureMemory'];\n if (typeof payload['featureSkills'] === 'boolean')\n config.features.skills = payload['featureSkills'];\n if (typeof payload['featureModelsRegistry'] === 'boolean')\n config.features.modelsRegistry = payload['featureModelsRegistry'];\n\n // Runtime effects: apply prefs that change server behaviour immediately.\n\n // contextAutoCompact — toggle AutoCompactionMiddleware in/out of the\n // contextWindow pipeline. When off, the pipeline skips the compaction\n // step entirely (zero overhead). When on, re-adds the middleware.\n if (typeof payload['contextAutoCompact'] === 'boolean') {\n if (payload['contextAutoCompact'] && autoCompactor) {\n // Re-add: remove first (idempotent via optional), then insert.\n pipelines.contextWindow.remove('AutoCompaction', { optional: true });\n pipelines.contextWindow.use({ name: 'AutoCompaction', handler: autoCompactor.handler() });\n } else {\n pipelines.contextWindow.remove('AutoCompaction', { optional: true });\n }\n }\n\n // logLevel — the DefaultLogger.level property is a public mutable\n // field. Setting it at runtime changes the log threshold immediately\n // (the log() method checks LEVEL_RANK on every call).\n if (typeof payload['logLevel'] === 'string') {\n const valid = ['debug', 'info', 'warn', 'error'] as const;\n if ((valid as readonly string[]).includes(payload['logLevel'])) {\n logger.level = payload['logLevel'] as typeof valid[number];\n }\n }\n\n // auditLevel — stored in context.meta by the generic loop above.\n // Consumed by the session audit log system at session-close time.\n\n // Broadcast the full current prefs snapshot to ALL clients.\n broadcast(clients, { type: 'prefs.updated', payload: prefSnapshot() });\n break;\n }\n\n case 'prefs.get': {\n // Return the current pref snapshot so a freshly-connected client\n // can seed its local-prefs store from the server's truth.\n send(ws, { type: 'prefs.updated', payload: prefSnapshot() });\n break;\n }\n\n case 'session.checkpoints': {\n // Return session checkpoints for the rewind timeline.\n try {\n const { DefaultSessionRewinder } = await import('@wrongstack/core');\n const rewinder = new DefaultSessionRewinder(\n path.join(projectRoot, '.wrongstack', 'sessions'),\n projectRoot,\n );\n const checkpoints = await rewinder.listCheckpoints(session.id);\n send(ws, {\n type: 'session.checkpoints',\n payload: { checkpoints },\n });\n } catch (err) {\n send(ws, {\n type: 'session.checkpoints',\n payload: { checkpoints: [] },\n });\n }\n break;\n }\n\n case 'session.rewind': {\n const { checkpointIndex } = (msg as { payload: { checkpointIndex: number } }).payload;\n try {\n const { DefaultSessionRewinder } = await import('@wrongstack/core');\n const rewinder = new DefaultSessionRewinder(\n path.join(projectRoot, '.wrongstack', 'sessions'),\n projectRoot,\n );\n await rewinder.rewindToCheckpoint(session.id, checkpointIndex);\n await context.session.truncateToCheckpoint(checkpointIndex);\n sendResult(ws, true, `Rewound to checkpoint ${checkpointIndex}`);\n broadcast(clients, {\n type: 'session.start',\n payload: { ...(await sessionStartPayload()), reset: true },\n });\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n // ── Project management ────────────────────────────────────────────\n\n case 'projects.list': {\n try {\n const manifest = await loadManifest(globalConfigPath);\n send(ws, {\n type: 'projects.list',\n payload: { projects: manifest.projects },\n });\n } catch (err) {\n send(ws, {\n type: 'projects.list',\n payload: { projects: [], error: errMessage(err) },\n });\n }\n break;\n }\n\n case 'projects.add': {\n const { root: addRoot, name: displayName } = (\n msg as { payload: { root: string; name?: string | undefined } }\n ).payload;\n try {\n const resolved = path.resolve(addRoot);\n await fs.access(resolved);\n const stat = await fs.stat(resolved);\n if (!stat.isDirectory()) throw new Error(`Not a directory: ${resolved}`);\n\n const manifest = await loadManifest(globalConfigPath);\n const existing = manifest.projects.find((p) => p.root === resolved);\n if (existing) {\n send(ws, {\n type: 'projects.added',\n payload: {\n name: existing.name,\n root: existing.root,\n slug: existing.slug,\n message: `Already registered as \"${existing.name}\"`,\n },\n });\n break;\n }\n\n const name = displayName?.trim() || path.basename(resolved);\n const slug = generateProjectSlug(resolved);\n await ensureProjectDataDir(slug, globalConfigPath);\n const now = new Date().toISOString();\n manifest.projects.push({ name, root: resolved, slug, lastSeen: now, createdAt: now });\n await saveManifest(manifest, globalConfigPath);\n\n send(ws, {\n type: 'projects.added',\n payload: {\n name,\n root: resolved,\n slug,\n message: `Registered project \"${name}\"`,\n },\n });\n } catch (err) {\n send(ws, {\n type: 'projects.added',\n payload: {\n name: path.basename(addRoot),\n root: addRoot,\n slug: '',\n message: errMessage(err),\n },\n });\n }\n break;\n }\n\n case 'projects.select': {\n const { root: selRoot, name: selName } = (\n msg as { payload: { root: string; name?: string | undefined } }\n ).payload;\n try {\n const resolved = path.resolve(selRoot);\n\n // Validate the directory exists\n try {\n await fs.access(resolved);\n const stat = await fs.stat(resolved);\n if (!stat.isDirectory()) throw new Error(`Not a directory: ${resolved}`);\n } catch (err) {\n send(ws, {\n type: 'projects.selected',\n payload: {\n root: selRoot,\n name: selName || path.basename(selRoot),\n message: `Cannot switch: ${errMessage(err)}`,\n },\n });\n break;\n }\n\n // Update lastSeen in manifest\n const manifest = await loadManifest(globalConfigPath);\n const entry = manifest.projects.find((p) => p.root === resolved);\n if (entry) {\n entry.lastSeen = new Date().toISOString();\n entry.lastWorkingDir = resolved;\n } else {\n // Auto-register if not in manifest\n const name = selName?.trim() || path.basename(resolved);\n const slug = generateProjectSlug(resolved);\n manifest.projects.push({\n name,\n root: resolved,\n slug,\n lastSeen: new Date().toISOString(),\n createdAt: new Date().toISOString(),\n lastWorkingDir: resolved,\n });\n await ensureProjectDataDir(slug, globalConfigPath);\n }\n await saveManifest(manifest, globalConfigPath);\n\n // ── Hot-swap the project root + working dir ─────────────────\n // Abort any in-flight agent run before switching — the agent's\n // context (cwd, projectRoot, session) is about to change and\n // continuing would cause inconsistent state.\n if (runLock) {\n runLock.abort();\n runLock = null;\n }\n\n projectRoot = resolved;\n workingDir = resolved;\n\n // Update the live context so tools use the new directory\n context.cwd = workingDir;\n context.projectRoot = projectRoot;\n\n const switchSlug = entry?.slug ?? generateProjectSlug(resolved);\n\n // Rebuild the system prompt for the NEW project. The environment\n // block (project root, git status, detected languages) is baked into\n // the prompt at boot and cached by projectRoot; without this rebuild\n // the agent keeps the launch-directory environment and tries to work\n // in the old folder until tool errors force a correction. Mirrors the\n // mode.switch rebuild; best-effort so a failure here leaves the prior\n // (stale-but-usable) prompt rather than breaking the switch.\n try {\n const switchMode =\n modeId === 'default' ? undefined : await modeStore.getMode(modeId);\n const switchBuilder = new DefaultSystemPromptBuilder({\n memoryStore,\n skillLoader,\n modeStore,\n modeId,\n modePrompt: switchMode?.prompt ?? '',\n modelCapabilities,\n });\n context.systemPrompt = await switchBuilder.build({\n cwd: workingDir,\n projectRoot,\n tools: toolRegistry.list(),\n provider: config.provider,\n model: config.model,\n });\n } catch {\n /* best-effort — keep the prior system prompt if rebuild fails */\n }\n\n // Create a new session store for the new project's sessions dir\n const newSessionsDir = path.join(\n path.dirname(globalConfigPath),\n 'projects',\n switchSlug,\n 'sessions',\n );\n await fs.mkdir(newSessionsDir, { recursive: true });\n const newSessionStore = new DefaultSessionStore({ dir: newSessionsDir });\n\n // Switch the session store for the new project.\n // Close the old session gracefully\n const oldSessionId = session.id;\n try {\n await session.append({\n type: 'session_end',\n ts: new Date().toISOString(),\n usage: tokenCounter.total(),\n });\n await session.close();\n } catch {\n // best-effort\n }\n\n // Create a fresh session in the new project\n sessionStore = newSessionStore;\n session = await sessionStore.create({\n id: '',\n title: '',\n model: config.model,\n provider: config.provider,\n });\n context.session = session;\n context.state.replaceMessages([]);\n context.state.replaceTodos([]);\n context.readFiles.clear();\n context.fileMtimes.clear();\n tokenCounter.reset();\n sessionStartedAt = Date.now();\n\n // Re-point the cross-process SessionRegistry at the new project +\n // session id. Without this, `/sessions status`, the WebUI sessions\n // dashboard, and the 5s status poll keep listing this process under\n // the launch project's root/workingDir — the WebUI looks like it is\n // still \"in\" the old folder after the switch. register() is now\n // re-entrant (drops the old entry, restarts a single heartbeat).\n try {\n const registry = getSessionRegistry(wpaths.globalRoot);\n await registry.register({\n sessionId: session.id,\n projectSlug: switchSlug,\n projectRoot,\n projectName: path.basename(projectRoot),\n workingDir,\n pid: process.pid,\n startedAt: new Date().toISOString(),\n });\n } catch {\n /* best-effort — discovery degrades gracefully */\n }\n\n send(ws, {\n type: 'projects.selected',\n payload: {\n root: resolved,\n name: selName || path.basename(resolved),\n message: `Switched to ${selName || path.basename(resolved)}`,\n },\n });\n\n // Broadcast old-session subagents as stopped so the frontend\n // fleet store cleans them via its normal event pipeline.\n broadcast(clients, {\n type: 'subagent.event',\n payload: {\n kind: 'session_stopped',\n sessionId: oldSessionId,\n },\n });\n\n // Broadcast updated project info to ALL clients so file\n // explorer / context bar pick up the new root.\n broadcast(clients, {\n type: 'session.start',\n payload: {\n ...(await sessionStartPayload()),\n reset: true,\n clearedSessionId: oldSessionId,\n },\n });\n } catch (err) {\n send(ws, {\n type: 'projects.selected',\n payload: {\n root: selRoot,\n name: selName || path.basename(selRoot),\n message: errMessage(err),\n },\n });\n }\n break;\n }\n\n // ── Working directory (within current project) ───────────────────\n\n case 'working_dir.set': {\n const { path: newPath } = (msg as { payload: { path: string } }).payload;\n try {\n const resolved = path.resolve(projectRoot, newPath);\n\n // Guard: must stay inside projectRoot\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n sendResult(ws, false, `Path must stay inside the project root: ${projectRoot}`);\n break;\n }\n\n try {\n await fs.access(resolved);\n const stat = await fs.stat(resolved);\n if (!stat.isDirectory()) throw new Error('Not a directory');\n } catch {\n sendResult(ws, false, `Directory not found or not accessible: ${resolved}`);\n break;\n }\n\n workingDir = resolved;\n context.cwd = resolved;\n\n // Notify all clients so the file explorer and context bar update\n broadcast(clients, {\n type: 'working_dir.changed',\n payload: { cwd: resolved, projectRoot },\n });\n\n sendResult(ws, true, `Working directory set to ${resolved}`);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n break;\n }\n\n // ── Shell open — spawn terminal or file manager at a path ─────────\n\n case 'shell.open': {\n // Logic lives in `shell-open.ts` so the CLI's `runWebUI` can\n // share the same metacharacter guard + cross-platform spawn\n // chain. See the docstring in shell-open.ts for the security\n // rationale and the fallback chain.\n const result: ShellOpenResult = await handleShellOpen(\n msg.payload as ShellOpenRequest,\n logger,\n );\n sendResult(ws, result.success, result.message);\n break;\n }\n\n // ── Mailbox operations — project-level inter-agent messaging ────\n case 'mailbox.messages':\n return handleMailboxMessages(\n ws,\n { projectRoot, globalRoot: path.dirname(globalConfigPath) },\n (msg as { payload?: { limit?: number; agentId?: string; unreadOnly?: boolean } }).payload,\n );\n case 'mailbox.agents':\n return handleMailboxAgents(\n ws,\n { projectRoot, globalRoot: path.dirname(globalConfigPath) },\n (msg as { payload?: { onlineOnly?: boolean } }).payload,\n );\n case 'mailbox.clear':\n return handleMailboxClear(\n ws,\n { projectRoot, globalRoot: path.dirname(globalConfigPath) },\n );\n\n // ── Brain — status, autonomy ceiling, direct decision support ───\n case 'brain.status':\n send(ws, {\n type: 'brain.status',\n payload: { maxAutoRisk: brainSettings.maxAutoRisk, log: brainLog },\n });\n break;\n case 'brain.risk': {\n const level = (msg as { payload?: { level?: string } }).payload?.level ?? '';\n const valid = ['off', 'low', 'medium', 'high', 'all'];\n if (!valid.includes(level)) {\n sendResult(ws, false, `Unknown risk level \"${level}\". Use: ${valid.join(', ')}.`);\n break;\n }\n brainSettings.maxAutoRisk = level as BrainAutoRisk;\n send(ws, {\n type: 'brain.status',\n payload: { maxAutoRisk: brainSettings.maxAutoRisk, log: brainLog },\n });\n break;\n }\n case 'brain.ask': {\n const question = (msg as { payload?: { question?: string } }).payload?.question?.trim();\n if (!question) {\n sendResult(ws, false, 'Usage: /brain ask <question>');\n break;\n }\n try {\n const decision = await brain.decide({\n id: `brain-ask-${Date.now().toString(36)}`,\n source: 'user',\n question,\n risk: 'medium',\n fallback: 'ask_human',\n });\n send(ws, { type: 'brain.answer', payload: { question, decision } });\n } catch (err) {\n sendResult(ws, false, `Brain consultation failed: ${errMessage(err)}`);\n }\n break;\n }\n\n default:\n if (msg.type.startsWith('autophase.')) {\n // Delegate all AutoPhase lifecycle messages to the handler\n await autoPhaseHandler.handleMessage(\n msg as { type: string; payload?: Record<string, unknown> },\n );\n } else {\n send(ws, {\n type: 'error',\n payload: { phase: 'handleMessage', message: `Unknown message type: ${msg.type}` },\n });\n }\n }\n }\n\n // ---- Provider/Key management helpers (extracted to provider-handlers.ts) ----\n const providerHandlers = createProviderHandlers({\n globalConfigPath,\n vault,\n getConfigWriteLock: () => configWriteLock,\n setConfigWriteLock: (p) => {\n configWriteLock = p;\n },\n broadcast,\n clients,\n });\n\n // HTTP server for the React frontend (port 3456) — see `http-server.ts`\n // for the static-serve, MIME matching, path-traversal guard, and CSP\n // header logic. Constructed here, listen()d below alongside the WS server.\n // `globalRoot` powers the /api/sessions and /api/sessions/:id/agents\n // handlers (read the cross-process SessionRegistry); `apiToken` is the\n // shared auth token the HTTP API requires when bound to a non-loopback\n // host (LAN exposure). Loopback binds skip the token check, mirroring\n // the WS verifyClient loopback-bootstrap policy.\n const httpServer = createHttpServer({\n host: wsHost,\n distDir: path.resolve(import.meta.dirname, '../../dist'),\n wsPort,\n globalRoot: wpaths.globalRoot,\n apiToken: wsToken,\n });\n // httpPort/wsPort were resolved (and possibly auto-advanced) at the top.\n // Base dir for the running-instance registry — keep it next to the rest of\n // the wstack home state (config.json lives here too).\n const registryBaseDir = path.dirname(globalConfigPath);\n httpServer.listen(httpPort, wsHost, () => {\n const openUrl = `http://${wsHost}:${httpPort}`;\n console.log(`[WebUI] HTTP server running on ${openUrl}`);\n // Optionally pop the browser open (best-effort; the URL is always printed).\n if (opts.open) openBrowser(openUrl);\n // Record this instance so `webui --list` (and `~/.wrongstack/\n // webui-instances.json`) show which ports are open for which project.\n // Best-effort: a registry write failure must not affect serving.\n void registerInstance(\n {\n pid: process.pid,\n httpPort,\n wsPort,\n host: wsHost,\n projectRoot,\n projectName: path.basename(projectRoot) || projectRoot,\n startedAt: new Date().toISOString(),\n url: `http://${wsHost}:${httpPort}`,\n },\n registryBaseDir,\n ).catch((err) => console.warn(JSON.stringify({\n level: 'warn',\n event: 'webui.instance_record_failed',\n message: errMessage(err),\n timestamp: new Date().toISOString(),\n })));\n });\n\n // Graceful shutdown on SIGINT/SIGTERM — see `lifecycle.ts`. The session\n // flush (session_end + close) is passed as a thunk so lifecycle stays\n // decoupled from the session/tokenCounter types.\n registerShutdownHandlers({\n flushSession: async () => {\n await session.append({\n type: 'session_end',\n ts: new Date().toISOString(),\n usage: tokenCounter.total(),\n });\n await session.close();\n },\n clients: () => clients.keys(),\n servers: [httpServer, wssPrimary, wssSecondary],\n // Drop this instance from the registry on a clean exit so the file reflects\n // reality. Crash exits are healed by the next register()/list() prune pass.\n onShutdown: () => {\n brainMonitor.stop();\n if (eternalSubscription) {\n eternalSubscription.dispose();\n eternalSubscription = null;\n }\n return unregisterInstance(process.pid, registryBaseDir);\n },\n });\n}\n","/**\n * Static-file HTTP server for the WebUI React frontend.\n *\n * - Serves files from `distDir` (typically `<webui>/dist`).\n * - Returns `index.html` for any unknown path so client-side routing works\n * (SPA fallback) — and applies the same Content-Security-Policy to that\n * fallback as to a direct `.html` response, so deep-linked routes are\n * not unprotected.\n * - **Path-traversal guard**: `path.join` alone does NOT prevent\n * `%2e%2e%2f` escapes (the `URL` constructor decodes percent-encoding\n * before we see the path). We re-`resolve` the candidate and verify it\n * stays under `distDir`.\n * - **CSP**: `connect-src` uses explicit loopback addresses for the WS\n * server (not bare `ws:` / `wss:`) so a malicious page script cannot\n * dial an attacker-controlled WebSocket. Combined with the\n * cookie-based WS auth delivery (`/ws-auth` → `Set-Cookie: ws_token=\n * …; HttpOnly; SameSite=Strict; Path=/`), this prevents cross-origin\n * WS abuse.\n * - **API auth**: the `/api/sessions` and `/api/sessions/:id/agents`\n * endpoints accept the same shared token as the WS upgrade, via the\n * `X-WS-Token` header. Without it, those endpoints return 401. This\n * closes the LAN-attacker enumeration vector when `wsHost` is\n * non-loopback (e.g. `WS_HOST=0.0.0.0`).\n *\n * Extracted from `index.ts` so the static-serve concern can be tested\n * with a tiny fake `distDir` and asserted on path-traversal, MIME\n * matching, and CSP header presence.\n */\nimport * as fs from 'node:fs/promises';\nimport * as http from 'node:http';\nimport * as path from 'node:path';\nimport { isLoopbackBind, tokenMatches } from './ws-auth.js';\n\nexport interface CreateHttpServerOptions {\n /** Port to listen on. Defaults to 3456 (or the `PORT` env var). */\n port?: number | undefined;\n /** Host/interface to bind. Typically the loopback for the WebUI. */\n host: string;\n /** Resolved path to the directory containing the built React assets. */\n distDir: string;\n /**\n * WS port — appears in the CSP `connect-src` directive so the browser\n * is allowed to open a WebSocket back to the local server.\n */\n wsPort: number;\n /**\n * Path to the global WrongStack root (~/.wrongstack). Used by the\n * /api/sessions and /api/sessions/:id/agents endpoints to read the\n * cross-process SessionRegistry.\n */\n globalRoot?: string | undefined;\n /**\n * Shared auth token for `/api/*` endpoints. Required for non-loopback\n * binds (LAN exposure). Loopback binds accept any local origin without\n * a token (the WS path's loopback-bootstrap policy — see ws-auth.ts).\n */\n apiToken?: string | undefined;\n /**\n * If true, the `/ws-auth` endpoint exchanges a `?token=` query param (or\n * `X-WS-Token` header) for an `HttpOnly` auth cookie. The cookie is then\n * sent automatically on the WS upgrade, closing the C-598 query-string\n * token exposure class. Default: true. Set to false to keep the legacy\n * URL-token-only flow (e.g. in tests that don't want cookie state).\n */\n enableWsCookie?: boolean | undefined;\n}\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n};\n\n/**\n * Inject the live WS port into the served HTML so the frontend connects to\n * THIS instance's backend instead of a hardcoded default. Enables running\n * several WebUI instances simultaneously on different PORT/WS_PORT pairs\n * (e.g. one per project) — each instance serves HTML stamped with its own\n * WS port.\n *\n * A `<meta>` tag is used deliberately rather than an inline `<script>`: the\n * CSP sets `script-src 'self'`, which would block an inline script, but meta\n * tags are not subject to script-src. The frontend reads\n * `meta[name=\"wrongstack-ws-port\"]` (see ws-client.ts `defaultWsUrl`).\n */\nexport function injectWsPort(html: string, wsPort: number): string {\n const tag = `<meta name=\"wrongstack-ws-port\" content=\"${wsPort}\" />`;\n // Idempotent: never inject twice if the source HTML already carries one.\n if (html.includes('name=\"wrongstack-ws-port\"')) return html;\n if (html.includes('</head>')) {\n return html.replace('</head>', ` ${tag}\\n </head>`);\n }\n // No <head> (unexpected) — prepend so the tag is still in the document.\n return `${tag}\\n${html}`;\n}\n\n/** Build the Content-Security-Policy value for the given WS port. */\nexport function buildCspHeader(wsPort: number): string {\n return (\n `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; ` +\n `connect-src 'self' ws://127.0.0.1:${wsPort} wss://127.0.0.1:${wsPort} ` +\n `ws://[::1]:${wsPort} wss://[::1]:${wsPort}; ` +\n `img-src 'self' data:; font-src 'self' data:; worker-src 'self' blob:; object-src 'none'; ` +\n `base-uri 'self'; frame-ancestors 'none'; form-action 'self'`\n );\n}\n\n/**\n * Returns true when `candidate` (a fully-resolved absolute path) lies\n * strictly inside `distDir` (or equals it). Used to reject path-traversal\n * attempts after `path.resolve` has normalised any `..` segments.\n *\n * Exported so tests can assert the guard's contract without having to\n * also defeat the WHATWG URL normaliser (which strips `..` from the\n * path string *before* the request even reaches the server, making a\n * black-box test via fetch impossible).\n */\nexport function isInsideDist(candidate: string, distDir: string): boolean {\n const root = path.resolve(distDir);\n const resolved = path.resolve(candidate);\n return resolved === root || resolved.startsWith(root + path.sep);\n}\n\n/**\n * Create the static-file HTTP server. Returns the `http.Server` (not\n * listening yet) so the caller can attach to a `shutdown()` hook and\n * coordinate the listen() with the WebSocket bootstrap.\n */\nexport function createHttpServer(opts: CreateHttpServerOptions): http.Server {\n const port = opts.port ?? Number.parseInt(process.env['PORT'] ?? '3456', 10);\n const distDir = path.resolve(opts.distDir);\n const wsPort = opts.wsPort;\n // Loopback bind: no API token required (mirrors WS loopback-bootstrap).\n // LAN bind: caller MUST supply a token; we reject any /api request that\n // doesn't present it via `X-WS-Token` (constant-time compared).\n const requireApiToken = !isLoopbackBind(opts.host) && Boolean(opts.apiToken);\n\n return http.createServer(async (req, res) => {\n try {\n const url = new URL(req.url ?? '/', `http://127.0.0.1:${port}`);\n\n // ── API routes ──────────────────────────────────────────────────\n // /ws-auth — exchange a one-shot token (header or query) for an\n // HttpOnly cookie. The browser then sends the cookie on the WS\n // upgrade automatically, closing C-598 (token-in-URL). Disabled\n // when `enableWsCookie: false` (tests, or operators who prefer\n // the URL-token flow for explicit dev).\n if (url.pathname === '/ws-auth' && req.method === 'GET' && (opts.enableWsCookie ?? true)) {\n // Accept the token from `?token=` query (browser navigation\n // from the server-printed URL) OR the `X-WS-Token` header\n // (scripted client).\n const provided =\n url.searchParams.get('token') ?? (req.headers['x-ws-token'] as string | undefined);\n if (!provided || !opts.apiToken || !tokenMatches(provided, opts.apiToken)) {\n res.writeHead(401, { 'Content-Type': 'text/plain' });\n res.end('Unauthorized');\n return;\n }\n // HttpOnly + SameSite=Strict + Path=/ — the cookie is immune to\n // XSS exfiltration (no JS access), cross-origin Referer leakage\n // (Strict blocks cross-site), and is scoped to this origin only.\n // No `Secure` flag: the dev server is plain HTTP on loopback,\n // and a Secure cookie over HTTP would not be sent by the browser.\n res.writeHead(200, {\n 'Content-Type': 'text/plain',\n 'Set-Cookie': `ws_token=${encodeURIComponent(opts.apiToken)}; HttpOnly; SameSite=Strict; Path=/; Max-Age=3600`,\n // Belt-and-braces: tell any caches the cookie response itself\n // is sensitive.\n 'Cache-Control': 'no-store',\n });\n res.end('ok');\n return;\n }\n\n if (url.pathname === '/api/sessions' && req.method === 'GET') {\n // `req.headers['x-ws-token']` is typed as `string | string[] | undefined`\n // because some Node http clients send repeated headers as an array.\n // Pick the first value for the constant-time compare.\n const headerToken = req.headers['x-ws-token'];\n const provided = Array.isArray(headerToken) ? headerToken[0] : headerToken;\n if (requireApiToken && !tokenMatches(provided, opts.apiToken ?? '')) {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Unauthorized' }));\n return;\n }\n await handleApiSessions(res, opts.globalRoot);\n return;\n }\n\n const agentsMatch = url.pathname.match(/^\\/api\\/sessions\\/([^/]+)\\/agents$/);\n if (agentsMatch && req.method === 'GET') {\n const headerToken = req.headers['x-ws-token'];\n const provided = Array.isArray(headerToken) ? headerToken[0] : headerToken;\n if (requireApiToken && !tokenMatches(provided, opts.apiToken ?? '')) {\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Unauthorized' }));\n return;\n }\n await handleApiSessionAgents(res, opts.globalRoot, agentsMatch[1]!);\n return;\n }\n\n let filePath: string;\n\n if (url.pathname === '/' || url.pathname === '') {\n filePath = path.join(distDir, 'index.html');\n } else if (url.pathname.startsWith('/assets/')) {\n filePath = path.join(distDir, url.pathname);\n } else if (url.pathname.startsWith('/')) {\n filePath = path.join(distDir, url.pathname);\n } else {\n filePath = path.join(distDir, 'index.html');\n }\n\n // Path traversal guard: the resolved path must stay inside distDir.\n // WHATWG URL leaves percent-encoding alone in `url.pathname` (it\n // does not decode `%2e%2e` to `..`), so percent-encoded escapes\n // are *not* a concern here — but unencoded `..` segments are\n // normalised by `path.resolve` and would walk the candidate up\n // out of distDir. `isInsideDist` catches that.\n const resolvedPath = path.resolve(filePath);\n if (!isInsideDist(resolvedPath, distDir)) {\n res.writeHead(403, { 'Content-Type': 'text/plain' });\n res.end('Forbidden');\n return;\n }\n\n const ext = path.extname(resolvedPath);\n const contentType = MIME_TYPES[ext] ?? 'application/octet-stream';\n res.setHeader('Content-Type', contentType);\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'DENY');\n res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');\n\n if (ext === '.html') {\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Content-Security-Policy', buildCspHeader(wsPort));\n // Stamp the live WS port into the HTML so the frontend dials this\n // instance's backend (not the hardcoded default) — required for\n // running multiple WebUI instances on different ports.\n const html = await fs.readFile(resolvedPath, 'utf8');\n res.writeHead(200);\n res.end(injectWsPort(html, wsPort));\n return;\n }\n\n const fileContent = await fs.readFile(resolvedPath);\n res.writeHead(200);\n res.end(fileContent);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n // SPA fallback: serve index.html so client-side routing still works.\n try {\n const html = await fs.readFile(path.join(distDir, 'index.html'), 'utf8');\n res.writeHead(200, {\n 'Content-Type': 'text/html',\n 'X-Content-Type-Options': 'nosniff',\n 'X-Frame-Options': 'DENY',\n 'Referrer-Policy': 'strict-origin-when-cross-origin',\n 'Content-Security-Policy': buildCspHeader(wsPort),\n });\n res.end(injectWsPort(html, wsPort));\n } catch {\n res.writeHead(404);\n res.end('Not found');\n }\n } else {\n res.writeHead(500);\n res.end('Server error');\n }\n }\n });\n}\n\n// ── API handlers ─────────────────────────────────────────────────────────\n\nasync function handleApiSessions(\n res: http.ServerResponse,\n globalRoot: string | undefined,\n): Promise<void> {\n if (!globalRoot) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'SessionRegistry not available' }));\n return;\n }\n\n try {\n const { SessionRegistry } = await import('@wrongstack/core');\n const registry = new SessionRegistry(globalRoot);\n const sessions = await registry.list();\n\n const result = sessions.map((s) => ({\n sessionId: s.sessionId,\n projectSlug: s.projectSlug,\n projectName: s.projectName,\n projectRoot: s.projectRoot,\n workingDir: s.workingDir,\n status: s.status,\n pid: s.pid,\n startedAt: s.startedAt,\n lastHeartbeatAt: s.lastHeartbeatAt,\n agentCount: s.agentCount,\n agents: s.agents.map((a) => ({\n id: a.id,\n name: a.name,\n status: a.status,\n currentTool: a.currentTool,\n iterations: a.iterations,\n toolCalls: a.toolCalls,\n lastActivityAt: a.lastActivityAt,\n })),\n }));\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(result));\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(err) }));\n }\n}\n\nasync function handleApiSessionAgents(\n res: http.ServerResponse,\n globalRoot: string | undefined,\n sessionId: string,\n): Promise<void> {\n if (!globalRoot) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'SessionRegistry not available' }));\n return;\n }\n\n try {\n const { SessionRegistry } = await import('@wrongstack/core');\n const registry = new SessionRegistry(globalRoot);\n const entry = await registry.get(sessionId);\n\n if (!entry) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Session not found' }));\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({\n sessionId: entry.sessionId,\n projectName: entry.projectName,\n status: entry.status,\n agents: entry.agents.map((a) => ({\n id: a.id,\n name: a.name,\n status: a.status,\n currentTool: a.currentTool,\n iterations: a.iterations,\n toolCalls: a.toolCalls,\n lastActivityAt: a.lastActivityAt,\n })),\n }));\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(err) }));\n }\n}\n","/**\n * WebSocket connection authentication for the WebUI server.\n *\n * Three layered defenses, all enforced in {@link verifyClient}:\n * 1. **DNS-rebinding guard** ({@link hostHeaderOk}) — on a loopback bind the\n * `Host` header must itself be a loopback name, so a rebound attacker page\n * (`Host: evil.com`) is rejected even though its TCP peer is 127.0.0.1.\n * 2. **Shared-token auth** ({@link tokenMatches}, constant-time) — required for\n * any non-loopback origin and for non-browser clients reaching a publicly\n * bound socket. Tokens are accepted via `Cookie: ws_token=…` (preferred;\n * set by the `/ws-auth` HTTP endpoint with HttpOnly+SameSite=Strict) OR\n * `?token=…` URL query param (non-browser fallback).\n * 3. **Loopback bootstrap** — same-machine browser origins are allowed without\n * a token; the Host-header guard above already blocks cross-site pages.\n *\n * Browser clients (those that send an `Origin` header) MAY use either the\n * cookie path (preferred; set via `/ws-auth`) or the URL-token path\n * (legacy, still accepted for backward compat while the frontend migrates).\n * Once the frontend is updated to call `/ws-auth` on startup, the URL\n * token path can be removed entirely, closing the C-598\n * (Information Exposure Through Query String) class. Non-browser\n * clients always use the URL-token path for ergonomics (curl, scripts,\n * tests).\n *\n * Extracted from `index.ts` as pure functions so the auth contract can be unit\n * tested without standing up a real `http.Server`/`WebSocketServer`. `index.ts`\n * builds a thin closure that pulls the fields below off the incoming request.\n */\nimport { Buffer } from 'node:buffer';\nimport { timingSafeEqual } from 'node:crypto';\n\n/** A hostname that refers to the local machine. */\nexport function isLoopbackHostname(hostname: string): boolean {\n return (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '::1' ||\n hostname === '[::1]'\n );\n}\n\n/**\n * Check if an origin is a trusted loopback browser origin.\n * Defense-in-depth: when wsHost=0.0.0.0, only accept explicit localhost origins,\n * not arbitrary loopback hostnames that could be spoofed by local malware.\n */\nfunction isTrustedLoopbackOrigin(origin: string): boolean {\n try {\n const url = new URL(origin);\n // Only allow http(s)://localhost(:PORT) and http(s)://127.0.0.1(:PORT)\n // Reject file://, data://, and other schemes even on loopback.\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return false;\n // Require explicit localhost or 127.0.0.1 (not just any loopback like ::1)\n return url.hostname === 'localhost' || url.hostname === '127.0.0.1';\n } catch {\n return false;\n }\n}\n\n/** True when the server is bound to a loopback interface (vs. LAN/0.0.0.0). */\nexport function isLoopbackBind(wsHost: string): boolean {\n return wsHost === '127.0.0.1' || wsHost === '::1' || wsHost === 'localhost';\n}\n\n/**\n * Constant-time comparison of a provided token against the expected one.\n * A length mismatch short-circuits (lengths aren't secret); equal-length\n * inputs are compared with `timingSafeEqual` so the token can't be recovered\n * byte-by-byte via response timing.\n */\nexport function tokenMatches(provided: string | undefined, expected: string): boolean {\n if (!provided) return false;\n const a = Buffer.from(provided);\n const b = Buffer.from(expected);\n if (a.length !== b.length) return false;\n return timingSafeEqual(a, b);\n}\n\n/** Pull the `token` query param out of a request URL (`/?token=…`). */\nexport function extractToken(url: string): string | undefined {\n const match = url.match(/[?&]token=([^&]+)/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Pull the `ws_token` value out of a Cookie header (`Cookie: ws_token=…`).\n * The WebUI's auth-token cookie is set via `Set-Cookie: ws_token=<token>;\n * HttpOnly; SameSite=Strict; Path=/` from the `/ws-auth` HTTP endpoint. The\n * browser then sends it back automatically on the WS upgrade request —\n * closing the C-598 (Information Exposure Through Query String) class\n * because the token never appears in the URL, browser history, or\n * reverse-proxy access logs.\n *\n * Returns `undefined` if the cookie header is absent or malformed.\n */\nexport function extractTokenFromCookie(cookieHeader: string | string[] | undefined): string | undefined {\n if (!cookieHeader) return undefined;\n const raw = Array.isArray(cookieHeader) ? cookieHeader.join('; ') : cookieHeader;\n for (const part of raw.split(';')) {\n const eq = part.indexOf('=');\n if (eq < 0) continue;\n const name = part.slice(0, eq).trim();\n if (name === 'ws_token') {\n // Cookie values are url-encoded in spec; decode for the constant-time\n // compare downstream. Trim trailing whitespace defensively.\n try {\n return decodeURIComponent(part.slice(eq + 1).trim());\n } catch {\n return part.slice(eq + 1).trim();\n }\n }\n }\n return undefined;\n}\n\n/**\n * DNS-rebinding defense. On a loopback bind, the `Host` header must resolve to\n * a loopback name. When the operator deliberately exposes the socket (wsHost is\n * a LAN/0.0.0.0 address) the Host is legitimately non-loopback, so the guard is\n * skipped and connection auth falls to the token check.\n */\nexport function hostHeaderOk(input: { hostHeader: string | undefined; wsHost: string }): boolean {\n if (!isLoopbackBind(input.wsHost)) return true; // operator opted into wider exposure\n const hostHeader = (input.hostHeader ?? '').trim();\n if (!hostHeader) return false;\n // Strip the port (handle bare host, host:port, and [::1]:port).\n let hostname: string;\n try {\n hostname = new URL(`http://${hostHeader}`).hostname;\n } catch {\n return false;\n }\n return isLoopbackHostname(hostname);\n}\n\nexport interface VerifyClientInput {\n /** Browser `Origin` header, or undefined for non-browser clients. */\n origin?: string | undefined;\n /** Request URL (`req.url`) — carries the `?token=…` query param. */\n url: string;\n /** `Host` header (`req.headers.host`). */\n hostHeader?: string | undefined;\n /** Peer address (`req.socket.remoteAddress`). */\n remoteAddress?: string | undefined;\n /** `Cookie` header (`req.headers.cookie`). Carries `ws_token=…` when the\n * browser went through `/ws-auth` to set the HttpOnly auth cookie. */\n cookieHeader?: string | string[] | undefined;\n /** Host/interface the WS server is bound to. */\n wsHost: string;\n /** The server's generated auth token. */\n expectedToken: string;\n}\n\n/**\n * Decide whether to accept an incoming WebSocket handshake. Pure mirror of the\n * closure previously inlined in `index.ts`; see the module doc for the layered\n * policy. Returns `true` to accept, `false` to reject.\n *\n * Token sources, in priority order:\n * 1. `Cookie: ws_token=…` (browser clients that went through `/ws-auth`)\n * 2. `?token=…` URL query param (non-browser clients: curl, scripts)\n *\n * Browser clients (with an `Origin` header) are restricted to the cookie path —\n * URL token is rejected for them, closing the C-598 query-string token\n * exposure class. Non-browser clients keep the URL-token fallback so curl\n * and tests continue to work.\n */\nexport function verifyClient(input: VerifyClientInput): boolean {\n const { origin, url, hostHeader, remoteAddress, cookieHeader, wsHost, expectedToken } = input;\n const urlToken = extractToken(url ?? '');\n const cookieToken = extractTokenFromCookie(cookieHeader);\n const tokenOk = tokenMatches(urlToken, expectedToken) || tokenMatches(cookieToken, expectedToken);\n\n // DNS-rebinding guard runs first on a loopback bind — independent of token\n // and Origin. Blocks a rebound attacker page (Host = attacker domain) even\n // though the TCP peer is 127.0.0.1.\n if (!hostHeaderOk({ hostHeader, wsHost })) return false;\n\n if (!origin) {\n // Non-browser clients (curl, scripts): require token unless on loopback.\n // When wsHost=0.0.0.0 the server accepts connections from any network\n // interface — a non-loopback peer is denied outright.\n const remoteIp = remoteAddress ?? '';\n const isRemoteLoopback = remoteIp === '127.0.0.1' || remoteIp === '::1';\n if (!isRemoteLoopback && wsHost === '0.0.0.0') return false; // LAN exposure = deny\n return tokenOk || isLoopbackBind(wsHost);\n }\n try {\n const { hostname } = new URL(origin);\n // Loopback browser origins: allow without token only if the origin is\n // explicitly http://localhost or http://127.0.0.1 (defense-in-depth).\n // Reject file://, data://, and other schemes even on loopback.\n if (isLoopbackHostname(hostname)) {\n // For stricter security on 0.0.0.0 binds, require a trusted origin scheme\n if (wsHost === '0.0.0.0' && !isTrustedLoopbackOrigin(origin)) {\n return false;\n }\n return true;\n }\n // Non-loopback browser origin: token is mandatory. Both the cookie\n // path (C-2 fix, preferred — set via /ws-auth) and the URL-token\n // path (legacy, still accepted for backward compat) authenticate\n // the connection. The token in the URL is no longer required —\n // once the frontend is updated to call /ws-auth on startup, it can\n // drop the `?token=` from the WS URL entirely.\n return tokenOk;\n } catch {\n return false;\n }\n}\n","/**\n * Shared file-operation WebSocket handlers for both the standalone WebUI\n * server and the CLI's `--webui` embedded server. Extracted from the\n * duplicated switch cases in `index.ts` and `cli/src/webui-server.ts`.\n *\n * Each function handles the full request→response cycle for one message\n * type. Callers drop them into their switch statement:\n *\n * case 'files.tree': return handleFilesTree(ws, msg, projectRoot);\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { WebSocket } from 'ws';\nimport { atomicWrite } from '@wrongstack/core';\nimport { SKIP_DIRS, isHiddenEntry, rankFiles } from './file-picker.js';\nimport { send, errMessage } from './ws-utils.js';\n\n// ── Type helpers (inlined, no dependence on types.ts) ──\n\ninterface FilesListPayload {\n query?: string | undefined;\n limit?: number | undefined;\n /** Optional directory root for the file list (relative to projectRoot).\n * When set, only files under this directory are returned. */\n path?: string | undefined;\n}\n\ninterface FilesReadPayload {\n filePath: string;\n}\n\ninterface FilesWritePayload {\n filePath: string;\n content: string;\n}\n\n// ── Shared handlers ───────────────────────────────────────────────────\n\n/**\n * Build and send a nested directory tree for the File Explorer.\n *\n * Walks `projectRoot` to depth 10 max, skipping heavyweight dirs\n * (node_modules, .git, dist, …) and dot-entries. Responds with\n * `{ type: 'files.tree', payload: { root, tree } }`.\n */\nexport async function handleFilesTree(\n ws: WebSocket,\n msg: unknown,\n projectRoot: string,\n): Promise<void> {\n interface TreeNode {\n name: string;\n path: string;\n type: 'file' | 'directory';\n children?: TreeNode[];\n }\n\n // Use the optional `path` from the message payload as the tree root.\n // When absent, empty, or \".\", fall back to projectRoot (backward compatible).\n const payload = (msg as { payload?: { path?: string | undefined } }).payload;\n const rawPath = payload?.path?.trim();\n const treeRoot = rawPath && rawPath !== '.'\n ? path.resolve(projectRoot, rawPath)\n : projectRoot;\n\n // Guard: treeRoot must stay inside projectRoot.\n if (!treeRoot.startsWith(projectRoot + path.sep) && treeRoot !== projectRoot) {\n send(ws, {\n type: 'files.tree',\n payload: { root: projectRoot, tree: [], error: 'Path outside project root' },\n });\n return;\n }\n\n // Compute the path prefix so tree paths are always relative to\n // projectRoot (not treeRoot). This ensures double-clicking a file in\n // the explorer sends the correct path to files.read/files.write.\n const pathPrefix = treeRoot === projectRoot\n ? ''\n : (path.relative(projectRoot, treeRoot) + '/').replace(/\\\\/g, '/');\n\n async function buildTree(dir: string, rel: string, depth: number): Promise<TreeNode[]> {\n if (depth > 10) return [];\n let entries: import('node:fs').Dirent[] = [];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n entries.sort((a, b) => {\n if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n const nodes: TreeNode[] = [];\n for (const e of entries) {\n if (isHiddenEntry(e.name)) continue;\n const childRel = rel ? `${rel}/${e.name}` : e.name;\n const childAbs = path.join(dir, e.name);\n // Prepend the workingDir prefix so the path is projectRoot-relative\n const childPath = pathPrefix + childRel;\n if (e.isDirectory()) {\n if (SKIP_DIRS.has(e.name)) continue;\n const children = await buildTree(childAbs, childRel, depth + 1);\n nodes.push({ name: e.name, path: childPath, type: 'directory', children });\n } else if (e.isFile()) {\n nodes.push({ name: e.name, path: childPath, type: 'file' });\n }\n }\n return nodes;\n }\n\n try {\n const tree = await buildTree(treeRoot, '', 0);\n const rootLabel = treeRoot === projectRoot\n ? projectRoot\n : path.relative(projectRoot, treeRoot) || '.';\n send(ws, { type: 'files.tree', payload: { root: rootLabel, tree } });\n } catch (err) {\n const rootLabel = treeRoot === projectRoot\n ? projectRoot\n : path.relative(projectRoot, treeRoot) || '.';\n send(ws, {\n type: 'files.tree',\n payload: { root: rootLabel, tree: [], error: errMessage(err) },\n });\n }\n}\n\n/**\n * Read a file's content for the Monaco editor.\n *\n * Guards against path traversal (`../` escapes). Responds with\n * `{ type: 'files.read', payload: { filePath, content } }`.\n */\nexport async function handleFilesRead(\n ws: WebSocket,\n msg: unknown,\n projectRoot: string,\n): Promise<void> {\n const { filePath } = (msg as { payload: FilesReadPayload }).payload;\n\n // Path traversal guard: resolve and verify the file stays inside projectRoot.\n const resolved = path.resolve(projectRoot, filePath);\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n send(ws, { type: 'files.read', payload: { filePath, content: '', error: 'Forbidden' } });\n return;\n }\n\n try {\n const content = await fs.readFile(resolved, 'utf8');\n send(ws, { type: 'files.read', payload: { filePath, content } });\n } catch (err) {\n send(ws, {\n type: 'files.read',\n payload: { filePath, content: '', error: errMessage(err) },\n });\n }\n}\n\n/**\n * Write file content back to disk (atomic write via tmp + rename).\n *\n * Guards against path traversal. Responds with\n * `{ type: 'files.written', payload: { filePath, success } }`.\n */\nexport async function handleFilesWrite(\n ws: WebSocket,\n msg: unknown,\n projectRoot: string,\n): Promise<void> {\n const { filePath, content } = (msg as { payload: FilesWritePayload }).payload;\n\n // Path traversal guard.\n const resolved = path.resolve(projectRoot, filePath);\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n send(ws, { type: 'files.written', payload: { filePath, success: false, error: 'Forbidden' } });\n return;\n }\n\n try {\n await atomicWrite(resolved, content);\n send(ws, { type: 'files.written', payload: { filePath, success: true } });\n } catch (err) {\n send(ws, {\n type: 'files.written',\n payload: { filePath, success: false, error: errMessage(err) },\n });\n }\n}\n\n/**\n * Lightweight project file picker for the chat `@` mention popup.\n *\n * Walks `projectRoot` (max depth 8), skipping hidden and heavyweight\n * dirs, then fuzzy-ranks results against `query`. Responds with\n * `{ type: 'files.list', payload: { files } }`.\n */\nexport async function handleFilesList(\n ws: WebSocket,\n msg: unknown,\n projectRoot: string,\n): Promise<void> {\n const payload = (msg as { payload?: FilesListPayload }).payload ?? {};\n const limit = payload.limit ?? 50;\n const listRoot = payload.path\n ? path.resolve(projectRoot, payload.path)\n : projectRoot;\n\n // Guard: listRoot must stay inside projectRoot.\n if (!listRoot.startsWith(projectRoot + path.sep) && listRoot !== projectRoot) {\n send(ws, { type: 'files.list', payload: { files: [] } });\n return;\n }\n\n const results: string[] = [];\n\n async function walk(dir: string, rel: string, depth: number): Promise<void> {\n if (depth > 8 || results.length >= 600) return;\n let entries: import('node:fs').Dirent[] = [];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (results.length >= 600) return;\n if (isHiddenEntry(e.name)) continue;\n const childRel = rel ? `${rel}/${e.name}` : e.name;\n if (e.isDirectory()) {\n if (SKIP_DIRS.has(e.name)) continue;\n await walk(path.join(dir, e.name), childRel, depth + 1);\n } else if (e.isFile()) {\n results.push(childRel);\n }\n }\n }\n\n await walk(listRoot, '', 0);\n send(ws, {\n type: 'files.list',\n payload: { files: rankFiles(results, payload.query ?? '', limit) },\n });\n}\n","/**\n * Pure filtering + ranking for the `files.list` project file picker (the chat\n * `@`-mention popup). The directory *walk* stays in index.ts (it's I/O), but\n * the two decisions that shape the result — which entries to hide and how to\n * rank matches — are pure and live here so the scoring weights, depth penalty,\n * and tie-break order can be unit tested. A silently-flipped weight would make\n * the picker feel subtly wrong with nothing to catch it.\n */\n/** Heavyweight build/vcs/dependency dirs the picker never descends into. */\nexport const SKIP_DIRS: ReadonlySet<string> = new Set([\n '.git',\n 'node_modules',\n 'dist',\n 'build',\n '.next',\n '.turbo',\n '.cache',\n 'target',\n 'coverage',\n '.nyc_output',\n 'out',\n '.pnpm-store',\n '.parcel-cache',\n]);\n\n/** Dotfiles/dirs kept despite the hide-dotfiles-by-default rule. */\nconst KEEP_DOTFILES: ReadonlySet<string> = new Set([\n '.wrongstack',\n '.env.example',\n '.gitignore',\n '.eslintrc',\n '.prettierrc',\n]);\n\n/**\n * Whether a directory entry should be hidden from the picker by its name.\n * Dotfiles are hidden by default, except a few commonly-wanted ones.\n */\nexport function isHiddenEntry(name: string): boolean {\n return name.startsWith('.') && !KEEP_DOTFILES.has(name);\n}\n\n/**\n * Rank `paths` against `query` and return up to `limit` paths, best first.\n *\n * Scoring (cheap heuristic, good enough for a picker): exact basename match\n * (100) > basename prefix (60) > path substring (20); non-matches are dropped.\n * Each match is penalized by its path depth so root files sort first. Ties\n * break by lexicographic path. An empty query keeps every path (score 0), so\n * the result is the paths sorted lexicographically, capped to `limit`.\n */\nexport function rankFiles(paths: readonly string[], query: string, limit: number): string[] {\n const q = query.toLowerCase();\n const scored: Array<{ path: string; score: number }> = [];\n for (const p of paths) {\n if (!q) {\n scored.push({ path: p, score: 0 });\n continue;\n }\n const lower = p.toLowerCase();\n const base = lower.split('/').pop() ?? lower;\n let score = 0;\n if (base === q) score = 100;\n else if (base.startsWith(q)) score = 60;\n else if (lower.includes(q)) score = 20;\n else continue;\n // Penalise depth so root files come first.\n score -= p.split('/').length;\n scored.push({ path: p, score });\n }\n scored.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));\n return scored.slice(0, limit).map((s) => s.path);\n}\n","/**\n * Shared WebSocket utilities for both the standalone WebUI server and the\n * CLI's `--webui` embedded server. Extracted from the duplicated `send` /\n * `broadcast` / `sendResult` / `generateAuthToken` patterns that were\n * copy-pasted between `packages/webui/src/server/index.ts` and\n * `packages/cli/src/webui-server.ts`.\n */\nimport { randomBytes } from 'node:crypto';\n// Value import (not `import type`): we reference `WebSocket.OPEN` below, which\n// is a runtime value, not just a type.\nimport { WebSocket } from 'ws';\nimport type { ConnectedClient, WSServerMessage } from './types.js';\n\n/**\n * Send a JSON message to a single WebSocket client.\n * No-op when the socket is not in OPEN state (disconnected / closing).\n */\nexport function send(ws: WebSocket, msg: WSServerMessage): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n}\n\n/**\n * Broadcast a JSON message to every connected client.\n * Swallows per-socket send errors — a client that disconnected between the\n * readyState check and `ws.send()` is cleaned up by its own `close` handler.\n */\nexport function broadcast(\n clients: Map<WebSocket, ConnectedClient>,\n msg: WSServerMessage,\n): void {\n const data = JSON.stringify(msg);\n for (const [ws] of clients) {\n if (ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(data);\n } catch {\n // Client disconnected between the readyState check and the send —\n // let the 'close' handler remove it from the map naturally.\n }\n }\n }\n}\n\n/**\n * Send a success/failure result message (used by key.* and provider.* handlers).\n * The frontend expects `key.operation_result` with `{ success, message }`.\n */\nexport function sendResult(ws: WebSocket, success: boolean, message: string): void {\n send(ws, { type: 'key.operation_result', payload: { success, message } });\n}\n\n/**\n * Extract a human-readable message from an unknown thrown value.\n */\nexport function errMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Generate a cryptographically random WebSocket auth token (hex string).\n * Shared between standalone and CLI-embedded WebUI servers.\n */\nexport function generateAuthToken(): string {\n return randomBytes(16).toString('hex');\n}\n","/**\n * Shared memory-operation WebSocket handlers for both the standalone WebUI\n * server and the CLI's `--webui` embedded server. Extracted from the\n * duplicated switch cases in `index.ts` and `cli/src/webui-server.ts`.\n *\n * Each function handles the full request→response cycle for one message\n * type. Callers drop them into their switch statement:\n *\n * case 'memory.list': return handleMemoryList(ws, memoryStore);\n */\n\nimport type { WebSocket } from 'ws';\nimport type { MemoryStore } from '@wrongstack/core';\nimport { send, sendResult, errMessage } from './ws-utils.js';\n\n// ── Shared handlers ───────────────────────────────────────────────────\n\n/**\n * List all memory entries across all scopes.\n * Responds with `{ type: 'memory.list', payload: { text } }`.\n */\nexport async function handleMemoryList(\n ws: WebSocket,\n memoryStore: MemoryStore,\n): Promise<void> {\n try {\n const text = await memoryStore.readAll();\n send(ws, { type: 'memory.list', payload: { text } });\n } catch (err) {\n send(ws, {\n type: 'memory.list',\n payload: { text: '', error: errMessage(err) },\n });\n }\n}\n\n/**\n * Persist a new memory entry.\n * Responds with `{ type: 'key.operation_result', payload: { success, message } }`.\n */\nexport async function handleMemoryRemember(\n ws: WebSocket,\n msg: unknown,\n memoryStore: MemoryStore,\n): Promise<void> {\n const { text, scope } = (\n msg as {\n payload: {\n text: string;\n scope?: 'project-agents' | 'project-memory' | 'user-memory' | undefined;\n };\n }\n ).payload;\n try {\n await memoryStore.remember(text, scope ?? 'project-memory');\n sendResult(ws, true, 'Saved to memory');\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n}\n\n/**\n * Remove memory entries matching the given text.\n * Responds with `{ type: 'key.operation_result', payload: { success, message } }`.\n */\nexport async function handleMemoryForget(\n ws: WebSocket,\n msg: unknown,\n memoryStore: MemoryStore,\n): Promise<void> {\n const { text, scope } = (\n msg as {\n payload: {\n text: string;\n scope?: 'project-agents' | 'project-memory' | 'user-memory' | undefined;\n };\n }\n ).payload;\n try {\n const removed = await memoryStore.forget(text, scope ?? 'project-memory');\n sendResult(\n ws,\n removed > 0,\n removed > 0\n ? `Removed ${removed} entr${removed === 1 ? 'y' : 'ies'}`\n : 'No matching entries',\n );\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n}\n","import {\n type Config,\n Container,\n DefaultConfigStore,\n DefaultErrorHandler,\n DefaultMemoryStore,\n DefaultModeStore,\n DefaultPermissionPolicy,\n DefaultRetryPolicy,\n DefaultSecretScrubber,\n DefaultSessionStore,\n DefaultSkillLoader,\n DefaultSystemPromptBuilder,\n DefaultTokenCounter,\n type EventBus,\n createStrategyCompactor,\n buildRecoveryStrategies,\n type Logger,\n type ModelsRegistry,\n TOKENS,\n type Tool,\n type WstackPaths,\n} from '@wrongstack/core';\nimport type { DefaultSystemPromptBuilderOptions } from '@wrongstack/core';\n\nexport interface CreateContainerOptions {\n config: Config;\n wpaths: WstackPaths;\n logger: Logger;\n modelsRegistry: ModelsRegistry;\n /**\n * Optional event bus — passed to DefaultMemoryStore so plugins and\n * subsystems can react to memory mutations in real time.\n */\n events?: EventBus | undefined;\n permission?: {\n yolo?: boolean | undefined;\n yoloDestructive?: boolean | undefined;\n /** @deprecated Use `yoloDestructive`. */\n forceAllYolo?: boolean | undefined;\n /** When true, destructive ops prompt even in YOLO mode. */\n confirmDestructive?: boolean | undefined;\n promptDelegate?: (\n tool: Tool,\n input: unknown,\n suggestedPattern: string,\n ) => Promise<'yes' | 'no' | 'always' | 'deny'>;\n };\n compactor?: { preserveK?: number | undefined; eliseThreshold?: number | undefined };\n systemPrompt?: Partial<DefaultSystemPromptBuilderOptions> | undefined;\n /** Bundled skills directory path (resolved at boot time). */\n bundledSkillsDir?: string | undefined;\n}\n\n/**\n * Create a Container pre-bound with all default service implementations.\n * Both CLI and WebUI use this factory so container wiring stays in one place.\n */\nexport function createDefaultContainer(opts: CreateContainerOptions): Container {\n const { config, wpaths, logger, modelsRegistry } = opts;\n const container = new Container();\n\n const configStore = new DefaultConfigStore(config);\n container.bind(TOKENS.ConfigStore, () => configStore);\n container.bind(TOKENS.Logger, () => logger);\n container.bind(TOKENS.SecretScrubber, () => new DefaultSecretScrubber());\n container.bind(TOKENS.RetryPolicy, () => new DefaultRetryPolicy());\n // Wire the compactor into the recovery chain so a 413 / context-overflow\n // response can shed tokens and retry. Without an explicit compactor the\n // default `context_overflow_reduce` strategy is a no-op. The Compactor\n // binding is resolved lazily (it is registered further below).\n container.bind(\n TOKENS.ErrorHandler,\n () =>\n new DefaultErrorHandler(\n buildRecoveryStrategies({\n compactor: container.resolve(TOKENS.Compactor),\n modelsRegistry,\n }),\n ),\n );\n container.bind(TOKENS.ModelsRegistry, () => modelsRegistry);\n container.bind(\n TOKENS.TokenCounter,\n () => new DefaultTokenCounter({ registry: modelsRegistry, providerId: config.provider }),\n );\n\n const modeStore = new DefaultModeStore({ directory: wpaths.configDir });\n container.bind(TOKENS.ModeStore, () => modeStore);\n container.bind(\n TOKENS.SessionStore,\n () =>\n new DefaultSessionStore({\n dir: wpaths.projectSessions,\n // Scrub secrets out of persisted user/model turns (F-06). Tool output\n // is already scrubbed by the executor.\n secretScrubber: container.resolve(TOKENS.SecretScrubber),\n }),\n );\n\n const memoryStore = new DefaultMemoryStore({ paths: wpaths, events: opts.events });\n container.bind(TOKENS.MemoryStore, () => memoryStore);\n\n const skillLoader = new DefaultSkillLoader({ paths: wpaths, bundledDir: opts.bundledSkillsDir });\n container.bind(TOKENS.SkillLoader, () => skillLoader);\n\n if (opts.systemPrompt) {\n container.bind(\n TOKENS.SystemPromptBuilder,\n () => new DefaultSystemPromptBuilder(opts.systemPrompt as DefaultSystemPromptBuilderOptions),\n );\n }\n\n container.bind(\n TOKENS.PermissionPolicy,\n () => {\n const policyOptions: ConstructorParameters<typeof DefaultPermissionPolicy>[0] = {\n trustFile: wpaths.projectTrust,\n yolo: opts.permission?.yolo ?? false,\n yoloDestructive: opts.permission?.yoloDestructive ?? opts.permission?.forceAllYolo ?? false,\n confirmDestructive: opts.permission?.confirmDestructive ?? false,\n };\n if (opts.permission?.promptDelegate !== undefined) {\n policyOptions.promptDelegate = opts.permission.promptDelegate;\n }\n return new DefaultPermissionPolicy(policyOptions);\n },\n );\n\n container.bind(\n TOKENS.Compactor,\n () =>\n // Strategy comes from config.context.strategy: 'hybrid' (default, lossless\n // rules, no LLM), 'intelligent' (LLM summarization), or 'selective'\n // (LLM-driven selection). The LLM strategies resolve their provider from\n // ctx at compact()-time, so binding here (before context.provider exists)\n // is safe. preserveK / eliseThreshold are class-level fallbacks; the active\n // ContextWindowPolicy in ctx.meta normally overrides both at runtime.\n // eliseThreshold is a TOKEN COUNT — a previous value of 0.7 elided\n // essentially every tool_result (anything > 1 token).\n createStrategyCompactor({\n strategy: config.context?.strategy,\n preserveK: opts.compactor?.preserveK ?? 10,\n eliseThreshold: opts.compactor?.eliseThreshold ?? 2000,\n smart: true,\n summarizerModel: config.context?.summarizerModel,\n llmSelector: config.context?.llmSelector,\n }),\n );\n\n return container;\n}\n","import {\n type Config,\n type DefaultLogger,\n type DefaultSecretVault,\n type WstackPaths,\n bootConfig as coreBootConfig,\n} from '@wrongstack/core';\n\nexport interface BootResult {\n config: Config;\n vault: DefaultSecretVault;\n globalConfigPath: string;\n projectRoot: string;\n wpaths: WstackPaths;\n logger: InstanceType<typeof DefaultLogger>;\n}\n\n/**\n * Thin WebUI wrapper over the canonical `bootConfig` in `@wrongstack/core`\n * (mirrors packages/cli/src/boot-config.ts). All real boot behavior — wstack\n * path resolution, the AES-GCM `DefaultSecretVault`, plaintext-secret\n * migration, and config load/merge — lives in core so the WebUI server and the\n * CLI can't drift. Only the secret-migration notice label (`WebUI`) differs.\n */\nexport async function bootConfig(): Promise<BootResult> {\n const { config, vault, globalConfigPath, projectRoot, wpaths, logger } = await coreBootConfig({\n appLabel: 'WebUI',\n });\n return { config, vault, globalConfigPath, projectRoot, wpaths, logger };\n}\n\nexport function patchConfig(config: Config, updates: Partial<Config>): Config {\n return Object.freeze({ ...config, ...updates });\n}\n","import { spawnSync } from 'node:child_process';\nimport type { WebSocket } from 'ws';\nimport {\n AutoPhasePlanner,\n PhaseGraphBuilder,\n PhaseOrchestrator,\n PhaseStore,\n WorktreeManager,\n type PhaseGraph,\n type PhaseTemplate,\n} from '@wrongstack/core';\nimport type { Agent, Context, EventBus, Logger } from '@wrongstack/core';\n\nfunction isGitRepo(cwd: string): boolean {\n try {\n const r = spawnSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd, encoding: 'utf8', windowsHide: true });\n return r.status === 0 && r.stdout.trim() === 'true';\n } catch {\n return false;\n }\n}\n\ninterface WSClient {\n ws: WebSocket;\n id: string;\n}\n\ninterface AutoPhaseWSMessage {\n type: string;\n payload?: Record<string, unknown>;\n}\n\n/**\n * AutoPhaseWebSocketHandler — WebSocket-based AutoPhase control.\n *\n * Message types:\n * autophase.start → { title, phases?, autonomous? }\n * autophase.pause → {}\n * autophase.resume → {}\n * autophase.stop → {}\n * autophase.status → {}\n * autophase.selectPhase → { phaseId }\n * autophase.taskStatus → { taskId, status }\n */\nexport class AutoPhaseWebSocketHandler {\n private orchestrator: PhaseOrchestrator | null = null;\n private graph: PhaseGraph | null = null;\n private store: PhaseStore;\n private clients = new Set<WSClient>();\n private broadcastInterval: ReturnType<typeof setInterval> | null = null;\n /** Aborts in-flight task agents when the run is stopped. */\n private abort: AbortController | null = null;\n /** Optional per-phase git-worktree isolation (lazily created at start). */\n private worktrees: WorktreeManager | null = null;\n\n constructor(\n private agent: Agent,\n private context: Context,\n private logger: Logger,\n storeDir: string,\n private events?: EventBus | undefined,\n private projectRoot?: string | undefined,\n ) {\n this.store = new PhaseStore({ baseDir: storeDir });\n }\n\n addClient(ws: WebSocket): void {\n const client: WSClient = { ws, id: crypto.randomUUID() };\n this.clients.add(client);\n\n ws.on('close', () => this.clients.delete(client));\n ws.on('error', () => this.clients.delete(client));\n\n // Send current state\n this.sendState(client);\n }\n\n async handleMessage(msg: AutoPhaseWSMessage): Promise<void> {\n switch (msg.type) {\n case 'autophase.start':\n await this.handleStart(msg.payload);\n break;\n case 'autophase.pause':\n this.orchestrator?.pause();\n this.broadcast({ type: 'autophase.paused', payload: {} });\n break;\n case 'autophase.resume':\n this.orchestrator?.resume();\n this.broadcast({ type: 'autophase.resumed', payload: {} });\n break;\n case 'autophase.stop':\n this.abort?.abort();\n this.orchestrator?.stop();\n this.stopBroadcast();\n if (this.graph) void this.store.save(this.graph);\n this.broadcast({ type: 'autophase.stopped', payload: {} });\n break;\n case 'autophase.status':\n this.broadcastState();\n break;\n case 'autophase.selectPhase': {\n const phaseId = msg.payload?.phaseId as string;\n if (phaseId && this.graph) {\n this.broadcastState(phaseId);\n }\n break;\n }\n case 'autophase.taskStatus': {\n const { taskId, status } = msg.payload as { taskId: string; status: string };\n await this.handleTaskStatusChange(taskId, status);\n break;\n }\n case 'autophase.toggleAutonomous': {\n const autonomous = (msg.payload?.autonomous as boolean) ?? !this.graph?.autonomous;\n if (this.graph) {\n this.graph.autonomous = autonomous;\n await this.store.save(this.graph);\n this.broadcast({ type: 'autophase.state', payload: this.buildState() });\n }\n break;\n }\n case 'autophase.save': {\n if (this.graph) {\n await this.store.save(this.graph);\n this.broadcast({ type: 'autophase.saved', payload: { graphId: this.graph.id } });\n }\n break;\n }\n case 'autophase.list': {\n const graphs = await this.store.list();\n this.broadcast({ type: 'autophase.list', payload: { graphs } });\n break;\n }\n case 'autophase.load': {\n const graphId = msg.payload?.graphId as string | undefined;\n if (graphId) {\n const graph = await this.store.load(graphId);\n if (graph) {\n this.graph = graph;\n this.broadcast({ type: 'autophase.state', payload: this.buildState() });\n } else {\n this.broadcast({ type: 'autophase.error', payload: { message: `Graph not found: ${graphId}` } });\n }\n }\n break;\n }\n }\n }\n\n private async handleStart(payload?: Record<string, unknown>): Promise<void> {\n const title = (payload?.goal as string) || (payload?.title as string) || 'Untitled Project';\n const autonomous = (payload?.autonomous as boolean) ?? true;\n\n // Phase plan resolution:\n // 1. explicit phases in the payload win (caller override);\n // 2. otherwise the LLM plans phases+todos for the goal;\n // 3. failing that, fall back to the generic default phases.\n const phases = Array.isArray(payload?.phases)\n ? (payload.phases as PhaseTemplate[])\n : await this.planPhases(title);\n\n this.logger.info(`[AutoPhase] Starting: ${title}`);\n\n // Build the graph up-front so we have a reference for live broadcasts and\n // persistence *before* the (long-running) build begins.\n const graph = await new PhaseGraphBuilder({ title, phases, autonomous }).build();\n this.graph = graph;\n this.abort = new AbortController();\n await this.store.save(graph);\n\n // Per-phase git-worktree isolation, when enabled and inside a git repo.\n // The shared agent/context means we can't run phases in parallel here\n // (we swap a single context.cwd per task), so phases stay sequential —\n // but each phase still commits + squash-merges back through its own\n // worktree, and the lifecycle events drive the live swim-lane/DAG view.\n if (\n !this.worktrees &&\n this.events &&\n this.projectRoot &&\n process.env['WRONGSTACK_AUTOPHASE_WORKTREES'] !== '0' &&\n isGitRepo(this.projectRoot)\n ) {\n this.worktrees = new WorktreeManager({ projectRoot: this.projectRoot, events: this.events });\n }\n\n this.orchestrator = new PhaseOrchestrator({\n graph,\n ctx: {\n executeTask: async (task, phaseId, env) => {\n this.logger.info(`[AutoPhase] [${phaseId}] Executing: ${task.title}`);\n const result = await this.executeTaskWithAgent(task, phaseId, env);\n this.logger.info(`[AutoPhase] [${phaseId}] Completed: ${task.title}`);\n return result;\n },\n onPhaseComplete: (phase) => {\n this.logger.info(`[AutoPhase] Phase completed: ${phase.name}`);\n void this.store.save(graph);\n this.broadcastState();\n },\n onPhaseFail: (phase, error) => {\n this.logger.error(`[AutoPhase] Phase failed: ${phase.name} — ${error.message}`);\n void this.store.save(graph);\n this.broadcastState();\n },\n },\n worktrees: this.worktrees ?? undefined,\n autonomous,\n // Must stay 1: phase tasks run on the single shared context whose cwd we\n // swap per phase, so parallel phases would race on context.cwd.\n maxConcurrentPhases: 1,\n // Sequential within a phase: each todo is a full-tool agent editing the\n // phase worktree, so running two at once risks concurrent writes.\n maxConcurrentTasks: 1,\n });\n\n // Start the live broadcast immediately, then run the orchestrator in the\n // background. Awaiting start() would block until the *entire* build\n // finishes — the periodic broadcast (below) reads the mutating graph, so\n // clients see live progress while it runs.\n this.startBroadcast();\n this.broadcastState();\n\n void this.orchestrator\n .start()\n .then(() => {\n this.orchestrator?.stop(); // clear the autonomous tick interval\n void this.store.save(graph);\n this.stopBroadcast();\n const failed = graph.failedPhaseIds.length > 0;\n this.broadcast(\n failed\n ? { type: 'autophase.failed', payload: { title } }\n : { type: 'autophase.completed', payload: { title } },\n );\n this.broadcastState();\n })\n .catch((err: unknown) => {\n this.logger.error(`[AutoPhase] Aborted: ${err instanceof Error ? err.message : String(err)}`);\n this.stopBroadcast();\n this.broadcast({ type: 'autophase.failed', payload: { title, error: String(err) } });\n });\n }\n\n /** Generic fallback phases when the LLM planner produces nothing usable. */\n private defaultPhases(): PhaseTemplate[] {\n return [\n { name: 'Discovery', description: 'Requirements gathering', priority: 'high', estimateHours: 2, parallelizable: false },\n { name: 'Design', description: 'Architecture and design', priority: 'critical', estimateHours: 4, parallelizable: false },\n { name: 'Implementation', description: 'Core development', priority: 'critical', estimateHours: 12, parallelizable: false },\n { name: 'Testing', description: 'Unit and integration tests', priority: 'high', estimateHours: 6, parallelizable: true },\n { name: 'Deployment', description: 'Deploy to production', priority: 'medium', estimateHours: 2, parallelizable: false },\n ];\n }\n\n /** Plan phases+todos for the goal via the LLM; fall back to defaults on failure. */\n private async planPhases(goal: string): Promise<PhaseTemplate[]> {\n try {\n const planner = new AutoPhasePlanner({\n goal,\n runOnce: async (prompt) => {\n const result = (await this.agent.run(prompt, { signal: new AbortController().signal })) as {\n status: string;\n finalText?: string | undefined;\n };\n return result.status === 'done' ? (result.finalText ?? '') : '';\n },\n });\n const { phases, parseFailed } = await planner.plan();\n if (!parseFailed && phases.length > 0) {\n const todos = phases.reduce((n, p) => n + (p.taskTemplates?.length ?? 0), 0);\n this.logger.info(`[AutoPhase] Planned ${phases.length} phases / ${todos} todos for: ${goal}`);\n return phases;\n }\n this.logger.info(`[AutoPhase] Planner produced no phases; using defaults for: ${goal}`);\n } catch (err) {\n this.logger.error(`[AutoPhase] Planning failed, using defaults: ${err instanceof Error ? err.message : String(err)}`);\n }\n return this.defaultPhases();\n }\n\n private async executeTaskWithAgent(\n task: import('@wrongstack/core').TaskNode,\n phaseId: string,\n env?: { cwd?: string | undefined; branch?: string | undefined },\n ): Promise<unknown> {\n // Execute task with agent\n const prompt = `Execute task: ${task.title}\\n\\nDescription: ${task.description}\\nPhase: ${phaseId}\\nPriority: ${task.priority}\\nType: ${task.type}`;\n const signal = this.abort?.signal ?? new AbortController().signal;\n // Redirect the shared context's cwd at the phase worktree for the duration\n // of this task. Safe because phases/tasks run strictly sequentially here;\n // tools read `ctx.cwd` live, so the agent operates inside the worktree.\n const prevCwd = this.context.cwd;\n if (env?.cwd) this.context.cwd = env.cwd;\n try {\n return await this.agent.run(prompt, { signal });\n } finally {\n this.context.cwd = prevCwd;\n }\n }\n\n private async handleTaskStatusChange(taskId: string, status: string): Promise<void> {\n if (!this.graph) return;\n\n for (const phase of this.graph.phases.values()) {\n const task = phase.taskGraph.nodes.get(taskId);\n if (task) {\n task.status = status as import('@wrongstack/core').TaskStatus;\n task.updatedAt = Date.now();\n this.broadcastState();\n return;\n }\n }\n }\n\n private startBroadcast(): void {\n if (this.broadcastInterval) return;\n this.broadcastInterval = setInterval(() => {\n const progress = this.orchestrator?.getProgress();\n if (progress) this.broadcast({ type: 'autophase.progress', payload: progress });\n this.broadcastState();\n }, 2000);\n }\n\n private stopBroadcast(): void {\n if (this.broadcastInterval) {\n clearInterval(this.broadcastInterval);\n this.broadcastInterval = null;\n }\n }\n\n private broadcastState(activePhaseId?: string): void {\n if (!this.graph) return;\n\n const state = this.buildState(activePhaseId);\n this.broadcast({ type: 'autophase.state', payload: state });\n }\n\n private buildState(activePhaseId?: string): Record<string, unknown> {\n if (!this.graph) {\n return { phases: [], tasks: [], overallPercent: 0, autonomous: true, title: '' };\n }\n\n const phases = Array.from(this.graph.phases.values());\n const currentActiveId = activePhaseId || phases.find((p) => p.status === 'running')?.id || phases[0]?.id || '';\n const activePhase = this.graph.phases.get(currentActiveId);\n\n const totalTasks = phases.reduce((sum, p) => sum + p.taskGraph.nodes.size, 0);\n const completedTasks = phases.reduce(\n (sum, p) => sum + Array.from(p.taskGraph.nodes.values()).filter((t) => t.status === 'completed').length,\n 0,\n );\n\n const phaseItems = phases.map((p) => ({\n id: p.id,\n name: p.name,\n description: p.description,\n status: p.status,\n priority: p.priority,\n estimateHours: p.estimateHours,\n actualDurationMs: p.actualDurationMs,\n startedAt: p.startedAt,\n completedAt: p.completedAt,\n progressPercent: p.taskGraph.nodes.size > 0\n ? Math.round((Array.from(p.taskGraph.nodes.values()).filter((t) => t.status === 'completed').length / p.taskGraph.nodes.size) * 100)\n : 0,\n taskCount: p.taskGraph.nodes.size,\n completedTasks: Array.from(p.taskGraph.nodes.values()).filter((t) => t.status === 'completed').length,\n assignedAgents: p.assignedAgents,\n isActive: p.id === currentActiveId,\n }));\n\n const taskItems = activePhase\n ? Array.from(activePhase.taskGraph.nodes.values()).map((t) => ({\n id: t.id,\n title: t.title,\n description: t.description,\n status: t.status,\n priority: t.priority,\n type: t.type,\n estimateHours: t.estimateHours,\n actualHours: t.actualHours,\n assignee: t.assignee,\n tags: t.tags || [],\n startedAt: t.startedAt,\n completedAt: t.completedAt,\n }))\n : [];\n\n const completedPhases = phases.filter((p) => p.status === 'completed').length;\n\n return {\n title: this.graph.title,\n phases: phaseItems,\n tasks: taskItems,\n activePhaseId: currentActiveId,\n overallPercent: phases.length > 0 ? Math.round((completedPhases / phases.length) * 100) : 0,\n autonomous: this.graph.autonomous,\n totalTasks,\n completedTasks,\n };\n }\n\n private sendState(client: WSClient): void {\n if (!this.graph) return;\n const state = this.buildState();\n this.send(client, { type: 'autophase.state', payload: state });\n }\n\n private broadcast(msg: { type: string; payload: unknown }): void {\n const data = JSON.stringify(msg);\n for (const client of this.clients) {\n if (client.ws.readyState === 1) { // OPEN\n client.ws.send(data);\n }\n }\n }\n\n private send(client: WSClient, msg: { type: string; payload: unknown }): void {\n if (client.ws.readyState === 1) {\n client.ws.send(JSON.stringify(msg));\n }\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport type { WebSocket } from 'ws';\nimport type { CollaborationBus, EventBus, Logger } from '@wrongstack/core';\nimport type { AnnotationsStore, SessionReader } from '@wrongstack/core/storage';\nimport type {\n CollabRole,\n WSCollabParticipantJoined,\n WSCollabParticipantLeft,\n WSCollabState,\n WSServerMessage,\n} from '../types.js';\n\n/** How many historical events to replay to a late-joining observer. */\nconst REPLAY_LIMIT = 50;\n\n/** How long the middleware waits before auto-resuming (mirrors the middleware default). */\nconst PAUSE_TIMEOUT_MS = 60_000;\n\n/**\n * CollaborationWebSocketHandler — passive read-only session observer (Phase 1\n * of idea #13 from IDEAS.md). Mirrors `WorktreeWebSocketHandler` and\n * `AutoPhaseWebSocketHandler`.\n *\n * Capabilities in this phase:\n * - A second human (or any client) joins an active agent run as an\n * `observer` and receives a live mirror of the kernel's iteration /\n * tool / subagent events.\n * - The observer declares a `sessionId` on join (used for state scoping\n * and future replay-on-join). Live event routing is session-agnostic\n * for now — see the limitation note below.\n * - The observer can leave at any time; cleanup runs on WS close/error.\n * - The observer CANNOT modify the agent's state, pause it, or inject\n * tool calls. Those capabilities land in Phase 2/3.\n *\n * Limitation (documented, acceptable for Phase 1):\n * The webui server multiplexes every active session onto a single\n * EventBus, and most event payloads (`tool.started`, `iteration.*`,\n * `subagent.*`) do NOT carry a `sessionId` field. The webui's primary\n * WS path works because it is the only consumer and assumes one\n * active session at a time. We mirror that assumption here. When a\n * future multi-session \"session router\" lands, this handler will be\n * upgraded to filter by sessionId.\n *\n * Protocol additions (see `packages/webui/src/types.ts`):\n * client → server: collab.join { sessionId, role: 'observer' }\n * collab.leave { sessionId }\n * server → client: collab.state (initial + 2s periodic)\n * collab.participant.joined\n * collab.participant.left\n * collab.event (live kernel event mirror)\n */\nexport class CollaborationWebSocketHandler {\n private readonly clients = new Set<WebSocket>();\n /** sessionId → participants currently watching it. */\n private readonly bySession = new Map<string, Set<Participant>>();\n private broadcastInterval: ReturnType<typeof setInterval> | null = null;\n private readonly offs: Array<() => void> = [];\n\n constructor(\n private readonly events: EventBus,\n private readonly logger: Logger,\n /**\n * Optional reader over the on-disk session log. When provided, late\n * joiners receive the last `REPLAY_LIMIT` events of the joined\n * session before live mirroring begins. Without a reader, joining\n * is still allowed — the observer simply starts from \"now\" with no\n * historical context.\n */\n private readonly reader?: SessionReader | undefined,\n /**\n * Optional sidecar store for collaboration annotations. Required\n * for the `annotator` role — without it, `collab.annotate` messages\n * are rejected with an error.\n */\n private readonly annotations?: AnnotationsStore | undefined,\n /**\n * Optional kernel-level pause/resume bus. Required for the\n * `controller` role — without it, `collab.request_pause` is rejected\n * with an error. Wired to the agent's `toolCall` pipeline via\n * `collabPauseMiddleware` in the webui server boot.\n */\n private readonly bus?: CollaborationBus | undefined,\n ) {\n this.subscribe();\n }\n\n // ── Public API (called by server/index.ts per WS connection) ───────────\n\n addClient(ws: WebSocket): void {\n this.clients.add(ws);\n this.ensureBroadcast();\n ws.on('close', () => this.handleDisconnect(ws));\n ws.on('error', () => this.handleDisconnect(ws));\n }\n\n dispose(): void {\n for (const off of this.offs) off();\n this.offs.length = 0;\n this.stopBroadcast();\n }\n\n // ── Inbound client messages ────────────────────────────────────────────\n\n /**\n * Dispatch a parsed client message. Returns true when the message was\n * recognized and handled; false when the caller should ignore / log.\n * Phase 1 only knows `collab.join` and `collab.leave`; unknown types\n * return false so the upstream router can decide.\n */\n handleMessage(\n ws: WebSocket,\n msg: { type: string; payload?: unknown | undefined },\n ): boolean {\n if (msg.type === 'collab.join') {\n const payload = msg.payload as { sessionId?: string | undefined; role?: CollabRole | undefined } | undefined;\n if (!payload?.sessionId) {\n this.send(ws, this.errorMessage('collab.join requires sessionId'));\n return true;\n }\n // The `role` field is accepted on the wire for forward-compat;\n // 'controller' (Phase 3) is not yet wired and is rejected here.\n this.join(ws, payload.sessionId, payload.role ?? 'observer');\n return true;\n }\n if (msg.type === 'collab.leave') {\n this.leave(ws);\n return true;\n }\n if (msg.type === 'collab.annotate') {\n void this.handleAnnotate(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.resolve') {\n void this.handleResolve(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.request_pause') {\n void this.handleRequestPause(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.resume') {\n void this.handleResume(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.grant_control') {\n void this.handleGrantControl(ws, msg.payload);\n return true;\n }\n if (msg.type === 'collab.inject_tool') {\n void this.handleInjectTool(ws, msg.payload);\n return true;\n }\n return false;\n }\n\n // ── Join / leave flow ──────────────────────────────────────────────────\n\n private join(ws: WebSocket, sessionId: string, role: CollabRole): void {\n if (role === 'controller' && !this.bus) {\n this.send(\n ws,\n this.errorMessage(\n `role 'controller' is not available: server has no CollaborationBus`,\n ),\n );\n return;\n }\n if (role === 'annotator' && !this.annotations) {\n this.send(\n ws,\n this.errorMessage(\n `role 'annotator' is not available: server has no annotations store`,\n ),\n );\n return;\n }\n const participant: Participant = {\n participantId: randomUUID(),\n ws,\n sessionId,\n role,\n joinedAt: new Date().toISOString(),\n };\n let bucket = this.bySession.get(sessionId);\n if (!bucket) {\n bucket = new Set();\n this.bySession.set(sessionId, bucket);\n }\n bucket.add(participant);\n\n // Per-participant hello: send the current state snapshot immediately\n // so the new observer knows who else is watching. Then broadcast the\n // join event AND a fresh state to every participant (including the\n // newcomer) so existing observers see the updated count without\n // waiting for the 2s timer.\n this.send(ws, this.stateMessage(sessionId));\n this.broadcast(sessionId, {\n type: 'collab.participant.joined',\n payload: {\n participantId: participant.participantId,\n sessionId,\n role,\n joinedAt: participant.joinedAt,\n },\n });\n this.broadcast(sessionId, this.stateMessage(sessionId));\n\n // Replay last N events to give the late joiner historical context.\n // Best-effort: failures are logged and silently ignored — the live\n // mirror continues regardless.\n if (this.reader) {\n this.replayHistory(ws, sessionId).catch((err) => {\n this.logger.debug?.(\n `collab: replay failed for ${sessionId}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n });\n }\n this.logger.debug?.(\n `collab: participant ${participant.participantId} joined ${sessionId}`,\n );\n }\n\n private leave(ws: WebSocket): void {\n this.handleDisconnect(ws);\n }\n\n private handleDisconnect(ws: WebSocket): void {\n this.clients.delete(ws);\n // Remove from every session bucket the WS may have joined (a single\n // WS is in at most one bucket in Phase 1, but the loop is cheap and\n // future-proofs multi-session observers).\n //\n // Order matters:\n // 1. Send `participant.left` to the leaving ws so they get a\n // confirmation that their leave registered.\n // 2. Delete from bucket.\n // 3. Broadcast the fresh state to remaining observers so they\n // see the updated count without waiting for the 2s timer.\n for (const [sessionId, bucket] of this.bySession) {\n for (const p of bucket) {\n if (p.ws === ws) {\n const leftEvent = {\n type: 'collab.participant.left' as const,\n payload: { participantId: p.participantId, sessionId },\n };\n // Send directly to the leaving ws first so they get an\n // immediate confirmation, then broadcast to the rest of the\n // bucket (which is still inclusive of the leaving ws here —\n // the per-iteration below strips it out).\n this.send(ws, leftEvent);\n bucket.delete(p);\n if (bucket.size === 0) {\n this.bySession.delete(sessionId);\n } else {\n this.broadcast(sessionId, leftEvent);\n this.broadcast(sessionId, this.stateMessage(sessionId));\n }\n break;\n }\n }\n }\n if (this.bySession.size === 0) this.stopBroadcast();\n }\n\n // ── Annotation flow (Phase 2) ───────────────────────────────────────────\n\n /**\n * Look up the participant record for a given WS across all sessions.\n * Returns null when the WS hasn't joined (e.g. the client sent a\n * `collab.annotate` before `collab.join`).\n */\n private findParticipant(ws: WebSocket): Participant | null {\n for (const bucket of this.bySession.values()) {\n for (const p of bucket) {\n if (p.ws === ws) return p;\n }\n }\n return null;\n }\n\n private async handleAnnotate(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.annotations) {\n this.send(ws, this.errorMessage('annotations store is not configured'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('annotate requires an active join'));\n return;\n }\n if (participant.role !== 'annotator') {\n this.send(\n ws,\n this.errorMessage(\n `annotate requires the 'annotator' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as\n | { sessionId?: string | undefined; atEventIndex?: number | undefined; text?: string | undefined }\n | undefined;\n if (\n !payload?.sessionId ||\n typeof payload.atEventIndex !== 'number' ||\n typeof payload.text !== 'string'\n ) {\n this.send(\n ws,\n this.errorMessage('annotate requires { sessionId, atEventIndex, text }'),\n );\n return;\n }\n if (payload.sessionId !== participant.sessionId) {\n this.send(\n ws,\n this.errorMessage(\n `annotate sessionId mismatch (joined: ${participant.sessionId})`,\n ),\n );\n return;\n }\n try {\n const annotation = await this.annotations.add({\n sessionId: payload.sessionId,\n atEventIndex: payload.atEventIndex,\n authorId: participant.participantId,\n text: payload.text,\n });\n this.broadcast(payload.sessionId, {\n type: 'collab.annotation.added',\n payload: {\n sessionId: payload.sessionId,\n annotation: {\n id: annotation.id,\n atEventIndex: annotation.atEventIndex,\n authorId: annotation.authorId,\n authorRole: annotation.authorRole,\n text: annotation.text,\n createdAt: annotation.createdAt,\n resolved: annotation.resolved,\n },\n },\n });\n } catch (err) {\n this.send(\n ws,\n this.errorMessage(\n `annotation rejected: ${\n err instanceof Error ? err.message : String(err)\n }`,\n ),\n );\n }\n }\n\n private async handleResolve(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.annotations) {\n this.send(ws, this.errorMessage('annotations store is not configured'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('resolve requires an active join'));\n return;\n }\n if (participant.role !== 'annotator') {\n this.send(\n ws,\n this.errorMessage(\n `resolve requires the 'annotator' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as\n | { sessionId?: string | undefined; annotationId?: string | undefined }\n | undefined;\n if (!payload?.sessionId || !payload.annotationId) {\n this.send(\n ws,\n this.errorMessage('resolve requires { sessionId, annotationId }'),\n );\n return;\n }\n if (payload.sessionId !== participant.sessionId) {\n this.send(\n ws,\n this.errorMessage(\n `resolve sessionId mismatch (joined: ${participant.sessionId})`,\n ),\n );\n return;\n }\n try {\n const updated = await this.annotations.resolve({\n sessionId: payload.sessionId,\n annotationId: payload.annotationId,\n resolvedBy: participant.participantId,\n });\n if (!updated) {\n this.send(\n ws,\n this.errorMessage(`annotation not found: ${payload.annotationId}`),\n );\n return;\n }\n this.broadcast(payload.sessionId, {\n type: 'collab.annotation.resolved',\n payload: {\n sessionId: payload.sessionId,\n annotationId: updated.id,\n resolvedBy: updated.resolvedBy ?? participant.participantId,\n resolvedAt: updated.resolvedAt ?? new Date().toISOString(),\n },\n });\n } catch (err) {\n this.send(\n ws,\n this.errorMessage(\n `resolve failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n ),\n );\n }\n }\n\n // ── Event subscription (live mirror) ───────────────────────────────────\n\n private subscribe(): void {\n // Same trick as WorktreeWebSocketHandler: bind a single typed-on helper\n // to a string-keyed signature so we can register many handlers.\n const on = this.events.on.bind(this.events) as unknown as (\n ev: string,\n fn: (p: unknown) => void,\n ) => () => void;\n\n // Mirror every event an observer would care about. Each is forwarded\n // to all joined participants as a generic `collab.event` envelope so\n // the client can render a flowing activity strip. Filtering /\n // denormalization happens on the client.\n const forwarded: Array<[string, string]> = [\n ['iteration.started', 'iteration.started'],\n ['iteration.completed', 'iteration.completed'],\n ['tool.started', 'tool.started'],\n ['tool.progress', 'tool.progress'],\n ['tool.executed', 'tool.executed'],\n ['tool.confirm_needed', 'tool.confirm_needed'],\n ['subagent.spawned', 'subagent.spawned'],\n ['subagent.task_started', 'subagent.task_started'],\n ['subagent.iteration_summary', 'subagent.iteration_summary'],\n ['subagent.task_completed', 'subagent.task_completed'],\n ['subagent.done', 'subagent.done'],\n ];\n for (const [kernelEvent, kind] of forwarded) {\n this.offs.push(\n on(kernelEvent, (raw) => {\n // Best-effort payload shape: we don't deeply validate, but we\n // make sure it's serializable. Observers must never receive\n // non-serializable objects (Functions, circular refs).\n let payload: unknown = raw;\n try {\n payload = JSON.parse(JSON.stringify(raw));\n } catch {\n // Skip unserializable payloads — better to drop than to crash\n // the broadcast loop.\n return;\n }\n this.broadcastEvent(kind, payload);\n }),\n );\n }\n }\n\n private broadcastEvent(kind: string, payload: unknown): void {\n if (this.bySession.size === 0) return; // nobody watching — no-op\n const msg: WSServerMessage = {\n type: 'collab.event',\n payload: { kind, payload, at: new Date().toISOString() },\n };\n const data = JSON.stringify(msg);\n for (const bucket of this.bySession.values()) {\n for (const p of bucket) {\n try {\n if (p.ws.readyState === 1) p.ws.send(data);\n } catch (err) {\n this.logger.debug?.(\n `collab broadcast failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n }\n }\n\n /**\n * Replay the last `REPLAY_LIMIT` events from the on-disk session log\n * to a single observer (the late joiner). Each event is forwarded as\n * a `collab.event` with `replay: true` so the client can distinguish\n * history from the live stream.\n *\n * The session log stores typed `SessionEvent`s (`user_input`,\n * `llm_response`, `tool_result`, etc.) — different from the kernel's\n * bus events. We translate the most useful subset (`tool.*` and\n * `iteration.*`-shaped ones) into the same `kind` namespace the live\n * mirror uses, so the client can render a single activity strip.\n */\n private async replayHistory(ws: WebSocket, sessionId: string): Promise<void> {\n if (!this.reader) return;\n const all: unknown[] = [];\n try {\n for await (const ev of this.reader.replay(sessionId)) {\n all.push(ev);\n }\n } catch (err) {\n this.logger.debug?.(\n `collab: session reader rejected ${sessionId}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n return;\n }\n const tail = all.slice(-REPLAY_LIMIT);\n if (tail.length === 0) return; // nothing to replay\n for (const raw of tail) {\n const ev = raw as { type?: string | undefined; ts?: string | undefined; [k: string]: unknown };\n const kind = this.historyEventToKind(ev);\n if (!kind) continue; // skip events we don't know how to mirror\n this.send(ws, {\n type: 'collab.event',\n payload: {\n kind,\n payload: ev,\n at: ev.ts ?? new Date().toISOString(),\n replay: true,\n },\n });\n }\n }\n\n /**\n * Map a stored `SessionEvent` to a `collab.event.kind` so the live\n * strip and the history strip can share a single rendering path.\n * Returns null for events that don't have a meaningful live analog\n * (e.g. `session_start`, file-snapshot bookkeeping, rewind markers).\n */\n private historyEventToKind(ev: { type?: string | undefined }): string | null {\n switch (ev.type) {\n case 'user_input':\n return 'user_input';\n case 'llm_response':\n return 'llm_response';\n case 'tool_result':\n return 'tool.executed';\n case 'compaction':\n return 'compaction';\n case 'error':\n return 'error';\n default:\n return null;\n }\n }\n\n // ── State snapshot + periodic broadcast ────────────────────────────────\n\n private stateMessage(sessionId: string): WSCollabState {\n const bucket = this.bySession.get(sessionId);\n return {\n type: 'collab.state',\n payload: {\n sessionId,\n participants: bucket\n ? [...bucket].map((p) => ({\n participantId: p.participantId,\n role: p.role,\n joinedAt: p.joinedAt,\n }))\n : [],\n },\n };\n }\n\n private ensureBroadcast(): void {\n if (this.broadcastInterval) return;\n this.broadcastInterval = setInterval(() => {\n for (const sessionId of this.bySession.keys()) {\n this.broadcast(sessionId, this.stateMessage(sessionId));\n }\n }, 2000);\n }\n\n private stopBroadcast(): void {\n if (this.broadcastInterval) {\n clearInterval(this.broadcastInterval);\n this.broadcastInterval = null;\n }\n }\n\n private broadcast(sessionId: string, msg: WSServerMessage): void {\n const data = JSON.stringify(msg);\n const bucket = this.bySession.get(sessionId);\n if (!bucket) return;\n for (const p of bucket) {\n try {\n if (p.ws.readyState === 1) p.ws.send(data);\n } catch (err) {\n this.logger.debug?.(\n `collab broadcast failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n }\n\n private send(ws: WebSocket, msg: WSServerMessage): void {\n try {\n if (ws.readyState === 1) ws.send(JSON.stringify(msg));\n } catch {\n /* client gone */\n }\n }\n\n private errorMessage(detail: string): WSServerMessage {\n return { type: 'error', payload: { phase: 'collab', message: detail } };\n }\n\n // ── Controller flow (Phase 3) ───────────────────────────────────────────\n\n private async handleRequestPause(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.bus) {\n this.send(ws, this.errorMessage('pause requires a CollaborationBus'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('pause requires an active join'));\n return;\n }\n if (participant.role !== 'controller') {\n this.send(\n ws,\n this.errorMessage(\n `pause requires the 'controller' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as { sessionId?: string | undefined } | undefined;\n if (!payload?.sessionId || payload.sessionId !== participant.sessionId) {\n this.send(ws, this.errorMessage('pause sessionId mismatch'));\n return;\n }\n const transitioned = this.bus.requestPause(participant.participantId);\n if (!transitioned) {\n // Already paused — surface the current state to the requester.\n const s = this.bus.getState();\n this.send(ws, {\n type: 'error',\n payload: {\n phase: 'collab',\n message: `bus already paused by ${s.pausedBy ?? '?'} at ${s.pausedAt ?? '?'}`,\n },\n });\n return;\n }\n const s = this.bus.getState();\n this.broadcast(payload.sessionId, {\n type: 'collab.pause.granted',\n payload: {\n sessionId: payload.sessionId,\n pausedBy: s.pausedBy ?? participant.participantId,\n pausedAt: s.pausedAt ?? new Date().toISOString(),\n autoResumeInMs: PAUSE_TIMEOUT_MS,\n },\n });\n }\n\n private async handleResume(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.bus) {\n this.send(ws, this.errorMessage('resume requires a CollaborationBus'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('resume requires an active join'));\n return;\n }\n // Permission: controller OR the original pauser. We do a simple\n // \"any controller can release\" check — fine for Phase 3, can be\n // tightened to \"only the pauser\" later.\n if (participant.role !== 'controller') {\n this.send(\n ws,\n this.errorMessage(\n `resume requires the 'controller' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as { sessionId?: string | undefined } | undefined;\n if (!payload?.sessionId || payload.sessionId !== participant.sessionId) {\n this.send(ws, this.errorMessage('resume sessionId mismatch'));\n return;\n }\n const transitioned = this.bus.resume();\n if (!transitioned) {\n this.send(ws, this.errorMessage('bus is not currently paused'));\n return;\n }\n this.broadcast(payload.sessionId, {\n type: 'collab.pause.released',\n payload: {\n sessionId: payload.sessionId,\n reason: 'controller',\n at: new Date().toISOString(),\n },\n });\n }\n\n private async handleGrantControl(ws: WebSocket, raw: unknown): Promise<void> {\n // Phase 3 metadata-only: record the grant in the log; the\n // existing controller's effective permissions do not change.\n // A future iteration can wire this to a per-participant RBAC\n // table that the `handleRequestPause`/`handleResume` checks read.\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('grant_control requires an active join'));\n return;\n }\n const payload = raw as\n | { sessionId?: string | undefined; toParticipant?: string | undefined }\n | undefined;\n if (\n !payload?.sessionId ||\n !payload.toParticipant ||\n payload.sessionId !== participant.sessionId\n ) {\n this.send(ws, this.errorMessage('grant_control requires { sessionId, toParticipant }'));\n return;\n }\n this.logger.debug?.(\n `collab: control granted from ${participant.participantId} to ${payload.toParticipant} in ${payload.sessionId}`,\n );\n }\n\n /**\n * Phase 4 — handle a controller's manual tool-call injection.\n * Validates the payload, queues it on the bus, and broadcasts\n * the grant so observers see what just happened. The actual\n * splice into the agent's pipeline is performed by the\n * `collabInjectMiddleware` on the next tool call.\n */\n private async handleInjectTool(ws: WebSocket, raw: unknown): Promise<void> {\n if (!this.bus) {\n this.send(ws, this.errorMessage('inject_tool requires a CollaborationBus'));\n return;\n }\n const participant = this.findParticipant(ws);\n if (!participant) {\n this.send(ws, this.errorMessage('inject_tool requires an active join'));\n return;\n }\n if (participant.role !== 'controller') {\n this.send(\n ws,\n this.errorMessage(\n `inject_tool requires the 'controller' role (current: '${participant.role}')`,\n ),\n );\n return;\n }\n const payload = raw as\n | {\n sessionId?: string | undefined;\n toolUseId?: string | undefined;\n content?: unknown | undefined;\n isError?: boolean | undefined;\n reason?: string | undefined;\n }\n | undefined;\n if (\n !payload?.sessionId ||\n !payload.toolUseId ||\n typeof payload.isError !== 'boolean' ||\n typeof payload.reason !== 'string' ||\n payload.content === undefined\n ) {\n this.send(\n ws,\n this.errorMessage(\n 'inject_tool requires { sessionId, toolUseId, content, isError, reason }',\n ),\n );\n return;\n }\n if (payload.sessionId !== participant.sessionId) {\n this.send(\n ws,\n this.errorMessage(\n `inject_tool sessionId mismatch (joined: ${participant.sessionId})`,\n ),\n );\n return;\n }\n const queued = this.bus.injectToolResult({\n toolUseId: payload.toolUseId,\n content: payload.content,\n isError: payload.isError,\n reason: payload.reason,\n authorId: participant.participantId,\n });\n if (!queued) {\n this.send(\n ws,\n this.errorMessage(\n `an injection for toolUseId ${payload.toolUseId} is already queued`,\n ),\n );\n return;\n }\n this.broadcast(payload.sessionId, {\n type: 'collab.injection.granted',\n payload: {\n sessionId: payload.sessionId,\n toolUseId: payload.toolUseId,\n // The tool name is unknown here (the injection is queued\n // before the model produces the tool call). We surface a\n // placeholder; the middleware will emit a `consumed` event\n // with the real name on match.\n toolName: '(pending match)',\n authorId: participant.participantId,\n reason: payload.reason,\n isError: payload.isError,\n phase: 'queued',\n at: new Date().toISOString(),\n },\n });\n }\n}\n\ninterface Participant {\n participantId: string;\n ws: WebSocket;\n sessionId: string;\n role: CollabRole;\n joinedAt: string;\n}\n","import type { WebSocket } from 'ws';\nimport type { EventBus, Logger } from '@wrongstack/core';\nimport type { WorktreeHandleView, WSServerMessage } from '../types.js';\n\nconst MAX_ACTIVITY = 6;\n\n/**\n * WorktreeWebSocketHandler — mirrors AutoPhaseWebSocketHandler. Subscribes to\n * the shared EventBus `worktree.*` lifecycle events, keeps a live snapshot of\n * every worktree, and broadcasts:\n * - `worktree.event` incrementally (drives the flowing activity strip)\n * - `worktree.state` on connect + on a 2s timer (drives swim-lanes/DAG)\n */\nexport class WorktreeWebSocketHandler {\n private readonly clients = new Set<WebSocket>();\n private readonly handles = new Map<string, WorktreeHandleView>();\n private baseBranch = '';\n private broadcastInterval: ReturnType<typeof setInterval> | null = null;\n private readonly offs: Array<() => void> = [];\n\n constructor(\n private readonly events: EventBus,\n private readonly logger: Logger,\n ) {\n this.subscribe();\n }\n\n addClient(ws: WebSocket): void {\n this.clients.add(ws);\n ws.on('close', () => this.clients.delete(ws));\n ws.on('error', () => this.clients.delete(ws));\n this.send(ws, this.stateMessage());\n }\n\n dispose(): void {\n for (const off of this.offs) off();\n this.offs.length = 0;\n this.stopBroadcast();\n }\n\n // ── internals ───────────────────────────────────────────────────────────\n\n private subscribe(): void {\n const on = this.events.on.bind(this.events) as unknown as (\n ev: string,\n fn: (p: unknown) => void,\n ) => () => void;\n\n this.offs.push(\n on('worktree.allocated', (p) => {\n const e = p as { handleId: string; ownerId: string; ownerLabel: string; branch: string; baseBranch: string };\n this.baseBranch = e.baseBranch || this.baseBranch;\n this.upsert(e.handleId, {\n handleId: e.handleId,\n ownerId: e.ownerId,\n ownerLabel: e.ownerLabel,\n branch: e.branch,\n baseBranch: e.baseBranch,\n status: 'active',\n insertions: 0,\n deletions: 0,\n files: 0,\n allocatedAt: Date.now(),\n lastEventAt: Date.now(),\n recentActivity: [],\n });\n this.activity(e.handleId, 'allocated', `branch ${e.branch}`);\n this.ensureBroadcast();\n }),\n on('worktree.committed', (p) => {\n const e = p as { handleId: string; insertions: number; deletions: number; files: number; committed: boolean };\n this.patch(e.handleId, { status: 'committing', insertions: e.insertions, deletions: e.deletions, files: e.files });\n if (e.committed) this.activity(e.handleId, 'committed', `+${e.insertions}/-${e.deletions} (${e.files}f)`);\n this.broadcastState();\n }),\n on('worktree.merged', (p) => {\n const e = p as { handleId: string; baseBranch: string };\n this.patch(e.handleId, { status: 'merged' });\n this.activity(e.handleId, 'merged', `→ ${e.baseBranch}`);\n this.broadcastState();\n }),\n on('worktree.conflict', (p) => {\n const e = p as { handleId: string; conflictFiles: string[] };\n this.patch(e.handleId, { status: 'needs-review', conflictFiles: e.conflictFiles });\n this.activity(e.handleId, 'conflict', e.conflictFiles.join(', '));\n this.broadcastState();\n }),\n on('worktree.failed', (p) => {\n const e = p as { handleId: string; error: string };\n this.patch(e.handleId, { status: 'failed' });\n this.activity(e.handleId, 'failed', e.error);\n this.broadcastState();\n }),\n on('worktree.released', (p) => {\n const e = p as { handleId: string; kept: boolean };\n if (!e.kept) this.handles.delete(e.handleId);\n this.activity(e.handleId, 'released', e.kept ? 'kept for review' : 'removed');\n if (this.handles.size === 0) this.stopBroadcast();\n else this.broadcastState();\n }),\n );\n }\n\n private upsert(id: string, view: WorktreeHandleView): void {\n this.handles.set(id, view);\n }\n\n private patch(id: string, patch: Partial<WorktreeHandleView>): void {\n const cur = this.handles.get(id);\n if (!cur) return;\n this.handles.set(id, { ...cur, ...patch, lastEventAt: Date.now() });\n }\n\n private activity(id: string, kind: string, text: string): void {\n const cur = this.handles.get(id);\n if (cur) {\n const recentActivity = [...cur.recentActivity, { kind, text, at: Date.now() }].slice(-MAX_ACTIVITY);\n this.handles.set(id, { ...cur, recentActivity });\n }\n this.broadcast({ type: 'worktree.event', payload: { kind, handleId: id, text, at: Date.now() } });\n }\n\n private stateMessage(): WSServerMessage {\n return {\n type: 'worktree.state',\n payload: { worktrees: [...this.handles.values()], baseBranch: this.baseBranch },\n };\n }\n\n private broadcastState(): void {\n this.broadcast(this.stateMessage());\n }\n\n private ensureBroadcast(): void {\n this.broadcast(this.stateMessage());\n if (this.broadcastInterval) return;\n this.broadcastInterval = setInterval(() => this.broadcast(this.stateMessage()), 2000);\n }\n\n private stopBroadcast(): void {\n this.broadcast(this.stateMessage());\n if (this.broadcastInterval) {\n clearInterval(this.broadcastInterval);\n this.broadcastInterval = null;\n }\n }\n\n private broadcast(msg: WSServerMessage): void {\n const data = JSON.stringify(msg);\n for (const ws of this.clients) {\n try {\n if (ws.readyState === 1) ws.send(data);\n } catch (err) {\n this.logger.debug?.(`worktree broadcast failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n\n private send(ws: WebSocket, msg: WSServerMessage): void {\n try {\n if (ws.readyState === 1) ws.send(JSON.stringify(msg));\n } catch {\n /* client gone */\n }\n }\n}\n","/**\n * Mailbox WebSocket handlers for the WebUI.\n *\n * Handles `mailbox.messages` and `mailbox.agents` message types.\n * The frontend sends these to populate the mailbox panel; the server\n * reads from the project-level GlobalMailbox and responds.\n */\n\nimport * as path from 'node:path';\nimport type { WebSocket } from 'ws';\nimport { GlobalMailbox } from '@wrongstack/core';\nimport { send, errMessage } from './ws-utils.js';\n\n// ── Helpers ───────────────────────────────────────────────────────────\n\nfunction resolveProjectDir(projectRoot: string, globalRoot: string): string {\n const { createHash } = require('node:crypto');\n const hash = createHash('sha256')\n .update(path.resolve(projectRoot))\n .digest('hex')\n .slice(0, 6);\n const slug = path\n .basename(projectRoot)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .slice(0, 40) || 'project';\n return path.join(globalRoot, 'projects', `${slug}-${hash}`);\n}\n\nexport interface MailboxHandlerDeps {\n /** Absolute project root. */\n projectRoot: string;\n /** Global WrongStack root (~/.wrongstack). */\n globalRoot: string;\n}\n\n// ── Handlers ──────────────────────────────────────────────────────────\n\n/**\n * List recent mailbox messages. Frontend sends:\n * { type: 'mailbox.messages', limit?: number, agentId?: string }\n */\nexport async function handleMailboxMessages(\n ws: WebSocket,\n deps: MailboxHandlerDeps,\n payload: { limit?: number; agentId?: string; unreadOnly?: boolean } | undefined,\n): Promise<void> {\n try {\n const dir = resolveProjectDir(deps.projectRoot, deps.globalRoot);\n const mb = new GlobalMailbox(dir);\n const messages = await mb.query({\n limit: payload?.limit ?? 30,\n to: payload?.agentId,\n unreadBy: payload?.unreadOnly ? payload.agentId : undefined,\n });\n send(ws, {\n type: 'mailbox.messages',\n payload: {\n messages: messages.map((m) => ({\n id: m.id, from: m.from, to: m.to, type: m.type,\n subject: m.subject, body: m.body, priority: m.priority,\n readBy: m.readBy, readByCount: Object.keys(m.readBy).length,\n completed: m.completed, completedBy: m.completedBy,\n outcome: m.outcome, timestamp: m.timestamp,\n replyTo: m.replyTo, senderSessionId: m.senderSessionId,\n })),\n },\n });\n } catch (err) {\n send(ws, { type: 'mailbox.messages', payload: { messages: [], error: errMessage(err) } });\n }\n}\n\n/**\n * List registered agents. Frontend sends:\n * { type: 'mailbox.agents', onlineOnly?: boolean }\n */\nexport async function handleMailboxAgents(\n ws: WebSocket,\n deps: MailboxHandlerDeps,\n payload: { onlineOnly?: boolean } | undefined,\n): Promise<void> {\n try {\n const dir = resolveProjectDir(deps.projectRoot, deps.globalRoot);\n const mb = new GlobalMailbox(dir);\n const agents = payload?.onlineOnly\n ? await mb.getOnlineAgents()\n : await mb.getAgentStatuses();\n send(ws, {\n type: 'mailbox.agents',\n payload: {\n agents: agents.map((a) => ({\n agentId: a.agentId, name: a.name, role: a.role,\n sessionId: a.sessionId, status: a.status,\n currentTool: a.currentTool, currentTask: a.currentTask,\n iterations: a.iterations, toolCalls: a.toolCalls,\n lastSeenAt: a.lastSeenAt, online: a.online,\n pid: a.pid, source: a.source,\n })),\n },\n });\n } catch (err) {\n send(ws, { type: 'mailbox.agents', payload: { agents: [], error: errMessage(err) } });\n }\n}\n\n/**\n * Delete all messages from the mailbox. Frontend sends:\n * { type: 'mailbox.clear' }\n * Server responds with 'mailbox.cleared'.\n */\nexport async function handleMailboxClear(\n ws: WebSocket,\n deps: MailboxHandlerDeps,\n): Promise<void> {\n try {\n const dir = resolveProjectDir(deps.projectRoot, deps.globalRoot);\n const mb = new GlobalMailbox(dir);\n await mb.clearAll();\n send(ws, { type: 'mailbox.cleared', payload: {} });\n } catch (err) {\n send(ws, { type: 'mailbox.cleared', payload: { error: errMessage(err) } });\n }\n}\n","/**\n * Process lifecycle for the WebUI server: graceful shutdown and the\n * SIGINT/SIGTERM wiring that triggers it.\n *\n * On a termination signal we (best-effort) flush + close the active session,\n * close every connected WebSocket, stop the HTTP and WS servers, then exit.\n * A re-entrancy guard makes a second signal during shutdown a no-op (rapid\n * double Ctrl+C no longer runs the teardown twice).\n *\n * Extracted from `index.ts` as a parameterized factory so the teardown\n * sequence can be unit tested without a real process signal, server, or\n * `process.exit` — `log` and `exit` are injectable seams.\n */\n\nexport interface LifecycleResources {\n /** Persist + close the active session (best-effort; errors are logged). */\n flushSession: () => Promise<void>;\n /**\n * Returns the currently-connected client sockets to close. A thunk (not a\n * snapshot) so shutdown closes whoever is connected *at signal time*, not\n * whoever was connected when the handler was registered.\n */\n clients: () => Iterable<{ close: () => void }>;\n /** Servers to stop (HTTP + WS). `null`/`undefined` entries are skipped. */\n servers: Array<{ close: () => void } | null | undefined>;\n /**\n * Optional best-effort cleanup run after the session flush and before exit\n * (e.g. removing this process from the running-instance registry). Errors are\n * logged, never thrown — cleanup must not block a clean shutdown.\n */\n onShutdown?: (() => Promise<void> | void) | undefined;\n /** Output sink. Defaults to `console.log`. */\n log?: ((msg: string) => void) | undefined;\n /** Process exit. Defaults to `process.exit`. Injectable for tests. */\n exit?: ((code: number) => void) | undefined;\n}\n\n/**\n * Build the graceful-shutdown handler. Returns an idempotent async function:\n * the first call runs the teardown, subsequent calls (e.g. a second SIGINT)\n * return immediately.\n */\nexport function createShutdown(res: LifecycleResources): () => Promise<void> {\n const log = res.log ?? ((m: string) => console.log(m));\n const exit = res.exit ?? ((code: number) => process.exit(code));\n let shuttingDown = false;\n\n return async () => {\n if (shuttingDown) return; // a second signal during teardown is a no-op\n shuttingDown = true;\n\n log('[WebUI] Shutting down...');\n try {\n await res.flushSession();\n } catch (e) {\n log(`[WebUI] Error closing session: ${e instanceof Error ? e.message : String(e)}`);\n }\n for (const ws of res.clients()) ws.close();\n for (const server of res.servers) server?.close();\n if (res.onShutdown) {\n try {\n await res.onShutdown();\n } catch (e) {\n log(`[WebUI] Error during shutdown cleanup: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n exit(0);\n };\n}\n\n/**\n * Register the shutdown handler on SIGINT and SIGTERM. Returns an unregister\n * function that detaches both listeners (useful for tests and clean restarts).\n */\nexport function registerShutdownHandlers(res: LifecycleResources): () => void {\n const shutdown = createShutdown(res);\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n return () => {\n process.off('SIGINT', shutdown);\n process.off('SIGTERM', shutdown);\n };\n}\n","/**\n * Running-instance registry for the standalone WebUI server.\n *\n * Every live `webui` process records itself in a single JSON file under the\n * wstack home dir (`~/.wrongstack/webui-instances.json`) so a user running\n * several instances (one per project, or several per project on different\n * ports) can see at a glance which ports are open for which path.\n *\n * Design notes:\n * - **Self-healing**: every register/unregister/list prunes entries whose PID\n * is no longer alive (`process.kill(pid, 0)`), so a crashed instance that\n * never got to unregister doesn't leave a ghost behind.\n * - **Atomic writes**: the file is rewritten via `atomicWrite` (tmp + rename),\n * so a concurrent reader never sees a half-written file. Two instances\n * starting at the *exact* same millisecond could still race the\n * read-modify-write — acceptable for a best-effort tracking file, and the\n * next register() heals any dropped entry.\n * - **Best-effort**: a failure to read/write the registry must NEVER take the\n * server down. Callers wrap these in `.catch()`.\n */\n\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs/promises';\nimport { atomicWrite } from '@wrongstack/core';\n\n/** One running WebUI process. */\nexport interface WebUIInstanceRecord {\n /** OS process id — also the liveness key. */\n pid: number;\n /** HTTP port serving the React frontend. */\n httpPort: number;\n /** WebSocket port for the agent backend. */\n wsPort: number;\n /** Bind host (e.g. 127.0.0.1 or 0.0.0.0). */\n host: string;\n /** Absolute project root the instance booted against. */\n projectRoot: string;\n /** Display name (basename of projectRoot). */\n projectName: string;\n /** ISO timestamp when the instance registered. */\n startedAt: string;\n /** Convenience open-in-browser URL. */\n url: string;\n}\n\ninterface RegistryFile {\n version: 1;\n instances: WebUIInstanceRecord[];\n}\n\n/** Default wstack home dir (`~/.wrongstack`). Callers may override the base. */\nexport function defaultBaseDir(): string {\n return path.join(os.homedir(), '.wrongstack');\n}\n\n/** Resolve the registry file path for a given base dir. */\nexport function registryPath(baseDir: string = defaultBaseDir()): string {\n return path.join(baseDir, 'webui-instances.json');\n}\n\n/**\n * Liveness probe. `process.kill(pid, 0)` sends no signal — it only checks the\n * process exists. ESRCH ⇒ dead; EPERM ⇒ alive but owned by another user (still\n * counts as alive). Any other error is treated conservatively as \"alive\" so we\n * never prune an instance we simply failed to probe.\n */\nexport function isPidAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n}\n\nasync function load(file: string): Promise<RegistryFile> {\n try {\n const raw = await fs.readFile(file, 'utf8');\n const parsed = JSON.parse(raw) as RegistryFile;\n if (parsed?.version === 1 && Array.isArray(parsed.instances)) {\n return parsed;\n }\n } catch {\n // Missing or corrupt → start fresh.\n }\n return { version: 1, instances: [] };\n}\n\nasync function save(file: string, instances: WebUIInstanceRecord[]): Promise<void> {\n await atomicWrite(file, `${JSON.stringify({ version: 1, instances }, null, 2)}\\n`, {\n mode: 0o600,\n });\n}\n\n/** Drop dead processes and (optionally) one specific pid. */\nfunction prune(instances: WebUIInstanceRecord[], excludePid?: number): WebUIInstanceRecord[] {\n return instances.filter((i) => i.pid !== excludePid && isPidAlive(i.pid));\n}\n\n/**\n * Register (or refresh) this instance. Prunes dead entries and any stale entry\n * for our own PID before adding the current record. Best-effort — rejects only\n * on a hard fs error, which callers swallow.\n */\nexport async function registerInstance(\n record: WebUIInstanceRecord,\n baseDir: string = defaultBaseDir(),\n): Promise<void> {\n const file = registryPath(baseDir);\n const data = await load(file);\n const instances = prune(data.instances, record.pid);\n instances.push(record);\n await save(file, instances);\n}\n\n/** Remove this instance (called on graceful shutdown). Also prunes dead pids. */\nexport async function unregisterInstance(\n pid: number,\n baseDir: string = defaultBaseDir(),\n): Promise<void> {\n const file = registryPath(baseDir);\n const data = await load(file);\n const instances = prune(data.instances, pid);\n await save(file, instances);\n}\n\n/** List live instances, pruning any dead entries encountered. */\nexport async function listInstances(\n baseDir: string = defaultBaseDir(),\n): Promise<WebUIInstanceRecord[]> {\n const file = registryPath(baseDir);\n const data = await load(file);\n const live = prune(data.instances);\n // Persist the pruned view so `cat`-ing the file also shows reality, but never\n // fail the list on a write error.\n if (live.length !== data.instances.length) {\n await save(file, live).catch(() => {});\n }\n return live;\n}\n\n/** Human-readable table of running instances for `webui --list`. */\nexport function formatInstances(instances: WebUIInstanceRecord[]): string {\n if (instances.length === 0) {\n return 'No WebUI instances are currently running.';\n }\n const lines = [`Running WebUI instances (${instances.length}):`, ''];\n for (const i of instances) {\n lines.push(\n ` • ${i.url} · ws:${i.wsPort} · pid ${i.pid}`,\n ` project: ${i.projectName} (${i.projectRoot})`,\n ` since: ${i.startedAt}`,\n );\n }\n return lines.join('\\n');\n}\n","/**\n * Free-port discovery for the standalone WebUI server.\n *\n * When a user runs several instances, the default ports (HTTP 3456 / WS 3457)\n * are taken by the first one. Rather than make the user hand-pick `PORT` /\n * `WS_PORT` for every extra instance, the server probes upward from the\n * requested port and binds the first free one — then stamps that real port into\n * the served HTML and the instance registry so everything stays consistent.\n *\n * The probe binds a throwaway `net.Server`, then closes it, so there is a tiny\n * TOCTOU window between \"found free\" and \"the real server binds it\". For local\n * single-user multi-instance use that race is negligible; if it ever loses, the\n * real bind fails loudly with EADDRINUSE exactly as before.\n */\n\nimport * as net from 'node:net';\n\n/** Resolve true when `port` can be bound on `host`, false on EADDRINUSE/EACCES. */\nexport function isPortFree(host: string, port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const srv = net.createServer();\n srv.once('error', () => resolve(false));\n srv.once('listening', () => {\n srv.close(() => resolve(true));\n });\n try {\n srv.listen(port, host);\n } catch {\n resolve(false);\n }\n });\n}\n\nexport interface FindFreePortOptions {\n /** Ports to skip even if free (e.g. one already chosen for the sibling server). */\n exclude?: Set<number> | undefined;\n /** How many consecutive ports to try before giving up. Default 200. */\n maxTries?: number | undefined;\n}\n\n/**\n * Find the first free port at or above `startPort` on `host`, skipping any in\n * `exclude`. Throws if nothing is free within `maxTries` steps.\n */\nexport async function findFreePort(\n host: string,\n startPort: number,\n opts: FindFreePortOptions = {},\n): Promise<number> {\n const exclude = opts.exclude ?? new Set<number>();\n const maxTries = opts.maxTries ?? 200;\n let port = startPort;\n for (let i = 0; i < maxTries; i++) {\n // Stay inside the valid TCP range; wrap into the high ephemeral band if a\n // pathological startPort pushes us past the ceiling.\n if (port > 65535) port = 1024 + (port % 50000);\n if (!exclude.has(port) && (await isPortFree(host, port))) {\n return port;\n }\n port++;\n }\n throw new Error(\n `No free port found near ${startPort} on ${host} after ${maxTries} attempts.`,\n );\n}\n","/**\n * Best-effort \"open this URL in the default browser\" for `--webui --open`.\n *\n * Cross-platform via the OS opener (`start` / `open` / `xdg-open`). Fully\n * fire-and-forget: a missing opener, a headless box, or a spawn failure must\n * NEVER take the server down — the URL is always also printed to the console.\n */\n\nimport { spawn } from 'node:child_process';\n\n/** Resolve the platform's URL-opener command + args. */\nexport function browserOpenCommand(\n url: string,\n platform: NodeJS.Platform = process.platform,\n): { command: string; args: string[] } {\n if (platform === 'win32') {\n // `start` is a cmd builtin; the empty \"\" is the window title slot so URLs\n // containing `&` / spaces are passed through intact.\n return { command: 'cmd', args: ['/c', 'start', '', url] };\n }\n if (platform === 'darwin') {\n return { command: 'open', args: [url] };\n }\n return { command: 'xdg-open', args: [url] };\n}\n\n/** Spawn the OS browser-opener for `url` and register it as a protected\n * process so it survives kill/killAll. Never throws. */\nexport function openBrowser(url: string, platform: NodeJS.Platform = process.platform): void {\n try {\n const { command, args } = browserOpenCommand(url, platform);\n const child = spawn(command, args, { stdio: 'ignore', detached: true, windowsHide: true });\n // A missing opener (e.g. xdg-open absent on a headless box) surfaces as an\n // async 'error' event — swallow it so it doesn't crash the process.\n child.on('error', () => {});\n child.unref();\n\n // Register the browser process as protected so process.kill / killAll\n // never accidentally terminates it — that would crash the webui session.\n // The registry is imported lazily to avoid a circular dependency with\n // @wrongstack/tools (which the webui server does not directly depend on).\n if (child.pid) {\n try {\n // Dynamic import to avoid hard dependency on @wrongstack/tools from\n // this module (the webui server may not have tools installed).\n import('@wrongstack/tools').then(({ getProcessRegistry }) => {\n getProcessRegistry().register({\n // biome-ignore lint/style/noNonNullAssertion: pid always present after spawn\n pid: child.pid!,\n name: 'browser',\n command: `${command} ${args.join(' ')}`,\n startedAt: Date.now(),\n child,\n protected: true,\n });\n // Auto-unregister on exit so the process list stays accurate.\n child.on('exit', () => {\n // biome-ignore lint/style/noNonNullAssertion: pid guaranteed after spawn\n getProcessRegistry().unregister(child.pid!);\n });\n }).catch(() => {\n // @wrongstack/tools may not be available — silently skip registration.\n });\n } catch {\n // Module resolution failure — silently skip.\n }\n }\n } catch {\n // Synchronous spawn failure — best-effort, ignore.\n }\n}\n","/**\n * Token-usage cost math for the WebUI server.\n *\n * models.dev pricing is expressed in **dollars per 1,000,000 tokens**, and\n * providers omit the field entirely for free/unmetered plans. Both the\n * `session.start` payload (which ships the per-token rates to the client) and\n * `stats.get` (which reports an actual dollar figure) repeated the same\n * \"read `model.cost.*` with a `?? 0` fallback, then divide by 1e6\" logic\n * inline. Pulling it here keeps the rate normalization and the cost formula in\n * one tested place — a wrong field name or a missing `/ 1e6` silently produces\n * a plausible-but-wrong number, which is exactly what a unit test should pin.\n */\n\n/** Per-1,000,000-token pricing, normalized to numbers (0 when unpriced). */\nexport interface CostRates {\n /** $ per 1M input tokens. */\n input: number;\n /** $ per 1M output tokens. */\n output: number;\n /** $ per 1M cache-read tokens. */\n cacheRead: number;\n}\n\n/** Token counts for a turn/session. `cacheRead` is optional (older counters). */\nexport interface TokenUsage {\n input: number;\n output: number;\n cacheRead?: number | undefined;\n}\n\n/**\n * Normalize a models.dev model object's pricing into {@link CostRates}.\n * Missing model, missing `cost`, or missing individual fields all yield 0 —\n * free/unmetered plans report `$0` rather than crashing.\n */\nexport function getCostRates(model: unknown): CostRates {\n const cost = (\n model as { cost?: { input?: number | undefined; output?: number | undefined; cache_read?: number | undefined } } | null | undefined\n )?.cost;\n return {\n input: cost?.input ?? 0,\n output: cost?.output ?? 0,\n cacheRead: cost?.cache_read ?? 0,\n };\n}\n\n/**\n * Dollar cost of `usage` at the given per-1M-token `rates`. Returns 0 when all\n * rates are 0 (unpriced plan).\n */\nexport function computeUsageCost(usage: TokenUsage, rates: CostRates): number {\n return (\n (usage.input * rates.input +\n usage.output * rates.output +\n (usage.cacheRead ?? 0) * rates.cacheRead) /\n 1_000_000\n );\n}\n","/**\n * Shared config I/O helpers for the `providers` map inside the global config.\n *\n * Extracted from both `packages/webui/src/server/index.ts` and\n * `packages/cli/src/webui-server.ts` so the CLI's `--webui` mode doesn't\n * duplicate the read-merge-decrypt / encrypt-write cycle. Callers supply\n * their own vault (already booted) and config path — this module is pure I/O\n * with no side-channel state.\n */\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { type ProviderConfig, type SecretVault, atomicWrite } from '@wrongstack/core';\nimport { decryptConfigSecrets, encryptConfigSecrets } from '@wrongstack/core/security';\n\n/**\n * Read the `providers` section from the global config, decrypting\n * secret-bearing fields. Returns an empty record when the config file\n * doesn't exist or has no `providers` key.\n */\nexport async function loadSavedProviders(\n configPath: string,\n vault: SecretVault,\n): Promise<Record<string, ProviderConfig>> {\n let raw: string;\n try {\n raw = await fs.readFile(configPath, 'utf8');\n } catch {\n return {};\n }\n let parsed: { providers?: Record<string, ProviderConfig> } = {};\n try {\n parsed = JSON.parse(raw) as { providers?: Record<string, ProviderConfig> };\n } catch {\n return {};\n }\n if (!parsed.providers) return {};\n return decryptConfigSecrets(parsed.providers, vault);\n}\n\n/**\n * Write `providers` back into the global config, encrypting secrets first.\n * Refuses to overwrite a corrupt-but-existing config file (the operator\n * should fix it manually). When the config file is missing (ENOENT), starts\n * from an empty object.\n */\nexport async function saveProviders(\n configPath: string,\n vault: SecretVault,\n providers: Record<string, ProviderConfig>,\n): Promise<void> {\n let raw: string;\n let fileExists = true;\n try {\n raw = await fs.readFile(configPath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new Error(\n `Refusing to mutate ${configPath}: ${(err as Error).message}`,\n { cause: err },\n );\n }\n fileExists = false;\n raw = '{}';\n }\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n if (fileExists) {\n throw new Error(\n `Refusing to overwrite corrupt config at ${configPath} ` +\n `(${(err as Error).message}). Fix or move the file aside before retrying.`,\n { cause: err },\n );\n }\n parsed = {};\n }\n parsed.providers = providers;\n const encrypted = encryptConfigSecrets(parsed, vault);\n await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 0o600 });\n}\n\n// ---------------------------------------------------------------------------\n// Standalone WebUI server helpers (boot phase — not WS-connected)\n// ---------------------------------------------------------------------------\n\nimport { DefaultSecretVault } from '@wrongstack/core';\n\n/**\n * Small helper for the standalone WebUI entry point: create a\n * `{ load, save }` pair from a config path alone (uses the\n * config-directory-relative `.key` file for the vault). The `--webui`\n * CLI mode and the standalone server both need to read/write the\n * `providers` map identically.\n */\nexport function createProviderConfigIO(configPath: string) {\n const keyFile = path.join(path.dirname(configPath), '.key');\n const vault = new DefaultSecretVault({ keyFile });\n\n return {\n load: () => loadSavedProviders(configPath, vault),\n save: (providers: Record<string, ProviderConfig>) =>\n saveProviders(configPath, vault, providers),\n };\n}\n","import { expectDefined } from '@wrongstack/core';\n/**\n * Pure provider/API-key record transforms for the WebUI server's `key.*` and\n * `provider.*` WebSocket handlers.\n *\n * These operate on an in-memory `providers` record (the decrypted\n * `config.providers` map) and return a `{ ok, message }` result mirroring the\n * status string the handler sends back to the client. All persistence\n * (load/decrypt, encrypt/atomic-write) and WS messaging stays in `index.ts` —\n * keeping this layer pure means the security-sensitive key bookkeeping (which\n * key is active, when a provider is dropped, how legacy single-key configs are\n * normalized) is unit-testable without a vault or a socket.\n *\n * Extracted from `index.ts`; transforms mutate the passed record in place, the\n * same way the original handlers did before calling `saveProviders`.\n */\nimport type { ProviderApiKey, ProviderConfig } from '@wrongstack/core';\nexport type ProvidersRecord = Record<string, ProviderConfig>;\n\nexport interface KeyOpResult {\n ok: boolean;\n message: string;\n}\n\n/**\n * Normalize a provider's keys to the array form, upgrading a legacy single\n * `apiKey` string to a one-element `[{ label: 'default', ... }]` list. Returns\n * fresh copies so callers can mutate without aliasing the stored config.\n */\nexport function normalizeKeys(cfg: ProviderConfig): ProviderApiKey[] {\n if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {\n return cfg.apiKeys.map((k) => ({ ...k }));\n }\n if (typeof cfg.apiKey === 'string' && cfg.apiKey.length > 0) {\n return [{ label: 'default', apiKey: cfg.apiKey, createdAt: '' }];\n }\n return [];\n}\n\n/**\n * Write a normalized key list back onto a provider config: drop all key fields\n * when empty, otherwise sync `apiKeys`, the legacy `apiKey` mirror (the active\n * key), and re-point `activeKey` if it no longer names a present key.\n */\nexport function writeKeysBack(cfg: ProviderConfig, keys: ProviderApiKey[]): void {\n if (keys.length === 0) {\n delete cfg.apiKeys;\n delete cfg.apiKey;\n delete cfg.activeKey;\n return;\n }\n cfg.apiKeys = keys;\n const active = keys.find((k) => k.label === cfg.activeKey) ?? expectDefined(keys[0]);\n cfg.apiKey = active.apiKey;\n if (!cfg.activeKey || !keys.some((k) => k.label === cfg.activeKey)) {\n cfg.activeKey = active.label;\n }\n}\n\n/** Mask a secret for display: `••••` for short keys, `abcd…wxyz` otherwise. */\nexport function maskedKey(key: string | undefined): string {\n if (!key) return '—';\n if (key.length <= 8) return '•'.repeat(key.length);\n return `${key.slice(0, 4)}…${key.slice(-4)}`;\n}\n\n/** Add or replace a labeled key for a provider, creating the provider if new. */\nexport function upsertKey(\n providers: ProvidersRecord,\n providerId: string,\n label: string,\n apiKey: string,\n nowIso: string,\n): KeyOpResult {\n const existing: ProviderConfig = providers[providerId] ?? { type: providerId };\n const keys = normalizeKeys(existing);\n const idx = keys.findIndex((k) => k.label === label);\n if (idx >= 0) {\n keys[idx] = { ...expectDefined(keys[idx]), apiKey, createdAt: nowIso };\n } else {\n keys.push({ label, apiKey, createdAt: nowIso });\n }\n writeKeysBack(existing, keys);\n if (!existing.activeKey) existing.activeKey = label;\n providers[providerId] = existing;\n return { ok: true, message: `Key \"${label}\" saved for ${providerId}` };\n}\n\n/** Remove a labeled key; drops the provider entirely when its last key goes. */\nexport function deleteKey(\n providers: ProvidersRecord,\n providerId: string,\n label: string,\n): KeyOpResult {\n const existing = providers[providerId];\n if (!existing) {\n return { ok: false, message: `Provider \"${providerId}\" not found` };\n }\n const keys = normalizeKeys(existing).filter((k) => k.label !== label);\n if (keys.length === 0) {\n delete providers[providerId];\n } else {\n writeKeysBack(existing, keys);\n if (existing.activeKey === label) existing.activeKey = keys[0]?.label;\n providers[providerId] = existing;\n }\n return { ok: true, message: `Key \"${label}\" deleted from ${providerId}` };\n}\n\n/** Point a provider's active key at the given label. */\nexport function setActiveKey(\n providers: ProvidersRecord,\n providerId: string,\n label: string,\n): KeyOpResult {\n const existing = providers[providerId];\n if (!existing) {\n return { ok: false, message: `Provider \"${providerId}\" not found` };\n }\n existing.activeKey = label;\n writeKeysBack(existing, normalizeKeys(existing));\n providers[providerId] = existing;\n return { ok: true, message: `Active key for ${providerId} set to \"${label}\"` };\n}\n\n/** Register a brand-new provider (optionally with an initial `default` key). */\nexport function addProvider(\n providers: ProvidersRecord,\n payload: { id: string; family: string; baseUrl?: string | undefined; apiKey?: string | undefined },\n nowIso: string,\n): KeyOpResult {\n if (providers[payload.id]) {\n return {\n ok: false,\n message: `Provider \"${payload.id}\" already exists. Use key.add to add a key.`,\n };\n }\n const newProv: ProviderConfig = {\n type: payload.id,\n family: payload.family as ProviderConfig['family'],\n baseUrl: payload.baseUrl,\n };\n if (payload.apiKey) {\n newProv.apiKeys = [{ label: 'default', apiKey: payload.apiKey, createdAt: nowIso }];\n newProv.activeKey = 'default';\n }\n providers[payload.id] = newProv;\n return { ok: true, message: `Provider \"${payload.id}\" added` };\n}\n\n/** Remove an entire provider and all its keys. */\nexport function removeProvider(providers: ProvidersRecord, providerId: string): KeyOpResult {\n if (!providers[providerId]) {\n return { ok: false, message: `Provider \"${providerId}\" not found` };\n }\n delete providers[providerId];\n return { ok: true, message: `Provider \"${providerId}\" removed` };\n}\n","import type { WebSocket } from 'ws';\nimport type { ProviderConfig } from '@wrongstack/core';\nimport { loadSavedProviders, saveProviders } from './provider-config-io.js';\nimport {\n upsertKey as upsertKeyRecord,\n deleteKey as deleteKeyRecord,\n setActiveKey as setActiveKeyRecord,\n addProvider as addProviderRecord,\n removeProvider as removeProviderRecord,\n maskedKey,\n normalizeKeys,\n} from './provider-keys.js';\nimport type { ConnectedClient, WSServerMessage } from './types.js';\nimport { sendResult, errMessage } from './ws-utils.js';\n\nexport interface ProviderHandlerDeps {\n globalConfigPath: string;\n vault: import('@wrongstack/core').SecretVault;\n /** Shared config write lock — serialized via chained promises */\n setConfigWriteLock: (lock: Promise<void>) => void;\n getConfigWriteLock: () => Promise<void>;\n /** Broadcast a message to all connected WebUI clients */\n broadcast: (clients: Map<WebSocket, ConnectedClient>, msg: WSServerMessage) => void;\n /** Connected WebUI clients map */\n clients: Map<WebSocket, ConnectedClient>;\n}\n\nexport function createProviderHandlers(deps: ProviderHandlerDeps) {\n const { globalConfigPath, vault, broadcast, clients } = deps;\n let configWriteLock = deps.getConfigWriteLock();\n\n async function loadConfigProviders(): Promise<Record<string, ProviderConfig>> {\n return loadSavedProviders(globalConfigPath, vault);\n }\n\n async function saveConfigProviders(providers: Record<string, ProviderConfig>): Promise<void> {\n const next = configWriteLock\n .then(() => saveProviders(globalConfigPath, vault, providers))\n .catch((err) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(JSON.stringify({\n level: 'error',\n event: 'webui.provider_save_failed',\n message: msg,\n timestamp: new Date().toISOString(),\n }));\n });\n configWriteLock = next;\n deps.setConfigWriteLock(next);\n await next;\n }\n\n async function handleKeyUpsert(ws: WebSocket, providerId: string, label: string, apiKey: string): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = upsertKeyRecord(providers, providerId, label, apiKey, new Date().toISOString());\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n async function handleKeyDelete(ws: WebSocket, providerId: string, label: string): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = deleteKeyRecord(providers, providerId, label);\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n async function handleKeySetActive(ws: WebSocket, providerId: string, label: string): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = setActiveKeyRecord(providers, providerId, label);\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n async function handleProviderAdd(ws: WebSocket, payload: { id: string; family: string; baseUrl?: string | undefined; apiKey?: string | undefined }): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = addProviderRecord(providers, payload, new Date().toISOString());\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n if (result.ok) {\n console.log(`[WebUI] Provider \"${payload.id}\" added via provider.add`);\n broadcast(clients, {\n type: 'providers.saved',\n payload: {\n providers: Object.entries(providers).map(([id, cfg]) => {\n const keys = normalizeKeys(cfg);\n return {\n id,\n family: cfg.family ?? id,\n baseUrl: cfg.baseUrl,\n apiKeys: keys.map((k) => ({\n label: k.label,\n maskedKey: maskedKey(k.apiKey),\n isActive: k.label === cfg.activeKey,\n createdAt: k.createdAt,\n })),\n };\n }),\n },\n });\n }\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n async function handleProviderRemove(ws: WebSocket, providerId: string): Promise<void> {\n try {\n const providers = await loadConfigProviders();\n const result = removeProviderRecord(providers, providerId);\n if (result.ok) await saveConfigProviders(providers);\n sendResult(ws, result.ok, result.message);\n } catch (err) {\n sendResult(ws, false, errMessage(err));\n }\n }\n\n return { handleKeyUpsert, handleKeyDelete, handleKeySetActive, handleProviderAdd, handleProviderRemove, loadConfigProviders };\n}\n","import type { EventBus, Context, SessionEventBridge } from '@wrongstack/core';\nimport type { WebSocket } from 'ws';\nimport type { ConnectedClient, WSServerMessage } from './types.js';\n\nimport * as path from 'node:path';\n\nexport interface SetupEventsDeps {\n events: EventBus;\n broadcast: (clients: Map<WebSocket, ConnectedClient>, msg: WSServerMessage) => void;\n clients: Map<WebSocket, ConnectedClient>;\n config: { tools?: { maxIterations?: number | undefined } };\n context: Context;\n pendingConfirms: Map<string, (d: 'yes' | 'no' | 'always' | 'deny') => void>;\n /** Optional global config dir (~/.wrongstack) — enables SessionRegistry poll for fleet view. */\n globalConfigPath?: string | undefined;\n /**\n * Audit-level-aware session log bridge. When provided, tool/error/provider\n * events are persisted to the session JSONL (same contract as the CLI) —\n * without it, standalone-WebUI sessions carry no audit events and resume\n * with no tool history.\n */\n sessionBridge?: SessionEventBridge | undefined;\n}\n\nexport function setupEvents(deps: SetupEventsDeps): void {\n const { events, broadcast, clients, config, context, pendingConfirms, globalConfigPath, sessionBridge } = deps;\n\n events.on('iteration.started', (e) => {\n // Read maxIterations from context.meta so the UI reflects the\n // webui setting, falling back to the startup config default.\n const maxIt = typeof context.meta['maxIterations'] === 'number'\n ? context.meta['maxIterations']\n : config.tools?.maxIterations ?? 100;\n broadcast(clients, {\n type: 'iteration.started',\n payload: { index: e.index, maxIterations: maxIt },\n });\n });\n\n events.on('provider.text_delta', (e) => {\n broadcast(clients, { type: 'provider.text_delta', payload: { text: e.text, messageId: 'current' } });\n });\n\n events.on('provider.thinking_delta', (e) => {\n broadcast(clients, { type: 'provider.thinking_delta', payload: { text: e.text } });\n });\n\n events.on('tool.started', (e) => {\n broadcast(clients, {\n type: 'tool.started',\n payload: { id: e.id, name: e.name, input: e.input, messageId: `tool_${e.id}` },\n });\n // Persist for audit + resume tool history (respects auditLevel).\n sessionBridge\n ?.append({\n type: 'tool_call_start',\n ts: new Date().toISOString(),\n name: e.name,\n id: e.id,\n input: e.input,\n })\n .catch(() => { /* best-effort */ });\n });\n\n events.on('tool.progress', (e) => {\n broadcast(clients, {\n type: 'tool.progress',\n payload: { id: e.id, name: e.name, eventType: e.event.type, text: e.event.text },\n });\n sessionBridge\n ?.append({\n type: 'tool_progress',\n ts: new Date().toISOString(),\n name: e.name,\n id: e.id,\n event: { type: e.event.type, text: e.event.text, data: e.event.data },\n })\n .catch(() => { /* best-effort */ });\n });\n\n events.on('tool.executed', (e) => {\n broadcast(clients, {\n type: 'tool.executed',\n payload: { id: e.id, name: e.name, durationMs: e.durationMs, ok: e.ok, input: e.input, output: e.output },\n });\n sessionBridge\n ?.append({\n type: 'tool_call_end',\n ts: new Date().toISOString(),\n name: e.name,\n id: e.id ?? '',\n durationMs: e.durationMs,\n outputSize: e.outputBytes ?? 0,\n ok: e.ok,\n outputBytes: e.outputBytes,\n outputTokens: e.outputTokens,\n outputLines: e.outputLines,\n })\n .catch(() => { /* best-effort */ });\n broadcast(clients, { type: 'todos.updated', payload: { todos: [...context.todos] } });\n\n // Broadcast task/plan updates after task/plan/todo tool executions.\n if (e.name === 'task' || e.name === 'plan' || e.name === 'todo') {\n void (async () => {\n try {\n const taskPath = (context.meta as Record<string, unknown>)['task.path'];\n if (typeof taskPath === 'string' && taskPath) {\n const { loadTasks } = await import('@wrongstack/core');\n const file = await loadTasks(taskPath);\n broadcast(clients, { type: 'tasks.updated', payload: { tasks: file?.tasks ?? [] } });\n }\n } catch { /* best-effort */ }\n try {\n const planPath = (context.meta as Record<string, unknown>)['plan.path'];\n if (typeof planPath === 'string' && planPath) {\n const { loadPlan } = await import('@wrongstack/core');\n const plan = await loadPlan(planPath);\n broadcast(clients, { type: 'plan.updated', payload: { plan: plan ?? { version: 1, sessionId: context.session?.id ?? '', updatedAt: new Date().toISOString(), items: [] } } });\n }\n } catch { /* best-effort */ }\n })();\n }\n });\n\n events.on('provider.response', (e) => {\n broadcast(clients, { type: 'provider.response', payload: { usage: e.usage, stopReason: e.stopReason, messageId: 'current' } });\n });\n\n events.on('context.repaired', (e) => {\n broadcast(clients, { type: 'context.repaired', payload: { removedToolUses: e.removedToolUses, removedToolResults: e.removedToolResults, removedMessages: e.removedMessages } });\n });\n\n events.on('tool.confirm_needed', (e) => {\n const id = e.toolUseId ?? `confirm_${Date.now()}`;\n pendingConfirms.set(id, e.resolve);\n broadcast(clients, { type: 'tool.confirm_needed', payload: { id, toolName: e.tool?.name ?? 'unknown', input: e.input, suggestedPattern: e.suggestedPattern } });\n });\n\n events.on('error', (e) => {\n broadcast(clients, { type: 'error', payload: { phase: e.phase, message: e.err instanceof Error ? e.err.message : String(e.err) } });\n sessionBridge\n ?.append({\n type: 'error',\n ts: new Date().toISOString(),\n message: e.err instanceof Error ? e.err.message : String(e.err),\n phase: e.phase,\n })\n .catch(() => { /* best-effort */ });\n });\n\n // Provider visibility — retry storms and provider failures in the JSONL\n // for forensics, mirroring the CLI's bridge wiring.\n events.on('provider.retry', (e) => {\n sessionBridge\n ?.append({\n type: 'provider_retry',\n ts: new Date().toISOString(),\n providerId: e.providerId,\n attempt: e.attempt,\n delayMs: e.delayMs,\n status: e.status,\n description: e.description,\n })\n .catch(() => { /* best-effort */ });\n });\n\n events.on('provider.error', (e) => {\n sessionBridge\n ?.append({\n type: 'provider_error',\n ts: new Date().toISOString(),\n providerId: e.providerId,\n status: e.status,\n description: e.description,\n retryable: e.retryable,\n })\n .catch(() => { /* best-effort */ });\n });\n\n // ── Inter-agent mailbox visibility ───────────────────────────────────\n // Forward cross-session mailbox activity (messages received by this\n // process's agents, new agent registrations on the project) to the\n // browser so the user sees multi-terminal/multi-surface chatter live.\n // These events are emitted via emit() with untyped names (GlobalMailbox\n // + mailbox-loop), so subscribe by pattern like the TUI does.\n events.onPattern('mailbox.received', (_e, payload) => {\n broadcast(clients, { type: 'mailbox.received', payload } as unknown as WSServerMessage);\n });\n events.onPattern('mailbox.agent_registered', (_e, payload) => {\n broadcast(clients, { type: 'mailbox.agent_registered', payload } as unknown as WSServerMessage);\n });\n\n // Subagent fleet lifecycle\n const forwardSubagent = (kind: string, payload: Record<string, unknown>) =>\n broadcast(clients, { type: 'subagent.event', payload: { kind, sessionId: context.session.id, ...payload } });\n\n events.on('subagent.spawned', (e) => forwardSubagent('spawned', { subagentId: e.subagentId, taskId: e.taskId, name: e.name, provider: e.provider, model: e.model, description: e.description }));\n events.on('subagent.task_started', (e) => forwardSubagent('task_started', { subagentId: e.subagentId, taskId: e.taskId, description: e.description }));\n events.on('subagent.tool_executed', (e) => forwardSubagent('tool_executed', { subagentId: e.subagentId, toolName: e.name, durationMs: e.durationMs, ok: e.ok }));\n events.on('subagent.iteration_summary', (e) => forwardSubagent('iteration_summary', { subagentId: e.subagentId, iteration: e.iteration, toolCalls: e.toolCalls, costUsd: e.costUsd, currentTool: e.currentTool, partialText: e.partialText }));\n events.on('subagent.budget_extended', (e) => forwardSubagent('budget_extended', { subagentId: e.subagentId, totalExtensions: e.totalExtensions }));\n events.on('subagent.ctx_pct', (e) => forwardSubagent('ctx_pct', { subagentId: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext }));\n events.on('subagent.task_completed', (e) => forwardSubagent('task_completed', { subagentId: e.subagentId, status: e.status, iterations: e.iterations, toolCalls: e.toolCalls, finalText: (e as Record<string, unknown>).finalText as string | undefined, error: e.error ? { kind: e.error.kind, message: e.error.message } : undefined }));\n\n // ── Leader (main session) events — forwarded as subagent.event with subagentId 'leader' ──\n // These give the AgentsPage a live leader row with real-time tool tracking,\n // context pressure — matching the TUI's leader entry.\n // Iteration counts, cost, and overall status come from the sessionStore on the frontend.\n\n // Leader spawned: sent on first iteration so the frontend creates the leader row.\n let leaderSpawned = false;\n events.on('iteration.started', () => {\n if (!leaderSpawned) {\n leaderSpawned = true;\n const provider = (context.provider as { id?: string } | undefined)?.id ?? 'unknown';\n forwardSubagent('spawned', {\n subagentId: 'leader',\n name: 'LEADER',\n provider,\n model: context.model,\n description: `Main agent session (${context.session.id})`,\n });\n }\n });\n\n // Leader tool execution: emitted on every tool.executed in the main session.\n events.on('tool.executed', (e) => {\n forwardSubagent('tool_executed', {\n subagentId: 'leader',\n toolName: e.name,\n durationMs: e.durationMs,\n ok: e.ok,\n });\n });\n\n // Leader context pressure + cost: emitted on every provider response.\n events.on('provider.response', (e) => {\n if (e.usage?.input != null) {\n const maxCtx = context.provider.capabilities.maxContext;\n const pct = maxCtx > 0 ? e.usage.input / maxCtx : 0;\n const costUsd = context.tokenCounter.estimateCost().total;\n forwardSubagent('ctx_pct', {\n subagentId: 'leader',\n load: pct,\n tokens: e.usage.input,\n maxContext: maxCtx,\n costUsd,\n });\n }\n });\n\n // Leader iteration updates: we already track iteration started above.\n // The frontend uses sessionStore for accurate cost/iteration counts.\n // When the run completes, the frontend's run.result handler resets isLoading,\n // making the leader go idle. We reset leader state on iteration.started.\n events.on('iteration.completed', () => {\n // Respawn leader if it was cleared (e.g., on session resume).\n if (!leaderSpawned) {\n leaderSpawned = true;\n const provider = (context.provider as { id?: string } | undefined)?.id ?? 'unknown';\n forwardSubagent('spawned', {\n subagentId: 'leader',\n name: 'LEADER',\n provider,\n model: context.model,\n description: `Main agent session (${context.session.id})`,\n });\n }\n });\n\n // ── Mailbox events — broadcast to WebUI for real-time per-project visibility ──\n events.onPattern('mailbox.*', (eventName, payload) => {\n broadcast(clients, { type: 'mailbox.event', payload: { event: eventName, ...payload as Record<string, unknown> } });\n });\n\n // ── Brain events — decisions + proactive interventions, live in the browser ──\n events.onPattern('brain.*', (eventName, payload) => {\n broadcast(clients, { type: 'brain.event', payload: { event: eventName, ...payload as Record<string, unknown> } } as unknown as WSServerMessage);\n });\n\n // ── Cross-process session / fleet status poll ──\n // Periodically read the SessionRegistry and broadcast live session+agent status\n // to all connected clients. Gives the AgentFlowViz a project-level overview of\n // how many sessions are active, what agents are doing, costs, and context usage.\n const globalRoot = globalConfigPath ? path.dirname(globalConfigPath) : undefined;\n if (globalRoot) {\n const statusInterval = setInterval(async () => {\n try {\n const { SessionRegistry } = await import('@wrongstack/core');\n const registry = new SessionRegistry(globalRoot);\n const sessions = await registry.list();\n const live = sessions\n .filter((s) => s.status !== 'stale')\n .map((s) => ({\n sessionId: s.sessionId,\n projectName: s.projectName,\n projectSlug: s.projectSlug,\n projectRoot: s.projectRoot,\n workingDir: s.workingDir,\n gitBranch: s.gitBranch,\n status: s.status,\n pid: s.pid,\n startedAt: s.startedAt,\n agentCount: s.agentCount,\n agents: (s.agents ?? []).map((a) => ({\n id: a.id,\n name: a.name,\n status: a.status,\n currentTool: a.currentTool,\n iterations: a.iterations,\n toolCalls: a.toolCalls,\n lastActivityAt: a.lastActivityAt,\n })),\n }));\n broadcast(clients, { type: 'sessions.status_update', payload: { sessions: live } });\n } catch {\n // Best-effort — never crash for status polling errors\n }\n }, 5_000);\n if (statusInterval.unref) statusInterval.unref();\n }\n}\n","import { listContextWindowModes, atomicWrite } from '@wrongstack/core';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\n/**\n * Custom context modes — user-defined presets that are loaded from disk,\n * merged with the built-in modes, and managed via WebSocket CRUD handlers.\n *\n * Stored in: ~/.wrongstack/custom-context-modes.json\n * Format: { \"modes\": ContextWindowMode[] }\n */\n\nexport interface CustomContextMode {\n id: string;\n name: string;\n description: string;\n thresholds: { warn: number; soft: number; hard: number };\n aggressiveOn: string;\n preserveK: number;\n eliseThreshold: number;\n targetLoad: number;\n /** Whether this is a user-defined (custom) or built-in mode. */\n custom: boolean;\n}\n\nexport interface CustomModeStore {\n modes: Map<string, CustomContextMode>;\n load: () => Promise<void>;\n save: () => Promise<void>;\n create: (mode: CustomContextMode) => { ok: boolean; error?: string | undefined };\n update: (id: string, patch: Partial<CustomContextMode>) => { ok: boolean; error?: string | undefined };\n remove: (id: string) => { ok: boolean; error?: string | undefined };\n list: () => CustomContextMode[];\n}\n\nconst STORE_FILENAME = 'custom-context-modes.json';\n\nfunction storePath(wrongstackDir: string): string {\n return path.join(wrongstackDir, STORE_FILENAME);\n}\n\nconst BUILTIN_IDS = new Set(['balanced', 'frugal', 'deep', 'archival']);\n\nexport function createCustomModeStore(wrongstackDir: string): CustomModeStore {\n const modes = new Map<string, CustomContextMode>();\n\n const load = async (): Promise<void> => {\n modes.clear();\n try {\n const raw = await fs.readFile(storePath(wrongstackDir), 'utf8');\n const parsed = JSON.parse(raw) as { modes?: CustomContextMode[] };\n if (Array.isArray(parsed.modes)) {\n for (const m of parsed.modes) {\n if (m.id && !BUILTIN_IDS.has(m.id)) {\n modes.set(m.id, { ...m, custom: true });\n }\n }\n }\n } catch {\n // File missing or corrupt — start with empty custom modes.\n }\n };\n\n const save = async (): Promise<void> => {\n const arr = [...modes.values()];\n const json = JSON.stringify({ modes: arr }, null, 2);\n await atomicWrite(storePath(wrongstackDir), json);\n };\n\n const create = (\n mode: CustomContextMode,\n ): { ok: boolean; error?: string | undefined } => {\n if (!mode.id || typeof mode.id !== 'string') {\n return { ok: false, error: 'id is required' };\n }\n if (BUILTIN_IDS.has(mode.id)) {\n return { ok: false, error: `Cannot override built-in mode \"${mode.id}\"` };\n }\n if (modes.has(mode.id)) {\n return { ok: false, error: `Mode \"${mode.id}\" already exists` };\n }\n if (!mode.name) {\n return { ok: false, error: 'name is required' };\n }\n const entry: CustomContextMode = {\n id: mode.id,\n name: mode.name,\n description: mode.description || '',\n thresholds: {\n warn: mode.thresholds?.warn ?? 0.6,\n soft: mode.thresholds?.soft ?? 0.75,\n hard: mode.thresholds?.hard ?? 0.9,\n },\n aggressiveOn: mode.aggressiveOn || 'soft',\n preserveK: mode.preserveK ?? 10,\n eliseThreshold: mode.eliseThreshold ?? 2000,\n targetLoad: mode.targetLoad ?? 0.65,\n custom: true,\n };\n modes.set(mode.id, entry);\n void save();\n return { ok: true };\n };\n\n const update = (\n id: string,\n patch: Partial<CustomContextMode>,\n ): { ok: boolean; error?: string | undefined } => {\n if (BUILTIN_IDS.has(id)) {\n return { ok: false, error: `Cannot modify built-in mode \"${id}\"` };\n }\n const existing = modes.get(id);\n if (!existing) {\n return { ok: false, error: `Mode \"${id}\" not found` };\n }\n const next: CustomContextMode = { ...existing };\n if (patch.name !== undefined) next.name = patch.name;\n if (patch.description !== undefined) next.description = patch.description;\n if (patch.thresholds) {\n next.thresholds = {\n warn: patch.thresholds.warn ?? existing.thresholds.warn,\n soft: patch.thresholds.soft ?? existing.thresholds.soft,\n hard: patch.thresholds.hard ?? existing.thresholds.hard,\n };\n }\n if (patch.preserveK !== undefined) next.preserveK = patch.preserveK;\n if (patch.eliseThreshold !== undefined) next.eliseThreshold = patch.eliseThreshold;\n if (patch.targetLoad !== undefined) next.targetLoad = patch.targetLoad;\n if (patch.aggressiveOn !== undefined) next.aggressiveOn = patch.aggressiveOn;\n modes.set(id, next);\n void save();\n return { ok: true };\n };\n\n const remove = (\n id: string,\n ): { ok: boolean; error?: string | undefined } => {\n if (BUILTIN_IDS.has(id)) {\n return { ok: false, error: `Cannot delete built-in mode \"${id}\"` };\n }\n if (!modes.delete(id)) {\n return { ok: false, error: `Mode \"${id}\" not found` };\n }\n void save();\n return { ok: true };\n };\n\n const list = (): CustomContextMode[] => {\n const builtins = listContextWindowModes().map((m) => ({\n id: m.id as string,\n name: m.name,\n description: m.description,\n thresholds: { ...m.thresholds },\n aggressiveOn: m.aggressiveOn as string,\n preserveK: m.preserveK,\n eliseThreshold: m.eliseThreshold,\n targetLoad: m.targetLoad,\n custom: false as const,\n }));\n const custom = [...modes.values()];\n return [...builtins, ...custom];\n };\n\n return { modes, load, save, create, update, remove, list };\n}\n","/**\n * Per-section context-window token estimate for the `context.debug` command.\n *\n * Uses the simple 4-chars-per-token heuristic — not exact, but close enough to\n * spot which section (system prompt, tool schemas, or message history) is\n * eating the context window. Tool schemas in particular are easy to overlook:\n * each tool ships its full JSON schema to the model every turn, so 20+ builtins\n * can cost 10-20k tokens on their own.\n *\n * Extracted from `index.ts` as a pure function so the breakdown maths can be\n * unit tested without standing up a Context/ToolRegistry.\n */\n\n/** 4-chars-per-token heuristic estimate for a string. */\nexport function estimateTokens(s: string): number {\n return Math.ceil(s.length / 4);\n}\n\n/** Stringify arbitrary content for length estimation (JSON, with fallbacks). */\nexport function stringifyContent(c: unknown): string {\n if (typeof c === 'string') return c;\n try {\n return JSON.stringify(c);\n } catch {\n return String(c);\n }\n}\n\ninterface PromptBlock {\n text?: string | undefined;\n}\ninterface ToolLike {\n name: string;\n inputSchema?: unknown | undefined;\n description?: string | undefined;\n}\ninterface ContentBlock {\n type?: string | undefined;\n text?: string | undefined;\n input?: unknown | undefined;\n content?: unknown | undefined;\n name?: string | undefined;\n}\ninterface MessageLike {\n role: string;\n content: unknown;\n}\n\nexport interface ToolTokenEntry {\n name: string;\n tokens: number;\n}\nexport interface MessageTokenEntry {\n index: number;\n role: string;\n tokens: number;\n preview: string;\n}\n\nexport interface ContextBreakdown {\n total: number;\n systemPrompt: number;\n tools: { total: number; count: number; breakdown: ToolTokenEntry[] };\n messages: { total: number; count: number; breakdown: MessageTokenEntry[] };\n}\n\nexport function messageTokens(content: unknown): number {\n if (typeof content === 'string') return estimateTokens(content);\n if (!Array.isArray(content)) return 0;\n let tk = 0;\n for (const b of content as ContentBlock[]) {\n if (b.type === 'text') tk += estimateTokens(b.text ?? '');\n else if (b.type === 'tool_use') tk += estimateTokens(stringifyContent(b.input));\n else if (b.type === 'tool_result') tk += estimateTokens(stringifyContent(b.content));\n else tk += estimateTokens(stringifyContent(b));\n }\n return tk;\n}\n\nexport function messagePreview(content: unknown): string {\n if (typeof content === 'string') return content.slice(0, 60);\n if (!Array.isArray(content)) return '';\n return (content as ContentBlock[])\n .map((b) =>\n b.type === 'text'\n ? (b.text ?? '').slice(0, 40)\n : b.type === 'tool_use'\n ? `[tool_use: ${b.name}]`\n : b.type === 'tool_result'\n ? '[tool_result]'\n : `[${b.type}]`,\n )\n .join(' ')\n .slice(0, 60);\n}\n\n/**\n * Compute the per-section token breakdown for the active context. Mirrors the\n * shape the `context.debug` WS reply expects (minus the `mode`/`policy` fields,\n * which the caller layers on from `context.meta`).\n */\nexport function estimateContextBreakdown(input: {\n systemPrompt: ReadonlyArray<PromptBlock>;\n tools: ReadonlyArray<ToolLike>;\n messages: ReadonlyArray<MessageLike>;\n}): ContextBreakdown {\n const sysTokens = input.systemPrompt.reduce((acc, b) => acc + estimateTokens(b.text ?? ''), 0);\n\n const toolBreakdown: ToolTokenEntry[] = input.tools.map((t) => {\n const schema = t.inputSchema ?? {};\n const desc = t.description ?? '';\n return {\n name: t.name,\n tokens:\n estimateTokens(t.name) + estimateTokens(desc) + estimateTokens(stringifyContent(schema)),\n };\n });\n const toolTokens = toolBreakdown.reduce((a, b) => a + b.tokens, 0);\n\n const messageBreakdown: MessageTokenEntry[] = input.messages.map((m, i) => ({\n index: i,\n role: m.role,\n tokens: messageTokens(m.content),\n preview: messagePreview(m.content),\n }));\n const msgTokens = messageBreakdown.reduce((a, b) => a + b.tokens, 0);\n\n return {\n total: sysTokens + toolTokens + msgTokens,\n systemPrompt: sysTokens,\n tools: { total: toolTokens, count: input.tools.length, breakdown: toolBreakdown },\n messages: { total: msgTokens, count: input.messages.length, breakdown: messageBreakdown },\n };\n}\n","// Eternal-autonomy iteration broadcast wiring.\n//\n// The CLI's `runWebUI` owns the eternal-autonomy engine and exposes a\n// `subscribeEternalIteration` callback that lets the webui hook into\n// the journal-entry stream. This module extracts the wiring in\n// isolation so it can be unit-tested without spinning up the full\n// server (clients, WS, HTTP bring-up).\n//\n// `createEternalSubscription` is the helper that:\n// 1. Calls the caller-supplied `subscribe` function with a broadcast\n// closure bound to the current `clients` Map.\n// 2. Captures the returned disposer so `tearDown` can invoke it on\n// server shutdown.\n//\n// Both the disposer and the `JournalEntry` are projected by the caller\n// — this module intentionally knows nothing about the engine itself.\n\nimport type { WebSocket } from 'ws';\nimport type { WSServerMessage } from './types.js';\nimport type { JournalEntry } from '@wrongstack/core';\n\nexport type EternalSubscribe = (\n fn: (entry: JournalEntry) => void,\n) => () => void;\n\n// `clients` is generic so callers that use a structurally-similar\n// `ConnectedClient` (the CLI's own Map<WebSocket, { ws; sessionId }>,\n// for example) don't have to alias their type to webui's\n// `ConnectedClient`. The helper doesn't read the value at all —\n// `broadcast` gets to decide whether to use the map or not — so we\n// only need the key type (WebSocket) to be present, which is\n// enforced by the constraint.\nexport type EternalBroadcast<C> = (clients: Map<WebSocket, C>, msg: WSServerMessage) => void;\n\nexport interface EternalSubscription {\n /** Tear down the underlying engine subscription. Idempotent. */\n dispose: () => void;\n}\n\nexport function createEternalSubscription<C>(\n subscribe: EternalSubscribe,\n broadcast: EternalBroadcast<C>,\n clientsRef: () => Map<WebSocket, C>,\n): EternalSubscription {\n let disposed = false;\n const dispose = subscribe((entry) => {\n if (disposed) return;\n broadcast(clientsRef(), {\n type: 'eternal.iteration',\n payload: { entry },\n });\n });\n return {\n dispose() {\n if (disposed) return;\n disposed = true;\n dispose();\n },\n };\n}\n","// Open the OS file manager or a terminal at a given path (the\n// `shell.open` WS message handler).\n//\n// Both the standalone `startWebUI` and the CLI's `runWebUI` needed\n// the same logic \\u2014 metacharacter guard, `path.resolve` to fold\n// any `..` traversal, and a cross-platform `spawn()` for the\n// platform's file manager (`explorer`/`open`/`xdg-open`) or\n// terminal (`cmd /c start cmd /k` / `open -a Terminal` /\n// `x-terminal-emulator` \\u2192 `gnome-terminal` \\u2192 `xterm` fallback chain).\n// Phase 1.2 added the `spawn`-based version on the CLI side; the\n// standalone got it shortly after. The two implementations have\n// drifted slightly (CLI lacks the `logger.warn` on spawn failure),\n// which is exactly the class of bug extraction prevents.\n//\n// This module returns a `ShellOpenResult` so the caller (the WS\n// router in either entry point) can pipe it through `sendResult`\n// with the same `success`/`message` shape both sides already use.\n// Spawn is async + detached + unref'd so a missing terminal\n// emulator (which fires `error` async) never crashes the server\n// \\u2014 the fallback chain tries the next one, the file-manager\n// branch just logs.\n//\n// SECURITY: the path arrives over the WebSocket. The\n// metacharacter guard (`/[&|<>^\\\"'`\\n\\r]/`) closes the cmd.exe\n// re-parsing injection class (`\"foo\" && calc.exe`,\n// `'$(...)'`, backticks, redirections) before anything reaches\n// the argv array. The `path.resolve` then folds any\n// `..` traversal, and `fs.access(resolved)` enforces that the\n// target exists \\u2014 callers can't ask us to open a non-existent\n// path. Defense in depth: the spawn below uses an argv array\n// (no string concatenation), and `windowsHide` keeps the\n// launcher console out of the way.\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport type { Logger } from '@wrongstack/core';\n\nexport type ShellOpenTarget = 'terminal' | 'file-manager';\n\nexport interface ShellOpenRequest {\n path: string;\n target: ShellOpenTarget;\n}\n\nexport interface ShellOpenResult {\n success: boolean;\n message: string;\n}\n\n/** Metacharacters that would let a path slip past an argv-array\n * spawn and into a shell re-parse on Windows. Real directory\n * paths virtually never contain these. */\nconst METACHAR_REGEX = /[&|<>^\"'`\\n\\r]/;\n\nexport async function handleShellOpen(\n req: ShellOpenRequest,\n logger: Logger,\n): Promise<ShellOpenResult> {\n try {\n const resolved = path.resolve(req.path);\n await fs.access(resolved);\n if (METACHAR_REGEX.test(resolved)) {\n return { success: false, message: 'Path contains unsupported characters.' };\n }\n\n const platform = process.platform;\n const launch = (cmd: string, args: string[], onError?: () => void) => {\n const child = spawn(cmd, args, {\n detached: true,\n stdio: 'ignore',\n windowsHide: true,\n });\n // `error` fires when the binary is missing (e.g. xterm not\n // installed) \\u2014 log it, but never block the WS caller. The\n // fallback chain in the terminal branch uses onError to try\n // the next emulator.\n child.on('error', (err) => {\n logger.warn(`shell.open spawn failed: ${err.message}`);\n onError?.();\n });\n child.unref();\n };\n\n if (req.target === 'file-manager') {\n if (platform === 'win32') launch('explorer', [resolved]);\n else if (platform === 'darwin') launch('open', [resolved]);\n else launch('xdg-open', [resolved]);\n } else if (req.target === 'terminal') {\n if (platform === 'win32') {\n // `start` is a cmd builtin; each token is a separate argv\n // entry (Node quotes them individually \\u2014 no string\n // concatenation). This replaces the previous\n // `start cmd /k cd /d \"...\"` exec() call that was\n // shell-injectable.\n launch('cmd', ['/c', 'start', 'cmd', '/k', 'cd', '/d', resolved]);\n } else if (platform === 'darwin') {\n launch('open', ['-a', 'Terminal', resolved]);\n } else {\n // Try several terminal emulators\n launch('x-terminal-emulator', [`--working-directory=${resolved}`], () =>\n launch('gnome-terminal', [`--working-directory=${resolved}`], () =>\n launch('xterm', ['-e', `cd '${resolved}' && ${process.env['SHELL'] ?? 'sh'}`]),\n ),\n );\n }\n } else {\n return { success: false, message: `Unknown shell.open target: ${String(req.target)}` };\n }\n return { success: true, message: `Opened ${req.target} at ${resolved}` };\n } catch (err) {\n return { success: false, message: err instanceof Error ? err.message : String(err) };\n }\n}\n","// Server entry point for standalone WebUI.\n// Bind defaults: 127.0.0.1:3457 (loopback only). Override with WS_HOST / WS_PORT.\n// HTTP frontend defaults to 3456 (override with PORT). Run several instances on\n// different PORT/WS_PORT pairs — `webui --list` shows which are open for which\n// project (registry: ~/.wrongstack/webui-instances.json).\nimport { startWebUI } from './index.js';\nimport { formatInstances, listInstances } from './instance-registry.js';\n\nconst argv = process.argv.slice(2);\n\n// `webui --list` / `webui ls` — print running instances and exit. Cheap,\n// side-effect-free (it only prunes dead pids), so it never boots a server.\nif (argv.includes('--list') || argv.includes('-l') || argv[0] === 'ls') {\n listInstances()\n .then((instances) => {\n console.log(formatInstances(instances));\n process.exit(0);\n })\n .catch((err) => {\n console.error(JSON.stringify({\n level: 'fatal',\n event: 'webui.instance_registry_read_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n process.exit(1);\n });\n} else {\n const wsPort = Number.parseInt(process.env['WS_PORT'] ?? '3457', 10);\n const wsHost = process.env['WS_HOST'] ?? '127.0.0.1';\n const open =\n argv.includes('--open') || argv.includes('-o') || process.env['WEBUI_OPEN'] === '1';\n\n console.log(`[WebUI] Starting standalone server on ${wsHost}:${wsPort}...`);\n\n startWebUI({ wsPort, wsHost, open }).catch((err) => {\n console.error(JSON.stringify({\n level: 'fatal',\n event: 'webui.startup_failed',\n message: err instanceof Error ? err.message : String(err),\n timestamp: new Date().toISOString(),\n }));\n process.exit(1);\n });\n}\n"],"mappings":"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","names":["expectDefined","GlobalMailbox","fs","path","Buffer","fs","path","DefaultMemoryStore","DefaultModeStore","DefaultSessionStore","DefaultSkillLoader","DefaultSystemPromptBuilder","DefaultTokenCounter","createStrategyCompactor","TOKENS","atomicWrite","decryptConfigSecrets","encryptConfigSecrets","s","path","path","fs","atomicWrite","resolve","fs","path","atomicWrite","broadcast","path","broadcast","atomicWrite","fs","path","load","save","broadcast","fs","path","spawn","expectDefined","TOKENS","DefaultMemoryStore","DefaultSessionStore","DefaultTokenCounter","DefaultModeStore","DefaultSkillLoader","DefaultSystemPromptBuilder","GlobalMailbox","decryptConfigSecrets","encryptConfigSecrets","atomicWrite","createStrategyCompactor","provider","verifyClient","resolve","globalConfigPath","modePrompt","stat"]}
|