@agimon-ai/po-file-edit 0.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/dist/apiContracts.cjs +156 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +151 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/extensions/server.cjs +1934 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +1903 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +1344 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/index.cjs +0 -0
- package/dist/index.d.mts +91 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1 -0
- package/package.json +129 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","names":["isInside","BINARY_SAMPLE_BYTES","hasCode","fs","hasCode","fs","fs","fs","routes","fs","rootSession","channelFileEdits","apiRoute","hookToolExecutionEnd","hookToolExecutionStart"],"sources":["../../src/services/doomIgnore/index.ts","../../src/services/lineDiff/index.ts","../../src/services/editTracker/index.ts","../../src/services/fileEditPaths/index.ts","../../src/services/gitStatus/index.ts","../../src/services/snapshotStore/index.ts","../../src/services/fileChanges/index.ts","../../src/services/timelineStore/index.ts","../../src/services/treeManifest/index.ts","../../src/types/webFiles.ts","../../src/types/apiRoutes.ts","../../src/types/fileEditsApi.ts","../../src/services/fileEditsApi/index.ts","../../src/services/sessionStateSweep/index.ts","../../src/services/webFilesChannel/index.ts","../../src/services/fileEditSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/(backend)/channel/fileEdits.server.ts","../../src/extensions/workspaces/sessions/(backend)/api/file-edits/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-start.server.ts","../../generated/server.ts"],"sourcesContent":["import ignore from 'ignore';\n\nimport type { FilesItemView } from '../../types/webFiles';\n\nfunction matchablePath(relPath: string): string | undefined {\n const normalized = relPath.replace(/\\\\/gu, '/');\n if (\n normalized.length === 0 ||\n normalized.includes('\\0') ||\n normalized === '..' ||\n normalized.startsWith('../') ||\n normalized.startsWith('/') ||\n /^[A-Za-z]:\\//u.test(normalized)\n ) {\n return undefined;\n }\n return normalized;\n}\n\n/**\n * Builds the project's ignore test, or nothing when there are no rules.\n *\n * Shared because the same rules are needed twice: once when deciding what to\n * record, and once when deciding what to list. A caller that built its own\n * matcher would let the two drift apart.\n *\n * The test answers false for anything it cannot safely match, so a path that\n * escapes the project stays visible rather than being hidden by accident.\n */\nexport function createDoomIgnoreMatcher(content: string): ((relPath: string) => boolean) | undefined {\n try {\n if (content.trim().length === 0) return undefined;\n const matcher = ignore().add(content);\n return (relPath: string): boolean => {\n const candidate = matchablePath(relPath);\n return candidate !== undefined && matcher.ignores(candidate);\n };\n } catch {\n return undefined;\n }\n}\n\n/** Applies project-local gitignore rules while keeping unsafe candidates visible. */\nexport function filterDoomIgnoredFiles(items: FilesItemView[], content: string): FilesItemView[] {\n const ignored = createDoomIgnoreMatcher(content);\n if (ignored === undefined) return items;\n return items.filter((item) => !ignored(item.relPath));\n}\n","import type { FileEditsDiffHunk, FileEditsDiffRow } from '../../types/fileEditsApi';\n\n/**\n * The line difference between two versions of a file, as bounded hunks.\n *\n * This is deliberately the package's own rather than Pi's `generateDiffString`:\n * that helper reaches the session process through an optional peer dependency,\n * and these routes run in a host that imports only the built entry, so its\n * presence is an assumption rather than a fact. A line diff is small enough to\n * own outright, and owning it means the wire carries structured rows instead of\n * a formatted string the browser has to parse back apart.\n *\n * Bounded twice over. Common prefix and suffix are trimmed first, which is what\n * makes a one-line change in a large file cheap. What remains is matched with a\n * longest-common-subsequence table, and only while that table stays under\n * MAX_MATRIX_CELLS: past it the changed region is reported as one replacement\n * rather than growing a matrix that would outweigh the file.\n */\n\n/** Lines of unchanged context kept either side of a change. */\nconst CONTEXT_LINES = 3;\n\n/** The largest LCS table worth building; past it the changed region is one replacement. */\nconst MAX_MATRIX_CELLS = 1_000_000;\n\nexport interface LineDiffResult {\n hunks: FileEditsDiffHunk[];\n additions: number;\n removals: number;\n /** True when the changed region was too large to match line by line. */\n approximate: boolean;\n}\n\n/** One step of the match, before it is grouped into hunks. */\ninterface DiffStep {\n marker: '+' | '-' | ' ';\n content: string;\n /** Line number in whichever side owns the step, 1-based. */\n line: number;\n}\n\nfunction splitLines(text: string): string[] {\n const normalized = text.replaceAll('\\r\\n', '\\n').replaceAll('\\r', '\\n');\n return normalized === '' ? [] : normalized.split('\\n');\n}\n\n/** How many leading lines the two sides share. */\nfunction commonPrefix(before: readonly string[], after: readonly string[]): number {\n const limit = Math.min(before.length, after.length);\n let count = 0;\n while (count < limit && before[count] === after[count]) count += 1;\n return count;\n}\n\n/** How many trailing lines the two sides share, never overlapping the prefix. */\nfunction commonSuffix(before: readonly string[], after: readonly string[], prefix: number): number {\n const limit = Math.min(before.length, after.length) - prefix;\n let count = 0;\n while (count < limit && before[before.length - 1 - count] === after[after.length - 1 - count]) count += 1;\n return count;\n}\n\n/**\n * The classic LCS length table. Only ever called once the caller has checked\n * the region against MAX_MATRIX_CELLS, so the allocation is bounded.\n */\nfunction lcsTable(before: readonly string[], after: readonly string[]): Uint32Array {\n const rows = before.length + 1;\n const columns = after.length + 1;\n const table = new Uint32Array(rows * columns);\n for (let row = before.length - 1; row >= 0; row -= 1) {\n for (let column = after.length - 1; column >= 0; column -= 1) {\n const index = row * columns + column;\n table[index] =\n before[row] === after[column]\n ? (table[index + columns + 1] ?? 0) + 1\n : Math.max(table[index + columns] ?? 0, table[index + 1] ?? 0);\n }\n }\n return table;\n}\n\n/** Walks the table to turn it back into an ordered run of keeps, removals and additions. */\nfunction backtrack(\n before: readonly string[],\n after: readonly string[],\n beforeStart: number,\n afterStart: number,\n): DiffStep[] {\n const table = lcsTable(before, after);\n const columns = after.length + 1;\n const steps: DiffStep[] = [];\n let row = 0;\n let column = 0;\n while (row < before.length && column < after.length) {\n if (before[row] === after[column]) {\n steps.push({ marker: ' ', content: after[column] ?? '', line: afterStart + column });\n row += 1;\n column += 1;\n continue;\n }\n if ((table[(row + 1) * columns + column] ?? 0) >= (table[row * columns + column + 1] ?? 0)) {\n steps.push({ marker: '-', content: before[row] ?? '', line: beforeStart + row });\n row += 1;\n continue;\n }\n steps.push({ marker: '+', content: after[column] ?? '', line: afterStart + column });\n column += 1;\n }\n while (row < before.length) {\n steps.push({ marker: '-', content: before[row] ?? '', line: beforeStart + row });\n row += 1;\n }\n while (column < after.length) {\n steps.push({ marker: '+', content: after[column] ?? '', line: afterStart + column });\n column += 1;\n }\n return steps;\n}\n\n/** The whole changed region as one replacement, for a region too large to match. */\nfunction wholesale(\n before: readonly string[],\n after: readonly string[],\n beforeStart: number,\n afterStart: number,\n): DiffStep[] {\n return [\n ...before.map((content, offset) => ({ marker: '-' as const, content, line: beforeStart + offset })),\n ...after.map((content, offset) => ({ marker: '+' as const, content, line: afterStart + offset })),\n ];\n}\n\n/** Groups the steps into hunks, keeping CONTEXT_LINES of unchanged text either side of a change. */\nfunction toHunks(steps: readonly DiffStep[]): FileEditsDiffHunk[] {\n const keep: boolean[] = Array.from({ length: steps.length }, () => false);\n for (const [index, step] of steps.entries()) {\n if (step.marker === ' ') continue;\n const from = Math.max(0, index - CONTEXT_LINES);\n const to = Math.min(steps.length - 1, index + CONTEXT_LINES);\n for (let cursor = from; cursor <= to; cursor += 1) keep[cursor] = true;\n }\n\n const hunks: FileEditsDiffHunk[] = [];\n let rows: FileEditsDiffRow[] = [];\n for (const [index, step] of steps.entries()) {\n if (!keep[index]) {\n if (rows.length > 0) {\n hunks.push({ start: rows[0]?.line ?? 1, rows });\n rows = [];\n }\n continue;\n }\n rows.push({ marker: step.marker, line: step.line, content: step.content });\n }\n if (rows.length > 0) hunks.push({ start: rows[0]?.line ?? 1, rows });\n return hunks;\n}\n\n/**\n * The difference between a file that did not exist and the one that now does.\n *\n * Kept separate from a two-sided diff because an empty baseline is the one case\n * where the trailing newline matters. `splitLines` reads \"a\\n\" as a line and\n * then an empty one, which is invisible in a normal diff because both sides\n * carry it and it cancels, but against nothing it would report a file of three\n * lines as four. A creation is counted on the lines the file actually holds.\n */\nexport function lineDiffFromEmpty(after: string): LineDiffResult {\n return lineDiff('', after.endsWith('\\n') ? after.slice(0, -1) : after);\n}\n\n/**\n * The difference between two versions of a file.\n *\n * Line numbers are the new file's for context and additions, and the old\n * file's for removals, which is the pairing a reader expects: the number\n * beside a line is where that line actually lives.\n */\nexport function lineDiff(before: string, after: string): LineDiffResult {\n const beforeLines = splitLines(before);\n const afterLines = splitLines(after);\n const prefix = commonPrefix(beforeLines, afterLines);\n const suffix = commonSuffix(beforeLines, afterLines, prefix);\n const beforeMiddle = beforeLines.slice(prefix, beforeLines.length - suffix);\n const afterMiddle = afterLines.slice(prefix, afterLines.length - suffix);\n\n const approximate = beforeMiddle.length * afterMiddle.length > MAX_MATRIX_CELLS;\n const middle = approximate\n ? wholesale(beforeMiddle, afterMiddle, prefix + 1, prefix + 1)\n : backtrack(beforeMiddle, afterMiddle, prefix + 1, prefix + 1);\n\n const head: DiffStep[] = beforeLines\n .slice(0, prefix)\n .map((content, offset) => ({ marker: ' ' as const, content, line: offset + 1 }));\n const tail: DiffStep[] = afterLines\n .slice(afterLines.length - suffix)\n .map((content, offset) => ({ marker: ' ' as const, content, line: afterLines.length - suffix + offset + 1 }));\n\n const steps = [...head, ...middle, ...tail];\n return {\n hunks: toHunks(steps),\n additions: steps.filter((step) => step.marker === '+').length,\n removals: steps.filter((step) => step.marker === '-').length,\n approximate,\n };\n}\n","import path from 'node:path';\n\nimport type { FileEditTool, TimelineEvent } from '../../types/domain';\nimport type { IEditTracker } from '../../types/editTracker';\nimport type { GitStatusPort } from '../../types/gitStatus';\nimport type { SnapshotStorePort } from '../../types/snapshotStore';\nimport type { ITimelineStore } from '../../types/timelineStore';\nimport type { TreeManifest, TreeManifestPort } from '../../types/treeManifest';\nimport { lineDiff, lineDiffFromEmpty } from '../lineDiff';\n\nconst EDIT_TOOL = 'edit';\nconst WRITE_TOOL = 'write';\nconst BASH_TOOL = 'bash';\n\n/**\n * Records what the session changed, and captures enough of it to be diffed.\n *\n * Two mechanisms, because one is not enough. `edit` and `write` name their file\n * in the call arguments, so the content is read before the tool runs and both\n * sides of the change are known exactly. `bash` names nothing reliable: the\n * agent can write a script and run it, and reading path-shaped tokens out of\n * the command would miss every file that script touches. So a bash call is\n * bracketed by tree manifests instead, and whatever moved between them is\n * recorded, however it was written.\n *\n * A manifest-found path is recorded without a baseline, because it was only\n * identified after it had already changed. That is a real limit and the wire\n * carries it as `origin: 'scan'` rather than pretending a diff exists.\n *\n * A manifest compares size and modification time, and it compares the tree as\n * this tracker last walked it against the tree now, so it answers whether a\n * file differs, never when it moved. A path a previous session left dirty, or\n * one a checkout or an install rewrote, differs exactly like a path the command\n * just wrote. So a candidate is called verified only when something actually\n * proves its bytes moved: it appeared, it vanished, its recorded size differs,\n * or its content hash differs from one this session already took. Absence of\n * proof is not proof, so a candidate that shows none of those is still\n * recorded, because the timeline is the evidence, but it goes out unverified\n * and every surface leaves it out.\n */\ninterface PendingEdit {\n tool: 'edit' | 'write';\n filePath: string;\n before: string | undefined;\n /**\n * Whether the file was there when the call began. A missing `before` cannot\n * say on its own: content is left uncaptured both for a file that does not\n * exist and for one too large or too binary to store.\n */\n existed: boolean;\n}\n\nfunction objectValue(value: unknown, key: string): string | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const found = (value as Record<string, unknown>)[key];\n return typeof found === 'string' ? found : undefined;\n}\n\nexport interface EditTrackerOptions {\n /** Injectable so a test can pin the recorded timestamps. */\n now?: () => number;\n /**\n * Decides whether a first-seen scan candidate actually changed. Optional:\n * without it the tracker keeps recording every candidate it cannot disprove.\n */\n git?: GitStatusPort;\n}\n\n/**\n * How far before a call started a write may claim to have happened and still be\n * attributed to it. A working tree can sit on a filesystem that stores whole\n * seconds, which floors a write made just after the call began to a stamp just\n * before it, and the cost of being generous here is one second of the staleness\n * this check exists to remove.\n */\nconst MODIFIED_TOLERANCE_MS = 1000;\n\nexport class EditTracker implements IEditTracker {\n private readonly pending = new Map<string, PendingEdit>();\n /** Start time per in-flight bash call, so its end knows what it may claim. */\n private readonly bracketed = new Map<string, number>();\n /**\n * The tree as this tracker last saw it. A bash call compares against it and\n * then replaces it, so each call costs one walk rather than two, and a change\n * made between calls is still noticed on the next one.\n */\n private manifest: TreeManifest | undefined;\n /**\n * This package's own storage, which the walk must never report. The timeline\n * and its snapshots can land inside the tree being watched, and recording\n * them would make every change cause another one.\n */\n private excluded: readonly string[] = [];\n /**\n * The project's own ignore rules. A path the project disowns is not worth a\n * git call, a read, or a stored copy of its content, so it is dropped before\n * it costs any of them.\n */\n private isIgnored: ((filePath: string) => boolean) | undefined;\n /**\n * The last content hash this session saw per path, from either mechanism.\n * A candidate whose hash has not moved was touched, not edited.\n */\n private readonly contents = new Map<string, string>();\n private readonly now: () => number;\n private readonly git: GitStatusPort | undefined;\n /**\n * The scan deferred by the last bash call. Every bracket joins this chain, so\n * at most one walk runs at a time and each one sees the baseline the previous\n * one left behind.\n */\n private scanning: Promise<void> = Promise.resolve();\n\n constructor(\n private readonly timeline: ITimelineStore,\n private readonly snapshots: SnapshotStorePort,\n private readonly manifests: TreeManifestPort,\n options: EditTrackerOptions = {},\n ) {\n this.now = options.now ?? Date.now;\n this.git = options.git;\n }\n\n /**\n * Forgets the previous session's tree so a new one does not inherit its\n * baseline, and takes the paths this session's own bookkeeping occupies.\n */\n reset(options: { exclude?: readonly string[]; isIgnored?: (filePath: string) => boolean } = {}): void {\n this.pending.clear();\n this.bracketed.clear();\n this.contents.clear();\n this.manifest = undefined;\n this.scanning = Promise.resolve();\n this.excluded = options.exclude ?? [];\n this.isIgnored = options.isIgnored;\n }\n\n /**\n * Awaits a scan still running in the background. A caller that is about to\n * clear the timeline needs this, or the scan appends after the clear.\n */\n async flush(): Promise<void> {\n await this.scanning;\n }\n async start(id: string, tool: string, args: unknown, cwd: string): Promise<void> {\n // A deferred scan still owns the baseline manifest, so it settles before\n // this bracket reads or replaces it.\n await this.scanning;\n if (tool === EDIT_TOOL || tool === WRITE_TOOL) {\n const supplied = objectValue(args, 'path');\n if (!supplied) return;\n const filePath = path.resolve(cwd, supplied);\n // Both are read before the tool runs. The fingerprint is the only thing\n // that tells a file being created from one whose content could not be\n // captured, because a capture answers undefined for either.\n const [before, fingerprint] = await Promise.all([\n this.snapshots.capture(filePath),\n this.manifests.fingerprint(filePath),\n ]);\n this.pending.set(id, { tool, filePath, before, existed: fingerprint !== undefined });\n return;\n }\n if (tool !== BASH_TOOL) return;\n // Read before the baseline walk, which is the one thing here that can take\n // long enough to matter: a write that races it belongs to this call.\n this.bracketed.set(id, this.now());\n // The first bash call of a session has nothing to compare against, so it\n // pays for the baseline walk; every later call reuses the previous end.\n this.manifest ??= await this.manifests.take(cwd, this.excluded);\n }\n\n async end(id: string, isError: boolean, cwd: string): Promise<void> {\n const pending = this.pending.get(id);\n this.pending.delete(id);\n const startedAt = this.bracketed.get(id);\n this.bracketed.delete(id);\n if (pending) {\n // A failed edit or write never landed, and its arguments named the file,\n // so there is nothing left to look for.\n if (!isError) await this.recordTool(pending);\n return;\n }\n // A failed command may still have written before it failed. Skipping the\n // walk would leave the baseline stale and hand those writes to whichever\n // call closes next.\n if (startedAt === undefined) return;\n // The walk is the expensive half of this hook and pi awaits the hook before\n // it hands the tool result back to the model. Deferring it onto the chain\n // moves that cost into the model's own thinking time instead of the user's\n // wait; start() joins the same chain, so ordering still holds.\n this.scanning = this.scanning.then(async () => {\n try {\n await this.recordScan(cwd, startedAt);\n } catch {\n // One failed scan must not poison the chain for every later call.\n }\n });\n }\n\n /** An `edit` or `write` whose file was read on both sides of the call. */\n private async recordTool(pending: PendingEdit): Promise<void> {\n const after = await this.snapshots.capture(pending.filePath);\n const created = !pending.existed;\n const counts = await this.countChanges(pending.before, after, created);\n if (after !== undefined) this.contents.set(pending.filePath, after);\n await this.append({\n version: 2,\n path: pending.filePath,\n tool: pending.tool,\n at: this.now(),\n origin: 'tool',\n ...(pending.before === undefined ? {} : { before: pending.before }),\n ...(after === undefined ? {} : { after }),\n ...(created ? { created: true } : {}),\n ...counts,\n });\n // The tool already accounted for this path, so the next bash comparison\n // must not report it a second time under its own name.\n await this.remember(pending.filePath);\n }\n\n /** Whatever a bash call actually edited, however the command wrote it. */\n private async recordScan(cwd: string, startedAt: number): Promise<void> {\n const before = this.manifest;\n if (before === undefined) return;\n const after = await this.manifests.take(cwd, this.excluded);\n this.manifest = after;\n const disowned = this.isIgnored;\n const named = this.manifests\n .changed(before, after)\n .filter((filePath) => disowned === undefined || !disowned(filePath));\n const candidates = await this.writtenDuring(named, startedAt);\n if (candidates.length === 0) return;\n const unchanged = await this.unchangedInGit(cwd, candidates);\n for (const filePath of candidates) {\n const known = this.contents.get(filePath);\n // Asked before the file is read: a path git vouches for is worth neither\n // the read nor the stored copy that reading it leaves behind.\n if (known === undefined && unchanged.has(filePath)) continue;\n const beforePrint = before.entries.get(filePath);\n const afterPrint = after.entries.get(filePath);\n // A path that is gone has nothing to read, and reading it back would only\n // confirm that.\n const captured = afterPrint === undefined ? undefined : await this.snapshots.capture(filePath);\n if (captured !== undefined) this.contents.set(filePath, captured);\n // The bytes are the ones this session already recorded: the call moved the\n // modification time and nothing else.\n if (captured !== undefined && captured === known) continue;\n const proven =\n beforePrint === undefined ||\n afterPrint === undefined ||\n this.manifests.sizeChanged(beforePrint, afterPrint) ||\n (known !== undefined && captured !== undefined && captured !== known);\n await this.append({\n version: 2,\n path: filePath,\n tool: BASH_TOOL,\n at: this.now(),\n origin: 'scan',\n ...(proven ? { verified: true } : {}),\n ...(captured === undefined ? {} : { after: captured }),\n });\n }\n }\n\n /**\n * Narrows candidates to the ones that could have been written by the call\n * being closed.\n *\n * This is what keeps a working tree that was already dirty out of the list. A\n * file whose bytes were last written before the call began was differed by\n * something else, whatever moved its fingerprint. A file that is no longer\n * there has no time to read and a delete is a change the call may well have\n * made, so it stays a candidate.\n */\n private async writtenDuring(candidates: readonly string[], startedAt: number): Promise<string[]> {\n const floor = startedAt - MODIFIED_TOLERANCE_MS;\n const written: string[] = [];\n for (const filePath of candidates) {\n const modifiedAt = await this.manifests.modifiedAt(filePath);\n if (modifiedAt === undefined || modifiedAt >= floor) written.push(filePath);\n }\n return written;\n }\n\n /** Which candidates git tracks and reports as untouched; empty when it cannot say. */\n private async unchangedInGit(cwd: string, candidates: readonly string[]): Promise<ReadonlySet<string>> {\n const unseen = candidates.filter((filePath) => !this.contents.has(filePath));\n if (this.git === undefined || unseen.length === 0) return new Set();\n return this.git.unchanged(cwd, unseen);\n }\n\n /** How many lines moved, when there is enough captured content to say. */\n private async countChanges(\n before: string | undefined,\n after: string | undefined,\n created: boolean,\n ): Promise<{ additions?: number; removals?: number }> {\n if (after === undefined) return {};\n if (before === undefined) {\n // A file that did not exist has an empty baseline, so every line it now\n // holds is an addition. A file that existed but went uncaptured has no\n // baseline to count against at all.\n if (!created) return {};\n const createdText = await this.snapshots.read(after);\n if (createdText === undefined) return {};\n const fresh = lineDiffFromEmpty(createdText);\n return { additions: fresh.additions, removals: fresh.removals };\n }\n if (before === after) return { additions: 0, removals: 0 };\n const [beforeText, afterText] = await Promise.all([this.snapshots.read(before), this.snapshots.read(after)]);\n if (beforeText === undefined || afterText === undefined) return {};\n const diff = lineDiff(beforeText, afterText);\n return { additions: diff.additions, removals: diff.removals };\n }\n\n /** Records the change unless it turned out to be no change at all. */\n private async append(event: TimelineEvent): Promise<void> {\n if (event.before !== undefined && event.before === event.after) return;\n await this.timeline.append(event);\n }\n\n /**\n * Folds a path the tool layer just handled into the tracked manifest, so the\n * next bash comparison sees it as already accounted for.\n *\n * The fingerprint has to be the one a walk would read, not a stand-in, or the\n * next comparison reports the file again under bash and every tool edit is\n * recorded twice.\n */\n private async remember(filePath: string): Promise<void> {\n if (this.manifest === undefined) return;\n const entries = new Map(this.manifest.entries);\n const fingerprint = await this.manifests.fingerprint(filePath);\n if (fingerprint === undefined) entries.delete(filePath);\n else entries.set(filePath, fingerprint);\n this.manifest = { entries, truncated: this.manifest.truncated };\n }\n}\n\n/** The tools this tracker knows how to attribute a change to. */\nexport const TRACKED_TOOLS: readonly FileEditTool[] = [EDIT_TOOL, WRITE_TOOL, BASH_TOOL];\n","import { execFileSync } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from '@agimon-ai/po-core/childProcess';\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport type { IFileEditPaths } from '../../types/fileEditPaths';\n\n/** The directory this package owns, under whichever root holds agent state. */\nconst STATE_DIRECTORY = 'doom-file-edit';\n\nfunction hash(value: string): string {\n return createHash('sha256').update(value).digest('hex').slice(0, 16);\n}\n\nexport class FileEditPaths implements IFileEditPaths {\n constructor(private readonly environment: Readonly<Record<string, string | undefined>> = process.env) {}\n\n sessionKey(sessionId: string, env: Readonly<Record<string, string | undefined>> = this.environment): string {\n if (env[SUBAGENT_CHILD_ENV]) {\n const parent = env[SUBAGENT_PARENT_SESSION_ENV];\n if (parent) return parent;\n }\n return sessionId;\n }\n\n /**\n * Where every session's state lives: the Pi agent directory, the same root\n * every other Doom package writes to, and never inside the working tree.\n *\n * An earlier layout put this in the repository's git common directory. That\n * is git's own storage: nothing in git's lifecycle prunes a directory we add\n * to it, a linked worktree writes into the main checkout rather than its own,\n * a submodule writes inside `.git/modules`, and `PI_CODING_AGENT_DIR` was\n * ignored outright. The snapshots are verbatim copies of files the session\n * touched, so where they land is a question about handling content, not a\n * question about convenience.\n */\n stateDirectory(): string {\n return path.join(piAgentDirectory(this.environment, this.environment.HOME), STATE_DIRECTORY);\n }\n\n /**\n * One session's timeline, named from its working directory and its session so\n * the hub and the session API can find it from another process knowing only\n * those two things.\n */\n timelinePath(cwd: string, sessionKey: string): string {\n const directory = this.stateDirectory();\n fs.mkdirSync(directory, { recursive: true });\n return path.join(directory, `${hash(fs.realpathSync(cwd))}-${hash(sessionKey)}.jsonl`);\n }\n\n /**\n * Where this session's content snapshots live: beside its timeline and named\n * after it, so the two are found together and cleared together.\n */\n snapshotsPath(cwd: string, sessionKey: string): string {\n return `${this.timelinePath(cwd, sessionKey).replace(/\\.jsonl$/u, '')}.blobs`;\n }\n\n legacyStateDirectory(cwd: string): string | undefined {\n const common = this.gitCommonDirectory(cwd);\n return common === undefined ? undefined : path.join(common, STATE_DIRECTORY);\n }\n\n /** Only still read so the state an older build left behind can be cleared away. */\n private gitCommonDirectory(cwd: string): string | undefined {\n try {\n const result = execFileSync('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], {\n cwd,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return result || undefined;\n } catch (error) {\n if (error instanceof Error) return undefined;\n throw error;\n }\n }\n}\n","import { execFile } from 'node:child_process';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\n\nimport type { GitStatusPort } from '../../types/gitStatus';\n\n/**\n * Answers the unchanged question with git, in two batched calls per chunk.\n *\n * One technology per adapter. This is where git enters the scan path; the\n * tracker that depends on the capability keeps importing the port.\n *\n * `ls-files` names which of the given paths git tracks at all, and `status`\n * names which of those moved. Everything else, an ignored build artefact, a\n * temporary file, a tree with no repository, falls out of both answers and is\n * therefore never reported as unchanged: the caller must keep recording it,\n * because git has nothing to say about a file it never saw.\n */\n\n/** Paths per git invocation, so a large scan cannot overflow the argument list. */\nconst CHUNK = 200;\nconst MAX_BUFFER = 4 * 1024 * 1024;\n\nconst run = promisify(execFile);\n\nfunction chunked<T>(values: readonly T[], size: number): T[][] {\n const groups: T[][] = [];\n for (let index = 0; index < values.length; index += size) groups.push(values.slice(index, index + size));\n return groups;\n}\n\nfunction splitRecords(output: string): string[] {\n return output.split('\\0').filter((entry) => entry !== '');\n}\n\n/**\n * The paths named by `status --porcelain -z`, whose records carry a two-letter\n * code and a space. A rename adds a bare second record; keeping it is harmless,\n * because every path in this set is one the caller records anyway.\n */\nfunction statusPaths(output: string): string[] {\n return splitRecords(output).map((record) => (record.length > 3 && record[2] === ' ' ? record.slice(3) : record));\n}\n\nfunction isInside(root: string, filePath: string): boolean {\n const relative = path.relative(root, filePath);\n return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative);\n}\n\nexport class NodeGitStatusAdapter implements GitStatusPort {\n async unchanged(cwd: string, filePaths: readonly string[]): Promise<ReadonlySet<string>> {\n const clean = new Set<string>();\n if (filePaths.length === 0) return clean;\n const root = await this.topLevel(cwd);\n if (root === undefined) return clean;\n\n for (const group of chunked(\n filePaths.filter((filePath) => isInside(root, filePath)),\n CHUNK,\n )) {\n const relative = group.map((filePath) => path.relative(root, filePath));\n const tracked = await this.git(root, ['ls-files', '-z', '--', ...relative]);\n if (tracked === undefined) continue;\n const dirty = await this.git(root, ['status', '--porcelain', '-z', '--untracked-files=no', '--', ...relative]);\n if (dirty === undefined) continue;\n const moved = new Set(statusPaths(dirty));\n for (const entry of splitRecords(tracked)) {\n if (!moved.has(entry)) clean.add(path.resolve(root, entry));\n }\n }\n return clean;\n }\n\n /** The repository root, or undefined when this tree is not one. */\n private async topLevel(cwd: string): Promise<string | undefined> {\n const output = await this.git(cwd, ['rev-parse', '--show-toplevel']);\n const root = output?.trim();\n return root === undefined || root === '' ? undefined : path.resolve(root);\n }\n\n /** Git's stdout, or undefined when the call failed for any reason at all. */\n private async git(cwd: string, args: readonly string[]): Promise<string | undefined> {\n try {\n const { stdout } = await run('git', [...args], { cwd, encoding: 'utf8', maxBuffer: MAX_BUFFER });\n return stdout;\n } catch {\n return undefined;\n }\n }\n}\n","import { createHash } from 'node:crypto';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { SnapshotStorePort } from '../../types/snapshotStore';\n\n/**\n * Store and read the file content this session's diffs are built from.\n *\n * One technology per adapter. This is where node enters the package; the\n * services that depend on the capability keep importing the port.\n *\n * Snapshots are content-addressed, so a file written twice with the same bytes\n * occupies one blob and the second capture is a hash and an exists check. The\n * whole directory goes when the session's timeline does, which is what keeps\n * this from growing without bound across a machine's lifetime.\n */\n\n/** Past this, a file is listed as changed without content rather than snapshotted. */\nexport const MAX_SNAPSHOT_BYTES = 1024 * 1024;\n\n/** How much of a file is read before deciding it is binary. */\nconst BINARY_SAMPLE_BYTES = 8192;\n\nconst MISSING_CODES = new Set(['ENOENT', 'ENOTDIR', 'EISDIR', 'ENAMETOOLONG']);\n\nfunction hasCode(error: unknown, codes: ReadonlySet<string>): boolean {\n return error instanceof Error && 'code' in error && typeof error.code === 'string' && codes.has(error.code);\n}\n\nexport class NodeSnapshotStoreAdapter implements SnapshotStorePort {\n private directory: string | undefined;\n\n initialize(directory: string): void {\n this.directory = directory;\n }\n\n async capture(filePath: string): Promise<string | undefined> {\n let raw: Buffer;\n try {\n raw = await fs.readFile(filePath);\n } catch (error) {\n if (hasCode(error, MISSING_CODES)) return undefined;\n throw error;\n }\n if (raw.byteLength > MAX_SNAPSHOT_BYTES) return undefined;\n if (raw.subarray(0, BINARY_SAMPLE_BYTES).includes(0)) return undefined;\n return this.put(raw.toString('utf8'));\n }\n\n async put(content: string): Promise<string> {\n const hash = createHash('sha256').update(content, 'utf8').digest('hex');\n const blobPath = path.join(this.requireDirectory(), hash);\n // An existing blob already holds exactly this content, by construction, so\n // rewriting it would only cost a write.\n try {\n await fs.access(blobPath);\n return hash;\n } catch (error) {\n if (!hasCode(error, MISSING_CODES)) throw error;\n }\n await fs.mkdir(this.requireDirectory(), { recursive: true });\n await fs.writeFile(blobPath, content, 'utf8');\n return hash;\n }\n\n async read(hash: string): Promise<string | undefined> {\n if (!/^[0-9a-f]{64}$/u.test(hash)) return undefined;\n try {\n return await fs.readFile(path.join(this.requireDirectory(), hash), 'utf8');\n } catch (error) {\n if (hasCode(error, MISSING_CODES)) return undefined;\n throw error;\n }\n }\n\n async clear(): Promise<void> {\n if (this.directory === undefined) return;\n await fs.rm(this.directory, { recursive: true, force: true });\n }\n\n private requireDirectory(): string {\n if (this.directory === undefined) throw new Error('Snapshot store is not initialized');\n return this.directory;\n }\n}\n","import type { FileEditEntry, FileEditVersion, LegacyTimelineEvent, TimelineEvent } from '../../types/domain';\n\n/**\n * Fold a session's recorded changes into the per-file rows and version\n * histories the surfaces show.\n *\n * The timeline is append-only, one line per change, so every reader folds it\n * the same way and the folding belongs somewhere both the store and its tests\n * can reach without a filesystem. Version 1 lines are still read: a session\n * already running when the package updates keeps appending to the file it\n * opened, and dropping its earlier lines would blank a list mid-session.\n */\n\n/** Either shape the timeline file can hold. */\nexport type AnyTimelineEvent = TimelineEvent | LegacyTimelineEvent;\n\nconst TOOLS = new Set(['edit', 'write', 'bash', 'user']);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction optionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n\nfunction optionalCount(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;\n}\n\n/** Narrows one parsed line, or answers null so a malformed line can be skipped. */\nexport function parseTimelineEvent(value: unknown): AnyTimelineEvent | null {\n if (!isRecord(value)) return null;\n const { version, path: filePath, tool, at } = value;\n if (typeof filePath !== 'string' || filePath === '') return null;\n if (typeof at !== 'number' || !Number.isFinite(at)) return null;\n if (typeof tool !== 'string' || !TOOLS.has(tool)) return null;\n\n if (version === 1) {\n if (tool === 'user') return null; // The version 1 vocabulary had no manual saves.\n return { version: 1, path: filePath, tool: tool as LegacyTimelineEvent['tool'], at };\n }\n if (version !== 2) return null;\n const origin = value.origin === 'scan' ? 'scan' : 'tool';\n return {\n version: 2,\n path: filePath,\n tool: tool as TimelineEvent['tool'],\n at,\n origin,\n ...(optionalString(value.before) === undefined ? {} : { before: value.before as string }),\n ...(optionalString(value.after) === undefined ? {} : { after: value.after as string }),\n ...(optionalCount(value.additions) === undefined ? {} : { additions: value.additions as number }),\n ...(optionalCount(value.removals) === undefined ? {} : { removals: value.removals as number }),\n ...(value.verified === true ? { verified: true } : {}),\n ...(value.created === true ? { created: true } : {}),\n };\n}\n\n/** Every recorded line of the timeline, malformed ones dropped, in file order. */\nexport function parseTimeline(content: string, onMalformed?: (line: string) => void): AnyTimelineEvent[] {\n const events: AnyTimelineEvent[] = [];\n for (const line of content.split('\\n')) {\n if (line === '') continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n onMalformed?.(line);\n continue;\n }\n const event = parseTimelineEvent(parsed);\n if (event === null) onMalformed?.(line);\n else events.push(event);\n }\n return events;\n}\n\n/**\n * Whether a recorded change is evidence that the file's content actually moved.\n *\n * A tool change names its file and reads both sides, so it always is. A scan\n * change is a tree comparison of size and modification time, which a checkout,\n * an install, or a command that merely touched a file moves as readily as an\n * edit does; only a scan the tracker confirmed carries `verified`. Version 1\n * lines predate the distinction entirely, so a bash one cannot be trusted.\n */\nexport function isConfirmedChange(event: AnyTimelineEvent): boolean {\n if (event.version !== 2) return event.tool !== 'bash';\n return event.origin !== 'scan' || event.verified === true;\n}\n\n/** The recorded changes worth listing, which is every change but an unconfirmed touch. */\nexport function confirmedChanges(events: readonly AnyTimelineEvent[]): AnyTimelineEvent[] {\n return events.filter(isConfirmedChange);\n}\n/** One row per file, newest change first, which is the order both docks list in. */\nexport function foldEntries(events: readonly AnyTimelineEvent[]): FileEditEntry[] {\n const folded = new Map<string, FileEditEntry>();\n for (const event of events) {\n const current = folded.get(event.path);\n folded.set(event.path, {\n path: event.path,\n tool: event.at >= (current?.at ?? 0) ? event.tool : (current?.tool ?? event.tool),\n at: Math.max(event.at, current?.at ?? 0),\n count: (current?.count ?? 0) + 1,\n });\n }\n return [...folded.values()].sort((left, right) => right.at - left.at);\n}\n\n/** One file's history, oldest first, numbered so a reader can name a version. */\nexport function foldVersions(events: readonly AnyTimelineEvent[], filePath: string): FileEditVersion[] {\n return events\n .filter((event) => event.path === filePath)\n .sort((left, right) => left.at - right.at)\n .map((event, offset) => {\n const version: FileEditVersion = {\n index: offset + 1,\n tool: event.tool,\n at: event.at,\n origin: event.version === 2 ? event.origin : 'scan',\n };\n if (event.version !== 2) return version;\n return {\n ...version,\n ...(event.before === undefined ? {} : { before: event.before }),\n ...(event.after === undefined ? {} : { after: event.after }),\n ...(event.additions === undefined ? {} : { additions: event.additions }),\n ...(event.removals === undefined ? {} : { removals: event.removals }),\n ...(event.verified === true ? { verified: true } : {}),\n ...(event.created === true ? { created: true } : {}),\n };\n });\n}\n\n/**\n * Whether a file can be diffed at all: some version has to give the diff a\n * starting point. A captured baseline is one. So is a version that created the\n * file, whose starting point is nothing at all.\n *\n * A file only ever seen after the fact, which is every file a bash script\n * wrote, still cannot be.\n */\nexport function isDiffable(versions: readonly FileEditVersion[]): boolean {\n return versions.some((version) => version.before !== undefined || version.created === true);\n}\n\n/** The oldest captured baseline for a file, which is what the whole-session diff starts from. */\nexport function baselineOf(versions: readonly FileEditVersion[]): string | undefined {\n return versions.find((version) => version.before !== undefined)?.before;\n}\n","import fs, { type FileHandle } from 'node:fs/promises';\n\nimport type { FileEditEntry, FileEditVersion, TimelineEvent } from '../../types/domain';\nimport type { ITimelineStore } from '../../types/timelineStore';\nimport { type AnyTimelineEvent, confirmedChanges, foldEntries, foldVersions, parseTimeline } from '../fileChanges';\n\nconst LOCK_RETRY_MS = 25;\nconst LOCK_RETRIES = 400;\n/**\n * How long a lock file may sit untouched before a waiter treats it as abandoned.\n *\n * An append writes one line, so a holder that has been there for half a minute\n * is a process that died without unlinking. Without this, that one file would\n * block every later append to the same timeline permanently.\n */\nconst LOCK_STALE_MS = 30_000;\n\nfunction hasCode(error: unknown, code: string): boolean {\n return error instanceof Error && 'code' in error && error.code === code;\n}\n\nexport class TimelineStore implements ITimelineStore {\n private filePath: string | undefined;\n\n initialize(filePath: string): void {\n this.filePath = filePath;\n }\n\n async append(event: TimelineEvent): Promise<void> {\n await this.withLock(async () => fs.appendFile(this.requirePath(), `${JSON.stringify(event)}\\n`, 'utf8'));\n }\n\n async list(): Promise<FileEditEntry[]> {\n // Listing shows what was edited, so a path a command only touched is left out.\n return foldEntries(confirmedChanges(await this.events()));\n }\n\n async versions(filePath: string): Promise<FileEditVersion[]> {\n // Same bar as the listing above: a file's history must not reintroduce the\n // touches the list already left out.\n return foldVersions(confirmedChanges(await this.events()), filePath);\n }\n\n async clear(): Promise<void> {\n try {\n await fs.unlink(this.requirePath());\n } catch (error) {\n if (!hasCode(error, 'ENOENT')) throw error;\n }\n }\n\n /** Every recorded change, or none at all when the session has not written yet. */\n private async events(): Promise<AnyTimelineEvent[]> {\n let content: string;\n try {\n content = await fs.readFile(this.requirePath(), 'utf8');\n } catch (error) {\n if (hasCode(error, 'ENOENT')) return [];\n throw error;\n }\n return parseTimeline(content, (line) =>\n console.warn(`Ignoring malformed file edit timeline line: ${line.slice(0, 200)}`),\n );\n }\n\n private requirePath(): string {\n if (!this.filePath) throw new Error('Timeline store is not initialized');\n return this.filePath;\n }\n\n private async withLock<T>(operation: () => Promise<T>): Promise<T> {\n const lockPath = `${this.requirePath()}.lock`;\n const lock = await this.acquire(lockPath);\n try {\n return await operation();\n } finally {\n await lock.close();\n await this.release(lockPath);\n }\n }\n\n /** Waits for the lock file, breaking one an earlier process left behind. */\n private async acquire(lockPath: string): Promise<FileHandle> {\n for (let attempt = 0; attempt < LOCK_RETRIES; attempt += 1) {\n try {\n return await fs.open(lockPath, 'wx');\n } catch (error) {\n if (!hasCode(error, 'EEXIST')) throw error;\n if (await this.breakStale(lockPath)) continue;\n await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));\n }\n }\n throw new Error(`Timed out acquiring timeline lock ${lockPath}`);\n }\n\n /** Whether the lock is now free to take, having removed it when its holder is gone. */\n private async breakStale(lockPath: string): Promise<boolean> {\n try {\n const stats = await fs.stat(lockPath);\n if (Date.now() - stats.mtimeMs < LOCK_STALE_MS) return false;\n await fs.unlink(lockPath);\n return true;\n } catch (error) {\n // Already gone, or another waiter broke it first: either way, try again.\n if (hasCode(error, 'ENOENT')) return true;\n throw error;\n }\n }\n\n private async release(lockPath: string): Promise<void> {\n try {\n await fs.unlink(lockPath);\n } catch (error) {\n if (!hasCode(error, 'ENOENT')) console.warn(`Could not remove timeline lock: ${String(error)}`);\n }\n }\n}\n","import type { Dirent } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { TreeManifest, TreeManifestPort } from '../../types/treeManifest';\n\n/**\n * Take a bounded manifest of a working tree and report which files moved\n * between two of them.\n *\n * One technology per adapter. This is where node enters the package; the\n * services that depend on the capability keep importing the port.\n *\n * The caps are the whole design. An unbounded walk of a working directory is\n * dominated by dependency and build trees, which change constantly and tell a\n * reader nothing, so those are skipped by name; what remains is capped on both\n * entries and depth so the walk stays a fixed cost rather than a function of\n * how large the checkout grew. Hitting a cap is reported, never hidden.\n */\n\n/**\n * Directories a working tree fills with output nobody wants listed as an edit.\n *\n * Two kinds live here. Most are the build and dependency trees every checkout\n * grows. The rest are where tooling, including this agent's own, writes while a\n * session runs: a telemetry log under `logs`, a test run's artifacts under\n * `test-results` and `playwright-report`, scratch space under `tmp`, and agent\n * state under `.pi`. Those are skipped by construction rather than judged file\n * by file, because a walk that enters them reports the session watching itself.\n */\nexport const IGNORED_DIRECTORIES: ReadonlySet<string> = new Set([\n '.git',\n '.hg',\n '.svn',\n '.cache',\n '.gradle',\n '.next',\n '.nuxt',\n '.nx',\n '.pi',\n '.pnpm-store',\n '.turbo',\n '.venv',\n '__pycache__',\n 'build',\n 'coverage',\n 'dist',\n 'logs',\n 'node_modules',\n 'out',\n 'playwright-report',\n 'target',\n 'test-results',\n 'tmp',\n 'vendor',\n 'venv',\n]);\n\nexport const MAX_ENTRIES = 20_000;\nexport const MAX_DEPTH = 12;\n\n/**\n * How a file is summarised. Size and modification time are what a walk can read\n * cheaply for thousands of files; hashing every one of them would turn a scan\n * into a read of the whole tree.\n */\nfunction fingerprintOf(size: number, modifiedMs: number): string {\n return `${size}:${modifiedMs}`;\n}\n\n/** The size half of a fingerprint, which only the adapter that wrote it can read. */\nfunction sizeOf(fingerprint: string): string {\n const separator = fingerprint.indexOf(':');\n return separator === -1 ? fingerprint : fingerprint.slice(0, separator);\n}\n\nexport interface NodeTreeManifestOptions {\n maxEntries?: number;\n maxDepth?: number;\n ignoredDirectories?: ReadonlySet<string>;\n}\n\nexport class NodeTreeManifestAdapter implements TreeManifestPort {\n private readonly maxEntries: number;\n private readonly maxDepth: number;\n private readonly ignored: ReadonlySet<string>;\n\n constructor(options: NodeTreeManifestOptions = {}) {\n this.maxEntries = options.maxEntries ?? MAX_ENTRIES;\n this.maxDepth = options.maxDepth ?? MAX_DEPTH;\n this.ignored = options.ignoredDirectories ?? IGNORED_DIRECTORIES;\n }\n\n async take(root: string, exclude: readonly string[] = []): Promise<TreeManifest> {\n const entries = new Map<string, string>();\n const skipped = new Set(exclude.map((entry) => path.resolve(entry)));\n const truncated = await this.walk(root, 0, entries, skipped);\n return { entries, truncated };\n }\n\n async fingerprint(filePath: string): Promise<string | undefined> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isFile() ? fingerprintOf(stat.size, stat.mtimeMs) : undefined;\n } catch {\n return undefined;\n }\n }\n\n async modifiedAt(filePath: string): Promise<number | undefined> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isFile() ? stat.mtimeMs : undefined;\n } catch {\n return undefined;\n }\n }\n\n changed(before: TreeManifest, after: TreeManifest): string[] {\n const moved = new Set<string>();\n for (const [filePath, fingerprint] of after.entries) {\n if (before.entries.get(filePath) !== fingerprint) moved.add(filePath);\n }\n for (const filePath of before.entries.keys()) {\n if (!after.entries.has(filePath)) moved.add(filePath);\n }\n return [...moved].sort();\n }\n\n sizeChanged(before: string | undefined, after: string | undefined): boolean {\n if (before === undefined || after === undefined) return false;\n return sizeOf(before) !== sizeOf(after);\n }\n /** Fills `entries` depth-first and answers whether a cap cut the walk short. */\n private async walk(\n directory: string,\n depth: number,\n entries: Map<string, string>,\n skipped: ReadonlySet<string>,\n ): Promise<boolean> {\n if (depth > this.maxDepth) return true;\n let listing: Dirent[];\n try {\n listing = await fs.readdir(directory, { withFileTypes: true });\n } catch {\n // A directory that vanished or refused a read is not the walk's problem;\n // the rest of the tree is still worth reporting.\n return false;\n }\n let truncated = false;\n for (const entry of listing) {\n if (entries.size >= this.maxEntries) return true;\n const entryPath = path.join(directory, entry.name);\n if (skipped.has(entryPath)) continue;\n // Symlinks are never followed: a link into a parent turns the walk into a\n // cycle, and a link out of the tree reports a file this session does not own.\n if (entry.isSymbolicLink()) continue;\n if (entry.isDirectory()) {\n if (this.ignored.has(entry.name)) continue;\n truncated = (await this.walk(entryPath, depth + 1, entries, skipped)) || truncated;\n continue;\n }\n if (!entry.isFile()) continue;\n try {\n const stat = await fs.stat(entryPath);\n entries.set(entryPath, fingerprintOf(stat.size, stat.mtimeMs));\n } catch {\n // Raced with a delete between readdir and stat; it is simply not there.\n }\n }\n return truncated;\n }\n}\n","import type { FileEditTool } from './domain';\n\n/**\n * View types shared by this package's hub channel and its web plugin. Wire\n * JSON only, no node types, so the cockpit bundle can carry them; the\n * cockpit receives them as 'file_edits' channel payloads.\n *\n * The channel carries the list alone. One file's diffs and content are large\n * and only wanted once a reader opens the file, so those come from the session\n * API instead (see fileEditsApi.ts).\n */\n\n/** One file this session changed, as the activity dock lists it. */\nexport interface FilesItemView {\n /** Absolute path, and the identity every API route takes back. */\n path: string;\n /** Path relative to the session's working directory, which is what a row shows. */\n relPath: string;\n /** The tool behind the most recent change. */\n tool: FileEditTool;\n /** When that change happened, epoch milliseconds. */\n at: number;\n /** How many changes this session recorded for the file. */\n count: number;\n /**\n * Whether any change captured a baseline, so a diff can be shown at all. A\n * file found by comparing the tree around a bash call has no \"before\", so it\n * is listed and readable but never diffed.\n */\n diffable: boolean;\n}\n\n/** The frame type the hub publishes and the web plugin claims; declared in the poWeb block too. */\nexport const filesChannelType = 'file_edits';\n\n/** Footer status key whose presence shows the activity group; the extension publishes it. */\nexport const filesStatusKey = 'doom-file-edit-files';\n","import { apiResponse, defineApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { FileEditsDetailView, FileEditsPreviewView, FileEditsSaveView } from './fileEditsApi';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path. The build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/file-edits/`, so the mount\n * is stated once, by the folder that creates it. They used to be written here,\n * in the contract and again inside every URL the page built, and the copies\n * were free to disagree.\n *\n * This is the one file the routes, the Hono app and the browser all read, so a\n * path cannot move on one side alone.\n */\nexport default defineApiRoutes({\n detail: {\n method: 'GET',\n path: '/detail',\n query: ['path'],\n response: apiResponse<FileEditsDetailView>(),\n },\n preview: {\n method: 'GET',\n path: '/preview',\n query: ['path'],\n response: apiResponse<FileEditsPreviewView>(),\n },\n save: {\n method: 'PUT',\n path: '/content',\n response: apiResponse<FileEditsSaveView>(),\n },\n /** The same path as `save`; the method is the whole of the difference. */\n remove: {\n method: 'DELETE',\n path: '/content',\n query: ['path'],\n },\n /**\n * The host's own bytes route, not this package's.\n *\n * `host: true` hangs it off the session root rather than under `/plugins`,\n * which is how the cockpit already serves it. Declaring it here is the first\n * time anything records that this package's previews depend on it.\n */\n file: {\n method: 'GET',\n path: '/file',\n query: ['path'],\n host: true,\n },\n});\n","import type { FileEditOrigin, FileEditTool } from './domain';\n\n/**\n * The wire vocabulary this package's API shares with whatever calls it.\n *\n * It lives under src/types because that is the one server root a browser\n * bundle may read: the cockpit plugin builds its URLs from these and the\n * routes answer them, so neither half can drift from the other.\n *\n * Routes live in `./apiRoutes`, which the contract, the Hono app and the\n * generated client all read. This file holds the shapes that travel over them.\n *\n * Opening a changed file wants its whole history at once, so `detail` answers\n * in one round trip rather than making the page stitch three reads together;\n * `content` takes the manual save back; `preview` reads a file the session\n * never changed, which has no history to answer with.\n */\n\n/** Where a host mounts this package's API; the segment after /api/plugins/. */\nexport const API_BASE_PATH = 'file-edits';\n\n/** Query parameter naming the file a request is about. */\nexport const PATH_QUERY_PARAM = 'path';\n\n/**\n * One line of a diff. The number is the new file's for context and additions\n * and the old file's for removals, so it always says where the line lives.\n */\nexport interface FileEditsDiffRow {\n marker: '+' | '-' | ' ';\n line: number;\n content: string;\n}\n\n/**\n * A run of changed lines with its surrounding context. Hunks are sent apart\n * rather than joined by an elision marker, so the page draws the gap between\n * them instead of parsing one out of a string.\n */\nexport interface FileEditsDiffHunk {\n /** The line number the hunk opens at. */\n start: number;\n rows: FileEditsDiffRow[];\n}\n\n/**\n * One change in a file's history, each rendered on its own so a file edited\n * four times shows four distinct diffs rather than one merged blur.\n */\nexport interface FileEditsVersionView {\n /** Position in the file's history, oldest first, starting at 1. */\n index: number;\n tool: FileEditTool;\n at: number;\n origin: FileEditOrigin;\n additions: number;\n removals: number;\n /** The changed lines of this one change; absent when no baseline was captured. */\n hunks?: FileEditsDiffHunk[];\n /** Why there is no diff, shown in its place. */\n note?: string;\n}\n\n/** Everything this session did to the file: its first baseline against what is on disk now. */\nexport interface FileEditsCumulativeView {\n additions: number;\n removals: number;\n hunks?: FileEditsDiffHunk[];\n note?: string;\n}\n\n/** The file as it stands on disk right now, which is what the source view edits. */\nexport interface FileEditsWorkingView {\n content: string;\n /** sha256 of the content; the token a save hands back to prove it is not stale. */\n hash: string;\n /** True when the file is binary, missing, or past the size cap, so `content` is empty. */\n unavailable: boolean;\n reason?: string;\n}\n\n/** What the detail route answers with. */\nexport interface FileEditsDetailView {\n path: string;\n relPath: string;\n versions: FileEditsVersionView[];\n cumulative: FileEditsCumulativeView;\n working: FileEditsWorkingView;\n}\n\n/**\n * What the preview route answers with: the file as it stands, and nothing\n * about how it got there. A file this session never touched has no history and\n * no baseline, so there is no diff to send and no save to take back.\n */\nexport interface FileEditsPreviewView {\n path: string;\n relPath: string;\n working: FileEditsWorkingView;\n}\n\n/** What the save route takes. */\nexport interface FileEditsSaveRequest {\n path: string;\n /** The hash the reader loaded; a mismatch means the file moved underneath them. */\n expectedHash: string;\n content: string;\n}\n\n/** What the save route answers with. */\nexport interface FileEditsSaveView {\n hash: string;\n}\n\n/** What a route reports when it refuses; the page shows `error` verbatim. */\nexport interface FileEditsErrorView {\n error: string;\n /** On a stale save, the hash the file actually has now. */\n hash?: string;\n}\n","import { createHash } from 'node:crypto';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { baselineOf } from '../../services/fileChanges';\nimport { FileEditPaths } from '../../services/fileEditPaths';\nimport { lineDiff, lineDiffFromEmpty } from '../../services/lineDiff';\nimport { NodeSnapshotStoreAdapter } from '../../services/snapshotStore';\nimport { TimelineStore } from '../../services/timelineStore';\nimport routes from '../../types/apiRoutes';\nimport type { FileEditVersion } from '../../types/domain';\nimport type { IFileEditPaths } from '../../types/fileEditPaths';\nimport {\n API_BASE_PATH,\n type FileEditsCumulativeView,\n type FileEditsDetailView,\n type FileEditsPreviewView,\n type FileEditsSaveRequest,\n type FileEditsSaveView,\n type FileEditsVersionView,\n type FileEditsWorkingView,\n PATH_QUERY_PARAM,\n} from '../../types/fileEditsApi';\nimport type { SnapshotStorePort } from '../../types/snapshotStore';\nimport type { ITimelineStore } from '../../types/timelineStore';\n\n/**\n * This package's HTTP surface: one file's history, and the manual save.\n *\n * Routes are relative to the mount its host gives it, so nothing here repeats\n * where it was mounted. The host is one session's own server, which is what\n * makes the session id and working directory available without the page\n * supplying either: a page names a file, never a session's private paths.\n *\n * The detail route answers everything a reader needs to open a changed file,\n * because splitting it would make the page stitch three responses together for\n * one click. The save route is the only mutation, and it refuses unless the\n * reader proves they were looking at the content that is still on disk. The\n * preview route is the read-only way in to a file the session never changed,\n * bounded by the working directory instead of by the timeline.\n */\n\n/** Past this, the working view reports the file rather than carrying it. */\nexport const MAX_WORKING_BYTES = 1024 * 1024;\n\nconst BINARY_SAMPLE_BYTES = 8192;\nconst NO_BASELINE_NOTE = 'changed by a command, so no baseline was captured; showing the file as it stands';\n/**\n * A change the tool layer named but never captured content for. That is what\n * every record written before this package learned to snapshot looks like, and\n * calling those \"changed by a command\" would be wrong: the tool is right there\n * in the record.\n */\nconst NO_SNAPSHOT_NOTE = 'recorded before this session began capturing content, so there is no diff for it';\nconst NO_AFTER_NOTE = 'the new content could not be captured: the file is binary or past the size cap';\nconst NO_BEFORE_NOTE = 'the previous content could not be captured: it was binary or past the size cap';\nconst APPROXIMATE_NOTE = 'the changed region was too large to match line by line';\n\nexport interface FileEditsApiOptions {\n /** The session these routes answer for; absent when the host is the hub. */\n sessionId?: string;\n /** The session's working directory, which relative paths are shown against. */\n cwd?: string;\n timeline?: ITimelineStore;\n snapshots?: SnapshotStorePort;\n paths?: IFileEditPaths;\n}\n\nfunction hashOf(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\nfunction hasCode(error: unknown, code: string): boolean {\n return error instanceof Error && 'code' in error && error.code === code;\n}\n\nfunction describe(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Reads the file as the source view will show it, or says why it cannot. */\nasync function readWorking(filePath: string): Promise<FileEditsWorkingView> {\n let raw: Buffer;\n try {\n raw = await fs.readFile(filePath);\n } catch {\n return { content: '', hash: '', unavailable: true, reason: 'the file no longer exists' };\n }\n if (raw.byteLength > MAX_WORKING_BYTES) {\n return { content: '', hash: '', unavailable: true, reason: `the file is larger than ${MAX_WORKING_BYTES} bytes` };\n }\n if (raw.subarray(0, BINARY_SAMPLE_BYTES).includes(0)) {\n return { content: '', hash: '', unavailable: true, reason: 'the file is binary' };\n }\n const content = raw.toString('utf8');\n return { content, hash: hashOf(content), unavailable: false };\n}\n\nfunction isInside(root: string, candidate: string): boolean {\n return candidate === root || candidate.startsWith(`${root}${path.sep}`);\n}\n\n/**\n * The same containment check after symlinks are followed. A path that does not\n * exist yet resolves to nothing, and there is nothing to hand out, so it\n * passes and the read below reports it missing.\n */\nasync function isRealPathInside(root: string, candidate: string): Promise<boolean> {\n let realRoot: string;\n let real: string;\n try {\n realRoot = await fs.realpath(root);\n real = await fs.realpath(candidate);\n } catch (error) {\n return hasCode(error, 'ENOENT');\n }\n return isInside(realRoot, real);\n}\n\n/**\n * One recorded change as the page renders it. A change with both sides captured\n * carries its own diff; one without says why, rather than showing nothing.\n *\n * A file this session created counts as captured on both sides. Its baseline is\n * empty rather than missing, so the whole file reads as arriving at once, which\n * is exactly what happened.\n */\nasync function toVersionView(version: FileEditVersion, snapshots: SnapshotStorePort): Promise<FileEditsVersionView> {\n const view: FileEditsVersionView = {\n index: version.index,\n tool: version.tool,\n at: version.at,\n origin: version.origin,\n additions: version.additions ?? 0,\n removals: version.removals ?? 0,\n };\n const createdHere = version.before === undefined && version.created === true;\n if (version.after === undefined || (version.before === undefined && !createdHere)) {\n // A scan found the path only after it changed, so no baseline exists. A\n // tool named the path but content was not stored: either it did not fit, or\n // the record predates content capture, which the tool tells apart.\n if (version.origin === 'tool') {\n return { ...view, note: version.after === undefined ? NO_AFTER_NOTE : NO_BEFORE_NOTE };\n }\n return { ...view, note: version.tool === 'bash' ? NO_BASELINE_NOTE : NO_SNAPSHOT_NOTE };\n }\n const after = await snapshots.read(version.after);\n if (after === undefined) {\n return { ...view, note: 'the content behind this change is no longer held' };\n }\n if (version.before === undefined) {\n const fresh = lineDiffFromEmpty(after);\n return {\n ...view,\n additions: fresh.additions,\n removals: fresh.removals,\n hunks: fresh.hunks,\n ...(fresh.approximate ? { note: APPROXIMATE_NOTE } : {}),\n };\n }\n const before = await snapshots.read(version.before);\n if (before === undefined) {\n return { ...view, note: 'the content behind this change is no longer held' };\n }\n const diff = lineDiff(before, after);\n return {\n ...view,\n additions: diff.additions,\n removals: diff.removals,\n hunks: diff.hunks,\n ...(diff.approximate ? { note: APPROXIMATE_NOTE } : {}),\n };\n}\n\n/** Everything the session did to the file: its oldest baseline against what is on disk now. */\nasync function toCumulativeView(\n versions: readonly FileEditVersion[],\n working: FileEditsWorkingView,\n snapshots: SnapshotStorePort,\n): Promise<FileEditsCumulativeView> {\n // A file this session created starts from nothing, and nothing is a baseline\n // the snapshot store never has to hold.\n const createdHere = versions[0]?.created === true;\n const baseline = baselineOf(versions);\n if (!createdHere && baseline === undefined) {\n return {\n additions: 0,\n removals: 0,\n note: versions[0]?.origin === 'tool' ? NO_BEFORE_NOTE : NO_BASELINE_NOTE,\n };\n }\n if (working.unavailable) {\n return { additions: 0, removals: 0, note: working.reason ?? 'the file cannot be read' };\n }\n if (createdHere || baseline === undefined) {\n const fresh = lineDiffFromEmpty(working.content);\n return {\n additions: fresh.additions,\n removals: fresh.removals,\n hunks: fresh.hunks,\n ...(fresh.approximate ? { note: APPROXIMATE_NOTE } : {}),\n };\n }\n const before = await snapshots.read(baseline);\n if (before === undefined) {\n return { additions: 0, removals: 0, note: 'the content this session started from is no longer held' };\n }\n const diff = lineDiff(before, working.content);\n return {\n additions: diff.additions,\n removals: diff.removals,\n hunks: diff.hunks,\n ...(diff.approximate ? { note: APPROXIMATE_NOTE } : {}),\n };\n}\n\nexport function createFileEditsApi(options: FileEditsApiOptions = {}): Hono {\n const timeline = options.timeline ?? new TimelineStore();\n const snapshots = options.snapshots ?? new NodeSnapshotStoreAdapter();\n const paths = options.paths ?? new FileEditPaths();\n const cwd = options.cwd ?? '';\n const app = new Hono();\n\n // The routes and the extension run in different processes, so this half\n // opens the same two locations from the same inputs rather than sharing an\n // instance with the half that writes them.\n //\n // Only what this function built gets pointed at them. A store handed in\n // through the options arrives already configured, and repointing it would\n // make the option a lie: the caller would be injecting a store the routes\n // then read somewhere else entirely.\n if (options.sessionId !== undefined && cwd !== '') {\n const sessionKey = paths.sessionKey(options.sessionId);\n if (options.timeline === undefined) timeline.initialize(paths.timelinePath(cwd, sessionKey));\n if (options.snapshots === undefined) snapshots.initialize(paths.snapshotsPath(cwd, sessionKey));\n }\n\n /**\n * The file a request names, rejected unless this session actually recorded a\n * change to it. That check is the authorization: these routes read and write\n * whatever path they are handed, so the timeline is what bounds them to files\n * the session already touched.\n */\n const recordedVersions = async (filePath: string | undefined): Promise<FileEditVersion[] | undefined> => {\n if (filePath === undefined || filePath === '') return undefined;\n const versions = await timeline.versions(path.resolve(cwd, filePath));\n return versions.length === 0 ? undefined : versions;\n };\n\n const isInsideSession = async (filePath: string): Promise<boolean> => {\n if (cwd === '') return false;\n const root = path.resolve(cwd);\n return isInside(root, filePath) && (await isRealPathInside(root, filePath));\n };\n app.get(routes.detail.path, async (context) => {\n const requested = context.req.query(PATH_QUERY_PARAM);\n const versions = await recordedVersions(requested);\n if (versions === undefined || requested === undefined) {\n return context.json({ error: 'This session recorded no changes to that file.' }, 404);\n }\n const filePath = path.resolve(cwd, requested);\n const working = await readWorking(filePath);\n const body: FileEditsDetailView = {\n path: filePath,\n relPath: cwd === '' ? filePath : path.relative(cwd, filePath),\n versions: await Promise.all(versions.map((version) => toVersionView(version, snapshots))),\n cumulative: await toCumulativeView(versions, working, snapshots),\n working,\n };\n return context.json(body);\n });\n\n /**\n * The file a request names, read only, for a file the timeline knows nothing\n * about: a path the agent read but never changed is still a path the reader\n * clicked, and refusing it would leave the link dead.\n *\n * The working directory is the boundary here, since the timeline is not.\n * Containment is checked lexically and again on the real path, so a symlink\n * inside the tree cannot hand out something beyond it, and a host that gave\n * these routes no working directory has no boundary to enforce and so serves\n * nothing.\n */\n app.get(routes.preview.path, async (context) => {\n const requested = context.req.query(PATH_QUERY_PARAM);\n if (requested === undefined || requested === '' || requested.includes('\\0')) {\n return context.json({ error: 'A preview names a path.' }, 400);\n }\n if (cwd === '') return context.json({ error: 'This session has no working directory to read from.' }, 403);\n const root = path.resolve(cwd);\n const filePath = path.resolve(root, requested);\n if (!(await isInsideSession(filePath))) {\n return context.json({ error: 'The path leaves the session directory.' }, 403);\n }\n const body: FileEditsPreviewView = {\n path: filePath,\n relPath: path.relative(root, filePath),\n working: await readWorking(filePath),\n };\n return context.json(body);\n });\n\n app.put(routes.save.path, async (context) => {\n let request: FileEditsSaveRequest;\n try {\n request = (await context.req.json()) as FileEditsSaveRequest;\n } catch {\n return context.json({ error: 'The save body is not JSON.' }, 400);\n }\n if (typeof request.path !== 'string' || typeof request.content !== 'string') {\n return context.json({ error: 'A save names a path and the content to write.' }, 400);\n }\n if ((await recordedVersions(request.path)) === undefined) {\n return context.json({ error: 'This session recorded no changes to that file.' }, 404);\n }\n const filePath = path.resolve(cwd, request.path);\n if (!(await isInsideSession(filePath))) {\n return context.json({ error: 'Files outside the session directory are read-only.' }, 403);\n }\n const current = await readWorking(filePath);\n if (current.unavailable) {\n return context.json({ error: current.reason ?? 'The file cannot be read.' }, 409);\n }\n // The agent runs in the same working directory, so it can rewrite the file\n // while a reader is still editing it. Refusing on a moved hash is what\n // stops the save from silently discarding whatever it did.\n if (current.hash !== request.expectedHash) {\n return context.json({ error: 'The file changed since it was opened.', hash: current.hash }, 409);\n }\n await fs.writeFile(filePath, request.content, 'utf8');\n const body: FileEditsSaveView = { hash: hashOf(request.content) };\n return context.json(body);\n });\n\n app.delete(routes.remove.path, async (context) => {\n const requested = context.req.query(PATH_QUERY_PARAM);\n if ((await recordedVersions(requested)) === undefined || requested === undefined) {\n return context.json({ error: 'This session recorded no changes to that file.' }, 404);\n }\n const filePath = path.resolve(cwd, requested);\n if (!(await isInsideSession(filePath))) {\n return context.json({ error: 'Files outside the session directory are read-only.' }, 403);\n }\n try {\n await fs.unlink(filePath);\n } catch (error) {\n // Already gone is the outcome the caller wanted, so it is not a failure.\n if (!hasCode(error, 'ENOENT')) {\n return context.json({ error: `The file could not be deleted: ${describe(error)}` }, 409);\n }\n }\n return context.body(null, 204);\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built entry. */\nexport const api: DoomApi = {\n basePath: API_BASE_PATH,\n start(context: DoomApiContext): DoomApiHandler {\n const app = createFileEditsApi({\n paths: new FileEditPaths(context.environment),\n ...(context.sessionId === undefined ? {} : { sessionId: context.sessionId }),\n ...(context.cwd === undefined ? {} : { cwd: context.cwd }),\n });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request here: every route reads, answers and closes.\n close: () => undefined,\n };\n },\n};\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\n\n/**\n * Remove the state sessions left behind when they never got to clean up.\n *\n * A session clears its own timeline and snapshots on shutdown, which covers the\n * ordinary case and nothing else: a killed process, a crash, or a machine that\n * lost power leaves both behind, and nothing ever looked at the directory\n * again. One week of that filled a real checkout with hundreds of megabytes.\n *\n * Age is the only signal used, because it is the only one that is true from\n * another process. A running session appends as it works, so its timeline is\n * recent; a dead one stops moving at the moment it died.\n */\n\n/**\n * How long state may sit untouched before a later session removes it.\n *\n * Generous on purpose. The cost of waiting is disk, and the cost of being wrong\n * is deleting a live session's history, so this is set far beyond any plausible\n * gap between two appends rather than close to it.\n */\nexport const STATE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;\n\nconst TIMELINE_SUFFIX = '.jsonl';\nconst SNAPSHOTS_SUFFIX = '.blobs';\n\nexport interface SweepSessionStateOptions {\n directory: string;\n /** The caller's own timeline, which is never a candidate however it looks. */\n keep?: string;\n /** Remove the directory itself once nothing is left in it. */\n removeDirectory?: boolean;\n now?: number;\n maxAgeMs?: number;\n}\n\nasync function remove(target: string): Promise<void> {\n await fs.rm(target, { recursive: true, force: true });\n}\n\n/** The most recent time anything under this session's state moved. */\nasync function lastTouched(paths: readonly string[]): Promise<number | undefined> {\n let newest: number | undefined;\n for (const target of paths) {\n try {\n const stat = await fs.stat(target);\n if (newest === undefined || stat.mtimeMs > newest) newest = stat.mtimeMs;\n } catch {\n // Not there is not a reason to keep the rest.\n }\n }\n return newest;\n}\n\n/** How many sessions' worth of state was removed. */\nexport async function sweepSessionState(options: SweepSessionStateOptions): Promise<number> {\n const { directory, keep, removeDirectory = false, now = Date.now(), maxAgeMs = STATE_MAX_AGE_MS } = options;\n let entries: string[];\n try {\n entries = await fs.readdir(directory);\n } catch {\n return 0;\n }\n const floor = now - maxAgeMs;\n const timelines = new Set(entries.filter((entry) => entry.endsWith(TIMELINE_SUFFIX)));\n let removed = 0;\n\n for (const entry of timelines) {\n const timelinePath = path.join(directory, entry);\n if (keep !== undefined && timelinePath === keep) continue;\n const snapshotsPath = `${timelinePath.slice(0, -TIMELINE_SUFFIX.length)}${SNAPSHOTS_SUFFIX}`;\n const lockPath = `${timelinePath}.lock`;\n const touched = await lastTouched([timelinePath, snapshotsPath, lockPath]);\n if (touched === undefined || touched >= floor) continue;\n // Content first. A crash between the two leaves a timeline whose snapshots\n // are gone, which reads as a session whose diffs expired. The other order\n // leaves a blob tree nothing will ever name again, which is the shape of\n // the leak this exists to clear.\n await remove(snapshotsPath);\n await remove(timelinePath);\n await remove(lockPath);\n removed += 1;\n }\n\n // A blob tree whose timeline is already gone is exactly what a half-finished\n // cleanup leaves, and nothing else will ever look for it.\n for (const entry of entries) {\n if (!entry.endsWith(SNAPSHOTS_SUFFIX)) continue;\n if (timelines.has(`${entry.slice(0, -SNAPSHOTS_SUFFIX.length)}${TIMELINE_SUFFIX}`)) continue;\n const orphan = path.join(directory, entry);\n const touched = await lastTouched([orphan]);\n if (touched === undefined || touched >= floor) continue;\n await remove(orphan);\n }\n\n if (removeDirectory) {\n try {\n await fs.rmdir(directory);\n } catch {\n // Still holding something, so it is not this sweep's to remove.\n }\n }\n return removed;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { DoomHubChannel, DoomHubChannelSource } from '@agimon-ai/po-core/hubChannel';\n\nimport { filterDoomIgnoredFiles } from '../../services/doomIgnore';\nimport { confirmedChanges, foldEntries, foldVersions, isDiffable, parseTimeline } from '../../services/fileChanges';\nimport { FileEditPaths } from '../../services/fileEditPaths';\nimport { filesChannelType, type FilesItemView } from '../../types/webFiles';\n\n/**\n * The changed-files channel consumes snapshots emitted by the owning session\n * facet. Durable timeline reads seed a channel that starts after session startup.\n */\n\n/** Whether the file is still there to be opened. */\nfunction stillExists(filePath: string): boolean {\n try {\n return fs.statSync(filePath).isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Reads one session's timeline and presents it as the rows the dock lists.\n *\n * A file the session changed and then removed is left out, and so is a path a\n * bash call only touched: a command that moves a modification time without\n * changing a byte is not an edit, and listing it buries the ones that are. The\n * dock is a list of things to open, and a row that can only ever answer \"this\n * is gone\" is a dead end; the timeline still holds the change, so a tab already\n * open on the file keeps working and says so.\n */\nexport function readSessionFiles(timelinePath: string, cwd: string): FilesItemView[] {\n let content: string;\n try {\n content = fs.readFileSync(timelinePath, 'utf8');\n } catch {\n return [];\n }\n const events = confirmedChanges(parseTimeline(content));\n const items = foldEntries(events)\n .filter((entry) => stillExists(entry.path))\n .map((entry) => ({\n path: entry.path,\n relPath: path.relative(cwd, entry.path) || entry.path,\n tool: entry.tool,\n at: entry.at,\n count: entry.count,\n diffable: isDiffable(foldVersions(events, entry.path)),\n }));\n\n try {\n const doomIgnore = fs.readFileSync(path.join(cwd, '.poignore'), 'utf8');\n return filterDoomIgnoredFiles(items, doomIgnore);\n } catch {\n return items;\n }\n}\n\nfunction filesPayload(value: unknown): value is { items: FilesItemView[] } {\n return typeof value === 'object' && value !== null && Array.isArray((value as { items?: unknown }).items);\n}\n\n/**\n * The files data channel: one direct-event subscription per managed session,\n * published as { items } payloads under the 'file_edits' frame type.\n */\nexport function createFilesChannel(): DoomHubChannel {\n return {\n frameType: filesChannelType,\n start(host) {\n const latest = new Map<string, FilesItemView[]>();\n const subscriptions = new Map<string, () => void>();\n const channelSource: DoomHubChannelSource = {\n payloadFor(scope) {\n const items = latest.get(scope.sessionId);\n return items === undefined ? undefined : { items };\n },\n sessionAdded(scope) {\n subscriptions.get(scope.sessionId)?.();\n const paths = new FileEditPaths(scope.environment);\n let timelinePath: string | undefined;\n try {\n timelinePath = paths.timelinePath(scope.cwd, paths.sessionKey(scope.sessionId));\n } catch {\n // The session has no readable state path yet.\n }\n const items = timelinePath === undefined ? [] : readSessionFiles(timelinePath, scope.cwd);\n latest.set(scope.sessionId, items);\n host.publish(scope.sessionId, { items });\n const unsubscribe = host.directEvents.subscribe(\n filesChannelType,\n scope.sessionId,\n (payload) => {\n if (!filesPayload(payload)) return;\n latest.set(scope.sessionId, payload.items);\n host.publish(scope.sessionId, payload);\n },\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n },\n sessionRemoved(sessionId) {\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n latest.delete(sessionId);\n },\n close() {\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n latest.clear();\n },\n };\n return channelSource;\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { SUBAGENT_CHILD_ENV } from '@agimon-ai/po-core/childProcess';\nimport {\n type DoomHeadlessExecutionContext,\n type DoomHeadlessActivity,\n type DoomHeadlessHook,\n} from '@agimon-ai/po-core/headless';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { createDoomTelemetry } from '@agimon-ai/po-telemetry';\n\nimport { createDoomIgnoreMatcher } from '../../services/doomIgnore';\nimport { EditTracker } from '../../services/editTracker';\nimport { FileEditPaths } from '../../services/fileEditPaths';\nimport { NodeGitStatusAdapter } from '../../services/gitStatus';\nimport { NodeSnapshotStoreAdapter } from '../../services/snapshotStore';\nimport { TimelineStore } from '../../services/timelineStore';\nimport { NodeTreeManifestAdapter } from '../../services/treeManifest';\nimport { filesChannelType, filesStatusKey } from '../../types/webFiles';\nimport { api } from '../fileEditsApi';\nimport { sweepSessionState } from '../sessionStateSweep';\nimport { readSessionFiles } from '../webFilesChannel';\n\nconst DOOM_IGNORE_FILE = '.poignore';\n\nfunction readDoomIgnore(cwd: string): ((filePath: string) => boolean) | undefined {\n try {\n const matcher = createDoomIgnoreMatcher(fs.readFileSync(path.join(cwd, DOOM_IGNORE_FILE), 'utf8'));\n return matcher === undefined ? undefined : (filePath: string) => matcher(path.relative(cwd, filePath));\n } catch {\n return undefined;\n }\n}\n\nfunction stringValue(value: Readonly<Record<string, unknown>>, key: string): string | undefined {\n const candidate = value[key];\n return typeof candidate === 'string' && candidate.length > 0 ? candidate : undefined;\n}\n\nexport function createFileEditSession({ agent, host }: DoomServerPluginContext): DoomServerSessionPlugin {\n if (!agent) return { api: [api] };\n // Storage selection and deletion must share host ownership, never activity environment.\n const environment = Object.freeze({ ...host.context.environment });\n const paths = new FileEditPaths(environment);\n const timeline = new TimelineStore();\n const snapshots = new NodeSnapshotStoreAdapter();\n const editTracker = new EditTracker(timeline, snapshots, new NodeTreeManifestAdapter(), {\n git: new NodeGitStatusAdapter(),\n });\n if (host.context.directEvents === undefined)\n throw new Error('File-edit headless facet requires the session direct event bus.');\n const directEvents = host.context.directEvents;\n const telemetry = createDoomTelemetry({\n serviceName: 'doom-file-edit',\n packageName: '@agimon-ai/po-file-edit',\n env: environment,\n enableLogs: true,\n enableTraces: true,\n });\n const toolStartedAt = new Map<string, number>();\n const pending = new Set<Promise<void>>();\n let closed = false;\n let disposal: Promise<void> | undefined;\n let stopping: Promise<void> | undefined;\n let initialized = false;\n // Admit work before disposal starts, then drain it before clearing its stores.\n const track = (work: () => Promise<void>): Promise<void> => {\n const task = work();\n pending.add(task);\n void task.finally(() => pending.delete(task)).catch(() => {});\n return task;\n };\n let active:\n | {\n readonly context: DoomHeadlessExecutionContext;\n readonly timelinePath: string;\n readonly snapshotsPath: string;\n }\n | undefined;\n let lastRuntime: NonNullable<typeof active> | undefined;\n /**\n * The channel carries the rows; the status decides whether the group exists.\n *\n * A cockpit drives this facet and never the Pi runtime, so without the status\n * the dock had the file list on the wire and no frame to draw it in. The empty\n * string is deliberate: the group declares `hideWhenEmpty`, so a session that\n * has edited nothing publishes a key with no content and stays hidden.\n */\n const publish = (runtime: NonNullable<typeof active>): void => {\n const items = readSessionFiles(runtime.timelinePath, runtime.context.cwd);\n directEvents.publish(filesChannelType, runtime.context.sessionId, { items });\n runtime.context.client.setStatus(\n filesStatusKey,\n items.length === 0 ? '' : `${items.length} ${items.length === 1 ? 'file' : 'files'}`,\n );\n };\n const stopActivity = (): Promise<void> => {\n if (stopping) return stopping;\n const runtime = active;\n active = undefined;\n stopping = (async () => {\n await Promise.allSettled(pending);\n await editTracker.flush();\n editTracker.reset();\n toolStartedAt.clear();\n runtime?.context.client.setStatus(filesStatusKey, undefined);\n })();\n return stopping;\n };\n const dispose = (): Promise<void> => {\n if (disposal) return disposal;\n closed = true;\n disposal = (async () => {\n try {\n await stopActivity();\n if (lastRuntime) {\n if (!environment[SUBAGENT_CHILD_ENV]) {\n await timeline.clear();\n await snapshots.clear();\n }\n await telemetry.recordEvent('doom_file_edit.timeline_finished', { outcome: 'closed' });\n }\n } finally {\n await telemetry.shutdown();\n }\n })();\n return disposal;\n };\n const activity: DoomHeadlessActivity = {\n name: 'file-edits',\n start: async (executionContext) => {\n if (closed) throw new Error('File-edit session is closed.');\n if (active) return stopActivity;\n if (stopping) await stopping;\n if (closed) throw new Error('File-edit session is closed.');\n stopping = undefined;\n const sessionKey = paths.sessionKey(executionContext.sessionId);\n const timelinePath = paths.timelinePath(executionContext.cwd, sessionKey);\n const snapshotsPath = paths.snapshotsPath(executionContext.cwd, sessionKey);\n timeline.initialize(timelinePath);\n snapshots.initialize(snapshotsPath);\n const isIgnored = readDoomIgnore(executionContext.cwd);\n editTracker.reset({\n exclude: [timelinePath, `${timelinePath}.lock`, snapshotsPath],\n ...(isIgnored === undefined ? {} : { isIgnored }),\n });\n const runtime = { context: executionContext, timelinePath, snapshotsPath };\n active = runtime;\n lastRuntime = runtime;\n publish(runtime);\n void track(async () => {\n try {\n await sweepSessionState({ directory: paths.stateDirectory(), keep: timelinePath });\n const legacy = paths.legacyStateDirectory(executionContext.cwd);\n if (legacy !== undefined && legacy !== paths.stateDirectory())\n await sweepSessionState({ directory: legacy, removeDirectory: true });\n } catch {\n // Abandoned state must never prevent session startup.\n }\n });\n if (!initialized) {\n initialized = true;\n await track(() => telemetry.recordEvent('doom_file_edit.timeline_started', { outcome: 'initialized' }));\n }\n return () => (active === runtime ? stopActivity() : Promise.resolve());\n },\n };\n const startHook: DoomHeadlessHook = {\n event: 'tool_execution_start',\n handle: (event, executionContext) => {\n const runtime = active;\n const toolCallId = stringValue(event, 'toolCallId');\n const toolName = stringValue(event, 'toolName');\n if (runtime === undefined || runtime.context.sessionId !== executionContext.sessionId || !toolCallId || !toolName)\n return;\n return track(async () => {\n toolStartedAt.set(toolCallId, Date.now());\n const hookStartedAt = Date.now();\n await editTracker.start(toolCallId, toolName, event.args, executionContext.cwd);\n if (active !== runtime) return;\n await telemetry.recordEvent('doom_file_edit.edit_started', {\n 'tool.name': toolName,\n hook_ms: Date.now() - hookStartedAt,\n });\n });\n },\n };\n const endHook: DoomHeadlessHook = {\n event: 'tool_execution_end',\n handle: (event, executionContext) => {\n const runtime = active;\n const toolCallId = stringValue(event, 'toolCallId');\n const toolName = stringValue(event, 'toolName');\n if (runtime === undefined || runtime.context.sessionId !== executionContext.sessionId || !toolCallId || !toolName)\n return;\n return track(async () => {\n const startedAt = toolStartedAt.get(toolCallId);\n toolStartedAt.delete(toolCallId);\n const hookStartedAt = Date.now();\n const isError = event.isError === true;\n await editTracker.end(toolCallId, isError, executionContext.cwd);\n if (active !== runtime) return;\n publish(runtime);\n await telemetry.recordEvent('doom_file_edit.edit_finished', {\n 'tool.name': toolName,\n 'tool.result.error': isError,\n ...(startedAt === undefined ? {} : { duration_ms: Date.now() - startedAt }),\n hook_ms: Date.now() - hookStartedAt,\n outcome: isError ? 'failed' : 'completed',\n });\n });\n },\n };\n return { api: [api], activities: [activity], hooks: [startHook, endHook], onDispose: dispose };\n}\n","import type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport { createFileEditSession } from '../../../../../services/fileEditSession';\nexport const createFileEditServerRoot = (context: DoomServerPluginContext) => {\n const runtime = createFileEditSession(context);\n return { value: runtime, activities: runtime.activities, onDispose: runtime.onDispose };\n};\nexport type FileEditServerScope = Awaited<ReturnType<typeof createFileEditServerRoot>>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createFileEditServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createFileEditServerRoot);\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { createFilesChannel } from '../../../services/webFilesChannel';\nexport default defineChannel(function fileEditsChannel(): DoomHubChannel {\n return createFilesChannel();\n});\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/fileEditsApi';\n\n/**\n * Mounts this package's HTTP API at `file-edits`, which the folder names.\n *\n * Until this file existed the API was never mounted at all. The session root\n * returns a runtime carrying `api`, but a root's `api` is not folded into the\n * generated entry the way its services and activities are, so the registration\n * was dropped in silence and the cockpit called routes that answered 404.\n */\nexport default defineRoute(api);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { FileEditServerScope } from '../_lib/serverRoot';\nexport default defineHook((context: WithRoot<DoomServerPluginContext, FileEditServerScope>) => context.root.hooks![1]!);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\nimport type { FileEditServerScope } from '../_lib/serverRoot';\nexport default defineHook((context: WithRoot<DoomServerPluginContext, FileEditServerScope>) => context.root.hooks![0]!);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelFileEdits from '../src/extensions/(backend)/channel/fileEdits.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/file-edits/route.server';\nimport hookToolExecutionEnd from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server';\nimport hookToolExecutionStart from '../src/extensions/workspaces/sessions/(backend)/hook/tool-execution-start.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-file-edit',\n providerOverrides: composeServerProviderOverrides(rootSession),\n global: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'file_edits' }, channelFileEdits())]; },\n }),\n workspace: () => ({\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'file_edits' }, channelFileEdits())]; },\n }),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): DoomServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): DoomServerSessionPlugin['channels'] { return [() => via({ frameType: 'file_edits' }, channelFileEdits())]; },\n get hooks(): DoomServerSessionPlugin['hooks'] { return [via({ event: 'tool_execution_end' }, at(hookToolExecutionEnd, contextSession)), via({ event: 'tool_execution_start' }, at(hookToolExecutionStart, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
|