@supanadit/morse-web 0.9.5 → 0.11.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.
@@ -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/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/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/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 | '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/** 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/** 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\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] 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] 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 = 19;\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 { 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 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", "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 { 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 * 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\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 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 default:\n return;\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 const result = await this.guard(() =>\n Promise.resolve(\n handler(command, args, { cwd: this.currentState().workspace.cwd }),\n ),\n );\n if (!requestId) {\n return;\n }\n // The failure detail is already surfaced by `guard` (notice + log), so the\n // caller only needs to know that it failed.\n if (result.ok) {\n this.answerHostCommand(requestId, true, result.value);\n return;\n }\n this.answerHostCommand(requestId, false, undefined, `\"${command}\" failed`);\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/**\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 { 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 { readGitDiff, readGitLog, readGitStatus } from '../workspace/git-log.js';\nimport { workspaceFiles } from '../workspace/workspace-index.js';\nimport { ServerProjectPolicy } from '../projects/project-policy.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 ) {}\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 // 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 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 '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.requireWritableCwd(context);\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.requireWritableCwd(context);\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 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 /** 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}", "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 { GitCommit, GitDiff, GitFileStatus, GitLog, GitStatus } 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/** 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", "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 { basename, dirname, isAbsolute, join } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\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 * 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 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\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", "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", "Injectable", "Inject", "Inject", "basename", "dirname", "isAbsolute", "join", "existsSync", "dirname", "join", "existsSync", "isAbsolute", "basename", "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/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/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/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/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 | '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/** 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 = 20;\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", "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, TerminalProcess } 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 /** The shells this client has open, keyed by the terminal id it named. */\n private readonly terminals = new Map<string, TerminalProcess>();\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 for (const process of this.terminals.values()) {\n process.kill();\n }\n this.terminals.clear();\n this.terminalGenerations.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 * Starts a shell for the bottom panel. The directory is the viewing session's\n * (or the draft's), never an arbitrary path the client names \u2014 the backend\n * still re-checks it against the host policy before spawning.\n */\n private async openTerminal(payload: {\n terminalId: string;\n cwd?: string;\n cols?: number;\n rows?: number;\n }): Promise<void> {\n if (this.options.terminal === undefined) {\n this.emitTerminalExit(payload.terminalId, undefined, 'This host has no terminal.');\n return;\n }\n // One terminal per id: a repeated open replaces the shell rather than\n // leaking a second one behind the same name.\n this.closeTerminal(payload.terminalId);\n const generation = this.terminalGenerations.get(payload.terminalId) ?? 0;\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 process: TerminalProcess;\n try {\n process = await this.options.terminal.open(\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 `open` was still awaiting the shell.\n if (!this.disposed && this.terminalGenerations.get(payload.terminalId) === generation) {\n this.terminals.set(payload.terminalId, process);\n } else {\n process.kill();\n }\n }\n\n private closeTerminal(terminalId: string): void {\n // Any pending exit for the old shell is now stale.\n this.terminalGenerations.set(terminalId, (this.terminalGenerations.get(terminalId) ?? 0) + 1);\n const process = this.terminals.get(terminalId);\n if (process === undefined) {\n return;\n }\n this.terminals.delete(terminalId);\n process.kill();\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 } from 'node-pty';\nimport { Inject, Injectable } from '@nestjs/common';\nimport { UnsupportedByHostError, type MorseLogger } from '@morse/core';\nimport type {\n TerminalBackend,\n TerminalOpenOptions,\n TerminalProcess,\n TerminalSink,\n} from '@morse/host-runtime';\nimport { MORSE_LOGGER, MORSE_PROJECT_POLICY } from '../../app/tokens.js';\nimport type { ServerProjectPolicy } from '../projects/project-policy.js';\n\n/** How long a shell gets to exit on kill before it is killed outright. */\nconst SIGKILL_AFTER_MS = 1_500;\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 * 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@Injectable()\nexport class ServerTerminalBackend implements TerminalBackend {\n constructor(\n @Inject(MORSE_PROJECT_POLICY) private readonly policy: ServerProjectPolicy,\n @Inject(MORSE_LOGGER) private readonly logger: MorseLogger,\n ) {}\n\n open(options: TerminalOpenOptions, sink: TerminalSink): TerminalProcess {\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(\n `Morse is not allowed to run a terminal in ${options.cwd}.`,\n );\n }\n const shell = defaultShell();\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 this.logger.info(`Terminal opened in ${options.cwd} (${shell}, pid ${pty.pid})`);\n\n let exited = false;\n const data = pty.onData((chunk) => sink.output(chunk));\n pty.onExit(({ exitCode }) => {\n exited = true;\n data.dispose();\n sink.exit(exitCode);\n });\n\n return {\n write: (input) => {\n if (!exited) {\n pty.write(input);\n }\n },\n resize: (cols, rows) => {\n if (!exited) {\n pty.resize(cols, rows);\n }\n },\n kill: () => {\n if (exited) {\n return;\n }\n exited = true;\n pty.kill();\n // A shell that ignored SIGTERM (a hung process group) still goes.\n const timer = setTimeout(() => pty.kill('SIGKILL'), SIGKILL_AFTER_MS);\n timer.unref?.();\n },\n };\n }\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 { 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 // 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 '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.requireWritableCwd(context);\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.requireWritableCwd(context);\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.requireWritableCwd(context);\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 /** 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 return { ok: result.ok, message: result.ok ? undefined : firstLine(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 : firstLine(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/** The first non-empty line of git's output, capped so a hint stays a hint. */\nfunction firstLine(output: string): string | undefined {\n const line = output.split('\\n').find((candidate) => candidate.trim().length > 0)?.trim();\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 resolve(\n error === null\n ? { ok: true, output: stdout.trim() }\n : { ok: false, output: (stderr || error.message || '').trim() },\n );\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 { basename, dirname, isAbsolute, join } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\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 * 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 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\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", "firstLine", "homedir", "join", "join", "homedir", "spawn", "readdir", "readFile", "stat", "join", "resolve", "resolve", "join", "stat", "readFile", "readdir", "toThinkingLevel", "toUsage", "toInteractionRequest", "process", "toInteractionRequest", "readFile", "join", "readFile", "join", "isAbsolute", "resolve", "sep", "Inject", "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", "Injectable", "Inject", "Inject", "basename", "dirname", "isAbsolute", "join", "existsSync", "dirname", "join", "existsSync", "isAbsolute", "basename", "Logger"]
7
7
  }