@dyyz1993/pi-coding-agent 0.74.21 → 0.74.24

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 +1 @@
1
- {"version":3,"file":"file-snapshot-manager.js","sourceRoot":"","sources":["../../../src/core/file-store/file-snapshot-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport type { CustomEntry, SessionEntry } from \"../session-manager.js\";\nimport type { InternalGit, TreeEntry } from \"./internal-git.js\";\n\nexport interface StepSnapshotData {\n\tbaselineTreeHash: string | null;\n\tsnapshotTreeHash: string;\n\tdiff: { added: string[]; modified: string[]; deleted: string[] } | null;\n\tturnIndex: number;\n}\n\nexport interface ModifiedFileInfo {\n\tpath: string;\n\tstatus: \"added\" | \"modified\" | \"deleted\";\n\tturnIndex: number;\n\tentryId: string;\n}\n\nexport interface FileDiffInfo {\n\tpath: string;\n\toldContent: string | null;\n\tnewContent: string | null;\n\toldHash: string | null;\n\tnewHash: string | null;\n\tunifiedDiff: string;\n}\n\nexport interface RestoreResult {\n\trestored: string[];\n\tdeleted: string[];\n\tskipped: string[];\n\tdirty: string[];\n}\n\nconst FILE_SIZE_LIMIT = 1024 * 1024;\n\nfunction findCanonicalGitRoot(cwd: string): string | null {\n\tlet dir: string;\n\ttry {\n\t\tdir = realpathSync(cwd);\n\t} catch {\n\t\treturn null;\n\t}\n\tfor (;;) {\n\t\tconst gitPath = join(dir, \".git\");\n\t\tif (!existsSync(gitPath)) {\n\t\t\tconst parent = dirname(dir);\n\t\t\tif (parent === dir) return null;\n\t\t\tdir = parent;\n\t\t\tcontinue;\n\t\t}\n\t\tconst stat = lstatSync(gitPath);\n\t\tif (stat.isDirectory()) return dir;\n\t\tif (stat.isFile()) {\n\t\t\tconst content = readFileSync(gitPath, \"utf-8\").trim();\n\t\t\tconst match = content.match(/^gitdir:\\s*(.+)/);\n\t\t\tif (!match) return null;\n\t\t\tconst gitdir = match[1]!.trim();\n\t\t\tif (gitdir.includes(\"/worktrees/\")) {\n\t\t\t\tconst commonPrefix = gitdir.replace(/\\/worktrees\\/[^/]+\\/?$/, \"\");\n\t\t\t\tlet rootDir = commonPrefix;\n\t\t\t\tif (rootDir.endsWith(\"/.git\")) rootDir = rootDir.slice(0, -4);\n\t\t\t\tif (!existsSync(join(rootDir, \".git\"))) return null;\n\t\t\t\treturn realpathSync(rootDir);\n\t\t\t}\n\t\t\tconst parent = dirname(gitdir);\n\t\t\tif (!existsSync(parent)) return null;\n\t\t\treturn parent;\n\t\t}\n\t\treturn null;\n\t}\n}\n\nfunction readFilteredWorkingDir(git: InternalGit, cwd: string): Map<string, string> {\n\tconst all = git.scanWorkingDir(cwd);\n\tconst filtered = new Map<string, string>();\n\tfor (const [path, content] of all) {\n\t\tif (content.length > FILE_SIZE_LIMIT) continue;\n\t\tfiltered.set(path, content);\n\t}\n\treturn filtered;\n}\n\nfunction generateUnifiedDiff(oldContent: string | null, newContent: string | null, filePath: string): string {\n\tconst oldLines = oldContent === null ? [] : oldContent.split(\"\\n\");\n\tconst newLines = newContent === null ? [] : newContent.split(\"\\n\");\n\tconst lines: string[] = [];\n\tlines.push(`--- ${filePath}`);\n\tlines.push(`+++ ${filePath}`);\n\n\tlet i = 0;\n\tlet j = 0;\n\tconst hunks: string[] = [];\n\n\twhile (i < oldLines.length || j < newLines.length) {\n\t\tif (i < oldLines.length && j < newLines.length && oldLines[i] === newLines[j]) {\n\t\t\ti++;\n\t\t\tj++;\n\t\t} else {\n\t\t\tconst hunkStart = Math.max(0, i - 3);\n\t\t\tconst hunkOldEnd = Math.min(oldLines.length, i + 3);\n\t\t\tconst hunkNewEnd = Math.min(newLines.length, j + 3);\n\n\t\t\tconst removed: string[] = [];\n\t\t\tconst added: string[] = [];\n\t\t\tlet oi = i;\n\t\t\tlet ni = j;\n\n\t\t\twhile (oi < hunkOldEnd || ni < hunkNewEnd) {\n\t\t\t\tif (oi < hunkOldEnd && ni < hunkNewEnd && oldLines[oi] === newLines[ni]) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (oi < hunkOldEnd) {\n\t\t\t\t\tremoved.push(oldLines[oi]);\n\t\t\t\t\toi++;\n\t\t\t\t}\n\t\t\t\tif (ni < hunkNewEnd && (oi >= hunkOldEnd || newLines[ni] !== oldLines[oi])) {\n\t\t\t\t\tadded.push(newLines[ni]);\n\t\t\t\t\tni++;\n\t\t\t\t} else if (ni < hunkNewEnd) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst contextStart = Math.max(0, i - 2);\n\t\t\tconst contextLines: string[] = [];\n\t\t\tfor (let c = contextStart; c < i; c++) {\n\t\t\t\tcontextLines.push(` ${oldLines[c]}`);\n\t\t\t}\n\t\t\tfor (const r of removed) {\n\t\t\t\tcontextLines.push(`-${r}`);\n\t\t\t}\n\t\t\tfor (const a of added) {\n\t\t\t\tcontextLines.push(`+${a}`);\n\t\t\t}\n\t\t\tconst contextEnd = Math.min(newLines.length, ni + 2);\n\t\t\tfor (let c = ni; c < contextEnd; c++) {\n\t\t\t\tcontextLines.push(` ${newLines[c]}`);\n\t\t\t}\n\n\t\t\thunks.push(`@@ -${i + 1},${oi - i} +${j + 1},${ni - j} @@`);\n\t\t\thunks.push(...contextLines);\n\n\t\t\ti = oi;\n\t\t\tj = ni;\n\t\t}\n\t}\n\n\tif (hunks.length === 0) return \"\";\n\tlines.push(...hunks);\n\treturn lines.join(\"\\n\");\n}\n\nexport interface BatchDiffResult {\n\tfiles: Array<{\n\t\tpath: string;\n\t\tstatus: \"added\" | \"modified\" | \"deleted\";\n\t\tdiff: FileDiffInfo | null;\n\t}>;\n\tsummary: {\n\t\ttotalFiles: number;\n\t\tadded: number;\n\t\tmodified: number;\n\t\tdeleted: number;\n\t};\n}\n\nexport interface FileHistoryEntry {\n\tentryId: string;\n\tturnIndex: number;\n\ttimestamp: string;\n\tstatus: \"added\" | \"modified\" | \"deleted\";\n\tsnapshotHash: string;\n\tpreviousHash: string | null;\n}\n\ninterface SnapshotWithEntryId extends StepSnapshotData {\n\tentryId: string;\n}\n\nexport class FileSnapshotManager {\n\tprivate git: InternalGit;\n\tprivate sessionStartTreeHash: string | null = null;\n\tprivate lastCommittedTreeHash: string | null = null;\n\tprivate turnIndex = 0;\n\tprivate snapshotIndex: Map<string, SnapshotWithEntryId> = new Map();\n\tprivate turnIndexMap: Map<number, string> = new Map();\n\n\tconstructor(git: InternalGit) {\n\t\tthis.git = git;\n\t}\n\n\tasync initialize(cwd: string): Promise<void> {\n\t\tthis.sessionStartTreeHash = null;\n\t\tthis.lastCommittedTreeHash = null;\n\t\tthis.turnIndex = 0;\n\t\tthis.snapshotIndex.clear();\n\t\tthis.turnIndexMap.clear();\n\n\t\tconst files = readFilteredWorkingDir(this.git, cwd);\n\t\tif (files.size > 0) {\n\t\t\tthis.sessionStartTreeHash = this.git.writeTree(files).treeHash;\n\t\t}\n\t}\n\n\tonTurnEnd(cwd: string, turnIndex: number, appendEntry: (type: string, data: unknown) => string | undefined): void {\n\t\tconst files = readFilteredWorkingDir(this.git, cwd);\n\t\tconst { treeHash: snapshotTreeHash, entries: newEntries } = this.git.writeTree(files);\n\n\t\tconst compareTo = this.lastCommittedTreeHash ?? this.sessionStartTreeHash;\n\t\tconst oldEntries = compareTo ? this.parseTreeEntriesFromHash(compareTo) : new Map<string, TreeEntry>();\n\n\t\tconst stepDiff = this.git.computeDiff(oldEntries, newEntries);\n\t\tconst hasChanges = stepDiff.added.length > 0 || stepDiff.modified.length > 0 || stepDiff.deleted.length > 0;\n\n\t\tif (hasChanges) {\n\t\t\tconst entryId = appendEntry(\"step-snapshot\", {\n\t\t\t\tbaselineTreeHash: compareTo,\n\t\t\t\tsnapshotTreeHash,\n\t\t\t\tdiff: stepDiff,\n\t\t\t\tturnIndex,\n\t\t\t});\n\n\t\t\tif (entryId) {\n\t\t\t\tthis.snapshotIndex.set(entryId, {\n\t\t\t\t\tbaselineTreeHash: compareTo,\n\t\t\t\t\tsnapshotTreeHash,\n\t\t\t\t\tdiff: stepDiff,\n\t\t\t\t\tturnIndex,\n\t\t\t\t\tentryId,\n\t\t\t\t});\n\t\t\t\tthis.turnIndexMap.set(turnIndex, entryId);\n\t\t\t}\n\t\t\tthis.lastCommittedTreeHash = snapshotTreeHash;\n\t\t}\n\n\t\tthis.turnIndex = turnIndex + 1;\n\t}\n\n\trebuildIndex(entries: SessionEntry[]): void {\n\t\tthis.snapshotIndex.clear();\n\t\tthis.turnIndexMap.clear();\n\t\tthis.lastCommittedTreeHash = null;\n\t\tthis.sessionStartTreeHash = null;\n\t\tthis.turnIndex = 0;\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.type !== \"custom\") continue;\n\t\t\tconst custom = entry as CustomEntry;\n\t\t\tif (custom.customType !== \"step-snapshot\") continue;\n\n\t\t\tconst data = custom.data as StepSnapshotData;\n\t\t\tif (!data) continue;\n\n\t\t\tthis.snapshotIndex.set(entry.id, {\n\t\t\t\t...data,\n\t\t\t\tentryId: entry.id,\n\t\t\t});\n\t\t\tthis.turnIndexMap.set(data.turnIndex, entry.id);\n\t\t\tthis.lastCommittedTreeHash = data.snapshotTreeHash;\n\t\t\tthis.turnIndex = Math.max(this.turnIndex, data.turnIndex + 1);\n\t\t}\n\t}\n\n\tgetLatestSnapshotOnPath(entries: SessionEntry[], leafId: string | null): StepSnapshotData | null {\n\t\tif (!leafId) return null;\n\n\t\tconst byId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\n\t\tconst snapshots: StepSnapshotData[] = [];\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.type !== \"custom\") continue;\n\t\t\tconst custom = entry as CustomEntry;\n\t\t\tif (custom.customType !== \"step-snapshot\") continue;\n\t\t\tif (!isOnPathTo(byId, leafId, entry.id)) continue;\n\t\t\tsnapshots.push(custom.data as StepSnapshotData);\n\t\t}\n\n\t\treturn snapshots.length > 0 ? snapshots[snapshots.length - 1] : null;\n\t}\n\n\tgetSnapshotAtTurn(turnIndex: number): StepSnapshotData | null {\n\t\tconst entryId = this.turnIndexMap.get(turnIndex);\n\t\tif (!entryId) return null;\n\t\tconst snap = this.snapshotIndex.get(entryId);\n\t\tif (!snap) return null;\n\t\treturn {\n\t\t\tbaselineTreeHash: snap.baselineTreeHash,\n\t\t\tsnapshotTreeHash: snap.snapshotTreeHash,\n\t\t\tdiff: snap.diff,\n\t\t\tturnIndex: snap.turnIndex,\n\t\t};\n\t}\n\n\tgetSnapshotAtEntry(entryId: string): StepSnapshotData | null {\n\t\tconst snap = this.snapshotIndex.get(entryId);\n\t\tif (!snap) return null;\n\t\treturn {\n\t\t\tbaselineTreeHash: snap.baselineTreeHash,\n\t\t\tsnapshotTreeHash: snap.snapshotTreeHash,\n\t\t\tdiff: snap.diff,\n\t\t\tturnIndex: snap.turnIndex,\n\t\t};\n\t}\n\n\tgetModifiedFiles(options?: { fromEntryId?: string; toEntryId?: string }): ModifiedFileInfo[] {\n\t\tconst snapshots = [...this.snapshotIndex.values()].sort((a, b) => a.turnIndex - b.turnIndex);\n\n\t\tconst fromIdx = options?.fromEntryId ? snapshots.findIndex((s) => s.entryId === options.fromEntryId) : -1;\n\t\tconst toIdx = options?.toEntryId ? snapshots.findIndex((s) => s.entryId === options.toEntryId) : -1;\n\n\t\tconst start = fromIdx === -1 ? 0 : fromIdx;\n\t\tconst end = toIdx === -1 ? snapshots.length - 1 : toIdx;\n\n\t\tif (start > end || snapshots.length === 0) return [];\n\n\t\tconst fileMap = new Map<string, ModifiedFileInfo>();\n\t\tfor (let i = start; i <= end; i++) {\n\t\t\tconst snap = snapshots[i];\n\t\t\tif (!snap?.diff) continue;\n\n\t\t\tfor (const path of snap.diff.added) {\n\t\t\t\tif (!fileMap.has(path)) {\n\t\t\t\t\tfileMap.set(path, { path, status: \"added\", turnIndex: snap.turnIndex, entryId: snap.entryId });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const path of snap.diff.modified) {\n\t\t\t\tif (!fileMap.has(path)) {\n\t\t\t\t\tfileMap.set(path, { path, status: \"modified\", turnIndex: snap.turnIndex, entryId: snap.entryId });\n\t\t\t\t} else {\n\t\t\t\t\tconst existing = fileMap.get(path)!;\n\t\t\t\t\tif (existing.status !== \"added\") {\n\t\t\t\t\t\texisting.status = \"modified\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const path of snap.diff.deleted) {\n\t\t\t\tif (!fileMap.has(path)) {\n\t\t\t\t\tfileMap.set(path, { path, status: \"deleted\", turnIndex: snap.turnIndex, entryId: snap.entryId });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn [...fileMap.values()].sort((a, b) => a.path.localeCompare(b.path));\n\t}\n\n\tgetFileDiff(options: { filePath: string; fromEntryId?: string; toEntryId?: string }): FileDiffInfo | null {\n\t\tconst snapshots = [...this.snapshotIndex.values()].sort((a, b) => a.turnIndex - b.turnIndex);\n\n\t\tconst fromHash = options.fromEntryId\n\t\t\t? (snapshots.find((s) => s.entryId === options.fromEntryId)?.snapshotTreeHash ?? null)\n\t\t\t: this.sessionStartTreeHash;\n\t\tconst toHash = options.toEntryId\n\t\t\t? (snapshots.find((s) => s.entryId === options.toEntryId)?.snapshotTreeHash ?? null)\n\t\t\t: (this.lastCommittedTreeHash ?? this.sessionStartTreeHash);\n\n\t\tif (!fromHash && !toHash) return null;\n\n\t\tconst fromFiles = fromHash ? this.git.readTree(fromHash) : new Map<string, string>();\n\t\tconst toFiles = toHash ? this.git.readTree(toHash) : new Map<string, string>();\n\n\t\tconst oldContent = fromFiles.get(options.filePath) ?? null;\n\t\tconst newContent = toFiles.get(options.filePath) ?? null;\n\n\t\tif (oldContent === null && newContent === null) return null;\n\n\t\treturn {\n\t\t\tpath: options.filePath,\n\t\t\toldContent,\n\t\t\tnewContent,\n\t\t\toldHash: oldContent !== null ? this.git.hashContent(oldContent) : null,\n\t\t\tnewHash: newContent !== null ? this.git.hashContent(newContent) : null,\n\t\t\tunifiedDiff: generateUnifiedDiff(oldContent, newContent, options.filePath),\n\t\t};\n\t}\n\n\tgetBatchDiffs(options?: { fromEntryId?: string; toEntryId?: string }): BatchDiffResult {\n\t\tconst files = this.getModifiedFiles({\n\t\t\tfromEntryId: options?.fromEntryId,\n\t\t\ttoEntryId: options?.toEntryId,\n\t\t});\n\n\t\tlet added = 0;\n\t\tlet modified = 0;\n\t\tlet deleted = 0;\n\n\t\tconst resultFiles = files.map((f) => {\n\t\t\tif (f.status === \"added\") added++;\n\t\t\telse if (f.status === \"modified\") modified++;\n\t\t\telse deleted++;\n\n\t\t\tlet diff: FileDiffInfo | null = null;\n\t\t\ttry {\n\t\t\t\tdiff = this.getFileDiff({\n\t\t\t\t\tfilePath: f.path,\n\t\t\t\t\tfromEntryId: options?.fromEntryId,\n\t\t\t\t\ttoEntryId: options?.toEntryId,\n\t\t\t\t});\n\t\t\t} catch {}\n\n\t\t\treturn { path: f.path, status: f.status, diff };\n\t\t});\n\n\t\treturn {\n\t\t\tfiles: resultFiles,\n\t\t\tsummary: {\n\t\t\t\ttotalFiles: files.length,\n\t\t\t\tadded,\n\t\t\t\tmodified,\n\t\t\t\tdeleted,\n\t\t\t},\n\t\t};\n\t}\n\n\tgetFileHistory(options: { filePath: string }): FileHistoryEntry[] {\n\t\tconst snapshots = [...this.snapshotIndex.values()].sort((a, b) => a.turnIndex - b.turnIndex);\n\t\tconst history: FileHistoryEntry[] = [];\n\n\t\tfor (const snap of snapshots) {\n\t\t\tif (!snap.diff) continue;\n\n\t\t\tlet status: \"added\" | \"modified\" | \"deleted\" | null = null;\n\t\t\tif (snap.diff.added.includes(options.filePath)) {\n\t\t\t\tstatus = \"added\";\n\t\t\t} else if (snap.diff.modified.includes(options.filePath)) {\n\t\t\t\tstatus = \"modified\";\n\t\t\t} else if (snap.diff.deleted.includes(options.filePath)) {\n\t\t\t\tstatus = \"deleted\";\n\t\t\t}\n\n\t\t\tif (status) {\n\t\t\t\tconst previousHash = snap.baselineTreeHash ?? null;\n\t\t\t\thistory.push({\n\t\t\t\t\tentryId: snap.entryId,\n\t\t\t\t\tturnIndex: snap.turnIndex,\n\t\t\t\t\ttimestamp: new Date(0).toISOString(),\n\t\t\t\t\tstatus,\n\t\t\t\t\tsnapshotHash: snap.snapshotTreeHash,\n\t\t\t\t\tpreviousHash,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn history;\n\t}\n\n\tasync restoreFiles(\n\t\tcwd: string,\n\t\toptions: {\n\t\t\ttargetEntryId?: string;\n\t\t\tsnapshotHash?: string;\n\t\t\tfiles?: string[];\n\t\t\tpreview?: boolean;\n\t\t\tcurrentLeafId?: string | null;\n\t\t\tentries: SessionEntry[];\n\t\t\tappendEntry: (type: string, data: unknown) => void;\n\t\t},\n\t): Promise<RestoreResult> {\n\t\tconst empty: RestoreResult = { restored: [], deleted: [], skipped: [], dirty: [] };\n\n\t\tlet targetTreeHash: string | null;\n\t\tif (options.snapshotHash) {\n\t\t\ttargetTreeHash = options.snapshotHash;\n\t\t} else if (options.targetEntryId) {\n\t\t\tconst snap = this.snapshotIndex.get(options.targetEntryId);\n\t\t\tif (snap) {\n\t\t\t\ttargetTreeHash = snap.snapshotTreeHash;\n\t\t\t} else {\n\t\t\t\tconst pathSnap = this.getLatestSnapshotOnPath(options.entries, options.targetEntryId);\n\t\t\t\ttargetTreeHash = pathSnap?.snapshotTreeHash ?? null;\n\t\t\t}\n\t\t} else {\n\t\t\ttargetTreeHash = this.sessionStartTreeHash ?? null;\n\t\t}\n\n\t\tlet currentTreeHash: string | null;\n\t\tif (options.currentLeafId !== undefined) {\n\t\t\tconst currentSnapshot = this.getLatestSnapshotOnPath(options.entries, options.currentLeafId);\n\t\t\tcurrentTreeHash = currentSnapshot?.snapshotTreeHash ?? null;\n\t\t} else {\n\t\t\tcurrentTreeHash = this.lastCommittedTreeHash ?? this.sessionStartTreeHash;\n\t\t}\n\n\t\tif (targetTreeHash === currentTreeHash) return empty;\n\n\t\tconst targetFiles = targetTreeHash ? this.git.readTree(targetTreeHash) : new Map<string, string>();\n\t\tconst currentFiles = currentTreeHash ? this.git.readTree(currentTreeHash) : new Map<string, string>();\n\n\t\tconst toRestore: string[] = [];\n\t\tfor (const [path, content] of targetFiles) {\n\t\t\tconst current = currentFiles.get(path);\n\t\t\tif (current !== content) {\n\t\t\t\ttoRestore.push(path);\n\t\t\t}\n\t\t}\n\n\t\tconst toDelete: string[] = [];\n\t\tfor (const path of currentFiles.keys()) {\n\t\t\tif (!targetFiles.has(path)) {\n\t\t\t\ttoDelete.push(path);\n\t\t\t}\n\t\t}\n\n\t\tlet filteredRestore = toRestore;\n\t\tlet filteredDelete = toDelete;\n\n\t\tif (options.files) {\n\t\t\tconst fileSet = new Set(options.files);\n\t\t\tfilteredRestore = toRestore.filter((p) => fileSet.has(p));\n\t\t\tfilteredDelete = toDelete.filter((p) => fileSet.has(p));\n\t\t}\n\n\t\tif (filteredRestore.length === 0 && filteredDelete.length === 0) return empty;\n\n\t\tconst dirty: string[] = [];\n\t\tfor (const path of filteredRestore) {\n\t\t\tconst absPath = join(cwd, path);\n\t\t\tif (existsSync(absPath)) {\n\t\t\t\tconst expectedContent = currentFiles.get(path);\n\t\t\t\tconst expectedHash = expectedContent !== undefined ? this.git.hashContent(expectedContent) : null;\n\t\t\t\tlet actualContent: string | null = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst stat = lstatSync(absPath);\n\t\t\t\t\tif (stat.size <= FILE_SIZE_LIMIT) {\n\t\t\t\t\t\tactualContent = readFileSync(absPath, \"utf-8\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst actualHash = actualContent !== null ? this.git.hashContent(actualContent) : null;\n\t\t\t\tif (expectedHash !== null && actualHash !== expectedHash) {\n\t\t\t\t\tdirty.push(path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdirty.sort();\n\n\t\tif (options.preview) {\n\t\t\treturn {\n\t\t\t\trestored: filteredRestore.sort(),\n\t\t\t\tdeleted: filteredDelete.sort(),\n\t\t\t\tskipped: [],\n\t\t\t\tdirty,\n\t\t\t};\n\t\t}\n\n\t\tconst preRollbackFiles = readFilteredWorkingDir(this.git, cwd);\n\t\tconst preRollbackTreeHash = preRollbackFiles.size > 0 ? this.git.writeTree(preRollbackFiles).treeHash : null;\n\n\t\toptions.appendEntry(\"unrevert-point\", {\n\t\t\tpreRollbackTreeHash,\n\t\t\trolledBackToLeaf: options.targetEntryId ?? \"\",\n\t\t\trestoredFiles: filteredRestore,\n\t\t});\n\n\t\tfor (const path of filteredRestore) {\n\t\t\tconst content = targetFiles.get(path);\n\t\t\tif (content === undefined) continue;\n\t\t\tconst absPath = join(cwd, path);\n\t\t\tmkdirSync(dirname(absPath), { recursive: true });\n\t\t\twriteFileSync(absPath, content, \"utf-8\");\n\t\t}\n\n\t\tfor (const path of filteredDelete) {\n\t\t\tconst absPath = join(cwd, path);\n\t\t\tif (existsSync(absPath)) {\n\t\t\t\trmSync(absPath, { force: true });\n\t\t\t}\n\t\t}\n\n\t\tthis.lastCommittedTreeHash = targetTreeHash;\n\n\t\treturn {\n\t\t\trestored: filteredRestore.sort(),\n\t\t\tdeleted: filteredDelete.sort(),\n\t\t\tskipped: [],\n\t\t\tdirty,\n\t\t};\n\t}\n\n\tprivate parseTreeEntriesFromHash(treeHash: string): Map<string, TreeEntry> {\n\t\tconst treeData = this.git.readObject(treeHash);\n\t\tconst entries = new Map<string, TreeEntry>();\n\t\tfor (const line of treeData.split(\"\\n\")) {\n\t\t\tif (!line) continue;\n\t\t\tconst sep = line.indexOf(\"\\0\");\n\t\t\tif (sep === -1) continue;\n\t\t\tconst path = line.slice(0, sep);\n\t\t\tconst hash = line.slice(sep + 1);\n\t\t\tentries.set(path, { path, hash });\n\t\t}\n\t\treturn entries;\n\t}\n}\n\nfunction isOnPathTo(byId: Map<string, SessionEntry>, startId: string, targetId: string): boolean {\n\tlet current: string | null = startId;\n\twhile (current !== null) {\n\t\tif (current === targetId) return true;\n\t\tconst entry = byId.get(current);\n\t\tif (!entry) break;\n\t\tcurrent = entry.parentId;\n\t}\n\treturn false;\n}\n"]}
1
+ {"version":3,"file":"file-snapshot-manager.js","sourceRoot":"","sources":["../../../src/core/file-store/file-snapshot-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport type { CustomEntry, SessionEntry } from \"../session-manager.js\";\nimport type { InternalGit, TreeEntry } from \"./internal-git.js\";\n\nexport interface StepSnapshotData {\n\tbaselineTreeHash: string | null;\n\tsnapshotTreeHash: string;\n\tdiff: { added: string[]; modified: string[]; deleted: string[] } | null;\n\tturnIndex: number;\n}\n\nexport interface ModifiedFileInfo {\n\tpath: string;\n\tstatus: \"added\" | \"modified\" | \"deleted\";\n\tturnIndex: number;\n\tentryId: string;\n}\n\nexport interface FileDiffInfo {\n\tpath: string;\n\toldContent: string | null;\n\tnewContent: string | null;\n\toldHash: string | null;\n\tnewHash: string | null;\n\tunifiedDiff: string;\n}\n\nexport interface RestoreResult {\n\trestored: string[];\n\tdeleted: string[];\n\tskipped: string[];\n\tdirty: string[];\n}\n\nconst FILE_SIZE_LIMIT = 1024 * 1024;\n\nfunction findCanonicalGitRoot(cwd: string): string | null {\n\tlet dir: string;\n\ttry {\n\t\tdir = realpathSync(cwd);\n\t} catch {\n\t\treturn null;\n\t}\n\tfor (;;) {\n\t\tconst gitPath = join(dir, \".git\");\n\t\tif (!existsSync(gitPath)) {\n\t\t\tconst parent = dirname(dir);\n\t\t\tif (parent === dir) return null;\n\t\t\tdir = parent;\n\t\t\tcontinue;\n\t\t}\n\t\tconst stat = lstatSync(gitPath);\n\t\tif (stat.isDirectory()) return dir;\n\t\tif (stat.isFile()) {\n\t\t\tconst content = readFileSync(gitPath, \"utf-8\").trim();\n\t\t\tconst match = content.match(/^gitdir:\\s*(.+)/);\n\t\t\tif (!match) return null;\n\t\t\tconst gitdir = match[1]!.trim();\n\t\t\tif (gitdir.includes(\"/worktrees/\")) {\n\t\t\t\tconst commonPrefix = gitdir.replace(/\\/worktrees\\/[^/]+\\/?$/, \"\");\n\t\t\t\tlet rootDir = commonPrefix;\n\t\t\t\tif (rootDir.endsWith(\"/.git\")) rootDir = rootDir.slice(0, -4);\n\t\t\t\tif (!existsSync(join(rootDir, \".git\"))) return null;\n\t\t\t\treturn realpathSync(rootDir);\n\t\t\t}\n\t\t\tconst parent = dirname(gitdir);\n\t\t\tif (!existsSync(parent)) return null;\n\t\t\treturn parent;\n\t\t}\n\t\treturn null;\n\t}\n}\n\nfunction readFilteredWorkingDir(git: InternalGit, cwd: string): Map<string, string> {\n\tconst all = git.scanWorkingDir(cwd);\n\tconst filtered = new Map<string, string>();\n\tfor (const [path, content] of all) {\n\t\tif (content.length > FILE_SIZE_LIMIT) continue;\n\t\tfiltered.set(path, content);\n\t}\n\treturn filtered;\n}\n\nfunction generateUnifiedDiff(oldContent: string | null, newContent: string | null, filePath: string): string {\n\tconst oldLines = oldContent === null ? [] : oldContent.split(\"\\n\");\n\tconst newLines = newContent === null ? [] : newContent.split(\"\\n\");\n\tconst lines: string[] = [];\n\tlines.push(`--- ${filePath}`);\n\tlines.push(`+++ ${filePath}`);\n\n\tlet i = 0;\n\tlet j = 0;\n\tconst hunks: string[] = [];\n\n\twhile (i < oldLines.length || j < newLines.length) {\n\t\tif (i < oldLines.length && j < newLines.length && oldLines[i] === newLines[j]) {\n\t\t\ti++;\n\t\t\tj++;\n\t\t} else {\n\t\t\tconst hunkStart = Math.max(0, i - 3);\n\t\t\tconst hunkOldEnd = Math.min(oldLines.length, i + 3);\n\t\t\tconst hunkNewEnd = Math.min(newLines.length, j + 3);\n\n\t\t\tconst removed: string[] = [];\n\t\t\tconst added: string[] = [];\n\t\t\tlet oi = i;\n\t\t\tlet ni = j;\n\n\t\t\twhile (oi < hunkOldEnd || ni < hunkNewEnd) {\n\t\t\t\tif (oi < hunkOldEnd && ni < hunkNewEnd && oldLines[oi] === newLines[ni]) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (oi < hunkOldEnd) {\n\t\t\t\t\tremoved.push(oldLines[oi]);\n\t\t\t\t\toi++;\n\t\t\t\t}\n\t\t\t\tif (ni < hunkNewEnd && (oi >= hunkOldEnd || newLines[ni] !== oldLines[oi])) {\n\t\t\t\t\tadded.push(newLines[ni]);\n\t\t\t\t\tni++;\n\t\t\t\t} else if (ni < hunkNewEnd) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst contextStart = Math.max(0, i - 2);\n\t\t\tconst contextLines: string[] = [];\n\t\t\tfor (let c = contextStart; c < i; c++) {\n\t\t\t\tcontextLines.push(` ${oldLines[c]}`);\n\t\t\t}\n\t\t\tfor (const r of removed) {\n\t\t\t\tcontextLines.push(`-${r}`);\n\t\t\t}\n\t\t\tfor (const a of added) {\n\t\t\t\tcontextLines.push(`+${a}`);\n\t\t\t}\n\t\t\tconst contextEnd = Math.min(newLines.length, ni + 2);\n\t\t\tfor (let c = ni; c < contextEnd; c++) {\n\t\t\t\tcontextLines.push(` ${newLines[c]}`);\n\t\t\t}\n\n\t\t\thunks.push(`@@ -${i + 1},${oi - i} +${j + 1},${ni - j} @@`);\n\t\t\thunks.push(...contextLines);\n\n\t\t\ti = oi;\n\t\t\tj = ni;\n\t\t}\n\t}\n\n\tif (hunks.length === 0) return \"\";\n\tlines.push(...hunks);\n\treturn lines.join(\"\\n\");\n}\n\nexport interface BatchDiffResult {\n\tfiles: Array<{\n\t\tpath: string;\n\t\tstatus: \"added\" | \"modified\" | \"deleted\";\n\t\tdiff: FileDiffInfo | null;\n\t}>;\n\tsummary: {\n\t\ttotalFiles: number;\n\t\tadded: number;\n\t\tmodified: number;\n\t\tdeleted: number;\n\t};\n}\n\nexport interface FileHistoryEntry {\n\tentryId: string;\n\tturnIndex: number;\n\ttimestamp: string;\n\tstatus: \"added\" | \"modified\" | \"deleted\";\n\tsnapshotHash: string;\n\tpreviousHash: string | null;\n}\n\ninterface SnapshotWithEntryId extends StepSnapshotData {\n\tentryId: string;\n}\n\nexport class FileSnapshotManager {\n\tprivate git: InternalGit;\n\tprivate sessionStartTreeHash: string | null = null;\n\tprivate lastCommittedTreeHash: string | null = null;\n\tprivate turnIndex = 0;\n\tprivate snapshotIndex: Map<string, SnapshotWithEntryId> = new Map();\n\tprivate turnIndexMap: Map<number, string> = new Map();\n\n\tconstructor(git: InternalGit) {\n\t\tthis.git = git;\n\t}\n\n\tasync initialize(cwd: string): Promise<void> {\n\t\tthis.sessionStartTreeHash = null;\n\t\tthis.lastCommittedTreeHash = null;\n\t\tthis.turnIndex = 0;\n\t\tthis.snapshotIndex.clear();\n\t\tthis.turnIndexMap.clear();\n\n\t\tconst files = readFilteredWorkingDir(this.git, cwd);\n\t\tif (files.size > 0) {\n\t\t\tthis.sessionStartTreeHash = this.git.writeTree(files).treeHash;\n\t\t}\n\t}\n\n\tonTurnEnd(cwd: string, turnIndex: number, appendEntry: (type: string, data: unknown) => string | undefined): void {\n\t\tconst files = readFilteredWorkingDir(this.git, cwd);\n\t\tconst { treeHash: snapshotTreeHash, entries: newEntries } = this.git.writeTree(files);\n\n\t\tconst compareTo = this.lastCommittedTreeHash ?? this.sessionStartTreeHash;\n\t\tconst oldEntries = compareTo ? this.parseTreeEntriesFromHash(compareTo) : new Map<string, TreeEntry>();\n\n\t\tconst stepDiff = this.git.computeDiff(oldEntries, newEntries);\n\t\tconst hasChanges = stepDiff.added.length > 0 || stepDiff.modified.length > 0 || stepDiff.deleted.length > 0;\n\n\t\tif (hasChanges) {\n\t\t\tconst entryId = appendEntry(\"step-snapshot\", {\n\t\t\t\tbaselineTreeHash: compareTo,\n\t\t\t\tsnapshotTreeHash,\n\t\t\t\tdiff: stepDiff,\n\t\t\t\tturnIndex,\n\t\t\t});\n\n\t\t\tif (entryId) {\n\t\t\t\tthis.snapshotIndex.set(entryId, {\n\t\t\t\t\tbaselineTreeHash: compareTo,\n\t\t\t\t\tsnapshotTreeHash,\n\t\t\t\t\tdiff: stepDiff,\n\t\t\t\t\tturnIndex,\n\t\t\t\t\tentryId,\n\t\t\t\t});\n\t\t\t\tthis.turnIndexMap.set(turnIndex, entryId);\n\t\t\t}\n\t\t\tthis.lastCommittedTreeHash = snapshotTreeHash;\n\t\t}\n\n\t\tthis.turnIndex = turnIndex + 1;\n\t}\n\n\trebuildIndex(entries: SessionEntry[]): void {\n\t\tthis.snapshotIndex.clear();\n\t\tthis.turnIndexMap.clear();\n\t\tthis.lastCommittedTreeHash = null;\n\t\tthis.sessionStartTreeHash = null;\n\t\tthis.turnIndex = 0;\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.type !== \"custom\") continue;\n\t\t\tconst custom = entry as CustomEntry;\n\t\t\tif (custom.customType !== \"step-snapshot\") continue;\n\n\t\t\tconst data = custom.data as StepSnapshotData;\n\t\t\tif (!data) continue;\n\n\t\t\tthis.snapshotIndex.set(entry.id, {\n\t\t\t\t...data,\n\t\t\t\tentryId: entry.id,\n\t\t\t});\n\t\t\tthis.turnIndexMap.set(data.turnIndex, entry.id);\n\t\t\tthis.lastCommittedTreeHash = data.snapshotTreeHash;\n\t\t\tthis.turnIndex = Math.max(this.turnIndex, data.turnIndex + 1);\n\t\t}\n\t}\n\n\tgetLatestSnapshotOnPath(entries: SessionEntry[], leafId: string | null): StepSnapshotData | null {\n\t\tif (!leafId) return null;\n\n\t\tconst byId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\n\t\tconst snapshots: StepSnapshotData[] = [];\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.type !== \"custom\") continue;\n\t\t\tconst custom = entry as CustomEntry;\n\t\t\tif (custom.customType !== \"step-snapshot\") continue;\n\t\t\tif (!isOnPathTo(byId, leafId, entry.id)) continue;\n\t\t\tsnapshots.push(custom.data as StepSnapshotData);\n\t\t}\n\n\t\treturn snapshots.length > 0 ? snapshots[snapshots.length - 1] : null;\n\t}\n\n\tgetSnapshotAtTurn(turnIndex: number): StepSnapshotData | null {\n\t\tconst entryId = this.turnIndexMap.get(turnIndex);\n\t\tif (!entryId) return null;\n\t\tconst snap = this.snapshotIndex.get(entryId);\n\t\tif (!snap) return null;\n\t\treturn {\n\t\t\tbaselineTreeHash: snap.baselineTreeHash,\n\t\t\tsnapshotTreeHash: snap.snapshotTreeHash,\n\t\t\tdiff: snap.diff,\n\t\t\tturnIndex: snap.turnIndex,\n\t\t};\n\t}\n\n\tgetSnapshotAtEntry(entryId: string): StepSnapshotData | null {\n\t\tconst snap = this.snapshotIndex.get(entryId);\n\t\tif (!snap) return null;\n\t\treturn {\n\t\t\tbaselineTreeHash: snap.baselineTreeHash,\n\t\t\tsnapshotTreeHash: snap.snapshotTreeHash,\n\t\t\tdiff: snap.diff,\n\t\t\tturnIndex: snap.turnIndex,\n\t\t};\n\t}\n\n\tgetModifiedFiles(options?: { fromEntryId?: string; toEntryId?: string }): ModifiedFileInfo[] {\n\t\tconst snapshots = [...this.snapshotIndex.values()].sort((a, b) => a.turnIndex - b.turnIndex);\n\n\t\tconst fromIdx = options?.fromEntryId ? snapshots.findIndex((s) => s.entryId === options.fromEntryId) : -1;\n\t\tconst toIdx = options?.toEntryId ? snapshots.findIndex((s) => s.entryId === options.toEntryId) : -1;\n\n\t\tconst start = fromIdx === -1 ? 0 : fromIdx;\n\t\tconst end = toIdx === -1 ? snapshots.length - 1 : toIdx;\n\n\t\tif (start > end || snapshots.length === 0) return [];\n\n\t\tconst fileMap = new Map<string, ModifiedFileInfo>();\n\t\tfor (let i = start; i <= end; i++) {\n\t\t\tconst snap = snapshots[i];\n\t\t\tif (!snap?.diff) continue;\n\n\t\t\tfor (const path of snap.diff.added) {\n\t\t\t\tif (!fileMap.has(path)) {\n\t\t\t\t\tfileMap.set(path, { path, status: \"added\", turnIndex: snap.turnIndex, entryId: snap.entryId });\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const path of snap.diff.modified) {\n\t\t\t\tif (!fileMap.has(path)) {\n\t\t\t\t\tfileMap.set(path, { path, status: \"modified\", turnIndex: snap.turnIndex, entryId: snap.entryId });\n\t\t\t\t} else {\n\t\t\t\t\tconst existing = fileMap.get(path)!;\n\t\t\t\t\tif (existing.status !== \"added\") {\n\t\t\t\t\t\texisting.status = \"modified\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const path of snap.diff.deleted) {\n\t\t\t\tif (!fileMap.has(path)) {\n\t\t\t\t\tfileMap.set(path, { path, status: \"deleted\", turnIndex: snap.turnIndex, entryId: snap.entryId });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn [...fileMap.values()].sort((a, b) => a.path.localeCompare(b.path));\n\t}\n\n\tgetFileDiff(options: { filePath: string; fromEntryId?: string; toEntryId?: string }): FileDiffInfo | null {\n\t\tconst snapshots = [...this.snapshotIndex.values()].sort((a, b) => a.turnIndex - b.turnIndex);\n\n\t\tconst fromHash = options.fromEntryId\n\t\t\t? (snapshots.find((s) => s.entryId === options.fromEntryId)?.snapshotTreeHash ?? null)\n\t\t\t: this.sessionStartTreeHash;\n\t\tconst toHash = options.toEntryId\n\t\t\t? (snapshots.find((s) => s.entryId === options.toEntryId)?.snapshotTreeHash ?? null)\n\t\t\t: (this.lastCommittedTreeHash ?? this.sessionStartTreeHash);\n\n\t\tif (!fromHash && !toHash) return null;\n\n\t\tconst fromFiles = fromHash ? this.git.readTree(fromHash) : new Map<string, string>();\n\t\tconst toFiles = toHash ? this.git.readTree(toHash) : new Map<string, string>();\n\n\t\tconst oldContent = fromFiles.get(options.filePath) ?? null;\n\t\tconst newContent = toFiles.get(options.filePath) ?? null;\n\n\t\tif (oldContent === null && newContent === null) return null;\n\n\t\treturn {\n\t\t\tpath: options.filePath,\n\t\t\toldContent,\n\t\t\tnewContent,\n\t\t\toldHash: oldContent !== null ? this.git.hashContent(oldContent) : null,\n\t\t\tnewHash: newContent !== null ? this.git.hashContent(newContent) : null,\n\t\t\tunifiedDiff: generateUnifiedDiff(oldContent, newContent, options.filePath),\n\t\t};\n\t}\n\n\tgetBatchDiffs(options?: { fromEntryId?: string; toEntryId?: string }): BatchDiffResult {\n\t\tconst files = this.getModifiedFiles({\n\t\t\tfromEntryId: options?.fromEntryId,\n\t\t\ttoEntryId: options?.toEntryId,\n\t\t});\n\n\t\tlet added = 0;\n\t\tlet modified = 0;\n\t\tlet deleted = 0;\n\n\t\tconst resultFiles = files.map((f) => {\n\t\t\tif (f.status === \"added\") added++;\n\t\t\telse if (f.status === \"modified\") modified++;\n\t\t\telse deleted++;\n\n\t\t\tlet diff: FileDiffInfo | null = null;\n\t\t\ttry {\n\t\t\t\tdiff = this.getFileDiff({\n\t\t\t\t\tfilePath: f.path,\n\t\t\t\t\tfromEntryId: options?.fromEntryId,\n\t\t\t\t\ttoEntryId: options?.toEntryId,\n\t\t\t\t});\n\t\t\t} catch {}\n\n\t\t\treturn { path: f.path, status: f.status, diff };\n\t\t});\n\n\t\treturn {\n\t\t\tfiles: resultFiles,\n\t\t\tsummary: {\n\t\t\t\ttotalFiles: files.length,\n\t\t\t\tadded,\n\t\t\t\tmodified,\n\t\t\t\tdeleted,\n\t\t\t},\n\t\t};\n\t}\n\n\tgetFileHistory(options: { filePath: string }): FileHistoryEntry[] {\n\t\tconst snapshots = [...this.snapshotIndex.values()].sort((a, b) => a.turnIndex - b.turnIndex);\n\t\tconst history: FileHistoryEntry[] = [];\n\n\t\tfor (const snap of snapshots) {\n\t\t\tif (!snap.diff) continue;\n\n\t\t\tlet status: \"added\" | \"modified\" | \"deleted\" | null = null;\n\t\t\tif (snap.diff.added.includes(options.filePath)) {\n\t\t\t\tstatus = \"added\";\n\t\t\t} else if (snap.diff.modified.includes(options.filePath)) {\n\t\t\t\tstatus = \"modified\";\n\t\t\t} else if (snap.diff.deleted.includes(options.filePath)) {\n\t\t\t\tstatus = \"deleted\";\n\t\t\t}\n\n\t\t\tif (status) {\n\t\t\t\tconst previousHash = snap.baselineTreeHash ?? null;\n\t\t\t\thistory.push({\n\t\t\t\t\tentryId: snap.entryId,\n\t\t\t\t\tturnIndex: snap.turnIndex,\n\t\t\t\t\ttimestamp: new Date(0).toISOString(),\n\t\t\t\t\tstatus,\n\t\t\t\t\tsnapshotHash: snap.snapshotTreeHash,\n\t\t\t\t\tpreviousHash,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn history;\n\t}\n\n\tasync restoreFiles(\n\t\tcwd: string,\n\t\toptions: {\n\t\t\ttargetEntryId?: string;\n\t\t\tsnapshotHash?: string;\n\t\t\tfiles?: string[];\n\t\t\tpreview?: boolean;\n\t\t\tcurrentLeafId?: string | null;\n\t\t\tentries: SessionEntry[];\n\t\t\tappendEntry: (type: string, data: unknown) => void;\n\t\t},\n\t): Promise<RestoreResult> {\n\t\tconst empty: RestoreResult = { restored: [], deleted: [], skipped: [], dirty: [] };\n\n\t\tlet targetTreeHash: string | null;\n\t\tif (options.snapshotHash) {\n\t\t\ttargetTreeHash = options.snapshotHash;\n\t\t} else if (options.targetEntryId) {\n\t\t\tconst snap = this.snapshotIndex.get(options.targetEntryId);\n\t\t\tif (snap) {\n\t\t\t\ttargetTreeHash = snap.snapshotTreeHash;\n\t\t\t} else {\n\t\t\t\tconst pathSnap = this.getLatestSnapshotOnPath(options.entries, options.targetEntryId);\n\t\t\t\ttargetTreeHash = pathSnap?.snapshotTreeHash ?? null;\n\t\t\t}\n\t\t} else {\n\t\t\ttargetTreeHash = this.sessionStartTreeHash ?? null;\n\t\t}\n\n\t\tlet currentTreeHash: string | null;\n\t\tif (options.currentLeafId !== undefined) {\n\t\t\tconst currentSnapshot = this.getLatestSnapshotOnPath(options.entries, options.currentLeafId);\n\t\t\tcurrentTreeHash = currentSnapshot?.snapshotTreeHash ?? null;\n\t\t} else {\n\t\t\tcurrentTreeHash = this.lastCommittedTreeHash ?? this.sessionStartTreeHash;\n\t\t}\n\n\t\tif (targetTreeHash === currentTreeHash) return empty;\n\n\t\t// Safety guard: if targetTreeHash is null, we cannot determine the target state.\n\t\t// Treating null as \"no files\" would delete everything on disk — a data-loss bug.\n\t\t// Instead, bail out safely.\n\t\tif (targetTreeHash === null) {\n\t\t\treturn empty;\n\t\t}\n\n\t\tconst targetFiles = this.git.readTree(targetTreeHash);\n\t\tconst currentFiles = currentTreeHash ? this.git.readTree(currentTreeHash) : new Map<string, string>();\n\n\t\tconst toRestore: string[] = [];\n\t\tfor (const [path, content] of targetFiles) {\n\t\t\tconst current = currentFiles.get(path);\n\t\t\tif (current !== content) {\n\t\t\t\ttoRestore.push(path);\n\t\t\t}\n\t\t}\n\n\t\tconst toDelete: string[] = [];\n\t\tfor (const path of currentFiles.keys()) {\n\t\t\tif (!targetFiles.has(path)) {\n\t\t\t\ttoDelete.push(path);\n\t\t\t}\n\t\t}\n\n\t\tlet filteredRestore = toRestore;\n\t\tlet filteredDelete = toDelete;\n\n\t\tif (options.files) {\n\t\t\tconst fileSet = new Set(options.files);\n\t\t\tfilteredRestore = toRestore.filter((p) => fileSet.has(p));\n\t\t\tfilteredDelete = toDelete.filter((p) => fileSet.has(p));\n\t\t}\n\n\t\tif (filteredRestore.length === 0 && filteredDelete.length === 0) return empty;\n\n\t\tconst dirty: string[] = [];\n\t\tfor (const path of filteredRestore) {\n\t\t\tconst absPath = join(cwd, path);\n\t\t\tif (existsSync(absPath)) {\n\t\t\t\tconst expectedContent = currentFiles.get(path);\n\t\t\t\tconst expectedHash = expectedContent !== undefined ? this.git.hashContent(expectedContent) : null;\n\t\t\t\tlet actualContent: string | null = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst stat = lstatSync(absPath);\n\t\t\t\t\tif (stat.size <= FILE_SIZE_LIMIT) {\n\t\t\t\t\t\tactualContent = readFileSync(absPath, \"utf-8\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst actualHash = actualContent !== null ? this.git.hashContent(actualContent) : null;\n\t\t\t\tif (expectedHash !== null && actualHash !== expectedHash) {\n\t\t\t\t\tdirty.push(path);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdirty.sort();\n\n\t\tif (options.preview) {\n\t\t\treturn {\n\t\t\t\trestored: filteredRestore.sort(),\n\t\t\t\tdeleted: filteredDelete.sort(),\n\t\t\t\tskipped: [],\n\t\t\t\tdirty,\n\t\t\t};\n\t\t}\n\n\t\tconst preRollbackFiles = readFilteredWorkingDir(this.git, cwd);\n\t\tconst preRollbackTreeHash = preRollbackFiles.size > 0 ? this.git.writeTree(preRollbackFiles).treeHash : null;\n\n\t\toptions.appendEntry(\"unrevert-point\", {\n\t\t\tpreRollbackTreeHash,\n\t\t\trolledBackToLeaf: options.targetEntryId ?? \"\",\n\t\t\trestoredFiles: filteredRestore,\n\t\t});\n\n\t\tfor (const path of filteredRestore) {\n\t\t\tconst content = targetFiles.get(path);\n\t\t\tif (content === undefined) continue;\n\t\t\tconst absPath = join(cwd, path);\n\t\t\tmkdirSync(dirname(absPath), { recursive: true });\n\t\t\twriteFileSync(absPath, content, \"utf-8\");\n\t\t}\n\n\t\tfor (const path of filteredDelete) {\n\t\t\tconst absPath = join(cwd, path);\n\t\t\tif (existsSync(absPath)) {\n\t\t\t\trmSync(absPath, { force: true });\n\t\t\t}\n\t\t}\n\n\t\tthis.lastCommittedTreeHash = targetTreeHash;\n\n\t\treturn {\n\t\t\trestored: filteredRestore.sort(),\n\t\t\tdeleted: filteredDelete.sort(),\n\t\t\tskipped: [],\n\t\t\tdirty,\n\t\t};\n\t}\n\n\tprivate parseTreeEntriesFromHash(treeHash: string): Map<string, TreeEntry> {\n\t\tconst treeData = this.git.readObject(treeHash);\n\t\tconst entries = new Map<string, TreeEntry>();\n\t\tfor (const line of treeData.split(\"\\n\")) {\n\t\t\tif (!line) continue;\n\t\t\tconst sep = line.indexOf(\"\\0\");\n\t\t\tif (sep === -1) continue;\n\t\t\tconst path = line.slice(0, sep);\n\t\t\tconst hash = line.slice(sep + 1);\n\t\t\tentries.set(path, { path, hash });\n\t\t}\n\t\treturn entries;\n\t}\n}\n\nfunction isOnPathTo(byId: Map<string, SessionEntry>, startId: string, targetId: string): boolean {\n\tlet current: string | null = startId;\n\twhile (current !== null) {\n\t\tif (current === targetId) return true;\n\t\tconst entry = byId.get(current);\n\t\tif (!entry) break;\n\t\tcurrent = entry.parentId;\n\t}\n\treturn false;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"rpc-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AA8B/E,YAAY,EACX,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,QAAQ,EACR,eAAe,EACf,OAAO,GACP,MAAM,gBAAgB,CAAC;AAExB;;;GAGG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAuqCjF","sourcesContent":["/**\n * RPC mode: Headless operation with JSON stdin/stdout protocol.\n *\n * Used for embedding the agent in other applications.\n * Receives commands as JSON on stdin, outputs events and responses as JSON on stdout.\n *\n * Protocol:\n * - Commands: JSON objects with `type` field, optional `id` for correlation\n * - Responses: JSON objects with `type: \"response\"`, `command`, `success`, and optional `data`/`error`\n * - Events: AgentSessionEvent objects streamed as they occur\n * - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response\n */\n\nimport * as crypto from \"node:crypto\";\nimport type { AgentMessage } from \"@dyyz1993/pi-agent-core\";\nimport type { AgentSessionRuntime } from \"../../core/agent-session-runtime.js\";\nimport { discoverAgents, getBuiltinAgents } from \"../../core/agent-types.js\";\nimport { DEFAULT_TIER_ALIASES } from \"../../core/defaults.js\";\nimport { ChannelManager } from \"../../core/extensions/channel-manager.js\";\nimport type { ChannelDataMessage } from \"../../core/extensions/channel-types.js\";\nimport type {\n\tExtensionUIContext,\n\tExtensionUIDialogOptions,\n\tExtensionWidgetOptions,\n\tWorkingIndicatorOptions,\n} from \"../../core/extensions/index.js\";\nimport { resolveModelAlias } from \"../../core/model-resolver.js\";\nimport { takeOverStdout, writeRawStdout } from \"../../core/output-guard.js\";\nimport { killTrackedDetachedChildren } from \"../../utils/shell.js\";\nimport { type Theme, theme } from \"../interactive/theme/theme.js\";\nimport { attachJsonlLineReader, serializeJsonLine } from \"./jsonl.js\";\nimport type {\n\tRpcCommand,\n\tRpcExtension,\n\tRpcExtensionUIRequest,\n\tRpcExtensionUIResponse,\n\tRpcMcpServer,\n\tRpcResponse,\n\tRpcSessionState,\n\tRpcSkill,\n\tRpcSlashCommand,\n\tRpcTool,\n} from \"./rpc-types.js\";\n\n// Re-export types for consumers\nexport type {\n\tRpcCommand,\n\tRpcExtension,\n\tRpcExtensionUIRequest,\n\tRpcExtensionUIResponse,\n\tRpcMcpServer,\n\tRpcResponse,\n\tRpcSessionState,\n\tRpcSkill,\n\tRpcSlashCommand,\n\tRpcTool,\n} from \"./rpc-types.js\";\n\n/**\n * Run in RPC mode.\n * Listens for JSON commands on stdin, outputs events and responses on stdout.\n */\nexport async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<never> {\n\ttakeOverStdout();\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\n\tconst output = (obj: RpcResponse | RpcExtensionUIRequest | object) => {\n\t\twriteRawStdout(serializeJsonLine(obj));\n\t};\n\n\tconst channelManager = new ChannelManager((message: ChannelDataMessage) => {\n\t\toutput(message);\n\t});\n\n\tconst success = <T extends RpcCommand[\"type\"]>(\n\t\tid: string | undefined,\n\t\tcommand: T,\n\t\tdata?: object | null,\n\t): RpcResponse => {\n\t\tif (data === undefined) {\n\t\t\treturn { id, type: \"response\", command, success: true } as RpcResponse;\n\t\t}\n\t\treturn { id, type: \"response\", command, success: true, data } as RpcResponse;\n\t};\n\n\tconst error = (id: string | undefined, command: string, message: string): RpcResponse => {\n\t\treturn { id, type: \"response\", command, success: false, error: message };\n\t};\n\n\t// Pending extension UI requests waiting for response\n\tconst pendingExtensionRequests = new Map<\n\t\tstring,\n\t\t{ resolve: (value: any) => void; reject: (error: Error) => void }\n\t>();\n\n\t// Shutdown request flag\n\tlet shutdownRequested = false;\n\tlet shuttingDown = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\t/** Helper for dialog methods with signal/timeout support */\n\tfunction createDialogPromise<T>(\n\t\topts: ExtensionUIDialogOptions | undefined,\n\t\tdefaultValue: T,\n\t\trequest: Record<string, unknown>,\n\t\tparseResponse: (response: RpcExtensionUIResponse) => T,\n\t): Promise<T> {\n\t\tif (opts?.signal?.aborted) return Promise.resolve(defaultValue);\n\n\t\tconst id = crypto.randomUUID();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n\t\t\tconst cleanup = () => {\n\t\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\t\topts?.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tpendingExtensionRequests.delete(id);\n\t\t\t};\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tcleanup();\n\t\t\t\tresolve(defaultValue);\n\t\t\t};\n\t\t\topts?.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\tif (opts?.timeout) {\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve(defaultValue);\n\t\t\t\t}, opts.timeout);\n\t\t\t}\n\n\t\t\tpendingExtensionRequests.set(id, {\n\t\t\t\tresolve: (response: RpcExtensionUIResponse) => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve(parseResponse(response));\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t});\n\t\t\toutput({ type: \"extension_ui_request\", id, ...request } as RpcExtensionUIRequest);\n\t\t});\n\t}\n\n\t/**\n\t * Create an extension UI context that uses the RPC protocol.\n\t */\n\tconst createExtensionUIContext = (): ExtensionUIContext => ({\n\t\tselect: (title, options, opts) =>\n\t\t\tcreateDialogPromise(\n\t\t\t\topts,\n\t\t\t\tundefined,\n\t\t\t\t{ method: \"select\", title, options, multiple: opts?.multiple, timeout: opts?.timeout },\n\t\t\t\t(r) => (\"cancelled\" in r && r.cancelled ? undefined : \"value\" in r ? r.value : undefined),\n\t\t\t),\n\n\t\tconfirm: (title, message, opts) =>\n\t\t\tcreateDialogPromise(opts, false, { method: \"confirm\", title, message, timeout: opts?.timeout }, (r) =>\n\t\t\t\t\"cancelled\" in r && r.cancelled ? false : \"confirmed\" in r ? r.confirmed : false,\n\t\t\t),\n\n\t\tinput: (title, placeholder, opts) =>\n\t\t\tcreateDialogPromise(opts, undefined, { method: \"input\", title, placeholder, timeout: opts?.timeout }, (r) =>\n\t\t\t\t\"cancelled\" in r && r.cancelled ? undefined : \"value\" in r ? r.value : undefined,\n\t\t\t),\n\n\t\tnotify(message: string, type?: \"info\" | \"warning\" | \"error\"): void {\n\t\t\t// Fire and forget - no response needed\n\t\t\toutput({\n\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tmethod: \"notify\",\n\t\t\t\tmessage,\n\t\t\t\tnotifyType: type,\n\t\t\t} as RpcExtensionUIRequest);\n\t\t},\n\n\t\tonTerminalInput(): () => void {\n\t\t\t// Raw terminal input not supported in RPC mode\n\t\t\treturn () => {};\n\t\t},\n\n\t\tsetStatus(key: string, text: string | undefined): void {\n\t\t\t// Fire and forget - no response needed\n\t\t\toutput({\n\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tmethod: \"setStatus\",\n\t\t\t\tstatusKey: key,\n\t\t\t\tstatusText: text,\n\t\t\t} as RpcExtensionUIRequest);\n\t\t},\n\n\t\tsetWorkingMessage(_message?: string): void {\n\t\t\t// Working message not supported in RPC mode - requires TUI loader access\n\t\t},\n\n\t\tsetWorkingVisible(_visible: boolean): void {\n\t\t\t// Working visibility not supported in RPC mode - requires TUI loader access\n\t\t},\n\n\t\tsetWorkingIndicator(_options?: WorkingIndicatorOptions): void {\n\t\t\t// Working indicator customization not supported in RPC mode - requires TUI loader access\n\t\t},\n\n\t\tsetHiddenThinkingLabel(_label?: string): void {\n\t\t\t// Hidden thinking label not supported in RPC mode - requires TUI message rendering access\n\t\t},\n\n\t\tsetWidget(key: string, content: unknown, options?: ExtensionWidgetOptions): void {\n\t\t\t// Only support string arrays in RPC mode - factory functions are ignored\n\t\t\tif (content === undefined || Array.isArray(content)) {\n\t\t\t\toutput({\n\t\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\t\tmethod: \"setWidget\",\n\t\t\t\t\twidgetKey: key,\n\t\t\t\t\twidgetLines: content as string[] | undefined,\n\t\t\t\t\twidgetPlacement: options?.placement,\n\t\t\t\t} as RpcExtensionUIRequest);\n\t\t\t}\n\t\t\t// Component factories are not supported in RPC mode - would need TUI access\n\t\t},\n\n\t\tsetFooter(_factory: unknown): void {\n\t\t\t// Custom footer not supported in RPC mode - requires TUI access\n\t\t},\n\n\t\tsetHeader(_factory: unknown): void {\n\t\t\t// Custom header not supported in RPC mode - requires TUI access\n\t\t},\n\n\t\tsetTitle(title: string): void {\n\t\t\t// Fire and forget - host can implement terminal title control\n\t\t\toutput({\n\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tmethod: \"setTitle\",\n\t\t\t\ttitle,\n\t\t\t} as RpcExtensionUIRequest);\n\t\t},\n\n\t\tasync custom() {\n\t\t\t// Custom UI not supported in RPC mode\n\t\t\treturn undefined as never;\n\t\t},\n\n\t\tpasteToEditor(text: string): void {\n\t\t\t// Paste handling not supported in RPC mode - falls back to setEditorText\n\t\t\tthis.setEditorText(text);\n\t\t},\n\n\t\tsetEditorText(text: string): void {\n\t\t\t// Fire and forget - host can implement editor control\n\t\t\toutput({\n\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tmethod: \"set_editor_text\",\n\t\t\t\ttext,\n\t\t\t} as RpcExtensionUIRequest);\n\t\t},\n\n\t\tgetEditorText(): string {\n\t\t\t// Synchronous method can't wait for RPC response\n\t\t\t// Host should track editor state locally if needed\n\t\t\treturn \"\";\n\t\t},\n\n\t\tasync editor(title: string, prefill?: string): Promise<string | undefined> {\n\t\t\tconst id = crypto.randomUUID();\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tpendingExtensionRequests.set(id, {\n\t\t\t\t\tresolve: (response: RpcExtensionUIResponse) => {\n\t\t\t\t\t\tif (\"cancelled\" in response && response.cancelled) {\n\t\t\t\t\t\t\tresolve(undefined);\n\t\t\t\t\t\t} else if (\"value\" in response) {\n\t\t\t\t\t\t\tresolve(response.value);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve(undefined);\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\treject,\n\t\t\t\t});\n\t\t\t\toutput({ type: \"extension_ui_request\", id, method: \"editor\", title, prefill } as RpcExtensionUIRequest);\n\t\t\t});\n\t\t},\n\n\t\taddAutocompleteProvider(): void {\n\t\t\t// Autocomplete provider composition is not supported in RPC mode\n\t\t},\n\n\t\tsetEditorComponent(): void {\n\t\t\t// Custom editor components not supported in RPC mode\n\t\t},\n\n\t\tgetEditorComponent() {\n\t\t\t// Custom editor components not supported in RPC mode\n\t\t\treturn undefined;\n\t\t},\n\n\t\tget theme() {\n\t\t\treturn theme;\n\t\t},\n\n\t\tgetAllThemes() {\n\t\t\treturn [];\n\t\t},\n\n\t\tgetTheme(_name: string) {\n\t\t\treturn undefined;\n\t\t},\n\n\t\tsetTheme(_theme: string | Theme) {\n\t\t\t// Theme switching not supported in RPC mode\n\t\t\treturn { success: false, error: \"Theme switching not supported in RPC mode\" };\n\t\t},\n\n\t\tgetToolsExpanded() {\n\t\t\t// Tool expansion not supported in RPC mode - no TUI\n\t\t\treturn false;\n\t\t},\n\n\t\tsetToolsExpanded(_expanded: boolean) {\n\t\t\t// Tool expansion not supported in RPC mode - no TUI\n\t\t},\n\t});\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tlet rebindDone: Promise<void>;\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tuiContext: createExtensionUIContext(),\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (options) => runtimeHost.newSession(options),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, options) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: options?.summarize,\n\t\t\t\t\t\tcustomInstructions: options?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: options?.replaceInstructions,\n\t\t\t\t\t\tlabel: options?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, options) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, options);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tshutdownHandler: () => {\n\t\t\t\tshutdownRequested = true;\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\toutput({ type: \"extension_error\", extensionPath: err.extensionPath, event: err.event, error: err.error });\n\t\t\t},\n\t\t\tregisterChannel: (name: string) => channelManager.register(name),\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\toutput(event);\n\t\t});\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid shutdown(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\trebindDone = rebindSession();\n\tregisterSignalHandlers();\n\toutput({ type: \"ready\" });\n\n\t// Handle a single command\n\tconst handleCommand = async (command: RpcCommand): Promise<RpcResponse | undefined> => {\n\t\tawait rebindDone;\n\t\tconst id = command.id;\n\n\t\tswitch (command.type) {\n\t\t\t// =================================================================\n\t\t\t// Prompting\n\t\t\t// =================================================================\n\n\t\t\tcase \"prompt\": {\n\t\t\t\t// Start prompt handling immediately, but emit the authoritative response only after\n\t\t\t\t// prompt preflight succeeds. Queued and immediately handled prompts also count as success.\n\t\t\t\tlet preflightSucceeded = false;\n\t\t\t\tvoid session\n\t\t\t\t\t.prompt(command.message, {\n\t\t\t\t\t\timages: command.images,\n\t\t\t\t\t\tstreamingBehavior: command.streamingBehavior,\n\t\t\t\t\t\tsource: \"rpc\",\n\t\t\t\t\t\tpreflightResult: (didSucceed) => {\n\t\t\t\t\t\t\tif (didSucceed) {\n\t\t\t\t\t\t\t\tpreflightSucceeded = true;\n\t\t\t\t\t\t\t\toutput(success(id, \"prompt\"));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tif (!preflightSucceeded) {\n\t\t\t\t\t\t\toutput(error(id, \"prompt\", e.message));\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tcase \"steer\": {\n\t\t\t\tawait session.steer(command.message, command.images);\n\t\t\t\treturn success(id, \"steer\");\n\t\t\t}\n\n\t\t\tcase \"follow_up\": {\n\t\t\t\tawait session.followUp(command.message, command.images);\n\t\t\t\treturn success(id, \"follow_up\");\n\t\t\t}\n\n\t\t\tcase \"abort\": {\n\t\t\t\tawait session.abort();\n\t\t\t\treturn success(id, \"abort\");\n\t\t\t}\n\n\t\t\tcase \"new_session\": {\n\t\t\t\tconst options = command.parentSession ? { parentSession: command.parentSession } : undefined;\n\t\t\t\tconst result = await runtimeHost.newSession(options);\n\t\t\t\tif (!result.cancelled) {\n\t\t\t\t\tawait rebindSession();\n\t\t\t\t}\n\t\t\t\treturn success(id, \"new_session\", result);\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// State\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_state\": {\n\t\t\t\tconst state: RpcSessionState = {\n\t\t\t\t\tmodel: session.model,\n\t\t\t\t\tthinkingLevel: session.thinkingLevel,\n\t\t\t\t\tisStreaming: session.isStreaming,\n\t\t\t\t\tisCompacting: session.isCompacting,\n\t\t\t\t\tsteeringMode: session.steeringMode,\n\t\t\t\t\tfollowUpMode: session.followUpMode,\n\t\t\t\t\tsessionFile: session.sessionFile,\n\t\t\t\t\tsessionId: session.sessionId,\n\t\t\t\t\tsessionName: session.sessionName,\n\t\t\t\t\tautoCompactionEnabled: session.autoCompactionEnabled,\n\t\t\t\t\tmessageCount: session.messages.length,\n\t\t\t\t\tpendingMessageCount: session.pendingMessageCount,\n\t\t\t\t};\n\t\t\t\treturn success(id, \"get_state\", state);\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Model\n\t\t\t// =================================================================\n\n\t\t\tcase \"set_model\": {\n\t\t\t\tlet provider = command.provider;\n\t\t\t\tlet modelId = command.modelId;\n\n\t\t\t\tconst tierModels = session.getTierModels();\n\t\t\t\tconst aliasTarget = resolveModelAlias(modelId, tierModels);\n\t\t\t\tif (aliasTarget) {\n\t\t\t\t\tconst slashIndex = aliasTarget.indexOf(\"/\");\n\t\t\t\t\tif (slashIndex !== -1) {\n\t\t\t\t\t\tprovider = aliasTarget.substring(0, slashIndex);\n\t\t\t\t\t\tmodelId = aliasTarget.substring(slashIndex + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmodelId = aliasTarget;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst models = await session.modelRegistry.getAvailable();\n\t\t\t\tconst model = models.find((m) => m.provider === provider && m.id === modelId);\n\t\t\t\tif (!model) {\n\t\t\t\t\treturn error(id, \"set_model\", `Model not found: ${provider}/${modelId}`);\n\t\t\t\t}\n\t\t\t\tawait session.setModel(model);\n\t\t\t\treturn success(id, \"set_model\", model);\n\t\t\t}\n\n\t\t\tcase \"cycle_model\": {\n\t\t\t\tconst result = await session.cycleModel();\n\t\t\t\tif (!result) {\n\t\t\t\t\treturn success(id, \"cycle_model\", null);\n\t\t\t\t}\n\t\t\t\treturn success(id, \"cycle_model\", result);\n\t\t\t}\n\n\t\t\tcase \"get_available_models\": {\n\t\t\t\tconst models = await session.modelRegistry.getAvailable();\n\t\t\t\treturn success(id, \"get_available_models\", { models });\n\t\t\t}\n\n\t\t\tcase \"get_tier_models\": {\n\t\t\t\tconst tierModels = session.getTierModels();\n\t\t\t\tconst merged = { ...DEFAULT_TIER_ALIASES, ...tierModels };\n\t\t\t\treturn success(id, \"get_tier_models\", { models: merged });\n\t\t\t}\n\n\t\t\tcase \"set_tier_models\": {\n\t\t\t\tconst { models } = command;\n\t\t\t\tconst validTiers = new Set([\"fast\", \"pro\", \"max\"]);\n\t\t\t\tfor (const key of Object.keys(models)) {\n\t\t\t\t\tif (!validTiers.has(key)) {\n\t\t\t\t\t\treturn error(id, \"set_tier_models\", `Invalid tier name: \"${key}\". Valid tiers are: fast, pro, max`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst mapping: Record<string, string> = {};\n\t\t\t\tif (models.fast !== undefined) mapping.fast = models.fast;\n\t\t\t\tif (models.pro !== undefined) mapping.pro = models.pro;\n\t\t\t\tif (models.max !== undefined) mapping.max = models.max;\n\t\t\t\tsession.setTierModels(mapping);\n\t\t\t\treturn success(id, \"set_tier_models\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Thinking\n\t\t\t// =================================================================\n\n\t\t\tcase \"set_thinking_level\": {\n\t\t\t\tsession.setThinkingLevel(command.level);\n\t\t\t\treturn success(id, \"set_thinking_level\");\n\t\t\t}\n\n\t\t\tcase \"cycle_thinking_level\": {\n\t\t\t\tconst level = session.cycleThinkingLevel();\n\t\t\t\tif (!level) {\n\t\t\t\t\treturn success(id, \"cycle_thinking_level\", null);\n\t\t\t\t}\n\t\t\t\treturn success(id, \"cycle_thinking_level\", { level });\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Queue Modes\n\t\t\t// =================================================================\n\n\t\t\tcase \"set_steering_mode\": {\n\t\t\t\tsession.setSteeringMode(command.mode);\n\t\t\t\treturn success(id, \"set_steering_mode\");\n\t\t\t}\n\n\t\t\tcase \"set_follow_up_mode\": {\n\t\t\t\tsession.setFollowUpMode(command.mode);\n\t\t\t\treturn success(id, \"set_follow_up_mode\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Compaction\n\t\t\t// =================================================================\n\n\t\t\tcase \"compact\": {\n\t\t\t\tconst result = await session.compact(command.customInstructions);\n\t\t\t\treturn success(id, \"compact\", result);\n\t\t\t}\n\n\t\t\tcase \"set_auto_compaction\": {\n\t\t\t\tsession.setAutoCompactionEnabled(command.enabled);\n\t\t\t\treturn success(id, \"set_auto_compaction\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Retry\n\t\t\t// =================================================================\n\n\t\t\tcase \"set_auto_retry\": {\n\t\t\t\tsession.setAutoRetryEnabled(command.enabled);\n\t\t\t\treturn success(id, \"set_auto_retry\");\n\t\t\t}\n\n\t\t\tcase \"abort_retry\": {\n\t\t\t\tsession.abortRetry();\n\t\t\t\treturn success(id, \"abort_retry\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Bash\n\t\t\t// =================================================================\n\n\t\t\tcase \"bash\": {\n\t\t\t\tconst result = await session.executeBash(command.command);\n\t\t\t\treturn success(id, \"bash\", result);\n\t\t\t}\n\n\t\t\tcase \"abort_bash\": {\n\t\t\t\tsession.abortBash();\n\t\t\t\treturn success(id, \"abort_bash\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Session\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_session_stats\": {\n\t\t\t\tconst stats = session.getSessionStats();\n\t\t\t\treturn success(id, \"get_session_stats\", stats);\n\t\t\t}\n\n\t\t\tcase \"export_html\": {\n\t\t\t\tconst path = await session.exportToHtml(command.outputPath);\n\t\t\t\treturn success(id, \"export_html\", { path });\n\t\t\t}\n\n\t\t\tcase \"switch_session\": {\n\t\t\t\tconst result = await runtimeHost.switchSession(command.sessionPath);\n\t\t\t\tif (!result.cancelled) {\n\t\t\t\t\tsession = runtimeHost.session;\n\t\t\t\t}\n\t\t\t\treturn success(id, \"switch_session\", result);\n\t\t\t}\n\n\t\t\tcase \"fork\": {\n\t\t\t\tconst previousName = session.sessionManager.getSessionName();\n\t\t\t\tconst forkPosition = (command as any).position === \"at\" ? { position: \"at\" as const } : undefined;\n\t\t\t\tconst result = await runtimeHost.fork(command.entryId, forkPosition);\n\t\t\t\tif (!result.cancelled) {\n\t\t\t\t\tconst newName = previousName ? `fork: ${previousName}` : \"fork\";\n\t\t\t\t\tsession = runtimeHost.session;\n\t\t\t\t\tsession.sessionManager.appendSessionInfo(newName);\n\t\t\t\t\tsession.sessionManager.flush();\n\t\t\t\t\tawait rebindSession();\n\t\t\t\t}\n\t\t\t\treturn success(id, \"fork\", {\n\t\t\t\t\ttext: result.selectedText,\n\t\t\t\t\tcancelled: result.cancelled,\n\t\t\t\t\tnewSessionFile: session.sessionManager.getSessionFile(),\n\t\t\t\t\tnewSessionId: session.sessionManager.getSessionId(),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"navigate_tree\": {\n\t\t\t\tconst navResult = await session.navigateTree(command.targetId, {\n\t\t\t\t\tsummarize: command.summarize ?? false,\n\t\t\t\t\tskipFiles: command.skipFiles,\n\t\t\t\t});\n\t\t\t\treturn success(id, \"navigate_tree\", { cancelled: navResult.cancelled });\n\t\t\t}\n\n\t\t\tcase \"rollback_preview\": {\n\t\t\t\tconst previewResult = await session.previewRollback(command.targetId);\n\t\t\t\treturn success(id, \"rollback_preview\", previewResult);\n\t\t\t}\n\n\t\t\tcase \"clone\": {\n\t\t\t\tconst leafId = session.sessionManager.getLeafId();\n\t\t\t\tif (!leafId) {\n\t\t\t\t\treturn error(id, \"clone\", \"Cannot clone session: no current entry selected\");\n\t\t\t\t}\n\t\t\t\tconst result = await runtimeHost.fork(leafId, { position: \"at\" });\n\t\t\t\tif (!result.cancelled) {\n\t\t\t\t\tawait rebindSession();\n\t\t\t\t}\n\t\t\t\treturn success(id, \"clone\", { cancelled: result.cancelled });\n\t\t\t}\n\n\t\t\tcase \"get_fork_messages\": {\n\t\t\t\tconst messages = session.getUserMessagesForForking();\n\t\t\t\treturn success(id, \"get_fork_messages\", { messages });\n\t\t\t}\n\n\t\t\tcase \"get_last_assistant_text\": {\n\t\t\t\tconst text = session.getLastAssistantText();\n\t\t\t\treturn success(id, \"get_last_assistant_text\", { text });\n\t\t\t}\n\n\t\t\tcase \"set_session_name\": {\n\t\t\t\tconst name = command.name.trim();\n\t\t\t\tif (!name) {\n\t\t\t\t\treturn error(id, \"set_session_name\", \"Session name cannot be empty\");\n\t\t\t\t}\n\t\t\t\tsession.setSessionName(name);\n\t\t\t\treturn success(id, \"set_session_name\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Messages\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_messages\": {\n\t\t\t\treturn success(id, \"get_messages\", { messages: session.messages });\n\t\t\t}\n\n\t\t\tcase \"get_full_messages\": {\n\t\t\t\tconst allEntries = session.sessionManager.getEntries();\n\t\t\t\tconst messageEntries = allEntries.filter((e) => e.type === \"message\");\n\t\t\t\tconst persistedMessages = messageEntries.map((e) => (e as { message: AgentMessage }).message);\n\t\t\t\tconst persistedSet = new Set(persistedMessages);\n\n\t\t\t\tconst memoryMessages = session.messages;\n\t\t\t\tconst unPersisted: AgentMessage[] = [];\n\t\t\t\tfor (let i = memoryMessages.length - 1; i >= 0; i--) {\n\t\t\t\t\tconst msg = memoryMessages[i];\n\t\t\t\t\tif (persistedSet.has(msg)) break;\n\t\t\t\t\tif (msg.role === \"compactionSummary\") continue;\n\t\t\t\t\tunPersisted.unshift(msg);\n\t\t\t\t}\n\n\t\t\t\tconst allMessages = [...persistedMessages, ...unPersisted];\n\t\t\t\tconst totalCount = allMessages.length;\n\n\t\t\t\tconst leafId = session.sessionManager.getLeafId();\n\n\t\t\t\tconst treeEntries = allEntries.map((e) => ({\n\t\t\t\t\tid: e.id,\n\t\t\t\t\tparentId: e.parentId,\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tlabel:\n\t\t\t\t\t\te.type === \"message\"\n\t\t\t\t\t\t\t? (e as any).message?.role\n\t\t\t\t\t\t\t: e.type === \"custom\"\n\t\t\t\t\t\t\t\t? (e as any).customType\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t}));\n\n\t\t\t\tconst customEntries = allEntries\n\t\t\t\t\t.filter((e) => e.type === \"custom\")\n\t\t\t\t\t.map((e) => ({\n\t\t\t\t\t\tid: e.id,\n\t\t\t\t\t\tcustomType: (e as any).customType ?? \"unknown\",\n\t\t\t\t\t\tdata: (e as any).data,\n\t\t\t\t\t\ttimestamp: new Date(e.timestamp).getTime(),\n\t\t\t\t\t}));\n\n\t\t\t\tconst compactionEntries = allEntries\n\t\t\t\t\t.filter((e) => e.type === \"compaction\")\n\t\t\t\t\t.map((e) => ({\n\t\t\t\t\t\tid: e.id,\n\t\t\t\t\t\tsummary: (e as any).summary ?? \"\",\n\t\t\t\t\t\ttokensBefore: (e as any).tokensBefore,\n\t\t\t\t\t\ttimestamp: new Date(e.timestamp).getTime(),\n\t\t\t\t\t}));\n\n\t\t\t\tif (command.limit !== undefined) {\n\t\t\t\t\tconst limit = command.limit;\n\t\t\t\t\tlet startIndex = 0;\n\t\t\t\t\tif (command.afterEntryId) {\n\t\t\t\t\t\tconst idx = messageEntries.findIndex((e) => e.id === command.afterEntryId);\n\t\t\t\t\t\tif (idx !== -1) {\n\t\t\t\t\t\t\tstartIndex = idx + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst page = allMessages.slice(startIndex, startIndex + limit);\n\t\t\t\t\tconst hasMore = startIndex + limit < totalCount;\n\t\t\t\t\tconst lastPersisted = messageEntries[Math.min(startIndex + limit, messageEntries.length) - 1];\n\t\t\t\t\treturn success(id, \"get_full_messages\", {\n\t\t\t\t\t\tmessages: page,\n\t\t\t\t\t\thasMore,\n\t\t\t\t\t\ttotalCount,\n\t\t\t\t\t\tnextCursor: hasMore && lastPersisted ? lastPersisted.id : null,\n\t\t\t\t\t\ttree: { entries: treeEntries, leafId },\n\t\t\t\t\t\tcustomEntries,\n\t\t\t\t\t\tcompactionEntries,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn success(id, \"get_full_messages\", {\n\t\t\t\t\tmessages: allMessages,\n\t\t\t\t\thasMore: false,\n\t\t\t\t\ttotalCount,\n\t\t\t\t\tnextCursor: null,\n\t\t\t\t\ttree: { entries: treeEntries, leafId },\n\t\t\t\t\tcustomEntries,\n\t\t\t\t\tcompactionEntries,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"get_tree\": {\n\t\t\t\tconst entries = session.sessionManager.getEntries();\n\t\t\t\tconst treeEntries = entries.map((e) => ({\n\t\t\t\t\tid: e.id,\n\t\t\t\t\tparentId: e.parentId,\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tlabel:\n\t\t\t\t\t\te.type === \"message\"\n\t\t\t\t\t\t\t? (e as any).message?.role\n\t\t\t\t\t\t\t: e.type === \"custom\"\n\t\t\t\t\t\t\t\t? (e as any).customType\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_tree\", { entries: treeEntries, leafId: session.sessionManager.getLeafId() });\n\t\t\t}\n\t\t\tcase \"get_tree_with_leaf\": {\n\t\t\t\tconst entries = session.sessionManager.getEntries();\n\t\t\t\tconst treeEntries = entries.map((e) => ({\n\t\t\t\t\tid: e.id,\n\t\t\t\t\tparentId: e.parentId,\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tlabel:\n\t\t\t\t\t\te.type === \"message\"\n\t\t\t\t\t\t\t? (e as any).message?.role\n\t\t\t\t\t\t\t: e.type === \"custom\"\n\t\t\t\t\t\t\t\t? (e as any).customType\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_tree_with_leaf\", {\n\t\t\t\t\tentries: treeEntries,\n\t\t\t\t\tleafId: session.sessionManager.getLeafId(),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Resources (skills, extensions, tools)\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_skills\": {\n\t\t\t\tconst { skills } = session.resourceLoader.getSkills();\n\t\t\t\tconst rpcSkills: RpcSkill[] = skills.map((s) => ({\n\t\t\t\t\tname: s.name,\n\t\t\t\t\tdescription: s.description,\n\t\t\t\t\tfilePath: s.filePath,\n\t\t\t\t\tbaseDir: s.baseDir,\n\t\t\t\t\tsourceInfo: s.sourceInfo,\n\t\t\t\t\tdisableModelInvocation: s.disableModelInvocation,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_skills\", { skills: rpcSkills });\n\t\t\t}\n\n\t\t\tcase \"get_extensions\": {\n\t\t\t\tconst { extensions } = session.resourceLoader.getExtensions();\n\t\t\t\tconst rpcExtensions: RpcExtension[] = extensions.map((e) => ({\n\t\t\t\t\tpath: e.path,\n\t\t\t\t\tresolvedPath: e.resolvedPath,\n\t\t\t\t\tsourceInfo: e.sourceInfo,\n\t\t\t\t\ttoolNames: Array.from(e.tools.keys()),\n\t\t\t\t\tcommandNames: Array.from(e.commands.keys()),\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_extensions\", { extensions: rpcExtensions });\n\t\t\t}\n\n\t\t\tcase \"get_tools\": {\n\t\t\t\tconst allTools = session.extensionRunner.getAllRegisteredTools();\n\t\t\t\tconst rpcTools: RpcTool[] = allTools.map((t) => ({\n\t\t\t\t\tname: t.definition.name,\n\t\t\t\t\tlabel: t.definition.label,\n\t\t\t\t\tdescription: t.definition.description,\n\t\t\t\t\tsourceInfo: t.sourceInfo,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_tools\", { tools: rpcTools });\n\t\t\t}\n\n\t\t\tcase \"get_mcp_servers\": {\n\t\t\t\tconst servers: RpcMcpServer[] = session.getMcpConnections();\n\t\t\t\treturn success(id, \"get_mcp_servers\", { servers });\n\t\t\t}\n\n\t\t\tcase \"mcp_toggle_server\": {\n\t\t\t\ttry {\n\t\t\t\t\tawait session.toggleMcpServer(command.name, command.enabled);\n\t\t\t\t\treturn success(id, \"mcp_toggle_server\");\n\t\t\t\t} catch (e) {\n\t\t\t\t\treturn error(id, \"mcp_toggle_server\", e instanceof Error ? e.message : String(e));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcase \"mcp_restart_server\": {\n\t\t\t\ttry {\n\t\t\t\t\tawait session.restartMcpServer(command.name);\n\t\t\t\t\treturn success(id, \"mcp_restart_server\");\n\t\t\t\t} catch (e) {\n\t\t\t\t\treturn error(id, \"mcp_restart_server\", e instanceof Error ? e.message : String(e));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Commands (available for invocation via prompt)\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_commands\": {\n\t\t\t\tconst commands: RpcSlashCommand[] = [];\n\n\t\t\t\tfor (const command of session.extensionRunner.getRegisteredCommands()) {\n\t\t\t\t\tcommands.push({\n\t\t\t\t\t\tname: command.invocationName,\n\t\t\t\t\t\tdescription: command.description,\n\t\t\t\t\t\tsource: \"extension\",\n\t\t\t\t\t\tsourceInfo: command.sourceInfo,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tfor (const template of session.promptTemplates) {\n\t\t\t\t\tcommands.push({\n\t\t\t\t\t\tname: template.name,\n\t\t\t\t\t\tdescription: template.description,\n\t\t\t\t\t\tsource: \"prompt\",\n\t\t\t\t\t\tsourceInfo: template.sourceInfo,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tfor (const skill of session.resourceLoader.getSkills().skills) {\n\t\t\t\t\tcommands.push({\n\t\t\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\t\t\tdescription: skill.description,\n\t\t\t\t\t\tsource: \"skill\",\n\t\t\t\t\t\tsourceInfo: skill.sourceInfo,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn success(id, \"get_commands\", { commands });\n\t\t\t}\n\n\t\t\tcase \"get_settings\": {\n\t\t\t\tconst scope = command.scope;\n\t\t\t\tlet data: import(\"../../core/settings-manager.js\").Settings;\n\t\t\t\tif (scope === \"global\") {\n\t\t\t\t\tdata = session.settingsManager.getGlobalSettings();\n\t\t\t\t} else if (scope === \"project\") {\n\t\t\t\t\tdata = session.settingsManager.getProjectSettings();\n\t\t\t\t} else {\n\t\t\t\t\tdata = session.settingsManager.getGlobalSettings();\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_settings\", data);\n\t\t\t}\n\n\t\t\tcase \"set_settings\": {\n\t\t\t\ttype Scope = import(\"../../core/settings-manager.js\").SettingsScope;\n\t\t\t\tconst setScope = (command.scope as Scope | undefined) ?? \"global\";\n\t\t\t\tsession.settingsManager.applyOverrides(command.settings, setScope);\n\t\t\t\treturn success(id, \"set_settings\");\n\t\t\t}\n\n\t\t\tcase \"get_context_usage\": {\n\t\t\t\tconst usage = session.getContextUsage();\n\t\t\t\tif (!usage) {\n\t\t\t\t\treturn success(id, \"get_context_usage\", {\n\t\t\t\t\t\ttokens: null,\n\t\t\t\t\t\tcontextWindow: session.model?.contextWindow ?? 0,\n\t\t\t\t\t\tpercent: null,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_context_usage\", usage);\n\t\t\t}\n\n\t\t\tcase \"get_system_prompt\": {\n\t\t\t\tconst systemPrompt = session.resourceLoader.getSystemPrompt() ?? \"\";\n\t\t\t\tconst appendSystemPrompt = session.resourceLoader.getAppendSystemPrompt();\n\t\t\t\treturn success(id, \"get_system_prompt\", { systemPrompt, appendSystemPrompt });\n\t\t\t}\n\n\t\t\tcase \"get_active_tools\": {\n\t\t\t\tconst toolNames = session.getActiveToolNames();\n\t\t\t\treturn success(id, \"get_active_tools\", { toolNames });\n\t\t\t}\n\n\t\t\tcase \"set_active_tools\": {\n\t\t\t\tsession.setActiveToolsByName(command.toolNames);\n\t\t\t\treturn success(id, \"set_active_tools\");\n\t\t\t}\n\n\t\t\tcase \"get_queue\": {\n\t\t\t\treturn success(id, \"get_queue\", {\n\t\t\t\t\tsteering: [...session.getSteeringMessages()],\n\t\t\t\t\tfollowUp: [...session.getFollowUpMessages()],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"clear_queue\": {\n\t\t\t\tconst cleared = session.clearQueue();\n\t\t\t\treturn success(id, \"clear_queue\", cleared);\n\t\t\t}\n\n\t\t\tcase \"get_flags\": {\n\t\t\t\tconst flagsMap = session.extensionRunner.getFlags();\n\t\t\t\tconst flags: Array<{\n\t\t\t\t\tname: string;\n\t\t\t\t\tdescription?: string;\n\t\t\t\t\ttype: \"boolean\" | \"string\";\n\t\t\t\t\tdefault?: boolean | string;\n\t\t\t\t\textensionPath: string;\n\t\t\t\t}> = [];\n\t\t\t\tfor (const [name, flag] of flagsMap) {\n\t\t\t\t\tflags.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tdescription: flag.description,\n\t\t\t\t\t\ttype: flag.type,\n\t\t\t\t\t\tdefault: flag.default,\n\t\t\t\t\t\textensionPath: flag.extensionPath,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_flags\", { flags });\n\t\t\t}\n\n\t\t\tcase \"get_flag_values\": {\n\t\t\t\tconst valuesMap = session.extensionRunner.getFlagValues();\n\t\t\t\tconst values: Record<string, boolean | string> = {};\n\t\t\t\tfor (const [name, value] of valuesMap) {\n\t\t\t\t\tvalues[name] = value;\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_flag_values\", { values });\n\t\t\t}\n\n\t\t\tcase \"set_flag\": {\n\t\t\t\tsession.extensionRunner.setFlagValue(command.name, command.value);\n\t\t\t\treturn success(id, \"set_flag\");\n\t\t\t}\n\n\t\t\tcase \"reload\": {\n\t\t\t\tawait session.reload();\n\t\t\t\tsession = runtimeHost.session;\n\t\t\t\treturn success(id, \"reload\");\n\t\t\t}\n\n\t\t\tcase \"set_cwd\": {\n\t\t\t\tawait session.setCwd(command.cwd);\n\t\t\t\treturn success(id, \"set_cwd\");\n\t\t\t}\n\n\t\t\tcase \"get_agents_files\": {\n\t\t\t\tconst result = session.resourceLoader.getAgentsFiles();\n\t\t\t\treturn success(id, \"get_agents_files\", { agentsFiles: result.agentsFiles });\n\t\t\t}\n\n\t\t\tcase \"get_agents\": {\n\t\t\t\tconst discovery = discoverAgents(runtimeHost.cwd, \"both\");\n\t\t\t\tconst builtin = getBuiltinAgents();\n\t\t\t\tconst agents = [...builtin, ...discovery.agents].map((a) => ({\n\t\t\t\t\tname: a.name,\n\t\t\t\t\tdescription: a.description,\n\t\t\t\t\ttier: a.tier,\n\t\t\t\t\ttools: a.tools,\n\t\t\t\t\tpermissionMode: a.permissionMode,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_agents\", { agents });\n\t\t\t}\n\n\t\t\tcase \"switch_agent\": {\n\t\t\t\tconst agentName = (command as { agentName: string }).agentName;\n\t\t\t\tconst discovery = discoverAgents(runtimeHost.cwd, \"both\");\n\t\t\t\tconst builtin = getBuiltinAgents();\n\t\t\t\tconst agent = [...builtin, ...discovery.agents].find((a) => a.name === agentName);\n\t\t\t\tif (!agent) {\n\t\t\t\t\treturn error(id, \"switch_agent\", `Agent \"${agentName}\" not found`);\n\t\t\t\t}\n\t\t\t\tawait session.applyAgentConfig(agent);\n\t\t\t\treturn success(id, \"switch_agent\", {\n\t\t\t\t\tagentName: agent.name,\n\t\t\t\t\ttools: agent.tools ?? [],\n\t\t\t\t\ttier: agent.tier,\n\t\t\t\t\tthinkingLevel: agent.thinkingLevel,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"get_current_agent\": {\n\t\t\t\tconst currentAgent = session.getCurrentAgent();\n\t\t\t\treturn success(id, \"get_current_agent\", { agentName: currentAgent });\n\t\t\t}\n\n\t\t\tcase \"get_modified_files\": {\n\t\t\t\tconst fileSnapshotManager = (session as any).fileSnapshotManager;\n\t\t\t\tif (!fileSnapshotManager) {\n\t\t\t\t\treturn success(id, \"get_modified_files\", { files: [] });\n\t\t\t\t}\n\t\t\t\tconst files = fileSnapshotManager.getModifiedFiles({\n\t\t\t\t\tfromEntryId: command.fromEntryId,\n\t\t\t\t\ttoEntryId: command.toEntryId,\n\t\t\t\t});\n\t\t\t\treturn success(id, \"get_modified_files\", { files });\n\t\t\t}\n\n\t\t\tcase \"get_file_diff\": {\n\t\t\t\tconst fileSnapshotManager = (session as any).fileSnapshotManager;\n\t\t\t\tif (!fileSnapshotManager) {\n\t\t\t\t\treturn success(id, \"get_file_diff\", null);\n\t\t\t\t}\n\t\t\t\tconst diff = fileSnapshotManager.getFileDiff({\n\t\t\t\t\tfilePath: command.filePath,\n\t\t\t\t\tfromEntryId: command.fromEntryId,\n\t\t\t\t\ttoEntryId: command.toEntryId,\n\t\t\t\t});\n\t\t\t\tif (!diff) {\n\t\t\t\t\treturn success(id, \"get_file_diff\", null);\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_file_diff\", {\n\t\t\t\t\tpath: diff.path,\n\t\t\t\t\toldContent: diff.oldContent,\n\t\t\t\t\tnewContent: diff.newContent,\n\t\t\t\t\tunifiedDiff: diff.unifiedDiff,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"get_batch_diffs\": {\n\t\t\t\tconst fileSnapshotManager = (session as any).fileSnapshotManager;\n\t\t\t\tif (!fileSnapshotManager) {\n\t\t\t\t\treturn success(id, \"get_batch_diffs\", {\n\t\t\t\t\t\tfiles: [],\n\t\t\t\t\t\tsummary: { totalFiles: 0, added: 0, modified: 0, deleted: 0 },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst result = fileSnapshotManager.getBatchDiffs({\n\t\t\t\t\tfromEntryId: command.fromEntryId,\n\t\t\t\t\ttoEntryId: command.toEntryId,\n\t\t\t\t});\n\t\t\t\tconst serializedFiles = result.files.map((f: any) => ({\n\t\t\t\t\tpath: f.path,\n\t\t\t\t\tstatus: f.status,\n\t\t\t\t\tdiff: f.diff\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tpath: f.diff.path,\n\t\t\t\t\t\t\t\toldContent: f.diff.oldContent,\n\t\t\t\t\t\t\t\tnewContent: f.diff.newContent,\n\t\t\t\t\t\t\t\tunifiedDiff: f.diff.unifiedDiff,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: null,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_batch_diffs\", {\n\t\t\t\t\tfiles: serializedFiles,\n\t\t\t\t\tsummary: result.summary,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"get_file_history\": {\n\t\t\t\tconst fileSnapshotManager = (session as any).fileSnapshotManager;\n\t\t\t\tif (!fileSnapshotManager) {\n\t\t\t\t\treturn success(id, \"get_file_history\", { history: [] });\n\t\t\t\t}\n\t\t\t\tconst result = fileSnapshotManager.getFileHistory({\n\t\t\t\t\tfilePath: command.filePath,\n\t\t\t\t});\n\t\t\t\treturn success(id, \"get_file_history\", { history: result });\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tconst unknownCommand = command as { type: string };\n\t\t\t\treturn error(undefined, unknownCommand.type, `Unknown command: ${unknownCommand.type}`);\n\t\t\t}\n\t\t}\n\t};\n\n\t/**\n\t * Check if shutdown was requested and perform shutdown if so.\n\t * Called after handling each command when waiting for the next command.\n\t */\n\tlet detachInput = () => {};\n\n\tasync function shutdown(exitCode = 0): Promise<never> {\n\t\tif (shuttingDown) {\n\t\t\tprocess.exit(exitCode);\n\t\t}\n\t\tshuttingDown = true;\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t\tdetachInput();\n\t\tprocess.stdin.pause();\n\t\tprocess.exit(exitCode);\n\t}\n\n\tasync function checkShutdownRequested(): Promise<void> {\n\t\tif (!shutdownRequested) return;\n\t\tawait shutdown();\n\t}\n\n\tconst handleInputLine = async (line: string) => {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(line);\n\t\t} catch (parseError: unknown) {\n\t\t\toutput(\n\t\t\t\terror(\n\t\t\t\t\tundefined,\n\t\t\t\t\t\"parse\",\n\t\t\t\t\t`Failed to parse command: ${parseError instanceof Error ? parseError.message : String(parseError)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle extension UI responses\n\t\tif (\n\t\t\ttypeof parsed === \"object\" &&\n\t\t\tparsed !== null &&\n\t\t\t\"type\" in parsed &&\n\t\t\tparsed.type === \"extension_ui_response\"\n\t\t) {\n\t\t\tconst response = parsed as RpcExtensionUIResponse;\n\t\t\tconst pending = pendingExtensionRequests.get(response.id);\n\t\t\tif (pending) {\n\t\t\t\tpendingExtensionRequests.delete(response.id);\n\t\t\t\tpending.resolve(response);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle channel data from RPC Client\n\t\tif (\n\t\t\ttypeof parsed === \"object\" &&\n\t\t\tparsed !== null &&\n\t\t\t\"type\" in parsed &&\n\t\t\tparsed.type === \"channel_data\" &&\n\t\t\t\"name\" in parsed\n\t\t) {\n\t\t\tchannelManager.handleInbound(parsed as ChannelDataMessage);\n\t\t\treturn;\n\t\t}\n\n\t\tconst command = parsed as RpcCommand;\n\t\ttry {\n\t\t\tconst response = await handleCommand(command);\n\t\t\tif (response) {\n\t\t\t\toutput(response);\n\t\t\t}\n\t\t\tawait checkShutdownRequested();\n\t\t} catch (commandError: unknown) {\n\t\t\toutput(\n\t\t\t\terror(\n\t\t\t\t\tcommand.id,\n\t\t\t\t\tcommand.type,\n\t\t\t\t\tcommandError instanceof Error ? commandError.message : String(commandError),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t};\n\n\tconst onInputEnd = () => {\n\t\tvoid shutdown();\n\t};\n\tprocess.stdin.on(\"end\", onInputEnd);\n\n\tdetachInput = (() => {\n\t\tconst detachJsonl = attachJsonlLineReader(process.stdin, (line) => {\n\t\t\tvoid handleInputLine(line);\n\t\t});\n\t\treturn () => {\n\t\t\tdetachJsonl();\n\t\t\tprocess.stdin.off(\"end\", onInputEnd);\n\t\t};\n\t})();\n\n\t// Keep process alive forever\n\treturn new Promise(() => {});\n}\n"]}
1
+ {"version":3,"file":"rpc-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AA8B/E,YAAY,EACX,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,QAAQ,EACR,eAAe,EACf,OAAO,GACP,MAAM,gBAAgB,CAAC;AAExB;;;GAGG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAyqCjF","sourcesContent":["/**\n * RPC mode: Headless operation with JSON stdin/stdout protocol.\n *\n * Used for embedding the agent in other applications.\n * Receives commands as JSON on stdin, outputs events and responses as JSON on stdout.\n *\n * Protocol:\n * - Commands: JSON objects with `type` field, optional `id` for correlation\n * - Responses: JSON objects with `type: \"response\"`, `command`, `success`, and optional `data`/`error`\n * - Events: AgentSessionEvent objects streamed as they occur\n * - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response\n */\n\nimport * as crypto from \"node:crypto\";\nimport type { AgentMessage } from \"@dyyz1993/pi-agent-core\";\nimport type { AgentSessionRuntime } from \"../../core/agent-session-runtime.js\";\nimport { discoverAgents, getBuiltinAgents } from \"../../core/agent-types.js\";\nimport { DEFAULT_TIER_ALIASES } from \"../../core/defaults.js\";\nimport { ChannelManager } from \"../../core/extensions/channel-manager.js\";\nimport type { ChannelDataMessage } from \"../../core/extensions/channel-types.js\";\nimport type {\n\tExtensionUIContext,\n\tExtensionUIDialogOptions,\n\tExtensionWidgetOptions,\n\tWorkingIndicatorOptions,\n} from \"../../core/extensions/index.js\";\nimport { resolveModelAlias } from \"../../core/model-resolver.js\";\nimport { takeOverStdout, writeRawStdout } from \"../../core/output-guard.js\";\nimport { killTrackedDetachedChildren } from \"../../utils/shell.js\";\nimport { type Theme, theme } from \"../interactive/theme/theme.js\";\nimport { attachJsonlLineReader, serializeJsonLine } from \"./jsonl.js\";\nimport type {\n\tRpcCommand,\n\tRpcExtension,\n\tRpcExtensionUIRequest,\n\tRpcExtensionUIResponse,\n\tRpcMcpServer,\n\tRpcResponse,\n\tRpcSessionState,\n\tRpcSkill,\n\tRpcSlashCommand,\n\tRpcTool,\n} from \"./rpc-types.js\";\n\n// Re-export types for consumers\nexport type {\n\tRpcCommand,\n\tRpcExtension,\n\tRpcExtensionUIRequest,\n\tRpcExtensionUIResponse,\n\tRpcMcpServer,\n\tRpcResponse,\n\tRpcSessionState,\n\tRpcSkill,\n\tRpcSlashCommand,\n\tRpcTool,\n} from \"./rpc-types.js\";\n\n/**\n * Run in RPC mode.\n * Listens for JSON commands on stdin, outputs events and responses on stdout.\n */\nexport async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<never> {\n\ttakeOverStdout();\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\n\tconst output = (obj: RpcResponse | RpcExtensionUIRequest | object) => {\n\t\twriteRawStdout(serializeJsonLine(obj));\n\t};\n\n\tconst channelManager = new ChannelManager((message: ChannelDataMessage) => {\n\t\toutput(message);\n\t});\n\n\tconst success = <T extends RpcCommand[\"type\"]>(\n\t\tid: string | undefined,\n\t\tcommand: T,\n\t\tdata?: object | null,\n\t): RpcResponse => {\n\t\tif (data === undefined) {\n\t\t\treturn { id, type: \"response\", command, success: true } as RpcResponse;\n\t\t}\n\t\treturn { id, type: \"response\", command, success: true, data } as RpcResponse;\n\t};\n\n\tconst error = (id: string | undefined, command: string, message: string): RpcResponse => {\n\t\treturn { id, type: \"response\", command, success: false, error: message };\n\t};\n\n\t// Pending extension UI requests waiting for response\n\tconst pendingExtensionRequests = new Map<\n\t\tstring,\n\t\t{ resolve: (value: any) => void; reject: (error: Error) => void }\n\t>();\n\n\t// Shutdown request flag\n\tlet shutdownRequested = false;\n\tlet shuttingDown = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\t/** Helper for dialog methods with signal/timeout support */\n\tfunction createDialogPromise<T>(\n\t\topts: ExtensionUIDialogOptions | undefined,\n\t\tdefaultValue: T,\n\t\trequest: Record<string, unknown>,\n\t\tparseResponse: (response: RpcExtensionUIResponse) => T,\n\t): Promise<T> {\n\t\tif (opts?.signal?.aborted) return Promise.resolve(defaultValue);\n\n\t\tconst id = crypto.randomUUID();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n\t\t\tconst cleanup = () => {\n\t\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\t\topts?.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tpendingExtensionRequests.delete(id);\n\t\t\t};\n\n\t\t\tconst onAbort = () => {\n\t\t\t\tcleanup();\n\t\t\t\tresolve(defaultValue);\n\t\t\t};\n\t\t\topts?.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\tif (opts?.timeout) {\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve(defaultValue);\n\t\t\t\t}, opts.timeout);\n\t\t\t}\n\n\t\t\tpendingExtensionRequests.set(id, {\n\t\t\t\tresolve: (response: RpcExtensionUIResponse) => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve(parseResponse(response));\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t});\n\t\t\toutput({ type: \"extension_ui_request\", id, ...request } as RpcExtensionUIRequest);\n\t\t});\n\t}\n\n\t/**\n\t * Create an extension UI context that uses the RPC protocol.\n\t */\n\tconst createExtensionUIContext = (): ExtensionUIContext => ({\n\t\tselect: (title, options, opts) =>\n\t\t\tcreateDialogPromise(\n\t\t\t\topts,\n\t\t\t\tundefined,\n\t\t\t\t{ method: \"select\", title, options, multiple: opts?.multiple, timeout: opts?.timeout },\n\t\t\t\t(r) => (\"cancelled\" in r && r.cancelled ? undefined : \"value\" in r ? r.value : undefined),\n\t\t\t),\n\n\t\tconfirm: (title, message, opts) =>\n\t\t\tcreateDialogPromise(opts, false, { method: \"confirm\", title, message, timeout: opts?.timeout }, (r) =>\n\t\t\t\t\"cancelled\" in r && r.cancelled ? false : \"confirmed\" in r ? r.confirmed : false,\n\t\t\t),\n\n\t\tinput: (title, placeholder, opts) =>\n\t\t\tcreateDialogPromise(opts, undefined, { method: \"input\", title, placeholder, timeout: opts?.timeout }, (r) =>\n\t\t\t\t\"cancelled\" in r && r.cancelled ? undefined : \"value\" in r ? r.value : undefined,\n\t\t\t),\n\n\t\tnotify(message: string, type?: \"info\" | \"warning\" | \"error\"): void {\n\t\t\t// Fire and forget - no response needed\n\t\t\toutput({\n\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tmethod: \"notify\",\n\t\t\t\tmessage,\n\t\t\t\tnotifyType: type,\n\t\t\t} as RpcExtensionUIRequest);\n\t\t},\n\n\t\tonTerminalInput(): () => void {\n\t\t\t// Raw terminal input not supported in RPC mode\n\t\t\treturn () => {};\n\t\t},\n\n\t\tsetStatus(key: string, text: string | undefined): void {\n\t\t\t// Fire and forget - no response needed\n\t\t\toutput({\n\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tmethod: \"setStatus\",\n\t\t\t\tstatusKey: key,\n\t\t\t\tstatusText: text,\n\t\t\t} as RpcExtensionUIRequest);\n\t\t},\n\n\t\tsetWorkingMessage(_message?: string): void {\n\t\t\t// Working message not supported in RPC mode - requires TUI loader access\n\t\t},\n\n\t\tsetWorkingVisible(_visible: boolean): void {\n\t\t\t// Working visibility not supported in RPC mode - requires TUI loader access\n\t\t},\n\n\t\tsetWorkingIndicator(_options?: WorkingIndicatorOptions): void {\n\t\t\t// Working indicator customization not supported in RPC mode - requires TUI loader access\n\t\t},\n\n\t\tsetHiddenThinkingLabel(_label?: string): void {\n\t\t\t// Hidden thinking label not supported in RPC mode - requires TUI message rendering access\n\t\t},\n\n\t\tsetWidget(key: string, content: unknown, options?: ExtensionWidgetOptions): void {\n\t\t\t// Only support string arrays in RPC mode - factory functions are ignored\n\t\t\tif (content === undefined || Array.isArray(content)) {\n\t\t\t\toutput({\n\t\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\t\tmethod: \"setWidget\",\n\t\t\t\t\twidgetKey: key,\n\t\t\t\t\twidgetLines: content as string[] | undefined,\n\t\t\t\t\twidgetPlacement: options?.placement,\n\t\t\t\t} as RpcExtensionUIRequest);\n\t\t\t}\n\t\t\t// Component factories are not supported in RPC mode - would need TUI access\n\t\t},\n\n\t\tsetFooter(_factory: unknown): void {\n\t\t\t// Custom footer not supported in RPC mode - requires TUI access\n\t\t},\n\n\t\tsetHeader(_factory: unknown): void {\n\t\t\t// Custom header not supported in RPC mode - requires TUI access\n\t\t},\n\n\t\tsetTitle(title: string): void {\n\t\t\t// Fire and forget - host can implement terminal title control\n\t\t\toutput({\n\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tmethod: \"setTitle\",\n\t\t\t\ttitle,\n\t\t\t} as RpcExtensionUIRequest);\n\t\t},\n\n\t\tasync custom() {\n\t\t\t// Custom UI not supported in RPC mode\n\t\t\treturn undefined as never;\n\t\t},\n\n\t\tpasteToEditor(text: string): void {\n\t\t\t// Paste handling not supported in RPC mode - falls back to setEditorText\n\t\t\tthis.setEditorText(text);\n\t\t},\n\n\t\tsetEditorText(text: string): void {\n\t\t\t// Fire and forget - host can implement editor control\n\t\t\toutput({\n\t\t\t\ttype: \"extension_ui_request\",\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tmethod: \"set_editor_text\",\n\t\t\t\ttext,\n\t\t\t} as RpcExtensionUIRequest);\n\t\t},\n\n\t\tgetEditorText(): string {\n\t\t\t// Synchronous method can't wait for RPC response\n\t\t\t// Host should track editor state locally if needed\n\t\t\treturn \"\";\n\t\t},\n\n\t\tasync editor(title: string, prefill?: string): Promise<string | undefined> {\n\t\t\tconst id = crypto.randomUUID();\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tpendingExtensionRequests.set(id, {\n\t\t\t\t\tresolve: (response: RpcExtensionUIResponse) => {\n\t\t\t\t\t\tif (\"cancelled\" in response && response.cancelled) {\n\t\t\t\t\t\t\tresolve(undefined);\n\t\t\t\t\t\t} else if (\"value\" in response) {\n\t\t\t\t\t\t\tresolve(response.value);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve(undefined);\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\treject,\n\t\t\t\t});\n\t\t\t\toutput({ type: \"extension_ui_request\", id, method: \"editor\", title, prefill } as RpcExtensionUIRequest);\n\t\t\t});\n\t\t},\n\n\t\taddAutocompleteProvider(): void {\n\t\t\t// Autocomplete provider composition is not supported in RPC mode\n\t\t},\n\n\t\tsetEditorComponent(): void {\n\t\t\t// Custom editor components not supported in RPC mode\n\t\t},\n\n\t\tgetEditorComponent() {\n\t\t\t// Custom editor components not supported in RPC mode\n\t\t\treturn undefined;\n\t\t},\n\n\t\tget theme() {\n\t\t\treturn theme;\n\t\t},\n\n\t\tgetAllThemes() {\n\t\t\treturn [];\n\t\t},\n\n\t\tgetTheme(_name: string) {\n\t\t\treturn undefined;\n\t\t},\n\n\t\tsetTheme(_theme: string | Theme) {\n\t\t\t// Theme switching not supported in RPC mode\n\t\t\treturn { success: false, error: \"Theme switching not supported in RPC mode\" };\n\t\t},\n\n\t\tgetToolsExpanded() {\n\t\t\t// Tool expansion not supported in RPC mode - no TUI\n\t\t\treturn false;\n\t\t},\n\n\t\tsetToolsExpanded(_expanded: boolean) {\n\t\t\t// Tool expansion not supported in RPC mode - no TUI\n\t\t},\n\t});\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tlet rebindDone: Promise<void>;\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tuiContext: createExtensionUIContext(),\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\t\tnewSession: async (options) => runtimeHost.newSession(options),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, options) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: options?.summarize,\n\t\t\t\t\t\tcustomInstructions: options?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: options?.replaceInstructions,\n\t\t\t\t\t\tlabel: options?.label,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, options) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, options);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tshutdownHandler: () => {\n\t\t\t\tshutdownRequested = true;\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\toutput({ type: \"extension_error\", extensionPath: err.extensionPath, event: err.event, error: err.error });\n\t\t\t},\n\t\t\tregisterChannel: (name: string) => channelManager.register(name),\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\toutput(event);\n\t\t});\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid shutdown(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\trebindDone = rebindSession();\n\tregisterSignalHandlers();\n\toutput({ type: \"ready\" });\n\n\t// Handle a single command\n\tconst handleCommand = async (command: RpcCommand): Promise<RpcResponse | undefined> => {\n\t\tawait rebindDone;\n\t\tconst id = command.id;\n\n\t\tswitch (command.type) {\n\t\t\t// =================================================================\n\t\t\t// Prompting\n\t\t\t// =================================================================\n\n\t\t\tcase \"prompt\": {\n\t\t\t\t// Start prompt handling immediately, but emit the authoritative response only after\n\t\t\t\t// prompt preflight succeeds. Queued and immediately handled prompts also count as success.\n\t\t\t\tlet preflightSucceeded = false;\n\t\t\t\tvoid session\n\t\t\t\t\t.prompt(command.message, {\n\t\t\t\t\t\timages: command.images,\n\t\t\t\t\t\tstreamingBehavior: command.streamingBehavior,\n\t\t\t\t\t\tsource: \"rpc\",\n\t\t\t\t\t\tpreflightResult: (didSucceed) => {\n\t\t\t\t\t\t\tif (didSucceed) {\n\t\t\t\t\t\t\t\tpreflightSucceeded = true;\n\t\t\t\t\t\t\t\toutput(success(id, \"prompt\"));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tif (!preflightSucceeded) {\n\t\t\t\t\t\t\toutput(error(id, \"prompt\", e.message));\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tcase \"steer\": {\n\t\t\t\tawait session.steer(command.message, command.images);\n\t\t\t\treturn success(id, \"steer\");\n\t\t\t}\n\n\t\t\tcase \"follow_up\": {\n\t\t\t\tawait session.followUp(command.message, command.images);\n\t\t\t\treturn success(id, \"follow_up\");\n\t\t\t}\n\n\t\t\tcase \"abort\": {\n\t\t\t\tawait session.abort();\n\t\t\t\treturn success(id, \"abort\");\n\t\t\t}\n\n\t\t\tcase \"new_session\": {\n\t\t\t\tconst options = command.parentSession ? { parentSession: command.parentSession } : undefined;\n\t\t\t\tconst result = await runtimeHost.newSession(options);\n\t\t\t\tif (!result.cancelled) {\n\t\t\t\t\tawait rebindSession();\n\t\t\t\t}\n\t\t\t\treturn success(id, \"new_session\", result);\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// State\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_state\": {\n\t\t\t\tconst state: RpcSessionState = {\n\t\t\t\t\tmodel: session.model,\n\t\t\t\t\tthinkingLevel: session.thinkingLevel,\n\t\t\t\t\tisStreaming: session.isStreaming,\n\t\t\t\t\tisCompacting: session.isCompacting,\n\t\t\t\t\tsteeringMode: session.steeringMode,\n\t\t\t\t\tfollowUpMode: session.followUpMode,\n\t\t\t\t\tsessionFile: session.sessionFile,\n\t\t\t\t\tsessionId: session.sessionId,\n\t\t\t\t\tsessionName: session.sessionName,\n\t\t\t\t\tautoCompactionEnabled: session.autoCompactionEnabled,\n\t\t\t\t\tmessageCount: session.messages.length,\n\t\t\t\t\tpendingMessageCount: session.pendingMessageCount,\n\t\t\t\t};\n\t\t\t\treturn success(id, \"get_state\", state);\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Model\n\t\t\t// =================================================================\n\n\t\t\tcase \"set_model\": {\n\t\t\t\tlet provider = command.provider;\n\t\t\t\tlet modelId = command.modelId;\n\n\t\t\t\tconst tierModels = session.getTierModels();\n\t\t\t\tconst aliasTarget = resolveModelAlias(modelId, tierModels);\n\t\t\t\tif (aliasTarget) {\n\t\t\t\t\tconst slashIndex = aliasTarget.indexOf(\"/\");\n\t\t\t\t\tif (slashIndex !== -1) {\n\t\t\t\t\t\tprovider = aliasTarget.substring(0, slashIndex);\n\t\t\t\t\t\tmodelId = aliasTarget.substring(slashIndex + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmodelId = aliasTarget;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst models = await session.modelRegistry.getAvailable();\n\t\t\t\tconst model = models.find((m) => m.provider === provider && m.id === modelId);\n\t\t\t\tif (!model) {\n\t\t\t\t\treturn error(id, \"set_model\", `Model not found: ${provider}/${modelId}`);\n\t\t\t\t}\n\t\t\t\tawait session.setModel(model);\n\t\t\t\treturn success(id, \"set_model\", model);\n\t\t\t}\n\n\t\t\tcase \"cycle_model\": {\n\t\t\t\tconst result = await session.cycleModel();\n\t\t\t\tif (!result) {\n\t\t\t\t\treturn success(id, \"cycle_model\", null);\n\t\t\t\t}\n\t\t\t\treturn success(id, \"cycle_model\", result);\n\t\t\t}\n\n\t\t\tcase \"get_available_models\": {\n\t\t\t\tconst models = await session.modelRegistry.getAvailable();\n\t\t\t\treturn success(id, \"get_available_models\", { models });\n\t\t\t}\n\n\t\t\tcase \"get_tier_models\": {\n\t\t\t\tconst tierModels = session.getTierModels();\n\t\t\t\tconst merged = { ...DEFAULT_TIER_ALIASES, ...tierModels };\n\t\t\t\treturn success(id, \"get_tier_models\", { models: merged });\n\t\t\t}\n\n\t\t\tcase \"set_tier_models\": {\n\t\t\t\tconst { models } = command;\n\t\t\t\tconst validTiers = new Set([\"fast\", \"pro\", \"max\"]);\n\t\t\t\tfor (const key of Object.keys(models)) {\n\t\t\t\t\tif (!validTiers.has(key)) {\n\t\t\t\t\t\treturn error(id, \"set_tier_models\", `Invalid tier name: \"${key}\". Valid tiers are: fast, pro, max`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst mapping: Record<string, string> = {};\n\t\t\t\tif (models.fast !== undefined) mapping.fast = models.fast;\n\t\t\t\tif (models.pro !== undefined) mapping.pro = models.pro;\n\t\t\t\tif (models.max !== undefined) mapping.max = models.max;\n\t\t\t\tsession.setTierModels(mapping);\n\t\t\t\treturn success(id, \"set_tier_models\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Thinking\n\t\t\t// =================================================================\n\n\t\t\tcase \"set_thinking_level\": {\n\t\t\t\tsession.setThinkingLevel(command.level);\n\t\t\t\treturn success(id, \"set_thinking_level\");\n\t\t\t}\n\n\t\t\tcase \"cycle_thinking_level\": {\n\t\t\t\tconst level = session.cycleThinkingLevel();\n\t\t\t\tif (!level) {\n\t\t\t\t\treturn success(id, \"cycle_thinking_level\", null);\n\t\t\t\t}\n\t\t\t\treturn success(id, \"cycle_thinking_level\", { level });\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Queue Modes\n\t\t\t// =================================================================\n\n\t\t\tcase \"set_steering_mode\": {\n\t\t\t\tsession.setSteeringMode(command.mode);\n\t\t\t\treturn success(id, \"set_steering_mode\");\n\t\t\t}\n\n\t\t\tcase \"set_follow_up_mode\": {\n\t\t\t\tsession.setFollowUpMode(command.mode);\n\t\t\t\treturn success(id, \"set_follow_up_mode\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Compaction\n\t\t\t// =================================================================\n\n\t\t\tcase \"compact\": {\n\t\t\t\tconst result = await session.compact(command.customInstructions);\n\t\t\t\treturn success(id, \"compact\", result);\n\t\t\t}\n\n\t\t\tcase \"set_auto_compaction\": {\n\t\t\t\tsession.setAutoCompactionEnabled(command.enabled);\n\t\t\t\treturn success(id, \"set_auto_compaction\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Retry\n\t\t\t// =================================================================\n\n\t\t\tcase \"set_auto_retry\": {\n\t\t\t\tsession.setAutoRetryEnabled(command.enabled);\n\t\t\t\treturn success(id, \"set_auto_retry\");\n\t\t\t}\n\n\t\t\tcase \"abort_retry\": {\n\t\t\t\tsession.abortRetry();\n\t\t\t\treturn success(id, \"abort_retry\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Bash\n\t\t\t// =================================================================\n\n\t\t\tcase \"bash\": {\n\t\t\t\tconst result = await session.executeBash(command.command);\n\t\t\t\treturn success(id, \"bash\", result);\n\t\t\t}\n\n\t\t\tcase \"abort_bash\": {\n\t\t\t\tsession.abortBash();\n\t\t\t\treturn success(id, \"abort_bash\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Session\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_session_stats\": {\n\t\t\t\tconst stats = session.getSessionStats();\n\t\t\t\treturn success(id, \"get_session_stats\", stats);\n\t\t\t}\n\n\t\t\tcase \"export_html\": {\n\t\t\t\tconst path = await session.exportToHtml(command.outputPath);\n\t\t\t\treturn success(id, \"export_html\", { path });\n\t\t\t}\n\n\t\t\tcase \"switch_session\": {\n\t\t\t\tconst result = await runtimeHost.switchSession(command.sessionPath);\n\t\t\t\tif (!result.cancelled) {\n\t\t\t\t\tsession = runtimeHost.session;\n\t\t\t\t}\n\t\t\t\treturn success(id, \"switch_session\", result);\n\t\t\t}\n\n\t\t\tcase \"fork\": {\n\t\t\t\tconst previousName = session.sessionManager.getSessionName();\n\t\t\t\tconst forkPosition = (command as any).position === \"at\" ? { position: \"at\" as const } : undefined;\n\t\t\t\tconst result = await runtimeHost.fork(command.entryId, forkPosition);\n\t\t\t\tif (!result.cancelled) {\n\t\t\t\t\tconst newName = previousName ? `fork: ${previousName}` : \"fork\";\n\t\t\t\t\tsession = runtimeHost.session;\n\t\t\t\t\tsession.sessionManager.appendSessionInfo(newName);\n\t\t\t\t\tsession.sessionManager.flush();\n\t\t\t\t\tawait rebindSession();\n\t\t\t\t}\n\t\t\t\treturn success(id, \"fork\", {\n\t\t\t\t\ttext: result.selectedText,\n\t\t\t\t\tcancelled: result.cancelled,\n\t\t\t\t\tnewSessionFile: session.sessionManager.getSessionFile(),\n\t\t\t\t\tnewSessionId: session.sessionManager.getSessionId(),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"navigate_tree\": {\n\t\t\t\tconst navResult = await session.navigateTree(command.targetId, {\n\t\t\t\t\tsummarize: command.summarize ?? false,\n\t\t\t\t\tskipFiles: command.skipFiles,\n\t\t\t\t});\n\t\t\t\treturn success(id, \"navigate_tree\", { cancelled: navResult.cancelled });\n\t\t\t}\n\n\t\t\tcase \"rollback_preview\": {\n\t\t\t\tconst previewResult = await session.previewRollback(command.targetId);\n\t\t\t\treturn success(id, \"rollback_preview\", previewResult);\n\t\t\t}\n\n\t\t\tcase \"clone\": {\n\t\t\t\tconst leafId = session.sessionManager.getLeafId();\n\t\t\t\tif (!leafId) {\n\t\t\t\t\treturn error(id, \"clone\", \"Cannot clone session: no current entry selected\");\n\t\t\t\t}\n\t\t\t\tconst result = await runtimeHost.fork(leafId, { position: \"at\" });\n\t\t\t\tif (!result.cancelled) {\n\t\t\t\t\tawait rebindSession();\n\t\t\t\t}\n\t\t\t\treturn success(id, \"clone\", { cancelled: result.cancelled });\n\t\t\t}\n\n\t\t\tcase \"get_fork_messages\": {\n\t\t\t\tconst messages = session.getUserMessagesForForking();\n\t\t\t\treturn success(id, \"get_fork_messages\", { messages });\n\t\t\t}\n\n\t\t\tcase \"get_last_assistant_text\": {\n\t\t\t\tconst text = session.getLastAssistantText();\n\t\t\t\treturn success(id, \"get_last_assistant_text\", { text });\n\t\t\t}\n\n\t\t\tcase \"set_session_name\": {\n\t\t\t\tconst name = command.name.trim();\n\t\t\t\tif (!name) {\n\t\t\t\t\treturn error(id, \"set_session_name\", \"Session name cannot be empty\");\n\t\t\t\t}\n\t\t\t\tsession.setSessionName(name);\n\t\t\t\treturn success(id, \"set_session_name\");\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Messages\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_messages\": {\n\t\t\t\treturn success(id, \"get_messages\", { messages: session.messages });\n\t\t\t}\n\n\t\t\tcase \"get_full_messages\": {\n\t\t\t\tconst allEntries = session.sessionManager.getEntries();\n\t\t\t\tconst messageEntries = allEntries.filter((e) => e.type === \"message\");\n\t\t\t\tconst persistedMessages = messageEntries.map((e) => (e as { message: AgentMessage }).message);\n\t\t\t\tconst persistedSet = new Set(persistedMessages);\n\n\t\t\t\tconst memoryMessages = session.messages;\n\t\t\t\tconst unPersisted: AgentMessage[] = [];\n\t\t\t\tfor (let i = memoryMessages.length - 1; i >= 0; i--) {\n\t\t\t\t\tconst msg = memoryMessages[i];\n\t\t\t\t\tif (persistedSet.has(msg)) break;\n\t\t\t\t\tif (msg.role === \"compactionSummary\") continue;\n\t\t\t\t\tunPersisted.unshift(msg);\n\t\t\t\t}\n\n\t\t\t\tconst allMessages = [...persistedMessages, ...unPersisted];\n\t\t\t\tconst totalCount = allMessages.length;\n\n\t\t\t\tconst leafId = session.sessionManager.getLeafId();\n\n\t\t\t\tconst treeEntries = allEntries.map((e) => ({\n\t\t\t\t\tid: e.id,\n\t\t\t\t\tparentId: e.parentId,\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tlabel:\n\t\t\t\t\t\te.type === \"message\"\n\t\t\t\t\t\t\t? (e as any).message?.role\n\t\t\t\t\t\t\t: e.type === \"custom\"\n\t\t\t\t\t\t\t\t? (e as any).customType\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t}));\n\n\t\t\t\tconst customEntries = allEntries\n\t\t\t\t\t.filter((e) => e.type === \"custom\")\n\t\t\t\t\t.map((e) => ({\n\t\t\t\t\t\tid: e.id,\n\t\t\t\t\t\tcustomType: (e as any).customType ?? \"unknown\",\n\t\t\t\t\t\tdata: (e as any).data,\n\t\t\t\t\t\ttimestamp: new Date(e.timestamp).getTime(),\n\t\t\t\t\t}));\n\n\t\t\t\tconst compactionEntries = allEntries\n\t\t\t\t\t.filter((e) => e.type === \"compaction\")\n\t\t\t\t\t.map((e) => ({\n\t\t\t\t\t\tid: e.id,\n\t\t\t\t\t\tsummary: (e as any).summary ?? \"\",\n\t\t\t\t\t\ttokensBefore: (e as any).tokensBefore,\n\t\t\t\t\t\ttimestamp: new Date(e.timestamp).getTime(),\n\t\t\t\t\t}));\n\n\t\t\t\tif (command.limit !== undefined) {\n\t\t\t\t\tconst limit = command.limit;\n\t\t\t\t\tlet startIndex = 0;\n\t\t\t\t\tif (command.afterEntryId) {\n\t\t\t\t\t\tconst idx = messageEntries.findIndex((e) => e.id === command.afterEntryId);\n\t\t\t\t\t\tif (idx !== -1) {\n\t\t\t\t\t\t\tstartIndex = idx + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst page = allMessages.slice(startIndex, startIndex + limit);\n\t\t\t\t\tconst hasMore = startIndex + limit < totalCount;\n\t\t\t\t\tconst lastPersisted = messageEntries[Math.min(startIndex + limit, messageEntries.length) - 1];\n\t\t\t\t\treturn success(id, \"get_full_messages\", {\n\t\t\t\t\t\tmessages: page,\n\t\t\t\t\t\thasMore,\n\t\t\t\t\t\ttotalCount,\n\t\t\t\t\t\tnextCursor: hasMore && lastPersisted ? lastPersisted.id : null,\n\t\t\t\t\t\ttree: { entries: treeEntries, leafId },\n\t\t\t\t\t\tcustomEntries,\n\t\t\t\t\t\tcompactionEntries,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn success(id, \"get_full_messages\", {\n\t\t\t\t\tmessages: allMessages,\n\t\t\t\t\thasMore: false,\n\t\t\t\t\ttotalCount,\n\t\t\t\t\tnextCursor: null,\n\t\t\t\t\ttree: { entries: treeEntries, leafId },\n\t\t\t\t\tcustomEntries,\n\t\t\t\t\tcompactionEntries,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"get_tree\": {\n\t\t\t\tconst entries = session.sessionManager.getEntries();\n\t\t\t\tconst treeEntries = entries.map((e) => ({\n\t\t\t\t\tid: e.id,\n\t\t\t\t\tparentId: e.parentId,\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tlabel:\n\t\t\t\t\t\te.type === \"message\"\n\t\t\t\t\t\t\t? (e as any).message?.role\n\t\t\t\t\t\t\t: e.type === \"custom\"\n\t\t\t\t\t\t\t\t? (e as any).customType\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_tree\", { entries: treeEntries, leafId: session.sessionManager.getLeafId() });\n\t\t\t}\n\t\t\tcase \"get_tree_with_leaf\": {\n\t\t\t\tconst entries = session.sessionManager.getEntries();\n\t\t\t\tconst treeEntries = entries.map((e) => ({\n\t\t\t\t\tid: e.id,\n\t\t\t\t\tparentId: e.parentId,\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tlabel:\n\t\t\t\t\t\te.type === \"message\"\n\t\t\t\t\t\t\t? (e as any).message?.role\n\t\t\t\t\t\t\t: e.type === \"custom\"\n\t\t\t\t\t\t\t\t? (e as any).customType\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_tree_with_leaf\", {\n\t\t\t\t\tentries: treeEntries,\n\t\t\t\t\tleafId: session.sessionManager.getLeafId(),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Resources (skills, extensions, tools)\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_skills\": {\n\t\t\t\tconst { skills } = session.resourceLoader.getSkills();\n\t\t\t\tconst rpcSkills: RpcSkill[] = skills.map((s) => ({\n\t\t\t\t\tname: s.name,\n\t\t\t\t\tdescription: s.description,\n\t\t\t\t\tfilePath: s.filePath,\n\t\t\t\t\tbaseDir: s.baseDir,\n\t\t\t\t\tsourceInfo: s.sourceInfo,\n\t\t\t\t\tdisableModelInvocation: s.disableModelInvocation,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_skills\", { skills: rpcSkills });\n\t\t\t}\n\n\t\t\tcase \"get_extensions\": {\n\t\t\t\tconst { extensions } = session.resourceLoader.getExtensions();\n\t\t\t\tconst rpcExtensions: RpcExtension[] = extensions.map((e) => ({\n\t\t\t\t\tpath: e.path,\n\t\t\t\t\tresolvedPath: e.resolvedPath,\n\t\t\t\t\tsourceInfo: e.sourceInfo,\n\t\t\t\t\ttoolNames: Array.from(e.tools.keys()),\n\t\t\t\t\tcommandNames: Array.from(e.commands.keys()),\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_extensions\", { extensions: rpcExtensions });\n\t\t\t}\n\n\t\t\tcase \"get_tools\": {\n\t\t\t\tconst allTools = session.extensionRunner.getAllRegisteredTools();\n\t\t\t\tconst rpcTools: RpcTool[] = allTools.map((t) => ({\n\t\t\t\t\tname: t.definition.name,\n\t\t\t\t\tlabel: t.definition.label,\n\t\t\t\t\tdescription: t.definition.description,\n\t\t\t\t\tsourceInfo: t.sourceInfo,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_tools\", { tools: rpcTools });\n\t\t\t}\n\n\t\t\tcase \"get_mcp_servers\": {\n\t\t\t\tconst servers: RpcMcpServer[] = session.getMcpConnections();\n\t\t\t\treturn success(id, \"get_mcp_servers\", { servers });\n\t\t\t}\n\n\t\t\tcase \"mcp_toggle_server\": {\n\t\t\t\ttry {\n\t\t\t\t\tawait session.toggleMcpServer(command.name, command.enabled);\n\t\t\t\t\treturn success(id, \"mcp_toggle_server\");\n\t\t\t\t} catch (e) {\n\t\t\t\t\treturn error(id, \"mcp_toggle_server\", e instanceof Error ? e.message : String(e));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcase \"mcp_restart_server\": {\n\t\t\t\ttry {\n\t\t\t\t\tawait session.restartMcpServer(command.name);\n\t\t\t\t\treturn success(id, \"mcp_restart_server\");\n\t\t\t\t} catch (e) {\n\t\t\t\t\treturn error(id, \"mcp_restart_server\", e instanceof Error ? e.message : String(e));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// =================================================================\n\t\t\t// Commands (available for invocation via prompt)\n\t\t\t// =================================================================\n\n\t\t\tcase \"get_commands\": {\n\t\t\t\tconst commands: RpcSlashCommand[] = [];\n\n\t\t\t\tfor (const command of session.extensionRunner.getRegisteredCommands()) {\n\t\t\t\t\tcommands.push({\n\t\t\t\t\t\tname: command.invocationName,\n\t\t\t\t\t\tdescription: command.description,\n\t\t\t\t\t\tsource: \"extension\",\n\t\t\t\t\t\tsourceInfo: command.sourceInfo,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tfor (const template of session.promptTemplates) {\n\t\t\t\t\tcommands.push({\n\t\t\t\t\t\tname: template.name,\n\t\t\t\t\t\tdescription: template.description,\n\t\t\t\t\t\tsource: \"prompt\",\n\t\t\t\t\t\tsourceInfo: template.sourceInfo,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tfor (const skill of session.resourceLoader.getSkills().skills) {\n\t\t\t\t\tcommands.push({\n\t\t\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\t\t\tdescription: skill.description,\n\t\t\t\t\t\tsource: \"skill\",\n\t\t\t\t\t\tsourceInfo: skill.sourceInfo,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn success(id, \"get_commands\", { commands });\n\t\t\t}\n\n\t\t\tcase \"get_settings\": {\n\t\t\t\tconst scope = command.scope;\n\t\t\t\tlet data: import(\"../../core/settings-manager.js\").Settings;\n\t\t\t\tif (scope === \"global\") {\n\t\t\t\t\tdata = session.settingsManager.getGlobalSettings();\n\t\t\t\t} else if (scope === \"project\") {\n\t\t\t\t\tdata = session.settingsManager.getProjectSettings();\n\t\t\t\t} else {\n\t\t\t\t\tdata = session.settingsManager.getGlobalSettings();\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_settings\", data);\n\t\t\t}\n\n\t\t\tcase \"set_settings\": {\n\t\t\t\ttype Scope = import(\"../../core/settings-manager.js\").SettingsScope;\n\t\t\t\tconst setScope = (command.scope as Scope | undefined) ?? \"global\";\n\t\t\t\tsession.settingsManager.applyOverrides(command.settings, setScope);\n\t\t\t\treturn success(id, \"set_settings\");\n\t\t\t}\n\n\t\t\tcase \"get_context_usage\": {\n\t\t\t\tconst usage = session.getContextUsage();\n\t\t\t\tif (!usage) {\n\t\t\t\t\treturn success(id, \"get_context_usage\", {\n\t\t\t\t\t\ttokens: null,\n\t\t\t\t\t\tcontextWindow: session.model?.contextWindow ?? 0,\n\t\t\t\t\t\tpercent: null,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_context_usage\", usage);\n\t\t\t}\n\n\t\t\tcase \"get_system_prompt\": {\n\t\t\t\tconst systemPrompt = session.resourceLoader.getSystemPrompt() ?? \"\";\n\t\t\t\tconst appendSystemPrompt = session.resourceLoader.getAppendSystemPrompt();\n\t\t\t\treturn success(id, \"get_system_prompt\", { systemPrompt, appendSystemPrompt });\n\t\t\t}\n\n\t\t\tcase \"get_active_tools\": {\n\t\t\t\tconst toolNames = session.getActiveToolNames();\n\t\t\t\treturn success(id, \"get_active_tools\", { toolNames });\n\t\t\t}\n\n\t\t\tcase \"set_active_tools\": {\n\t\t\t\tsession.setActiveToolsByName(command.toolNames);\n\t\t\t\treturn success(id, \"set_active_tools\");\n\t\t\t}\n\n\t\t\tcase \"get_queue\": {\n\t\t\t\treturn success(id, \"get_queue\", {\n\t\t\t\t\tsteering: [...session.getSteeringMessages()],\n\t\t\t\t\tfollowUp: [...session.getFollowUpMessages()],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"clear_queue\": {\n\t\t\t\tconst cleared = session.clearQueue();\n\t\t\t\treturn success(id, \"clear_queue\", cleared);\n\t\t\t}\n\n\t\t\tcase \"get_flags\": {\n\t\t\t\tconst flagsMap = session.extensionRunner.getFlags();\n\t\t\t\tconst flags: Array<{\n\t\t\t\t\tname: string;\n\t\t\t\t\tdescription?: string;\n\t\t\t\t\ttype: \"boolean\" | \"string\";\n\t\t\t\t\tdefault?: boolean | string;\n\t\t\t\t\textensionPath: string;\n\t\t\t\t}> = [];\n\t\t\t\tfor (const [name, flag] of flagsMap) {\n\t\t\t\t\tflags.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tdescription: flag.description,\n\t\t\t\t\t\ttype: flag.type,\n\t\t\t\t\t\tdefault: flag.default,\n\t\t\t\t\t\textensionPath: flag.extensionPath,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_flags\", { flags });\n\t\t\t}\n\n\t\t\tcase \"get_flag_values\": {\n\t\t\t\tconst valuesMap = session.extensionRunner.getFlagValues();\n\t\t\t\tconst values: Record<string, boolean | string> = {};\n\t\t\t\tfor (const [name, value] of valuesMap) {\n\t\t\t\t\tvalues[name] = value;\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_flag_values\", { values });\n\t\t\t}\n\n\t\t\tcase \"set_flag\": {\n\t\t\t\tsession.extensionRunner.setFlagValue(command.name, command.value);\n\t\t\t\treturn success(id, \"set_flag\");\n\t\t\t}\n\n\t\t\tcase \"reload\": {\n\t\t\t\tawait session.reload();\n\t\t\t\tsession = runtimeHost.session;\n\t\t\t\treturn success(id, \"reload\");\n\t\t\t}\n\n\t\t\tcase \"set_cwd\": {\n\t\t\t\tawait session.setCwd(command.cwd);\n\t\t\t\treturn success(id, \"set_cwd\");\n\t\t\t}\n\n\t\t\tcase \"get_agents_files\": {\n\t\t\t\tconst result = session.resourceLoader.getAgentsFiles();\n\t\t\t\treturn success(id, \"get_agents_files\", { agentsFiles: result.agentsFiles });\n\t\t\t}\n\n\t\t\tcase \"get_agents\": {\n\t\t\t\tconst discovery = discoverAgents(runtimeHost.cwd, \"both\");\n\t\t\t\tconst builtin = getBuiltinAgents();\n\t\t\t\tconst agents = [...builtin, ...discovery.agents].map((a) => ({\n\t\t\t\t\tname: a.name,\n\t\t\t\t\tdescription: a.description,\n\t\t\t\t\ttier: a.tier,\n\t\t\t\t\ttools: a.tools,\n\t\t\t\t\tpermissionMode: a.permissionMode,\n\t\t\t\t\tsource: a.source,\n\t\t\t\t\tfilePath: a.filePath,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_agents\", { agents });\n\t\t\t}\n\n\t\t\tcase \"switch_agent\": {\n\t\t\t\tconst agentName = (command as { agentName: string }).agentName;\n\t\t\t\tconst discovery = discoverAgents(runtimeHost.cwd, \"both\");\n\t\t\t\tconst builtin = getBuiltinAgents();\n\t\t\t\tconst agent = [...builtin, ...discovery.agents].find((a) => a.name === agentName);\n\t\t\t\tif (!agent) {\n\t\t\t\t\treturn error(id, \"switch_agent\", `Agent \"${agentName}\" not found`);\n\t\t\t\t}\n\t\t\t\tawait session.applyAgentConfig(agent);\n\t\t\t\treturn success(id, \"switch_agent\", {\n\t\t\t\t\tagentName: agent.name,\n\t\t\t\t\ttools: agent.tools ?? [],\n\t\t\t\t\ttier: agent.tier,\n\t\t\t\t\tthinkingLevel: agent.thinkingLevel,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"get_current_agent\": {\n\t\t\t\tconst currentAgent = session.getCurrentAgent();\n\t\t\t\treturn success(id, \"get_current_agent\", { agentName: currentAgent });\n\t\t\t}\n\n\t\t\tcase \"get_modified_files\": {\n\t\t\t\tconst fileSnapshotManager = (session as any).fileSnapshotManager;\n\t\t\t\tif (!fileSnapshotManager) {\n\t\t\t\t\treturn success(id, \"get_modified_files\", { files: [] });\n\t\t\t\t}\n\t\t\t\tconst files = fileSnapshotManager.getModifiedFiles({\n\t\t\t\t\tfromEntryId: command.fromEntryId,\n\t\t\t\t\ttoEntryId: command.toEntryId,\n\t\t\t\t});\n\t\t\t\treturn success(id, \"get_modified_files\", { files });\n\t\t\t}\n\n\t\t\tcase \"get_file_diff\": {\n\t\t\t\tconst fileSnapshotManager = (session as any).fileSnapshotManager;\n\t\t\t\tif (!fileSnapshotManager) {\n\t\t\t\t\treturn success(id, \"get_file_diff\", null);\n\t\t\t\t}\n\t\t\t\tconst diff = fileSnapshotManager.getFileDiff({\n\t\t\t\t\tfilePath: command.filePath,\n\t\t\t\t\tfromEntryId: command.fromEntryId,\n\t\t\t\t\ttoEntryId: command.toEntryId,\n\t\t\t\t});\n\t\t\t\tif (!diff) {\n\t\t\t\t\treturn success(id, \"get_file_diff\", null);\n\t\t\t\t}\n\t\t\t\treturn success(id, \"get_file_diff\", {\n\t\t\t\t\tpath: diff.path,\n\t\t\t\t\toldContent: diff.oldContent,\n\t\t\t\t\tnewContent: diff.newContent,\n\t\t\t\t\tunifiedDiff: diff.unifiedDiff,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"get_batch_diffs\": {\n\t\t\t\tconst fileSnapshotManager = (session as any).fileSnapshotManager;\n\t\t\t\tif (!fileSnapshotManager) {\n\t\t\t\t\treturn success(id, \"get_batch_diffs\", {\n\t\t\t\t\t\tfiles: [],\n\t\t\t\t\t\tsummary: { totalFiles: 0, added: 0, modified: 0, deleted: 0 },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst result = fileSnapshotManager.getBatchDiffs({\n\t\t\t\t\tfromEntryId: command.fromEntryId,\n\t\t\t\t\ttoEntryId: command.toEntryId,\n\t\t\t\t});\n\t\t\t\tconst serializedFiles = result.files.map((f: any) => ({\n\t\t\t\t\tpath: f.path,\n\t\t\t\t\tstatus: f.status,\n\t\t\t\t\tdiff: f.diff\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tpath: f.diff.path,\n\t\t\t\t\t\t\t\toldContent: f.diff.oldContent,\n\t\t\t\t\t\t\t\tnewContent: f.diff.newContent,\n\t\t\t\t\t\t\t\tunifiedDiff: f.diff.unifiedDiff,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: null,\n\t\t\t\t}));\n\t\t\t\treturn success(id, \"get_batch_diffs\", {\n\t\t\t\t\tfiles: serializedFiles,\n\t\t\t\t\tsummary: result.summary,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"get_file_history\": {\n\t\t\t\tconst fileSnapshotManager = (session as any).fileSnapshotManager;\n\t\t\t\tif (!fileSnapshotManager) {\n\t\t\t\t\treturn success(id, \"get_file_history\", { history: [] });\n\t\t\t\t}\n\t\t\t\tconst result = fileSnapshotManager.getFileHistory({\n\t\t\t\t\tfilePath: command.filePath,\n\t\t\t\t});\n\t\t\t\treturn success(id, \"get_file_history\", { history: result });\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tconst unknownCommand = command as { type: string };\n\t\t\t\treturn error(undefined, unknownCommand.type, `Unknown command: ${unknownCommand.type}`);\n\t\t\t}\n\t\t}\n\t};\n\n\t/**\n\t * Check if shutdown was requested and perform shutdown if so.\n\t * Called after handling each command when waiting for the next command.\n\t */\n\tlet detachInput = () => {};\n\n\tasync function shutdown(exitCode = 0): Promise<never> {\n\t\tif (shuttingDown) {\n\t\t\tprocess.exit(exitCode);\n\t\t}\n\t\tshuttingDown = true;\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tunsubscribe?.();\n\t\tawait runtimeHost.dispose();\n\t\tdetachInput();\n\t\tprocess.stdin.pause();\n\t\tprocess.exit(exitCode);\n\t}\n\n\tasync function checkShutdownRequested(): Promise<void> {\n\t\tif (!shutdownRequested) return;\n\t\tawait shutdown();\n\t}\n\n\tconst handleInputLine = async (line: string) => {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(line);\n\t\t} catch (parseError: unknown) {\n\t\t\toutput(\n\t\t\t\terror(\n\t\t\t\t\tundefined,\n\t\t\t\t\t\"parse\",\n\t\t\t\t\t`Failed to parse command: ${parseError instanceof Error ? parseError.message : String(parseError)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle extension UI responses\n\t\tif (\n\t\t\ttypeof parsed === \"object\" &&\n\t\t\tparsed !== null &&\n\t\t\t\"type\" in parsed &&\n\t\t\tparsed.type === \"extension_ui_response\"\n\t\t) {\n\t\t\tconst response = parsed as RpcExtensionUIResponse;\n\t\t\tconst pending = pendingExtensionRequests.get(response.id);\n\t\t\tif (pending) {\n\t\t\t\tpendingExtensionRequests.delete(response.id);\n\t\t\t\tpending.resolve(response);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle channel data from RPC Client\n\t\tif (\n\t\t\ttypeof parsed === \"object\" &&\n\t\t\tparsed !== null &&\n\t\t\t\"type\" in parsed &&\n\t\t\tparsed.type === \"channel_data\" &&\n\t\t\t\"name\" in parsed\n\t\t) {\n\t\t\tchannelManager.handleInbound(parsed as ChannelDataMessage);\n\t\t\treturn;\n\t\t}\n\n\t\tconst command = parsed as RpcCommand;\n\t\ttry {\n\t\t\tconst response = await handleCommand(command);\n\t\t\tif (response) {\n\t\t\t\toutput(response);\n\t\t\t}\n\t\t\tawait checkShutdownRequested();\n\t\t} catch (commandError: unknown) {\n\t\t\toutput(\n\t\t\t\terror(\n\t\t\t\t\tcommand.id,\n\t\t\t\t\tcommand.type,\n\t\t\t\t\tcommandError instanceof Error ? commandError.message : String(commandError),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t};\n\n\tconst onInputEnd = () => {\n\t\tvoid shutdown();\n\t};\n\tprocess.stdin.on(\"end\", onInputEnd);\n\n\tdetachInput = (() => {\n\t\tconst detachJsonl = attachJsonlLineReader(process.stdin, (line) => {\n\t\t\tvoid handleInputLine(line);\n\t\t});\n\t\treturn () => {\n\t\t\tdetachJsonl();\n\t\t\tprocess.stdin.off(\"end\", onInputEnd);\n\t\t};\n\t})();\n\n\t// Keep process alive forever\n\treturn new Promise(() => {});\n}\n"]}
@@ -854,6 +854,8 @@ export async function runRpcMode(runtimeHost) {
854
854
  tier: a.tier,
855
855
  tools: a.tools,
856
856
  permissionMode: a.permissionMode,
857
+ source: a.source,
858
+ filePath: a.filePath,
857
859
  }));
858
860
  return success(id, "get_agents", { agents });
859
861
  }