@supanadit/morse-web 0.13.0 → 0.14.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/README.md +53 -10
- package/dist/server.mjs +692 -85
- package/dist/server.mjs.map +4 -4
- package/dist/ui/3rdpartylicenses.txt +4 -4
- package/dist/ui/{chunk-D1etuzzZ.js → chunk-CwVAC7xx.js} +1 -1
- package/dist/ui/{chunk-CVTlMVVu.js → chunk-Dk_j780x.js} +1 -1
- package/dist/ui/{chunk-ourdPesa.js → chunk-veepV9dF.js} +1 -1
- package/dist/ui/index.html +1 -1
- package/dist/ui/main-L3KG4R7V.js +120 -0
- package/dist/ui/webview.manifest.json +3 -3
- package/package.json +12 -1
- package/dist/ui/main-TLDRX4Y6.js +0 -120
package/dist/server.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../server/src/main.ts", "../../protocol/src/wire.ts", "../../protocol/src/version.ts", "../../protocol/src/frontend-manifest.ts", "../../server/src/app.module.ts", "../../adapter-pi-rpc/src/pi-rpc-agent.ts", "../../core/src/domain.ts", "../../core/src/errors.ts", "../../core/src/session/service.ts", "../../core/src/chat/service.ts", "../../adapter-pi-rpc/src/internal/rpc-types.ts", "../../adapter-pi-rpc/src/event-mapping.ts", "../../adapter-pi-rpc/src/internal/rpc-client.ts", "../../adapter-pi-rpc/src/internal/jsonl-framer.ts", "../../adapter-pi-rpc/src/internal/resolve-pi.ts", "../../adapter-pi-rpc/src/pi-rpc-agent-factory.ts", "../../adapter-pi-rpc/src/pi-rpc-session-catalog.ts", "../../adapter-pi-rpc/src/create-pi-rpc-adapter.ts", "../../host-runtime/src/terminal-buffer.ts", "../../host-runtime/src/transcript-projector.ts", "../../host-runtime/src/transcript-store.ts", "../../host-runtime/src/view-state.ts", "../../host-runtime/src/session-controller.ts", "../../server/src/internal/http/health.controller.ts", "../../server/src/app/tokens.ts", "../../server/src/internal/logging/nest-logger.ts", "../../server/src/internal/projects/project-policy.ts", "../../server/src/internal/terminal/terminal.service.ts", "../../server/src/internal/terminal/terminal-log.ts", "../../server/src/internal/registry.ts", "../../server/src/internal/ws/morse.gateway.ts", "../../server/src/internal/ws/session-factory.service.ts", "../../server/src/internal/uploads/upload-store.ts", "../../server/src/internal/workspace/directory-browser.ts", "../../server/src/internal/workspace/file-store.ts", "../../server/src/internal/workspace/git-log.ts", "../../server/src/internal/workspace/workspace-index.ts", "../../server/src/internal/workspace/workbench-store.ts", "../../server/src/app/config.ts"],
|
|
4
|
-
"sourcesContent": ["import 'reflect-metadata';\nimport { Logger } from '@nestjs/common';\nimport { NestFactory } from '@nestjs/core';\nimport type { NestExpressApplication } from '@nestjs/platform-express';\nimport { WsAdapter } from '@nestjs/platform-ws';\nimport { DEFAULT_WS_PATH } from '@morse/protocol';\nimport { AppModule } from './app.module.js';\nimport { MORSE_CONFIG } from './app/tokens.js';\nimport type { MorseServerConfig } from './app/config.js';\n\n/**\n * Composition root entry point. Morse has two of these \u2014 this one and the VS\n * Code extension \u2014 and both wire the same core + adapter packages.\n */\nasync function bootstrap(): Promise<void> {\n const app = await NestFactory.create<NestExpressApplication>(AppModule);\n const config = app.get<MorseServerConfig>(MORSE_CONFIG);\n\n app.useWebSocketAdapter(new WsAdapter(app));\n // Serves the built Angular frontend. No SPA fallback is registered, so\n // /api/* and /ws keep working.\n app.useStaticAssets(config.uiDir);\n app.enableCors({ origin: true });\n // Release the port promptly and dispose agent sessions on SIGINT/SIGTERM.\n // Without this, `node --watch` restarts race with the old process and fail\n // with EADDRINUSE, and spawned pi processes are leaked.\n app.enableShutdownHooks();\n\n await app.listen(config.port, config.host);\n\n const logger = new Logger('morse');\n logger.log(`Morse server listening on http://${config.host}:${config.port}`);\n logger.log(`WebSocket endpoint: ws://${config.host}:${config.port}${DEFAULT_WS_PATH}`);\n logger.log(`Frontend served from ${config.uiDir}`);\n logger.log(`Workspace: ${config.workspace.cwd} (override with MORSE_WORKSPACE)`);\n}\n\nvoid bootstrap();\n", "import type {\n ChatPin,\n ComposerSeed,\n FrontendIdentity,\n HostCapabilities,\n InteractionRequest,\n InteractionResponse,\n NoticeLevel,\n ProjectSummary,\n PromptImage,\n SessionActivity,\n SessionSummary,\n SessionViewState,\n ThinkingLevel,\n TranscriptItem,\n} from './dto.js';\n\nexport type PromptMode = 'new' | 'steer' | 'followUp';\n\nexport type HostCommand =\n | 'listFiles'\n | 'readFile'\n | 'listDirectories'\n | 'uploadFile'\n | 'gitLog'\n | 'gitStatus'\n | 'gitDiff'\n /** The paths a single commit touched, for the graph's expandable row. */\n | 'gitCommitFiles'\n /** One file's unified diff inside a commit (not against HEAD). */\n | 'gitCommitDiff'\n /** How far HEAD is from its upstream, for the pull/push controls. */\n | 'gitSync'\n /** Pull the upstream into the working tree. */\n | 'gitPull'\n /** Push HEAD to its upstream. */\n | 'gitPush'\n /** Commit the staged changes with a message. */\n | 'gitCommit'\n /** The branches the panel can switch to. */\n | 'gitBranches'\n /** Switch to a branch, optionally creating it first. */\n | 'gitCheckout'\n /** Stage the named paths in the viewing session's repository. */\n | 'gitStage'\n /** Unstage the named paths (keep the working-tree change). */\n | 'gitUnstage'\n /** The shell layout the reader left behind (`~/.morse/workbench.json`). */\n | 'readWorkbench'\n /** Writes the shell layout back, so the next visit opens the same tabs. */\n | 'saveWorkbench'\n /**\n * The half-written prompts the reader left behind, per tab (`~/.morse/drafts.json`).\n * Separate from the layout because a draft can carry inline images and is much\n * larger, and because it is rewritten as the reader types.\n */\n | 'readDrafts'\n | 'saveDrafts'\n | 'openSettings'\n | 'showOutput'\n | 'copyToClipboard'\n | 'insertIntoEditor'\n | 'revealFile'\n | 'getEditorContext'\n /** Ask the host to confirm a destructive action in its own dialog. */\n | 'confirmDeleteSession'\n /**\n * Show a notification of the host's own (a finished run the reader stepped\n * away from). Only offered where `capabilities.notify` is set.\n */\n | 'notify';\n\n/** Host -> frontend. */\nexport type HostToClientMessage =\n | {\n type: 'host/ready';\n payload: {\n protocolVersion: number;\n capabilities: HostCapabilities;\n state: SessionViewState;\n frontend?: FrontendIdentity;\n };\n }\n | { type: 'session/state'; payload: SessionViewState }\n | { type: 'project/list'; payload: { projects: ProjectSummary[] } }\n /** Every live session, so the frontend can mark several as working at once. */\n | { type: 'session/activity'; payload: { sessions: SessionActivity[] } }\n | { type: 'transcript/append'; payload: TranscriptItem }\n | { type: 'transcript/update'; payload: TranscriptItem }\n | { type: 'transcript/delta'; payload: { id: string; text?: string; thinking?: string } }\n | { type: 'transcript/replace'; payload: { items: TranscriptItem[] } }\n /** A page of older history, inserted in front of what the client already has. */\n | { type: 'transcript/prepend'; payload: { items: TranscriptItem[] } }\n | { type: 'session/list'; payload: { sessions: SessionSummary[] } }\n | { type: 'interaction/request'; payload: InteractionRequest }\n | { type: 'interaction/dismiss'; payload: { requestId: string } }\n | { type: 'notice'; payload: { level: NoticeLevel; text: string; at: number } }\n /**\n * The host pinned the editor selection (the title-bar attach command). The\n * frontend owns the chip; the prompt text itself never changes behind the\n * user's back.\n */\n | { type: 'context/selection'; payload: { path: string; startLine?: number; endLine?: number } }\n /**\n * What the user has selected in the editor right now, streamed as they drag.\n * This is a preview, not a pin: the frontend shows it as a live chip that\n * keeps updating (dragging and even multi-selection) until the user clicks\n * it to lock. A payload without `startLine` means the selection is gone; the\n * frontend hides the preview without touching the locked pins.\n */\n | { type: 'context/selectionLive'; payload: { path: string; startLine?: number; endLine?: number } }\n /**\n * Hands a forked prompt back to the composer: the message the branch\n * re-opened, with its attachments. Sent after a successful `chat/fork` \u2014 a\n * fork that pi cancelled leaves the composer alone.\n */\n | { type: 'composer/seed'; payload: ComposerSeed }\n /** Answer to `host/command` when the frontend asked for a value. */\n | {\n type: 'host/command/result';\n payload: { requestId: string; ok: boolean; data?: unknown; error?: string };\n }\n | { type: 'error'; payload: { message: string; detail?: string; at: number } }\n /**\n * Bytes the shell wrote, as they arrive. A terminal is a stream, not a\n * request: `terminal/open` starts it and this carries everything after.\n */\n | { type: 'terminal/output'; payload: { terminalId: string; data: string } }\n /** The shell ended (or never started); no more output follows for this id. */\n | {\n type: 'terminal/exit';\n payload: { terminalId: string; code?: number; error?: string };\n };\n\n/** Frontend -> host. */\nexport type ClientToHostMessage =\n | { type: 'client/ready'; payload: { protocolVersion: number; frontend?: FrontendIdentity } }\n | {\n type: 'chat/prompt';\n payload: { text: string; mode?: PromptMode; images?: PromptImage[]; pins?: ChatPin[] };\n }\n /**\n * Replaces a past user message: the host forks the conversation before it,\n * drops that turn, and sends the edited text as a new prompt. `itemId` is the\n * transcript item being edited; `text` is what the user typed in its place.\n */\n | { type: 'chat/edit'; payload: { itemId: string; text: string } }\n /**\n * Branches a new session before a past user message: the host forks the\n * conversation (pi's `fork`), leaves the old branch resumable, and re-homes\n * the transcript onto the new branch with an empty tail. Then it seeds the\n * composer with the forked prompt (`composer/seed`).\n */\n | { type: 'chat/fork'; payload: { itemId: string } }\n | { type: 'chat/abort'; payload: Record<string, never> }\n | { type: 'session/new'; payload: { cwd?: string } }\n | { type: 'session/load'; payload: { sessionId: string; cwd?: string } }\n | { type: 'session/activate'; payload: { sessionId: string; cwd?: string } }\n | { type: 'session/close'; payload: { sessionId: string } }\n /**\n * Deletes a session and its stored conversation for good. Closing only\n * retires the agent process (the session stays resumable); deleting removes\n * the session file. What that means per host is the catalog's business.\n */\n | { type: 'session/delete'; payload: { sessionId: string } }\n | { type: 'session/compact'; payload: { instructions?: string } }\n | { type: 'history/load'; payload: Record<string, never> }\n | { type: 'session/list'; payload: Record<string, never> }\n /**\n * Re-reads the resources the composer's palette lists. pi caches its prompt\n * templates at spawn, so a newly added template is invisible until this \u2014 the\n * host re-reads the template files and answers with a fresh `session/state`.\n */\n | { type: 'commands/refresh'; payload: Record<string, never> }\n | { type: 'project/list'; payload: Record<string, never> }\n | { type: 'project/open'; payload: { path: string } }\n | { type: 'model/set'; payload: { provider: string; id: string } }\n | { type: 'thinking/set'; payload: { level: ThinkingLevel } }\n | { type: 'interaction/respond'; payload: InteractionResponse }\n | {\n type: 'host/command';\n payload: { command: HostCommand; args?: Record<string, unknown>; requestId?: string };\n }\n /**\n * Starts an interactive shell in the viewing session's directory (or `cwd`)\n * and streams it back as `terminal/output` / `terminal/exit`.\n */\n | {\n type: 'terminal/open';\n payload: { terminalId: string; cwd?: string; cols?: number; rows?: number };\n }\n /** Keystrokes (or a pasted line) for a running terminal. */\n | { type: 'terminal/input'; payload: { terminalId: string; data: string } }\n /** The viewer's terminal was resized; the shell gets the new geometry. */\n | { type: 'terminal/resize'; payload: { terminalId: string; cols: number; rows: number } }\n /** Closes a terminal and kills its shell. */\n | { type: 'terminal/close'; payload: { terminalId: string } };\n\nexport type WireMessage = HostToClientMessage | ClientToHostMessage;\n\nexport const HOST_MESSAGE_TYPES = [\n 'host/ready',\n 'session/state',\n 'project/list',\n 'session/activity',\n 'transcript/append',\n 'transcript/update',\n 'transcript/delta',\n 'transcript/replace',\n 'transcript/prepend',\n 'session/list',\n 'interaction/request',\n 'interaction/dismiss',\n 'notice',\n 'context/selection',\n 'context/selectionLive',\n 'composer/seed',\n 'host/command/result',\n 'error',\n 'terminal/output',\n 'terminal/exit',\n] as const;\n\nexport const CLIENT_MESSAGE_TYPES = [\n 'client/ready',\n 'chat/prompt',\n 'chat/edit',\n 'chat/fork',\n 'chat/abort',\n 'session/new',\n 'session/load',\n 'session/activate',\n 'session/close',\n 'session/delete',\n 'session/compact',\n 'history/load',\n 'session/list',\n 'commands/refresh',\n 'project/list',\n 'project/open',\n 'model/set',\n 'thinking/set',\n 'interaction/respond',\n 'host/command',\n 'terminal/open',\n 'terminal/input',\n 'terminal/resize',\n 'terminal/close',\n] as const;\n\nexport function encodeWireMessage(message: WireMessage): string {\n return JSON.stringify(message);\n}\n\nexport function isHostToClientMessage(value: unknown): value is HostToClientMessage {\n return hasKnownType(value, HOST_MESSAGE_TYPES);\n}\n\nexport function isClientToHostMessage(value: unknown): value is ClientToHostMessage {\n return hasKnownType(value, CLIENT_MESSAGE_TYPES);\n}\n\n/** Accepts a JSON string or an already parsed object; returns undefined when invalid. */\nexport function parseHostMessage(raw: unknown): HostToClientMessage | undefined {\n const value = decode(raw);\n return isHostToClientMessage(value) ? value : undefined;\n}\n\nexport function parseClientMessage(raw: unknown): ClientToHostMessage | undefined {\n const value = decode(raw);\n return isClientToHostMessage(value) ? value : undefined;\n}\n\n/**\n * Transport-agnostic decode: WebSocket frames may arrive as Buffer/Uint8Array,\n * the VS Code webview passes objects, tests pass strings.\n */\nexport function decode(raw: unknown): unknown {\n if (typeof raw === 'string') {\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n }\n if (raw instanceof Uint8Array) {\n return decode(new TextDecoder().decode(raw));\n }\n if (raw instanceof ArrayBuffer) {\n return decode(new TextDecoder().decode(new Uint8Array(raw)));\n }\n return raw;\n}\n\nfunction hasKnownType(value: unknown, allowed: readonly string[]): boolean {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const type = (value as { type?: unknown }).type;\n return typeof type === 'string' && allowed.includes(type);\n}\n", "/**\n * Wire protocol version. Bump this (and the frontends) whenever a message shape\n * changes in a breaking way \u2014 hosts refuse to talk to a mismatched frontend.\n */\nexport const PROTOCOL_VERSION = 21;\n\n/** File a frontend build writes next to its index.html so any host can identify it. */\nexport const FRONTEND_MANIFEST_FILE = 'webview.manifest.json';\n\n/** Default HTTP path the NestJS host exposes its WebSocket endpoint on. */\nexport const DEFAULT_WS_PATH = '/ws';\n", "import type { FrontendIdentity } from './dto.js';\nimport { FRONTEND_MANIFEST_FILE, PROTOCOL_VERSION } from './version.js';\n\n/**\n * A frontend build writes this next to its index.html. It is what makes the\n * frontend swappable without touching host code: the host reads the manifest,\n * checks `protocolVersion`, and serves whatever is in the directory.\n */\nexport interface FrontendManifest extends FrontendIdentity {\n protocolVersion: number;\n entry: string;\n generatedAt: string;\n}\n\nexport function createFrontendManifest(input: {\n name: string;\n version: string;\n entry?: string;\n generatedAt?: string;\n}): FrontendManifest {\n return {\n name: input.name,\n version: input.version,\n protocolVersion: PROTOCOL_VERSION,\n entry: input.entry ?? 'index.html',\n generatedAt: input.generatedAt ?? new Date().toISOString(),\n };\n}\n\nexport function isFrontendManifest(value: unknown): value is FrontendManifest {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const candidate = value as Partial<Record<keyof FrontendManifest, unknown>>;\n return (\n typeof candidate.name === 'string' &&\n typeof candidate.version === 'string' &&\n typeof candidate.protocolVersion === 'number' &&\n typeof candidate.entry === 'string'\n );\n}\n\nexport function parseFrontendManifest(raw: string | undefined | null): FrontendManifest | undefined {\n if (!raw) {\n return undefined;\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n return isFrontendManifest(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function frontendIdentity(manifest: FrontendManifest | undefined): FrontendIdentity | undefined {\n return manifest ? { name: manifest.name, version: manifest.version } : undefined;\n}\n\nexport { FRONTEND_MANIFEST_FILE, PROTOCOL_VERSION };\n", "import { Module } from '@nestjs/common';\nimport { createPiRpcAdapter, type PiRpcAdapter } from '@morse/adapter-pi-rpc';\nimport { SessionRegistry, type MorseLogger } from '@morse/core';\nimport type { ProjectPolicy } from '@morse/host-runtime';\nimport { SessionTranscriptStore } from '@morse/host-runtime';\nimport { HealthController } from './internal/http/health.controller.js';\nimport { NestMorseLogger } from './internal/logging/nest-logger.js';\nimport { ServerProjectPolicy } from './internal/projects/project-policy.js';\nimport { ServerTerminalBackend } from './internal/terminal/terminal.service.js';\nimport { createSessionRegistry, MorseRegistryLifecycle } from './internal/registry.js';\nimport { MorseGateway } from './internal/ws/morse.gateway.js';\nimport { MorseSessionFactory } from './internal/ws/session-factory.service.js';\nimport { loadConfig, type MorseServerConfig } from './app/config.js';\nimport {\n MORSE_CONFIG,\n MORSE_LOGGER,\n MORSE_PI_ADAPTER,\n MORSE_PROJECT_POLICY,\n MORSE_SESSION_REGISTRY,\n MORSE_TRANSCRIPT_STORE,\n} from './app/tokens.js';\n\n/**\n * Every provider here is wiring (clean-architecture R7): config in, concrete\n * adapters out. The NestJS DI container *is* this host's composition root.\n *\n * The session registry is a singleton, so the agent sessions (and their\n * projects) keep running across client reconnects; only the per-connection\n * controller is created and disposed with the WebSocket.\n */\n@Module({\n controllers: [HealthController],\n providers: [\n {\n provide: MORSE_CONFIG,\n useFactory: (): MorseServerConfig => loadConfig(),\n },\n {\n provide: MORSE_LOGGER,\n useFactory: (): MorseLogger => new NestMorseLogger(),\n },\n {\n provide: MORSE_PI_ADAPTER,\n useFactory: (config: MorseServerConfig, logger: MorseLogger): PiRpcAdapter =>\n createPiRpcAdapter(\n {\n piPath: config.piPath,\n nodeEntryPath: config.nodeEntryPath,\n sessionDir: config.sessionDir,\n noSession: config.noSession,\n requestTimeoutMs: config.requestTimeoutMs,\n },\n logger,\n ),\n inject: [MORSE_CONFIG, MORSE_LOGGER],\n },\n {\n provide: MORSE_SESSION_REGISTRY,\n useFactory: (\n adapter: PiRpcAdapter,\n config: MorseServerConfig,\n logger: MorseLogger,\n ): SessionRegistry => createSessionRegistry(adapter, config, logger),\n inject: [MORSE_PI_ADAPTER, MORSE_CONFIG, MORSE_LOGGER],\n },\n {\n provide: MORSE_TRANSCRIPT_STORE,\n useFactory: (): SessionTranscriptStore => new SessionTranscriptStore(),\n },\n {\n provide: MORSE_PROJECT_POLICY,\n useFactory: (config: MorseServerConfig): ProjectPolicy =>\n new ServerProjectPolicy(config.projects),\n inject: [MORSE_CONFIG],\n },\n MorseRegistryLifecycle,\n ServerTerminalBackend,\n MorseSessionFactory,\n MorseGateway,\n ],\n})\nexport class AppModule {}\n", "import { readdir, readFile, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, join, resolve } from 'node:path';\nimport {\n THINKING_LEVELS,\n type AgentEvent,\n type AgentEventListener,\n type AgentForkMessage,\n type AgentGateway,\n type AgentHistoryEntry,\n type AgentHistoryPage,\n type AgentInteractionRequest,\n type AgentInteractionResponse,\n type AgentSessionState,\n type AgentCommand,\n type AgentCommandSource,\n type ChatPin,\n type ModelRef,\n type MorseLogger,\n type NoticeLevel,\n type PromptDisposition,\n type PromptImage,\n type PromptMode,\n type ThinkingLevel,\n type WorkspaceRef,\n} from '@morse/core';\nimport { mapSessionEvent, activePathEntries, toEntryHistory, toSessionStats } from './event-mapping.js';\nimport { PiRpcClient } from './internal/rpc-client.js';\nimport {\n asRecord,\n asString,\n type RpcExtensionUiRequest,\n type RpcCommandInfo,\n type RpcModel,\n type RpcRecord,\n type RpcSessionStateData,\n type RpcSessionStatsData,\n} from './internal/rpc-types.js';\nimport type { PiSpawn } from './internal/resolve-pi.js';\n\nexport interface PiRpcAgentOptions {\n spawn: PiSpawn;\n workspace: WorkspaceRef;\n logger: MorseLogger;\n sessionPath?: string;\n env?: NodeJS.ProcessEnv;\n requestTimeoutMs?: number;\n now?: () => number;\n}\n\n/**\n * Compaction summarizes the entire context in one model request \u2014 minutes on a\n * slow local model. Far above the transport's 30 s default, yet still a cap: a\n * hung pi must not wedge the composer's Working state forever.\n */\nconst COMPACT_REQUEST_TIMEOUT_MS = 600_000;\n\n/**\n * `AgentGateway` implemented on top of a `pi --mode rpc` subprocess.\n * Swapping transports (SDK in-process, remote container, ...) means writing\n * another implementation of this port \u2014 nothing above it changes.\n */\nexport class PiRpcAgent implements AgentGateway {\n private readonly client: PiRpcClient;\n private readonly listeners = new Set<AgentEventListener>();\n private readonly now: () => number;\n private sessionState: AgentSessionState;\n private ready = false;\n private disposed = false;\n /** Full mapped history, fetched once and paged locally (see `history`). */\n private historyCache: AgentHistoryEntry[] | undefined;\n\n constructor(private readonly options: PiRpcAgentOptions) {\n this.now = options.now ?? (() => Date.now());\n this.sessionState = {\n workspace: options.workspace,\n thinkingLevel: 'off',\n availableModels: [],\n availableThinkingLevels: [],\n availableCommands: [],\n streaming: false,\n };\n this.client = new PiRpcClient({\n command: options.spawn.command,\n args: options.spawn.args,\n cwd: options.workspace.cwd,\n env: options.env,\n requestTimeoutMs: options.requestTimeoutMs,\n onRecord: (record) => {\n this.onRecord(record);\n },\n onStderr: (text) => {\n const trimmed = text.trim();\n if (trimmed.length > 0) {\n this.options.logger.debug(`pi: ${trimmed}`);\n }\n },\n onExit: (info) => {\n this.ready = false;\n if (!this.disposed) {\n this.emit({\n type: 'agent/fatal',\n at: this.now(),\n message: `The pi agent stopped (code=${info.code === null ? 'null' : info.code}).`,\n });\n }\n },\n });\n }\n\n /** Spawns the subprocess and reads its initial state. Throws when pi is missing. */\n async initialize(): Promise<AgentSessionState> {\n this.client.start();\n await this.refreshState();\n return this.snapshotState();\n }\n\n subscribe(listener: AgentEventListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n async state(): Promise<AgentSessionState> {\n if (!this.ready) {\n await this.refreshState();\n }\n return this.snapshotState();\n }\n\n /**\n * Resumed sessions keep their messages on disk; `get_messages` is how a client\n * shows what was said before this connection existed.\n *\n * pi's `get_messages` has no cursor, so the full mapping is fetched once and\n * cached; pages are then sliced locally from the end. The cache is dropped on\n * the next prompt, so a re-seed never serves stale history.\n */\n async history(options: { limit?: number; before?: string } = {}): Promise<AgentHistoryPage> {\n const entries = await this.historyEntries();\n const total = entries.length;\n const limit = options.limit !== undefined && options.limit > 0 ? options.limit : total;\n // `before` is the index of the oldest entry the caller already holds;\n // absent means \"the newest page\".\n const requested = options.before === undefined ? total : Number(options.before);\n const end = Number.isFinite(requested)\n ? Math.max(0, Math.min(total, Math.trunc(requested)))\n : total;\n const start = Math.max(0, end - limit);\n return {\n entries: entries.slice(start, end),\n hasOlder: start > 0,\n ...(start > 0 ? { before: String(start) } : {}),\n };\n }\n\n private async historyEntries(): Promise<AgentHistoryEntry[]> {\n if (this.historyCache !== undefined) {\n return this.historyCache;\n }\n // `get_entries` carries the full session history INCLUDING the entries a\n // compaction folded away (with the compaction entry marking the cut), and\n // id/parentId link the current branch: `get_messages` only mirrors what the\n // agent sees now, so a resumed session would silently shrink to everything\n // after the last compaction (with the start reading \"Start of\n // conversation\").\n const data = await this.client.request<{ entries?: unknown[]; leafId?: unknown }>({\n type: 'get_entries',\n });\n this.historyCache = toEntryHistory(activePathEntries(data));\n return this.historyCache;\n }\n\n async prompt(\n text: string,\n mode: PromptMode,\n images?: PromptImage[],\n pins?: ChatPin[],\n ): Promise<PromptDisposition> {\n const attachment = images && images.length > 0 ? toPiImages(images) : undefined;\n const imageArgs = attachment ? { images: attachment } : {};\n // Pins render as `@path[:start-end]` mention lines at the end of the\n // message: pi conventions, and visible in every pi frontend as exactly the\n // references they are \u2014 never prose pretending to be the user's words.\n const message = appendPinMentions(text, pins);\n // A new prompt appends to pi's history, so any cached read is now stale.\n this.historyCache = undefined;\n const command =\n mode === 'steer'\n ? ({ type: 'steer', message, ...imageArgs } as const)\n : mode === 'followUp'\n ? ({ type: 'follow_up', message, ...imageArgs } as const)\n : ({ type: 'prompt', message, ...imageArgs } as const);\n const data = await this.client.request<{ disposition?: string }>(command);\n const disposition = data?.disposition;\n if (disposition === 'queued' || disposition === 'handled') {\n return disposition;\n }\n return 'started';\n }\n\n async abort(): Promise<void> {\n await this.client.request({ type: 'abort' });\n }\n\n /** Past user messages pi can fork from (its `get_fork_messages`). */\n async forkMessages(): Promise<AgentForkMessage[]> {\n const data = await this.client.request<{ messages?: unknown[] }>({\n type: 'get_fork_messages',\n });\n const messages = Array.isArray(data?.messages) ? data.messages : [];\n const result: AgentForkMessage[] = [];\n for (const raw of messages) {\n const record = asRecord(raw);\n const entryId = record ? asString(record['entryId']) : undefined;\n if (!record || !entryId) {\n continue;\n }\n result.push({ entryId, text: asString(record['text']) ?? '' });\n }\n return result;\n }\n\n /**\n * Forks the conversation before a user entry. pi writes a new session file and\n * rebinds in place, so the process stays alive but `sessionFile` changes \u2014\n * the cached state is refreshed before returning.\n */\n async fork(entryId: string): Promise<{ text: string; cancelled: boolean }> {\n const data = await this.client.request<{ text?: string; cancelled?: boolean }>({\n type: 'fork',\n entryId,\n });\n // The conversation was re-parented: any cached history read is now stale.\n this.historyCache = undefined;\n if (data?.cancelled === true) {\n return { text: data.text ?? '', cancelled: true };\n }\n await this.refreshState();\n return { text: data?.text ?? '', cancelled: false };\n }\n\n async setModel(model: ModelRef): Promise<void> {\n const data = await this.client.request<RpcModel>({\n type: 'set_model',\n provider: model.provider,\n modelId: model.id,\n });\n this.patchState({ model: data ? toModelRef(data) : model });\n }\n\n async setThinkingLevel(level: ThinkingLevel): Promise<void> {\n await this.client.request({ type: 'set_thinking_level', level });\n this.patchState({ thinkingLevel: level });\n }\n\n async compact(customInstructions?: string): Promise<void> {\n // Compaction summarizes the whole context in one model request, so the\n // transport's 30 s default (sized for state/model calls) is far too short:\n // a slow local model takes minutes, and timing out here showed \"pi did not\n // answer 'compact' within 30000 ms\" while the agent was still working.\n await this.client.request(\n customInstructions === undefined\n ? { type: 'compact' }\n : { type: 'compact', customInstructions },\n COMPACT_REQUEST_TIMEOUT_MS,\n );\n await this.refreshStats();\n }\n\n respondToInteraction(response: AgentInteractionResponse): Promise<void> {\n this.client.respond({\n type: 'extension_ui_response',\n id: response.requestId,\n value: response.value,\n confirmed: response.confirmed,\n cancelled: response.cancelled,\n });\n return Promise.resolve();\n }\n\n async dispose(): Promise<void> {\n this.disposed = true;\n this.listeners.clear();\n await this.client.dispose();\n }\n\n private async refreshState(): Promise<void> {\n const state = await this.client.request<RpcSessionStateData>({ type: 'get_state' });\n const models = await this.client\n .request<{ models?: RpcModel[] }>({ type: 'get_available_models' })\n .catch(() => undefined);\n const levels = await this.client\n .request<{ levels?: string[] }>({ type: 'get_available_thinking_levels' })\n .catch(() => undefined);\n const commands = await this.client\n .request<{ commands?: RpcCommandInfo[] }>({ type: 'get_commands' })\n .catch(() => undefined);\n\n const availableModels = (models?.models ?? []).map(toModelRef);\n const availableThinkingLevels = dedupeThinkingLevels(\n (levels?.levels ?? []).map(toThinkingLevel).filter(isDefined),\n );\n const thinkingLevel = toThinkingLevel(state.thinkingLevel);\n\n this.sessionState = {\n ...this.sessionState,\n sessionId: state.sessionFile ?? state.sessionId,\n sessionTitle: state.sessionName,\n model: state.model ? toModelRef(state.model) : this.sessionState.model,\n thinkingLevel: thinkingLevel ?? this.sessionState.thinkingLevel,\n availableModels,\n availableThinkingLevels:\n availableThinkingLevels.length > 0 ? availableThinkingLevels : [...THINKING_LEVELS],\n availableCommands: await buildCommandList(commands?.commands, this.commandContext()),\n streaming: state.isStreaming === true,\n };\n this.ready = true;\n await this.refreshStats();\n this.emit({ type: 'agent/ready', at: this.now(), state: this.snapshotState() });\n }\n\n /**\n * Re-reads the resources behind `get_commands` and reports the result the same\n * way a fresh spawn would. The palette calls it the moment it opens, so a new\n * or edited template is there by the time the reader finishes typing.\n */\n async refreshCommands(): Promise<void> {\n if (this.disposed || !this.ready) {\n return;\n }\n const commands = await this.client\n .request<{ commands?: RpcCommandInfo[] }>({ type: 'get_commands' })\n .catch(() => undefined);\n this.sessionState = {\n ...this.sessionState,\n availableCommands: await buildCommandList(commands?.commands, this.commandContext()),\n };\n this.emit({ type: 'agent/state', at: this.now(), state: this.snapshotState() });\n }\n\n /** Where a command list is rebuilt from: the session's workspace and env. */\n private commandContext(): CommandContext {\n return { cwd: this.options.workspace.cwd, env: this.options.env };\n }\n\n /**\n * Pulls the cumulative token/cost/context totals pi keeps for the session, so\n * the footer can show the same numbers the TUI does. Best-effort: an older pi\n * without `get_session_stats` simply leaves the previous values in place.\n */\n private async refreshStats(): Promise<void> {\n const stats = await this.client\n .request<RpcSessionStatsData>({ type: 'get_session_stats' })\n .catch(() => undefined);\n const patch = toSessionStats(stats);\n if (Object.keys(patch).length === 0) {\n return;\n }\n this.sessionState = { ...this.sessionState, ...patch };\n this.emit({ type: 'agent/state', at: this.now(), state: this.snapshotState() });\n }\n\n private onRecord(record: RpcRecord): void {\n if (record['type'] === 'extension_ui_request') {\n this.onUiRequest(record as unknown as RpcExtensionUiRequest);\n return;\n }\n\n switch (record['type']) {\n case 'agent_settled':\n this.patchState({ streaming: false });\n this.emit({ type: 'agent/run-end', at: this.now(), reason: 'settled' });\n // A settled run is the moment cumulative tokens/cost/context are final.\n void this.refreshStats();\n return;\n case 'thinking_level_changed': {\n const level = toThinkingLevel(asString(record['level']));\n if (level) {\n this.patchState({ thinkingLevel: level });\n }\n return;\n }\n case 'session_info_changed':\n this.patchState({ sessionTitle: asString(record['name']) });\n return;\n case 'queue_update':\n return;\n default:\n break;\n }\n\n const mapped = mapSessionEvent(record, this.now);\n if (mapped.state) {\n this.patchState(mapped.state);\n }\n for (const event of mapped.events) {\n this.emit(event);\n }\n // Compaction rewrites the context, so the footer's percentage must follow.\n if (record['type'] === 'compaction_end') {\n void this.refreshStats();\n }\n }\n\n private onUiRequest(request: RpcExtensionUiRequest): void {\n const method = request.method;\n if (method === 'notify') {\n this.emit({\n type: 'agent/notice',\n at: this.now(),\n level: toNoticeLevel(request.notifyType),\n text: request.message ?? '',\n });\n return;\n }\n if (\n method !== 'select' &&\n method !== 'confirm' &&\n method !== 'input' &&\n method !== 'editor'\n ) {\n // Fire-and-forget terminal concerns (setTitle/setStatus/setWidget/...):\n // nothing to answer, and Morse has no terminal chrome to update.\n this.options.logger.debug(`pi requested unsupported UI method \"${method}\"; ignoring.`);\n return;\n }\n this.emit({\n type: 'agent/interaction',\n at: this.now(),\n request: toInteractionRequest(method, request),\n });\n }\n\n private patchState(patch: Partial<AgentSessionState>): void {\n this.sessionState = { ...this.sessionState, ...patch };\n this.emit({ type: 'agent/state', at: this.now(), state: this.snapshotState() });\n }\n\n private snapshotState(): AgentSessionState {\n return {\n ...this.sessionState,\n workspace: { ...this.sessionState.workspace },\n availableModels: [...this.sessionState.availableModels],\n availableThinkingLevels: [...this.sessionState.availableThinkingLevels],\n availableCommands: [...this.sessionState.availableCommands],\n };\n }\n\n private emit(event: AgentEvent): void {\n for (const listener of [...this.listeners]) {\n try {\n listener(event);\n } catch (error: unknown) {\n this.options.logger.error('Agent event listener failed', error);\n }\n }\n }\n}\n\nfunction toInteractionRequest(\n kind: 'select' | 'confirm' | 'input' | 'editor',\n request: RpcExtensionUiRequest,\n): AgentInteractionRequest { const title = request.title ?? kind;\n switch (kind) {\n case 'select':\n return {\n requestId: request.id,\n kind: 'select',\n title,\n options: (request.options ?? []).map((value) => ({ value, label: value })),\n };\n case 'confirm':\n return { requestId: request.id, kind: 'confirm', title, message: request.message ?? '' };\n case 'input':\n return {\n requestId: request.id,\n kind: 'input',\n title,\n placeholder: request.placeholder,\n };\n case 'editor':\n return { requestId: request.id, kind: 'editor', title, value: request.prefill };\n }\n}\n\nfunction toModelRef(model: RpcModel): ModelRef {\n return {\n provider: model.provider,\n id: model.id,\n name: model.name ?? model.id,\n contextWindow: model.contextWindow,\n maxTokens: model.maxTokens,\n };\n}\n\nfunction toThinkingLevel(value: string | undefined): ThinkingLevel | undefined {\n if (!value) {\n return undefined;\n }\n return THINKING_LEVELS.find((level) => level === value);\n}\n\nfunction dedupeThinkingLevels(levels: ThinkingLevel[]): ThinkingLevel[] {\n return [...new Set(levels)];\n}\n\n/**\n * pi reports command sources as `extension`, `prompt` or `skill`. Anything else\n * (a future source) is treated as an extension command so it still runs.\n */\nfunction toCommandSource(value: string | undefined): AgentCommandSource {\n return value === 'prompt' || value === 'skill' ? value : 'extension';\n}\n\n/**\n * pi lists a prompt template in `get_commands` but does not send its body over\n * RPC, and it does not notice a file added after spawn. Read the file directly \u2014\n * both for the templates pi named and for the ones Morse finds on disk. Best\n * effort: an unreadable file is left out rather than offered as a dead command.\n */\nasync function readTemplateFile(path: string | undefined): Promise<string | undefined> {\n if (path === undefined || path.length === 0) {\n return undefined;\n }\n try {\n return await readFile(path, 'utf8');\n } catch {\n return undefined;\n }\n}\n\n/** What a command list is rebuilt from: the workspace and the pi environment. */\nexport interface CommandContext {\n cwd: string;\n env: NodeJS.ProcessEnv | undefined;\n}\n\n/** The subset of a `get_commands` entry the command list cares about. */\nexport interface CommandListInput {\n name?: string;\n description?: string;\n source?: string;\n sourceInfo?: { path?: string; scope?: string };\n}\n\n/**\n * The palette's command list, rebuilt from disk. pi only exposes the templates\n * it loaded at spawn (`get_commands` is a cache), so this also re-reads every\n * template file and scans the conventional prompt directories \u2014 that is what\n * makes a template added, changed or deleted since spawn show up without a pi\n * reload.\n *\n * pi's own list stays the source for extension and skill commands, and for\n * prompt templates that live outside the conventional directories (packages,\n * explicit paths); a listed template whose file is gone is dropped.\n */\nexport async function buildCommandList(\n items: CommandListInput[] | undefined,\n context: CommandContext,\n): Promise<AgentCommand[]> {\n const commands: AgentCommand[] = [];\n const seen = new Set<string>();\n let projectTrusted = false;\n\n for (const item of items ?? []) {\n if (typeof item.name !== 'string' || item.name.trim().length === 0) {\n continue;\n }\n const source = toCommandSource(item.source);\n if (source !== 'prompt') {\n commands.push({ name: item.name, description: item.description, source });\n seen.add(item.name);\n continue;\n }\n if (item.sourceInfo?.scope === 'project') {\n projectTrusted = true;\n }\n const template = await readTemplateFile(item.sourceInfo?.path);\n if (template === undefined) {\n // The file pi listed is gone: hide it rather than offer a dead command.\n continue;\n }\n commands.push({\n name: item.name,\n // Prefer the freshly parsed description so an edited frontmatter reflects\n // too; pi's cached description is the fallback.\n description: promptDescription(template) ?? item.description,\n source,\n template,\n });\n seen.add(item.name);\n }\n\n for (const dir of await promptDirs(projectTrusted, context)) {\n for (const file of await listPromptFiles(dir)) {\n if (seen.has(file.name)) {\n continue;\n }\n const template = await readTemplateFile(file.path);\n if (template === undefined) {\n continue;\n }\n commands.push({\n name: file.name,\n description: promptDescription(template),\n source: 'prompt',\n template,\n });\n seen.add(file.name);\n }\n }\n return commands;\n}\n\n/**\n * Directories Morse re-scans for prompt templates: the user's global prompts,\n * and the project's \u2014 the latter only once that project is trusted, exactly as\n * pi gates project resources.\n */\nasync function promptDirs(projectTrusted: boolean, context: CommandContext): Promise<string[]> {\n const dirs = [join(agentDir(context.env), 'prompts')];\n if (projectTrusted || (await isProjectTrusted(context.cwd, context.env))) {\n dirs.push(resolve(context.cwd, '.pi', 'prompts'));\n }\n return dirs;\n}\n\n/** pi's agent config directory (`PI_CODING_AGENT_DIR`, else `~/.pi/agent`). */\nfunction agentDir(env: NodeJS.ProcessEnv | undefined): string {\n const configured = env?.PI_CODING_AGENT_DIR?.trim();\n return configured && configured.length > 0\n ? expandHome(configured)\n : join(homedir(), '.pi', 'agent');\n}\n\nfunction expandHome(path: string): string {\n if (path === '~') {\n return homedir();\n }\n if (path.startsWith('~/') || path.startsWith('~\\\\')) {\n return join(homedir(), path.slice(2));\n }\n return path;\n}\n\n/**\n * pi records project trust in `<agentDir>/trust.json` as `{ \"<cwd>\": true }`.\n * The project prompt directory is only scanned when that says yes, so an\n * untrusted checkout cannot inject a template into the palette.\n */\nasync function isProjectTrusted(cwd: string, env: NodeJS.ProcessEnv | undefined): Promise<boolean> {\n try {\n const raw = await readFile(join(agentDir(env), 'trust.json'), 'utf8');\n const decisions = JSON.parse(raw) as Record<string, unknown>;\n return decisions[cwd] === true || decisions[resolve(cwd)] === true;\n } catch {\n return false;\n }\n}\n\n/** Direct `.md` children of a prompt directory \u2014 pi loads direct children only. */\nasync function listPromptFiles(dir: string): Promise<{ name: string; path: string }[]> {\n const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);\n const files: { name: string; path: string }[] = [];\n for (const entry of entries) {\n if (!entry.name.endsWith('.md')) {\n continue;\n }\n const path = join(dir, entry.name);\n let isFile = entry.isFile();\n if (entry.isSymbolicLink()) {\n try {\n isFile = (await stat(path)).isFile();\n } catch {\n continue;\n }\n }\n if (isFile) {\n files.push({ name: basename(entry.name, '.md'), path });\n }\n }\n return files;\n}\n\n/**\n * What the palette prints for a scanned template: pi's `description` frontmatter,\n * or the first non-empty body line, truncated the way pi truncates it.\n */\nfunction promptDescription(raw: string): string | undefined {\n let body = raw;\n const frontmatter = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/.exec(raw);\n if (frontmatter !== null) {\n const described = /^description:[ \\t]*(.*)$/m.exec(frontmatter[1]);\n if (described !== null) {\n const value = unquote(described[1].trim());\n if (value.length > 0) {\n return value;\n }\n }\n body = raw.slice(frontmatter[0].length);\n }\n const firstLine = body.split('\\n').find((line) => line.trim().length > 0);\n if (firstLine === undefined) {\n return undefined;\n }\n const trimmed = firstLine.trim();\n return trimmed.length > 60 ? `${trimmed.slice(0, 60)}...` : trimmed;\n}\n\nfunction unquote(value: string): string {\n const match = /^(['\"])([\\s\\S]*)\\1$/.exec(value);\n return match === null ? value : match[2];\n}\n\nfunction toNoticeLevel(value: string | undefined): NoticeLevel {\n if (value === 'error') {\n return 'error';\n }\n if (value === 'warning' || value === 'warn') {\n return 'warn';\n }\n return 'info';\n}\n\nfunction isDefined<T>(value: T | undefined): value is T {\n return value !== undefined;\n}\n\n/** pi's prompt command takes images as content blocks. */\nfunction toPiImages(images: PromptImage[]): Array<{ type: 'image'; data: string; mimeType: string }> {\n return images.map((image) => ({ type: 'image', data: image.data, mimeType: image.mimeType }));\n}\n\n/**\n * Apples the pin mentions after the user's words: `@path` for a whole file,\n * `@path:start-end` for an editor selection. When the message is pins only (an\n * image with no caption, say), the message is the mentions.\n */\nexport function appendPinMentions(text: string, pins: ChatPin[] | undefined): string {\n if (!pins || pins.length === 0) {\n return text;\n }\n const mentions = pins\n .map((pin) => {\n if (pin.startLine === undefined) {\n return `@${pin.path}`;\n }\n const end = pin.endLine ?? pin.startLine;\n return `@${pin.path}:${pin.startLine}-${end}`;\n })\n .join('\\n');\n const prose = text.trim();\n return prose.length > 0 ? `${prose}\\n\\n${mentions}` : mentions;\n}\n", "/**\n * Domain layer: anemic data shapes, value objects and sentinel errors only.\n * No I/O, no ports, no framework imports (clean-architecture R2, R8).\n */\n\nexport type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';\n\nexport const THINKING_LEVELS: readonly ThinkingLevel[] = [\n 'off',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n 'max',\n];\n\nexport type NoticeLevel = 'info' | 'success' | 'warn' | 'error';\n\nexport interface ModelRef {\n provider: string;\n id: string;\n name: string;\n contextWindow?: number;\n /** Provider cap on the response length, for the footer's output limit. */\n maxTokens?: number;\n}\n\nexport type AgentCommandSource = 'extension' | 'prompt' | 'skill';\n\n/** A command the agent exposes to the composer (pi `get_commands`). */\nexport interface AgentCommand {\n name: string;\n description?: string;\n source: AgentCommandSource;\n /**\n * Raw Markdown of a prompt template, for `source: 'prompt'` only. pi does not\n * send the body over RPC, so the adapter reads the file `get_commands` named\n * and the modal expands it here. Absent when the file could not be read.\n */\n template?: string;\n}\n\n/** An image attached to a prompt: base64 payload plus its media type. */\nexport interface PromptImage {\n /** Base64 data, without a `data:` URL prefix. */\n data: string;\n mimeType: string;\n}\n\n/** A file (or editor selection) pinned to a message as an attachment chip. */\nexport interface ChatPin {\n path: string;\n startLine?: number;\n endLine?: number;\n}\n\nexport interface TokenUsage {\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n /** Prompt tokens served from the provider's prompt cache. */\n cacheReadTokens?: number;\n /** Prompt tokens written into the provider's prompt cache. */\n cacheWriteTokens?: number;\n /** Reasoning tokens; a subset of `outputTokens`. */\n reasoningTokens?: number;\n}\n\n/** How full the model's context window is, straight from the agent. */\nexport interface ContextUsage {\n /** Tokens currently in context; `null` right after a compaction. */\n tokens: number | null;\n contextWindow: number;\n percent: number | null;\n}\n\n/** Message/step counts for the session, for the usage panel. */\nexport interface SessionCounts {\n userMessages: number;\n assistantMessages: number;\n toolCalls: number;\n toolResults: number;\n totalMessages: number;\n}\n\nexport interface WorkspaceRef {\n cwd: string;\n name: string;\n}\n\nexport interface SessionSummary {\n id: string;\n title: string;\n cwd: string;\n updatedAt: number;\n messageCount: number;\n}\n\n/** A project is a directory the agent has worked in (one pi session bucket). */\nexport interface ProjectSummary {\n path: string;\n name: string;\n sessionCount: number;\n lastUsedAt: number;\n}\n\nexport interface AgentSessionState {\n sessionId?: string;\n sessionTitle?: string;\n workspace: WorkspaceRef;\n model?: ModelRef;\n thinkingLevel: ThinkingLevel;\n availableModels: ModelRef[];\n availableThinkingLevels: ThinkingLevel[];\n /** Commands pi exposes (`get_commands`): extensions, templates, skills. */\n availableCommands: AgentCommand[];\n streaming: boolean;\n /** Cumulative session tokens (input/output/cache) reported by the agent. */\n usage?: TokenUsage;\n /** Tokens of the most recent assistant message, for the usage panel. */\n lastUsage?: TokenUsage;\n /** Cumulative session cost in USD, when the provider reports it. */\n costUsd?: number;\n contextUsage?: ContextUsage;\n counts?: SessionCounts;\n}\n\n/**\n * One already-finished exchange from a persisted session, so a resumed session\n * can show its history instead of an empty panel.\n */\n/**\n * One entry of an agent's past conversation, normalised away from any transport.\n *\n * `tool` entries carry a finished tool call: a persisted session replays its\n * calls too, otherwise a resumed transcript would look like the agent did\n * nothing between two messages.\n */\nexport type AgentHistoryEntry =\n | {\n role: 'user' | 'assistant';\n text: string;\n thinking?: string;\n model?: string;\n /** Image attachments the user pinned to this message (user role). */\n images?: PromptImage[];\n /** File/selection pins attached to this message (user role). */\n pins?: ChatPin[];\n at?: number;\n }\n | {\n role: 'tool';\n /** Provider-side id, used to attach the matching tool result. */\n toolCallId: string;\n name: string;\n title: string;\n input?: string;\n output?: string;\n status: 'ok' | 'error';\n at?: number;\n }\n /**\n * A compaction boundary in the session's history: everything before it was\n * summarized into `summary` (that is the context the agent carries); the\n * transcript marks the point instead of hiding the folded messages.\n */\n | {\n role: 'compaction';\n summary: string;\n tokensBefore?: number;\n at?: number;\n };\n\n/**\n * A past user message the agent can fork from: stable entry id plus the exact\n * text the model received. Forking re-parents the conversation before it, which\n * is how a frontend lets a user edit a prompt that already has an answer.\n */\nexport interface AgentForkMessage {\n entryId: string;\n text: string;\n}\n\nexport interface SelectOption {\n value: string;\n label: string;\n description?: string;\n}\n\n/** The agent asking the user for input, normalised away from any transport. */\nexport type AgentInteractionRequest =\n | { requestId: string; kind: 'select'; title: string; message?: string; options: SelectOption[] }\n | { requestId: string; kind: 'confirm'; title: string; message: string; danger?: boolean }\n | { requestId: string; kind: 'input'; title: string; placeholder?: string; value?: string }\n | { requestId: string; kind: 'editor'; title: string; value?: string; language?: string };\n\nexport interface AgentInteractionResponse {\n requestId: string;\n value?: string;\n confirmed?: boolean;\n cancelled?: boolean;\n}\n\n/** Everything a host can observe from a running agent. */\nexport type AgentEvent =\n | { type: 'agent/ready'; at: number; state: AgentSessionState }\n | { type: 'agent/state'; at: number; state: AgentSessionState }\n | { type: 'agent/run-start'; at: number }\n | { type: 'agent/delta'; at: number; channel: 'text' | 'thinking'; delta: string }\n | { type: 'agent/message'; at: number; text: string; thinking: string }\n | {\n type: 'agent/tool-start';\n at: number;\n toolCallId: string;\n name: string;\n title: string;\n input?: string;\n }\n | {\n type: 'agent/tool-update';\n at: number;\n toolCallId: string;\n output?: string;\n status?: 'running' | 'ok' | 'error';\n }\n | {\n type: 'agent/tool-end';\n at: number;\n toolCallId: string;\n status: 'ok' | 'error';\n output?: string;\n durationMs?: number;\n }\n | { type: 'agent/run-end'; at: number; reason: 'settled' | 'aborted' | 'error'; error?: string }\n | { type: 'agent/interaction'; at: number; request: AgentInteractionRequest }\n | { type: 'agent/notice'; at: number; level: NoticeLevel; text: string }\n /**\n * A successful compaction finished while the session was live. The projector\n * records the boundary as its own marker row \u2014 so the transcript shows where\n * the agent's context was summarized, including after a reload replays this\n * warm transcript; a cold resume gets the same markers out of session\n * history instead.\n */\n | { type: 'agent/compaction'; at: number; summary: string; tokensBefore?: number }\n | { type: 'agent/fatal'; at: number; message: string; detail?: string };\n", "export class MorseError extends Error {\n constructor(\n message: string,\n readonly code: string,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\n/** What the user can do about a failure, when the layer that failed knows. */\nexport interface AgentRemedy {\n /** Shell command that installs what is missing, e.g. the pi CLI. */\n install?: string;\n}\n\n/** The agent backend could not be started (missing `pi` binary, spawn failure, ...). */\nexport class AgentUnavailableError extends MorseError {\n /**\n * The next step, in a form a frontend can render (see `SessionViewState\n * .agentFailure`). The message stays human-readable on its own; this is the\n * machine-readable half, and only the layer that knows pi's package name can\n * fill it in.\n */\n readonly remedy: AgentRemedy | undefined;\n\n constructor(message: string, options?: { cause?: unknown; remedy?: AgentRemedy }) {\n super(message, 'agent-unavailable', options);\n this.remedy = options?.remedy;\n }\n}\n\n/** The agent answered with something the adapter could not understand. */\nexport class AgentProtocolError extends MorseError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, 'agent-protocol', options);\n }\n}\n\n/** The host was asked to do something it cannot do (server host has no editor). */\nexport class UnsupportedByHostError extends MorseError {\n constructor(message: string) {\n super(message, 'unsupported-by-host');\n }\n}\n", "import type {\n AgentEvent,\n AgentForkMessage,\n AgentHistoryEntry,\n AgentInteractionResponse,\n AgentSessionState,\n ChatPin,\n ModelRef,\n ProjectSummary,\n PromptImage,\n SessionSummary,\n ThinkingLevel,\n WorkspaceRef,\n} from '../domain.js';\nimport { AgentUnavailableError, UnsupportedByHostError } from '../errors.js';\nimport type { MorseLogger } from '../logger.js';\n\n/** Re-exported so the chat use case (and older imports) keep their import path. */\nexport type { PromptImage };\n\nexport type PromptMode = 'new' | 'steer' | 'followUp';\n\nexport type PromptDisposition = 'started' | 'queued' | 'handled';\n\nexport type AgentEventListener = (event: AgentEvent) => void;\n\n/**\n * Driven port (R2): the coding agent Morse drives, one instance per session.\n * Declared in the module that owns the gateway lifetime, implemented by\n * `@morse/adapter-pi-rpc` (subprocess) \u2014 or an SDK adapter later.\n */\nexport interface AgentGateway {\n subscribe(listener: AgentEventListener): () => void;\n state(): Promise<AgentSessionState>;\n /**\n * Finished exchanges of a resumed session (empty for a new one), newest page\n * first. Page from the end so a large session opens without loading it all.\n */\n history(options?: { limit?: number; before?: string }): Promise<AgentHistoryPage>;\n prompt(\n text: string,\n mode: PromptMode,\n images?: PromptImage[],\n pins?: ChatPin[],\n ): Promise<PromptDisposition>;\n /**\n * Past user messages the agent can fork from, in conversation order. A\n * frontend uses this to turn \"edit my old prompt\" into a fork of that entry.\n */\n forkMessages(): Promise<AgentForkMessage[]>;\n /**\n * Re-parents the conversation before `entryId`, discarding that message and\n * everything after it, and returns the message text that was dropped. The\n * agent keeps running, but its session id changes: a fork is a new session.\n */\n fork(entryId: string): Promise<{ text: string; cancelled: boolean }>;\n abort(): Promise<void>;\n /**\n * Re-reads the commands the agent exposes \u2014 prompt templates especially, which\n * pi caches at spawn. Optional: an adapter that cannot re-read resources simply\n * omits it and the palette keeps what the last `state()` reported.\n */\n refreshCommands?(): Promise<void>;\n setModel(model: ModelRef): Promise<void>;\n setThinkingLevel(level: ThinkingLevel): Promise<void>;\n compact(customInstructions?: string): Promise<void>;\n respondToInteraction(response: AgentInteractionResponse): Promise<void>;\n dispose(): Promise<void>;\n}\n\n/** Driven port (R2): creates a gateway for a workspace / persisted session. */\nexport interface AgentGatewayFactory {\n create(options: { workspace: WorkspaceRef; sessionId?: string }): Promise<AgentGateway>;\n /**\n * Optional: a gateway that never records \u2014 pi spawned with `--no-session` \u2014\n * used only to read what the backend offers before any session exists (model\n * catalog, thinking levels, commands). Adapters without a session-less mode\n * omit it, and the draft state simply ships without a catalog.\n */\n probeDefaults?(options: { workspace: WorkspaceRef }): Promise<AgentGateway>;\n}\n\n/** Driven port (R2): reads persisted sessions. */\nexport interface SessionCatalog {\n list(): Promise<SessionSummary[]>;\n /**\n * Deletes a persisted session. Optional on purpose: a catalog whose storage\n * Morse must not touch simply omits it, and the use case reports that\n * instead of pretending the session is gone. How a session is stored \u2014 and\n * therefore what deleting it means \u2014 belongs to the implementation.\n */\n remove?(id: string): Promise<void>;\n}\n\n/** An event from any hot session, tagged with the session it came from. */\nexport interface TaggedAgentEvent {\n sessionKey: string;\n event: AgentEvent;\n}\n\nexport interface OpenedSession {\n key: string;\n state: AgentSessionState;\n /** True when an already-running session was reused (no new `pi` process). */\n reused: boolean;\n}\n\n/** One page of a resumed session's finished exchanges. */\nexport interface AgentHistoryPage {\n entries: AgentHistoryEntry[];\n /** True when entries exist before this page. */\n hasOlder: boolean;\n /** Opaque cursor to pass as `before` for the next older page. */\n before?: string;\n}\n\n/** Result of forking the active session before a past user message. */\nexport interface ForkedSession {\n /** Registry key after the fork (the new session id); unchanged when cancelled. */\n key: string;\n /** Registry key the session had before the fork. */\n previousKey: string;\n state: AgentSessionState;\n /** Text of the message the fork re-opened, for restoring the editor. */\n text: string;\n cancelled: boolean;\n}\n\nexport interface SessionRegistryDeps {\n factory: AgentGatewayFactory;\n catalog: SessionCatalog;\n /** Workspace for sessions started without an explicit project. */\n defaultWorkspace: WorkspaceRef;\n /** How many `pi` processes stay alive at once (least recently used first). */\n hotLimit?: number;\n logger: MorseLogger;\n}\n\nconst DEFAULT_HOT_LIMIT = 4;\n\ninterface HotSession {\n key: string;\n gateway: AgentGateway;\n workspace: WorkspaceRef;\n detach: () => void;\n state: AgentSessionState;\n}\n\n/**\n * Session use case: owns every live agent gateway, keeps the most recent ones\n * hot (LRU), and fans their events out tagged with the session key.\n *\n * Multiple projects and multiple sessions therefore run at the same time, which\n * is what a browser host needs \u2014 and because `AgentGatewayFactory.create()`\n * already takes a workspace per session, the adapter did not change at all.\n */\nexport class SessionRegistry {\n private readonly hot = new Map<string, HotSession>();\n /** Least recently used first. */\n private readonly recency: string[] = [];\n private readonly listeners = new Set<(event: TaggedAgentEvent) => void>();\n private readonly hotLimit: number;\n private activeKey: string | undefined;\n private syntheticKeys = 0;\n /** Cached snapshot of a never-recording spawn (see `draftDefaults`). */\n private draftCache: AgentSessionState | undefined;\n private draftProbe: Promise<AgentSessionState | undefined> | undefined;\n\n constructor(private readonly deps: SessionRegistryDeps) {\n this.hotLimit = Math.max(1, deps.hotLimit ?? DEFAULT_HOT_LIMIT);\n }\n\n get defaultWorkspace(): WorkspaceRef {\n return this.deps.defaultWorkspace;\n }\n\n subscribe(listener: (event: TaggedAgentEvent) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n activeKeyOf(): string | undefined {\n return this.activeKey;\n }\n\n active(): AgentGateway | undefined {\n return this.activeKey === undefined ? undefined : this.hot.get(this.activeKey)?.gateway;\n }\n\n requireActive(): AgentGateway {\n const gateway = this.active();\n if (!gateway) {\n throw new AgentUnavailableError('No active Morse session. Open a session first.');\n }\n return gateway;\n }\n\n stateOf(key: string): AgentSessionState | undefined {\n return this.hot.get(key)?.state;\n }\n\n hotKeys(): string[] {\n return [...this.recency];\n }\n\n /**\n * What the backend offers before any session exists: the model catalog,\n * thinking levels and commands of an untouched spawn. Probed once per\n * registry \u2014 never per client \u2014 from a gateway that never records itself\n * (pi `--no-session`), so the empty state can ship a real model picker\n * without creating a session.\n */\n async draftDefaults(): Promise<AgentSessionState | undefined> {\n if (this.draftCache) {\n return this.draftCache;\n }\n if (!this.draftProbe) {\n // A rejected probe must not be memoized: the setup screen's Retry (or the\n // next draft) has to be able to reach pi once the user installed it.\n this.draftProbe = this.probeDraft().catch((error: unknown) => {\n this.draftProbe = undefined;\n throw error;\n });\n }\n return this.draftProbe;\n }\n\n /** Opens a new (or resumes an existing) session and makes it active. */\n async open(options: { workspace?: WorkspaceRef; sessionId?: string } = {}): Promise<OpenedSession> {\n const workspace = options.workspace ?? this.deps.defaultWorkspace;\n const gateway = await this.deps.factory.create({\n workspace,\n sessionId: options.sessionId,\n });\n const state = await gateway.state();\n const key = state.sessionId ?? this.nextSyntheticKey(workspace);\n\n const alreadyHot = this.hot.get(key);\n if (alreadyHot) {\n // Same session file: keep the running process and drop the duplicate.\n await gateway.dispose().catch((error: unknown) => {\n this.deps.logger.warn('Failed to dispose a duplicate agent gateway', error);\n });\n this.touch(key);\n this.activeKey = key;\n return { key, state: alreadyHot.state, reused: true };\n }\n\n this.hot.set(key, {\n key,\n gateway,\n workspace,\n detach: gateway.subscribe((event) => {\n this.emit({ sessionKey: key, event: this.absorb(key, event) });\n }),\n state,\n });\n this.touch(key);\n this.activeKey = key;\n await this.evictBeyondLimit();\n return { key, state, reused: false };\n }\n\n /** Switches to a hot session, or opens it when it is not running anymore. */\n async activate(key: string, workspace?: WorkspaceRef): Promise<OpenedSession> {\n const existing = this.hot.get(key);\n if (existing) {\n this.touch(key);\n this.activeKey = key;\n return { key, state: existing.state, reused: true };\n }\n return this.open({ workspace, sessionId: key });\n }\n\n /**\n * Forks the active session before a past user message. pi's fork writes a\n * *new* session file while reusing the same subprocess, so the hot entry is\n * re-keyed to the new session id instead of respawning the agent. The\n * controller truncates its transcript to match, then sends the edited prompt.\n */\n async forkActive(entryId: string): Promise<ForkedSession> {\n const previousKey = this.activeKey;\n const hot = previousKey === undefined ? undefined : this.hot.get(previousKey);\n if (previousKey === undefined || !hot) {\n throw new AgentUnavailableError('No active Morse session. Open a session first.');\n }\n const result = await hot.gateway.fork(entryId);\n // A cancelled fork leaves the session untouched and still reports its state.\n const state = await hot.gateway.state();\n if (result.cancelled) {\n return { key: previousKey, previousKey, state, text: result.text, cancelled: true };\n }\n const key = state.sessionId ?? this.nextSyntheticKey(hot.workspace);\n if (key === previousKey) {\n return { key, previousKey, state, text: result.text, cancelled: false };\n }\n\n hot.detach();\n this.hot.delete(previousKey);\n this.hot.set(key, {\n key,\n gateway: hot.gateway,\n workspace: hot.workspace,\n state,\n detach: hot.gateway.subscribe((event) => {\n this.emit({ sessionKey: key, event: this.absorb(key, event) });\n }),\n });\n const index = this.recency.indexOf(previousKey);\n if (index >= 0) {\n this.recency[index] = key;\n } else {\n this.recency.push(key);\n }\n this.activeKey = key;\n return { key, previousKey, state, text: result.text, cancelled: false };\n }\n\n async close(key: string): Promise<void> {\n const session = this.hot.get(key);\n this.forget(key);\n if (!session) {\n return;\n }\n session.detach();\n await session.gateway.dispose().catch((error: unknown) => {\n this.deps.logger.warn('Failed to dispose an agent gateway', error);\n });\n }\n\n /**\n * Deletes a session for good: its hot process first (it may hold the file\n * open), then the stored conversation through the catalog. Storage rules stay\n * in the adapter; this use case owns only the order and the refusal when the\n * host cannot delete at all.\n */\n async remove(key: string): Promise<void> {\n await this.close(key);\n const remove = this.deps.catalog.remove;\n if (!remove) {\n throw new UnsupportedByHostError('This host cannot delete sessions.');\n }\n await remove.call(this.deps.catalog, key);\n }\n\n listSessions(): Promise<SessionSummary[]> {\n return this.deps.catalog.list();\n }\n\n /** Projects are the directories pi has session buckets for. */\n async listProjects(sessions?: readonly SessionSummary[]): Promise<ProjectSummary[]> {\n const all = sessions ?? (await this.listSessions());\n const projects = new Map<string, ProjectSummary>();\n for (const session of all) {\n if (session.cwd.length === 0) {\n continue;\n }\n const existing = projects.get(session.cwd);\n if (existing) {\n existing.sessionCount += 1;\n existing.lastUsedAt = Math.max(existing.lastUsedAt, session.updatedAt);\n continue;\n }\n projects.set(session.cwd, {\n path: session.cwd,\n name: baseName(session.cwd),\n sessionCount: 1,\n lastUsedAt: session.updatedAt,\n });\n }\n const defaultPath = this.deps.defaultWorkspace.cwd;\n if (!projects.has(defaultPath)) {\n projects.set(defaultPath, {\n path: defaultPath,\n name: this.deps.defaultWorkspace.name,\n sessionCount: 0,\n lastUsedAt: 0,\n });\n }\n return [...projects.values()].sort((left, right) => right.lastUsedAt - left.lastUsedAt);\n }\n\n async dispose(): Promise<void> {\n const sessions = [...this.hot.values()];\n this.hot.clear();\n this.recency.length = 0;\n this.listeners.clear();\n this.activeKey = undefined;\n this.draftCache = undefined;\n this.draftProbe = undefined;\n await Promise.all(\n sessions.map((session) => {\n session.detach();\n return session.gateway.dispose().catch((error: unknown) => {\n this.deps.logger.warn('Failed to dispose an agent gateway', error);\n });\n }),\n );\n }\n\n private absorb(key: string, event: AgentEvent): AgentEvent {\n const session = this.hot.get(key);\n if (session && (event.type === 'agent/ready' || event.type === 'agent/state')) {\n session.state = event.state;\n }\n return event;\n }\n\n private emit(event: TaggedAgentEvent): void {\n for (const listener of [...this.listeners]) {\n try {\n listener(event);\n } catch (error: unknown) {\n this.deps.logger.error('Agent event listener failed', error);\n }\n }\n }\n\n private touch(key: string): void {\n const index = this.recency.indexOf(key);\n if (index >= 0) {\n this.recency.splice(index, 1);\n }\n this.recency.push(key);\n }\n\n private forget(key: string): void {\n const index = this.recency.indexOf(key);\n if (index >= 0) {\n this.recency.splice(index, 1);\n }\n this.hot.delete(key);\n if (this.activeKey === key) {\n this.activeKey = undefined;\n }\n }\n\n /**\n * Keeps at most `hotLimit` processes alive, retiring the coldest one that is\n * not working. A running session is never killed: when every candidate is\n * mid-run the registry goes over the limit instead, because dropping a\n * parallel conversation is worse than a few extra processes.\n */\n private async evictBeyondLimit(): Promise<void> {\n while (this.recency.length > this.hotLimit) {\n const coldest = this.recency.find((key) => key !== this.activeKey && !this.isWorking(key));\n if (coldest === undefined) {\n return;\n }\n this.deps.logger.info(`Retiring idle session ${coldest} (hot limit ${this.hotLimit})`);\n await this.close(coldest);\n }\n }\n\n /** A session the agent is answering in is off limits for the LRU. */\n private isWorking(key: string): boolean {\n return this.hot.get(key)?.state.streaming === true;\n }\n\n private nextSyntheticKey(workspace: WorkspaceRef): string {\n this.syntheticKeys += 1;\n return `${workspace.cwd}#ephemeral-${this.syntheticKeys}`;\n }\n\n /** Asks the factory for a never-recording gateway and snapshots its state. */\n private async probeDraft(): Promise<AgentSessionState | undefined> {\n const factory = this.deps.factory.probeDefaults;\n if (!factory) {\n return undefined;\n }\n try {\n const gateway = await factory.call(this.deps.factory, { workspace: this.deps.defaultWorkspace });\n const state = await gateway.state();\n await gateway.dispose();\n return state;\n } catch (error: unknown) {\n // A missing (or un-spawnable) pi is not \"no catalog\": the empty panel has\n // to say so on load, not only once the first prompt fails. Every other\n // probe failure \u2014 a timeout, a protocol slip \u2014 keeps the pickers empty and\n // lets the real (recorded) spawn surface it with its actionable hint.\n if (error instanceof AgentUnavailableError) {\n throw error;\n }\n this.deps.logger.warn('Could not probe the draft model catalog', error);\n return undefined;\n }\n }\n}\n\nfunction baseName(path: string): string {\n const parts = path.split(/[\\\\/]/).filter((part) => part.length > 0);\n return parts.at(-1) ?? path;\n}\n", "import type { AgentSessionState, ChatPin, ModelRef, NoticeLevel, ThinkingLevel } from '../domain.js';\nimport type { MorseLogger } from '../logger.js';\nimport type { PromptDisposition, PromptImage, PromptMode } from '../session/service.js';\n\n/**\n * Local port (R10): the slice of the agent the chat use case needs. The concrete\n * `SessionRegistry` satisfies it structurally \u2014 the chat module never imports the\n * session module's class.\n */\nexport interface ChatAgent {\n state(): Promise<AgentSessionState>;\n prompt(\n text: string,\n mode: PromptMode,\n images?: PromptImage[],\n pins?: ChatPin[],\n ): Promise<PromptDisposition>;\n abort(): Promise<void>;\n setModel(model: ModelRef): Promise<void>;\n setThinkingLevel(level: ThinkingLevel): Promise<void>;\n compact(customInstructions?: string): Promise<void>;\n}\n\n/** Also satisfied structurally by `SessionRegistry`. */\nexport interface ChatAgentHolder {\n requireActive(): ChatAgent;\n}\n\nexport interface PromptOutcome {\n accepted: boolean;\n disposition?: PromptDisposition;\n reason?: string;\n}\n\nexport interface ChatServiceDeps {\n agent: ChatAgentHolder;\n logger: MorseLogger;\n onNotice?: (level: NoticeLevel, text: string) => void;\n}\n\n/**\n * Chat use case: validates prompts and decides between running, steering and\n * following up. Shared by every host.\n *\n * Attachments (images and pins) travel on the turn as attachments \u2014 the prompt\n * text stays exactly what the user typed. A host that wants the agent to know\n * about an editor selection pins it like any other attachment instead of\n * prepending an invisible \"context from the editor\" preamble: preamble text\n * cannot be untangled later, and it made every persisted session carry words\n * the user never wrote.\n */\nexport class ChatService {\n constructor(private readonly deps: ChatServiceDeps) {}\n\n async prompt(\n text: string,\n mode: PromptMode = 'new',\n images?: PromptImage[],\n pins?: ChatPin[],\n ): Promise<PromptOutcome> {\n const trimmed = text.trim();\n // Attachments count as content: an image (or a pinned selection) is allowed\n // to travel on its own, without the user being forced to type filler text.\n const hasAttachments =\n (images !== undefined && images.length > 0) || (pins !== undefined && pins.length > 0);\n if (trimmed.length === 0 && !hasAttachments) {\n this.notice('warn', 'Nothing to send \u2014 the prompt is empty.');\n return { accepted: false, reason: 'empty-prompt' };\n }\n\n let agent: ChatAgent;\n try {\n agent = this.deps.agent.requireActive();\n } catch (error: unknown) {\n this.deps.logger.error('Prompt rejected: no active agent session', error);\n return { accepted: false, reason: 'no-session' };\n }\n\n let effectiveMode = mode;\n const state = await agent.state();\n if (state.streaming && effectiveMode === 'new') {\n effectiveMode = 'followUp';\n this.notice('info', 'The agent is still working \u2014 queued as a follow-up.');\n }\n\n const disposition = await agent.prompt(trimmed, effectiveMode, images, pins);\n return { accepted: true, disposition };\n }\n\n async abort(): Promise<void> {\n await this.deps.agent.requireActive().abort();\n }\n\n async setModel(model: ModelRef): Promise<void> {\n await this.deps.agent.requireActive().setModel(model);\n }\n\n async setThinkingLevel(level: ThinkingLevel): Promise<void> {\n await this.deps.agent.requireActive().setThinkingLevel(level);\n }\n\n /** Shrinks the conversation context (`/compact` in the TUI). */\n async compact(customInstructions?: string): Promise<void> {\n await this.deps.agent.requireActive().compact(customInstructions);\n }\n\n async state(): Promise<AgentSessionState> {\n return this.deps.agent.requireActive().state();\n }\n\n private notice(level: NoticeLevel, text: string): void {\n this.deps.onNotice?.(level, text);\n }\n}", "/**\n * Subset of the pi RPC protocol this adapter needs.\n *\n * Mirrors the documented contract (docs/rpc-commands.md, docs/json.md,\n * docs/rpc-extension-ui.md) rather than depending on the very large\n * `@earendil-works/pi-coding-agent` package, which stays a runtime-only,\n * spawn-based dependency.\n */\n\nexport interface RpcImage {\n type: 'image';\n data: string;\n mimeType: string;\n}\n\nexport type RpcCommand =\n | { type: 'prompt'; message: string; images?: RpcImage[]; streamingBehavior?: 'steer' | 'followUp' }\n | { type: 'steer'; message: string; images?: RpcImage[] }\n | { type: 'follow_up'; message: string; images?: RpcImage[] }\n | { type: 'abort' }\n | { type: 'new_session'; parentSession?: string }\n | { type: 'switch_session'; sessionPath: string }\n | { type: 'get_state' }\n | { type: 'get_messages' }\n | { type: 'get_entries'; since?: string }\n | { type: 'get_available_models' }\n | { type: 'get_available_thinking_levels' }\n | { type: 'get_commands' }\n | { type: 'get_session_stats' }\n | { type: 'fork'; entryId: string }\n | { type: 'get_fork_messages' }\n | { type: 'set_model'; provider: string; modelId: string }\n | { type: 'set_thinking_level'; level: string }\n | { type: 'compact'; customInstructions?: string }\n | { type: 'set_session_name'; name: string }\n | { type: 'bash'; command: string };\n\nexport interface RpcResponse {\n type: 'response';\n id?: string;\n command: string;\n success: boolean;\n data?: unknown;\n error?: string;\n}\n\nexport interface RpcModel {\n id: string;\n name?: string;\n provider: string;\n api?: string;\n reasoning?: boolean;\n contextWindow?: number;\n maxTokens?: number;\n}\n\n/** `get_commands`: extension commands, prompt templates and skills. */\nexport interface RpcCommandInfo {\n name?: string;\n description?: string;\n source?: string;\n /** Where pi loaded the resource from; a prompt template's `.md` lives at `path`. */\n sourceInfo?: { path?: string; scope?: string; origin?: string };\n}\n\nexport interface RpcSessionStateData {\n model?: RpcModel;\n thinkingLevel?: string;\n isStreaming?: boolean;\n isCompacting?: boolean;\n sessionFile?: string;\n sessionId?: string;\n sessionName?: string;\n messageCount?: number;\n pendingMessageCount?: number;\n}\n\nexport interface RpcUsage {\n input?: number;\n output?: number;\n totalTokens?: number;\n cacheRead?: number;\n cacheWrite?: number;\n /** Reasoning tokens; a subset of `output`. */\n reasoning?: number;\n}\n\nexport interface RpcContextUsage {\n tokens?: number | null;\n contextWindow?: number;\n percent?: number | null;\n}\n\n/** `get_session_stats`: cumulative tokens/cost, counts, and the live context. */\nexport interface RpcSessionStatsData {\n tokens?: RpcUsage & { total?: number };\n cost?: number;\n contextUsage?: RpcContextUsage;\n userMessages?: number;\n assistantMessages?: number;\n toolCalls?: number;\n toolResults?: number;\n totalMessages?: number;\n}\n\nexport interface RpcAssistantMessageEvent {\n type: string;\n contentIndex?: number;\n delta?: string;\n id?: string;\n toolName?: string;\n content?: string;\n}\n\nexport interface RpcMessage {\n role?: string;\n content?: unknown;\n model?: string;\n provider?: string;\n stopReason?: string;\n usage?: RpcUsage;\n [key: string]: unknown;\n}\n\nexport interface RpcExtensionUiRequest {\n type: 'extension_ui_request';\n id: string;\n method: string;\n title?: string;\n message?: string;\n options?: string[];\n placeholder?: string;\n prefill?: string;\n notifyType?: string;\n statusKey?: string;\n statusText?: string;\n widgetKey?: string;\n widgetLines?: string[];\n text?: string;\n}\n\nexport interface RpcExtensionUiResponse {\n type: 'extension_ui_response';\n id: string;\n value?: string;\n confirmed?: boolean;\n cancelled?: boolean;\n}\n\n/** Anything else pi writes on stdout: a session event we may or may not map. */\nexport type RpcRecord = (RpcResponse | RpcExtensionUiRequest | { type: string }) &\n Record<string, unknown>;\n\nexport function asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : undefined;\n}\n\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nexport function asArray(value: unknown): unknown[] | undefined {\n return Array.isArray(value) ? value : undefined;\n}\n", "import type {\n AgentEvent,\n AgentHistoryEntry,\n AgentSessionState,\n ChatPin,\n NoticeLevel,\n PromptImage,\n TokenUsage,\n} from '@morse/core';\nimport {\n asArray,\n asNumber,\n asRecord,\n asString,\n type RpcAssistantMessageEvent,\n type RpcMessage,\n type RpcRecord,\n type RpcSessionStatsData,\n type RpcUsage,\n} from './internal/rpc-types.js';\n\nexport interface MappedRecord {\n events: AgentEvent[];\n state?: Partial<AgentSessionState>;\n}\n\n/**\n * Pure mapping from pi session events (docs/json.md) to Morse domain events.\n * No subprocess, no clock: the caller injects `now`, which keeps it trivially\n * testable and shared by every pi transport.\n */\nexport function mapSessionEvent(record: RpcRecord, now: () => number): MappedRecord {\n switch (record.type) {\n case 'agent_start':\n return { events: [{ type: 'agent/run-start', at: now() }], state: { streaming: true } };\n case 'message_update':\n return mapMessageUpdate(record, now);\n case 'message_end':\n return mapMessageEnd(record, now);\n case 'tool_execution_start':\n return mapToolStart(record, now);\n case 'tool_execution_update':\n return mapToolUpdate(record, now);\n case 'tool_execution_end':\n return mapToolEnd(record, now);\n case 'compaction_start':\n return { events: [notice('info', 'Compacting the conversation context\u2026', now)] };\n case 'compaction_end':\n return mapCompactionEnd(record, now);\n case 'auto_retry_start': {\n const attempt = numberField(record, 'attempt');\n const max = numberField(record, 'maxAttempts');\n const reason = stringField(record, 'errorMessage');\n const label = attempt && max ? ` (attempt ${attempt}/${max})` : '';\n return { events: [notice('warn', `Retrying${label}: ${reason ?? 'provider error'}`, now)] };\n }\n case 'auto_retry_end':\n return record['success'] === true\n ? { events: [] }\n : {\n events: [\n notice('error', stringField(record, 'finalError') ?? 'Retries exhausted.', now),\n ],\n };\n case 'extension_error':\n return {\n events: [\n notice(\n 'error',\n `pi extension failed: ${stringField(record, 'error') ?? 'unknown error'}`,\n now,\n ),\n ],\n };\n default:\n return { events: [] };\n }\n}\n\n/**\n * One entry of pi's session tree (`get_entries`): messages interleaved with\n * compaction/branch-summary markers. Unlike `get_messages`, this carries the\n * FULL history pre-dating compaction, so a resumed session can show the whole\n * conversation with the compacted span marked instead of hidden.\n */\nexport function toEntryHistory(entries: readonly unknown[]): AgentHistoryEntry[] {\n const history: AgentHistoryEntry[] = [];\n /** Tool entries by provider call id, so the result can fill in its output. */\n const toolEntries = new Map<string, Extract<AgentHistoryEntry, { role: 'tool' }>>();\n\n for (const raw of entries) {\n const entry = asRecord(raw);\n if (!entry) {\n continue;\n }\n switch (asString(entry['type'])) {\n case 'message': {\n const message = asRecord(entry['message']);\n if (message) {\n collectMessage(message, history, toolEntries);\n }\n break;\n }\n case 'compaction': {\n const at = entryTimestamp(entry['timestamp']);\n history.push({\n role: 'compaction',\n summary: asString(entry['summary']) ?? '',\n tokensBefore: asNumber(entry['tokensBefore']),\n ...(at !== undefined ? { at } : {}),\n });\n break;\n }\n case 'branch_summary': {\n const summary = asString(entry['summary']);\n if (!summary) {\n break;\n }\n const at = entryTimestamp(entry['timestamp']);\n history.push({\n role: 'user',\n text: `*The conversation briefly explored another branch and returned with this summary:*\n\n${summary}`,\n ...(at !== undefined ? { at } : {}),\n });\n break;\n }\n default:\n break;\n }\n }\n\n return history;\n}\n\n/** A flat message list (`get_messages`) seen as entries, keeping the old shape. */\nexport function toHistory(messages: readonly unknown[]): AgentHistoryEntry[] {\n return toEntryHistory(\n messages.map((message) => ({ type: 'message', message })),\n );\n}\n\n/**\n * The branch the leaf entry sits on, keeping append order: walking back through\n * `parentId` from the leaf drops abandoned branches (renamed/re-edited paths)\n * while the full pre-compaction history stays in. When the leaf (or its chain)\n * is unresolvable, everything is passed through \u2014 showing more beats hiding a\n * whole conversation behind a format quirk.\n */\nexport function activePathEntries(\n data: { entries?: unknown[]; leafId?: unknown } | undefined,\n): unknown[] {\n const records = (data?.entries ?? []).map(asRecord).filter(isDefined);\n const leafId = asString(data?.leafId);\n if (!leafId) {\n return records;\n }\n const byId = new Map(\n records\n .map((record) => [asString(record['id']), record] as const)\n .filter((pair): pair is [string, Record<string, unknown>] => pair[0] !== undefined),\n );\n if (!byId.has(leafId)) {\n return records;\n }\n const keep = new Set<string>([leafId]);\n let cursor: string | null = leafId;\n while (cursor !== null) {\n const parent = byId.get(cursor);\n const parentId = parent ? (asString(parent['parentId']) ?? null) : null;\n if (parentId === null || keep.has(parentId)) {\n break;\n }\n keep.add(parentId);\n cursor = parentId;\n }\n return records.filter((record) => keep.has(asString(record['id']) ?? ''));\n}\n\nfunction collectMessage(\n message: Record<string, unknown>,\n entries: AgentHistoryEntry[],\n toolEntries: Map<string, Extract<AgentHistoryEntry, { role: 'tool' }>>,\n): void {\n const role = asString(message['role']);\n\n if (role === 'toolResult') {\n const toolCallId = asString(message['toolCallId']);\n const entry = toolCallId ? toolEntries.get(toolCallId) : undefined;\n if (entry) {\n const output = contentToText(message['content']);\n if (output.length > 0) {\n entry.output = output;\n }\n entry.status = message['isError'] === true ? 'error' : 'ok';\n }\n return;\n }\n\n if (role !== 'user' && role !== 'assistant') {\n return;\n }\n\n let text = contentToText(message['content']);\n const thinking = contentToThinking(message['content']);\n let pins: ChatPin[] | undefined;\n const images = role === 'user' ? contentToImages(message['content']) : [];\n if (role === 'user') {\n // The transcript keeps what the user actually typed: `@mention` lines\n // Morse appended for pins come back out and render as attachment chips\n // again, instead of a blob of mentions inside the message.\n const split = splitTrailingMentions(text);\n text = split.text;\n pins = split.pins.length > 0 ? split.pins : undefined;\n }\n if (\n text.length > 0 ||\n thinking.length > 0 ||\n (pins !== undefined && pins.length > 0) ||\n images.length > 0\n ) {\n entries.push({\n role,\n text,\n thinking: thinking.length > 0 ? thinking : undefined,\n ...(images.length > 0 ? { images } : {}),\n ...(pins !== undefined ? { pins } : {}),\n model: asString(message['model']),\n at: asNumber(message['timestamp']),\n });\n }\n\n if (role === 'assistant') {\n for (const call of toolCallsOf(message['content'])) {\n const entry: Extract<AgentHistoryEntry, { role: 'tool' }> = {\n role: 'tool',\n toolCallId: call.id,\n name: call.name,\n title: describeToolCall(call.name, call.arguments),\n input: safeStringify(call.arguments),\n status: 'ok',\n at: asNumber(message['timestamp']),\n };\n toolEntries.set(call.id, entry);\n entries.push(entry);\n }\n }\n}\n\ninterface HistoryToolCall {\n id: string;\n name: string;\n arguments: Record<string, unknown> | undefined;\n}\n\nfunction toolCallsOf(content: unknown): HistoryToolCall[] {\n const blocks = asArray(content);\n if (!blocks) {\n return [];\n }\n const calls: HistoryToolCall[] = [];\n for (const block of blocks) {\n const record = asRecord(block);\n if (!record || asString(record['type']) !== 'toolCall') {\n continue;\n }\n const name = asString(record['name']);\n if (!name) {\n continue;\n }\n calls.push({\n id: asString(record['id']) ?? `call-${calls.length}`,\n name,\n arguments: asRecord(record['arguments']),\n });\n }\n return calls;\n}\n\nfunction mapMessageUpdate(record: RpcRecord, now: () => number): MappedRecord {\n const assistant = asRecord(record['assistantMessageEvent']) as RpcAssistantMessageEvent | undefined;\n if (!assistant) {\n return { events: [] };\n }\n if (assistant.type === 'text_delta' && assistant.delta) {\n return {\n events: [{ type: 'agent/delta', at: now(), channel: 'text', delta: assistant.delta }],\n };\n }\n if (assistant.type === 'thinking_delta' && assistant.delta) {\n return {\n events: [{ type: 'agent/delta', at: now(), channel: 'thinking', delta: assistant.delta }],\n };\n }\n return { events: [] };\n}\n\nfunction mapMessageEnd(record: RpcRecord, now: () => number): MappedRecord {\n const message = asRecord(record['message']) as RpcMessage | undefined;\n if (!message || message.role !== 'assistant') {\n return { events: [] };\n }\n\n const events: AgentEvent[] = [];\n // Deliberately no `streaming: false` here. A run contains many turns\n // (thinking -> tool -> thinking -> tool), so the flag means \"the agent is\n // working\", exactly like pi's own `isStreaming`; only `agent_settled` and a\n // fatal error end it. Flipping it per message made every progress indicator\n // (and the process timeline) blink once per step.\n const state: Partial<AgentSessionState> = {};\n const usage = toUsage(message.usage);\n if (usage) {\n // The panel's \"last assistant message\" breakdown; cumulative totals come\n // from `get_session_stats` and are set separately.\n state.lastUsage = usage;\n }\n\n const text = contentToText(message.content);\n const thinking = contentToThinking(message.content);\n if (text.length > 0 || thinking.length > 0) {\n events.push({ type: 'agent/message', at: now(), text, thinking });\n }\n\n if (message.stopReason === 'aborted') {\n state.streaming = false;\n events.push({ type: 'agent/run-end', at: now(), reason: 'aborted' });\n } else if (message.stopReason === 'error') {\n events.push(notice('error', 'The model request failed.', now));\n }\n\n return { events, state };\n}\n\nfunction mapToolStart(record: RpcRecord, now: () => number): MappedRecord {\n const toolCallId = stringField(record, 'toolCallId') ?? `tool-${now()}`;\n const toolName = stringField(record, 'toolName') ?? 'tool';\n const args = asRecord(record['args']);\n return {\n events: [\n {\n type: 'agent/tool-start',\n at: now(),\n toolCallId,\n name: toolName,\n title: describeToolCall(toolName, args),\n input: safeStringify(record['args']),\n },\n ],\n };\n}\n\nfunction mapToolUpdate(record: RpcRecord, now: () => number): MappedRecord {\n const toolCallId = stringField(record, 'toolCallId');\n if (!toolCallId) {\n return { events: [] };\n }\n return {\n events: [\n {\n type: 'agent/tool-update',\n at: now(),\n toolCallId,\n output: resultToText(record['partialResult']),\n status: 'running',\n },\n ],\n };\n}\n\nfunction mapToolEnd(record: RpcRecord, now: () => number): MappedRecord {\n const toolCallId = stringField(record, 'toolCallId');\n if (!toolCallId) {\n return { events: [] };\n }\n return {\n events: [\n {\n type: 'agent/tool-end',\n at: now(),\n toolCallId,\n status: record['isError'] === true ? 'error' : 'ok',\n output: resultToText(record['result']),\n },\n ],\n };\n}\n\nfunction mapCompactionEnd(record: RpcRecord, now: () => number): MappedRecord {\n if (record['aborted'] === true) {\n return { events: [notice('warn', 'Compaction was aborted.', now)] };\n }\n const error = stringField(record, 'errorMessage');\n if (error) {\n return { events: [notice('error', `Compaction failed: ${error}`, now)] };\n }\n // Success records the boundary as its own marker row (summary included),\n // replacing the old \"Context compacted.\" notice: the divider now sits where\n // the fold happened, in the live transcript as well, and the summary it\n // carries is the one the agent went forward with.\n const result = asRecord(record['result']);\n return {\n events: [\n {\n type: 'agent/compaction',\n at: now(),\n summary: asString(result?.['summary']) ?? '',\n tokensBefore: asNumber(result?.['tokensBefore']),\n },\n ],\n };\n}\n\nfunction notice(level: NoticeLevel, text: string, now: () => number): AgentEvent {\n return { type: 'agent/notice', at: now(), level, text };\n}\n\nfunction stringField(record: RpcRecord, key: string): string | undefined {\n return asString(record[key]);\n}\n\nfunction numberField(record: RpcRecord, key: string): number | undefined {\n const value = record[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction toUsage(usage: (RpcUsage & { total?: number }) | undefined): TokenUsage | undefined {\n if (!usage) {\n return undefined;\n }\n const input = usage.input ?? 0;\n const output = usage.output ?? 0;\n const cacheRead = usage.cacheRead ?? 0;\n const cacheWrite = usage.cacheWrite ?? 0;\n const reasoning = usage.reasoning ?? 0;\n return {\n inputTokens: input,\n outputTokens: output,\n totalTokens: usage.totalTokens ?? usage.total ?? input + output,\n ...(cacheRead > 0 ? { cacheReadTokens: cacheRead } : {}),\n ...(cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}),\n ...(reasoning > 0 ? { reasoningTokens: reasoning } : {}),\n };\n}\n\n/**\n * `get_session_stats` -> the state patch the footer renders: cumulative tokens,\n * cumulative cost, and how full the context window is. Cumulative (not\n * per-message) is what pi's own footer shows, so Morse shows the same numbers.\n */\nexport function toSessionStats(\n stats: RpcSessionStatsData | undefined,\n): Partial<AgentSessionState> {\n if (!stats) {\n return {};\n }\n const patch: Partial<AgentSessionState> = {};\n const usage = toUsage(stats.tokens);\n if (usage) {\n patch.usage = usage;\n }\n if (typeof stats.cost === 'number' && Number.isFinite(stats.cost)) {\n patch.costUsd = stats.cost;\n }\n const context = stats.contextUsage;\n if (context && typeof context.contextWindow === 'number') {\n patch.contextUsage = {\n tokens: typeof context.tokens === 'number' ? context.tokens : null,\n contextWindow: context.contextWindow,\n percent: typeof context.percent === 'number' ? context.percent : null,\n };\n }\n if (typeof stats.totalMessages === 'number') {\n patch.counts = {\n userMessages: stats.userMessages ?? 0,\n assistantMessages: stats.assistantMessages ?? 0,\n toolCalls: stats.toolCalls ?? 0,\n toolResults: stats.toolResults ?? 0,\n totalMessages: stats.totalMessages,\n };\n }\n return patch;\n}\n\nexport function contentToText(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n const blocks = asArray(content);\n if (!blocks) {\n return '';\n }\n const parts: string[] = [];\n for (const block of blocks) {\n const record = asRecord(block);\n if (!record || record['type'] !== 'text') {\n continue;\n }\n parts.push(asString(record['text']) ?? '');\n }\n return parts.join('');\n}\n\nexport function contentToThinking(content: unknown): string {\n const blocks = asArray(content);\n if (!blocks) {\n return '';\n }\n const parts: string[] = [];\n for (const block of blocks) {\n const record = asRecord(block);\n if (!record) {\n continue;\n }\n const type = asString(record['type']);\n if (type !== 'thinking' && type !== 'reasoning') {\n continue;\n }\n parts.push(asString(record['thinking']) ?? asString(record['text']) ?? '');\n }\n return parts.join('');\n}\n\n/** Image content blocks (persisted attachments) of a user message. */\nexport function contentToImages(content: unknown): PromptImage[] {\n const blocks = asArray(content);\n if (!blocks) {\n return [];\n }\n const images: PromptImage[] = [];\n for (const block of blocks) {\n const record = asRecord(block);\n if (!record || asString(record['type']) !== 'image') {\n continue;\n }\n const data = asString(record['data']);\n if (!data) {\n continue;\n }\n images.push({ data, mimeType: asString(record['mimeType']) ?? 'image/png' });\n }\n return images;\n}\n\nconst PIN_MENTION = /^@([^\\s]+?)(?::(\\d+)-(\\d+))?$/;\n\n/**\n * Inverse of the adapter's `appendPinMentions`: the trailing `@path[:s-e]`\n * lines a prompt was sent with come back out, so a resumed session renders the\n * pins as attachment chips and the bubble keeps the user's own words only.\n */\nexport function splitTrailingMentions(\n text: string,\n): { text: string; pins: Array<{ path: string; startLine?: number; endLine?: number }> } {\n const lines = text.split('\\n');\n let cursor = lines.length;\n const pins: Array<{ path: string; startLine?: number; endLine?: number }> = [];\n while (cursor > 0) {\n const match = PIN_MENTION.exec(lines[cursor - 1] ?? '');\n const record = match\n ? {\n path: match[1] ?? '',\n ...(match[2] !== undefined ? { startLine: Number(match[2]) } : {}),\n ...(match[3] !== undefined ? { endLine: Number(match[3]) } : {}),\n }\n : undefined;\n // A pin path is a plain file path; a prose sentence with an `@` in the\n // middle (\"what about @me?\") does not end with one, so prose stays intact.\n if (!record || record.path.length === 0) {\n break;\n }\n pins.unshift(record);\n cursor -= 1;\n }\n if (cursor === lines.length) {\n return { text, pins: [] };\n }\n // Drop the single blank line the adapter put between the prose and the pins.\n let prose = lines.slice(0, cursor).join('\\n');\n while (prose.endsWith('\\n')) {\n prose = prose.slice(0, -1);\n }\n return { text: prose, pins };\n}\n\nexport function resultToText(result: unknown): string | undefined {\n if (result === undefined || result === null) {\n return undefined;\n }\n if (typeof result === 'string') {\n return result;\n }\n const record = asRecord(result);\n if (!record) {\n return safeStringify(result);\n }\n const text = contentToText(record['content']);\n return text.length > 0 ? text : safeStringify(record);\n}\n\nexport function describeToolCall(toolName: string, args: Record<string, unknown> | undefined): string {\n if (!args) {\n return toolName;\n }\n const command = asString(args['command']) ?? asString(args['cmd']);\n if (toolName === 'bash' && command) {\n return `bash: ${truncate(command, 100)}`;\n }\n const path =\n asString(args['path']) ??\n asString(args['file_path']) ??\n asString(args['filePath']) ??\n asString(args['file']) ??\n asString(args['target']);\n if (path) {\n return `${toolName}: ${truncate(path, 100)}`;\n }\n const pattern = asString(args['pattern']) ?? asString(args['query']);\n if (pattern) {\n return `${toolName}: ${truncate(pattern, 100)}`;\n }\n return toolName;\n}\n\nfunction safeStringify(value: unknown): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n try {\n return typeof value === 'string' ? value : JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\nfunction truncate(value: string, max: number): string {\n const single = value.replace(/\\s+/g, ' ').trim();\n return single.length > max ? `${single.slice(0, max)}\u2026` : single;\n}\n\n/** Session entries timestamp in ISO (`2026-\u2026`); agent messages in Unix ms. */\nfunction entryTimestamp(value: unknown): number | undefined {\n const ms = asNumber(value);\n if (ms !== undefined) {\n return ms;\n }\n const iso = asString(value);\n if (iso) {\n const parsed = Date.parse(iso);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n return undefined;\n}\n\nfunction isDefined<T>(value: T | undefined): value is T {\n return value !== undefined;\n}\n", "import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { AgentProtocolError, AgentUnavailableError } from '@morse/core';\nimport { JsonlFramer } from './jsonl-framer.js';\nimport type { RpcCommand, RpcExtensionUiResponse, RpcRecord } from './rpc-types.js';\n\nexport interface PiRpcClientOptions {\n command: string;\n args: string[];\n cwd: string;\n env?: NodeJS.ProcessEnv;\n requestTimeoutMs?: number;\n onRecord: (record: RpcRecord) => void;\n onStderr?: (text: string) => void;\n onExit?: (info: { code: number | null; signal: NodeJS.Signals | null }) => void;\n}\n\ninterface PendingRequest {\n command: string;\n resolve: (data: unknown) => void;\n reject: (error: unknown) => void;\n timer: NodeJS.Timeout;\n}\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 30_000;\nconst SHUTDOWN_GRACE_MS = 2_000;\n\n/**\n * Minimal JSONL RPC client for `pi --mode rpc`.\n *\n * Deliberately hand-written: it keeps the extension bundle thin and pins the\n * protocol framing rules (LF only, strip CR, honour stdin backpressure, never\n * parse stderr as protocol data).\n */\nexport class PiRpcClient {\n private child: ChildProcessWithoutNullStreams | undefined;\n private readonly framer = new JsonlFramer();\n private nextId = 1;\n private readonly pending = new Map<string, PendingRequest>();\n private failed: Error | undefined;\n\n constructor(private readonly options: PiRpcClientOptions) {}\n\n get running(): boolean {\n return this.child !== undefined;\n }\n\n start(): void {\n if (this.child) {\n return;\n }\n this.failed = undefined;\n const child = spawn(this.options.command, this.options.args, {\n cwd: this.options.cwd,\n env: this.options.env ?? process.env,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n });\n this.child = child;\n child.stdout.on('data', (chunk: Buffer) => this.onStdout(chunk));\n child.stderr.on('data', (chunk: Buffer) => {\n this.options.onStderr?.(chunk.toString('utf8'));\n });\n child.on('error', (error: Error) => {\n this.failAll(\n new AgentUnavailableError(\n `Failed to start the pi agent (${this.options.command}): ${error.message}`,\n { cause: error },\n ),\n );\n });\n child.on('exit', (code, signal) => {\n this.child = undefined;\n this.failAll(\n new AgentUnavailableError(\n `The pi agent exited (code=${code === null ? 'null' : code}, signal=${signal ?? 'null'}).`,\n ),\n );\n this.options.onExit?.({ code, signal });\n });\n }\n\n async request<T>(\n command: RpcCommand,\n timeoutMs = this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,\n ): Promise<T> {\n if (!this.child) {\n throw this.failed ?? new AgentUnavailableError('The pi agent is not running.');\n }\n const id = `morse-${this.nextId++}`;\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(\n new AgentProtocolError(`pi did not answer \"${command.type}\" within ${timeoutMs} ms.`),\n );\n }, timeoutMs);\n this.pending.set(id, {\n command: command.type,\n resolve: (data) => resolve(data as T),\n reject,\n timer,\n });\n this.write({ id, ...command }, (error) => {\n this.settle(id, error);\n });\n });\n }\n\n /** Answers an `extension_ui_request`; never expects a matching response. */\n respond(response: RpcExtensionUiResponse): void {\n this.write(response);\n }\n\n async dispose(): Promise<void> {\n const child = this.child;\n if (!child) {\n return;\n }\n let exited = false;\n await new Promise<void>((resolve) => {\n const finish = (): void => {\n exited = true;\n resolve();\n };\n child.once('exit', finish);\n // Documented orderly shutdown: closing stdin lets pi dispose its runtime.\n child.stdin.end();\n setTimeout(resolve, SHUTDOWN_GRACE_MS).unref?.();\n });\n if (!exited) {\n child.kill();\n }\n this.child = undefined;\n }\n\n private onStdout(chunk: Buffer): void {\n for (const line of this.framer.push(chunk)) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n this.options.onStderr?.(`[morse] unreadable pi record: ${line}\\n`);\n continue;\n }\n this.handleRecord(parsed);\n }\n }\n\n private handleRecord(parsed: unknown): void {\n const record = parsed as RpcRecord;\n if (record !== null && typeof record === 'object' && record['type'] === 'response') {\n this.handleResponse(record);\n }\n this.options.onRecord(record);\n }\n\n private handleResponse(record: RpcRecord): void {\n const id = typeof record['id'] === 'string' ? record['id'] : undefined;\n if (!id) {\n return;\n }\n const pending = this.pending.get(id);\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n this.pending.delete(id);\n if (record['success'] === true) {\n pending.resolve(record['data']);\n return;\n }\n const error = typeof record['error'] === 'string' ? record['error'] : 'unknown error';\n pending.reject(new AgentProtocolError(`pi rejected \"${pending.command}\": ${error}`));\n }\n\n private write(payload: unknown, onError?: (error: Error) => void): void {\n const child = this.child;\n if (!child) {\n onError?.(new AgentUnavailableError('The pi agent is not running.'));\n return;\n }\n const frame = `${JSON.stringify(payload)}\\n`;\n child.stdin.write(frame, 'utf8', (error) => {\n if (error) {\n onError?.(error);\n }\n });\n }\n\n private settle(id: string, error: Error): void {\n const pending = this.pending.get(id);\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n this.pending.delete(id);\n pending.reject(error);\n }\n\n private failAll(error: Error): void {\n this.failed = error;\n for (const [id, pending] of [...this.pending]) {\n clearTimeout(pending.timer);\n this.pending.delete(id);\n pending.reject(error);\n }\n }\n}\n", "import { StringDecoder } from 'node:string_decoder';\n\n/**\n * Strict JSONL framing for the pi RPC stream.\n *\n * The docs are explicit: split only on LF, strip an optional preceding CR, and\n * never use `readline` (it also splits on U+2028/U+2029, which are legal inside\n * JSON strings). `StringDecoder` keeps multi-byte UTF-8 sequences intact when a\n * chunk boundary falls inside a character.\n */\nexport class JsonlFramer {\n private readonly decoder = new StringDecoder('utf8');\n private buffer = '';\n\n push(chunk: Buffer | string): string[] {\n this.buffer += typeof chunk === 'string' ? chunk : this.decoder.write(chunk);\n const records: string[] = [];\n let newline = this.buffer.indexOf('\\n');\n while (newline !== -1) {\n const line = stripCarriageReturn(this.buffer.slice(0, newline));\n this.buffer = this.buffer.slice(newline + 1);\n if (line.trim().length > 0) {\n records.push(line);\n }\n newline = this.buffer.indexOf('\\n');\n }\n return records;\n }\n\n /** Returns a trailing record that never got its LF, if any. */\n flush(): string | undefined {\n const rest = stripCarriageReturn(this.buffer + this.decoder.end());\n this.buffer = '';\n return rest.trim().length > 0 ? rest : undefined;\n }\n}\n\nfunction stripCarriageReturn(line: string): string {\n return line.endsWith('\\r') ? line.slice(0, -1) : line;\n}\n", "import { accessSync, constants } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { delimiter, isAbsolute, join } from 'node:path';\nimport { AgentUnavailableError } from '@morse/core';\n\nexport interface ResolvePiOptions {\n /** Explicit path to the `pi` binary (setting `morse.pi.path` / `MORSE_PI_PATH`). */\n piPath?: string;\n /** Run the bundled RPC entry with node instead of the `pi` binary. */\n nodeEntryPath?: string;\n /** Override the session directory (`--session-dir`). */\n sessionDir?: string;\n /** Do not persist a session (`--no-session`). */\n noSession?: boolean;\n /** Resume an existing session file (`--session <path>`). */\n sessionPath?: string;\n extraArgs?: string[];\n env?: NodeJS.ProcessEnv;\n}\n\nexport type PiSpawnSource = 'configured' | 'path' | 'node-entry';\n\n/**\n * How to install the agent. The error message and the `remedy` both spell it out\n * from here, so what the setup screen offers and what the log says cannot drift.\n */\nexport const PI_INSTALL_COMMAND = 'npm install -g @earendil-works/pi-coding-agent';\n\nexport interface PiSpawn {\n command: string;\n args: string[];\n source: PiSpawnSource;\n}\n\nexport function buildRpcArgs(options: ResolvePiOptions): string[] {\n const args = ['--mode', 'rpc'];\n if (options.noSession) {\n args.push('--no-session');\n }\n if (options.sessionDir) {\n args.push('--session-dir', options.sessionDir);\n }\n if (options.sessionPath) {\n args.push('--session', options.sessionPath);\n }\n args.push(...(options.extraArgs ?? []));\n return args;\n}\n\n/**\n * Resolution order: explicit path, `node <rpc-entry>` when asked for, then the\n * `pi` binary on PATH. Everything else raises an actionable error instead of\n * failing later with a confusing spawn message.\n */\nexport function resolvePi(options: ResolvePiOptions = {}): PiSpawn {\n const env = options.env ?? process.env;\n const args = buildRpcArgs(options);\n const configured = options.piPath ?? env.MORSE_PI_PATH;\n if (configured) {\n return { command: configured, args, source: 'configured' };\n }\n\n const entry = options.nodeEntryPath ?? env.MORSE_PI_ENTRY;\n if (entry && isExecutableEntry(entry)) {\n return { command: process.execPath, args: [entry, ...args], source: 'node-entry' };\n }\n\n const onPath = findOnPath('pi', env);\n if (onPath) {\n return { command: onPath, args, source: 'path' };\n }\n\n throw new AgentUnavailableError(\n [\n 'The pi coding agent was not found.',\n `Install it (\\`${PI_INSTALL_COMMAND}\\`) or point Morse at it:`,\n '- VS Code: setting \"morse.pi.path\"',\n '- NestJS host: env MORSE_PI_PATH (or MORSE_PI_ENTRY for a bundled rpc-entry.js)',\n ].join('\\n'),\n { remedy: { install: PI_INSTALL_COMMAND } },\n );\n}\n\nexport function findOnPath(binary: string, env: NodeJS.ProcessEnv = process.env): string | undefined {\n const pathValue = env.PATH ?? env.Path ?? '';\n if (pathValue.length === 0) {\n return undefined;\n }\n const extensions =\n process.platform === 'win32'\n ? (env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';').filter((value) => value.length > 0)\n : [''];\n for (const directory of pathValue.split(delimiter)) {\n if (directory.length === 0) {\n continue;\n }\n for (const extension of extensions) {\n const candidate = join(directory, `${binary}${extension}`);\n if (isExecutableEntry(candidate)) {\n return candidate;\n }\n }\n }\n return undefined;\n}\n\nexport function resolveSessionDir(configured?: string, env: NodeJS.ProcessEnv = process.env): string {\n const fromEnv = env.PI_CODING_AGENT_SESSION_DIR;\n const value = configured ?? (fromEnv && fromEnv.length > 0 ? fromEnv : undefined);\n const base = value ?? join(homedir(), '.pi', 'agent', 'sessions');\n return isAbsolute(base) ? base : join(process.cwd(), base);\n}\n\nfunction isExecutableEntry(path: string): boolean {\n try {\n accessSync(path, constants.R_OK);\n return true;\n } catch {\n return false;\n }\n}\n", "import type {\n AgentGateway,\n AgentGatewayFactory,\n MorseLogger,\n WorkspaceRef,\n} from '@morse/core';\nimport { PiRpcAgent } from './pi-rpc-agent.js';\nimport { resolvePi, type ResolvePiOptions } from './internal/resolve-pi.js';\n\nexport interface PiRpcAgentFactoryOptions {\n piPath?: string;\n nodeEntryPath?: string;\n sessionDir?: string;\n noSession?: boolean;\n extraArgs?: string[];\n env?: NodeJS.ProcessEnv;\n requestTimeoutMs?: number;\n}\n\n/** Implements `AgentGatewayFactory` (declared by core) by spawning `pi --mode rpc`. */\nexport class PiRpcAgentFactory implements AgentGatewayFactory {\n constructor(\n private readonly options: PiRpcAgentFactoryOptions,\n private readonly logger: MorseLogger,\n ) {}\n\n async create(input: {\n workspace: WorkspaceRef;\n sessionId?: string;\n }): Promise<AgentGateway> {\n const resolveOptions: ResolvePiOptions = {\n piPath: this.options.piPath,\n nodeEntryPath: this.options.nodeEntryPath,\n sessionDir: this.options.sessionDir,\n noSession: this.options.noSession,\n // The catalog hands back session file paths as ids, which is what\n // `pi --session <path>` expects.\n sessionPath: input.sessionId,\n extraArgs: this.options.extraArgs,\n env: this.options.env,\n };\n return this.spawnAgent(resolveOptions, input.workspace, input.sessionId);\n }\n\n /**\n * A gateway that never records: pi is forced onto `--no-session` and no\n * session path is passed, so this spawn cannot touch the session list on\n * disk. Its only job is to answer state once \u2014 model catalog, thinking\n * levels, commands \u2014 for the draft (empty panel) state.\n */\n async probeDefaults(input: { workspace: WorkspaceRef }): Promise<AgentGateway> {\n const resolveOptions: ResolvePiOptions = {\n piPath: this.options.piPath,\n nodeEntryPath: this.options.nodeEntryPath,\n sessionDir: this.options.sessionDir,\n noSession: true,\n extraArgs: this.options.extraArgs,\n env: this.options.env,\n };\n return this.spawnAgent(resolveOptions, input.workspace);\n }\n\n private async spawnAgent(\n resolveOptions: ResolvePiOptions,\n workspace: WorkspaceRef,\n sessionPath?: string,\n ): Promise<AgentGateway> {\n const spawn = resolvePi(resolveOptions);\n this.logger.info(\n `Starting pi agent (${spawn.source}): ${spawn.command} ${spawn.args.join(' ')}`,\n );\n\n const agent = new PiRpcAgent({\n spawn,\n workspace,\n logger: this.logger,\n sessionPath,\n env: this.options.env,\n requestTimeoutMs: this.options.requestTimeoutMs,\n });\n await agent.initialize();\n return agent;\n }\n}\n", "import { open, readdir, readFile, stat, unlink } from 'node:fs/promises';\nimport { join, resolve, sep } from 'node:path';\nimport type { SessionCatalog, SessionSummary } from '@morse/core';\nimport { asRecord, asString } from './internal/rpc-types.js';\nimport { resolveSessionDir } from './internal/resolve-pi.js';\n\nexport interface PiRpcSessionCatalogOptions {\n sessionDir?: string;\n maxSessions?: number;\n env?: NodeJS.ProcessEnv;\n /**\n * How one session file is removed. Defaults to `unlink`; a host can inject its\n * own (VS Code goes through the workspace filesystem, so the file lands in the\n * OS trash and the call works in remote windows too).\n */\n removeFile?: (path: string) => Promise<void>;\n /**\n * How a session file is summarised. Defaults to reading it; a test injects its\n * own to see *when* a file is read at all (the cache below is the point).\n */\n readSummary?: (\n path: string,\n updatedAt: number,\n previous?: CachedSummary,\n ) => Promise<SessionScan | undefined>;\n}\n\n/** What reading one session file produced, plus where to resume next time. */\nexport interface SessionScan {\n summary: SessionSummary;\n scanned: number;\n name?: string;\n firstUserText?: string;\n}\n\n/** A summary plus everything needed to continue reading the file where it stopped. */\ninterface CachedSummary {\n size: number;\n updatedAt: number;\n summary: SessionSummary;\n /** Byte offset already scanned, always just past a newline. */\n scanned: number;\n /** Title sources found so far, kept apart from the rendered title. */\n name?: string;\n firstUserText?: string;\n}\n\n/**\n * pi writes one compact JSON object per line with `type` first. Reading a line's\n * type is therefore a couple of string searches, with no object to allocate and\n * no parse: reading a project's sessions means scanning tens of megabytes, and\n * `JSON.parse` on every line whose only contribution was bumping `messageCount`\n * cost ~0.6 s of CPU per `session/list` on a real catalog. A line that is *not*\n * in that shape is parsed the slow way, so nothing is silently missed.\n */\nconst TYPE_PREFIX = '{\"type\":\"';\n\nfunction typeAt(raw: string, start: number, end: number): string | undefined {\n if (!raw.startsWith(TYPE_PREFIX, start)) {\n return undefined;\n }\n const close = raw.indexOf('\"', start + TYPE_PREFIX.length);\n return close === -1 || close > end ? undefined : raw.slice(start + TYPE_PREFIX.length, close);\n}\n\n/**\n * Implements `SessionCatalog` by reading pi's session files\n * (`<session-dir>/--<cwd>--/<timestamp>_<id>.jsonl`, see docs/session-format.md).\n * The id returned is the session file path so it can be resumed with\n * `pi --session <path>`.\n */\nexport class PiRpcSessionCatalog implements SessionCatalog {\n private readonly directory: string;\n private readonly maxSessions: number;\n private readonly removeFile: (path: string) => Promise<void>;\n private readonly readSummary: (\n path: string,\n updatedAt: number,\n previous?: CachedSummary,\n ) => Promise<SessionScan | undefined>;\n /**\n * Summaries survive between calls, keyed by path and validated by size + mtime.\n * The list is asked for far more often than sessions change \u2014 every reconnect,\n * every new session, every rename \u2014 and re-reading 50 transcripts each time was\n * the single most expensive thing the host did.\n */\n private readonly cache = new Map<string, CachedSummary>();\n\n constructor(options: PiRpcSessionCatalogOptions = {}) {\n this.directory = resolveSessionDir(options.sessionDir, options.env);\n this.maxSessions = options.maxSessions ?? 50;\n this.removeFile = options.removeFile ?? unlink;\n this.readSummary = options.readSummary ?? readSessionSummary;\n }\n\n get sessionDirectory(): string {\n return this.directory;\n }\n\n /**\n * A list already being built is handed to every caller instead of being started\n * again: two frontends connecting at the same moment, or a refresh landing while\n * a push is in flight, used to mean two full scans of the whole catalog.\n */\n private inFlight: Promise<SessionSummary[]> | undefined;\n\n async list(): Promise<SessionSummary[]> {\n this.inFlight ??= this.buildList().finally(() => {\n this.inFlight = undefined;\n });\n return this.inFlight;\n }\n\n private async buildList(): Promise<SessionSummary[]> {\n const files = await this.collectSessionFiles();\n const summaries: SessionSummary[] = [];\n const seen = new Set<string>();\n for (const file of files.slice(0, this.maxSessions)) {\n seen.add(file.path);\n const cached = this.cache.get(file.path);\n if (cached !== undefined && cached.size === file.size && cached.updatedAt === file.updatedAt) {\n summaries.push(cached.summary);\n continue;\n }\n const scan = await this.readSummary(file.path, file.updatedAt, cached);\n if (scan === undefined) {\n this.cache.delete(file.path);\n continue;\n }\n this.cache.set(file.path, { size: file.size, updatedAt: file.updatedAt, ...scan });\n summaries.push(scan.summary);\n }\n // A deleted session (or one that fell out of `maxSessions`) must not keep its\n // text alive here.\n for (const path of [...this.cache.keys()]) {\n if (!seen.has(path)) {\n this.cache.delete(path);\n }\n }\n return summaries;\n }\n\n /**\n * Deletes one stored session. The id is a session file path, so the guard is\n * the point: only a `.jsonl` inside this catalog's session directory is ever\n * removed. A stale client or a hostile payload cannot turn a session id into\n * a delete of an arbitrary file.\n */\n async remove(id: string): Promise<void> {\n const path = resolve(id);\n const directory = resolve(this.directory);\n if (!path.startsWith(`${directory}${sep}`) || !path.endsWith('.jsonl')) {\n throw new Error(`Refusing to delete a session outside ${directory}`);\n }\n await this.removeFile(path);\n }\n\n private async collectSessionFiles(): Promise<\n { path: string; size: number; updatedAt: number }[]\n > {\n const buckets = await safeReadDir(this.directory);\n const files: { path: string; size: number; updatedAt: number }[] = [];\n for (const bucket of buckets) {\n if (!bucket.isDirectory()) {\n continue;\n }\n const bucketPath = join(this.directory, bucket.name);\n for (const entry of await safeReadDir(bucketPath)) {\n if (!entry.isFile() || !entry.name.endsWith('.jsonl')) {\n continue;\n }\n const path = join(bucketPath, entry.name);\n const info = await stat(path).catch(() => undefined);\n files.push({ path, size: info?.size ?? 0, updatedAt: info?.mtimeMs ?? 0 });\n }\n }\n files.sort((left, right) => right.updatedAt - left.updatedAt);\n return files;\n }\n}\n\n/**\n * Reads one session file into a summary.\n *\n * A file that only *grew* since the last scan is read from where that scan\n * stopped: a long session appends a message at a time, and re-reading all 18 MB of\n * it for every `session/list` cost ~65 ms against ~1 ms for the tail. Anything else\n * \u2014 a shorter file, an older mtime, no previous scan \u2014 is read whole, so the\n * summary can never drift from the file.\n */\nasync function readSessionSummary(\n path: string,\n updatedAt: number,\n previous?: CachedSummary,\n): Promise<SessionScan | undefined> {\n let resumable =\n previous !== undefined && previous.scanned > 0 && updatedAt >= previous.updatedAt;\n if (resumable && previous !== undefined) {\n const info = await stat(path).catch(() => undefined);\n // Only an append can be read from the tail. A shorter file (or one of the same\n // length) was rewritten, and the prefix we scanned is not the prefix on disk.\n resumable = info !== undefined && info.size > previous.scanned;\n }\n const from = resumable && previous !== undefined ? previous.scanned : 0;\n const raw = resumable\n ? await readFrom(path, from).catch(() => undefined)\n : await readFile(path, 'utf8').catch(() => undefined);\n if (raw === undefined) {\n return undefined;\n }\n\n let cwd = resumable && previous !== undefined ? previous.summary.cwd : '';\n let name = resumable && previous !== undefined ? previous.name : undefined;\n let firstUserText = resumable && previous !== undefined ? previous.firstUserText : undefined;\n let messageCount = resumable && previous !== undefined ? previous.summary.messageCount : 0;\n let scanned = from;\n /** Characters consumed up to the last complete line, in this read. */\n let consumed = 0;\n\n const parseAt = (start: number, end: number) => parseLine(raw.slice(start, end));\n\n for (let start = 0; start < raw.length; ) {\n const newline = raw.indexOf('\\n', start);\n const end = newline === -1 ? raw.length : newline;\n // Walk the buffer by offset: slicing every line (or `split`ting the whole file\n // into 2000 strings) allocated tens of megabytes per scan, for lines that\n // mostly only needed counting.\n //\n // The last chunk may be a line still being written, and a half-flushed entry\n // must not be read as a (broken) line: it only counts once it parses, which is\n // what the scan did before this shortcut existed.\n const complete = newline !== -1;\n let type: string | undefined;\n if (raw.charCodeAt(start) === 123 /* '{' */) {\n type = complete\n ? (typeAt(raw, start, end) ?? asString(parseAt(start, end)?.['type']))\n : asString(parseAt(start, end)?.['type']);\n if (type === 'message') {\n messageCount += 1;\n if (firstUserText === undefined) {\n const message = asRecord(parseAt(start, end)?.['message']);\n if (message?.['role'] === 'user') {\n firstUserText = textOf(message['content']);\n }\n }\n } else if (type === 'session') {\n if (cwd.length === 0) {\n cwd = asString(parseAt(start, end)?.['cwd']) ?? '';\n }\n } else if ((type === 'session_info' || type === 'session_name') && !name) {\n name = asString(parseAt(start, end)?.['name']);\n }\n }\n if (!complete) {\n // The tail is either a whole entry whose newline has not landed yet \u2014 it\n // counted above, so the next scan must start after it \u2014 or a half-written\n // line, which is left in place to be read again whole.\n if (type !== undefined) {\n consumed = raw.length;\n }\n break;\n }\n consumed = newline + 1;\n start = newline + 1;\n }\n\n // Byte offsets are what the next scan resumes from, and they have to sit on a\n // character boundary: measured once from the text, not per line.\n if (consumed > 0) {\n scanned = from + Buffer.byteLength(raw.slice(0, consumed));\n }\n\n return {\n summary: {\n id: path,\n title: name ?? titleFromText(firstUserText) ?? 'Untitled session',\n cwd,\n updatedAt,\n messageCount,\n },\n scanned,\n name,\n firstUserText,\n };\n}\n\n/** The tail of a file, from a byte offset that sits on a character boundary. */\nasync function readFrom(path: string, from: number): Promise<string> {\n const handle = await open(path, 'r');\n try {\n const info = await handle.stat();\n if (info.size <= from) {\n return '';\n }\n const length = info.size - from;\n const buffer = Buffer.alloc(length);\n let filled = 0;\n while (filled < length) {\n const { bytesRead } = await handle.read(buffer, filled, length - filled, from + filled);\n if (bytesRead === 0) {\n break;\n }\n filled += bytesRead;\n }\n return buffer.subarray(0, filled).toString('utf8');\n } finally {\n await handle.close();\n }\n}\n\n/** Only reached for a line that is not in pi's canonical shape. */\nfunction parseLine(line: string): Record<string, unknown> | undefined {\n try {\n return JSON.parse(line) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\n/**\n * User content is either a plain string or a list of blocks; only text blocks\n * carry a title-worth string (images and tool results do not).\n */\nfunction textOf(content: unknown): string | undefined {\n const direct = asString(content);\n if (direct !== undefined) {\n return direct.length > 0 ? direct : undefined;\n }\n const blocks = Array.isArray(content) ? content : undefined;\n if (!blocks) {\n return undefined;\n }\n for (const block of blocks) {\n const record = asRecord(block);\n if (record?.['type'] !== 'text') {\n continue;\n }\n const text = asString(record['text']);\n if (text !== undefined && text.trim().length > 0) {\n return text;\n }\n }\n return undefined;\n}\n\nfunction titleFromText(text: string | undefined): string | undefined {\n if (!text) {\n return undefined;\n }\n const single = text.replace(/\\s+/g, ' ').trim();\n if (single.length === 0) {\n return undefined;\n }\n return single.length > 72 ? `${single.slice(0, 72)}\u2026` : single;\n}\n\nasync function safeReadDir(path: string): Promise<import('node:fs').Dirent[]> {\n try {\n return await readdir(path, { withFileTypes: true });\n } catch {\n return [];\n }\n}\n", "import type { AgentGatewayFactory, MorseLogger, SessionCatalog } from '@morse/core';\nimport { PiRpcAgentFactory, type PiRpcAgentFactoryOptions } from './pi-rpc-agent-factory.js';\nimport { PiRpcSessionCatalog } from './pi-rpc-session-catalog.js';\nimport { resolvePi, type PiSpawn } from './internal/resolve-pi.js';\n\nexport interface PiRpcAdapterConfig extends PiRpcAgentFactoryOptions {\n sessionDir?: string;\n maxSessions?: number;\n /** How session files are removed; the host supplies its own (see the catalog). */\n removeSessionFile?: (path: string) => Promise<void>;\n}\n\nexport interface PiRpcAdapter {\n factory: AgentGatewayFactory;\n catalog: SessionCatalog;\n /** Resolves the spawn command; throws with an actionable message when pi is missing. */\n describe(): PiSpawn;\n}\n\n/**\n * Wires the pi RPC adapter. Both hosts (VS Code extension and NestJS server)\n * call this and receive the same ports \u2014 only the delivery differs.\n */\nexport function createPiRpcAdapter(config: PiRpcAdapterConfig, logger: MorseLogger): PiRpcAdapter {\n const factory = new PiRpcAgentFactory(config, logger);\n const catalog = new PiRpcSessionCatalog({\n sessionDir: config.sessionDir,\n maxSessions: config.maxSessions,\n env: config.env,\n removeFile: config.removeSessionFile,\n });\n return {\n factory,\n catalog,\n describe: () =>\n resolvePi({\n piPath: config.piPath,\n nodeEntryPath: config.nodeEntryPath,\n sessionDir: config.sessionDir,\n noSession: config.noSession,\n extraArgs: config.extraArgs,\n env: config.env,\n }),\n };\n}\n", "/**\n * The bounded text a terminal replays to a viewer that attached late.\n *\n * A shell's output is unbounded (`yes` never stops), so both the in-memory\n * replay and the on-disk scrollback keep only the tail. One implementation, so\n * the two never disagree about what \"the last N\" means.\n */\n\n/**\n * How much scrollback one terminal keeps. Small enough to hold for every pane a\n * reader might leave open, large enough to cover what a person scrolls back to.\n */\nexport const TERMINAL_BUFFER_CHARS = 256 * 1024;\n\n/**\n * Keeps the last `maxChars` of `text`, cut at a newline so a replayed scrollback\n * does not start in the middle of an escape sequence (which would paint\n * garbage). A single line longer than the cap has no newline to cut at, so its\n * raw tail is kept \u2014 better partial output than none.\n */\nexport function trimTail(text: string, maxChars = TERMINAL_BUFFER_CHARS): string {\n // `slice` with a negative start is relative to the end, so this is the tail.\n if (text.length <= maxChars) {\n return text;\n }\n const tail = text.slice(-maxChars);\n const newline = tail.indexOf('\\n');\n return newline === -1 ? tail : tail.slice(newline + 1);\n}\n", "import type { AgentEvent } from '@morse/core';\nimport type {\n AssistantTranscriptItem,\n ChatPin,\n HostToClientMessage,\n NoticeLevel,\n PromptImage,\n ToolTranscriptItem,\n TranscriptItem,\n UserTranscriptItem,\n} from '@morse/protocol';\n\nexport interface TranscriptProjectorOptions {\n emit: (message: HostToClientMessage) => void;\n now?: () => number;\n idPrefix?: string;\n}\n\n/**\n * Turns the agent's transport-neutral events into the frontend's transcript.\n * One implementation shared by the VS Code host and the NestJS host, so both\n * render byte-identical conversations.\n */\nexport class TranscriptProjector {\n private readonly items: TranscriptItem[] = [];\n private readonly toolIndexes = new Map<string, number>();\n private assistantIndex = -1;\n private counter = 0;\n private readonly now: () => number;\n private readonly idPrefix: string;\n\n constructor(private readonly options: TranscriptProjectorOptions) {\n this.now = options.now ?? (() => Date.now());\n this.idPrefix = options.idPrefix ?? 'morse';\n }\n\n snapshot(): TranscriptItem[] {\n return this.items.map((item) => ({ ...item }));\n }\n\n reset(): void {\n this.items.length = 0;\n this.toolIndexes.clear();\n this.assistantIndex = -1;\n this.options.emit({ type: 'transcript/replace', payload: { items: [] } });\n }\n\n userPrompt(text: string, images?: PromptImage[], pins?: ChatPin[]): void {\n const item: UserTranscriptItem = {\n kind: 'user',\n id: this.nextId('user'),\n at: this.now(),\n text,\n ...(images && images.length > 0 ? { images } : {}),\n ...(pins && pins.length > 0 ? { pins } : {}),\n };\n this.assistantIndex = -1;\n this.appendItem(item);\n }\n\n /** Appends an item that already happened (a seeded history entry). */\n append(item: TranscriptItem): void {\n this.items.push(item);\n this.options.emit({ type: 'transcript/append', payload: { ...item } });\n }\n\n /**\n * Replaces the whole transcript in one message. Used when a fork re-parents\n * the session: the branch after the edited message no longer exists, so the\n * client must drop those rows instead of appending to them.\n */\n replace(items: TranscriptItem[]): void {\n this.items.length = 0;\n this.items.push(...items);\n this.toolIndexes.clear();\n this.assistantIndex = -1;\n this.options.emit({ type: 'transcript/replace', payload: { items: this.snapshot() } });\n }\n\n /**\n * Inserts an older history page in front of everything already shown. The\n * recorded indexes point into `items`, so they shift by the page size.\n */\n prepend(items: TranscriptItem[]): void {\n if (items.length === 0) {\n return;\n }\n this.items.unshift(...items);\n if (this.assistantIndex !== -1) {\n this.assistantIndex += items.length;\n }\n for (const [toolCallId, index] of this.toolIndexes) {\n this.toolIndexes.set(toolCallId, index + items.length);\n }\n this.options.emit({ type: 'transcript/prepend', payload: { items } });\n }\n\n notice(level: NoticeLevel, text: string): void {\n this.appendItem({ kind: 'notice', id: this.nextId('notice'), at: this.now(), level, text });\n }\n\n /** The live compaction boundary: a marker row for where the context reset. */\n private appendMarker(event: { at: number; summary: string; tokensBefore?: number }): void {\n const { summary, tokensBefore } = event;\n this.appendItem({\n kind: 'compaction',\n id: this.nextId('compaction'),\n at: this.now(),\n ...(summary.length > 0 ? { summary } : {}),\n ...(tokensBefore !== undefined ? { tokensBefore } : {}),\n });\n }\n\n error(message: string, detail?: string): void {\n // A command failure with a session on screen is conversation content: the\n // red row says what failed, where it happened. It must NOT also raise the\n // wire `error` \u2014 the frontend renders that as its \"Offline \u2014 the host\n // wouldn't take this frontend\" banner (Reload / Retry buttons included),\n // which describes a handshake that never failed: a rejected `compact`\n // looked like a dead connection. The wire error stays reserved for a\n // refusing handshake (see the controller's `emitWireError`).\n this.appendItem({\n kind: 'notice',\n id: this.nextId('error'),\n at: this.now(),\n level: 'error',\n text: detail ? `${message}\\n${detail}` : message,\n });\n }\n\n apply(event: AgentEvent): void {\n switch (event.type) {\n case 'agent/run-start':\n this.assistantIndex = -1;\n break;\n case 'agent/delta':\n this.applyDelta(event.channel, event.delta);\n break;\n case 'agent/message':\n this.applyMessage(event.text, event.thinking);\n break;\n case 'agent/tool-start':\n this.applyToolStart(event.toolCallId, event.name, event.title, event.input);\n break;\n case 'agent/tool-update':\n this.applyToolUpdate(event.toolCallId, event.output, event.status);\n break;\n case 'agent/tool-end':\n this.applyToolEnd(event.toolCallId, event.status, event.output, event.durationMs);\n break;\n case 'agent/run-end':\n this.finishAssistant(event.reason === 'error' ? undefined : event.reason);\n break;\n case 'agent/notice':\n this.notice(event.level, event.text);\n break;\n case 'agent/compaction':\n // Live compaction: the marker lands right here, in the middle of the\n // conversation it summarizes \u2014 the transcript row and the divider stay\n // in place for every later replay of this warm transcript.\n this.appendMarker(event);\n break;\n case 'agent/fatal':\n this.error(event.message, event.detail);\n break;\n default:\n break;\n }\n }\n\n private applyDelta(channel: 'text' | 'thinking', delta: string): void {\n const item = this.ensureAssistant();\n this.options.emit({\n type: 'transcript/delta',\n payload: channel === 'text' ? { id: item.id, text: delta } : { id: item.id, thinking: delta },\n });\n item[channel] += delta;\n item.streaming = true;\n }\n\n private applyMessage(text: string, thinking: string): void {\n const item = this.ensureAssistant();\n item.text = text;\n item.thinking = thinking;\n item.streaming = false;\n this.assistantIndex = -1;\n this.options.emit({ type: 'transcript/update', payload: { ...item } });\n }\n\n private finishAssistant(reason: 'settled' | 'aborted' | undefined): void {\n if (this.assistantIndex === -1) {\n return;\n }\n const item = this.items[this.assistantIndex];\n if (item && item.kind === 'assistant') {\n item.streaming = false;\n if (reason === 'aborted' && item.text.length === 0) {\n item.text = '_Run aborted._';\n }\n this.options.emit({ type: 'transcript/update', payload: { ...item } });\n }\n this.assistantIndex = -1;\n }\n\n private applyToolStart(\n toolCallId: string,\n name: string,\n title: string,\n input: string | undefined,\n ): void {\n const item: ToolTranscriptItem = {\n kind: 'tool',\n id: this.nextId('tool'),\n at: this.now(),\n name,\n title,\n status: 'running',\n input,\n };\n this.toolIndexes.set(toolCallId, this.items.length);\n this.appendItem(item);\n }\n\n private applyToolUpdate(\n toolCallId: string,\n output: string | undefined,\n status: 'running' | 'ok' | 'error' | undefined,\n ): void {\n const item = this.toolItem(toolCallId);\n if (!item) {\n return;\n }\n if (output !== undefined) {\n item.output = output;\n }\n if (status !== undefined) {\n item.status = status;\n }\n this.updateItem(item);\n }\n\n private applyToolEnd(\n toolCallId: string,\n status: 'ok' | 'error',\n output: string | undefined,\n durationMs: number | undefined,\n ): void {\n const item = this.toolItem(toolCallId);\n if (!item) {\n return;\n }\n item.status = status;\n if (output !== undefined) {\n item.output = output;\n }\n // pi's `tool_execution_end` carries no duration, and a finished turn can\n // only show \"Worked for Ns\" if one is recorded. Measure it from the start\n // we stamped ourselves; a host that does report one still wins.\n item.durationMs = durationMs ?? Math.max(0, this.now() - item.at);\n this.updateItem(item);\n this.toolIndexes.delete(toolCallId);\n }\n\n private ensureAssistant(): AssistantTranscriptItem {\n const existing = this.assistantIndex >= 0 ? this.items[this.assistantIndex] : undefined;\n if (existing && existing.kind === 'assistant') {\n return existing;\n }\n const item: AssistantTranscriptItem = {\n kind: 'assistant',\n id: this.nextId('assistant'),\n at: this.now(),\n text: '',\n thinking: '',\n streaming: true,\n };\n this.assistantIndex = this.items.length;\n this.appendItem(item);\n return item;\n }\n\n private toolItem(toolCallId: string): ToolTranscriptItem | undefined {\n const index = this.toolIndexes.get(toolCallId);\n if (index === undefined) {\n return undefined;\n }\n const item = this.items[index];\n return item && item.kind === 'tool' ? item : undefined;\n }\n\n private appendItem(item: TranscriptItem): void {\n this.items.push(item);\n this.options.emit({ type: 'transcript/append', payload: { ...item } });\n }\n\n private updateItem(item: TranscriptItem): void {\n this.options.emit({ type: 'transcript/update', payload: { ...item } });\n }\n\n private nextId(kind: string): string {\n this.counter += 1;\n return `${this.idPrefix}-${kind}-${this.counter}`;\n }\n}\n", "import type { AgentEvent, NoticeLevel } from '@morse/core';\nimport type { ChatPin, HostToClientMessage, PromptImage, TranscriptItem } from '@morse/protocol';\nimport { TranscriptProjector } from './transcript-projector.js';\n\nexport interface TranscriptUpdate {\n sessionKey: string;\n message: HostToClientMessage;\n /**\n * The transcript after this update, copied only when it is read. The controller\n * forwards `message` and never looks at the items, so copying every item on\n * every delta was garbage for nothing.\n */\n readonly items: TranscriptItem[];\n}\n\n/** How far back a session's transcript has been paged. */\nexport interface HistoryCursor {\n before?: string;\n hasOlder: boolean;\n}\n\n/**\n * Transcript per session, shared by every connection of a host.\n *\n * It sits next to the session registry because it has the same lifetime: the\n * agent process outlives a client, so its conversation must too. A controller\n * feeds agent events in and forwards the updates of whichever session it is\n * displaying \u2014 which is what makes a refresh (or a second window) keep the\n * transcript instead of starting from an empty panel.\n */\nexport class SessionTranscriptStore {\n private readonly projectors = new Map<string, TranscriptProjector>();\n private readonly cursors = new Map<string, HistoryCursor>();\n private readonly listeners = new Set<(update: TranscriptUpdate) => void>();\n /**\n * Agent events already projected, by instance (see `apply`). A weak set, so\n * a projected record never keeps itself alive after it has been fanned out.\n */\n private readonly applied = new WeakSet<AgentEvent>();\n\n subscribe(listener: (update: TranscriptUpdate) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n items(sessionKey: string): TranscriptItem[] {\n return this.projectors.get(sessionKey)?.snapshot() ?? [];\n }\n\n /**\n * Paging state lives with the transcript, not the connection: a refresh\n * reattaches to the same warm session and must keep offering older pages.\n */\n historyCursor(sessionKey: string): HistoryCursor {\n return this.cursors.get(sessionKey) ?? { hasOlder: false };\n }\n\n setHistoryCursor(sessionKey: string, cursor: HistoryCursor): void {\n this.cursors.set(sessionKey, cursor);\n }\n\n /**\n * Feeds one agent event in. Idempotent per event *instance*: the registry\n * fans a single tagged event out to every subscribed controller, and each of\n * them writes it here, so a host with more than one connected client would\n * otherwise project the same stream once per connection. The symptoms are\n * unmistakable \u2014 a delta doubled into \"therethere\", a second `message_end`\n * creating a duplicate prose row, and a second `tool-start` re-binding the\n * call id so the first tool item never receives its `tool-end` and stays\n * `running` forever (\"Working for \u2026\" never settling).\n *\n * Identity is the right key because `SessionRegistry.emit` passes the same\n * event object to every listener: the first writer projects it, the rest are\n * no-ops, whatever the connection count.\n */\n apply(sessionKey: string, event: AgentEvent): void {\n if (this.applied.has(event)) {\n return;\n }\n this.applied.add(event);\n this.projectorFor(sessionKey).apply(event);\n }\n\n /** Replays a resumed session's history into an empty transcript. */\n seed(sessionKey: string, items: TranscriptItem[]): void {\n const projector = this.projectorFor(sessionKey);\n for (const item of items) {\n projector.append(item);\n }\n }\n\n /** Inserts an older history page in front of a session's transcript. */\n prepend(sessionKey: string, items: TranscriptItem[]): void {\n this.projectorFor(sessionKey).prepend(items);\n }\n\n /**\n * Moves a transcript to a new session key after the agent forked the session\n * before a past message. The caller passes the rows that survive the fork;\n * the branch after the edited message is dropped with a single replace, so no\n * stale rows linger under the new session.\n */\n move(fromKey: string, toKey: string, items: TranscriptItem[]): void {\n if (fromKey === toKey) {\n this.projectorFor(toKey).replace(items);\n return;\n }\n this.projectors.delete(fromKey);\n this.cursors.delete(fromKey);\n this.cursors.set(toKey, { hasOlder: false });\n this.projectorFor(toKey).replace(items);\n }\n\n userPrompt(sessionKey: string, text: string, images?: PromptImage[], pins?: ChatPin[]): void {\n this.projectorFor(sessionKey).userPrompt(text, images, pins);\n }\n\n notice(sessionKey: string, level: NoticeLevel, text: string): void {\n this.projectorFor(sessionKey).notice(level, text);\n }\n\n error(sessionKey: string, message: string, detail?: string): void {\n this.projectorFor(sessionKey).error(message, detail);\n }\n\n /** Drops a transcript (used when a session is closed or restarted). */\n reset(sessionKey: string): void {\n // A replayed transcript may be fed the same records again on a cold resume;\n // those are fresh instances, so the identity guard needs no clearing here.\n this.cursors.delete(sessionKey);\n const projector = this.projectors.get(sessionKey);\n if (!projector) {\n return;\n }\n projector.reset();\n }\n\n clear(sessionKey: string): void {\n this.cursors.delete(sessionKey);\n this.projectors.delete(sessionKey);\n }\n\n dispose(): void {\n this.projectors.clear();\n this.cursors.clear();\n this.listeners.clear();\n }\n\n private projectorFor(sessionKey: string): TranscriptProjector {\n let projector = this.projectors.get(sessionKey);\n if (!projector) {\n projector = new TranscriptProjector({\n emit: (message) => {\n this.emit({\n sessionKey,\n message,\n get items(): TranscriptItem[] {\n return projector?.snapshot() ?? [];\n },\n });\n },\n });\n this.projectors.set(sessionKey, projector);\n }\n return projector;\n }\n\n private emit(update: TranscriptUpdate): void {\n for (const listener of [...this.listeners]) {\n listener(update);\n }\n }\n}\n", "import type {\n AgentCommand,\n AgentInteractionRequest,\n AgentSessionState,\n ModelRef,\n ThinkingLevel,\n TokenUsage,\n WorkspaceRef,\n} from '@morse/core';\nimport type {\n AgentFailure,\n CommandOption,\n InteractionRequest,\n ModelOption,\n NoticeLevel,\n SessionViewState,\n} from '@morse/protocol';\n\nexport interface SessionStateMeta {\n agentReady: boolean;\n agentStarting: boolean;\n agentError?: string;\n /** What `agentError` means and what to do about it (see `AgentFailure`). */\n agentFailure?: AgentFailure;\n busy: boolean;\n /** True when older transcript entries can still be loaded from the session. */\n hasOlderHistory?: boolean;\n /** True while the host is fetching the previous history page. */\n loadingOlderHistory?: boolean;\n}\n\nexport function emptySessionViewState(\n workspace: WorkspaceRef,\n meta: SessionStateMeta,\n): SessionViewState {\n return {\n workspace: { cwd: workspace.cwd, name: workspace.name },\n thinkingLevel: 'off',\n availableModels: [],\n availableThinkingLevels: [],\n availableCommands: [],\n streaming: false,\n busy: meta.busy,\n agentReady: meta.agentReady,\n agentStarting: meta.agentStarting,\n agentError: meta.agentError,\n agentFailure: meta.agentFailure,\n hasOlderHistory: meta.hasOlderHistory,\n loadingOlderHistory: meta.loadingOlderHistory,\n };\n}\n\n/**\n * The empty panel as a *draft*: no session exists yet, but the state still\n * carries what the backend offers without one \u2014 model catalog, thinking\n * levels, commands (from a never-recording probe, see `SessionRegistry\n * .draftDefaults`) \u2014 plus whatever the user has already picked. Those pending\n * picks are applied to the session the first prompt opens.\n */\nexport function draftSessionViewState(\n workspace: WorkspaceRef,\n meta: SessionStateMeta,\n catalog: AgentSessionState | undefined,\n pendingModel: ModelRef | undefined,\n pendingThinking: ThinkingLevel | undefined,\n): SessionViewState {\n const empty = emptySessionViewState(workspace, meta);\n if (!catalog) {\n return empty;\n }\n return {\n ...empty,\n model: toModelOption(pendingModel ?? catalog.model),\n thinkingLevel: pendingThinking ?? catalog.thinkingLevel ?? 'off',\n availableModels: catalog.availableModels.map(toModelOption).filter(isModelOption),\n availableThinkingLevels: catalog.availableThinkingLevels.map(toThinkingLevel),\n availableCommands: catalog.availableCommands.map(toCommandOption),\n };\n}\n\nexport function toSessionViewState(\n state: AgentSessionState,\n meta: SessionStateMeta,\n): SessionViewState {\n return {\n sessionId: state.sessionId,\n sessionTitle: state.sessionTitle,\n workspace: { cwd: state.workspace.cwd, name: state.workspace.name },\n model: toModelOption(state.model),\n thinkingLevel: state.thinkingLevel,\n availableModels: state.availableModels.map(toModelOption).filter(isModelOption),\n availableThinkingLevels: state.availableThinkingLevels.map(toThinkingLevel),\n availableCommands: state.availableCommands.map(toCommandOption),\n streaming: state.streaming,\n busy: meta.busy,\n usage: toUsage(state.usage),\n lastUsage: toUsage(state.lastUsage),\n costUsd: state.costUsd,\n contextUsage: state.contextUsage ? { ...state.contextUsage } : undefined,\n counts: state.counts ? { ...state.counts } : undefined,\n agentReady: meta.agentReady,\n agentStarting: meta.agentStarting,\n agentError: meta.agentError,\n agentFailure: meta.agentFailure,\n hasOlderHistory: meta.hasOlderHistory,\n loadingOlderHistory: meta.loadingOlderHistory,\n };\n}\n\nfunction toModelOption(model: ModelRef | undefined): ModelOption | undefined {\n if (!model) {\n return undefined;\n }\n return {\n provider: model.provider,\n id: model.id,\n name: model.name,\n contextWindow: model.contextWindow,\n maxTokens: model.maxTokens,\n };\n}\n\nfunction isModelOption(value: ModelOption | undefined): value is ModelOption {\n return value !== undefined;\n}\n\nfunction toCommandOption(command: AgentCommand): CommandOption {\n return {\n name: command.name,\n description: command.description,\n source: command.source,\n template: command.template,\n };\n}\n\nfunction toThinkingLevel(level: ThinkingLevel): ThinkingLevel {\n return level;\n}\n\nfunction toUsage(usage: TokenUsage | undefined): TokenUsage | undefined {\n return usage ? { ...usage } : undefined;\n}\n/** The agent's interaction requests already match the wire shape 1:1. */\nexport function toInteractionRequest(request: AgentInteractionRequest): InteractionRequest {\n switch (request.kind) {\n case 'select':\n return {\n requestId: request.requestId,\n kind: 'select',\n title: request.title,\n message: request.message,\n options: request.options.map((option) => ({ ...option })),\n };\n case 'confirm':\n return {\n requestId: request.requestId,\n kind: 'confirm',\n title: request.title,\n message: request.message,\n danger: request.danger,\n };\n case 'input':\n return {\n requestId: request.requestId,\n kind: 'input',\n title: request.title,\n placeholder: request.placeholder,\n value: request.value,\n };\n case 'editor':\n return {\n requestId: request.requestId,\n kind: 'editor',\n title: request.title,\n value: request.value,\n language: request.language,\n };\n }\n}\n\nexport function noticeMessage(\n level: NoticeLevel,\n text: string,\n at: number,\n): { type: 'notice'; payload: { level: NoticeLevel; text: string; at: number } } {\n return { type: 'notice', payload: { level, text, at } };\n}\n", "import { AgentUnavailableError, MorseError } from '@morse/core';\nimport type {\n AgentEvent,\n AgentForkMessage,\n AgentHistoryEntry,\n AgentInteractionRequest,\n AgentInteractionResponse,\n AgentSessionState,\n ChatService,\n ModelRef,\n MorseLogger,\n OpenedSession,\n PromptImage,\n PromptMode,\n SessionRegistry,\n SessionSummary,\n ThinkingLevel,\n WorkspaceRef,\n} from '@morse/core';\nimport {\n PROTOCOL_VERSION,\n type AgentErrorCode,\n type AgentFailure,\n type ChatPin,\n type ClientToHostMessage,\n type FrontendIdentity,\n type HostCapabilities,\n type HostToClientMessage,\n type InteractionResponse,\n type SessionActivity,\n type SessionViewState,\n type TranscriptItem,\n type UserTranscriptItem,\n} from '@morse/protocol';\nimport type { HostCommandHandler, NativeDialogs } from './native-dialogs.js';\nimport type { TerminalBackend, TerminalSession } from './terminal.js';\nimport { SessionTranscriptStore } from './transcript-store.js';\nimport {\n draftSessionViewState,\n noticeMessage,\n toInteractionRequest,\n toSessionViewState,\n} from './view-state.js';\n\nexport interface HostSessionServices {\n registry: SessionRegistry;\n chat: ChatService;\n}\n\n/**\n * Driven port (R2) declared by this glue: which directories the host lets the\n * agent work in. The server enforces an allow-list; a local VS Code window\n * allows anything the user can already open.\n */\nexport interface ProjectPolicy {\n canOpen(path: string): boolean;\n}\n\n/** The directories a host is allowed to show and work in. */\nexport type HostScopeOptions =\n | { kind: 'global' }\n | { kind: 'workspace'; roots: string[] };\n\nexport interface HostSessionControllerOptions {\n services: HostSessionServices;\n capabilities: HostCapabilities;\n emit: (message: HostToClientMessage) => void;\n logger: MorseLogger;\n /**\n * Which sessions/projects this host may show and open. `workspace` restricts\n * everything to the given roots (VS Code); `global` shows them all.\n */\n scope?: HostScopeOptions;\n /** Present when the host can render interactions natively; omitted otherwise. */\n dialogs?: NativeDialogs;\n /** Transcript storage shared by every client of this host. */\n transcripts: SessionTranscriptStore;\n frontend?: FrontendIdentity;\n onHostCommand?: HostCommandHandler;\n autoOpen?: boolean;\n /**\n * Runs interactive shells for the bottom panel's terminal. Omitted by a host\n * that leaves `capabilities.terminal` off (VS Code keeps its own terminal).\n */\n terminal?: TerminalBackend;\n /**\n * Dispose the shared session registry together with this controller.\n * Defaults to false: registries outlive a client so a refresh reattaches.\n */\n ownsRegistry?: boolean;\n /** Shown to the user when the agent backend cannot be started. */\n agentHint?: string;\n policy?: ProjectPolicy;\n}\n\ntype Guarded<T> = { ok: true; value: T } | { ok: false };\n\n/** How many history entries one page carries when a resumed session is seeded. */\nconst HISTORY_PAGE_SIZE = 50;\n\n/** The title a session without a name shows until the user says something. */\nconst UNTITLED_SESSION = 'New session';\n\n/**\n * The application service both hosts share: it owns the wire conversation for\n * one client, routes client messages into the core use cases, and projects agent\n * events back onto the wire.\n *\n * Multiple sessions and projects run at once: every session gets its own\n * transcript projector, and only the active one is streamed to the client (the\n * others stay warm and are replayed in full when the client switches to them).\n */\nexport class HostSessionController {\n private summaries = new Map<string, SessionSummary>();\n private detachRegistry: (() => void) | undefined;\n private detachTranscripts: (() => void) | undefined;\n private activeKey: string | undefined;\n /** True while the panel shows the intentional no-session draft state. */\n private isDraft = false;\n /** Project a draft opens its first session in (browser \"New session here\"). */\n private draftWorkspace: WorkspaceRef | undefined;\n /** Catalog of a never-recording probe \u2014 fills the draft pickers. */\n private draftCatalog: AgentSessionState | undefined;\n /** Picks made in the draft, applied to the session the first prompt opens. */\n private draftModel: ModelRef | undefined;\n private draftThinking: ThinkingLevel | undefined;\n private agentReady = false;\n private agentStarting = false;\n private agentError: string | undefined;\n /** The machine-readable half of `agentError` (see `AgentFailure`). */\n private agentFailure: AgentFailure | undefined;\n private busy = false;\n private disposed = false;\n private hasOpenedOnce = false;\n private hostReadyEmitted = false;\n private readonly pendingInteractions = new Set<string>();\n /** Cursor of the oldest history page loaded for the active session. */\n private historyBefore: string | undefined;\n private hasOlderHistory = false;\n private loadingOlderHistory = false;\n /** Discriminates history item ids across pages (see `historyItems`). */\n private historyPageSeq = 0;\n /** This client's live attachments to shells, keyed by the terminal id it named. */\n private readonly terminals = new Map<string, TerminalSession>();\n /**\n * The ids this connection has already opened. It separates a *first* open \u2014 a\n * page reattaching to a shell the host still runs \u2014 from a repeat open (the\n * Restart button), which must replace the shell and clear its scrollback\n * instead of replaying it into the emulator that already shows it.\n */\n private readonly openedTerminals = new Set<string>();\n /**\n * Bumped whenever a terminal id is opened or closed. A shell's late `exit`\n * (a replaced one, a killed one) compares its generation and stays silent, so\n * it cannot delete or report over the shell now holding the id.\n */\n private readonly terminalGenerations = new Map<string, number>();\n\n constructor(private readonly options: HostSessionControllerOptions) {}\n\n async start(): Promise<void> {\n this.detachRegistry = this.options.services.registry.subscribe((tagged) => {\n void this.onTaggedEvent(tagged.sessionKey, tagged.event);\n });\n // The transcript lives with the session, not with this connection: forward\n // the updates of whichever session this client is showing.\n this.detachTranscripts = this.options.transcripts.subscribe((update) => {\n if (update.sessionKey === this.activeKey) {\n this.options.emit(update.message);\n }\n });\n if (this.options.autoOpen !== false) {\n // Answer the handshake immediately with a truthful state: the agent is\n // starting. Otherwise the client renders its defaults for the whole spawn.\n this.agentStarting = true;\n }\n this.emitReady();\n if (this.options.autoOpen !== false) {\n await this.openDefaultSession();\n } else {\n // A reconnecting client (webview reload, browser refresh) must reattach\n // the session the host was already showing: the warm transcript replays\n // and the Reload button keeps the conversation instead of landing on an\n // empty panel. Activating a hot session spawns nothing; only a host with\n // no active session stays on the empty draft, which is what keeps a fresh\n // window from spawning a throwaway \"New session\" \u2014 the panel stays empty\n // until the user actually says something.\n const existingKey = this.options.services.registry.activeKeyOf();\n if (existingKey === undefined) {\n this.isDraft = true;\n void this.warmDraft();\n } else {\n await this.runSessionChange(() => this.options.services.registry.activate(existingKey));\n }\n }\n void this.publishLists();\n }\n\n /**\n * Points this controller at a new client. Hosts that reattach a still-warm\n * session to a reconnecting client use this so the same agent process keeps\n * serving without being respawned.\n */\n setEmitter(emit: (message: HostToClientMessage) => void): void {\n this.options.emit = emit;\n }\n\n async dispose(): Promise<void> {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n // Detach, do not kill: the shell and its scrollback belong to the host now,\n // so a reloaded page (a browser refresh) can attach to the same process. An\n // explicit `terminal/close` is the only thing that ends a shell.\n for (const session of this.terminals.values()) {\n session.detach();\n }\n this.terminals.clear();\n this.terminalGenerations.clear();\n // The next connection is a new viewer, so every id is a first open again.\n this.openedTerminals.clear();\n this.detachRegistry?.();\n this.detachRegistry = undefined;\n this.detachTranscripts?.();\n this.detachTranscripts = undefined;\n if (this.options.ownsRegistry === true) {\n await this.options.services.registry.dispose();\n }\n }\n\n async handleClientMessage(message: ClientToHostMessage): Promise<void> {\n if (this.disposed) {\n return;\n }\n switch (message.type) {\n case 'client/ready':\n if (message.payload.protocolVersion !== PROTOCOL_VERSION) {\n // A refused handshake is a banner, not a conversation entry: only the\n // transient error is sent, so no transcript is polluted.\n this.emitWireError(\n 'Frontend and host speak different protocol versions.',\n `frontend=${message.payload.protocolVersion} host=${PROTOCOL_VERSION}`,\n );\n return;\n }\n if (message.payload.frontend) {\n this.options.logger.info(\n `Frontend ${message.payload.frontend.name}@${message.payload.frontend.version} connected`,\n );\n }\n // While the first session is still starting, `start()` owns the single\n // `host/ready` we send. Answering here would flash \"agent unavailable\".\n if (!this.hostReadyEmitted) {\n return;\n }\n this.emitReady();\n void this.publishLists();\n return;\n case 'chat/prompt':\n await this.prompt(\n message.payload.text,\n message.payload.mode,\n message.payload.images,\n message.payload.pins,\n );\n return;\n case 'chat/edit':\n await this.editPrompt(message.payload.itemId, message.payload.text);\n return;\n case 'chat/fork':\n await this.forkPrompt(message.payload.itemId);\n return;\n case 'chat/abort':\n await this.guard(() => this.options.services.chat.abort());\n return;\n case 'session/new':\n // \"New session\" is an empty draft, not a spawn: no agent process, no\n // session file, no navigator entry. The first prompt opens the actual\n // session (see `prompt`). The optional cwd targets it \u2014 the browser\n // nav's \"New session here\" picks a project this way.\n await this.enterDraft(message.payload.cwd ? workspaceOf(message.payload.cwd) : undefined);\n return;\n case 'session/activate':\n case 'session/load':\n await this.activateSession(message.payload.sessionId, message.payload.cwd);\n return;\n case 'session/close':\n await this.closeSession(message.payload.sessionId);\n return;\n case 'session/delete':\n await this.deleteSession(message.payload.sessionId);\n return;\n case 'session/compact':\n // Compaction is part of the process, not a side note: like a run, the\n // same `busy` flag lights the composer's Working indicator (spinner,\n // elapsed chip, Stop button) and pulses the session in the navigator\n // while the agent summarizes the context. Reset in `finally` \u2014 a failed\n // compaction must hand the composer back, not wedge it on \"Working\".\n this.setBusy(true);\n try {\n await this.guard(() => this.options.services.chat.compact(message.payload.instructions));\n } finally {\n this.setBusy(false);\n }\n return;\n case 'history/load':\n await this.loadOlderHistory();\n return;\n case 'session/list':\n await this.publishSessions();\n return;\n case 'commands/refresh':\n await this.refreshCommands();\n return;\n case 'project/list':\n await this.publishProjects();\n return;\n case 'project/open':\n // Target the draft at that project (browser navigation): still no\n // spawn \u2014 the first prompt opens the session there.\n await this.enterDraft(workspaceOf(message.payload.path));\n return;\n case 'model/set':\n await this.setModel(message.payload.provider, message.payload.id);\n return;\n case 'thinking/set':\n await this.setThinkingLevel(message.payload.level);\n return;\n case 'interaction/respond':\n await this.respondToInteraction(message.payload);\n return;\n case 'host/command':\n await this.runHostCommand(\n message.payload.command,\n message.payload.args,\n message.payload.requestId,\n );\n return;\n case 'terminal/open':\n await this.openTerminal(message.payload);\n return;\n case 'terminal/input':\n this.terminals.get(message.payload.terminalId)?.write(message.payload.data);\n return;\n case 'terminal/resize':\n this.terminals\n .get(message.payload.terminalId)\n ?.resize(message.payload.cols, message.payload.rows);\n return;\n case 'terminal/close':\n this.closeTerminal(message.payload.terminalId);\n return;\n default:\n return;\n }\n }\n\n /**\n * Attaches this client to a shell for the bottom panel. The directory is the\n * viewing session's (or the draft's), never an arbitrary path the client\n * names \u2014 the backend still re-checks it against the host policy before\n * spawning.\n *\n * A *first* open on this connection may land on a shell that is already running\n * (a reloaded page reattaching); a repeat open (the Restart button) replaces\n * it, scrollback and all, so the fresh shell does not replay the old one.\n */\n private async openTerminal(payload: {\n terminalId: string;\n cwd?: string;\n cols?: number;\n rows?: number;\n }): Promise<void> {\n const backend = this.options.terminal;\n if (backend === undefined) {\n this.emitTerminalExit(payload.terminalId, undefined, 'This host has no terminal.');\n return;\n }\n // A repeat open on this connection replaces the shell rather than leaking a\n // second one behind the same name. A first open must not: the shell may be\n // one this connection is reattaching to, and that one keeps running.\n const previous = this.terminals.get(payload.terminalId);\n if (previous !== undefined) {\n previous.detach();\n this.terminals.delete(payload.terminalId);\n backend.close(payload.terminalId);\n } else if (this.openedTerminals.has(payload.terminalId)) {\n // A restart after the shell exited: drop the dead shell's scrollback, so\n // the fresh shell does not replay it over the emulator that still has it.\n backend.close(payload.terminalId);\n }\n this.openedTerminals.add(payload.terminalId);\n // Any pending exit for a superseded shell is now stale.\n const generation = (this.terminalGenerations.get(payload.terminalId) ?? 0) + 1;\n this.terminalGenerations.set(payload.terminalId, generation);\n\n const cwd = payload.cwd ?? this.currentState().workspace.cwd;\n const cols = clampGeometry(payload.cols, 80);\n const rows = clampGeometry(payload.rows, 24);\n let session: TerminalSession;\n try {\n session = await backend.attach(\n payload.terminalId,\n { cwd, cols, rows },\n {\n output: (data) => {\n if (this.terminalGenerations.get(payload.terminalId) === generation) {\n this.emitTerminalOutput(payload.terminalId, data);\n }\n },\n exit: (code, error) => {\n // A shell replaced or closed after this one opened is not the shell\n // the client is looking at; its end is not news.\n if (this.terminalGenerations.get(payload.terminalId) !== generation) {\n return;\n }\n this.terminals.delete(payload.terminalId);\n this.emitTerminalExit(payload.terminalId, code, error);\n },\n },\n );\n } catch (error: unknown) {\n this.options.logger.warn('Failed to open a terminal', error);\n this.emitTerminalExit(payload.terminalId, undefined, describeTerminalError(error));\n return;\n }\n // The client may have closed it while `attach` was still awaiting the shell.\n if (!this.disposed && this.terminalGenerations.get(payload.terminalId) === generation) {\n this.terminals.set(payload.terminalId, session);\n // Catch the viewer up before the shell's next chunk: what it missed while\n // no page was attached is the whole point of a surviving terminal.\n if (session.replay.length > 0) {\n this.emitTerminalOutput(payload.terminalId, session.replay);\n }\n } else {\n // Nobody is watching any more: leave the shell running for the next attach.\n session.detach();\n }\n }\n\n /**\n * Ends a shell for good: the reader closed the pane. Bumps the generation first,\n * so a late `exit` for the shell being replaced is not reported as news.\n */\n private closeTerminal(terminalId: string): void {\n this.terminalGenerations.set(terminalId, (this.terminalGenerations.get(terminalId) ?? 0) + 1);\n const session = this.terminals.get(terminalId);\n this.terminals.delete(terminalId);\n this.openedTerminals.delete(terminalId);\n session?.detach();\n this.options.terminal?.close(terminalId);\n }\n\n private emitTerminalOutput(terminalId: string, data: string): void {\n if (data.length > 0 && !this.disposed) {\n this.options.emit({ type: 'terminal/output', payload: { terminalId, data } });\n }\n }\n\n private emitTerminalExit(terminalId: string, code?: number, error?: string): void {\n if (!this.disposed) {\n this.options.emit({ type: 'terminal/exit', payload: { terminalId, code, error } });\n }\n }\n\n private async openDefaultSession(): Promise<void> {\n const existingKey = this.options.services.registry.activeKeyOf();\n if (existingKey) {\n await this.runSessionChange(() => this.options.services.registry.activate(existingKey));\n return;\n }\n await this.runSessionChange(() => this.options.services.registry.open());\n }\n\n /**\n * The empty panel is an intentional draft: no agent process, no session\n * file, no navigator entry \u2014 \"New session\" becomes real only once the first\n * prompt runs. A pending project and pending picks (model, thinking) wait\n * here until that first prompt opens the session they belong to.\n */\n private async enterDraft(workspace?: WorkspaceRef): Promise<void> {\n if (workspace && !this.canOpen(workspace.cwd)) {\n this.fail(\n `Morse is not allowed to run an agent in ${workspace.cwd}.`,\n 'Add it to MORSE_PROJECTS first.',\n );\n return;\n }\n this.activeKey = undefined;\n this.isDraft = true;\n this.draftWorkspace = workspace;\n this.resetViewState();\n this.options.emit({ type: 'transcript/replace', payload: { items: [] } });\n this.emitState();\n await this.warmDraft();\n }\n\n /** Clears everything the previous session had left on the view state. */\n private resetViewState(): void {\n this.historyBefore = undefined;\n this.hasOlderHistory = false;\n this.loadingOlderHistory = false;\n this.agentReady = false;\n this.agentStarting = false;\n this.clearAgentFailure();\n }\n\n /**\n * Forgets the agent failure, message and all. Called wherever the agent comes\n * back (a ready event, an adopted session), so a stale screen never outlives\n * the problem it described.\n */\n private clearAgentFailure(): void {\n this.agentError = undefined;\n this.agentFailure = undefined;\n }\n\n /** Fills the draft pickers from the registry's session-less probe. */\n private async warmDraft(): Promise<void> {\n try {\n this.draftCatalog = await this.options.services.registry.draftDefaults();\n } catch (error: unknown) {\n // The session-less probe is where a missing pi shows up on load. Without\n // this the draft looked healthy (empty pickers) until the first prompt\n // failed to spawn, and a reader with a restored ~/.pi \u2014 sessions in the\n // sidebar, no binary on PATH \u2014 had no way to know the agent was gone.\n this.agentError = describeError(error);\n this.agentFailure = describeAgentFailure(error, this.options.agentHint);\n this.options.logger.warn('Could not probe the draft model catalog', error);\n this.emitState();\n return;\n }\n // The catalog lands a moment after the handshake; this refresh is what\n // turns the disabled \"no model\" pickers on while the reader is looking.\n if (this.activeKey === undefined && !this.disposed) {\n this.emitState();\n }\n }\n\n private async activateSession(sessionId: string, cwd?: string): Promise<void> {\n const known = this.summaries.get(sessionId);\n const path = cwd ?? known?.cwd;\n if (path && !this.canOpen(path)) {\n this.fail(`Morse is not allowed to run an agent in ${path}.`, 'Add it to MORSE_PROJECTS first.');\n return;\n }\n // Activating a session ends any draft, and its pending picks belonged to\n // a session that was never created \u2014 drop them here, not on the resumed\n // conversation.\n this.isDraft = false;\n this.draftWorkspace = undefined;\n this.draftModel = undefined;\n this.draftThinking = undefined;\n await this.runSessionChange(() =>\n this.options.services.registry.activate(\n sessionId,\n path ? workspaceOf(path) : undefined,\n ),\n );\n }\n\n private async closeSession(sessionId: string): Promise<void> {\n await this.guard(() => this.options.services.registry.close(sessionId));\n this.options.transcripts.clear(sessionId);\n await this.fallBackAfterSessionGone(sessionId);\n }\n\n /**\n * Deletes a session for good: the registry disposes its process and the\n * catalog drops the stored conversation. Which directories an agent may\n * touch is the host's rule (`ProjectPolicy`), so one use case is safe behind\n * a server allow-list and inside a VS Code window \u2014 the wiring differs, the\n * decision does not.\n */\n private async deleteSession(sessionId: string): Promise<void> {\n const path = this.summaries.get(sessionId)?.cwd;\n if (path !== undefined && path.length > 0 && !this.canOpen(path)) {\n this.fail(\n `Morse is not allowed to delete sessions in ${path}.`,\n 'Add it to MORSE_PROJECTS first.',\n );\n return;\n }\n const result = await this.guard(() => this.options.services.registry.remove(sessionId));\n if (!result.ok) {\n return;\n }\n this.options.transcripts.clear(sessionId);\n await this.fallBackAfterSessionGone(sessionId);\n this.options.emit(noticeMessage('info', 'Session deleted.', Date.now()));\n }\n\n /** The panel must stay usable once the session it showed is gone. */\n private async fallBackAfterSessionGone(sessionId: string): Promise<void> {\n if (this.activeKey !== sessionId) {\n this.publishActivity();\n await this.publishSessions();\n return;\n }\n\n // Keep the client usable: adopt another hot session, or return to the\n // empty panel. Spawning a replacement nobody asked for would recreate the\n // throwaway-session problem \u2014 the next prompt lazily opens one instead.\n this.activeKey = undefined;\n this.historyBefore = undefined;\n this.hasOlderHistory = false;\n this.loadingOlderHistory = false;\n this.agentReady = false;\n this.clearAgentFailure();\n const next = this.options.services.registry.hotKeys().at(-1);\n if (next) {\n await this.runSessionChange(() => this.options.services.registry.activate(next));\n } else {\n // Return to the draft the way session/new made it: no replacement\n // session nobody asked for; the next prompt opens one.\n await this.enterDraft();\n }\n await this.publishSessions();\n }\n\n /** Opens/activates a session while reporting progress and failures. */\n private async runSessionChange(\n change: () => Promise<OpenedSession>,\n ): Promise<void> {\n const announce = this.hasOpenedOnce;\n if (announce) {\n this.setBusy(true);\n }\n this.agentStarting = true;\n if (announce) {\n this.emitState();\n }\n try {\n const opened = await change();\n this.adopt(opened);\n } catch (error: unknown) {\n this.agentReady = false;\n this.agentError = describeError(error);\n this.agentFailure = describeAgentFailure(error, this.options.agentHint);\n this.options.logger.warn('Could not start the Morse agent', error);\n this.fail(\n `Could not start the Morse agent: ${this.agentError}`,\n this.options.agentHint,\n );\n } finally {\n this.agentStarting = false;\n this.hasOpenedOnce = true;\n if (announce) {\n this.setBusy(false);\n } else {\n this.emitState();\n }\n }\n }\n\n private adopt(opened: OpenedSession): void {\n this.activeKey = opened.key;\n // A real session is showing from now on; the draft target is consumed by\n // the open that just happened.\n this.isDraft = false;\n this.draftWorkspace = undefined;\n if (!opened.reused) {\n // A brand new process for this key: drop any stale transcript.\n this.options.transcripts.reset(opened.key);\n }\n // Paging state belongs to the session being adopted, never to the previous one.\n this.historyBefore = undefined;\n this.hasOlderHistory = false;\n this.loadingOlderHistory = false;\n this.agentReady = true;\n this.clearAgentFailure();\n this.emitTranscript(opened.key);\n this.emitState();\n // A new/activated session must show up in the list immediately; otherwise\n // the user picks \"New session\" and it is nowhere to be seen.\n void this.publishSessions();\n void this.seedHistory(opened.key);\n }\n\n /**\n * A resumed session has its messages on disk but streams nothing on attach, so\n * the transcript would look empty. Fill it from the *newest* page of history:\n * a session with thousands of messages must open without rendering them all,\n * and older pages are fetched on demand (see `loadOlderHistory`).\n */\n private async seedHistory(key: string): Promise<void> {\n if (this.options.transcripts.items(key).length > 0) {\n // A reattach (refresh, second window) already has the transcript; restore\n // how far back it was paged instead of forgetting older pages exist.\n const cursor = this.options.transcripts.historyCursor(key);\n this.historyBefore = cursor.before;\n this.hasOlderHistory = cursor.hasOlder;\n this.emitState();\n return;\n }\n if (this.activeKey !== key) {\n return;\n }\n const gateway = this.options.services.registry.active();\n if (!gateway) {\n return;\n }\n // A resumed session is seeded from history, so until the page lands we do\n // not yet know that it reaches the beginning: show \"Loading older\u2026\" rather\n // than claiming \"Start of conversation\".\n this.loadingOlderHistory = true;\n this.emitState();\n const result = await this.guard(() => gateway.history({ limit: HISTORY_PAGE_SIZE }));\n this.loadingOlderHistory = false;\n if (!result.ok || this.options.transcripts.items(key).length > 0 || this.activeKey !== key) {\n this.emitState();\n return;\n }\n this.historyBefore = result.value.before;\n this.hasOlderHistory = result.value.hasOlder;\n this.options.transcripts.setHistoryCursor(key, {\n before: result.value.before,\n hasOlder: result.value.hasOlder,\n });\n this.options.transcripts.seed(\n key,\n historyItems(key, result.value.entries, this.historyPageSeq++),\n );\n this.emitState();\n }\n\n /** Prepends the previous page of history when the reader reaches the top. */\n private async loadOlderHistory(): Promise<void> {\n if (!this.hasOlderHistory || this.loadingOlderHistory) {\n return;\n }\n const key = this.activeKey;\n const gateway = this.options.services.registry.active();\n if (key === undefined || !gateway) {\n return;\n }\n this.loadingOlderHistory = true;\n this.emitState();\n const result = await this.guard(() =>\n gateway.history({ limit: HISTORY_PAGE_SIZE, before: this.historyBefore }),\n );\n this.loadingOlderHistory = false;\n if (!result.ok || this.activeKey !== key) {\n this.emitState();\n return;\n }\n this.historyBefore = result.value.before;\n this.hasOlderHistory = result.value.hasOlder;\n this.options.transcripts.setHistoryCursor(key, {\n before: result.value.before,\n hasOlder: result.value.hasOlder,\n });\n this.options.transcripts.prepend(\n key,\n historyItems(key, result.value.entries, this.historyPageSeq++),\n );\n this.emitState();\n }\n\n private canOpen(path: string): boolean {\n const scope = this.options.scope ?? { kind: 'global' };\n if (scope.kind === 'workspace' && !insideRoots(path, scope.roots)) {\n return false;\n }\n return this.options.policy?.canOpen(path) ?? true;\n }\n\n private withinScope(path: string): boolean {\n const scope = this.options.scope ?? { kind: 'global' };\n return scope.kind === 'global' || insideRoots(path, scope.roots);\n }\n\n private async prompt(\n text: string,\n mode: PromptMode | undefined,\n images: PromptImage[] | undefined,\n pins: ChatPin[] | undefined,\n ): Promise<void> {\n if (!this.agentReady) {\n if (this.isDraft) {\n // The draft becomes the session it was waiting for: a fresh, recorded\n // one in the pending workspace \u2014 never a re-activation of whatever\n // session another connection happens to have active.\n await this.runSessionChange(() =>\n this.options.services.registry.open({ workspace: this.draftWorkspace }),\n );\n this.draftWorkspace = undefined;\n } else {\n await this.openDefaultSession();\n }\n if (!this.agentReady) {\n return;\n }\n }\n this.isDraft = false;\n // Picks made in the draft become the fresh session's settings, applied\n // before the first prompt so the answer already answers to them.\n const pendingModel = this.draftModel;\n const pendingThinking = this.draftThinking;\n this.draftModel = undefined;\n this.draftThinking = undefined;\n if (pendingModel) {\n await this.guard(() =>\n this.options.services.chat.setModel({\n provider: pendingModel.provider,\n id: pendingModel.id,\n name: pendingModel.name,\n }),\n );\n }\n if (pendingThinking) {\n await this.guard(() => this.options.services.chat.setThinkingLevel(pendingThinking));\n }\n const result = await this.guard(() =>\n this.options.services.chat.prompt(text, mode ?? 'new', images, pins),\n );\n if (!result.ok || !result.value.accepted) {\n return;\n }\n const key = this.activeKey;\n if (key) {\n // The transcript shows the message with its attachments: the pins and\n // images stay chips on the user item, whatever a later reload replays.\n this.options.transcripts.userPrompt(key, text, images, pins);\n // A nameless session takes its title from the first message (pi's name, or\n // the text the catalog derives), so refresh the navigator right away\n // instead of leaving it on \"New session\" until the next list request.\n void this.publishSessions();\n }\n }\n\n /**\n * Edits a past user message: fork the conversation before it (which discards\n * that turn and everything after), truncate the transcript to match, then send\n * the edited text as a fresh prompt. Attachments on the original message ride\n * along, so an image or pin is not silently dropped by the edit.\n */\n private async editPrompt(itemId: string, text: string): Promise<void> {\n const target = await this.forkBefore(itemId, 'edit');\n if (!target) {\n return;\n }\n await this.prompt(text, 'new', target.images, target.pins);\n }\n\n /**\n * Branches the conversation before a past user message without sending\n * anything. pi's fork writes a new session file while the old branch stays\n * resumable; the transcript re-homes onto the new, shorter branch and the\n * forked prompt goes back to the composer, so the user continues from the\n * fork point instead of retyping it.\n */\n private async forkPrompt(itemId: string): Promise<void> {\n const target = await this.forkBefore(itemId, 'fork');\n if (!target) {\n return;\n }\n // Only a committed fork seeds the composer; a cancelled one leaves it alone.\n this.options.emit({\n type: 'composer/seed',\n payload: {\n text: target.text,\n ...(target.images ? { images: target.images } : {}),\n ...(target.pins ? { pins: target.pins } : {}),\n },\n });\n // The fork is a new session (pi wrote a new file): surface it in the\n // navigator now instead of waiting for a prompt to refresh the list.\n void this.publishSessions();\n }\n\n /**\n * Forks the active session before a past user message and re-homes the\n * transcript onto the new branch. Shared by edit (which then sends) and fork\n * (which seeds the composer). Returns the forked message, or `undefined` when\n * the fork was refused \u2014 a notice has already been emitted in that case.\n */\n private async forkBefore(\n itemId: string,\n action: 'edit' | 'fork',\n ): Promise<UserTranscriptItem | undefined> {\n const gerund = action === 'edit' ? 'editing' : 'forking';\n const past = action === 'edit' ? 'edited' : 'forked';\n if (this.isDraft) {\n // No session exists in a draft, so there is nothing to fork: do not spawn\n // one just to fail the lookup (or re-activate another session).\n this.options.emit(\n noticeMessage('warn', `Nothing to ${action} yet \u2014 this session has no messages.`, Date.now()),\n );\n return undefined;\n }\n if (!this.agentReady) {\n await this.openDefaultSession();\n if (!this.agentReady) {\n return undefined;\n }\n }\n const key = this.activeKey;\n const gateway = this.options.services.registry.active();\n if (key === undefined || !gateway) {\n this.options.emit(noticeMessage('warn', `No session to ${action}.`, Date.now()));\n return undefined;\n }\n const items = this.options.transcripts.items(key);\n const target = items.find((item) => item.id === itemId);\n if (!target || target.kind !== 'user') {\n this.options.emit(\n noticeMessage('warn', `That message is no longer available to ${action}.`, Date.now()),\n );\n return undefined;\n }\n if (this.options.services.registry.stateOf(key)?.streaming === true) {\n this.options.emit(\n noticeMessage(\n 'warn',\n `Wait for the agent to finish before ${gerund} a message.`,\n Date.now(),\n ),\n );\n return undefined;\n }\n\n const messages = await this.guard(() => gateway.forkMessages());\n if (!messages.ok) {\n return undefined;\n }\n const entryId = entryIdForEdit(items, itemId, messages.value);\n if (entryId === undefined) {\n this.options.emit(\n noticeMessage(\n 'warn',\n `This message cannot be ${past} \u2014 the agent has no fork point for it.`,\n Date.now(),\n ),\n );\n return undefined;\n }\n\n const forked = await this.guard(() => this.options.services.registry.forkActive(entryId));\n if (!forked.ok) {\n return undefined;\n }\n if (forked.value.cancelled) {\n this.options.emit(noticeMessage('warn', 'The fork was cancelled.', Date.now()));\n return undefined;\n }\n\n // The forked branch ends right before the chosen message: drop that row and\n // everything after it, then re-home the transcript under the new session id.\n const index = items.findIndex((item) => item.id === itemId);\n const kept = items.slice(0, index);\n this.activeKey = forked.value.key;\n this.options.transcripts.move(key, forked.value.key, kept);\n this.historyBefore = undefined;\n this.hasOlderHistory = false;\n this.loadingOlderHistory = false;\n this.emitState();\n\n return target;\n }\n\n private async setModel(provider: string, id: string): Promise<void> {\n if (this.activeKey === undefined) {\n // Draft pick: shown as the pending model, applied to the session the\n // first prompt opens. Without a probe-cached catalog the name falls back\n // to the id the client sent.\n const match = this.draftCatalog?.availableModels.find(\n (model) => model.provider === provider && model.id === id,\n );\n this.draftModel = match ?? { provider, id, name: id };\n this.emitState();\n return;\n }\n const result = await this.guard(() =>\n this.options.services.chat.setModel({ provider, id, name: id }),\n );\n if (result.ok) {\n this.emitState();\n }\n }\n\n private async setThinkingLevel(level: SessionViewState['thinkingLevel']): Promise<void> {\n if (this.activeKey === undefined) {\n this.draftThinking = level;\n this.emitState();\n return;\n }\n const result = await this.guard(() => this.options.services.chat.setThinkingLevel(level));\n if (result.ok) {\n this.emitState();\n }\n }\n\n private async respondToInteraction(response: InteractionResponse): Promise<void> {\n this.pendingInteractions.delete(response.requestId);\n this.options.emit({ type: 'interaction/dismiss', payload: { requestId: response.requestId } });\n const gateway = this.options.services.registry.active();\n if (!gateway) {\n return;\n }\n await this.guard(() => gateway.respondToInteraction(response));\n }\n\n /**\n * The palette asked for a fresh command list. The agent re-reads the template\n * files and reports back through `agent/state`, so the composer sees a template\n * added, changed or deleted since the session started \u2014 without a pi reload.\n */\n private async refreshCommands(): Promise<void> {\n const gateway = this.options.services.registry.active();\n if (gateway?.refreshCommands === undefined) {\n return;\n }\n await this.guard(() => gateway.refreshCommands!());\n }\n\n private async runHostCommand(\n command: string,\n args: Record<string, unknown> | undefined,\n requestId?: string,\n ): Promise<void> {\n const handler = this.options.onHostCommand;\n if (!handler) {\n this.options.emit(\n noticeMessage('warn', `This host does not support \"${command}\".`, Date.now()),\n );\n this.answerHostCommand(requestId, false, undefined, `This host does not support \"${command}\".`);\n return;\n }\n // A host command answers a surface that asked for a value \u2014 the folder a user\n // is typing, a file being previewed, a working tree on a timer. Its failure\n // belongs to that caller (it receives `ok: false`) and to the log; it is not\n // conversation content. Appending it here is what filled the transcript with a\n // red \"scandir\" row for every unfinished or wrong path the folder browser was\n // asked about, and for every poll of a directory that had gone away. `guard`\n // stays for the session/agent commands, whose failure *is* the panel's news.\n let value: unknown;\n try {\n value = await handler(command, args, { cwd: this.currentState().workspace.cwd });\n } catch (error: unknown) {\n this.options.logger.error(`Morse command failed: ${describeError(error)}`, error);\n this.answerHostCommand(requestId, false, undefined, `\"${command}\" failed`);\n return;\n }\n this.answerHostCommand(requestId, true, value);\n }\n\n /** Replies to a host command the frontend asked a value for. */\n private answerHostCommand(\n requestId: string | undefined,\n ok: boolean,\n data?: unknown,\n error?: string,\n ): void {\n if (!requestId) {\n return;\n }\n this.options.emit({ type: 'host/command/result', payload: { requestId, ok, data, error } });\n }\n\n private async onTaggedEvent(sessionKey: string, event: AgentEvent): Promise<void> {\n if (sessionKey !== this.activeKey) {\n // Keep inactive sessions' transcripts warm without streaming them, and\n // still report their activity: that is what makes several sessions show\n // as working at once in the navigator.\n if (event.type !== 'agent/ready' && event.type !== 'agent/state') {\n this.options.transcripts.apply(sessionKey, event);\n if (event.type === 'agent/run-end') {\n void this.publishSessions();\n }\n } else {\n this.publishActivity();\n }\n return;\n }\n\n switch (event.type) {\n case 'agent/ready':\n this.agentReady = true;\n this.clearAgentFailure();\n this.emitState();\n return;\n case 'agent/state':\n this.emitState();\n return;\n case 'agent/run-end':\n this.options.transcripts.apply(sessionKey, event);\n // pi writes the session file at the first user message and the catalog\n // derives its title from that text, so a finished run is the moment to\n // replace the placeholder \"New session\" in the navigator.\n void this.publishSessions();\n return;\n case 'agent/interaction':\n await this.handleInteraction(event.request);\n return;\n case 'agent/fatal':\n this.agentReady = false;\n this.agentError = event.message;\n // A process that died carries no code the frontend could act on, but the\n // host still knows where to look: keep the hint, drop any stale code.\n this.agentFailure = hintOnly(this.options.agentHint);\n // Through the projector (not `transcripts.error`) so the error row is\n // guarded by the store's per-event identity: every connection reacts to\n // the same fatal event, and only the first must write it.\n this.options.transcripts.apply(sessionKey, event);\n this.emitState();\n return;\n default:\n this.options.transcripts.apply(sessionKey, event);\n }\n }\n\n private async handleInteraction(request: AgentInteractionRequest): Promise<void> {\n const dialogs = this.options.dialogs;\n if (!dialogs) {\n this.pendingInteractions.add(request.requestId);\n this.options.emit({ type: 'interaction/request', payload: toInteractionRequest(request) });\n return;\n }\n const response = await this.askNatively(dialogs, request);\n const gateway = this.options.services.registry.active();\n if (!gateway) {\n return;\n }\n await this.guard(() => gateway.respondToInteraction(response));\n }\n\n private async askNatively(\n dialogs: NativeDialogs,\n request: AgentInteractionRequest,\n ): Promise<AgentInteractionResponse> {\n switch (request.kind) {\n case 'select': {\n const value = await dialogs.select(request);\n return value === undefined\n ? { requestId: request.requestId, cancelled: true }\n : { requestId: request.requestId, value };\n }\n case 'confirm':\n return { requestId: request.requestId, confirmed: await dialogs.confirm(request) };\n case 'input': {\n const value = await dialogs.input(request);\n return value === undefined\n ? { requestId: request.requestId, cancelled: true }\n : { requestId: request.requestId, value };\n }\n case 'editor': {\n const value = await dialogs.editor(request);\n return value === undefined\n ? { requestId: request.requestId, cancelled: true }\n : { requestId: request.requestId, value };\n }\n }\n }\n\n private async publishLists(): Promise<void> {\n // `publishSessions` also refreshes the project list: both are derived from\n // the same catalog, and the navigator needs both to place a session in a\n // group.\n await this.publishSessions();\n }\n\n /**\n * Projects are derived from sessions, so the caller can hand in the list it\n * just built instead of making the registry read the catalog again. That also\n * lets a brand-new project show up before pi has persisted its first session.\n */\n private async publishProjects(sessions?: readonly SessionSummary[]): Promise<void> {\n const result = await this.guard(() => this.options.services.registry.listProjects(sessions));\n if (result.ok) {\n // A workspace-scoped host only ever shows the folders it has open.\n const projects = result.value.filter((project) => this.withinScope(project.path));\n this.options.emit({ type: 'project/list', payload: { projects } });\n }\n }\n\n private async publishSessions(): Promise<void> {\n const result = await this.guard(() => this.options.services.registry.listSessions());\n if (result.ok) {\n const sessions = result.value.filter((session) => this.withinScope(session.cwd));\n const known = new Set(sessions.map((session) => session.id));\n // A session pi has not persisted yet is invisible in the catalog, so the\n // list would be missing the very session the user just created. Fold the\n // live ones in (id = registry key) so the list always matches reality.\n for (const key of this.options.services.registry.hotKeys()) {\n if (known.has(key)) {\n continue;\n }\n const state = this.options.services.registry.stateOf(key);\n const cwd = state?.workspace.cwd ?? this.options.services.registry.defaultWorkspace.cwd;\n if (!this.withinScope(cwd)) {\n continue;\n }\n sessions.push({\n id: key,\n title: state?.sessionTitle ?? this.derivedTitle(key) ?? UNTITLED_SESSION,\n cwd,\n updatedAt: Date.now(),\n messageCount: 0,\n });\n }\n this.summaries = new Map(sessions.map((session) => [session.id, session]));\n this.options.emit({ type: 'session/list', payload: { sessions } });\n // A session change is a project change (cwd, count, last-used), and the\n // navigator builds its groups from `project/list`. Without this the group\n // for a new project \u2014 and every session inside a project missing from the\n // last list \u2014 stayed invisible until a manual refresh.\n await this.publishProjects(sessions);\n }\n }\n\n /**\n * Title for a session pi has not persisted yet. pi names a session only when\n * something calls `set_session_name`, and until the file exists the catalog\n * cannot derive a title from the file either \u2014 so derive it from the first\n * user message here, the same way the catalog does. A session must not stay\n * \"New session\" once the user has said something.\n */\n private derivedTitle(key: string): string | undefined {\n for (const item of this.options.transcripts.items(key)) {\n if (item.kind !== 'user' || item.text.trim().length === 0) {\n continue;\n }\n const single = item.text.replace(/\\s+/g, ' ').trim();\n return single.length > 72 ? `${single.slice(0, 72)}\u2026` : single;\n }\n return undefined;\n }\n\n private emitReady(): void {\n this.hostReadyEmitted = true;\n this.options.emit({\n type: 'transcript/replace',\n payload: { items: this.activeKey ? this.options.transcripts.items(this.activeKey) : [] },\n });\n this.options.emit({\n type: 'host/ready',\n payload: {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: this.options.capabilities,\n state: this.currentState(),\n frontend: this.options.frontend,\n },\n });\n this.publishActivity();\n }\n\n private emitTranscript(key: string): void {\n this.options.emit({\n type: 'transcript/replace',\n payload: { items: this.options.transcripts.items(key) },\n });\n }\n\n private emitState(): void {\n this.options.emit({ type: 'session/state', payload: this.currentState() });\n this.publishActivity();\n }\n\n /**\n * Reports every live session (hot process), not only the selected one. The\n * navigator marks the ones the agent is actively working in, so two parallel\n * conversations both read as running instead of one silently taking over.\n */\n private publishActivity(): void {\n const sessions: SessionActivity[] = this.options.services.registry\n .hotKeys()\n .map((key) => {\n const state = this.options.services.registry.stateOf(key);\n const isActive = key === this.activeKey;\n return {\n sessionKey: key,\n streaming: state?.streaming ?? false,\n busy: isActive ? this.busy : false,\n agentReady: isActive ? this.agentReady : state !== undefined,\n agentStarting: isActive ? this.agentStarting : false,\n ...(isActive && this.agentError !== undefined ? { agentError: this.agentError } : {}),\n };\n });\n this.options.emit({ type: 'session/activity', payload: { sessions } });\n }\n\n private currentState(): SessionViewState {\n const meta = {\n agentReady: this.agentReady,\n agentStarting: this.agentStarting,\n agentError: this.agentError,\n agentFailure: this.agentFailure,\n busy: this.busy,\n hasOlderHistory: this.hasOlderHistory,\n loadingOlderHistory: this.loadingOlderHistory,\n };\n const state = this.activeKey === undefined\n ? undefined\n : this.options.services.registry.stateOf(this.activeKey);\n if (state) {\n const view = toSessionViewState(state, meta);\n // pi names a session only when it emits `session_name`, so a *resumed*\n // session arrives without one \u2014 and the panel then falls back to the\n // workspace name for a title it already knows. The catalog is the\n // authority (it is what the sidebar lists); a session still on the\n // placeholder takes its title from its first prompt instead.\n if (view.sessionTitle === undefined && this.activeKey !== undefined) {\n const catalog = this.summaries.get(this.activeKey)?.title;\n view.sessionTitle =\n catalog !== undefined && catalog !== UNTITLED_SESSION\n ? catalog\n : this.derivedTitle(this.activeKey);\n }\n return view;\n }\n // The autoOpen:false draft: a truthful workspace line, and \u2014 once the\n // session-less probe lands \u2014 pickers that work before any session exists.\n return draftSessionViewState(\n this.draftWorkspace ?? this.options.services.registry.defaultWorkspace,\n meta,\n this.draftCatalog,\n this.draftModel,\n this.draftThinking,\n );\n }\n\n private setBusy(busy: boolean): void {\n if (this.busy === busy) {\n return;\n }\n this.busy = busy;\n this.emitState();\n }\n\n private fail(message: string, detail?: string): void {\n const key = this.activeKey;\n if (key !== undefined) {\n this.options.transcripts.error(key, message, detail);\n return;\n }\n // No transcript to attach to (draft): say it as a notice. The wire `error`\n // stays reserved for a refusing handshake \u2014 reusing it here raised the\n // frontend's \"Offline \u2014 the host wouldn't take this frontend\" banner, whose\n // Reload button then wiped the panel for an error that never touched the\n // connection. A spawn failure additionally sets `agentError`, which shows\n // the banner that actually fits.\n this.options.emit(noticeMessage('error', detail ? `${message}\\n${detail}` : message, Date.now()));\n }\n\n /** Transient error for the client (banner), never a transcript item. */\n private emitWireError(message: string, detail?: string): void {\n this.options.emit({ type: 'error', payload: { message, detail, at: Date.now() } });\n this.options.emit(\n noticeMessage('error', detail ? `${message}\\n${detail}` : message, Date.now()),\n );\n }\n\n private async guard<T>(action: () => Promise<T>): Promise<Guarded<T>> {\n try {\n return { ok: true, value: await action() };\n } catch (error: unknown) {\n const message = describeError(error);\n this.options.logger.error(`Morse command failed: ${message}`, error);\n this.fail(message);\n return { ok: false };\n }\n }\n}\n\nfunction workspaceOf(path: string): WorkspaceRef {\n const parts = path.split(/[\\\\/]/).filter((part) => part.length > 0);\n return { cwd: path, name: parts.at(-1) ?? path };\n}\n\nfunction insideRoots(path: string, roots: string[]): boolean {\n return roots.some((root) => path === root || path.startsWith(`${root}/`) || path.startsWith(`${root}\\\\`));\n}\n\n/** Domain history -> transcript items (presentation mapping stays in this layer). */\nfunction historyItems(key: string, entries: AgentHistoryEntry[], page: number): TranscriptItem[] {\n return entries.map((entry, index): TranscriptItem => {\n const id = `${key}#history-${page}-${index}`;\n const at = entry.at ?? 0;\n if (entry.role === 'user') {\n return {\n kind: 'user',\n id,\n at,\n text: entry.text,\n ...(entry.images ? { images: entry.images } : {}),\n ...(entry.pins ? { pins: entry.pins } : {}),\n };\n }\n if (entry.role === 'tool') {\n return {\n kind: 'tool',\n id,\n at,\n name: entry.name,\n title: entry.title,\n status: entry.status,\n input: entry.input,\n output: entry.output,\n };\n }\n if (entry.role === 'compaction') {\n // A marker row, not a message: the transcript keeps every folded entry \u2014\n // the divider says where the agent's context was reset, not where history\n // ends (\"Start of conversation\" belongs to the real beginning again).\n return {\n kind: 'compaction',\n id,\n at,\n summary: entry.summary,\n ...(entry.tokensBefore !== undefined ? { tokensBefore: entry.tokensBefore } : {}),\n };\n }\n return {\n kind: 'assistant',\n id,\n at,\n text: entry.text,\n thinking: entry.thinking ?? '',\n streaming: false,\n model: entry.model,\n };\n });\n}\n\n/**\n * Maps a transcript item to the fork entry pi knows.\n *\n * pi's `get_fork_messages` lists every user message with non-empty text \u2014 the\n * same messages the transcript shows as text or pins (an image-only message has\n * no text and is not forkable). Matching the same distance from the end keeps\n * the two lists aligned even when an older history page is still unloaded.\n */\nfunction entryIdForEdit(\n items: TranscriptItem[],\n itemId: string,\n forkMessages: AgentForkMessage[],\n): string | undefined {\n const forkable = items.filter(\n (item): item is UserTranscriptItem =>\n item.kind === 'user' && (item.text.trim().length > 0 || (item.pins?.length ?? 0) > 0),\n );\n const index = forkable.findIndex((item) => item.id === itemId);\n if (index === -1) {\n return undefined;\n }\n const position = forkMessages.length - 1 - (forkable.length - 1 - index);\n return position >= 0 ? forkMessages[position]?.entryId : undefined;\n}\n\nfunction describeError(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return typeof error === 'string' ? error : JSON.stringify(error);\n}\n\n/** Terminal geometry the wire named: a positive integer, or the default. */\nfunction clampGeometry(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value)) {\n return fallback;\n }\n return Math.max(1, Math.min(1000, Math.floor(value)));\n}\n\n/** A terminal that could not open says why, the same way a command failure does. */\nfunction describeTerminalError(error: unknown): string {\n return error instanceof MorseError ? error.message : describeError(error);\n}\n\n/**\n * The machine-readable half of a failed start. Only the codes a frontend has help\n * for cross the wire; everything else keeps its message and the host's hint \u2014 a\n * frontend that guessed would offer the wrong remedy.\n */\nfunction describeAgentFailure(\n error: unknown,\n hint: string | undefined,\n): AgentFailure | undefined {\n const code = agentErrorCode(error);\n const install = error instanceof AgentUnavailableError ? error.remedy?.install : undefined;\n const failure: AgentFailure = {\n ...(code ? { code } : {}),\n ...(install ? { install } : {}),\n ...(hint ? { hint } : {}),\n };\n return Object.keys(failure).length > 0 ? failure : undefined;\n}\n\nfunction agentErrorCode(error: unknown): AgentErrorCode | undefined {\n if (!(error instanceof MorseError)) {\n return undefined;\n }\n // `MorseError.code` is a plain string; only the two the UI can help with pass.\n return error.code === 'agent-unavailable' || error.code === 'agent-protocol'\n ? error.code\n : undefined;\n}\n\n/** The host's next step without a code: a dead process, not a resolution failure. */\nfunction hintOnly(hint: string | undefined): AgentFailure | undefined {\n return hint ? { hint } : undefined;\n}", "import { Controller, Get } from '@nestjs/common';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { PROTOCOL_VERSION, parseFrontendManifest } from '@morse/protocol';\nimport type { MorseServerConfig } from '../../app/config.js';\nimport { Inject } from '@nestjs/common';\nimport { MORSE_CONFIG } from '../../app/tokens.js';\n\n@Controller('api')\nexport class HealthController {\n constructor(@Inject(MORSE_CONFIG) private readonly config: MorseServerConfig) {}\n\n @Get('health')\n health(): {\n status: string;\n protocolVersion: number;\n workspace: string;\n frontend: string;\n instance?: string;\n } {\n return {\n status: 'ok',\n protocolVersion: PROTOCOL_VERSION,\n workspace: this.config.workspace.cwd,\n frontend: this.config.uiDir,\n // Echoed so the CLI that spawned this daemon can tell it apart from an\n // unrelated process that later reused the same pid.\n ...(this.config.instance ? { instance: this.config.instance } : {}),\n };\n }\n\n @Get('manifest')\n async manifest(): Promise<unknown> {\n const raw = await readFile(join(this.config.uiDir, 'webview.manifest.json'), 'utf8').catch(\n () => undefined,\n );\n return parseFrontendManifest(raw) ?? { name: 'unknown', protocolVersion: PROTOCOL_VERSION };\n }\n}\n", "import type { MorseLogger } from '@morse/core';\nimport type { PiRpcAdapter } from '@morse/adapter-pi-rpc';\n\nexport const MORSE_CONFIG = 'MORSE_CONFIG';\nexport const MORSE_LOGGER = 'MORSE_LOGGER';\nexport const MORSE_PI_ADAPTER = 'MORSE_PI_ADAPTER';\nexport const MORSE_SESSION_REGISTRY = 'MORSE_SESSION_REGISTRY';\nexport const MORSE_TRANSCRIPT_STORE = 'MORSE_TRANSCRIPT_STORE';\nexport const MORSE_PROJECT_POLICY = 'MORSE_PROJECT_POLICY';\n\nexport type { MorseLogger, PiRpcAdapter };\n\n\n\n", "import { Logger } from '@nestjs/common';\nimport type { MorseLogger } from '@morse/core';\n\n/** Morse's logging port implemented on top of the NestJS logger. */\nexport class NestMorseLogger implements MorseLogger {\n private readonly logger = new Logger('morse');\n\n debug(message: string, detail?: unknown): void {\n this.logger.debug(withDetail(message, detail));\n }\n\n info(message: string, detail?: unknown): void {\n this.logger.log(withDetail(message, detail));\n }\n\n warn(message: string, detail?: unknown): void {\n this.logger.warn(withDetail(message, detail));\n }\n\n error(message: string, detail?: unknown): void {\n this.logger.error(withDetail(message, detail));\n }\n}\n\nfunction withDetail(message: string, detail: unknown): string {\n if (detail === undefined) {\n return message;\n }\n if (detail instanceof Error) {\n return `${message}: ${detail.message}`;\n }\n try {\n return `${message} ${JSON.stringify(detail)}`;\n } catch {\n return `${message} ${String(detail)}`;\n }\n}\n", "import { isAbsolute, resolve, sep } from 'node:path';\nimport type { ProjectPolicy } from '@morse/host-runtime';\n\n/**\n * Which directories the server lets the agent work in.\n *\n * With `MORSE_PROJECTS` set, only those roots (and their subdirectories) are\n * allowed. Without it the host trusts the operator: any absolute path is\n * accepted, which is the right default for a server bound to 127.0.0.1 and for\n * the UI, which only offers discovered pi projects plus the default workspace.\n */\nexport class ServerProjectPolicy implements ProjectPolicy {\n private readonly roots: string[];\n\n constructor(roots: string[]) {\n this.roots = roots.filter((root) => root.trim().length > 0).map((root) => resolve(root));\n }\n\n get allowList(): string[] {\n return [...this.roots];\n }\n\n canOpen(path: string): boolean {\n if (!isAbsolute(path)) {\n return false;\n }\n if (this.roots.length === 0) {\n return true;\n }\n const target = resolve(path);\n return this.roots.some(\n (root) => target === root || target.startsWith(`${root}${sep}`),\n );\n }\n}\n", "import { spawn as spawnPty, type IPty } from 'node-pty';\nimport { Inject, Injectable, type OnModuleDestroy } from '@nestjs/common';\nimport { UnsupportedByHostError, type MorseLogger } from '@morse/core';\nimport {\n TERMINAL_BUFFER_CHARS,\n trimTail,\n type TerminalBackend,\n type TerminalOpenOptions,\n type TerminalSession,\n type TerminalSink,\n} from '@morse/host-runtime';\nimport type { MorseServerConfig } from '../../app/config.js';\nimport { MORSE_CONFIG, MORSE_LOGGER, MORSE_PROJECT_POLICY } from '../../app/tokens.js';\nimport type { ServerProjectPolicy } from '../projects/project-policy.js';\nimport { readTerminalLog, removeTerminalLog, writeTerminalLog } from './terminal-log.js';\n\n/** How long a shell gets to exit on kill before it is killed outright. */\nconst SIGKILL_AFTER_MS = 1_500;\n/** How often the streaming scrollback is written down. A shell can output fast. */\nconst FLUSH_DEBOUNCE_MS = 2_000;\n\n/** The user's shell, falling back per platform when `$SHELL` is not set. */\nfunction defaultShell(): string {\n const configured = process.env.SHELL?.trim();\n if (configured !== undefined && configured.length > 0) {\n return configured;\n }\n return process.platform === 'win32' ? 'powershell.exe' : '/bin/bash';\n}\n\n/**\n * One terminal the host is holding: the live shell (when it has one), the viewers\n * attached to it, and the scrollback.\n *\n * `output` is both the replay buffer and the on-disk log's in-memory copy: the\n * shell appends, a viewer that attaches late gets the whole bounded tail, and the\n * file is written on a debounce so a host restart still has it.\n */\ninterface TerminalEntry {\n pty?: IPty;\n sinks: Set<TerminalSink>;\n output: string;\n /** There is output the file has not seen yet. */\n dirty: boolean;\n flushTimer?: ReturnType<typeof setTimeout>;\n idleTimer?: ReturnType<typeof setTimeout>;\n /**\n * Bumped whenever the shell is replaced or closed. A dead shell's late\n * callbacks compare it and stay silent, so they cannot append to (or report\n * over) the shell now holding the id.\n */\n generation: number;\n}\n\n/**\n * The browser host's terminal (driven adapter). A real pseudo-terminal via\n * `node-pty`, so the shell is interactive exactly as in a desktop terminal:\n * prompts, colours, line editing, resize and full-screen programs (vim, top)\n * all work.\n *\n * The process runs in the viewing session's directory and is gated by\n * `ProjectPolicy`, like opening a session. A directory the policy refuses throws\n * before anything is spawned.\n *\n * This class is a *registry*, not a per-connection spawner: it is a singleton, so\n * the shell and its scrollback outlive the connection that opened it. A reloaded\n * page attaches to the same process and catches up on `replay`; only an explicit\n * `close` (or the idle timeout) ends a shell. The scrollback also lands under\n * `<MORSE_HOME>/terminals/`, so the output survives the *host* restarting too.\n */\n@Injectable()\nexport class ServerTerminalBackend implements TerminalBackend, OnModuleDestroy {\n private readonly entries = new Map<string, TerminalEntry>();\n\n constructor(\n @Inject(MORSE_PROJECT_POLICY) private readonly policy: ServerProjectPolicy,\n @Inject(MORSE_LOGGER) private readonly logger: MorseLogger,\n @Inject(MORSE_CONFIG) private readonly config: MorseServerConfig,\n ) {}\n\n attach(\n terminalId: string,\n options: TerminalOpenOptions,\n sink: TerminalSink,\n ): TerminalSession {\n const entry = this.entryFor(terminalId);\n if (entry.pty === undefined) {\n // No live shell: either the first open ever, or one that exited / was\n // reclaimed. The log is already loaded, so a fresh shell continues the\n // history instead of starting blank.\n this.spawn(terminalId, entry, options);\n } else {\n // A live shell takes the reattaching viewer's size.\n this.resize(entry, options.cols, options.rows);\n }\n entry.sinks.add(sink);\n this.cancelIdle(entry);\n return {\n replay: entry.output,\n write: (data) => entry.pty?.write(data),\n resize: (cols, rows) => this.resize(entry, cols, rows),\n detach: () => this.detach(terminalId, entry, sink),\n };\n }\n\n /** Ends a shell for good, drops its scrollback, and tells any other viewer. */\n close(terminalId: string): void {\n const entry = this.entries.get(terminalId);\n this.entries.delete(terminalId);\n if (entry !== undefined) {\n this.stopFlush(entry);\n this.cancelIdle(entry);\n entry.generation += 1;\n const sinks = [...entry.sinks];\n entry.sinks.clear();\n this.killPty(entry.pty);\n entry.pty = undefined;\n // A viewer in another window is looking at a shell that is now gone.\n for (const sink of sinks) {\n sink.exit();\n }\n }\n removeTerminalLog(this.config.dataDir, terminalId);\n }\n\n onModuleDestroy(): void {\n // The host is going away, so the shells cannot outlive it \u2014 but the\n // scrollback can. Persist first, then kill; the next start replays the file.\n for (const [terminalId, entry] of this.entries) {\n entry.generation += 1;\n this.persist(terminalId, entry);\n this.killPty(entry.pty);\n entry.pty = undefined;\n this.stopFlush(entry);\n this.cancelIdle(entry);\n }\n this.entries.clear();\n }\n\n /** SIGTERM, then SIGKILL for a shell that ignores it (a hung process group). */\n private killPty(pty: IPty | undefined): void {\n if (pty === undefined) {\n return;\n }\n try {\n pty.kill();\n } catch {\n return;\n }\n const timer = setTimeout(() => {\n try {\n pty.kill('SIGKILL');\n } catch {\n // Already gone.\n }\n }, SIGKILL_AFTER_MS);\n timer.unref?.();\n }\n\n private entryFor(terminalId: string): TerminalEntry {\n const existing = this.entries.get(terminalId);\n if (existing !== undefined) {\n return existing;\n }\n const entry: TerminalEntry = {\n sinks: new Set(),\n output: readTerminalLog(this.config.dataDir, terminalId),\n dirty: false,\n generation: 0,\n };\n this.entries.set(terminalId, entry);\n return entry;\n }\n\n private spawn(terminalId: string, entry: TerminalEntry, options: TerminalOpenOptions): void {\n // The controller passes the session's directory, but the payload can name\n // its own: the policy is what decides, exactly like opening a session.\n if (!this.policy.canOpen(options.cwd)) {\n throw new UnsupportedByHostError(`Morse is not allowed to run a terminal in ${options.cwd}.`);\n }\n const shell = defaultShell();\n const generation = ++entry.generation;\n const pty = spawnPty(shell, [], {\n name: 'xterm-256color',\n cols: options.cols,\n rows: options.rows,\n cwd: options.cwd,\n env: { ...process.env, TERM: 'xterm-256color', MORSE_TERMINAL: '1' },\n });\n entry.pty = pty;\n this.logger.info(`Terminal opened in ${options.cwd} (${shell}, pid ${pty.pid})`);\n\n pty.onData((chunk) => {\n if (entry.generation !== generation) {\n return;\n }\n // `+=` builds a rope cheaply; trimming every chunk would flatten (copy) the\n // whole buffer per chunk, which a command like `yes` would feel. Let it\n // overshoot, then cut back to the cap in one pass.\n entry.output += chunk;\n if (entry.output.length > TERMINAL_BUFFER_CHARS * 1.5) {\n entry.output = trimTail(entry.output, TERMINAL_BUFFER_CHARS);\n }\n entry.dirty = true;\n this.scheduleFlush(terminalId, entry);\n for (const sink of entry.sinks) {\n sink.output(chunk);\n }\n });\n pty.onExit(({ exitCode }) => {\n if (entry.generation !== generation) {\n return;\n }\n entry.pty = undefined;\n this.persist(terminalId, entry);\n for (const sink of [...entry.sinks]) {\n sink.exit(exitCode);\n }\n });\n }\n\n private resize(entry: TerminalEntry, cols: number, rows: number): void {\n try {\n entry.pty?.resize(cols, rows);\n } catch {\n // A shell that exited between the attach and the resize: nothing to size.\n }\n }\n\n private detach(terminalId: string, entry: TerminalEntry, sink: TerminalSink): void {\n entry.sinks.delete(sink);\n if (entry.sinks.size > 0) {\n return;\n }\n // Nobody is watching: write the scrollback down and start the idle clock.\n this.persist(terminalId, entry);\n this.scheduleIdle(terminalId, entry);\n }\n\n /**\n * Reclaims a shell nobody has reattached to. The log stays (the output should\n * still be there next time); only the process goes.\n */\n private scheduleIdle(terminalId: string, entry: TerminalEntry): void {\n if (this.config.terminalIdleMs <= 0) {\n return;\n }\n this.cancelIdle(entry);\n entry.idleTimer = setTimeout(() => {\n entry.idleTimer = undefined;\n if (entry.sinks.size > 0) {\n return;\n }\n entry.generation += 1;\n this.killPty(entry.pty);\n entry.pty = undefined;\n this.persist(terminalId, entry);\n this.entries.delete(terminalId);\n }, this.config.terminalIdleMs);\n entry.idleTimer.unref?.();\n }\n\n private cancelIdle(entry: TerminalEntry): void {\n if (entry.idleTimer !== undefined) {\n clearTimeout(entry.idleTimer);\n entry.idleTimer = undefined;\n }\n }\n\n private scheduleFlush(terminalId: string, entry: TerminalEntry): void {\n if (entry.flushTimer !== undefined) {\n return;\n }\n entry.flushTimer = setTimeout(() => {\n entry.flushTimer = undefined;\n this.persist(terminalId, entry);\n }, FLUSH_DEBOUNCE_MS);\n entry.flushTimer.unref?.();\n }\n\n private stopFlush(entry: TerminalEntry): void {\n if (entry.flushTimer !== undefined) {\n clearTimeout(entry.flushTimer);\n entry.flushTimer = undefined;\n }\n }\n\n private persist(terminalId: string, entry: TerminalEntry): void {\n this.stopFlush(entry);\n if (!entry.dirty) {\n return;\n }\n entry.dirty = false;\n writeTerminalLog(this.config.dataDir, terminalId, entry.output);\n }\n}\n", "import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { TERMINAL_BUFFER_CHARS, trimTail } from '@morse/host-runtime';\n\n/** The directory under `<MORSE_HOME>` holding one scrollback file per terminal. */\nconst LOG_DIR = 'terminals';\n/**\n * How much scrollback a terminal keeps across a host restart. Larger than the\n * in-memory replay because the file is the long-term copy: a reader who comes\n * back the next day still finds what they were looking at.\n */\nconst LOG_MAX_CHARS = TERMINAL_BUFFER_CHARS * 4;\n\n/**\n * The scrollback a shell produced, kept on disk under `<MORSE_HOME>/terminals/`.\n *\n * This is what makes a terminal's *output* survive the host itself: the live\n * buffer lives in memory and is lost on a restart, so the file is written as the\n * shell streams (debounced by the caller) and read back the next time a pane\n * with the same id attaches. A missing or unreadable file is not an error \u2014 it\n * just means there is nothing to replay.\n */\nexport function readTerminalLog(dataDir: string, terminalId: string): string {\n try {\n const raw = readFileSync(logPath(dataDir, terminalId), 'utf8');\n return trimTail(raw, LOG_MAX_CHARS);\n } catch {\n return '';\n }\n}\n\n/** Writes the current scrollback, capped and trimmed to the tail. Atomic, so a crash cannot truncate it. */\nexport function writeTerminalLog(dataDir: string, terminalId: string, text: string): void {\n const trimmed = trimTail(text, LOG_MAX_CHARS);\n const target = logPath(dataDir, terminalId);\n const temp = `${target}.${process.pid}.tmp`;\n try {\n mkdirSync(join(dataDir, LOG_DIR), { recursive: true });\n writeFileSync(temp, trimmed);\n renameSync(temp, target);\n } catch {\n // Some filesystems refuse a rename over an existing file (a Windows mount).\n try {\n writeFileSync(target, trimmed);\n } catch {\n rmSync(temp, { force: true });\n }\n }\n}\n\n/** Forgets a terminal's scrollback (the reader closed the pane). */\nexport function removeTerminalLog(dataDir: string, terminalId: string): void {\n rmSync(logPath(dataDir, terminalId), { force: true });\n}\n\n/**\n * A terminal id the client named, turned into a filename. An id is not trusted:\n * a `../` in it must not become a path, so anything outside a conservative set\n * is replaced. The frontend mints `term-N`, which passes through untouched.\n */\nexport function safeTerminalName(terminalId: string): string {\n const safe = terminalId.replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 100);\n return safe.length > 0 ? safe : 'terminal';\n}\n\nfunction logPath(dataDir: string, terminalId: string): string {\n return join(dataDir, LOG_DIR, `${safeTerminalName(terminalId)}.log`);\n}\n", "import { Inject, Injectable, type OnModuleDestroy } from '@nestjs/common';\nimport { SessionRegistry, type MorseLogger } from '@morse/core';\nimport { SessionTranscriptStore } from '@morse/host-runtime';\nimport type { PiRpcAdapter } from '@morse/adapter-pi-rpc';\nimport type { MorseServerConfig } from '../app/config.js';\nimport { MORSE_SESSION_REGISTRY, MORSE_TRANSCRIPT_STORE } from '../app/tokens.js';\n\n/**\n * Composition root glue (R7): builds the one session registry this server owns.\n * Because it is shared, multiple projects and sessions keep running while\n * clients connect, refresh and disconnect.\n */\nexport function createSessionRegistry(\n adapter: PiRpcAdapter,\n config: MorseServerConfig,\n logger: MorseLogger,\n): SessionRegistry {\n return new SessionRegistry({\n factory: adapter.factory,\n catalog: adapter.catalog,\n defaultWorkspace: config.workspace,\n hotLimit: config.hotSessions,\n logger,\n });\n}\n\n/** Shutdown hook: dispose every warm `pi` process and the transcripts. */\n@Injectable()\nexport class MorseRegistryLifecycle implements OnModuleDestroy {\n constructor(\n @Inject(MORSE_SESSION_REGISTRY) private readonly registry: SessionRegistry,\n @Inject(MORSE_TRANSCRIPT_STORE) private readonly transcripts: SessionTranscriptStore,\n ) {}\n\n async onModuleDestroy(): Promise<void> {\n await this.registry.dispose();\n this.transcripts.dispose();\n }\n}\n", "import {\n OnGatewayConnection,\n OnGatewayDisconnect,\n WebSocketGateway,\n} from '@nestjs/websockets';\nimport { Inject } from '@nestjs/common';\nimport type { RawData, WebSocket } from 'ws';\nimport {\n DEFAULT_WS_PATH,\n decode,\n encodeWireMessage,\n parseClientMessage,\n type HostToClientMessage,\n} from '@morse/protocol';\nimport { HostSessionController } from '@morse/host-runtime';\nimport type { MorseLogger } from '@morse/core';\nimport { MORSE_LOGGER } from '../../app/tokens.js';\nimport { MorseSessionFactory } from './session-factory.service.js';\n\n/**\n * Driving adapter: one WebSocket per client, one controller per connection, and\n * a shared session registry \u2014 so sessions in several projects keep running while\n * clients connect, refresh or disconnect. Everything protocol-shaped is\n * delegated to `HostSessionController`, which the VS Code host also uses.\n */\n@WebSocketGateway({ path: DEFAULT_WS_PATH })\nexport class MorseGateway implements OnGatewayConnection, OnGatewayDisconnect {\n private readonly clients = new Map<WebSocket, HostSessionController>();\n\n constructor(\n @Inject(MorseSessionFactory) private readonly sessions: MorseSessionFactory,\n @Inject(MORSE_LOGGER) private readonly logger: MorseLogger,\n ) {}\n\n handleConnection(client: WebSocket): void {\n const emit = (message: HostToClientMessage): void => {\n if (client.readyState === 1) {\n client.send(encodeWireMessage(message));\n }\n };\n\n const controller = this.sessions.createFor(emit);\n this.clients.set(client, controller);\n\n client.on('message', (data: RawData) => {\n void this.onMessage(controller, data);\n });\n client.on('error', (error: Error) => {\n this.logger.warn('WebSocket error', error);\n });\n\n void controller.start().catch((error: unknown) => {\n this.logger.error('Failed to start a Morse session for a new client', error);\n });\n }\n\n handleDisconnect(client: WebSocket): void {\n const controller = this.clients.get(client);\n this.clients.delete(client);\n // Only the connection's controller goes away; its sessions stay warm in the\n // shared registry, so a refresh reattaches to the same `pi` processes.\n void controller?.dispose().catch((error: unknown) => {\n this.logger.warn('Failed to dispose a Morse session controller', error);\n });\n }\n\n private async onMessage(controller: HostSessionController, data: RawData): Promise<void> {\n const message = parseClientMessage(decode(data));\n if (!message) {\n this.logger.warn('Ignoring an unrecognised client message');\n return;\n }\n await controller.handleClientMessage(message);\n }\n}", "import { Inject, Injectable } from '@nestjs/common';\nimport { ChatService, SessionRegistry, UnsupportedByHostError, type MorseLogger } from '@morse/core';\nimport {\n HostSessionController,\n type HostCommandContext,\n type HostSessionServices,\n type SessionTranscriptStore,\n} from '@morse/host-runtime';\nimport type { HostCapabilities, HostToClientMessage } from '@morse/protocol';\nimport type { MorseServerConfig } from '../../app/config.js';\nimport {\n MORSE_CONFIG,\n MORSE_LOGGER,\n MORSE_PROJECT_POLICY,\n MORSE_SESSION_REGISTRY,\n MORSE_TRANSCRIPT_STORE,\n} from '../../app/tokens.js';\nimport { saveUpload } from '../uploads/upload-store.js';\nimport { browseDirectory } from '../workspace/directory-browser.js';\nimport { readWorkspaceFile } from '../workspace/file-store.js';\nimport {\n checkoutGit,\n commitGit,\n pullGit,\n pushGit,\n readCommitDiff,\n readCommitFiles,\n readGitBranches,\n readGitDiff,\n readGitLog,\n readGitStatus,\n readGitSync,\n stageGitPaths,\n unstageGitPaths,\n} from '../workspace/git-log.js';\nimport { workspaceFiles } from '../workspace/workspace-index.js';\nimport { readWorkbench, saveWorkbench, readDrafts, saveDrafts } from '../workspace/workbench-store.js';\nimport { ServerProjectPolicy } from '../projects/project-policy.js';\nimport { ServerTerminalBackend } from '../terminal/terminal.service.js';\n\n/**\n * Composition root for one client connection (clean-architecture R7): the only\n * place that knows the concrete pi adapter, the core services and the WebSocket\n * delivery. Wiring only \u2014 no business logic lives here.\n *\n * The session registry is shared by every connection, so sessions and projects\n * keep running while clients come and go.\n */\n@Injectable()\nexport class MorseSessionFactory {\n constructor(\n @Inject(MORSE_SESSION_REGISTRY) private readonly registry: SessionRegistry,\n @Inject(MORSE_TRANSCRIPT_STORE) private readonly transcripts: SessionTranscriptStore,\n @Inject(MORSE_PROJECT_POLICY) private readonly policy: ServerProjectPolicy,\n @Inject(MORSE_LOGGER) private readonly logger: MorseLogger,\n @Inject(MORSE_CONFIG) private readonly config: MorseServerConfig,\n @Inject(ServerTerminalBackend) private readonly terminal: ServerTerminalBackend,\n ) {}\n\n capabilities(): HostCapabilities {\n return {\n hostKind: 'server',\n // The browser host is global: every project pi knows about.\n scope: 'global',\n // A browser has no local editor to read, so there is nothing to pin.\n editorContext: false,\n // No QuickPick/InputBox here, so the frontend renders interactions.\n nativeDialogs: false,\n insertIntoEditor: false,\n revealFile: false,\n // The server can walk the active session's directory, so the composer's\n // `@` picker works here too \u2014 the same list VS Code offers for a workspace.\n filePicker: true,\n // A browser has no editor of its own, so this host reads files for the\n // frontend's Explorer and preview tabs. VS Code keeps its native ones.\n filePreview: true,\n // The same reasoning for history: VS Code has Source Control, so the\n // browser host is the one that answers `gitLog` for its own git panel.\n gitPanel: true,\n // VS Code already has an integrated terminal; the browser host has none, so\n // the bottom panel's terminal is this host's. It runs in the viewing\n // session's directory and is gated by `ProjectPolicy`, like a session.\n terminal: true,\n // The browser host restores the tabs, panel, terminals and half-written\n // prompts the reader left open, kept under `<MORSE_HOME>`. VS Code has its\n // own tab restoration and leaves this off.\n workbench: true,\n // A browser cannot hand a dragged file's path to pi, so the host takes the\n // bytes and writes them next to the session; the frontend then `@mentions`\n // the path it gets back.\n fileUpload: true,\n // The browser host has no workspace folder, so \"New session\" has to ask\n // which directory the agent should run in. The frontend opens its folder\n // modal; VS Code has a folder already and leaves this off.\n directoryPicker: true,\n // pi's fork re-parents the conversation, so a past prompt can be edited.\n editMessage: true,\n // The same fork can branch a new session and hand the prompt back instead.\n forkMessage: true,\n // A page served by this host may ask the registry for the latest release, so\n // the sidebar can say when a newer Morse is out. A host without it stays\n // quiet; this one is on unless `MORSE_UPDATE_CHECK=0` says otherwise.\n updateCheck: this.config.updateCheck,\n };\n }\n\n createFor(emit: (message: HostToClientMessage) => void): HostSessionController {\n const services: HostSessionServices = {\n registry: this.registry,\n chat: new ChatService({ agent: this.registry, logger: this.logger }),\n };\n\n return new HostSessionController({\n services,\n capabilities: this.capabilities(),\n emit,\n logger: this.logger,\n transcripts: this.transcripts,\n policy: this.policy,\n scope: { kind: 'global' },\n // The bundle this host serves, so the panel can name its own version.\n ...(this.config.frontend ? { frontend: this.config.frontend } : {}),\n // The registry outlives this connection on purpose.\n ownsRegistry: false,\n // A page load must not create a session; the first prompt opens one.\n autoOpen: false,\n onHostCommand: (command, args, context) => this.runHostCommand(command, args, context),\n // The bottom panel's shell: this host can spawn one on its own machine.\n terminal: this.terminal,\n agentHint:\n 'Install the pi CLI and make sure it is on PATH, or start the server with MORSE_PI_PATH set.',\n });\n }\n\n /**\n * The host commands this host answers: browsing the filesystem for a project\n * folder, listing the active session's files for the composer's `@mention`\n * picker, and writing a browser upload down next to the session.\n *\n * `listDirectories` is read-only and may look anywhere the server can read;\n * the commands that touch a session are scoped to its cwd (which\n * `ProjectPolicy` already approved when the session was opened), never to a\n * path the client names.\n */\n private async runHostCommand(\n command: string,\n args: Record<string, unknown> | undefined,\n context?: HostCommandContext,\n ): Promise<unknown> {\n switch (command) {\n case 'listDirectories': {\n // Feeds the frontend's \"New session\" folder modal. Browsing is harmless,\n // but the chosen folder still has to pass the policy before a session\n // may run there, so the result says so (`canOpen`).\n const requested = typeof args?.path === 'string' ? args.path : undefined;\n const listing = await browseDirectory(requested, {\n roots: this.policy.allowList,\n defaultPath: this.registry.defaultWorkspace.cwd,\n });\n return { ...listing, canOpen: this.policy.canOpen(listing.path) };\n }\n case 'readWorkbench': {\n // The shell layout the reader left behind. Read-only and host-scoped (a\n // file under `MORSE_HOME`), so it never consults the project policy.\n return readWorkbench(this.config.dataDir) ?? null;\n }\n case 'saveWorkbench': {\n // The frontend owns the inner shape; the store guards version and size.\n return { ok: saveWorkbench(this.config.dataDir, args) };\n }\n case 'readDrafts': {\n // The half-written prompts, keyed by tab. Same host-scoped file as the\n // layout, but its own file because a draft can carry inline images.\n return readDrafts(this.config.dataDir) ?? null;\n }\n case 'saveDrafts': {\n return { ok: saveDrafts(this.config.dataDir, args) };\n }\n case 'listFiles': {\n const cwd = this.requireWritableCwd(context);\n // There is no workspace folder here, so the list is the directory the\n // client is viewing (its session, or the draft it is about to open) \u2014\n // exactly what pi resolves a mention against. `fresh` skips the index\n // cache: the Explorer polls, so a file added on disk has to appear.\n const files = await workspaceFiles(cwd, this.logger, { fresh: args?.fresh === true });\n return { files };\n }\n case 'readFile': {\n // The Explorer and the preview tabs. `readWorkspaceFile` resolves the\n // path inside the session's directory and refuses anything that escapes.\n const cwd = this.commandCwd(context, args);\n const path = typeof args?.path === 'string' ? args.path : '';\n if (path.length === 0) {\n throw new UnsupportedByHostError('readFile needs a \"path\" argument.');\n }\n return readWorkspaceFile(cwd, path);\n }\n case 'uploadFile': {\n const cwd = this.requireWritableCwd(context);\n const saved = await saveUpload(cwd, args ?? {}, this.config.uploadDir);\n this.logger.info(`Upload stored: ${saved.path} (${saved.bytes} bytes)`);\n return saved;\n }\n case 'gitLog': {\n // The git panel's history and graph. Like `readFile`, the repository is\n // the viewing session's directory, never a path the client names. `skip`\n // pages towards the root commit as the panel scrolls.\n const cwd = this.requireWritableCwd(context);\n const max = typeof args?.max === 'number' ? args.max : undefined;\n const skip = typeof args?.skip === 'number' ? args.skip : undefined;\n return readGitLog(cwd, max, skip);\n }\n case 'gitStatus': {\n // The working tree's changes, for the Explorer's per-file badges. Polled\n // alongside `listFiles`, so the two always describe the same moment.\n const cwd = this.requireWritableCwd(context);\n return readGitStatus(cwd);\n }\n case 'gitDiff': {\n // One file's unified diff for the preview's diff modes. The path is\n // resolved inside the session's directory, exactly like `readFile`.\n const cwd = this.commandCwd(context, args);\n const path = typeof args?.path === 'string' ? args.path : '';\n if (path.length === 0) {\n throw new UnsupportedByHostError('gitDiff needs a \"path\" argument.');\n }\n return readGitDiff(cwd, path);\n }\n case 'gitStage': {\n // Staging writes the index of the viewing session's repository. Paths are\n // resolved inside it, exactly like `readFile`, and the answer is the\n // fresh working tree so the panel updates in one round trip.\n const cwd = this.requireWritableCwd(context);\n return stageGitPaths(cwd, gitPaths(args));\n }\n case 'gitUnstage': {\n const cwd = this.requireWritableCwd(context);\n return unstageGitPaths(cwd, gitPaths(args));\n }\n case 'gitCommitFiles': {\n // The graph's expandable row: the paths one commit touched. The hash is\n // client-named, so it is validated as a hex object id, never a ref.\n const cwd = this.requireWritableCwd(context);\n const hash = typeof args?.hash === 'string' ? args.hash : '';\n return readCommitFiles(cwd, hash);\n }\n case 'gitCommitDiff': {\n const cwd = this.commandCwd(context, args);\n const hash = typeof args?.hash === 'string' ? args.hash : '';\n const path = typeof args?.path === 'string' ? args.path : '';\n if (path.length === 0) {\n throw new UnsupportedByHostError('gitCommitDiff needs a \"path\" argument.');\n }\n return readCommitDiff(cwd, hash, path);\n }\n case 'gitSync': {\n // How far HEAD is from its upstream, for the panel's pull/push controls.\n const cwd = this.requireWritableCwd(context);\n return readGitSync(cwd);\n }\n case 'gitPull': {\n // Network commands: git runs with prompts disabled, so a missing\n // credential fails instead of blocking the host on stdin.\n const cwd = this.requireWritableCwd(context);\n return pullGit(cwd);\n }\n case 'gitPush': {\n const cwd = this.requireWritableCwd(context);\n return pushGit(cwd);\n }\n case 'gitCommit': {\n // Commits the staged changes. The message is client-named but passed as\n // a single argv element, never through a shell.\n const cwd = this.requireWritableCwd(context);\n const message = typeof args?.message === 'string' ? args.message : '';\n return commitGit(cwd, message);\n }\n case 'gitBranches': {\n const cwd = this.requireWritableCwd(context);\n return readGitBranches(cwd);\n }\n case 'gitCheckout': {\n const cwd = this.requireWritableCwd(context);\n const branch = typeof args?.branch === 'string' ? args.branch : '';\n return checkoutGit(cwd, branch, args?.create === true);\n }\n default:\n throw new UnsupportedByHostError(`This host does not support \"${command}\".`);\n }\n }\n\n /**\n * The directory the command should touch, after `ProjectPolicy` approved it.\n * The controller hands in what the client is viewing; the registry's active\n * session is only a fallback for a host command issued without one.\n */\n private requireWritableCwd(context?: HostCommandContext): string {\n const cwd = context?.cwd ?? this.activeCwd();\n if (!cwd) {\n throw new UnsupportedByHostError('No active Morse session yet.');\n }\n if (!this.policy.canOpen(cwd)) {\n throw new UnsupportedByHostError(`Morse is not allowed to work in ${cwd}.`);\n }\n return cwd;\n }\n\n /**\n * The directory a file command works in. A restored preview tab carries the\n * project it was read from (`args.cwd`), because the host may still be on\n * another session when that tab is loaded; the policy checks it exactly like a\n * terminal's directory. Without one, the viewing session's directory is used.\n */\n private commandCwd(context?: HostCommandContext, args?: Record<string, unknown>): string {\n const requested = args?.cwd;\n if (typeof requested === 'string' && requested.length > 0) {\n if (!this.policy.canOpen(requested)) {\n throw new UnsupportedByHostError(`Morse is not allowed to work in ${requested}.`);\n }\n return requested;\n }\n return this.requireWritableCwd(context);\n }\n\n /** The cwd of the session this connection is showing. */\n private activeCwd(): string | undefined {\n const key = this.registry.activeKeyOf();\n const workspace = key === undefined ? undefined : this.registry.stateOf(key)?.workspace;\n return workspace?.cwd ?? this.registry.defaultWorkspace.cwd;\n }\n}\n\n/** The `paths` argument of a `gitStage`/`gitUnstage` command, with bad entries dropped. */\nfunction gitPaths(args: Record<string, unknown> | undefined): string[] {\n const paths = args?.paths;\n if (!Array.isArray(paths)) {\n return [];\n }\n return paths.filter((path): path is string => typeof path === 'string' && path.length > 0);\n}", "import { existsSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, isAbsolute, join, relative, sep } from 'node:path';\n\n/**\n * A browser cannot hand the agent a dragged file's path, so it sends the bytes\n * instead and the host \u2014 which owns a disk next to the session \u2014 writes them\n * down and answers with a path the frontend can `@mention`.\n *\n * Files land under the session's own directory by default, so pi's `read` (and\n * `grep`/`find`) resolve the mention exactly like any other workspace file.\n */\n\n/** Refuse anything larger: base64 of a huge file would also bloat the socket. */\nexport const MAX_UPLOAD_BYTES = 10 * 1024 * 1024;\n\n/** Default inbox, relative to the session cwd; `MORSE_UPLOAD_DIR` overrides it. */\nexport const DEFAULT_UPLOAD_DIR = '.morse/uploads';\n\nexport interface SavedUpload {\n /** Path for the `@mention`: cwd-relative when it lives under the cwd. */\n path: string;\n /** The name the file was actually stored under (deduplicated). */\n name: string;\n bytes: number;\n}\n\n/** A rejected upload; the message is shown to the user, so it says what to fix. */\nexport class UploadError extends Error {}\n\nexport async function saveUpload(\n cwd: string,\n request: Record<string, unknown>,\n uploadDir: string = DEFAULT_UPLOAD_DIR,\n): Promise<SavedUpload> {\n const name = safeFileName(request.name);\n const data = request.data;\n if (typeof data !== 'string' || data.length === 0) {\n throw new UploadError('That upload carried no data.');\n }\n const bytes = Buffer.from(data, 'base64');\n if (bytes.length === 0) {\n throw new UploadError('That upload carried no data.');\n }\n if (bytes.length > MAX_UPLOAD_BYTES) {\n throw new UploadError(\n `That file is ${formatMb(bytes.length)} MB; the limit is ${formatMb(MAX_UPLOAD_BYTES)} MB.`,\n );\n }\n\n const directory = isAbsolute(uploadDir) ? uploadDir : join(cwd, uploadDir);\n await mkdir(directory, { recursive: true });\n const target = uniquePath(directory, name);\n await writeFile(target, bytes);\n\n return { path: mentionPath(cwd, target), name: basename(target), bytes: bytes.length };\n}\n\n/**\n * Keeps only the last path segment and strips what a filesystem rejects, so a\n * hostile client cannot escape the inbox with `../../etc/passwd`.\n */\nexport function safeFileName(raw: unknown): string {\n const value = typeof raw === 'string' ? raw : '';\n const base = basename(value.replace(/\\\\/g, '/')).trim();\n const cleaned = base\n .replace(/[\\u0000-\\u001f<>:\"|?*]/g, '_')\n .replace(/^\\.+/, '')\n .trim();\n if (cleaned.length === 0) {\n throw new UploadError('That upload had no usable file name.');\n }\n return cleaned.slice(0, 120);\n}\n\n/** `report.pdf` -> `report-1.pdf` when the first name is taken. */\nfunction uniquePath(directory: string, name: string): string {\n const dot = name.lastIndexOf('.');\n const stem = dot > 0 ? name.slice(0, dot) : name;\n const extension = dot > 0 ? name.slice(dot) : '';\n let candidate = join(directory, name);\n let suffix = 1;\n while (existsSync(candidate)) {\n candidate = join(directory, `${stem}-${suffix}${extension}`);\n suffix += 1;\n }\n return candidate;\n}\n\nfunction mentionPath(cwd: string, target: string): string {\n const rel = relative(cwd, target);\n const posix = rel.split(sep).join('/');\n // An absolute `MORSE_UPLOAD_DIR` may point outside the cwd; then the mention\n // has to be absolute, because there is no relative form that resolves.\n return posix.length === 0 || posix.startsWith('..') || isAbsolute(rel) ? target : posix;\n}\n\nfunction formatMb(bytes: number): string {\n return (bytes / (1024 * 1024)).toFixed(1).replace(/\\.0$/, '');\n}\n", "import { readdir, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path';\n\n/**\n * Heavy or generated directories a project picker never wants to step into.\n * Mirrors the file picker's skip list so both hosts hide the same noise.\n */\nconst SKIPPED_DIRECTORIES = new Set([\n 'node_modules',\n 'dist',\n 'out',\n 'build',\n 'coverage',\n 'target',\n 'vendor',\n 'venv',\n '__pycache__',\n '.angular',\n '.next',\n '.nuxt',\n '.turbo',\n '.cache',\n]);\n\n/** One subdirectory row of the browser. */\nexport interface DirectoryEntry {\n name: string;\n path: string;\n}\n\n/** What the frontend's folder modal needs to render one directory. */\nexport interface DirectoryListing {\n /** Absolute path of the directory being shown. */\n path: string;\n /** Absolute parent, absent when `path` is the filesystem root. */\n parent?: string;\n directories: DirectoryEntry[];\n /** True when `path` itself is a git worktree \u2014 a strong \"this is a project\" hint. */\n isGitRepo: boolean;\n /** Shortcuts the modal offers: the allow-list, the home dir, the default workspace. */\n roots: DirectoryEntry[];\n}\n\nexport interface BrowseOptions {\n /** Directories the operator allowed the agent to work in (`MORSE_PROJECTS`). */\n roots?: readonly string[];\n /** Where to start when the client does not name a path. */\n defaultPath: string;\n}\n\n/**\n * Lists the subdirectories of one folder on the host's filesystem.\n *\n * The browser host serves a machine, not a webview with a workspace folder, so\n * the user has to be able to point the agent at any directory. Browsing is\n * read-only and never creates anything; whether a session may actually run in\n * the chosen folder stays `ProjectPolicy`'s call (the host decorates the result\n * with `canOpen`).\n */\nexport async function browseDirectory(\n path: string | undefined,\n options: BrowseOptions,\n): Promise<DirectoryListing> {\n const requested = path?.trim();\n const start = resolve(requested && requested.length > 0 ? requested : options.defaultPath);\n\n // `resolve` already made it absolute; this guard is for the caller's sake when\n // a relative path slips through (it would otherwise be resolved against the\n // server's cwd, which is rarely what the user typed).\n if (requested && !isAbsolute(requested)) {\n throw new Error(`A folder path must be absolute: ${requested}`);\n }\n\n const entries = await readdir(start, { withFileTypes: true });\n const directories = entries\n .filter((entry) => entry.isDirectory())\n .filter((entry) => !shouldSkip(entry.name))\n .map((entry) => ({ name: entry.name, path: join(start, entry.name) }))\n .sort((left, right) => left.name.localeCompare(right.name));\n\n const parent = dirname(start);\n return {\n path: start,\n parent: parent === start ? undefined : parent,\n directories,\n isGitRepo: await isGitRepo(start),\n roots: shortcutRoots(options),\n };\n}\n\n/** Hidden folders are noise when the point is to pick a project. */\nfunction shouldSkip(name: string): boolean {\n return name.startsWith('.') || SKIPPED_DIRECTORIES.has(name);\n}\n\nasync function isGitRepo(path: string): Promise<boolean> {\n try {\n await stat(join(path, '.git'));\n return true;\n } catch {\n return false;\n }\n}\n\nfunction shortcutRoots(options: BrowseOptions): DirectoryEntry[] {\n const seen = new Set<string>();\n const roots: DirectoryEntry[] = [];\n for (const candidate of [...(options.roots ?? []), homedir(), options.defaultPath]) {\n if (!candidate || !isAbsolute(candidate)) {\n continue;\n }\n const path = resolve(candidate);\n if (seen.has(path)) {\n continue;\n }\n seen.add(path);\n roots.push({ name: basename(path) || path, path });\n }\n return roots;\n}\n", "import { open, stat } from 'node:fs/promises';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport { UnsupportedByHostError } from '@morse/core';\n\n/**\n * How much of a file a preview reads. A 40 MB bundle in a text view is not a\n * preview; the response says it was cut short so the frontend can tell the user.\n */\nconst MAX_PREVIEW_BYTES = 512 * 1024;\n\n/** How much of a file is sniffed for NUL bytes: a file is binary or it is not near the top. */\nconst SNIFF_BYTES = 8 * 1024;\n\n/** What the frontend needs to draw a file: its text, size and whether it was cut. */\nexport interface WorkspaceFilePreview {\n path: string;\n content: string;\n size: number;\n truncated: boolean;\n /** A binary file has no useful text; `content` is empty and the UI says so. */\n binary: boolean;\n}\n\n/**\n * Reads one file for the frontend's preview tab. The path is resolved against\n * the project the client is viewing and must stay inside it: the browser host\n * never opens a path the client names outside the session's directory (which\n * `ProjectPolicy` already approved when the session was opened).\n */\nexport async function readWorkspaceFile(\n cwd: string,\n requested: string,\n): Promise<WorkspaceFilePreview> {\n const target = resolveWithin(cwd, requested);\n const info = await stat(target).catch(() => undefined);\n if (info === undefined) {\n throw new UnsupportedByHostError(`No such file: ${requested}`);\n }\n if (info.isDirectory()) {\n throw new UnsupportedByHostError(`${requested} is a directory, not a file.`);\n }\n const handle = await open(target, 'r');\n try {\n const length = Math.min(info.size, MAX_PREVIEW_BYTES);\n const buffer = Buffer.alloc(length);\n const { bytesRead } = await handle.read(buffer, 0, length, 0);\n const slice = buffer.subarray(0, bytesRead);\n const binary = slice.subarray(0, Math.min(bytesRead, SNIFF_BYTES)).includes(0);\n return {\n path: toPosix(relative(cwd, target)),\n content: binary ? '' : slice.toString('utf8'),\n size: info.size,\n truncated: info.size > bytesRead,\n binary,\n };\n } finally {\n await handle.close();\n }\n}\n\n/**\n * Resolves a client path under `cwd` and refuses anything that escapes it. An\n * absolute path is a client naming a file the session does not own, so it is\n * rejected rather than silently reinterpreted.\n */\nexport function resolveWithin(cwd: string, requested: string): string {\n if (isAbsolute(requested)) {\n throw new UnsupportedByHostError('A preview only opens files inside the project.');\n }\n const target = resolve(cwd, requested);\n const rel = relative(cwd, target);\n if (rel.length === 0 || rel.startsWith('..') || isAbsolute(rel)) {\n throw new UnsupportedByHostError('A preview only opens files inside the project.');\n }\n return target;\n}\n\n/** Preview paths read with `/` on every platform, the way pi prints them. */\nfunction toPosix(path: string): string {\n return path.split('\\\\').join('/');\n}\n", "import { execFile } from 'node:child_process';\nimport { relative } from 'node:path';\nimport type {\n GitBranches,\n GitCommit,\n GitCommitFiles,\n GitDiff,\n GitFileStatus,\n GitLog,\n GitMutation,\n GitStatus,\n GitSync,\n} from '@morse/protocol';\nimport { resolveWithin } from './file-store.js';\n\n/**\n * How many commits the panel reads. A history view is for orientation, not for\n * replaying a repository: past a few hundred rows the graph is unreadable and\n * the log gets slow, so the list is capped and says so.\n */\nconst DEFAULT_MAX_COMMITS = 250;\nconst HARD_MAX_COMMITS = 500;\n\n/** Field separator (US) and record separator (RS): safe inside any commit text. */\nconst FIELD = '\\u001f';\nconst RECORD = '\\u001e';\nconst PRETTY_FORMAT = ['%H', '%h', '%P', '%D', '%an', '%aI', '%s'].join(FIELD) + RECORD;\n\n/**\n * The active project's git history for the browser host's git panel. The project\n * is the viewing session's directory (already approved by `ProjectPolicy`), never\n * a path the client names. A directory that is not a repository is a normal\n * answer (`isRepo: false`), not an error \u2014 the panel says \"no repository here\".\n *\n * `--all --date-order` walks every ref so the graph shows side branches, and the\n * lanes are derived by the frontend from each commit's `parents`.\n */\nexport async function readGitLog(\n cwd: string,\n requestedMax?: number,\n requestedSkip?: number,\n): Promise<GitLog> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, commits: [] };\n }\n const max = clampMax(requestedMax);\n const skip = clampSkip(requestedSkip);\n const [root, branch, raw] = await Promise.all([\n gitText(['rev-parse', '--show-toplevel'], cwd),\n gitText(['rev-parse', '--abbrev-ref', 'HEAD'], cwd),\n gitText(\n [\n 'log',\n '--all',\n '--date-order',\n `--skip=${skip}`,\n `--max-count=${max}`,\n `--pretty=format:${PRETTY_FORMAT}`,\n ],\n cwd,\n ),\n ]);\n return {\n isRepo: true,\n root: root?.trim() || cwd,\n branch: branch?.trim() || undefined,\n commits: parseCommits(raw ?? ''),\n };\n}\n\nfunction clampMax(requested?: number): number {\n if (requested === undefined || !Number.isFinite(requested)) {\n return DEFAULT_MAX_COMMITS;\n }\n return Math.max(1, Math.min(HARD_MAX_COMMITS, Math.floor(requested)));\n}\n\n/**\n * The working tree's changed paths, for the Explorer's per-file git badges.\n * Paths are made relative to `cwd` (matching `listFiles`), so a session opened\n * in a subdirectory of the repository lines up with the listing it shows.\n */\nexport async function readGitStatus(cwd: string): Promise<GitStatus> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, files: [] };\n }\n const [prefix, raw] = await Promise.all([\n gitText(['rev-parse', '--show-prefix'], cwd),\n gitText(['status', '--porcelain=v1', '-z', '--untracked-files=all'], cwd),\n ]);\n return { isRepo: true, files: parseStatus(raw ?? '', prefix?.trim() ?? '') };\n}\n\n/**\n * Porcelain v1 with `-z`: one `XY path` record per entry, NUL-separated, and a\n * rename/copy adds the origin path as a following record.\n */\nfunction parseStatus(raw: string, root: string): GitFileStatus[] {\n const records = raw.split('\\0');\n const files: GitFileStatus[] = [];\n for (let index = 0; index < records.length; index += 1) {\n const record = records[index];\n if (!record || record.length < 4) {\n continue;\n }\n const status = record.slice(0, 2);\n // A rename/copy keeps its new path here and its origin in the next record.\n if (status[0] === 'R' || status[0] === 'C') {\n index += 1;\n }\n const full = record.slice(3);\n if (root.length > 0 && !full.startsWith(root)) {\n continue;\n }\n const path = full.slice(root.length);\n if (path.length > 0) {\n files.push({ path: toPosix(path), status });\n }\n }\n return files;\n}\n\n/**\n * One file's unified diff against HEAD, for the preview's diff modes. The path\n * is resolved inside the viewing session's directory, the same guard `readFile`\n * uses, so a client cannot name a file outside the project. An untracked file\n * has no diff against HEAD; the frontend renders its content as all-added.\n */\nexport async function readGitDiff(cwd: string, requested: string): Promise<GitDiff> {\n const rel = toPosix(relative(cwd, resolveWithin(cwd, requested)));\n const diff = await gitText(\n ['diff', '--no-color', '--no-ext-diff', '-U3', 'HEAD', '--', rel],\n cwd,\n );\n return { path: rel, diff: diff ?? '' };\n}\n\n/**\n * The paths a commit touched, for the git panel's expandable row. Paths are made\n * relative to `cwd` (`--relative`), matching `listFiles` and `readGitStatus`, so\n * a session opened in a subdirectory sees only what it can open. A commit that\n * touched nothing there is an empty list, not an error.\n */\nexport async function readCommitFiles(cwd: string, requested: string): Promise<GitCommitFiles> {\n const hash = requested.trim();\n if (!isCommitHash(hash)) {\n return { isRepo: false, hash, files: [] };\n }\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, hash, files: [] };\n }\n const raw = await gitText(\n ['show', '--name-status', '--format=', '-z', '--find-renames', '--relative', hash],\n cwd,\n );\n return { isRepo: true, hash, files: parseNameStatus(raw ?? '') };\n}\n\n/**\n * One file's diff *inside a commit* (`git show <hash> -- <path>`), for a file\n * opened from the git panel's commit row. The path is resolved inside `cwd`,\n * exactly like `readGitDiff`. A root commit shows the whole file as added.\n */\nexport async function readCommitDiff(cwd: string, hash: string, requested: string): Promise<GitDiff> {\n const rel = toPosix(relative(cwd, resolveWithin(cwd, requested)));\n if (!isCommitHash(hash.trim())) {\n return { path: rel, diff: '' };\n }\n const diff = await gitText(\n ['show', '--format=', '--no-color', '--no-ext-diff', '-U3', '--relative', hash.trim(), '--', rel],\n cwd,\n );\n return { path: rel, diff: diff ?? '' };\n}\n\n/** A revision the client may name: a hex object id, never an option or a ref. */\nfunction isCommitHash(hash: string): boolean {\n return /^[0-9a-fA-F]{4,40}$/.test(hash);\n}\n\n/**\n * `git show --name-status -z` pairs a status with its path, NUL-separated, and a\n * rename/copy carries both the old and the new path. Only the new path is kept,\n * with the status reduced to its letter so the shared badge mapping applies.\n */\nfunction parseNameStatus(raw: string): GitFileStatus[] {\n const tokens = raw.split('\\0');\n const files: GitFileStatus[] = [];\n for (let index = 0; index < tokens.length; index += 1) {\n const status = tokens[index];\n if (!status) {\n continue;\n }\n const letter = status[0] ?? '';\n const rename = letter === 'R' || letter === 'C';\n const path = tokens[index + (rename ? 2 : 1)];\n index += rename ? 2 : 1;\n if (path) {\n files.push({ path: toPosix(path), status: `${letter} ` });\n }\n }\n return files;\n}\n\n/**\n * Stages the named paths in the viewing session's repository (`git add`). Paths\n * are resolved inside `cwd` exactly like `readFile`, so a client cannot stage\n * anything outside its project. The fresh working tree comes back as the answer,\n * so the panel updates without a second `gitStatus` round trip.\n */\nexport async function stageGitPaths(cwd: string, requested: readonly string[]): Promise<GitStatus> {\n const paths = resolvePaths(cwd, requested);\n if (paths.length > 0) {\n await gitRun(['add', '--', ...paths], cwd);\n }\n return readGitStatus(cwd);\n}\n\n/**\n * Unstages the named paths (`git reset HEAD`), keeping the working-tree change:\n * a staged edit becomes an unstaged one, and an added file becomes untracked\n * again. An unborn `HEAD` (a repository with no commits) cannot be reset, so the\n * entry is removed from the index directly instead.\n */\nexport async function unstageGitPaths(cwd: string, requested: readonly string[]): Promise<GitStatus> {\n const paths = resolvePaths(cwd, requested);\n if (paths.length > 0) {\n const reset = await gitRun(['reset', '--quiet', 'HEAD', '--', ...paths], cwd);\n if (!reset) {\n await gitRun(['rm', '--cached', '--quiet', '--', ...paths], cwd);\n }\n }\n return readGitStatus(cwd);\n}\n\n/**\n * How far HEAD is from its upstream: `behind` commits to pull, `ahead` commits\n * to push. A branch with no upstream (or an unborn branch) reports zero for both,\n * so the panel still shows the pull/push controls without a distance.\n */\nexport async function readGitSync(cwd: string): Promise<GitSync> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, ahead: 0, behind: 0 };\n }\n const [branch, upstream] = await Promise.all([\n gitText(['rev-parse', '--abbrev-ref', 'HEAD'], cwd),\n gitText(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'], cwd),\n ]);\n const name = branch?.trim() || undefined;\n const tracked = upstream?.trim() || undefined;\n if (tracked === undefined) {\n return { isRepo: true, branch: name, ahead: 0, behind: 0 };\n }\n const counts = await gitText(\n ['rev-list', '--left-right', '--count', `${tracked}...HEAD`],\n cwd,\n );\n const [behind, ahead] = parseCounts(counts ?? '');\n return { isRepo: true, branch: name, upstream: tracked, ahead, behind };\n}\n\n/** Pulls the upstream (`git pull`) and reports the new distance. */\nexport async function pullGit(cwd: string): Promise<GitSync> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() === 'true') {\n await gitRun(['pull', '--no-edit'], cwd, 180_000);\n }\n return readGitSync(cwd);\n}\n\n/**\n * Pushes HEAD (`git push`) and reports the new distance. `--follow-tags` sends\n * the annotated tags that point into the pushed history, so a release tag travels\n * with the commit it names instead of waiting for a separate `git push --tags`.\n */\nexport async function pushGit(cwd: string): Promise<GitSync> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() === 'true') {\n await gitRun(['push', '--follow-tags'], cwd, 180_000);\n }\n return readGitSync(cwd);\n}\n\n/**\n * The branches the panel can switch to: the current one, the locals, the\n * remote-tracking branches (minus `origin/HEAD`, which is not a branch), and the\n * tags (checking one out detaches HEAD).\n */\nexport async function readGitBranches(cwd: string): Promise<GitBranches> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, local: [], remote: [], tags: [] };\n }\n const [current, local, remote, tags] = await Promise.all([\n gitText(['rev-parse', '--abbrev-ref', 'HEAD'], cwd),\n gitText(['for-each-ref', '--format=%(refname:short)', 'refs/heads'], cwd),\n gitText(['for-each-ref', '--format=%(refname:short)', 'refs/remotes'], cwd),\n gitText(['for-each-ref', '--format=%(refname:short)', 'refs/tags'], cwd),\n ]);\n return {\n isRepo: true,\n current: current?.trim() || undefined,\n local: nonEmptyLines(local),\n remote: nonEmptyLines(remote).filter((name) => !name.endsWith('/HEAD')),\n tags: nonEmptyLines(tags),\n };\n}\n\n/** Commits the staged changes with `message`; git's refusal is reported back. */\nexport async function commitGit(cwd: string, message: string): Promise<GitMutation> {\n const text = message.trim();\n if (text.length === 0) {\n return { ok: false, message: 'A commit needs a message.' };\n }\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { ok: false, message: 'This project is not a git repository.' };\n }\n const result = await gitExec(['commit', '-m', text], cwd, 60_000);\n if (result.ok) {\n return { ok: true };\n }\n return { ok: false, message: describeCommitFailure(result.output) };\n}\n\n/**\n * Switches branches, creating the branch first when `create` is set. The name is\n * validated as a refname, so a client cannot smuggle a git option into the\n * command line.\n */\nexport async function checkoutGit(\n cwd: string,\n branch: string,\n create: boolean,\n): Promise<GitMutation> {\n const name = branch.trim();\n if (!isBranchName(name)) {\n return { ok: false, message: 'That is not a usable branch name.' };\n }\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { ok: false, message: 'This project is not a git repository.' };\n }\n const result = await gitExec(\n create ? ['checkout', '-b', name] : ['checkout', name],\n cwd,\n 60_000,\n );\n return { ok: result.ok, message: result.ok ? undefined : gitRefusal(result.output) };\n}\n\n/** A conservative refname guard: no options, whitespace, or revision syntax. */\nfunction isBranchName(name: string): boolean {\n return (\n name.length > 0 &&\n !name.startsWith('-') &&\n !name.includes('..') &&\n !name.includes('@{') &&\n !name.endsWith('/') &&\n !/\\s/.test(name) &&\n !/[~^:?*\\[\\\\]/.test(name)\n );\n}\n\n/**\n * What to tell the reader when `git commit` refused. \"Nothing to commit\" is the\n * everyday case \u2014 the branch is clean, or the changes were never staged \u2014 and it\n * gets a plain instruction instead of git's paragraph; anything else (a failing\n * hook, a rejected signature) keeps git's own line.\n */\nfunction describeCommitFailure(output: string): string | undefined {\n if (/nothing to commit|no changes added to commit|nothing added to commit/i.test(output)) {\n return 'Nothing is staged to commit. Stage a change first.';\n }\n return gitRefusal(output);\n}\n\n/**\n * The line from a refused command a reader should see: the first line that is\n * git's own, not Node's `Command failed: \u2026` wrapper or the branch chatter\n * (`On branch main`, `Your branch is up to date with \u2026`) that always leads a\n * commit's output. Capped so a hint stays a hint.\n */\nfunction gitRefusal(output: string): string | undefined {\n const line = output\n .split('\\n')\n .map((candidate) => candidate.trim())\n .find(\n (candidate) =>\n candidate.length > 0 &&\n !candidate.startsWith('Command failed:') &&\n !/^On branch /.test(candidate) &&\n !/^Your branch /.test(candidate),\n );\n if (line === undefined) {\n return undefined;\n }\n return line.length > 200 ? `${line.slice(0, 197)}\u2026` : line;\n}\n\nfunction nonEmptyLines(output: string | undefined): string[] {\n return (output ?? '')\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n}\n\n/** `<behind>\\t<ahead>` from `git rev-list --left-right --count`; 0 when unreadable. */\nfunction parseCounts(raw: string): [number, number] {\n const [behind, ahead] = raw.trim().split(/\\s+/).map((value) => Number.parseInt(value, 10));\n return [Number.isFinite(behind) ? behind : 0, Number.isFinite(ahead) ? ahead : 0];\n}\n\n/** Paths read with `/` on every platform, the way pi prints them. */\nfunction toPosix(path: string): string {\n return path.split('\\\\').join('/');\n}\n\n/** How many commits to walk past while paging towards the root commit. */\nfunction clampSkip(requested?: number): number {\n if (requested === undefined || !Number.isFinite(requested)) {\n return 0;\n }\n return Math.max(0, Math.floor(requested));\n}\n\n/** One record per commit: `hash \u00B7 short \u00B7 parents \u00B7 refs \u00B7 author \u00B7 date \u00B7 subject`. */\nfunction parseCommits(raw: string): GitCommit[] {\n const commits: GitCommit[] = [];\n for (const record of raw.split(RECORD)) {\n const line = record.replace(/^\\n+/, '');\n if (line.trim().length === 0) {\n continue;\n }\n const fields = line.split(FIELD);\n const [hash, shortHash, parents, refs, author, date, subject] = fields;\n if (!hash || !shortHash || !date) {\n continue;\n }\n commits.push({\n hash,\n shortHash,\n parents: (parents ?? '').split(' ').filter((parent) => parent.length > 0),\n refs: parseRefs(refs ?? ''),\n author: author ?? '',\n date,\n subject: subject ?? '',\n });\n }\n return commits;\n}\n\n/**\n * `%D` prints decorations comma-separated (`HEAD -> main, tag: v1.0`). The panel\n * renders each as a chip, so split here rather than shipping one long string.\n */\nfunction parseRefs(raw: string): string[] {\n const refs = raw\n .split(',')\n .map((ref) => ref.trim())\n .filter((ref) => ref.length > 0);\n return refs;\n}\n\nfunction gitText(args: string[], cwd: string): Promise<string | undefined> {\n return new Promise((resolve) => {\n execFile(\n 'git',\n args,\n { cwd, timeout: 5_000, maxBuffer: 16 * 1024 * 1024, windowsHide: true },\n (error, stdout) => resolve(error ? undefined : stdout),\n );\n });\n}\n\n/** Resolves client paths inside `cwd` and normalises them to `/` separators. */\nfunction resolvePaths(cwd: string, requested: readonly string[]): string[] {\n return requested\n .filter((path): path is string => typeof path === 'string' && path.length > 0)\n .map((path) => toPosix(relative(cwd, resolveWithin(cwd, path))));\n}\n\n/**\n * A git mutation: true when git accepted it, false when it failed. A network\n * command (pull/push) gets a long timeout and no terminal prompt, so a missing\n * credential fails instead of hanging the host on a blocked stdin.\n */\nasync function gitRun(args: string[], cwd: string, timeout = 5_000): Promise<boolean> {\n return (await gitExec(args, cwd, timeout)).ok;\n}\n\n/**\n * Runs git and keeps both its verdict and its words: a refused command (nothing\n * staged, a failing hook, a checkout conflict) has its stderr to show the user.\n */\nfunction gitExec(\n args: string[],\n cwd: string,\n timeout = 5_000,\n): Promise<{ ok: boolean; output: string }> {\n return new Promise((resolve) => {\n execFile(\n 'git',\n args,\n {\n cwd,\n timeout,\n maxBuffer: 8 * 1024 * 1024,\n windowsHide: true,\n env: {\n ...process.env,\n GIT_TERMINAL_PROMPT: '0',\n GIT_EDITOR: 'true',\n GIT_MERGE_AUTOEDIT: 'no',\n },\n },\n (error, stdout, stderr) => {\n if (error === null) {\n resolve({ ok: true, output: stdout.trim() });\n return;\n }\n // git writes a refusal to stderr *or* stdout \u2014 \"nothing to commit\" is on\n // stdout \u2014 while Node's `error.message` is only the `Command failed: git \u2026`\n // wrapper around that same output. Prefer git's own words and keep the\n // wrapper only for a failure with no output at all (a spawn error).\n const output = [stderr.trim(), stdout.trim()]\n .filter((part) => part.length > 0)\n .join('\\n');\n resolve({ ok: false, output: output.length > 0 ? output : error.message.trim() });\n },\n );\n });\n}\n", "import { execFile } from 'node:child_process';\nimport { readdir } from 'node:fs/promises';\nimport type { Dirent } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { MorseLogger } from '@morse/core';\n\n/**\n * Directories a file picker never wants, and which make a tree walk slow enough\n * to feel broken. Mirrors the VS Code host's list on purpose: a browser host and\n * a webview host should offer the same files for the same project.\n */\nconst SKIPPED_DIRECTORIES = new Set([\n 'node_modules',\n 'dist',\n 'out',\n 'build',\n 'coverage',\n 'target',\n 'vendor',\n 'venv',\n '__pycache__',\n '.angular',\n '.next',\n '.nuxt',\n '.turbo',\n '.cache',\n]);\n\nconst MAX_INDEXED_FILES = 5_000;\nconst INDEX_TTL_MS = 60_000;\n\ninterface FileIndex {\n files: string[];\n /** How the list was produced, for the log line. */\n source: 'git' | 'walk';\n at: number;\n}\n\n/** One index per directory: a global browser host can switch projects. */\nconst indexes = new Map<string, FileIndex>();\nconst inFlight = new Map<string, Promise<FileIndex>>();\n\n/**\n * Entries under `cwd`, relative to it, for the frontend's `@mention` picker:\n * files *and* directories, the latter with a trailing `/` \u2014 the vocabulary pi's\n * own completion offers (fd with `--type f --type d`).\n *\n * The VS Code host lists its workspace folders; the browser host has no\n * workspace folder, so it lists the active session's directory \u2014 the cwd pi\n * resolves a mention against. Git first (`ls-files` honours `.gitignore` and is\n * far faster than a walk), then a capped walk with an explicit skip list.\n */\nexport async function workspaceFiles(\n cwd: string,\n logger: MorseLogger,\n options: { fresh?: boolean } = {},\n): Promise<string[]> {\n const { files, source } = await cachedIndex(cwd, options.fresh === true);\n logger.info(`File picker: ${files.length} entries via ${source} in ${cwd}`);\n return files;\n}\n\nasync function cachedIndex(cwd: string, fresh: boolean): Promise<FileIndex> {\n const now = Date.now();\n const cached = indexes.get(cwd);\n if (!fresh && cached && now - cached.at < INDEX_TTL_MS) {\n return cached;\n }\n const pending = inFlight.get(cwd);\n if (pending) {\n return pending;\n }\n const task = indexWorkspace(cwd)\n .then((index) => {\n const entry: FileIndex = { ...index, at: Date.now() };\n indexes.set(cwd, entry);\n return entry;\n })\n .finally(() => {\n inFlight.delete(cwd);\n });\n inFlight.set(cwd, task);\n return task;\n}\n\nasync function indexWorkspace(cwd: string): Promise<Omit<FileIndex, 'at'>> {\n const fromGit = await gitFileList(cwd);\n if (fromGit) {\n return { files: indexEntries(fromGit), source: 'git' };\n }\n const fromWalk: string[] = [];\n await walk(cwd, cwd, fromWalk);\n return { files: indexEntries(fromWalk), source: 'walk' };\n}\n\n/**\n * Turns a list of file paths into the picker's entries: the files plus every\n * directory they imply, the latter marked with a trailing `/`. Deriving the\n * directories from the files keeps the git and walk listings identical and never\n * offers a folder the agent could not reach anyway. The frontend ranks a\n * directory first and lets the user drill into it (`@docs/` lists its contents).\n */\nfunction indexEntries(paths: string[]): string[] {\n const entries = new Set(dedupe(paths).slice(0, MAX_INDEXED_FILES));\n for (const path of [...entries]) {\n const segments = path.split('/');\n for (let depth = 1; depth < segments.length; depth += 1) {\n entries.add(`${segments.slice(0, depth).join('/')}/`);\n }\n }\n return [...entries].sort();\n}\n\nfunction dedupe(paths: string[]): string[] {\n return [...new Set(paths)];\n}\n\n/**\n * `git ls-files` reports paths relative to the repository root; a directory\n * below that root is handled by stripping `--show-prefix` and dropping entries\n * outside it, so the result is always cwd-relative. `--full-name` is what makes\n * that true when the cwd is a subdirectory: without it git prints cwd-relative\n * paths and every entry would be filtered out.\n */\nasync function gitFileList(cwd: string): Promise<string[] | undefined> {\n try {\n const [prefix, listed] = await Promise.all([\n runGit(['rev-parse', '--show-prefix'], cwd),\n runGit(['ls-files', '-z', '--cached', '--others', '--exclude-standard', '--full-name'], cwd),\n ]);\n if (listed === undefined) {\n return undefined;\n }\n const root = prefix?.trim() ?? '';\n return listed\n .split('\\0')\n .filter((path) => path.length > 0)\n .filter((path) => root.length === 0 || path.startsWith(root))\n .map((path) => path.slice(root.length))\n .filter((path) => path.length > 0 && !isInSkippedDirectory(path));\n } catch {\n return undefined;\n }\n}\n\nfunction runGit(args: string[], cwd: string): Promise<string | undefined> {\n return new Promise((resolve) => {\n execFile(\n 'git',\n args,\n { cwd, timeout: 5_000, maxBuffer: 8 * 1024 * 1024, windowsHide: true },\n (error, stdout) => resolve(error ? undefined : stdout),\n );\n });\n}\n\n/**\n * Only *folders* are skipped by the name rules: a dotfile such as `.gitignore`\n * is perfectly pickable, while a dot-folder (`.git`, `.github`) is not.\n */\nfunction isInSkippedDirectory(path: string): boolean {\n const folders = path.split('/').slice(0, -1);\n return folders.some((folder) => SKIPPED_DIRECTORIES.has(folder) || folder.startsWith('.'));\n}\n\nasync function walk(root: string, directory: string, files: string[]): Promise<void> {\n if (files.length >= MAX_INDEXED_FILES) {\n return;\n }\n let entries: Dirent[];\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch {\n // An unreadable directory is not a reason to fail the whole listing.\n return;\n }\n for (const entry of entries) {\n if (files.length >= MAX_INDEXED_FILES) {\n return;\n }\n const full = join(directory, entry.name);\n if (entry.isDirectory()) {\n // Hidden directories (`.git`, `.cache`, `.venv`, ...) are never picked.\n if (SKIPPED_DIRECTORIES.has(entry.name) || entry.name.startsWith('.')) {\n continue;\n }\n await walk(root, full, files);\n continue;\n }\n if (entry.isFile() || entry.isSymbolicLink()) {\n files.push(toPosix(relative(root, full)));\n }\n }\n}\n\n/** Mention paths read with `/` on every platform, the way pi prints them. */\nfunction toPosix(path: string): string {\n return path.split('\\\\').join('/');\n}\n", "import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { UnsupportedByHostError } from '@morse/core';\nimport type { WorkbenchSnapshot } from '@morse/protocol';\n\n/** The reader's shell layout: tabs, panel and terminals. Small, rewritten on a change. */\nconst LAYOUT_FILE = 'workbench.json';\n/**\n * The half-written prompts, keyed by tab. Larger than the layout because a draft\n * can carry inline image attachments, and rewritten as the reader types.\n */\nconst DRAFTS_FILE = 'drafts.json';\n\n/**\n * A layout is a handful of tab names and shell ids, so anything past a quarter\n * of a megabyte is not a layout \u2014 it is a client trying to use the host as a\n * scratch disk. The cap keeps the file small and the parse cheap.\n */\nconst LAYOUT_MAX_BYTES = 256 * 1024;\n/**\n * A draft carries text, pins and (possibly) base64 images, so it is allowed to\n * be much larger. The cap is still bounded: a client that keeps pushing past this\n * is not writing drafts, and an unbounded file would eventually fail to parse.\n */\nconst DRAFTS_MAX_BYTES = 32 * 1024 * 1024;\n\n/**\n * The browser host's persisted shell state under `<MORSE_HOME>`: the layout the\n * reader left (`workbench.json`) and the prompts they were typing (`drafts.json`),\n * so opening the page again lands them on the tab they left, with the words and\n * attachments still in the composer.\n *\n * The frontend owns the inner shape (`snapshot.data`); the host only guards the\n * envelope's version and size, so a newer frontend can add a field without a\n * server change. A missing, unreadable or stale file is not an error: it just\n * means \"nothing to restore\", and the frontend starts clean.\n */\nexport function readWorkbench(dataDir: string): WorkbenchSnapshot | undefined {\n return readSlot(join(dataDir, LAYOUT_FILE), LAYOUT_MAX_BYTES);\n}\n\n/**\n * Writes the layout the frontend just handed back. The write is atomic (a temp\n * file beside it, then a rename), so a crash mid-write leaves the previous\n * layout intact instead of a truncated one. Returns false when the envelope is\n * unusable or the directory is not writable; a failed save is not fatal.\n */\nexport function saveWorkbench(dataDir: string, snapshot: unknown): boolean {\n return saveSlot(dataDir, LAYOUT_FILE, snapshot, LAYOUT_MAX_BYTES);\n}\n\n/** The half-written prompts, per tab, as the frontend last saved them. */\nexport function readDrafts(dataDir: string): WorkbenchSnapshot | undefined {\n return readSlot(join(dataDir, DRAFTS_FILE), DRAFTS_MAX_BYTES);\n}\n\n/** Writes the drafts back, with the same atomic write and envelope guard. */\nexport function saveDrafts(dataDir: string, snapshot: unknown): boolean {\n return saveSlot(dataDir, DRAFTS_FILE, snapshot, DRAFTS_MAX_BYTES);\n}\n\nfunction readSlot(path: string, maxBytes: number): WorkbenchSnapshot | undefined {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return undefined;\n }\n if (raw.length > maxBytes) {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n return asSnapshot(parsed);\n}\n\nfunction saveSlot(dataDir: string, file: string, snapshot: unknown, maxBytes: number): boolean {\n const valid = asSnapshot(snapshot);\n if (valid === undefined) {\n throw new UnsupportedByHostError(`Saving ${file} needs a { version, data } snapshot.`);\n }\n const serialized = JSON.stringify(valid);\n if (Buffer.byteLength(serialized, 'utf8') > maxBytes) {\n throw new UnsupportedByHostError(`That ${file} snapshot is too large to save.`);\n }\n const target = join(dataDir, file);\n const temp = `${target}.${process.pid}.tmp`;\n try {\n mkdirSync(dataDir, { recursive: true });\n writeFileSync(temp, serialized);\n renameSync(temp, target);\n return true;\n } catch {\n // Some filesystems refuse a rename over an existing file (a Windows mount).\n try {\n writeFileSync(target, serialized);\n return true;\n } catch {\n rmSync(temp, { force: true });\n return false;\n }\n }\n}\n\n/** The envelope, with anything that is not a `{ version, data }` object dropped. */\nfunction asSnapshot(value: unknown): WorkbenchSnapshot | undefined {\n if (typeof value !== 'object' || value === null) {\n return undefined;\n }\n const candidate = value as Record<string, unknown>;\n const version = candidate['version'];\n if (typeof version !== 'number' || !Number.isInteger(version) || version < 1) {\n return undefined;\n }\n return { version, data: candidate['data'] };\n}\n", "import { basename, dirname, isAbsolute, join } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { fileURLToPath } from 'node:url';\nimport type { WorkspaceRef } from '@morse/core';\nimport { FRONTEND_MANIFEST_FILE, frontendIdentity, parseFrontendManifest } from '@morse/protocol';\nimport type { FrontendIdentity } from '@morse/protocol';\nimport { DEFAULT_UPLOAD_DIR } from '../internal/uploads/upload-store.js';\n\nexport interface MorseServerConfig {\n host: string;\n port: number;\n /** Directory holding the built frontend (index.html + webview.manifest.json). */\n uiDir: string;\n workspace: WorkspaceRef;\n piPath?: string;\n nodeEntryPath?: string;\n sessionDir?: string;\n noSession: boolean;\n requestTimeoutMs: number;\n /** How many `pi` processes stay alive at once (least recently used first). */\n hotSessions: number;\n /** Roots the agent may work in; empty means \"any absolute path\". */\n projects: string[];\n /** Where browser uploads land: relative to a session cwd, or absolute. */\n uploadDir: string;\n /**\n * The Morse data directory (`MORSE_HOME`, default `~/.morse`): where this host\n * keeps state that outlives a browser tab \u2014 the shell layout the reader left\n * behind. It is the same directory the `morse` CLI writes its state and logs to.\n */\n dataDir: string;\n /**\n * How long a detached shell keeps running before the host reclaims it. A\n * terminal survives a page reload, so the process needs its own expiry: only\n * an explicit close ends one sooner. `0` disables the timeout.\n */\n terminalIdleMs: number;\n /**\n * Whether the frontend may ask the registry for the latest release. On unless\n * `MORSE_UPDATE_CHECK=0`; an air-gapped host turns it off rather than letting\n * the panel try and fail.\n */\n updateCheck: boolean;\n /**\n * Opaque token the `morse` CLI mints for each daemon it spawns. Relayed by\n * `/api/health`, it lets the CLI prove which process is answering instead of\n * trusting a pid that the OS may have recycled to an unrelated process.\n * Undefined when the server is started without the CLI.\n */\n instance?: string;\n /**\n * The bundle this host serves, as its own manifest declares it. Relayed to\n * clients in `host/ready`, so a frontend can name itself (the About dialog) and\n * the host log says which build is talking. Undefined when the directory has no\n * usable manifest \u2014 a bare dev directory, say.\n */\n frontend?: FrontendIdentity;\n}\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): MorseServerConfig {\n const workspacePath = resolveWorkspacePath(env);\n const configuredUiDir = env.MORSE_UI_DIR;\n // ESM has no __dirname. In the monorepo this file lives in\n // <repo>/packages/server/dist/app, so the Angular build sits three levels up;\n // in the published bundle the frontend is copied next to the bundled server\n // (<pkg>/dist/ui). Pick whichever actually holds an index.html.\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const bundledUiDir = join(moduleDir, 'ui');\n const monorepoUiDir = join(moduleDir, '..', '..', '..', 'ui-angular', 'dist');\n const defaultUiDir = existsSync(join(bundledUiDir, 'index.html'))\n ? bundledUiDir\n : monorepoUiDir;\n const defaultHost = '127.0.0.1';\n const uiDir = configuredUiDir\n ? isAbsolute(configuredUiDir)\n ? configuredUiDir\n : join(process.cwd(), configuredUiDir)\n : defaultUiDir;\n\n return {\n host: env.MORSE_HOST ?? defaultHost,\n port: parsePositiveInt(env.MORSE_PORT, 4399),\n uiDir,\n frontend: readFrontendIdentity(uiDir),\n workspace: { cwd: workspacePath, name: basename(workspacePath) || workspacePath },\n piPath: env.MORSE_PI_PATH,\n nodeEntryPath: env.MORSE_PI_ENTRY,\n sessionDir: env.MORSE_SESSION_DIR,\n noSession: env.MORSE_NO_SESSION === '1',\n requestTimeoutMs: parsePositiveInt(env.MORSE_REQUEST_TIMEOUT_MS, 30_000),\n hotSessions: parsePositiveInt(env.MORSE_HOT_SESSIONS, 4),\n projects: parseList(env.MORSE_PROJECTS),\n uploadDir: env.MORSE_UPLOAD_DIR?.trim() || DEFAULT_UPLOAD_DIR,\n dataDir: resolveDataDir(env),\n terminalIdleMs: parsePositiveInt(env.MORSE_TERMINAL_IDLE_MS, 30 * 60_000),\n updateCheck: !isOff(env.MORSE_UPDATE_CHECK),\n instance: env.MORSE_INSTANCE?.trim() || undefined,\n };\n}\n\n/**\n * Which frontend this host is serving, read from the directory it will serve. The\n * manifest is the frontend's own statement about itself; the host only relays it,\n * exactly like the VS Code host relays the same file.\n */\nfunction readFrontendIdentity(uiDir: string): FrontendIdentity | undefined {\n try {\n return frontendIdentity(\n parseFrontendManifest(readFileSync(join(uiDir, FRONTEND_MANIFEST_FILE), 'utf8')),\n );\n } catch {\n return undefined;\n }\n}\n\n/** `MORSE_HOME` wins, else the same `~/.morse` the CLI uses. */\nfunction resolveDataDir(env: NodeJS.ProcessEnv): string {\n const explicit = env.MORSE_HOME?.trim();\n return explicit && explicit.length > 0 ? explicit : join(homedir(), '.morse');\n}\n\nfunction parsePositiveInt(value: string | undefined, fallback: number): number {\n if (!value) {\n return fallback;\n }\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;\n}\n\n/** `MORSE_UPDATE_CHECK=0` (or `false`/`off`) turns the release check off. */\nfunction isOff(value: string | undefined): boolean {\n const normalized = value?.trim().toLowerCase();\n return normalized === '0' || normalized === 'false' || normalized === 'off';\n}\n\n/** `MORSE_PROJECTS=/a:/b` or `MORSE_PROJECTS=/a,/b` -> ['/a', '/b']. */\nfunction parseList(value: string | undefined): string[] {\n if (!value) {\n return [];\n }\n return value\n .split(/[:,]/)\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n}\n\n/**\n * Where the agent runs. `MORSE_WORKSPACE` wins; otherwise use the current\n * directory, except when that is a package inside a workspace monorepo \u2014\n * `npm run -w @morse/server` sets the cwd to `packages/server`, which is\n * almost never the project the user wants the agent to work on.\n */\nfunction resolveWorkspacePath(env: NodeJS.ProcessEnv): string {\n const explicit = env.MORSE_WORKSPACE;\n if (explicit && explicit.trim().length > 0) {\n return explicit;\n }\n\n const start = process.cwd();\n let directory = start;\n for (;;) {\n const manifest = join(directory, 'package.json');\n if (existsSync(manifest) && declaresWorkspaces(manifest)) {\n return directory;\n }\n const parent = dirname(directory);\n if (parent === directory) {\n return start;\n }\n directory = parent;\n }\n}\n\nfunction declaresWorkspaces(manifestPath: string): boolean {\n try {\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as { workspaces?: unknown };\n return manifest.workspaces !== undefined;\n } catch {\n return false;\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["Logger", "resolve", "isDefined", "homedir", "join", "join", "homedir", "spawn", "readdir", "readFile", "stat", "join", "resolve", "resolve", "join", "stat", "readFile", "readdir", "toThinkingLevel", "toUsage", "toInteractionRequest", "toInteractionRequest", "readFile", "join", "readFile", "join", "isAbsolute", "resolve", "sep", "Inject", "join", "join", "Inject", "Inject", "Injectable", "Injectable", "Inject", "Inject", "Inject", "Injectable", "basename", "isAbsolute", "join", "sep", "readdir", "stat", "homedir", "basename", "isAbsolute", "join", "resolve", "open", "stat", "isAbsolute", "relative", "resolve", "stat", "open", "relative", "isAbsolute", "resolve", "relative", "toPosix", "relative", "resolve", "execFile", "readdir", "join", "relative", "SKIPPED_DIRECTORIES", "resolve", "toPosix", "mkdirSync", "readFileSync", "renameSync", "rmSync", "writeFileSync", "join", "join", "readFileSync", "mkdirSync", "writeFileSync", "renameSync", "rmSync", "Injectable", "Inject", "Inject", "basename", "dirname", "isAbsolute", "join", "existsSync", "readFileSync", "homedir", "dirname", "join", "existsSync", "isAbsolute", "basename", "readFileSync", "homedir", "Logger"]
|
|
3
|
+
"sources": ["../../server/src/main.ts", "../../protocol/src/wire.ts", "../../protocol/src/version.ts", "../../protocol/src/frontend-manifest.ts", "../../server/src/app.module.ts", "../../adapter-pi-rpc/src/pi-rpc-agent.ts", "../../core/src/domain.ts", "../../core/src/errors.ts", "../../core/src/session/service.ts", "../../core/src/chat/service.ts", "../../adapter-pi-rpc/src/internal/rpc-types.ts", "../../adapter-pi-rpc/src/event-mapping.ts", "../../adapter-pi-rpc/src/internal/rpc-client.ts", "../../adapter-pi-rpc/src/internal/spawn-env.ts", "../../adapter-pi-rpc/src/internal/jsonl-framer.ts", "../../adapter-pi-rpc/src/internal/resolve-pi.ts", "../../adapter-pi-rpc/src/pi-rpc-agent-factory.ts", "../../adapter-pi-rpc/src/pi-mcp.ts", "../../adapter-pi-rpc/src/internal/mcp-config.ts", "../../adapter-pi-rpc/src/pi-rpc-session-catalog.ts", "../../adapter-pi-rpc/src/create-pi-rpc-adapter.ts", "../../host-runtime/src/terminal-buffer.ts", "../../host-runtime/src/transcript-projector.ts", "../../host-runtime/src/transcript-store.ts", "../../host-runtime/src/view-state.ts", "../../host-runtime/src/session-controller.ts", "../../server/src/internal/http/health.controller.ts", "../../server/src/app/tokens.ts", "../../server/src/internal/logging/nest-logger.ts", "../../server/src/internal/projects/project-policy.ts", "../../server/src/internal/terminal/terminal.service.ts", "../../server/src/internal/terminal/terminal-log.ts", "../../server/src/internal/registry.ts", "../../server/src/internal/ws/morse.gateway.ts", "../../server/src/internal/ws/session-factory.service.ts", "../../server/src/internal/uploads/upload-store.ts", "../../server/src/internal/workspace/directory-browser.ts", "../../server/src/internal/workspace/file-store.ts", "../../server/src/internal/workspace/git-log.ts", "../../server/src/internal/workspace/workspace-index.ts", "../../server/src/internal/workspace/workbench-store.ts", "../../server/src/app/config.ts"],
|
|
4
|
+
"sourcesContent": ["import 'reflect-metadata';\nimport { Logger } from '@nestjs/common';\nimport { NestFactory } from '@nestjs/core';\nimport type { NestExpressApplication } from '@nestjs/platform-express';\nimport { WsAdapter } from '@nestjs/platform-ws';\nimport { DEFAULT_WS_PATH } from '@morse/protocol';\nimport { AppModule } from './app.module.js';\nimport { MORSE_CONFIG } from './app/tokens.js';\nimport type { MorseServerConfig } from './app/config.js';\n\n/**\n * Composition root entry point. Morse has two of these \u2014 this one and the VS\n * Code extension \u2014 and both wire the same core + adapter packages.\n */\nasync function bootstrap(): Promise<void> {\n const app = await NestFactory.create<NestExpressApplication>(AppModule);\n const config = app.get<MorseServerConfig>(MORSE_CONFIG);\n\n app.useWebSocketAdapter(new WsAdapter(app));\n // Serves the built Angular frontend. No SPA fallback is registered, so\n // /api/* and /ws keep working.\n app.useStaticAssets(config.uiDir);\n app.enableCors({ origin: true });\n // Release the port promptly and dispose agent sessions on SIGINT/SIGTERM.\n // Without this, `node --watch` restarts race with the old process and fail\n // with EADDRINUSE, and spawned pi processes are leaked.\n app.enableShutdownHooks();\n\n await app.listen(config.port, config.host);\n\n const logger = new Logger('morse');\n logger.log(`Morse server listening on http://${config.host}:${config.port}`);\n logger.log(`WebSocket endpoint: ws://${config.host}:${config.port}${DEFAULT_WS_PATH}`);\n logger.log(`Frontend served from ${config.uiDir}`);\n logger.log(`Workspace: ${config.workspace.cwd} (override with MORSE_WORKSPACE)`);\n}\n\nvoid bootstrap();\n", "import type {\n ChatPin,\n ComposerSeed,\n FrontendIdentity,\n HostCapabilities,\n InteractionRequest,\n InteractionResponse,\n NoticeLevel,\n ProjectSummary,\n PromptImage,\n SessionActivity,\n SessionSummary,\n SessionViewState,\n ThinkingLevel,\n TranscriptItem,\n} from './dto.js';\n\nexport type PromptMode = 'new' | 'steer' | 'followUp';\n\nexport type HostCommand =\n | 'listFiles'\n | 'readFile'\n | 'listDirectories'\n | 'uploadFile'\n | 'gitLog'\n | 'gitStatus'\n | 'gitDiff'\n /** The paths a single commit touched, for the graph's expandable row. */\n | 'gitCommitFiles'\n /** One file's unified diff inside a commit (not against HEAD). */\n | 'gitCommitDiff'\n /** How far HEAD is from its upstream, for the pull/push controls. */\n | 'gitSync'\n /** Pull the upstream into the working tree. */\n | 'gitPull'\n /** Push HEAD to its upstream. */\n | 'gitPush'\n /** Commit the staged changes with a message. */\n | 'gitCommit'\n /** The branches the panel can switch to. */\n | 'gitBranches'\n /** Switch to a branch, optionally creating it first. */\n | 'gitCheckout'\n /**\n * pi's MCP servers for the viewing session's directory: their connection\n * state, tools and configuration source (`pi mcp list --json`).\n */\n | 'mcpStatus'\n /** Add or replace an MCP server in the global or the project `mcp.json`. */\n | 'mcpAdd'\n /** Remove an MCP server from whichever `mcp.json` defines it. */\n | 'mcpRemove'\n /** Turn an MCP server on or off in the `mcp.json` that defines it. */\n | 'mcpSetEnabled'\n /** Stage the named paths in the viewing session's repository. */\n | 'gitStage'\n /** Unstage the named paths (keep the working-tree change). */\n | 'gitUnstage'\n /** The shell layout the reader left behind (`~/.morse/workbench.json`). */\n | 'readWorkbench'\n /** Writes the shell layout back, so the next visit opens the same tabs. */\n | 'saveWorkbench'\n /**\n * The half-written prompts the reader left behind, per tab (`~/.morse/drafts.json`).\n * Separate from the layout because a draft can carry inline images and is much\n * larger, and because it is rewritten as the reader types.\n */\n | 'readDrafts'\n | 'saveDrafts'\n | 'openSettings'\n | 'showOutput'\n | 'copyToClipboard'\n | 'insertIntoEditor'\n | 'revealFile'\n | 'getEditorContext'\n /** Ask the host to confirm a destructive action in its own dialog. */\n | 'confirmDeleteSession'\n /**\n * Show a notification of the host's own (a finished run the reader stepped\n * away from). Only offered where `capabilities.notify` is set.\n */\n | 'notify';\n\n/** Host -> frontend. */\nexport type HostToClientMessage =\n | {\n type: 'host/ready';\n payload: {\n protocolVersion: number;\n capabilities: HostCapabilities;\n state: SessionViewState;\n frontend?: FrontendIdentity;\n };\n }\n | { type: 'session/state'; payload: SessionViewState }\n | { type: 'project/list'; payload: { projects: ProjectSummary[] } }\n /** Every live session, so the frontend can mark several as working at once. */\n | { type: 'session/activity'; payload: { sessions: SessionActivity[] } }\n | { type: 'transcript/append'; payload: TranscriptItem }\n | { type: 'transcript/update'; payload: TranscriptItem }\n | { type: 'transcript/delta'; payload: { id: string; text?: string; thinking?: string } }\n | { type: 'transcript/replace'; payload: { items: TranscriptItem[] } }\n /** A page of older history, inserted in front of what the client already has. */\n | { type: 'transcript/prepend'; payload: { items: TranscriptItem[] } }\n | { type: 'session/list'; payload: { sessions: SessionSummary[] } }\n | { type: 'interaction/request'; payload: InteractionRequest }\n | { type: 'interaction/dismiss'; payload: { requestId: string } }\n | { type: 'notice'; payload: { level: NoticeLevel; text: string; at: number } }\n /**\n * The host pinned the editor selection (the title-bar attach command). The\n * frontend owns the chip; the prompt text itself never changes behind the\n * user's back.\n */\n | { type: 'context/selection'; payload: { path: string; startLine?: number; endLine?: number } }\n /**\n * What the user has selected in the editor right now, streamed as they drag.\n * This is a preview, not a pin: the frontend shows it as a live chip that\n * keeps updating (dragging and even multi-selection) until the user clicks\n * it to lock. A payload without `startLine` means the selection is gone; the\n * frontend hides the preview without touching the locked pins.\n */\n | { type: 'context/selectionLive'; payload: { path: string; startLine?: number; endLine?: number } }\n /**\n * Hands a forked prompt back to the composer: the message the branch\n * re-opened, with its attachments. Sent after a successful `chat/fork` \u2014 a\n * fork that pi cancelled leaves the composer alone.\n */\n | { type: 'composer/seed'; payload: ComposerSeed }\n /** Answer to `host/command` when the frontend asked for a value. */\n | {\n type: 'host/command/result';\n payload: { requestId: string; ok: boolean; data?: unknown; error?: string };\n }\n | { type: 'error'; payload: { message: string; detail?: string; at: number } }\n /**\n * Bytes the shell wrote, as they arrive. A terminal is a stream, not a\n * request: `terminal/open` starts it and this carries everything after.\n */\n | { type: 'terminal/output'; payload: { terminalId: string; data: string } }\n /** The shell ended (or never started); no more output follows for this id. */\n | {\n type: 'terminal/exit';\n payload: { terminalId: string; code?: number; error?: string };\n };\n\n/** Frontend -> host. */\nexport type ClientToHostMessage =\n | { type: 'client/ready'; payload: { protocolVersion: number; frontend?: FrontendIdentity } }\n | {\n type: 'chat/prompt';\n payload: { text: string; mode?: PromptMode; images?: PromptImage[]; pins?: ChatPin[] };\n }\n /**\n * Replaces a past user message: the host forks the conversation before it,\n * drops that turn, and sends the edited text as a new prompt. `itemId` is the\n * transcript item being edited; `text` is what the user typed in its place.\n */\n | { type: 'chat/edit'; payload: { itemId: string; text: string } }\n /**\n * Branches a new session before a past user message: the host forks the\n * conversation (pi's `fork`), leaves the old branch resumable, and re-homes\n * the transcript onto the new branch with an empty tail. Then it seeds the\n * composer with the forked prompt (`composer/seed`).\n */\n | { type: 'chat/fork'; payload: { itemId: string } }\n | { type: 'chat/abort'; payload: Record<string, never> }\n | { type: 'session/new'; payload: { cwd?: string } }\n | { type: 'session/load'; payload: { sessionId: string; cwd?: string } }\n | { type: 'session/activate'; payload: { sessionId: string; cwd?: string } }\n | { type: 'session/close'; payload: { sessionId: string } }\n /**\n * Deletes a session and its stored conversation for good. Closing only\n * retires the agent process (the session stays resumable); deleting removes\n * the session file. What that means per host is the catalog's business.\n */\n | { type: 'session/delete'; payload: { sessionId: string } }\n | { type: 'session/compact'; payload: { instructions?: string } }\n | { type: 'history/load'; payload: Record<string, never> }\n | { type: 'session/list'; payload: Record<string, never> }\n /**\n * Re-reads the resources the composer's palette lists. pi caches its prompt\n * templates at spawn, so a newly added template is invisible until this \u2014 the\n * host re-reads the template files and answers with a fresh `session/state`.\n */\n | { type: 'commands/refresh'; payload: Record<string, never> }\n /**\n * Re-reads the model catalog. pi caches its configured models while a session\n * is warm, so a model added to `models.json` is invisible until this \u2014 the\n * host re-reads it and answers with a fresh `session/state`.\n */\n | { type: 'models/refresh'; payload: Record<string, never> }\n | { type: 'project/list'; payload: Record<string, never> }\n | { type: 'project/open'; payload: { path: string } }\n | { type: 'model/set'; payload: { provider: string; id: string } }\n | { type: 'thinking/set'; payload: { level: ThinkingLevel } }\n | { type: 'interaction/respond'; payload: InteractionResponse }\n | {\n type: 'host/command';\n payload: { command: HostCommand; args?: Record<string, unknown>; requestId?: string };\n }\n /**\n * Starts an interactive shell in the viewing session's directory (or `cwd`)\n * and streams it back as `terminal/output` / `terminal/exit`.\n */\n | {\n type: 'terminal/open';\n payload: { terminalId: string; cwd?: string; cols?: number; rows?: number };\n }\n /** Keystrokes (or a pasted line) for a running terminal. */\n | { type: 'terminal/input'; payload: { terminalId: string; data: string } }\n /** The viewer's terminal was resized; the shell gets the new geometry. */\n | { type: 'terminal/resize'; payload: { terminalId: string; cols: number; rows: number } }\n /** Closes a terminal and kills its shell. */\n | { type: 'terminal/close'; payload: { terminalId: string } };\n\nexport type WireMessage = HostToClientMessage | ClientToHostMessage;\n\nexport const HOST_MESSAGE_TYPES = [\n 'host/ready',\n 'session/state',\n 'project/list',\n 'session/activity',\n 'transcript/append',\n 'transcript/update',\n 'transcript/delta',\n 'transcript/replace',\n 'transcript/prepend',\n 'session/list',\n 'interaction/request',\n 'interaction/dismiss',\n 'notice',\n 'context/selection',\n 'context/selectionLive',\n 'composer/seed',\n 'host/command/result',\n 'error',\n 'terminal/output',\n 'terminal/exit',\n] as const;\n\nexport const CLIENT_MESSAGE_TYPES = [\n 'client/ready',\n 'chat/prompt',\n 'chat/edit',\n 'chat/fork',\n 'chat/abort',\n 'session/new',\n 'session/load',\n 'session/activate',\n 'session/close',\n 'session/delete',\n 'session/compact',\n 'history/load',\n 'session/list',\n 'commands/refresh',\n 'models/refresh',\n 'project/list',\n 'project/open',\n 'model/set',\n 'thinking/set',\n 'interaction/respond',\n 'host/command',\n 'terminal/open',\n 'terminal/input',\n 'terminal/resize',\n 'terminal/close',\n] as const;\n\nexport function encodeWireMessage(message: WireMessage): string {\n return JSON.stringify(message);\n}\n\nexport function isHostToClientMessage(value: unknown): value is HostToClientMessage {\n return hasKnownType(value, HOST_MESSAGE_TYPES);\n}\n\nexport function isClientToHostMessage(value: unknown): value is ClientToHostMessage {\n return hasKnownType(value, CLIENT_MESSAGE_TYPES);\n}\n\n/** Accepts a JSON string or an already parsed object; returns undefined when invalid. */\nexport function parseHostMessage(raw: unknown): HostToClientMessage | undefined {\n const value = decode(raw);\n return isHostToClientMessage(value) ? value : undefined;\n}\n\nexport function parseClientMessage(raw: unknown): ClientToHostMessage | undefined {\n const value = decode(raw);\n return isClientToHostMessage(value) ? value : undefined;\n}\n\n/**\n * Transport-agnostic decode: WebSocket frames may arrive as Buffer/Uint8Array,\n * the VS Code webview passes objects, tests pass strings.\n */\nexport function decode(raw: unknown): unknown {\n if (typeof raw === 'string') {\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n }\n if (raw instanceof Uint8Array) {\n return decode(new TextDecoder().decode(raw));\n }\n if (raw instanceof ArrayBuffer) {\n return decode(new TextDecoder().decode(new Uint8Array(raw)));\n }\n return raw;\n}\n\nfunction hasKnownType(value: unknown, allowed: readonly string[]): boolean {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const type = (value as { type?: unknown }).type;\n return typeof type === 'string' && allowed.includes(type);\n}\n", "/**\n * Wire protocol version. Bump this (and the frontends) whenever a message shape\n * changes in a breaking way \u2014 hosts refuse to talk to a mismatched frontend.\n */\nexport const PROTOCOL_VERSION = 22;\n\n/** File a frontend build writes next to its index.html so any host can identify it. */\nexport const FRONTEND_MANIFEST_FILE = 'webview.manifest.json';\n\n/** Default HTTP path the NestJS host exposes its WebSocket endpoint on. */\nexport const DEFAULT_WS_PATH = '/ws';\n", "import type { FrontendIdentity } from './dto.js';\nimport { FRONTEND_MANIFEST_FILE, PROTOCOL_VERSION } from './version.js';\n\n/**\n * A frontend build writes this next to its index.html. It is what makes the\n * frontend swappable without touching host code: the host reads the manifest,\n * checks `protocolVersion`, and serves whatever is in the directory.\n */\nexport interface FrontendManifest extends FrontendIdentity {\n protocolVersion: number;\n entry: string;\n generatedAt: string;\n}\n\nexport function createFrontendManifest(input: {\n name: string;\n version: string;\n entry?: string;\n generatedAt?: string;\n}): FrontendManifest {\n return {\n name: input.name,\n version: input.version,\n protocolVersion: PROTOCOL_VERSION,\n entry: input.entry ?? 'index.html',\n generatedAt: input.generatedAt ?? new Date().toISOString(),\n };\n}\n\nexport function isFrontendManifest(value: unknown): value is FrontendManifest {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const candidate = value as Partial<Record<keyof FrontendManifest, unknown>>;\n return (\n typeof candidate.name === 'string' &&\n typeof candidate.version === 'string' &&\n typeof candidate.protocolVersion === 'number' &&\n typeof candidate.entry === 'string'\n );\n}\n\nexport function parseFrontendManifest(raw: string | undefined | null): FrontendManifest | undefined {\n if (!raw) {\n return undefined;\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n return isFrontendManifest(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function frontendIdentity(manifest: FrontendManifest | undefined): FrontendIdentity | undefined {\n return manifest ? { name: manifest.name, version: manifest.version } : undefined;\n}\n\nexport { FRONTEND_MANIFEST_FILE, PROTOCOL_VERSION };\n", "import { Module } from '@nestjs/common';\nimport { createPiRpcAdapter, type PiRpcAdapter } from '@morse/adapter-pi-rpc';\nimport { SessionRegistry, type MorseLogger } from '@morse/core';\nimport type { ProjectPolicy } from '@morse/host-runtime';\nimport { SessionTranscriptStore } from '@morse/host-runtime';\nimport { HealthController } from './internal/http/health.controller.js';\nimport { NestMorseLogger } from './internal/logging/nest-logger.js';\nimport { ServerProjectPolicy } from './internal/projects/project-policy.js';\nimport { ServerTerminalBackend } from './internal/terminal/terminal.service.js';\nimport { createSessionRegistry, MorseRegistryLifecycle } from './internal/registry.js';\nimport { MorseGateway } from './internal/ws/morse.gateway.js';\nimport { MorseSessionFactory } from './internal/ws/session-factory.service.js';\nimport { loadConfig, type MorseServerConfig } from './app/config.js';\nimport {\n MORSE_CONFIG,\n MORSE_LOGGER,\n MORSE_PI_ADAPTER,\n MORSE_PROJECT_POLICY,\n MORSE_SESSION_REGISTRY,\n MORSE_TRANSCRIPT_STORE,\n} from './app/tokens.js';\n\n/**\n * Every provider here is wiring (clean-architecture R7): config in, concrete\n * adapters out. The NestJS DI container *is* this host's composition root.\n *\n * The session registry is a singleton, so the agent sessions (and their\n * projects) keep running across client reconnects; only the per-connection\n * controller is created and disposed with the WebSocket.\n */\n@Module({\n controllers: [HealthController],\n providers: [\n {\n provide: MORSE_CONFIG,\n useFactory: (): MorseServerConfig => loadConfig(),\n },\n {\n provide: MORSE_LOGGER,\n useFactory: (): MorseLogger => new NestMorseLogger(),\n },\n {\n provide: MORSE_PI_ADAPTER,\n useFactory: (config: MorseServerConfig, logger: MorseLogger): PiRpcAdapter =>\n createPiRpcAdapter(\n {\n piPath: config.piPath,\n nodeEntryPath: config.nodeEntryPath,\n sessionDir: config.sessionDir,\n noSession: config.noSession,\n requestTimeoutMs: config.requestTimeoutMs,\n },\n logger,\n ),\n inject: [MORSE_CONFIG, MORSE_LOGGER],\n },\n {\n provide: MORSE_SESSION_REGISTRY,\n useFactory: (\n adapter: PiRpcAdapter,\n config: MorseServerConfig,\n logger: MorseLogger,\n ): SessionRegistry => createSessionRegistry(adapter, config, logger),\n inject: [MORSE_PI_ADAPTER, MORSE_CONFIG, MORSE_LOGGER],\n },\n {\n provide: MORSE_TRANSCRIPT_STORE,\n useFactory: (): SessionTranscriptStore => new SessionTranscriptStore(),\n },\n {\n provide: MORSE_PROJECT_POLICY,\n useFactory: (config: MorseServerConfig): ProjectPolicy =>\n new ServerProjectPolicy(config.projects),\n inject: [MORSE_CONFIG],\n },\n MorseRegistryLifecycle,\n ServerTerminalBackend,\n MorseSessionFactory,\n MorseGateway,\n ],\n})\nexport class AppModule {}\n", "import { readdir, readFile, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, join, resolve } from 'node:path';\nimport {\n THINKING_LEVELS,\n type AgentEvent,\n type AgentEventListener,\n type AgentForkMessage,\n type AgentGateway,\n type AgentHistoryEntry,\n type AgentHistoryPage,\n type AgentInteractionRequest,\n type AgentInteractionResponse,\n type AgentSessionState,\n type AgentCommand,\n type AgentCommandSource,\n type ChatPin,\n type ModelRef,\n type MorseLogger,\n type NoticeLevel,\n type PromptDisposition,\n type PromptImage,\n type PromptMode,\n type ThinkingLevel,\n type WorkspaceRef,\n} from '@morse/core';\nimport { mapSessionEvent, activePathEntries, toEntryHistory, toSessionStats } from './event-mapping.js';\nimport { PiRpcClient } from './internal/rpc-client.js';\nimport {\n asRecord,\n asString,\n type RpcExtensionUiRequest,\n type RpcCommandInfo,\n type RpcModel,\n type RpcRecord,\n type RpcSessionStateData,\n type RpcSessionStatsData,\n} from './internal/rpc-types.js';\nimport type { PiSpawn } from './internal/resolve-pi.js';\n\nexport interface PiRpcAgentOptions {\n spawn: PiSpawn;\n workspace: WorkspaceRef;\n logger: MorseLogger;\n sessionPath?: string;\n env?: NodeJS.ProcessEnv;\n requestTimeoutMs?: number;\n now?: () => number;\n}\n\n/**\n * Compaction summarizes the entire context in one model request \u2014 minutes on a\n * slow local model. Far above the transport's 30 s default, yet still a cap: a\n * hung pi must not wedge the composer's Working state forever.\n */\nconst COMPACT_REQUEST_TIMEOUT_MS = 600_000;\n\n/**\n * `AgentGateway` implemented on top of a `pi --mode rpc` subprocess.\n * Swapping transports (SDK in-process, remote container, ...) means writing\n * another implementation of this port \u2014 nothing above it changes.\n */\nexport class PiRpcAgent implements AgentGateway {\n private readonly client: PiRpcClient;\n private readonly listeners = new Set<AgentEventListener>();\n private readonly now: () => number;\n private sessionState: AgentSessionState;\n private ready = false;\n private disposed = false;\n /** Full mapped history, fetched once and paged locally (see `history`). */\n private historyCache: AgentHistoryEntry[] | undefined;\n\n constructor(private readonly options: PiRpcAgentOptions) {\n this.now = options.now ?? (() => Date.now());\n this.sessionState = {\n workspace: options.workspace,\n thinkingLevel: 'off',\n availableModels: [],\n availableThinkingLevels: [],\n availableCommands: [],\n streaming: false,\n };\n this.client = new PiRpcClient({\n command: options.spawn.command,\n args: options.spawn.args,\n cwd: options.workspace.cwd,\n env: options.env,\n requestTimeoutMs: options.requestTimeoutMs,\n onRecord: (record) => {\n this.onRecord(record);\n },\n onStderr: (text) => {\n const trimmed = text.trim();\n if (trimmed.length > 0) {\n this.options.logger.debug(`pi: ${trimmed}`);\n }\n },\n onExit: (info) => {\n this.ready = false;\n if (!this.disposed) {\n this.emit({\n type: 'agent/fatal',\n at: this.now(),\n message: `The pi agent stopped (code=${info.code === null ? 'null' : info.code}).`,\n });\n }\n },\n });\n }\n\n /** Spawns the subprocess and reads its initial state. Throws when pi is missing. */\n async initialize(): Promise<AgentSessionState> {\n this.client.start();\n await this.refreshState();\n return this.snapshotState();\n }\n\n subscribe(listener: AgentEventListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n async state(): Promise<AgentSessionState> {\n if (!this.ready) {\n await this.refreshState();\n }\n return this.snapshotState();\n }\n\n /**\n * Resumed sessions keep their messages on disk; `get_messages` is how a client\n * shows what was said before this connection existed.\n *\n * pi's `get_messages` has no cursor, so the full mapping is fetched once and\n * cached; pages are then sliced locally from the end. The cache is dropped on\n * the next prompt, so a re-seed never serves stale history.\n */\n async history(options: { limit?: number; before?: string } = {}): Promise<AgentHistoryPage> {\n const entries = await this.historyEntries();\n const total = entries.length;\n const limit = options.limit !== undefined && options.limit > 0 ? options.limit : total;\n // `before` is the index of the oldest entry the caller already holds;\n // absent means \"the newest page\".\n const requested = options.before === undefined ? total : Number(options.before);\n const end = Number.isFinite(requested)\n ? Math.max(0, Math.min(total, Math.trunc(requested)))\n : total;\n const start = Math.max(0, end - limit);\n return {\n entries: entries.slice(start, end),\n hasOlder: start > 0,\n ...(start > 0 ? { before: String(start) } : {}),\n };\n }\n\n private async historyEntries(): Promise<AgentHistoryEntry[]> {\n if (this.historyCache !== undefined) {\n return this.historyCache;\n }\n // `get_entries` carries the full session history INCLUDING the entries a\n // compaction folded away (with the compaction entry marking the cut), and\n // id/parentId link the current branch: `get_messages` only mirrors what the\n // agent sees now, so a resumed session would silently shrink to everything\n // after the last compaction (with the start reading \"Start of\n // conversation\").\n const data = await this.client.request<{ entries?: unknown[]; leafId?: unknown }>({\n type: 'get_entries',\n });\n this.historyCache = toEntryHistory(activePathEntries(data));\n return this.historyCache;\n }\n\n async prompt(\n text: string,\n mode: PromptMode,\n images?: PromptImage[],\n pins?: ChatPin[],\n ): Promise<PromptDisposition> {\n const attachment = images && images.length > 0 ? toPiImages(images) : undefined;\n const imageArgs = attachment ? { images: attachment } : {};\n // Pins render as `@path[:start-end]` mention lines at the end of the\n // message: pi conventions, and visible in every pi frontend as exactly the\n // references they are \u2014 never prose pretending to be the user's words.\n const message = appendPinMentions(text, pins);\n // A new prompt appends to pi's history, so any cached read is now stale.\n this.historyCache = undefined;\n const command =\n mode === 'steer'\n ? ({ type: 'steer', message, ...imageArgs } as const)\n : mode === 'followUp'\n ? ({ type: 'follow_up', message, ...imageArgs } as const)\n : ({ type: 'prompt', message, ...imageArgs } as const);\n const data = await this.client.request<{ disposition?: string }>(command);\n const disposition = data?.disposition;\n if (disposition === 'queued' || disposition === 'handled') {\n return disposition;\n }\n return 'started';\n }\n\n async abort(): Promise<void> {\n await this.client.request({ type: 'abort' });\n }\n\n /** Past user messages pi can fork from (its `get_fork_messages`). */\n async forkMessages(): Promise<AgentForkMessage[]> {\n const data = await this.client.request<{ messages?: unknown[] }>({\n type: 'get_fork_messages',\n });\n const messages = Array.isArray(data?.messages) ? data.messages : [];\n const result: AgentForkMessage[] = [];\n for (const raw of messages) {\n const record = asRecord(raw);\n const entryId = record ? asString(record['entryId']) : undefined;\n if (!record || !entryId) {\n continue;\n }\n result.push({ entryId, text: asString(record['text']) ?? '' });\n }\n return result;\n }\n\n /**\n * Forks the conversation before a user entry. pi writes a new session file and\n * rebinds in place, so the process stays alive but `sessionFile` changes \u2014\n * the cached state is refreshed before returning.\n */\n async fork(entryId: string): Promise<{ text: string; cancelled: boolean }> {\n const data = await this.client.request<{ text?: string; cancelled?: boolean }>({\n type: 'fork',\n entryId,\n });\n // The conversation was re-parented: any cached history read is now stale.\n this.historyCache = undefined;\n if (data?.cancelled === true) {\n return { text: data.text ?? '', cancelled: true };\n }\n await this.refreshState();\n return { text: data?.text ?? '', cancelled: false };\n }\n\n async setModel(model: ModelRef): Promise<void> {\n const data = await this.client.request<RpcModel>({\n type: 'set_model',\n provider: model.provider,\n modelId: model.id,\n });\n this.patchState({ model: data ? toModelRef(data) : model });\n // pi scopes the thinking levels to the *current* model, so switching models\n // can change which ones exist (or whether reasoning is offered at all). pi's\n // own TUI re-reads them after a model change; without this the picker kept\n // showing the previous model's levels for every model.\n await this.refreshThinkingLevels();\n }\n\n /**\n * Re-read the levels the current model supports, and the level pi actually\n * settled the session on (a model without reasoning resets it to `off`).\n */\n private async refreshThinkingLevels(): Promise<void> {\n const [levels, state] = await Promise.all([\n this.client\n .request<{ levels?: string[] }>({ type: 'get_available_thinking_levels' })\n .catch(() => undefined),\n this.client.request<RpcSessionStateData>({ type: 'get_state' }).catch(() => undefined),\n ]);\n const available = dedupeThinkingLevels(\n (levels?.levels ?? []).map(toThinkingLevel).filter(isDefined),\n );\n const thinkingLevel = state ? toThinkingLevel(state.thinkingLevel) : undefined;\n this.patchState({\n availableThinkingLevels: available.length > 0 ? available : [...THINKING_LEVELS],\n ...(thinkingLevel !== undefined ? { thinkingLevel } : {}),\n });\n }\n\n async setThinkingLevel(level: ThinkingLevel): Promise<void> {\n await this.client.request({ type: 'set_thinking_level', level });\n this.patchState({ thinkingLevel: level });\n }\n\n async compact(customInstructions?: string): Promise<void> {\n // Compaction summarizes the whole context in one model request, so the\n // transport's 30 s default (sized for state/model calls) is far too short:\n // a slow local model takes minutes, and timing out here showed \"pi did not\n // answer 'compact' within 30000 ms\" while the agent was still working.\n await this.client.request(\n customInstructions === undefined\n ? { type: 'compact' }\n : { type: 'compact', customInstructions },\n COMPACT_REQUEST_TIMEOUT_MS,\n );\n await this.refreshStats();\n }\n\n respondToInteraction(response: AgentInteractionResponse): Promise<void> {\n this.client.respond({\n type: 'extension_ui_response',\n id: response.requestId,\n value: response.value,\n confirmed: response.confirmed,\n cancelled: response.cancelled,\n });\n return Promise.resolve();\n }\n\n async dispose(): Promise<void> {\n this.disposed = true;\n this.listeners.clear();\n await this.client.dispose();\n }\n\n private async refreshState(): Promise<void> {\n const state = await this.client.request<RpcSessionStateData>({ type: 'get_state' });\n const models = await this.client\n .request<{ models?: RpcModel[] }>({ type: 'get_available_models' })\n .catch(() => undefined);\n const levels = await this.client\n .request<{ levels?: string[] }>({ type: 'get_available_thinking_levels' })\n .catch(() => undefined);\n const commands = await this.client\n .request<{ commands?: RpcCommandInfo[] }>({ type: 'get_commands' })\n .catch(() => undefined);\n\n const availableModels = (models?.models ?? []).map(toModelRef);\n const availableThinkingLevels = dedupeThinkingLevels(\n (levels?.levels ?? []).map(toThinkingLevel).filter(isDefined),\n );\n const thinkingLevel = toThinkingLevel(state.thinkingLevel);\n\n this.sessionState = {\n ...this.sessionState,\n sessionId: state.sessionFile ?? state.sessionId,\n sessionTitle: state.sessionName,\n model: state.model ? toModelRef(state.model) : this.sessionState.model,\n thinkingLevel: thinkingLevel ?? this.sessionState.thinkingLevel,\n availableModels,\n availableThinkingLevels:\n availableThinkingLevels.length > 0 ? availableThinkingLevels : [...THINKING_LEVELS],\n availableCommands: await buildCommandList(commands?.commands, this.commandContext()),\n streaming: state.isStreaming === true,\n };\n this.ready = true;\n await this.refreshStats();\n this.emit({ type: 'agent/ready', at: this.now(), state: this.snapshotState() });\n }\n\n /**\n * Re-reads the resources behind `get_commands` and reports the result the same\n * way a fresh spawn would. The palette calls it the moment it opens, so a new\n * or edited template is there by the time the reader finishes typing.\n */\n async refreshCommands(): Promise<void> {\n if (this.disposed || !this.ready) {\n return;\n }\n const commands = await this.client\n .request<{ commands?: RpcCommandInfo[] }>({ type: 'get_commands' })\n .catch(() => undefined);\n this.sessionState = {\n ...this.sessionState,\n availableCommands: await buildCommandList(commands?.commands, this.commandContext()),\n };\n this.emit({ type: 'agent/state', at: this.now(), state: this.snapshotState() });\n }\n\n /** Where a command list is rebuilt from: the session's workspace and env. */\n private commandContext(): CommandContext {\n return { cwd: this.options.workspace.cwd, env: this.options.env };\n }\n\n /**\n * Re-reads `get_available_models` and reports it the same way a fresh spawn\n * would. The model picker calls it the moment it opens, so a model added to\n * `models.json` while this session is warm is there without reopening the\n * session or restarting the host.\n */\n async refreshModels(): Promise<void> {\n if (this.disposed || !this.ready) {\n return;\n }\n const models = await this.client\n .request<{ models?: RpcModel[] }>({ type: 'get_available_models' })\n .catch(() => undefined);\n if (models === undefined) {\n return;\n }\n this.patchState({ availableModels: (models.models ?? []).map(toModelRef) });\n }\n\n /**\n * Pulls the cumulative token/cost/context totals pi keeps for the session, so\n * the footer can show the same numbers the TUI does. Best-effort: an older pi\n * without `get_session_stats` simply leaves the previous values in place.\n */\n private async refreshStats(): Promise<void> {\n const stats = await this.client\n .request<RpcSessionStatsData>({ type: 'get_session_stats' })\n .catch(() => undefined);\n const patch = toSessionStats(stats);\n if (Object.keys(patch).length === 0) {\n return;\n }\n this.sessionState = { ...this.sessionState, ...patch };\n this.emit({ type: 'agent/state', at: this.now(), state: this.snapshotState() });\n }\n\n private onRecord(record: RpcRecord): void {\n if (record['type'] === 'extension_ui_request') {\n this.onUiRequest(record as unknown as RpcExtensionUiRequest);\n return;\n }\n\n switch (record['type']) {\n case 'agent_settled':\n this.patchState({ streaming: false });\n this.emit({ type: 'agent/run-end', at: this.now(), reason: 'settled' });\n // A settled run is the moment cumulative tokens/cost/context are final.\n void this.refreshStats();\n return;\n case 'thinking_level_changed': {\n const level = toThinkingLevel(asString(record['level']));\n if (level) {\n this.patchState({ thinkingLevel: level });\n }\n return;\n }\n case 'session_info_changed':\n this.patchState({ sessionTitle: asString(record['name']) });\n return;\n case 'queue_update':\n return;\n default:\n break;\n }\n\n const mapped = mapSessionEvent(record, this.now);\n if (mapped.state) {\n this.patchState(mapped.state);\n }\n for (const event of mapped.events) {\n this.emit(event);\n }\n // Compaction rewrites the context, so the footer's percentage must follow.\n if (record['type'] === 'compaction_end') {\n void this.refreshStats();\n }\n }\n\n private onUiRequest(request: RpcExtensionUiRequest): void {\n const method = request.method;\n if (method === 'notify') {\n this.emit({\n type: 'agent/notice',\n at: this.now(),\n level: toNoticeLevel(request.notifyType),\n text: request.message ?? '',\n });\n return;\n }\n if (\n method !== 'select' &&\n method !== 'confirm' &&\n method !== 'input' &&\n method !== 'editor'\n ) {\n // Fire-and-forget terminal concerns (setTitle/setStatus/setWidget/...):\n // nothing to answer, and Morse has no terminal chrome to update.\n this.options.logger.debug(`pi requested unsupported UI method \"${method}\"; ignoring.`);\n return;\n }\n this.emit({\n type: 'agent/interaction',\n at: this.now(),\n request: toInteractionRequest(method, request),\n });\n }\n\n private patchState(patch: Partial<AgentSessionState>): void {\n this.sessionState = { ...this.sessionState, ...patch };\n this.emit({ type: 'agent/state', at: this.now(), state: this.snapshotState() });\n }\n\n private snapshotState(): AgentSessionState {\n return {\n ...this.sessionState,\n workspace: { ...this.sessionState.workspace },\n availableModels: [...this.sessionState.availableModels],\n availableThinkingLevels: [...this.sessionState.availableThinkingLevels],\n availableCommands: [...this.sessionState.availableCommands],\n };\n }\n\n private emit(event: AgentEvent): void {\n for (const listener of [...this.listeners]) {\n try {\n listener(event);\n } catch (error: unknown) {\n this.options.logger.error('Agent event listener failed', error);\n }\n }\n }\n}\n\nfunction toInteractionRequest(\n kind: 'select' | 'confirm' | 'input' | 'editor',\n request: RpcExtensionUiRequest,\n): AgentInteractionRequest { const title = request.title ?? kind;\n switch (kind) {\n case 'select':\n return {\n requestId: request.id,\n kind: 'select',\n title,\n options: (request.options ?? []).map((value) => ({ value, label: value })),\n };\n case 'confirm':\n return { requestId: request.id, kind: 'confirm', title, message: request.message ?? '' };\n case 'input':\n return {\n requestId: request.id,\n kind: 'input',\n title,\n placeholder: request.placeholder,\n };\n case 'editor':\n return { requestId: request.id, kind: 'editor', title, value: request.prefill };\n }\n}\n\nfunction toModelRef(model: RpcModel): ModelRef {\n return {\n provider: model.provider,\n id: model.id,\n name: model.name ?? model.id,\n contextWindow: model.contextWindow,\n maxTokens: model.maxTokens,\n };\n}\n\nfunction toThinkingLevel(value: string | undefined): ThinkingLevel | undefined {\n if (!value) {\n return undefined;\n }\n // pi's canonical list is off..max, but the level names arrive from pi and are\n // what pi validates against (`set_thinking_level` accepts whatever it\n // reported). An unfamiliar name \u2014 say a future provider tier \u2014 is passed\n // through intact: the picker's brain falls back to its default intensity and\n // the label is just capitalized, so hiding it would silently drop a real level.\n return THINKING_LEVELS.find((level) => level === value) ?? (value.trim() as ThinkingLevel);\n}\n\nfunction dedupeThinkingLevels(levels: ThinkingLevel[]): ThinkingLevel[] {\n return [...new Set(levels)];\n}\n\n/**\n * pi reports command sources as `extension`, `prompt` or `skill`. Anything else\n * (a future source) is treated as an extension command so it still runs.\n */\nfunction toCommandSource(value: string | undefined): AgentCommandSource {\n return value === 'prompt' || value === 'skill' ? value : 'extension';\n}\n\n/**\n * pi lists a prompt template in `get_commands` but does not send its body over\n * RPC, and it does not notice a file added after spawn. Read the file directly \u2014\n * both for the templates pi named and for the ones Morse finds on disk. Best\n * effort: an unreadable file is left out rather than offered as a dead command.\n */\nasync function readTemplateFile(path: string | undefined): Promise<string | undefined> {\n if (path === undefined || path.length === 0) {\n return undefined;\n }\n try {\n return await readFile(path, 'utf8');\n } catch {\n return undefined;\n }\n}\n\n/** What a command list is rebuilt from: the workspace and the pi environment. */\nexport interface CommandContext {\n cwd: string;\n env: NodeJS.ProcessEnv | undefined;\n}\n\n/** The subset of a `get_commands` entry the command list cares about. */\nexport interface CommandListInput {\n name?: string;\n description?: string;\n source?: string;\n sourceInfo?: { path?: string; scope?: string };\n}\n\n/**\n * The palette's command list, rebuilt from disk. pi only exposes the templates\n * it loaded at spawn (`get_commands` is a cache), so this also re-reads every\n * template file and scans the conventional prompt directories \u2014 that is what\n * makes a template added, changed or deleted since spawn show up without a pi\n * reload.\n *\n * pi's own list stays the source for extension and skill commands, and for\n * prompt templates that live outside the conventional directories (packages,\n * explicit paths); a listed template whose file is gone is dropped.\n */\nexport async function buildCommandList(\n items: CommandListInput[] | undefined,\n context: CommandContext,\n): Promise<AgentCommand[]> {\n const commands: AgentCommand[] = [];\n const seen = new Set<string>();\n let projectTrusted = false;\n\n for (const item of items ?? []) {\n if (typeof item.name !== 'string' || item.name.trim().length === 0) {\n continue;\n }\n const source = toCommandSource(item.source);\n if (source !== 'prompt') {\n commands.push({ name: item.name, description: item.description, source });\n seen.add(item.name);\n continue;\n }\n if (item.sourceInfo?.scope === 'project') {\n projectTrusted = true;\n }\n const template = await readTemplateFile(item.sourceInfo?.path);\n if (template === undefined) {\n // The file pi listed is gone: hide it rather than offer a dead command.\n continue;\n }\n commands.push({\n name: item.name,\n // Prefer the freshly parsed description so an edited frontmatter reflects\n // too; pi's cached description is the fallback.\n description: promptDescription(template) ?? item.description,\n source,\n template,\n });\n seen.add(item.name);\n }\n\n for (const dir of await promptDirs(projectTrusted, context)) {\n for (const file of await listPromptFiles(dir)) {\n if (seen.has(file.name)) {\n continue;\n }\n const template = await readTemplateFile(file.path);\n if (template === undefined) {\n continue;\n }\n commands.push({\n name: file.name,\n description: promptDescription(template),\n source: 'prompt',\n template,\n });\n seen.add(file.name);\n }\n }\n return commands;\n}\n\n/**\n * Directories Morse re-scans for prompt templates: the user's global prompts,\n * and the project's \u2014 the latter only once that project is trusted, exactly as\n * pi gates project resources.\n */\nasync function promptDirs(projectTrusted: boolean, context: CommandContext): Promise<string[]> {\n const dirs = [join(agentDir(context.env), 'prompts')];\n if (projectTrusted || (await isProjectTrusted(context.cwd, context.env))) {\n dirs.push(resolve(context.cwd, '.pi', 'prompts'));\n }\n return dirs;\n}\n\n/** pi's agent config directory (`PI_CODING_AGENT_DIR`, else `~/.pi/agent`). */\nfunction agentDir(env: NodeJS.ProcessEnv | undefined): string {\n const configured = env?.PI_CODING_AGENT_DIR?.trim();\n return configured && configured.length > 0\n ? expandHome(configured)\n : join(homedir(), '.pi', 'agent');\n}\n\nfunction expandHome(path: string): string {\n if (path === '~') {\n return homedir();\n }\n if (path.startsWith('~/') || path.startsWith('~\\\\')) {\n return join(homedir(), path.slice(2));\n }\n return path;\n}\n\n/**\n * pi records project trust in `<agentDir>/trust.json` as `{ \"<cwd>\": true }`.\n * The project prompt directory is only scanned when that says yes, so an\n * untrusted checkout cannot inject a template into the palette.\n */\nasync function isProjectTrusted(cwd: string, env: NodeJS.ProcessEnv | undefined): Promise<boolean> {\n try {\n const raw = await readFile(join(agentDir(env), 'trust.json'), 'utf8');\n const decisions = JSON.parse(raw) as Record<string, unknown>;\n return decisions[cwd] === true || decisions[resolve(cwd)] === true;\n } catch {\n return false;\n }\n}\n\n/** Direct `.md` children of a prompt directory \u2014 pi loads direct children only. */\nasync function listPromptFiles(dir: string): Promise<{ name: string; path: string }[]> {\n const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);\n const files: { name: string; path: string }[] = [];\n for (const entry of entries) {\n if (!entry.name.endsWith('.md')) {\n continue;\n }\n const path = join(dir, entry.name);\n let isFile = entry.isFile();\n if (entry.isSymbolicLink()) {\n try {\n isFile = (await stat(path)).isFile();\n } catch {\n continue;\n }\n }\n if (isFile) {\n files.push({ name: basename(entry.name, '.md'), path });\n }\n }\n return files;\n}\n\n/**\n * What the palette prints for a scanned template: pi's `description` frontmatter,\n * or the first non-empty body line, truncated the way pi truncates it.\n */\nfunction promptDescription(raw: string): string | undefined {\n let body = raw;\n const frontmatter = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/.exec(raw);\n if (frontmatter !== null) {\n const described = /^description:[ \\t]*(.*)$/m.exec(frontmatter[1]);\n if (described !== null) {\n const value = unquote(described[1].trim());\n if (value.length > 0) {\n return value;\n }\n }\n body = raw.slice(frontmatter[0].length);\n }\n const firstLine = body.split('\\n').find((line) => line.trim().length > 0);\n if (firstLine === undefined) {\n return undefined;\n }\n const trimmed = firstLine.trim();\n return trimmed.length > 60 ? `${trimmed.slice(0, 60)}...` : trimmed;\n}\n\nfunction unquote(value: string): string {\n const match = /^(['\"])([\\s\\S]*)\\1$/.exec(value);\n return match === null ? value : match[2];\n}\n\nfunction toNoticeLevel(value: string | undefined): NoticeLevel {\n if (value === 'error') {\n return 'error';\n }\n if (value === 'warning' || value === 'warn') {\n return 'warn';\n }\n return 'info';\n}\n\nfunction isDefined<T>(value: T | undefined): value is T {\n return value !== undefined;\n}\n\n/** pi's prompt command takes images as content blocks. */\nfunction toPiImages(images: PromptImage[]): Array<{ type: 'image'; data: string; mimeType: string }> {\n return images.map((image) => ({ type: 'image', data: image.data, mimeType: image.mimeType }));\n}\n\n/**\n * Apples the pin mentions after the user's words: `@path` for a whole file,\n * `@path:start-end` for an editor selection. When the message is pins only (an\n * image with no caption, say), the message is the mentions.\n */\nexport function appendPinMentions(text: string, pins: ChatPin[] | undefined): string {\n if (!pins || pins.length === 0) {\n return text;\n }\n const mentions = pins\n .map((pin) => {\n if (pin.startLine === undefined) {\n return `@${pin.path}`;\n }\n const end = pin.endLine ?? pin.startLine;\n return `@${pin.path}:${pin.startLine}-${end}`;\n })\n .join('\\n');\n const prose = text.trim();\n return prose.length > 0 ? `${prose}\\n\\n${mentions}` : mentions;\n}\n", "/**\n * Domain layer: anemic data shapes, value objects and sentinel errors only.\n * No I/O, no ports, no framework imports (clean-architecture R2, R8).\n */\n\nexport type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';\n\nexport const THINKING_LEVELS: readonly ThinkingLevel[] = [\n 'off',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n 'max',\n];\n\nexport type NoticeLevel = 'info' | 'success' | 'warn' | 'error';\n\nexport interface ModelRef {\n provider: string;\n id: string;\n name: string;\n contextWindow?: number;\n /** Provider cap on the response length, for the footer's output limit. */\n maxTokens?: number;\n}\n\nexport type AgentCommandSource = 'extension' | 'prompt' | 'skill';\n\n/** A command the agent exposes to the composer (pi `get_commands`). */\nexport interface AgentCommand {\n name: string;\n description?: string;\n source: AgentCommandSource;\n /**\n * Raw Markdown of a prompt template, for `source: 'prompt'` only. pi does not\n * send the body over RPC, so the adapter reads the file `get_commands` named\n * and the modal expands it here. Absent when the file could not be read.\n */\n template?: string;\n}\n\n/** An image attached to a prompt: base64 payload plus its media type. */\nexport interface PromptImage {\n /** Base64 data, without a `data:` URL prefix. */\n data: string;\n mimeType: string;\n}\n\n/** A file (or editor selection) pinned to a message as an attachment chip. */\nexport interface ChatPin {\n path: string;\n startLine?: number;\n endLine?: number;\n}\n\nexport interface TokenUsage {\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n /** Prompt tokens served from the provider's prompt cache. */\n cacheReadTokens?: number;\n /** Prompt tokens written into the provider's prompt cache. */\n cacheWriteTokens?: number;\n /** Reasoning tokens; a subset of `outputTokens`. */\n reasoningTokens?: number;\n}\n\n/** How full the model's context window is, straight from the agent. */\nexport interface ContextUsage {\n /** Tokens currently in context; `null` right after a compaction. */\n tokens: number | null;\n contextWindow: number;\n percent: number | null;\n}\n\n/** Message/step counts for the session, for the usage panel. */\nexport interface SessionCounts {\n userMessages: number;\n assistantMessages: number;\n toolCalls: number;\n toolResults: number;\n totalMessages: number;\n}\n\nexport interface WorkspaceRef {\n cwd: string;\n name: string;\n}\n\nexport interface SessionSummary {\n id: string;\n title: string;\n cwd: string;\n updatedAt: number;\n messageCount: number;\n}\n\n/** A project is a directory the agent has worked in (one pi session bucket). */\nexport interface ProjectSummary {\n path: string;\n name: string;\n sessionCount: number;\n lastUsedAt: number;\n}\n\nexport interface AgentSessionState {\n sessionId?: string;\n sessionTitle?: string;\n workspace: WorkspaceRef;\n model?: ModelRef;\n thinkingLevel: ThinkingLevel;\n availableModels: ModelRef[];\n availableThinkingLevels: ThinkingLevel[];\n /** Commands pi exposes (`get_commands`): extensions, templates, skills. */\n availableCommands: AgentCommand[];\n streaming: boolean;\n /** Cumulative session tokens (input/output/cache) reported by the agent. */\n usage?: TokenUsage;\n /** Tokens of the most recent assistant message, for the usage panel. */\n lastUsage?: TokenUsage;\n /** Cumulative session cost in USD, when the provider reports it. */\n costUsd?: number;\n contextUsage?: ContextUsage;\n counts?: SessionCounts;\n}\n\n/**\n * One already-finished exchange from a persisted session, so a resumed session\n * can show its history instead of an empty panel.\n */\n/**\n * One entry of an agent's past conversation, normalised away from any transport.\n *\n * `tool` entries carry a finished tool call: a persisted session replays its\n * calls too, otherwise a resumed transcript would look like the agent did\n * nothing between two messages.\n */\nexport type AgentHistoryEntry =\n | {\n role: 'user' | 'assistant';\n text: string;\n thinking?: string;\n model?: string;\n /** Image attachments the user pinned to this message (user role). */\n images?: PromptImage[];\n /** File/selection pins attached to this message (user role). */\n pins?: ChatPin[];\n at?: number;\n }\n | {\n role: 'tool';\n /** Provider-side id, used to attach the matching tool result. */\n toolCallId: string;\n name: string;\n title: string;\n input?: string;\n output?: string;\n status: 'ok' | 'error';\n at?: number;\n }\n /**\n * A compaction boundary in the session's history: everything before it was\n * summarized into `summary` (that is the context the agent carries); the\n * transcript marks the point instead of hiding the folded messages.\n */\n | {\n role: 'compaction';\n summary: string;\n tokensBefore?: number;\n at?: number;\n };\n\n/**\n * A past user message the agent can fork from: stable entry id plus the exact\n * text the model received. Forking re-parents the conversation before it, which\n * is how a frontend lets a user edit a prompt that already has an answer.\n */\nexport interface AgentForkMessage {\n entryId: string;\n text: string;\n}\n\nexport interface SelectOption {\n value: string;\n label: string;\n description?: string;\n}\n\n/** The agent asking the user for input, normalised away from any transport. */\nexport type AgentInteractionRequest =\n | { requestId: string; kind: 'select'; title: string; message?: string; options: SelectOption[] }\n | { requestId: string; kind: 'confirm'; title: string; message: string; danger?: boolean }\n | { requestId: string; kind: 'input'; title: string; placeholder?: string; value?: string }\n | { requestId: string; kind: 'editor'; title: string; value?: string; language?: string };\n\nexport interface AgentInteractionResponse {\n requestId: string;\n value?: string;\n confirmed?: boolean;\n cancelled?: boolean;\n}\n\n/** Everything a host can observe from a running agent. */\nexport type AgentEvent =\n | { type: 'agent/ready'; at: number; state: AgentSessionState }\n | { type: 'agent/state'; at: number; state: AgentSessionState }\n | { type: 'agent/run-start'; at: number }\n | { type: 'agent/delta'; at: number; channel: 'text' | 'thinking'; delta: string }\n | { type: 'agent/message'; at: number; text: string; thinking: string }\n | {\n type: 'agent/tool-start';\n at: number;\n toolCallId: string;\n name: string;\n title: string;\n input?: string;\n }\n | {\n type: 'agent/tool-update';\n at: number;\n toolCallId: string;\n output?: string;\n status?: 'running' | 'ok' | 'error';\n }\n | {\n type: 'agent/tool-end';\n at: number;\n toolCallId: string;\n status: 'ok' | 'error';\n output?: string;\n durationMs?: number;\n }\n | { type: 'agent/run-end'; at: number; reason: 'settled' | 'aborted' | 'error'; error?: string }\n | { type: 'agent/interaction'; at: number; request: AgentInteractionRequest }\n | { type: 'agent/notice'; at: number; level: NoticeLevel; text: string }\n /**\n * A successful compaction finished while the session was live. The projector\n * records the boundary as its own marker row \u2014 so the transcript shows where\n * the agent's context was summarized, including after a reload replays this\n * warm transcript; a cold resume gets the same markers out of session\n * history instead.\n */\n | { type: 'agent/compaction'; at: number; summary: string; tokensBefore?: number }\n | { type: 'agent/fatal'; at: number; message: string; detail?: string };\n", "export class MorseError extends Error {\n constructor(\n message: string,\n readonly code: string,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\n/** What the user can do about a failure, when the layer that failed knows. */\nexport interface AgentRemedy {\n /** Shell command that installs what is missing, e.g. the pi CLI. */\n install?: string;\n}\n\n/** The agent backend could not be started (missing `pi` binary, spawn failure, ...). */\nexport class AgentUnavailableError extends MorseError {\n /**\n * The next step, in a form a frontend can render (see `SessionViewState\n * .agentFailure`). The message stays human-readable on its own; this is the\n * machine-readable half, and only the layer that knows pi's package name can\n * fill it in.\n */\n readonly remedy: AgentRemedy | undefined;\n\n constructor(message: string, options?: { cause?: unknown; remedy?: AgentRemedy }) {\n super(message, 'agent-unavailable', options);\n this.remedy = options?.remedy;\n }\n}\n\n/** The agent answered with something the adapter could not understand. */\nexport class AgentProtocolError extends MorseError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, 'agent-protocol', options);\n }\n}\n\n/** The host was asked to do something it cannot do (server host has no editor). */\nexport class UnsupportedByHostError extends MorseError {\n constructor(message: string) {\n super(message, 'unsupported-by-host');\n }\n}\n", "import type {\n AgentEvent,\n AgentForkMessage,\n AgentHistoryEntry,\n AgentInteractionResponse,\n AgentSessionState,\n ChatPin,\n ModelRef,\n ProjectSummary,\n PromptImage,\n SessionSummary,\n ThinkingLevel,\n WorkspaceRef,\n} from '../domain.js';\nimport { AgentUnavailableError, UnsupportedByHostError } from '../errors.js';\nimport type { MorseLogger } from '../logger.js';\n\n/** Re-exported so the chat use case (and older imports) keep their import path. */\nexport type { PromptImage };\n\nexport type PromptMode = 'new' | 'steer' | 'followUp';\n\nexport type PromptDisposition = 'started' | 'queued' | 'handled';\n\nexport type AgentEventListener = (event: AgentEvent) => void;\n\n/**\n * Driven port (R2): the coding agent Morse drives, one instance per session.\n * Declared in the module that owns the gateway lifetime, implemented by\n * `@morse/adapter-pi-rpc` (subprocess) \u2014 or an SDK adapter later.\n */\nexport interface AgentGateway {\n subscribe(listener: AgentEventListener): () => void;\n state(): Promise<AgentSessionState>;\n /**\n * Finished exchanges of a resumed session (empty for a new one), newest page\n * first. Page from the end so a large session opens without loading it all.\n */\n history(options?: { limit?: number; before?: string }): Promise<AgentHistoryPage>;\n prompt(\n text: string,\n mode: PromptMode,\n images?: PromptImage[],\n pins?: ChatPin[],\n ): Promise<PromptDisposition>;\n /**\n * Past user messages the agent can fork from, in conversation order. A\n * frontend uses this to turn \"edit my old prompt\" into a fork of that entry.\n */\n forkMessages(): Promise<AgentForkMessage[]>;\n /**\n * Re-parents the conversation before `entryId`, discarding that message and\n * everything after it, and returns the message text that was dropped. The\n * agent keeps running, but its session id changes: a fork is a new session.\n */\n fork(entryId: string): Promise<{ text: string; cancelled: boolean }>;\n abort(): Promise<void>;\n /**\n * Re-reads the commands the agent exposes \u2014 prompt templates especially, which\n * pi caches at spawn. Optional: an adapter that cannot re-read resources simply\n * omits it and the palette keeps what the last `state()` reported.\n */\n refreshCommands?(): Promise<void>;\n /**\n * Optional: re-reads the model catalog for this session. The model picker\n * calls it when it opens, so a model added to `models.json` while the host\n * runs shows up without restarting the host or the session. Adapters that\n * cannot re-read the catalog simply omit it.\n */\n refreshModels?(): Promise<void>;\n setModel(model: ModelRef): Promise<void>;\n setThinkingLevel(level: ThinkingLevel): Promise<void>;\n compact(customInstructions?: string): Promise<void>;\n respondToInteraction(response: AgentInteractionResponse): Promise<void>;\n dispose(): Promise<void>;\n}\n\n/** Driven port (R2): creates a gateway for a workspace / persisted session. */\nexport interface AgentGatewayFactory {\n create(options: { workspace: WorkspaceRef; sessionId?: string }): Promise<AgentGateway>;\n /**\n * Optional: a gateway that never records \u2014 pi spawned with `--no-session` \u2014\n * used only to read what the backend offers before any session exists (model\n * catalog, thinking levels, commands). Adapters without a session-less mode\n * omit it, and the draft state simply ships without a catalog. `model` asks\n * the probe for the levels of a specific model, so the draft picker can follow\n * a model the user selected before the session exists.\n */\n probeDefaults?(options: { workspace: WorkspaceRef; model?: ModelRef }): Promise<AgentGateway>;\n}\n\n/** Driven port (R2): reads persisted sessions. */\nexport interface SessionCatalog {\n list(): Promise<SessionSummary[]>;\n /**\n * Deletes a persisted session. Optional on purpose: a catalog whose storage\n * Morse must not touch simply omits it, and the use case reports that\n * instead of pretending the session is gone. How a session is stored \u2014 and\n * therefore what deleting it means \u2014 belongs to the implementation.\n */\n remove?(id: string): Promise<void>;\n}\n\n/** An event from any hot session, tagged with the session it came from. */\nexport interface TaggedAgentEvent {\n sessionKey: string;\n event: AgentEvent;\n}\n\nexport interface OpenedSession {\n key: string;\n state: AgentSessionState;\n /** True when an already-running session was reused (no new `pi` process). */\n reused: boolean;\n}\n\n/** One page of a resumed session's finished exchanges. */\nexport interface AgentHistoryPage {\n entries: AgentHistoryEntry[];\n /** True when entries exist before this page. */\n hasOlder: boolean;\n /** Opaque cursor to pass as `before` for the next older page. */\n before?: string;\n}\n\n/** Result of forking the active session before a past user message. */\nexport interface ForkedSession {\n /** Registry key after the fork (the new session id); unchanged when cancelled. */\n key: string;\n /** Registry key the session had before the fork. */\n previousKey: string;\n state: AgentSessionState;\n /** Text of the message the fork re-opened, for restoring the editor. */\n text: string;\n cancelled: boolean;\n}\n\nexport interface SessionRegistryDeps {\n factory: AgentGatewayFactory;\n catalog: SessionCatalog;\n /** Workspace for sessions started without an explicit project. */\n defaultWorkspace: WorkspaceRef;\n /** How many `pi` processes stay alive at once (least recently used first). */\n hotLimit?: number;\n logger: MorseLogger;\n}\n\nconst DEFAULT_HOT_LIMIT = 4;\n\ninterface HotSession {\n key: string;\n gateway: AgentGateway;\n workspace: WorkspaceRef;\n detach: () => void;\n state: AgentSessionState;\n}\n\n/**\n * Session use case: owns every live agent gateway, keeps the most recent ones\n * hot (LRU), and fans their events out tagged with the session key.\n *\n * Multiple projects and multiple sessions therefore run at the same time, which\n * is what a browser host needs \u2014 and because `AgentGatewayFactory.create()`\n * already takes a workspace per session, the adapter did not change at all.\n */\nexport class SessionRegistry {\n private readonly hot = new Map<string, HotSession>();\n /** Least recently used first. */\n private readonly recency: string[] = [];\n private readonly listeners = new Set<(event: TaggedAgentEvent) => void>();\n private readonly hotLimit: number;\n private activeKey: string | undefined;\n private syntheticKeys = 0;\n /** Cached snapshots of never-recording spawns, keyed by model (`''` = the default). */\n private readonly draftCache = new Map<string, AgentSessionState>();\n private readonly draftProbes = new Map<string, Promise<AgentSessionState | undefined>>();\n\n constructor(private readonly deps: SessionRegistryDeps) {\n this.hotLimit = Math.max(1, deps.hotLimit ?? DEFAULT_HOT_LIMIT);\n }\n\n get defaultWorkspace(): WorkspaceRef {\n return this.deps.defaultWorkspace;\n }\n\n subscribe(listener: (event: TaggedAgentEvent) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n activeKeyOf(): string | undefined {\n return this.activeKey;\n }\n\n active(): AgentGateway | undefined {\n return this.activeKey === undefined ? undefined : this.hot.get(this.activeKey)?.gateway;\n }\n\n requireActive(): AgentGateway {\n const gateway = this.active();\n if (!gateway) {\n throw new AgentUnavailableError('No active Morse session. Open a session first.');\n }\n return gateway;\n }\n\n stateOf(key: string): AgentSessionState | undefined {\n return this.hot.get(key)?.state;\n }\n\n hotKeys(): string[] {\n return [...this.recency];\n }\n\n /**\n * Forgets the session-less probe caches, so the next `draftDefaults()` runs pi\n * again. The model picker calls it while the panel is still on a draft, where\n * spawning nothing would otherwise freeze the catalog for the host's lifetime.\n */\n refreshDraftDefaults(): void {\n this.draftCache.clear();\n this.draftProbes.clear();\n }\n\n /**\n * What the backend offers before any session exists: the model catalog,\n * thinking levels and commands of an untouched spawn. Probed once per model \u2014\n * never per client \u2014 from a gateway that never records itself (pi\n * `--no-session`), so the empty state can ship a real model picker without\n * creating a session. Passing `model` asks for that model's thinking levels,\n * which pi reports per current model.\n */\n async draftDefaults(model?: ModelRef): Promise<AgentSessionState | undefined> {\n const key = model ? `${model.provider}/${model.id}` : '';\n const cached = this.draftCache.get(key);\n if (cached) {\n return cached;\n }\n let probe = this.draftProbes.get(key);\n if (!probe) {\n // A rejected probe must not be memoized: the setup screen's Retry (or the\n // next draft) has to be able to reach pi once the user installed it.\n probe = this.probeDraft(model)\n .then((state) => {\n if (state) {\n this.draftCache.set(key, state);\n }\n return state;\n })\n .catch((error: unknown) => {\n this.draftProbes.delete(key);\n throw error;\n });\n this.draftProbes.set(key, probe);\n }\n return probe;\n }\n\n /** Opens a new (or resumes an existing) session and makes it active. */\n async open(options: { workspace?: WorkspaceRef; sessionId?: string } = {}): Promise<OpenedSession> {\n const workspace = options.workspace ?? this.deps.defaultWorkspace;\n const gateway = await this.deps.factory.create({\n workspace,\n sessionId: options.sessionId,\n });\n const state = await gateway.state();\n const key = state.sessionId ?? this.nextSyntheticKey(workspace);\n\n const alreadyHot = this.hot.get(key);\n if (alreadyHot) {\n // Same session file: keep the running process and drop the duplicate.\n await gateway.dispose().catch((error: unknown) => {\n this.deps.logger.warn('Failed to dispose a duplicate agent gateway', error);\n });\n this.touch(key);\n this.activeKey = key;\n return { key, state: alreadyHot.state, reused: true };\n }\n\n this.hot.set(key, {\n key,\n gateway,\n workspace,\n detach: gateway.subscribe((event) => {\n this.emit({ sessionKey: key, event: this.absorb(key, event) });\n }),\n state,\n });\n this.touch(key);\n this.activeKey = key;\n await this.evictBeyondLimit();\n return { key, state, reused: false };\n }\n\n /** Switches to a hot session, or opens it when it is not running anymore. */\n async activate(key: string, workspace?: WorkspaceRef): Promise<OpenedSession> {\n const existing = this.hot.get(key);\n if (existing) {\n this.touch(key);\n this.activeKey = key;\n return { key, state: existing.state, reused: true };\n }\n return this.open({ workspace, sessionId: key });\n }\n\n /**\n * Forks the active session before a past user message. pi's fork writes a\n * *new* session file while reusing the same subprocess, so the hot entry is\n * re-keyed to the new session id instead of respawning the agent. The\n * controller truncates its transcript to match, then sends the edited prompt.\n */\n async forkActive(entryId: string): Promise<ForkedSession> {\n const previousKey = this.activeKey;\n const hot = previousKey === undefined ? undefined : this.hot.get(previousKey);\n if (previousKey === undefined || !hot) {\n throw new AgentUnavailableError('No active Morse session. Open a session first.');\n }\n const result = await hot.gateway.fork(entryId);\n // A cancelled fork leaves the session untouched and still reports its state.\n const state = await hot.gateway.state();\n if (result.cancelled) {\n return { key: previousKey, previousKey, state, text: result.text, cancelled: true };\n }\n const key = state.sessionId ?? this.nextSyntheticKey(hot.workspace);\n if (key === previousKey) {\n return { key, previousKey, state, text: result.text, cancelled: false };\n }\n\n hot.detach();\n this.hot.delete(previousKey);\n this.hot.set(key, {\n key,\n gateway: hot.gateway,\n workspace: hot.workspace,\n state,\n detach: hot.gateway.subscribe((event) => {\n this.emit({ sessionKey: key, event: this.absorb(key, event) });\n }),\n });\n const index = this.recency.indexOf(previousKey);\n if (index >= 0) {\n this.recency[index] = key;\n } else {\n this.recency.push(key);\n }\n this.activeKey = key;\n return { key, previousKey, state, text: result.text, cancelled: false };\n }\n\n async close(key: string): Promise<void> {\n const session = this.hot.get(key);\n this.forget(key);\n if (!session) {\n return;\n }\n session.detach();\n await session.gateway.dispose().catch((error: unknown) => {\n this.deps.logger.warn('Failed to dispose an agent gateway', error);\n });\n }\n\n /**\n * Deletes a session for good: its hot process first (it may hold the file\n * open), then the stored conversation through the catalog. Storage rules stay\n * in the adapter; this use case owns only the order and the refusal when the\n * host cannot delete at all.\n */\n async remove(key: string): Promise<void> {\n await this.close(key);\n const remove = this.deps.catalog.remove;\n if (!remove) {\n throw new UnsupportedByHostError('This host cannot delete sessions.');\n }\n await remove.call(this.deps.catalog, key);\n }\n\n listSessions(): Promise<SessionSummary[]> {\n return this.deps.catalog.list();\n }\n\n /** Projects are the directories pi has session buckets for. */\n async listProjects(sessions?: readonly SessionSummary[]): Promise<ProjectSummary[]> {\n const all = sessions ?? (await this.listSessions());\n const projects = new Map<string, ProjectSummary>();\n for (const session of all) {\n if (session.cwd.length === 0) {\n continue;\n }\n const existing = projects.get(session.cwd);\n if (existing) {\n existing.sessionCount += 1;\n existing.lastUsedAt = Math.max(existing.lastUsedAt, session.updatedAt);\n continue;\n }\n projects.set(session.cwd, {\n path: session.cwd,\n name: baseName(session.cwd),\n sessionCount: 1,\n lastUsedAt: session.updatedAt,\n });\n }\n const defaultPath = this.deps.defaultWorkspace.cwd;\n if (!projects.has(defaultPath)) {\n projects.set(defaultPath, {\n path: defaultPath,\n name: this.deps.defaultWorkspace.name,\n sessionCount: 0,\n lastUsedAt: 0,\n });\n }\n return [...projects.values()].sort((left, right) => right.lastUsedAt - left.lastUsedAt);\n }\n\n async dispose(): Promise<void> {\n const sessions = [...this.hot.values()];\n this.hot.clear();\n this.recency.length = 0;\n this.listeners.clear();\n this.activeKey = undefined;\n this.draftCache.clear();\n this.draftProbes.clear();\n await Promise.all(\n sessions.map((session) => {\n session.detach();\n return session.gateway.dispose().catch((error: unknown) => {\n this.deps.logger.warn('Failed to dispose an agent gateway', error);\n });\n }),\n );\n }\n\n private absorb(key: string, event: AgentEvent): AgentEvent {\n const session = this.hot.get(key);\n if (session && (event.type === 'agent/ready' || event.type === 'agent/state')) {\n session.state = event.state;\n }\n return event;\n }\n\n private emit(event: TaggedAgentEvent): void {\n for (const listener of [...this.listeners]) {\n try {\n listener(event);\n } catch (error: unknown) {\n this.deps.logger.error('Agent event listener failed', error);\n }\n }\n }\n\n private touch(key: string): void {\n const index = this.recency.indexOf(key);\n if (index >= 0) {\n this.recency.splice(index, 1);\n }\n this.recency.push(key);\n }\n\n private forget(key: string): void {\n const index = this.recency.indexOf(key);\n if (index >= 0) {\n this.recency.splice(index, 1);\n }\n this.hot.delete(key);\n if (this.activeKey === key) {\n this.activeKey = undefined;\n }\n }\n\n /**\n * Keeps at most `hotLimit` processes alive, retiring the coldest one that is\n * not working. A running session is never killed: when every candidate is\n * mid-run the registry goes over the limit instead, because dropping a\n * parallel conversation is worse than a few extra processes.\n */\n private async evictBeyondLimit(): Promise<void> {\n while (this.recency.length > this.hotLimit) {\n const coldest = this.recency.find((key) => key !== this.activeKey && !this.isWorking(key));\n if (coldest === undefined) {\n return;\n }\n this.deps.logger.info(`Retiring idle session ${coldest} (hot limit ${this.hotLimit})`);\n await this.close(coldest);\n }\n }\n\n /** A session the agent is answering in is off limits for the LRU. */\n private isWorking(key: string): boolean {\n return this.hot.get(key)?.state.streaming === true;\n }\n\n private nextSyntheticKey(workspace: WorkspaceRef): string {\n this.syntheticKeys += 1;\n return `${workspace.cwd}#ephemeral-${this.syntheticKeys}`;\n }\n\n /** Asks the factory for a never-recording gateway and snapshots its state. */\n private async probeDraft(model?: ModelRef): Promise<AgentSessionState | undefined> {\n const factory = this.deps.factory.probeDefaults;\n if (!factory) {\n return undefined;\n }\n try {\n const gateway = await factory.call(this.deps.factory, {\n workspace: this.deps.defaultWorkspace,\n ...(model ? { model } : {}),\n });\n const state = await gateway.state();\n await gateway.dispose();\n return state;\n } catch (error: unknown) {\n // A missing (or un-spawnable) pi is not \"no catalog\": the empty panel has\n // to say so on load, not only once the first prompt fails. Every other\n // probe failure \u2014 a timeout, a protocol slip \u2014 keeps the pickers empty and\n // lets the real (recorded) spawn surface it with its actionable hint.\n if (error instanceof AgentUnavailableError) {\n throw error;\n }\n this.deps.logger.warn('Could not probe the draft model catalog', error);\n return undefined;\n }\n }\n}\n\nfunction baseName(path: string): string {\n const parts = path.split(/[\\\\/]/).filter((part) => part.length > 0);\n return parts.at(-1) ?? path;\n}\n", "import type { AgentSessionState, ChatPin, ModelRef, NoticeLevel, ThinkingLevel } from '../domain.js';\nimport type { MorseLogger } from '../logger.js';\nimport type { PromptDisposition, PromptImage, PromptMode } from '../session/service.js';\n\n/**\n * Local port (R10): the slice of the agent the chat use case needs. The concrete\n * `SessionRegistry` satisfies it structurally \u2014 the chat module never imports the\n * session module's class.\n */\nexport interface ChatAgent {\n state(): Promise<AgentSessionState>;\n prompt(\n text: string,\n mode: PromptMode,\n images?: PromptImage[],\n pins?: ChatPin[],\n ): Promise<PromptDisposition>;\n abort(): Promise<void>;\n setModel(model: ModelRef): Promise<void>;\n setThinkingLevel(level: ThinkingLevel): Promise<void>;\n compact(customInstructions?: string): Promise<void>;\n}\n\n/** Also satisfied structurally by `SessionRegistry`. */\nexport interface ChatAgentHolder {\n requireActive(): ChatAgent;\n}\n\nexport interface PromptOutcome {\n accepted: boolean;\n disposition?: PromptDisposition;\n reason?: string;\n}\n\nexport interface ChatServiceDeps {\n agent: ChatAgentHolder;\n logger: MorseLogger;\n onNotice?: (level: NoticeLevel, text: string) => void;\n}\n\n/**\n * Chat use case: validates prompts and decides between running, steering and\n * following up. Shared by every host.\n *\n * Attachments (images and pins) travel on the turn as attachments \u2014 the prompt\n * text stays exactly what the user typed. A host that wants the agent to know\n * about an editor selection pins it like any other attachment instead of\n * prepending an invisible \"context from the editor\" preamble: preamble text\n * cannot be untangled later, and it made every persisted session carry words\n * the user never wrote.\n */\nexport class ChatService {\n constructor(private readonly deps: ChatServiceDeps) {}\n\n async prompt(\n text: string,\n mode: PromptMode = 'new',\n images?: PromptImage[],\n pins?: ChatPin[],\n ): Promise<PromptOutcome> {\n const trimmed = text.trim();\n // Attachments count as content: an image (or a pinned selection) is allowed\n // to travel on its own, without the user being forced to type filler text.\n const hasAttachments =\n (images !== undefined && images.length > 0) || (pins !== undefined && pins.length > 0);\n if (trimmed.length === 0 && !hasAttachments) {\n this.notice('warn', 'Nothing to send \u2014 the prompt is empty.');\n return { accepted: false, reason: 'empty-prompt' };\n }\n\n let agent: ChatAgent;\n try {\n agent = this.deps.agent.requireActive();\n } catch (error: unknown) {\n this.deps.logger.error('Prompt rejected: no active agent session', error);\n return { accepted: false, reason: 'no-session' };\n }\n\n let effectiveMode = mode;\n const state = await agent.state();\n if (state.streaming && effectiveMode === 'new') {\n effectiveMode = 'followUp';\n this.notice('info', 'The agent is still working \u2014 queued as a follow-up.');\n }\n\n const disposition = await agent.prompt(trimmed, effectiveMode, images, pins);\n return { accepted: true, disposition };\n }\n\n async abort(): Promise<void> {\n await this.deps.agent.requireActive().abort();\n }\n\n async setModel(model: ModelRef): Promise<void> {\n await this.deps.agent.requireActive().setModel(model);\n }\n\n async setThinkingLevel(level: ThinkingLevel): Promise<void> {\n await this.deps.agent.requireActive().setThinkingLevel(level);\n }\n\n /** Shrinks the conversation context (`/compact` in the TUI). */\n async compact(customInstructions?: string): Promise<void> {\n await this.deps.agent.requireActive().compact(customInstructions);\n }\n\n async state(): Promise<AgentSessionState> {\n return this.deps.agent.requireActive().state();\n }\n\n private notice(level: NoticeLevel, text: string): void {\n this.deps.onNotice?.(level, text);\n }\n}", "/**\n * Subset of the pi RPC protocol this adapter needs.\n *\n * Mirrors the documented contract (docs/rpc-commands.md, docs/json.md,\n * docs/rpc-extension-ui.md) rather than depending on the very large\n * `@earendil-works/pi-coding-agent` package, which stays a runtime-only,\n * spawn-based dependency.\n */\n\nexport interface RpcImage {\n type: 'image';\n data: string;\n mimeType: string;\n}\n\nexport type RpcCommand =\n | { type: 'prompt'; message: string; images?: RpcImage[]; streamingBehavior?: 'steer' | 'followUp' }\n | { type: 'steer'; message: string; images?: RpcImage[] }\n | { type: 'follow_up'; message: string; images?: RpcImage[] }\n | { type: 'abort' }\n | { type: 'new_session'; parentSession?: string }\n | { type: 'switch_session'; sessionPath: string }\n | { type: 'get_state' }\n | { type: 'get_messages' }\n | { type: 'get_entries'; since?: string }\n | { type: 'get_available_models' }\n | { type: 'get_available_thinking_levels' }\n | { type: 'get_commands' }\n | { type: 'get_session_stats' }\n | { type: 'fork'; entryId: string }\n | { type: 'get_fork_messages' }\n | { type: 'set_model'; provider: string; modelId: string }\n | { type: 'set_thinking_level'; level: string }\n | { type: 'compact'; customInstructions?: string }\n | { type: 'set_session_name'; name: string }\n | { type: 'bash'; command: string };\n\nexport interface RpcResponse {\n type: 'response';\n id?: string;\n command: string;\n success: boolean;\n data?: unknown;\n error?: string;\n}\n\nexport interface RpcModel {\n id: string;\n name?: string;\n provider: string;\n api?: string;\n reasoning?: boolean;\n contextWindow?: number;\n maxTokens?: number;\n}\n\n/** `get_commands`: extension commands, prompt templates and skills. */\nexport interface RpcCommandInfo {\n name?: string;\n description?: string;\n source?: string;\n /** Where pi loaded the resource from; a prompt template's `.md` lives at `path`. */\n sourceInfo?: { path?: string; scope?: string; origin?: string };\n}\n\nexport interface RpcSessionStateData {\n model?: RpcModel;\n thinkingLevel?: string;\n isStreaming?: boolean;\n isCompacting?: boolean;\n sessionFile?: string;\n sessionId?: string;\n sessionName?: string;\n messageCount?: number;\n pendingMessageCount?: number;\n}\n\nexport interface RpcUsage {\n input?: number;\n output?: number;\n totalTokens?: number;\n cacheRead?: number;\n cacheWrite?: number;\n /** Reasoning tokens; a subset of `output`. */\n reasoning?: number;\n}\n\nexport interface RpcContextUsage {\n tokens?: number | null;\n contextWindow?: number;\n percent?: number | null;\n}\n\n/** `get_session_stats`: cumulative tokens/cost, counts, and the live context. */\nexport interface RpcSessionStatsData {\n tokens?: RpcUsage & { total?: number };\n cost?: number;\n contextUsage?: RpcContextUsage;\n userMessages?: number;\n assistantMessages?: number;\n toolCalls?: number;\n toolResults?: number;\n totalMessages?: number;\n}\n\nexport interface RpcAssistantMessageEvent {\n type: string;\n contentIndex?: number;\n delta?: string;\n id?: string;\n toolName?: string;\n content?: string;\n}\n\nexport interface RpcMessage {\n role?: string;\n content?: unknown;\n model?: string;\n provider?: string;\n stopReason?: string;\n usage?: RpcUsage;\n [key: string]: unknown;\n}\n\nexport interface RpcExtensionUiRequest {\n type: 'extension_ui_request';\n id: string;\n method: string;\n title?: string;\n message?: string;\n options?: string[];\n placeholder?: string;\n prefill?: string;\n notifyType?: string;\n statusKey?: string;\n statusText?: string;\n widgetKey?: string;\n widgetLines?: string[];\n text?: string;\n}\n\nexport interface RpcExtensionUiResponse {\n type: 'extension_ui_response';\n id: string;\n value?: string;\n confirmed?: boolean;\n cancelled?: boolean;\n}\n\n/** Anything else pi writes on stdout: a session event we may or may not map. */\nexport type RpcRecord = (RpcResponse | RpcExtensionUiRequest | { type: string }) &\n Record<string, unknown>;\n\nexport function asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : undefined;\n}\n\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nexport function asArray(value: unknown): unknown[] | undefined {\n return Array.isArray(value) ? value : undefined;\n}\n", "import type {\n AgentEvent,\n AgentHistoryEntry,\n AgentSessionState,\n ChatPin,\n NoticeLevel,\n PromptImage,\n TokenUsage,\n} from '@morse/core';\nimport {\n asArray,\n asNumber,\n asRecord,\n asString,\n type RpcAssistantMessageEvent,\n type RpcMessage,\n type RpcRecord,\n type RpcSessionStatsData,\n type RpcUsage,\n} from './internal/rpc-types.js';\n\nexport interface MappedRecord {\n events: AgentEvent[];\n state?: Partial<AgentSessionState>;\n}\n\n/**\n * Pure mapping from pi session events (docs/json.md) to Morse domain events.\n * No subprocess, no clock: the caller injects `now`, which keeps it trivially\n * testable and shared by every pi transport.\n */\nexport function mapSessionEvent(record: RpcRecord, now: () => number): MappedRecord {\n switch (record.type) {\n case 'agent_start':\n return { events: [{ type: 'agent/run-start', at: now() }], state: { streaming: true } };\n case 'message_update':\n return mapMessageUpdate(record, now);\n case 'message_end':\n return mapMessageEnd(record, now);\n case 'tool_execution_start':\n return mapToolStart(record, now);\n case 'tool_execution_update':\n return mapToolUpdate(record, now);\n case 'tool_execution_end':\n return mapToolEnd(record, now);\n case 'compaction_start':\n return { events: [notice('info', 'Compacting the conversation context\u2026', now)] };\n case 'compaction_end':\n return mapCompactionEnd(record, now);\n case 'auto_retry_start': {\n const attempt = numberField(record, 'attempt');\n const max = numberField(record, 'maxAttempts');\n const reason = stringField(record, 'errorMessage');\n const label = attempt && max ? ` (attempt ${attempt}/${max})` : '';\n return { events: [notice('warn', `Retrying${label}: ${reason ?? 'provider error'}`, now)] };\n }\n case 'auto_retry_end':\n return record['success'] === true\n ? { events: [] }\n : {\n events: [\n notice('error', stringField(record, 'finalError') ?? 'Retries exhausted.', now),\n ],\n };\n case 'extension_error':\n return {\n events: [\n notice(\n 'error',\n `pi extension failed: ${stringField(record, 'error') ?? 'unknown error'}`,\n now,\n ),\n ],\n };\n default:\n return { events: [] };\n }\n}\n\n/**\n * One entry of pi's session tree (`get_entries`): messages interleaved with\n * compaction/branch-summary markers. Unlike `get_messages`, this carries the\n * FULL history pre-dating compaction, so a resumed session can show the whole\n * conversation with the compacted span marked instead of hidden.\n */\nexport function toEntryHistory(entries: readonly unknown[]): AgentHistoryEntry[] {\n const history: AgentHistoryEntry[] = [];\n /** Tool entries by provider call id, so the result can fill in its output. */\n const toolEntries = new Map<string, Extract<AgentHistoryEntry, { role: 'tool' }>>();\n\n for (const raw of entries) {\n const entry = asRecord(raw);\n if (!entry) {\n continue;\n }\n switch (asString(entry['type'])) {\n case 'message': {\n const message = asRecord(entry['message']);\n if (message) {\n collectMessage(message, history, toolEntries);\n }\n break;\n }\n case 'compaction': {\n const at = entryTimestamp(entry['timestamp']);\n history.push({\n role: 'compaction',\n summary: asString(entry['summary']) ?? '',\n tokensBefore: asNumber(entry['tokensBefore']),\n ...(at !== undefined ? { at } : {}),\n });\n break;\n }\n case 'branch_summary': {\n const summary = asString(entry['summary']);\n if (!summary) {\n break;\n }\n const at = entryTimestamp(entry['timestamp']);\n history.push({\n role: 'user',\n text: `*The conversation briefly explored another branch and returned with this summary:*\n\n${summary}`,\n ...(at !== undefined ? { at } : {}),\n });\n break;\n }\n default:\n break;\n }\n }\n\n return history;\n}\n\n/** A flat message list (`get_messages`) seen as entries, keeping the old shape. */\nexport function toHistory(messages: readonly unknown[]): AgentHistoryEntry[] {\n return toEntryHistory(\n messages.map((message) => ({ type: 'message', message })),\n );\n}\n\n/**\n * The branch the leaf entry sits on, keeping append order: walking back through\n * `parentId` from the leaf drops abandoned branches (renamed/re-edited paths)\n * while the full pre-compaction history stays in. When the leaf (or its chain)\n * is unresolvable, everything is passed through \u2014 showing more beats hiding a\n * whole conversation behind a format quirk.\n */\nexport function activePathEntries(\n data: { entries?: unknown[]; leafId?: unknown } | undefined,\n): unknown[] {\n const records = (data?.entries ?? []).map(asRecord).filter(isDefined);\n const leafId = asString(data?.leafId);\n if (!leafId) {\n return records;\n }\n const byId = new Map(\n records\n .map((record) => [asString(record['id']), record] as const)\n .filter((pair): pair is [string, Record<string, unknown>] => pair[0] !== undefined),\n );\n if (!byId.has(leafId)) {\n return records;\n }\n const keep = new Set<string>([leafId]);\n let cursor: string | null = leafId;\n while (cursor !== null) {\n const parent = byId.get(cursor);\n const parentId = parent ? (asString(parent['parentId']) ?? null) : null;\n if (parentId === null || keep.has(parentId)) {\n break;\n }\n keep.add(parentId);\n cursor = parentId;\n }\n return records.filter((record) => keep.has(asString(record['id']) ?? ''));\n}\n\nfunction collectMessage(\n message: Record<string, unknown>,\n entries: AgentHistoryEntry[],\n toolEntries: Map<string, Extract<AgentHistoryEntry, { role: 'tool' }>>,\n): void {\n const role = asString(message['role']);\n\n if (role === 'toolResult') {\n const toolCallId = asString(message['toolCallId']);\n const entry = toolCallId ? toolEntries.get(toolCallId) : undefined;\n if (entry) {\n const output = contentToText(message['content']);\n if (output.length > 0) {\n entry.output = output;\n }\n entry.status = message['isError'] === true ? 'error' : 'ok';\n }\n return;\n }\n\n if (role !== 'user' && role !== 'assistant') {\n return;\n }\n\n let text = contentToText(message['content']);\n const thinking = contentToThinking(message['content']);\n let pins: ChatPin[] | undefined;\n const images = role === 'user' ? contentToImages(message['content']) : [];\n if (role === 'user') {\n // The transcript keeps what the user actually typed: `@mention` lines\n // Morse appended for pins come back out and render as attachment chips\n // again, instead of a blob of mentions inside the message.\n const split = splitTrailingMentions(text);\n text = split.text;\n pins = split.pins.length > 0 ? split.pins : undefined;\n }\n if (\n text.length > 0 ||\n thinking.length > 0 ||\n (pins !== undefined && pins.length > 0) ||\n images.length > 0\n ) {\n entries.push({\n role,\n text,\n thinking: thinking.length > 0 ? thinking : undefined,\n ...(images.length > 0 ? { images } : {}),\n ...(pins !== undefined ? { pins } : {}),\n model: asString(message['model']),\n at: asNumber(message['timestamp']),\n });\n }\n\n if (role === 'assistant') {\n for (const call of toolCallsOf(message['content'])) {\n const entry: Extract<AgentHistoryEntry, { role: 'tool' }> = {\n role: 'tool',\n toolCallId: call.id,\n name: call.name,\n title: describeToolCall(call.name, call.arguments),\n input: safeStringify(call.arguments),\n status: 'ok',\n at: asNumber(message['timestamp']),\n };\n toolEntries.set(call.id, entry);\n entries.push(entry);\n }\n }\n}\n\ninterface HistoryToolCall {\n id: string;\n name: string;\n arguments: Record<string, unknown> | undefined;\n}\n\nfunction toolCallsOf(content: unknown): HistoryToolCall[] {\n const blocks = asArray(content);\n if (!blocks) {\n return [];\n }\n const calls: HistoryToolCall[] = [];\n for (const block of blocks) {\n const record = asRecord(block);\n if (!record || asString(record['type']) !== 'toolCall') {\n continue;\n }\n const name = asString(record['name']);\n if (!name) {\n continue;\n }\n calls.push({\n id: asString(record['id']) ?? `call-${calls.length}`,\n name,\n arguments: asRecord(record['arguments']),\n });\n }\n return calls;\n}\n\nfunction mapMessageUpdate(record: RpcRecord, now: () => number): MappedRecord {\n const assistant = asRecord(record['assistantMessageEvent']) as RpcAssistantMessageEvent | undefined;\n if (!assistant) {\n return { events: [] };\n }\n if (assistant.type === 'text_delta' && assistant.delta) {\n return {\n events: [{ type: 'agent/delta', at: now(), channel: 'text', delta: assistant.delta }],\n };\n }\n if (assistant.type === 'thinking_delta' && assistant.delta) {\n return {\n events: [{ type: 'agent/delta', at: now(), channel: 'thinking', delta: assistant.delta }],\n };\n }\n return { events: [] };\n}\n\nfunction mapMessageEnd(record: RpcRecord, now: () => number): MappedRecord {\n const message = asRecord(record['message']) as RpcMessage | undefined;\n if (!message || message.role !== 'assistant') {\n return { events: [] };\n }\n\n const events: AgentEvent[] = [];\n // Deliberately no `streaming: false` here. A run contains many turns\n // (thinking -> tool -> thinking -> tool), so the flag means \"the agent is\n // working\", exactly like pi's own `isStreaming`; only `agent_settled` and a\n // fatal error end it. Flipping it per message made every progress indicator\n // (and the process timeline) blink once per step.\n const state: Partial<AgentSessionState> = {};\n const usage = toUsage(message.usage);\n if (usage) {\n // The panel's \"last assistant message\" breakdown; cumulative totals come\n // from `get_session_stats` and are set separately.\n state.lastUsage = usage;\n }\n\n const text = contentToText(message.content);\n const thinking = contentToThinking(message.content);\n if (text.length > 0 || thinking.length > 0) {\n events.push({ type: 'agent/message', at: now(), text, thinking });\n }\n\n if (message.stopReason === 'aborted') {\n state.streaming = false;\n events.push({ type: 'agent/run-end', at: now(), reason: 'aborted' });\n } else if (message.stopReason === 'error') {\n events.push(notice('error', 'The model request failed.', now));\n }\n\n return { events, state };\n}\n\nfunction mapToolStart(record: RpcRecord, now: () => number): MappedRecord {\n const toolCallId = stringField(record, 'toolCallId') ?? `tool-${now()}`;\n const toolName = stringField(record, 'toolName') ?? 'tool';\n const args = asRecord(record['args']);\n return {\n events: [\n {\n type: 'agent/tool-start',\n at: now(),\n toolCallId,\n name: toolName,\n title: describeToolCall(toolName, args),\n input: safeStringify(record['args']),\n },\n ],\n };\n}\n\nfunction mapToolUpdate(record: RpcRecord, now: () => number): MappedRecord {\n const toolCallId = stringField(record, 'toolCallId');\n if (!toolCallId) {\n return { events: [] };\n }\n return {\n events: [\n {\n type: 'agent/tool-update',\n at: now(),\n toolCallId,\n output: resultToText(record['partialResult']),\n status: 'running',\n },\n ],\n };\n}\n\nfunction mapToolEnd(record: RpcRecord, now: () => number): MappedRecord {\n const toolCallId = stringField(record, 'toolCallId');\n if (!toolCallId) {\n return { events: [] };\n }\n return {\n events: [\n {\n type: 'agent/tool-end',\n at: now(),\n toolCallId,\n status: record['isError'] === true ? 'error' : 'ok',\n output: resultToText(record['result']),\n },\n ],\n };\n}\n\nfunction mapCompactionEnd(record: RpcRecord, now: () => number): MappedRecord {\n if (record['aborted'] === true) {\n return { events: [notice('warn', 'Compaction was aborted.', now)] };\n }\n const error = stringField(record, 'errorMessage');\n if (error) {\n return { events: [notice('error', `Compaction failed: ${error}`, now)] };\n }\n // Success records the boundary as its own marker row (summary included),\n // replacing the old \"Context compacted.\" notice: the divider now sits where\n // the fold happened, in the live transcript as well, and the summary it\n // carries is the one the agent went forward with.\n const result = asRecord(record['result']);\n return {\n events: [\n {\n type: 'agent/compaction',\n at: now(),\n summary: asString(result?.['summary']) ?? '',\n tokensBefore: asNumber(result?.['tokensBefore']),\n },\n ],\n };\n}\n\nfunction notice(level: NoticeLevel, text: string, now: () => number): AgentEvent {\n return { type: 'agent/notice', at: now(), level, text };\n}\n\nfunction stringField(record: RpcRecord, key: string): string | undefined {\n return asString(record[key]);\n}\n\nfunction numberField(record: RpcRecord, key: string): number | undefined {\n const value = record[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction toUsage(usage: (RpcUsage & { total?: number }) | undefined): TokenUsage | undefined {\n if (!usage) {\n return undefined;\n }\n const input = usage.input ?? 0;\n const output = usage.output ?? 0;\n const cacheRead = usage.cacheRead ?? 0;\n const cacheWrite = usage.cacheWrite ?? 0;\n const reasoning = usage.reasoning ?? 0;\n return {\n inputTokens: input,\n outputTokens: output,\n totalTokens: usage.totalTokens ?? usage.total ?? input + output,\n ...(cacheRead > 0 ? { cacheReadTokens: cacheRead } : {}),\n ...(cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}),\n ...(reasoning > 0 ? { reasoningTokens: reasoning } : {}),\n };\n}\n\n/**\n * `get_session_stats` -> the state patch the footer renders: cumulative tokens,\n * cumulative cost, and how full the context window is. Cumulative (not\n * per-message) is what pi's own footer shows, so Morse shows the same numbers.\n */\nexport function toSessionStats(\n stats: RpcSessionStatsData | undefined,\n): Partial<AgentSessionState> {\n if (!stats) {\n return {};\n }\n const patch: Partial<AgentSessionState> = {};\n const usage = toUsage(stats.tokens);\n if (usage) {\n patch.usage = usage;\n }\n if (typeof stats.cost === 'number' && Number.isFinite(stats.cost)) {\n patch.costUsd = stats.cost;\n }\n const context = stats.contextUsage;\n if (context && typeof context.contextWindow === 'number') {\n patch.contextUsage = {\n tokens: typeof context.tokens === 'number' ? context.tokens : null,\n contextWindow: context.contextWindow,\n percent: typeof context.percent === 'number' ? context.percent : null,\n };\n }\n if (typeof stats.totalMessages === 'number') {\n patch.counts = {\n userMessages: stats.userMessages ?? 0,\n assistantMessages: stats.assistantMessages ?? 0,\n toolCalls: stats.toolCalls ?? 0,\n toolResults: stats.toolResults ?? 0,\n totalMessages: stats.totalMessages,\n };\n }\n return patch;\n}\n\nexport function contentToText(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n const blocks = asArray(content);\n if (!blocks) {\n return '';\n }\n const parts: string[] = [];\n for (const block of blocks) {\n const record = asRecord(block);\n if (!record || record['type'] !== 'text') {\n continue;\n }\n parts.push(asString(record['text']) ?? '');\n }\n return parts.join('');\n}\n\nexport function contentToThinking(content: unknown): string {\n const blocks = asArray(content);\n if (!blocks) {\n return '';\n }\n const parts: string[] = [];\n for (const block of blocks) {\n const record = asRecord(block);\n if (!record) {\n continue;\n }\n const type = asString(record['type']);\n if (type !== 'thinking' && type !== 'reasoning') {\n continue;\n }\n parts.push(asString(record['thinking']) ?? asString(record['text']) ?? '');\n }\n return parts.join('');\n}\n\n/** Image content blocks (persisted attachments) of a user message. */\nexport function contentToImages(content: unknown): PromptImage[] {\n const blocks = asArray(content);\n if (!blocks) {\n return [];\n }\n const images: PromptImage[] = [];\n for (const block of blocks) {\n const record = asRecord(block);\n if (!record || asString(record['type']) !== 'image') {\n continue;\n }\n const data = asString(record['data']);\n if (!data) {\n continue;\n }\n images.push({ data, mimeType: asString(record['mimeType']) ?? 'image/png' });\n }\n return images;\n}\n\nconst PIN_MENTION = /^@([^\\s]+?)(?::(\\d+)-(\\d+))?$/;\n\n/**\n * Inverse of the adapter's `appendPinMentions`: the trailing `@path[:s-e]`\n * lines a prompt was sent with come back out, so a resumed session renders the\n * pins as attachment chips and the bubble keeps the user's own words only.\n */\nexport function splitTrailingMentions(\n text: string,\n): { text: string; pins: Array<{ path: string; startLine?: number; endLine?: number }> } {\n const lines = text.split('\\n');\n let cursor = lines.length;\n const pins: Array<{ path: string; startLine?: number; endLine?: number }> = [];\n while (cursor > 0) {\n const match = PIN_MENTION.exec(lines[cursor - 1] ?? '');\n const record = match\n ? {\n path: match[1] ?? '',\n ...(match[2] !== undefined ? { startLine: Number(match[2]) } : {}),\n ...(match[3] !== undefined ? { endLine: Number(match[3]) } : {}),\n }\n : undefined;\n // A pin path is a plain file path; a prose sentence with an `@` in the\n // middle (\"what about @me?\") does not end with one, so prose stays intact.\n if (!record || record.path.length === 0) {\n break;\n }\n pins.unshift(record);\n cursor -= 1;\n }\n if (cursor === lines.length) {\n return { text, pins: [] };\n }\n // Drop the single blank line the adapter put between the prose and the pins.\n let prose = lines.slice(0, cursor).join('\\n');\n while (prose.endsWith('\\n')) {\n prose = prose.slice(0, -1);\n }\n return { text: prose, pins };\n}\n\nexport function resultToText(result: unknown): string | undefined {\n if (result === undefined || result === null) {\n return undefined;\n }\n if (typeof result === 'string') {\n return result;\n }\n const record = asRecord(result);\n if (!record) {\n return safeStringify(result);\n }\n const text = contentToText(record['content']);\n return text.length > 0 ? text : safeStringify(record);\n}\n\nexport function describeToolCall(toolName: string, args: Record<string, unknown> | undefined): string {\n if (!args) {\n return toolName;\n }\n const command = asString(args['command']) ?? asString(args['cmd']);\n if (toolName === 'bash' && command) {\n return `bash: ${truncate(command, 100)}`;\n }\n const path =\n asString(args['path']) ??\n asString(args['file_path']) ??\n asString(args['filePath']) ??\n asString(args['file']) ??\n asString(args['target']);\n if (path) {\n return `${toolName}: ${truncate(path, 100)}`;\n }\n const pattern = asString(args['pattern']) ?? asString(args['query']);\n if (pattern) {\n return `${toolName}: ${truncate(pattern, 100)}`;\n }\n return toolName;\n}\n\nfunction safeStringify(value: unknown): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n try {\n return typeof value === 'string' ? value : JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\nfunction truncate(value: string, max: number): string {\n const single = value.replace(/\\s+/g, ' ').trim();\n return single.length > max ? `${single.slice(0, max)}\u2026` : single;\n}\n\n/** Session entries timestamp in ISO (`2026-\u2026`); agent messages in Unix ms. */\nfunction entryTimestamp(value: unknown): number | undefined {\n const ms = asNumber(value);\n if (ms !== undefined) {\n return ms;\n }\n const iso = asString(value);\n if (iso) {\n const parsed = Date.parse(iso);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n return undefined;\n}\n\nfunction isDefined<T>(value: T | undefined): value is T {\n return value !== undefined;\n}\n", "import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { cleanSpawnEnv } from './spawn-env.js';\nimport { AgentProtocolError, AgentUnavailableError } from '@morse/core';\nimport { JsonlFramer } from './jsonl-framer.js';\nimport type { RpcCommand, RpcExtensionUiResponse, RpcRecord } from './rpc-types.js';\n\nexport interface PiRpcClientOptions {\n command: string;\n args: string[];\n cwd: string;\n env?: NodeJS.ProcessEnv;\n requestTimeoutMs?: number;\n onRecord: (record: RpcRecord) => void;\n onStderr?: (text: string) => void;\n onExit?: (info: { code: number | null; signal: NodeJS.Signals | null }) => void;\n}\n\ninterface PendingRequest {\n command: string;\n resolve: (data: unknown) => void;\n reject: (error: unknown) => void;\n timer: NodeJS.Timeout;\n}\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 30_000;\nconst SHUTDOWN_GRACE_MS = 2_000;\n\n/**\n * Minimal JSONL RPC client for `pi --mode rpc`.\n *\n * Deliberately hand-written: it keeps the extension bundle thin and pins the\n * protocol framing rules (LF only, strip CR, honour stdin backpressure, never\n * parse stderr as protocol data).\n */\nexport class PiRpcClient {\n private child: ChildProcessWithoutNullStreams | undefined;\n private readonly framer = new JsonlFramer();\n private nextId = 1;\n private readonly pending = new Map<string, PendingRequest>();\n private failed: Error | undefined;\n\n constructor(private readonly options: PiRpcClientOptions) {}\n\n get running(): boolean {\n return this.child !== undefined;\n }\n\n start(): void {\n if (this.child) {\n return;\n }\n this.failed = undefined;\n const child = spawn(this.options.command, this.options.args, {\n cwd: this.options.cwd,\n env: cleanSpawnEnv(this.options.env),\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n });\n this.child = child;\n child.stdout.on('data', (chunk: Buffer) => this.onStdout(chunk));\n child.stderr.on('data', (chunk: Buffer) => {\n this.options.onStderr?.(chunk.toString('utf8'));\n });\n child.on('error', (error: Error) => {\n this.failAll(\n new AgentUnavailableError(\n `Failed to start the pi agent (${this.options.command}): ${error.message}`,\n { cause: error },\n ),\n );\n });\n child.on('exit', (code, signal) => {\n this.child = undefined;\n this.failAll(\n new AgentUnavailableError(\n `The pi agent exited (code=${code === null ? 'null' : code}, signal=${signal ?? 'null'}).`,\n ),\n );\n this.options.onExit?.({ code, signal });\n });\n }\n\n async request<T>(\n command: RpcCommand,\n timeoutMs = this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,\n ): Promise<T> {\n if (!this.child) {\n throw this.failed ?? new AgentUnavailableError('The pi agent is not running.');\n }\n const id = `morse-${this.nextId++}`;\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(\n new AgentProtocolError(`pi did not answer \"${command.type}\" within ${timeoutMs} ms.`),\n );\n }, timeoutMs);\n this.pending.set(id, {\n command: command.type,\n resolve: (data) => resolve(data as T),\n reject,\n timer,\n });\n this.write({ id, ...command }, (error) => {\n this.settle(id, error);\n });\n });\n }\n\n /** Answers an `extension_ui_request`; never expects a matching response. */\n respond(response: RpcExtensionUiResponse): void {\n this.write(response);\n }\n\n async dispose(): Promise<void> {\n const child = this.child;\n if (!child) {\n return;\n }\n let exited = false;\n await new Promise<void>((resolve) => {\n const finish = (): void => {\n exited = true;\n resolve();\n };\n child.once('exit', finish);\n // Documented orderly shutdown: closing stdin lets pi dispose its runtime.\n child.stdin.end();\n setTimeout(resolve, SHUTDOWN_GRACE_MS).unref?.();\n });\n if (!exited) {\n child.kill();\n }\n this.child = undefined;\n }\n\n private onStdout(chunk: Buffer): void {\n for (const line of this.framer.push(chunk)) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n this.options.onStderr?.(`[morse] unreadable pi record: ${line}\\n`);\n continue;\n }\n this.handleRecord(parsed);\n }\n }\n\n private handleRecord(parsed: unknown): void {\n const record = parsed as RpcRecord;\n if (record !== null && typeof record === 'object' && record['type'] === 'response') {\n this.handleResponse(record);\n }\n this.options.onRecord(record);\n }\n\n private handleResponse(record: RpcRecord): void {\n const id = typeof record['id'] === 'string' ? record['id'] : undefined;\n if (!id) {\n return;\n }\n const pending = this.pending.get(id);\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n this.pending.delete(id);\n if (record['success'] === true) {\n pending.resolve(record['data']);\n return;\n }\n const error = typeof record['error'] === 'string' ? record['error'] : 'unknown error';\n pending.reject(new AgentProtocolError(`pi rejected \"${pending.command}\": ${error}`));\n }\n\n private write(payload: unknown, onError?: (error: Error) => void): void {\n const child = this.child;\n if (!child) {\n onError?.(new AgentUnavailableError('The pi agent is not running.'));\n return;\n }\n const frame = `${JSON.stringify(payload)}\\n`;\n child.stdin.write(frame, 'utf8', (error) => {\n if (error) {\n onError?.(error);\n }\n });\n }\n\n private settle(id: string, error: Error): void {\n const pending = this.pending.get(id);\n if (!pending) {\n return;\n }\n clearTimeout(pending.timer);\n this.pending.delete(id);\n pending.reject(error);\n }\n\n private failAll(error: Error): void {\n this.failed = error;\n for (const [id, pending] of [...this.pending]) {\n clearTimeout(pending.timer);\n this.pending.delete(id);\n pending.reject(error);\n }\n }\n}\n", "/**\n * The environment an external `pi` process should inherit.\n *\n * `node --watch` (and any process started with `fork`) puts an IPC channel into\n * the environment as `NODE_CHANNEL_FD` + `NODE_CHANNEL_SERIALIZATION_MODE`. A\n * child that inherits them tries to speak the parent's IPC protocol instead of\n * running as a plain process and dies with `write EINVAL` before it can do\n * anything. Node strips those variables for `fork`, but a plain `spawn` or\n * `execFile` does not, and both the RPC session and `pi mcp` run through those.\n *\n * Everything else \u2014 `PATH`, `HOME`, `PI_CODING_AGENT_DIR`, credentials \u2014 is left\n * untouched, so pi behaves exactly as it does from a shell.\n */\nexport function cleanSpawnEnv(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {\n const copy: NodeJS.ProcessEnv = { ...env };\n delete copy.NODE_CHANNEL_FD;\n delete copy.NODE_CHANNEL_SERIALIZATION_MODE;\n return copy;\n}\n", "import { StringDecoder } from 'node:string_decoder';\n\n/**\n * Strict JSONL framing for the pi RPC stream.\n *\n * The docs are explicit: split only on LF, strip an optional preceding CR, and\n * never use `readline` (it also splits on U+2028/U+2029, which are legal inside\n * JSON strings). `StringDecoder` keeps multi-byte UTF-8 sequences intact when a\n * chunk boundary falls inside a character.\n */\nexport class JsonlFramer {\n private readonly decoder = new StringDecoder('utf8');\n private buffer = '';\n\n push(chunk: Buffer | string): string[] {\n this.buffer += typeof chunk === 'string' ? chunk : this.decoder.write(chunk);\n const records: string[] = [];\n let newline = this.buffer.indexOf('\\n');\n while (newline !== -1) {\n const line = stripCarriageReturn(this.buffer.slice(0, newline));\n this.buffer = this.buffer.slice(newline + 1);\n if (line.trim().length > 0) {\n records.push(line);\n }\n newline = this.buffer.indexOf('\\n');\n }\n return records;\n }\n\n /** Returns a trailing record that never got its LF, if any. */\n flush(): string | undefined {\n const rest = stripCarriageReturn(this.buffer + this.decoder.end());\n this.buffer = '';\n return rest.trim().length > 0 ? rest : undefined;\n }\n}\n\nfunction stripCarriageReturn(line: string): string {\n return line.endsWith('\\r') ? line.slice(0, -1) : line;\n}\n", "import { accessSync, constants } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { delimiter, dirname, isAbsolute, join } from 'node:path';\nimport { AgentUnavailableError } from '@morse/core';\n\nexport interface ResolvePiOptions {\n /** Explicit path to the `pi` binary (setting `morse.pi.path` / `MORSE_PI_PATH`). */\n piPath?: string;\n /** Run the bundled RPC entry with node instead of the `pi` binary. */\n nodeEntryPath?: string;\n /** Override the session directory (`--session-dir`). */\n sessionDir?: string;\n /** Do not persist a session (`--no-session`). */\n noSession?: boolean;\n /** Resume an existing session file (`--session <path>`). */\n sessionPath?: string;\n extraArgs?: string[];\n env?: NodeJS.ProcessEnv;\n}\n\nexport type PiSpawnSource = 'configured' | 'path' | 'node-entry';\n\n/**\n * How to install the agent. The error message and the `remedy` both spell it out\n * from here, so what the setup screen offers and what the log says cannot drift.\n */\nexport const PI_INSTALL_COMMAND = 'npm install -g @earendil-works/pi-coding-agent';\n\nexport interface PiSpawn {\n command: string;\n args: string[];\n source: PiSpawnSource;\n}\n\nexport function buildRpcArgs(options: ResolvePiOptions): string[] {\n const args = ['--mode', 'rpc'];\n if (options.noSession) {\n args.push('--no-session');\n }\n if (options.sessionDir) {\n args.push('--session-dir', options.sessionDir);\n }\n if (options.sessionPath) {\n args.push('--session', options.sessionPath);\n }\n args.push(...(options.extraArgs ?? []));\n return args;\n}\n\n/**\n * Resolution order: explicit path, `node <rpc-entry>` when asked for, then the\n * `pi` binary on PATH. Everything else raises an actionable error instead of\n * failing later with a confusing spawn message.\n */\nexport function resolvePi(options: ResolvePiOptions = {}): PiSpawn {\n const env = options.env ?? process.env;\n const args = buildRpcArgs(options);\n const configured = options.piPath ?? env.MORSE_PI_PATH;\n if (configured) {\n return { command: configured, args, source: 'configured' };\n }\n\n const entry = options.nodeEntryPath ?? env.MORSE_PI_ENTRY;\n if (entry && isExecutableEntry(entry)) {\n return { command: process.execPath, args: [entry, ...args], source: 'node-entry' };\n }\n\n const onPath = findOnPath('pi', env);\n if (onPath) {\n return { command: onPath, args, source: 'path' };\n }\n\n throw new AgentUnavailableError(\n [\n 'The pi coding agent was not found.',\n `Install it (\\`${PI_INSTALL_COMMAND}\\`) or point Morse at it:`,\n '- VS Code: setting \"morse.pi.path\"',\n '- NestJS host: env MORSE_PI_PATH (or MORSE_PI_ENTRY for a bundled rpc-entry.js)',\n ].join('\\n'),\n { remedy: { install: PI_INSTALL_COMMAND } },\n );\n}\n\nexport interface PiCliSpawn {\n command: string;\n /** Arguments that select pi itself, before the subcommand (the node RPC entry). */\n baseArgs: string[];\n source: PiSpawnSource;\n}\n\n/**\n * The `pi` CLI, for the subcommands Morse runs *outside* a session (`pi mcp ...`).\n *\n * Not `resolvePi`: that one builds `--mode rpc`, which the CLI would treat as an\n * RPC session rather than a subcommand. A configured path or the binary on PATH\n * is used directly; a bundled RPC entry is only useful for `--mode rpc`, so its\n * sibling `cli.js` is preferred and a clear error is raised when there is none.\n */\nexport function resolvePiCli(options: ResolvePiOptions = {}): PiCliSpawn {\n const env = options.env ?? process.env;\n const configured = options.piPath ?? env.MORSE_PI_PATH;\n if (configured) {\n return { command: configured, baseArgs: [], source: 'configured' };\n }\n\n const onPath = findOnPath('pi', env);\n if (onPath) {\n return { command: onPath, baseArgs: [], source: 'path' };\n }\n\n const entry = options.nodeEntryPath ?? env.MORSE_PI_ENTRY;\n const cli = entry ? siblingCliEntry(entry) : undefined;\n if (cli) {\n return { command: process.execPath, baseArgs: [cli], source: 'node-entry' };\n }\n\n throw new AgentUnavailableError(\n [\n 'The pi CLI was not found, so Morse cannot run `pi mcp`.',\n `Install it (\\`${PI_INSTALL_COMMAND}\\`) or point Morse at it:`,\n '- VS Code: setting \"morse.pi.path\"',\n '- NestJS host: env MORSE_PI_PATH',\n ].join('\\n'),\n { remedy: { install: PI_INSTALL_COMMAND } },\n );\n}\n\n/** `.../dist/bundle/rpc-entry.js` -> `.../dist/bundle/cli.js` when it exists. */\nfunction siblingCliEntry(entry: string): string | undefined {\n const directory = dirname(entry);\n const cli = join(directory, 'cli.js');\n return entry.endsWith('rpc-entry.js') && isExecutableEntry(cli) ? cli : undefined;\n}\n\nexport function findOnPath(binary: string, env: NodeJS.ProcessEnv = process.env): string | undefined {\n const pathValue = env.PATH ?? env.Path ?? '';\n if (pathValue.length === 0) {\n return undefined;\n }\n const extensions =\n process.platform === 'win32'\n ? (env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';').filter((value) => value.length > 0)\n : [''];\n for (const directory of pathValue.split(delimiter)) {\n if (directory.length === 0) {\n continue;\n }\n for (const extension of extensions) {\n const candidate = join(directory, `${binary}${extension}`);\n if (isExecutableEntry(candidate)) {\n return candidate;\n }\n }\n }\n return undefined;\n}\n\nexport function resolveSessionDir(configured?: string, env: NodeJS.ProcessEnv = process.env): string {\n const fromEnv = env.PI_CODING_AGENT_SESSION_DIR;\n const value = configured ?? (fromEnv && fromEnv.length > 0 ? fromEnv : undefined);\n const base = value ?? join(homedir(), '.pi', 'agent', 'sessions');\n return isAbsolute(base) ? base : join(process.cwd(), base);\n}\n\nfunction isExecutableEntry(path: string): boolean {\n try {\n accessSync(path, constants.R_OK);\n return true;\n } catch {\n return false;\n }\n}\n", "import type {\n AgentGateway,\n AgentGatewayFactory,\n ModelRef,\n MorseLogger,\n WorkspaceRef,\n} from '@morse/core';\nimport { PiRpcAgent } from './pi-rpc-agent.js';\nimport { resolvePi, type ResolvePiOptions } from './internal/resolve-pi.js';\n\nexport interface PiRpcAgentFactoryOptions {\n piPath?: string;\n nodeEntryPath?: string;\n sessionDir?: string;\n noSession?: boolean;\n extraArgs?: string[];\n env?: NodeJS.ProcessEnv;\n requestTimeoutMs?: number;\n}\n\n/** Implements `AgentGatewayFactory` (declared by core) by spawning `pi --mode rpc`. */\nexport class PiRpcAgentFactory implements AgentGatewayFactory {\n constructor(\n private readonly options: PiRpcAgentFactoryOptions,\n private readonly logger: MorseLogger,\n ) {}\n\n async create(input: {\n workspace: WorkspaceRef;\n sessionId?: string;\n }): Promise<AgentGateway> {\n const resolveOptions: ResolvePiOptions = {\n piPath: this.options.piPath,\n nodeEntryPath: this.options.nodeEntryPath,\n sessionDir: this.options.sessionDir,\n noSession: this.options.noSession,\n // The catalog hands back session file paths as ids, which is what\n // `pi --session <path>` expects.\n sessionPath: input.sessionId,\n extraArgs: this.options.extraArgs,\n env: this.options.env,\n };\n return this.spawnAgent(resolveOptions, input.workspace, input.sessionId);\n }\n\n /**\n * A gateway that never records: pi is forced onto `--no-session` and no\n * session path is passed, so this spawn cannot touch the session list on\n * disk. Its only job is to answer state once \u2014 model catalog, thinking\n * levels, commands \u2014 for the draft (empty panel) state.\n */\n async probeDefaults(input: { workspace: WorkspaceRef; model?: ModelRef }): Promise<AgentGateway> {\n const resolveOptions: ResolvePiOptions = {\n piPath: this.options.piPath,\n nodeEntryPath: this.options.nodeEntryPath,\n sessionDir: this.options.sessionDir,\n noSession: true,\n extraArgs: this.options.extraArgs,\n env: this.options.env,\n };\n const agent = await this.spawnAgent(resolveOptions, input.workspace);\n // Selecting the model on the probe is what makes pi report that model's\n // thinking levels (they are read per current model), so the draft picker\n // mirrors the TUI before a real session exists.\n if (input.model) {\n await agent.setModel(input.model).catch((error: unknown) => {\n this.logger.warn('Could not select the model on the draft probe', error);\n });\n }\n return agent;\n }\n\n private async spawnAgent(\n resolveOptions: ResolvePiOptions,\n workspace: WorkspaceRef,\n sessionPath?: string,\n ): Promise<AgentGateway> {\n const spawn = resolvePi(resolveOptions);\n this.logger.info(\n `Starting pi agent (${spawn.source}): ${spawn.command} ${spawn.args.join(' ')}`,\n );\n\n const agent = new PiRpcAgent({\n spawn,\n workspace,\n logger: this.logger,\n sessionPath,\n env: this.options.env,\n requestTimeoutMs: this.options.requestTimeoutMs,\n });\n await agent.initialize();\n return agent;\n }\n}\n", "import { execFile } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n definesMcpServer,\n findMcpServer,\n isValidMcpServerName,\n removeMcpServer,\n setMcpServerEnabled,\n setMcpServerOverrideEnabled,\n upsertMcpServer,\n type McpConfigScope,\n type McpFileExposure,\n type McpFileServer,\n} from './internal/mcp-config.js';\nimport { resolvePiCli } from './internal/resolve-pi.js';\nimport { cleanSpawnEnv } from './internal/spawn-env.js';\n\n/** How a server reaches the model (pi's `McpExposure`). */\nexport type McpExposure = McpFileExposure;\n\n/** pi's connection lifecycle, plus the entry that is configured but turned off. */\nexport type McpServerState =\n | 'connecting'\n | 'connected'\n | 'disconnected'\n | 'needs-auth'\n | 'failed'\n | 'closed'\n | 'disabled';\n\nexport type McpServerScope = 'global' | 'project' | 'extension';\n\n/** Where a mutation edits: the user file or this project's `.pi/mcp.json`. */\nexport type { McpConfigScope };\n\n/** One server, as `pi mcp list --json` reports it. */\nexport interface McpServerStatus {\n name: string;\n scope: McpServerScope;\n /** The `mcp.json` that defines it (or the extension path). */\n source: string;\n enabled: boolean;\n exposure: McpExposure;\n /** A one-line summary of how it connects: `stdio: command args` or `http: url`. */\n transport: string;\n state: McpServerState;\n /** The tool names the server offers once connected. */\n tools: string[];\n /**\n * The project `.pi/mcp.json` that overrides a user-level server's\n * `enabled`/`exposure` for this directory. Present only when there is one.\n */\n override?: string;\n /** Per-tool exposure overrides, when they differ from the server's. */\n toolExposure?: Record<string, McpExposure>;\n resources?: number;\n resourceTemplates?: number;\n /** Why it is not connected: the connection error, or the config-file complaint. */\n error?: string;\n}\n\n/** What `mcpStatus` answers. `errors` are config-file problems pi could not attach to a server. */\nexport interface McpStatus {\n servers: McpServerStatus[];\n errors: string[];\n note?: string;\n}\n\n/** What `mcpAdd` takes. Exactly one of `command` (stdio) or `url` (http) is required. */\nexport interface McpServerInput {\n name: string;\n /** `project` writes `<cwd>/.pi/mcp.json`; default `global` (`~/.pi/agent/mcp.json`). */\n scope?: 'global' | 'project';\n type?: 'stdio' | 'http';\n command?: string;\n args?: string[];\n cwd?: string;\n env?: Record<string, string>;\n url?: string;\n headers?: Record<string, string>;\n enabled?: boolean;\n exposure?: McpExposure;\n description?: string;\n}\n\n/** What a mutation answers. `message` carries the reason when it refused. */\nexport interface McpMutation {\n ok: boolean;\n message?: string;\n /** The `mcp.json` that was written, for the panel to name. */\n path?: string;\n /** Which file the change landed in. */\n scope?: McpConfigScope;\n /** True when a user-level server got a project override instead of a rewrite. */\n override?: boolean;\n}\n\n/**\n * Rebuild an `McpServerInput` from a wire payload, dropping anything of the\n * wrong shape. Every host parses the panel's `mcpAdd` arguments through this, so\n * the validation lives in one place.\n */\nexport function parseMcpServerInput(raw: unknown): McpServerInput {\n const args = isRecord(raw) ? raw : {};\n const input: McpServerInput = { name: asString(args.name) };\n if (args.scope === 'global' || args.scope === 'project') {\n input.scope = args.scope;\n }\n if (args.type === 'stdio' || args.type === 'http') {\n input.type = args.type;\n }\n for (const key of ['command', 'cwd', 'url', 'description'] as const) {\n const value = asString(args[key]);\n if (value.length > 0) {\n input[key] = value;\n }\n }\n if (args.exposure === 'codemode' || args.exposure === 'deferred' || args.exposure === 'direct' || args.exposure === 'hidden') {\n input.exposure = args.exposure;\n }\n if (Array.isArray(args.args)) {\n input.args = args.args.filter(isString);\n }\n input.env = stringRecord(args.env);\n input.headers = stringRecord(args.headers);\n return input;\n}\n\nexport interface PiMcpOptions {\n piPath?: string;\n nodeEntryPath?: string;\n env?: NodeJS.ProcessEnv;\n /** Override the agent directory; defaults to `PI_CODING_AGENT_DIR` or `~/.pi/agent`. */\n agentDir?: string;\n /** How long `pi mcp list` may take before it is killed. */\n timeoutMs?: number;\n}\n\n/** `pi mcp list` connects every enabled server, so it is not instant. */\nconst DEFAULT_TIMEOUT_MS = 20_000;\n\n/**\n * Driven adapter for pi's MCP configuration: list server state through the CLI,\n * and add, remove or toggle servers by editing the same `mcp.json` pi reads.\n *\n * A session's MCP servers are scoped to its working directory (project\n * `.pi/mcp.json` next to the user-level file), so every method takes the `cwd`\n * of the session it is asked about.\n */\nexport class PiMcp {\n constructor(private readonly options: PiMcpOptions = {}) {}\n\n /** Connect every enabled server and report what happened. */\n async status(cwd: string): Promise<McpStatus> {\n const spawn = resolvePiCli(this.options);\n const { stdout, stderr, failed, timedOut } = await run(\n spawn.command,\n [...spawn.baseArgs, 'mcp', 'list', '--json'],\n cwd,\n cleanSpawnEnv(this.env()),\n this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n );\n const parsed = parseStatus(stdout);\n if (!parsed) {\n const seconds = Math.round((this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS) / 1000);\n const detail = timedOut\n ? `\\`pi mcp list\\` did not finish within ${seconds}s \u2014 a server is likely hanging (try \\`pi mcp list\\` in a terminal)`\n : stderr.trim() || (failed ? `\\`pi mcp list\\` exited with an error` : 'no output');\n throw new Error(`Could not read the MCP server list: ${detail}`);\n }\n return parsed;\n }\n\n async add(input: McpServerInput, cwd: string): Promise<McpMutation> {\n if (!isValidMcpServerName(input.name)) {\n return {\n ok: false,\n message: `\"${input.name}\" is not a valid server name (letters, digits, \"_\" and \"-\").`,\n };\n }\n const isHttp = input.type === 'http' || (input.url !== undefined && input.command === undefined);\n const config = this.toConfig(input, isHttp);\n if (typeof config === 'string') {\n return { ok: false, message: config };\n }\n const path = this.configPath(cwd, input.scope);\n try {\n upsertMcpServer(path, input.name, config);\n } catch (error: unknown) {\n return { ok: false, message: describe(error) };\n }\n return { ok: true, path };\n }\n\n async remove(name: string, cwd: string, scope?: McpConfigScope): Promise<McpMutation> {\n if (scope === 'global') {\n return this.removeFrom(this.globalPath(), name, 'global');\n }\n if (scope === 'project') {\n return this.removeFrom(this.projectPath(cwd), name, 'project');\n }\n // No scope named: the defining file first (a project definition wins), then\n // the user file \u2014 the behaviour before the editor exposed a scope.\n for (const path of this.configPaths(cwd)) {\n try {\n if (removeMcpServer(path, name)) {\n return { ok: true, path };\n }\n } catch (error: unknown) {\n return { ok: false, message: describe(error) };\n }\n }\n return { ok: false, message: `No server named \"${name}\" is configured.` };\n }\n\n private removeFrom(path: string, name: string, scope: McpConfigScope): McpMutation {\n try {\n if (!removeMcpServer(path, name)) {\n return {\n ok: false,\n message:\n scope === 'project'\n ? `\"${name}\" is not defined in this project's .pi/mcp.json.`\n : `\"${name}\" is not defined in the user mcp.json.`,\n };\n }\n } catch (error: unknown) {\n return { ok: false, message: describe(error) };\n }\n return { ok: true, path, scope };\n }\n\n /**\n * Enable or disable a server in one scope.\n *\n * Global edits the user file. Project edits the project entry when the project\n * defines the server, and otherwise writes a project **override** \u2014 the way\n * pi's `/mcp` does \"Disable in this project\" \u2014 so a user-level server can be\n * turned off in one directory without touching every other project.\n */\n async setEnabled(\n name: string,\n enabled: boolean,\n cwd: string,\n scope?: McpConfigScope,\n ): Promise<McpMutation> {\n if (scope === 'global') {\n return this.setEnabledIn(this.globalPath(), name, enabled, 'global');\n }\n if (scope === 'project') {\n const projectPath = this.projectPath(cwd);\n if (definesMcpServer(findMcpServer(projectPath, name))) {\n return this.setEnabledIn(projectPath, name, enabled, 'project');\n }\n // A user-level server (or an existing project override): write the\n // override rather than the defining file.\n const known =\n findMcpServer(projectPath, name) !== undefined ||\n findMcpServer(this.globalPath(), name) !== undefined;\n if (!known) {\n return {\n ok: false,\n message: `No server named \"${name}\" is configured.`,\n };\n }\n try {\n setMcpServerOverrideEnabled(projectPath, name, enabled);\n } catch (error: unknown) {\n return { ok: false, message: describe(error) };\n }\n return { ok: true, path: projectPath, scope: 'project', override: true };\n }\n // No scope named: the defining file first (project, then global).\n for (const path of this.configPaths(cwd)) {\n try {\n if (findMcpServer(path, name) === undefined) {\n continue;\n }\n setMcpServerEnabled(path, name, enabled);\n return { ok: true, path };\n } catch (error: unknown) {\n return { ok: false, message: describe(error) };\n }\n }\n return { ok: false, message: `No server named \"${name}\" is configured.` };\n }\n\n private setEnabledIn(\n path: string,\n name: string,\n enabled: boolean,\n scope: McpConfigScope,\n ): McpMutation {\n try {\n if (findMcpServer(path, name) === undefined) {\n return {\n ok: false,\n message:\n scope === 'project'\n ? `\"${name}\" is not defined in this project's .pi/mcp.json.`\n : `\"${name}\" is not defined in the user mcp.json.`,\n };\n }\n setMcpServerEnabled(path, name, enabled);\n } catch (error: unknown) {\n return { ok: false, message: describe(error) };\n }\n return { ok: true, path, scope };\n }\n\n /** The user-level file first, then the project's \u2014 so a project definition wins. */\n private configPaths(cwd: string): string[] {\n return [this.projectPath(cwd), this.globalPath()];\n }\n\n private configPath(cwd: string, scope: 'global' | 'project' | undefined): string {\n return scope === 'project' ? this.projectPath(cwd) : this.globalPath();\n }\n\n private globalPath(): string {\n return join(this.agentDir(), 'mcp.json');\n }\n\n private projectPath(cwd: string): string {\n return join(cwd, '.pi', 'mcp.json');\n }\n\n private agentDir(): string {\n if (this.options.agentDir) {\n return this.options.agentDir;\n }\n const fromEnv = this.env().PI_CODING_AGENT_DIR;\n return fromEnv && fromEnv.length > 0 ? fromEnv : join(homedir(), '.pi', 'agent');\n }\n\n private env(): NodeJS.ProcessEnv {\n return this.options.env ?? process.env;\n }\n\n /** The stored entry, or an error message when the input is incomplete. */\n private toConfig(input: McpServerInput, isHttp: boolean): McpFileServer | string {\n const config: McpFileServer = {};\n if (isHttp) {\n const url = input.url?.trim();\n if (!url) {\n return 'An HTTP server needs a \"url\".';\n }\n config.type = 'http';\n config.url = url;\n if (input.headers && Object.keys(input.headers).length > 0) {\n config.headers = input.headers;\n }\n } else {\n const command = input.command?.trim();\n if (!command) {\n return 'A stdio server needs a \"command\".';\n }\n config.type = 'stdio';\n config.command = command;\n if (input.args && input.args.length > 0) {\n config.args = input.args;\n }\n if (input.cwd?.trim()) {\n config.cwd = input.cwd.trim();\n }\n if (input.env && Object.keys(input.env).length > 0) {\n config.env = input.env;\n }\n }\n if (input.exposure && input.exposure !== 'codemode') {\n config.exposure = input.exposure;\n }\n if (input.description?.trim()) {\n config.description = input.description.trim();\n }\n if (input.enabled === false) {\n config.enabled = false;\n }\n return config;\n }\n}\n\ninterface CliResult {\n stdout: string;\n stderr: string;\n failed: boolean;\n /** `execFile`'s timeout killed it (as opposed to the CLI choosing to exit non-zero). */\n timedOut: boolean;\n}\n\n/**\n * Run the CLI and always resolve, even on a non-zero exit: `pi mcp list --json`\n * prints the full report to stdout and then exits 1 when a server is not\n * connected, which is exactly the answer Morse wants.\n */\nfunction run(\n command: string,\n args: string[],\n cwd: string,\n env: NodeJS.ProcessEnv,\n timeoutMs: number,\n): Promise<CliResult> {\n return new Promise((resolve, reject) => {\n execFile(\n command,\n args,\n { cwd, env, timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024 },\n (error, stdout, stderr) => {\n const err = error as (NodeJS.ErrnoException & { killed?: boolean }) | null;\n const code = err?.code;\n if (typeof code === 'string') {\n // A spawn failure (ENOENT/EACCES), not an exit status.\n reject(new Error(`Could not run \"${command}\": ${stderr.trim() || code}`));\n return;\n }\n resolve({ stdout, stderr, failed: error !== null, timedOut: err?.killed === true });\n },\n );\n });\n}\n\nfunction parseStatus(stdout: string): McpStatus | undefined {\n const trimmed = stdout.trim();\n if (trimmed.length === 0) {\n return undefined;\n }\n try {\n const parsed = JSON.parse(trimmed) as Partial<McpStatus>;\n if (!Array.isArray(parsed.servers)) {\n return undefined;\n }\n return {\n servers: parsed.servers.map(toServerStatus),\n errors: Array.isArray(parsed.errors) ? parsed.errors.filter(isString) : [],\n ...(typeof parsed.note === 'string' ? { note: parsed.note } : {}),\n };\n } catch {\n return undefined;\n }\n}\n\n/** Normalise one row. The CLI's shape is stable, but the panel must not crash on a surprising one. */\nfunction toServerStatus(raw: unknown): McpServerStatus {\n const row = (typeof raw === 'object' && raw !== null ? raw : {}) as Record<string, unknown>;\n const scope = row.scope;\n return {\n name: isString(row.name) ? row.name : 'unknown',\n scope: scope === 'project' || scope === 'extension' ? scope : 'global',\n source: isString(row.source) ? row.source : '',\n ...(isString(row.override) ? { override: row.override } : {}),\n enabled: row.enabled !== false,\n exposure: toExposure(row.exposure),\n transport: isString(row.transport) ? row.transport : '',\n state: toState(row.state),\n tools: Array.isArray(row.tools) ? row.tools.filter(isString) : [],\n ...(isRecord(row.toolExposure) ? { toolExposure: toExposureMap(row.toolExposure) } : {}),\n ...(typeof row.resources === 'number' ? { resources: row.resources } : {}),\n ...(typeof row.resourceTemplates === 'number'\n ? { resourceTemplates: row.resourceTemplates }\n : {}),\n ...(isString(row.error) ? { error: row.error } : {}),\n };\n}\n\nfunction toState(value: unknown): McpServerState {\n switch (value) {\n case 'connecting':\n case 'connected':\n case 'disconnected':\n case 'needs-auth':\n case 'failed':\n case 'closed':\n case 'disabled':\n return value;\n default:\n return 'failed';\n }\n}\n\nfunction toExposure(value: unknown): McpExposure {\n switch (value) {\n case 'codemode':\n case 'deferred':\n case 'direct':\n case 'hidden':\n return value;\n default:\n return 'codemode';\n }\n}\n\nfunction toExposureMap(value: Record<string, unknown>): Record<string, McpExposure> {\n const result: Record<string, McpExposure> = {};\n for (const [key, entry] of Object.entries(value)) {\n result[key] = toExposure(entry);\n }\n return result;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction asString(value: unknown): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction stringRecord(value: unknown): Record<string, string> | undefined {\n if (!isRecord(value)) {\n return undefined;\n }\n const result: Record<string, string> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (typeof entry === 'string') {\n result[key] = entry;\n }\n }\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction describe(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n", "import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\n/**\n * Reading and writing `mcp.json`, the file pi and other MCP clients share.\n *\n * Morse edits the file directly instead of shelling out to `pi mcp add/remove`:\n * the CLI has no command for enabling or disabling a server, and the file format\n * is the contract every client already agrees on. `pi mcp list --json` is still\n * the source of truth for *connection state* (see `pi-mcp.ts`) \u2014 only the edits\n * happen here, and pi reports anything malformed on the next list.\n */\n\n/** How a server's tools reach the model (pi's `McpExposure`). */\nexport type McpFileExposure = 'codemode' | 'deferred' | 'direct' | 'hidden';\n\n/** Where an edit lands: the user file (every project) or this project's `.pi/mcp.json`. */\nexport type McpConfigScope = 'global' | 'project';\n\n/** One `mcpServers` entry, as stored. All fields optional: some clients only pin `enabled`. */\nexport interface McpFileServer {\n type?: 'stdio' | 'http';\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n url?: string;\n headers?: Record<string, string>;\n enabled?: boolean;\n exposure?: McpFileExposure;\n description?: string;\n /** OAuth and per-tool exposure are preserved verbatim; Morse does not edit them. */\n [key: string]: unknown;\n}\n\ninterface McpFile {\n mcpServers: Record<string, McpFileServer>;\n [key: string]: unknown;\n}\n\n/** Server names pi accepts: letters, digits, `_` and `-`. */\nconst SERVER_NAME = /^[A-Za-z0-9_-]+$/;\n\nexport function isValidMcpServerName(name: string): boolean {\n return SERVER_NAME.test(name);\n}\n\n/** The parsed file, or an empty one when it does not exist yet. A malformed file is an error. */\nexport function readMcpFile(path: string): McpFile {\n if (!existsSync(path)) {\n return { mcpServers: {} };\n }\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error(`${path} is not a JSON object.`);\n }\n const servers = (parsed as { mcpServers?: unknown }).mcpServers;\n if (servers !== undefined && (typeof servers !== 'object' || servers === null || Array.isArray(servers))) {\n throw new Error(`${path}: \"mcpServers\" must be an object.`);\n }\n return {\n ...(parsed as Record<string, unknown>),\n mcpServers: (servers as Record<string, McpFileServer> | undefined) ?? {},\n };\n}\n\nexport function writeMcpFile(path: string, file: McpFile): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, `${JSON.stringify(file, null, 2)}\\n`, 'utf8');\n}\n\nexport function findMcpServer(path: string, name: string): McpFileServer | undefined {\n return readMcpFile(path).mcpServers[name];\n}\n\n/**\n * Whether an entry defines a server, as opposed to a project *override* of a\n * user-level one. pi's rule: an entry without `command`, `url`, or `type` only\n * overrides `enabled`, `exposure` and `toolExposure`, and keeps the global\n * server's command, env, headers and auth.\n */\nexport function definesMcpServer(entry: McpFileServer | undefined): boolean {\n return (\n entry !== undefined &&\n (entry.command !== undefined || entry.url !== undefined || entry.type !== undefined)\n );\n}\n\n/**\n * Turn a user-level server on or off for one project only, by writing (or\n * clearing) a project override. Enabling removes the override's `enabled` \u2014 and\n * the entry itself when nothing else is overridden, since `enabled: true` is\n * the default pi restores.\n */\nexport function setMcpServerOverrideEnabled(\n path: string,\n name: string,\n enabled: boolean,\n): void {\n const file = readMcpFile(path);\n const existing = file.mcpServers[name];\n if (enabled) {\n if (!existing) {\n return;\n }\n delete existing.enabled;\n if (Object.keys(existing).length === 0) {\n delete file.mcpServers[name];\n }\n } else {\n // Keep any other override keys (exposure, toolExposure) the user set.\n const entry = definesMcpServer(existing) ? {} : (existing ?? {});\n entry.enabled = false;\n file.mcpServers[name] = entry;\n }\n writeMcpFile(path, file);\n}\n\n/** Add or replace one entry, keeping every other entry and top-level key. */\nexport function upsertMcpServer(path: string, name: string, config: McpFileServer): void {\n if (!isValidMcpServerName(name)) {\n throw new Error(`\"${name}\" is not a valid MCP server name (letters, digits, \"_\" and \"-\").`);\n }\n const file = readMcpFile(path);\n file.mcpServers[name] = config;\n writeMcpFile(path, file);\n}\n\n/** Delete one entry. Returns false when the file did not define it. */\nexport function removeMcpServer(path: string, name: string): boolean {\n const file = readMcpFile(path);\n if (!(name in file.mcpServers)) {\n return false;\n }\n delete file.mcpServers[name];\n writeMcpFile(path, file);\n return true;\n}\n\n/** Set (or clear) one entry's `enabled`. Returns false when the file did not define it. */\nexport function setMcpServerEnabled(path: string, name: string, enabled: boolean): boolean {\n const file = readMcpFile(path);\n const server = file.mcpServers[name];\n if (!server) {\n return false;\n }\n // `enabled: true` is the default, so the key is dropped rather than stored.\n if (enabled) {\n delete server.enabled;\n } else {\n server.enabled = false;\n }\n writeMcpFile(path, file);\n return true;\n}\n", "import { open, readdir, readFile, stat, unlink } from 'node:fs/promises';\nimport { join, resolve, sep } from 'node:path';\nimport type { SessionCatalog, SessionSummary } from '@morse/core';\nimport { asRecord, asString } from './internal/rpc-types.js';\nimport { resolveSessionDir } from './internal/resolve-pi.js';\n\nexport interface PiRpcSessionCatalogOptions {\n sessionDir?: string;\n maxSessions?: number;\n env?: NodeJS.ProcessEnv;\n /**\n * How one session file is removed. Defaults to `unlink`; a host can inject its\n * own (VS Code goes through the workspace filesystem, so the file lands in the\n * OS trash and the call works in remote windows too).\n */\n removeFile?: (path: string) => Promise<void>;\n /**\n * How a session file is summarised. Defaults to reading it; a test injects its\n * own to see *when* a file is read at all (the cache below is the point).\n */\n readSummary?: (\n path: string,\n updatedAt: number,\n previous?: CachedSummary,\n ) => Promise<SessionScan | undefined>;\n}\n\n/** What reading one session file produced, plus where to resume next time. */\nexport interface SessionScan {\n summary: SessionSummary;\n scanned: number;\n name?: string;\n firstUserText?: string;\n}\n\n/** A summary plus everything needed to continue reading the file where it stopped. */\ninterface CachedSummary {\n size: number;\n updatedAt: number;\n summary: SessionSummary;\n /** Byte offset already scanned, always just past a newline. */\n scanned: number;\n /** Title sources found so far, kept apart from the rendered title. */\n name?: string;\n firstUserText?: string;\n}\n\n/**\n * pi writes one compact JSON object per line with `type` first. Reading a line's\n * type is therefore a couple of string searches, with no object to allocate and\n * no parse: reading a project's sessions means scanning tens of megabytes, and\n * `JSON.parse` on every line whose only contribution was bumping `messageCount`\n * cost ~0.6 s of CPU per `session/list` on a real catalog. A line that is *not*\n * in that shape is parsed the slow way, so nothing is silently missed.\n */\nconst TYPE_PREFIX = '{\"type\":\"';\n\nfunction typeAt(raw: string, start: number, end: number): string | undefined {\n if (!raw.startsWith(TYPE_PREFIX, start)) {\n return undefined;\n }\n const close = raw.indexOf('\"', start + TYPE_PREFIX.length);\n return close === -1 || close > end ? undefined : raw.slice(start + TYPE_PREFIX.length, close);\n}\n\n/**\n * Implements `SessionCatalog` by reading pi's session files\n * (`<session-dir>/--<cwd>--/<timestamp>_<id>.jsonl`, see docs/session-format.md).\n * The id returned is the session file path so it can be resumed with\n * `pi --session <path>`.\n */\nexport class PiRpcSessionCatalog implements SessionCatalog {\n private readonly directory: string;\n private readonly maxSessions: number;\n private readonly removeFile: (path: string) => Promise<void>;\n private readonly readSummary: (\n path: string,\n updatedAt: number,\n previous?: CachedSummary,\n ) => Promise<SessionScan | undefined>;\n /**\n * Summaries survive between calls, keyed by path and validated by size + mtime.\n * The list is asked for far more often than sessions change \u2014 every reconnect,\n * every new session, every rename \u2014 and re-reading 50 transcripts each time was\n * the single most expensive thing the host did.\n */\n private readonly cache = new Map<string, CachedSummary>();\n\n constructor(options: PiRpcSessionCatalogOptions = {}) {\n this.directory = resolveSessionDir(options.sessionDir, options.env);\n this.maxSessions = options.maxSessions ?? 50;\n this.removeFile = options.removeFile ?? unlink;\n this.readSummary = options.readSummary ?? readSessionSummary;\n }\n\n get sessionDirectory(): string {\n return this.directory;\n }\n\n /**\n * A list already being built is handed to every caller instead of being started\n * again: two frontends connecting at the same moment, or a refresh landing while\n * a push is in flight, used to mean two full scans of the whole catalog.\n */\n private inFlight: Promise<SessionSummary[]> | undefined;\n\n async list(): Promise<SessionSummary[]> {\n this.inFlight ??= this.buildList().finally(() => {\n this.inFlight = undefined;\n });\n return this.inFlight;\n }\n\n private async buildList(): Promise<SessionSummary[]> {\n const files = await this.collectSessionFiles();\n const summaries: SessionSummary[] = [];\n const seen = new Set<string>();\n for (const file of files.slice(0, this.maxSessions)) {\n seen.add(file.path);\n const cached = this.cache.get(file.path);\n if (cached !== undefined && cached.size === file.size && cached.updatedAt === file.updatedAt) {\n summaries.push(cached.summary);\n continue;\n }\n const scan = await this.readSummary(file.path, file.updatedAt, cached);\n if (scan === undefined) {\n this.cache.delete(file.path);\n continue;\n }\n this.cache.set(file.path, { size: file.size, updatedAt: file.updatedAt, ...scan });\n summaries.push(scan.summary);\n }\n // A deleted session (or one that fell out of `maxSessions`) must not keep its\n // text alive here.\n for (const path of [...this.cache.keys()]) {\n if (!seen.has(path)) {\n this.cache.delete(path);\n }\n }\n return summaries;\n }\n\n /**\n * Deletes one stored session. The id is a session file path, so the guard is\n * the point: only a `.jsonl` inside this catalog's session directory is ever\n * removed. A stale client or a hostile payload cannot turn a session id into\n * a delete of an arbitrary file.\n */\n async remove(id: string): Promise<void> {\n const path = resolve(id);\n const directory = resolve(this.directory);\n if (!path.startsWith(`${directory}${sep}`) || !path.endsWith('.jsonl')) {\n throw new Error(`Refusing to delete a session outside ${directory}`);\n }\n await this.removeFile(path);\n }\n\n private async collectSessionFiles(): Promise<\n { path: string; size: number; updatedAt: number }[]\n > {\n const buckets = await safeReadDir(this.directory);\n const files: { path: string; size: number; updatedAt: number }[] = [];\n for (const bucket of buckets) {\n if (!bucket.isDirectory()) {\n continue;\n }\n const bucketPath = join(this.directory, bucket.name);\n for (const entry of await safeReadDir(bucketPath)) {\n if (!entry.isFile() || !entry.name.endsWith('.jsonl')) {\n continue;\n }\n const path = join(bucketPath, entry.name);\n const info = await stat(path).catch(() => undefined);\n files.push({ path, size: info?.size ?? 0, updatedAt: info?.mtimeMs ?? 0 });\n }\n }\n files.sort((left, right) => right.updatedAt - left.updatedAt);\n return files;\n }\n}\n\n/**\n * Reads one session file into a summary.\n *\n * A file that only *grew* since the last scan is read from where that scan\n * stopped: a long session appends a message at a time, and re-reading all 18 MB of\n * it for every `session/list` cost ~65 ms against ~1 ms for the tail. Anything else\n * \u2014 a shorter file, an older mtime, no previous scan \u2014 is read whole, so the\n * summary can never drift from the file.\n */\nasync function readSessionSummary(\n path: string,\n updatedAt: number,\n previous?: CachedSummary,\n): Promise<SessionScan | undefined> {\n let resumable =\n previous !== undefined && previous.scanned > 0 && updatedAt >= previous.updatedAt;\n if (resumable && previous !== undefined) {\n const info = await stat(path).catch(() => undefined);\n // Only an append can be read from the tail. A shorter file (or one of the same\n // length) was rewritten, and the prefix we scanned is not the prefix on disk.\n resumable = info !== undefined && info.size > previous.scanned;\n }\n const from = resumable && previous !== undefined ? previous.scanned : 0;\n const raw = resumable\n ? await readFrom(path, from).catch(() => undefined)\n : await readFile(path, 'utf8').catch(() => undefined);\n if (raw === undefined) {\n return undefined;\n }\n\n let cwd = resumable && previous !== undefined ? previous.summary.cwd : '';\n let name = resumable && previous !== undefined ? previous.name : undefined;\n let firstUserText = resumable && previous !== undefined ? previous.firstUserText : undefined;\n let messageCount = resumable && previous !== undefined ? previous.summary.messageCount : 0;\n let scanned = from;\n /** Characters consumed up to the last complete line, in this read. */\n let consumed = 0;\n\n const parseAt = (start: number, end: number) => parseLine(raw.slice(start, end));\n\n for (let start = 0; start < raw.length; ) {\n const newline = raw.indexOf('\\n', start);\n const end = newline === -1 ? raw.length : newline;\n // Walk the buffer by offset: slicing every line (or `split`ting the whole file\n // into 2000 strings) allocated tens of megabytes per scan, for lines that\n // mostly only needed counting.\n //\n // The last chunk may be a line still being written, and a half-flushed entry\n // must not be read as a (broken) line: it only counts once it parses, which is\n // what the scan did before this shortcut existed.\n const complete = newline !== -1;\n let type: string | undefined;\n if (raw.charCodeAt(start) === 123 /* '{' */) {\n type = complete\n ? (typeAt(raw, start, end) ?? asString(parseAt(start, end)?.['type']))\n : asString(parseAt(start, end)?.['type']);\n if (type === 'message') {\n messageCount += 1;\n if (firstUserText === undefined) {\n const message = asRecord(parseAt(start, end)?.['message']);\n if (message?.['role'] === 'user') {\n firstUserText = textOf(message['content']);\n }\n }\n } else if (type === 'session') {\n if (cwd.length === 0) {\n cwd = asString(parseAt(start, end)?.['cwd']) ?? '';\n }\n } else if ((type === 'session_info' || type === 'session_name') && !name) {\n name = asString(parseAt(start, end)?.['name']);\n }\n }\n if (!complete) {\n // The tail is either a whole entry whose newline has not landed yet \u2014 it\n // counted above, so the next scan must start after it \u2014 or a half-written\n // line, which is left in place to be read again whole.\n if (type !== undefined) {\n consumed = raw.length;\n }\n break;\n }\n consumed = newline + 1;\n start = newline + 1;\n }\n\n // Byte offsets are what the next scan resumes from, and they have to sit on a\n // character boundary: measured once from the text, not per line.\n if (consumed > 0) {\n scanned = from + Buffer.byteLength(raw.slice(0, consumed));\n }\n\n return {\n summary: {\n id: path,\n title: name ?? titleFromText(firstUserText) ?? 'Untitled session',\n cwd,\n updatedAt,\n messageCount,\n },\n scanned,\n name,\n firstUserText,\n };\n}\n\n/** The tail of a file, from a byte offset that sits on a character boundary. */\nasync function readFrom(path: string, from: number): Promise<string> {\n const handle = await open(path, 'r');\n try {\n const info = await handle.stat();\n if (info.size <= from) {\n return '';\n }\n const length = info.size - from;\n const buffer = Buffer.alloc(length);\n let filled = 0;\n while (filled < length) {\n const { bytesRead } = await handle.read(buffer, filled, length - filled, from + filled);\n if (bytesRead === 0) {\n break;\n }\n filled += bytesRead;\n }\n return buffer.subarray(0, filled).toString('utf8');\n } finally {\n await handle.close();\n }\n}\n\n/** Only reached for a line that is not in pi's canonical shape. */\nfunction parseLine(line: string): Record<string, unknown> | undefined {\n try {\n return JSON.parse(line) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\n/**\n * User content is either a plain string or a list of blocks; only text blocks\n * carry a title-worth string (images and tool results do not).\n */\nfunction textOf(content: unknown): string | undefined {\n const direct = asString(content);\n if (direct !== undefined) {\n return direct.length > 0 ? direct : undefined;\n }\n const blocks = Array.isArray(content) ? content : undefined;\n if (!blocks) {\n return undefined;\n }\n for (const block of blocks) {\n const record = asRecord(block);\n if (record?.['type'] !== 'text') {\n continue;\n }\n const text = asString(record['text']);\n if (text !== undefined && text.trim().length > 0) {\n return text;\n }\n }\n return undefined;\n}\n\nfunction titleFromText(text: string | undefined): string | undefined {\n if (!text) {\n return undefined;\n }\n const single = text.replace(/\\s+/g, ' ').trim();\n if (single.length === 0) {\n return undefined;\n }\n return single.length > 72 ? `${single.slice(0, 72)}\u2026` : single;\n}\n\nasync function safeReadDir(path: string): Promise<import('node:fs').Dirent[]> {\n try {\n return await readdir(path, { withFileTypes: true });\n } catch {\n return [];\n }\n}\n", "import type { AgentGatewayFactory, MorseLogger, SessionCatalog } from '@morse/core';\nimport { PiRpcAgentFactory, type PiRpcAgentFactoryOptions } from './pi-rpc-agent-factory.js';\nimport { PiMcp } from './pi-mcp.js';\nimport { PiRpcSessionCatalog } from './pi-rpc-session-catalog.js';\nimport { resolvePi, resolvePiCli, type PiCliSpawn, type PiSpawn } from './internal/resolve-pi.js';\n\nexport interface PiRpcAdapterConfig extends PiRpcAgentFactoryOptions {\n sessionDir?: string;\n maxSessions?: number;\n /** How session files are removed; the host supplies its own (see the catalog). */\n removeSessionFile?: (path: string) => Promise<void>;\n}\n\nexport interface PiRpcAdapter {\n factory: AgentGatewayFactory;\n catalog: SessionCatalog;\n /** pi's MCP configuration, read and written through the CLI and `mcp.json`. */\n mcp: PiMcp;\n /** Resolves the spawn command; throws with an actionable message when pi is missing. */\n describe(): PiSpawn;\n /** The `pi` CLI when it can be run, for host capabilities that need a subcommand. */\n describeCli(): PiCliSpawn | undefined;\n}\n\n/**\n * Wires the pi RPC adapter. Both hosts (VS Code extension and NestJS server)\n * call this and receive the same ports \u2014 only the delivery differs.\n */\nexport function createPiRpcAdapter(config: PiRpcAdapterConfig, logger: MorseLogger): PiRpcAdapter {\n const factory = new PiRpcAgentFactory(config, logger);\n const catalog = new PiRpcSessionCatalog({\n sessionDir: config.sessionDir,\n maxSessions: config.maxSessions,\n env: config.env,\n removeFile: config.removeSessionFile,\n });\n const cliOptions = {\n piPath: config.piPath,\n nodeEntryPath: config.nodeEntryPath,\n env: config.env,\n };\n const mcp = new PiMcp(cliOptions);\n return {\n factory,\n catalog,\n mcp,\n describe: () =>\n resolvePi({\n piPath: config.piPath,\n nodeEntryPath: config.nodeEntryPath,\n sessionDir: config.sessionDir,\n noSession: config.noSession,\n extraArgs: config.extraArgs,\n env: config.env,\n }),\n describeCli: () => {\n try {\n return resolvePiCli(cliOptions);\n } catch {\n return undefined;\n }\n },\n };\n}\n", "/**\n * The bounded text a terminal replays to a viewer that attached late.\n *\n * A shell's output is unbounded (`yes` never stops), so both the in-memory\n * replay and the on-disk scrollback keep only the tail. One implementation, so\n * the two never disagree about what \"the last N\" means.\n */\n\n/**\n * How much scrollback one terminal keeps. Small enough to hold for every pane a\n * reader might leave open, large enough to cover what a person scrolls back to.\n */\nexport const TERMINAL_BUFFER_CHARS = 256 * 1024;\n\n/**\n * Keeps the last `maxChars` of `text`, cut at a newline so a replayed scrollback\n * does not start in the middle of an escape sequence (which would paint\n * garbage). A single line longer than the cap has no newline to cut at, so its\n * raw tail is kept \u2014 better partial output than none.\n */\nexport function trimTail(text: string, maxChars = TERMINAL_BUFFER_CHARS): string {\n // `slice` with a negative start is relative to the end, so this is the tail.\n if (text.length <= maxChars) {\n return text;\n }\n const tail = text.slice(-maxChars);\n const newline = tail.indexOf('\\n');\n return newline === -1 ? tail : tail.slice(newline + 1);\n}\n", "import type { AgentEvent } from '@morse/core';\nimport type {\n AssistantTranscriptItem,\n ChatPin,\n HostToClientMessage,\n NoticeLevel,\n PromptImage,\n ToolTranscriptItem,\n TranscriptItem,\n UserTranscriptItem,\n} from '@morse/protocol';\n\nexport interface TranscriptProjectorOptions {\n emit: (message: HostToClientMessage) => void;\n now?: () => number;\n idPrefix?: string;\n}\n\n/**\n * Turns the agent's transport-neutral events into the frontend's transcript.\n * One implementation shared by the VS Code host and the NestJS host, so both\n * render byte-identical conversations.\n */\nexport class TranscriptProjector {\n private readonly items: TranscriptItem[] = [];\n private readonly toolIndexes = new Map<string, number>();\n private assistantIndex = -1;\n private counter = 0;\n private readonly now: () => number;\n private readonly idPrefix: string;\n\n constructor(private readonly options: TranscriptProjectorOptions) {\n this.now = options.now ?? (() => Date.now());\n this.idPrefix = options.idPrefix ?? 'morse';\n }\n\n snapshot(): TranscriptItem[] {\n return this.items.map((item) => ({ ...item }));\n }\n\n reset(): void {\n this.items.length = 0;\n this.toolIndexes.clear();\n this.assistantIndex = -1;\n this.options.emit({ type: 'transcript/replace', payload: { items: [] } });\n }\n\n userPrompt(text: string, images?: PromptImage[], pins?: ChatPin[]): void {\n const item: UserTranscriptItem = {\n kind: 'user',\n id: this.nextId('user'),\n at: this.now(),\n text,\n ...(images && images.length > 0 ? { images } : {}),\n ...(pins && pins.length > 0 ? { pins } : {}),\n };\n this.assistantIndex = -1;\n this.appendItem(item);\n }\n\n /** Appends an item that already happened (a seeded history entry). */\n append(item: TranscriptItem): void {\n this.items.push(item);\n this.options.emit({ type: 'transcript/append', payload: { ...item } });\n }\n\n /**\n * Replaces the whole transcript in one message. Used when a fork re-parents\n * the session: the branch after the edited message no longer exists, so the\n * client must drop those rows instead of appending to them.\n */\n replace(items: TranscriptItem[]): void {\n this.items.length = 0;\n this.items.push(...items);\n this.toolIndexes.clear();\n this.assistantIndex = -1;\n this.options.emit({ type: 'transcript/replace', payload: { items: this.snapshot() } });\n }\n\n /**\n * Inserts an older history page in front of everything already shown. The\n * recorded indexes point into `items`, so they shift by the page size.\n */\n prepend(items: TranscriptItem[]): void {\n if (items.length === 0) {\n return;\n }\n this.items.unshift(...items);\n if (this.assistantIndex !== -1) {\n this.assistantIndex += items.length;\n }\n for (const [toolCallId, index] of this.toolIndexes) {\n this.toolIndexes.set(toolCallId, index + items.length);\n }\n this.options.emit({ type: 'transcript/prepend', payload: { items } });\n }\n\n notice(level: NoticeLevel, text: string): void {\n this.appendItem({ kind: 'notice', id: this.nextId('notice'), at: this.now(), level, text });\n }\n\n /** The live compaction boundary: a marker row for where the context reset. */\n private appendMarker(event: { at: number; summary: string; tokensBefore?: number }): void {\n const { summary, tokensBefore } = event;\n this.appendItem({\n kind: 'compaction',\n id: this.nextId('compaction'),\n at: this.now(),\n ...(summary.length > 0 ? { summary } : {}),\n ...(tokensBefore !== undefined ? { tokensBefore } : {}),\n });\n }\n\n error(message: string, detail?: string): void {\n // A command failure with a session on screen is conversation content: the\n // red row says what failed, where it happened. It must NOT also raise the\n // wire `error` \u2014 the frontend renders that as its \"Offline \u2014 the host\n // wouldn't take this frontend\" banner (Reload / Retry buttons included),\n // which describes a handshake that never failed: a rejected `compact`\n // looked like a dead connection. The wire error stays reserved for a\n // refusing handshake (see the controller's `emitWireError`).\n this.appendItem({\n kind: 'notice',\n id: this.nextId('error'),\n at: this.now(),\n level: 'error',\n text: detail ? `${message}\\n${detail}` : message,\n });\n }\n\n apply(event: AgentEvent): void {\n switch (event.type) {\n case 'agent/run-start':\n this.assistantIndex = -1;\n break;\n case 'agent/delta':\n this.applyDelta(event.channel, event.delta);\n break;\n case 'agent/message':\n this.applyMessage(event.text, event.thinking);\n break;\n case 'agent/tool-start':\n this.applyToolStart(event.toolCallId, event.name, event.title, event.input);\n break;\n case 'agent/tool-update':\n this.applyToolUpdate(event.toolCallId, event.output, event.status);\n break;\n case 'agent/tool-end':\n this.applyToolEnd(event.toolCallId, event.status, event.output, event.durationMs);\n break;\n case 'agent/run-end':\n this.finishAssistant(event.reason === 'error' ? undefined : event.reason);\n break;\n case 'agent/notice':\n this.notice(event.level, event.text);\n break;\n case 'agent/compaction':\n // Live compaction: the marker lands right here, in the middle of the\n // conversation it summarizes \u2014 the transcript row and the divider stay\n // in place for every later replay of this warm transcript.\n this.appendMarker(event);\n break;\n case 'agent/fatal':\n this.error(event.message, event.detail);\n break;\n default:\n break;\n }\n }\n\n private applyDelta(channel: 'text' | 'thinking', delta: string): void {\n const item = this.ensureAssistant();\n this.options.emit({\n type: 'transcript/delta',\n payload: channel === 'text' ? { id: item.id, text: delta } : { id: item.id, thinking: delta },\n });\n item[channel] += delta;\n item.streaming = true;\n }\n\n private applyMessage(text: string, thinking: string): void {\n const item = this.ensureAssistant();\n item.text = text;\n item.thinking = thinking;\n item.streaming = false;\n this.assistantIndex = -1;\n this.options.emit({ type: 'transcript/update', payload: { ...item } });\n }\n\n private finishAssistant(reason: 'settled' | 'aborted' | undefined): void {\n if (this.assistantIndex === -1) {\n return;\n }\n const item = this.items[this.assistantIndex];\n if (item && item.kind === 'assistant') {\n item.streaming = false;\n if (reason === 'aborted' && item.text.length === 0) {\n item.text = '_Run aborted._';\n }\n this.options.emit({ type: 'transcript/update', payload: { ...item } });\n }\n this.assistantIndex = -1;\n }\n\n private applyToolStart(\n toolCallId: string,\n name: string,\n title: string,\n input: string | undefined,\n ): void {\n const item: ToolTranscriptItem = {\n kind: 'tool',\n id: this.nextId('tool'),\n at: this.now(),\n name,\n title,\n status: 'running',\n input,\n };\n this.toolIndexes.set(toolCallId, this.items.length);\n this.appendItem(item);\n }\n\n private applyToolUpdate(\n toolCallId: string,\n output: string | undefined,\n status: 'running' | 'ok' | 'error' | undefined,\n ): void {\n const item = this.toolItem(toolCallId);\n if (!item) {\n return;\n }\n if (output !== undefined) {\n item.output = output;\n }\n if (status !== undefined) {\n item.status = status;\n }\n this.updateItem(item);\n }\n\n private applyToolEnd(\n toolCallId: string,\n status: 'ok' | 'error',\n output: string | undefined,\n durationMs: number | undefined,\n ): void {\n const item = this.toolItem(toolCallId);\n if (!item) {\n return;\n }\n item.status = status;\n if (output !== undefined) {\n item.output = output;\n }\n // pi's `tool_execution_end` carries no duration, and a finished turn can\n // only show \"Worked for Ns\" if one is recorded. Measure it from the start\n // we stamped ourselves; a host that does report one still wins.\n item.durationMs = durationMs ?? Math.max(0, this.now() - item.at);\n this.updateItem(item);\n this.toolIndexes.delete(toolCallId);\n }\n\n private ensureAssistant(): AssistantTranscriptItem {\n const existing = this.assistantIndex >= 0 ? this.items[this.assistantIndex] : undefined;\n if (existing && existing.kind === 'assistant') {\n return existing;\n }\n const item: AssistantTranscriptItem = {\n kind: 'assistant',\n id: this.nextId('assistant'),\n at: this.now(),\n text: '',\n thinking: '',\n streaming: true,\n };\n this.assistantIndex = this.items.length;\n this.appendItem(item);\n return item;\n }\n\n private toolItem(toolCallId: string): ToolTranscriptItem | undefined {\n const index = this.toolIndexes.get(toolCallId);\n if (index === undefined) {\n return undefined;\n }\n const item = this.items[index];\n return item && item.kind === 'tool' ? item : undefined;\n }\n\n private appendItem(item: TranscriptItem): void {\n this.items.push(item);\n this.options.emit({ type: 'transcript/append', payload: { ...item } });\n }\n\n private updateItem(item: TranscriptItem): void {\n this.options.emit({ type: 'transcript/update', payload: { ...item } });\n }\n\n private nextId(kind: string): string {\n this.counter += 1;\n return `${this.idPrefix}-${kind}-${this.counter}`;\n }\n}\n", "import type { AgentEvent, NoticeLevel } from '@morse/core';\nimport type { ChatPin, HostToClientMessage, PromptImage, TranscriptItem } from '@morse/protocol';\nimport { TranscriptProjector } from './transcript-projector.js';\n\nexport interface TranscriptUpdate {\n sessionKey: string;\n message: HostToClientMessage;\n /**\n * The transcript after this update, copied only when it is read. The controller\n * forwards `message` and never looks at the items, so copying every item on\n * every delta was garbage for nothing.\n */\n readonly items: TranscriptItem[];\n}\n\n/** How far back a session's transcript has been paged. */\nexport interface HistoryCursor {\n before?: string;\n hasOlder: boolean;\n}\n\n/**\n * Transcript per session, shared by every connection of a host.\n *\n * It sits next to the session registry because it has the same lifetime: the\n * agent process outlives a client, so its conversation must too. A controller\n * feeds agent events in and forwards the updates of whichever session it is\n * displaying \u2014 which is what makes a refresh (or a second window) keep the\n * transcript instead of starting from an empty panel.\n */\nexport class SessionTranscriptStore {\n private readonly projectors = new Map<string, TranscriptProjector>();\n private readonly cursors = new Map<string, HistoryCursor>();\n private readonly listeners = new Set<(update: TranscriptUpdate) => void>();\n /**\n * Agent events already projected, by instance (see `apply`). A weak set, so\n * a projected record never keeps itself alive after it has been fanned out.\n */\n private readonly applied = new WeakSet<AgentEvent>();\n\n subscribe(listener: (update: TranscriptUpdate) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n items(sessionKey: string): TranscriptItem[] {\n return this.projectors.get(sessionKey)?.snapshot() ?? [];\n }\n\n /**\n * Paging state lives with the transcript, not the connection: a refresh\n * reattaches to the same warm session and must keep offering older pages.\n */\n historyCursor(sessionKey: string): HistoryCursor {\n return this.cursors.get(sessionKey) ?? { hasOlder: false };\n }\n\n setHistoryCursor(sessionKey: string, cursor: HistoryCursor): void {\n this.cursors.set(sessionKey, cursor);\n }\n\n /**\n * Feeds one agent event in. Idempotent per event *instance*: the registry\n * fans a single tagged event out to every subscribed controller, and each of\n * them writes it here, so a host with more than one connected client would\n * otherwise project the same stream once per connection. The symptoms are\n * unmistakable \u2014 a delta doubled into \"therethere\", a second `message_end`\n * creating a duplicate prose row, and a second `tool-start` re-binding the\n * call id so the first tool item never receives its `tool-end` and stays\n * `running` forever (\"Working for \u2026\" never settling).\n *\n * Identity is the right key because `SessionRegistry.emit` passes the same\n * event object to every listener: the first writer projects it, the rest are\n * no-ops, whatever the connection count.\n */\n apply(sessionKey: string, event: AgentEvent): void {\n if (this.applied.has(event)) {\n return;\n }\n this.applied.add(event);\n this.projectorFor(sessionKey).apply(event);\n }\n\n /** Replays a resumed session's history into an empty transcript. */\n seed(sessionKey: string, items: TranscriptItem[]): void {\n const projector = this.projectorFor(sessionKey);\n for (const item of items) {\n projector.append(item);\n }\n }\n\n /** Inserts an older history page in front of a session's transcript. */\n prepend(sessionKey: string, items: TranscriptItem[]): void {\n this.projectorFor(sessionKey).prepend(items);\n }\n\n /**\n * Moves a transcript to a new session key after the agent forked the session\n * before a past message. The caller passes the rows that survive the fork;\n * the branch after the edited message is dropped with a single replace, so no\n * stale rows linger under the new session.\n */\n move(fromKey: string, toKey: string, items: TranscriptItem[]): void {\n if (fromKey === toKey) {\n this.projectorFor(toKey).replace(items);\n return;\n }\n this.projectors.delete(fromKey);\n this.cursors.delete(fromKey);\n this.cursors.set(toKey, { hasOlder: false });\n this.projectorFor(toKey).replace(items);\n }\n\n userPrompt(sessionKey: string, text: string, images?: PromptImage[], pins?: ChatPin[]): void {\n this.projectorFor(sessionKey).userPrompt(text, images, pins);\n }\n\n notice(sessionKey: string, level: NoticeLevel, text: string): void {\n this.projectorFor(sessionKey).notice(level, text);\n }\n\n error(sessionKey: string, message: string, detail?: string): void {\n this.projectorFor(sessionKey).error(message, detail);\n }\n\n /** Drops a transcript (used when a session is closed or restarted). */\n reset(sessionKey: string): void {\n // A replayed transcript may be fed the same records again on a cold resume;\n // those are fresh instances, so the identity guard needs no clearing here.\n this.cursors.delete(sessionKey);\n const projector = this.projectors.get(sessionKey);\n if (!projector) {\n return;\n }\n projector.reset();\n }\n\n clear(sessionKey: string): void {\n this.cursors.delete(sessionKey);\n this.projectors.delete(sessionKey);\n }\n\n dispose(): void {\n this.projectors.clear();\n this.cursors.clear();\n this.listeners.clear();\n }\n\n private projectorFor(sessionKey: string): TranscriptProjector {\n let projector = this.projectors.get(sessionKey);\n if (!projector) {\n projector = new TranscriptProjector({\n emit: (message) => {\n this.emit({\n sessionKey,\n message,\n get items(): TranscriptItem[] {\n return projector?.snapshot() ?? [];\n },\n });\n },\n });\n this.projectors.set(sessionKey, projector);\n }\n return projector;\n }\n\n private emit(update: TranscriptUpdate): void {\n for (const listener of [...this.listeners]) {\n listener(update);\n }\n }\n}\n", "import type {\n AgentCommand,\n AgentInteractionRequest,\n AgentSessionState,\n ModelRef,\n ThinkingLevel,\n TokenUsage,\n WorkspaceRef,\n} from '@morse/core';\nimport type {\n AgentFailure,\n CommandOption,\n InteractionRequest,\n ModelOption,\n NoticeLevel,\n SessionViewState,\n} from '@morse/protocol';\n\nexport interface SessionStateMeta {\n agentReady: boolean;\n agentStarting: boolean;\n agentError?: string;\n /** What `agentError` means and what to do about it (see `AgentFailure`). */\n agentFailure?: AgentFailure;\n busy: boolean;\n /** True when older transcript entries can still be loaded from the session. */\n hasOlderHistory?: boolean;\n /** True while the host is fetching the previous history page. */\n loadingOlderHistory?: boolean;\n}\n\nexport function emptySessionViewState(\n workspace: WorkspaceRef,\n meta: SessionStateMeta,\n): SessionViewState {\n return {\n workspace: { cwd: workspace.cwd, name: workspace.name },\n thinkingLevel: 'off',\n availableModels: [],\n availableThinkingLevels: [],\n availableCommands: [],\n streaming: false,\n busy: meta.busy,\n agentReady: meta.agentReady,\n agentStarting: meta.agentStarting,\n agentError: meta.agentError,\n agentFailure: meta.agentFailure,\n hasOlderHistory: meta.hasOlderHistory,\n loadingOlderHistory: meta.loadingOlderHistory,\n };\n}\n\n/**\n * The empty panel as a *draft*: no session exists yet, but the state still\n * carries what the backend offers without one \u2014 model catalog, thinking\n * levels, commands (from a never-recording probe, see `SessionRegistry\n * .draftDefaults`) \u2014 plus whatever the user has already picked. Those pending\n * picks are applied to the session the first prompt opens.\n */\nexport function draftSessionViewState(\n workspace: WorkspaceRef,\n meta: SessionStateMeta,\n catalog: AgentSessionState | undefined,\n pendingModel: ModelRef | undefined,\n pendingThinking: ThinkingLevel | undefined,\n): SessionViewState {\n const empty = emptySessionViewState(workspace, meta);\n if (!catalog) {\n return empty;\n }\n return {\n ...empty,\n model: toModelOption(pendingModel ?? catalog.model),\n thinkingLevel: pendingThinking ?? catalog.thinkingLevel ?? 'off',\n availableModels: catalog.availableModels.map(toModelOption).filter(isModelOption),\n availableThinkingLevels: catalog.availableThinkingLevels.map(toThinkingLevel),\n availableCommands: catalog.availableCommands.map(toCommandOption),\n };\n}\n\nexport function toSessionViewState(\n state: AgentSessionState,\n meta: SessionStateMeta,\n): SessionViewState {\n return {\n sessionId: state.sessionId,\n sessionTitle: state.sessionTitle,\n workspace: { cwd: state.workspace.cwd, name: state.workspace.name },\n model: toModelOption(state.model),\n thinkingLevel: state.thinkingLevel,\n availableModels: state.availableModels.map(toModelOption).filter(isModelOption),\n availableThinkingLevels: state.availableThinkingLevels.map(toThinkingLevel),\n availableCommands: state.availableCommands.map(toCommandOption),\n streaming: state.streaming,\n busy: meta.busy,\n usage: toUsage(state.usage),\n lastUsage: toUsage(state.lastUsage),\n costUsd: state.costUsd,\n contextUsage: state.contextUsage ? { ...state.contextUsage } : undefined,\n counts: state.counts ? { ...state.counts } : undefined,\n agentReady: meta.agentReady,\n agentStarting: meta.agentStarting,\n agentError: meta.agentError,\n agentFailure: meta.agentFailure,\n hasOlderHistory: meta.hasOlderHistory,\n loadingOlderHistory: meta.loadingOlderHistory,\n };\n}\n\nfunction toModelOption(model: ModelRef | undefined): ModelOption | undefined {\n if (!model) {\n return undefined;\n }\n return {\n provider: model.provider,\n id: model.id,\n name: model.name,\n contextWindow: model.contextWindow,\n maxTokens: model.maxTokens,\n };\n}\n\nfunction isModelOption(value: ModelOption | undefined): value is ModelOption {\n return value !== undefined;\n}\n\nfunction toCommandOption(command: AgentCommand): CommandOption {\n return {\n name: command.name,\n description: command.description,\n source: command.source,\n template: command.template,\n };\n}\n\nfunction toThinkingLevel(level: ThinkingLevel): ThinkingLevel {\n return level;\n}\n\nfunction toUsage(usage: TokenUsage | undefined): TokenUsage | undefined {\n return usage ? { ...usage } : undefined;\n}\n/** The agent's interaction requests already match the wire shape 1:1. */\nexport function toInteractionRequest(request: AgentInteractionRequest): InteractionRequest {\n switch (request.kind) {\n case 'select':\n return {\n requestId: request.requestId,\n kind: 'select',\n title: request.title,\n message: request.message,\n options: request.options.map((option) => ({ ...option })),\n };\n case 'confirm':\n return {\n requestId: request.requestId,\n kind: 'confirm',\n title: request.title,\n message: request.message,\n danger: request.danger,\n };\n case 'input':\n return {\n requestId: request.requestId,\n kind: 'input',\n title: request.title,\n placeholder: request.placeholder,\n value: request.value,\n };\n case 'editor':\n return {\n requestId: request.requestId,\n kind: 'editor',\n title: request.title,\n value: request.value,\n language: request.language,\n };\n }\n}\n\nexport function noticeMessage(\n level: NoticeLevel,\n text: string,\n at: number,\n): { type: 'notice'; payload: { level: NoticeLevel; text: string; at: number } } {\n return { type: 'notice', payload: { level, text, at } };\n}\n", "import { AgentUnavailableError, MorseError } from '@morse/core';\nimport type {\n AgentEvent,\n AgentForkMessage,\n AgentHistoryEntry,\n AgentInteractionRequest,\n AgentInteractionResponse,\n AgentSessionState,\n ChatService,\n ModelRef,\n MorseLogger,\n OpenedSession,\n PromptImage,\n PromptMode,\n SessionRegistry,\n SessionSummary,\n ThinkingLevel,\n WorkspaceRef,\n} from '@morse/core';\nimport {\n PROTOCOL_VERSION,\n type AgentErrorCode,\n type AgentFailure,\n type ChatPin,\n type ClientToHostMessage,\n type FrontendIdentity,\n type HostCapabilities,\n type HostToClientMessage,\n type InteractionResponse,\n type SessionActivity,\n type SessionViewState,\n type TranscriptItem,\n type UserTranscriptItem,\n} from '@morse/protocol';\nimport type { HostCommandHandler, NativeDialogs } from './native-dialogs.js';\nimport type { TerminalBackend, TerminalSession } from './terminal.js';\nimport { SessionTranscriptStore } from './transcript-store.js';\nimport {\n draftSessionViewState,\n noticeMessage,\n toInteractionRequest,\n toSessionViewState,\n} from './view-state.js';\n\nexport interface HostSessionServices {\n registry: SessionRegistry;\n chat: ChatService;\n}\n\n/**\n * Driven port (R2) declared by this glue: which directories the host lets the\n * agent work in. The server enforces an allow-list; a local VS Code window\n * allows anything the user can already open.\n */\nexport interface ProjectPolicy {\n canOpen(path: string): boolean;\n}\n\n/** The directories a host is allowed to show and work in. */\nexport type HostScopeOptions =\n | { kind: 'global' }\n | { kind: 'workspace'; roots: string[] };\n\nexport interface HostSessionControllerOptions {\n services: HostSessionServices;\n capabilities: HostCapabilities;\n emit: (message: HostToClientMessage) => void;\n logger: MorseLogger;\n /**\n * Which sessions/projects this host may show and open. `workspace` restricts\n * everything to the given roots (VS Code); `global` shows them all.\n */\n scope?: HostScopeOptions;\n /** Present when the host can render interactions natively; omitted otherwise. */\n dialogs?: NativeDialogs;\n /** Transcript storage shared by every client of this host. */\n transcripts: SessionTranscriptStore;\n frontend?: FrontendIdentity;\n onHostCommand?: HostCommandHandler;\n autoOpen?: boolean;\n /**\n * Runs interactive shells for the bottom panel's terminal. Omitted by a host\n * that leaves `capabilities.terminal` off (VS Code keeps its own terminal).\n */\n terminal?: TerminalBackend;\n /**\n * Dispose the shared session registry together with this controller.\n * Defaults to false: registries outlive a client so a refresh reattaches.\n */\n ownsRegistry?: boolean;\n /** Shown to the user when the agent backend cannot be started. */\n agentHint?: string;\n policy?: ProjectPolicy;\n}\n\ntype Guarded<T> = { ok: true; value: T } | { ok: false };\n\n/** How many history entries one page carries when a resumed session is seeded. */\nconst HISTORY_PAGE_SIZE = 50;\n\n/** The title a session without a name shows until the user says something. */\nconst UNTITLED_SESSION = 'New session';\n\n/**\n * The application service both hosts share: it owns the wire conversation for\n * one client, routes client messages into the core use cases, and projects agent\n * events back onto the wire.\n *\n * Multiple sessions and projects run at once: every session gets its own\n * transcript projector, and only the active one is streamed to the client (the\n * others stay warm and are replayed in full when the client switches to them).\n */\nexport class HostSessionController {\n private summaries = new Map<string, SessionSummary>();\n private detachRegistry: (() => void) | undefined;\n private detachTranscripts: (() => void) | undefined;\n private activeKey: string | undefined;\n /** True while the panel shows the intentional no-session draft state. */\n private isDraft = false;\n /** Project a draft opens its first session in (browser \"New session here\"). */\n private draftWorkspace: WorkspaceRef | undefined;\n /** Catalog of a never-recording probe \u2014 fills the draft pickers. */\n private draftCatalog: AgentSessionState | undefined;\n /** Picks made in the draft, applied to the session the first prompt opens. */\n private draftModel: ModelRef | undefined;\n private draftThinking: ThinkingLevel | undefined;\n private agentReady = false;\n private agentStarting = false;\n private agentError: string | undefined;\n /** The machine-readable half of `agentError` (see `AgentFailure`). */\n private agentFailure: AgentFailure | undefined;\n private busy = false;\n private disposed = false;\n private hasOpenedOnce = false;\n private hostReadyEmitted = false;\n private readonly pendingInteractions = new Set<string>();\n /** Cursor of the oldest history page loaded for the active session. */\n private historyBefore: string | undefined;\n private hasOlderHistory = false;\n private loadingOlderHistory = false;\n /** Discriminates history item ids across pages (see `historyItems`). */\n private historyPageSeq = 0;\n /** This client's live attachments to shells, keyed by the terminal id it named. */\n private readonly terminals = new Map<string, TerminalSession>();\n /**\n * The ids this connection has already opened. It separates a *first* open \u2014 a\n * page reattaching to a shell the host still runs \u2014 from a repeat open (the\n * Restart button), which must replace the shell and clear its scrollback\n * instead of replaying it into the emulator that already shows it.\n */\n private readonly openedTerminals = new Set<string>();\n /**\n * Bumped whenever a terminal id is opened or closed. A shell's late `exit`\n * (a replaced one, a killed one) compares its generation and stays silent, so\n * it cannot delete or report over the shell now holding the id.\n */\n private readonly terminalGenerations = new Map<string, number>();\n\n constructor(private readonly options: HostSessionControllerOptions) {}\n\n async start(): Promise<void> {\n this.detachRegistry = this.options.services.registry.subscribe((tagged) => {\n void this.onTaggedEvent(tagged.sessionKey, tagged.event);\n });\n // The transcript lives with the session, not with this connection: forward\n // the updates of whichever session this client is showing.\n this.detachTranscripts = this.options.transcripts.subscribe((update) => {\n if (update.sessionKey === this.activeKey) {\n this.options.emit(update.message);\n }\n });\n if (this.options.autoOpen !== false) {\n // Answer the handshake immediately with a truthful state: the agent is\n // starting. Otherwise the client renders its defaults for the whole spawn.\n this.agentStarting = true;\n }\n this.emitReady();\n if (this.options.autoOpen !== false) {\n await this.openDefaultSession();\n } else {\n // A reconnecting client (webview reload, browser refresh) must reattach\n // the session the host was already showing: the warm transcript replays\n // and the Reload button keeps the conversation instead of landing on an\n // empty panel. Activating a hot session spawns nothing; only a host with\n // no active session stays on the empty draft, which is what keeps a fresh\n // window from spawning a throwaway \"New session\" \u2014 the panel stays empty\n // until the user actually says something.\n const existingKey = this.options.services.registry.activeKeyOf();\n if (existingKey === undefined) {\n this.isDraft = true;\n void this.warmDraft();\n } else {\n await this.runSessionChange(() => this.options.services.registry.activate(existingKey));\n // A reload keeps the warm session: re-read its model catalog so a model\n // added while the panel was away shows up without restarting the host.\n void this.refreshModels();\n }\n }\n void this.publishLists();\n }\n\n /**\n * Points this controller at a new client. Hosts that reattach a still-warm\n * session to a reconnecting client use this so the same agent process keeps\n * serving without being respawned.\n */\n setEmitter(emit: (message: HostToClientMessage) => void): void {\n this.options.emit = emit;\n }\n\n async dispose(): Promise<void> {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n // Detach, do not kill: the shell and its scrollback belong to the host now,\n // so a reloaded page (a browser refresh) can attach to the same process. An\n // explicit `terminal/close` is the only thing that ends a shell.\n for (const session of this.terminals.values()) {\n session.detach();\n }\n this.terminals.clear();\n this.terminalGenerations.clear();\n // The next connection is a new viewer, so every id is a first open again.\n this.openedTerminals.clear();\n this.detachRegistry?.();\n this.detachRegistry = undefined;\n this.detachTranscripts?.();\n this.detachTranscripts = undefined;\n if (this.options.ownsRegistry === true) {\n await this.options.services.registry.dispose();\n }\n }\n\n async handleClientMessage(message: ClientToHostMessage): Promise<void> {\n if (this.disposed) {\n return;\n }\n switch (message.type) {\n case 'client/ready':\n if (message.payload.protocolVersion !== PROTOCOL_VERSION) {\n // A refused handshake is a banner, not a conversation entry: only the\n // transient error is sent, so no transcript is polluted.\n this.emitWireError(\n 'Frontend and host speak different protocol versions.',\n `frontend=${message.payload.protocolVersion} host=${PROTOCOL_VERSION}`,\n );\n return;\n }\n if (message.payload.frontend) {\n this.options.logger.info(\n `Frontend ${message.payload.frontend.name}@${message.payload.frontend.version} connected`,\n );\n }\n // While the first session is still starting, `start()` owns the single\n // `host/ready` we send. Answering here would flash \"agent unavailable\".\n if (!this.hostReadyEmitted) {\n return;\n }\n this.emitReady();\n void this.publishLists();\n return;\n case 'chat/prompt':\n await this.prompt(\n message.payload.text,\n message.payload.mode,\n message.payload.images,\n message.payload.pins,\n );\n return;\n case 'chat/edit':\n await this.editPrompt(message.payload.itemId, message.payload.text);\n return;\n case 'chat/fork':\n await this.forkPrompt(message.payload.itemId);\n return;\n case 'chat/abort':\n await this.guard(() => this.options.services.chat.abort());\n return;\n case 'session/new':\n // \"New session\" is an empty draft, not a spawn: no agent process, no\n // session file, no navigator entry. The first prompt opens the actual\n // session (see `prompt`). The optional cwd targets it \u2014 the browser\n // nav's \"New session here\" picks a project this way.\n await this.enterDraft(message.payload.cwd ? workspaceOf(message.payload.cwd) : undefined);\n return;\n case 'session/activate':\n case 'session/load':\n await this.activateSession(message.payload.sessionId, message.payload.cwd);\n return;\n case 'session/close':\n await this.closeSession(message.payload.sessionId);\n return;\n case 'session/delete':\n await this.deleteSession(message.payload.sessionId);\n return;\n case 'session/compact':\n // Compaction is part of the process, not a side note: like a run, the\n // same `busy` flag lights the composer's Working indicator (spinner,\n // elapsed chip, Stop button) and pulses the session in the navigator\n // while the agent summarizes the context. Reset in `finally` \u2014 a failed\n // compaction must hand the composer back, not wedge it on \"Working\".\n this.setBusy(true);\n try {\n await this.guard(() => this.options.services.chat.compact(message.payload.instructions));\n } finally {\n this.setBusy(false);\n }\n return;\n case 'history/load':\n await this.loadOlderHistory();\n return;\n case 'session/list':\n await this.publishSessions();\n return;\n case 'commands/refresh':\n await this.refreshCommands();\n return;\n case 'models/refresh':\n await this.refreshModels();\n return;\n case 'project/list':\n await this.publishProjects();\n return;\n case 'project/open':\n // Target the draft at that project (browser navigation): still no\n // spawn \u2014 the first prompt opens the session there.\n await this.enterDraft(workspaceOf(message.payload.path));\n return;\n case 'model/set':\n await this.setModel(message.payload.provider, message.payload.id);\n return;\n case 'thinking/set':\n await this.setThinkingLevel(message.payload.level);\n return;\n case 'interaction/respond':\n await this.respondToInteraction(message.payload);\n return;\n case 'host/command':\n await this.runHostCommand(\n message.payload.command,\n message.payload.args,\n message.payload.requestId,\n );\n return;\n case 'terminal/open':\n await this.openTerminal(message.payload);\n return;\n case 'terminal/input':\n this.terminals.get(message.payload.terminalId)?.write(message.payload.data);\n return;\n case 'terminal/resize':\n this.terminals\n .get(message.payload.terminalId)\n ?.resize(message.payload.cols, message.payload.rows);\n return;\n case 'terminal/close':\n this.closeTerminal(message.payload.terminalId);\n return;\n default:\n return;\n }\n }\n\n /**\n * Attaches this client to a shell for the bottom panel. The directory is the\n * viewing session's (or the draft's), never an arbitrary path the client\n * names \u2014 the backend still re-checks it against the host policy before\n * spawning.\n *\n * A *first* open on this connection may land on a shell that is already running\n * (a reloaded page reattaching); a repeat open (the Restart button) replaces\n * it, scrollback and all, so the fresh shell does not replay the old one.\n */\n private async openTerminal(payload: {\n terminalId: string;\n cwd?: string;\n cols?: number;\n rows?: number;\n }): Promise<void> {\n const backend = this.options.terminal;\n if (backend === undefined) {\n this.emitTerminalExit(payload.terminalId, undefined, 'This host has no terminal.');\n return;\n }\n // A repeat open on this connection replaces the shell rather than leaking a\n // second one behind the same name. A first open must not: the shell may be\n // one this connection is reattaching to, and that one keeps running.\n const previous = this.terminals.get(payload.terminalId);\n if (previous !== undefined) {\n previous.detach();\n this.terminals.delete(payload.terminalId);\n backend.close(payload.terminalId);\n } else if (this.openedTerminals.has(payload.terminalId)) {\n // A restart after the shell exited: drop the dead shell's scrollback, so\n // the fresh shell does not replay it over the emulator that still has it.\n backend.close(payload.terminalId);\n }\n this.openedTerminals.add(payload.terminalId);\n // Any pending exit for a superseded shell is now stale.\n const generation = (this.terminalGenerations.get(payload.terminalId) ?? 0) + 1;\n this.terminalGenerations.set(payload.terminalId, generation);\n\n const cwd = payload.cwd ?? this.currentState().workspace.cwd;\n const cols = clampGeometry(payload.cols, 80);\n const rows = clampGeometry(payload.rows, 24);\n let session: TerminalSession;\n try {\n session = await backend.attach(\n payload.terminalId,\n { cwd, cols, rows },\n {\n output: (data) => {\n if (this.terminalGenerations.get(payload.terminalId) === generation) {\n this.emitTerminalOutput(payload.terminalId, data);\n }\n },\n exit: (code, error) => {\n // A shell replaced or closed after this one opened is not the shell\n // the client is looking at; its end is not news.\n if (this.terminalGenerations.get(payload.terminalId) !== generation) {\n return;\n }\n this.terminals.delete(payload.terminalId);\n this.emitTerminalExit(payload.terminalId, code, error);\n },\n },\n );\n } catch (error: unknown) {\n this.options.logger.warn('Failed to open a terminal', error);\n this.emitTerminalExit(payload.terminalId, undefined, describeTerminalError(error));\n return;\n }\n // The client may have closed it while `attach` was still awaiting the shell.\n if (!this.disposed && this.terminalGenerations.get(payload.terminalId) === generation) {\n this.terminals.set(payload.terminalId, session);\n // Catch the viewer up before the shell's next chunk: what it missed while\n // no page was attached is the whole point of a surviving terminal.\n if (session.replay.length > 0) {\n this.emitTerminalOutput(payload.terminalId, session.replay);\n }\n } else {\n // Nobody is watching any more: leave the shell running for the next attach.\n session.detach();\n }\n }\n\n /**\n * Ends a shell for good: the reader closed the pane. Bumps the generation first,\n * so a late `exit` for the shell being replaced is not reported as news.\n */\n private closeTerminal(terminalId: string): void {\n this.terminalGenerations.set(terminalId, (this.terminalGenerations.get(terminalId) ?? 0) + 1);\n const session = this.terminals.get(terminalId);\n this.terminals.delete(terminalId);\n this.openedTerminals.delete(terminalId);\n session?.detach();\n this.options.terminal?.close(terminalId);\n }\n\n private emitTerminalOutput(terminalId: string, data: string): void {\n if (data.length > 0 && !this.disposed) {\n this.options.emit({ type: 'terminal/output', payload: { terminalId, data } });\n }\n }\n\n private emitTerminalExit(terminalId: string, code?: number, error?: string): void {\n if (!this.disposed) {\n this.options.emit({ type: 'terminal/exit', payload: { terminalId, code, error } });\n }\n }\n\n private async openDefaultSession(): Promise<void> {\n const existingKey = this.options.services.registry.activeKeyOf();\n if (existingKey) {\n await this.runSessionChange(() => this.options.services.registry.activate(existingKey));\n return;\n }\n await this.runSessionChange(() => this.options.services.registry.open());\n }\n\n /**\n * The empty panel is an intentional draft: no agent process, no session\n * file, no navigator entry \u2014 \"New session\" becomes real only once the first\n * prompt runs. A pending project and pending picks (model, thinking) wait\n * here until that first prompt opens the session they belong to.\n */\n private async enterDraft(workspace?: WorkspaceRef): Promise<void> {\n if (workspace && !this.canOpen(workspace.cwd)) {\n this.fail(\n `Morse is not allowed to run an agent in ${workspace.cwd}.`,\n 'Add it to MORSE_PROJECTS first.',\n );\n return;\n }\n this.activeKey = undefined;\n this.isDraft = true;\n this.draftWorkspace = workspace;\n this.resetViewState();\n this.options.emit({ type: 'transcript/replace', payload: { items: [] } });\n this.emitState();\n await this.warmDraft();\n }\n\n /** Clears everything the previous session had left on the view state. */\n private resetViewState(): void {\n this.historyBefore = undefined;\n this.hasOlderHistory = false;\n this.loadingOlderHistory = false;\n this.agentReady = false;\n this.agentStarting = false;\n this.clearAgentFailure();\n }\n\n /**\n * Forgets the agent failure, message and all. Called wherever the agent comes\n * back (a ready event, an adopted session), so a stale screen never outlives\n * the problem it described.\n */\n private clearAgentFailure(): void {\n this.agentError = undefined;\n this.agentFailure = undefined;\n }\n\n /** Fills the draft pickers from the registry's session-less probe. */\n private async warmDraft(): Promise<void> {\n try {\n this.draftCatalog = await this.options.services.registry.draftDefaults(this.draftModel);\n } catch (error: unknown) {\n // The session-less probe is where a missing pi shows up on load. Without\n // this the draft looked healthy (empty pickers) until the first prompt\n // failed to spawn, and a reader with a restored ~/.pi \u2014 sessions in the\n // sidebar, no binary on PATH \u2014 had no way to know the agent was gone.\n this.agentError = describeError(error);\n this.agentFailure = describeAgentFailure(error, this.options.agentHint);\n this.options.logger.warn('Could not probe the draft model catalog', error);\n this.emitState();\n return;\n }\n // The catalog lands a moment after the handshake; this refresh is what\n // turns the disabled \"no model\" pickers on while the reader is looking.\n if (this.activeKey === undefined && !this.disposed) {\n this.emitState();\n }\n }\n\n private async activateSession(sessionId: string, cwd?: string): Promise<void> {\n const known = this.summaries.get(sessionId);\n const path = cwd ?? known?.cwd;\n if (path && !this.canOpen(path)) {\n this.fail(`Morse is not allowed to run an agent in ${path}.`, 'Add it to MORSE_PROJECTS first.');\n return;\n }\n // Activating a session ends any draft, and its pending picks belonged to\n // a session that was never created \u2014 drop them here, not on the resumed\n // conversation.\n this.isDraft = false;\n this.draftWorkspace = undefined;\n this.draftModel = undefined;\n this.draftThinking = undefined;\n await this.runSessionChange(() =>\n this.options.services.registry.activate(\n sessionId,\n path ? workspaceOf(path) : undefined,\n ),\n );\n }\n\n private async closeSession(sessionId: string): Promise<void> {\n await this.guard(() => this.options.services.registry.close(sessionId));\n this.options.transcripts.clear(sessionId);\n await this.fallBackAfterSessionGone(sessionId);\n }\n\n /**\n * Deletes a session for good: the registry disposes its process and the\n * catalog drops the stored conversation. Which directories an agent may\n * touch is the host's rule (`ProjectPolicy`), so one use case is safe behind\n * a server allow-list and inside a VS Code window \u2014 the wiring differs, the\n * decision does not.\n */\n private async deleteSession(sessionId: string): Promise<void> {\n const path = this.summaries.get(sessionId)?.cwd;\n if (path !== undefined && path.length > 0 && !this.canOpen(path)) {\n this.fail(\n `Morse is not allowed to delete sessions in ${path}.`,\n 'Add it to MORSE_PROJECTS first.',\n );\n return;\n }\n const result = await this.guard(() => this.options.services.registry.remove(sessionId));\n if (!result.ok) {\n return;\n }\n this.options.transcripts.clear(sessionId);\n await this.fallBackAfterSessionGone(sessionId);\n this.options.emit(noticeMessage('info', 'Session deleted.', Date.now()));\n }\n\n /** The panel must stay usable once the session it showed is gone. */\n private async fallBackAfterSessionGone(sessionId: string): Promise<void> {\n if (this.activeKey !== sessionId) {\n this.publishActivity();\n await this.publishSessions();\n return;\n }\n\n // Keep the client usable: adopt another hot session, or return to the\n // empty panel. Spawning a replacement nobody asked for would recreate the\n // throwaway-session problem \u2014 the next prompt lazily opens one instead.\n this.activeKey = undefined;\n this.historyBefore = undefined;\n this.hasOlderHistory = false;\n this.loadingOlderHistory = false;\n this.agentReady = false;\n this.clearAgentFailure();\n const next = this.options.services.registry.hotKeys().at(-1);\n if (next) {\n await this.runSessionChange(() => this.options.services.registry.activate(next));\n } else {\n // Return to the draft the way session/new made it: no replacement\n // session nobody asked for; the next prompt opens one.\n await this.enterDraft();\n }\n await this.publishSessions();\n }\n\n /** Opens/activates a session while reporting progress and failures. */\n private async runSessionChange(\n change: () => Promise<OpenedSession>,\n ): Promise<void> {\n const announce = this.hasOpenedOnce;\n if (announce) {\n this.setBusy(true);\n }\n this.agentStarting = true;\n if (announce) {\n this.emitState();\n }\n try {\n const opened = await change();\n this.adopt(opened);\n } catch (error: unknown) {\n this.agentReady = false;\n this.agentError = describeError(error);\n this.agentFailure = describeAgentFailure(error, this.options.agentHint);\n this.options.logger.warn('Could not start the Morse agent', error);\n this.fail(\n `Could not start the Morse agent: ${this.agentError}`,\n this.options.agentHint,\n );\n } finally {\n this.agentStarting = false;\n this.hasOpenedOnce = true;\n if (announce) {\n this.setBusy(false);\n } else {\n this.emitState();\n }\n }\n }\n\n private adopt(opened: OpenedSession): void {\n this.activeKey = opened.key;\n // A real session is showing from now on; the draft target is consumed by\n // the open that just happened.\n this.isDraft = false;\n this.draftWorkspace = undefined;\n if (!opened.reused) {\n // A brand new process for this key: drop any stale transcript.\n this.options.transcripts.reset(opened.key);\n }\n // Paging state belongs to the session being adopted, never to the previous one.\n this.historyBefore = undefined;\n this.hasOlderHistory = false;\n this.loadingOlderHistory = false;\n this.agentReady = true;\n this.clearAgentFailure();\n this.emitTranscript(opened.key);\n this.emitState();\n // A new/activated session must show up in the list immediately; otherwise\n // the user picks \"New session\" and it is nowhere to be seen.\n void this.publishSessions();\n void this.seedHistory(opened.key);\n }\n\n /**\n * A resumed session has its messages on disk but streams nothing on attach, so\n * the transcript would look empty. Fill it from the *newest* page of history:\n * a session with thousands of messages must open without rendering them all,\n * and older pages are fetched on demand (see `loadOlderHistory`).\n */\n private async seedHistory(key: string): Promise<void> {\n if (this.options.transcripts.items(key).length > 0) {\n // A reattach (refresh, second window) already has the transcript; restore\n // how far back it was paged instead of forgetting older pages exist.\n const cursor = this.options.transcripts.historyCursor(key);\n this.historyBefore = cursor.before;\n this.hasOlderHistory = cursor.hasOlder;\n this.emitState();\n return;\n }\n if (this.activeKey !== key) {\n return;\n }\n const gateway = this.options.services.registry.active();\n if (!gateway) {\n return;\n }\n // A resumed session is seeded from history, so until the page lands we do\n // not yet know that it reaches the beginning: show \"Loading older\u2026\" rather\n // than claiming \"Start of conversation\".\n this.loadingOlderHistory = true;\n this.emitState();\n const result = await this.guard(() => gateway.history({ limit: HISTORY_PAGE_SIZE }));\n this.loadingOlderHistory = false;\n if (!result.ok || this.options.transcripts.items(key).length > 0 || this.activeKey !== key) {\n this.emitState();\n return;\n }\n this.historyBefore = result.value.before;\n this.hasOlderHistory = result.value.hasOlder;\n this.options.transcripts.setHistoryCursor(key, {\n before: result.value.before,\n hasOlder: result.value.hasOlder,\n });\n this.options.transcripts.seed(\n key,\n historyItems(key, result.value.entries, this.historyPageSeq++),\n );\n this.emitState();\n }\n\n /** Prepends the previous page of history when the reader reaches the top. */\n private async loadOlderHistory(): Promise<void> {\n if (!this.hasOlderHistory || this.loadingOlderHistory) {\n return;\n }\n const key = this.activeKey;\n const gateway = this.options.services.registry.active();\n if (key === undefined || !gateway) {\n return;\n }\n this.loadingOlderHistory = true;\n this.emitState();\n const result = await this.guard(() =>\n gateway.history({ limit: HISTORY_PAGE_SIZE, before: this.historyBefore }),\n );\n this.loadingOlderHistory = false;\n if (!result.ok || this.activeKey !== key) {\n this.emitState();\n return;\n }\n this.historyBefore = result.value.before;\n this.hasOlderHistory = result.value.hasOlder;\n this.options.transcripts.setHistoryCursor(key, {\n before: result.value.before,\n hasOlder: result.value.hasOlder,\n });\n this.options.transcripts.prepend(\n key,\n historyItems(key, result.value.entries, this.historyPageSeq++),\n );\n this.emitState();\n }\n\n private canOpen(path: string): boolean {\n const scope = this.options.scope ?? { kind: 'global' };\n if (scope.kind === 'workspace' && !insideRoots(path, scope.roots)) {\n return false;\n }\n return this.options.policy?.canOpen(path) ?? true;\n }\n\n private withinScope(path: string): boolean {\n const scope = this.options.scope ?? { kind: 'global' };\n return scope.kind === 'global' || insideRoots(path, scope.roots);\n }\n\n private async prompt(\n text: string,\n mode: PromptMode | undefined,\n images: PromptImage[] | undefined,\n pins: ChatPin[] | undefined,\n ): Promise<void> {\n if (!this.agentReady) {\n if (this.isDraft) {\n // The draft becomes the session it was waiting for: a fresh, recorded\n // one in the pending workspace \u2014 never a re-activation of whatever\n // session another connection happens to have active.\n await this.runSessionChange(() =>\n this.options.services.registry.open({ workspace: this.draftWorkspace }),\n );\n this.draftWorkspace = undefined;\n } else {\n await this.openDefaultSession();\n }\n if (!this.agentReady) {\n return;\n }\n }\n this.isDraft = false;\n // Picks made in the draft become the fresh session's settings, applied\n // before the first prompt so the answer already answers to them.\n const pendingModel = this.draftModel;\n const pendingThinking = this.draftThinking;\n this.draftModel = undefined;\n this.draftThinking = undefined;\n if (pendingModel) {\n await this.guard(() =>\n this.options.services.chat.setModel({\n provider: pendingModel.provider,\n id: pendingModel.id,\n name: pendingModel.name,\n }),\n );\n }\n if (pendingThinking) {\n await this.guard(() => this.options.services.chat.setThinkingLevel(pendingThinking));\n }\n const result = await this.guard(() =>\n this.options.services.chat.prompt(text, mode ?? 'new', images, pins),\n );\n if (!result.ok || !result.value.accepted) {\n return;\n }\n const key = this.activeKey;\n if (key) {\n // The transcript shows the message with its attachments: the pins and\n // images stay chips on the user item, whatever a later reload replays.\n this.options.transcripts.userPrompt(key, text, images, pins);\n // A nameless session takes its title from the first message (pi's name, or\n // the text the catalog derives), so refresh the navigator right away\n // instead of leaving it on \"New session\" until the next list request.\n void this.publishSessions();\n }\n }\n\n /**\n * Edits a past user message: fork the conversation before it (which discards\n * that turn and everything after), truncate the transcript to match, then send\n * the edited text as a fresh prompt. Attachments on the original message ride\n * along, so an image or pin is not silently dropped by the edit.\n */\n private async editPrompt(itemId: string, text: string): Promise<void> {\n const target = await this.forkBefore(itemId, 'edit');\n if (!target) {\n return;\n }\n await this.prompt(text, 'new', target.images, target.pins);\n }\n\n /**\n * Branches the conversation before a past user message without sending\n * anything. pi's fork writes a new session file while the old branch stays\n * resumable; the transcript re-homes onto the new, shorter branch and the\n * forked prompt goes back to the composer, so the user continues from the\n * fork point instead of retyping it.\n */\n private async forkPrompt(itemId: string): Promise<void> {\n const target = await this.forkBefore(itemId, 'fork');\n if (!target) {\n return;\n }\n // Only a committed fork seeds the composer; a cancelled one leaves it alone.\n this.options.emit({\n type: 'composer/seed',\n payload: {\n text: target.text,\n ...(target.images ? { images: target.images } : {}),\n ...(target.pins ? { pins: target.pins } : {}),\n },\n });\n // The fork is a new session (pi wrote a new file): surface it in the\n // navigator now instead of waiting for a prompt to refresh the list.\n void this.publishSessions();\n }\n\n /**\n * Forks the active session before a past user message and re-homes the\n * transcript onto the new branch. Shared by edit (which then sends) and fork\n * (which seeds the composer). Returns the forked message, or `undefined` when\n * the fork was refused \u2014 a notice has already been emitted in that case.\n */\n private async forkBefore(\n itemId: string,\n action: 'edit' | 'fork',\n ): Promise<UserTranscriptItem | undefined> {\n const gerund = action === 'edit' ? 'editing' : 'forking';\n const past = action === 'edit' ? 'edited' : 'forked';\n if (this.isDraft) {\n // No session exists in a draft, so there is nothing to fork: do not spawn\n // one just to fail the lookup (or re-activate another session).\n this.options.emit(\n noticeMessage('warn', `Nothing to ${action} yet \u2014 this session has no messages.`, Date.now()),\n );\n return undefined;\n }\n if (!this.agentReady) {\n await this.openDefaultSession();\n if (!this.agentReady) {\n return undefined;\n }\n }\n const key = this.activeKey;\n const gateway = this.options.services.registry.active();\n if (key === undefined || !gateway) {\n this.options.emit(noticeMessage('warn', `No session to ${action}.`, Date.now()));\n return undefined;\n }\n const items = this.options.transcripts.items(key);\n const target = items.find((item) => item.id === itemId);\n if (!target || target.kind !== 'user') {\n this.options.emit(\n noticeMessage('warn', `That message is no longer available to ${action}.`, Date.now()),\n );\n return undefined;\n }\n if (this.options.services.registry.stateOf(key)?.streaming === true) {\n this.options.emit(\n noticeMessage(\n 'warn',\n `Wait for the agent to finish before ${gerund} a message.`,\n Date.now(),\n ),\n );\n return undefined;\n }\n\n const messages = await this.guard(() => gateway.forkMessages());\n if (!messages.ok) {\n return undefined;\n }\n const entryId = entryIdForEdit(items, itemId, messages.value);\n if (entryId === undefined) {\n this.options.emit(\n noticeMessage(\n 'warn',\n `This message cannot be ${past} \u2014 the agent has no fork point for it.`,\n Date.now(),\n ),\n );\n return undefined;\n }\n\n const forked = await this.guard(() => this.options.services.registry.forkActive(entryId));\n if (!forked.ok) {\n return undefined;\n }\n if (forked.value.cancelled) {\n this.options.emit(noticeMessage('warn', 'The fork was cancelled.', Date.now()));\n return undefined;\n }\n\n // The forked branch ends right before the chosen message: drop that row and\n // everything after it, then re-home the transcript under the new session id.\n const index = items.findIndex((item) => item.id === itemId);\n const kept = items.slice(0, index);\n this.activeKey = forked.value.key;\n this.options.transcripts.move(key, forked.value.key, kept);\n this.historyBefore = undefined;\n this.hasOlderHistory = false;\n this.loadingOlderHistory = false;\n this.emitState();\n\n return target;\n }\n\n private async setModel(provider: string, id: string): Promise<void> {\n if (this.activeKey === undefined) {\n // Draft pick: shown as the pending model, applied to the session the\n // first prompt opens. Without a probe-cached catalog the name falls back\n // to the id the client sent.\n const match = this.draftCatalog?.availableModels.find(\n (model) => model.provider === provider && model.id === id,\n );\n this.draftModel = match ?? { provider, id, name: id };\n this.emitState();\n // pi reports thinking levels per *current* model, so the picker has to be\n // re-probed for the model the reader just chose before a session exists.\n void this.refreshDraftCatalog(this.draftModel);\n return;\n }\n const result = await this.guard(() =>\n this.options.services.chat.setModel({ provider, id, name: id }),\n );\n if (result.ok) {\n this.emitState();\n }\n }\n\n /**\n * Re-reads the draft catalog for a model picked before any session exists, so\n * the thinking picker mirrors that model's levels instead of the default\n * model's. The request is dropped when the reader picks again or a session\n * opens while the probe is in flight.\n */\n private async refreshDraftCatalog(model: ModelRef): Promise<void> {\n try {\n const catalog = await this.options.services.registry.draftDefaults(model);\n if (\n catalog &&\n this.activeKey === undefined &&\n !this.disposed &&\n this.draftModel === model\n ) {\n this.draftCatalog = catalog;\n this.emitState();\n }\n } catch (error: unknown) {\n this.options.logger.warn('Could not probe the draft catalog for the picked model', error);\n }\n }\n\n private async setThinkingLevel(level: SessionViewState['thinkingLevel']): Promise<void> {\n if (this.activeKey === undefined) {\n this.draftThinking = level;\n this.emitState();\n return;\n }\n const result = await this.guard(() => this.options.services.chat.setThinkingLevel(level));\n if (result.ok) {\n this.emitState();\n }\n }\n\n private async respondToInteraction(response: InteractionResponse): Promise<void> {\n this.pendingInteractions.delete(response.requestId);\n this.options.emit({ type: 'interaction/dismiss', payload: { requestId: response.requestId } });\n const gateway = this.options.services.registry.active();\n if (!gateway) {\n return;\n }\n await this.guard(() => gateway.respondToInteraction(response));\n }\n\n /**\n * The palette asked for a fresh command list. The agent re-reads the template\n * files and reports back through `agent/state`, so the composer sees a template\n * added, changed or deleted since the session started \u2014 without a pi reload.\n */\n private async refreshCommands(): Promise<void> {\n const gateway = this.options.services.registry.active();\n if (gateway?.refreshCommands === undefined) {\n return;\n }\n await this.guard(() => gateway.refreshCommands!());\n }\n\n /**\n * Re-reads the model catalog on request. A warm session re-asks pi over RPC\n * (cheap); a draft re-runs the session-less probe, since that is where its\n * picker's catalog came from and it would otherwise be frozen for the host's\n * lifetime.\n */\n private async refreshModels(): Promise<void> {\n if (this.activeKey === undefined) {\n this.options.services.registry.refreshDraftDefaults();\n await this.warmDraft();\n return;\n }\n const gateway = this.options.services.registry.active();\n if (gateway?.refreshModels === undefined) {\n return;\n }\n await this.guard(() => gateway.refreshModels!());\n }\n\n private async runHostCommand(\n command: string,\n args: Record<string, unknown> | undefined,\n requestId?: string,\n ): Promise<void> {\n const handler = this.options.onHostCommand;\n if (!handler) {\n this.options.emit(\n noticeMessage('warn', `This host does not support \"${command}\".`, Date.now()),\n );\n this.answerHostCommand(requestId, false, undefined, `This host does not support \"${command}\".`);\n return;\n }\n // A host command answers a surface that asked for a value \u2014 the folder a user\n // is typing, a file being previewed, a working tree on a timer. Its failure\n // belongs to that caller (it receives `ok: false`) and to the log; it is not\n // conversation content. Appending it here is what filled the transcript with a\n // red \"scandir\" row for every unfinished or wrong path the folder browser was\n // asked about, and for every poll of a directory that had gone away. `guard`\n // stays for the session/agent commands, whose failure *is* the panel's news.\n let value: unknown;\n try {\n value = await handler(command, args, { cwd: this.currentState().workspace.cwd });\n } catch (error: unknown) {\n this.options.logger.error(`Morse command failed: ${describeError(error)}`, error);\n this.answerHostCommand(requestId, false, undefined, `\"${command}\" failed`);\n return;\n }\n this.answerHostCommand(requestId, true, value);\n }\n\n /** Replies to a host command the frontend asked a value for. */\n private answerHostCommand(\n requestId: string | undefined,\n ok: boolean,\n data?: unknown,\n error?: string,\n ): void {\n if (!requestId) {\n return;\n }\n this.options.emit({ type: 'host/command/result', payload: { requestId, ok, data, error } });\n }\n\n private async onTaggedEvent(sessionKey: string, event: AgentEvent): Promise<void> {\n if (sessionKey !== this.activeKey) {\n // Keep inactive sessions' transcripts warm without streaming them, and\n // still report their activity: that is what makes several sessions show\n // as working at once in the navigator.\n if (event.type !== 'agent/ready' && event.type !== 'agent/state') {\n this.options.transcripts.apply(sessionKey, event);\n if (event.type === 'agent/run-end') {\n void this.publishSessions();\n }\n } else {\n this.publishActivity();\n }\n return;\n }\n\n switch (event.type) {\n case 'agent/ready':\n this.agentReady = true;\n this.clearAgentFailure();\n this.emitState();\n return;\n case 'agent/state':\n this.emitState();\n return;\n case 'agent/run-end':\n this.options.transcripts.apply(sessionKey, event);\n // pi writes the session file at the first user message and the catalog\n // derives its title from that text, so a finished run is the moment to\n // replace the placeholder \"New session\" in the navigator.\n void this.publishSessions();\n return;\n case 'agent/interaction':\n await this.handleInteraction(event.request);\n return;\n case 'agent/fatal':\n this.agentReady = false;\n this.agentError = event.message;\n // A process that died carries no code the frontend could act on, but the\n // host still knows where to look: keep the hint, drop any stale code.\n this.agentFailure = hintOnly(this.options.agentHint);\n // Through the projector (not `transcripts.error`) so the error row is\n // guarded by the store's per-event identity: every connection reacts to\n // the same fatal event, and only the first must write it.\n this.options.transcripts.apply(sessionKey, event);\n this.emitState();\n return;\n default:\n this.options.transcripts.apply(sessionKey, event);\n }\n }\n\n private async handleInteraction(request: AgentInteractionRequest): Promise<void> {\n const dialogs = this.options.dialogs;\n if (!dialogs) {\n this.pendingInteractions.add(request.requestId);\n this.options.emit({ type: 'interaction/request', payload: toInteractionRequest(request) });\n return;\n }\n const response = await this.askNatively(dialogs, request);\n const gateway = this.options.services.registry.active();\n if (!gateway) {\n return;\n }\n await this.guard(() => gateway.respondToInteraction(response));\n }\n\n private async askNatively(\n dialogs: NativeDialogs,\n request: AgentInteractionRequest,\n ): Promise<AgentInteractionResponse> {\n switch (request.kind) {\n case 'select': {\n const value = await dialogs.select(request);\n return value === undefined\n ? { requestId: request.requestId, cancelled: true }\n : { requestId: request.requestId, value };\n }\n case 'confirm':\n return { requestId: request.requestId, confirmed: await dialogs.confirm(request) };\n case 'input': {\n const value = await dialogs.input(request);\n return value === undefined\n ? { requestId: request.requestId, cancelled: true }\n : { requestId: request.requestId, value };\n }\n case 'editor': {\n const value = await dialogs.editor(request);\n return value === undefined\n ? { requestId: request.requestId, cancelled: true }\n : { requestId: request.requestId, value };\n }\n }\n }\n\n private async publishLists(): Promise<void> {\n // `publishSessions` also refreshes the project list: both are derived from\n // the same catalog, and the navigator needs both to place a session in a\n // group.\n await this.publishSessions();\n }\n\n /**\n * Projects are derived from sessions, so the caller can hand in the list it\n * just built instead of making the registry read the catalog again. That also\n * lets a brand-new project show up before pi has persisted its first session.\n */\n private async publishProjects(sessions?: readonly SessionSummary[]): Promise<void> {\n const result = await this.guard(() => this.options.services.registry.listProjects(sessions));\n if (result.ok) {\n // A workspace-scoped host only ever shows the folders it has open.\n const projects = result.value.filter((project) => this.withinScope(project.path));\n this.options.emit({ type: 'project/list', payload: { projects } });\n }\n }\n\n private async publishSessions(): Promise<void> {\n const result = await this.guard(() => this.options.services.registry.listSessions());\n if (result.ok) {\n const sessions = result.value.filter((session) => this.withinScope(session.cwd));\n const known = new Set(sessions.map((session) => session.id));\n // A session pi has not persisted yet is invisible in the catalog, so the\n // list would be missing the very session the user just created. Fold the\n // live ones in (id = registry key) so the list always matches reality.\n for (const key of this.options.services.registry.hotKeys()) {\n if (known.has(key)) {\n continue;\n }\n const state = this.options.services.registry.stateOf(key);\n const cwd = state?.workspace.cwd ?? this.options.services.registry.defaultWorkspace.cwd;\n if (!this.withinScope(cwd)) {\n continue;\n }\n sessions.push({\n id: key,\n title: state?.sessionTitle ?? this.derivedTitle(key) ?? UNTITLED_SESSION,\n cwd,\n updatedAt: Date.now(),\n messageCount: 0,\n });\n }\n this.summaries = new Map(sessions.map((session) => [session.id, session]));\n this.options.emit({ type: 'session/list', payload: { sessions } });\n // A session change is a project change (cwd, count, last-used), and the\n // navigator builds its groups from `project/list`. Without this the group\n // for a new project \u2014 and every session inside a project missing from the\n // last list \u2014 stayed invisible until a manual refresh.\n await this.publishProjects(sessions);\n }\n }\n\n /**\n * Title for a session pi has not persisted yet. pi names a session only when\n * something calls `set_session_name`, and until the file exists the catalog\n * cannot derive a title from the file either \u2014 so derive it from the first\n * user message here, the same way the catalog does. A session must not stay\n * \"New session\" once the user has said something.\n */\n private derivedTitle(key: string): string | undefined {\n for (const item of this.options.transcripts.items(key)) {\n if (item.kind !== 'user' || item.text.trim().length === 0) {\n continue;\n }\n const single = item.text.replace(/\\s+/g, ' ').trim();\n return single.length > 72 ? `${single.slice(0, 72)}\u2026` : single;\n }\n return undefined;\n }\n\n private emitReady(): void {\n this.hostReadyEmitted = true;\n this.options.emit({\n type: 'transcript/replace',\n payload: { items: this.activeKey ? this.options.transcripts.items(this.activeKey) : [] },\n });\n this.options.emit({\n type: 'host/ready',\n payload: {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: this.options.capabilities,\n state: this.currentState(),\n frontend: this.options.frontend,\n },\n });\n this.publishActivity();\n }\n\n private emitTranscript(key: string): void {\n this.options.emit({\n type: 'transcript/replace',\n payload: { items: this.options.transcripts.items(key) },\n });\n }\n\n private emitState(): void {\n this.options.emit({ type: 'session/state', payload: this.currentState() });\n this.publishActivity();\n }\n\n /**\n * Reports every live session (hot process), not only the selected one. The\n * navigator marks the ones the agent is actively working in, so two parallel\n * conversations both read as running instead of one silently taking over.\n */\n private publishActivity(): void {\n const sessions: SessionActivity[] = this.options.services.registry\n .hotKeys()\n .map((key) => {\n const state = this.options.services.registry.stateOf(key);\n const isActive = key === this.activeKey;\n return {\n sessionKey: key,\n streaming: state?.streaming ?? false,\n busy: isActive ? this.busy : false,\n agentReady: isActive ? this.agentReady : state !== undefined,\n agentStarting: isActive ? this.agentStarting : false,\n ...(isActive && this.agentError !== undefined ? { agentError: this.agentError } : {}),\n };\n });\n this.options.emit({ type: 'session/activity', payload: { sessions } });\n }\n\n private currentState(): SessionViewState {\n const meta = {\n agentReady: this.agentReady,\n agentStarting: this.agentStarting,\n agentError: this.agentError,\n agentFailure: this.agentFailure,\n busy: this.busy,\n hasOlderHistory: this.hasOlderHistory,\n loadingOlderHistory: this.loadingOlderHistory,\n };\n const state = this.activeKey === undefined\n ? undefined\n : this.options.services.registry.stateOf(this.activeKey);\n if (state) {\n const view = toSessionViewState(state, meta);\n // pi names a session only when it emits `session_name`, so a *resumed*\n // session arrives without one \u2014 and the panel then falls back to the\n // workspace name for a title it already knows. The catalog is the\n // authority (it is what the sidebar lists); a session still on the\n // placeholder takes its title from its first prompt instead.\n if (view.sessionTitle === undefined && this.activeKey !== undefined) {\n const catalog = this.summaries.get(this.activeKey)?.title;\n view.sessionTitle =\n catalog !== undefined && catalog !== UNTITLED_SESSION\n ? catalog\n : this.derivedTitle(this.activeKey);\n }\n return view;\n }\n // The autoOpen:false draft: a truthful workspace line, and \u2014 once the\n // session-less probe lands \u2014 pickers that work before any session exists.\n return draftSessionViewState(\n this.draftWorkspace ?? this.options.services.registry.defaultWorkspace,\n meta,\n this.draftCatalog,\n this.draftModel,\n this.draftThinking,\n );\n }\n\n private setBusy(busy: boolean): void {\n if (this.busy === busy) {\n return;\n }\n this.busy = busy;\n this.emitState();\n }\n\n private fail(message: string, detail?: string): void {\n const key = this.activeKey;\n if (key !== undefined) {\n this.options.transcripts.error(key, message, detail);\n return;\n }\n // No transcript to attach to (draft): say it as a notice. The wire `error`\n // stays reserved for a refusing handshake \u2014 reusing it here raised the\n // frontend's \"Offline \u2014 the host wouldn't take this frontend\" banner, whose\n // Reload button then wiped the panel for an error that never touched the\n // connection. A spawn failure additionally sets `agentError`, which shows\n // the banner that actually fits.\n this.options.emit(noticeMessage('error', detail ? `${message}\\n${detail}` : message, Date.now()));\n }\n\n /** Transient error for the client (banner), never a transcript item. */\n private emitWireError(message: string, detail?: string): void {\n this.options.emit({ type: 'error', payload: { message, detail, at: Date.now() } });\n this.options.emit(\n noticeMessage('error', detail ? `${message}\\n${detail}` : message, Date.now()),\n );\n }\n\n private async guard<T>(action: () => Promise<T>): Promise<Guarded<T>> {\n try {\n return { ok: true, value: await action() };\n } catch (error: unknown) {\n const message = describeError(error);\n this.options.logger.error(`Morse command failed: ${message}`, error);\n this.fail(message);\n return { ok: false };\n }\n }\n}\n\nfunction workspaceOf(path: string): WorkspaceRef {\n const parts = path.split(/[\\\\/]/).filter((part) => part.length > 0);\n return { cwd: path, name: parts.at(-1) ?? path };\n}\n\nfunction insideRoots(path: string, roots: string[]): boolean {\n return roots.some((root) => path === root || path.startsWith(`${root}/`) || path.startsWith(`${root}\\\\`));\n}\n\n/** Domain history -> transcript items (presentation mapping stays in this layer). */\nfunction historyItems(key: string, entries: AgentHistoryEntry[], page: number): TranscriptItem[] {\n return entries.map((entry, index): TranscriptItem => {\n const id = `${key}#history-${page}-${index}`;\n const at = entry.at ?? 0;\n if (entry.role === 'user') {\n return {\n kind: 'user',\n id,\n at,\n text: entry.text,\n ...(entry.images ? { images: entry.images } : {}),\n ...(entry.pins ? { pins: entry.pins } : {}),\n };\n }\n if (entry.role === 'tool') {\n return {\n kind: 'tool',\n id,\n at,\n name: entry.name,\n title: entry.title,\n status: entry.status,\n input: entry.input,\n output: entry.output,\n };\n }\n if (entry.role === 'compaction') {\n // A marker row, not a message: the transcript keeps every folded entry \u2014\n // the divider says where the agent's context was reset, not where history\n // ends (\"Start of conversation\" belongs to the real beginning again).\n return {\n kind: 'compaction',\n id,\n at,\n summary: entry.summary,\n ...(entry.tokensBefore !== undefined ? { tokensBefore: entry.tokensBefore } : {}),\n };\n }\n return {\n kind: 'assistant',\n id,\n at,\n text: entry.text,\n thinking: entry.thinking ?? '',\n streaming: false,\n model: entry.model,\n };\n });\n}\n\n/**\n * Maps a transcript item to the fork entry pi knows.\n *\n * pi's `get_fork_messages` lists every user message with non-empty text \u2014 the\n * same messages the transcript shows as text or pins (an image-only message has\n * no text and is not forkable). Matching the same distance from the end keeps\n * the two lists aligned even when an older history page is still unloaded.\n */\nfunction entryIdForEdit(\n items: TranscriptItem[],\n itemId: string,\n forkMessages: AgentForkMessage[],\n): string | undefined {\n const forkable = items.filter(\n (item): item is UserTranscriptItem =>\n item.kind === 'user' && (item.text.trim().length > 0 || (item.pins?.length ?? 0) > 0),\n );\n const index = forkable.findIndex((item) => item.id === itemId);\n if (index === -1) {\n return undefined;\n }\n const position = forkMessages.length - 1 - (forkable.length - 1 - index);\n return position >= 0 ? forkMessages[position]?.entryId : undefined;\n}\n\nfunction describeError(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return typeof error === 'string' ? error : JSON.stringify(error);\n}\n\n/** Terminal geometry the wire named: a positive integer, or the default. */\nfunction clampGeometry(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value)) {\n return fallback;\n }\n return Math.max(1, Math.min(1000, Math.floor(value)));\n}\n\n/** A terminal that could not open says why, the same way a command failure does. */\nfunction describeTerminalError(error: unknown): string {\n return error instanceof MorseError ? error.message : describeError(error);\n}\n\n/**\n * The machine-readable half of a failed start. Only the codes a frontend has help\n * for cross the wire; everything else keeps its message and the host's hint \u2014 a\n * frontend that guessed would offer the wrong remedy.\n */\nfunction describeAgentFailure(\n error: unknown,\n hint: string | undefined,\n): AgentFailure | undefined {\n const code = agentErrorCode(error);\n const install = error instanceof AgentUnavailableError ? error.remedy?.install : undefined;\n const failure: AgentFailure = {\n ...(code ? { code } : {}),\n ...(install ? { install } : {}),\n ...(hint ? { hint } : {}),\n };\n return Object.keys(failure).length > 0 ? failure : undefined;\n}\n\nfunction agentErrorCode(error: unknown): AgentErrorCode | undefined {\n if (!(error instanceof MorseError)) {\n return undefined;\n }\n // `MorseError.code` is a plain string; only the two the UI can help with pass.\n return error.code === 'agent-unavailable' || error.code === 'agent-protocol'\n ? error.code\n : undefined;\n}\n\n/** The host's next step without a code: a dead process, not a resolution failure. */\nfunction hintOnly(hint: string | undefined): AgentFailure | undefined {\n return hint ? { hint } : undefined;\n}", "import { Controller, Get } from '@nestjs/common';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { PROTOCOL_VERSION, parseFrontendManifest } from '@morse/protocol';\nimport type { MorseServerConfig } from '../../app/config.js';\nimport { Inject } from '@nestjs/common';\nimport { MORSE_CONFIG } from '../../app/tokens.js';\n\n@Controller('api')\nexport class HealthController {\n constructor(@Inject(MORSE_CONFIG) private readonly config: MorseServerConfig) {}\n\n @Get('health')\n health(): {\n status: string;\n protocolVersion: number;\n workspace: string;\n frontend: string;\n instance?: string;\n } {\n return {\n status: 'ok',\n protocolVersion: PROTOCOL_VERSION,\n workspace: this.config.workspace.cwd,\n frontend: this.config.uiDir,\n // Echoed so the CLI that spawned this daemon can tell it apart from an\n // unrelated process that later reused the same pid.\n ...(this.config.instance ? { instance: this.config.instance } : {}),\n };\n }\n\n @Get('manifest')\n async manifest(): Promise<unknown> {\n const raw = await readFile(join(this.config.uiDir, 'webview.manifest.json'), 'utf8').catch(\n () => undefined,\n );\n return parseFrontendManifest(raw) ?? { name: 'unknown', protocolVersion: PROTOCOL_VERSION };\n }\n}\n", "import type { MorseLogger } from '@morse/core';\nimport type { PiRpcAdapter } from '@morse/adapter-pi-rpc';\n\nexport const MORSE_CONFIG = 'MORSE_CONFIG';\nexport const MORSE_LOGGER = 'MORSE_LOGGER';\nexport const MORSE_PI_ADAPTER = 'MORSE_PI_ADAPTER';\nexport const MORSE_SESSION_REGISTRY = 'MORSE_SESSION_REGISTRY';\nexport const MORSE_TRANSCRIPT_STORE = 'MORSE_TRANSCRIPT_STORE';\nexport const MORSE_PROJECT_POLICY = 'MORSE_PROJECT_POLICY';\n\nexport type { MorseLogger, PiRpcAdapter };\n\n\n\n", "import { Logger } from '@nestjs/common';\nimport type { MorseLogger } from '@morse/core';\n\n/** Morse's logging port implemented on top of the NestJS logger. */\nexport class NestMorseLogger implements MorseLogger {\n private readonly logger = new Logger('morse');\n\n debug(message: string, detail?: unknown): void {\n this.logger.debug(withDetail(message, detail));\n }\n\n info(message: string, detail?: unknown): void {\n this.logger.log(withDetail(message, detail));\n }\n\n warn(message: string, detail?: unknown): void {\n this.logger.warn(withDetail(message, detail));\n }\n\n error(message: string, detail?: unknown): void {\n this.logger.error(withDetail(message, detail));\n }\n}\n\nfunction withDetail(message: string, detail: unknown): string {\n if (detail === undefined) {\n return message;\n }\n if (detail instanceof Error) {\n return `${message}: ${detail.message}`;\n }\n try {\n return `${message} ${JSON.stringify(detail)}`;\n } catch {\n return `${message} ${String(detail)}`;\n }\n}\n", "import { isAbsolute, resolve, sep } from 'node:path';\nimport type { ProjectPolicy } from '@morse/host-runtime';\n\n/**\n * Which directories the server lets the agent work in.\n *\n * With `MORSE_PROJECTS` set, only those roots (and their subdirectories) are\n * allowed. Without it the host trusts the operator: any absolute path is\n * accepted, which is the right default for a server bound to 127.0.0.1 and for\n * the UI, which only offers discovered pi projects plus the default workspace.\n */\nexport class ServerProjectPolicy implements ProjectPolicy {\n private readonly roots: string[];\n\n constructor(roots: string[]) {\n this.roots = roots.filter((root) => root.trim().length > 0).map((root) => resolve(root));\n }\n\n get allowList(): string[] {\n return [...this.roots];\n }\n\n canOpen(path: string): boolean {\n if (!isAbsolute(path)) {\n return false;\n }\n if (this.roots.length === 0) {\n return true;\n }\n const target = resolve(path);\n return this.roots.some(\n (root) => target === root || target.startsWith(`${root}${sep}`),\n );\n }\n}\n", "import { spawn as spawnPty, type IPty } from 'node-pty';\nimport { Inject, Injectable, type OnModuleDestroy } from '@nestjs/common';\nimport { UnsupportedByHostError, type MorseLogger } from '@morse/core';\nimport {\n TERMINAL_BUFFER_CHARS,\n trimTail,\n type TerminalBackend,\n type TerminalOpenOptions,\n type TerminalSession,\n type TerminalSink,\n} from '@morse/host-runtime';\nimport type { MorseServerConfig } from '../../app/config.js';\nimport { MORSE_CONFIG, MORSE_LOGGER, MORSE_PROJECT_POLICY } from '../../app/tokens.js';\nimport type { ServerProjectPolicy } from '../projects/project-policy.js';\nimport { readTerminalLog, removeTerminalLog, writeTerminalLog } from './terminal-log.js';\n\n/** How long a shell gets to exit on kill before it is killed outright. */\nconst SIGKILL_AFTER_MS = 1_500;\n/** How often the streaming scrollback is written down. A shell can output fast. */\nconst FLUSH_DEBOUNCE_MS = 2_000;\n\n/** The user's shell, falling back per platform when `$SHELL` is not set. */\nfunction defaultShell(): string {\n const configured = process.env.SHELL?.trim();\n if (configured !== undefined && configured.length > 0) {\n return configured;\n }\n return process.platform === 'win32' ? 'powershell.exe' : '/bin/bash';\n}\n\n/**\n * One terminal the host is holding: the live shell (when it has one), the viewers\n * attached to it, and the scrollback.\n *\n * `output` is both the replay buffer and the on-disk log's in-memory copy: the\n * shell appends, a viewer that attaches late gets the whole bounded tail, and the\n * file is written on a debounce so a host restart still has it.\n */\ninterface TerminalEntry {\n pty?: IPty;\n sinks: Set<TerminalSink>;\n output: string;\n /** There is output the file has not seen yet. */\n dirty: boolean;\n flushTimer?: ReturnType<typeof setTimeout>;\n idleTimer?: ReturnType<typeof setTimeout>;\n /**\n * Bumped whenever the shell is replaced or closed. A dead shell's late\n * callbacks compare it and stay silent, so they cannot append to (or report\n * over) the shell now holding the id.\n */\n generation: number;\n}\n\n/**\n * The browser host's terminal (driven adapter). A real pseudo-terminal via\n * `node-pty`, so the shell is interactive exactly as in a desktop terminal:\n * prompts, colours, line editing, resize and full-screen programs (vim, top)\n * all work.\n *\n * The process runs in the viewing session's directory and is gated by\n * `ProjectPolicy`, like opening a session. A directory the policy refuses throws\n * before anything is spawned.\n *\n * This class is a *registry*, not a per-connection spawner: it is a singleton, so\n * the shell and its scrollback outlive the connection that opened it. A reloaded\n * page attaches to the same process and catches up on `replay`; only an explicit\n * `close` (or the idle timeout) ends a shell. The scrollback also lands under\n * `<MORSE_HOME>/terminals/`, so the output survives the *host* restarting too.\n */\n@Injectable()\nexport class ServerTerminalBackend implements TerminalBackend, OnModuleDestroy {\n private readonly entries = new Map<string, TerminalEntry>();\n\n constructor(\n @Inject(MORSE_PROJECT_POLICY) private readonly policy: ServerProjectPolicy,\n @Inject(MORSE_LOGGER) private readonly logger: MorseLogger,\n @Inject(MORSE_CONFIG) private readonly config: MorseServerConfig,\n ) {}\n\n attach(\n terminalId: string,\n options: TerminalOpenOptions,\n sink: TerminalSink,\n ): TerminalSession {\n const entry = this.entryFor(terminalId);\n if (entry.pty === undefined) {\n // No live shell: either the first open ever, or one that exited / was\n // reclaimed. The log is already loaded, so a fresh shell continues the\n // history instead of starting blank.\n this.spawn(terminalId, entry, options);\n } else {\n // A live shell takes the reattaching viewer's size.\n this.resize(entry, options.cols, options.rows);\n }\n entry.sinks.add(sink);\n this.cancelIdle(entry);\n return {\n replay: entry.output,\n write: (data) => entry.pty?.write(data),\n resize: (cols, rows) => this.resize(entry, cols, rows),\n detach: () => this.detach(terminalId, entry, sink),\n };\n }\n\n /** Ends a shell for good, drops its scrollback, and tells any other viewer. */\n close(terminalId: string): void {\n const entry = this.entries.get(terminalId);\n this.entries.delete(terminalId);\n if (entry !== undefined) {\n this.stopFlush(entry);\n this.cancelIdle(entry);\n entry.generation += 1;\n const sinks = [...entry.sinks];\n entry.sinks.clear();\n this.killPty(entry.pty);\n entry.pty = undefined;\n // A viewer in another window is looking at a shell that is now gone.\n for (const sink of sinks) {\n sink.exit();\n }\n }\n removeTerminalLog(this.config.dataDir, terminalId);\n }\n\n onModuleDestroy(): void {\n // The host is going away, so the shells cannot outlive it \u2014 but the\n // scrollback can. Persist first, then kill; the next start replays the file.\n for (const [terminalId, entry] of this.entries) {\n entry.generation += 1;\n this.persist(terminalId, entry);\n this.killPty(entry.pty);\n entry.pty = undefined;\n this.stopFlush(entry);\n this.cancelIdle(entry);\n }\n this.entries.clear();\n }\n\n /** SIGTERM, then SIGKILL for a shell that ignores it (a hung process group). */\n private killPty(pty: IPty | undefined): void {\n if (pty === undefined) {\n return;\n }\n try {\n pty.kill();\n } catch {\n return;\n }\n const timer = setTimeout(() => {\n try {\n pty.kill('SIGKILL');\n } catch {\n // Already gone.\n }\n }, SIGKILL_AFTER_MS);\n timer.unref?.();\n }\n\n private entryFor(terminalId: string): TerminalEntry {\n const existing = this.entries.get(terminalId);\n if (existing !== undefined) {\n return existing;\n }\n const entry: TerminalEntry = {\n sinks: new Set(),\n output: readTerminalLog(this.config.dataDir, terminalId),\n dirty: false,\n generation: 0,\n };\n this.entries.set(terminalId, entry);\n return entry;\n }\n\n private spawn(terminalId: string, entry: TerminalEntry, options: TerminalOpenOptions): void {\n // The controller passes the session's directory, but the payload can name\n // its own: the policy is what decides, exactly like opening a session.\n if (!this.policy.canOpen(options.cwd)) {\n throw new UnsupportedByHostError(`Morse is not allowed to run a terminal in ${options.cwd}.`);\n }\n const shell = defaultShell();\n const generation = ++entry.generation;\n const pty = spawnPty(shell, [], {\n name: 'xterm-256color',\n cols: options.cols,\n rows: options.rows,\n cwd: options.cwd,\n env: { ...process.env, TERM: 'xterm-256color', MORSE_TERMINAL: '1' },\n });\n entry.pty = pty;\n this.logger.info(`Terminal opened in ${options.cwd} (${shell}, pid ${pty.pid})`);\n\n pty.onData((chunk) => {\n if (entry.generation !== generation) {\n return;\n }\n // `+=` builds a rope cheaply; trimming every chunk would flatten (copy) the\n // whole buffer per chunk, which a command like `yes` would feel. Let it\n // overshoot, then cut back to the cap in one pass.\n entry.output += chunk;\n if (entry.output.length > TERMINAL_BUFFER_CHARS * 1.5) {\n entry.output = trimTail(entry.output, TERMINAL_BUFFER_CHARS);\n }\n entry.dirty = true;\n this.scheduleFlush(terminalId, entry);\n for (const sink of entry.sinks) {\n sink.output(chunk);\n }\n });\n pty.onExit(({ exitCode }) => {\n if (entry.generation !== generation) {\n return;\n }\n entry.pty = undefined;\n this.persist(terminalId, entry);\n for (const sink of [...entry.sinks]) {\n sink.exit(exitCode);\n }\n });\n }\n\n private resize(entry: TerminalEntry, cols: number, rows: number): void {\n try {\n entry.pty?.resize(cols, rows);\n } catch {\n // A shell that exited between the attach and the resize: nothing to size.\n }\n }\n\n private detach(terminalId: string, entry: TerminalEntry, sink: TerminalSink): void {\n entry.sinks.delete(sink);\n if (entry.sinks.size > 0) {\n return;\n }\n // Nobody is watching: write the scrollback down and start the idle clock.\n this.persist(terminalId, entry);\n this.scheduleIdle(terminalId, entry);\n }\n\n /**\n * Reclaims a shell nobody has reattached to. The log stays (the output should\n * still be there next time); only the process goes.\n */\n private scheduleIdle(terminalId: string, entry: TerminalEntry): void {\n if (this.config.terminalIdleMs <= 0) {\n return;\n }\n this.cancelIdle(entry);\n entry.idleTimer = setTimeout(() => {\n entry.idleTimer = undefined;\n if (entry.sinks.size > 0) {\n return;\n }\n entry.generation += 1;\n this.killPty(entry.pty);\n entry.pty = undefined;\n this.persist(terminalId, entry);\n this.entries.delete(terminalId);\n }, this.config.terminalIdleMs);\n entry.idleTimer.unref?.();\n }\n\n private cancelIdle(entry: TerminalEntry): void {\n if (entry.idleTimer !== undefined) {\n clearTimeout(entry.idleTimer);\n entry.idleTimer = undefined;\n }\n }\n\n private scheduleFlush(terminalId: string, entry: TerminalEntry): void {\n if (entry.flushTimer !== undefined) {\n return;\n }\n entry.flushTimer = setTimeout(() => {\n entry.flushTimer = undefined;\n this.persist(terminalId, entry);\n }, FLUSH_DEBOUNCE_MS);\n entry.flushTimer.unref?.();\n }\n\n private stopFlush(entry: TerminalEntry): void {\n if (entry.flushTimer !== undefined) {\n clearTimeout(entry.flushTimer);\n entry.flushTimer = undefined;\n }\n }\n\n private persist(terminalId: string, entry: TerminalEntry): void {\n this.stopFlush(entry);\n if (!entry.dirty) {\n return;\n }\n entry.dirty = false;\n writeTerminalLog(this.config.dataDir, terminalId, entry.output);\n }\n}\n", "import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { TERMINAL_BUFFER_CHARS, trimTail } from '@morse/host-runtime';\n\n/** The directory under `<MORSE_HOME>` holding one scrollback file per terminal. */\nconst LOG_DIR = 'terminals';\n/**\n * How much scrollback a terminal keeps across a host restart. Larger than the\n * in-memory replay because the file is the long-term copy: a reader who comes\n * back the next day still finds what they were looking at.\n */\nconst LOG_MAX_CHARS = TERMINAL_BUFFER_CHARS * 4;\n\n/**\n * The scrollback a shell produced, kept on disk under `<MORSE_HOME>/terminals/`.\n *\n * This is what makes a terminal's *output* survive the host itself: the live\n * buffer lives in memory and is lost on a restart, so the file is written as the\n * shell streams (debounced by the caller) and read back the next time a pane\n * with the same id attaches. A missing or unreadable file is not an error \u2014 it\n * just means there is nothing to replay.\n */\nexport function readTerminalLog(dataDir: string, terminalId: string): string {\n try {\n const raw = readFileSync(logPath(dataDir, terminalId), 'utf8');\n return trimTail(raw, LOG_MAX_CHARS);\n } catch {\n return '';\n }\n}\n\n/** Writes the current scrollback, capped and trimmed to the tail. Atomic, so a crash cannot truncate it. */\nexport function writeTerminalLog(dataDir: string, terminalId: string, text: string): void {\n const trimmed = trimTail(text, LOG_MAX_CHARS);\n const target = logPath(dataDir, terminalId);\n const temp = `${target}.${process.pid}.tmp`;\n try {\n mkdirSync(join(dataDir, LOG_DIR), { recursive: true });\n writeFileSync(temp, trimmed);\n renameSync(temp, target);\n } catch {\n // Some filesystems refuse a rename over an existing file (a Windows mount).\n try {\n writeFileSync(target, trimmed);\n } catch {\n rmSync(temp, { force: true });\n }\n }\n}\n\n/** Forgets a terminal's scrollback (the reader closed the pane). */\nexport function removeTerminalLog(dataDir: string, terminalId: string): void {\n rmSync(logPath(dataDir, terminalId), { force: true });\n}\n\n/**\n * A terminal id the client named, turned into a filename. An id is not trusted:\n * a `../` in it must not become a path, so anything outside a conservative set\n * is replaced. The frontend mints `term-N`, which passes through untouched.\n */\nexport function safeTerminalName(terminalId: string): string {\n const safe = terminalId.replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 100);\n return safe.length > 0 ? safe : 'terminal';\n}\n\nfunction logPath(dataDir: string, terminalId: string): string {\n return join(dataDir, LOG_DIR, `${safeTerminalName(terminalId)}.log`);\n}\n", "import { Inject, Injectable, type OnModuleDestroy } from '@nestjs/common';\nimport { SessionRegistry, type MorseLogger } from '@morse/core';\nimport { SessionTranscriptStore } from '@morse/host-runtime';\nimport type { PiRpcAdapter } from '@morse/adapter-pi-rpc';\nimport type { MorseServerConfig } from '../app/config.js';\nimport { MORSE_SESSION_REGISTRY, MORSE_TRANSCRIPT_STORE } from '../app/tokens.js';\n\n/**\n * Composition root glue (R7): builds the one session registry this server owns.\n * Because it is shared, multiple projects and sessions keep running while\n * clients connect, refresh and disconnect.\n */\nexport function createSessionRegistry(\n adapter: PiRpcAdapter,\n config: MorseServerConfig,\n logger: MorseLogger,\n): SessionRegistry {\n return new SessionRegistry({\n factory: adapter.factory,\n catalog: adapter.catalog,\n defaultWorkspace: config.workspace,\n hotLimit: config.hotSessions,\n logger,\n });\n}\n\n/** Shutdown hook: dispose every warm `pi` process and the transcripts. */\n@Injectable()\nexport class MorseRegistryLifecycle implements OnModuleDestroy {\n constructor(\n @Inject(MORSE_SESSION_REGISTRY) private readonly registry: SessionRegistry,\n @Inject(MORSE_TRANSCRIPT_STORE) private readonly transcripts: SessionTranscriptStore,\n ) {}\n\n async onModuleDestroy(): Promise<void> {\n await this.registry.dispose();\n this.transcripts.dispose();\n }\n}\n", "import {\n OnGatewayConnection,\n OnGatewayDisconnect,\n WebSocketGateway,\n} from '@nestjs/websockets';\nimport { Inject } from '@nestjs/common';\nimport type { RawData, WebSocket } from 'ws';\nimport {\n DEFAULT_WS_PATH,\n decode,\n encodeWireMessage,\n parseClientMessage,\n type HostToClientMessage,\n} from '@morse/protocol';\nimport { HostSessionController } from '@morse/host-runtime';\nimport type { MorseLogger } from '@morse/core';\nimport { MORSE_LOGGER } from '../../app/tokens.js';\nimport { MorseSessionFactory } from './session-factory.service.js';\n\n/**\n * Driving adapter: one WebSocket per client, one controller per connection, and\n * a shared session registry \u2014 so sessions in several projects keep running while\n * clients connect, refresh or disconnect. Everything protocol-shaped is\n * delegated to `HostSessionController`, which the VS Code host also uses.\n */\n@WebSocketGateway({ path: DEFAULT_WS_PATH })\nexport class MorseGateway implements OnGatewayConnection, OnGatewayDisconnect {\n private readonly clients = new Map<WebSocket, HostSessionController>();\n\n constructor(\n @Inject(MorseSessionFactory) private readonly sessions: MorseSessionFactory,\n @Inject(MORSE_LOGGER) private readonly logger: MorseLogger,\n ) {}\n\n handleConnection(client: WebSocket): void {\n const emit = (message: HostToClientMessage): void => {\n if (client.readyState === 1) {\n client.send(encodeWireMessage(message));\n }\n };\n\n const controller = this.sessions.createFor(emit);\n this.clients.set(client, controller);\n\n client.on('message', (data: RawData) => {\n void this.onMessage(controller, data);\n });\n client.on('error', (error: Error) => {\n this.logger.warn('WebSocket error', error);\n });\n\n void controller.start().catch((error: unknown) => {\n this.logger.error('Failed to start a Morse session for a new client', error);\n });\n }\n\n handleDisconnect(client: WebSocket): void {\n const controller = this.clients.get(client);\n this.clients.delete(client);\n // Only the connection's controller goes away; its sessions stay warm in the\n // shared registry, so a refresh reattaches to the same `pi` processes.\n void controller?.dispose().catch((error: unknown) => {\n this.logger.warn('Failed to dispose a Morse session controller', error);\n });\n }\n\n private async onMessage(controller: HostSessionController, data: RawData): Promise<void> {\n const message = parseClientMessage(decode(data));\n if (!message) {\n this.logger.warn('Ignoring an unrecognised client message');\n return;\n }\n await controller.handleClientMessage(message);\n }\n}", "import { Inject, Injectable } from '@nestjs/common';\nimport { ChatService, SessionRegistry, UnsupportedByHostError, type MorseLogger } from '@morse/core';\nimport type { PiRpcAdapter } from '@morse/adapter-pi-rpc';\nimport { parseMcpServerInput } from '@morse/adapter-pi-rpc';\nimport {\n HostSessionController,\n type HostCommandContext,\n type HostSessionServices,\n type SessionTranscriptStore,\n} from '@morse/host-runtime';\nimport type { HostCapabilities, HostToClientMessage } from '@morse/protocol';\nimport type { MorseServerConfig } from '../../app/config.js';\nimport {\n MORSE_CONFIG,\n MORSE_LOGGER,\n MORSE_PI_ADAPTER,\n MORSE_PROJECT_POLICY,\n MORSE_SESSION_REGISTRY,\n MORSE_TRANSCRIPT_STORE,\n} from '../../app/tokens.js';\nimport { saveUpload } from '../uploads/upload-store.js';\nimport { browseDirectory } from '../workspace/directory-browser.js';\nimport { readWorkspaceFile } from '../workspace/file-store.js';\nimport {\n checkoutGit,\n commitGit,\n pullGit,\n pushGit,\n readCommitDiff,\n readCommitFiles,\n readGitBranches,\n readGitDiff,\n readGitLog,\n readGitStatus,\n readGitSync,\n stageGitPaths,\n unstageGitPaths,\n} from '../workspace/git-log.js';\nimport { workspaceFiles } from '../workspace/workspace-index.js';\nimport { readWorkbench, saveWorkbench, readDrafts, saveDrafts } from '../workspace/workbench-store.js';\nimport { ServerProjectPolicy } from '../projects/project-policy.js';\nimport { ServerTerminalBackend } from '../terminal/terminal.service.js';\n\n/**\n * Composition root for one client connection (clean-architecture R7): the only\n * place that knows the concrete pi adapter, the core services and the WebSocket\n * delivery. Wiring only \u2014 no business logic lives here.\n *\n * The session registry is shared by every connection, so sessions and projects\n * keep running while clients come and go.\n */\n@Injectable()\nexport class MorseSessionFactory {\n constructor(\n @Inject(MORSE_SESSION_REGISTRY) private readonly registry: SessionRegistry,\n @Inject(MORSE_TRANSCRIPT_STORE) private readonly transcripts: SessionTranscriptStore,\n @Inject(MORSE_PROJECT_POLICY) private readonly policy: ServerProjectPolicy,\n @Inject(MORSE_LOGGER) private readonly logger: MorseLogger,\n @Inject(MORSE_CONFIG) private readonly config: MorseServerConfig,\n @Inject(ServerTerminalBackend) private readonly terminal: ServerTerminalBackend,\n @Inject(MORSE_PI_ADAPTER) private readonly pi: PiRpcAdapter,\n ) {}\n\n capabilities(): HostCapabilities {\n return {\n hostKind: 'server',\n // The browser host is global: every project pi knows about.\n scope: 'global',\n // A browser has no local editor to read, so there is nothing to pin.\n editorContext: false,\n // No QuickPick/InputBox here, so the frontend renders interactions.\n nativeDialogs: false,\n insertIntoEditor: false,\n revealFile: false,\n // The server can walk the active session's directory, so the composer's\n // `@` picker works here too \u2014 the same list VS Code offers for a workspace.\n filePicker: true,\n // A browser has no editor of its own, so this host reads files for the\n // frontend's Explorer and preview tabs. VS Code keeps its native ones.\n filePreview: true,\n // The same reasoning for history: VS Code has Source Control, so the\n // browser host is the one that answers `gitLog` for its own git panel.\n gitPanel: true,\n // VS Code already has an integrated terminal; the browser host has none, so\n // the bottom panel's terminal is this host's. It runs in the viewing\n // session's directory and is gated by `ProjectPolicy`, like a session.\n terminal: true,\n // The browser host restores the tabs, panel, terminals and half-written\n // prompts the reader left open, kept under `<MORSE_HOME>`. VS Code has its\n // own tab restoration and leaves this off.\n workbench: true,\n // A browser cannot hand a dragged file's path to pi, so the host takes the\n // bytes and writes them next to the session; the frontend then `@mentions`\n // the path it gets back.\n fileUpload: true,\n // The browser host has no workspace folder, so \"New session\" has to ask\n // which directory the agent should run in. The frontend opens its folder\n // modal; VS Code has a folder already and leaves this off.\n directoryPicker: true,\n // pi's fork re-parents the conversation, so a past prompt can be edited.\n editMessage: true,\n // The same fork can branch a new session and hand the prompt back instead.\n forkMessage: true,\n // A page served by this host may ask the registry for the latest release, so\n // the sidebar can say when a newer Morse is out. A host without it stays\n // quiet; this one is on unless `MORSE_UPDATE_CHECK=0` says otherwise.\n updateCheck: this.config.updateCheck,\n // Managing MCP servers runs the `pi` CLI, so this host offers it only when\n // it found one. A host without it hides the affordance.\n mcp: this.pi.describeCli() !== undefined,\n };\n }\n\n createFor(emit: (message: HostToClientMessage) => void): HostSessionController {\n const services: HostSessionServices = {\n registry: this.registry,\n chat: new ChatService({ agent: this.registry, logger: this.logger }),\n };\n\n return new HostSessionController({\n services,\n capabilities: this.capabilities(),\n emit,\n logger: this.logger,\n transcripts: this.transcripts,\n policy: this.policy,\n scope: { kind: 'global' },\n // The bundle this host serves, so the panel can name its own version.\n ...(this.config.frontend ? { frontend: this.config.frontend } : {}),\n // The registry outlives this connection on purpose.\n ownsRegistry: false,\n // A page load must not create a session; the first prompt opens one.\n autoOpen: false,\n onHostCommand: (command, args, context) => this.runHostCommand(command, args, context),\n // The bottom panel's shell: this host can spawn one on its own machine.\n terminal: this.terminal,\n agentHint:\n 'Install the pi CLI and make sure it is on PATH, or start the server with MORSE_PI_PATH set.',\n });\n }\n\n /**\n * The host commands this host answers: browsing the filesystem for a project\n * folder, listing the active session's files for the composer's `@mention`\n * picker, and writing a browser upload down next to the session.\n *\n * `listDirectories` is read-only and may look anywhere the server can read;\n * the commands that touch a session are scoped to its cwd (which\n * `ProjectPolicy` already approved when the session was opened), never to a\n * path the client names.\n */\n private async runHostCommand(\n command: string,\n args: Record<string, unknown> | undefined,\n context?: HostCommandContext,\n ): Promise<unknown> {\n switch (command) {\n case 'listDirectories': {\n // Feeds the frontend's \"New session\" folder modal. Browsing is harmless,\n // but the chosen folder still has to pass the policy before a session\n // may run there, so the result says so (`canOpen`).\n const requested = typeof args?.path === 'string' ? args.path : undefined;\n const listing = await browseDirectory(requested, {\n roots: this.policy.allowList,\n defaultPath: this.registry.defaultWorkspace.cwd,\n });\n return { ...listing, canOpen: this.policy.canOpen(listing.path) };\n }\n case 'readWorkbench': {\n // The shell layout the reader left behind. Read-only and host-scoped (a\n // file under `MORSE_HOME`), so it never consults the project policy.\n return readWorkbench(this.config.dataDir) ?? null;\n }\n case 'saveWorkbench': {\n // The frontend owns the inner shape; the store guards version and size.\n return { ok: saveWorkbench(this.config.dataDir, args) };\n }\n case 'readDrafts': {\n // The half-written prompts, keyed by tab. Same host-scoped file as the\n // layout, but its own file because a draft can carry inline images.\n return readDrafts(this.config.dataDir) ?? null;\n }\n case 'saveDrafts': {\n return { ok: saveDrafts(this.config.dataDir, args) };\n }\n case 'listFiles': {\n const cwd = this.requireWritableCwd(context);\n // There is no workspace folder here, so the list is the directory the\n // client is viewing (its session, or the draft it is about to open) \u2014\n // exactly what pi resolves a mention against. `fresh` skips the index\n // cache: the Explorer polls, so a file added on disk has to appear.\n const files = await workspaceFiles(cwd, this.logger, { fresh: args?.fresh === true });\n return { files };\n }\n case 'readFile': {\n // The Explorer and the preview tabs. `readWorkspaceFile` resolves the\n // path inside the session's directory and refuses anything that escapes.\n const cwd = this.commandCwd(context, args);\n const path = typeof args?.path === 'string' ? args.path : '';\n if (path.length === 0) {\n throw new UnsupportedByHostError('readFile needs a \"path\" argument.');\n }\n return readWorkspaceFile(cwd, path);\n }\n case 'uploadFile': {\n const cwd = this.requireWritableCwd(context);\n const saved = await saveUpload(cwd, args ?? {}, this.config.uploadDir);\n this.logger.info(`Upload stored: ${saved.path} (${saved.bytes} bytes)`);\n return saved;\n }\n case 'gitLog': {\n // The git panel's history and graph. Like `readFile`, the repository is\n // the viewing session's directory, never a path the client names. `skip`\n // pages towards the root commit as the panel scrolls.\n const cwd = this.requireWritableCwd(context);\n const max = typeof args?.max === 'number' ? args.max : undefined;\n const skip = typeof args?.skip === 'number' ? args.skip : undefined;\n return readGitLog(cwd, max, skip);\n }\n case 'gitStatus': {\n // The working tree's changes, for the Explorer's per-file badges. Polled\n // alongside `listFiles`, so the two always describe the same moment.\n const cwd = this.requireWritableCwd(context);\n return readGitStatus(cwd);\n }\n case 'gitDiff': {\n // One file's unified diff for the preview's diff modes. The path is\n // resolved inside the session's directory, exactly like `readFile`.\n const cwd = this.commandCwd(context, args);\n const path = typeof args?.path === 'string' ? args.path : '';\n if (path.length === 0) {\n throw new UnsupportedByHostError('gitDiff needs a \"path\" argument.');\n }\n return readGitDiff(cwd, path);\n }\n case 'gitStage': {\n // Staging writes the index of the viewing session's repository. Paths are\n // resolved inside it, exactly like `readFile`, and the answer is the\n // fresh working tree so the panel updates in one round trip.\n const cwd = this.requireWritableCwd(context);\n return stageGitPaths(cwd, gitPaths(args));\n }\n case 'gitUnstage': {\n const cwd = this.requireWritableCwd(context);\n return unstageGitPaths(cwd, gitPaths(args));\n }\n case 'gitCommitFiles': {\n // The graph's expandable row: the paths one commit touched. The hash is\n // client-named, so it is validated as a hex object id, never a ref.\n const cwd = this.requireWritableCwd(context);\n const hash = typeof args?.hash === 'string' ? args.hash : '';\n return readCommitFiles(cwd, hash);\n }\n case 'gitCommitDiff': {\n const cwd = this.commandCwd(context, args);\n const hash = typeof args?.hash === 'string' ? args.hash : '';\n const path = typeof args?.path === 'string' ? args.path : '';\n if (path.length === 0) {\n throw new UnsupportedByHostError('gitCommitDiff needs a \"path\" argument.');\n }\n return readCommitDiff(cwd, hash, path);\n }\n case 'gitSync': {\n // How far HEAD is from its upstream, for the panel's pull/push controls.\n const cwd = this.requireWritableCwd(context);\n return readGitSync(cwd);\n }\n case 'gitPull': {\n // Network commands: git runs with prompts disabled, so a missing\n // credential fails instead of blocking the host on stdin.\n const cwd = this.requireWritableCwd(context);\n return pullGit(cwd);\n }\n case 'gitPush': {\n const cwd = this.requireWritableCwd(context);\n return pushGit(cwd);\n }\n case 'gitCommit': {\n // Commits the staged changes. The message is client-named but passed as\n // a single argv element, never through a shell.\n const cwd = this.requireWritableCwd(context);\n const message = typeof args?.message === 'string' ? args.message : '';\n return commitGit(cwd, message);\n }\n case 'gitBranches': {\n const cwd = this.requireWritableCwd(context);\n return readGitBranches(cwd);\n }\n case 'gitCheckout': {\n const cwd = this.requireWritableCwd(context);\n const branch = typeof args?.branch === 'string' ? args.branch : '';\n return checkoutGit(cwd, branch, args?.create === true);\n }\n case 'mcpStatus': {\n // The viewing session's MCP servers. `pi mcp list --json` connects every\n // enabled server, so a failure is reported as a value (the panel renders\n // it) rather than thrown \u2014 the panel has something to say either way.\n const cwd = this.requireWritableCwd(context);\n try {\n return await this.pi.mcp.status(cwd);\n } catch (error: unknown) {\n return { servers: [], errors: [describeError(error)] };\n }\n }\n case 'mcpAdd': {\n const cwd = this.requireWritableCwd(context);\n return this.pi.mcp.add(parseMcpServerInput(args), cwd);\n }\n case 'mcpRemove': {\n const cwd = this.requireWritableCwd(context);\n return this.pi.mcp.remove(stringArg(args, 'name'), cwd, mcpScope(args));\n }\n case 'mcpSetEnabled': {\n const cwd = this.requireWritableCwd(context);\n return this.pi.mcp.setEnabled(\n stringArg(args, 'name'),\n args?.enabled === true,\n cwd,\n mcpScope(args),\n );\n }\n default:\n throw new UnsupportedByHostError(`This host does not support \"${command}\".`);\n }\n }\n\n /**\n * The directory the command should touch, after `ProjectPolicy` approved it.\n * The controller hands in what the client is viewing; the registry's active\n * session is only a fallback for a host command issued without one.\n */\n private requireWritableCwd(context?: HostCommandContext): string {\n const cwd = context?.cwd ?? this.activeCwd();\n if (!cwd) {\n throw new UnsupportedByHostError('No active Morse session yet.');\n }\n if (!this.policy.canOpen(cwd)) {\n throw new UnsupportedByHostError(`Morse is not allowed to work in ${cwd}.`);\n }\n return cwd;\n }\n\n /**\n * The directory a file command works in. A restored preview tab carries the\n * project it was read from (`args.cwd`), because the host may still be on\n * another session when that tab is loaded; the policy checks it exactly like a\n * terminal's directory. Without one, the viewing session's directory is used.\n */\n private commandCwd(context?: HostCommandContext, args?: Record<string, unknown>): string {\n const requested = args?.cwd;\n if (typeof requested === 'string' && requested.length > 0) {\n if (!this.policy.canOpen(requested)) {\n throw new UnsupportedByHostError(`Morse is not allowed to work in ${requested}.`);\n }\n return requested;\n }\n return this.requireWritableCwd(context);\n }\n\n /** The cwd of the session this connection is showing. */\n private activeCwd(): string | undefined {\n const key = this.registry.activeKeyOf();\n const workspace = key === undefined ? undefined : this.registry.stateOf(key)?.workspace;\n return workspace?.cwd ?? this.registry.defaultWorkspace.cwd;\n }\n}\n\n/** The `paths` argument of a `gitStage`/`gitUnstage` command, with bad entries dropped. */\nfunction gitPaths(args: Record<string, unknown> | undefined): string[] {\n const paths = args?.paths;\n if (!Array.isArray(paths)) {\n return [];\n }\n return paths.filter((path): path is string => typeof path === 'string' && path.length > 0);\n}\n\nfunction stringArg(args: Record<string, unknown> | undefined, key: string): string {\n const value = args?.[key];\n return typeof value === 'string' ? value : '';\n}\n\n/** The scope a mutation edits, when the panel named one. */\nfunction mcpScope(args: Record<string, unknown> | undefined): 'global' | 'project' | undefined {\n return args?.scope === 'global' || args?.scope === 'project' ? args.scope : undefined;\n}\n\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}", "import { existsSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, isAbsolute, join, relative, sep } from 'node:path';\n\n/**\n * A browser cannot hand the agent a dragged file's path, so it sends the bytes\n * instead and the host \u2014 which owns a disk next to the session \u2014 writes them\n * down and answers with a path the frontend can `@mention`.\n *\n * Files land under the session's own directory by default, so pi's `read` (and\n * `grep`/`find`) resolve the mention exactly like any other workspace file.\n */\n\n/** Refuse anything larger: base64 of a huge file would also bloat the socket. */\nexport const MAX_UPLOAD_BYTES = 10 * 1024 * 1024;\n\n/** Default inbox, relative to the session cwd; `MORSE_UPLOAD_DIR` overrides it. */\nexport const DEFAULT_UPLOAD_DIR = '.morse/uploads';\n\nexport interface SavedUpload {\n /** Path for the `@mention`: cwd-relative when it lives under the cwd. */\n path: string;\n /** The name the file was actually stored under (deduplicated). */\n name: string;\n bytes: number;\n}\n\n/** A rejected upload; the message is shown to the user, so it says what to fix. */\nexport class UploadError extends Error {}\n\nexport async function saveUpload(\n cwd: string,\n request: Record<string, unknown>,\n uploadDir: string = DEFAULT_UPLOAD_DIR,\n): Promise<SavedUpload> {\n const name = safeFileName(request.name);\n const data = request.data;\n if (typeof data !== 'string' || data.length === 0) {\n throw new UploadError('That upload carried no data.');\n }\n const bytes = Buffer.from(data, 'base64');\n if (bytes.length === 0) {\n throw new UploadError('That upload carried no data.');\n }\n if (bytes.length > MAX_UPLOAD_BYTES) {\n throw new UploadError(\n `That file is ${formatMb(bytes.length)} MB; the limit is ${formatMb(MAX_UPLOAD_BYTES)} MB.`,\n );\n }\n\n const directory = isAbsolute(uploadDir) ? uploadDir : join(cwd, uploadDir);\n await mkdir(directory, { recursive: true });\n const target = uniquePath(directory, name);\n await writeFile(target, bytes);\n\n return { path: mentionPath(cwd, target), name: basename(target), bytes: bytes.length };\n}\n\n/**\n * Keeps only the last path segment and strips what a filesystem rejects, so a\n * hostile client cannot escape the inbox with `../../etc/passwd`.\n */\nexport function safeFileName(raw: unknown): string {\n const value = typeof raw === 'string' ? raw : '';\n const base = basename(value.replace(/\\\\/g, '/')).trim();\n const cleaned = base\n .replace(/[\\u0000-\\u001f<>:\"|?*]/g, '_')\n .replace(/^\\.+/, '')\n .trim();\n if (cleaned.length === 0) {\n throw new UploadError('That upload had no usable file name.');\n }\n return cleaned.slice(0, 120);\n}\n\n/** `report.pdf` -> `report-1.pdf` when the first name is taken. */\nfunction uniquePath(directory: string, name: string): string {\n const dot = name.lastIndexOf('.');\n const stem = dot > 0 ? name.slice(0, dot) : name;\n const extension = dot > 0 ? name.slice(dot) : '';\n let candidate = join(directory, name);\n let suffix = 1;\n while (existsSync(candidate)) {\n candidate = join(directory, `${stem}-${suffix}${extension}`);\n suffix += 1;\n }\n return candidate;\n}\n\nfunction mentionPath(cwd: string, target: string): string {\n const rel = relative(cwd, target);\n const posix = rel.split(sep).join('/');\n // An absolute `MORSE_UPLOAD_DIR` may point outside the cwd; then the mention\n // has to be absolute, because there is no relative form that resolves.\n return posix.length === 0 || posix.startsWith('..') || isAbsolute(rel) ? target : posix;\n}\n\nfunction formatMb(bytes: number): string {\n return (bytes / (1024 * 1024)).toFixed(1).replace(/\\.0$/, '');\n}\n", "import { readdir, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path';\n\n/**\n * Heavy or generated directories a project picker never wants to step into.\n * Mirrors the file picker's skip list so both hosts hide the same noise.\n */\nconst SKIPPED_DIRECTORIES = new Set([\n 'node_modules',\n 'dist',\n 'out',\n 'build',\n 'coverage',\n 'target',\n 'vendor',\n 'venv',\n '__pycache__',\n '.angular',\n '.next',\n '.nuxt',\n '.turbo',\n '.cache',\n]);\n\n/** One subdirectory row of the browser. */\nexport interface DirectoryEntry {\n name: string;\n path: string;\n}\n\n/** What the frontend's folder modal needs to render one directory. */\nexport interface DirectoryListing {\n /** Absolute path of the directory being shown. */\n path: string;\n /** Absolute parent, absent when `path` is the filesystem root. */\n parent?: string;\n directories: DirectoryEntry[];\n /** True when `path` itself is a git worktree \u2014 a strong \"this is a project\" hint. */\n isGitRepo: boolean;\n /** Shortcuts the modal offers: the allow-list, the home dir, the default workspace. */\n roots: DirectoryEntry[];\n}\n\nexport interface BrowseOptions {\n /** Directories the operator allowed the agent to work in (`MORSE_PROJECTS`). */\n roots?: readonly string[];\n /** Where to start when the client does not name a path. */\n defaultPath: string;\n}\n\n/**\n * Lists the subdirectories of one folder on the host's filesystem.\n *\n * The browser host serves a machine, not a webview with a workspace folder, so\n * the user has to be able to point the agent at any directory. Browsing is\n * read-only and never creates anything; whether a session may actually run in\n * the chosen folder stays `ProjectPolicy`'s call (the host decorates the result\n * with `canOpen`).\n */\nexport async function browseDirectory(\n path: string | undefined,\n options: BrowseOptions,\n): Promise<DirectoryListing> {\n const requested = path?.trim();\n const start = resolve(requested && requested.length > 0 ? requested : options.defaultPath);\n\n // `resolve` already made it absolute; this guard is for the caller's sake when\n // a relative path slips through (it would otherwise be resolved against the\n // server's cwd, which is rarely what the user typed).\n if (requested && !isAbsolute(requested)) {\n throw new Error(`A folder path must be absolute: ${requested}`);\n }\n\n const entries = await readdir(start, { withFileTypes: true });\n const directories = entries\n .filter((entry) => entry.isDirectory())\n .filter((entry) => !shouldSkip(entry.name))\n .map((entry) => ({ name: entry.name, path: join(start, entry.name) }))\n .sort((left, right) => left.name.localeCompare(right.name));\n\n const parent = dirname(start);\n return {\n path: start,\n parent: parent === start ? undefined : parent,\n directories,\n isGitRepo: await isGitRepo(start),\n roots: shortcutRoots(options),\n };\n}\n\n/** Hidden folders are noise when the point is to pick a project. */\nfunction shouldSkip(name: string): boolean {\n return name.startsWith('.') || SKIPPED_DIRECTORIES.has(name);\n}\n\nasync function isGitRepo(path: string): Promise<boolean> {\n try {\n await stat(join(path, '.git'));\n return true;\n } catch {\n return false;\n }\n}\n\nfunction shortcutRoots(options: BrowseOptions): DirectoryEntry[] {\n const seen = new Set<string>();\n const roots: DirectoryEntry[] = [];\n for (const candidate of [...(options.roots ?? []), homedir(), options.defaultPath]) {\n if (!candidate || !isAbsolute(candidate)) {\n continue;\n }\n const path = resolve(candidate);\n if (seen.has(path)) {\n continue;\n }\n seen.add(path);\n roots.push({ name: basename(path) || path, path });\n }\n return roots;\n}\n", "import { open, stat } from 'node:fs/promises';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport { UnsupportedByHostError } from '@morse/core';\n\n/**\n * How much of a file a preview reads. A 40 MB bundle in a text view is not a\n * preview; the response says it was cut short so the frontend can tell the user.\n */\nconst MAX_PREVIEW_BYTES = 512 * 1024;\n\n/** How much of a file is sniffed for NUL bytes: a file is binary or it is not near the top. */\nconst SNIFF_BYTES = 8 * 1024;\n\n/** What the frontend needs to draw a file: its text, size and whether it was cut. */\nexport interface WorkspaceFilePreview {\n path: string;\n content: string;\n size: number;\n truncated: boolean;\n /** A binary file has no useful text; `content` is empty and the UI says so. */\n binary: boolean;\n}\n\n/**\n * Reads one file for the frontend's preview tab. The path is resolved against\n * the project the client is viewing and must stay inside it: the browser host\n * never opens a path the client names outside the session's directory (which\n * `ProjectPolicy` already approved when the session was opened).\n */\nexport async function readWorkspaceFile(\n cwd: string,\n requested: string,\n): Promise<WorkspaceFilePreview> {\n const target = resolveWithin(cwd, requested);\n const info = await stat(target).catch(() => undefined);\n if (info === undefined) {\n throw new UnsupportedByHostError(`No such file: ${requested}`);\n }\n if (info.isDirectory()) {\n throw new UnsupportedByHostError(`${requested} is a directory, not a file.`);\n }\n const handle = await open(target, 'r');\n try {\n const length = Math.min(info.size, MAX_PREVIEW_BYTES);\n const buffer = Buffer.alloc(length);\n const { bytesRead } = await handle.read(buffer, 0, length, 0);\n const slice = buffer.subarray(0, bytesRead);\n const binary = slice.subarray(0, Math.min(bytesRead, SNIFF_BYTES)).includes(0);\n return {\n path: toPosix(relative(cwd, target)),\n content: binary ? '' : slice.toString('utf8'),\n size: info.size,\n truncated: info.size > bytesRead,\n binary,\n };\n } finally {\n await handle.close();\n }\n}\n\n/**\n * Resolves a client path under `cwd` and refuses anything that escapes it. An\n * absolute path is a client naming a file the session does not own, so it is\n * rejected rather than silently reinterpreted.\n */\nexport function resolveWithin(cwd: string, requested: string): string {\n if (isAbsolute(requested)) {\n throw new UnsupportedByHostError('A preview only opens files inside the project.');\n }\n const target = resolve(cwd, requested);\n const rel = relative(cwd, target);\n if (rel.length === 0 || rel.startsWith('..') || isAbsolute(rel)) {\n throw new UnsupportedByHostError('A preview only opens files inside the project.');\n }\n return target;\n}\n\n/** Preview paths read with `/` on every platform, the way pi prints them. */\nfunction toPosix(path: string): string {\n return path.split('\\\\').join('/');\n}\n", "import { execFile } from 'node:child_process';\nimport { relative } from 'node:path';\nimport type {\n GitBranches,\n GitCommit,\n GitCommitFiles,\n GitDiff,\n GitFileStatus,\n GitLog,\n GitMutation,\n GitStatus,\n GitSync,\n} from '@morse/protocol';\nimport { resolveWithin } from './file-store.js';\n\n/**\n * How many commits the panel reads. A history view is for orientation, not for\n * replaying a repository: past a few hundred rows the graph is unreadable and\n * the log gets slow, so the list is capped and says so.\n */\nconst DEFAULT_MAX_COMMITS = 250;\nconst HARD_MAX_COMMITS = 500;\n\n/** Field separator (US) and record separator (RS): safe inside any commit text. */\nconst FIELD = '\\u001f';\nconst RECORD = '\\u001e';\nconst PRETTY_FORMAT = ['%H', '%h', '%P', '%D', '%an', '%aI', '%s'].join(FIELD) + RECORD;\n\n/**\n * The active project's git history for the browser host's git panel. The project\n * is the viewing session's directory (already approved by `ProjectPolicy`), never\n * a path the client names. A directory that is not a repository is a normal\n * answer (`isRepo: false`), not an error \u2014 the panel says \"no repository here\".\n *\n * `--all --date-order` walks every ref so the graph shows side branches, and the\n * lanes are derived by the frontend from each commit's `parents`.\n */\nexport async function readGitLog(\n cwd: string,\n requestedMax?: number,\n requestedSkip?: number,\n): Promise<GitLog> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, commits: [] };\n }\n const max = clampMax(requestedMax);\n const skip = clampSkip(requestedSkip);\n const [root, branch, raw] = await Promise.all([\n gitText(['rev-parse', '--show-toplevel'], cwd),\n gitText(['rev-parse', '--abbrev-ref', 'HEAD'], cwd),\n gitText(\n [\n 'log',\n '--all',\n '--date-order',\n `--skip=${skip}`,\n `--max-count=${max}`,\n `--pretty=format:${PRETTY_FORMAT}`,\n ],\n cwd,\n ),\n ]);\n return {\n isRepo: true,\n root: root?.trim() || cwd,\n branch: branch?.trim() || undefined,\n commits: parseCommits(raw ?? ''),\n };\n}\n\nfunction clampMax(requested?: number): number {\n if (requested === undefined || !Number.isFinite(requested)) {\n return DEFAULT_MAX_COMMITS;\n }\n return Math.max(1, Math.min(HARD_MAX_COMMITS, Math.floor(requested)));\n}\n\n/**\n * The working tree's changed paths, for the Explorer's per-file git badges.\n * Paths are made relative to `cwd` (matching `listFiles`), so a session opened\n * in a subdirectory of the repository lines up with the listing it shows.\n */\nexport async function readGitStatus(cwd: string): Promise<GitStatus> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, files: [] };\n }\n const [prefix, raw] = await Promise.all([\n gitText(['rev-parse', '--show-prefix'], cwd),\n gitText(['status', '--porcelain=v1', '-z', '--untracked-files=all'], cwd),\n ]);\n return { isRepo: true, files: parseStatus(raw ?? '', prefix?.trim() ?? '') };\n}\n\n/**\n * Porcelain v1 with `-z`: one `XY path` record per entry, NUL-separated, and a\n * rename/copy adds the origin path as a following record.\n */\nfunction parseStatus(raw: string, root: string): GitFileStatus[] {\n const records = raw.split('\\0');\n const files: GitFileStatus[] = [];\n for (let index = 0; index < records.length; index += 1) {\n const record = records[index];\n if (!record || record.length < 4) {\n continue;\n }\n const status = record.slice(0, 2);\n // A rename/copy keeps its new path here and its origin in the next record.\n if (status[0] === 'R' || status[0] === 'C') {\n index += 1;\n }\n const full = record.slice(3);\n if (root.length > 0 && !full.startsWith(root)) {\n continue;\n }\n const path = full.slice(root.length);\n if (path.length > 0) {\n files.push({ path: toPosix(path), status });\n }\n }\n return files;\n}\n\n/**\n * One file's unified diff against HEAD, for the preview's diff modes. The path\n * is resolved inside the viewing session's directory, the same guard `readFile`\n * uses, so a client cannot name a file outside the project. An untracked file\n * has no diff against HEAD; the frontend renders its content as all-added.\n */\nexport async function readGitDiff(cwd: string, requested: string): Promise<GitDiff> {\n const rel = toPosix(relative(cwd, resolveWithin(cwd, requested)));\n const diff = await gitText(\n ['diff', '--no-color', '--no-ext-diff', '-U3', 'HEAD', '--', rel],\n cwd,\n );\n return { path: rel, diff: diff ?? '' };\n}\n\n/**\n * The paths a commit touched, for the git panel's expandable row. Paths are made\n * relative to `cwd` (`--relative`), matching `listFiles` and `readGitStatus`, so\n * a session opened in a subdirectory sees only what it can open. A commit that\n * touched nothing there is an empty list, not an error.\n */\nexport async function readCommitFiles(cwd: string, requested: string): Promise<GitCommitFiles> {\n const hash = requested.trim();\n if (!isCommitHash(hash)) {\n return { isRepo: false, hash, files: [] };\n }\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, hash, files: [] };\n }\n const raw = await gitText(\n ['show', '--name-status', '--format=', '-z', '--find-renames', '--relative', hash],\n cwd,\n );\n return { isRepo: true, hash, files: parseNameStatus(raw ?? '') };\n}\n\n/**\n * One file's diff *inside a commit* (`git show <hash> -- <path>`), for a file\n * opened from the git panel's commit row. The path is resolved inside `cwd`,\n * exactly like `readGitDiff`. A root commit shows the whole file as added.\n */\nexport async function readCommitDiff(cwd: string, hash: string, requested: string): Promise<GitDiff> {\n const rel = toPosix(relative(cwd, resolveWithin(cwd, requested)));\n if (!isCommitHash(hash.trim())) {\n return { path: rel, diff: '' };\n }\n const diff = await gitText(\n ['show', '--format=', '--no-color', '--no-ext-diff', '-U3', '--relative', hash.trim(), '--', rel],\n cwd,\n );\n return { path: rel, diff: diff ?? '' };\n}\n\n/** A revision the client may name: a hex object id, never an option or a ref. */\nfunction isCommitHash(hash: string): boolean {\n return /^[0-9a-fA-F]{4,40}$/.test(hash);\n}\n\n/**\n * `git show --name-status -z` pairs a status with its path, NUL-separated, and a\n * rename/copy carries both the old and the new path. Only the new path is kept,\n * with the status reduced to its letter so the shared badge mapping applies.\n */\nfunction parseNameStatus(raw: string): GitFileStatus[] {\n const tokens = raw.split('\\0');\n const files: GitFileStatus[] = [];\n for (let index = 0; index < tokens.length; index += 1) {\n const status = tokens[index];\n if (!status) {\n continue;\n }\n const letter = status[0] ?? '';\n const rename = letter === 'R' || letter === 'C';\n const path = tokens[index + (rename ? 2 : 1)];\n index += rename ? 2 : 1;\n if (path) {\n files.push({ path: toPosix(path), status: `${letter} ` });\n }\n }\n return files;\n}\n\n/**\n * Stages the named paths in the viewing session's repository (`git add`). Paths\n * are resolved inside `cwd` exactly like `readFile`, so a client cannot stage\n * anything outside its project. The fresh working tree comes back as the answer,\n * so the panel updates without a second `gitStatus` round trip.\n */\nexport async function stageGitPaths(cwd: string, requested: readonly string[]): Promise<GitStatus> {\n const paths = resolvePaths(cwd, requested);\n if (paths.length > 0) {\n await gitRun(['add', '--', ...paths], cwd);\n }\n return readGitStatus(cwd);\n}\n\n/**\n * Unstages the named paths (`git reset HEAD`), keeping the working-tree change:\n * a staged edit becomes an unstaged one, and an added file becomes untracked\n * again. An unborn `HEAD` (a repository with no commits) cannot be reset, so the\n * entry is removed from the index directly instead.\n */\nexport async function unstageGitPaths(cwd: string, requested: readonly string[]): Promise<GitStatus> {\n const paths = resolvePaths(cwd, requested);\n if (paths.length > 0) {\n const reset = await gitRun(['reset', '--quiet', 'HEAD', '--', ...paths], cwd);\n if (!reset) {\n await gitRun(['rm', '--cached', '--quiet', '--', ...paths], cwd);\n }\n }\n return readGitStatus(cwd);\n}\n\n/**\n * How far HEAD is from its upstream: `behind` commits to pull, `ahead` commits\n * to push. A branch with no upstream (or an unborn branch) reports zero for both,\n * so the panel still shows the pull/push controls without a distance.\n */\nexport async function readGitSync(cwd: string): Promise<GitSync> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, ahead: 0, behind: 0 };\n }\n const [branch, upstream] = await Promise.all([\n gitText(['rev-parse', '--abbrev-ref', 'HEAD'], cwd),\n gitText(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'], cwd),\n ]);\n const name = branch?.trim() || undefined;\n const tracked = upstream?.trim() || undefined;\n if (tracked === undefined) {\n return { isRepo: true, branch: name, ahead: 0, behind: 0 };\n }\n const counts = await gitText(\n ['rev-list', '--left-right', '--count', `${tracked}...HEAD`],\n cwd,\n );\n const [behind, ahead] = parseCounts(counts ?? '');\n return { isRepo: true, branch: name, upstream: tracked, ahead, behind };\n}\n\n/** Pulls the upstream (`git pull`) and reports the new distance. */\nexport async function pullGit(cwd: string): Promise<GitSync> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() === 'true') {\n await gitRun(['pull', '--no-edit'], cwd, 180_000);\n }\n return readGitSync(cwd);\n}\n\n/**\n * Pushes HEAD (`git push`) and reports the new distance. `--follow-tags` sends\n * the annotated tags that point into the pushed history, so a release tag travels\n * with the commit it names instead of waiting for a separate `git push --tags`.\n */\nexport async function pushGit(cwd: string): Promise<GitSync> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() === 'true') {\n await gitRun(['push', '--follow-tags'], cwd, 180_000);\n }\n return readGitSync(cwd);\n}\n\n/**\n * The branches the panel can switch to: the current one, the locals, the\n * remote-tracking branches (minus `origin/HEAD`, which is not a branch), and the\n * tags (checking one out detaches HEAD).\n */\nexport async function readGitBranches(cwd: string): Promise<GitBranches> {\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { isRepo: false, local: [], remote: [], tags: [] };\n }\n const [current, local, remote, tags] = await Promise.all([\n gitText(['rev-parse', '--abbrev-ref', 'HEAD'], cwd),\n gitText(['for-each-ref', '--format=%(refname:short)', 'refs/heads'], cwd),\n gitText(['for-each-ref', '--format=%(refname:short)', 'refs/remotes'], cwd),\n gitText(['for-each-ref', '--format=%(refname:short)', 'refs/tags'], cwd),\n ]);\n return {\n isRepo: true,\n current: current?.trim() || undefined,\n local: nonEmptyLines(local),\n remote: nonEmptyLines(remote).filter((name) => !name.endsWith('/HEAD')),\n tags: nonEmptyLines(tags),\n };\n}\n\n/** Commits the staged changes with `message`; git's refusal is reported back. */\nexport async function commitGit(cwd: string, message: string): Promise<GitMutation> {\n const text = message.trim();\n if (text.length === 0) {\n return { ok: false, message: 'A commit needs a message.' };\n }\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { ok: false, message: 'This project is not a git repository.' };\n }\n const result = await gitExec(['commit', '-m', text], cwd, 60_000);\n if (result.ok) {\n return { ok: true };\n }\n return { ok: false, message: describeCommitFailure(result.output) };\n}\n\n/**\n * Switches branches, creating the branch first when `create` is set. The name is\n * validated as a refname, so a client cannot smuggle a git option into the\n * command line.\n */\nexport async function checkoutGit(\n cwd: string,\n branch: string,\n create: boolean,\n): Promise<GitMutation> {\n const name = branch.trim();\n if (!isBranchName(name)) {\n return { ok: false, message: 'That is not a usable branch name.' };\n }\n const inside = await gitText(['rev-parse', '--is-inside-work-tree'], cwd);\n if (inside?.trim() !== 'true') {\n return { ok: false, message: 'This project is not a git repository.' };\n }\n const result = await gitExec(\n create ? ['checkout', '-b', name] : ['checkout', name],\n cwd,\n 60_000,\n );\n return { ok: result.ok, message: result.ok ? undefined : gitRefusal(result.output) };\n}\n\n/** A conservative refname guard: no options, whitespace, or revision syntax. */\nfunction isBranchName(name: string): boolean {\n return (\n name.length > 0 &&\n !name.startsWith('-') &&\n !name.includes('..') &&\n !name.includes('@{') &&\n !name.endsWith('/') &&\n !/\\s/.test(name) &&\n !/[~^:?*\\[\\\\]/.test(name)\n );\n}\n\n/**\n * What to tell the reader when `git commit` refused. \"Nothing to commit\" is the\n * everyday case \u2014 the branch is clean, or the changes were never staged \u2014 and it\n * gets a plain instruction instead of git's paragraph; anything else (a failing\n * hook, a rejected signature) keeps git's own line.\n */\nfunction describeCommitFailure(output: string): string | undefined {\n if (/nothing to commit|no changes added to commit|nothing added to commit/i.test(output)) {\n return 'Nothing is staged to commit. Stage a change first.';\n }\n return gitRefusal(output);\n}\n\n/**\n * The line from a refused command a reader should see: the first line that is\n * git's own, not Node's `Command failed: \u2026` wrapper or the branch chatter\n * (`On branch main`, `Your branch is up to date with \u2026`) that always leads a\n * commit's output. Capped so a hint stays a hint.\n */\nfunction gitRefusal(output: string): string | undefined {\n const line = output\n .split('\\n')\n .map((candidate) => candidate.trim())\n .find(\n (candidate) =>\n candidate.length > 0 &&\n !candidate.startsWith('Command failed:') &&\n !/^On branch /.test(candidate) &&\n !/^Your branch /.test(candidate),\n );\n if (line === undefined) {\n return undefined;\n }\n return line.length > 200 ? `${line.slice(0, 197)}\u2026` : line;\n}\n\nfunction nonEmptyLines(output: string | undefined): string[] {\n return (output ?? '')\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n}\n\n/** `<behind>\\t<ahead>` from `git rev-list --left-right --count`; 0 when unreadable. */\nfunction parseCounts(raw: string): [number, number] {\n const [behind, ahead] = raw.trim().split(/\\s+/).map((value) => Number.parseInt(value, 10));\n return [Number.isFinite(behind) ? behind : 0, Number.isFinite(ahead) ? ahead : 0];\n}\n\n/** Paths read with `/` on every platform, the way pi prints them. */\nfunction toPosix(path: string): string {\n return path.split('\\\\').join('/');\n}\n\n/** How many commits to walk past while paging towards the root commit. */\nfunction clampSkip(requested?: number): number {\n if (requested === undefined || !Number.isFinite(requested)) {\n return 0;\n }\n return Math.max(0, Math.floor(requested));\n}\n\n/** One record per commit: `hash \u00B7 short \u00B7 parents \u00B7 refs \u00B7 author \u00B7 date \u00B7 subject`. */\nfunction parseCommits(raw: string): GitCommit[] {\n const commits: GitCommit[] = [];\n for (const record of raw.split(RECORD)) {\n const line = record.replace(/^\\n+/, '');\n if (line.trim().length === 0) {\n continue;\n }\n const fields = line.split(FIELD);\n const [hash, shortHash, parents, refs, author, date, subject] = fields;\n if (!hash || !shortHash || !date) {\n continue;\n }\n commits.push({\n hash,\n shortHash,\n parents: (parents ?? '').split(' ').filter((parent) => parent.length > 0),\n refs: parseRefs(refs ?? ''),\n author: author ?? '',\n date,\n subject: subject ?? '',\n });\n }\n return commits;\n}\n\n/**\n * `%D` prints decorations comma-separated (`HEAD -> main, tag: v1.0`). The panel\n * renders each as a chip, so split here rather than shipping one long string.\n */\nfunction parseRefs(raw: string): string[] {\n const refs = raw\n .split(',')\n .map((ref) => ref.trim())\n .filter((ref) => ref.length > 0);\n return refs;\n}\n\nfunction gitText(args: string[], cwd: string): Promise<string | undefined> {\n return new Promise((resolve) => {\n execFile(\n 'git',\n args,\n { cwd, timeout: 5_000, maxBuffer: 16 * 1024 * 1024, windowsHide: true },\n (error, stdout) => resolve(error ? undefined : stdout),\n );\n });\n}\n\n/** Resolves client paths inside `cwd` and normalises them to `/` separators. */\nfunction resolvePaths(cwd: string, requested: readonly string[]): string[] {\n return requested\n .filter((path): path is string => typeof path === 'string' && path.length > 0)\n .map((path) => toPosix(relative(cwd, resolveWithin(cwd, path))));\n}\n\n/**\n * A git mutation: true when git accepted it, false when it failed. A network\n * command (pull/push) gets a long timeout and no terminal prompt, so a missing\n * credential fails instead of hanging the host on a blocked stdin.\n */\nasync function gitRun(args: string[], cwd: string, timeout = 5_000): Promise<boolean> {\n return (await gitExec(args, cwd, timeout)).ok;\n}\n\n/**\n * Runs git and keeps both its verdict and its words: a refused command (nothing\n * staged, a failing hook, a checkout conflict) has its stderr to show the user.\n */\nfunction gitExec(\n args: string[],\n cwd: string,\n timeout = 5_000,\n): Promise<{ ok: boolean; output: string }> {\n return new Promise((resolve) => {\n execFile(\n 'git',\n args,\n {\n cwd,\n timeout,\n maxBuffer: 8 * 1024 * 1024,\n windowsHide: true,\n env: {\n ...process.env,\n GIT_TERMINAL_PROMPT: '0',\n GIT_EDITOR: 'true',\n GIT_MERGE_AUTOEDIT: 'no',\n },\n },\n (error, stdout, stderr) => {\n if (error === null) {\n resolve({ ok: true, output: stdout.trim() });\n return;\n }\n // git writes a refusal to stderr *or* stdout \u2014 \"nothing to commit\" is on\n // stdout \u2014 while Node's `error.message` is only the `Command failed: git \u2026`\n // wrapper around that same output. Prefer git's own words and keep the\n // wrapper only for a failure with no output at all (a spawn error).\n const output = [stderr.trim(), stdout.trim()]\n .filter((part) => part.length > 0)\n .join('\\n');\n resolve({ ok: false, output: output.length > 0 ? output : error.message.trim() });\n },\n );\n });\n}\n", "import { execFile } from 'node:child_process';\nimport { readdir } from 'node:fs/promises';\nimport type { Dirent } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { MorseLogger } from '@morse/core';\n\n/**\n * Directories a file picker never wants, and which make a tree walk slow enough\n * to feel broken. Mirrors the VS Code host's list on purpose: a browser host and\n * a webview host should offer the same files for the same project.\n */\nconst SKIPPED_DIRECTORIES = new Set([\n 'node_modules',\n 'dist',\n 'out',\n 'build',\n 'coverage',\n 'target',\n 'vendor',\n 'venv',\n '__pycache__',\n '.angular',\n '.next',\n '.nuxt',\n '.turbo',\n '.cache',\n]);\n\nconst MAX_INDEXED_FILES = 5_000;\nconst INDEX_TTL_MS = 60_000;\n\ninterface FileIndex {\n files: string[];\n /** How the list was produced, for the log line. */\n source: 'git' | 'walk';\n at: number;\n}\n\n/** One index per directory: a global browser host can switch projects. */\nconst indexes = new Map<string, FileIndex>();\nconst inFlight = new Map<string, Promise<FileIndex>>();\n\n/**\n * Entries under `cwd`, relative to it, for the frontend's `@mention` picker:\n * files *and* directories, the latter with a trailing `/` \u2014 the vocabulary pi's\n * own completion offers (fd with `--type f --type d`).\n *\n * The VS Code host lists its workspace folders; the browser host has no\n * workspace folder, so it lists the active session's directory \u2014 the cwd pi\n * resolves a mention against. Git first (`ls-files` honours `.gitignore` and is\n * far faster than a walk), then a capped walk with an explicit skip list.\n */\nexport async function workspaceFiles(\n cwd: string,\n logger: MorseLogger,\n options: { fresh?: boolean } = {},\n): Promise<string[]> {\n const { files, source } = await cachedIndex(cwd, options.fresh === true);\n logger.info(`File picker: ${files.length} entries via ${source} in ${cwd}`);\n return files;\n}\n\nasync function cachedIndex(cwd: string, fresh: boolean): Promise<FileIndex> {\n const now = Date.now();\n const cached = indexes.get(cwd);\n if (!fresh && cached && now - cached.at < INDEX_TTL_MS) {\n return cached;\n }\n const pending = inFlight.get(cwd);\n if (pending) {\n return pending;\n }\n const task = indexWorkspace(cwd)\n .then((index) => {\n const entry: FileIndex = { ...index, at: Date.now() };\n indexes.set(cwd, entry);\n return entry;\n })\n .finally(() => {\n inFlight.delete(cwd);\n });\n inFlight.set(cwd, task);\n return task;\n}\n\nasync function indexWorkspace(cwd: string): Promise<Omit<FileIndex, 'at'>> {\n const fromGit = await gitFileList(cwd);\n if (fromGit) {\n return { files: indexEntries(fromGit), source: 'git' };\n }\n const fromWalk: string[] = [];\n await walk(cwd, cwd, fromWalk);\n return { files: indexEntries(fromWalk), source: 'walk' };\n}\n\n/**\n * Turns a list of file paths into the picker's entries: the files plus every\n * directory they imply, the latter marked with a trailing `/`. Deriving the\n * directories from the files keeps the git and walk listings identical and never\n * offers a folder the agent could not reach anyway. The frontend ranks a\n * directory first and lets the user drill into it (`@docs/` lists its contents).\n */\nfunction indexEntries(paths: string[]): string[] {\n const entries = new Set(dedupe(paths).slice(0, MAX_INDEXED_FILES));\n for (const path of [...entries]) {\n const segments = path.split('/');\n for (let depth = 1; depth < segments.length; depth += 1) {\n entries.add(`${segments.slice(0, depth).join('/')}/`);\n }\n }\n return [...entries].sort();\n}\n\nfunction dedupe(paths: string[]): string[] {\n return [...new Set(paths)];\n}\n\n/**\n * `git ls-files` reports paths relative to the repository root; a directory\n * below that root is handled by stripping `--show-prefix` and dropping entries\n * outside it, so the result is always cwd-relative. `--full-name` is what makes\n * that true when the cwd is a subdirectory: without it git prints cwd-relative\n * paths and every entry would be filtered out.\n */\nasync function gitFileList(cwd: string): Promise<string[] | undefined> {\n try {\n const [prefix, listed] = await Promise.all([\n runGit(['rev-parse', '--show-prefix'], cwd),\n runGit(['ls-files', '-z', '--cached', '--others', '--exclude-standard', '--full-name'], cwd),\n ]);\n if (listed === undefined) {\n return undefined;\n }\n const root = prefix?.trim() ?? '';\n return listed\n .split('\\0')\n .filter((path) => path.length > 0)\n .filter((path) => root.length === 0 || path.startsWith(root))\n .map((path) => path.slice(root.length))\n .filter((path) => path.length > 0 && !isInSkippedDirectory(path));\n } catch {\n return undefined;\n }\n}\n\nfunction runGit(args: string[], cwd: string): Promise<string | undefined> {\n return new Promise((resolve) => {\n execFile(\n 'git',\n args,\n { cwd, timeout: 5_000, maxBuffer: 8 * 1024 * 1024, windowsHide: true },\n (error, stdout) => resolve(error ? undefined : stdout),\n );\n });\n}\n\n/**\n * Only *folders* are skipped by the name rules: a dotfile such as `.gitignore`\n * is perfectly pickable, while a dot-folder (`.git`, `.github`) is not.\n */\nfunction isInSkippedDirectory(path: string): boolean {\n const folders = path.split('/').slice(0, -1);\n return folders.some((folder) => SKIPPED_DIRECTORIES.has(folder) || folder.startsWith('.'));\n}\n\nasync function walk(root: string, directory: string, files: string[]): Promise<void> {\n if (files.length >= MAX_INDEXED_FILES) {\n return;\n }\n let entries: Dirent[];\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch {\n // An unreadable directory is not a reason to fail the whole listing.\n return;\n }\n for (const entry of entries) {\n if (files.length >= MAX_INDEXED_FILES) {\n return;\n }\n const full = join(directory, entry.name);\n if (entry.isDirectory()) {\n // Hidden directories (`.git`, `.cache`, `.venv`, ...) are never picked.\n if (SKIPPED_DIRECTORIES.has(entry.name) || entry.name.startsWith('.')) {\n continue;\n }\n await walk(root, full, files);\n continue;\n }\n if (entry.isFile() || entry.isSymbolicLink()) {\n files.push(toPosix(relative(root, full)));\n }\n }\n}\n\n/** Mention paths read with `/` on every platform, the way pi prints them. */\nfunction toPosix(path: string): string {\n return path.split('\\\\').join('/');\n}\n", "import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { UnsupportedByHostError } from '@morse/core';\nimport type { WorkbenchSnapshot } from '@morse/protocol';\n\n/** The reader's shell layout: tabs, panel and terminals. Small, rewritten on a change. */\nconst LAYOUT_FILE = 'workbench.json';\n/**\n * The half-written prompts, keyed by tab. Larger than the layout because a draft\n * can carry inline image attachments, and rewritten as the reader types.\n */\nconst DRAFTS_FILE = 'drafts.json';\n\n/**\n * A layout is a handful of tab names and shell ids, so anything past a quarter\n * of a megabyte is not a layout \u2014 it is a client trying to use the host as a\n * scratch disk. The cap keeps the file small and the parse cheap.\n */\nconst LAYOUT_MAX_BYTES = 256 * 1024;\n/**\n * A draft carries text, pins and (possibly) base64 images, so it is allowed to\n * be much larger. The cap is still bounded: a client that keeps pushing past this\n * is not writing drafts, and an unbounded file would eventually fail to parse.\n */\nconst DRAFTS_MAX_BYTES = 32 * 1024 * 1024;\n\n/**\n * The browser host's persisted shell state under `<MORSE_HOME>`: the layout the\n * reader left (`workbench.json`) and the prompts they were typing (`drafts.json`),\n * so opening the page again lands them on the tab they left, with the words and\n * attachments still in the composer.\n *\n * The frontend owns the inner shape (`snapshot.data`); the host only guards the\n * envelope's version and size, so a newer frontend can add a field without a\n * server change. A missing, unreadable or stale file is not an error: it just\n * means \"nothing to restore\", and the frontend starts clean.\n */\nexport function readWorkbench(dataDir: string): WorkbenchSnapshot | undefined {\n return readSlot(join(dataDir, LAYOUT_FILE), LAYOUT_MAX_BYTES);\n}\n\n/**\n * Writes the layout the frontend just handed back. The write is atomic (a temp\n * file beside it, then a rename), so a crash mid-write leaves the previous\n * layout intact instead of a truncated one. Returns false when the envelope is\n * unusable or the directory is not writable; a failed save is not fatal.\n */\nexport function saveWorkbench(dataDir: string, snapshot: unknown): boolean {\n return saveSlot(dataDir, LAYOUT_FILE, snapshot, LAYOUT_MAX_BYTES);\n}\n\n/** The half-written prompts, per tab, as the frontend last saved them. */\nexport function readDrafts(dataDir: string): WorkbenchSnapshot | undefined {\n return readSlot(join(dataDir, DRAFTS_FILE), DRAFTS_MAX_BYTES);\n}\n\n/** Writes the drafts back, with the same atomic write and envelope guard. */\nexport function saveDrafts(dataDir: string, snapshot: unknown): boolean {\n return saveSlot(dataDir, DRAFTS_FILE, snapshot, DRAFTS_MAX_BYTES);\n}\n\nfunction readSlot(path: string, maxBytes: number): WorkbenchSnapshot | undefined {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return undefined;\n }\n if (raw.length > maxBytes) {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n return asSnapshot(parsed);\n}\n\nfunction saveSlot(dataDir: string, file: string, snapshot: unknown, maxBytes: number): boolean {\n const valid = asSnapshot(snapshot);\n if (valid === undefined) {\n throw new UnsupportedByHostError(`Saving ${file} needs a { version, data } snapshot.`);\n }\n const serialized = JSON.stringify(valid);\n if (Buffer.byteLength(serialized, 'utf8') > maxBytes) {\n throw new UnsupportedByHostError(`That ${file} snapshot is too large to save.`);\n }\n const target = join(dataDir, file);\n const temp = `${target}.${process.pid}.tmp`;\n try {\n mkdirSync(dataDir, { recursive: true });\n writeFileSync(temp, serialized);\n renameSync(temp, target);\n return true;\n } catch {\n // Some filesystems refuse a rename over an existing file (a Windows mount).\n try {\n writeFileSync(target, serialized);\n return true;\n } catch {\n rmSync(temp, { force: true });\n return false;\n }\n }\n}\n\n/** The envelope, with anything that is not a `{ version, data }` object dropped. */\nfunction asSnapshot(value: unknown): WorkbenchSnapshot | undefined {\n if (typeof value !== 'object' || value === null) {\n return undefined;\n }\n const candidate = value as Record<string, unknown>;\n const version = candidate['version'];\n if (typeof version !== 'number' || !Number.isInteger(version) || version < 1) {\n return undefined;\n }\n return { version, data: candidate['data'] };\n}\n", "import { basename, dirname, isAbsolute, join } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { fileURLToPath } from 'node:url';\nimport type { WorkspaceRef } from '@morse/core';\nimport { FRONTEND_MANIFEST_FILE, frontendIdentity, parseFrontendManifest } from '@morse/protocol';\nimport type { FrontendIdentity } from '@morse/protocol';\nimport { DEFAULT_UPLOAD_DIR } from '../internal/uploads/upload-store.js';\n\nexport interface MorseServerConfig {\n host: string;\n port: number;\n /** Directory holding the built frontend (index.html + webview.manifest.json). */\n uiDir: string;\n workspace: WorkspaceRef;\n piPath?: string;\n nodeEntryPath?: string;\n sessionDir?: string;\n noSession: boolean;\n requestTimeoutMs: number;\n /** How many `pi` processes stay alive at once (least recently used first). */\n hotSessions: number;\n /** Roots the agent may work in; empty means \"any absolute path\". */\n projects: string[];\n /** Where browser uploads land: relative to a session cwd, or absolute. */\n uploadDir: string;\n /**\n * The Morse data directory (`MORSE_HOME`, default `~/.morse`): where this host\n * keeps state that outlives a browser tab \u2014 the shell layout the reader left\n * behind. It is the same directory the `morse` CLI writes its state and logs to.\n */\n dataDir: string;\n /**\n * How long a detached shell keeps running before the host reclaims it. A\n * terminal survives a page reload, so the process needs its own expiry: only\n * an explicit close ends one sooner. `0` disables the timeout.\n */\n terminalIdleMs: number;\n /**\n * Whether the frontend may ask the registry for the latest release. On unless\n * `MORSE_UPDATE_CHECK=0`; an air-gapped host turns it off rather than letting\n * the panel try and fail.\n */\n updateCheck: boolean;\n /**\n * Opaque token the `morse` CLI mints for each daemon it spawns. Relayed by\n * `/api/health`, it lets the CLI prove which process is answering instead of\n * trusting a pid that the OS may have recycled to an unrelated process.\n * Undefined when the server is started without the CLI.\n */\n instance?: string;\n /**\n * The bundle this host serves, as its own manifest declares it. Relayed to\n * clients in `host/ready`, so a frontend can name itself (the About dialog) and\n * the host log says which build is talking. Undefined when the directory has no\n * usable manifest \u2014 a bare dev directory, say.\n */\n frontend?: FrontendIdentity;\n}\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): MorseServerConfig {\n const workspacePath = resolveWorkspacePath(env);\n const configuredUiDir = env.MORSE_UI_DIR;\n // ESM has no __dirname. In the monorepo this file lives in\n // <repo>/packages/server/dist/app, so the Angular build sits three levels up;\n // in the published bundle the frontend is copied next to the bundled server\n // (<pkg>/dist/ui). Pick whichever actually holds an index.html.\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const bundledUiDir = join(moduleDir, 'ui');\n const monorepoUiDir = join(moduleDir, '..', '..', '..', 'ui-angular', 'dist');\n const defaultUiDir = existsSync(join(bundledUiDir, 'index.html'))\n ? bundledUiDir\n : monorepoUiDir;\n const defaultHost = '127.0.0.1';\n const uiDir = configuredUiDir\n ? isAbsolute(configuredUiDir)\n ? configuredUiDir\n : join(process.cwd(), configuredUiDir)\n : defaultUiDir;\n\n return {\n host: env.MORSE_HOST ?? defaultHost,\n port: parsePositiveInt(env.MORSE_PORT, 4399),\n uiDir,\n frontend: readFrontendIdentity(uiDir),\n workspace: { cwd: workspacePath, name: basename(workspacePath) || workspacePath },\n piPath: env.MORSE_PI_PATH,\n nodeEntryPath: env.MORSE_PI_ENTRY,\n sessionDir: env.MORSE_SESSION_DIR,\n noSession: env.MORSE_NO_SESSION === '1',\n requestTimeoutMs: parsePositiveInt(env.MORSE_REQUEST_TIMEOUT_MS, 30_000),\n hotSessions: parsePositiveInt(env.MORSE_HOT_SESSIONS, 4),\n projects: parseList(env.MORSE_PROJECTS),\n uploadDir: env.MORSE_UPLOAD_DIR?.trim() || DEFAULT_UPLOAD_DIR,\n dataDir: resolveDataDir(env),\n terminalIdleMs: parsePositiveInt(env.MORSE_TERMINAL_IDLE_MS, 30 * 60_000),\n updateCheck: !isOff(env.MORSE_UPDATE_CHECK),\n instance: env.MORSE_INSTANCE?.trim() || undefined,\n };\n}\n\n/**\n * Which frontend this host is serving, read from the directory it will serve. The\n * manifest is the frontend's own statement about itself; the host only relays it,\n * exactly like the VS Code host relays the same file.\n */\nfunction readFrontendIdentity(uiDir: string): FrontendIdentity | undefined {\n try {\n return frontendIdentity(\n parseFrontendManifest(readFileSync(join(uiDir, FRONTEND_MANIFEST_FILE), 'utf8')),\n );\n } catch {\n return undefined;\n }\n}\n\n/** `MORSE_HOME` wins, else the same `~/.morse` the CLI uses. */\nfunction resolveDataDir(env: NodeJS.ProcessEnv): string {\n const explicit = env.MORSE_HOME?.trim();\n return explicit && explicit.length > 0 ? explicit : join(homedir(), '.morse');\n}\n\nfunction parsePositiveInt(value: string | undefined, fallback: number): number {\n if (!value) {\n return fallback;\n }\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;\n}\n\n/** `MORSE_UPDATE_CHECK=0` (or `false`/`off`) turns the release check off. */\nfunction isOff(value: string | undefined): boolean {\n const normalized = value?.trim().toLowerCase();\n return normalized === '0' || normalized === 'false' || normalized === 'off';\n}\n\n/** `MORSE_PROJECTS=/a:/b` or `MORSE_PROJECTS=/a,/b` -> ['/a', '/b']. */\nfunction parseList(value: string | undefined): string[] {\n if (!value) {\n return [];\n }\n return value\n .split(/[:,]/)\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n}\n\n/**\n * Where the agent runs. `MORSE_WORKSPACE` wins; otherwise use the current\n * directory, except when that is a package inside a workspace monorepo \u2014\n * `npm run -w @morse/server` sets the cwd to `packages/server`, which is\n * almost never the project the user wants the agent to work on.\n */\nfunction resolveWorkspacePath(env: NodeJS.ProcessEnv): string {\n const explicit = env.MORSE_WORKSPACE;\n if (explicit && explicit.trim().length > 0) {\n return explicit;\n }\n\n const start = process.cwd();\n let directory = start;\n for (;;) {\n const manifest = join(directory, 'package.json');\n if (existsSync(manifest) && declaresWorkspaces(manifest)) {\n return directory;\n }\n const parent = dirname(directory);\n if (parent === directory) {\n return start;\n }\n directory = parent;\n }\n}\n\nfunction declaresWorkspaces(manifestPath: string): boolean {\n try {\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as { workspaces?: unknown };\n return manifest.workspaces !== undefined;\n } catch {\n return false;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Logger", "resolve", "isDefined", "homedir", "join", "join", "homedir", "spawn", "homedir", "join", "dirname", "asString", "spawn", "join", "homedir", "resolve", "readdir", "readFile", "stat", "join", "resolve", "resolve", "join", "stat", "readFile", "readdir", "toThinkingLevel", "toUsage", "toInteractionRequest", "toInteractionRequest", "readFile", "join", "readFile", "join", "isAbsolute", "resolve", "sep", "Inject", "mkdirSync", "readFileSync", "writeFileSync", "join", "readFileSync", "mkdirSync", "join", "writeFileSync", "Inject", "Inject", "Injectable", "Injectable", "Inject", "Inject", "Inject", "Injectable", "existsSync", "basename", "isAbsolute", "join", "sep", "readdir", "stat", "homedir", "basename", "dirname", "isAbsolute", "join", "resolve", "open", "stat", "isAbsolute", "relative", "resolve", "stat", "open", "relative", "isAbsolute", "resolve", "execFile", "relative", "parseStatus", "toPosix", "relative", "resolve", "execFile", "execFile", "readdir", "join", "relative", "SKIPPED_DIRECTORIES", "resolve", "toPosix", "mkdirSync", "readFileSync", "renameSync", "rmSync", "writeFileSync", "join", "join", "readFileSync", "mkdirSync", "writeFileSync", "renameSync", "rmSync", "describeError", "Injectable", "Inject", "Inject", "basename", "dirname", "isAbsolute", "join", "existsSync", "readFileSync", "homedir", "dirname", "join", "existsSync", "isAbsolute", "basename", "readFileSync", "homedir", "Logger"]
|
|
7
7
|
}
|