@agimon-ai/doompi-runner 0.0.1-alpha.77 → 0.0.1-alpha.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/mcp.cjs +17 -4091
- package/dist/extensions/mcp.cjs.map +1 -1
- package/dist/extensions/mcp.mjs +18 -4088
- package/dist/extensions/mcp.mjs.map +1 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.cjs +13 -5
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.cjs.map +1 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.mjs +13 -6
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.mjs.map +1 -1
- package/dist/src/services/launcher/index.cjs +8 -3
- package/dist/src/services/launcher/index.cjs.map +1 -1
- package/dist/src/services/launcher/index.d.cts +3 -1
- package/dist/src/services/launcher/index.d.cts.map +1 -1
- package/dist/src/services/launcher/index.d.mts +3 -1
- package/dist/src/services/launcher/index.d.mts.map +1 -1
- package/dist/src/services/launcher/index.mjs +8 -3
- package/dist/src/services/launcher/index.mjs.map +1 -1
- package/dist/src/services/lifeline/index.cjs +0 -3
- package/dist/src/services/lifeline/index.cjs.map +1 -1
- package/dist/src/services/lifeline/index.d.cts.map +1 -1
- package/dist/src/services/lifeline/index.d.mts.map +1 -1
- package/dist/src/services/lifeline/index.mjs +0 -3
- package/dist/src/services/lifeline/index.mjs.map +1 -1
- package/dist/src/services/runnerDependencies/index.cjs +1 -1
- package/dist/src/services/runnerDependencies/index.cjs.map +1 -1
- package/dist/src/services/runnerDependencies/index.mjs +1 -1
- package/dist/src/services/runnerDependencies/index.mjs.map +1 -1
- package/dist/src/services/runnerServerRuntime/index.cjs +17 -19
- package/dist/src/services/runnerServerRuntime/index.cjs.map +1 -1
- package/dist/src/services/runnerServerRuntime/index.mjs +17 -19
- package/dist/src/services/runnerServerRuntime/index.mjs.map +1 -1
- package/package.json +19 -19
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.cjs","names":["defineMcpSkill","readPackageResource","Type","utf8Tail","fs","StringDecoder","randomBytes","NON_INTERACTIVE_ENV","TERM_GRACE_MS","NOT_FOUND_ERROR_CODE","path","fs","fileURLToPath","NON_INTERACTIVE_ENV","TERM_GRACE_MS","net","path","createHash","os","fs","LOG_DRAIN_TIMEOUT_MS","PANE_STATE_FORMAT","STOP_GRACE_MS","SESSION_PREFIX","OWNED_TARGET_PATTERN","NON_INTERACTIVE_ENV","headless","PACKAGE_BY_PLATFORM","panePid","readTail","errorMessage","signalPane","waitForPaneStop","bundledBinary","RMUX","socketName","environment","path","waitForRunEnd","waitForLogDrain","delay","createRequire","createHash","readPaneExit","fs","fs","spawn","createRequire","path","NOT_FOUND_ERROR_CODE","execFileSync","fs","os","path","createProcessRegistryService","promisify","execFile","resolveRootSessionId","resolveProcessTags","fs","path","normalizeRepositoryPath","spawn","SESSION_PREFIX","OWNED_TARGET_PATTERN","path","createHash","NON_INTERACTIVE_ENV","fs","LOG_DRAIN_TIMEOUT_MS","PANE_STATE_FORMAT","defineMcpTool","defineMcpPlugin","skillDoompiUseRunner","toolBash"],"sources":["../../src/extensions/workspaces/sessions/(backend)/skill/doompi-use-runner.mcp.ts","../../src/constants/bashTool.ts","../../src/constants/headless.ts","../../src/schemas/bashTool.ts","../../src/constants/bashResult.ts","../../src/constants/ansiScrub.ts","../../src/services/ansiScrub/index.ts","../../src/constants/runnerConfig.ts","../../src/models/runnerSettings.ts","../../src/services/runnerConfig/index.ts","../../src/constants/tokenEstimate.ts","../../src/services/tokenEstimate/index.ts","../../src/services/bashResult/index.ts","../../src/services/headless/index.ts","../../src/constants/logReader.ts","../../src/services/logReader/index.ts","../../src/constants/bashRunService.ts","../../src/types/rtkProcessor.ts","../../src/services/bashRunService/index.ts","../../src/services/clock/index.ts","../../src/constants/launcher.ts","../../src/constants/session.ts","../../src/services/runnerSupervisor/index.ts","../../src/services/launcher/index.ts","../../src/constants/lifeline.ts","../../src/services/lifeline/client.ts","../../src/services/lifeline/index.ts","../../src/services/logFile/index.ts","../../src/services/processControl/index.ts","../../src/constants/tmuxBackend.ts","../../src/services/ptyBackendChain/index.ts","../../src/constants/ptyHost.ts","../../src/services/ptyHost/index.ts","../../src/services/ptySpawner/index.ts","../../src/constants/rmuxBackend.ts","../../src/services/rmuxBackend/index.ts","../../src/constants/rtkProcessor.ts","../../src/services/rtkProcessor/index.ts","../../src/constants/runnerNamer.ts","../../src/services/runnerNamer/index.ts","../../src/constants/runnerPaths.ts","../../src/services/session/index.ts","../../src/services/runnerPaths/index.ts","../../src/constants/runnerRegistry.ts","../../src/services/runnerRecord/index.ts","../../src/services/runnerRegistry/index.ts","../../src/constants/spawner.ts","../../src/services/spawner/index.ts","../../src/services/tmuxBackend/TmuxClient.ts","../../src/services/tmuxBackend/index.ts","../../src/services/runnerDependencies/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/bash.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { defineMcpSkill } from '@agimon-ai/doompi-core/mcp-facet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/server-facet';\n\nexport default defineMcpSkill({\n name: 'doompi-use-runner',\n description:\n 'Use Doom Pi Runner to supervise shell commands, inspect durable logs, provide interactive input, and stop background runs.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-runner/SKILL.md'),\n});\n","export const BASH_TOOL_NAME = 'bash';\nexport const BASH_TOOL_LABEL = 'bash';\n\nexport const MS_PER_SECOND = 1000;\n","export const MS_PER_SECOND = 1000;\n","import { type Static, Type } from 'typebox';\n\n/** Replaces pi's built-in tool of the same name, so hooks keyed on `bash` keep working. */\n\n/**\n * Built-in bash parameters plus the backgrounding controls.\n *\n * `command` and `timeout` keep their original names and wording so the tool is\n * a drop-in replacement. Every `description` is prompt copy the model reads.\n */\nexport const BashParamsSchema = Type.Object({\n command: Type.String({ description: 'Bash command to execute' }),\n timeout: Type.Optional(Type.Number({ description: 'Timeout in seconds (optional, no default timeout)' })),\n background: Type.Optional(\n Type.Boolean({\n description:\n 'Start the command in the background immediately instead of waiting. Use for dev servers, watchers and other commands you already know will not finish.',\n }),\n ),\n interactive: Type.Optional(\n Type.Boolean({\n description:\n 'Run under a pseudo terminal so the command can prompt. Use only when the command asks for confirmation or input; answer it with doom-runner input. Logs from interactive runs are noisier.',\n }),\n ),\n name: Type.Optional(\n Type.String({\n description: 'Preferred runner name if the command ends up in the background. A name in use gets a suffix.',\n }),\n ),\n});\n\nexport type BashParams = Static<typeof BashParamsSchema>;\n","export const BYTES_PER_KB = 1024;\nexport const BYTES_PER_MB = 1_048_576;\nexport const MAX_VARIANTS_TRACKED = 12;\nexport const MIN_SHARED_AFFIX = 8;\nexport const ERROR_WINDOW_CHARS = 60;\nexport const PRAGMA_PATTERN = /^\\s*(?:#|\\/\\/)\\s*@doom:\\s*(\\{.*\\})\\s*$/u;\nexport const PRAGMA_BYTES_CEILING = 262_144;\nexport const PRAGMA_LINES_CEILING = 5_000;\nexport const PRAGMA_TOKENS_CEILING = 100_000;\n","// oxlint-disable-next-line no-control-regex -- matching terminal control bytes is the whole job\nexport const CONTROL_PATTERN = /[\\u0007\\u0008]/g;\n","import { CONTROL_PATTERN } from '../../constants/ansiScrub';\n/**\n * Turns terminal output into text worth grepping.\n *\n * PTY output is written for a screen, not a file: colours, cursor moves and\n * carriage-return redraws all end up in the byte stream. Without this a single\n * progress bar becomes thousands of near-identical log lines.\n */\n\n/**\n * CSI sequences (colours, cursor moves), OSC strings, the two-byte Fe escapes,\n * and the keypad-mode escapes shells emit around every prompt.\n */\nconst ANSI_PATTERN = new RegExp(\n [\n '\\\\u001B\\\\[[0-9;?]*[ -/]*[@-~]',\n '\\\\u001B\\\\][^\\\\u0007\\\\u001B]*(?:\\\\u0007|\\\\u001B\\\\\\\\)',\n '\\\\u001B[@-Z\\\\\\\\-_]',\n '\\\\u001B[=><]',\n ].join('|'),\n 'g',\n);\n\n/** Bell and backspace, which a terminal consumes rather than displays. */\n// oxlint-disable-next-line no-control-regex -- matching terminal control bytes is the whole job\n\n/**\n * The same set minus SGR, the sequence that only sets colour and weight.\n *\n * The final byte class omits `m`, so `ESC[32m` survives while cursor moves,\n * OSC strings and keypad escapes are still removed.\n */\nconst ANSI_PATTERN_KEEPING_COLOUR = new RegExp(\n [\n '\\\\u001B\\\\[[0-9;?]*[ -/]*[@-ln-~]',\n '\\\\u001B\\\\][^\\\\u0007\\\\u001B]*(?:\\\\u0007|\\\\u001B\\\\\\\\)',\n '\\\\u001B[@-Z\\\\\\\\-_]',\n '\\\\u001B[=><]',\n ].join('|'),\n 'g',\n);\n\n/** Removes escape sequences, leaving the characters a user would have seen. */\nexport function stripAnsi(text: string): string {\n return text.replace(ANSI_PATTERN, '').replace(CONTROL_PATTERN, '');\n}\n\n/**\n * Removes everything `stripAnsi` does except colour.\n *\n * Logs are read back by a terminal renderer as well as by grep, and a command\n * that colours its own output has already said how it should look.\n */\nexport function stripAnsiKeepingColour(text: string): string {\n return text.replace(ANSI_PATTERN_KEEPING_COLOUR, '').replace(CONTROL_PATTERN, '');\n}\n\n/**\n * Collapses carriage-return redraws.\n *\n * A bare `\\r` returns the cursor to the start of the current line, so whatever\n * follows overwrites it. Only the final state of each line is kept. The `\\r` of\n * a CRLF pair is a line terminator rather than a redraw, so it is normalised\n * away first: a pseudo terminal ends every line that way, and treating it as a\n * redraw would blank the whole log.\n */\nexport function collapseCarriageReturns(text: string): string {\n return text\n .replace(/\\r\\n/g, '\\n')\n .split('\\n')\n .map((line) => (line.includes('\\r') ? (line.split('\\r').at(-1) ?? '') : line))\n .join('\\n');\n}\n\n/** Full scrub applied to PTY output before it reaches the log file. */\nexport function scrubTerminalOutput(text: string): string {\n return collapseCarriageReturns(stripAnsiKeepingColour(text));\n}\n\n/** Plain-text scrub, for the copy the model reads. */\nexport function scrubTerminalOutputToPlainText(text: string): string {\n return collapseCarriageReturns(stripAnsi(text));\n}\n","export const BG_THRESHOLD_MS_ENV = 'DOOM_RUNNER_BG_THRESHOLD_MS';\nexport const RESULT_MAX_BYTES_ENV = 'DOOM_RUNNER_RESULT_MAX_BYTES';\nexport const SUCCESS_RESULT_MAX_BYTES_ENV = 'DOOM_RUNNER_SUCCESS_RESULT_MAX_BYTES';\nexport const RESULT_MAX_TOKENS_ENV = 'DOOM_RUNNER_RESULT_MAX_TOKENS';\nexport const SUCCESS_RESULT_MAX_TOKENS_ENV = 'DOOM_RUNNER_SUCCESS_RESULT_MAX_TOKENS';\nexport const LOG_MAX_BYTES_ENV = 'DOOM_RUNNER_LOG_MAX_BYTES';\nexport const LOG_TTL_MS_ENV = 'DOOM_RUNNER_LOG_TTL_MS';\nexport const MAX_COMPLETED_RUNNERS_ENV = 'DOOM_RUNNER_MAX_COMPLETED_RUNNERS';\nexport const LOG_DIR_ENV = 'DOOM_RUNNER_LOG_DIR';\nexport const DEFAULT_BG_THRESHOLD_MS = 60_000;\nexport const DEFAULT_RESULT_MAX_BYTES = 8_192;\nexport const DEFAULT_RESULT_MAX_LINES = 120;\nexport const DEFAULT_SUCCESS_RESULT_MAX_BYTES = 2_048;\nexport const DEFAULT_RESULT_MAX_TOKENS = 2_048;\nexport const DEFAULT_SUCCESS_RESULT_MAX_TOKENS = 512;\nexport const DEFAULT_HEAD_RATIO = 0.2;\nexport const DEFAULT_ERROR_BUDGET_RATIO = 0.2;\nexport const DEFAULT_ERROR_MAX_ENTRIES = 10;\nexport const DEFAULT_ERROR_MAX_VARIANTS_JOINED = 6;\nexport const DEFAULT_LOG_MAX_BYTES = 5_242_880;\nexport const DEFAULT_LOG_TTL_MS = 604_800_000;\nexport const DEFAULT_MAX_COMPLETED_RUNNERS = 100;\n","import type { RunnerSettings } from '../types/runnerSettings';\nexport const runnerSettingsState: { value: RunnerSettings } = { value: {} };\n","import {\n BG_THRESHOLD_MS_ENV,\n DEFAULT_BG_THRESHOLD_MS,\n DEFAULT_ERROR_BUDGET_RATIO,\n DEFAULT_ERROR_MAX_ENTRIES,\n DEFAULT_ERROR_MAX_VARIANTS_JOINED,\n DEFAULT_HEAD_RATIO,\n DEFAULT_LOG_MAX_BYTES,\n DEFAULT_LOG_TTL_MS,\n DEFAULT_MAX_COMPLETED_RUNNERS,\n DEFAULT_RESULT_MAX_BYTES,\n DEFAULT_RESULT_MAX_LINES,\n DEFAULT_RESULT_MAX_TOKENS,\n DEFAULT_SUCCESS_RESULT_MAX_BYTES,\n DEFAULT_SUCCESS_RESULT_MAX_TOKENS,\n LOG_MAX_BYTES_ENV,\n LOG_TTL_MS_ENV,\n MAX_COMPLETED_RUNNERS_ENV,\n RESULT_MAX_BYTES_ENV,\n RESULT_MAX_TOKENS_ENV,\n SUCCESS_RESULT_MAX_BYTES_ENV,\n SUCCESS_RESULT_MAX_TOKENS_ENV,\n} from '../../constants/runnerConfig';\n/**\n * Extension configuration, read from the environment.\n *\n * Env rather than a config file, matching doom-task: doom-pi already carries\n * per-launch state to Pi through env vars.\n */\n/** Unscoped override for the runner log directory. */\nimport type { RunnerSettings } from '../../types/runnerSettings';\n\n/** A bash call still running at this point is promoted to a named runner. */\n/** Tool results past this size are excerpted and spilled to the log file. */\n/** Line ceiling for a single tool result, applied alongside the byte ceiling. */\n/**\n * A successful command gets a much smaller budget than a failing one. Exiting 0\n * has already reported the outcome, so its output buys far less than a failure's.\n */\n/**\n * Token ceilings sit alongside the byte ones because bytes do not track context\n * cost: 8 KiB of prose is roughly 2k tokens, while 8 KiB of base64 or CJK is\n * several times that. Whichever ceiling binds first wins.\n */\n/** Share of the budget spent on the leading excerpt; the remainder goes to the tail. */\n/** Share reserved for errors rescued from the elided middle, when any exist. */\n/** Distinct failures rescued from the elided middle. */\n/** Variants joined inside one bracket before the rest become a count. */\n/** Legacy rotation value retained so existing configuration imports remain compatible. */\n/** Retention window for logs with no live registry record. */\n/**\n * Completed runners a single session keeps on disk.\n *\n * A count bound, not just an age one, because a session that stays open for a\n * working day never ages its own records past the TTL: they would accumulate\n * for as long as the session lives.\n */\n\nfunction positiveNumber(raw: string | undefined, fallback: number): number {\n const value = Number(raw);\n return Number.isFinite(value) && value > 0 ? value : fallback;\n}\n\n/** Age at which a bash call becomes a background runner. */\nexport function getBackgroundThresholdMs(env: NodeJS.ProcessEnv = process.env): number {\n return positiveNumber(env[BG_THRESHOLD_MS_ENV], DEFAULT_BG_THRESHOLD_MS);\n}\n\n/** Byte ceiling for a single tool result before tail truncation kicks in. */\nexport function getResultMaxBytes(env: NodeJS.ProcessEnv = process.env): number {\n return positiveNumber(\n env[RESULT_MAX_BYTES_ENV],\n runnerSettingsState.value.maxResultBytes ?? DEFAULT_RESULT_MAX_BYTES,\n );\n}\n\n/** Legacy rotation setting retained for public API compatibility. */\nexport function getLogMaxBytes(env: NodeJS.ProcessEnv = process.env): number {\n return positiveNumber(env[LOG_MAX_BYTES_ENV], DEFAULT_LOG_MAX_BYTES);\n}\n\n/** Retention window for orphaned runner logs. */\nexport function getLogTtlMs(env: NodeJS.ProcessEnv = process.env): number {\n return positiveNumber(env[LOG_TTL_MS_ENV], DEFAULT_LOG_TTL_MS);\n}\n\n/** Completed runners one session retains before the oldest are swept. */\nexport function getMaxCompletedRunners(env: NodeJS.ProcessEnv = process.env): number {\n return positiveNumber(env[MAX_COMPLETED_RUNNERS_ENV], DEFAULT_MAX_COMPLETED_RUNNERS);\n}\n\n/**\n * Project settings sit below the environment on purpose: a checked-in file is\n * the project's baseline, while an environment variable is set for one run.\n */\nimport { runnerSettingsState } from '../../models/runnerSettings';\n\nexport function setRunnerSettings(settings: RunnerSettings): void {\n runnerSettingsState.value = settings;\n}\n\nexport function getRunnerSettings(): RunnerSettings {\n return runnerSettingsState.value;\n}\n\n/** Byte ceiling for the result of a command that succeeded. */\nexport function getSuccessResultMaxBytes(env: NodeJS.ProcessEnv = process.env): number {\n return positiveNumber(\n env[SUCCESS_RESULT_MAX_BYTES_ENV],\n runnerSettingsState.value.successMaxResultBytes ?? DEFAULT_SUCCESS_RESULT_MAX_BYTES,\n );\n}\n\n/** Token ceiling for a single tool result. */\nexport function getResultMaxTokens(env: NodeJS.ProcessEnv = process.env): number {\n return positiveNumber(\n env[RESULT_MAX_TOKENS_ENV],\n runnerSettingsState.value.maxResultTokens ?? DEFAULT_RESULT_MAX_TOKENS,\n );\n}\n\n/** Token ceiling for the result of a command that succeeded. */\nexport function getSuccessResultMaxTokens(env: NodeJS.ProcessEnv = process.env): number {\n return positiveNumber(\n env[SUCCESS_RESULT_MAX_TOKENS_ENV],\n runnerSettingsState.value.successMaxResultTokens ?? DEFAULT_SUCCESS_RESULT_MAX_TOKENS,\n );\n}\n\n/** Line ceiling for a single tool result. */\nexport function getResultMaxLines(): number {\n return runnerSettingsState.value.maxResultLines ?? DEFAULT_RESULT_MAX_LINES;\n}\n\nexport function getHeadRatio(): number {\n return runnerSettingsState.value.headRatio ?? DEFAULT_HEAD_RATIO;\n}\n\nexport function getErrorBudgetRatio(): number {\n return runnerSettingsState.value.errorBudgetRatio ?? DEFAULT_ERROR_BUDGET_RATIO;\n}\n\nexport function getErrorMaxEntries(): number {\n return runnerSettingsState.value.errorMaxEntries ?? DEFAULT_ERROR_MAX_ENTRIES;\n}\n\nexport function getErrorMaxVariantsJoined(): number {\n return runnerSettingsState.value.errorMaxVariantsJoined ?? DEFAULT_ERROR_MAX_VARIANTS_JOINED;\n}\n\n/** Extra severity patterns appended to the built-in matcher. */\nexport function getErrorPatterns(): readonly string[] {\n return runnerSettingsState.value.errorPatterns ?? [];\n}\n","export const WORD_CHARS_PER_TOKEN = 5;\nexport const MIXED_CHARS_PER_TOKEN = 2.1;\nexport const PUNCTUATION_CHARS_PER_TOKEN = 2;\nexport const DENSE_SCRIPT_START = 0x2fff;\nexport const ASTRAL_TOKEN_COST = 2;\nexport const ALPHANUMERIC_RUN = /([^\\p{L}\\p{N}]+)/u;\nexport const HAS_ALPHANUMERIC = /[\\p{L}\\p{N}]/u;\nexport const HAS_DIGIT = /\\d/u;\nexport const HAS_LETTER = /[A-Za-z]/u;\n","import {\n ALPHANUMERIC_RUN,\n ASTRAL_TOKEN_COST,\n DENSE_SCRIPT_START,\n HAS_ALPHANUMERIC,\n HAS_DIGIT,\n HAS_LETTER,\n MIXED_CHARS_PER_TOKEN,\n PUNCTUATION_CHARS_PER_TOKEN,\n WORD_CHARS_PER_TOKEN,\n} from '../../constants/tokenEstimate';\n/**\n * Estimates BPE tokens without carrying a vocabulary.\n *\n * Bytes are a poor proxy for what a result costs: measured against\n * `gpt-tokenizer`, real text ranges from 1.4 characters per token for base64 to\n * 4.7 for English prose, a spread of more than three. Truncation runs on every\n * command and is synchronous, so loading a real tokenizer here would charge\n * every session for a table most of them never need.\n *\n * The shape below follows how BPE actually behaves: punctuation forces token\n * boundaries, natural words are cheap, and runs mixing letters with digits are\n * expensive. Calibrated against `gpt-tokenizer` over build logs, source, JSON,\n * stack traces, CJK, base64, hex, and UUIDs, the worst error is a factor of\n * 1.46 either way. It stays an estimate, so the byte ceiling remains the hard\n * bound behind it.\n */\n\n/** Characters per token for a natural word. */\n/** Characters per token for a run mixing letters and digits, which tokenizes poorly. */\n/** Characters per token for a run of punctuation or whitespace. */\n/** Dense scripts such as CJK cost about one token per character. */\n\nexport function estimateTokens(text: string): number {\n let tokens = 0;\n for (const run of text.split(ALPHANUMERIC_RUN)) {\n if (run.length === 0) continue;\n if (!HAS_ALPHANUMERIC.test(run)) {\n tokens += run.length / PUNCTUATION_CHARS_PER_TOKEN;\n continue;\n }\n\n let ascii = 0;\n let dense = 0;\n let astral = 0;\n for (const character of run) {\n const code = character.codePointAt(0) ?? 0;\n if (code > 0xffff) astral += 1;\n else if (code > DENSE_SCRIPT_START) dense += 1;\n else ascii += 1;\n }\n tokens += astral * ASTRAL_TOKEN_COST + dense;\n if (ascii > 0) {\n const mixed = HAS_DIGIT.test(run) && HAS_LETTER.test(run);\n tokens += Math.max(1, ascii / (mixed ? MIXED_CHARS_PER_TOKEN : WORD_CHARS_PER_TOKEN));\n }\n }\n return Math.ceil(tokens);\n}\n","import {\n BYTES_PER_KB,\n BYTES_PER_MB,\n ERROR_WINDOW_CHARS,\n MAX_VARIANTS_TRACKED,\n MIN_SHARED_AFFIX,\n PRAGMA_BYTES_CEILING,\n PRAGMA_LINES_CEILING,\n PRAGMA_PATTERN,\n PRAGMA_TOKENS_CEILING,\n} from '../../constants/bashResult';\nimport type {\n ErrorLineScanner,\n Excerpt,\n LogSummarizer,\n LogSummary,\n ResultBudget,\n SalvagedError,\n ToolResult,\n} from '../../types/bashResult';\nimport type { BashRunResult, CompletedRun } from '../../types/bashRunService';\nimport type { RunnerRecord } from '../../types/runnerRegistry';\nimport { stripAnsi } from '../ansiScrub';\nimport {\n getErrorBudgetRatio,\n getErrorMaxEntries,\n getErrorMaxVariantsJoined,\n getErrorPatterns,\n getHeadRatio,\n getResultMaxBytes,\n getResultMaxLines,\n getResultMaxTokens,\n getSuccessResultMaxBytes,\n getSuccessResultMaxTokens,\n} from '../runnerConfig';\nimport { estimateTokens } from '../tokenEstimate';\n\n/** Variants retained per group while scanning; the rest only add to the count. */\n/** Below this much shared text, joining reads worse than separate lines. */\n/**\n * Severity anywhere in the opening of the line, not only at its start. Real\n * tooling buries it behind a tag or a timestamp: `[widget] build: error TS2322`.\n */\nconst SEVERITY_TOKEN =\n /(?<![\\w\\-/=.])(?:errors?|fatal|panic|exception|traceback|assertion|assert|fail(?:ed|ure|ures)?)\\b/iu;\n/** `0 errors` and `no failures` are the success case wearing the same words. */\nconst NEGATED_SEVERITY = /\\b(?:no|not|without|zero|0)\\s+$/iu;\n\nexport function textResult(text: string, details: Record<string, unknown> = {}): ToolResult {\n return { content: [{ type: 'text', text }], details };\n}\n\nexport function errorResult(message: string): ToolResult {\n return textResult(`Error: ${message}`, { error: message });\n}\n\n/** Human-readable byte count, matching how pi reports output sizes. */\nexport function formatSize(bytes: number): string {\n if (bytes < BYTES_PER_KB) return `${bytes} B`;\n if (bytes < BYTES_PER_MB) return `${(bytes / BYTES_PER_KB).toFixed(1)} KB`;\n return `${(bytes / BYTES_PER_MB).toFixed(1)} MB`;\n}\n\nfunction formatCount(value: number): string {\n return value.toLocaleString('en-US');\n}\n\nexport function countLines(text: string): number {\n if (text.length === 0) return 0;\n return text.endsWith('\\n') ? text.split('\\n').length - 1 : text.split('\\n').length;\n}\n\n/** Accumulates grouped error lines while a log streams past, without holding it. */\nclass ErrorScan {\n private readonly byKey = new Map<string, { variants: string[]; count: number }>();\n\n constructor(private readonly maxEntries = getErrorMaxEntries()) {}\n\n push(line: string): void {\n if (!isErrorLine(line)) return;\n const trimmed = line.trim();\n const key = normalizeErrorLine(line);\n const existing = this.byKey.get(key);\n if (existing) {\n existing.count += 1;\n // Only a line we have not seen verbatim earns a slot; identical repeats\n // are carried by the count alone.\n if (!existing.variants.includes(trimmed) && existing.variants.length < MAX_VARIANTS_TRACKED) {\n existing.variants.push(trimmed);\n }\n return;\n }\n if (this.byKey.size >= this.maxEntries) return;\n this.byKey.set(key, { variants: [trimmed], count: 1 });\n }\n\n entries(): SalvagedError[] {\n return [...this.byKey.values()];\n }\n}\n\nexport function createErrorScanner(maxEntries = getErrorMaxEntries()): ErrorLineScanner {\n return new ErrorScan(maxEntries);\n}\n\n/** Codex-style first-line pragma, for the one command that needs a wider result. */\n\nfunction positiveInteger(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined;\n}\n\n/**\n * Reads `# @doom: {\"maxResultBytes\": 32768}` from the command's first line.\n *\n * The pragma is a shell comment, so it stays harmless if this parse is ever\n * skipped, and it survives verbatim into the log where a wider result can be\n * explained after the fact.\n */\nexport function parseResultPragma(command: string): ResultBudget {\n const match = PRAGMA_PATTERN.exec(command.split('\\n', 1)[0] ?? '');\n if (!match?.[1]) return {};\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(match[1]);\n } catch {\n return {};\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {};\n\n const source = parsed as Record<string, unknown>;\n const maxBytes = positiveInteger(source.maxResultBytes);\n const maxLines = positiveInteger(source.maxResultLines);\n const maxTokens = positiveInteger(source.maxResultTokens);\n return {\n ...(maxBytes === undefined ? {} : { maxBytes: Math.min(maxBytes, PRAGMA_BYTES_CEILING) }),\n ...(maxLines === undefined ? {} : { maxLines: Math.min(maxLines, PRAGMA_LINES_CEILING) }),\n ...(maxTokens === undefined ? {} : { maxTokens: Math.min(maxTokens, PRAGMA_TOKENS_CEILING) }),\n };\n}\n\nfunction isErrorLine(line: string): boolean {\n const opening = line.slice(0, ERROR_WINDOW_CHARS);\n const match = SEVERITY_TOKEN.exec(opening);\n if (match && !NEGATED_SEVERITY.test(opening.slice(0, match.index))) return true;\n // Project patterns extend the built-in matcher; tooling this does not\n // recognise is exactly why they exist, so they are not negation-checked.\n return getErrorPatterns().some((pattern) => matchesCustomPattern(pattern, line));\n}\n\nconst customPatterns = new Map<string, RegExp | undefined>();\n\nfunction matchesCustomPattern(pattern: string, line: string): boolean {\n if (!customPatterns.has(pattern)) {\n try {\n customPatterns.set(pattern, new RegExp(pattern, 'iu'));\n } catch {\n customPatterns.set(pattern, undefined);\n }\n }\n return customPatterns.get(pattern)?.test(line) ?? false;\n}\n\n/** Masks the parts that differ between repeats of the same underlying failure. */\nfunction normalizeErrorLine(line: string): string {\n return line\n .replace(/0x[0-9a-f]+/giu, '0x#')\n .replace(/\\/[^\\s:,)]+/gu, '/#')\n .replace(/\\d+/gu, '#')\n .trim()\n .toLowerCase();\n}\n\n/**\n * Collapses repeats so one failure repeated two hundred times costs one slot.\n * Counting continues past the entry cap so the tallies stay honest.\n */\nexport function collectErrorLines(lines: Iterable<string>, maxEntries = getErrorMaxEntries()): SalvagedError[] {\n const scan = new ErrorScan(maxEntries);\n for (const line of lines) scan.push(line);\n return scan.entries();\n}\n\nfunction commonAffixLengths(values: readonly string[]): { prefix: number; suffix: number } {\n const shortest = values.reduce((min, value) => Math.min(min, value.length), Infinity);\n let prefix = 0;\n while (prefix < shortest && values.every((value) => value[prefix] === values[0]![prefix])) prefix += 1;\n let suffix = 0;\n while (\n suffix < shortest - prefix &&\n values.every((value) => value[value.length - 1 - suffix] === values[0]![values[0]!.length - 1 - suffix])\n ) {\n suffix += 1;\n }\n return { prefix, suffix };\n}\n\n/** Back off to a whitespace boundary so the brackets hold whole tokens. */\nfunction retreatToBoundary(value: string, length: number): number {\n const boundary = value.lastIndexOf(' ', Math.max(0, length - 1));\n return boundary >= 0 ? boundary + 1 : 0;\n}\n\nfunction advanceToBoundary(value: string, length: number): number {\n const cut = value.length - length;\n const boundary = value.indexOf(' ', cut);\n return boundary >= 0 ? value.length - boundary : 0;\n}\n\n/**\n * One line per distinct failure would be ideal, but repeats of one failure\n * across many files are the common case. Sharing the fixed text and joining\n * only the parts that differ keeps every file name without paying for the\n * message N times.\n */\nfunction renderGroup(group: SalvagedError): string {\n const [first] = group.variants;\n if (first === undefined) return '';\n if (group.variants.length === 1) {\n return group.count > 1 ? `${first} (\\u00d7${formatCount(group.count)})` : first;\n }\n\n const shown = group.variants.slice(0, getErrorMaxVariantsJoined());\n const { prefix, suffix } = commonAffixLengths(shown);\n const head = first.slice(0, retreatToBoundary(first, prefix));\n const tailLength = advanceToBoundary(first, suffix);\n const tail = tailLength > 0 ? first.slice(first.length - tailLength) : '';\n if (head.length + tail.length < MIN_SHARED_AFFIX) return shown.join('\\n');\n\n const middles = shown.map((value) => value.slice(head.length, value.length - tail.length));\n const overflow = group.variants.length - shown.length;\n const joined = overflow > 0 ? `${middles.join('|')}|+${formatCount(overflow)}` : middles.join('|');\n return `${head}[${joined}]${tail}`;\n}\n\nfunction renderSalvaged(errors: readonly SalvagedError[], maxBytes: number): string[] {\n const rendered: string[] = [];\n let used = 0;\n for (const group of errors) {\n const text = renderGroup(group);\n if (text.length === 0) continue;\n const cost = Buffer.byteLength(text, 'utf8') + 1;\n if (used + cost > maxBytes) break;\n rendered.push(text);\n used += cost;\n }\n return rendered;\n}\n\ninterface Side {\n readonly text: string;\n readonly count: number;\n}\n\nfunction elisionMarker(elidedLines: number, salvagedCount = 0): string {\n const unit = elidedLines === 1 ? 'line' : 'lines';\n const errors = salvagedCount > 0 ? `, ${formatCount(salvagedCount)} shown below` : '';\n return `\\u2026 [${formatCount(elidedLines)} ${unit} elided${errors}] \\u2026`;\n}\n\nfunction splitTextLines(text: string): { lines: string[]; trailingNewline: boolean } {\n const trailingNewline = text.endsWith('\\n');\n const lines = text.split('\\n');\n if (trailingNewline) lines.pop();\n return { lines, trailingNewline };\n}\n\n/** Whole lines only: a fragment of a line reads as data that was never there. */\nfunction takeSide(\n lines: readonly string[],\n maxCount: number,\n maxBytes: number,\n fromEnd: boolean,\n maxTokens = Number.POSITIVE_INFINITY,\n): Side {\n if (maxBytes <= 0 || maxCount <= 0) return { text: '', count: 0 };\n const ordered = fromEnd ? [...lines].reverse() : [...lines];\n const taken: string[] = [];\n let used = 0;\n let tokens = 0;\n for (const line of ordered) {\n if (taken.length >= maxCount) break;\n const cost = Buffer.byteLength(line, 'utf8') + (taken.length > 0 ? 1 : 0);\n // Dense output costs far more context than its byte count implies, so the\n // token ceiling stops the side even while bytes remain. The separator is\n // charged too: summing per-line estimates otherwise drifts past the ceiling\n // once the newlines between them are counted.\n const lineTokens = estimateTokens(line) + (taken.length > 0 ? 1 : 0);\n if (used + cost > maxBytes || tokens + lineTokens > maxTokens) break;\n taken.push(line);\n used += cost;\n tokens += lineTokens;\n }\n if (taken.length === 0) return { text: '', count: 0 };\n const restored = fromEnd ? taken.reverse() : taken;\n return { text: restored.join('\\n'), count: restored.length };\n}\n\n/**\n * Used only when no whole line fits either side, which means the source has no\n * usable line boundary. A clamped fragment beats returning nothing at all.\n */\nfunction clampedFragment(text: string, limitBytes: number): Side {\n const clamped = utf8Tail(Buffer.from(text, 'utf8'), limitBytes);\n const newline = clamped.indexOf('\\n');\n const whole = newline >= 0 ? clamped.slice(newline + 1) : clamped;\n return { text: whole, count: whole.length > 0 ? 1 : 0 };\n}\n\n/** Charged against the budget so the composed excerpt still honours the ceiling. */\nfunction markerReservation(totalLines: number): number {\n return Buffer.byteLength(elisionMarker(totalLines), 'utf8') + 2;\n}\n\nfunction assembleSides(\n head: Side,\n tail: Side,\n elidedLines: number,\n trailingNewline: boolean,\n limitBytes: number,\n salvaged: readonly string[] = [],\n): Excerpt {\n const opening = elidedLines > 0 ? [elisionMarker(elidedLines, salvaged.length)] : [];\n const closing = elidedLines > 0 && salvaged.length > 0 ? ['\\u2026'] : [];\n const parts = [head.text, ...opening, ...salvaged, ...closing, tail.text].filter((part) => part.length > 0);\n const body = parts.join('\\n');\n const withNewline = trailingNewline && body.length > 0 ? `${body}\\n` : body;\n const encoded = Buffer.from(withNewline, 'utf8');\n // Last resort: the ceiling is a hard promise to the caller.\n const text = encoded.byteLength > limitBytes ? utf8Tail(encoded, limitBytes) : withNewline;\n return { text, lines: head.count + tail.count, truncated: true, elidedLines: Math.max(0, elidedLines) };\n}\n\nfunction budgetFor(maxLines: number, maxBytes: number, totalLines: number, maxTokens: number) {\n const limitLines = Math.max(1, Math.floor(maxLines));\n const usable = Math.max(0, maxBytes - markerReservation(totalLines));\n // The marker is charged against both ceilings; counting it in bytes alone let\n // the composed excerpt drift past the token ceiling.\n const tokens = Math.max(1, Math.floor(maxTokens) - estimateTokens(elisionMarker(totalLines)));\n return {\n limitLines,\n headLines: Math.max(1, Math.floor(limitLines * getHeadRatio())),\n headBytes: Math.max(0, Math.floor(usable * getHeadRatio())),\n headTokens: Math.max(1, Math.floor(tokens * getHeadRatio())),\n usable,\n tokens,\n };\n}\n\n/**\n * Bounds text to the budget, keeping a leading excerpt as well as the tail.\n *\n * Failures announce themselves at both ends: a command that could not start\n * says so on line one, and a command that died says so on the last. Keeping\n * only the tail loses the first kind entirely.\n */\nexport function boundExcerpt(\n text: string,\n maxLines = getResultMaxLines(),\n maxBytes = getResultMaxBytes(),\n maxTokens = getResultMaxTokens(),\n): Excerpt {\n if (text.length === 0) return { text: '', lines: 0, truncated: false, elidedLines: 0 };\n const { lines, trailingNewline } = splitTextLines(text);\n const totalBytes = Buffer.byteLength(text, 'utf8');\n const limitBytes = Number.isFinite(maxBytes) ? Math.max(0, Math.floor(maxBytes)) : totalBytes;\n const limitLines = Math.max(1, Math.floor(maxLines));\n if (lines.length <= limitLines && totalBytes <= limitBytes && estimateTokens(text) <= maxTokens) {\n return { text, lines: lines.length, truncated: false, elidedLines: 0 };\n }\n\n const budget = budgetFor(maxLines, limitBytes, lines.length, maxTokens);\n const head = takeSide(lines, budget.headLines, budget.headBytes, false, budget.headTokens);\n const rest = lines.slice(head.count);\n // Reserved only when the region beyond the head actually holds errors, so a\n // clean run still spends the whole remaining budget on its tail.\n const errorBytes = rest.some(isErrorLine) ? Math.floor(budget.usable * getErrorBudgetRatio()) : 0;\n const tailBytes = budget.usable - Buffer.byteLength(head.text, 'utf8') - errorBytes;\n const tailTokens = budget.tokens - estimateTokens(head.text);\n const tail = takeSide(rest, Math.max(1, budget.limitLines - head.count), tailBytes, true, tailTokens);\n if (head.count === 0 && tail.count === 0) {\n const fragment = clampedFragment(text, limitBytes);\n return { text: fragment.text, lines: fragment.count, truncated: true, elidedLines: lines.length - fragment.count };\n }\n const middle = lines.slice(head.count, lines.length - tail.count);\n const salvaged = errorBytes > 0 ? renderSalvaged(collectErrorLines(middle), errorBytes) : [];\n return assembleSides(head, tail, lines.length - head.count - tail.count, trailingNewline, limitBytes, salvaged);\n}\n\n/**\n * Same budget as `boundExcerpt`, for callers that streamed the two ends\n * separately and never held the middle. `totalLines` counts the whole source,\n * so the marker states what was dropped rather than what was captured.\n */\nexport function composeExcerpt(\n headSource: string,\n tailSource: string,\n totalLines: number,\n maxLines = getResultMaxLines(),\n maxBytes = getResultMaxBytes(),\n errors: readonly SalvagedError[] = [],\n maxTokens = getResultMaxTokens(),\n): Excerpt {\n const limitBytes = Number.isFinite(maxBytes) ? Math.max(0, Math.floor(maxBytes)) : Number.MAX_SAFE_INTEGER;\n const budget = budgetFor(maxLines, limitBytes, totalLines, maxTokens);\n const headLines = splitTextLines(headSource).lines;\n const { lines: tailLines, trailingNewline } = splitTextLines(tailSource);\n // The line at each cut is a fragment of a line neither capture saw whole.\n const head = takeSide(\n headLines.slice(0, Math.max(0, headLines.length - 1)),\n budget.headLines,\n budget.headBytes,\n false,\n budget.headTokens,\n );\n const errorBytes = errors.length > 0 ? Math.floor(budget.usable * getErrorBudgetRatio()) : 0;\n const tailBytes = budget.usable - Buffer.byteLength(head.text, 'utf8') - errorBytes;\n const tailTokens = budget.tokens - estimateTokens(head.text);\n const tail = takeSide(tailLines.slice(1), Math.max(1, budget.limitLines - head.count), tailBytes, true, tailTokens);\n if (head.count === 0 && tail.count === 0) {\n const fragment = clampedFragment(tailSource, limitBytes);\n return {\n text: fragment.text,\n lines: fragment.count,\n truncated: true,\n elidedLines: Math.max(0, totalLines - fragment.count),\n };\n }\n const salvaged = errorBytes > 0 ? renderSalvaged(errors, errorBytes) : [];\n return assembleSides(\n head,\n tail,\n Math.max(0, totalLines - head.count - tail.count),\n trailingNewline,\n limitBytes,\n salvaged,\n );\n}\n\n/** Applies Doom's line and byte ceilings to text already held in memory. */\nexport function summarizeText(\n text: string,\n maxLines = getResultMaxLines(),\n maxBytes = getResultMaxBytes(),\n): Pick<LogSummary, 'tail' | 'tailLines'> {\n const excerpt = boundExcerpt(text, maxLines, maxBytes);\n return { tail: excerpt.text, tailLines: excerpt.lines };\n}\n\n/** Bounds the complete model-facing result after status and recovery text are attached. */\nexport function boundResultText(text: string, maxBytes = getResultMaxBytes()): string {\n return boundExcerpt(text, getResultMaxLines(), maxBytes).text;\n}\n\n/**\n * Keeps both ends of an oversized result and points at the file holding the rest.\n *\n * The notice states the file's real size and line count so the model does not\n * guess at offsets when it goes on to read the file, which is the failure this\n * whole policy exists to prevent.\n */\n\nfunction utf8Tail(encoded: Buffer<ArrayBufferLike>, maxBytes: number): string {\n let start = Math.max(0, encoded.byteLength - maxBytes);\n while (start < encoded.byteLength && (encoded[start]! & 0xc0) === 0x80) start += 1;\n return encoded.subarray(start).toString('utf8');\n}\n\n/** `name pid 1234 up 3m sleep 60` — one runner per line. */\nexport function formatRunnerLine(record: RunnerRecord, now = Date.now()): string {\n const mode = record.interactive ? ' interactive' : '';\n return `${record.name} pid ${record.pid} up ${formatUptime(record.startedAt, now)}${mode} ${record.command}`;\n}\n\nexport function formatUptime(startedAt: string, now: number): string {\n const started = Date.parse(startedAt);\n if (Number.isNaN(started)) return 'unknown';\n const seconds = Math.max(0, Math.floor((now - started) / 1000));\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.floor(minutes / 60);\n return hours < 24 ? `${hours}h` : `${Math.floor(hours / 24)}d`;\n}\n\nconst PROMOTION_REASONS: Record<'requested' | 'threshold' | 'interactive', string> = {\n requested: 'Started in the background',\n threshold: 'Still running after the background threshold',\n interactive: 'Started interactively',\n};\n\nfunction completionFailed(result: CompletedRun): boolean {\n return result.timedOut === true || result.signal !== null || (result.exitCode !== null && result.exitCode !== 0);\n}\n\nfunction completionStatus(result: CompletedRun): string | undefined {\n if (result.timedOut === true) return 'Timed out: exceeded the requested timeout.';\n if (result.signal !== null) return `Signal: ${result.signal}`;\n if (result.exitCode === null) return 'Exit status unavailable.';\n if (result.exitCode !== 0) return `Exit: ${result.exitCode}`;\n return undefined;\n}\n\nconst emptyLogSummary: LogSummarizer = () => ({ tail: '', bytes: 0, lines: 0, tailLines: 0 });\n\nexport function formatRunResult(\n result: BashRunResult,\n budget: ResultBudget = {},\n summarizeLog: LogSummarizer = emptyLogSummary,\n): ToolResult {\n if (result.kind === 'failed') {\n throw new Error(\n [\n `Could not start runner \"${result.name}\": ${result.error}`,\n 'Next: correct the reported launch or supervision problem. Retry only after changing the command or environment.',\n ].join('\\n'),\n );\n }\n\n if (result.kind === 'promoted') {\n const reason = PROMOTION_REASONS[result.reason];\n const body = [\n `${reason}: runner \"${result.name}\" (${result.id}).`,\n `Streaming log: ${result.logPath}`,\n `Inspect: doom-runner logs ${result.id}`,\n ].join('\\n');\n return textResult(body, {\n id: result.id,\n runner: result.name,\n pid: result.pid,\n logPath: result.logPath,\n promoted: true,\n reason: result.reason,\n });\n }\n\n const succeeded = !completionFailed(result);\n const maxLines = budget.maxLines ?? getResultMaxLines();\n const maxBytes = budget.maxBytes ?? (succeeded ? getSuccessResultMaxBytes() : getResultMaxBytes());\n const maxTokens = budget.maxTokens ?? (succeeded ? getSuccessResultMaxTokens() : getResultMaxTokens());\n const log = summarizeLog(result.logPath, maxLines, maxBytes, maxTokens);\n const useCapturedOutput = !result.rtkOutput && log.tail.length === 0 && result.output.length > 0;\n let tail: string;\n let tailLines: number;\n let outputLines: number;\n let outputBytes: number;\n let truncated: boolean;\n if (result.rtkOutput) {\n const clipped = Buffer.byteLength(result.rtkOutput.output, 'utf8') < result.rtkOutput.bytes;\n const bounded = clipped\n ? composeExcerpt(\n result.rtkOutput.head,\n result.rtkOutput.output,\n result.rtkOutput.lines,\n maxLines,\n maxBytes,\n [],\n maxTokens,\n )\n : boundExcerpt(result.rtkOutput.output, maxLines, maxBytes, maxTokens);\n tail = bounded.text;\n tailLines = bounded.lines;\n outputLines = result.rtkOutput.lines;\n outputBytes = result.rtkOutput.bytes;\n truncated = outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8');\n } else {\n tail = useCapturedOutput ? result.output : log.tail;\n tailLines = useCapturedOutput ? countLines(tail) : log.tailLines;\n outputLines = Math.max(log.lines, tailLines);\n outputBytes = useCapturedOutput ? Buffer.byteLength(tail, 'utf8') : log.bytes;\n truncated = !useCapturedOutput && (outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8'));\n }\n const plainTail = stripAnsi(tail).replace(/\\r?\\n$/u, '');\n const failed = completionFailed(result);\n const status = completionStatus(result);\n const textLines: string[] = [];\n if (plainTail.length === 0) {\n textLines.push(status === undefined ? 'Completed with no output.' : 'No output.');\n } else if (truncated) {\n const label = result.rtkOutput ? `RTK ${result.rtkOutput.filter} excerpt` : 'Log excerpt';\n textLines.push(\n `${label} (${tailLines.toLocaleString('en-US')} of ${outputLines.toLocaleString('en-US')} lines):\\n${plainTail}`,\n );\n } else {\n textLines.push(plainTail);\n }\n if (status !== undefined) textLines.push(status);\n if (result.rtkWarning) textLines.push(result.rtkWarning);\n if (truncated) {\n const label = result.rtkOutput ? 'Complete raw log' : 'Full log';\n textLines.push(\n `${label}: ${result.logPath} (${formatSize(log.bytes)}, ${log.lines.toLocaleString('en-US')} lines); inspect with doom-runner logs ${result.id}`,\n );\n } else if (failed && plainTail.length === 0) {\n textLines.push(\n `Log: ${result.logPath}`,\n 'Next: run one read-only diagnostic; retry only after correcting the cause.',\n );\n }\n const text = boundResultText(textLines.join('\\n'), maxBytes);\n const details = {\n id: result.id,\n runner: result.name,\n exitCode: result.exitCode,\n logPath: result.logPath,\n backend: result.backend,\n fileSize: log.bytes,\n lines: useCapturedOutput ? tailLines : log.lines,\n tail,\n tailLines,\n ...(result.rtkOutput\n ? {\n rtkFilter: result.rtkOutput.filter,\n rtkOutputBytes: result.rtkOutput.bytes,\n rtkOutputLines: result.rtkOutput.lines,\n }\n : {}),\n ...(result.rtkWarning ? { rtkWarning: result.rtkWarning } : {}),\n ...(result.timedOut ? { timedOut: true } : {}),\n };\n if (failed) throw new Error(text);\n return textResult(text, details);\n}\n","import type {\n DoomHeadlessCommand,\n DoomHeadlessExecutionContext,\n DoomHeadlessTool,\n DoomHeadlessToolResult,\n} from '@agimon-ai/doompi-core/headless';\n\nimport { BASH_TOOL_LABEL, BASH_TOOL_NAME } from '../../constants/bashTool';\nimport { MS_PER_SECOND } from '../../constants/headless';\nimport { COMMAND_NAME, SERVER_COMMAND_DESCRIPTION } from '../../constants/runners';\nimport { type BashParams, BashParamsSchema } from '../../schemas/bashTool';\nimport { formatRunResult, parseResultPragma, textResult } from '../../services/bashResult';\nimport { stopRunnerProcess } from '../../services/reconcile';\nimport { getBackgroundThresholdMs } from '../../services/runnerConfig';\nimport type { RunnerDependencies } from '../../services/runnerDependencies/type';\nimport { parseRunnersCommand } from '../../services/runnersCommand';\nimport { type LogSummarizer } from '../../types/bashResult';\nimport type { IBashRunService } from '../../types/bashRunService';\n\nexport function createHeadlessBashTool(\n bashRunService: IBashRunService,\n summarizeLog?: LogSummarizer,\n): DoomHeadlessTool<typeof BashParamsSchema> {\n return {\n name: BASH_TOOL_NAME,\n label: BASH_TOOL_LABEL,\n description:\n 'Execute one Bash command in the current working directory. Foreground commands return bounded output; commands that outlive the threshold return a supervised runner id and streaming log path.',\n promptSnippet: 'Execute shell commands with bounded foreground output and supervised background runners',\n promptGuidelines: [\n `A command still running after ${Math.round(getBackgroundThresholdMs() / MS_PER_SECOND)} seconds remains active as a background runner with an id and streaming log path.`,\n 'Pass background: true for commands that should remain active, such as dev servers, watchers, and tails.',\n 'Stop background runners when they are no longer needed.',\n ],\n parameters: BashParamsSchema,\n executionMode: 'serial',\n execute: async (\n _toolCallId: string,\n params: BashParams,\n signal: AbortSignal | undefined,\n onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ) => {\n const { command, timeout, background, interactive, name } = params;\n if (signal?.aborted) throw new Error('Operation aborted');\n if (onUpdate)\n onUpdate(\n textResult(\n `Starting ${interactive === true ? 'interactive runner' : background === true ? 'background runner' : 'command'}...`,\n ),\n );\n let onOutput: ((output: string) => void) | undefined;\n if (background !== true && interactive !== true && onUpdate) onOutput = (output) => onUpdate(textResult(output));\n const result = await bashRunService.run({\n command,\n timeoutMs: timeout === undefined ? undefined : timeout * MS_PER_SECOND,\n background,\n interactive,\n name,\n ...(onOutput ? { onOutput } : {}),\n ...(signal ? { signal } : {}),\n cwd: context.cwd,\n sessionId: context.sessionId,\n });\n if (signal?.aborted) throw new Error('Operation aborted');\n return formatRunResult(result, parseResultPragma(command), summarizeLog);\n },\n };\n}\n\nexport function createHeadlessRunnersCommand(dependencies: RunnerDependencies): DoomHeadlessCommand {\n return {\n name: COMMAND_NAME,\n description: SERVER_COMMAND_DESCRIPTION,\n async execute(args: string, context: DoomHeadlessExecutionContext) {\n const request = parseRunnersCommand(args);\n if (request.kind === 'space') {\n const active = await dependencies.runnerRegistry.listBySession(context.sessionId);\n await context.client.notify({\n body: `${active.length} active runner${active.length === 1 ? '' : 's'}.`,\n level: 'info',\n });\n return;\n }\n if (request.kind === 'start') {\n if (!request.command) throw new Error('Usage: /runners start [name=x] [cwd=y] [interactive=true] -- <command>');\n const result = await dependencies.bashRunService.run({\n command: request.command,\n background: true,\n sessionId: context.sessionId,\n cwd: request.cwd === undefined ? context.cwd : request.cwd,\n ...(request.name ? { name: request.name } : {}),\n ...(request.interactive ? { interactive: true } : {}),\n });\n await context.client.notify({\n body: formatRunResult(result).content[0]?.text ?? 'Runner started.',\n level: 'info',\n });\n return;\n }\n if (!request.id) throw new Error('Usage: /runners stop <runner-id> [reason]');\n const record = await dependencies.runnerRegistry.get(request.id, context.sessionId);\n if (!record) {\n await context.client.notify({ body: `No active runner ${request.id} in this session.`, level: 'warning' });\n return;\n }\n const stopped = await stopRunnerProcess(record, dependencies.launcher, dependencies.rmuxBackend);\n if (stopped) {\n await dependencies.runnerRegistry.complete(\n request.id,\n {\n reason: 'stopped',\n code: null,\n signal: null,\n ...(request.reason ? { stopReason: request.reason } : {}),\n },\n context.sessionId,\n );\n }\n await context.client.notify({\n body: stopped ? `Stopped runner ${request.id}.` : `Could not stop runner ${request.id}.`,\n level: stopped ? 'info' : 'error',\n });\n },\n };\n}\n","export const DEFAULT_LINES = 200;\nexport const READ_CHUNK_BYTES = 65_536;\n","import fs from 'node:fs';\nimport { StringDecoder } from 'node:string_decoder';\n\nimport { DEFAULT_LINES, READ_CHUNK_BYTES } from '../../constants/logReader';\nimport { type Excerpt, type LogSummary, type TruncatedOutput } from '../../types/bashResult';\nimport type { ILogReader, LogQuery, LogSlice } from '../../types/logReader';\nimport { boundExcerpt, composeExcerpt, countLines, createErrorScanner, formatSize } from '../bashResult';\nimport { getResultMaxBytes, getResultMaxLines, getResultMaxTokens } from '../runnerConfig';\n\ninterface IndexedLine {\n readonly index: number;\n readonly text: string;\n}\n\ninterface ScannedLog {\n readonly text: string;\n readonly lineCount: number;\n readonly totalLines: number;\n readonly completeBytes: number;\n readonly lineNumbers?: number[];\n}\n\nexport class LogReader implements ILogReader {\n read(logPath: string, query: LogQuery = {}): LogSlice {\n let handle: number | undefined;\n try {\n handle = fs.openSync(logPath, 'r');\n const scanned = scanLog(handle, query);\n return {\n ...scanned,\n fileSize: fs.fstatSync(handle).size,\n path: logPath,\n exists: true,\n };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n return {\n text: '',\n lineCount: 0,\n totalLines: 0,\n fileSize: 0,\n completeBytes: 0,\n path: logPath,\n exists: false,\n };\n } finally {\n if (handle !== undefined) fs.closeSync(handle);\n }\n }\n}\n\nfunction scanLog(handle: number, query: LogQuery): ScannedLog {\n const limit = Math.max(0, Math.floor(query.lines ?? DEFAULT_LINES));\n const needle = query.grep ? (query.ignoreCase ? query.grep.toLowerCase() : query.grep) : undefined;\n const contextLines = Math.max(0, Math.floor(query.contextLines ?? 0));\n const decoder = new StringDecoder('utf8');\n const buffer = Buffer.alloc(READ_CHUNK_BYTES);\n const before: IndexedLine[] = [];\n const output: IndexedLine[] = [];\n let pending = '';\n let totalLines = 0;\n let afterThrough = -1;\n let lastEmitted = -1;\n let lastByte: number | undefined;\n let bytesSeen = 0;\n let completeBytes = 0;\n\n const retain = (line: IndexedLine): void => {\n if (limit === 0) return;\n output.push(line);\n if (output.length > limit) output.shift();\n };\n const emit = (line: IndexedLine): void => {\n if (line.index <= lastEmitted) return;\n lastEmitted = line.index;\n retain(line);\n };\n const consume = (text: string): void => {\n const line = { index: totalLines, text };\n totalLines += 1;\n if (!needle) {\n retain(line);\n return;\n }\n\n const haystack = query.ignoreCase ? text.toLowerCase() : text;\n if (haystack.includes(needle)) {\n for (const previous of before) emit(previous);\n emit(line);\n afterThrough = Math.max(afterThrough, line.index + contextLines);\n } else if (line.index <= afterThrough) {\n emit(line);\n }\n before.push(line);\n if (before.length > contextLines) before.shift();\n };\n\n for (;;) {\n const bytesRead = fs.readSync(handle, buffer, 0, buffer.byteLength, null);\n if (bytesRead === 0) break;\n lastByte = buffer[bytesRead - 1];\n // A newline is a single byte in UTF-8 and never a continuation byte, so the\n // last one in this chunk ends the last complete line seen so far.\n const lastNewline = buffer.subarray(0, bytesRead).lastIndexOf(0x0a);\n if (lastNewline >= 0) completeBytes = bytesSeen + lastNewline + 1;\n bytesSeen += bytesRead;\n pending += decoder.write(buffer.subarray(0, bytesRead));\n let newline = pending.indexOf('\\n');\n while (newline >= 0) {\n consume(pending.slice(0, newline));\n pending = pending.slice(newline + 1);\n newline = pending.indexOf('\\n');\n }\n }\n pending += decoder.end();\n if (pending.length > 0) consume(pending);\n\n const trailingNewline = lastByte === 0x0a;\n const body = output.map((line) => line.text).join('\\n');\n const text = `${body}${!needle && trailingNewline && output.length > 0 ? '\\n' : ''}`;\n const scanned: ScannedLog = { text, lineCount: output.length, totalLines, completeBytes };\n if (!needle) return scanned;\n return { ...scanned, lineNumbers: output.map((line) => line.index + 1) };\n}\n\n/**\n * Grep with context, matching the `product-stack logs` semantics so the two\n * surfaces behave the same. Overlapping and adjacent context windows merge.\n */\nexport function filterLogText(text: string, query: Pick<LogQuery, 'grep' | 'ignoreCase' | 'contextLines'>): string {\n if (!query.grep) return text;\n const lines = text.split('\\n');\n const needle = query.ignoreCase ? query.grep.toLowerCase() : query.grep;\n const contextLines = Math.max(0, query.contextLines ?? 0);\n const ranges: Array<{ start: number; end: number }> = [];\n\n lines.forEach((line, index) => {\n const haystack = query.ignoreCase ? line.toLowerCase() : line;\n if (!haystack.includes(needle)) return;\n ranges.push({\n start: Math.max(0, index - contextLines),\n end: Math.min(lines.length - 1, index + contextLines),\n });\n });\n\n if (ranges.length === 0) return '';\n const merged: Array<{ start: number; end: number }> = [];\n for (const range of ranges) {\n const previous = merged.at(-1);\n if (previous && range.start <= previous.end + 1) {\n previous.end = Math.max(previous.end, range.end);\n } else {\n merged.push({ ...range });\n }\n }\n return merged.flatMap((range) => lines.slice(range.start, range.end + 1)).join('\\n');\n}\n\n/** Reads a bounded head and tail plus exact metadata after the writer has flushed. */\nexport function summarizeLog(\n path: string,\n maxLines = getResultMaxLines(),\n maxBytes = getResultMaxBytes(),\n maxTokens = getResultMaxTokens(),\n): LogSummary {\n let handle: number | undefined;\n try {\n const size = fs.statSync(path).size;\n handle = fs.openSync(path, 'r');\n const readBuffer = Buffer.alloc(READ_CHUNK_BYTES);\n // Both ends are captured at the full budget; the line trim decides the split.\n const endLimit = Math.max(1, maxBytes + 1);\n let headBuffer: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let tailBuffer: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let newlineCount = 0;\n let lastByte: number | undefined;\n const decoder = new StringDecoder('utf8');\n const scanned = createErrorScanner();\n let pending = '';\n for (;;) {\n const bytesRead = fs.readSync(handle, readBuffer, 0, readBuffer.byteLength, null);\n if (bytesRead === 0) break;\n const chunk = readBuffer.subarray(0, bytesRead);\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (headBuffer.byteLength < endLimit) {\n headBuffer = Buffer.concat([headBuffer, chunk.subarray(0, endLimit - headBuffer.byteLength)]);\n }\n tailBuffer = appendBufferTail(tailBuffer, chunk, endLimit);\n pending += decoder.write(chunk);\n let newline = pending.indexOf('\\n');\n while (newline >= 0) {\n scanned.push(pending.slice(0, newline));\n pending = pending.slice(newline + 1);\n newline = pending.indexOf('\\n');\n }\n }\n pending += decoder.end();\n if (pending.length > 0) scanned.push(pending);\n const lines = newlineCount + (size > 0 && lastByte !== 0x0a ? 1 : 0);\n // The tail capture holds the whole file only while the file fits inside one\n // buffer. Past that the start is already gone from it, so the two ends have\n // to be composed instead.\n const excerpt =\n size <= tailBuffer.byteLength\n ? boundExcerpt(utf8Tail(tailBuffer, tailBuffer.byteLength), maxLines, maxBytes, maxTokens)\n : composeExcerpt(\n headBuffer.toString('utf8'),\n utf8Tail(tailBuffer, tailBuffer.byteLength),\n lines,\n maxLines,\n maxBytes,\n scanned.entries(),\n maxTokens,\n );\n return { tail: excerpt.text, bytes: size, lines, tailLines: excerpt.lines };\n } catch {\n return { tail: '', bytes: 0, lines: 0, tailLines: 0 };\n } finally {\n if (handle !== undefined) fs.closeSync(handle);\n }\n}\n\n/**\n * Keeps both ends of an oversized result and points at the file holding the rest.\n *\n * The notice states the file's real size and line count so the model does not\n * guess at offsets when it goes on to read the file, which is the failure this\n * whole policy exists to prevent.\n */\nexport function truncateForResult(\n text: string,\n fullOutputPath: string,\n maxBytes = getResultMaxBytes(),\n maxLines = getResultMaxLines(),\n): TruncatedOutput {\n const excerpt = boundExcerpt(text, maxLines, maxBytes);\n if (!excerpt.truncated) return { text, truncated: false, outputLines: excerpt.lines };\n\n return {\n text: `${excerpt.text}\\n${truncationNotice(excerpt, fullOutputPath, text)}`,\n truncated: true,\n outputLines: excerpt.lines,\n };\n}\n\nfunction truncationNotice(excerpt: Excerpt, fullOutputPath: string, text: string): string {\n // Counted from the file, never from `text`. What the caller holds in memory\n // is itself capped, so using it here would understate the file by orders of\n // magnitude and send the reader looking past the end of a much longer file.\n const file = measureFile(fullOutputPath);\n const totalLines = file?.lines ?? countLines(text);\n const fileSize = file?.bytes ?? Buffer.byteLength(text, 'utf8');\n\n return [\n `[output truncated: showing ${excerpt.lines.toLocaleString('en-US')} of ${totalLines.toLocaleString('en-US')} lines,`,\n `${excerpt.elidedLines.toLocaleString('en-US')} elided from the middle.`,\n `Full output is at ${fullOutputPath} (${formatSize(fileSize)}, ${totalLines.toLocaleString('en-US')} lines).`,\n `Inspect it with doom-runner logs, or read the file with an offset near line ${totalLines.toLocaleString('en-US')}.]`,\n ].join(' ');\n}\n\n/** Real size and line count of the file the notice points at. */\nfunction measureFile(path: string): { bytes: number; lines: number } | undefined {\n let handle: number | undefined;\n try {\n const bytes = fs.statSync(path).size;\n handle = fs.openSync(path, 'r');\n const buffer = Buffer.alloc(READ_CHUNK_BYTES);\n let newlineCount = 0;\n let lastByte: number | undefined;\n for (;;) {\n const bytesRead = fs.readSync(handle, buffer, 0, buffer.byteLength, null);\n if (bytesRead === 0) break;\n const chunk = buffer.subarray(0, bytesRead);\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n }\n return { bytes, lines: newlineCount + (bytes > 0 && lastByte !== 0x0a ? 1 : 0) };\n } catch {\n return undefined;\n } finally {\n if (handle !== undefined) fs.closeSync(handle);\n }\n}\n\nfunction utf8Tail(encoded: Buffer<ArrayBufferLike>, maxBytes: number): string {\n let start = Math.max(0, encoded.byteLength - maxBytes);\n while (start < encoded.byteLength && (encoded[start]! & 0xc0) === 0x80) start += 1;\n return encoded.subarray(start).toString('utf8');\n}\n\nfunction appendBufferTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n","export const RUNNER_ID_RANDOM_BYTES = 3;\nexport const OUTPUT_UPDATE_POLL_MS = 100;\nexport const COMPLETED = 'completed';\nexport const FAILED = 'failed';\nexport const LAUNCHER_ERROR = 'launcher_error';\nexport const SIGNALED = 'signaled';\n","export type RtkFilter = 'cargo-test' | 'pytest' | 'git-diff' | 'grep' | 'go-test' | 'ruff-check';\n\nexport interface RtkProcessedOutput {\n readonly filter: RtkFilter;\n /** Leading excerpt of RTK stdout, empty when `output` already holds the start. */\n readonly head: string;\n /** Tail-capped RTK stdout. The raw log remains the authoritative complete output. */\n readonly output: string;\n readonly bytes: number;\n readonly lines: number;\n}\n\nexport type RtkProcessResult =\n | { readonly kind: 'skipped' }\n | { readonly kind: 'processed'; readonly result: RtkProcessedOutput }\n | { readonly kind: 'fallback'; readonly warning: string };\n\nexport interface RtkProcessRequest {\n readonly command: string;\n readonly logPath: string;\n}\n\nexport interface IRtkProcessor {\n process(request: RtkProcessRequest): Promise<RtkProcessResult>;\n}\n\n/** Raw-output fallback notices. Shared so the processor and its caller cannot drift. */\nexport const RTK_UNAVAILABLE_WARNING = 'Warning: RTK is unavailable; showing raw output.';\nexport const RTK_FAILED_WARNING = 'Warning: RTK processing failed; showing raw output.';\nexport const RTK_TIMEOUT_WARNING = 'Warning: RTK processing timed out; showing raw output.';\nexport const RTK_OVERSIZED_WARNING = 'Warning: RTK input exceeds 10 MiB; showing raw output.';\n","import { randomBytes } from 'node:crypto';\n\nimport {\n COMPLETED,\n FAILED,\n LAUNCHER_ERROR,\n OUTPUT_UPDATE_POLL_MS,\n RUNNER_ID_RANDOM_BYTES,\n SIGNALED,\n} from '../../constants/bashRunService';\nimport type {\n BashRunRequest,\n BashRunResult,\n CompletedRun,\n IBashRunService,\n PromotedRun,\n} from '../../types/bashRunService';\nimport type { IClock } from '../../types/clock';\nimport type { ILauncher, RunHandle } from '../../types/launcher';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport { type IRtkProcessor, RTK_FAILED_WARNING } from '../../types/rtkProcessor';\nimport type { IRunnerRegistry } from '../../types/runnerRegistry';\nimport { getBackgroundThresholdMs } from '../runnerConfig';\nimport type { IRunnerNamer } from '../runnerNamer/type';\n\n/** The command outlived the background threshold and should be promoted. */\nconst TIMED_OUT = Symbol('threshold-reached');\n/** The command outlived the caller's explicit timeout and should be stopped. */\nconst DEADLINE = Symbol('deadline-reached');\n/** The caller cancelled the command and its owned runner should be stopped. */\nconst ABORTED = Symbol('aborted');\n\nfunction runnerId(): string {\n const timestamp = Date.now().toString(36);\n const random = randomBytes(RUNNER_ID_RANDOM_BYTES).toString('base64url');\n return `${timestamp}-${random}`;\n}\n\nexport class BashRunService implements IBashRunService {\n constructor(\n private readonly launcher: ILauncher,\n private readonly rmuxBackend: IRmuxBackend,\n private readonly namer: IRunnerNamer,\n private readonly registry: IRunnerRegistry,\n private readonly clock: IClock,\n private readonly rtkProcessor: IRtkProcessor,\n ) {}\n\n async run(request: BashRunRequest): Promise<BashRunResult> {\n const id = runnerId();\n const name = await this.namer.allocate(request.command, request.sessionId, request.name);\n const cwd = request.cwd ?? process.cwd();\n const interactive = request.interactive === true;\n if (request.signal?.aborted) return { kind: FAILED, id, name, error: 'Operation aborted' };\n\n let handle: RunHandle;\n try {\n const rmuxHandle = await this.rmuxBackend.launch({\n id,\n name,\n command: request.command,\n cwd,\n sessionId: request.sessionId,\n interactive,\n });\n if (interactive && !rmuxHandle) {\n return { kind: FAILED, id, name, error: 'RMUX is required for interactive commands but is unavailable' };\n }\n handle =\n rmuxHandle ?? this.launcher.launch({ id, name, command: request.command, cwd, sessionId: request.sessionId });\n } catch (error) {\n return { kind: FAILED, id, name, error: error instanceof Error ? error.message : String(error) };\n }\n\n if (handle.pid === undefined) {\n return { kind: FAILED, id, name, error: 'The command did not start, so it cannot be supervised' };\n }\n if (request.signal?.aborted) {\n await handle.stop();\n return { kind: FAILED, id, name, error: 'Operation aborted' };\n }\n\n try {\n await this.registry.register({\n id,\n name,\n pid: handle.pid,\n command: request.command,\n cwd,\n logPath: handle.logPath,\n interactive,\n sessionId: request.sessionId,\n backend: handle.backend,\n ...(handle.backendTarget ? { backendTarget: handle.backendTarget } : {}),\n });\n } catch (error) {\n await handle.stop();\n return { kind: FAILED, id, name, error: error instanceof Error ? error.message : String(error) };\n }\n\n // An interactive run has nothing to wait for: it is going to prompt, so\n // the model needs the runner name before it can answer.\n if (request.background === true || interactive) {\n return this.promote(handle, request.background === true ? 'requested' : 'interactive', request.signal);\n }\n\n const outputUpdates = request.onOutput ? this.followOutput(handle, request.onOutput) : undefined;\n try {\n const outcome = await this.race(handle, request.timeoutMs, request.signal);\n outputUpdates?.flush();\n if (outcome === TIMED_OUT) {\n return this.promote(handle, 'threshold', request.signal);\n }\n if (outcome === ABORTED) {\n await handle.stop();\n outputUpdates?.flush();\n await this.registry.complete(id, { reason: 'stopped', code: null, signal: 'SIGTERM' });\n return {\n kind: COMPLETED,\n id,\n name,\n output: handle.output(),\n exitCode: null,\n signal: 'SIGTERM',\n logPath: handle.logPath,\n backend: handle.backend,\n aborted: true,\n };\n }\n if (outcome === DEADLINE) {\n // An explicit timeout means the caller wants the command dead, not\n // supervised, so it is stopped rather than promoted.\n await handle.stop();\n outputUpdates?.flush();\n await this.registry.complete(id, { reason: 'timed_out', code: null, signal: 'SIGTERM' });\n return {\n kind: COMPLETED,\n id,\n name,\n output: handle.output(),\n exitCode: null,\n signal: 'SIGTERM',\n logPath: handle.logPath,\n backend: handle.backend,\n timedOut: true,\n };\n }\n if (outcome instanceof Error) {\n await this.registry.complete(id, { reason: LAUNCHER_ERROR, code: null, signal: null });\n return { kind: FAILED, id, name, error: outcome.message };\n }\n\n await this.registry.complete(id, {\n reason: outcome.signal ? SIGNALED : outcome.code === 0 ? COMPLETED : FAILED,\n code: outcome.code,\n signal: outcome.signal,\n });\n\n return {\n kind: COMPLETED,\n id,\n name,\n output: handle.output(),\n exitCode: outcome.code,\n signal: outcome.signal,\n logPath: handle.logPath,\n backend: handle.backend,\n ...(await this.processLog(request.command, handle.logPath)),\n };\n } finally {\n outputUpdates?.dispose();\n }\n }\n\n private async processLog(command: string, logPath: string): Promise<Pick<CompletedRun, 'rtkOutput' | 'rtkWarning'>> {\n try {\n const result = await this.rtkProcessor.process({ command, logPath });\n if (result.kind === 'processed') return { rtkOutput: result.result };\n if (result.kind === 'fallback') return { rtkWarning: result.warning };\n return {};\n } catch {\n return { rtkWarning: RTK_FAILED_WARNING };\n }\n }\n\n private followOutput(\n handle: RunHandle,\n onOutput: NonNullable<BashRunRequest['onOutput']>,\n ): { flush(): void; dispose(): void } {\n let active = true;\n let previous = '';\n let cancelPoll: (() => void) | undefined;\n const flush = (): void => {\n if (!active) return;\n const output = handle.output();\n if (output === previous) return;\n previous = output;\n onOutput(output);\n };\n const poll = (): void => {\n if (!active) return;\n flush();\n if (active) cancelPoll = this.clock.after(OUTPUT_UPDATE_POLL_MS, poll);\n };\n cancelPoll = this.clock.after(OUTPUT_UPDATE_POLL_MS, poll);\n return {\n flush,\n dispose: () => {\n active = false;\n cancelPoll?.();\n cancelPoll = undefined;\n },\n };\n }\n\n /**\n * Waits for the command, giving up at the background threshold or at the\n * caller's timeout, whichever comes first.\n *\n * A rejected completion is returned rather than thrown so the caller can\n * report a start failure as a tool result instead of an exception.\n */\n private async race(\n handle: RunHandle,\n timeoutMs: number | undefined,\n signal: AbortSignal | undefined,\n ): Promise<\n Awaited<ReturnType<RunHandle['completion']>> | Error | typeof TIMED_OUT | typeof DEADLINE | typeof ABORTED\n > {\n const cancels: Array<() => void> = [];\n const racers: Array<Promise<unknown>> = [handle.completion().catch((error: Error) => error)];\n\n racers.push(\n new Promise((resolve) => {\n cancels.push(this.clock.after(getBackgroundThresholdMs(), () => resolve(TIMED_OUT)));\n }),\n );\n if (timeoutMs !== undefined && timeoutMs > 0) {\n racers.push(\n new Promise((resolve) => {\n cancels.push(this.clock.after(timeoutMs, () => resolve(DEADLINE)));\n }),\n );\n }\n if (signal) {\n racers.push(\n new Promise((resolve) => {\n if (signal.aborted) {\n resolve(ABORTED);\n return;\n }\n const abort = (): void => resolve(ABORTED);\n signal.addEventListener('abort', abort, { once: true });\n cancels.push(() => signal.removeEventListener('abort', abort));\n }),\n );\n }\n\n try {\n return (await Promise.race(racers)) as\n | Awaited<ReturnType<RunHandle['completion']>>\n | Error\n | typeof TIMED_OUT\n | typeof DEADLINE\n | typeof ABORTED;\n } finally {\n for (const cancel of cancels) cancel();\n }\n }\n\n private async promote(\n handle: RunHandle,\n reason: PromotedRun['reason'],\n signal: AbortSignal | undefined,\n ): Promise<BashRunResult> {\n if (handle.pid === undefined)\n return { kind: FAILED, id: handle.id, name: handle.name, error: 'The command did not start' };\n await this.registry.markPromoted(handle.id);\n // Registration first: an unregistered runner is invisible, and the handle\n // stops buffering the moment it is detached.\n handle.detach();\n let abortStop: Promise<boolean> | undefined;\n const abort = (): void => {\n abortStop = handle\n .stop()\n .then(async () => {\n await this.registry.complete(handle.id, { reason: 'stopped', code: null, signal: 'SIGTERM' });\n return true;\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.emitWarning(`Failed to stop aborted runner ${handle.id}: ${message}`);\n return false;\n });\n };\n if (signal) {\n if (signal.aborted) abort();\n else signal.addEventListener('abort', abort, { once: true });\n }\n void handle\n .completion()\n .then(\n async (outcome) => {\n if (await abortStop) return;\n await this.registry.complete(handle.id, {\n reason: outcome.signal ? SIGNALED : outcome.code === 0 ? COMPLETED : FAILED,\n code: outcome.code,\n signal: outcome.signal,\n });\n },\n async () => {\n if (await abortStop) return;\n await this.registry.complete(handle.id, { reason: LAUNCHER_ERROR, code: null, signal: null });\n },\n )\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.emitWarning(`Failed to complete runner ${handle.id}: ${message}`);\n })\n .finally(() => signal?.removeEventListener('abort', abort));\n\n return {\n kind: 'promoted',\n id: handle.id,\n name: handle.name,\n pid: handle.pid,\n logPath: handle.logPath,\n backend: handle.backend,\n reason,\n };\n }\n}\n","import type { IClock } from '../../types/clock';\n\n/** Real wall clock and timers. */\nexport class SystemClock implements IClock {\n now(): number {\n return Date.now();\n }\n\n after(ms: number, handler: () => void): () => void {\n const timer = setTimeout(handler, ms);\n // The timer must never be the reason the host process stays alive.\n timer.unref?.();\n return () => clearTimeout(timer);\n }\n}\n","export const NON_INTERACTIVE_ENV = { FORCE_COLOR: '1', CI: '1' };\nexport const TERM_GRACE_MS = 2_000;\nexport const LIVENESS_POLL_MS = 100;\nexport const MEMORY_BUFFER_FACTOR = 2;\nexport const GATE_MODE = 0o600;\n","export const PI_SESSION_ID_ENV = 'PI_SESSION_ID';\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/** What a supervisor needs to run one command. */\nexport interface CommandSpec {\n command: string;\n cwd: string;\n env: Record<string, string>;\n}\n\n/** Exit status the supervisor records once its command is gone. */\nexport interface ExitMetadata {\n code: number | null;\n signal: NodeJS.Signals | null;\n}\n\n/** Sidecars a supervised run reads and writes beside its log. */\nexport interface SupervisorPaths {\n spec: string;\n gate: string;\n exit: string;\n logDone: string;\n}\n\n/**\n * Settings that make a terminal safe for a caller with no keyboard.\n *\n * Commands run inside an rmux pane, so their stdout is a real tty. Git and\n * friends take that as permission to open a pager, and `less` then waits at\n * `(END)` for a keypress nobody is there to send, so the run hangs until its\n * timeout. The same tty invites credential prompts, which hang the same way.\n * A conventional piped stdout disables both automatically; a pane has to be\n * told. A command that genuinely wants a pager can still ask for one, as in\n * `git -c core.pager=less log`.\n */\nexport const NO_TERMINAL_INPUT_ENV = {\n GIT_PAGER: 'cat',\n PAGER: 'cat',\n GIT_TERMINAL_PROMPT: '0',\n};\n\nconst SPEC_MODE = 0o600;\nconst NOT_FOUND_ERROR_CODE = 'ENOENT';\n\nexport function supervisorPaths(directory: string, id: string): SupervisorPaths {\n return {\n spec: path.join(directory, `${id}.command.json`),\n gate: path.join(directory, `${id}.gate`),\n exit: path.join(directory, `${id}.exit.json`),\n logDone: path.join(directory, `${id}.log.done`),\n };\n}\n\nexport function writeCommandSpec(target: string, spec: CommandSpec): void {\n fs.writeFileSync(target, `${JSON.stringify(spec)}\\n`, { mode: SPEC_MODE });\n}\n\nexport function readExitMetadata(target: string): ExitMetadata | undefined {\n try {\n const value: unknown = JSON.parse(fs.readFileSync(target, 'utf8'));\n if (typeof value !== 'object' || value === null) return undefined;\n const metadata = value as Partial<ExitMetadata>;\n const code = typeof metadata.code === 'number' ? metadata.code : null;\n const signal = typeof metadata.signal === 'string' ? (metadata.signal as NodeJS.Signals) : null;\n return { code, signal };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n process.emitWarning(`Could not read runner exit metadata ${target}: ${String(error)}`);\n }\n return undefined;\n }\n}\n\nexport function cleanupSupervisorFiles(paths: SupervisorPaths): void {\n for (const target of Object.values(paths)) {\n try {\n fs.unlinkSync(target);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) process.emitWarning(String(error));\n }\n }\n}\n\n/**\n * Resolves an executable entry point by walking up to the `bin` directory that\n * ships beside this module. Source and published layouts nest services at\n * different depths. Source modules select built subprocess entries so Node can resolve their imports. A hardcoded depth stays silent\n * until the shell tries to exec it.\n */\nexport function runtimeEntry(name: 'runnerHost' | 'logSink', moduleUrl = import.meta.url): string {\n const modulePath = fileURLToPath(moduleUrl);\n const source = path.extname(modulePath) === '.ts';\n const extension = 'mjs';\n let directory = path.dirname(modulePath);\n\n while (true) {\n const entry = source\n ? path.join(directory, 'dist', 'bin', `${name}.${extension}`)\n : path.join(directory, 'bin', `${name}.${extension}`);\n if (fs.existsSync(entry)) return fs.realpathSync(entry);\n\n const parent = path.dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n\n throw new Error(`Cannot find bin/${name}.${extension} by walking up from ${modulePath}.`);\n}\n\n/**\n * Replaces the shell rather than nesting under it, so the pid handed back is the\n * supervisor's and a group signal reaches it directly.\n */\nexport function supervisorCommand(paths: SupervisorPaths): string {\n return `exec ${shellJoin([process.execPath, runtimeEntry('runnerHost'), paths.spec, paths.gate, paths.exit])}`;\n}\n\nexport function shellJoin(values: readonly string[]): string {\n return values.map((value) => `'${value.replaceAll(\"'\", `'\"'\"'`)}'`).join(' ');\n}\n\n/** A pending appearance of one supervisor sidecar, watched rather than polled for. */\nexport interface FileWatch {\n /** Resolves true once the file exists, or false when the slice elapses. */\n appeared(withinMs: number): Promise<boolean>;\n close(): void;\n}\n\ninterface DirectoryWatch {\n watcher: fs.FSWatcher | undefined;\n readonly waiters: Map<string, Set<() => void>>;\n references: number;\n}\n\n/**\n * One watcher per directory, shared by every waiter inside it.\n *\n * A session's runners share a runs directory, and `DOOMPI_LOG_DIR` makes every\n * session using it share one. A watcher per waited file would then put a watcher\n * per in-flight runner on the same directory, each woken by every other runner's\n * sidecar writes, so the watchers are pooled and reference counted instead.\n */\nconst directoryWatches = new Map<string, DirectoryWatch>();\n\nfunction acquireDirectoryWatch(directory: string): DirectoryWatch {\n const existing = directoryWatches.get(directory);\n if (existing) {\n existing.references += 1;\n return existing;\n }\n const entry: DirectoryWatch = { watcher: undefined, waiters: new Map(), references: 1 };\n try {\n entry.watcher = fs.watch(directory, (_event, changed) => {\n // A rename can arrive without a name, and then the only safe reading is\n // that any waiter in this directory might be the one it belongs to.\n const named = changed === null ? undefined : String(changed);\n const targets = named === undefined ? [...entry.waiters.values()] : [entry.waiters.get(named)];\n for (const listeners of targets) {\n if (!listeners) continue;\n for (const listener of listeners) listener();\n }\n });\n // A watcher that cannot report is a watcher whose slices time out, which the\n // caller's own probe already covers.\n entry.watcher.on('error', () => undefined);\n } catch {\n entry.watcher = undefined;\n }\n directoryWatches.set(directory, entry);\n return entry;\n}\n\nfunction releaseDirectoryWatch(directory: string, entry: DirectoryWatch): void {\n entry.references -= 1;\n if (entry.references > 0) return;\n entry.watcher?.close();\n entry.watcher = undefined;\n entry.waiters.clear();\n directoryWatches.delete(directory);\n}\n\n/**\n * Reports a supervisor sidecar landing on disk without polling for it.\n *\n * The sidecars are written once and never rewritten, so the directory's own\n * create event answers the only question a waiter has. Polling for them costs a\n * full poll interval on every run that finishes inside one, and the runs that\n * finish fastest are the ones that pay that tax most often.\n *\n * The watch is best effort: a platform or a directory that cannot deliver events\n * leaves every slice to time out, which is exactly the poll the caller already\n * runs as its fallback.\n */\nexport function watchForFile(filePath: string): FileWatch {\n const directory = path.dirname(filePath);\n const name = path.basename(filePath);\n const entry = acquireDirectoryWatch(directory);\n let released = false;\n let found = false;\n let notify: (() => void) | undefined;\n\n const settle = (): void => {\n if (found || !fs.existsSync(filePath)) return;\n found = true;\n notify?.();\n };\n const listeners = entry.waiters.get(name) ?? new Set<() => void>();\n listeners.add(settle);\n entry.waiters.set(name, listeners);\n\n // The file can land between opening the watcher and the first wait, and that\n // create event has nobody listening for it yet.\n settle();\n\n return {\n async appeared(withinMs: number): Promise<boolean> {\n if (found) return true;\n return await new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => {\n notify = undefined;\n // The watcher is an optimisation, not the answer. A create event that\n // was coalesced, reported under the temporary name of a rename, or\n // never delivered at all must not outlive the slice it belongs to, so\n // every slice ends by asking the filesystem itself.\n settle();\n resolve(found);\n }, withinMs);\n // Deliberately left holding the event loop. A run still being waited on\n // is work, and a process that exits during the gap between two probes\n // drops the result its caller is awaiting.\n notify = () => {\n clearTimeout(timer);\n notify = undefined;\n resolve(true);\n };\n });\n },\n close(): void {\n if (released) return;\n released = true;\n notify = undefined;\n listeners.delete(settle);\n if (listeners.size === 0) entry.waiters.delete(name);\n releaseDirectoryWatch(directory, entry);\n },\n };\n}\n","import fs from 'node:fs';\n\nimport {\n GATE_MODE,\n LIVENESS_POLL_MS,\n MEMORY_BUFFER_FACTOR,\n NON_INTERACTIVE_ENV,\n TERM_GRACE_MS,\n} from '../../constants/launcher';\nimport { PI_SESSION_ID_ENV } from '../../constants/session';\nimport type { IClock } from '../../types/clock';\nimport type { ILauncher, LaunchRequest, RunHandle } from '../../types/launcher';\nimport type { ILogFile } from '../../types/logFile';\nimport type { IProcessControl } from '../../types/processControl';\nimport type { ExitResult, ISpawner } from '../../types/spawner';\nimport { getResultMaxBytes } from '../runnerConfig';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport {\n cleanupSupervisorFiles,\n NO_TERMINAL_INPUT_ENV,\n readExitMetadata,\n supervisorCommand,\n supervisorPaths,\n writeCommandSpec,\n} from '../runnerSupervisor';\n\n/**\n * Prompts belong on a screen, not in a log file, so `CI` stays set.\n *\n * Colour is kept: the log is replayed into the transcript, and the scrub keeps\n * SGR while still removing the cursor moves that made raw output unreadable.\n */\n/** In-memory output is a multiple of the result ceiling; the log holds the rest. */\n\nexport class Launcher implements ILauncher {\n constructor(\n private readonly spawner: ISpawner,\n private readonly processControl: IProcessControl,\n private readonly logFile: ILogFile,\n private readonly clock: IClock,\n private readonly paths: IRunnerPaths,\n ) {}\n\n launch(request: LaunchRequest): RunHandle {\n const writer = this.logFile.open(request.id);\n const memoryLimit = getResultMaxBytes() * MEMORY_BUFFER_FACTOR;\n\n let buffer = '';\n let buffering = true;\n let settled = false;\n let resolveCompletion: (result: ExitResult) => void = () => undefined;\n let rejectCompletion: (error: Error) => void = () => undefined;\n const completion = new Promise<ExitResult>((resolve, reject) => {\n resolveCompletion = resolve;\n rejectCompletion = reject;\n });\n\n this.paths.ensureDirectories(request.sessionId);\n const supervisor = supervisorPaths(this.paths.stateDirectory(request.sessionId), request.id);\n const env = {\n ...process.env,\n ...NON_INTERACTIVE_ENV,\n ...NO_TERMINAL_INPUT_ENV,\n [PI_SESSION_ID_ENV]: request.sessionId,\n };\n cleanupSupervisorFiles(supervisor);\n writeCommandSpec(supervisor.spec, { command: request.command, cwd: request.cwd, env: defined(env) });\n // Nothing has to be attached before this runner starts, so its gate opens at once.\n fs.writeFileSync(supervisor.gate, '', { mode: GATE_MODE });\n\n const child = this.spawner.spawn({\n // runnerHost supervises the command: it forwards signals and, if the owning\n // pi dies without running its shutdown handler, kills the group itself.\n command: supervisorCommand(supervisor),\n cwd: request.cwd,\n env,\n // Its own process group, so stopping a runner takes the whole tree with it.\n detached: true,\n });\n\n const failLaunch = (error: unknown): void => {\n if (settled) return;\n settled = true;\n writer.close();\n cleanupSupervisorFiles(supervisor);\n try {\n child.kill('SIGTERM');\n } catch (killError) {\n process.emitWarning(`Could not stop runner after log failure: ${String(killError)}`);\n }\n rejectCompletion(error instanceof Error ? error : new Error(String(error)));\n };\n child.onOutput((chunk) => {\n if (settled) return;\n try {\n writer.append(chunk);\n } catch (error) {\n failLaunch(error);\n return;\n }\n if (!buffering) return;\n buffer += chunk;\n if (buffer.length > memoryLimit) buffer = buffer.slice(-memoryLimit);\n });\n\n child.onError(failLaunch);\n\n child.onExit((result) => {\n if (settled) return;\n settled = true;\n writer.close();\n // The supervisor collapses a signalled command into an exit code, so its\n // own sidecar is the only place the original signal survives.\n const outcome = readExitMetadata(supervisor.exit) ?? result;\n cleanupSupervisorFiles(supervisor);\n resolveCompletion(outcome);\n });\n\n // The launcher must never hold the pi process open on a runner's behalf.\n child.unref();\n\n const stop = async (): Promise<boolean> => (child.pid === undefined ? false : this.stop(child.pid));\n\n return {\n id: request.id,\n name: request.name,\n pid: child.pid,\n logPath: writer.path,\n backend: 'native',\n output: () => buffer,\n completion: () => completion,\n detach: () => {\n buffering = false;\n buffer = '';\n },\n stop,\n };\n }\n\n async stop(pid: number): Promise<boolean> {\n if (!this.processControl.isAlive(pid)) return false;\n\n this.processControl.signalGroup(pid, 'SIGTERM');\n const deadline = this.clock.now() + TERM_GRACE_MS;\n while (this.clock.now() < deadline) {\n if (!this.processControl.isAlive(pid)) return true;\n await this.sleep(LIVENESS_POLL_MS);\n }\n\n this.processControl.signalGroup(pid, 'SIGKILL');\n return true;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n this.clock.after(ms, resolve);\n });\n }\n}\n\n/** The spec is JSON, which has no way to carry an unset variable. */\nfunction defined(env: NodeJS.ProcessEnv): Record<string, string> {\n return Object.fromEntries(Object.entries(env).filter((entry): entry is [string, string] => entry[1] !== undefined));\n}\n","export const SOCKET_NAME = 'lifeline.sock';\nexport const SOCKET_PATH_LIMIT = 100;\nexport const SOCKET_HASH_LENGTH = 12;\nexport const SOCKET_MODE = 0o600;\n","import net from 'node:net';\n\n/** Environment variable carrying the lifeline socket path to every runner supervisor. */\nexport const LIFELINE_ENV = 'DOOM_RUNNER_LIFELINE';\n\n/**\n * Reports the owning pi process dying to a runner's supervisor.\n *\n * A connected Unix socket drops the moment the owner's descriptors close, which\n * happens on every exit path including SIGKILL, so it is the one death signal a\n * graceful shutdown handler cannot provide. Connecting never blocks, the close\n * edge arrives through the event loop rather than a poll, and an unref'd socket\n * can never keep the supervisor alive: the watcher is therefore incapable of\n * outliving the process that installed it.\n *\n * A refused or missing socket means the owner died before this runner started,\n * which is reported the same way as a later death rather than left to hang.\n *\n * Runners run unwatched when no lifeline is armed, so a missing environment\n * variable can never be mistaken for a dead owner.\n */\nexport function watchOwner(\n onOwnerLost: () => void,\n target: string | undefined = process.env[LIFELINE_ENV],\n): () => void {\n if (!target) return () => undefined;\n\n let settled = false;\n const settle = (): void => {\n if (settled) return;\n settled = true;\n onOwnerLost();\n };\n\n let connection: net.Socket;\n try {\n connection = net.connect(target);\n } catch (error) {\n process.emitWarning(`Could not watch the doom-runner lifeline: ${String(error)}`);\n return () => undefined;\n }\n\n // The supervisor's own exit must never wait on this socket.\n connection.unref();\n // The owner never sends anything, so drain rather than accumulate.\n connection.on('connect', () => connection.resume());\n connection.on('error', settle);\n connection.on('close', settle);\n\n return () => {\n settled = true;\n connection.destroy();\n };\n}\n","import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport net from 'node:net';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { SOCKET_HASH_LENGTH, SOCKET_MODE, SOCKET_NAME, SOCKET_PATH_LIMIT } from '../../constants/lifeline';\nimport type { ILifeline } from '../../types/lifeline';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport { LIFELINE_ENV } from './client';\n\n/** Unix socket paths are capped at 104 bytes, so a long agent directory falls back to the temp dir. */\n\nexport class NodeLifeline implements ILifeline {\n private server: net.Server | undefined;\n private target: string | undefined;\n /** Closing a server stops it accepting; only the connections carry the death signal. */\n private readonly connections = new Set<net.Socket>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async arm(sessionId: string): Promise<string | undefined> {\n if (this.target) return this.target;\n try {\n this.paths.ensureDirectories(sessionId);\n const target = this.socketPath(sessionId);\n // A socket left behind by a SIGKILLed predecessor refuses connections, never accepts them.\n fs.rmSync(target, { force: true });\n const server = net.createServer((connection) => {\n // Runners need only the close edge, so their liveness and their data are equally irrelevant.\n connection.unref();\n connection.resume();\n this.connections.add(connection);\n connection.on('close', () => this.connections.delete(connection));\n });\n await listen(server, target);\n fs.chmodSync(target, SOCKET_MODE);\n // The lifeline must never be the reason pi stays alive.\n server.unref();\n this.server = server;\n this.target = target;\n process.env[LIFELINE_ENV] = target;\n return target;\n } catch (error) {\n // Runners stay unwatched rather than unlaunchable, so a session without a\n // lifeline degrades to running them unsupervised instead of failing.\n process.emitWarning(`Could not arm the doom-runner lifeline: ${String(error)}`);\n return undefined;\n }\n }\n\n path(): string | undefined {\n return this.target;\n }\n\n dispose(): void {\n // Dropping the connections is what a runner sees, and it is the whole point\n // of disposing: closing the server alone would leave every runner waiting.\n for (const connection of this.connections) connection.destroy();\n this.connections.clear();\n if (this.server) {\n try {\n this.server.close();\n } catch (error) {\n process.emitWarning(`Could not close the doom-runner lifeline: ${String(error)}`);\n }\n this.server = undefined;\n }\n if (this.target) {\n try {\n fs.rmSync(this.target, { force: true });\n } catch (error) {\n process.emitWarning(`Could not remove the doom-runner lifeline: ${String(error)}`);\n }\n this.target = undefined;\n }\n if (process.env[LIFELINE_ENV]) delete process.env[LIFELINE_ENV];\n }\n\n /** Keeps the socket beside the session it belongs to unless that path is too long to bind. */\n private socketPath(sessionId: string): string {\n const preferred = path.join(this.paths.stateDirectory(sessionId), SOCKET_NAME);\n if (Buffer.byteLength(preferred) <= SOCKET_PATH_LIMIT) return preferred;\n const hash = createHash('sha256').update(preferred).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return path.join(os.tmpdir(), `doom-runner-${hash}.sock`);\n }\n}\n\n/** Binding must complete before the first runner launches, so arming waits for it. */\nfunction listen(server: net.Server, target: string): Promise<void> {\n return new Promise((resolve, reject) => {\n server.once('error', reject);\n server.listen(target, () => {\n server.removeListener('error', reject);\n // Later errors would otherwise reach the process as unhandled.\n server.on('error', (error: Error) => process.emitWarning(`doom-runner lifeline failed: ${error.message}`));\n resolve();\n });\n });\n}\n","import fs from 'node:fs';\n\nimport type { ILogFile, LogWriter } from '../../types/logFile';\nimport { getLogMaxBytes } from '../runnerConfig';\nimport type { IRunnerPaths } from '../runnerPaths/type';\n\nexport class LogFile implements ILogFile {\n constructor(private readonly paths: IRunnerPaths) {}\n\n open(id: string): LogWriter {\n this.paths.ensureDirectories();\n const logPath = this.paths.logPathFor(id);\n const rotatedPath = this.paths.rotatedLogPathFor(id);\n const maxBytes = getLogMaxBytes();\n\n // A reused name starts clean: the previous runner's output is already\n // available to whoever wanted it, and mixing two runs in one file makes\n // both unreadable.\n let handle = fs.openSync(logPath, 'w');\n let written = 0;\n let closed = false;\n\n /**\n * A run that keeps producing output for hours would otherwise grow one file\n * without limit. Rotating keeps the newest window at the advertised path\n * and the window before it alongside, so on-disk cost is bounded at roughly\n * twice the ceiling, the path a result advertises stays valid, and the line\n * counts a reader reports still describe the file it actually read.\n */\n const rotate = (): void => {\n fs.closeSync(handle);\n try {\n fs.renameSync(logPath, rotatedPath);\n } catch (error) {\n // Holding the ceiling matters more than keeping the previous window,\n // so the new file is opened either way and the loss is reported.\n process.emitWarning(`Could not rotate runner log ${logPath}: ${String(error)}`);\n }\n handle = fs.openSync(logPath, 'w');\n written = 0;\n };\n\n return {\n path: logPath,\n append(text: string): void {\n const chunk = Buffer.from(text, 'utf8');\n // An empty file always takes the chunk, so a single write larger than\n // the ceiling is stored rather than rotated away into nothing.\n if (written > 0 && written + chunk.byteLength > maxBytes) rotate();\n fs.writeSync(handle, chunk);\n written += chunk.byteLength;\n },\n size(): number {\n return written;\n },\n close(): void {\n // Closing twice is normal: a launch that fails closes the log before\n // the exit handler does. Only a first close that fails is news.\n if (closed) return;\n closed = true;\n try {\n fs.closeSync(handle);\n } catch (error) {\n process.emitWarning(`Could not close runner log ${logPath}: ${String(error)}`);\n }\n },\n };\n }\n}\n","import { LIVENESS_PROBE } from '../../constants/processControl';\nimport type { IProcessControl } from '../../types/processControl';\n\n/** Wraps `process.kill`, which doubles as the liveness probe on POSIX. */\nexport class NodeProcessControl implements IProcessControl {\n isAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false;\n try {\n process.kill(pid, LIVENESS_PROBE);\n return true;\n } catch {\n // EPERM would also land here, but a process we cannot signal is not one\n // this extension can manage, so treating it as gone is the safe answer.\n return false;\n }\n }\n\n signalGroup(pid: number, signal: NodeJS.Signals): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false;\n try {\n process.kill(-pid, signal);\n return true;\n } catch {\n return false;\n }\n }\n}\n","export const POLL_MS = 100;\nexport const LOG_DRAIN_TIMEOUT_MS = 2_000;\nexport const LOG_DRAIN_POLL_MS = 25;\nexport const PANE_STATE_FORMAT = '#{pane_dead}:#{pane_dead_status}:#{session_name}';\nexport const STOP_GRACE_MS = 3_000;\nexport const SESSION_PREFIX = 'doom-tmux-';\nexport const OWNED_TARGET_PATTERN = /^doom-tmux-[A-Za-z0-9_-]+$/;\nexport const TMUX_TARGET_PREFIX = SESSION_PREFIX;\nexport const SOCKET_HASH_LENGTH = 12;\nexport const SOCKET_PREFIX = 'doom-tmux-';\nexport const NON_INTERACTIVE_ENV = { NO_COLOR: '1', CI: '1' };\n","import { TMUX_TARGET_PREFIX } from '../../constants/tmuxBackend';\nimport type { RunHandle } from '../../types/launcher';\nimport type { PtyRun } from '../../types/ptyHost';\nimport type { IRmuxBackend, RmuxLaunchRequest } from '../../types/rmuxBackend';\nimport type { ExitResult } from '../../types/spawner';\n\n/**\n * Presents the available PTY backends as one, preferring RMUX.\n *\n * A launch takes the first backend that accepts it, so a host without a\n * compatible RMUX binary still gets panes rather than falling through to the\n * plain subprocess launcher. Every later call routes on the target's prefix\n * instead of asking each backend in turn: a backend cannot tell \"this session\n * is gone\" from \"this session was never mine\", and guessing would let one\n * report success for the other's run.\n */\nexport class PtyBackendChain implements IRmuxBackend {\n constructor(\n private readonly rmux: IRmuxBackend,\n private readonly tmux: IRmuxBackend,\n ) {}\n\n async launch(request: RmuxLaunchRequest): Promise<RunHandle | undefined> {\n return (await this.rmux.launch(request)) ?? (await this.tmux.launch(request));\n }\n\n watch(id: string, target: string, sessionId?: string): Promise<ExitResult | undefined> {\n return this.owner(target).watch(id, target, sessionId);\n }\n\n stop(target: string, expectedPid: number): Promise<boolean> {\n return this.owner(target).stop(target, expectedPid);\n }\n\n input(target: string, text: string): Promise<boolean> {\n return this.owner(target).input(target, text);\n }\n\n capture(target: string): Promise<string | undefined> {\n return this.owner(target).capture(target);\n }\n\n /** Both backends persist terminal evidence at the same path, so either answers. */\n readOutcome(id: string, sessionId: string): ExitResult | undefined {\n return this.rmux.readOutcome(id, sessionId) ?? this.tmux.readOutcome(id, sessionId);\n }\n\n get(name: string): PtyRun | undefined {\n return this.rmux.get(name) ?? this.tmux.get(name);\n }\n\n private owner(target: string): IRmuxBackend {\n return target.startsWith(TMUX_TARGET_PREFIX) ? this.tmux : this.rmux;\n }\n}\n","export const DEFAULT_COLS = 120;\nexport const DEFAULT_ROWS = 30;\nexport const SCROLLBACK = 2_000;\nexport const MEMORY_BUFFER_FACTOR = 2;\nexport const TERM_GRACE_MS = 2_000;\nexport const LIVENESS_POLL_MS = 100;\nexport const NEWLINE = '\\n';\n","import headless, { type Terminal } from '@xterm/headless';\n\nimport {\n DEFAULT_COLS,\n DEFAULT_ROWS,\n LIVENESS_POLL_MS,\n MEMORY_BUFFER_FACTOR,\n NEWLINE,\n SCROLLBACK,\n TERM_GRACE_MS,\n} from '../../constants/ptyHost';\nimport { PI_SESSION_ID_ENV } from '../../constants/session';\nimport type { IClock } from '../../types/clock';\nimport type { ILogFile } from '../../types/logFile';\nimport type { IProcessControl } from '../../types/processControl';\nimport type { IPtyHost, PtyLaunchRequest, PtyRun } from '../../types/ptyHost';\nimport type { IPtySpawner, PtyProcess } from '../../types/ptySpawner';\nimport type { ExitResult } from '../../types/spawner';\nimport { scrubTerminalOutput } from '../ansiScrub';\nimport { getResultMaxBytes } from '../runnerConfig';\nimport { NO_TERMINAL_INPUT_ENV } from '../runnerSupervisor';\n\n/** Enough history for the overlay to scroll without holding a whole build. */\n\n// Use the CommonJS default export: Node cannot detect this package's named exports.\n// A static import also lets server bundles include it without a live node_modules lookup.\nconst { Terminal: HeadlessTerminal } = headless;\n\nexport class PtyHost implements IPtyHost {\n private readonly runs = new Map<string, PtyRun>();\n\n constructor(\n private readonly spawner: IPtySpawner,\n private readonly logFile: ILogFile,\n private readonly processControl: IProcessControl,\n private readonly clock: IClock,\n ) {}\n\n async launch(request: PtyLaunchRequest): Promise<PtyRun> {\n const cols = request.cols ?? DEFAULT_COLS;\n const rows = request.rows ?? DEFAULT_ROWS;\n const writer = this.logFile.open(request.id);\n const terminal = new HeadlessTerminal({ cols, rows, scrollback: SCROLLBACK, allowProposedApi: true });\n const memoryLimit = getResultMaxBytes() * MEMORY_BUFFER_FACTOR;\n\n const child = await this.spawner.spawn({\n command: request.command,\n cwd: request.cwd,\n env: { ...process.env, ...NO_TERMINAL_INPUT_ENV, [PI_SESSION_ID_ENV]: request.sessionId },\n cols,\n rows,\n });\n\n let buffer = '';\n let buffering = true;\n let settled = false;\n let resolveCompletion: (result: ExitResult) => void = () => undefined;\n const completion = new Promise<ExitResult>((resolve) => {\n resolveCompletion = resolve;\n });\n const dataHandlers = new Set<(data: string) => void>();\n\n child.onData((data) => {\n // The terminal keeps the screen; the log keeps text worth grepping.\n terminal.write(data);\n const scrubbed = scrubTerminalOutput(data);\n writer.append(scrubbed);\n if (buffering) {\n buffer += scrubbed;\n if (buffer.length > memoryLimit) buffer = buffer.slice(-memoryLimit);\n }\n for (const handler of dataHandlers) handler(data);\n });\n\n child.onExit((result) => {\n if (settled) return;\n settled = true;\n this.runs.delete(request.name);\n writer.close();\n resolveCompletion({ code: result.exitCode, signal: null });\n });\n\n const run: PtyRun = {\n id: request.id,\n name: request.name,\n pid: child.pid,\n logPath: writer.path,\n backend: 'native',\n output: () => buffer,\n completion: () => completion,\n detach: () => {\n buffering = false;\n buffer = '';\n },\n stop: () => this.stopChild(child, request.name),\n write: (text) => child.write(text),\n screen: () => renderScreen(terminal),\n onData: (handler) => {\n dataHandlers.add(handler);\n return () => dataHandlers.delete(handler);\n },\n resize: (nextCols, nextRows) => {\n child.resize(nextCols, nextRows);\n terminal.resize(nextCols, nextRows);\n },\n };\n\n this.runs.set(request.name, run);\n return run;\n }\n\n get(name: string): PtyRun | undefined {\n return this.runs.get(name);\n }\n\n write(name: string, text: string): boolean {\n const run = this.runs.get(name);\n if (!run) return false;\n run.write(text.endsWith(NEWLINE) ? text : `${text}${NEWLINE}`);\n return true;\n }\n\n list(): PtyRun[] {\n return [...this.runs.values()];\n }\n\n async disposeAll(): Promise<void> {\n const runs = [...this.runs.values()];\n this.runs.clear();\n for (const run of runs) await run.stop();\n }\n\n private async stopChild(child: PtyProcess, name: string): Promise<boolean> {\n this.runs.delete(name);\n if (!this.processControl.isAlive(child.pid)) return false;\n\n child.kill('SIGTERM');\n const deadline = this.clock.now() + TERM_GRACE_MS;\n while (this.clock.now() < deadline) {\n if (!this.processControl.isAlive(child.pid)) return true;\n await new Promise<void>((resolve) => {\n this.clock.after(LIVENESS_POLL_MS, resolve);\n });\n }\n child.kill('SIGKILL');\n return true;\n }\n}\n\n/** The visible rows, trimmed of trailing blanks, as plain text. */\nfunction renderScreen(terminal: Terminal): string {\n const buffer = terminal.buffer.active;\n const lines: string[] = [];\n for (let row = buffer.baseY; row < buffer.baseY + terminal.rows; row += 1) {\n lines.push(buffer.getLine(row)?.translateToString(true) ?? '');\n }\n while (lines.length > 0 && lines.at(-1) === '') lines.pop();\n return lines.join(NEWLINE);\n}\n","import type { IPtySpawner, PtyProcess, PtySpawnRequest } from '../../types/ptySpawner';\n\nconst RMUX_REQUIRED_ERROR =\n 'The node-pty fallback has been removed. Interactive commands require a supported RMUX runtime.';\n\n/**\n * Compatibility stub for the published NodePtySpawner subpath.\n *\n * Doom Runner no longer starts interactive commands through node-pty. The class\n * remains exportable so existing imports fail at launch with an actionable error.\n */\nexport class NodePtySpawner implements IPtySpawner {\n async spawn(_request: PtySpawnRequest): Promise<PtyProcess> {\n throw new Error(RMUX_REQUIRED_ERROR);\n }\n}\n","export const RMUX_BINARY_ENV = 'DOOMPI_RMUX_BINARY';\nexport const POLL_MS = 100;\nexport const LOG_DRAIN_TIMEOUT_MS = 2_000;\n/**\n * Fallback interval for a missed log-completion event or a lost session.\n * The retained pane lets the sink write its marker before session cleanup.\n */\nexport const LOG_DRAIN_POLL_MS = 25;\nexport const PANE_STATE_FORMAT = '#{pane_dead}:#{pane_dead_status}:#{session_name}';\nexport const PANE_PID_FORMAT = '#{pane_pid}';\nexport const STOP_GRACE_MS = 3_000;\nexport const STOP_CLOSE_TIMEOUT_MS = 1_000;\nexport const EXECUTABLE_MODE = 0o755;\nexport const SESSION_PREFIX = 'doom-runner-';\nexport const OWNED_TARGET_PATTERN = /^doom-runner-[A-Za-z0-9_-]+$/;\nexport const SOCKET_HASH_LENGTH = 12;\nexport const NON_INTERACTIVE_ENV = { NO_COLOR: '1', CI: '1' };\n","import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport { type Pane, RMUX, type Rmux } from '@rmux/sdk';\n\nimport {\n EXECUTABLE_MODE,\n LOG_DRAIN_POLL_MS,\n LOG_DRAIN_TIMEOUT_MS,\n NON_INTERACTIVE_ENV,\n OWNED_TARGET_PATTERN,\n PANE_PID_FORMAT,\n PANE_STATE_FORMAT,\n POLL_MS,\n RMUX_BINARY_ENV,\n SESSION_PREFIX,\n SOCKET_HASH_LENGTH,\n STOP_CLOSE_TIMEOUT_MS,\n STOP_GRACE_MS,\n} from '../../constants/rmuxBackend';\nimport type { RunHandle } from '../../types/launcher';\nimport type { PtyRun } from '../../types/ptyHost';\nimport type { IRmuxBackend, RmuxLaunchRequest } from '../../types/rmuxBackend';\nimport type { ExitResult } from '../../types/spawner';\nimport { getLogMaxBytes, getResultMaxBytes } from '../runnerConfig';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport {\n cleanupSupervisorFiles,\n type CommandSpec,\n NO_TERMINAL_INPUT_ENV,\n readExitMetadata,\n runtimeEntry,\n shellJoin,\n type SupervisorPaths,\n supervisorPaths,\n watchForFile,\n writeCommandSpec,\n} from '../runnerSupervisor';\n\n/** Pane liveness, its exit status, and whether the session still exists, in one read. */\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rmux-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rmux-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rmux-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rmux-linux-x64',\n};\n\nexport class RmuxBackend implements IRmuxBackend {\n private clientPromise: Promise<Rmux | undefined> | undefined;\n private readonly runs = new Map<string, PtyRun>();\n private readonly completions = new Map<string, Promise<ExitResult>>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async launch(request: RmuxLaunchRequest): Promise<RunHandle | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n\n this.paths.ensureDirectories();\n const target = `${SESSION_PREFIX}${request.id}`;\n const logPath = this.paths.logPathFor(request.id);\n const aux = this.auxPaths(request.id, request.sessionId);\n let created = false;\n let released = false;\n\n try {\n this.prepareFiles(request, logPath, aux);\n // `-P -F` reports the pane pid from the call that creates the pane, so the\n // launch does not spend a second round trip asking for it.\n const opened = await rmux.cmd(\n 'new-session',\n '-d',\n '-P',\n '-F',\n PANE_PID_FORMAT,\n '-s',\n target,\n '-c',\n request.cwd,\n process.execPath,\n runtimeEntry('runnerHost'),\n aux.spec,\n aux.gate,\n aux.exit,\n { check: true },\n );\n created = true;\n // Keep the pane until the log reader drains. Otherwise a fast shell can\n // end the session before the reader starts, losing its final output.\n await rmux.cmd('set-option', '-t', target, 'remain-on-exit', 'on', { check: true });\n await rmux.cmd('pipe-pane', '-t', target, this.logPipeCommand(logPath, aux.logDone, request), { check: true });\n\n const pane = rmux.session(target).pane(0, 0);\n const pid = reportedPid(String(opened.stdout ?? '')) ?? (await panePid(rmux, target));\n fs.writeFileSync(aux.gate, '', { mode: 0o600 });\n released = true;\n\n let buffering = true;\n const completion = this.monitor(rmux, pane, target, aux);\n this.trackCompletion(target, completion);\n const stop = (): Promise<boolean> => this.stop(target, pid);\n const handle: RunHandle = {\n id: request.id,\n name: request.name,\n pid,\n logPath,\n backend: 'rmux',\n backendTarget: target,\n output: () => (buffering ? readTail(logPath, getResultMaxBytes() * 2) : ''),\n completion: () => completion,\n detach: () => {\n buffering = false;\n },\n stop,\n };\n\n if (request.interactive) this.runs.set(request.name, this.interactiveRun(handle, pane, completion));\n void completion\n .finally(() => this.runs.delete(request.name))\n .catch((error: unknown) => process.emitWarning(`RMUX completion failed for ${target}: ${errorMessage(error)}`));\n return handle;\n } catch (error) {\n if (released) throw error;\n if (created) {\n await rmux\n .session(target)\n .kill()\n .catch((cleanupError: unknown) =>\n process.emitWarning(`Could not clean up failed RMUX launch ${target}: ${errorMessage(cleanupError)}`),\n );\n }\n cleanupSupervisorFiles(aux);\n process.emitWarning(\n request.interactive\n ? `RMUX launch unavailable for interactive runner: ${errorMessage(error)}`\n : `RMUX launch unavailable, using supervised subprocess: ${errorMessage(error)}`,\n );\n return undefined;\n }\n }\n\n async stop(target: string, expectedPid: number): Promise<boolean> {\n if (!OWNED_TARGET_PATTERN.test(target) || !Number.isInteger(expectedPid) || expectedPid <= 0) return false;\n\n const rmux = await this.client();\n if (!rmux) return false;\n try {\n // A session that already ended took its process group with it.\n if (await sessionMissing(rmux, target)) return true;\n\n const pid = await panePid(rmux, target);\n if (pid !== expectedPid) return false;\n const signalled = signalPane(pid, target);\n if (signalled === 'gone') return true;\n if (signalled === 'failed') return false;\n\n if (await waitForPaneStop(rmux, target, STOP_GRACE_MS)) return true;\n if (await sessionMissing(rmux, target)) return true;\n\n // Revalidate after the grace period so a replacement pane is never closed.\n const currentPid = await panePid(rmux, target);\n if (currentPid !== expectedPid) return false;\n\n return closeOwnedSession(rmux, target);\n } catch (error) {\n process.emitWarning(`Could not stop RMUX target ${target}: ${errorMessage(error)}`);\n return false;\n }\n }\n\n async watch(id: string, target: string, sessionId?: string): Promise<ExitResult | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n const pane = rmux.session(target).pane(0, 0);\n try {\n await rmux.displayMessage('#{pane_id}', { target });\n } catch (error) {\n process.emitWarning(`Could not watch RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n }\n const completion = this.monitor(rmux, pane, target, this.auxPaths(id, sessionId));\n this.trackCompletion(target, completion);\n return completion;\n }\n\n async input(target: string, text: string): Promise<boolean> {\n const rmux = await this.client();\n if (!rmux) return false;\n const result = await rmux\n .session(target)\n .pane(0, 0)\n .sendText(text)\n .catch((error: unknown) => {\n process.emitWarning(`Could not send input to RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n });\n return result !== undefined;\n }\n\n async capture(target: string): Promise<string | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n return rmux\n .session(target)\n .pane(0, 0)\n .captureText()\n .catch((error: unknown) => {\n process.emitWarning(`Could not capture RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n });\n }\n\n get(name: string): PtyRun | undefined {\n return this.runs.get(name);\n }\n\n readOutcome(id: string, sessionId: string): ExitResult | undefined {\n return readExitMetadata(this.auxPaths(id, sessionId).exit);\n }\n\n private trackCompletion(target: string, completion: Promise<ExitResult>): void {\n this.completions.set(target, completion);\n void completion\n .finally(() => this.completions.delete(target))\n .catch((error: unknown) => process.emitWarning(`RMUX monitor failed for ${target}: ${errorMessage(error)}`));\n }\n\n private client(): Promise<Rmux | undefined> {\n this.clientPromise ??= this.resolveClient();\n return this.clientPromise;\n }\n\n private async resolveClient(): Promise<Rmux | undefined> {\n const binaries = [bundledBinary(), 'rmux'].filter((value): value is string => value !== undefined);\n for (const binary of binaries) {\n try {\n if (binary !== 'rmux') fs.chmodSync(binary, EXECUTABLE_MODE);\n const client = new RMUX({ binary, socketName: socketName(this.paths.repositoryPath()) });\n await client.capabilities();\n return client;\n } catch (error) {\n process.emitWarning(`RMUX candidate ${binary} is unavailable: ${errorMessage(error)}`);\n // Try the next compatible candidate. Non-interactive calls can use the subprocess launcher.\n }\n }\n return undefined;\n }\n\n private prepareFiles(request: RmuxLaunchRequest, logPath: string, aux: SupervisorPaths): void {\n cleanupSupervisorFiles(aux);\n fs.writeFileSync(logPath, '', { mode: 0o600 });\n const env = environment(request.sessionId, request.interactive);\n // runnerHost supervises the command and watches the lifeline itself, so an\n // owner that dies mid-run is handled inside the pane rather than around it.\n const spec: CommandSpec = { command: request.command, cwd: request.cwd, env };\n writeCommandSpec(aux.spec, spec);\n }\n\n private auxPaths(id: string, sessionId?: string): SupervisorPaths {\n return supervisorPaths(this.paths.stateDirectory(sessionId), id);\n }\n\n /**\n * The sink the pane pipes into.\n *\n * An interactive run also asks for a raw copy: the scrubbed log it writes\n * beside it has had cursor movement taken out, which is what a terminal\n * needs most. Only interactive runs pay for it, because only they can be\n * attached to.\n */\n private logPipeCommand(logPath: string, donePath: string, request: RmuxLaunchRequest): string {\n const id = path.basename(logPath, '.log');\n return shellJoin([\n process.execPath,\n runtimeEntry('logSink'),\n logPath,\n this.paths.rotatedLogPathFor(id),\n String(getLogMaxBytes()),\n donePath,\n ...(request.interactive ? [this.paths.rawLogPathFor(id)] : []),\n ]);\n }\n\n private async monitor(rmux: Rmux, pane: Pane, target: string, aux: SupervisorPaths): Promise<ExitResult> {\n try {\n const paneCode = await waitForRunEnd(rmux, target, aux.exit);\n await waitForLogDrain(rmux, target, aux.logDone);\n return readExitMetadata(aux.exit) ?? { code: paneCode, signal: null };\n } finally {\n // The pane stays alive for log capture. Remove its session after draining,\n // allowing the last completed run to shut down the mux server too.\n let timer: ReturnType<typeof setTimeout> | undefined;\n const cleanup = async (): Promise<void> => {\n if (!(await sessionMissing(rmux, target))) await pane.server.session(target).kill();\n };\n try {\n await Promise.race([\n cleanup().catch((error: unknown) =>\n process.emitWarning(`Could not clean up completed RMUX target ${target}: ${errorMessage(error)}`),\n ),\n new Promise<void>((resolve) => {\n timer = setTimeout(() => {\n process.emitWarning(`Timed out cleaning up RMUX target ${target} after ${STOP_CLOSE_TIMEOUT_MS}ms`);\n resolve();\n }, STOP_CLOSE_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n cleanupSupervisorFiles(aux);\n }\n }\n }\n\n private interactiveRun(handle: RunHandle, pane: Pane, completion: Promise<ExitResult>): PtyRun {\n let screen = '';\n let active = true;\n const handlers = new Set<(data: string) => void>();\n const refresh = async (): Promise<void> => {\n while (active) {\n const next = await pane.captureText().catch((error: unknown) => {\n process.emitWarning(`Could not capture RMUX screen for ${handle.name}: ${errorMessage(error)}`);\n return screen;\n });\n if (next !== screen) {\n screen = next;\n for (const handler of handlers) handler(next);\n }\n await delay(POLL_MS);\n }\n };\n void refresh();\n void completion\n .finally(() => {\n active = false;\n })\n .catch((error: unknown) =>\n process.emitWarning(`RMUX interactive completion failed for ${handle.name}: ${errorMessage(error)}`),\n );\n return {\n ...handle,\n write: (text) => {\n void pane.sendText(text).catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n screen: () => screen,\n onData: (handler) => {\n handlers.add(handler);\n return () => handlers.delete(handler);\n },\n resize: (cols, rows) => {\n void pane\n .resize({ width: cols, height: rows })\n .catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n };\n }\n}\n\nexport function rmuxPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RMUX binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nexport function resolveBundledRmuxBinary(moduleUrl = import.meta.url): string | undefined {\n const configured = process.env[RMUX_BINARY_ENV];\n if (configured !== undefined && configured !== '') return configured;\n const packageName = rmuxPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(moduleUrl);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rmux');\n } catch (error) {\n process.emitWarning(`Bundled RMUX binary is unavailable for ${packageName}: ${errorMessage(error)}`);\n return undefined;\n }\n}\n\nfunction bundledBinary(): string | undefined {\n return resolveBundledRmuxBinary();\n}\n\nfunction socketName(repositoryPath: string): string {\n const hash = createHash('sha256').update(repositoryPath).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return `${SESSION_PREFIX}${hash}`;\n}\n\nfunction environment(sessionId: string, interactive: boolean): Record<string, string> {\n const values: NodeJS.ProcessEnv = {\n ...process.env,\n ...(interactive ? {} : NON_INTERACTIVE_ENV),\n // Applied to interactive runs as well: a pane is a tty either way, and an\n // agent sending input to a prompt still has no way past a pager.\n ...NO_TERMINAL_INPUT_ENV,\n PI_SESSION_ID: sessionId,\n };\n if (!interactive) delete values['FORCE_COLOR'];\n return Object.fromEntries(\n Object.entries(values).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n}\n\nasync function panePid(rmux: Rmux, target: string): Promise<number> {\n const response = await rmux.displayMessage(PANE_PID_FORMAT, { target });\n const message = typeof response['message'] === 'string' ? response['message'] : '';\n const pid = reportedPid(message);\n if (pid === undefined) throw new Error(`RMUX did not report a pane pid for ${target}`);\n return pid;\n}\n\n/** A pane pid as rmux prints it, or nothing when the output holds no usable pid. */\nfunction reportedPid(output: string): number | undefined {\n const pid = Number(output.trim());\n return Number.isInteger(pid) && pid > 0 ? pid : undefined;\n}\n\n/**\n * Waits for the run to end, preferring the supervisor's own record of it.\n *\n * The sidecar is written before the command's pane can end, and it outlives the\n * server that the pane's exit takes down with it. Watching for it means a run\n * that finishes inside a poll interval is observed when it finishes rather than\n * on the next tick. Asking the server stays the fallback for a supervisor that\n * died without recording anything.\n */\nasync function waitForRunEnd(rmux: Rmux, target: string, exitPath: string): Promise<number | null> {\n const landed = watchForFile(exitPath);\n let active = true;\n const readEvidence = async (): Promise<null> => {\n while (active) {\n if (await landed.appeared(POLL_MS)) return null;\n }\n return null;\n };\n const probePane = async (): Promise<number | null> => {\n while (active) {\n await delay(POLL_MS);\n if (!active) return null;\n const state = await readPaneExit(rmux, target);\n if (state.exited) return state.code;\n }\n return null;\n };\n try {\n // A stalled SDK request must not block the supervisor's durable evidence.\n // Keep only one pane request in flight, even when it never answers.\n return await Promise.race([readEvidence(), probePane()]);\n } finally {\n active = false;\n landed.close();\n }\n}\n\n/**\n * Gives the log sink a moment to mark the log complete.\n *\n * Normal completion retains the session until the sink records its final\n * writes. A lost session cannot guarantee a later marker, so it ends the wait.\n */\nasync function waitForLogDrain(rmux: Rmux, target: string, donePath: string): Promise<void> {\n const deadline = Date.now() + LOG_DRAIN_TIMEOUT_MS;\n const landed = watchForFile(donePath);\n let active = true;\n const readEvidence = async (): Promise<void> => {\n while (active && Date.now() < deadline) {\n if (await landed.appeared(LOG_DRAIN_POLL_MS)) return;\n }\n };\n const probeSession = async (): Promise<void> => {\n while (active && Date.now() < deadline) {\n await delay(LOG_DRAIN_POLL_MS);\n if (!active) return;\n if (await sessionMissing(rmux, target)) return;\n }\n };\n try {\n // The file loop enforces the deadline while a session query is outstanding.\n await Promise.race([readEvidence(), probeSession()]);\n } finally {\n active = false;\n landed.close();\n }\n}\n\nasync function closeOwnedSession(rmux: Rmux, target: string): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (result: boolean): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(result);\n };\n const timer = setTimeout(() => {\n process.emitWarning(`Timed out closing RMUX target ${target} after ${STOP_CLOSE_TIMEOUT_MS}ms`);\n finish(false);\n }, STOP_CLOSE_TIMEOUT_MS);\n void rmux\n .session(target)\n .kill()\n .then(\n () => finish(true),\n (error: unknown) => {\n process.emitWarning(`Could not close RMUX target ${target}: ${errorMessage(error)}`);\n finish(false);\n },\n );\n });\n}\n\nasync function waitForPaneStop(rmux: Rmux, target: string, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n if ((await readPaneExit(rmux, target)).exited) return true;\n } catch (error) {\n process.emitWarning(`Could not verify RMUX target ${target} stopped: ${errorMessage(error)}`);\n return false;\n }\n await delay(POLL_MS);\n }\n return false;\n}\n\n/**\n * One formatted read answers both questions this poll asks, so watching a run\n * costs the same single rmux call it always did.\n */\nasync function readPaneExit(rmux: Rmux, target: string): Promise<{ exited: boolean; code: number | null }> {\n let message: string;\n try {\n const response = await rmux.displayMessage(PANE_STATE_FORMAT, { target });\n message = typeof response['message'] === 'string' ? response['message'] : '';\n } catch {\n // The last session ending stops the server, so a server that cannot be\n // reached is the ordinary end of the last run rather than a fault.\n return { exited: true, code: null };\n }\n const [dead, status, ...rest] = message.trim().split(':');\n // A missing session formats as a live pane with no name rather than failing,\n // so the name is what says whether there is still anything to watch.\n if (rest.join(':') === '') return { exited: true, code: null };\n if (dead === '1' || dead === 'true') {\n const code = Number(status);\n return { exited: true, code: Number.isInteger(code) ? code : null };\n }\n return { exited: false, code: null };\n}\n\n/** A session that cannot be found, on a server that may itself be gone. */\nasync function sessionMissing(rmux: Rmux, target: string): Promise<boolean> {\n try {\n const probe = await rmux.cmd('has-session', '-t', target);\n return probe.returnCode !== 0;\n } catch {\n // No server to ask means no session to find.\n return true;\n }\n}\n\n/**\n * A pane that was already gone and a signal that could not be delivered are\n * opposite answers to \"is it stopped\", so they are reported apart.\n */\nfunction signalPane(pid: number, target: string): 'signalled' | 'gone' | 'failed' {\n try {\n process.kill(pid, 'SIGTERM');\n return 'signalled';\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ESRCH') return 'gone';\n process.emitWarning(`Could not signal RMUX target ${target}: ${errorMessage(error)}`);\n return 'failed';\n }\n}\n\nfunction readTail(target: string, maxBytes: number): string {\n let handle: number | undefined;\n try {\n const size = fs.statSync(target).size;\n const bytes = Math.min(size, maxBytes);\n const buffer = Buffer.alloc(bytes);\n handle = fs.openSync(target, 'r');\n fs.readSync(handle, buffer, 0, bytes, size - bytes);\n return buffer.toString('utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n process.emitWarning(`Could not read RMUX log ${target}: ${errorMessage(error)}`);\n }\n return '';\n } finally {\n if (handle !== undefined) fs.closeSync(handle);\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","export const BYTES_PER_MIB = 1_048_576;\nexport const RTK_STDIN_MAX_BYTES = 10_485_760;\nexport const DEFAULT_RTK_TIMEOUT_MS = 10_000;\nexport const RTK_KILL_WAIT_MS = 1_000;\nexport const OUTPUT_BUFFER_FACTOR = 2;\nexport const EXECUTABLE_MODE = 0o755;\nexport const SIMPLE_TOKEN_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/u;\nexport const SHELL_CONTROL_PATTERN = /[\\r\\n;&|<>`$\\\\'\"(){}*?!]/u;\nexport const RTK_BINARY_ENV = 'DOOMPI_RTK_BINARY';\n","import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport {\n DEFAULT_RTK_TIMEOUT_MS,\n EXECUTABLE_MODE,\n OUTPUT_BUFFER_FACTOR,\n RTK_BINARY_ENV,\n RTK_KILL_WAIT_MS,\n RTK_STDIN_MAX_BYTES,\n SHELL_CONTROL_PATTERN,\n SIMPLE_TOKEN_PATTERN,\n} from '../../constants/rtkProcessor';\nimport {\n type IRtkProcessor,\n RTK_FAILED_WARNING,\n RTK_OVERSIZED_WARNING,\n RTK_TIMEOUT_WARNING,\n RTK_UNAVAILABLE_WARNING,\n type RtkFilter,\n type RtkProcessRequest,\n type RtkProcessResult,\n} from '../../types/rtkProcessor';\nimport { getResultMaxBytes } from '../runnerConfig';\n\nconst GIT_DIFF_INCOMPATIBLE_OPTIONS = [\n '-s',\n '-z',\n '--raw',\n '--numstat',\n '--shortstat',\n '--stat',\n '--dirstat',\n '--dirstat-by-file',\n '--summary',\n '--compact-summary',\n '--name-only',\n '--name-status',\n '--check',\n '--binary',\n '--word-diff',\n '--word-diff-regex',\n '--color',\n '--color-words',\n '--color-moved',\n '--color-moved-ws',\n '--line-prefix',\n '--no-patch',\n '--patch-with-raw',\n '--patch-with-stat',\n '--ext-diff',\n '--textconv',\n '--quiet',\n '--submodule',\n] as const;\nconst STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS = [\n '--json',\n '--null',\n '--null-data',\n '--color',\n '--colour',\n '--after-context',\n '--before-context',\n '--context',\n '--no-filename',\n '--no-line-number',\n '--heading',\n] as const;\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rtk-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rtk-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rtk-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rtk-linux-x64',\n};\n\ntype BinaryResolver = () => string | undefined;\n\nexport class RtkProcessor implements IRtkProcessor {\n /**\n * Latched once a binary proves unrunnable on this host.\n *\n * A glibc build installed on a musl host satisfies os and cpu, so it\n * resolves and then fails to exec. Without this the same doomed spawn is\n * paid on every command, and each one is reported as a processing failure\n * rather than the absence it actually is.\n */\n private unrunnable = false;\n\n constructor(\n private readonly resolveBinary: BinaryResolver = bundledBinary,\n private readonly timeoutMs = DEFAULT_RTK_TIMEOUT_MS,\n ) {}\n\n async process(request: RtkProcessRequest): Promise<RtkProcessResult> {\n const filter = classifyRtkFilter(request.command);\n if (!filter) return { kind: 'skipped' };\n\n let inputBytes: number;\n try {\n inputBytes = fs.statSync(request.logPath).size;\n } catch {\n return { kind: 'fallback', warning: RTK_FAILED_WARNING };\n }\n if (inputBytes > RTK_STDIN_MAX_BYTES) return { kind: 'fallback', warning: RTK_OVERSIZED_WARNING };\n\n if (this.unrunnable) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n\n let binary: string | undefined;\n try {\n binary = this.resolveBinary();\n if (!binary) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n if ((fs.statSync(binary).mode & 0o111) === 0) fs.chmodSync(binary, EXECUTABLE_MODE);\n } catch {\n return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n }\n\n return this.pipe(binary, filter, request.logPath, inputBytes);\n }\n\n private pipe(binary: string, filter: RtkFilter, logPath: string, inputBytes: number): Promise<RtkProcessResult> {\n return new Promise((resolve) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawn(binary, ['pipe', '-f', filter], { stdio: 'pipe' });\n } catch {\n this.unrunnable = true;\n resolve({ kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING });\n return;\n }\n\n const input = fs.createReadStream(logPath);\n const outputLimit = getResultMaxBytes() * OUTPUT_BUFFER_FACTOR;\n let head: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let output: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let outputBytes = 0;\n let newlineCount = 0;\n let lastByte: number | undefined;\n let settled = false;\n let fallbackWarning: string | undefined;\n let killWaitTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finish = (result: RtkProcessResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (killWaitTimer) clearTimeout(killWaitTimer);\n input.destroy();\n resolve(result);\n };\n const stop = (warning: string): void => {\n if (settled) return;\n fallbackWarning = warning;\n let killed = false;\n try {\n killed = child.kill('SIGKILL');\n } catch {\n finish({ kind: 'fallback', warning });\n return;\n }\n if (!killed) {\n finish({ kind: 'fallback', warning });\n return;\n }\n killWaitTimer ??= setTimeout(() => finish({ kind: 'fallback', warning }), RTK_KILL_WAIT_MS);\n };\n const timer = setTimeout(() => stop(RTK_TIMEOUT_WARNING), this.timeoutMs);\n\n child.stdout.on('data', (chunk: Buffer) => {\n outputBytes += chunk.byteLength;\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (head.byteLength < outputLimit) {\n head = Buffer.concat([head, chunk.subarray(0, outputLimit - head.byteLength)]);\n }\n output = appendTail(output, chunk, outputLimit);\n });\n child.stderr.resume();\n child.once('error', (error: NodeJS.ErrnoException) => {\n // Reaching the process is a precondition, not part of processing, so a\n // failure to start reports absence and is not attempted again.\n if (error.code === 'ENOENT' || error.code === 'EACCES' || error.code === 'ENOEXEC') {\n this.unrunnable = true;\n stop(RTK_UNAVAILABLE_WARNING);\n return;\n }\n stop(RTK_FAILED_WARNING);\n });\n child.stdin.once('error', () => stop(RTK_FAILED_WARNING));\n input.once('error', () => stop(RTK_FAILED_WARNING));\n child.once('close', (code) => {\n if (fallbackWarning) {\n finish({ kind: 'fallback', warning: fallbackWarning });\n return;\n }\n const processedOutput = decodeUtf8Tail(output);\n if (code !== 0 || (inputBytes > 0 && processedOutput.trim().length === 0)) {\n finish({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n const lines = newlineCount + (outputBytes > 0 && lastByte !== 0x0a ? 1 : 0);\n finish({\n kind: 'processed',\n result: {\n filter,\n // Only meaningful once the stream outgrew the buffer; below that\n // `output` already starts at byte zero.\n head: outputBytes > output.byteLength ? head.toString('utf8') : '',\n output: processedOutput,\n bytes: outputBytes,\n lines,\n },\n });\n });\n\n input.pipe(child.stdin);\n });\n }\n}\n\n/**\n * Classifies only whitespace-delimited single commands. Shell syntax, expansion,\n * quoting, and globbing are rejected instead of being partially interpreted.\n */\nexport function classifyRtkFilter(command: string): RtkFilter | undefined {\n if (SHELL_CONTROL_PATTERN.test(command)) return undefined;\n const trimmed = command.trim();\n if (!trimmed) return undefined;\n const tokens = trimmed.split(/[ \\t]+/u);\n if (tokens.some((token) => !SIMPLE_TOKEN_PATTERN.test(token))) return undefined;\n\n if (tokens[0] === 'cargo' && tokens[1] === 'test') {\n if (tokens.includes('--') || tokens.some((token) => token.startsWith('--message-format') || token === '--json')) {\n return undefined;\n }\n return 'cargo-test';\n }\n if (tokens[0] === 'pytest') {\n if (tokens.some((token) => token.startsWith('--json-report'))) return undefined;\n return 'pytest';\n }\n if (tokens[0] === 'git' && tokens[1] === 'diff') {\n if (\n tokens.some((token) =>\n GIT_DIFF_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n )\n ) {\n return undefined;\n }\n return 'git-diff';\n }\n if (tokens[0] === 'grep') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens)) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'rg') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens) || !tokens.includes('--no-heading')) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'go' && tokens[1] === 'test') {\n let jsonOutput = false;\n for (const token of tokens.slice(2)) {\n if (token === '-json' || token === '-json=true') jsonOutput = true;\n if (token === '-json=false') jsonOutput = false;\n }\n return jsonOutput ? 'go-test' : undefined;\n }\n if (tokens[0] === 'ruff' && tokens[1] === 'check') {\n const jsonOutput = tokens.some(\n (token, index) =>\n token === '--output-format=json' || (token === '--output-format' && tokens[index + 1] === 'json'),\n );\n return jsonOutput ? 'ruff-check' : undefined;\n }\n return undefined;\n}\n\nexport function rtkPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const configured = process.env[RTK_BINARY_ENV];\n if (configured !== undefined && configured !== '') return configured;\n const packageName = rtkPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rtk');\n } catch {\n return undefined;\n }\n}\n\nfunction appendTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n\nfunction decodeUtf8Tail(output: Buffer<ArrayBufferLike>): string {\n let start = 0;\n while (start < output.byteLength && (output[start]! & 0xc0) === 0x80) start += 1;\n return output.subarray(start).toString('utf8');\n}\nfunction hasShortOrLongOption(tokens: readonly string[], short: string, long: string): boolean {\n return tokens.some(\n (token) => token === long || (token.startsWith('-') && !token.startsWith('--') && token.slice(1).includes(short)),\n );\n}\n\nfunction incompatibleStructuredSearch(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) =>\n (token.startsWith('-') && !token.startsWith('--') && /[0ABCILNchzZ]/u.test(token.slice(1))) ||\n STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n );\n}\n","export const MAX_SLUG_LENGTH = 32;\nexport const MAX_ATTEMPTS = 1000;\nexport const FALLBACK_SLUG = 'runner';\n","import { FALLBACK_SLUG, MAX_ATTEMPTS, MAX_SLUG_LENGTH } from '../../constants/runnerNamer';\nimport type { IRunnerRegistry } from '../../types/runnerRegistry';\nimport type { IRunnerNamer } from './type';\n\n/** Shell noise that says nothing about what the command actually runs. */\nconst IGNORED_LEADING_WORDS = new Set(['sudo', 'env', 'time', 'nohup', 'exec']);\n\nexport class RunnerNamer implements IRunnerNamer {\n constructor(private readonly registry: IRunnerRegistry) {}\n\n async allocate(command: string, sessionId: string, requested?: string): Promise<string> {\n const base = slugify(requested ?? deriveSlug(command));\n const taken = new Set((await this.registry.listBySession(sessionId)).map((record) => record.name));\n if (!taken.has(base)) return base;\n for (let suffix = 2; suffix < MAX_ATTEMPTS; suffix += 1) {\n const candidate = `${base}-${suffix}`;\n if (!taken.has(candidate)) return candidate;\n }\n throw new Error(`Could not find a free runner name based on \"${base}\"`);\n }\n}\n\n/**\n * Names come from the first few meaningful words of the command, which is what\n * a reader recognises in a runner list: `nx-dev-start-api`, not `runner-3`.\n */\nfunction deriveSlug(command: string): string {\n const words = command.split(/\\s+/).filter((word) => word.length > 0 && !word.startsWith('-') && !word.includes('='));\n const meaningful = words.filter((word) => !IGNORED_LEADING_WORDS.has(word));\n return meaningful.slice(0, 4).join('-');\n}\n\nfunction slugify(value: string): string {\n const slug = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, MAX_SLUG_LENGTH)\n .replace(/-+$/, '');\n return slug.length > 0 ? slug : FALLBACK_SLUG;\n}\n","export const STORE_DIR_NAME = 'doom-runner';\nexport const DEFAULT_CONFIG_DIR_NAME = '.pi';\nexport const AGENT_DIR_NAME = 'agent';\nexport const LOG_DIR_NAME = 'logs';\nexport const STATE_DIR_NAME = 'runs';\nexport const LOG_EXTENSION = '.log';\nexport const STATE_EXTENSION = '.json';\nexport const ROTATED_SUFFIX = '.1';\nexport const RAW_SUFFIX = '.raw';\nexport const PI_CODING_AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\nexport const HOME_ALIAS = '~';\nexport const HOME_ALIAS_PREFIX = '~/';\nexport const FILE_ENCODING = 'utf8';\nexport const NOT_FOUND_ERROR_CODE = 'ENOENT';\nexport const EMPTY_SESSION_GRACE_MS = 300_000;\n","import { PI_SESSION_ID_ENV } from '../../constants/session';\n/** Session identity inherited by commands launched from a Pi session. */\n\n/** Returns a usable inherited session id, failing closed on blank values. */\nexport function inheritedSessionId(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const value = env[PI_SESSION_ID_ENV]?.trim();\n return value ? value : undefined;\n}\n\n/** Returns the inherited Pi session id or fails closed outside a session. */\nexport function requiredSessionId(env: NodeJS.ProcessEnv = process.env): string {\n const sessionId = inheritedSessionId(env);\n if (!sessionId) throw new Error(`${PI_SESSION_ID_ENV} is required for doom-runner`);\n return sessionId;\n}\n","import { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { LOG_DIR_ENV } from '../../constants/runnerConfig';\nimport {\n AGENT_DIR_NAME,\n DEFAULT_CONFIG_DIR_NAME,\n EMPTY_SESSION_GRACE_MS,\n FILE_ENCODING,\n HOME_ALIAS,\n HOME_ALIAS_PREFIX,\n LOG_DIR_NAME,\n LOG_EXTENSION,\n NOT_FOUND_ERROR_CODE,\n PI_CODING_AGENT_DIR_ENV,\n RAW_SUFFIX,\n ROTATED_SUFFIX,\n STATE_DIR_NAME,\n STATE_EXTENSION,\n STORE_DIR_NAME,\n} from '../../constants/runnerPaths';\nimport { getMaxCompletedRunners } from '../runnerConfig';\nimport { inheritedSessionId } from '../session';\nimport type { IRunnerPaths, LogSweepResult } from './type';\n\n/** Unscrubbed pane bytes, kept beside the log so an attached terminal can replay them. */\n/**\n * How long an entirely empty session directory is left alone before it counts\n * as an orphan. A session that has started but not yet run anything owns such a\n * directory for a moment, and reclaiming it out from under that session would\n * be churn for no gain.\n */\n\nfunction git(args: string[], cwd: string): string | undefined {\n try {\n const output = execFileSync('git', args, { cwd, encoding: FILE_ENCODING, stdio: ['ignore', 'pipe', 'ignore'] });\n const trimmed = output.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction realPath(target: string): string {\n try {\n return fs.realpathSync(target);\n } catch {\n return target;\n }\n}\n\nfunction resolveAgentDirectory(env: NodeJS.ProcessEnv): string {\n const configured = env[PI_CODING_AGENT_DIR_ENV]?.trim();\n if (configured === HOME_ALIAS) return os.homedir();\n if (configured?.startsWith(HOME_ALIAS_PREFIX)) {\n return path.join(os.homedir(), configured.slice(HOME_ALIAS_PREFIX.length));\n }\n return configured ? path.resolve(configured) : path.join(os.homedir(), DEFAULT_CONFIG_DIR_NAME, AGENT_DIR_NAME);\n}\n\n/**\n * Newest mtime anywhere one level inside the session, falling back to the\n * directory itself. A session still being written to therefore always looks\n * recent, whatever state its individual records are in.\n */\nfunction newestEntryTime(directory: string): number {\n let newest = fs.statSync(directory).mtimeMs;\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n const target = path.join(directory, entry.name);\n newest = Math.max(newest, fs.statSync(target).mtimeMs);\n if (!entry.isDirectory()) continue;\n for (const child of fs.readdirSync(target)) {\n newest = Math.max(newest, fs.statSync(path.join(target, child)).mtimeMs);\n }\n }\n return newest;\n}\n\nasync function newestEntryTimeAsync(directory: string): Promise<number> {\n let newest = (await fs.promises.stat(directory)).mtimeMs;\n for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {\n const target = path.join(directory, entry.name);\n newest = Math.max(newest, (await fs.promises.stat(target)).mtimeMs);\n if (!entry.isDirectory()) continue;\n for (const child of await fs.promises.readdir(target)) {\n newest = Math.max(newest, (await fs.promises.stat(path.join(target, child))).mtimeMs);\n }\n }\n return newest;\n}\n\n/**\n * Whether any run in this session names a pi process that is still running.\n *\n * One live record is enough: records are only ever written by their owner, so a\n * live host pid means the session is still in use however old its files look.\n */\nfunction ownerAlive(stateDirectory: string): boolean {\n let entries: string[];\n try {\n entries = fs.readdirSync(stateDirectory);\n } catch {\n // No records left to consult, so nothing claims the directory.\n return false;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n try {\n const value: unknown = JSON.parse(fs.readFileSync(path.join(stateDirectory, entry), FILE_ENCODING));\n const hostPid = (value as { hostPid?: unknown }).hostPid;\n if (typeof hostPid !== 'number') continue;\n process.kill(hostPid, 0);\n return true;\n } catch {\n // An unreadable record and a dead pid mean the same thing here: no claim.\n }\n }\n return false;\n}\n\nasync function ownerAliveAsync(stateDirectory: string): Promise<boolean> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(stateDirectory);\n } catch {\n return false;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(path.join(stateDirectory, entry), FILE_ENCODING));\n const hostPid = (value as { hostPid?: unknown }).hostPid;\n if (typeof hostPid !== 'number') continue;\n process.kill(hostPid, 0);\n return true;\n } catch {\n // An unreadable record and a dead pid mean the same thing here: no claim.\n }\n }\n return false;\n}\n\nfunction completedAt(value: unknown): number | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = value as { state?: unknown; exit?: { finishedAt?: unknown } };\n if (record.state !== 'completed' || typeof record.exit?.finishedAt !== 'string') return undefined;\n const timestamp = Date.parse(record.exit.finishedAt);\n return Number.isFinite(timestamp) ? timestamp : undefined;\n}\n\ninterface CompletedRecord {\n readonly id: string;\n readonly statePath: string;\n readonly finishedAt: number;\n}\n\n/**\n * Completed records a sweep may delete: everything past the TTL, and everything\n * beyond the newest `keep`.\n *\n * The count bound is what keeps a live session from growing without limit. Its\n * records only reach a multi-day TTL long after the session that is still\n * writing them would have filled its directory, so age alone bounds nothing\n * while the session lasts.\n */\nfunction expiredCompletedRecords(\n records: readonly CompletedRecord[],\n ttlMs: number,\n now: number,\n keep: number,\n): CompletedRecord[] {\n return [...records]\n .sort((left, right) => right.finishedAt - left.finishedAt)\n .filter((record, index) => index >= keep || now - record.finishedAt > ttlMs);\n}\n\n/**\n * Whether the directory holds no files at all, only empty subdirectories.\n *\n * A session that ran something leaves metadata behind, and a live session keeps\n * its lifeline socket in the state directory, so an entirely empty tree names\n * no owner and no history.\n */\nfunction isEmptySession(directory: string): boolean {\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n if (!entry.isDirectory()) return false;\n if (fs.readdirSync(path.join(directory, entry.name)).length > 0) return false;\n }\n return true;\n}\n\nasync function isEmptySessionAsync(directory: string): Promise<boolean> {\n for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {\n if (!entry.isDirectory()) return false;\n if ((await fs.promises.readdir(path.join(directory, entry.name))).length > 0) return false;\n }\n return true;\n}\n\n/**\n * The root every session's runner state lives under, before any log-directory\n * override: `<agent dir>/doom-runner/<session id>/{runs,logs}`. A reader in\n * another process, such as the cockpit hub, resolves the same root from its\n * own environment.\n */\nexport function resolveRunnerStoreDirectory(env: NodeJS.ProcessEnv): string {\n return path.join(resolveAgentDirectory(env), STORE_DIR_NAME);\n}\n\n/** Durable metadata directory for one session. Used by APIs and history reads, not live transport. */\nexport function runnerStateDirFor(storeDir: string, sessionId: string): string {\n return path.join(storeDir, sessionId, STATE_DIR_NAME);\n}\n\n/** Session-scoped runner storage under the Pi agent directory. */\nexport class RunnerPaths implements IRunnerPaths {\n private adoptedSessionId: string | undefined;\n private repository: string | undefined;\n\n repositoryPath(): string {\n this.repository ??= realPath(git(['rev-parse', '--show-toplevel'], process.cwd()) ?? process.cwd());\n return this.repository;\n }\n\n setSessionId(sessionId: string): void {\n const trimmed = sessionId.trim();\n if (!trimmed) throw new Error('doom-runner session id cannot be blank');\n this.adoptedSessionId = trimmed;\n }\n\n logDirectory(sessionId?: string): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.resolve(override);\n return path.join(this.sessionDirectory(sessionId), LOG_DIR_NAME);\n }\n\n stateDirectory(sessionId?: string): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.join(path.resolve(override), '..', STATE_DIR_NAME);\n return path.join(this.sessionDirectory(sessionId), STATE_DIR_NAME);\n }\n\n logPathFor(id: string, sessionId?: string): string {\n return path.join(this.logDirectory(sessionId), `${id}${LOG_EXTENSION}`);\n }\n\n rotatedLogPathFor(id: string, sessionId?: string): string {\n return `${this.logPathFor(id, sessionId)}${ROTATED_SUFFIX}`;\n }\n\n rawLogPathFor(id: string, sessionId?: string): string {\n return `${this.logPathFor(id, sessionId)}${RAW_SUFFIX}`;\n }\n\n statePathFor(id: string, sessionId?: string): string {\n return path.join(this.stateDirectory(sessionId), `${id}${STATE_EXTENSION}`);\n }\n\n ensureDirectories(sessionId?: string): void {\n fs.mkdirSync(this.logDirectory(sessionId), { recursive: true });\n fs.mkdirSync(this.stateDirectory(sessionId), { recursive: true });\n }\n\n sweepHistory(ttlMs: number, now: number = Date.now()): LogSweepResult {\n const result: LogSweepResult = { removed: [], errors: [] };\n // A pinned log directory belongs to whoever pinned it, so that branch only\n // ever removes the files this package put there.\n if (process.env[LOG_DIR_ENV]?.trim()) {\n this.sweepStateDirectory(this.stateDirectory(), this.sessionId(), ttlMs, now, result);\n return result;\n }\n const root = this.storeDirectory();\n let sessions: string[];\n try {\n sessions = fs.readdirSync(root);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return result;\n result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);\n return result;\n }\n\n // Sweeping whole session directories rather than the three files a completed\n // record names is what keeps sockets, sidecars, runs that never reached a\n // terminal state, and the emptied directories themselves from accumulating.\n const active = this.currentSessionId();\n for (const sessionId of sessions) {\n if (sessionId === active) {\n this.sweepActiveSession(sessionId, ttlMs, now, result);\n continue;\n }\n this.sweepSessionDirectory(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n async sweepHistoryAsync(ttlMs: number, now: number = Date.now()): Promise<LogSweepResult> {\n const result: LogSweepResult = { removed: [], errors: [] };\n if (process.env[LOG_DIR_ENV]?.trim()) {\n await this.sweepStateDirectoryAsync(this.stateDirectory(), this.sessionId(), ttlMs, now, result);\n return result;\n }\n\n const root = this.storeDirectory();\n let sessions: string[];\n try {\n sessions = await fs.promises.readdir(root);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return result;\n result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);\n return result;\n }\n\n const active = this.currentSessionId();\n for (const sessionId of sessions) {\n if (sessionId === active) {\n await this.sweepActiveSessionAsync(sessionId, ttlMs, now, result);\n continue;\n }\n await this.sweepSessionDirectoryAsync(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n legacyDirectory(): string | undefined {\n const commonDirectory = git(['rev-parse', '--path-format=absolute', '--git-common-dir'], process.cwd());\n return commonDirectory ? path.join(commonDirectory, STORE_DIR_NAME) : undefined;\n }\n\n removeLegacyStore(): string | undefined {\n const target = this.legacyDirectory();\n if (!target || path.basename(target) !== STORE_DIR_NAME || !fs.existsSync(target)) return undefined;\n fs.rmSync(target, { recursive: true, force: true });\n return target;\n }\n\n /** The live session, if one is known, so a sweep never deletes underneath itself. */\n private currentSessionId(): string | undefined {\n try {\n return this.sessionId();\n } catch {\n // A CLI invocation outside a session still sweeps; it just has nothing to spare.\n return undefined;\n }\n }\n\n /**\n * Retention inside the session that is still running.\n *\n * The whole-directory sweep cannot be pointed at the live session: it would\n * take the lifeline socket, the supervisor sidecars and the running runners'\n * own metadata with it. Per-record retention is safe there because it only\n * ever removes a record whose state is `completed` together with the two log\n * files that record names, and never the directory itself. A running record\n * has no `exit.finishedAt`, so it is not a candidate at any age.\n */\n private sweepActiveSession(sessionId: string, ttlMs: number, now: number, result: LogSweepResult): void {\n this.sweepStateDirectory(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);\n }\n\n private async sweepActiveSessionAsync(\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n await this.sweepStateDirectoryAsync(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);\n }\n\n /** Removes a session's whole directory once its newest entry is past the TTL. */\n private sweepSessionDirectory(directory: string, ttlMs: number, now: number, result: LogSweepResult): void {\n try {\n const newest = newestEntryTime(directory);\n // An orphan left by a session that never ran anything holds nothing worth\n // a retention window, so it goes as soon as it is clearly not in use.\n if (now - newest > EMPTY_SESSION_GRACE_MS && isEmptySession(directory)) {\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n return;\n }\n if (now - newest <= ttlMs) return;\n // An idle session can go quiet for longer than the TTL, and deleting one\n // takes its lifeline socket with it, which would read to its own runners\n // as an owner that had died. Its records name the owner, so ask.\n if (ownerAlive(path.join(directory, STATE_DIR_NAME))) return;\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private async sweepSessionDirectoryAsync(\n directory: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n try {\n const newest = await newestEntryTimeAsync(directory);\n if (now - newest > EMPTY_SESSION_GRACE_MS && (await isEmptySessionAsync(directory))) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n return;\n }\n if (now - newest <= ttlMs) return;\n if (await ownerAliveAsync(path.join(directory, STATE_DIR_NAME))) return;\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private sessionId(explicit?: string): string {\n const sessionId = explicit?.trim() || this.adoptedSessionId || inheritedSessionId();\n if (!sessionId) throw new Error('PI_SESSION_ID is required for doom-runner storage');\n return sessionId;\n }\n\n private storeDirectory(): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.dirname(path.resolve(override));\n return resolveRunnerStoreDirectory(process.env);\n }\n\n private sessionDirectory(sessionId?: string): string {\n return path.join(this.storeDirectory(), this.sessionId(sessionId));\n }\n\n private sweepStateDirectory(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): void {\n let entries: string[];\n try {\n entries = fs.readdirSync(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n const completed: CompletedRecord[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(fs.readFileSync(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined) continue;\n completed.push({ id: entry.slice(0, -STATE_EXTENSION.length), statePath, finishedAt });\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n\n for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners())) {\n this.removeHistoryFiles(sessionId, record.id, record.statePath, result);\n }\n }\n\n private async sweepStateDirectoryAsync(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n const completed: CompletedRecord[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined) continue;\n completed.push({ id: entry.slice(0, -STATE_EXTENSION.length), statePath, finishedAt });\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n\n for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners())) {\n await this.removeHistoryFilesAsync(sessionId, record.id, record.statePath, result);\n }\n }\n\n private removeHistoryFiles(sessionId: string, id: string, statePath: string, result: LogSweepResult): void {\n const candidates = [\n statePath,\n this.logPathFor(id, sessionId),\n this.rotatedLogPathFor(id, sessionId),\n this.rawLogPathFor(id, sessionId),\n ];\n for (const candidate of candidates) {\n try {\n fs.unlinkSync(candidate);\n result.removed.push(candidate);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);\n }\n }\n }\n }\n\n private async removeHistoryFilesAsync(\n sessionId: string,\n id: string,\n statePath: string,\n result: LogSweepResult,\n ): Promise<void> {\n const candidates = [statePath, this.logPathFor(id, sessionId), this.rotatedLogPathFor(id, sessionId)];\n for (const candidate of candidates) {\n try {\n await fs.promises.unlink(candidate);\n result.removed.push(candidate);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);\n }\n }\n }\n }\n}\n","export const SERVICE_TYPE = 'tool';\nexport const SERVICE_PREFIX = 'doom-runner:';\nexport const RUNNER_TAG = 'doom-runner';\nexport const REGISTRY_PATH_ENV = 'PROCESS_REGISTRY_PATH';\nexport const JSON_EXTENSION = '.json';\nexport const COMMAND_SIDECAR_SUFFIX = '.command.json';\nexport const EXIT_SIDECAR_SUFFIX = '.exit.json';\nexport const NOT_FOUND_ERROR_CODE = 'ENOENT';\n","import type { RunnerRecord } from '../../types/runnerRegistry';\n\n/** The disk record's shape guard, shared by the registry and every reader of its files. */\nexport function isRunnerRecord(value: unknown): value is RunnerRecord {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Partial<RunnerRecord>;\n return (\n typeof record.id === 'string' &&\n typeof record.name === 'string' &&\n typeof record.pid === 'number' &&\n typeof record.command === 'string' &&\n typeof record.cwd === 'string' &&\n typeof record.logPath === 'string' &&\n typeof record.sessionId === 'string' &&\n typeof record.startedAt === 'string' &&\n (record.state === 'running' || record.state === 'completed') &&\n typeof record.promoted === 'boolean' &&\n (record.backend === 'rmux' || record.backend === 'tmux' || record.backend === 'native')\n );\n}\n","import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\n\nimport { resolveRootSessionId } from '@agimon-ai/doompi-core/child-process';\nimport {\n createProcessRegistryService,\n normalizeRepositoryPath,\n type ProcessRegistryRecord,\n type ProcessRegistryService,\n resolveProcessTags,\n} from '@agimon-ai/foundation-process-registry';\n\nimport {\n COMMAND_SIDECAR_SUFFIX,\n EXIT_SIDECAR_SUFFIX,\n JSON_EXTENSION,\n NOT_FOUND_ERROR_CODE,\n REGISTRY_PATH_ENV,\n RUNNER_TAG,\n SERVICE_PREFIX,\n SERVICE_TYPE,\n} from '../../constants/runnerRegistry';\nimport type { IProcessControl } from '../../types/processControl';\nimport type {\n CompleteRunnerInput,\n IRunnerRegistry,\n RegisterRunnerInput,\n RunnerRecord,\n} from '../../types/runnerRegistry';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport { isRunnerRecord } from '../runnerRecord';\n\n/** The slice of the shared registry service this wrapper depends on. */\nexport type ProcessRegistryPort = Pick<\n ProcessRegistryService,\n 'registerProcess' | 'releaseProcess' | 'listProcesses' | 'close'\n>;\n\n/** Opens the SQLite-backed registry the rest of the monorepo already shares. */\nexport function createDefaultProcessRegistry(): ProcessRegistryPort {\n return createProcessRegistryService(process.env[REGISTRY_PATH_ENV]);\n}\n\nfunction sessionTag(sessionId: string): string {\n return `session:${sessionId}`;\n}\n\nfunction readString(record: ProcessRegistryRecord, key: string): string {\n const value = record.metadata?.[key];\n return typeof value === 'string' ? value : '';\n}\n\nfunction readNumber(record: ProcessRegistryRecord, key: string): number {\n const value = record.metadata?.[key];\n return typeof value === 'number' ? value : 0;\n}\n\nconst runCommand = promisify(execFile);\n\n/**\n * The process start time the registry stores to prove a pid is still the\n * process it registered.\n *\n * The registry resolves it itself when the caller omits it, and it does so with\n * `execFileSync`, which stops this process for the length of a `ps` on every\n * runner that starts. Reading it here costs the same subprocess and blocks\n * nothing: the command is already running by the time a runner is registered.\n *\n * The value has to match what the registry would have read, so the command and\n * the trimming are deliberately identical to its own.\n */\nasync function processStartedAt(pid: number): Promise<string | undefined> {\n if (process.platform === 'win32') return undefined;\n try {\n const { stdout } = await runCommand('ps', ['-o', 'lstart=', '-p', String(pid)], { encoding: 'utf8' });\n return stdout.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\ntype ListenerRegistration = Readonly<{ key: string; listener: () => void }>;\n\n// Facet replacement can leave an in-flight tool and its publisher on different registry instances.\nconst listenersBySession = new Map<string, Set<() => void>>();\n\nexport class RunnerRegistry implements IRunnerRegistry {\n private readonly subscriptions = new Set<ListenerRegistration>();\n\n constructor(\n private readonly paths: IRunnerPaths,\n private readonly processControl: IProcessControl,\n private readonly registry: ProcessRegistryPort,\n private readonly environment: Readonly<Record<string, string | undefined>>,\n ) {}\n\n async register(input: RegisterRunnerInput): Promise<RunnerRecord> {\n const startedAt = new Date().toISOString();\n const rootSessionId = resolveRootSessionId(input.sessionId, this.environment);\n const identity = await processStartedAt(input.pid);\n const response = await this.registry.registerProcess({\n repositoryPath: this.repositoryPath(),\n serviceName: `${SERVICE_PREFIX}${input.id}`,\n serviceType: SERVICE_TYPE,\n pid: input.pid,\n command: input.command,\n ...(identity === undefined ? {} : { processStartedAt: identity }),\n tags: resolveProcessTags([RUNNER_TAG, `runner:${input.id}`, sessionTag(input.sessionId)]),\n metadata: {\n id: input.id,\n name: input.name,\n command: input.command,\n cwd: input.cwd,\n logPath: input.logPath,\n interactive: input.interactive,\n sessionId: input.sessionId,\n rootSessionId,\n backend: input.backend,\n ...(input.backendTarget ? { backendTarget: input.backendTarget } : {}),\n startedAt,\n hostPid: process.pid,\n },\n // The caller has already resolved name collisions against live records,\n // so anything still holding this name is stale.\n force: true,\n });\n if (!response.success) throw new Error(response.error ?? `Failed to register runner ${input.name}`);\n const record: RunnerRecord = {\n id: input.id,\n name: input.name,\n pid: input.pid,\n command: input.command,\n cwd: input.cwd,\n logPath: input.logPath,\n interactive: input.interactive,\n sessionId: input.sessionId,\n rootSessionId,\n startedAt,\n state: 'running',\n promoted: false,\n backend: input.backend,\n ...(input.backendTarget ? { backendTarget: input.backendTarget } : {}),\n hostPid: process.pid,\n };\n this.writeRecord(record);\n this.notify(record.sessionId);\n return record;\n }\n\n async list(): Promise<RunnerRecord[]> {\n const records = await this.registry.listProcesses({\n repositoryPath: this.repositoryPath(),\n serviceType: SERVICE_TYPE,\n tags: resolveProcessTags([RUNNER_TAG]),\n });\n return records.map(toRunnerRecord);\n }\n\n async listAcrossRepositories(): Promise<RunnerRecord[]> {\n const records = await this.registry.listProcesses({\n serviceType: SERVICE_TYPE,\n tags: resolveProcessTags([RUNNER_TAG]),\n });\n return records.map(toRunnerRecord);\n }\n\n async listBySession(sessionId: string): Promise<RunnerRecord[]> {\n const records = await this.list();\n return records.filter((record) => record.sessionId === sessionId);\n }\n\n async listByRootSession(rootSessionId: string): Promise<RunnerRecord[]> {\n const records = await this.list();\n return records.filter((record) => (record.rootSessionId ?? record.sessionId) === rootSessionId);\n }\n\n async listAll(sessionId?: string): Promise<RunnerRecord[]> {\n this.paths.ensureDirectories(sessionId);\n const records: RunnerRecord[] = [];\n const directory = this.paths.stateDirectory(sessionId);\n for (const entry of fs.readdirSync(directory)) {\n if (!isPrimaryMetadataEntry(entry)) continue;\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(`${directory}/${entry}`, 'utf8'));\n if (isRunnerRecord(parsed)) records.push(parsed);\n } catch (error) {\n process.emitWarning(`Could not read runner metadata ${entry}: ${String(error)}`);\n }\n }\n return records.sort((left, right) => right.startedAt.localeCompare(left.startedAt));\n }\n\n async get(id: string, sessionId?: string): Promise<RunnerRecord | undefined> {\n const target = this.paths.statePathFor(id, sessionId);\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(target, 'utf8'));\n if (!isRunnerRecord(parsed) || parsed.id !== id) {\n process.emitWarning(`Could not read runner metadata ${path.basename(target)}: invalid runner record`);\n return undefined;\n }\n return parsed;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return undefined;\n process.emitWarning(`Could not read runner metadata ${path.basename(target)}: ${String(error)}`);\n return undefined;\n }\n }\n\n async markPromoted(id: string): Promise<RunnerRecord | undefined> {\n const record = await this.get(id);\n if (!record) return undefined;\n const promoted = { ...record, promoted: true };\n this.writeRecord(promoted);\n this.notify(promoted.sessionId);\n return promoted;\n }\n\n async complete(id: string, outcome: CompleteRunnerInput, sessionId?: string): Promise<RunnerRecord | undefined> {\n const persisted = await this.get(id, sessionId);\n if (persisted?.state === 'completed') {\n await this.release(id, persisted.sessionId);\n return persisted;\n }\n const active = await this.list();\n const record =\n persisted ??\n active.find((candidate) => candidate.id === id && (sessionId === undefined || candidate.sessionId === sessionId));\n if (!record) {\n const belongsToAnotherSession =\n sessionId !== undefined && active.some((candidate) => candidate.id === id && candidate.sessionId !== sessionId);\n if (!belongsToAnotherSession) await this.release(id, sessionId);\n return undefined;\n }\n const completed: RunnerRecord = {\n ...record,\n state: 'completed',\n exit: { ...outcome, finishedAt: new Date().toISOString() },\n };\n this.writeRecord(completed);\n await this.release(id, completed.sessionId);\n return completed;\n }\n\n async release(id: string, sessionId?: string): Promise<void> {\n const activeSessionId = sessionId ?? (await this.list()).find((record) => record.id === id)?.sessionId;\n const ownedSessionId = activeSessionId ?? (await this.get(id))?.sessionId;\n const response = await this.registry.releaseProcess({\n repositoryPath: this.repositoryPath(),\n serviceName: `${SERVICE_PREFIX}${id}`,\n serviceType: SERVICE_TYPE,\n kill: false,\n releasePort: false,\n force: true,\n });\n if (!response.success && !response.error?.includes('No matching process entry')) {\n throw new Error(response.error ?? `Failed to release runner ${id}`);\n }\n if (ownedSessionId !== undefined) this.notify(ownedSessionId);\n }\n\n async pruneDead(): Promise<string[]> {\n const dead = (await this.list()).filter((record) => !this.processControl.isAlive(record.pid));\n for (const record of dead) {\n await this.complete(record.id, { reason: 'backend_lost', code: null, signal: null }, record.sessionId);\n }\n return dead.map((record) => record.id);\n }\n\n subscribe(listener: () => void, sessionId: string): () => void {\n const subscribed = (): void => listener();\n const registration = { key: this.subscriptionKey(sessionId), listener: subscribed };\n const listeners = listenersBySession.get(registration.key) ?? new Set<() => void>();\n listeners.add(subscribed);\n listenersBySession.set(registration.key, listeners);\n this.subscriptions.add(registration);\n return () => this.removeSubscription(registration);\n }\n\n close(): void {\n for (const registration of this.subscriptions) this.removeSubscription(registration);\n this.registry.close();\n }\n\n private notify(sessionId: string): void {\n for (const listener of listenersBySession.get(this.subscriptionKey(sessionId)) ?? []) {\n try {\n listener();\n } catch (error) {\n process.emitWarning(`Runner registry listener failed: ${String(error)}`);\n }\n }\n }\n\n private removeSubscription(registration: ListenerRegistration): void {\n const listeners = listenersBySession.get(registration.key);\n listeners?.delete(registration.listener);\n if (listeners?.size === 0) listenersBySession.delete(registration.key);\n this.subscriptions.delete(registration);\n }\n\n private subscriptionKey(sessionId: string): string {\n return JSON.stringify([path.resolve(this.paths.stateDirectory(sessionId)), sessionId]);\n }\n\n private repositoryPath(): string {\n return normalizeRepositoryPath(this.paths.repositoryPath());\n }\n\n private writeRecord(record: RunnerRecord): void {\n this.paths.ensureDirectories(record.sessionId);\n const target = this.paths.statePathFor(record.id, record.sessionId);\n const temporary = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(temporary, `${JSON.stringify(record, null, 2)}\\n`, 'utf8');\n fs.renameSync(temporary, target);\n }\n}\n\n/** Every field is read back from metadata this class wrote on registration. */\nfunction toRunnerRecord(record: ProcessRegistryRecord): RunnerRecord {\n return {\n id: readString(record, 'id'),\n name: readString(record, 'name'),\n pid: record.pid,\n command: readString(record, 'command'),\n cwd: readString(record, 'cwd'),\n logPath: readString(record, 'logPath'),\n interactive: record.metadata?.interactive === true,\n sessionId: readString(record, 'sessionId'),\n ...(readString(record, 'rootSessionId') ? { rootSessionId: readString(record, 'rootSessionId') } : {}),\n startedAt: readString(record, 'startedAt'),\n state: 'running',\n promoted: false,\n backend: readBackend(record.metadata?.backend),\n ...(readString(record, 'backendTarget') ? { backendTarget: readString(record, 'backendTarget') } : {}),\n hostPid: readNumber(record, 'hostPid'),\n };\n}\n\nfunction readBackend(value: unknown): RunnerRecord['backend'] {\n return value === 'rmux' || value === 'tmux' ? value : 'native';\n}\n\nfunction isPrimaryMetadataEntry(entry: string): boolean {\n return (\n entry.endsWith(JSON_EXTENSION) && !entry.endsWith(COMMAND_SIDECAR_SUFFIX) && !entry.endsWith(EXIT_SIDECAR_SUFFIX)\n );\n}\n","export const SHELL = '/bin/bash';\n/** Allow the supervisor's final pipe bytes to arrive after its exit event. */\nexport const EXIT_OUTPUT_DRAIN_MS = 250;\n","import { spawn } from 'node:child_process';\n\nimport { EXIT_OUTPUT_DRAIN_MS, SHELL } from '../../constants/spawner';\nimport type { ExitResult, ISpawner, OutputStream, SpawnedProcess, SpawnRequest } from '../../types/spawner';\n\n/** Wraps `node:child_process.spawn`. */\nexport class NodeSpawner implements ISpawner {\n spawn(request: SpawnRequest): SpawnedProcess {\n const child = spawn(request.command, {\n cwd: request.cwd,\n env: request.env,\n detached: request.detached,\n shell: SHELL,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n child.stdout?.setEncoding('utf8');\n child.stderr?.setEncoding('utf8');\n\n return {\n pid: child.pid,\n onOutput(handler: (chunk: string, stream: OutputStream) => void): void {\n child.stdout?.on('data', (chunk: string) => handler(chunk, 'stdout'));\n child.stderr?.on('data', (chunk: string) => handler(chunk, 'stderr'));\n },\n onExit(handler: (result: ExitResult) => void): void {\n let finished = false;\n let drainTimer: ReturnType<typeof setTimeout> | undefined;\n const finish = (result: ExitResult): void => {\n if (finished) return;\n finished = true;\n if (drainTimer) clearTimeout(drainTimer);\n handler(result);\n };\n child.once('exit', (code, signal) => {\n // Descendants can keep inherited stdout/stderr open after the\n // supervised command exits. `close` would then wait for them too.\n drainTimer = setTimeout(() => finish({ code, signal }), EXIT_OUTPUT_DRAIN_MS);\n });\n child.once('close', (code, signal) => finish({ code, signal }));\n },\n onError(handler: (error: Error) => void): void {\n child.on('error', handler);\n },\n kill(signal?: NodeJS.Signals): void {\n child.kill(signal);\n },\n unref(): void {\n child.unref();\n },\n };\n }\n}\n","import { execFile } from 'node:child_process';\n\nimport type { ITmuxClient, TmuxResult } from '../../types/tmuxClient';\n\nconst TMUX_BINARY = 'tmux';\nconst SOCKET_OPTION = '-L';\n/** Long enough for a loaded server, short enough that a hung tmux is not fatal. */\nconst COMMAND_TIMEOUT_MS = 10_000;\nconst MAX_OUTPUT_BYTES = 4 * 1024 * 1024;\n\nexport class TmuxClient implements ITmuxClient {\n constructor(\n private readonly socket: string,\n private readonly binary = TMUX_BINARY,\n ) {}\n\n run(args: readonly string[]): Promise<TmuxResult> {\n return new Promise((resolve, reject) => {\n execFile(\n this.binary,\n [SOCKET_OPTION, this.socket, ...args],\n { timeout: COMMAND_TIMEOUT_MS, maxBuffer: MAX_OUTPUT_BYTES, encoding: 'utf8' },\n (error, stdout, stderr) => {\n // A non-zero tmux exit is an answer, not a fault: `has-session`\n // reports absence that way. Only a missing or hung binary rejects.\n const code = (error as (Error & { code?: number | string }) | null)?.code;\n if (error && typeof code !== 'number') {\n reject(error);\n return;\n }\n resolve({ returnCode: typeof code === 'number' ? code : 0, stdout, stderr });\n },\n );\n });\n }\n\n async format(target: string, format: string): Promise<string | undefined> {\n const result = await this.run(['display-message', '-p', '-t', target, '-F', format]).catch(() => undefined);\n if (!result || result.returnCode !== 0) return undefined;\n return result.stdout.replace(/\\n$/, '');\n }\n\n async sessionMissing(target: string): Promise<boolean> {\n const result = await this.run(['has-session', '-t', target]).catch(() => undefined);\n return !result || result.returnCode !== 0;\n }\n}\n","import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n LOG_DRAIN_POLL_MS,\n LOG_DRAIN_TIMEOUT_MS,\n NON_INTERACTIVE_ENV,\n OWNED_TARGET_PATTERN,\n PANE_STATE_FORMAT,\n POLL_MS,\n SESSION_PREFIX,\n SOCKET_HASH_LENGTH,\n SOCKET_PREFIX,\n STOP_GRACE_MS,\n} from '../../constants/tmuxBackend';\nimport type { RunHandle } from '../../types/launcher';\nimport type { PtyRun } from '../../types/ptyHost';\nimport type { IRmuxBackend, RmuxLaunchRequest } from '../../types/rmuxBackend';\nimport type { ExitResult } from '../../types/spawner';\nimport type { ITmuxClient } from '../../types/tmuxClient';\nimport { getLogMaxBytes, getResultMaxBytes } from '../runnerConfig';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport {\n type CommandSpec,\n type SupervisorPaths,\n cleanupSupervisorFiles,\n NO_TERMINAL_INPUT_ENV,\n readExitMetadata,\n runtimeEntry,\n shellJoin,\n supervisorPaths,\n writeCommandSpec,\n} from '../runnerSupervisor';\nimport { TmuxClient } from './TmuxClient';\n\n/** Pane liveness, its exit status, and whether the session still exists, in one read. */\n/** Distinct from the RMUX prefix so a target names the backend that owns it. */\n\n/**\n * Supervises runs in tmux panes, for hosts with no compatible RMUX binary.\n *\n * The pane runs the same runnerHost supervisor as the RMUX backend, so exit\n * metadata, log rotation, and lifeline handling are shared rather than\n * reimplemented: only the multiplexer differs. Kept beside the RMUX backend\n * rather than folded into it because that path has no unit coverage to\n * refactor against.\n */\nexport class TmuxBackend implements IRmuxBackend {\n private clientPromise: Promise<ITmuxClient | undefined> | undefined;\n private readonly runs = new Map<string, PtyRun>();\n private readonly completions = new Map<string, Promise<ExitResult>>();\n\n constructor(\n private readonly paths: IRunnerPaths,\n private readonly createClient: (socket: string) => ITmuxClient = (socket) => new TmuxClient(socket),\n ) {}\n\n async launch(request: RmuxLaunchRequest): Promise<RunHandle | undefined> {\n const tmux = await this.client();\n if (!tmux) return undefined;\n\n this.paths.ensureDirectories();\n const target = `${SESSION_PREFIX}${request.id}`;\n const logPath = this.paths.logPathFor(request.id);\n const aux = this.auxPaths(request.id, request.sessionId);\n let created = false;\n let released = false;\n\n try {\n this.prepareFiles(request, logPath, aux);\n // One quoted command rather than an argv: tmux takes shell-command as a\n // single word, and quoting here is what keeps a path with spaces intact.\n const supervisor = shellJoin([process.execPath, runtimeEntry('runnerHost'), aux.spec, aux.gate, aux.exit]);\n await this.checked(tmux, ['new-session', '-d', '-s', target, '-c', request.cwd, supervisor]);\n created = true;\n // Set before the gate opens, so it is in force by the time the command\n // can exit. A pane that vanishes on exit takes the log sink down with it\n // before Node has finished starting, losing the output of fast commands,\n // and leaves #{pane_dead} with nothing to report.\n await this.checked(tmux, ['set-option', '-t', target, 'remain-on-exit', 'on']);\n await this.checked(tmux, ['pipe-pane', '-t', target, this.logPipeCommand(logPath, aux.logDone, request)]);\n\n const pid = await panePid(tmux, target);\n fs.writeFileSync(aux.gate, '', { mode: 0o600 });\n released = true;\n\n let buffering = true;\n const completion = this.monitor(tmux, target, aux);\n this.trackCompletion(target, completion);\n const handle: RunHandle = {\n id: request.id,\n name: request.name,\n pid,\n logPath,\n backend: 'tmux',\n backendTarget: target,\n output: () => (buffering ? readTail(logPath, getResultMaxBytes() * 2) : ''),\n completion: () => completion,\n detach: () => {\n buffering = false;\n },\n stop: () => this.stop(target, pid),\n };\n\n if (request.interactive) this.runs.set(request.name, this.interactiveRun(handle, tmux, target, completion));\n void completion\n .finally(() => this.runs.delete(request.name))\n .catch((error: unknown) => process.emitWarning(`tmux completion failed for ${target}: ${errorMessage(error)}`));\n return handle;\n } catch (error) {\n if (released) throw error;\n if (created) await killSession(tmux, target);\n cleanupSupervisorFiles(aux);\n process.emitWarning(\n request.interactive\n ? `tmux launch unavailable for interactive runner: ${errorMessage(error)}`\n : `tmux launch unavailable, using supervised subprocess: ${errorMessage(error)}`,\n );\n return undefined;\n }\n }\n\n async stop(target: string, expectedPid: number): Promise<boolean> {\n if (!OWNED_TARGET_PATTERN.test(target) || !Number.isInteger(expectedPid) || expectedPid <= 0) return false;\n\n const tmux = await this.client();\n if (!tmux) return false;\n try {\n if (await tmux.sessionMissing(target)) return true;\n\n const pid = await panePid(tmux, target);\n if (pid !== expectedPid) return false;\n const signalled = signalPane(pid, target);\n if (signalled === 'gone') return true;\n if (signalled === 'failed') return false;\n\n if (await waitForPaneStop(tmux, target, STOP_GRACE_MS)) return true;\n if (await tmux.sessionMissing(target)) return true;\n\n // Revalidate after the grace period so a replacement pane is never closed.\n const currentPid = await panePid(tmux, target).catch(() => undefined);\n if (currentPid !== expectedPid) return false;\n\n return killSession(tmux, target);\n } catch (error) {\n process.emitWarning(`Could not stop tmux target ${target}: ${errorMessage(error)}`);\n return false;\n }\n }\n\n async watch(id: string, target: string, sessionId?: string): Promise<ExitResult | undefined> {\n const tmux = await this.client();\n if (!tmux) return undefined;\n if (await tmux.sessionMissing(target)) return undefined;\n const completion = this.monitor(tmux, target, this.auxPaths(id, sessionId));\n this.trackCompletion(target, completion);\n return completion;\n }\n\n async input(target: string, text: string): Promise<boolean> {\n const tmux = await this.client();\n if (!tmux) return false;\n const result = await tmux.run(['send-keys', '-t', target, '-l', text]).catch((error: unknown) => {\n process.emitWarning(`Could not send input to tmux target ${target}: ${errorMessage(error)}`);\n return undefined;\n });\n return result?.returnCode === 0;\n }\n\n async capture(target: string): Promise<string | undefined> {\n const tmux = await this.client();\n if (!tmux) return undefined;\n const result = await tmux.run(['capture-pane', '-p', '-t', target]).catch((error: unknown) => {\n process.emitWarning(`Could not capture tmux target ${target}: ${errorMessage(error)}`);\n return undefined;\n });\n return result?.returnCode === 0 ? result.stdout : undefined;\n }\n\n get(name: string): PtyRun | undefined {\n return this.runs.get(name);\n }\n\n readOutcome(id: string, sessionId: string): ExitResult | undefined {\n return readExitMetadata(this.auxPaths(id, sessionId).exit);\n }\n\n private trackCompletion(target: string, completion: Promise<ExitResult>): void {\n this.completions.set(target, completion);\n void completion\n .finally(() => this.completions.delete(target))\n .catch((error: unknown) => process.emitWarning(`tmux monitor failed for ${target}: ${errorMessage(error)}`));\n }\n\n private client(): Promise<ITmuxClient | undefined> {\n this.clientPromise ??= this.resolveClient();\n return this.clientPromise;\n }\n\n private async resolveClient(): Promise<ITmuxClient | undefined> {\n const client = this.createClient(socketName(this.paths.repositoryPath()));\n try {\n const version = await client.run(['-V']);\n if (version.returnCode !== 0) throw new Error(version.stderr.trim() || 'tmux reported no version');\n return client;\n } catch (error) {\n process.emitWarning(`tmux is unavailable: ${errorMessage(error)}`);\n return undefined;\n }\n }\n\n private async checked(tmux: ITmuxClient, args: readonly string[]): Promise<void> {\n const result = await tmux.run(args);\n if (result.returnCode !== 0) {\n throw new Error(`tmux ${args[0] ?? ''} failed: ${result.stderr.trim() || `exit ${result.returnCode}`}`);\n }\n }\n\n private prepareFiles(request: RmuxLaunchRequest, logPath: string, aux: SupervisorPaths): void {\n cleanupSupervisorFiles(aux);\n fs.writeFileSync(logPath, '', { mode: 0o600 });\n const env = environment(request.sessionId, request.interactive);\n const spec: CommandSpec = { command: request.command, cwd: request.cwd, env };\n writeCommandSpec(aux.spec, spec);\n }\n\n private auxPaths(id: string, sessionId?: string): SupervisorPaths {\n return supervisorPaths(this.paths.stateDirectory(sessionId), id);\n }\n\n /**\n * The sink the pane pipes into.\n *\n * An interactive run also asks for a raw copy: the scrubbed log it writes\n * beside it has had cursor movement taken out, which is what a terminal\n * needs most. Only interactive runs pay for it, because only they can be\n * attached to.\n */\n private logPipeCommand(logPath: string, donePath: string, request: RmuxLaunchRequest): string {\n const id = path.basename(logPath, '.log');\n return shellJoin([\n process.execPath,\n runtimeEntry('logSink'),\n logPath,\n this.paths.rotatedLogPathFor(id),\n String(getLogMaxBytes()),\n donePath,\n ...(request.interactive ? [this.paths.rawLogPathFor(id)] : []),\n ]);\n }\n\n private async monitor(tmux: ITmuxClient, target: string, aux: SupervisorPaths): Promise<ExitResult> {\n try {\n const paneCode = await waitForRunEnd(tmux, target, aux.exit);\n await waitForLogDrain(tmux, target, aux.logDone);\n return readExitMetadata(aux.exit) ?? { code: paneCode, signal: null };\n } finally {\n if (!(await tmux.sessionMissing(target).catch(() => false))) await killSession(tmux, target);\n cleanupSupervisorFiles(aux);\n }\n }\n\n private interactiveRun(\n handle: RunHandle,\n tmux: ITmuxClient,\n target: string,\n completion: Promise<ExitResult>,\n ): PtyRun {\n let screen = '';\n let active = true;\n const handlers = new Set<(data: string) => void>();\n const refresh = async (): Promise<void> => {\n while (active) {\n const captured = await tmux.run(['capture-pane', '-p', '-t', target]).catch((error: unknown) => {\n process.emitWarning(`Could not capture tmux screen for ${handle.name}: ${errorMessage(error)}`);\n return undefined;\n });\n const next = captured?.returnCode === 0 ? captured.stdout : screen;\n if (next !== screen) {\n screen = next;\n for (const handler of handlers) handler(next);\n }\n await delay(POLL_MS);\n }\n };\n void refresh();\n void completion\n .finally(() => {\n active = false;\n })\n .catch((error: unknown) =>\n process.emitWarning(`tmux interactive completion failed for ${handle.name}: ${errorMessage(error)}`),\n );\n return {\n ...handle,\n write: (text) => {\n void tmux\n .run(['send-keys', '-t', target, '-l', text])\n .catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n screen: () => screen,\n onData: (handler) => {\n handlers.add(handler);\n return () => handlers.delete(handler);\n },\n resize: (cols, rows) => {\n void tmux\n .run(['resize-window', '-t', target, '-x', String(cols), '-y', String(rows)])\n .catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n };\n }\n}\n\nfunction socketName(repositoryPath: string): string {\n const hash = createHash('sha256').update(repositoryPath).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return `${SOCKET_PREFIX}${hash}`;\n}\n\nfunction environment(sessionId: string, interactive: boolean): Record<string, string> {\n const values: NodeJS.ProcessEnv = {\n ...process.env,\n ...(interactive ? {} : NON_INTERACTIVE_ENV),\n ...NO_TERMINAL_INPUT_ENV,\n PI_SESSION_ID: sessionId,\n };\n if (!interactive) delete values['FORCE_COLOR'];\n return Object.fromEntries(\n Object.entries(values).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n}\n\nasync function killSession(tmux: ITmuxClient, target: string): Promise<boolean> {\n const result = await tmux.run(['kill-session', '-t', target]).catch((error: unknown) => {\n process.emitWarning(`Could not close tmux target ${target}: ${errorMessage(error)}`);\n return undefined;\n });\n return result?.returnCode === 0;\n}\n\nasync function panePid(tmux: ITmuxClient, target: string): Promise<number> {\n const message = await tmux.format(target, '#{pane_pid}');\n const pid = Number(message);\n if (!Number.isInteger(pid) || pid <= 0) throw new Error(`tmux did not report a pane pid for ${target}`);\n return pid;\n}\n\n/**\n * Waits for the run to end, preferring the supervisor's own record of it.\n *\n * The sidecar is written before the pane can end and outlives the server the\n * pane's exit takes down, so asking tmux is the fallback for a supervisor that\n * died without recording anything.\n */\nasync function waitForRunEnd(tmux: ITmuxClient, target: string, exitPath: string): Promise<number | null> {\n for (;;) {\n if (fs.existsSync(exitPath)) return null;\n const state = await readPaneExit(tmux, target);\n if (state.exited) return state.code;\n await delay(POLL_MS);\n }\n}\n\nasync function waitForLogDrain(tmux: ITmuxClient, target: string, donePath: string): Promise<void> {\n const deadline = Date.now() + LOG_DRAIN_TIMEOUT_MS;\n while (Date.now() < deadline) {\n if (fs.existsSync(donePath)) return;\n if (await tmux.sessionMissing(target)) return;\n await delay(LOG_DRAIN_POLL_MS);\n }\n}\n\nasync function waitForPaneStop(tmux: ITmuxClient, target: string, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if ((await readPaneExit(tmux, target)).exited) return true;\n await delay(POLL_MS);\n }\n return false;\n}\n\n/** One formatted read answers both questions this poll asks. */\nasync function readPaneExit(tmux: ITmuxClient, target: string): Promise<{ exited: boolean; code: number | null }> {\n const message = await tmux.format(target, PANE_STATE_FORMAT);\n // An unreachable server is the ordinary end of the last run: the pane exiting\n // ends its session, and the last session ending stops the server.\n if (message === undefined) return { exited: true, code: null };\n const [dead, status, ...rest] = message.trim().split(':');\n if (rest.join(':') === '') return { exited: true, code: null };\n if (dead === '1' || dead === 'true') {\n const code = Number(status);\n return { exited: true, code: Number.isInteger(code) ? code : null };\n }\n return { exited: false, code: null };\n}\n\nfunction signalPane(pid: number, target: string): 'signalled' | 'gone' | 'failed' {\n try {\n process.kill(pid, 'SIGTERM');\n return 'signalled';\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ESRCH') return 'gone';\n process.emitWarning(`Could not signal tmux target ${target}: ${errorMessage(error)}`);\n return 'failed';\n }\n}\n\nfunction readTail(target: string, maxBytes: number): string {\n let handle: number | undefined;\n try {\n const size = fs.statSync(target).size;\n const bytes = Math.min(size, maxBytes);\n const buffer = Buffer.alloc(bytes);\n handle = fs.openSync(target, 'r');\n fs.readSync(handle, buffer, 0, bytes, size - bytes);\n return buffer.toString('utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n process.emitWarning(`Could not read tmux log ${target}: ${errorMessage(error)}`);\n }\n return '';\n } finally {\n if (handle !== undefined) fs.closeSync(handle);\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { BashRunService } from '../bashRunService';\nimport { SystemClock } from '../clock';\nimport { Launcher } from '../launcher';\nimport { NodeLifeline } from '../lifeline';\nimport { LogFile } from '../logFile';\nimport { LogReader } from '../logReader';\nimport { NodeProcessControl } from '../processControl';\nimport { PtyBackendChain } from '../ptyBackendChain';\nimport { PtyHost } from '../ptyHost';\nimport { NodePtySpawner } from '../ptySpawner';\nimport { RmuxBackend } from '../rmuxBackend';\nimport { RtkProcessor } from '../rtkProcessor';\nimport { RunnerNamer } from '../runnerNamer';\nimport { RunnerPaths } from '../runnerPaths';\nimport { RunnerRegistry, createDefaultProcessRegistry } from '../runnerRegistry';\nimport { NodeSpawner } from '../spawner';\nimport { TmuxBackend } from '../tmuxBackend';\nimport type { RunnerDependencies } from './type';\n/** Build once on first access, then hand back the same instance. */\nfunction memoize<T>(build: () => T): () => T {\n let value: T | undefined;\n let built = false;\n return () => {\n if (!built) {\n value = build();\n built = true;\n }\n return value as T;\n };\n}\n\n/**\n * Builds the extension's service graph.\n *\n * A factory rather than a module-level singleton: the extension owns one graph\n * per session, and tests get a fresh one they can substitute ports on through\n * `overrides` without leaking state between cases.\n *\n * Every slot resolves lazily and memoizes, which keeps the previous container's\n * behaviour: nothing is constructed until it is asked for, so opening the SQLite\n * process registry stays deferred until something actually needs a runner.\n */\nexport function createRunnerDependencies(\n overrides: Partial<RunnerDependencies> & {\n environment: Readonly<Record<string, string | undefined>>;\n },\n): RunnerDependencies {\n const environment = overrides.environment;\n const clock = memoize(() => overrides.clock ?? new SystemClock());\n const spawner = memoize(() => overrides.spawner ?? new NodeSpawner());\n const processControl = memoize(() => overrides.processControl ?? new NodeProcessControl());\n const ptySpawner = memoize(() => overrides.ptySpawner ?? new NodePtySpawner());\n const paths = memoize(() => overrides.paths ?? new RunnerPaths());\n const lifeline = memoize(() => overrides.lifeline ?? new NodeLifeline(paths()));\n const processRegistry = memoize(() => overrides.processRegistry ?? createDefaultProcessRegistry());\n const runnerRegistry = memoize(\n () => overrides.runnerRegistry ?? new RunnerRegistry(paths(), processControl(), processRegistry(), environment),\n );\n const namer = memoize(() => overrides.namer ?? new RunnerNamer(runnerRegistry()));\n const logFile = memoize(() => overrides.logFile ?? new LogFile(paths()));\n const logReader = memoize(() => overrides.logReader ?? new LogReader());\n const launcher = memoize(\n () => overrides.launcher ?? new Launcher(spawner(), processControl(), logFile(), clock(), paths()),\n );\n const rmuxBackend = memoize(\n () => overrides.rmuxBackend ?? new PtyBackendChain(new RmuxBackend(paths()), new TmuxBackend(paths())),\n );\n const rtkProcessor = memoize(() => overrides.rtkProcessor ?? new RtkProcessor());\n const ptyHost = memoize(() => overrides.ptyHost ?? new PtyHost(ptySpawner(), logFile(), processControl(), clock()));\n const bashRunService = memoize(\n () =>\n overrides.bashRunService ??\n new BashRunService(launcher(), rmuxBackend(), namer(), runnerRegistry(), clock(), rtkProcessor()),\n );\n\n return {\n environment,\n get clock() {\n return clock();\n },\n get spawner() {\n return spawner();\n },\n get processControl() {\n return processControl();\n },\n get ptySpawner() {\n return ptySpawner();\n },\n get paths() {\n return paths();\n },\n get lifeline() {\n return lifeline();\n },\n get processRegistry() {\n return processRegistry();\n },\n get runnerRegistry() {\n return runnerRegistry();\n },\n get namer() {\n return namer();\n },\n get logFile() {\n return logFile();\n },\n get logReader() {\n return logReader();\n },\n get launcher() {\n return launcher();\n },\n get rmuxBackend() {\n return rmuxBackend();\n },\n get rtkProcessor() {\n return rtkProcessor();\n },\n get ptyHost() {\n return ptyHost();\n },\n get bashRunService() {\n return bashRunService();\n },\n };\n}\n","import { defineMcpTool } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { createHeadlessBashTool } from '../../../../../services/headless';\nimport { summarizeLog } from '../../../../../services/logReader';\nimport { createRunnerDependencies } from '../../../../../services/runnerDependencies';\n\nexport default defineMcpTool(() => {\n const dependencies = createRunnerDependencies({ environment: Object.freeze({ ...process.env }) });\n return createHeadlessBashTool({ run: (request) => dependencies.bashRunService.run(request) }, summarizeLog);\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport skillDoompiUseRunner from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-use-runner.mcp';\nimport toolBash from '../src/extensions/workspaces/sessions/(backend)/tool/bash.mcp';\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 => ({ ...identity, ...(value as object) }) as T;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-runner',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [at(skillDoompiUseRunner, context)]; },\n get tools(): DoomMcpSessionPlugin['tools'] { return [via({ name: 'bash' }, at(toolBash, context))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp.cjs","names":["defineMcpSkill","readPackageResource","Type","headless","promisify","execFile","defineMcpTool","defineMcpPlugin","skillDoompiUseRunner","toolBash"],"sources":["../../src/extensions/workspaces/sessions/(backend)/skill/doompi-use-runner.mcp.ts","../../src/schemas/bashTool.ts","../../src/services/ansiScrub/index.ts","../../src/services/ptyHost/index.ts","../../src/services/runnerRegistry/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/tool/bash.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { defineMcpSkill } from '@agimon-ai/doompi-core/mcp-facet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/server-facet';\n\nexport default defineMcpSkill({\n name: 'doompi-use-runner',\n description:\n 'Use Doom Pi Runner to supervise shell commands, inspect durable logs, provide interactive input, and stop background runs.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-runner/SKILL.md'),\n});\n","import { type Static, Type } from 'typebox';\n\n/** Replaces pi's built-in tool of the same name, so hooks keyed on `bash` keep working. */\n\n/**\n * Built-in bash parameters plus the backgrounding controls.\n *\n * `command` and `timeout` keep their original names and wording so the tool is\n * a drop-in replacement. Every `description` is prompt copy the model reads.\n */\nexport const BashParamsSchema = Type.Object({\n command: Type.String({ description: 'Bash command to execute' }),\n timeout: Type.Optional(Type.Number({ description: 'Timeout in seconds (optional, no default timeout)' })),\n background: Type.Optional(\n Type.Boolean({\n description:\n 'Start the command in the background immediately instead of waiting. Use for dev servers, watchers and other commands you already know will not finish.',\n }),\n ),\n interactive: Type.Optional(\n Type.Boolean({\n description:\n 'Run under a pseudo terminal so the command can prompt. Use only when the command asks for confirmation or input; answer it with doom-runner input. Logs from interactive runs are noisier.',\n }),\n ),\n name: Type.Optional(\n Type.String({\n description: 'Preferred runner name if the command ends up in the background. A name in use gets a suffix.',\n }),\n ),\n});\n\nexport type BashParams = Static<typeof BashParamsSchema>;\n","import { CONTROL_PATTERN } from '../../constants/ansiScrub';\n/**\n * Turns terminal output into text worth grepping.\n *\n * PTY output is written for a screen, not a file: colours, cursor moves and\n * carriage-return redraws all end up in the byte stream. Without this a single\n * progress bar becomes thousands of near-identical log lines.\n */\n\n/**\n * CSI sequences (colours, cursor moves), OSC strings, the two-byte Fe escapes,\n * and the keypad-mode escapes shells emit around every prompt.\n */\nconst ANSI_PATTERN = new RegExp(\n [\n '\\\\u001B\\\\[[0-9;?]*[ -/]*[@-~]',\n '\\\\u001B\\\\][^\\\\u0007\\\\u001B]*(?:\\\\u0007|\\\\u001B\\\\\\\\)',\n '\\\\u001B[@-Z\\\\\\\\-_]',\n '\\\\u001B[=><]',\n ].join('|'),\n 'g',\n);\n\n/** Bell and backspace, which a terminal consumes rather than displays. */\n// oxlint-disable-next-line no-control-regex -- matching terminal control bytes is the whole job\n\n/**\n * The same set minus SGR, the sequence that only sets colour and weight.\n *\n * The final byte class omits `m`, so `ESC[32m` survives while cursor moves,\n * OSC strings and keypad escapes are still removed.\n */\nconst ANSI_PATTERN_KEEPING_COLOUR = new RegExp(\n [\n '\\\\u001B\\\\[[0-9;?]*[ -/]*[@-ln-~]',\n '\\\\u001B\\\\][^\\\\u0007\\\\u001B]*(?:\\\\u0007|\\\\u001B\\\\\\\\)',\n '\\\\u001B[@-Z\\\\\\\\-_]',\n '\\\\u001B[=><]',\n ].join('|'),\n 'g',\n);\n\n/** Removes escape sequences, leaving the characters a user would have seen. */\nexport function stripAnsi(text: string): string {\n return text.replace(ANSI_PATTERN, '').replace(CONTROL_PATTERN, '');\n}\n\n/**\n * Removes everything `stripAnsi` does except colour.\n *\n * Logs are read back by a terminal renderer as well as by grep, and a command\n * that colours its own output has already said how it should look.\n */\nexport function stripAnsiKeepingColour(text: string): string {\n return text.replace(ANSI_PATTERN_KEEPING_COLOUR, '').replace(CONTROL_PATTERN, '');\n}\n\n/**\n * Collapses carriage-return redraws.\n *\n * A bare `\\r` returns the cursor to the start of the current line, so whatever\n * follows overwrites it. Only the final state of each line is kept. The `\\r` of\n * a CRLF pair is a line terminator rather than a redraw, so it is normalised\n * away first: a pseudo terminal ends every line that way, and treating it as a\n * redraw would blank the whole log.\n */\nexport function collapseCarriageReturns(text: string): string {\n return text\n .replace(/\\r\\n/g, '\\n')\n .split('\\n')\n .map((line) => (line.includes('\\r') ? (line.split('\\r').at(-1) ?? '') : line))\n .join('\\n');\n}\n\n/** Full scrub applied to PTY output before it reaches the log file. */\nexport function scrubTerminalOutput(text: string): string {\n return collapseCarriageReturns(stripAnsiKeepingColour(text));\n}\n\n/** Plain-text scrub, for the copy the model reads. */\nexport function scrubTerminalOutputToPlainText(text: string): string {\n return collapseCarriageReturns(stripAnsi(text));\n}\n","import headless, { type Terminal } from '@xterm/headless';\n\nimport {\n DEFAULT_COLS,\n DEFAULT_ROWS,\n LIVENESS_POLL_MS,\n MEMORY_BUFFER_FACTOR,\n NEWLINE,\n SCROLLBACK,\n TERM_GRACE_MS,\n} from '../../constants/ptyHost';\nimport { PI_SESSION_ID_ENV } from '../../constants/session';\nimport type { IClock } from '../../types/clock';\nimport type { ILogFile } from '../../types/logFile';\nimport type { IProcessControl } from '../../types/processControl';\nimport type { IPtyHost, PtyLaunchRequest, PtyRun } from '../../types/ptyHost';\nimport type { IPtySpawner, PtyProcess } from '../../types/ptySpawner';\nimport type { ExitResult } from '../../types/spawner';\nimport { scrubTerminalOutput } from '../ansiScrub';\nimport { getResultMaxBytes } from '../runnerConfig';\nimport { NO_TERMINAL_INPUT_ENV } from '../runnerSupervisor';\n\n/** Enough history for the overlay to scroll without holding a whole build. */\n\n// Use the CommonJS default export: Node cannot detect this package's named exports.\n// A static import also lets server bundles include it without a live node_modules lookup.\nconst { Terminal: HeadlessTerminal } = headless;\n\nexport class PtyHost implements IPtyHost {\n private readonly runs = new Map<string, PtyRun>();\n\n constructor(\n private readonly spawner: IPtySpawner,\n private readonly logFile: ILogFile,\n private readonly processControl: IProcessControl,\n private readonly clock: IClock,\n ) {}\n\n async launch(request: PtyLaunchRequest): Promise<PtyRun> {\n const cols = request.cols ?? DEFAULT_COLS;\n const rows = request.rows ?? DEFAULT_ROWS;\n const writer = this.logFile.open(request.id);\n const terminal = new HeadlessTerminal({ cols, rows, scrollback: SCROLLBACK, allowProposedApi: true });\n const memoryLimit = getResultMaxBytes() * MEMORY_BUFFER_FACTOR;\n\n const child = await this.spawner.spawn({\n command: request.command,\n cwd: request.cwd,\n env: { ...process.env, ...NO_TERMINAL_INPUT_ENV, [PI_SESSION_ID_ENV]: request.sessionId },\n cols,\n rows,\n });\n\n let buffer = '';\n let buffering = true;\n let settled = false;\n let resolveCompletion: (result: ExitResult) => void = () => undefined;\n const completion = new Promise<ExitResult>((resolve) => {\n resolveCompletion = resolve;\n });\n const dataHandlers = new Set<(data: string) => void>();\n\n child.onData((data) => {\n // The terminal keeps the screen; the log keeps text worth grepping.\n terminal.write(data);\n const scrubbed = scrubTerminalOutput(data);\n writer.append(scrubbed);\n if (buffering) {\n buffer += scrubbed;\n if (buffer.length > memoryLimit) buffer = buffer.slice(-memoryLimit);\n }\n for (const handler of dataHandlers) handler(data);\n });\n\n child.onExit((result) => {\n if (settled) return;\n settled = true;\n this.runs.delete(request.name);\n writer.close();\n resolveCompletion({ code: result.exitCode, signal: null });\n });\n\n const run: PtyRun = {\n id: request.id,\n name: request.name,\n pid: child.pid,\n logPath: writer.path,\n backend: 'native',\n output: () => buffer,\n completion: () => completion,\n detach: () => {\n buffering = false;\n buffer = '';\n },\n stop: () => this.stopChild(child, request.name),\n write: (text) => child.write(text),\n screen: () => renderScreen(terminal),\n onData: (handler) => {\n dataHandlers.add(handler);\n return () => dataHandlers.delete(handler);\n },\n resize: (nextCols, nextRows) => {\n child.resize(nextCols, nextRows);\n terminal.resize(nextCols, nextRows);\n },\n };\n\n this.runs.set(request.name, run);\n return run;\n }\n\n get(name: string): PtyRun | undefined {\n return this.runs.get(name);\n }\n\n write(name: string, text: string): boolean {\n const run = this.runs.get(name);\n if (!run) return false;\n run.write(text.endsWith(NEWLINE) ? text : `${text}${NEWLINE}`);\n return true;\n }\n\n list(): PtyRun[] {\n return [...this.runs.values()];\n }\n\n async disposeAll(): Promise<void> {\n const runs = [...this.runs.values()];\n this.runs.clear();\n for (const run of runs) await run.stop();\n }\n\n private async stopChild(child: PtyProcess, name: string): Promise<boolean> {\n this.runs.delete(name);\n if (!this.processControl.isAlive(child.pid)) return false;\n\n child.kill('SIGTERM');\n const deadline = this.clock.now() + TERM_GRACE_MS;\n while (this.clock.now() < deadline) {\n if (!this.processControl.isAlive(child.pid)) return true;\n await new Promise<void>((resolve) => {\n this.clock.after(LIVENESS_POLL_MS, resolve);\n });\n }\n child.kill('SIGKILL');\n return true;\n }\n}\n\n/** The visible rows, trimmed of trailing blanks, as plain text. */\nfunction renderScreen(terminal: Terminal): string {\n const buffer = terminal.buffer.active;\n const lines: string[] = [];\n for (let row = buffer.baseY; row < buffer.baseY + terminal.rows; row += 1) {\n lines.push(buffer.getLine(row)?.translateToString(true) ?? '');\n }\n while (lines.length > 0 && lines.at(-1) === '') lines.pop();\n return lines.join(NEWLINE);\n}\n","import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\n\nimport { resolveRootSessionId } from '@agimon-ai/doompi-core/child-process';\nimport {\n createProcessRegistryService,\n normalizeRepositoryPath,\n type ProcessRegistryRecord,\n type ProcessRegistryService,\n resolveProcessTags,\n} from '@agimon-ai/foundation-process-registry';\n\nimport {\n COMMAND_SIDECAR_SUFFIX,\n EXIT_SIDECAR_SUFFIX,\n JSON_EXTENSION,\n NOT_FOUND_ERROR_CODE,\n REGISTRY_PATH_ENV,\n RUNNER_TAG,\n SERVICE_PREFIX,\n SERVICE_TYPE,\n} from '../../constants/runnerRegistry';\nimport type { IProcessControl } from '../../types/processControl';\nimport type {\n CompleteRunnerInput,\n IRunnerRegistry,\n RegisterRunnerInput,\n RunnerRecord,\n} from '../../types/runnerRegistry';\nimport type { IRunnerPaths } from '../runnerPaths/type';\nimport { isRunnerRecord } from '../runnerRecord';\n\n/** The slice of the shared registry service this wrapper depends on. */\nexport type ProcessRegistryPort = Pick<\n ProcessRegistryService,\n 'registerProcess' | 'releaseProcess' | 'listProcesses' | 'close'\n>;\n\n/** Opens the SQLite-backed registry the rest of the monorepo already shares. */\nexport function createDefaultProcessRegistry(): ProcessRegistryPort {\n return createProcessRegistryService(process.env[REGISTRY_PATH_ENV]);\n}\n\nfunction sessionTag(sessionId: string): string {\n return `session:${sessionId}`;\n}\n\nfunction readString(record: ProcessRegistryRecord, key: string): string {\n const value = record.metadata?.[key];\n return typeof value === 'string' ? value : '';\n}\n\nfunction readNumber(record: ProcessRegistryRecord, key: string): number {\n const value = record.metadata?.[key];\n return typeof value === 'number' ? value : 0;\n}\n\nconst runCommand = promisify(execFile);\n\n/**\n * The process start time the registry stores to prove a pid is still the\n * process it registered.\n *\n * The registry resolves it itself when the caller omits it, and it does so with\n * `execFileSync`, which stops this process for the length of a `ps` on every\n * runner that starts. Reading it here costs the same subprocess and blocks\n * nothing: the command is already running by the time a runner is registered.\n *\n * The value has to match what the registry would have read, so the command and\n * the trimming are deliberately identical to its own.\n */\nasync function processStartedAt(pid: number): Promise<string | undefined> {\n if (process.platform === 'win32') return undefined;\n try {\n const { stdout } = await runCommand('ps', ['-o', 'lstart=', '-p', String(pid)], { encoding: 'utf8' });\n return stdout.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\ntype ListenerRegistration = Readonly<{ key: string; listener: () => void }>;\n\n// Facet replacement can leave an in-flight tool and its publisher on different registry instances.\nconst listenersBySession = new Map<string, Set<() => void>>();\n\nexport class RunnerRegistry implements IRunnerRegistry {\n private readonly subscriptions = new Set<ListenerRegistration>();\n\n constructor(\n private readonly paths: IRunnerPaths,\n private readonly processControl: IProcessControl,\n private readonly registry: ProcessRegistryPort,\n private readonly environment: Readonly<Record<string, string | undefined>>,\n ) {}\n\n async register(input: RegisterRunnerInput): Promise<RunnerRecord> {\n const startedAt = new Date().toISOString();\n const rootSessionId = resolveRootSessionId(input.sessionId, this.environment);\n const identity = await processStartedAt(input.pid);\n const response = await this.registry.registerProcess({\n repositoryPath: this.repositoryPath(),\n serviceName: `${SERVICE_PREFIX}${input.id}`,\n serviceType: SERVICE_TYPE,\n pid: input.pid,\n command: input.command,\n ...(identity === undefined ? {} : { processStartedAt: identity }),\n tags: resolveProcessTags([RUNNER_TAG, `runner:${input.id}`, sessionTag(input.sessionId)]),\n metadata: {\n id: input.id,\n name: input.name,\n command: input.command,\n cwd: input.cwd,\n logPath: input.logPath,\n interactive: input.interactive,\n sessionId: input.sessionId,\n rootSessionId,\n backend: input.backend,\n ...(input.backendTarget ? { backendTarget: input.backendTarget } : {}),\n startedAt,\n hostPid: process.pid,\n },\n // The caller has already resolved name collisions against live records,\n // so anything still holding this name is stale.\n force: true,\n });\n if (!response.success) throw new Error(response.error ?? `Failed to register runner ${input.name}`);\n const record: RunnerRecord = {\n id: input.id,\n name: input.name,\n pid: input.pid,\n command: input.command,\n cwd: input.cwd,\n logPath: input.logPath,\n interactive: input.interactive,\n sessionId: input.sessionId,\n rootSessionId,\n startedAt,\n state: 'running',\n promoted: false,\n backend: input.backend,\n ...(input.backendTarget ? { backendTarget: input.backendTarget } : {}),\n hostPid: process.pid,\n };\n this.writeRecord(record);\n this.notify(record.sessionId);\n return record;\n }\n\n async list(): Promise<RunnerRecord[]> {\n const records = await this.registry.listProcesses({\n repositoryPath: this.repositoryPath(),\n serviceType: SERVICE_TYPE,\n tags: resolveProcessTags([RUNNER_TAG]),\n });\n return records.map(toRunnerRecord);\n }\n\n async listAcrossRepositories(): Promise<RunnerRecord[]> {\n const records = await this.registry.listProcesses({\n serviceType: SERVICE_TYPE,\n tags: resolveProcessTags([RUNNER_TAG]),\n });\n return records.map(toRunnerRecord);\n }\n\n async listBySession(sessionId: string): Promise<RunnerRecord[]> {\n const records = await this.list();\n return records.filter((record) => record.sessionId === sessionId);\n }\n\n async listByRootSession(rootSessionId: string): Promise<RunnerRecord[]> {\n const records = await this.list();\n return records.filter((record) => (record.rootSessionId ?? record.sessionId) === rootSessionId);\n }\n\n async listAll(sessionId?: string): Promise<RunnerRecord[]> {\n this.paths.ensureDirectories(sessionId);\n const records: RunnerRecord[] = [];\n const directory = this.paths.stateDirectory(sessionId);\n for (const entry of fs.readdirSync(directory)) {\n if (!isPrimaryMetadataEntry(entry)) continue;\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(`${directory}/${entry}`, 'utf8'));\n if (isRunnerRecord(parsed)) records.push(parsed);\n } catch (error) {\n process.emitWarning(`Could not read runner metadata ${entry}: ${String(error)}`);\n }\n }\n return records.sort((left, right) => right.startedAt.localeCompare(left.startedAt));\n }\n\n async get(id: string, sessionId?: string): Promise<RunnerRecord | undefined> {\n const target = this.paths.statePathFor(id, sessionId);\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(target, 'utf8'));\n if (!isRunnerRecord(parsed) || parsed.id !== id) {\n process.emitWarning(`Could not read runner metadata ${path.basename(target)}: invalid runner record`);\n return undefined;\n }\n return parsed;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return undefined;\n process.emitWarning(`Could not read runner metadata ${path.basename(target)}: ${String(error)}`);\n return undefined;\n }\n }\n\n async markPromoted(id: string): Promise<RunnerRecord | undefined> {\n const record = await this.get(id);\n if (!record) return undefined;\n const promoted = { ...record, promoted: true };\n this.writeRecord(promoted);\n this.notify(promoted.sessionId);\n return promoted;\n }\n\n async complete(id: string, outcome: CompleteRunnerInput, sessionId?: string): Promise<RunnerRecord | undefined> {\n const persisted = await this.get(id, sessionId);\n if (persisted?.state === 'completed') {\n await this.release(id, persisted.sessionId);\n return persisted;\n }\n const active = await this.list();\n const record =\n persisted ??\n active.find((candidate) => candidate.id === id && (sessionId === undefined || candidate.sessionId === sessionId));\n if (!record) {\n const belongsToAnotherSession =\n sessionId !== undefined && active.some((candidate) => candidate.id === id && candidate.sessionId !== sessionId);\n if (!belongsToAnotherSession) await this.release(id, sessionId);\n return undefined;\n }\n const completed: RunnerRecord = {\n ...record,\n state: 'completed',\n exit: { ...outcome, finishedAt: new Date().toISOString() },\n };\n this.writeRecord(completed);\n await this.release(id, completed.sessionId);\n return completed;\n }\n\n async release(id: string, sessionId?: string): Promise<void> {\n const activeSessionId = sessionId ?? (await this.list()).find((record) => record.id === id)?.sessionId;\n const ownedSessionId = activeSessionId ?? (await this.get(id))?.sessionId;\n const response = await this.registry.releaseProcess({\n repositoryPath: this.repositoryPath(),\n serviceName: `${SERVICE_PREFIX}${id}`,\n serviceType: SERVICE_TYPE,\n kill: false,\n releasePort: false,\n force: true,\n });\n if (!response.success && !response.error?.includes('No matching process entry')) {\n throw new Error(response.error ?? `Failed to release runner ${id}`);\n }\n if (ownedSessionId !== undefined) this.notify(ownedSessionId);\n }\n\n async pruneDead(): Promise<string[]> {\n const dead = (await this.list()).filter((record) => !this.processControl.isAlive(record.pid));\n for (const record of dead) {\n await this.complete(record.id, { reason: 'backend_lost', code: null, signal: null }, record.sessionId);\n }\n return dead.map((record) => record.id);\n }\n\n subscribe(listener: () => void, sessionId: string): () => void {\n const subscribed = (): void => listener();\n const registration = { key: this.subscriptionKey(sessionId), listener: subscribed };\n const listeners = listenersBySession.get(registration.key) ?? new Set<() => void>();\n listeners.add(subscribed);\n listenersBySession.set(registration.key, listeners);\n this.subscriptions.add(registration);\n return () => this.removeSubscription(registration);\n }\n\n close(): void {\n for (const registration of this.subscriptions) this.removeSubscription(registration);\n this.registry.close();\n }\n\n private notify(sessionId: string): void {\n for (const listener of listenersBySession.get(this.subscriptionKey(sessionId)) ?? []) {\n try {\n listener();\n } catch (error) {\n process.emitWarning(`Runner registry listener failed: ${String(error)}`);\n }\n }\n }\n\n private removeSubscription(registration: ListenerRegistration): void {\n const listeners = listenersBySession.get(registration.key);\n listeners?.delete(registration.listener);\n if (listeners?.size === 0) listenersBySession.delete(registration.key);\n this.subscriptions.delete(registration);\n }\n\n private subscriptionKey(sessionId: string): string {\n return JSON.stringify([path.resolve(this.paths.stateDirectory(sessionId)), sessionId]);\n }\n\n private repositoryPath(): string {\n return normalizeRepositoryPath(this.paths.repositoryPath());\n }\n\n private writeRecord(record: RunnerRecord): void {\n this.paths.ensureDirectories(record.sessionId);\n const target = this.paths.statePathFor(record.id, record.sessionId);\n const temporary = `${target}.${process.pid}.tmp`;\n fs.writeFileSync(temporary, `${JSON.stringify(record, null, 2)}\\n`, 'utf8');\n fs.renameSync(temporary, target);\n }\n}\n\n/** Every field is read back from metadata this class wrote on registration. */\nfunction toRunnerRecord(record: ProcessRegistryRecord): RunnerRecord {\n return {\n id: readString(record, 'id'),\n name: readString(record, 'name'),\n pid: record.pid,\n command: readString(record, 'command'),\n cwd: readString(record, 'cwd'),\n logPath: readString(record, 'logPath'),\n interactive: record.metadata?.interactive === true,\n sessionId: readString(record, 'sessionId'),\n ...(readString(record, 'rootSessionId') ? { rootSessionId: readString(record, 'rootSessionId') } : {}),\n startedAt: readString(record, 'startedAt'),\n state: 'running',\n promoted: false,\n backend: readBackend(record.metadata?.backend),\n ...(readString(record, 'backendTarget') ? { backendTarget: readString(record, 'backendTarget') } : {}),\n hostPid: readNumber(record, 'hostPid'),\n };\n}\n\nfunction readBackend(value: unknown): RunnerRecord['backend'] {\n return value === 'rmux' || value === 'tmux' ? value : 'native';\n}\n\nfunction isPrimaryMetadataEntry(entry: string): boolean {\n return (\n entry.endsWith(JSON_EXTENSION) && !entry.endsWith(COMMAND_SIDECAR_SUFFIX) && !entry.endsWith(EXIT_SIDECAR_SUFFIX)\n );\n}\n","import type { DoomServerPluginContext } from '@agimon-ai/doompi-core/server-facet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { createHeadlessBashTool, createHeadlessRunnersCommand } from '../../../../../services/headless';\nimport { summarizeLog } from '../../../../../services/logReader';\nimport { createRunnerDependencies } from '../../../../../services/runnerDependencies';\nimport { createRunnerServerRuntime } from '../../../../../services/runnerServerRuntime';\n\nexport const RUNNER_SERVER_SCOPE_SERVICE = 'doom/runner-server-scope';\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'doom/runner-server-scope': RunnerServerScope;\n }\n}\n\nexport const createRunnerServerRoot = ({ host }: DoomServerPluginContext) => {\n if (!host.context.directEvents) throw new Error('Runner headless facet requires the session direct event bus.');\n const dependencies = createRunnerDependencies({ environment: Object.freeze({ ...process.env }) });\n const runtime = createRunnerServerRuntime(dependencies, host.context.directEvents);\n const value = {\n tool: createHeadlessBashTool(\n {\n run: async (request) => {\n await runtime.ensureSession(request.sessionId);\n return dependencies.bashRunService.run(request);\n },\n },\n summarizeLog,\n ),\n command: createHeadlessRunnersCommand(dependencies),\n };\n return {\n value,\n services: [\n runtime.backgroundWorkPlugin,\n (context: Context) => {\n context.plugin((providerContext) => providerContext.provide(RUNNER_SERVER_SCOPE_SERVICE, value));\n },\n ],\n activities: [runtime.activity],\n onStop: runtime.dispose,\n };\n};\n\nexport type RunnerServerScope = Awaited<ReturnType<typeof createRunnerServerRoot>>['value'];\n","import { defineMcpTool, type DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { RUNNER_SERVER_SCOPE_SERVICE, type RunnerServerScope } from '../_lib/serverRoot';\n\nexport default defineMcpTool((context: DoomMcpPluginContext) => {\n const scope = context.services.get<RunnerServerScope>(RUNNER_SERVER_SCOPE_SERVICE);\n if (!scope) throw new Error('Runner session service is unavailable.');\n return {\n ...scope.tool,\n execute(toolCallId, parameters, signal, onUpdate, execution) {\n return scope.tool.execute(\n toolCallId,\n parameters,\n AbortSignal.any([context.signal, signal ?? context.signal]),\n onUpdate,\n execution,\n );\n },\n };\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport skillDoompiUseRunner from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-use-runner.mcp';\nimport toolBash from '../src/extensions/workspaces/sessions/(backend)/tool/bash.mcp';\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 => ({ ...identity, ...(value as object) }) as T;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-runner',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [at(skillDoompiUseRunner, context)]; },\n get tools(): DoomMcpSessionPlugin['tools'] { return [via({ name: 'bash' }, at(toolBash, context))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,IAAA,iCAAA,GAAeA,iCAAAA,eAAAA,CAAe;CAC5B,MAAM;CACN,aACE;CACF,aAAA,GAAYC,oCAAAA,oBAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,MAAqC,wCAAwC;AAC3F,CAAC;ACE+BC,QAAAA,KAAK,OAAO;CAC1C,SAASA,QAAAA,KAAK,OAAO,EAAE,aAAa,0BAA0B,CAAC;CAC/D,SAASA,QAAAA,KAAK,SAASA,QAAAA,KAAK,OAAO,EAAE,aAAa,oDAAoD,CAAC,CAAC;CACxG,YAAYA,QAAAA,KAAK,SACfA,QAAAA,KAAK,QAAQ,EACX,aACE,yJACJ,CAAC,CACH;CACA,aAAaA,QAAAA,KAAK,SAChBA,QAAAA,KAAK,QAAQ,EACX,aACE,6LACJ,CAAC,CACH;CACA,MAAMA,QAAAA,KAAK,SACTA,QAAAA,KAAK,OAAO,EACV,aAAa,+FACf,CAAC,CACH;AACF,CAAC;ACjBoB,IAAI,OACvB;CACE;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,GAAG,GACV,GACF;AAWoC,IAAI,OACtC;CACE;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,GAAG,GACV,GACF;;;;ACdA,MAAM,EAAE,UAAU,qBAAqBC,gBAAAA;CCiCjC,GAAaC,UAAAA,UAAAA,CAAUC,mBAAAA,QAAQ;;;ACnDrC,MAAa,8BAA8B;;;ACJ3C,IAAA,oBAAA,GAAeC,iCAAAA,cAAAA,EAAe,YAAkC;CAC9D,MAAM,QAAQ,QAAQ,SAAS,IAAuB,2BAA2B;CACjF,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,wCAAwC;CACpE,OAAO;EACL,GAAG,MAAM;EACT,QAAQ,YAAY,YAAY,QAAQ,UAAU,WAAW;GAC3D,OAAO,MAAM,KAAK,QAChB,YACA,YACA,YAAY,IAAI,CAAC,QAAQ,QAAQ,UAAU,QAAQ,MAAM,CAAC,GAC1D,UACA,SACF;EACF;CACF;AACF,CAAC;;;ACZD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,WAAuB;CAAE,GAAG;CAAU,GAAI;AAAiB;AAE9G,MAAa,OAAA,GAAMC,iCAAAA,gBAAAA,CAAgB;CACjC,MAAM;CACN,UAAU,aAAa;EACrB,IAAI,SAAyC;GAAE,OAAO,CAAC,GAAGC,+BAAsB,OAAO,CAAC;EAAG;EAC3F,IAAI,QAAuC;GAAE,OAAO,CAAC,IAAI,EAAE,MAAM,OAAO,GAAG,GAAGC,kBAAU,OAAO,CAAC,CAAC;EAAG;CACtG;AACF,CAAC"}
|