@agimon-ai/po-runner 0.0.0-alpha.28 → 0.0.0-alpha.29

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.
@@ -1324,11 +1324,11 @@ const serverPlugin = (0, _agimon_ai_po_core_serverPlugin.defineServerPlugin)({
1324
1324
  name: "@agimon-ai/po-runner",
1325
1325
  providerOverrides: (0, _agimon_ai_po_core_serverPlugin.composeServerProviderOverrides)(root_server_default),
1326
1326
  injects: (0, _agimon_ai_po_core_extensionFile.composeRootInjects)({ session: [root_server_default] }),
1327
- global: () => ({ get channels() {
1328
- return [() => via({ frameType: "runner_runs" }, runnerRuns_server_default())];
1327
+ global: (context) => ({ get channels() {
1328
+ return [() => via({ frameType: "runner_runs" }, at(runnerRuns_server_default, context))];
1329
1329
  } }),
1330
1330
  workspace: (context) => ({ get channels() {
1331
- return defined([context.hosted("global") ? void 0 : () => via({ frameType: "runner_runs" }, runnerRuns_server_default())]);
1331
+ return defined([context.hosted("global") ? void 0 : () => via({ frameType: "runner_runs" }, at(runnerRuns_server_default, context))]);
1332
1332
  } }),
1333
1333
  session: async (context) => {
1334
1334
  const scopeSession = await context.scopeRoot("session", root_server_default, context);
@@ -1342,7 +1342,7 @@ const serverPlugin = (0, _agimon_ai_po_core_serverPlugin.defineServerPlugin)({
1342
1342
  return [at(route_server_default, contextSession)];
1343
1343
  },
1344
1344
  get channels() {
1345
- return defined([context.hosted("global") ? void 0 : () => via({ frameType: "runner_runs" }, runnerRuns_server_default())]);
1345
+ return defined([context.hosted("global") ? void 0 : () => via({ frameType: "runner_runs" }, at(runnerRuns_server_default, context))]);
1346
1346
  },
1347
1347
  get commands() {
1348
1348
  return [via({ name: "runners" }, at(runners_server_default, contextSession))];
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","names":["isRunnerRecord","createPoTelemetry","reconcileActiveRunners","getLogTtlMs","runCleanups","stopRunnerProcess","TERM_GRACE_MS","resolveRootSessionId","formatRunnerStatus","formatRunnerFinished","formatRunnerFinishedNotice","activeRunnerRecovery","PO_BACKGROUND_WORK_SERVICE","readPoBackgroundWorkService","fs","path","CONFIG_DIR_NAME","createDefaultProcessRegistry","SystemClock","createRunnerDependencies","createHeadlessBashTool","summarizeLog","createHeadlessRunnersCommand","readPoFs","defineRoot","defineChannel","fs","path","fs","LogReader","RunnerPaths","resolveRunnerStoreDirectory","runnerStateDirFor","Hono","PtyBackendChain","RmuxBackend","TmuxBackend","streamSSE","defineRoute","defineServerCommand","defineHook","defineResource","PO_HELP_WHEN","packageResourcePath","readPackageResource","defineServerTool","defineServerPlugin","composeServerProviderOverrides","rootSession","composeRootInjects","channelRunnerRuns","composeRootHooks","apiRoute","commandRunners","hookSessionCompact","resourcePoUseRunner","toolBash"],"sources":["../../src/constants/runnerRuntime.ts","../../src/constants/runnerSettings.ts","../../src/services/runnerHost/index.ts","../../src/constants/webRunners.ts","../../src/constants/webRunnerRuns.ts","../../src/services/webRunnerRuns/index.ts","../../src/services/runnerServerRuntime/index.ts","../../src/services/runnerSettings/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/runnersChannel/index.ts","../../src/extensions/(backend)/channel/runnerRuns.server.ts","../../src/constants/runnerLogApi.ts","../../src/constants/webRunnerLog.ts","../../src/services/logTail/index.ts","../../src/types/webRunnerLog.ts","../../src/types/apiRoutes.ts","../../src/services/runnerLogApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/runner/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/runners.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-compact.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/po-use-runner.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/bash.server.ts","../../generated/server.ts"],"sourcesContent":["export const LEADER_SOURCE = '@agimon-ai/po-runner';\nexport const LEADER_GROUP_ORDER = 67;\nexport const ERR_REQUIRES_INTERACTIVE = '/runners requires interactive mode';\nexport const ERR_STOP_USAGE = 'Usage: /runners stop <runner-id> [reason]';\nexport const ERR_START_USAGE = 'Usage: /runners start [name=x] [cwd=y] [interactive=true] -- <command>';\nexport const SESSION_START_EVENT = 'session_start';\nexport const RUNNER_FINISHED_MESSAGE = 'po-runner-finished';\nexport const COMPLETED_STATE = 'completed';\nexport const RMUX_BACKEND = 'rmux';\nexport const STOPPED_REASON = 'stopped';\nexport const RUNNER_STATUS_KEY = 'po-runner-runners';\nexport const BACKGROUND_WORK_PROVIDER = 'po-runner';\nexport const RUNNER_FOOTER_ORDER = 10;\nexport const RUNNER_STATUS_POLL_MS = 500;\nexport const HISTORY_SWEEP_INTERVAL_MS = 600_000;\n","export const RUNNER_SETTINGS_FILE = 'po-runner.json';\nexport const RATIO_KEYS = ['headRatio', 'errorBudgetRatio'] as const;\nexport const MAX_RESULT_BYTES_CEILING = 262_144;\nexport const MAX_RESULT_LINES_CEILING = 5_000;\nexport const MAX_RESULT_TOKENS_CEILING = 100_000;\nexport const MAX_ERROR_ENTRIES_CEILING = 100;\nexport const MAX_PATTERNS = 32;\nexport const MAX_PATTERN_LENGTH = 512;\n","import type { IClock } from '../../types/clock';\nimport type { ProcessRegistryPort } from '../runnerRegistry';\nimport type { RunnerHost, RunnerHostOptions, RunnerHostSession } from './type';\n\nexport type { RunnerHost, RunnerHostOptions, RunnerHostSession } from './type';\n\nconst describe = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\n/** Runs `pass` again `ms` after the previous pass finished, so two passes never overlap. */\nfunction repeat(clock: IClock, ms: number, pass: () => Promise<void>): { stop(): Promise<void> } {\n let stopped = false;\n let cancel: (() => void) | undefined;\n let running: Promise<void> | undefined;\n const arm = (): void => {\n cancel = clock.after(ms, () => {\n cancel = undefined;\n running = pass().finally(() => {\n running = undefined;\n if (!stopped) arm();\n });\n });\n };\n arm();\n return {\n async stop() {\n stopped = true;\n cancel?.();\n await running;\n },\n };\n}\n\n/**\n * The housekeeping every session running one po-runner build in one hub shares.\n *\n * A session used to open its own registry connection and arm its own status poll and history\n * sweep, and each sweep walked the whole runner store. Here one loop refreshes the attached sessions\n * one at a time, one sweep per store spares every attached session at once, and runners a crashed\n * server left behind are reclaimed once rather than at every session start.\n */\nexport function createRunnerHost(options: RunnerHostOptions): RunnerHost {\n const sessions = new Set<RunnerHostSession>();\n let closed = false;\n let reclaiming: Promise<void> | undefined;\n let sweeping: Promise<void> | undefined;\n\n const refreshAll = async (): Promise<void> => {\n // A Set skips what is deleted mid-pass, so a session detached during a pass is not refreshed after.\n for (const session of sessions) {\n if (closed) return;\n try {\n await session.refresh();\n } catch (error) {\n options.onWarning(`Could not refresh po-runner session ${session.sessionId}: ${describe(error)}`);\n }\n }\n };\n\n const sweepAll = (): Promise<void> => {\n sweeping ??= (async () => {\n try {\n const byStore = new Map<string, RunnerHostSession[]>();\n for (const session of sessions) {\n const store = session.paths.storeDirectory();\n byStore.set(store, [...(byStore.get(store) ?? []), session]);\n }\n for (const group of byStore.values()) {\n const [first] = group;\n if (closed || first === undefined) return;\n const live = new Set(group.map((session) => session.sessionId));\n const now = options.clock.now();\n const result = first.paths.sweepHistoryAsync\n ? await first.paths.sweepHistoryAsync(first.historyTtlMs, now, live)\n : first.paths.sweepHistory(first.historyTtlMs, now, live);\n for (const error of result.errors) options.onWarning(error);\n }\n } catch (error) {\n options.onWarning(`Could not sweep po-runner history: ${describe(error)}`);\n }\n })().finally(() => {\n sweeping = undefined;\n });\n return sweeping;\n };\n\n const polling = repeat(options.clock, options.statusPollMs, refreshAll);\n const sweeper = repeat(options.clock, options.historySweepMs, sweepAll);\n const processRegistry: ProcessRegistryPort = {\n registerProcess: (...args) => options.processRegistry.registerProcess(...args),\n releaseProcess: (...args) => options.processRegistry.releaseProcess(...args),\n listProcesses: (...args) => options.processRegistry.listProcesses(...args),\n // Sessions come and go; the connection lives as long as the host.\n close: () => undefined,\n };\n\n return {\n processRegistry,\n attach(session) {\n if (closed) throw new Error('The po-runner host is disposed.');\n sessions.add(session);\n if (reclaiming === undefined) {\n // Once per host: runners and history a crashed server left behind belong to no session.\n reclaiming = session\n .reclaimOrphans()\n .catch((error: unknown) => options.onWarning(`Could not reclaim orphaned runners: ${describe(error)}`));\n void sweepAll();\n }\n return () => {\n sessions.delete(session);\n };\n },\n async dispose() {\n if (closed) return;\n closed = true;\n sessions.clear();\n await Promise.all([polling.stop(), sweeper.stop(), reclaiming, sweeping]);\n options.processRegistry.close();\n },\n };\n}\n","export const RUNNER_RUNS_TYPE = 'runner_runs';\n","export const FINISHED_RUNNER_RETENTION_MS = 600_000;\n","import { FINISHED_RUNNER_RETENTION_MS } from '../../constants/webRunnerRuns';\nimport type { RunnerRecord } from '../../types/runnerRegistry';\nimport type { RunnerRunView } from '../../types/webRunners';\nimport { isRunnerRecord } from '../runnerRecord';\n\n/** How long a finished runner stays in the cockpit's feed after it exited. */\n\n/** One on-disk metadata record, or undefined when the text is not a runner record. */\nexport function parseRunnerRecord(raw: string): RunnerRecord | undefined {\n try {\n const parsed: unknown = JSON.parse(raw);\n return isRunnerRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined; // Mid-write or foreign file; the next scan settles it.\n }\n}\n\nexport function toRunnerRunView(record: RunnerRecord): RunnerRunView {\n return {\n id: record.id,\n name: record.name,\n pid: record.pid,\n command: record.command,\n cwd: record.cwd,\n interactive: record.interactive,\n backend: record.backend,\n state: record.state,\n promoted: record.promoted,\n startedAt: record.startedAt,\n logPath: record.logPath,\n ...(record.exit\n ? {\n exit: {\n reason: record.exit.reason,\n code: record.exit.code,\n signal: record.exit.signal,\n ...(record.exit.terminationReason === undefined\n ? {}\n : { terminationReason: record.exit.terminationReason }),\n ...(record.exit.stopReason === undefined ? {} : { stopReason: record.exit.stopReason }),\n finishedAt: record.exit.finishedAt,\n },\n }\n : {}),\n };\n}\n\nfunction finishedAt(record: RunnerRecord): number {\n const parsed = record.exit === undefined ? Number.NaN : Date.parse(record.exit.finishedAt);\n return Number.isFinite(parsed) ? parsed : Date.parse(record.startedAt);\n}\n\n/**\n * The feed the cockpit shows: running runners first, newest start first, then\n * the recently finished ones, newest exit first. Finished runners older than\n * the retention window are left out; the registry keeps them for the CLI.\n */\nexport function presentRunnerRuns(records: readonly RunnerRecord[], now: number): RunnerRunView[] {\n const running = records\n .filter((record) => record.state === 'running')\n .sort((left, right) => right.startedAt.localeCompare(left.startedAt));\n const finished = records\n .filter((record) => record.state === 'completed' && now - finishedAt(record) <= FINISHED_RUNNER_RETENTION_MS)\n .sort((left, right) => finishedAt(right) - finishedAt(left));\n return [...running, ...finished].map(toRunnerRunView);\n}\n","import {\n PO_BACKGROUND_WORK_SERVICE,\n readPoBackgroundWorkService,\n type BackgroundProviderWorkItem,\n type BackgroundWorkProviderHandle,\n} from '@agimon-ai/po-core/backgroundWork';\nimport { resolveRootSessionId } from '@agimon-ai/po-core/childProcess';\nimport { runCleanups } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessActivity, PoHeadlessExecutionContext } from '@agimon-ai/po-core/headless';\nimport type { PoServerHostService } from '@agimon-ai/po-core/serverPlugin';\nimport { createPoTelemetry } from '@agimon-ai/po-telemetry';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { LIVENESS_POLL_MS, TERM_GRACE_MS } from '../../constants/launcher';\nimport { BACKGROUND_WORK_PROVIDER, RUNNER_STATUS_KEY } from '../../constants/runnerRuntime';\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport type { RunnerStartFailureView } from '../../types/webRunners';\nimport { activeRunnerRecovery } from '../compaction';\nimport { reconcileActiveRunners, stopRunnerProcess } from '../reconcile';\nimport { getLogTtlMs } from '../runnerConfig';\nimport type { RunnerDependencies } from '../runnerDependencies/type';\nimport type { RunnerHost } from '../runnerHost';\nimport { formatRunnerStatus } from '../runnerRecord';\nimport { formatRunnerFinished, formatRunnerFinishedNotice } from '../runnerRecord';\nimport { presentRunnerRuns } from '../webRunnerRuns';\n\nconst COMPLETION_RETRY_MS = 1_000;\n\nexport interface RunnerServerRuntime {\n readonly container: RunnerDependencies;\n readonly activity: PoHeadlessActivity;\n readonly backgroundWorkPlugin: (context: Context) => void;\n ensureSession(sessionId: string): Promise<void>;\n /** Wakes the agent once when this runner, which it sent to the background, exits. */\n watchRunner(id: string): void;\n /** Tells the runners panel why a start it asked for did not happen; undefined clears it after one that did. */\n reportStart(sessionId: string, failure: string | undefined): void;\n recoverCompaction(this: void, event: Readonly<Record<string, unknown>>): Promise<void>;\n dispose(this: void): Promise<void>;\n}\nexport function createRunnerServerRuntime(\n container: RunnerDependencies,\n /** What every session of this build in the hub shares: the registry and the housekeeping loop. */\n host: RunnerHost,\n directEvents: NonNullable<PoServerHostService['context']['directEvents']>,\n /** Absent when no agent runs in this session, so there is nobody to wake. */\n wakeAgent?: (content: string, requestId: string, notice: string) => Promise<void>,\n execution?: PoHeadlessExecutionContext,\n): RunnerServerRuntime {\n const telemetry = execution\n ? createPoTelemetry({\n serviceName: 'po-runner',\n packageName: '@agimon-ai/po-runner',\n cwd: execution.cwd,\n env: execution.environment,\n enableLogs: true,\n enableTraces: true,\n })\n : undefined;\n let sessionId: string | undefined;\n let recoveredCompactionId: string | undefined;\n let supervision: Promise<void> | undefined;\n let runtimeDisposal: Promise<void> | undefined;\n let disposal: Promise<void> | undefined;\n let backgroundWorkItems: BackgroundProviderWorkItem[] = [];\n let backgroundWorkProvider: BackgroundWorkProviderHandle | undefined;\n // Nothing else tells the model a background runner ended, so without this an\n // agent that finished its turn waiting on one idles until a person prompts it.\n const watched = new Set<string>();\n let unsubscribeWatched: (() => void) | undefined;\n let wakes: Promise<void> = Promise.resolve();\n let closing = false;\n let retryTimer: ReturnType<typeof setTimeout> | undefined;\n /** Stops the shared loop refreshing this session and sweeping around it. */\n let detach: (() => void) | undefined;\n let startFailure: RunnerStartFailureView | undefined;\n let startFailures = 0;\n\n /** Every runner of every session the registry knows, reclaimed when its owning process is gone. */\n const reclaimOrphans = async (): Promise<void> => {\n const reclaimed = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: false,\n });\n for (const error of reclaimed.errors) process.emitWarning(error);\n };\n\n // The activity is optional and source-gated. The retained server plugin owns the\n // session runtime, so disabling the activity must not tear down its jobs.\n const ensureSession = (requestedSessionId: string): Promise<void> => {\n if (closing) return Promise.reject(new Error('Runner runtime is closing.'));\n if (sessionId !== undefined && sessionId !== requestedSessionId) {\n throw new Error(`Runner runtime belongs to session ${sessionId}.`);\n }\n sessionId ??= requestedSessionId;\n supervision ??= (async () => {\n void telemetry\n ?.recordEvent('po_runner.session_started', { outcome: 'started' })\n .catch((error: unknown) => process.emitWarning(`Could not record runner session telemetry: ${String(error)}`));\n if (closing) return;\n container.paths.setSessionId(requestedSessionId);\n if ((await container.lifeline.arm(requestedSessionId)) === undefined) {\n throw new Error('Could not arm runner supervision lifeline.');\n }\n if (closing) return;\n const promotedIds = (await container.runnerRegistry.listAll(requestedSessionId))\n .filter((record) => record.state === 'running' && record.promoted)\n .map((record) => record.id);\n // Only this session's runners: the shared host reclaims the ones other sessions left behind.\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: true,\n startupSessionId: requestedSessionId,\n active: await container.runnerRegistry.listBySession(requestedSessionId),\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n void telemetry\n ?.recordEvent('po_runner.reconciled', {\n 'runner.reclaimed_count': reconciled.reclaimed.length,\n 'runner.error_count': reconciled.errors.length,\n outcome: reconciled.errors.length === 0 ? 'completed' : 'degraded',\n })\n .catch((error: unknown) =>\n process.emitWarning(`Could not record runner reconcile telemetry: ${String(error)}`),\n );\n if (!closing) {\n unsubscribeRunnerUpdates ??= container.runnerRegistry.subscribe(\n () => requestPublish(requestedSessionId),\n requestedSessionId,\n );\n // Reconciliation may have completed retained promotions already.\n for (const id of promotedIds) watchRunner(id);\n requestPublish(requestedSessionId);\n // One shared loop refreshes this session from here on, and sweeps history around it.\n detach ??= host.attach({\n sessionId: requestedSessionId,\n paths: container.paths,\n historyTtlMs: getLogTtlMs(container.environment),\n refresh: () => refreshSession(requestedSessionId),\n reclaimOrphans,\n });\n }\n })().catch((error: unknown) => {\n supervision = undefined;\n throw error;\n });\n return supervision;\n };\n\n const disposeRuntime = (): Promise<void> => {\n runtimeDisposal ??= (async () => {\n await supervision?.catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n const ownedSessionId = sessionId;\n if (ownedSessionId === undefined) return;\n await runCleanups(\n [\n async () => {\n const records = await container.runnerRegistry.listBySession(ownedSessionId);\n const results = await Promise.allSettled(\n records.map(async (record) => {\n await stopRunnerProcess(record, container.launcher, container.rmuxBackend, {\n registry: container.runnerRegistry,\n intent: {\n reason: 'stopped',\n terminationReason: 'parent_session_cleanup',\n stopReason: 'session ended',\n },\n });\n // Success can mean only that SIGKILL or the mux close was accepted.\n // Allow asynchronous exit to land, but never complete a still-live runner.\n const deadline = container.clock.now() + TERM_GRACE_MS;\n while (container.processControl.isAlive(record.pid)) {\n if (container.clock.now() >= deadline) {\n throw new Error(`Could not stop runner ${record.id} during session shutdown`);\n }\n await new Promise<void>((resolve) => container.clock.after(LIVENESS_POLL_MS, resolve));\n }\n await container.runnerRegistry.complete(\n record.id,\n { reason: 'stopped', code: null, signal: null, stopReason: 'session ended' },\n ownedSessionId,\n );\n }),\n );\n const errors = results.filter((result) => result.status === 'rejected').map((result) => result.reason);\n if (errors.length) throw new AggregateError(errors, 'Runner process cleanup failed.');\n },\n () => container.ptyHost.disposeAll(),\n () => container.lifeline.dispose(),\n () => container.runnerRegistry.close(),\n ],\n 'Runner runtime cleanup failed.',\n );\n })();\n return runtimeDisposal;\n };\n\n let refreshInFlight: Promise<void> | undefined;\n const refreshSession = (ownedSessionId: string): Promise<void> => {\n if (closing) return Promise.resolve();\n refreshInFlight ??= refreshNow(ownedSessionId).finally(() => {\n refreshInFlight = undefined;\n });\n return refreshInFlight;\n };\n const refreshNow = async (ownedSessionId: string): Promise<void> => {\n if (closing) return;\n try {\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: false,\n active: await container.runnerRegistry.listBySession(ownedSessionId),\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n if (!closing) {\n requestPublish(ownedSessionId);\n wakeForFinished(ownedSessionId);\n }\n } catch (error) {\n process.emitWarning(`Could not refresh po-runner: ${String(error)}`);\n }\n };\n const publishRuns = async (ownedSessionId: string): Promise<void> => {\n try {\n const records = await container.runnerRegistry.listAll(ownedSessionId);\n if (closing) return;\n if (execution) {\n const root = resolveRootSessionId(ownedSessionId, container.environment);\n const count = (await container.runnerRegistry.list()).filter(\n (record) => (record.rootSessionId ?? record.sessionId) === root,\n ).length;\n if (closing) return;\n execution.client.setStatus(RUNNER_STATUS_KEY, formatRunnerStatus(count));\n }\n if (closing) return;\n const nextBackgroundWork = records\n .filter((record) => record.state === 'running' || watched.has(record.id))\n .map((record) => ({\n id: record.id,\n sessionId: record.sessionId,\n label: record.name,\n // An absent status holds ownership until completion admission succeeds.\n ...(record.state === 'running' ? { status: record.state } : {}),\n }));\n if (JSON.stringify(nextBackgroundWork) !== JSON.stringify(backgroundWorkItems)) {\n backgroundWorkItems = nextBackgroundWork;\n backgroundWorkProvider?.update();\n }\n directEvents.publish(RUNNER_RUNS_TYPE, ownedSessionId, {\n runs: presentRunnerRuns(records, Date.now()),\n ...(execution ? { sessionDirectory: execution.cwd } : {}),\n ...(startFailure ? { startFailure } : {}),\n });\n } catch (error) {\n process.emitWarning(`Could not publish runner state: ${String(error)}`);\n }\n };\n let publishInFlight: Promise<void> | undefined;\n let publishAgain = false;\n let unsubscribeRunnerUpdates: (() => void) | undefined;\n const requestPublish = (ownedSessionId: string): void => {\n if (closing) return;\n publishAgain = true;\n if (publishInFlight !== undefined) return;\n publishInFlight = (async () => {\n do {\n publishAgain = false;\n await publishRuns(ownedSessionId);\n } while (publishAgain && !closing);\n })()\n .catch((error: unknown) => {\n process.emitWarning(`Could not schedule runner state publication: ${String(error)}`);\n })\n .finally(() => {\n publishInFlight = undefined;\n if (publishAgain) requestPublish(ownedSessionId);\n });\n };\n\n // Sequenced so two runners exiting in the same tick each wake the agent once.\n const wakeForFinished = (ownedSessionId: string): void => {\n if (closing) return;\n wakes = wakes\n .then(async () => {\n if (closing) return;\n let retry = false;\n // Deleting the entry being visited is safe for a Set iterator.\n for (const id of watched) {\n if (closing) return;\n try {\n const record = await container.runnerRegistry.get(id, ownedSessionId);\n if (closing) return;\n if (record?.state === 'running') continue;\n // A record swept before it was seen leaves nothing to report.\n if (record)\n await wakeAgent?.(\n formatRunnerFinished(record),\n `runner-finished:${JSON.stringify([ownedSessionId, id])}`,\n formatRunnerFinishedNotice(record),\n );\n if (record)\n await telemetry?.recordEvent('po_runner.process_finished', {\n outcome: 'completed',\n 'runner.exit_code': record.exit?.code ?? 0,\n 'runner.backend': record.backend,\n });\n watched.delete(id);\n requestPublish(ownedSessionId);\n } catch (error) {\n retry = true;\n process.emitWarning(`Could not report finished runner ${id}: ${String(error)}`);\n }\n }\n if (retry && !closing && retryTimer === undefined) {\n retryTimer = setTimeout(() => {\n retryTimer = undefined;\n wakeForFinished(ownedSessionId);\n }, COMPLETION_RETRY_MS);\n retryTimer.unref?.();\n }\n if (watched.size === 0) {\n clearTimeout(retryTimer);\n retryTimer = undefined;\n unsubscribeWatched?.();\n unsubscribeWatched = undefined;\n }\n })\n .catch((error: unknown) => {\n process.emitWarning(`Could not report a finished runner: ${String(error)}`);\n });\n };\n\n const watchRunner = (id: string): void => {\n const ownedSessionId = sessionId;\n if (!wakeAgent || closing || ownedSessionId === undefined) return;\n watched.add(id);\n requestPublish(ownedSessionId);\n // Independent of the optional activity: the wake-up must not depend on a\n // browser watching the runner list.\n unsubscribeWatched ??= container.runnerRegistry.subscribe(() => wakeForFinished(ownedSessionId), ownedSessionId);\n // It may already have exited between promotion and subscription.\n wakeForFinished(ownedSessionId);\n };\n\n const activity: PoHeadlessActivity = {\n name: 'runner',\n // Session creation waits on activity start, so supervision runs in the\n // background (it publishes once ready); bash and compaction recovery still\n // await `ensureSession`.\n start: async (executionContext: PoHeadlessExecutionContext) => {\n void ensureSession(executionContext.sessionId).catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n requestPublish(executionContext.sessionId);\n return () => {\n // The retained owner keeps supervision alive. Optional activity toggles\n // must not repeat startup cleanup, subscriptions, or history initialization.\n };\n },\n };\n return {\n container,\n activity,\n ensureSession,\n watchRunner,\n reportStart(startedSessionId, failure) {\n startFailures += 1;\n startFailure =\n failure === undefined ? undefined : { id: `${String(Date.now())}-${String(startFailures)}`, message: failure };\n requestPublish(startedSessionId);\n },\n async recoverCompaction(event) {\n if (\n event.status !== 'completed' ||\n closing ||\n !execution ||\n typeof event.entryId !== 'string' ||\n event.entryId === recoveredCompactionId\n )\n return;\n const entries = await execution.session.entries();\n const compacted = entries.findLast((entry) => entry.type === 'compaction');\n if (closing || compacted?.id !== event.entryId) return;\n await ensureSession(execution.sessionId);\n let records;\n try {\n records = await container.runnerRegistry.listBySession(execution.sessionId);\n } catch {\n return;\n }\n if (closing) return;\n const active = records.filter((record) => record.state === 'running');\n const text = activeRunnerRecovery(active);\n if (!text) return;\n await execution.session.appendMessage?.({\n type: 'po-runner-compaction',\n text,\n display: false,\n details: { sessionId: execution.sessionId, runnerIds: active.map((record) => record.id) },\n });\n if (!closing) recoveredCompactionId = event.entryId;\n },\n backgroundWorkPlugin: (context) => {\n context.inject([PO_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readPoBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => backgroundWorkItems,\n });\n backgroundWorkProvider = registration;\n return () => {\n registration.dispose();\n if (backgroundWorkProvider === registration) backgroundWorkProvider = undefined;\n };\n });\n },\n dispose() {\n if (disposal) return disposal;\n closing = true;\n disposal = (async () => {\n // Session cleanup completes every runner it stops, and none of those may\n // start a turn in a session that is going away.\n closing = true;\n detach?.();\n detach = undefined;\n clearTimeout(retryTimer);\n retryTimer = undefined;\n watched.clear();\n unsubscribeWatched?.();\n unsubscribeWatched = undefined;\n unsubscribeRunnerUpdates?.();\n unsubscribeRunnerUpdates = undefined;\n await wakes;\n await publishInFlight;\n await refreshInFlight;\n try {\n execution?.client.setStatus(RUNNER_STATUS_KEY, undefined);\n } catch (error) {\n process.emitWarning(`Could not clear runner status: ${String(error)}`);\n }\n await runCleanups(\n [\n disposeRuntime,\n () => telemetry?.recordEvent('po_runner.session_finished', { outcome: 'stopped' }),\n () => telemetry?.shutdown(),\n ],\n 'Runner shutdown failed.',\n );\n })();\n return disposal;\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { CONFIG_DIR_NAME } from '@earendil-works/pi-coding-agent';\n\nimport {\n MAX_ERROR_ENTRIES_CEILING,\n MAX_PATTERNS,\n MAX_PATTERN_LENGTH,\n MAX_RESULT_BYTES_CEILING,\n MAX_RESULT_LINES_CEILING,\n MAX_RESULT_TOKENS_CEILING,\n RATIO_KEYS,\n RUNNER_SETTINGS_FILE,\n} from '../../constants/runnerSettings';\nimport type { IRunnerSettingsLoader, RunnerSettings, RunnerSettingsLoad } from '../../types/runnerSettings';\n\n/** Pi's convention is one JSON file per extension under the config directory. */\n\nconst EMPTY: RunnerSettingsLoad = { settings: {}, issues: [] };\nconst COUNT_KEYS = [\n 'maxResultBytes',\n 'maxResultLines',\n 'successMaxResultBytes',\n 'maxResultTokens',\n 'successMaxResultTokens',\n 'errorMaxEntries',\n 'errorMaxVariantsJoined',\n] as const;\nconst KNOWN_KEYS = new Set<string>([...RATIO_KEYS, ...COUNT_KEYS, 'errorPatterns']);\n/** Ceilings so a project file cannot flood the model's context. */\nconst CEILINGS: Readonly<Record<string, number>> = {\n maxResultBytes: MAX_RESULT_BYTES_CEILING,\n successMaxResultBytes: MAX_RESULT_BYTES_CEILING,\n maxResultTokens: MAX_RESULT_TOKENS_CEILING,\n successMaxResultTokens: MAX_RESULT_TOKENS_CEILING,\n maxResultLines: MAX_RESULT_LINES_CEILING,\n errorMaxEntries: MAX_ERROR_ENTRIES_CEILING,\n errorMaxVariantsJoined: MAX_ERROR_ENTRIES_CEILING,\n};\n\nexport class RunnerSettingsLoader implements IRunnerSettingsLoader {\n load(cwd: string, trusted: boolean): RunnerSettingsLoad {\n // Project-local configuration is untrusted input until the project is.\n if (!trusted) return EMPTY;\n\n let raw: string;\n try {\n raw = fs.readFileSync(path.join(cwd, CONFIG_DIR_NAME, RUNNER_SETTINGS_FILE), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return EMPTY;\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} could not be read`] };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} is not valid JSON`] };\n }\n return validate(parsed);\n }\n}\n\nfunction validate(parsed: unknown): RunnerSettingsLoad {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} must contain a JSON object`] };\n }\n\n const source = parsed as Record<string, unknown>;\n const settings: Record<string, unknown> = {};\n const issues: string[] = [];\n // A rejected key is reported rather than dropped: a silent typo reads as a\n // setting that simply does not work.\n for (const key of Object.keys(source)) if (!KNOWN_KEYS.has(key)) issues.push(`unknown key ${key}`);\n\n for (const key of COUNT_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n issues.push(`${key} must be a positive integer`);\n continue;\n }\n settings[key] = Math.min(value, CEILINGS[key] ?? value);\n }\n\n for (const key of RATIO_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {\n issues.push(`${key} must be a number above 0 and below 1`);\n continue;\n }\n settings[key] = value;\n }\n\n const patterns = source.errorPatterns;\n if (patterns !== undefined) {\n if (!Array.isArray(patterns) || patterns.some((entry) => typeof entry !== 'string')) {\n issues.push('errorPatterns must be an array of strings');\n } else {\n const accepted: string[] = [];\n for (const pattern of patterns.slice(0, MAX_PATTERNS) as string[]) {\n if (pattern.length > MAX_PATTERN_LENGTH) {\n issues.push('errorPatterns entry is too long');\n continue;\n }\n try {\n new RegExp(pattern, 'iu');\n accepted.push(pattern);\n } catch {\n issues.push(`errorPatterns entry is not a valid regular expression: ${pattern}`);\n }\n }\n if (accepted.length > 0) settings.errorPatterns = accepted;\n }\n }\n\n return { settings: settings as RunnerSettings, issues };\n}\n","import { readPoFs } from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { REGISTRY_PATH_ENV } from '../../../../../constants/runnerRegistry';\nimport {\n HISTORY_SWEEP_INTERVAL_MS,\n RUNNER_FINISHED_MESSAGE,\n RUNNER_STATUS_POLL_MS,\n} from '../../../../../constants/runnerRuntime';\nimport { RUNNER_SETTINGS_FILE } from '../../../../../constants/runnerSettings';\nimport { SystemClock } from '../../../../../services/clock';\nimport { createHeadlessBashTool, createHeadlessRunnersCommand } from '../../../../../services/headless';\nimport { summarizeLog } from '../../../../../services/logReader';\nimport { createRunnerDependencies } from '../../../../../services/runnerDependencies';\nimport { createRunnerHost } from '../../../../../services/runnerHost';\nimport { createDefaultProcessRegistry } from '../../../../../services/runnerRegistry';\nimport { createRunnerServerRuntime } from '../../../../../services/runnerServerRuntime';\nimport { RunnerSettingsLoader } from '../../../../../services/runnerSettings';\n\nexport const RUNNER_SERVER_SCOPE_SERVICE = 'po/runner-server-scope';\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/runner-server-scope': RunnerServerScope;\n }\n}\n\nexport const createRunnerServerRoot = async ({ context, host, agent, signal, resources }: PoServerPluginContext) => {\n if (!host.context.directEvents) throw new Error('Runner server plugin requires the session direct event bus.');\n if (!host.context.cwd || !host.context.environment) {\n throw new Error('Runner server plugin requires session cwd and environment.');\n }\n // Inherit the current headless Pi bridge's isProjectTrusted: () => true.\n // This preserves runner configuration parity, not a new general trust policy.\n const loaded = new RunnerSettingsLoader().load(host.context.cwd, true);\n const settings = Object.freeze({\n ...loaded.settings,\n ...(loaded.settings.errorPatterns ? { errorPatterns: Object.freeze([...loaded.settings.errorPatterns]) } : {}),\n });\n for (const issue of loaded.issues) process.emitWarning(`${RUNNER_SETTINGS_FILE}: ${issue}`);\n const environment = host.context.environment;\n // One per registry for this build in the hub: every session shares the connection and the loop that\n // keeps house for it, and the last session to close disposes them.\n const runnerHost = await resources.acquire(\n ['runner-host', environment[REGISTRY_PATH_ENV] ?? null],\n () =>\n createRunnerHost({\n processRegistry: createDefaultProcessRegistry(environment),\n clock: new SystemClock(),\n statusPollMs: RUNNER_STATUS_POLL_MS,\n historySweepMs: HISTORY_SWEEP_INTERVAL_MS,\n onWarning: (message) => process.emitWarning(message),\n }),\n (shared) => shared.dispose(),\n );\n const dependencies = createRunnerDependencies({\n settings,\n cwd: host.context.cwd,\n environment,\n processRegistry: runnerHost.processRegistry,\n ...(host.context.executionBudget === undefined ? {} : { executionBudget: host.context.executionBudget }),\n });\n const execution = agent?.context;\n const wakeAgent =\n execution &&\n (async (content: string, requestId: string, notice: string): Promise<void> => {\n try {\n // 'steer' joins a running turn and starts one on an idle session.\n // Reuse completion identity across retries and acknowledge admission, not settlement.\n // A custom message, not a prompt: the model reads it as input, while the\n // transcript shows the notice as an event rather than as the person's words.\n if (!execution.session.admitMessage) throw new Error('The session cannot admit a runner notification.');\n await execution.session.admitMessage(\n { type: RUNNER_FINISHED_MESSAGE, text: content, display: true, details: { notice } },\n 'steer',\n requestId,\n );\n } catch (error) {\n // Keep completion ownership with the watcher until admission succeeds.\n await Promise.resolve()\n .then(() => execution.client.notify({ body: String(error), level: 'warning' }))\n .catch(() => undefined);\n throw error;\n }\n });\n const runtime = createRunnerServerRuntime(dependencies, runnerHost, host.context.directEvents, wakeAgent, execution);\n const bash = (wakeOnExit: boolean) =>\n createHeadlessBashTool(\n {\n run: async (request) => {\n await runtime.ensureSession(request.sessionId);\n const result = await dependencies.bashRunService.run({\n ...request,\n ...(signal ? { signal: request.signal ? AbortSignal.any([signal, request.signal]) : signal } : {}),\n });\n // Without this, nothing tells the agent the runner ended.\n if (wakeOnExit && result.kind === 'promoted') runtime.watchRunner(result.id);\n return result;\n },\n },\n (path, lines, bytes, tokens) => summarizeLog(path, lines, bytes, tokens, settings),\n settings,\n dependencies.environment,\n );\n const value = {\n tool: bash(true),\n // Remote MCP runners must not wake the local agent; the remote caller polls instead.\n remoteTool: bash(false),\n command: createHeadlessRunnersCommand(dependencies, {\n // Without the platform fs a missing folder still fails, only at spawn and less plainly.\n fs: readPoFs(context),\n onStart: (startedSessionId, failure) => runtime.reportStart(startedSessionId, failure),\n }),\n recoverCompaction: runtime.recoverCompaction,\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 // Package install is on the session-create path; bash awaits supervision itself.\n onStart: execution\n ? () => {\n void runtime\n .ensureSession(execution.sessionId)\n .catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n }\n : undefined,\n onStop: runtime.dispose,\n };\n};\n\nexport type RunnerServerScope = Awaited<ReturnType<typeof createRunnerServerRoot>>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createRunnerServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createRunnerServerRoot);\n","import type { PoHubChannel, PoHubChannelSource } from '@agimon-ai/po-core/hubChannel';\n\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport { type RunnerRunView } from '../../types/webRunners';\n\nfunction runnerPayload(value: unknown): value is { runs: RunnerRunView[] } {\n return typeof value === 'object' && value !== null && Array.isArray((value as { runs?: unknown }).runs);\n}\n\n/**\n * The runners data channel consumes lifecycle-owned snapshots from the session\n * server. Durable registry files are read by the owning session only, never by\n * this hub channel.\n */\nexport function createRunnersChannel(): PoHubChannel {\n return {\n frameType: RUNNER_RUNS_TYPE,\n start(host) {\n const latest = new Map<string, RunnerRunView[]>();\n const subscriptions = new Map<string, () => void>();\n const source: PoHubChannelSource = {\n payloadFor(scope) {\n const runs = latest.get(scope.sessionId);\n return runs === undefined ? undefined : { runs };\n },\n sessionAdded(scope) {\n subscriptions.get(scope.sessionId)?.();\n latest.delete(scope.sessionId);\n const unsubscribe = host.directEvents.subscribe(\n RUNNER_RUNS_TYPE,\n scope.sessionId,\n (payload) => {\n if (!runnerPayload(payload)) return;\n latest.set(scope.sessionId, payload.runs);\n host.publish(scope.sessionId, payload);\n },\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n },\n sessionRemoved(sessionId) {\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n latest.delete(sessionId);\n },\n close() {\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n latest.clear();\n },\n };\n return source;\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { createRunnersChannel } from '../../../services/runnersChannel';\nexport default defineChannel(function runnerRunsChannel(): PoHubChannel {\n return createRunnersChannel();\n});\n","export const LOG_DIR_NAME = 'logs';\nexport const STATE_EXTENSION = '.json';\nexport const RUNNING_STATE = 'running';\nexport const STATE_POLL_MS = 1000;\nexport const HEARTBEAT_MS = 15_000;\nexport const DEFAULT_LINES = 200;\nexport const MAX_LINES = 5000;\nexport const SAFE_SEGMENT = /^[\\w.@-]+$/;\nexport const SCREEN_POLL_MS = 100;\nexport const RAW_LOG_SUFFIX = '.raw';\nexport const MAX_INPUT_CHARS = 4096;\n/** Ceiling for one finite log/screen read, before base64 encoding. */\nexport const MAX_OUTPUT_BYTES = 64 * 1024;\nexport const NOT_ATTACHABLE = 'No attachable runner by that name in this session.';\n","export const RUNNER_API_BASE_PATH = 'runner';\nexport const RUN_ID_PARAM = 'runId';\nexport const RUNNER_SCREEN_EVENT = 'screen';\nexport const RUNNER_LOG_STREAM_EVENT = 'append';\nexport const RUNNER_LOG_PING_EVENT = 'ping';\n","import fs from 'node:fs';\n\nimport { POLL_MS } from '../../constants/logTail';\nimport type { ILogTail, LogTailHandle, LogTailOptions } from '../../types/logTail';\n\n/** How often the tail restats the file; the same cadence the CLI's --follow uses. */\n\n/**\n * Polling tail: read the delta between the last offset and the file's current\n * size, and hand back only complete lines.\n *\n * Same reliability posture as this package's runner watcher: the poll is the\n * mechanism, not an accelerator for a change notification. fs.watchFile only\n * fires when it observes a difference from a baseline it took itself, so a\n * write that lands before its first stat is a change it never reports; a\n * restat every tick has no such window.\n *\n * A write that lands mid-line is common, so the trailing fragment is held\n * until its newline arrives rather than emitted as a line that will change.\n */\nexport class LogTail implements ILogTail {\n follow(logPath: string, options: LogTailOptions): LogTailHandle {\n let offset = options.from;\n let pending = '';\n let closed = false;\n\n const drain = (size: number): void => {\n // A shrunken file was rotated or truncated; start over rather than\n // reading from an offset that now points into different content.\n if (size < offset) {\n offset = 0;\n pending = '';\n }\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(logPath, 'r');\n let bytesRead: number;\n try {\n bytesRead = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n offset = size;\n if (bytesRead <= 0) return;\n pending += buffer.subarray(0, bytesRead).toString('utf8');\n const lines = pending.split('\\n');\n pending = lines.pop() ?? '';\n if (lines.length > 0) options.onLines(lines, offset - Buffer.byteLength(pending, 'utf8'));\n };\n\n const tick = (): void => {\n if (closed) return;\n try {\n drain(fs.statSync(logPath).size);\n } catch (error) {\n // The runner has not opened its log yet; the next tick settles it.\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n options.onError(error instanceof Error ? error : new Error(String(error)));\n }\n };\n\n const timer = setInterval(tick, POLL_MS);\n // Following a log must never be the reason a process stays alive.\n timer.unref?.();\n return {\n close() {\n if (closed) return;\n closed = true;\n clearInterval(timer);\n },\n };\n }\n}\n","import type { LogSlice } from './logReader';\n\n/**\n * The runner log API, shared by this package's session-scoped routes and its\n * cockpit plugin. The two halves run in different processes, so the wire\n * vocabulary is declared here: the browser half may reach `src/types` and\n * nothing else on the server side.\n *\n * Where the routes live is not here. That is `src/types/apiRoutes`, which both\n * halves read, and the generated client turns into the URLs the page addresses.\n * The file a run writes to is still read from its metadata record by the server\n * alone.\n */\n\n/**\n * One chunk of an attached pane's output.\n *\n * Base64 because these are terminal bytes, escape sequences and all, and a\n * server-sent event is a line-oriented text frame that would mangle them.\n */\nexport interface RunnerScreenEvent {\n chunk: string;\n /** Byte offset just past this chunk, so a reconnect resumes rather than replays. */\n offset?: number;\n /** Set once the runner exits, so the page can stop watching. */\n ended?: boolean;\n}\n\n/** A finite byte slice for sealed HTTP followers. Decode chunks incrementally, not independently. */\nexport interface RunnerOutputResponse {\n /** Base64 bytes, preserving split UTF-8 characters and terminal escape sequences. */\n chunk: string;\n offset: number;\n /** The file was replaced or became shorter than the requested offset; discard the previous display/decoder state. */\n reset: boolean;\n /** Opaque raw file identity. Send back on screen/read to detect replacement, including regrowth. */\n generation?: string;\n exists: boolean;\n /** More bytes were present at read time; request the next slice before waiting. */\n hasMore: boolean;\n /** True only once the runner ended and all available bytes have been read. */\n ended: boolean;\n}\n\n/** The body of a keystroke POST. Text, not key codes: the pane wants bytes. */\nexport interface RunnerInputRequest {\n text: string;\n}\n\n/** The named SSE event carrying a RunnerScreenEvent payload. */\n\n/**\n * A log request as query parameters. `grep` is a literal substring, not a\n * regular expression, because that is what the reader matches; `lines` bounds\n * the answer to the last N lines of whatever survived the filter.\n */\nexport interface RunnerLogQueryParams {\n lines?: number;\n grep?: string;\n ignoreCase?: boolean;\n contextLines?: number;\n}\n\n/** The slice the log route answers with, plus which runner it came from. */\nexport interface RunnerLogResponse extends LogSlice {\n runId: string;\n /** True while the runner is still writing, so the page knows following is worth offering. */\n running: boolean;\n}\n\n/**\n * One server-sent event on the follow stream: the lines appended since the last\n * one. The stream never filters: a page that sets a query stops following and\n * reads a filtered slice instead, because grep with context is a whole-file\n * answer and cannot be assembled from a chunk at a time.\n */\nexport interface RunnerLogStreamEvent {\n /** Appended lines, in order; empty when the event only reports the runner ending. */\n lines: string[];\n /**\n * Byte offset just past the last line in this event.\n *\n * A follower that loses the stream resumes here. Without it the page could\n * only fall back to the offset its last slice reported, which every line\n * delivered since has already moved past, so a reconnect would replay them.\n */\n offset?: number;\n /** Set once the runner exits, so the page can stop following without polling the run list. */\n ended?: boolean;\n}\n\n/** The named SSE event carrying a RunnerLogStreamEvent payload. */\n\n/**\n * The named SSE event that carries nothing.\n *\n * A runner can be alive and silent for minutes, which on the wire is\n * indistinguishable from a connection that died. This is sent on a timer so\n * the socket keeps proving itself. Readers that only listen for `append`\n * ignore it, which is what makes it safe to add.\n */\n\n/** Query parameter names, shared so the page and the route cannot drift apart. */\nexport const RUNNER_LOG_PARAMS = {\n lines: 'lines',\n grep: 'grep',\n ignoreCase: 'ignoreCase',\n contextLines: 'contextLines',\n from: 'from',\n generation: 'generation',\n} as const;\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport { RUN_ID_PARAM } from '../constants/webRunnerLog';\nimport { RUNNER_LOG_PARAMS, type RunnerLogResponse, type RunnerOutputResponse } from './webRunnerLog';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path: the build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/runner/`, so the mount is\n * stated once, by the folder that creates it. This package stated it four times\n * before, once per URL builder, each of them re-spelling `/plugins/` and the\n * session prefix by hand.\n *\n * The Hono app registers these paths verbatim and the browser addresses them,\n * so a path cannot move on one side alone. `:runId` is Hono's own parameter\n * syntax for that reason; a caller supplies the value as `params`, and the\n * client encodes the segment.\n */\nconst routes = {\n log: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log`,\n query: [\n RUNNER_LOG_PARAMS.lines,\n RUNNER_LOG_PARAMS.grep,\n RUNNER_LOG_PARAMS.ignoreCase,\n RUNNER_LOG_PARAMS.contextLines,\n ],\n response: undefined as unknown as ApiResponseOf<RunnerLogResponse>,\n },\n /**\n * The live tail of that same log.\n *\n * `stream: true`, so the client exposes a URL and no call method: the\n * consumer is the browser’s own SSE reader, and a relayed call would\n * buffer a stream that never ends.\n */\n logStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** The unscrubbed pane bytes, for a terminal emulator rather than a reader. */\n screenStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** Finite byte reads keep sealed HTTP exchanges bounded, unlike SSE. */\n logRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/read`,\n query: [RUNNER_LOG_PARAMS.from],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n screenRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/read`,\n query: [RUNNER_LOG_PARAMS.from, RUNNER_LOG_PARAMS.generation],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n input: {\n method: 'POST',\n path: `/runners/:${RUN_ID_PARAM}/screen/input`,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { PoApi, PoApiContext, PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport {\n DEFAULT_LINES,\n HEARTBEAT_MS,\n LOG_DIR_NAME,\n MAX_INPUT_CHARS,\n MAX_OUTPUT_BYTES,\n MAX_LINES,\n NOT_ATTACHABLE,\n RAW_LOG_SUFFIX,\n RUNNING_STATE,\n SAFE_SEGMENT,\n SCREEN_POLL_MS,\n STATE_EXTENSION,\n STATE_POLL_MS,\n} from '../../constants/runnerLogApi';\nimport {\n RUN_ID_PARAM,\n RUNNER_API_BASE_PATH,\n RUNNER_LOG_PING_EVENT,\n RUNNER_LOG_STREAM_EVENT,\n RUNNER_SCREEN_EVENT,\n} from '../../constants/webRunnerLog';\nimport { LogReader } from '../../services/logReader';\nimport { LogTail } from '../../services/logTail';\nimport { PtyBackendChain } from '../../services/ptyBackendChain';\nimport { RmuxBackend } from '../../services/rmuxBackend';\nimport { RunnerPaths, resolveRunnerStoreDirectory, runnerStateDirFor } from '../../services/runnerPaths';\nimport { TmuxBackend } from '../../services/tmuxBackend';\nimport { parseRunnerRecord } from '../../services/webRunnerRuns';\nimport routes from '../../types/apiRoutes';\nimport type { ILogReader, LogQuery } from '../../types/logReader';\nimport type { ILogTail } from '../../types/logTail';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport type { RunnerRecord } from '../../types/runnerRegistry';\nimport {\n RUNNER_LOG_PARAMS,\n type RunnerLogResponse,\n type RunnerLogStreamEvent,\n type RunnerOutputResponse,\n type RunnerScreenEvent,\n} from '../../types/webRunnerLog';\n\n/** How often a follow re-reads the record to notice the runner exiting. */\n/**\n * How often a silent but living stream proves it is still there. Short enough\n * that an idle proxy does not reap the socket, long enough to cost nothing.\n */\n/** Kept in step with LogQuery's own default, so an unasked-for tail is the same size everywhere. */\n/** A page that asks for more than this gets this; the whole point of the route is a bounded read. */\n/** Path segments the client supplies are names, never paths. */\n/** Backends that expose a pane another process can read and write. */\nconst MULTIPLEXER_BACKENDS: ReadonlySet<string> = new Set(['rmux', 'tmux']);\n/** How often an attached pane's new bytes are picked up. */\n/** The unscrubbed copy the runner's sink writes beside the log, for interactive runs. */\n/** One keystroke batch; a paste is not a file transfer. */\n/** Said the same way for every reason, so the route never reports which check failed. */\n\nexport interface RunnerLogApiOptions {\n /** The session these routes answer for; the host owns exactly one. */\n sessionId: string;\n /** The store root; defaults to this environment's agent directory. */\n storeDir?: string;\n environment?: Readonly<Record<string, string | undefined>>;\n cwd?: string;\n logReader?: ILogReader;\n logTail?: ILogTail;\n /**\n * Reaches a live pane to read its screen and send it input.\n *\n * A session API is handed nothing but its session id, so it cannot touch the\n * in-process PTY handles the extension holds. This talks to the multiplexer\n * over its own socket instead, the same way the CLI's `input` verb does,\n * which is what makes an attached pane reachable across processes at all.\n */\n pane?: IRmuxBackend;\n}\n\n/** Rejects anything that could climb out of the session's own directory before a path is built. */\nfunction isSafeSegment(value: string): boolean {\n return value !== '' && value !== '.' && value !== '..' && SAFE_SEGMENT.test(value);\n}\n\n/** A log must stay inside its session even when a path component is a symlink. Missing files are normal. */\nfunction isSessionLog(file: string, logsDir: string): boolean {\n const resolved = path.resolve(file);\n if (resolved !== logsDir && !resolved.startsWith(`${logsDir}${path.sep}`)) return false;\n try {\n if (fs.lstatSync(file).isSymbolicLink()) return false;\n const realFile = fs.realpathSync(file);\n const realLogs = fs.realpathSync(logsDir);\n return realFile.startsWith(`${realLogs}${path.sep}`);\n } catch (error) {\n // The sink has not created its output yet; a later request validates the created path again.\n return isRecord(error) && error.code === 'ENOENT';\n }\n}\n\n/**\n * The log file a run wrote, or undefined when there is no such run or its\n * record points somewhere it has no business pointing.\n *\n * The record names its own log path, and that record is written by another\n * process, so the path is checked rather than trusted: it must sit inside this\n * session's own logs directory. Without that check a doctored record would\n * turn this route into an arbitrary file read.\n */\nfunction resolveLogFile(stateDir: string, logsDir: string, sessionId: string, runId: string): RunnerRecord | undefined {\n const statePath = path.join(stateDir, `${runId}${STATE_EXTENSION}`);\n let raw: string;\n try {\n raw = fs.readFileSync(statePath, 'utf8');\n } catch {\n return undefined; // No such run in this session; the route answers 404.\n }\n const record = parseRunnerRecord(raw);\n if (record === undefined || record.id !== runId || record.sessionId !== sessionId) return undefined;\n if (!isSessionLog(record.logPath, logsDir)) return undefined;\n return record;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction numberParam(value: string | undefined, fallback: number, max: number): number {\n const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed < 0) return fallback;\n return Math.min(parsed, max);\n}\n\n/** Missing output is normal before the sink opens it; other IO failures must not look like an empty read. */\nfunction readOutput(\n file: string,\n from: number,\n running: boolean,\n priorGeneration?: string,\n screen = false,\n): RunnerOutputResponse {\n let descriptor: number;\n try {\n descriptor = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);\n } catch (error) {\n if (!isRecord(error) || error.code !== 'ENOENT') throw error;\n return { chunk: '', offset: from, reset: false, exists: false, hasMore: false, ended: !running };\n }\n try {\n const stat = fs.fstatSync(descriptor, { bigint: true });\n const size = Number(stat.size);\n // Identity and bytes come from the same descriptor, never a separate marker read.\n const generation = screen ? `${stat.dev}:${stat.ino}:${stat.birthtimeNs}` : undefined;\n const reset = from > size || (priorGeneration !== undefined && generation !== priorGeneration);\n const start = reset ? 0 : from;\n const length = Math.min(size - start, MAX_OUTPUT_BYTES);\n const buffer = Buffer.alloc(length);\n const read = length === 0 ? 0 : fs.readSync(descriptor, buffer, 0, length, start);\n const offset = start + read;\n const hasMore = offset < size;\n return {\n ...(generation === undefined ? {} : { generation }),\n chunk: buffer.subarray(0, read).toString('base64'),\n offset,\n reset,\n exists: true,\n hasMore,\n ended: !running && !hasMore,\n };\n } finally {\n fs.closeSync(descriptor);\n }\n}\n\nfunction logQueryOf(url: URL): LogQuery {\n const grep = url.searchParams.get(RUNNER_LOG_PARAMS.grep) ?? undefined;\n return {\n lines: numberParam(url.searchParams.get(RUNNER_LOG_PARAMS.lines) ?? undefined, DEFAULT_LINES, MAX_LINES),\n ...(grep === undefined || grep === '' ? {} : { grep }),\n ignoreCase: url.searchParams.get(RUNNER_LOG_PARAMS.ignoreCase) === 'true',\n contextLines: numberParam(url.searchParams.get(RUNNER_LOG_PARAMS.contextLines) ?? undefined, 0, MAX_LINES),\n };\n}\n\n/**\n * This package's HTTP surface: one runner's log, as a bounded slice and as a\n * live tail.\n *\n * The routes are mounted inside one session's own server, so they name a runner\n * and nothing else; the session is the host, not a parameter. The reader only\n * ever returns the last N lines of what matched, so both routes stay bounded no\n * matter how large the file on disk is.\n *\n * Paths come from src/types/apiRoutes, the one table the browser's generated\n * client reads too, so this app and the cockpit cannot disagree about where a\n * route lives. They stay relative to the mount the host strips.\n */\nexport function createRunnerLogApi(options: RunnerLogApiOptions): Hono {\n const logReader = options.logReader ?? new LogReader();\n const logTail = options.logTail ?? new LogTail();\n const sessionId = options.sessionId;\n const paths = new RunnerPaths(options.cwd, options.environment);\n const storeDir = options.storeDir ?? resolveRunnerStoreDirectory(options.environment ?? process.env);\n const stateDir =\n options.storeDir === undefined ? paths.stateDirectory(sessionId) : runnerStateDirFor(storeDir, sessionId);\n const logsDir =\n options.storeDir === undefined ? paths.logDirectory(sessionId) : path.join(storeDir, sessionId, LOG_DIR_NAME);\n const app = new Hono();\n\n // Built on first use, not on mount: most sessions never attach to a pane,\n // and constructing this probes the filesystem for a multiplexer binary.\n let paneBackend: IRmuxBackend | undefined = options.pane;\n const pane = (): IRmuxBackend => {\n paneBackend ??= (() => {\n const paths = new RunnerPaths(options.cwd, options.environment);\n return new PtyBackendChain(new RmuxBackend(paths), new TmuxBackend(paths));\n })();\n return paneBackend;\n };\n\n /** The record behind a request, or undefined when this session has no such run. */\n const runnerOf = (runId: string): RunnerRecord | undefined =>\n isSafeSegment(runId) ? resolveLogFile(stateDir, logsDir, sessionId, runId) : undefined;\n\n /**\n * The owned pane whose recorded output a request may read.\n *\n * Same posture as the log routes: the record is written by another process,\n * so nothing on it is trusted until it is checked. The target must be one\n * this package owns, and the run must have been started\n * interactive. A non-interactive run has no terminal to type into, and a\n * subprocess-backed run has no pane at all.\n */\n const paneOf = (runId: string): { record: RunnerRecord; target: string } | undefined => {\n const record = runnerOf(runId);\n if (record === undefined || record.interactive !== true) return undefined;\n const target = record.backendTarget;\n if (target === undefined || !MULTIPLEXER_BACKENDS.has(record.backend) || !isSafeSegment(target)) return undefined;\n return { record, target };\n };\n const attachableOf = (runId: string): { record: RunnerRecord; target: string } | undefined => {\n const owned = paneOf(runId);\n return owned?.record.state === RUNNING_STATE ? owned : undefined;\n };\n\n for (const [route, screen] of [\n [routes.logRead, false],\n [routes.screenRead, true],\n ] as const) {\n app.get(route.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n // A completed pane is readable so a finite follower can drain its last bytes. Input still requires a live pane.\n const record = screen ? paneOf(runId)?.record : runnerOf(runId);\n if (record === undefined)\n return context.json({ error: screen ? NOT_ATTACHABLE : `No runner '${runId}' in this session.` }, 404);\n const value = context.req.query(RUNNER_LOG_PARAMS.from);\n const from = value === undefined ? 0 : Number(value);\n if ((value !== undefined && !/^\\d+$/.test(value)) || !Number.isSafeInteger(from) || from < 0) {\n return context.json({ error: 'Expected a non-negative safe integer byte offset.' }, 400);\n }\n const file = screen ? `${record.logPath}${RAW_LOG_SUFFIX}` : record.logPath;\n if (!isSessionLog(file, logsDir)) return context.json({ error: 'No readable output in this session.' }, 404);\n return context.json(\n readOutput(\n file,\n from,\n record.state === RUNNING_STATE,\n screen ? context.req.query(RUNNER_LOG_PARAMS.generation) : undefined,\n screen,\n ),\n );\n });\n }\n\n app.get(routes.log.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const record = runnerOf(runId);\n if (record === undefined) return context.json({ error: `No runner '${runId}' in this session.` }, 404);\n const slice = logReader.read(record.logPath, logQueryOf(new URL(context.req.url)));\n const body: RunnerLogResponse = { ...slice, runId, running: record.state === RUNNING_STATE };\n return context.json(body);\n });\n\n app.get(routes.logStream.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const record = runnerOf(runId);\n if (record === undefined) return context.json({ error: `No runner '${runId}' in this session.` }, 404);\n const from = numberParam(\n new URL(context.req.url).searchParams.get(RUNNER_LOG_PARAMS.from) ?? undefined,\n 0,\n Number.MAX_SAFE_INTEGER,\n );\n\n return streamSSE(context, async (stream) => {\n const send = (event: RunnerLogStreamEvent): Promise<void> =>\n stream.writeSSE({ event: RUNNER_LOG_STREAM_EVENT, data: JSON.stringify(event) });\n\n // The tail and the state poll both push onto one queue, so the stream\n // writes in the order things happened rather than interleaving two\n // writers onto the same socket.\n let pump: Promise<void> = Promise.resolve();\n const queue = (event: RunnerLogStreamEvent): void => {\n pump = pump.then(() => send(event)).catch(() => undefined);\n };\n\n let finish = (): void => undefined;\n const done = new Promise<void>((resolve) => {\n finish = resolve;\n });\n const handle = logTail.follow(record.logPath, {\n from,\n onLines: (lines, completeThrough) => queue({ lines, offset: completeThrough }),\n onError: () => finish(),\n });\n // A finished runner writes nothing more, so the page is told once and\n // the stream ends rather than holding a socket open forever.\n const poll = setInterval(() => {\n const current = runnerOf(runId);\n if (current !== undefined && current.state === RUNNING_STATE) return;\n queue({ lines: [], ended: true });\n finish();\n }, STATE_POLL_MS);\n // A runner can be alive and produce nothing for minutes. Without this the\n // page cannot tell that from a stream that quietly died.\n const heartbeat = setInterval(() => {\n pump = pump.then(() => stream.writeSSE({ event: RUNNER_LOG_PING_EVENT, data: '' })).catch(() => undefined);\n }, HEARTBEAT_MS);\n\n stream.onAbort(() => finish());\n await done;\n clearInterval(poll);\n clearInterval(heartbeat);\n handle.close();\n await pump;\n });\n });\n\n app.get(routes.screenStream.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const attachable = attachableOf(runId);\n if (attachable === undefined) return context.json({ error: NOT_ATTACHABLE }, 404);\n // The record's log path is already proven to sit inside this session's own\n // logs directory, and the raw copy is that same path with a suffix, so it\n // cannot point anywhere new.\n const rawPath = `${attachable.record.logPath}${RAW_LOG_SUFFIX}`;\n const from = numberParam(\n new URL(context.req.url).searchParams.get(RUNNER_LOG_PARAMS.from) ?? undefined,\n 0,\n Number.MAX_SAFE_INTEGER,\n );\n\n return streamSSE(context, async (stream) => {\n let pump: Promise<void> = Promise.resolve();\n const queue = (event: RunnerScreenEvent): void => {\n pump = pump\n .then(() => stream.writeSSE({ event: RUNNER_SCREEN_EVENT, data: JSON.stringify(event) }))\n .catch(() => undefined);\n };\n\n let finish = (): void => undefined;\n const done = new Promise<void>((resolve) => {\n finish = resolve;\n });\n\n let offset = from;\n const drain = (): void => {\n let size: number;\n try {\n size = fs.statSync(rawPath).size;\n } catch {\n return; // The sink has not opened the file yet; the next tick settles it.\n }\n // The sink starts the file over when it passes its ceiling, so a\n // smaller file is a fresh one rather than a bad offset.\n if (size < offset) offset = 0;\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(rawPath, 'r');\n let read: number;\n try {\n read = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n if (read <= 0) return;\n offset += read;\n // Bytes, not lines: a terminal's escape sequences straddle newlines and\n // mean nothing once split on them.\n queue({ chunk: buffer.subarray(0, read).toString('base64'), offset });\n };\n\n const poll = setInterval(() => {\n const current = runnerOf(runId);\n drain();\n if (current === undefined || current.state !== RUNNING_STATE) {\n queue({ chunk: '', offset, ended: true });\n finish();\n }\n }, SCREEN_POLL_MS);\n\n stream.onAbort(() => finish());\n await done;\n clearInterval(poll);\n await pump;\n });\n });\n\n /**\n * Types into a live pane.\n *\n * This is the one route in the package that writes rather than reads, so it\n * is deliberately narrow: an owned target, still running, started\n * interactive, and a body that is nothing but text.\n */\n app.post(routes.input.path, async (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const attachable = attachableOf(runId);\n if (attachable === undefined) return context.json({ error: NOT_ATTACHABLE }, 404);\n\n let body: unknown;\n try {\n body = await context.req.json();\n } catch {\n return context.json({ error: 'Expected a JSON body carrying text.' }, 400);\n }\n const text = isRecord(body) ? body.text : undefined;\n if (typeof text !== 'string' || text === '') return context.json({ error: 'Expected a non-empty text.' }, 400);\n if (text.length > MAX_INPUT_CHARS) return context.json({ error: 'That is more input than a pane takes.' }, 413);\n\n const delivered = await pane().input(attachable.target, text);\n if (!delivered) return context.json({ error: 'The runner did not take the input.' }, 502);\n return context.json({ delivered: true });\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built session entry. */\nexport const api: PoApi = {\n basePath: RUNNER_API_BASE_PATH,\n start(context: PoApiContext): PoApiHandler {\n // The host is one session's server, so its id is the whole scope; a hub\n // would have handed no session at all, and there is nothing to answer.\n const app = createRunnerLogApi({\n sessionId: context.sessionId ?? '',\n cwd: context.cwd,\n environment: context.environment,\n });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request: a follow's watch and its poll are both\n // torn down when its own stream ends or the socket aborts.\n close: () => undefined,\n };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/runnerLogApi';\n\n/**\n * Mounts this package's HTTP API at `runner`, which the folder names.\n *\n * The folder is the declaration: the build reads the base path off it and hands\n * the browser a client bound to the same segment, so the mount is stated once\n * rather than in a constant and in four URL builders that were each free to\n * drift.\n */\nexport default defineRoute(api);\n","import { defineServerCommand, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\nexport default defineServerCommand(\n (context: WithRoot<PoServerPluginContext, RunnerServerScope>) => context.root.command,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(({ root }: WithRoot<PoServerPluginContext, RunnerServerScope>) => ({\n event: 'session_compact' as const,\n handle: root.recoverCompaction,\n}));\n","import { defineResource } from '@agimon-ai/po-core/extensionFile';\nimport { PO_HELP_WHEN } from '@agimon-ai/po-core/help';\nimport { packageResourcePath, readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineResource({\n name: 'po-use-runner',\n description:\n 'Use Po Pi Runner to supervise shell commands, inspect durable logs, provide interactive input, and stop background runs.',\n when: PO_HELP_WHEN,\n kind: 'skill' as const,\n path: packageResourcePath(import.meta.url, 'src/prompts/po-use-runner/SKILL.md'),\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-runner/SKILL.md'),\n});\n","import { defineServerTool, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\nexport default defineServerTool((context: WithRoot<PoServerPluginContext, RunnerServerScope>) => context.root.tool);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelRunnerRuns from '../src/extensions/(backend)/channel/runnerRuns.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/runner/route.server';\nimport commandRunners from '../src/extensions/workspaces/sessions/(backend)/command/runners.server';\nimport hookSessionCompact from '../src/extensions/workspaces/sessions/(backend)/hook/session-compact.server';\nimport resourcePoUseRunner from '../src/extensions/workspaces/sessions/(backend)/resource/po-use-runner.server';\nimport toolBash from '../src/extensions/workspaces/sessions/(backend)/tool/bash.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-runner',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n global: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'runner_runs' }, channelRunnerRuns())]; },\n }),\n workspace: (context) => ({\n get channels(): PoServerSessionPlugin['channels'] { return defined([context.hosted('global') ? undefined : () => via({ frameType: 'runner_runs' }, channelRunnerRuns())]); },\n }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): PoServerSessionPlugin['channels'] { return defined([context.hosted('global') ? undefined : () => via({ frameType: 'runner_runs' }, channelRunnerRuns())]); },\n get commands(): PoServerSessionPlugin['commands'] { return [via({ name: 'runners' }, at(commandRunners, contextSession))]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return [via({ event: 'session_compact' }, at(hookSessionCompact, contextSession))]; },\n get resources(): PoServerSessionPlugin['resources'] { return [at(resourcePoUseRunner, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return [via({ name: 'bash' }, at(toolBash, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.cjs","names":["isRunnerRecord","createPoTelemetry","reconcileActiveRunners","getLogTtlMs","runCleanups","stopRunnerProcess","TERM_GRACE_MS","resolveRootSessionId","formatRunnerStatus","formatRunnerFinished","formatRunnerFinishedNotice","activeRunnerRecovery","PO_BACKGROUND_WORK_SERVICE","readPoBackgroundWorkService","fs","path","CONFIG_DIR_NAME","createDefaultProcessRegistry","SystemClock","createRunnerDependencies","createHeadlessBashTool","summarizeLog","createHeadlessRunnersCommand","readPoFs","defineRoot","defineChannel","fs","path","fs","LogReader","RunnerPaths","resolveRunnerStoreDirectory","runnerStateDirFor","Hono","PtyBackendChain","RmuxBackend","TmuxBackend","streamSSE","defineRoute","defineServerCommand","defineHook","defineResource","PO_HELP_WHEN","packageResourcePath","readPackageResource","defineServerTool","defineServerPlugin","composeServerProviderOverrides","rootSession","composeRootInjects","channelRunnerRuns","composeRootHooks","apiRoute","commandRunners","hookSessionCompact","resourcePoUseRunner","toolBash"],"sources":["../../src/constants/runnerRuntime.ts","../../src/constants/runnerSettings.ts","../../src/services/runnerHost/index.ts","../../src/constants/webRunners.ts","../../src/constants/webRunnerRuns.ts","../../src/services/webRunnerRuns/index.ts","../../src/services/runnerServerRuntime/index.ts","../../src/services/runnerSettings/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/runnersChannel/index.ts","../../src/extensions/(backend)/channel/runnerRuns.server.ts","../../src/constants/runnerLogApi.ts","../../src/constants/webRunnerLog.ts","../../src/services/logTail/index.ts","../../src/types/webRunnerLog.ts","../../src/types/apiRoutes.ts","../../src/services/runnerLogApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/runner/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/runners.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-compact.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/po-use-runner.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/bash.server.ts","../../generated/server.ts"],"sourcesContent":["export const LEADER_SOURCE = '@agimon-ai/po-runner';\nexport const LEADER_GROUP_ORDER = 67;\nexport const ERR_REQUIRES_INTERACTIVE = '/runners requires interactive mode';\nexport const ERR_STOP_USAGE = 'Usage: /runners stop <runner-id> [reason]';\nexport const ERR_START_USAGE = 'Usage: /runners start [name=x] [cwd=y] [interactive=true] -- <command>';\nexport const SESSION_START_EVENT = 'session_start';\nexport const RUNNER_FINISHED_MESSAGE = 'po-runner-finished';\nexport const COMPLETED_STATE = 'completed';\nexport const RMUX_BACKEND = 'rmux';\nexport const STOPPED_REASON = 'stopped';\nexport const RUNNER_STATUS_KEY = 'po-runner-runners';\nexport const BACKGROUND_WORK_PROVIDER = 'po-runner';\nexport const RUNNER_FOOTER_ORDER = 10;\nexport const RUNNER_STATUS_POLL_MS = 500;\nexport const HISTORY_SWEEP_INTERVAL_MS = 600_000;\n","export const RUNNER_SETTINGS_FILE = 'po-runner.json';\nexport const RATIO_KEYS = ['headRatio', 'errorBudgetRatio'] as const;\nexport const MAX_RESULT_BYTES_CEILING = 262_144;\nexport const MAX_RESULT_LINES_CEILING = 5_000;\nexport const MAX_RESULT_TOKENS_CEILING = 100_000;\nexport const MAX_ERROR_ENTRIES_CEILING = 100;\nexport const MAX_PATTERNS = 32;\nexport const MAX_PATTERN_LENGTH = 512;\n","import type { IClock } from '../../types/clock';\nimport type { ProcessRegistryPort } from '../runnerRegistry';\nimport type { RunnerHost, RunnerHostOptions, RunnerHostSession } from './type';\n\nexport type { RunnerHost, RunnerHostOptions, RunnerHostSession } from './type';\n\nconst describe = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\n/** Runs `pass` again `ms` after the previous pass finished, so two passes never overlap. */\nfunction repeat(clock: IClock, ms: number, pass: () => Promise<void>): { stop(): Promise<void> } {\n let stopped = false;\n let cancel: (() => void) | undefined;\n let running: Promise<void> | undefined;\n const arm = (): void => {\n cancel = clock.after(ms, () => {\n cancel = undefined;\n running = pass().finally(() => {\n running = undefined;\n if (!stopped) arm();\n });\n });\n };\n arm();\n return {\n async stop() {\n stopped = true;\n cancel?.();\n await running;\n },\n };\n}\n\n/**\n * The housekeeping every session running one po-runner build in one hub shares.\n *\n * A session used to open its own registry connection and arm its own status poll and history\n * sweep, and each sweep walked the whole runner store. Here one loop refreshes the attached sessions\n * one at a time, one sweep per store spares every attached session at once, and runners a crashed\n * server left behind are reclaimed once rather than at every session start.\n */\nexport function createRunnerHost(options: RunnerHostOptions): RunnerHost {\n const sessions = new Set<RunnerHostSession>();\n let closed = false;\n let reclaiming: Promise<void> | undefined;\n let sweeping: Promise<void> | undefined;\n\n const refreshAll = async (): Promise<void> => {\n // A Set skips what is deleted mid-pass, so a session detached during a pass is not refreshed after.\n for (const session of sessions) {\n if (closed) return;\n try {\n await session.refresh();\n } catch (error) {\n options.onWarning(`Could not refresh po-runner session ${session.sessionId}: ${describe(error)}`);\n }\n }\n };\n\n const sweepAll = (): Promise<void> => {\n sweeping ??= (async () => {\n try {\n const byStore = new Map<string, RunnerHostSession[]>();\n for (const session of sessions) {\n const store = session.paths.storeDirectory();\n byStore.set(store, [...(byStore.get(store) ?? []), session]);\n }\n for (const group of byStore.values()) {\n const [first] = group;\n if (closed || first === undefined) return;\n const live = new Set(group.map((session) => session.sessionId));\n const now = options.clock.now();\n const result = first.paths.sweepHistoryAsync\n ? await first.paths.sweepHistoryAsync(first.historyTtlMs, now, live)\n : first.paths.sweepHistory(first.historyTtlMs, now, live);\n for (const error of result.errors) options.onWarning(error);\n }\n } catch (error) {\n options.onWarning(`Could not sweep po-runner history: ${describe(error)}`);\n }\n })().finally(() => {\n sweeping = undefined;\n });\n return sweeping;\n };\n\n const polling = repeat(options.clock, options.statusPollMs, refreshAll);\n const sweeper = repeat(options.clock, options.historySweepMs, sweepAll);\n const processRegistry: ProcessRegistryPort = {\n registerProcess: (...args) => options.processRegistry.registerProcess(...args),\n releaseProcess: (...args) => options.processRegistry.releaseProcess(...args),\n listProcesses: (...args) => options.processRegistry.listProcesses(...args),\n // Sessions come and go; the connection lives as long as the host.\n close: () => undefined,\n };\n\n return {\n processRegistry,\n attach(session) {\n if (closed) throw new Error('The po-runner host is disposed.');\n sessions.add(session);\n if (reclaiming === undefined) {\n // Once per host: runners and history a crashed server left behind belong to no session.\n reclaiming = session\n .reclaimOrphans()\n .catch((error: unknown) => options.onWarning(`Could not reclaim orphaned runners: ${describe(error)}`));\n void sweepAll();\n }\n return () => {\n sessions.delete(session);\n };\n },\n async dispose() {\n if (closed) return;\n closed = true;\n sessions.clear();\n await Promise.all([polling.stop(), sweeper.stop(), reclaiming, sweeping]);\n options.processRegistry.close();\n },\n };\n}\n","export const RUNNER_RUNS_TYPE = 'runner_runs';\n","export const FINISHED_RUNNER_RETENTION_MS = 600_000;\n","import { FINISHED_RUNNER_RETENTION_MS } from '../../constants/webRunnerRuns';\nimport type { RunnerRecord } from '../../types/runnerRegistry';\nimport type { RunnerRunView } from '../../types/webRunners';\nimport { isRunnerRecord } from '../runnerRecord';\n\n/** How long a finished runner stays in the cockpit's feed after it exited. */\n\n/** One on-disk metadata record, or undefined when the text is not a runner record. */\nexport function parseRunnerRecord(raw: string): RunnerRecord | undefined {\n try {\n const parsed: unknown = JSON.parse(raw);\n return isRunnerRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined; // Mid-write or foreign file; the next scan settles it.\n }\n}\n\nexport function toRunnerRunView(record: RunnerRecord): RunnerRunView {\n return {\n id: record.id,\n name: record.name,\n pid: record.pid,\n command: record.command,\n cwd: record.cwd,\n interactive: record.interactive,\n backend: record.backend,\n state: record.state,\n promoted: record.promoted,\n startedAt: record.startedAt,\n logPath: record.logPath,\n ...(record.exit\n ? {\n exit: {\n reason: record.exit.reason,\n code: record.exit.code,\n signal: record.exit.signal,\n ...(record.exit.terminationReason === undefined\n ? {}\n : { terminationReason: record.exit.terminationReason }),\n ...(record.exit.stopReason === undefined ? {} : { stopReason: record.exit.stopReason }),\n finishedAt: record.exit.finishedAt,\n },\n }\n : {}),\n };\n}\n\nfunction finishedAt(record: RunnerRecord): number {\n const parsed = record.exit === undefined ? Number.NaN : Date.parse(record.exit.finishedAt);\n return Number.isFinite(parsed) ? parsed : Date.parse(record.startedAt);\n}\n\n/**\n * The feed the cockpit shows: running runners first, newest start first, then\n * the recently finished ones, newest exit first. Finished runners older than\n * the retention window are left out; the registry keeps them for the CLI.\n */\nexport function presentRunnerRuns(records: readonly RunnerRecord[], now: number): RunnerRunView[] {\n const running = records\n .filter((record) => record.state === 'running')\n .sort((left, right) => right.startedAt.localeCompare(left.startedAt));\n const finished = records\n .filter((record) => record.state === 'completed' && now - finishedAt(record) <= FINISHED_RUNNER_RETENTION_MS)\n .sort((left, right) => finishedAt(right) - finishedAt(left));\n return [...running, ...finished].map(toRunnerRunView);\n}\n","import {\n PO_BACKGROUND_WORK_SERVICE,\n readPoBackgroundWorkService,\n type BackgroundProviderWorkItem,\n type BackgroundWorkProviderHandle,\n} from '@agimon-ai/po-core/backgroundWork';\nimport { resolveRootSessionId } from '@agimon-ai/po-core/childProcess';\nimport { runCleanups } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessActivity, PoHeadlessExecutionContext } from '@agimon-ai/po-core/headless';\nimport type { PoServerHostService } from '@agimon-ai/po-core/serverPlugin';\nimport { createPoTelemetry } from '@agimon-ai/po-telemetry';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { LIVENESS_POLL_MS, TERM_GRACE_MS } from '../../constants/launcher';\nimport { BACKGROUND_WORK_PROVIDER, RUNNER_STATUS_KEY } from '../../constants/runnerRuntime';\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport type { RunnerStartFailureView } from '../../types/webRunners';\nimport { activeRunnerRecovery } from '../compaction';\nimport { reconcileActiveRunners, stopRunnerProcess } from '../reconcile';\nimport { getLogTtlMs } from '../runnerConfig';\nimport type { RunnerDependencies } from '../runnerDependencies/type';\nimport type { RunnerHost } from '../runnerHost';\nimport { formatRunnerStatus } from '../runnerRecord';\nimport { formatRunnerFinished, formatRunnerFinishedNotice } from '../runnerRecord';\nimport { presentRunnerRuns } from '../webRunnerRuns';\n\nconst COMPLETION_RETRY_MS = 1_000;\n\nexport interface RunnerServerRuntime {\n readonly container: RunnerDependencies;\n readonly activity: PoHeadlessActivity;\n readonly backgroundWorkPlugin: (context: Context) => void;\n ensureSession(sessionId: string): Promise<void>;\n /** Wakes the agent once when this runner, which it sent to the background, exits. */\n watchRunner(id: string): void;\n /** Tells the runners panel why a start it asked for did not happen; undefined clears it after one that did. */\n reportStart(sessionId: string, failure: string | undefined): void;\n recoverCompaction(this: void, event: Readonly<Record<string, unknown>>): Promise<void>;\n dispose(this: void): Promise<void>;\n}\nexport function createRunnerServerRuntime(\n container: RunnerDependencies,\n /** What every session of this build in the hub shares: the registry and the housekeeping loop. */\n host: RunnerHost,\n directEvents: NonNullable<PoServerHostService['context']['directEvents']>,\n /** Absent when no agent runs in this session, so there is nobody to wake. */\n wakeAgent?: (content: string, requestId: string, notice: string) => Promise<void>,\n execution?: PoHeadlessExecutionContext,\n): RunnerServerRuntime {\n const telemetry = execution\n ? createPoTelemetry({\n serviceName: 'po-runner',\n packageName: '@agimon-ai/po-runner',\n cwd: execution.cwd,\n env: execution.environment,\n enableLogs: true,\n enableTraces: true,\n })\n : undefined;\n let sessionId: string | undefined;\n let recoveredCompactionId: string | undefined;\n let supervision: Promise<void> | undefined;\n let runtimeDisposal: Promise<void> | undefined;\n let disposal: Promise<void> | undefined;\n let backgroundWorkItems: BackgroundProviderWorkItem[] = [];\n let backgroundWorkProvider: BackgroundWorkProviderHandle | undefined;\n // Nothing else tells the model a background runner ended, so without this an\n // agent that finished its turn waiting on one idles until a person prompts it.\n const watched = new Set<string>();\n let unsubscribeWatched: (() => void) | undefined;\n let wakes: Promise<void> = Promise.resolve();\n let closing = false;\n let retryTimer: ReturnType<typeof setTimeout> | undefined;\n /** Stops the shared loop refreshing this session and sweeping around it. */\n let detach: (() => void) | undefined;\n let startFailure: RunnerStartFailureView | undefined;\n let startFailures = 0;\n\n /** Every runner of every session the registry knows, reclaimed when its owning process is gone. */\n const reclaimOrphans = async (): Promise<void> => {\n const reclaimed = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: false,\n });\n for (const error of reclaimed.errors) process.emitWarning(error);\n };\n\n // The activity is optional and source-gated. The retained server plugin owns the\n // session runtime, so disabling the activity must not tear down its jobs.\n const ensureSession = (requestedSessionId: string): Promise<void> => {\n if (closing) return Promise.reject(new Error('Runner runtime is closing.'));\n if (sessionId !== undefined && sessionId !== requestedSessionId) {\n throw new Error(`Runner runtime belongs to session ${sessionId}.`);\n }\n sessionId ??= requestedSessionId;\n supervision ??= (async () => {\n void telemetry\n ?.recordEvent('po_runner.session_started', { outcome: 'started' })\n .catch((error: unknown) => process.emitWarning(`Could not record runner session telemetry: ${String(error)}`));\n if (closing) return;\n container.paths.setSessionId(requestedSessionId);\n if ((await container.lifeline.arm(requestedSessionId)) === undefined) {\n throw new Error('Could not arm runner supervision lifeline.');\n }\n if (closing) return;\n const promotedIds = (await container.runnerRegistry.listAll(requestedSessionId))\n .filter((record) => record.state === 'running' && record.promoted)\n .map((record) => record.id);\n // Only this session's runners: the shared host reclaims the ones other sessions left behind.\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: true,\n startupSessionId: requestedSessionId,\n active: await container.runnerRegistry.listBySession(requestedSessionId),\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n void telemetry\n ?.recordEvent('po_runner.reconciled', {\n 'runner.reclaimed_count': reconciled.reclaimed.length,\n 'runner.error_count': reconciled.errors.length,\n outcome: reconciled.errors.length === 0 ? 'completed' : 'degraded',\n })\n .catch((error: unknown) =>\n process.emitWarning(`Could not record runner reconcile telemetry: ${String(error)}`),\n );\n if (!closing) {\n unsubscribeRunnerUpdates ??= container.runnerRegistry.subscribe(\n () => requestPublish(requestedSessionId),\n requestedSessionId,\n );\n // Reconciliation may have completed retained promotions already.\n for (const id of promotedIds) watchRunner(id);\n requestPublish(requestedSessionId);\n // One shared loop refreshes this session from here on, and sweeps history around it.\n detach ??= host.attach({\n sessionId: requestedSessionId,\n paths: container.paths,\n historyTtlMs: getLogTtlMs(container.environment),\n refresh: () => refreshSession(requestedSessionId),\n reclaimOrphans,\n });\n }\n })().catch((error: unknown) => {\n supervision = undefined;\n throw error;\n });\n return supervision;\n };\n\n const disposeRuntime = (): Promise<void> => {\n runtimeDisposal ??= (async () => {\n await supervision?.catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n const ownedSessionId = sessionId;\n if (ownedSessionId === undefined) return;\n await runCleanups(\n [\n async () => {\n const records = await container.runnerRegistry.listBySession(ownedSessionId);\n const results = await Promise.allSettled(\n records.map(async (record) => {\n await stopRunnerProcess(record, container.launcher, container.rmuxBackend, {\n registry: container.runnerRegistry,\n intent: {\n reason: 'stopped',\n terminationReason: 'parent_session_cleanup',\n stopReason: 'session ended',\n },\n });\n // Success can mean only that SIGKILL or the mux close was accepted.\n // Allow asynchronous exit to land, but never complete a still-live runner.\n const deadline = container.clock.now() + TERM_GRACE_MS;\n while (container.processControl.isAlive(record.pid)) {\n if (container.clock.now() >= deadline) {\n throw new Error(`Could not stop runner ${record.id} during session shutdown`);\n }\n await new Promise<void>((resolve) => container.clock.after(LIVENESS_POLL_MS, resolve));\n }\n await container.runnerRegistry.complete(\n record.id,\n { reason: 'stopped', code: null, signal: null, stopReason: 'session ended' },\n ownedSessionId,\n );\n }),\n );\n const errors = results.filter((result) => result.status === 'rejected').map((result) => result.reason);\n if (errors.length) throw new AggregateError(errors, 'Runner process cleanup failed.');\n },\n () => container.ptyHost.disposeAll(),\n () => container.lifeline.dispose(),\n () => container.runnerRegistry.close(),\n ],\n 'Runner runtime cleanup failed.',\n );\n })();\n return runtimeDisposal;\n };\n\n let refreshInFlight: Promise<void> | undefined;\n const refreshSession = (ownedSessionId: string): Promise<void> => {\n if (closing) return Promise.resolve();\n refreshInFlight ??= refreshNow(ownedSessionId).finally(() => {\n refreshInFlight = undefined;\n });\n return refreshInFlight;\n };\n const refreshNow = async (ownedSessionId: string): Promise<void> => {\n if (closing) return;\n try {\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: false,\n active: await container.runnerRegistry.listBySession(ownedSessionId),\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n if (!closing) {\n requestPublish(ownedSessionId);\n wakeForFinished(ownedSessionId);\n }\n } catch (error) {\n process.emitWarning(`Could not refresh po-runner: ${String(error)}`);\n }\n };\n const publishRuns = async (ownedSessionId: string): Promise<void> => {\n try {\n const records = await container.runnerRegistry.listAll(ownedSessionId);\n if (closing) return;\n if (execution) {\n const root = resolveRootSessionId(ownedSessionId, container.environment);\n const count = (await container.runnerRegistry.list()).filter(\n (record) => (record.rootSessionId ?? record.sessionId) === root,\n ).length;\n if (closing) return;\n execution.client.setStatus(RUNNER_STATUS_KEY, formatRunnerStatus(count));\n }\n if (closing) return;\n const nextBackgroundWork = records\n .filter((record) => record.state === 'running' || watched.has(record.id))\n .map((record) => ({\n id: record.id,\n sessionId: record.sessionId,\n label: record.name,\n // An absent status holds ownership until completion admission succeeds.\n ...(record.state === 'running' ? { status: record.state } : {}),\n }));\n if (JSON.stringify(nextBackgroundWork) !== JSON.stringify(backgroundWorkItems)) {\n backgroundWorkItems = nextBackgroundWork;\n backgroundWorkProvider?.update();\n }\n directEvents.publish(RUNNER_RUNS_TYPE, ownedSessionId, {\n runs: presentRunnerRuns(records, Date.now()),\n ...(execution ? { sessionDirectory: execution.cwd } : {}),\n ...(startFailure ? { startFailure } : {}),\n });\n } catch (error) {\n process.emitWarning(`Could not publish runner state: ${String(error)}`);\n }\n };\n let publishInFlight: Promise<void> | undefined;\n let publishAgain = false;\n let unsubscribeRunnerUpdates: (() => void) | undefined;\n const requestPublish = (ownedSessionId: string): void => {\n if (closing) return;\n publishAgain = true;\n if (publishInFlight !== undefined) return;\n publishInFlight = (async () => {\n do {\n publishAgain = false;\n await publishRuns(ownedSessionId);\n } while (publishAgain && !closing);\n })()\n .catch((error: unknown) => {\n process.emitWarning(`Could not schedule runner state publication: ${String(error)}`);\n })\n .finally(() => {\n publishInFlight = undefined;\n if (publishAgain) requestPublish(ownedSessionId);\n });\n };\n\n // Sequenced so two runners exiting in the same tick each wake the agent once.\n const wakeForFinished = (ownedSessionId: string): void => {\n if (closing) return;\n wakes = wakes\n .then(async () => {\n if (closing) return;\n let retry = false;\n // Deleting the entry being visited is safe for a Set iterator.\n for (const id of watched) {\n if (closing) return;\n try {\n const record = await container.runnerRegistry.get(id, ownedSessionId);\n if (closing) return;\n if (record?.state === 'running') continue;\n // A record swept before it was seen leaves nothing to report.\n if (record)\n await wakeAgent?.(\n formatRunnerFinished(record),\n `runner-finished:${JSON.stringify([ownedSessionId, id])}`,\n formatRunnerFinishedNotice(record),\n );\n if (record)\n await telemetry?.recordEvent('po_runner.process_finished', {\n outcome: 'completed',\n 'runner.exit_code': record.exit?.code ?? 0,\n 'runner.backend': record.backend,\n });\n watched.delete(id);\n requestPublish(ownedSessionId);\n } catch (error) {\n retry = true;\n process.emitWarning(`Could not report finished runner ${id}: ${String(error)}`);\n }\n }\n if (retry && !closing && retryTimer === undefined) {\n retryTimer = setTimeout(() => {\n retryTimer = undefined;\n wakeForFinished(ownedSessionId);\n }, COMPLETION_RETRY_MS);\n retryTimer.unref?.();\n }\n if (watched.size === 0) {\n clearTimeout(retryTimer);\n retryTimer = undefined;\n unsubscribeWatched?.();\n unsubscribeWatched = undefined;\n }\n })\n .catch((error: unknown) => {\n process.emitWarning(`Could not report a finished runner: ${String(error)}`);\n });\n };\n\n const watchRunner = (id: string): void => {\n const ownedSessionId = sessionId;\n if (!wakeAgent || closing || ownedSessionId === undefined) return;\n watched.add(id);\n requestPublish(ownedSessionId);\n // Independent of the optional activity: the wake-up must not depend on a\n // browser watching the runner list.\n unsubscribeWatched ??= container.runnerRegistry.subscribe(() => wakeForFinished(ownedSessionId), ownedSessionId);\n // It may already have exited between promotion and subscription.\n wakeForFinished(ownedSessionId);\n };\n\n const activity: PoHeadlessActivity = {\n name: 'runner',\n // Session creation waits on activity start, so supervision runs in the\n // background (it publishes once ready); bash and compaction recovery still\n // await `ensureSession`.\n start: async (executionContext: PoHeadlessExecutionContext) => {\n void ensureSession(executionContext.sessionId).catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n requestPublish(executionContext.sessionId);\n return () => {\n // The retained owner keeps supervision alive. Optional activity toggles\n // must not repeat startup cleanup, subscriptions, or history initialization.\n };\n },\n };\n return {\n container,\n activity,\n ensureSession,\n watchRunner,\n reportStart(startedSessionId, failure) {\n startFailures += 1;\n startFailure =\n failure === undefined ? undefined : { id: `${String(Date.now())}-${String(startFailures)}`, message: failure };\n requestPublish(startedSessionId);\n },\n async recoverCompaction(event) {\n if (\n event.status !== 'completed' ||\n closing ||\n !execution ||\n typeof event.entryId !== 'string' ||\n event.entryId === recoveredCompactionId\n )\n return;\n const entries = await execution.session.entries();\n const compacted = entries.findLast((entry) => entry.type === 'compaction');\n if (closing || compacted?.id !== event.entryId) return;\n await ensureSession(execution.sessionId);\n let records;\n try {\n records = await container.runnerRegistry.listBySession(execution.sessionId);\n } catch {\n return;\n }\n if (closing) return;\n const active = records.filter((record) => record.state === 'running');\n const text = activeRunnerRecovery(active);\n if (!text) return;\n await execution.session.appendMessage?.({\n type: 'po-runner-compaction',\n text,\n display: false,\n details: { sessionId: execution.sessionId, runnerIds: active.map((record) => record.id) },\n });\n if (!closing) recoveredCompactionId = event.entryId;\n },\n backgroundWorkPlugin: (context) => {\n context.inject([PO_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readPoBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => backgroundWorkItems,\n });\n backgroundWorkProvider = registration;\n return () => {\n registration.dispose();\n if (backgroundWorkProvider === registration) backgroundWorkProvider = undefined;\n };\n });\n },\n dispose() {\n if (disposal) return disposal;\n closing = true;\n disposal = (async () => {\n // Session cleanup completes every runner it stops, and none of those may\n // start a turn in a session that is going away.\n closing = true;\n detach?.();\n detach = undefined;\n clearTimeout(retryTimer);\n retryTimer = undefined;\n watched.clear();\n unsubscribeWatched?.();\n unsubscribeWatched = undefined;\n unsubscribeRunnerUpdates?.();\n unsubscribeRunnerUpdates = undefined;\n await wakes;\n await publishInFlight;\n await refreshInFlight;\n try {\n execution?.client.setStatus(RUNNER_STATUS_KEY, undefined);\n } catch (error) {\n process.emitWarning(`Could not clear runner status: ${String(error)}`);\n }\n await runCleanups(\n [\n disposeRuntime,\n () => telemetry?.recordEvent('po_runner.session_finished', { outcome: 'stopped' }),\n () => telemetry?.shutdown(),\n ],\n 'Runner shutdown failed.',\n );\n })();\n return disposal;\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { CONFIG_DIR_NAME } from '@earendil-works/pi-coding-agent';\n\nimport {\n MAX_ERROR_ENTRIES_CEILING,\n MAX_PATTERNS,\n MAX_PATTERN_LENGTH,\n MAX_RESULT_BYTES_CEILING,\n MAX_RESULT_LINES_CEILING,\n MAX_RESULT_TOKENS_CEILING,\n RATIO_KEYS,\n RUNNER_SETTINGS_FILE,\n} from '../../constants/runnerSettings';\nimport type { IRunnerSettingsLoader, RunnerSettings, RunnerSettingsLoad } from '../../types/runnerSettings';\n\n/** Pi's convention is one JSON file per extension under the config directory. */\n\nconst EMPTY: RunnerSettingsLoad = { settings: {}, issues: [] };\nconst COUNT_KEYS = [\n 'maxResultBytes',\n 'maxResultLines',\n 'successMaxResultBytes',\n 'maxResultTokens',\n 'successMaxResultTokens',\n 'errorMaxEntries',\n 'errorMaxVariantsJoined',\n] as const;\nconst KNOWN_KEYS = new Set<string>([...RATIO_KEYS, ...COUNT_KEYS, 'errorPatterns']);\n/** Ceilings so a project file cannot flood the model's context. */\nconst CEILINGS: Readonly<Record<string, number>> = {\n maxResultBytes: MAX_RESULT_BYTES_CEILING,\n successMaxResultBytes: MAX_RESULT_BYTES_CEILING,\n maxResultTokens: MAX_RESULT_TOKENS_CEILING,\n successMaxResultTokens: MAX_RESULT_TOKENS_CEILING,\n maxResultLines: MAX_RESULT_LINES_CEILING,\n errorMaxEntries: MAX_ERROR_ENTRIES_CEILING,\n errorMaxVariantsJoined: MAX_ERROR_ENTRIES_CEILING,\n};\n\nexport class RunnerSettingsLoader implements IRunnerSettingsLoader {\n load(cwd: string, trusted: boolean): RunnerSettingsLoad {\n // Project-local configuration is untrusted input until the project is.\n if (!trusted) return EMPTY;\n\n let raw: string;\n try {\n raw = fs.readFileSync(path.join(cwd, CONFIG_DIR_NAME, RUNNER_SETTINGS_FILE), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return EMPTY;\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} could not be read`] };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} is not valid JSON`] };\n }\n return validate(parsed);\n }\n}\n\nfunction validate(parsed: unknown): RunnerSettingsLoad {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} must contain a JSON object`] };\n }\n\n const source = parsed as Record<string, unknown>;\n const settings: Record<string, unknown> = {};\n const issues: string[] = [];\n // A rejected key is reported rather than dropped: a silent typo reads as a\n // setting that simply does not work.\n for (const key of Object.keys(source)) if (!KNOWN_KEYS.has(key)) issues.push(`unknown key ${key}`);\n\n for (const key of COUNT_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n issues.push(`${key} must be a positive integer`);\n continue;\n }\n settings[key] = Math.min(value, CEILINGS[key] ?? value);\n }\n\n for (const key of RATIO_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {\n issues.push(`${key} must be a number above 0 and below 1`);\n continue;\n }\n settings[key] = value;\n }\n\n const patterns = source.errorPatterns;\n if (patterns !== undefined) {\n if (!Array.isArray(patterns) || patterns.some((entry) => typeof entry !== 'string')) {\n issues.push('errorPatterns must be an array of strings');\n } else {\n const accepted: string[] = [];\n for (const pattern of patterns.slice(0, MAX_PATTERNS) as string[]) {\n if (pattern.length > MAX_PATTERN_LENGTH) {\n issues.push('errorPatterns entry is too long');\n continue;\n }\n try {\n new RegExp(pattern, 'iu');\n accepted.push(pattern);\n } catch {\n issues.push(`errorPatterns entry is not a valid regular expression: ${pattern}`);\n }\n }\n if (accepted.length > 0) settings.errorPatterns = accepted;\n }\n }\n\n return { settings: settings as RunnerSettings, issues };\n}\n","import { readPoFs } from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { REGISTRY_PATH_ENV } from '../../../../../constants/runnerRegistry';\nimport {\n HISTORY_SWEEP_INTERVAL_MS,\n RUNNER_FINISHED_MESSAGE,\n RUNNER_STATUS_POLL_MS,\n} from '../../../../../constants/runnerRuntime';\nimport { RUNNER_SETTINGS_FILE } from '../../../../../constants/runnerSettings';\nimport { SystemClock } from '../../../../../services/clock';\nimport { createHeadlessBashTool, createHeadlessRunnersCommand } from '../../../../../services/headless';\nimport { summarizeLog } from '../../../../../services/logReader';\nimport { createRunnerDependencies } from '../../../../../services/runnerDependencies';\nimport { createRunnerHost } from '../../../../../services/runnerHost';\nimport { createDefaultProcessRegistry } from '../../../../../services/runnerRegistry';\nimport { createRunnerServerRuntime } from '../../../../../services/runnerServerRuntime';\nimport { RunnerSettingsLoader } from '../../../../../services/runnerSettings';\n\nexport const RUNNER_SERVER_SCOPE_SERVICE = 'po/runner-server-scope';\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/runner-server-scope': RunnerServerScope;\n }\n}\n\nexport const createRunnerServerRoot = async ({ context, host, agent, signal, resources }: PoServerPluginContext) => {\n if (!host.context.directEvents) throw new Error('Runner server plugin requires the session direct event bus.');\n if (!host.context.cwd || !host.context.environment) {\n throw new Error('Runner server plugin requires session cwd and environment.');\n }\n // Inherit the current headless Pi bridge's isProjectTrusted: () => true.\n // This preserves runner configuration parity, not a new general trust policy.\n const loaded = new RunnerSettingsLoader().load(host.context.cwd, true);\n const settings = Object.freeze({\n ...loaded.settings,\n ...(loaded.settings.errorPatterns ? { errorPatterns: Object.freeze([...loaded.settings.errorPatterns]) } : {}),\n });\n for (const issue of loaded.issues) process.emitWarning(`${RUNNER_SETTINGS_FILE}: ${issue}`);\n const environment = host.context.environment;\n // One per registry for this build in the hub: every session shares the connection and the loop that\n // keeps house for it, and the last session to close disposes them.\n const runnerHost = await resources.acquire(\n ['runner-host', environment[REGISTRY_PATH_ENV] ?? null],\n () =>\n createRunnerHost({\n processRegistry: createDefaultProcessRegistry(environment),\n clock: new SystemClock(),\n statusPollMs: RUNNER_STATUS_POLL_MS,\n historySweepMs: HISTORY_SWEEP_INTERVAL_MS,\n onWarning: (message) => process.emitWarning(message),\n }),\n (shared) => shared.dispose(),\n );\n const dependencies = createRunnerDependencies({\n settings,\n cwd: host.context.cwd,\n environment,\n processRegistry: runnerHost.processRegistry,\n ...(host.context.executionBudget === undefined ? {} : { executionBudget: host.context.executionBudget }),\n });\n const execution = agent?.context;\n const wakeAgent =\n execution &&\n (async (content: string, requestId: string, notice: string): Promise<void> => {\n try {\n // 'steer' joins a running turn and starts one on an idle session.\n // Reuse completion identity across retries and acknowledge admission, not settlement.\n // A custom message, not a prompt: the model reads it as input, while the\n // transcript shows the notice as an event rather than as the person's words.\n if (!execution.session.admitMessage) throw new Error('The session cannot admit a runner notification.');\n await execution.session.admitMessage(\n { type: RUNNER_FINISHED_MESSAGE, text: content, display: true, details: { notice } },\n 'steer',\n requestId,\n );\n } catch (error) {\n // Keep completion ownership with the watcher until admission succeeds.\n await Promise.resolve()\n .then(() => execution.client.notify({ body: String(error), level: 'warning' }))\n .catch(() => undefined);\n throw error;\n }\n });\n const runtime = createRunnerServerRuntime(dependencies, runnerHost, host.context.directEvents, wakeAgent, execution);\n const bash = (wakeOnExit: boolean) =>\n createHeadlessBashTool(\n {\n run: async (request) => {\n await runtime.ensureSession(request.sessionId);\n const result = await dependencies.bashRunService.run({\n ...request,\n ...(signal ? { signal: request.signal ? AbortSignal.any([signal, request.signal]) : signal } : {}),\n });\n // Without this, nothing tells the agent the runner ended.\n if (wakeOnExit && result.kind === 'promoted') runtime.watchRunner(result.id);\n return result;\n },\n },\n (path, lines, bytes, tokens) => summarizeLog(path, lines, bytes, tokens, settings),\n settings,\n dependencies.environment,\n );\n const value = {\n tool: bash(true),\n // Remote MCP runners must not wake the local agent; the remote caller polls instead.\n remoteTool: bash(false),\n command: createHeadlessRunnersCommand(dependencies, {\n // Without the platform fs a missing folder still fails, only at spawn and less plainly.\n fs: readPoFs(context),\n onStart: (startedSessionId, failure) => runtime.reportStart(startedSessionId, failure),\n }),\n recoverCompaction: runtime.recoverCompaction,\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 // Package install is on the session-create path; bash awaits supervision itself.\n onStart: execution\n ? () => {\n void runtime\n .ensureSession(execution.sessionId)\n .catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n }\n : undefined,\n onStop: runtime.dispose,\n };\n};\n\nexport type RunnerServerScope = Awaited<ReturnType<typeof createRunnerServerRoot>>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createRunnerServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createRunnerServerRoot);\n","import type { PoHubChannel, PoHubChannelSource } from '@agimon-ai/po-core/hubChannel';\n\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport { type RunnerRunView } from '../../types/webRunners';\n\nfunction runnerPayload(value: unknown): value is { runs: RunnerRunView[] } {\n return typeof value === 'object' && value !== null && Array.isArray((value as { runs?: unknown }).runs);\n}\n\n/**\n * The runners data channel consumes lifecycle-owned snapshots from the session\n * server. Durable registry files are read by the owning session only, never by\n * this hub channel.\n */\nexport function createRunnersChannel(): PoHubChannel {\n return {\n frameType: RUNNER_RUNS_TYPE,\n start(host) {\n const latest = new Map<string, RunnerRunView[]>();\n const subscriptions = new Map<string, () => void>();\n const source: PoHubChannelSource = {\n payloadFor(scope) {\n const runs = latest.get(scope.sessionId);\n return runs === undefined ? undefined : { runs };\n },\n sessionAdded(scope) {\n subscriptions.get(scope.sessionId)?.();\n latest.delete(scope.sessionId);\n const unsubscribe = host.directEvents.subscribe(\n RUNNER_RUNS_TYPE,\n scope.sessionId,\n (payload) => {\n if (!runnerPayload(payload)) return;\n latest.set(scope.sessionId, payload.runs);\n host.publish(scope.sessionId, payload);\n },\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n },\n sessionRemoved(sessionId) {\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n latest.delete(sessionId);\n },\n close() {\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n latest.clear();\n },\n };\n return source;\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { createRunnersChannel } from '../../../services/runnersChannel';\nexport default defineChannel(function runnerRunsChannel(): PoHubChannel {\n return createRunnersChannel();\n});\n","export const LOG_DIR_NAME = 'logs';\nexport const STATE_EXTENSION = '.json';\nexport const RUNNING_STATE = 'running';\nexport const STATE_POLL_MS = 1000;\nexport const HEARTBEAT_MS = 15_000;\nexport const DEFAULT_LINES = 200;\nexport const MAX_LINES = 5000;\nexport const SAFE_SEGMENT = /^[\\w.@-]+$/;\nexport const SCREEN_POLL_MS = 100;\nexport const RAW_LOG_SUFFIX = '.raw';\nexport const MAX_INPUT_CHARS = 4096;\n/** Ceiling for one finite log/screen read, before base64 encoding. */\nexport const MAX_OUTPUT_BYTES = 64 * 1024;\nexport const NOT_ATTACHABLE = 'No attachable runner by that name in this session.';\n","export const RUNNER_API_BASE_PATH = 'runner';\nexport const RUN_ID_PARAM = 'runId';\nexport const RUNNER_SCREEN_EVENT = 'screen';\nexport const RUNNER_LOG_STREAM_EVENT = 'append';\nexport const RUNNER_LOG_PING_EVENT = 'ping';\n","import fs from 'node:fs';\n\nimport { POLL_MS } from '../../constants/logTail';\nimport type { ILogTail, LogTailHandle, LogTailOptions } from '../../types/logTail';\n\n/** How often the tail restats the file; the same cadence the CLI's --follow uses. */\n\n/**\n * Polling tail: read the delta between the last offset and the file's current\n * size, and hand back only complete lines.\n *\n * Same reliability posture as this package's runner watcher: the poll is the\n * mechanism, not an accelerator for a change notification. fs.watchFile only\n * fires when it observes a difference from a baseline it took itself, so a\n * write that lands before its first stat is a change it never reports; a\n * restat every tick has no such window.\n *\n * A write that lands mid-line is common, so the trailing fragment is held\n * until its newline arrives rather than emitted as a line that will change.\n */\nexport class LogTail implements ILogTail {\n follow(logPath: string, options: LogTailOptions): LogTailHandle {\n let offset = options.from;\n let pending = '';\n let closed = false;\n\n const drain = (size: number): void => {\n // A shrunken file was rotated or truncated; start over rather than\n // reading from an offset that now points into different content.\n if (size < offset) {\n offset = 0;\n pending = '';\n }\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(logPath, 'r');\n let bytesRead: number;\n try {\n bytesRead = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n offset = size;\n if (bytesRead <= 0) return;\n pending += buffer.subarray(0, bytesRead).toString('utf8');\n const lines = pending.split('\\n');\n pending = lines.pop() ?? '';\n if (lines.length > 0) options.onLines(lines, offset - Buffer.byteLength(pending, 'utf8'));\n };\n\n const tick = (): void => {\n if (closed) return;\n try {\n drain(fs.statSync(logPath).size);\n } catch (error) {\n // The runner has not opened its log yet; the next tick settles it.\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n options.onError(error instanceof Error ? error : new Error(String(error)));\n }\n };\n\n const timer = setInterval(tick, POLL_MS);\n // Following a log must never be the reason a process stays alive.\n timer.unref?.();\n return {\n close() {\n if (closed) return;\n closed = true;\n clearInterval(timer);\n },\n };\n }\n}\n","import type { LogSlice } from './logReader';\n\n/**\n * The runner log API, shared by this package's session-scoped routes and its\n * cockpit plugin. The two halves run in different processes, so the wire\n * vocabulary is declared here: the browser half may reach `src/types` and\n * nothing else on the server side.\n *\n * Where the routes live is not here. That is `src/types/apiRoutes`, which both\n * halves read, and the generated client turns into the URLs the page addresses.\n * The file a run writes to is still read from its metadata record by the server\n * alone.\n */\n\n/**\n * One chunk of an attached pane's output.\n *\n * Base64 because these are terminal bytes, escape sequences and all, and a\n * server-sent event is a line-oriented text frame that would mangle them.\n */\nexport interface RunnerScreenEvent {\n chunk: string;\n /** Byte offset just past this chunk, so a reconnect resumes rather than replays. */\n offset?: number;\n /** Set once the runner exits, so the page can stop watching. */\n ended?: boolean;\n}\n\n/** A finite byte slice for sealed HTTP followers. Decode chunks incrementally, not independently. */\nexport interface RunnerOutputResponse {\n /** Base64 bytes, preserving split UTF-8 characters and terminal escape sequences. */\n chunk: string;\n offset: number;\n /** The file was replaced or became shorter than the requested offset; discard the previous display/decoder state. */\n reset: boolean;\n /** Opaque raw file identity. Send back on screen/read to detect replacement, including regrowth. */\n generation?: string;\n exists: boolean;\n /** More bytes were present at read time; request the next slice before waiting. */\n hasMore: boolean;\n /** True only once the runner ended and all available bytes have been read. */\n ended: boolean;\n}\n\n/** The body of a keystroke POST. Text, not key codes: the pane wants bytes. */\nexport interface RunnerInputRequest {\n text: string;\n}\n\n/** The named SSE event carrying a RunnerScreenEvent payload. */\n\n/**\n * A log request as query parameters. `grep` is a literal substring, not a\n * regular expression, because that is what the reader matches; `lines` bounds\n * the answer to the last N lines of whatever survived the filter.\n */\nexport interface RunnerLogQueryParams {\n lines?: number;\n grep?: string;\n ignoreCase?: boolean;\n contextLines?: number;\n}\n\n/** The slice the log route answers with, plus which runner it came from. */\nexport interface RunnerLogResponse extends LogSlice {\n runId: string;\n /** True while the runner is still writing, so the page knows following is worth offering. */\n running: boolean;\n}\n\n/**\n * One server-sent event on the follow stream: the lines appended since the last\n * one. The stream never filters: a page that sets a query stops following and\n * reads a filtered slice instead, because grep with context is a whole-file\n * answer and cannot be assembled from a chunk at a time.\n */\nexport interface RunnerLogStreamEvent {\n /** Appended lines, in order; empty when the event only reports the runner ending. */\n lines: string[];\n /**\n * Byte offset just past the last line in this event.\n *\n * A follower that loses the stream resumes here. Without it the page could\n * only fall back to the offset its last slice reported, which every line\n * delivered since has already moved past, so a reconnect would replay them.\n */\n offset?: number;\n /** Set once the runner exits, so the page can stop following without polling the run list. */\n ended?: boolean;\n}\n\n/** The named SSE event carrying a RunnerLogStreamEvent payload. */\n\n/**\n * The named SSE event that carries nothing.\n *\n * A runner can be alive and silent for minutes, which on the wire is\n * indistinguishable from a connection that died. This is sent on a timer so\n * the socket keeps proving itself. Readers that only listen for `append`\n * ignore it, which is what makes it safe to add.\n */\n\n/** Query parameter names, shared so the page and the route cannot drift apart. */\nexport const RUNNER_LOG_PARAMS = {\n lines: 'lines',\n grep: 'grep',\n ignoreCase: 'ignoreCase',\n contextLines: 'contextLines',\n from: 'from',\n generation: 'generation',\n} as const;\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport { RUN_ID_PARAM } from '../constants/webRunnerLog';\nimport { RUNNER_LOG_PARAMS, type RunnerLogResponse, type RunnerOutputResponse } from './webRunnerLog';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path: the build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/runner/`, so the mount is\n * stated once, by the folder that creates it. This package stated it four times\n * before, once per URL builder, each of them re-spelling `/plugins/` and the\n * session prefix by hand.\n *\n * The Hono app registers these paths verbatim and the browser addresses them,\n * so a path cannot move on one side alone. `:runId` is Hono's own parameter\n * syntax for that reason; a caller supplies the value as `params`, and the\n * client encodes the segment.\n */\nconst routes = {\n log: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log`,\n query: [\n RUNNER_LOG_PARAMS.lines,\n RUNNER_LOG_PARAMS.grep,\n RUNNER_LOG_PARAMS.ignoreCase,\n RUNNER_LOG_PARAMS.contextLines,\n ],\n response: undefined as unknown as ApiResponseOf<RunnerLogResponse>,\n },\n /**\n * The live tail of that same log.\n *\n * `stream: true`, so the client exposes a URL and no call method: the\n * consumer is the browser’s own SSE reader, and a relayed call would\n * buffer a stream that never ends.\n */\n logStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** The unscrubbed pane bytes, for a terminal emulator rather than a reader. */\n screenStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** Finite byte reads keep sealed HTTP exchanges bounded, unlike SSE. */\n logRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/read`,\n query: [RUNNER_LOG_PARAMS.from],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n screenRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/read`,\n query: [RUNNER_LOG_PARAMS.from, RUNNER_LOG_PARAMS.generation],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n input: {\n method: 'POST',\n path: `/runners/:${RUN_ID_PARAM}/screen/input`,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { PoApi, PoApiContext, PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport {\n DEFAULT_LINES,\n HEARTBEAT_MS,\n LOG_DIR_NAME,\n MAX_INPUT_CHARS,\n MAX_OUTPUT_BYTES,\n MAX_LINES,\n NOT_ATTACHABLE,\n RAW_LOG_SUFFIX,\n RUNNING_STATE,\n SAFE_SEGMENT,\n SCREEN_POLL_MS,\n STATE_EXTENSION,\n STATE_POLL_MS,\n} from '../../constants/runnerLogApi';\nimport {\n RUN_ID_PARAM,\n RUNNER_API_BASE_PATH,\n RUNNER_LOG_PING_EVENT,\n RUNNER_LOG_STREAM_EVENT,\n RUNNER_SCREEN_EVENT,\n} from '../../constants/webRunnerLog';\nimport { LogReader } from '../../services/logReader';\nimport { LogTail } from '../../services/logTail';\nimport { PtyBackendChain } from '../../services/ptyBackendChain';\nimport { RmuxBackend } from '../../services/rmuxBackend';\nimport { RunnerPaths, resolveRunnerStoreDirectory, runnerStateDirFor } from '../../services/runnerPaths';\nimport { TmuxBackend } from '../../services/tmuxBackend';\nimport { parseRunnerRecord } from '../../services/webRunnerRuns';\nimport routes from '../../types/apiRoutes';\nimport type { ILogReader, LogQuery } from '../../types/logReader';\nimport type { ILogTail } from '../../types/logTail';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport type { RunnerRecord } from '../../types/runnerRegistry';\nimport {\n RUNNER_LOG_PARAMS,\n type RunnerLogResponse,\n type RunnerLogStreamEvent,\n type RunnerOutputResponse,\n type RunnerScreenEvent,\n} from '../../types/webRunnerLog';\n\n/** How often a follow re-reads the record to notice the runner exiting. */\n/**\n * How often a silent but living stream proves it is still there. Short enough\n * that an idle proxy does not reap the socket, long enough to cost nothing.\n */\n/** Kept in step with LogQuery's own default, so an unasked-for tail is the same size everywhere. */\n/** A page that asks for more than this gets this; the whole point of the route is a bounded read. */\n/** Path segments the client supplies are names, never paths. */\n/** Backends that expose a pane another process can read and write. */\nconst MULTIPLEXER_BACKENDS: ReadonlySet<string> = new Set(['rmux', 'tmux']);\n/** How often an attached pane's new bytes are picked up. */\n/** The unscrubbed copy the runner's sink writes beside the log, for interactive runs. */\n/** One keystroke batch; a paste is not a file transfer. */\n/** Said the same way for every reason, so the route never reports which check failed. */\n\nexport interface RunnerLogApiOptions {\n /** The session these routes answer for; the host owns exactly one. */\n sessionId: string;\n /** The store root; defaults to this environment's agent directory. */\n storeDir?: string;\n environment?: Readonly<Record<string, string | undefined>>;\n cwd?: string;\n logReader?: ILogReader;\n logTail?: ILogTail;\n /**\n * Reaches a live pane to read its screen and send it input.\n *\n * A session API is handed nothing but its session id, so it cannot touch the\n * in-process PTY handles the extension holds. This talks to the multiplexer\n * over its own socket instead, the same way the CLI's `input` verb does,\n * which is what makes an attached pane reachable across processes at all.\n */\n pane?: IRmuxBackend;\n}\n\n/** Rejects anything that could climb out of the session's own directory before a path is built. */\nfunction isSafeSegment(value: string): boolean {\n return value !== '' && value !== '.' && value !== '..' && SAFE_SEGMENT.test(value);\n}\n\n/** A log must stay inside its session even when a path component is a symlink. Missing files are normal. */\nfunction isSessionLog(file: string, logsDir: string): boolean {\n const resolved = path.resolve(file);\n if (resolved !== logsDir && !resolved.startsWith(`${logsDir}${path.sep}`)) return false;\n try {\n if (fs.lstatSync(file).isSymbolicLink()) return false;\n const realFile = fs.realpathSync(file);\n const realLogs = fs.realpathSync(logsDir);\n return realFile.startsWith(`${realLogs}${path.sep}`);\n } catch (error) {\n // The sink has not created its output yet; a later request validates the created path again.\n return isRecord(error) && error.code === 'ENOENT';\n }\n}\n\n/**\n * The log file a run wrote, or undefined when there is no such run or its\n * record points somewhere it has no business pointing.\n *\n * The record names its own log path, and that record is written by another\n * process, so the path is checked rather than trusted: it must sit inside this\n * session's own logs directory. Without that check a doctored record would\n * turn this route into an arbitrary file read.\n */\nfunction resolveLogFile(stateDir: string, logsDir: string, sessionId: string, runId: string): RunnerRecord | undefined {\n const statePath = path.join(stateDir, `${runId}${STATE_EXTENSION}`);\n let raw: string;\n try {\n raw = fs.readFileSync(statePath, 'utf8');\n } catch {\n return undefined; // No such run in this session; the route answers 404.\n }\n const record = parseRunnerRecord(raw);\n if (record === undefined || record.id !== runId || record.sessionId !== sessionId) return undefined;\n if (!isSessionLog(record.logPath, logsDir)) return undefined;\n return record;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction numberParam(value: string | undefined, fallback: number, max: number): number {\n const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed < 0) return fallback;\n return Math.min(parsed, max);\n}\n\n/** Missing output is normal before the sink opens it; other IO failures must not look like an empty read. */\nfunction readOutput(\n file: string,\n from: number,\n running: boolean,\n priorGeneration?: string,\n screen = false,\n): RunnerOutputResponse {\n let descriptor: number;\n try {\n descriptor = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);\n } catch (error) {\n if (!isRecord(error) || error.code !== 'ENOENT') throw error;\n return { chunk: '', offset: from, reset: false, exists: false, hasMore: false, ended: !running };\n }\n try {\n const stat = fs.fstatSync(descriptor, { bigint: true });\n const size = Number(stat.size);\n // Identity and bytes come from the same descriptor, never a separate marker read.\n const generation = screen ? `${stat.dev}:${stat.ino}:${stat.birthtimeNs}` : undefined;\n const reset = from > size || (priorGeneration !== undefined && generation !== priorGeneration);\n const start = reset ? 0 : from;\n const length = Math.min(size - start, MAX_OUTPUT_BYTES);\n const buffer = Buffer.alloc(length);\n const read = length === 0 ? 0 : fs.readSync(descriptor, buffer, 0, length, start);\n const offset = start + read;\n const hasMore = offset < size;\n return {\n ...(generation === undefined ? {} : { generation }),\n chunk: buffer.subarray(0, read).toString('base64'),\n offset,\n reset,\n exists: true,\n hasMore,\n ended: !running && !hasMore,\n };\n } finally {\n fs.closeSync(descriptor);\n }\n}\n\nfunction logQueryOf(url: URL): LogQuery {\n const grep = url.searchParams.get(RUNNER_LOG_PARAMS.grep) ?? undefined;\n return {\n lines: numberParam(url.searchParams.get(RUNNER_LOG_PARAMS.lines) ?? undefined, DEFAULT_LINES, MAX_LINES),\n ...(grep === undefined || grep === '' ? {} : { grep }),\n ignoreCase: url.searchParams.get(RUNNER_LOG_PARAMS.ignoreCase) === 'true',\n contextLines: numberParam(url.searchParams.get(RUNNER_LOG_PARAMS.contextLines) ?? undefined, 0, MAX_LINES),\n };\n}\n\n/**\n * This package's HTTP surface: one runner's log, as a bounded slice and as a\n * live tail.\n *\n * The routes are mounted inside one session's own server, so they name a runner\n * and nothing else; the session is the host, not a parameter. The reader only\n * ever returns the last N lines of what matched, so both routes stay bounded no\n * matter how large the file on disk is.\n *\n * Paths come from src/types/apiRoutes, the one table the browser's generated\n * client reads too, so this app and the cockpit cannot disagree about where a\n * route lives. They stay relative to the mount the host strips.\n */\nexport function createRunnerLogApi(options: RunnerLogApiOptions): Hono {\n const logReader = options.logReader ?? new LogReader();\n const logTail = options.logTail ?? new LogTail();\n const sessionId = options.sessionId;\n const paths = new RunnerPaths(options.cwd, options.environment);\n const storeDir = options.storeDir ?? resolveRunnerStoreDirectory(options.environment ?? process.env);\n const stateDir =\n options.storeDir === undefined ? paths.stateDirectory(sessionId) : runnerStateDirFor(storeDir, sessionId);\n const logsDir =\n options.storeDir === undefined ? paths.logDirectory(sessionId) : path.join(storeDir, sessionId, LOG_DIR_NAME);\n const app = new Hono();\n\n // Built on first use, not on mount: most sessions never attach to a pane,\n // and constructing this probes the filesystem for a multiplexer binary.\n let paneBackend: IRmuxBackend | undefined = options.pane;\n const pane = (): IRmuxBackend => {\n paneBackend ??= (() => {\n const paths = new RunnerPaths(options.cwd, options.environment);\n return new PtyBackendChain(new RmuxBackend(paths), new TmuxBackend(paths));\n })();\n return paneBackend;\n };\n\n /** The record behind a request, or undefined when this session has no such run. */\n const runnerOf = (runId: string): RunnerRecord | undefined =>\n isSafeSegment(runId) ? resolveLogFile(stateDir, logsDir, sessionId, runId) : undefined;\n\n /**\n * The owned pane whose recorded output a request may read.\n *\n * Same posture as the log routes: the record is written by another process,\n * so nothing on it is trusted until it is checked. The target must be one\n * this package owns, and the run must have been started\n * interactive. A non-interactive run has no terminal to type into, and a\n * subprocess-backed run has no pane at all.\n */\n const paneOf = (runId: string): { record: RunnerRecord; target: string } | undefined => {\n const record = runnerOf(runId);\n if (record === undefined || record.interactive !== true) return undefined;\n const target = record.backendTarget;\n if (target === undefined || !MULTIPLEXER_BACKENDS.has(record.backend) || !isSafeSegment(target)) return undefined;\n return { record, target };\n };\n const attachableOf = (runId: string): { record: RunnerRecord; target: string } | undefined => {\n const owned = paneOf(runId);\n return owned?.record.state === RUNNING_STATE ? owned : undefined;\n };\n\n for (const [route, screen] of [\n [routes.logRead, false],\n [routes.screenRead, true],\n ] as const) {\n app.get(route.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n // A completed pane is readable so a finite follower can drain its last bytes. Input still requires a live pane.\n const record = screen ? paneOf(runId)?.record : runnerOf(runId);\n if (record === undefined)\n return context.json({ error: screen ? NOT_ATTACHABLE : `No runner '${runId}' in this session.` }, 404);\n const value = context.req.query(RUNNER_LOG_PARAMS.from);\n const from = value === undefined ? 0 : Number(value);\n if ((value !== undefined && !/^\\d+$/.test(value)) || !Number.isSafeInteger(from) || from < 0) {\n return context.json({ error: 'Expected a non-negative safe integer byte offset.' }, 400);\n }\n const file = screen ? `${record.logPath}${RAW_LOG_SUFFIX}` : record.logPath;\n if (!isSessionLog(file, logsDir)) return context.json({ error: 'No readable output in this session.' }, 404);\n return context.json(\n readOutput(\n file,\n from,\n record.state === RUNNING_STATE,\n screen ? context.req.query(RUNNER_LOG_PARAMS.generation) : undefined,\n screen,\n ),\n );\n });\n }\n\n app.get(routes.log.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const record = runnerOf(runId);\n if (record === undefined) return context.json({ error: `No runner '${runId}' in this session.` }, 404);\n const slice = logReader.read(record.logPath, logQueryOf(new URL(context.req.url)));\n const body: RunnerLogResponse = { ...slice, runId, running: record.state === RUNNING_STATE };\n return context.json(body);\n });\n\n app.get(routes.logStream.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const record = runnerOf(runId);\n if (record === undefined) return context.json({ error: `No runner '${runId}' in this session.` }, 404);\n const from = numberParam(\n new URL(context.req.url).searchParams.get(RUNNER_LOG_PARAMS.from) ?? undefined,\n 0,\n Number.MAX_SAFE_INTEGER,\n );\n\n return streamSSE(context, async (stream) => {\n const send = (event: RunnerLogStreamEvent): Promise<void> =>\n stream.writeSSE({ event: RUNNER_LOG_STREAM_EVENT, data: JSON.stringify(event) });\n\n // The tail and the state poll both push onto one queue, so the stream\n // writes in the order things happened rather than interleaving two\n // writers onto the same socket.\n let pump: Promise<void> = Promise.resolve();\n const queue = (event: RunnerLogStreamEvent): void => {\n pump = pump.then(() => send(event)).catch(() => undefined);\n };\n\n let finish = (): void => undefined;\n const done = new Promise<void>((resolve) => {\n finish = resolve;\n });\n const handle = logTail.follow(record.logPath, {\n from,\n onLines: (lines, completeThrough) => queue({ lines, offset: completeThrough }),\n onError: () => finish(),\n });\n // A finished runner writes nothing more, so the page is told once and\n // the stream ends rather than holding a socket open forever.\n const poll = setInterval(() => {\n const current = runnerOf(runId);\n if (current !== undefined && current.state === RUNNING_STATE) return;\n queue({ lines: [], ended: true });\n finish();\n }, STATE_POLL_MS);\n // A runner can be alive and produce nothing for minutes. Without this the\n // page cannot tell that from a stream that quietly died.\n const heartbeat = setInterval(() => {\n pump = pump.then(() => stream.writeSSE({ event: RUNNER_LOG_PING_EVENT, data: '' })).catch(() => undefined);\n }, HEARTBEAT_MS);\n\n stream.onAbort(() => finish());\n await done;\n clearInterval(poll);\n clearInterval(heartbeat);\n handle.close();\n await pump;\n });\n });\n\n app.get(routes.screenStream.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const attachable = attachableOf(runId);\n if (attachable === undefined) return context.json({ error: NOT_ATTACHABLE }, 404);\n // The record's log path is already proven to sit inside this session's own\n // logs directory, and the raw copy is that same path with a suffix, so it\n // cannot point anywhere new.\n const rawPath = `${attachable.record.logPath}${RAW_LOG_SUFFIX}`;\n const from = numberParam(\n new URL(context.req.url).searchParams.get(RUNNER_LOG_PARAMS.from) ?? undefined,\n 0,\n Number.MAX_SAFE_INTEGER,\n );\n\n return streamSSE(context, async (stream) => {\n let pump: Promise<void> = Promise.resolve();\n const queue = (event: RunnerScreenEvent): void => {\n pump = pump\n .then(() => stream.writeSSE({ event: RUNNER_SCREEN_EVENT, data: JSON.stringify(event) }))\n .catch(() => undefined);\n };\n\n let finish = (): void => undefined;\n const done = new Promise<void>((resolve) => {\n finish = resolve;\n });\n\n let offset = from;\n const drain = (): void => {\n let size: number;\n try {\n size = fs.statSync(rawPath).size;\n } catch {\n return; // The sink has not opened the file yet; the next tick settles it.\n }\n // The sink starts the file over when it passes its ceiling, so a\n // smaller file is a fresh one rather than a bad offset.\n if (size < offset) offset = 0;\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(rawPath, 'r');\n let read: number;\n try {\n read = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n if (read <= 0) return;\n offset += read;\n // Bytes, not lines: a terminal's escape sequences straddle newlines and\n // mean nothing once split on them.\n queue({ chunk: buffer.subarray(0, read).toString('base64'), offset });\n };\n\n const poll = setInterval(() => {\n const current = runnerOf(runId);\n drain();\n if (current === undefined || current.state !== RUNNING_STATE) {\n queue({ chunk: '', offset, ended: true });\n finish();\n }\n }, SCREEN_POLL_MS);\n\n stream.onAbort(() => finish());\n await done;\n clearInterval(poll);\n await pump;\n });\n });\n\n /**\n * Types into a live pane.\n *\n * This is the one route in the package that writes rather than reads, so it\n * is deliberately narrow: an owned target, still running, started\n * interactive, and a body that is nothing but text.\n */\n app.post(routes.input.path, async (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const attachable = attachableOf(runId);\n if (attachable === undefined) return context.json({ error: NOT_ATTACHABLE }, 404);\n\n let body: unknown;\n try {\n body = await context.req.json();\n } catch {\n return context.json({ error: 'Expected a JSON body carrying text.' }, 400);\n }\n const text = isRecord(body) ? body.text : undefined;\n if (typeof text !== 'string' || text === '') return context.json({ error: 'Expected a non-empty text.' }, 400);\n if (text.length > MAX_INPUT_CHARS) return context.json({ error: 'That is more input than a pane takes.' }, 413);\n\n const delivered = await pane().input(attachable.target, text);\n if (!delivered) return context.json({ error: 'The runner did not take the input.' }, 502);\n return context.json({ delivered: true });\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built session entry. */\nexport const api: PoApi = {\n basePath: RUNNER_API_BASE_PATH,\n start(context: PoApiContext): PoApiHandler {\n // The host is one session's server, so its id is the whole scope; a hub\n // would have handed no session at all, and there is nothing to answer.\n const app = createRunnerLogApi({\n sessionId: context.sessionId ?? '',\n cwd: context.cwd,\n environment: context.environment,\n });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request: a follow's watch and its poll are both\n // torn down when its own stream ends or the socket aborts.\n close: () => undefined,\n };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/runnerLogApi';\n\n/**\n * Mounts this package's HTTP API at `runner`, which the folder names.\n *\n * The folder is the declaration: the build reads the base path off it and hands\n * the browser a client bound to the same segment, so the mount is stated once\n * rather than in a constant and in four URL builders that were each free to\n * drift.\n */\nexport default defineRoute(api);\n","import { defineServerCommand, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\nexport default defineServerCommand(\n (context: WithRoot<PoServerPluginContext, RunnerServerScope>) => context.root.command,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(({ root }: WithRoot<PoServerPluginContext, RunnerServerScope>) => ({\n event: 'session_compact' as const,\n handle: root.recoverCompaction,\n}));\n","import { defineResource } from '@agimon-ai/po-core/extensionFile';\nimport { PO_HELP_WHEN } from '@agimon-ai/po-core/help';\nimport { packageResourcePath, readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineResource({\n name: 'po-use-runner',\n description:\n 'Use Po Pi Runner to supervise shell commands, inspect durable logs, provide interactive input, and stop background runs.',\n when: PO_HELP_WHEN,\n kind: 'skill' as const,\n path: packageResourcePath(import.meta.url, 'src/prompts/po-use-runner/SKILL.md'),\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-runner/SKILL.md'),\n});\n","import { defineServerTool, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\nexport default defineServerTool((context: WithRoot<PoServerPluginContext, RunnerServerScope>) => context.root.tool);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelRunnerRuns from '../src/extensions/(backend)/channel/runnerRuns.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/runner/route.server';\nimport commandRunners from '../src/extensions/workspaces/sessions/(backend)/command/runners.server';\nimport hookSessionCompact from '../src/extensions/workspaces/sessions/(backend)/hook/session-compact.server';\nimport resourcePoUseRunner from '../src/extensions/workspaces/sessions/(backend)/resource/po-use-runner.server';\nimport toolBash from '../src/extensions/workspaces/sessions/(backend)/tool/bash.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-runner',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n global: (context) => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'runner_runs' }, at(channelRunnerRuns, context))]; },\n }),\n workspace: (context) => ({\n get channels(): PoServerSessionPlugin['channels'] { return defined([context.hosted('global') ? undefined : () => via({ frameType: 'runner_runs' }, at(channelRunnerRuns, context))]); },\n }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): PoServerSessionPlugin['channels'] { return defined([context.hosted('global') ? undefined : () => via({ frameType: 'runner_runs' }, at(channelRunnerRuns, context))]); },\n get commands(): PoServerSessionPlugin['commands'] { return [via({ name: 'runners' }, at(commandRunners, contextSession))]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return [via({ event: 'session_compact' }, at(hookSessionCompact, contextSession))]; },\n get resources(): PoServerSessionPlugin['resources'] { return [at(resourcePoUseRunner, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return [via({ name: 'bash' }, at(toolBash, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
@@ -1315,11 +1315,11 @@ const serverPlugin = defineServerPlugin({
1315
1315
  name: "@agimon-ai/po-runner",
1316
1316
  providerOverrides: composeServerProviderOverrides(root_server_default),
1317
1317
  injects: composeRootInjects({ session: [root_server_default] }),
1318
- global: () => ({ get channels() {
1319
- return [() => via({ frameType: "runner_runs" }, runnerRuns_server_default())];
1318
+ global: (context) => ({ get channels() {
1319
+ return [() => via({ frameType: "runner_runs" }, at(runnerRuns_server_default, context))];
1320
1320
  } }),
1321
1321
  workspace: (context) => ({ get channels() {
1322
- return defined([context.hosted("global") ? void 0 : () => via({ frameType: "runner_runs" }, runnerRuns_server_default())]);
1322
+ return defined([context.hosted("global") ? void 0 : () => via({ frameType: "runner_runs" }, at(runnerRuns_server_default, context))]);
1323
1323
  } }),
1324
1324
  session: async (context) => {
1325
1325
  const scopeSession = await context.scopeRoot("session", root_server_default, context);
@@ -1333,7 +1333,7 @@ const serverPlugin = defineServerPlugin({
1333
1333
  return [at(route_server_default, contextSession)];
1334
1334
  },
1335
1335
  get channels() {
1336
- return defined([context.hosted("global") ? void 0 : () => via({ frameType: "runner_runs" }, runnerRuns_server_default())]);
1336
+ return defined([context.hosted("global") ? void 0 : () => via({ frameType: "runner_runs" }, at(runnerRuns_server_default, context))]);
1337
1337
  },
1338
1338
  get commands() {
1339
1339
  return [via({ name: "runners" }, at(runners_server_default, contextSession))];
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":["rootSession","channelRunnerRuns","apiRoute","commandRunners","hookSessionCompact","resourcePoUseRunner","toolBash"],"sources":["../../src/constants/runnerRuntime.ts","../../src/constants/runnerSettings.ts","../../src/services/runnerHost/index.ts","../../src/constants/webRunners.ts","../../src/constants/webRunnerRuns.ts","../../src/services/webRunnerRuns/index.ts","../../src/services/runnerServerRuntime/index.ts","../../src/services/runnerSettings/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/runnersChannel/index.ts","../../src/extensions/(backend)/channel/runnerRuns.server.ts","../../src/constants/runnerLogApi.ts","../../src/constants/webRunnerLog.ts","../../src/services/logTail/index.ts","../../src/types/webRunnerLog.ts","../../src/types/apiRoutes.ts","../../src/services/runnerLogApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/runner/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/runners.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-compact.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/po-use-runner.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/bash.server.ts","../../generated/server.ts"],"sourcesContent":["export const LEADER_SOURCE = '@agimon-ai/po-runner';\nexport const LEADER_GROUP_ORDER = 67;\nexport const ERR_REQUIRES_INTERACTIVE = '/runners requires interactive mode';\nexport const ERR_STOP_USAGE = 'Usage: /runners stop <runner-id> [reason]';\nexport const ERR_START_USAGE = 'Usage: /runners start [name=x] [cwd=y] [interactive=true] -- <command>';\nexport const SESSION_START_EVENT = 'session_start';\nexport const RUNNER_FINISHED_MESSAGE = 'po-runner-finished';\nexport const COMPLETED_STATE = 'completed';\nexport const RMUX_BACKEND = 'rmux';\nexport const STOPPED_REASON = 'stopped';\nexport const RUNNER_STATUS_KEY = 'po-runner-runners';\nexport const BACKGROUND_WORK_PROVIDER = 'po-runner';\nexport const RUNNER_FOOTER_ORDER = 10;\nexport const RUNNER_STATUS_POLL_MS = 500;\nexport const HISTORY_SWEEP_INTERVAL_MS = 600_000;\n","export const RUNNER_SETTINGS_FILE = 'po-runner.json';\nexport const RATIO_KEYS = ['headRatio', 'errorBudgetRatio'] as const;\nexport const MAX_RESULT_BYTES_CEILING = 262_144;\nexport const MAX_RESULT_LINES_CEILING = 5_000;\nexport const MAX_RESULT_TOKENS_CEILING = 100_000;\nexport const MAX_ERROR_ENTRIES_CEILING = 100;\nexport const MAX_PATTERNS = 32;\nexport const MAX_PATTERN_LENGTH = 512;\n","import type { IClock } from '../../types/clock';\nimport type { ProcessRegistryPort } from '../runnerRegistry';\nimport type { RunnerHost, RunnerHostOptions, RunnerHostSession } from './type';\n\nexport type { RunnerHost, RunnerHostOptions, RunnerHostSession } from './type';\n\nconst describe = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\n/** Runs `pass` again `ms` after the previous pass finished, so two passes never overlap. */\nfunction repeat(clock: IClock, ms: number, pass: () => Promise<void>): { stop(): Promise<void> } {\n let stopped = false;\n let cancel: (() => void) | undefined;\n let running: Promise<void> | undefined;\n const arm = (): void => {\n cancel = clock.after(ms, () => {\n cancel = undefined;\n running = pass().finally(() => {\n running = undefined;\n if (!stopped) arm();\n });\n });\n };\n arm();\n return {\n async stop() {\n stopped = true;\n cancel?.();\n await running;\n },\n };\n}\n\n/**\n * The housekeeping every session running one po-runner build in one hub shares.\n *\n * A session used to open its own registry connection and arm its own status poll and history\n * sweep, and each sweep walked the whole runner store. Here one loop refreshes the attached sessions\n * one at a time, one sweep per store spares every attached session at once, and runners a crashed\n * server left behind are reclaimed once rather than at every session start.\n */\nexport function createRunnerHost(options: RunnerHostOptions): RunnerHost {\n const sessions = new Set<RunnerHostSession>();\n let closed = false;\n let reclaiming: Promise<void> | undefined;\n let sweeping: Promise<void> | undefined;\n\n const refreshAll = async (): Promise<void> => {\n // A Set skips what is deleted mid-pass, so a session detached during a pass is not refreshed after.\n for (const session of sessions) {\n if (closed) return;\n try {\n await session.refresh();\n } catch (error) {\n options.onWarning(`Could not refresh po-runner session ${session.sessionId}: ${describe(error)}`);\n }\n }\n };\n\n const sweepAll = (): Promise<void> => {\n sweeping ??= (async () => {\n try {\n const byStore = new Map<string, RunnerHostSession[]>();\n for (const session of sessions) {\n const store = session.paths.storeDirectory();\n byStore.set(store, [...(byStore.get(store) ?? []), session]);\n }\n for (const group of byStore.values()) {\n const [first] = group;\n if (closed || first === undefined) return;\n const live = new Set(group.map((session) => session.sessionId));\n const now = options.clock.now();\n const result = first.paths.sweepHistoryAsync\n ? await first.paths.sweepHistoryAsync(first.historyTtlMs, now, live)\n : first.paths.sweepHistory(first.historyTtlMs, now, live);\n for (const error of result.errors) options.onWarning(error);\n }\n } catch (error) {\n options.onWarning(`Could not sweep po-runner history: ${describe(error)}`);\n }\n })().finally(() => {\n sweeping = undefined;\n });\n return sweeping;\n };\n\n const polling = repeat(options.clock, options.statusPollMs, refreshAll);\n const sweeper = repeat(options.clock, options.historySweepMs, sweepAll);\n const processRegistry: ProcessRegistryPort = {\n registerProcess: (...args) => options.processRegistry.registerProcess(...args),\n releaseProcess: (...args) => options.processRegistry.releaseProcess(...args),\n listProcesses: (...args) => options.processRegistry.listProcesses(...args),\n // Sessions come and go; the connection lives as long as the host.\n close: () => undefined,\n };\n\n return {\n processRegistry,\n attach(session) {\n if (closed) throw new Error('The po-runner host is disposed.');\n sessions.add(session);\n if (reclaiming === undefined) {\n // Once per host: runners and history a crashed server left behind belong to no session.\n reclaiming = session\n .reclaimOrphans()\n .catch((error: unknown) => options.onWarning(`Could not reclaim orphaned runners: ${describe(error)}`));\n void sweepAll();\n }\n return () => {\n sessions.delete(session);\n };\n },\n async dispose() {\n if (closed) return;\n closed = true;\n sessions.clear();\n await Promise.all([polling.stop(), sweeper.stop(), reclaiming, sweeping]);\n options.processRegistry.close();\n },\n };\n}\n","export const RUNNER_RUNS_TYPE = 'runner_runs';\n","export const FINISHED_RUNNER_RETENTION_MS = 600_000;\n","import { FINISHED_RUNNER_RETENTION_MS } from '../../constants/webRunnerRuns';\nimport type { RunnerRecord } from '../../types/runnerRegistry';\nimport type { RunnerRunView } from '../../types/webRunners';\nimport { isRunnerRecord } from '../runnerRecord';\n\n/** How long a finished runner stays in the cockpit's feed after it exited. */\n\n/** One on-disk metadata record, or undefined when the text is not a runner record. */\nexport function parseRunnerRecord(raw: string): RunnerRecord | undefined {\n try {\n const parsed: unknown = JSON.parse(raw);\n return isRunnerRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined; // Mid-write or foreign file; the next scan settles it.\n }\n}\n\nexport function toRunnerRunView(record: RunnerRecord): RunnerRunView {\n return {\n id: record.id,\n name: record.name,\n pid: record.pid,\n command: record.command,\n cwd: record.cwd,\n interactive: record.interactive,\n backend: record.backend,\n state: record.state,\n promoted: record.promoted,\n startedAt: record.startedAt,\n logPath: record.logPath,\n ...(record.exit\n ? {\n exit: {\n reason: record.exit.reason,\n code: record.exit.code,\n signal: record.exit.signal,\n ...(record.exit.terminationReason === undefined\n ? {}\n : { terminationReason: record.exit.terminationReason }),\n ...(record.exit.stopReason === undefined ? {} : { stopReason: record.exit.stopReason }),\n finishedAt: record.exit.finishedAt,\n },\n }\n : {}),\n };\n}\n\nfunction finishedAt(record: RunnerRecord): number {\n const parsed = record.exit === undefined ? Number.NaN : Date.parse(record.exit.finishedAt);\n return Number.isFinite(parsed) ? parsed : Date.parse(record.startedAt);\n}\n\n/**\n * The feed the cockpit shows: running runners first, newest start first, then\n * the recently finished ones, newest exit first. Finished runners older than\n * the retention window are left out; the registry keeps them for the CLI.\n */\nexport function presentRunnerRuns(records: readonly RunnerRecord[], now: number): RunnerRunView[] {\n const running = records\n .filter((record) => record.state === 'running')\n .sort((left, right) => right.startedAt.localeCompare(left.startedAt));\n const finished = records\n .filter((record) => record.state === 'completed' && now - finishedAt(record) <= FINISHED_RUNNER_RETENTION_MS)\n .sort((left, right) => finishedAt(right) - finishedAt(left));\n return [...running, ...finished].map(toRunnerRunView);\n}\n","import {\n PO_BACKGROUND_WORK_SERVICE,\n readPoBackgroundWorkService,\n type BackgroundProviderWorkItem,\n type BackgroundWorkProviderHandle,\n} from '@agimon-ai/po-core/backgroundWork';\nimport { resolveRootSessionId } from '@agimon-ai/po-core/childProcess';\nimport { runCleanups } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessActivity, PoHeadlessExecutionContext } from '@agimon-ai/po-core/headless';\nimport type { PoServerHostService } from '@agimon-ai/po-core/serverPlugin';\nimport { createPoTelemetry } from '@agimon-ai/po-telemetry';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { LIVENESS_POLL_MS, TERM_GRACE_MS } from '../../constants/launcher';\nimport { BACKGROUND_WORK_PROVIDER, RUNNER_STATUS_KEY } from '../../constants/runnerRuntime';\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport type { RunnerStartFailureView } from '../../types/webRunners';\nimport { activeRunnerRecovery } from '../compaction';\nimport { reconcileActiveRunners, stopRunnerProcess } from '../reconcile';\nimport { getLogTtlMs } from '../runnerConfig';\nimport type { RunnerDependencies } from '../runnerDependencies/type';\nimport type { RunnerHost } from '../runnerHost';\nimport { formatRunnerStatus } from '../runnerRecord';\nimport { formatRunnerFinished, formatRunnerFinishedNotice } from '../runnerRecord';\nimport { presentRunnerRuns } from '../webRunnerRuns';\n\nconst COMPLETION_RETRY_MS = 1_000;\n\nexport interface RunnerServerRuntime {\n readonly container: RunnerDependencies;\n readonly activity: PoHeadlessActivity;\n readonly backgroundWorkPlugin: (context: Context) => void;\n ensureSession(sessionId: string): Promise<void>;\n /** Wakes the agent once when this runner, which it sent to the background, exits. */\n watchRunner(id: string): void;\n /** Tells the runners panel why a start it asked for did not happen; undefined clears it after one that did. */\n reportStart(sessionId: string, failure: string | undefined): void;\n recoverCompaction(this: void, event: Readonly<Record<string, unknown>>): Promise<void>;\n dispose(this: void): Promise<void>;\n}\nexport function createRunnerServerRuntime(\n container: RunnerDependencies,\n /** What every session of this build in the hub shares: the registry and the housekeeping loop. */\n host: RunnerHost,\n directEvents: NonNullable<PoServerHostService['context']['directEvents']>,\n /** Absent when no agent runs in this session, so there is nobody to wake. */\n wakeAgent?: (content: string, requestId: string, notice: string) => Promise<void>,\n execution?: PoHeadlessExecutionContext,\n): RunnerServerRuntime {\n const telemetry = execution\n ? createPoTelemetry({\n serviceName: 'po-runner',\n packageName: '@agimon-ai/po-runner',\n cwd: execution.cwd,\n env: execution.environment,\n enableLogs: true,\n enableTraces: true,\n })\n : undefined;\n let sessionId: string | undefined;\n let recoveredCompactionId: string | undefined;\n let supervision: Promise<void> | undefined;\n let runtimeDisposal: Promise<void> | undefined;\n let disposal: Promise<void> | undefined;\n let backgroundWorkItems: BackgroundProviderWorkItem[] = [];\n let backgroundWorkProvider: BackgroundWorkProviderHandle | undefined;\n // Nothing else tells the model a background runner ended, so without this an\n // agent that finished its turn waiting on one idles until a person prompts it.\n const watched = new Set<string>();\n let unsubscribeWatched: (() => void) | undefined;\n let wakes: Promise<void> = Promise.resolve();\n let closing = false;\n let retryTimer: ReturnType<typeof setTimeout> | undefined;\n /** Stops the shared loop refreshing this session and sweeping around it. */\n let detach: (() => void) | undefined;\n let startFailure: RunnerStartFailureView | undefined;\n let startFailures = 0;\n\n /** Every runner of every session the registry knows, reclaimed when its owning process is gone. */\n const reclaimOrphans = async (): Promise<void> => {\n const reclaimed = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: false,\n });\n for (const error of reclaimed.errors) process.emitWarning(error);\n };\n\n // The activity is optional and source-gated. The retained server plugin owns the\n // session runtime, so disabling the activity must not tear down its jobs.\n const ensureSession = (requestedSessionId: string): Promise<void> => {\n if (closing) return Promise.reject(new Error('Runner runtime is closing.'));\n if (sessionId !== undefined && sessionId !== requestedSessionId) {\n throw new Error(`Runner runtime belongs to session ${sessionId}.`);\n }\n sessionId ??= requestedSessionId;\n supervision ??= (async () => {\n void telemetry\n ?.recordEvent('po_runner.session_started', { outcome: 'started' })\n .catch((error: unknown) => process.emitWarning(`Could not record runner session telemetry: ${String(error)}`));\n if (closing) return;\n container.paths.setSessionId(requestedSessionId);\n if ((await container.lifeline.arm(requestedSessionId)) === undefined) {\n throw new Error('Could not arm runner supervision lifeline.');\n }\n if (closing) return;\n const promotedIds = (await container.runnerRegistry.listAll(requestedSessionId))\n .filter((record) => record.state === 'running' && record.promoted)\n .map((record) => record.id);\n // Only this session's runners: the shared host reclaims the ones other sessions left behind.\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: true,\n startupSessionId: requestedSessionId,\n active: await container.runnerRegistry.listBySession(requestedSessionId),\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n void telemetry\n ?.recordEvent('po_runner.reconciled', {\n 'runner.reclaimed_count': reconciled.reclaimed.length,\n 'runner.error_count': reconciled.errors.length,\n outcome: reconciled.errors.length === 0 ? 'completed' : 'degraded',\n })\n .catch((error: unknown) =>\n process.emitWarning(`Could not record runner reconcile telemetry: ${String(error)}`),\n );\n if (!closing) {\n unsubscribeRunnerUpdates ??= container.runnerRegistry.subscribe(\n () => requestPublish(requestedSessionId),\n requestedSessionId,\n );\n // Reconciliation may have completed retained promotions already.\n for (const id of promotedIds) watchRunner(id);\n requestPublish(requestedSessionId);\n // One shared loop refreshes this session from here on, and sweeps history around it.\n detach ??= host.attach({\n sessionId: requestedSessionId,\n paths: container.paths,\n historyTtlMs: getLogTtlMs(container.environment),\n refresh: () => refreshSession(requestedSessionId),\n reclaimOrphans,\n });\n }\n })().catch((error: unknown) => {\n supervision = undefined;\n throw error;\n });\n return supervision;\n };\n\n const disposeRuntime = (): Promise<void> => {\n runtimeDisposal ??= (async () => {\n await supervision?.catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n const ownedSessionId = sessionId;\n if (ownedSessionId === undefined) return;\n await runCleanups(\n [\n async () => {\n const records = await container.runnerRegistry.listBySession(ownedSessionId);\n const results = await Promise.allSettled(\n records.map(async (record) => {\n await stopRunnerProcess(record, container.launcher, container.rmuxBackend, {\n registry: container.runnerRegistry,\n intent: {\n reason: 'stopped',\n terminationReason: 'parent_session_cleanup',\n stopReason: 'session ended',\n },\n });\n // Success can mean only that SIGKILL or the mux close was accepted.\n // Allow asynchronous exit to land, but never complete a still-live runner.\n const deadline = container.clock.now() + TERM_GRACE_MS;\n while (container.processControl.isAlive(record.pid)) {\n if (container.clock.now() >= deadline) {\n throw new Error(`Could not stop runner ${record.id} during session shutdown`);\n }\n await new Promise<void>((resolve) => container.clock.after(LIVENESS_POLL_MS, resolve));\n }\n await container.runnerRegistry.complete(\n record.id,\n { reason: 'stopped', code: null, signal: null, stopReason: 'session ended' },\n ownedSessionId,\n );\n }),\n );\n const errors = results.filter((result) => result.status === 'rejected').map((result) => result.reason);\n if (errors.length) throw new AggregateError(errors, 'Runner process cleanup failed.');\n },\n () => container.ptyHost.disposeAll(),\n () => container.lifeline.dispose(),\n () => container.runnerRegistry.close(),\n ],\n 'Runner runtime cleanup failed.',\n );\n })();\n return runtimeDisposal;\n };\n\n let refreshInFlight: Promise<void> | undefined;\n const refreshSession = (ownedSessionId: string): Promise<void> => {\n if (closing) return Promise.resolve();\n refreshInFlight ??= refreshNow(ownedSessionId).finally(() => {\n refreshInFlight = undefined;\n });\n return refreshInFlight;\n };\n const refreshNow = async (ownedSessionId: string): Promise<void> => {\n if (closing) return;\n try {\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: false,\n active: await container.runnerRegistry.listBySession(ownedSessionId),\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n if (!closing) {\n requestPublish(ownedSessionId);\n wakeForFinished(ownedSessionId);\n }\n } catch (error) {\n process.emitWarning(`Could not refresh po-runner: ${String(error)}`);\n }\n };\n const publishRuns = async (ownedSessionId: string): Promise<void> => {\n try {\n const records = await container.runnerRegistry.listAll(ownedSessionId);\n if (closing) return;\n if (execution) {\n const root = resolveRootSessionId(ownedSessionId, container.environment);\n const count = (await container.runnerRegistry.list()).filter(\n (record) => (record.rootSessionId ?? record.sessionId) === root,\n ).length;\n if (closing) return;\n execution.client.setStatus(RUNNER_STATUS_KEY, formatRunnerStatus(count));\n }\n if (closing) return;\n const nextBackgroundWork = records\n .filter((record) => record.state === 'running' || watched.has(record.id))\n .map((record) => ({\n id: record.id,\n sessionId: record.sessionId,\n label: record.name,\n // An absent status holds ownership until completion admission succeeds.\n ...(record.state === 'running' ? { status: record.state } : {}),\n }));\n if (JSON.stringify(nextBackgroundWork) !== JSON.stringify(backgroundWorkItems)) {\n backgroundWorkItems = nextBackgroundWork;\n backgroundWorkProvider?.update();\n }\n directEvents.publish(RUNNER_RUNS_TYPE, ownedSessionId, {\n runs: presentRunnerRuns(records, Date.now()),\n ...(execution ? { sessionDirectory: execution.cwd } : {}),\n ...(startFailure ? { startFailure } : {}),\n });\n } catch (error) {\n process.emitWarning(`Could not publish runner state: ${String(error)}`);\n }\n };\n let publishInFlight: Promise<void> | undefined;\n let publishAgain = false;\n let unsubscribeRunnerUpdates: (() => void) | undefined;\n const requestPublish = (ownedSessionId: string): void => {\n if (closing) return;\n publishAgain = true;\n if (publishInFlight !== undefined) return;\n publishInFlight = (async () => {\n do {\n publishAgain = false;\n await publishRuns(ownedSessionId);\n } while (publishAgain && !closing);\n })()\n .catch((error: unknown) => {\n process.emitWarning(`Could not schedule runner state publication: ${String(error)}`);\n })\n .finally(() => {\n publishInFlight = undefined;\n if (publishAgain) requestPublish(ownedSessionId);\n });\n };\n\n // Sequenced so two runners exiting in the same tick each wake the agent once.\n const wakeForFinished = (ownedSessionId: string): void => {\n if (closing) return;\n wakes = wakes\n .then(async () => {\n if (closing) return;\n let retry = false;\n // Deleting the entry being visited is safe for a Set iterator.\n for (const id of watched) {\n if (closing) return;\n try {\n const record = await container.runnerRegistry.get(id, ownedSessionId);\n if (closing) return;\n if (record?.state === 'running') continue;\n // A record swept before it was seen leaves nothing to report.\n if (record)\n await wakeAgent?.(\n formatRunnerFinished(record),\n `runner-finished:${JSON.stringify([ownedSessionId, id])}`,\n formatRunnerFinishedNotice(record),\n );\n if (record)\n await telemetry?.recordEvent('po_runner.process_finished', {\n outcome: 'completed',\n 'runner.exit_code': record.exit?.code ?? 0,\n 'runner.backend': record.backend,\n });\n watched.delete(id);\n requestPublish(ownedSessionId);\n } catch (error) {\n retry = true;\n process.emitWarning(`Could not report finished runner ${id}: ${String(error)}`);\n }\n }\n if (retry && !closing && retryTimer === undefined) {\n retryTimer = setTimeout(() => {\n retryTimer = undefined;\n wakeForFinished(ownedSessionId);\n }, COMPLETION_RETRY_MS);\n retryTimer.unref?.();\n }\n if (watched.size === 0) {\n clearTimeout(retryTimer);\n retryTimer = undefined;\n unsubscribeWatched?.();\n unsubscribeWatched = undefined;\n }\n })\n .catch((error: unknown) => {\n process.emitWarning(`Could not report a finished runner: ${String(error)}`);\n });\n };\n\n const watchRunner = (id: string): void => {\n const ownedSessionId = sessionId;\n if (!wakeAgent || closing || ownedSessionId === undefined) return;\n watched.add(id);\n requestPublish(ownedSessionId);\n // Independent of the optional activity: the wake-up must not depend on a\n // browser watching the runner list.\n unsubscribeWatched ??= container.runnerRegistry.subscribe(() => wakeForFinished(ownedSessionId), ownedSessionId);\n // It may already have exited between promotion and subscription.\n wakeForFinished(ownedSessionId);\n };\n\n const activity: PoHeadlessActivity = {\n name: 'runner',\n // Session creation waits on activity start, so supervision runs in the\n // background (it publishes once ready); bash and compaction recovery still\n // await `ensureSession`.\n start: async (executionContext: PoHeadlessExecutionContext) => {\n void ensureSession(executionContext.sessionId).catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n requestPublish(executionContext.sessionId);\n return () => {\n // The retained owner keeps supervision alive. Optional activity toggles\n // must not repeat startup cleanup, subscriptions, or history initialization.\n };\n },\n };\n return {\n container,\n activity,\n ensureSession,\n watchRunner,\n reportStart(startedSessionId, failure) {\n startFailures += 1;\n startFailure =\n failure === undefined ? undefined : { id: `${String(Date.now())}-${String(startFailures)}`, message: failure };\n requestPublish(startedSessionId);\n },\n async recoverCompaction(event) {\n if (\n event.status !== 'completed' ||\n closing ||\n !execution ||\n typeof event.entryId !== 'string' ||\n event.entryId === recoveredCompactionId\n )\n return;\n const entries = await execution.session.entries();\n const compacted = entries.findLast((entry) => entry.type === 'compaction');\n if (closing || compacted?.id !== event.entryId) return;\n await ensureSession(execution.sessionId);\n let records;\n try {\n records = await container.runnerRegistry.listBySession(execution.sessionId);\n } catch {\n return;\n }\n if (closing) return;\n const active = records.filter((record) => record.state === 'running');\n const text = activeRunnerRecovery(active);\n if (!text) return;\n await execution.session.appendMessage?.({\n type: 'po-runner-compaction',\n text,\n display: false,\n details: { sessionId: execution.sessionId, runnerIds: active.map((record) => record.id) },\n });\n if (!closing) recoveredCompactionId = event.entryId;\n },\n backgroundWorkPlugin: (context) => {\n context.inject([PO_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readPoBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => backgroundWorkItems,\n });\n backgroundWorkProvider = registration;\n return () => {\n registration.dispose();\n if (backgroundWorkProvider === registration) backgroundWorkProvider = undefined;\n };\n });\n },\n dispose() {\n if (disposal) return disposal;\n closing = true;\n disposal = (async () => {\n // Session cleanup completes every runner it stops, and none of those may\n // start a turn in a session that is going away.\n closing = true;\n detach?.();\n detach = undefined;\n clearTimeout(retryTimer);\n retryTimer = undefined;\n watched.clear();\n unsubscribeWatched?.();\n unsubscribeWatched = undefined;\n unsubscribeRunnerUpdates?.();\n unsubscribeRunnerUpdates = undefined;\n await wakes;\n await publishInFlight;\n await refreshInFlight;\n try {\n execution?.client.setStatus(RUNNER_STATUS_KEY, undefined);\n } catch (error) {\n process.emitWarning(`Could not clear runner status: ${String(error)}`);\n }\n await runCleanups(\n [\n disposeRuntime,\n () => telemetry?.recordEvent('po_runner.session_finished', { outcome: 'stopped' }),\n () => telemetry?.shutdown(),\n ],\n 'Runner shutdown failed.',\n );\n })();\n return disposal;\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { CONFIG_DIR_NAME } from '@earendil-works/pi-coding-agent';\n\nimport {\n MAX_ERROR_ENTRIES_CEILING,\n MAX_PATTERNS,\n MAX_PATTERN_LENGTH,\n MAX_RESULT_BYTES_CEILING,\n MAX_RESULT_LINES_CEILING,\n MAX_RESULT_TOKENS_CEILING,\n RATIO_KEYS,\n RUNNER_SETTINGS_FILE,\n} from '../../constants/runnerSettings';\nimport type { IRunnerSettingsLoader, RunnerSettings, RunnerSettingsLoad } from '../../types/runnerSettings';\n\n/** Pi's convention is one JSON file per extension under the config directory. */\n\nconst EMPTY: RunnerSettingsLoad = { settings: {}, issues: [] };\nconst COUNT_KEYS = [\n 'maxResultBytes',\n 'maxResultLines',\n 'successMaxResultBytes',\n 'maxResultTokens',\n 'successMaxResultTokens',\n 'errorMaxEntries',\n 'errorMaxVariantsJoined',\n] as const;\nconst KNOWN_KEYS = new Set<string>([...RATIO_KEYS, ...COUNT_KEYS, 'errorPatterns']);\n/** Ceilings so a project file cannot flood the model's context. */\nconst CEILINGS: Readonly<Record<string, number>> = {\n maxResultBytes: MAX_RESULT_BYTES_CEILING,\n successMaxResultBytes: MAX_RESULT_BYTES_CEILING,\n maxResultTokens: MAX_RESULT_TOKENS_CEILING,\n successMaxResultTokens: MAX_RESULT_TOKENS_CEILING,\n maxResultLines: MAX_RESULT_LINES_CEILING,\n errorMaxEntries: MAX_ERROR_ENTRIES_CEILING,\n errorMaxVariantsJoined: MAX_ERROR_ENTRIES_CEILING,\n};\n\nexport class RunnerSettingsLoader implements IRunnerSettingsLoader {\n load(cwd: string, trusted: boolean): RunnerSettingsLoad {\n // Project-local configuration is untrusted input until the project is.\n if (!trusted) return EMPTY;\n\n let raw: string;\n try {\n raw = fs.readFileSync(path.join(cwd, CONFIG_DIR_NAME, RUNNER_SETTINGS_FILE), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return EMPTY;\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} could not be read`] };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} is not valid JSON`] };\n }\n return validate(parsed);\n }\n}\n\nfunction validate(parsed: unknown): RunnerSettingsLoad {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} must contain a JSON object`] };\n }\n\n const source = parsed as Record<string, unknown>;\n const settings: Record<string, unknown> = {};\n const issues: string[] = [];\n // A rejected key is reported rather than dropped: a silent typo reads as a\n // setting that simply does not work.\n for (const key of Object.keys(source)) if (!KNOWN_KEYS.has(key)) issues.push(`unknown key ${key}`);\n\n for (const key of COUNT_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n issues.push(`${key} must be a positive integer`);\n continue;\n }\n settings[key] = Math.min(value, CEILINGS[key] ?? value);\n }\n\n for (const key of RATIO_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {\n issues.push(`${key} must be a number above 0 and below 1`);\n continue;\n }\n settings[key] = value;\n }\n\n const patterns = source.errorPatterns;\n if (patterns !== undefined) {\n if (!Array.isArray(patterns) || patterns.some((entry) => typeof entry !== 'string')) {\n issues.push('errorPatterns must be an array of strings');\n } else {\n const accepted: string[] = [];\n for (const pattern of patterns.slice(0, MAX_PATTERNS) as string[]) {\n if (pattern.length > MAX_PATTERN_LENGTH) {\n issues.push('errorPatterns entry is too long');\n continue;\n }\n try {\n new RegExp(pattern, 'iu');\n accepted.push(pattern);\n } catch {\n issues.push(`errorPatterns entry is not a valid regular expression: ${pattern}`);\n }\n }\n if (accepted.length > 0) settings.errorPatterns = accepted;\n }\n }\n\n return { settings: settings as RunnerSettings, issues };\n}\n","import { readPoFs } from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { REGISTRY_PATH_ENV } from '../../../../../constants/runnerRegistry';\nimport {\n HISTORY_SWEEP_INTERVAL_MS,\n RUNNER_FINISHED_MESSAGE,\n RUNNER_STATUS_POLL_MS,\n} from '../../../../../constants/runnerRuntime';\nimport { RUNNER_SETTINGS_FILE } from '../../../../../constants/runnerSettings';\nimport { SystemClock } from '../../../../../services/clock';\nimport { createHeadlessBashTool, createHeadlessRunnersCommand } from '../../../../../services/headless';\nimport { summarizeLog } from '../../../../../services/logReader';\nimport { createRunnerDependencies } from '../../../../../services/runnerDependencies';\nimport { createRunnerHost } from '../../../../../services/runnerHost';\nimport { createDefaultProcessRegistry } from '../../../../../services/runnerRegistry';\nimport { createRunnerServerRuntime } from '../../../../../services/runnerServerRuntime';\nimport { RunnerSettingsLoader } from '../../../../../services/runnerSettings';\n\nexport const RUNNER_SERVER_SCOPE_SERVICE = 'po/runner-server-scope';\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/runner-server-scope': RunnerServerScope;\n }\n}\n\nexport const createRunnerServerRoot = async ({ context, host, agent, signal, resources }: PoServerPluginContext) => {\n if (!host.context.directEvents) throw new Error('Runner server plugin requires the session direct event bus.');\n if (!host.context.cwd || !host.context.environment) {\n throw new Error('Runner server plugin requires session cwd and environment.');\n }\n // Inherit the current headless Pi bridge's isProjectTrusted: () => true.\n // This preserves runner configuration parity, not a new general trust policy.\n const loaded = new RunnerSettingsLoader().load(host.context.cwd, true);\n const settings = Object.freeze({\n ...loaded.settings,\n ...(loaded.settings.errorPatterns ? { errorPatterns: Object.freeze([...loaded.settings.errorPatterns]) } : {}),\n });\n for (const issue of loaded.issues) process.emitWarning(`${RUNNER_SETTINGS_FILE}: ${issue}`);\n const environment = host.context.environment;\n // One per registry for this build in the hub: every session shares the connection and the loop that\n // keeps house for it, and the last session to close disposes them.\n const runnerHost = await resources.acquire(\n ['runner-host', environment[REGISTRY_PATH_ENV] ?? null],\n () =>\n createRunnerHost({\n processRegistry: createDefaultProcessRegistry(environment),\n clock: new SystemClock(),\n statusPollMs: RUNNER_STATUS_POLL_MS,\n historySweepMs: HISTORY_SWEEP_INTERVAL_MS,\n onWarning: (message) => process.emitWarning(message),\n }),\n (shared) => shared.dispose(),\n );\n const dependencies = createRunnerDependencies({\n settings,\n cwd: host.context.cwd,\n environment,\n processRegistry: runnerHost.processRegistry,\n ...(host.context.executionBudget === undefined ? {} : { executionBudget: host.context.executionBudget }),\n });\n const execution = agent?.context;\n const wakeAgent =\n execution &&\n (async (content: string, requestId: string, notice: string): Promise<void> => {\n try {\n // 'steer' joins a running turn and starts one on an idle session.\n // Reuse completion identity across retries and acknowledge admission, not settlement.\n // A custom message, not a prompt: the model reads it as input, while the\n // transcript shows the notice as an event rather than as the person's words.\n if (!execution.session.admitMessage) throw new Error('The session cannot admit a runner notification.');\n await execution.session.admitMessage(\n { type: RUNNER_FINISHED_MESSAGE, text: content, display: true, details: { notice } },\n 'steer',\n requestId,\n );\n } catch (error) {\n // Keep completion ownership with the watcher until admission succeeds.\n await Promise.resolve()\n .then(() => execution.client.notify({ body: String(error), level: 'warning' }))\n .catch(() => undefined);\n throw error;\n }\n });\n const runtime = createRunnerServerRuntime(dependencies, runnerHost, host.context.directEvents, wakeAgent, execution);\n const bash = (wakeOnExit: boolean) =>\n createHeadlessBashTool(\n {\n run: async (request) => {\n await runtime.ensureSession(request.sessionId);\n const result = await dependencies.bashRunService.run({\n ...request,\n ...(signal ? { signal: request.signal ? AbortSignal.any([signal, request.signal]) : signal } : {}),\n });\n // Without this, nothing tells the agent the runner ended.\n if (wakeOnExit && result.kind === 'promoted') runtime.watchRunner(result.id);\n return result;\n },\n },\n (path, lines, bytes, tokens) => summarizeLog(path, lines, bytes, tokens, settings),\n settings,\n dependencies.environment,\n );\n const value = {\n tool: bash(true),\n // Remote MCP runners must not wake the local agent; the remote caller polls instead.\n remoteTool: bash(false),\n command: createHeadlessRunnersCommand(dependencies, {\n // Without the platform fs a missing folder still fails, only at spawn and less plainly.\n fs: readPoFs(context),\n onStart: (startedSessionId, failure) => runtime.reportStart(startedSessionId, failure),\n }),\n recoverCompaction: runtime.recoverCompaction,\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 // Package install is on the session-create path; bash awaits supervision itself.\n onStart: execution\n ? () => {\n void runtime\n .ensureSession(execution.sessionId)\n .catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n }\n : undefined,\n onStop: runtime.dispose,\n };\n};\n\nexport type RunnerServerScope = Awaited<ReturnType<typeof createRunnerServerRoot>>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createRunnerServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createRunnerServerRoot);\n","import type { PoHubChannel, PoHubChannelSource } from '@agimon-ai/po-core/hubChannel';\n\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport { type RunnerRunView } from '../../types/webRunners';\n\nfunction runnerPayload(value: unknown): value is { runs: RunnerRunView[] } {\n return typeof value === 'object' && value !== null && Array.isArray((value as { runs?: unknown }).runs);\n}\n\n/**\n * The runners data channel consumes lifecycle-owned snapshots from the session\n * server. Durable registry files are read by the owning session only, never by\n * this hub channel.\n */\nexport function createRunnersChannel(): PoHubChannel {\n return {\n frameType: RUNNER_RUNS_TYPE,\n start(host) {\n const latest = new Map<string, RunnerRunView[]>();\n const subscriptions = new Map<string, () => void>();\n const source: PoHubChannelSource = {\n payloadFor(scope) {\n const runs = latest.get(scope.sessionId);\n return runs === undefined ? undefined : { runs };\n },\n sessionAdded(scope) {\n subscriptions.get(scope.sessionId)?.();\n latest.delete(scope.sessionId);\n const unsubscribe = host.directEvents.subscribe(\n RUNNER_RUNS_TYPE,\n scope.sessionId,\n (payload) => {\n if (!runnerPayload(payload)) return;\n latest.set(scope.sessionId, payload.runs);\n host.publish(scope.sessionId, payload);\n },\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n },\n sessionRemoved(sessionId) {\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n latest.delete(sessionId);\n },\n close() {\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n latest.clear();\n },\n };\n return source;\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { createRunnersChannel } from '../../../services/runnersChannel';\nexport default defineChannel(function runnerRunsChannel(): PoHubChannel {\n return createRunnersChannel();\n});\n","export const LOG_DIR_NAME = 'logs';\nexport const STATE_EXTENSION = '.json';\nexport const RUNNING_STATE = 'running';\nexport const STATE_POLL_MS = 1000;\nexport const HEARTBEAT_MS = 15_000;\nexport const DEFAULT_LINES = 200;\nexport const MAX_LINES = 5000;\nexport const SAFE_SEGMENT = /^[\\w.@-]+$/;\nexport const SCREEN_POLL_MS = 100;\nexport const RAW_LOG_SUFFIX = '.raw';\nexport const MAX_INPUT_CHARS = 4096;\n/** Ceiling for one finite log/screen read, before base64 encoding. */\nexport const MAX_OUTPUT_BYTES = 64 * 1024;\nexport const NOT_ATTACHABLE = 'No attachable runner by that name in this session.';\n","export const RUNNER_API_BASE_PATH = 'runner';\nexport const RUN_ID_PARAM = 'runId';\nexport const RUNNER_SCREEN_EVENT = 'screen';\nexport const RUNNER_LOG_STREAM_EVENT = 'append';\nexport const RUNNER_LOG_PING_EVENT = 'ping';\n","import fs from 'node:fs';\n\nimport { POLL_MS } from '../../constants/logTail';\nimport type { ILogTail, LogTailHandle, LogTailOptions } from '../../types/logTail';\n\n/** How often the tail restats the file; the same cadence the CLI's --follow uses. */\n\n/**\n * Polling tail: read the delta between the last offset and the file's current\n * size, and hand back only complete lines.\n *\n * Same reliability posture as this package's runner watcher: the poll is the\n * mechanism, not an accelerator for a change notification. fs.watchFile only\n * fires when it observes a difference from a baseline it took itself, so a\n * write that lands before its first stat is a change it never reports; a\n * restat every tick has no such window.\n *\n * A write that lands mid-line is common, so the trailing fragment is held\n * until its newline arrives rather than emitted as a line that will change.\n */\nexport class LogTail implements ILogTail {\n follow(logPath: string, options: LogTailOptions): LogTailHandle {\n let offset = options.from;\n let pending = '';\n let closed = false;\n\n const drain = (size: number): void => {\n // A shrunken file was rotated or truncated; start over rather than\n // reading from an offset that now points into different content.\n if (size < offset) {\n offset = 0;\n pending = '';\n }\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(logPath, 'r');\n let bytesRead: number;\n try {\n bytesRead = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n offset = size;\n if (bytesRead <= 0) return;\n pending += buffer.subarray(0, bytesRead).toString('utf8');\n const lines = pending.split('\\n');\n pending = lines.pop() ?? '';\n if (lines.length > 0) options.onLines(lines, offset - Buffer.byteLength(pending, 'utf8'));\n };\n\n const tick = (): void => {\n if (closed) return;\n try {\n drain(fs.statSync(logPath).size);\n } catch (error) {\n // The runner has not opened its log yet; the next tick settles it.\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n options.onError(error instanceof Error ? error : new Error(String(error)));\n }\n };\n\n const timer = setInterval(tick, POLL_MS);\n // Following a log must never be the reason a process stays alive.\n timer.unref?.();\n return {\n close() {\n if (closed) return;\n closed = true;\n clearInterval(timer);\n },\n };\n }\n}\n","import type { LogSlice } from './logReader';\n\n/**\n * The runner log API, shared by this package's session-scoped routes and its\n * cockpit plugin. The two halves run in different processes, so the wire\n * vocabulary is declared here: the browser half may reach `src/types` and\n * nothing else on the server side.\n *\n * Where the routes live is not here. That is `src/types/apiRoutes`, which both\n * halves read, and the generated client turns into the URLs the page addresses.\n * The file a run writes to is still read from its metadata record by the server\n * alone.\n */\n\n/**\n * One chunk of an attached pane's output.\n *\n * Base64 because these are terminal bytes, escape sequences and all, and a\n * server-sent event is a line-oriented text frame that would mangle them.\n */\nexport interface RunnerScreenEvent {\n chunk: string;\n /** Byte offset just past this chunk, so a reconnect resumes rather than replays. */\n offset?: number;\n /** Set once the runner exits, so the page can stop watching. */\n ended?: boolean;\n}\n\n/** A finite byte slice for sealed HTTP followers. Decode chunks incrementally, not independently. */\nexport interface RunnerOutputResponse {\n /** Base64 bytes, preserving split UTF-8 characters and terminal escape sequences. */\n chunk: string;\n offset: number;\n /** The file was replaced or became shorter than the requested offset; discard the previous display/decoder state. */\n reset: boolean;\n /** Opaque raw file identity. Send back on screen/read to detect replacement, including regrowth. */\n generation?: string;\n exists: boolean;\n /** More bytes were present at read time; request the next slice before waiting. */\n hasMore: boolean;\n /** True only once the runner ended and all available bytes have been read. */\n ended: boolean;\n}\n\n/** The body of a keystroke POST. Text, not key codes: the pane wants bytes. */\nexport interface RunnerInputRequest {\n text: string;\n}\n\n/** The named SSE event carrying a RunnerScreenEvent payload. */\n\n/**\n * A log request as query parameters. `grep` is a literal substring, not a\n * regular expression, because that is what the reader matches; `lines` bounds\n * the answer to the last N lines of whatever survived the filter.\n */\nexport interface RunnerLogQueryParams {\n lines?: number;\n grep?: string;\n ignoreCase?: boolean;\n contextLines?: number;\n}\n\n/** The slice the log route answers with, plus which runner it came from. */\nexport interface RunnerLogResponse extends LogSlice {\n runId: string;\n /** True while the runner is still writing, so the page knows following is worth offering. */\n running: boolean;\n}\n\n/**\n * One server-sent event on the follow stream: the lines appended since the last\n * one. The stream never filters: a page that sets a query stops following and\n * reads a filtered slice instead, because grep with context is a whole-file\n * answer and cannot be assembled from a chunk at a time.\n */\nexport interface RunnerLogStreamEvent {\n /** Appended lines, in order; empty when the event only reports the runner ending. */\n lines: string[];\n /**\n * Byte offset just past the last line in this event.\n *\n * A follower that loses the stream resumes here. Without it the page could\n * only fall back to the offset its last slice reported, which every line\n * delivered since has already moved past, so a reconnect would replay them.\n */\n offset?: number;\n /** Set once the runner exits, so the page can stop following without polling the run list. */\n ended?: boolean;\n}\n\n/** The named SSE event carrying a RunnerLogStreamEvent payload. */\n\n/**\n * The named SSE event that carries nothing.\n *\n * A runner can be alive and silent for minutes, which on the wire is\n * indistinguishable from a connection that died. This is sent on a timer so\n * the socket keeps proving itself. Readers that only listen for `append`\n * ignore it, which is what makes it safe to add.\n */\n\n/** Query parameter names, shared so the page and the route cannot drift apart. */\nexport const RUNNER_LOG_PARAMS = {\n lines: 'lines',\n grep: 'grep',\n ignoreCase: 'ignoreCase',\n contextLines: 'contextLines',\n from: 'from',\n generation: 'generation',\n} as const;\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport { RUN_ID_PARAM } from '../constants/webRunnerLog';\nimport { RUNNER_LOG_PARAMS, type RunnerLogResponse, type RunnerOutputResponse } from './webRunnerLog';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path: the build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/runner/`, so the mount is\n * stated once, by the folder that creates it. This package stated it four times\n * before, once per URL builder, each of them re-spelling `/plugins/` and the\n * session prefix by hand.\n *\n * The Hono app registers these paths verbatim and the browser addresses them,\n * so a path cannot move on one side alone. `:runId` is Hono's own parameter\n * syntax for that reason; a caller supplies the value as `params`, and the\n * client encodes the segment.\n */\nconst routes = {\n log: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log`,\n query: [\n RUNNER_LOG_PARAMS.lines,\n RUNNER_LOG_PARAMS.grep,\n RUNNER_LOG_PARAMS.ignoreCase,\n RUNNER_LOG_PARAMS.contextLines,\n ],\n response: undefined as unknown as ApiResponseOf<RunnerLogResponse>,\n },\n /**\n * The live tail of that same log.\n *\n * `stream: true`, so the client exposes a URL and no call method: the\n * consumer is the browser’s own SSE reader, and a relayed call would\n * buffer a stream that never ends.\n */\n logStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** The unscrubbed pane bytes, for a terminal emulator rather than a reader. */\n screenStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** Finite byte reads keep sealed HTTP exchanges bounded, unlike SSE. */\n logRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/read`,\n query: [RUNNER_LOG_PARAMS.from],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n screenRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/read`,\n query: [RUNNER_LOG_PARAMS.from, RUNNER_LOG_PARAMS.generation],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n input: {\n method: 'POST',\n path: `/runners/:${RUN_ID_PARAM}/screen/input`,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { PoApi, PoApiContext, PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport {\n DEFAULT_LINES,\n HEARTBEAT_MS,\n LOG_DIR_NAME,\n MAX_INPUT_CHARS,\n MAX_OUTPUT_BYTES,\n MAX_LINES,\n NOT_ATTACHABLE,\n RAW_LOG_SUFFIX,\n RUNNING_STATE,\n SAFE_SEGMENT,\n SCREEN_POLL_MS,\n STATE_EXTENSION,\n STATE_POLL_MS,\n} from '../../constants/runnerLogApi';\nimport {\n RUN_ID_PARAM,\n RUNNER_API_BASE_PATH,\n RUNNER_LOG_PING_EVENT,\n RUNNER_LOG_STREAM_EVENT,\n RUNNER_SCREEN_EVENT,\n} from '../../constants/webRunnerLog';\nimport { LogReader } from '../../services/logReader';\nimport { LogTail } from '../../services/logTail';\nimport { PtyBackendChain } from '../../services/ptyBackendChain';\nimport { RmuxBackend } from '../../services/rmuxBackend';\nimport { RunnerPaths, resolveRunnerStoreDirectory, runnerStateDirFor } from '../../services/runnerPaths';\nimport { TmuxBackend } from '../../services/tmuxBackend';\nimport { parseRunnerRecord } from '../../services/webRunnerRuns';\nimport routes from '../../types/apiRoutes';\nimport type { ILogReader, LogQuery } from '../../types/logReader';\nimport type { ILogTail } from '../../types/logTail';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport type { RunnerRecord } from '../../types/runnerRegistry';\nimport {\n RUNNER_LOG_PARAMS,\n type RunnerLogResponse,\n type RunnerLogStreamEvent,\n type RunnerOutputResponse,\n type RunnerScreenEvent,\n} from '../../types/webRunnerLog';\n\n/** How often a follow re-reads the record to notice the runner exiting. */\n/**\n * How often a silent but living stream proves it is still there. Short enough\n * that an idle proxy does not reap the socket, long enough to cost nothing.\n */\n/** Kept in step with LogQuery's own default, so an unasked-for tail is the same size everywhere. */\n/** A page that asks for more than this gets this; the whole point of the route is a bounded read. */\n/** Path segments the client supplies are names, never paths. */\n/** Backends that expose a pane another process can read and write. */\nconst MULTIPLEXER_BACKENDS: ReadonlySet<string> = new Set(['rmux', 'tmux']);\n/** How often an attached pane's new bytes are picked up. */\n/** The unscrubbed copy the runner's sink writes beside the log, for interactive runs. */\n/** One keystroke batch; a paste is not a file transfer. */\n/** Said the same way for every reason, so the route never reports which check failed. */\n\nexport interface RunnerLogApiOptions {\n /** The session these routes answer for; the host owns exactly one. */\n sessionId: string;\n /** The store root; defaults to this environment's agent directory. */\n storeDir?: string;\n environment?: Readonly<Record<string, string | undefined>>;\n cwd?: string;\n logReader?: ILogReader;\n logTail?: ILogTail;\n /**\n * Reaches a live pane to read its screen and send it input.\n *\n * A session API is handed nothing but its session id, so it cannot touch the\n * in-process PTY handles the extension holds. This talks to the multiplexer\n * over its own socket instead, the same way the CLI's `input` verb does,\n * which is what makes an attached pane reachable across processes at all.\n */\n pane?: IRmuxBackend;\n}\n\n/** Rejects anything that could climb out of the session's own directory before a path is built. */\nfunction isSafeSegment(value: string): boolean {\n return value !== '' && value !== '.' && value !== '..' && SAFE_SEGMENT.test(value);\n}\n\n/** A log must stay inside its session even when a path component is a symlink. Missing files are normal. */\nfunction isSessionLog(file: string, logsDir: string): boolean {\n const resolved = path.resolve(file);\n if (resolved !== logsDir && !resolved.startsWith(`${logsDir}${path.sep}`)) return false;\n try {\n if (fs.lstatSync(file).isSymbolicLink()) return false;\n const realFile = fs.realpathSync(file);\n const realLogs = fs.realpathSync(logsDir);\n return realFile.startsWith(`${realLogs}${path.sep}`);\n } catch (error) {\n // The sink has not created its output yet; a later request validates the created path again.\n return isRecord(error) && error.code === 'ENOENT';\n }\n}\n\n/**\n * The log file a run wrote, or undefined when there is no such run or its\n * record points somewhere it has no business pointing.\n *\n * The record names its own log path, and that record is written by another\n * process, so the path is checked rather than trusted: it must sit inside this\n * session's own logs directory. Without that check a doctored record would\n * turn this route into an arbitrary file read.\n */\nfunction resolveLogFile(stateDir: string, logsDir: string, sessionId: string, runId: string): RunnerRecord | undefined {\n const statePath = path.join(stateDir, `${runId}${STATE_EXTENSION}`);\n let raw: string;\n try {\n raw = fs.readFileSync(statePath, 'utf8');\n } catch {\n return undefined; // No such run in this session; the route answers 404.\n }\n const record = parseRunnerRecord(raw);\n if (record === undefined || record.id !== runId || record.sessionId !== sessionId) return undefined;\n if (!isSessionLog(record.logPath, logsDir)) return undefined;\n return record;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction numberParam(value: string | undefined, fallback: number, max: number): number {\n const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed < 0) return fallback;\n return Math.min(parsed, max);\n}\n\n/** Missing output is normal before the sink opens it; other IO failures must not look like an empty read. */\nfunction readOutput(\n file: string,\n from: number,\n running: boolean,\n priorGeneration?: string,\n screen = false,\n): RunnerOutputResponse {\n let descriptor: number;\n try {\n descriptor = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);\n } catch (error) {\n if (!isRecord(error) || error.code !== 'ENOENT') throw error;\n return { chunk: '', offset: from, reset: false, exists: false, hasMore: false, ended: !running };\n }\n try {\n const stat = fs.fstatSync(descriptor, { bigint: true });\n const size = Number(stat.size);\n // Identity and bytes come from the same descriptor, never a separate marker read.\n const generation = screen ? `${stat.dev}:${stat.ino}:${stat.birthtimeNs}` : undefined;\n const reset = from > size || (priorGeneration !== undefined && generation !== priorGeneration);\n const start = reset ? 0 : from;\n const length = Math.min(size - start, MAX_OUTPUT_BYTES);\n const buffer = Buffer.alloc(length);\n const read = length === 0 ? 0 : fs.readSync(descriptor, buffer, 0, length, start);\n const offset = start + read;\n const hasMore = offset < size;\n return {\n ...(generation === undefined ? {} : { generation }),\n chunk: buffer.subarray(0, read).toString('base64'),\n offset,\n reset,\n exists: true,\n hasMore,\n ended: !running && !hasMore,\n };\n } finally {\n fs.closeSync(descriptor);\n }\n}\n\nfunction logQueryOf(url: URL): LogQuery {\n const grep = url.searchParams.get(RUNNER_LOG_PARAMS.grep) ?? undefined;\n return {\n lines: numberParam(url.searchParams.get(RUNNER_LOG_PARAMS.lines) ?? undefined, DEFAULT_LINES, MAX_LINES),\n ...(grep === undefined || grep === '' ? {} : { grep }),\n ignoreCase: url.searchParams.get(RUNNER_LOG_PARAMS.ignoreCase) === 'true',\n contextLines: numberParam(url.searchParams.get(RUNNER_LOG_PARAMS.contextLines) ?? undefined, 0, MAX_LINES),\n };\n}\n\n/**\n * This package's HTTP surface: one runner's log, as a bounded slice and as a\n * live tail.\n *\n * The routes are mounted inside one session's own server, so they name a runner\n * and nothing else; the session is the host, not a parameter. The reader only\n * ever returns the last N lines of what matched, so both routes stay bounded no\n * matter how large the file on disk is.\n *\n * Paths come from src/types/apiRoutes, the one table the browser's generated\n * client reads too, so this app and the cockpit cannot disagree about where a\n * route lives. They stay relative to the mount the host strips.\n */\nexport function createRunnerLogApi(options: RunnerLogApiOptions): Hono {\n const logReader = options.logReader ?? new LogReader();\n const logTail = options.logTail ?? new LogTail();\n const sessionId = options.sessionId;\n const paths = new RunnerPaths(options.cwd, options.environment);\n const storeDir = options.storeDir ?? resolveRunnerStoreDirectory(options.environment ?? process.env);\n const stateDir =\n options.storeDir === undefined ? paths.stateDirectory(sessionId) : runnerStateDirFor(storeDir, sessionId);\n const logsDir =\n options.storeDir === undefined ? paths.logDirectory(sessionId) : path.join(storeDir, sessionId, LOG_DIR_NAME);\n const app = new Hono();\n\n // Built on first use, not on mount: most sessions never attach to a pane,\n // and constructing this probes the filesystem for a multiplexer binary.\n let paneBackend: IRmuxBackend | undefined = options.pane;\n const pane = (): IRmuxBackend => {\n paneBackend ??= (() => {\n const paths = new RunnerPaths(options.cwd, options.environment);\n return new PtyBackendChain(new RmuxBackend(paths), new TmuxBackend(paths));\n })();\n return paneBackend;\n };\n\n /** The record behind a request, or undefined when this session has no such run. */\n const runnerOf = (runId: string): RunnerRecord | undefined =>\n isSafeSegment(runId) ? resolveLogFile(stateDir, logsDir, sessionId, runId) : undefined;\n\n /**\n * The owned pane whose recorded output a request may read.\n *\n * Same posture as the log routes: the record is written by another process,\n * so nothing on it is trusted until it is checked. The target must be one\n * this package owns, and the run must have been started\n * interactive. A non-interactive run has no terminal to type into, and a\n * subprocess-backed run has no pane at all.\n */\n const paneOf = (runId: string): { record: RunnerRecord; target: string } | undefined => {\n const record = runnerOf(runId);\n if (record === undefined || record.interactive !== true) return undefined;\n const target = record.backendTarget;\n if (target === undefined || !MULTIPLEXER_BACKENDS.has(record.backend) || !isSafeSegment(target)) return undefined;\n return { record, target };\n };\n const attachableOf = (runId: string): { record: RunnerRecord; target: string } | undefined => {\n const owned = paneOf(runId);\n return owned?.record.state === RUNNING_STATE ? owned : undefined;\n };\n\n for (const [route, screen] of [\n [routes.logRead, false],\n [routes.screenRead, true],\n ] as const) {\n app.get(route.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n // A completed pane is readable so a finite follower can drain its last bytes. Input still requires a live pane.\n const record = screen ? paneOf(runId)?.record : runnerOf(runId);\n if (record === undefined)\n return context.json({ error: screen ? NOT_ATTACHABLE : `No runner '${runId}' in this session.` }, 404);\n const value = context.req.query(RUNNER_LOG_PARAMS.from);\n const from = value === undefined ? 0 : Number(value);\n if ((value !== undefined && !/^\\d+$/.test(value)) || !Number.isSafeInteger(from) || from < 0) {\n return context.json({ error: 'Expected a non-negative safe integer byte offset.' }, 400);\n }\n const file = screen ? `${record.logPath}${RAW_LOG_SUFFIX}` : record.logPath;\n if (!isSessionLog(file, logsDir)) return context.json({ error: 'No readable output in this session.' }, 404);\n return context.json(\n readOutput(\n file,\n from,\n record.state === RUNNING_STATE,\n screen ? context.req.query(RUNNER_LOG_PARAMS.generation) : undefined,\n screen,\n ),\n );\n });\n }\n\n app.get(routes.log.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const record = runnerOf(runId);\n if (record === undefined) return context.json({ error: `No runner '${runId}' in this session.` }, 404);\n const slice = logReader.read(record.logPath, logQueryOf(new URL(context.req.url)));\n const body: RunnerLogResponse = { ...slice, runId, running: record.state === RUNNING_STATE };\n return context.json(body);\n });\n\n app.get(routes.logStream.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const record = runnerOf(runId);\n if (record === undefined) return context.json({ error: `No runner '${runId}' in this session.` }, 404);\n const from = numberParam(\n new URL(context.req.url).searchParams.get(RUNNER_LOG_PARAMS.from) ?? undefined,\n 0,\n Number.MAX_SAFE_INTEGER,\n );\n\n return streamSSE(context, async (stream) => {\n const send = (event: RunnerLogStreamEvent): Promise<void> =>\n stream.writeSSE({ event: RUNNER_LOG_STREAM_EVENT, data: JSON.stringify(event) });\n\n // The tail and the state poll both push onto one queue, so the stream\n // writes in the order things happened rather than interleaving two\n // writers onto the same socket.\n let pump: Promise<void> = Promise.resolve();\n const queue = (event: RunnerLogStreamEvent): void => {\n pump = pump.then(() => send(event)).catch(() => undefined);\n };\n\n let finish = (): void => undefined;\n const done = new Promise<void>((resolve) => {\n finish = resolve;\n });\n const handle = logTail.follow(record.logPath, {\n from,\n onLines: (lines, completeThrough) => queue({ lines, offset: completeThrough }),\n onError: () => finish(),\n });\n // A finished runner writes nothing more, so the page is told once and\n // the stream ends rather than holding a socket open forever.\n const poll = setInterval(() => {\n const current = runnerOf(runId);\n if (current !== undefined && current.state === RUNNING_STATE) return;\n queue({ lines: [], ended: true });\n finish();\n }, STATE_POLL_MS);\n // A runner can be alive and produce nothing for minutes. Without this the\n // page cannot tell that from a stream that quietly died.\n const heartbeat = setInterval(() => {\n pump = pump.then(() => stream.writeSSE({ event: RUNNER_LOG_PING_EVENT, data: '' })).catch(() => undefined);\n }, HEARTBEAT_MS);\n\n stream.onAbort(() => finish());\n await done;\n clearInterval(poll);\n clearInterval(heartbeat);\n handle.close();\n await pump;\n });\n });\n\n app.get(routes.screenStream.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const attachable = attachableOf(runId);\n if (attachable === undefined) return context.json({ error: NOT_ATTACHABLE }, 404);\n // The record's log path is already proven to sit inside this session's own\n // logs directory, and the raw copy is that same path with a suffix, so it\n // cannot point anywhere new.\n const rawPath = `${attachable.record.logPath}${RAW_LOG_SUFFIX}`;\n const from = numberParam(\n new URL(context.req.url).searchParams.get(RUNNER_LOG_PARAMS.from) ?? undefined,\n 0,\n Number.MAX_SAFE_INTEGER,\n );\n\n return streamSSE(context, async (stream) => {\n let pump: Promise<void> = Promise.resolve();\n const queue = (event: RunnerScreenEvent): void => {\n pump = pump\n .then(() => stream.writeSSE({ event: RUNNER_SCREEN_EVENT, data: JSON.stringify(event) }))\n .catch(() => undefined);\n };\n\n let finish = (): void => undefined;\n const done = new Promise<void>((resolve) => {\n finish = resolve;\n });\n\n let offset = from;\n const drain = (): void => {\n let size: number;\n try {\n size = fs.statSync(rawPath).size;\n } catch {\n return; // The sink has not opened the file yet; the next tick settles it.\n }\n // The sink starts the file over when it passes its ceiling, so a\n // smaller file is a fresh one rather than a bad offset.\n if (size < offset) offset = 0;\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(rawPath, 'r');\n let read: number;\n try {\n read = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n if (read <= 0) return;\n offset += read;\n // Bytes, not lines: a terminal's escape sequences straddle newlines and\n // mean nothing once split on them.\n queue({ chunk: buffer.subarray(0, read).toString('base64'), offset });\n };\n\n const poll = setInterval(() => {\n const current = runnerOf(runId);\n drain();\n if (current === undefined || current.state !== RUNNING_STATE) {\n queue({ chunk: '', offset, ended: true });\n finish();\n }\n }, SCREEN_POLL_MS);\n\n stream.onAbort(() => finish());\n await done;\n clearInterval(poll);\n await pump;\n });\n });\n\n /**\n * Types into a live pane.\n *\n * This is the one route in the package that writes rather than reads, so it\n * is deliberately narrow: an owned target, still running, started\n * interactive, and a body that is nothing but text.\n */\n app.post(routes.input.path, async (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const attachable = attachableOf(runId);\n if (attachable === undefined) return context.json({ error: NOT_ATTACHABLE }, 404);\n\n let body: unknown;\n try {\n body = await context.req.json();\n } catch {\n return context.json({ error: 'Expected a JSON body carrying text.' }, 400);\n }\n const text = isRecord(body) ? body.text : undefined;\n if (typeof text !== 'string' || text === '') return context.json({ error: 'Expected a non-empty text.' }, 400);\n if (text.length > MAX_INPUT_CHARS) return context.json({ error: 'That is more input than a pane takes.' }, 413);\n\n const delivered = await pane().input(attachable.target, text);\n if (!delivered) return context.json({ error: 'The runner did not take the input.' }, 502);\n return context.json({ delivered: true });\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built session entry. */\nexport const api: PoApi = {\n basePath: RUNNER_API_BASE_PATH,\n start(context: PoApiContext): PoApiHandler {\n // The host is one session's server, so its id is the whole scope; a hub\n // would have handed no session at all, and there is nothing to answer.\n const app = createRunnerLogApi({\n sessionId: context.sessionId ?? '',\n cwd: context.cwd,\n environment: context.environment,\n });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request: a follow's watch and its poll are both\n // torn down when its own stream ends or the socket aborts.\n close: () => undefined,\n };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/runnerLogApi';\n\n/**\n * Mounts this package's HTTP API at `runner`, which the folder names.\n *\n * The folder is the declaration: the build reads the base path off it and hands\n * the browser a client bound to the same segment, so the mount is stated once\n * rather than in a constant and in four URL builders that were each free to\n * drift.\n */\nexport default defineRoute(api);\n","import { defineServerCommand, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\nexport default defineServerCommand(\n (context: WithRoot<PoServerPluginContext, RunnerServerScope>) => context.root.command,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(({ root }: WithRoot<PoServerPluginContext, RunnerServerScope>) => ({\n event: 'session_compact' as const,\n handle: root.recoverCompaction,\n}));\n","import { defineResource } from '@agimon-ai/po-core/extensionFile';\nimport { PO_HELP_WHEN } from '@agimon-ai/po-core/help';\nimport { packageResourcePath, readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineResource({\n name: 'po-use-runner',\n description:\n 'Use Po Pi Runner to supervise shell commands, inspect durable logs, provide interactive input, and stop background runs.',\n when: PO_HELP_WHEN,\n kind: 'skill' as const,\n path: packageResourcePath(import.meta.url, 'src/prompts/po-use-runner/SKILL.md'),\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-runner/SKILL.md'),\n});\n","import { defineServerTool, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\nexport default defineServerTool((context: WithRoot<PoServerPluginContext, RunnerServerScope>) => context.root.tool);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelRunnerRuns from '../src/extensions/(backend)/channel/runnerRuns.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/runner/route.server';\nimport commandRunners from '../src/extensions/workspaces/sessions/(backend)/command/runners.server';\nimport hookSessionCompact from '../src/extensions/workspaces/sessions/(backend)/hook/session-compact.server';\nimport resourcePoUseRunner from '../src/extensions/workspaces/sessions/(backend)/resource/po-use-runner.server';\nimport toolBash from '../src/extensions/workspaces/sessions/(backend)/tool/bash.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-runner',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n global: () => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'runner_runs' }, channelRunnerRuns())]; },\n }),\n workspace: (context) => ({\n get channels(): PoServerSessionPlugin['channels'] { return defined([context.hosted('global') ? undefined : () => via({ frameType: 'runner_runs' }, channelRunnerRuns())]); },\n }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): PoServerSessionPlugin['channels'] { return defined([context.hosted('global') ? undefined : () => via({ frameType: 'runner_runs' }, channelRunnerRuns())]); },\n get commands(): PoServerSessionPlugin['commands'] { return [via({ name: 'runners' }, at(commandRunners, contextSession))]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return [via({ event: 'session_compact' }, at(hookSessionCompact, contextSession))]; },\n get resources(): PoServerSessionPlugin['resources'] { return [at(resourcePoUseRunner, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return [via({ name: 'bash' }, at(toolBash, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","names":["rootSession","channelRunnerRuns","apiRoute","commandRunners","hookSessionCompact","resourcePoUseRunner","toolBash"],"sources":["../../src/constants/runnerRuntime.ts","../../src/constants/runnerSettings.ts","../../src/services/runnerHost/index.ts","../../src/constants/webRunners.ts","../../src/constants/webRunnerRuns.ts","../../src/services/webRunnerRuns/index.ts","../../src/services/runnerServerRuntime/index.ts","../../src/services/runnerSettings/index.ts","../../src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/services/runnersChannel/index.ts","../../src/extensions/(backend)/channel/runnerRuns.server.ts","../../src/constants/runnerLogApi.ts","../../src/constants/webRunnerLog.ts","../../src/services/logTail/index.ts","../../src/types/webRunnerLog.ts","../../src/types/apiRoutes.ts","../../src/services/runnerLogApi/index.ts","../../src/extensions/workspaces/sessions/(backend)/api/runner/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/command/runners.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-compact.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/po-use-runner.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/bash.server.ts","../../generated/server.ts"],"sourcesContent":["export const LEADER_SOURCE = '@agimon-ai/po-runner';\nexport const LEADER_GROUP_ORDER = 67;\nexport const ERR_REQUIRES_INTERACTIVE = '/runners requires interactive mode';\nexport const ERR_STOP_USAGE = 'Usage: /runners stop <runner-id> [reason]';\nexport const ERR_START_USAGE = 'Usage: /runners start [name=x] [cwd=y] [interactive=true] -- <command>';\nexport const SESSION_START_EVENT = 'session_start';\nexport const RUNNER_FINISHED_MESSAGE = 'po-runner-finished';\nexport const COMPLETED_STATE = 'completed';\nexport const RMUX_BACKEND = 'rmux';\nexport const STOPPED_REASON = 'stopped';\nexport const RUNNER_STATUS_KEY = 'po-runner-runners';\nexport const BACKGROUND_WORK_PROVIDER = 'po-runner';\nexport const RUNNER_FOOTER_ORDER = 10;\nexport const RUNNER_STATUS_POLL_MS = 500;\nexport const HISTORY_SWEEP_INTERVAL_MS = 600_000;\n","export const RUNNER_SETTINGS_FILE = 'po-runner.json';\nexport const RATIO_KEYS = ['headRatio', 'errorBudgetRatio'] as const;\nexport const MAX_RESULT_BYTES_CEILING = 262_144;\nexport const MAX_RESULT_LINES_CEILING = 5_000;\nexport const MAX_RESULT_TOKENS_CEILING = 100_000;\nexport const MAX_ERROR_ENTRIES_CEILING = 100;\nexport const MAX_PATTERNS = 32;\nexport const MAX_PATTERN_LENGTH = 512;\n","import type { IClock } from '../../types/clock';\nimport type { ProcessRegistryPort } from '../runnerRegistry';\nimport type { RunnerHost, RunnerHostOptions, RunnerHostSession } from './type';\n\nexport type { RunnerHost, RunnerHostOptions, RunnerHostSession } from './type';\n\nconst describe = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n\n/** Runs `pass` again `ms` after the previous pass finished, so two passes never overlap. */\nfunction repeat(clock: IClock, ms: number, pass: () => Promise<void>): { stop(): Promise<void> } {\n let stopped = false;\n let cancel: (() => void) | undefined;\n let running: Promise<void> | undefined;\n const arm = (): void => {\n cancel = clock.after(ms, () => {\n cancel = undefined;\n running = pass().finally(() => {\n running = undefined;\n if (!stopped) arm();\n });\n });\n };\n arm();\n return {\n async stop() {\n stopped = true;\n cancel?.();\n await running;\n },\n };\n}\n\n/**\n * The housekeeping every session running one po-runner build in one hub shares.\n *\n * A session used to open its own registry connection and arm its own status poll and history\n * sweep, and each sweep walked the whole runner store. Here one loop refreshes the attached sessions\n * one at a time, one sweep per store spares every attached session at once, and runners a crashed\n * server left behind are reclaimed once rather than at every session start.\n */\nexport function createRunnerHost(options: RunnerHostOptions): RunnerHost {\n const sessions = new Set<RunnerHostSession>();\n let closed = false;\n let reclaiming: Promise<void> | undefined;\n let sweeping: Promise<void> | undefined;\n\n const refreshAll = async (): Promise<void> => {\n // A Set skips what is deleted mid-pass, so a session detached during a pass is not refreshed after.\n for (const session of sessions) {\n if (closed) return;\n try {\n await session.refresh();\n } catch (error) {\n options.onWarning(`Could not refresh po-runner session ${session.sessionId}: ${describe(error)}`);\n }\n }\n };\n\n const sweepAll = (): Promise<void> => {\n sweeping ??= (async () => {\n try {\n const byStore = new Map<string, RunnerHostSession[]>();\n for (const session of sessions) {\n const store = session.paths.storeDirectory();\n byStore.set(store, [...(byStore.get(store) ?? []), session]);\n }\n for (const group of byStore.values()) {\n const [first] = group;\n if (closed || first === undefined) return;\n const live = new Set(group.map((session) => session.sessionId));\n const now = options.clock.now();\n const result = first.paths.sweepHistoryAsync\n ? await first.paths.sweepHistoryAsync(first.historyTtlMs, now, live)\n : first.paths.sweepHistory(first.historyTtlMs, now, live);\n for (const error of result.errors) options.onWarning(error);\n }\n } catch (error) {\n options.onWarning(`Could not sweep po-runner history: ${describe(error)}`);\n }\n })().finally(() => {\n sweeping = undefined;\n });\n return sweeping;\n };\n\n const polling = repeat(options.clock, options.statusPollMs, refreshAll);\n const sweeper = repeat(options.clock, options.historySweepMs, sweepAll);\n const processRegistry: ProcessRegistryPort = {\n registerProcess: (...args) => options.processRegistry.registerProcess(...args),\n releaseProcess: (...args) => options.processRegistry.releaseProcess(...args),\n listProcesses: (...args) => options.processRegistry.listProcesses(...args),\n // Sessions come and go; the connection lives as long as the host.\n close: () => undefined,\n };\n\n return {\n processRegistry,\n attach(session) {\n if (closed) throw new Error('The po-runner host is disposed.');\n sessions.add(session);\n if (reclaiming === undefined) {\n // Once per host: runners and history a crashed server left behind belong to no session.\n reclaiming = session\n .reclaimOrphans()\n .catch((error: unknown) => options.onWarning(`Could not reclaim orphaned runners: ${describe(error)}`));\n void sweepAll();\n }\n return () => {\n sessions.delete(session);\n };\n },\n async dispose() {\n if (closed) return;\n closed = true;\n sessions.clear();\n await Promise.all([polling.stop(), sweeper.stop(), reclaiming, sweeping]);\n options.processRegistry.close();\n },\n };\n}\n","export const RUNNER_RUNS_TYPE = 'runner_runs';\n","export const FINISHED_RUNNER_RETENTION_MS = 600_000;\n","import { FINISHED_RUNNER_RETENTION_MS } from '../../constants/webRunnerRuns';\nimport type { RunnerRecord } from '../../types/runnerRegistry';\nimport type { RunnerRunView } from '../../types/webRunners';\nimport { isRunnerRecord } from '../runnerRecord';\n\n/** How long a finished runner stays in the cockpit's feed after it exited. */\n\n/** One on-disk metadata record, or undefined when the text is not a runner record. */\nexport function parseRunnerRecord(raw: string): RunnerRecord | undefined {\n try {\n const parsed: unknown = JSON.parse(raw);\n return isRunnerRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined; // Mid-write or foreign file; the next scan settles it.\n }\n}\n\nexport function toRunnerRunView(record: RunnerRecord): RunnerRunView {\n return {\n id: record.id,\n name: record.name,\n pid: record.pid,\n command: record.command,\n cwd: record.cwd,\n interactive: record.interactive,\n backend: record.backend,\n state: record.state,\n promoted: record.promoted,\n startedAt: record.startedAt,\n logPath: record.logPath,\n ...(record.exit\n ? {\n exit: {\n reason: record.exit.reason,\n code: record.exit.code,\n signal: record.exit.signal,\n ...(record.exit.terminationReason === undefined\n ? {}\n : { terminationReason: record.exit.terminationReason }),\n ...(record.exit.stopReason === undefined ? {} : { stopReason: record.exit.stopReason }),\n finishedAt: record.exit.finishedAt,\n },\n }\n : {}),\n };\n}\n\nfunction finishedAt(record: RunnerRecord): number {\n const parsed = record.exit === undefined ? Number.NaN : Date.parse(record.exit.finishedAt);\n return Number.isFinite(parsed) ? parsed : Date.parse(record.startedAt);\n}\n\n/**\n * The feed the cockpit shows: running runners first, newest start first, then\n * the recently finished ones, newest exit first. Finished runners older than\n * the retention window are left out; the registry keeps them for the CLI.\n */\nexport function presentRunnerRuns(records: readonly RunnerRecord[], now: number): RunnerRunView[] {\n const running = records\n .filter((record) => record.state === 'running')\n .sort((left, right) => right.startedAt.localeCompare(left.startedAt));\n const finished = records\n .filter((record) => record.state === 'completed' && now - finishedAt(record) <= FINISHED_RUNNER_RETENTION_MS)\n .sort((left, right) => finishedAt(right) - finishedAt(left));\n return [...running, ...finished].map(toRunnerRunView);\n}\n","import {\n PO_BACKGROUND_WORK_SERVICE,\n readPoBackgroundWorkService,\n type BackgroundProviderWorkItem,\n type BackgroundWorkProviderHandle,\n} from '@agimon-ai/po-core/backgroundWork';\nimport { resolveRootSessionId } from '@agimon-ai/po-core/childProcess';\nimport { runCleanups } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessActivity, PoHeadlessExecutionContext } from '@agimon-ai/po-core/headless';\nimport type { PoServerHostService } from '@agimon-ai/po-core/serverPlugin';\nimport { createPoTelemetry } from '@agimon-ai/po-telemetry';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { LIVENESS_POLL_MS, TERM_GRACE_MS } from '../../constants/launcher';\nimport { BACKGROUND_WORK_PROVIDER, RUNNER_STATUS_KEY } from '../../constants/runnerRuntime';\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport type { RunnerStartFailureView } from '../../types/webRunners';\nimport { activeRunnerRecovery } from '../compaction';\nimport { reconcileActiveRunners, stopRunnerProcess } from '../reconcile';\nimport { getLogTtlMs } from '../runnerConfig';\nimport type { RunnerDependencies } from '../runnerDependencies/type';\nimport type { RunnerHost } from '../runnerHost';\nimport { formatRunnerStatus } from '../runnerRecord';\nimport { formatRunnerFinished, formatRunnerFinishedNotice } from '../runnerRecord';\nimport { presentRunnerRuns } from '../webRunnerRuns';\n\nconst COMPLETION_RETRY_MS = 1_000;\n\nexport interface RunnerServerRuntime {\n readonly container: RunnerDependencies;\n readonly activity: PoHeadlessActivity;\n readonly backgroundWorkPlugin: (context: Context) => void;\n ensureSession(sessionId: string): Promise<void>;\n /** Wakes the agent once when this runner, which it sent to the background, exits. */\n watchRunner(id: string): void;\n /** Tells the runners panel why a start it asked for did not happen; undefined clears it after one that did. */\n reportStart(sessionId: string, failure: string | undefined): void;\n recoverCompaction(this: void, event: Readonly<Record<string, unknown>>): Promise<void>;\n dispose(this: void): Promise<void>;\n}\nexport function createRunnerServerRuntime(\n container: RunnerDependencies,\n /** What every session of this build in the hub shares: the registry and the housekeeping loop. */\n host: RunnerHost,\n directEvents: NonNullable<PoServerHostService['context']['directEvents']>,\n /** Absent when no agent runs in this session, so there is nobody to wake. */\n wakeAgent?: (content: string, requestId: string, notice: string) => Promise<void>,\n execution?: PoHeadlessExecutionContext,\n): RunnerServerRuntime {\n const telemetry = execution\n ? createPoTelemetry({\n serviceName: 'po-runner',\n packageName: '@agimon-ai/po-runner',\n cwd: execution.cwd,\n env: execution.environment,\n enableLogs: true,\n enableTraces: true,\n })\n : undefined;\n let sessionId: string | undefined;\n let recoveredCompactionId: string | undefined;\n let supervision: Promise<void> | undefined;\n let runtimeDisposal: Promise<void> | undefined;\n let disposal: Promise<void> | undefined;\n let backgroundWorkItems: BackgroundProviderWorkItem[] = [];\n let backgroundWorkProvider: BackgroundWorkProviderHandle | undefined;\n // Nothing else tells the model a background runner ended, so without this an\n // agent that finished its turn waiting on one idles until a person prompts it.\n const watched = new Set<string>();\n let unsubscribeWatched: (() => void) | undefined;\n let wakes: Promise<void> = Promise.resolve();\n let closing = false;\n let retryTimer: ReturnType<typeof setTimeout> | undefined;\n /** Stops the shared loop refreshing this session and sweeping around it. */\n let detach: (() => void) | undefined;\n let startFailure: RunnerStartFailureView | undefined;\n let startFailures = 0;\n\n /** Every runner of every session the registry knows, reclaimed when its owning process is gone. */\n const reclaimOrphans = async (): Promise<void> => {\n const reclaimed = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: false,\n });\n for (const error of reclaimed.errors) process.emitWarning(error);\n };\n\n // The activity is optional and source-gated. The retained server plugin owns the\n // session runtime, so disabling the activity must not tear down its jobs.\n const ensureSession = (requestedSessionId: string): Promise<void> => {\n if (closing) return Promise.reject(new Error('Runner runtime is closing.'));\n if (sessionId !== undefined && sessionId !== requestedSessionId) {\n throw new Error(`Runner runtime belongs to session ${sessionId}.`);\n }\n sessionId ??= requestedSessionId;\n supervision ??= (async () => {\n void telemetry\n ?.recordEvent('po_runner.session_started', { outcome: 'started' })\n .catch((error: unknown) => process.emitWarning(`Could not record runner session telemetry: ${String(error)}`));\n if (closing) return;\n container.paths.setSessionId(requestedSessionId);\n if ((await container.lifeline.arm(requestedSessionId)) === undefined) {\n throw new Error('Could not arm runner supervision lifeline.');\n }\n if (closing) return;\n const promotedIds = (await container.runnerRegistry.listAll(requestedSessionId))\n .filter((record) => record.state === 'running' && record.promoted)\n .map((record) => record.id);\n // Only this session's runners: the shared host reclaims the ones other sessions left behind.\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: true,\n startupSessionId: requestedSessionId,\n active: await container.runnerRegistry.listBySession(requestedSessionId),\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n void telemetry\n ?.recordEvent('po_runner.reconciled', {\n 'runner.reclaimed_count': reconciled.reclaimed.length,\n 'runner.error_count': reconciled.errors.length,\n outcome: reconciled.errors.length === 0 ? 'completed' : 'degraded',\n })\n .catch((error: unknown) =>\n process.emitWarning(`Could not record runner reconcile telemetry: ${String(error)}`),\n );\n if (!closing) {\n unsubscribeRunnerUpdates ??= container.runnerRegistry.subscribe(\n () => requestPublish(requestedSessionId),\n requestedSessionId,\n );\n // Reconciliation may have completed retained promotions already.\n for (const id of promotedIds) watchRunner(id);\n requestPublish(requestedSessionId);\n // One shared loop refreshes this session from here on, and sweeps history around it.\n detach ??= host.attach({\n sessionId: requestedSessionId,\n paths: container.paths,\n historyTtlMs: getLogTtlMs(container.environment),\n refresh: () => refreshSession(requestedSessionId),\n reclaimOrphans,\n });\n }\n })().catch((error: unknown) => {\n supervision = undefined;\n throw error;\n });\n return supervision;\n };\n\n const disposeRuntime = (): Promise<void> => {\n runtimeDisposal ??= (async () => {\n await supervision?.catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n const ownedSessionId = sessionId;\n if (ownedSessionId === undefined) return;\n await runCleanups(\n [\n async () => {\n const records = await container.runnerRegistry.listBySession(ownedSessionId);\n const results = await Promise.allSettled(\n records.map(async (record) => {\n await stopRunnerProcess(record, container.launcher, container.rmuxBackend, {\n registry: container.runnerRegistry,\n intent: {\n reason: 'stopped',\n terminationReason: 'parent_session_cleanup',\n stopReason: 'session ended',\n },\n });\n // Success can mean only that SIGKILL or the mux close was accepted.\n // Allow asynchronous exit to land, but never complete a still-live runner.\n const deadline = container.clock.now() + TERM_GRACE_MS;\n while (container.processControl.isAlive(record.pid)) {\n if (container.clock.now() >= deadline) {\n throw new Error(`Could not stop runner ${record.id} during session shutdown`);\n }\n await new Promise<void>((resolve) => container.clock.after(LIVENESS_POLL_MS, resolve));\n }\n await container.runnerRegistry.complete(\n record.id,\n { reason: 'stopped', code: null, signal: null, stopReason: 'session ended' },\n ownedSessionId,\n );\n }),\n );\n const errors = results.filter((result) => result.status === 'rejected').map((result) => result.reason);\n if (errors.length) throw new AggregateError(errors, 'Runner process cleanup failed.');\n },\n () => container.ptyHost.disposeAll(),\n () => container.lifeline.dispose(),\n () => container.runnerRegistry.close(),\n ],\n 'Runner runtime cleanup failed.',\n );\n })();\n return runtimeDisposal;\n };\n\n let refreshInFlight: Promise<void> | undefined;\n const refreshSession = (ownedSessionId: string): Promise<void> => {\n if (closing) return Promise.resolve();\n refreshInFlight ??= refreshNow(ownedSessionId).finally(() => {\n refreshInFlight = undefined;\n });\n return refreshInFlight;\n };\n const refreshNow = async (ownedSessionId: string): Promise<void> => {\n if (closing) return;\n try {\n const reconciled = await reconcileActiveRunners({\n registry: container.runnerRegistry,\n launcher: container.launcher,\n rmuxBackend: container.rmuxBackend,\n processControl: container.processControl,\n currentHostPid: process.pid,\n startup: false,\n active: await container.runnerRegistry.listBySession(ownedSessionId),\n });\n for (const error of reconciled.errors) process.emitWarning(error);\n if (!closing) {\n requestPublish(ownedSessionId);\n wakeForFinished(ownedSessionId);\n }\n } catch (error) {\n process.emitWarning(`Could not refresh po-runner: ${String(error)}`);\n }\n };\n const publishRuns = async (ownedSessionId: string): Promise<void> => {\n try {\n const records = await container.runnerRegistry.listAll(ownedSessionId);\n if (closing) return;\n if (execution) {\n const root = resolveRootSessionId(ownedSessionId, container.environment);\n const count = (await container.runnerRegistry.list()).filter(\n (record) => (record.rootSessionId ?? record.sessionId) === root,\n ).length;\n if (closing) return;\n execution.client.setStatus(RUNNER_STATUS_KEY, formatRunnerStatus(count));\n }\n if (closing) return;\n const nextBackgroundWork = records\n .filter((record) => record.state === 'running' || watched.has(record.id))\n .map((record) => ({\n id: record.id,\n sessionId: record.sessionId,\n label: record.name,\n // An absent status holds ownership until completion admission succeeds.\n ...(record.state === 'running' ? { status: record.state } : {}),\n }));\n if (JSON.stringify(nextBackgroundWork) !== JSON.stringify(backgroundWorkItems)) {\n backgroundWorkItems = nextBackgroundWork;\n backgroundWorkProvider?.update();\n }\n directEvents.publish(RUNNER_RUNS_TYPE, ownedSessionId, {\n runs: presentRunnerRuns(records, Date.now()),\n ...(execution ? { sessionDirectory: execution.cwd } : {}),\n ...(startFailure ? { startFailure } : {}),\n });\n } catch (error) {\n process.emitWarning(`Could not publish runner state: ${String(error)}`);\n }\n };\n let publishInFlight: Promise<void> | undefined;\n let publishAgain = false;\n let unsubscribeRunnerUpdates: (() => void) | undefined;\n const requestPublish = (ownedSessionId: string): void => {\n if (closing) return;\n publishAgain = true;\n if (publishInFlight !== undefined) return;\n publishInFlight = (async () => {\n do {\n publishAgain = false;\n await publishRuns(ownedSessionId);\n } while (publishAgain && !closing);\n })()\n .catch((error: unknown) => {\n process.emitWarning(`Could not schedule runner state publication: ${String(error)}`);\n })\n .finally(() => {\n publishInFlight = undefined;\n if (publishAgain) requestPublish(ownedSessionId);\n });\n };\n\n // Sequenced so two runners exiting in the same tick each wake the agent once.\n const wakeForFinished = (ownedSessionId: string): void => {\n if (closing) return;\n wakes = wakes\n .then(async () => {\n if (closing) return;\n let retry = false;\n // Deleting the entry being visited is safe for a Set iterator.\n for (const id of watched) {\n if (closing) return;\n try {\n const record = await container.runnerRegistry.get(id, ownedSessionId);\n if (closing) return;\n if (record?.state === 'running') continue;\n // A record swept before it was seen leaves nothing to report.\n if (record)\n await wakeAgent?.(\n formatRunnerFinished(record),\n `runner-finished:${JSON.stringify([ownedSessionId, id])}`,\n formatRunnerFinishedNotice(record),\n );\n if (record)\n await telemetry?.recordEvent('po_runner.process_finished', {\n outcome: 'completed',\n 'runner.exit_code': record.exit?.code ?? 0,\n 'runner.backend': record.backend,\n });\n watched.delete(id);\n requestPublish(ownedSessionId);\n } catch (error) {\n retry = true;\n process.emitWarning(`Could not report finished runner ${id}: ${String(error)}`);\n }\n }\n if (retry && !closing && retryTimer === undefined) {\n retryTimer = setTimeout(() => {\n retryTimer = undefined;\n wakeForFinished(ownedSessionId);\n }, COMPLETION_RETRY_MS);\n retryTimer.unref?.();\n }\n if (watched.size === 0) {\n clearTimeout(retryTimer);\n retryTimer = undefined;\n unsubscribeWatched?.();\n unsubscribeWatched = undefined;\n }\n })\n .catch((error: unknown) => {\n process.emitWarning(`Could not report a finished runner: ${String(error)}`);\n });\n };\n\n const watchRunner = (id: string): void => {\n const ownedSessionId = sessionId;\n if (!wakeAgent || closing || ownedSessionId === undefined) return;\n watched.add(id);\n requestPublish(ownedSessionId);\n // Independent of the optional activity: the wake-up must not depend on a\n // browser watching the runner list.\n unsubscribeWatched ??= container.runnerRegistry.subscribe(() => wakeForFinished(ownedSessionId), ownedSessionId);\n // It may already have exited between promotion and subscription.\n wakeForFinished(ownedSessionId);\n };\n\n const activity: PoHeadlessActivity = {\n name: 'runner',\n // Session creation waits on activity start, so supervision runs in the\n // background (it publishes once ready); bash and compaction recovery still\n // await `ensureSession`.\n start: async (executionContext: PoHeadlessExecutionContext) => {\n void ensureSession(executionContext.sessionId).catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n requestPublish(executionContext.sessionId);\n return () => {\n // The retained owner keeps supervision alive. Optional activity toggles\n // must not repeat startup cleanup, subscriptions, or history initialization.\n };\n },\n };\n return {\n container,\n activity,\n ensureSession,\n watchRunner,\n reportStart(startedSessionId, failure) {\n startFailures += 1;\n startFailure =\n failure === undefined ? undefined : { id: `${String(Date.now())}-${String(startFailures)}`, message: failure };\n requestPublish(startedSessionId);\n },\n async recoverCompaction(event) {\n if (\n event.status !== 'completed' ||\n closing ||\n !execution ||\n typeof event.entryId !== 'string' ||\n event.entryId === recoveredCompactionId\n )\n return;\n const entries = await execution.session.entries();\n const compacted = entries.findLast((entry) => entry.type === 'compaction');\n if (closing || compacted?.id !== event.entryId) return;\n await ensureSession(execution.sessionId);\n let records;\n try {\n records = await container.runnerRegistry.listBySession(execution.sessionId);\n } catch {\n return;\n }\n if (closing) return;\n const active = records.filter((record) => record.state === 'running');\n const text = activeRunnerRecovery(active);\n if (!text) return;\n await execution.session.appendMessage?.({\n type: 'po-runner-compaction',\n text,\n display: false,\n details: { sessionId: execution.sessionId, runnerIds: active.map((record) => record.id) },\n });\n if (!closing) recoveredCompactionId = event.entryId;\n },\n backgroundWorkPlugin: (context) => {\n context.inject([PO_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readPoBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n const registration = service.register({\n provider: BACKGROUND_WORK_PROVIDER,\n listActiveWork: () => backgroundWorkItems,\n });\n backgroundWorkProvider = registration;\n return () => {\n registration.dispose();\n if (backgroundWorkProvider === registration) backgroundWorkProvider = undefined;\n };\n });\n },\n dispose() {\n if (disposal) return disposal;\n closing = true;\n disposal = (async () => {\n // Session cleanup completes every runner it stops, and none of those may\n // start a turn in a session that is going away.\n closing = true;\n detach?.();\n detach = undefined;\n clearTimeout(retryTimer);\n retryTimer = undefined;\n watched.clear();\n unsubscribeWatched?.();\n unsubscribeWatched = undefined;\n unsubscribeRunnerUpdates?.();\n unsubscribeRunnerUpdates = undefined;\n await wakes;\n await publishInFlight;\n await refreshInFlight;\n try {\n execution?.client.setStatus(RUNNER_STATUS_KEY, undefined);\n } catch (error) {\n process.emitWarning(`Could not clear runner status: ${String(error)}`);\n }\n await runCleanups(\n [\n disposeRuntime,\n () => telemetry?.recordEvent('po_runner.session_finished', { outcome: 'stopped' }),\n () => telemetry?.shutdown(),\n ],\n 'Runner shutdown failed.',\n );\n })();\n return disposal;\n },\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { CONFIG_DIR_NAME } from '@earendil-works/pi-coding-agent';\n\nimport {\n MAX_ERROR_ENTRIES_CEILING,\n MAX_PATTERNS,\n MAX_PATTERN_LENGTH,\n MAX_RESULT_BYTES_CEILING,\n MAX_RESULT_LINES_CEILING,\n MAX_RESULT_TOKENS_CEILING,\n RATIO_KEYS,\n RUNNER_SETTINGS_FILE,\n} from '../../constants/runnerSettings';\nimport type { IRunnerSettingsLoader, RunnerSettings, RunnerSettingsLoad } from '../../types/runnerSettings';\n\n/** Pi's convention is one JSON file per extension under the config directory. */\n\nconst EMPTY: RunnerSettingsLoad = { settings: {}, issues: [] };\nconst COUNT_KEYS = [\n 'maxResultBytes',\n 'maxResultLines',\n 'successMaxResultBytes',\n 'maxResultTokens',\n 'successMaxResultTokens',\n 'errorMaxEntries',\n 'errorMaxVariantsJoined',\n] as const;\nconst KNOWN_KEYS = new Set<string>([...RATIO_KEYS, ...COUNT_KEYS, 'errorPatterns']);\n/** Ceilings so a project file cannot flood the model's context. */\nconst CEILINGS: Readonly<Record<string, number>> = {\n maxResultBytes: MAX_RESULT_BYTES_CEILING,\n successMaxResultBytes: MAX_RESULT_BYTES_CEILING,\n maxResultTokens: MAX_RESULT_TOKENS_CEILING,\n successMaxResultTokens: MAX_RESULT_TOKENS_CEILING,\n maxResultLines: MAX_RESULT_LINES_CEILING,\n errorMaxEntries: MAX_ERROR_ENTRIES_CEILING,\n errorMaxVariantsJoined: MAX_ERROR_ENTRIES_CEILING,\n};\n\nexport class RunnerSettingsLoader implements IRunnerSettingsLoader {\n load(cwd: string, trusted: boolean): RunnerSettingsLoad {\n // Project-local configuration is untrusted input until the project is.\n if (!trusted) return EMPTY;\n\n let raw: string;\n try {\n raw = fs.readFileSync(path.join(cwd, CONFIG_DIR_NAME, RUNNER_SETTINGS_FILE), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return EMPTY;\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} could not be read`] };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} is not valid JSON`] };\n }\n return validate(parsed);\n }\n}\n\nfunction validate(parsed: unknown): RunnerSettingsLoad {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} must contain a JSON object`] };\n }\n\n const source = parsed as Record<string, unknown>;\n const settings: Record<string, unknown> = {};\n const issues: string[] = [];\n // A rejected key is reported rather than dropped: a silent typo reads as a\n // setting that simply does not work.\n for (const key of Object.keys(source)) if (!KNOWN_KEYS.has(key)) issues.push(`unknown key ${key}`);\n\n for (const key of COUNT_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n issues.push(`${key} must be a positive integer`);\n continue;\n }\n settings[key] = Math.min(value, CEILINGS[key] ?? value);\n }\n\n for (const key of RATIO_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {\n issues.push(`${key} must be a number above 0 and below 1`);\n continue;\n }\n settings[key] = value;\n }\n\n const patterns = source.errorPatterns;\n if (patterns !== undefined) {\n if (!Array.isArray(patterns) || patterns.some((entry) => typeof entry !== 'string')) {\n issues.push('errorPatterns must be an array of strings');\n } else {\n const accepted: string[] = [];\n for (const pattern of patterns.slice(0, MAX_PATTERNS) as string[]) {\n if (pattern.length > MAX_PATTERN_LENGTH) {\n issues.push('errorPatterns entry is too long');\n continue;\n }\n try {\n new RegExp(pattern, 'iu');\n accepted.push(pattern);\n } catch {\n issues.push(`errorPatterns entry is not a valid regular expression: ${pattern}`);\n }\n }\n if (accepted.length > 0) settings.errorPatterns = accepted;\n }\n }\n\n return { settings: settings as RunnerSettings, issues };\n}\n","import { readPoFs } from '@agimon-ai/po-core/platform';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { REGISTRY_PATH_ENV } from '../../../../../constants/runnerRegistry';\nimport {\n HISTORY_SWEEP_INTERVAL_MS,\n RUNNER_FINISHED_MESSAGE,\n RUNNER_STATUS_POLL_MS,\n} from '../../../../../constants/runnerRuntime';\nimport { RUNNER_SETTINGS_FILE } from '../../../../../constants/runnerSettings';\nimport { SystemClock } from '../../../../../services/clock';\nimport { createHeadlessBashTool, createHeadlessRunnersCommand } from '../../../../../services/headless';\nimport { summarizeLog } from '../../../../../services/logReader';\nimport { createRunnerDependencies } from '../../../../../services/runnerDependencies';\nimport { createRunnerHost } from '../../../../../services/runnerHost';\nimport { createDefaultProcessRegistry } from '../../../../../services/runnerRegistry';\nimport { createRunnerServerRuntime } from '../../../../../services/runnerServerRuntime';\nimport { RunnerSettingsLoader } from '../../../../../services/runnerSettings';\n\nexport const RUNNER_SERVER_SCOPE_SERVICE = 'po/runner-server-scope';\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/runner-server-scope': RunnerServerScope;\n }\n}\n\nexport const createRunnerServerRoot = async ({ context, host, agent, signal, resources }: PoServerPluginContext) => {\n if (!host.context.directEvents) throw new Error('Runner server plugin requires the session direct event bus.');\n if (!host.context.cwd || !host.context.environment) {\n throw new Error('Runner server plugin requires session cwd and environment.');\n }\n // Inherit the current headless Pi bridge's isProjectTrusted: () => true.\n // This preserves runner configuration parity, not a new general trust policy.\n const loaded = new RunnerSettingsLoader().load(host.context.cwd, true);\n const settings = Object.freeze({\n ...loaded.settings,\n ...(loaded.settings.errorPatterns ? { errorPatterns: Object.freeze([...loaded.settings.errorPatterns]) } : {}),\n });\n for (const issue of loaded.issues) process.emitWarning(`${RUNNER_SETTINGS_FILE}: ${issue}`);\n const environment = host.context.environment;\n // One per registry for this build in the hub: every session shares the connection and the loop that\n // keeps house for it, and the last session to close disposes them.\n const runnerHost = await resources.acquire(\n ['runner-host', environment[REGISTRY_PATH_ENV] ?? null],\n () =>\n createRunnerHost({\n processRegistry: createDefaultProcessRegistry(environment),\n clock: new SystemClock(),\n statusPollMs: RUNNER_STATUS_POLL_MS,\n historySweepMs: HISTORY_SWEEP_INTERVAL_MS,\n onWarning: (message) => process.emitWarning(message),\n }),\n (shared) => shared.dispose(),\n );\n const dependencies = createRunnerDependencies({\n settings,\n cwd: host.context.cwd,\n environment,\n processRegistry: runnerHost.processRegistry,\n ...(host.context.executionBudget === undefined ? {} : { executionBudget: host.context.executionBudget }),\n });\n const execution = agent?.context;\n const wakeAgent =\n execution &&\n (async (content: string, requestId: string, notice: string): Promise<void> => {\n try {\n // 'steer' joins a running turn and starts one on an idle session.\n // Reuse completion identity across retries and acknowledge admission, not settlement.\n // A custom message, not a prompt: the model reads it as input, while the\n // transcript shows the notice as an event rather than as the person's words.\n if (!execution.session.admitMessage) throw new Error('The session cannot admit a runner notification.');\n await execution.session.admitMessage(\n { type: RUNNER_FINISHED_MESSAGE, text: content, display: true, details: { notice } },\n 'steer',\n requestId,\n );\n } catch (error) {\n // Keep completion ownership with the watcher until admission succeeds.\n await Promise.resolve()\n .then(() => execution.client.notify({ body: String(error), level: 'warning' }))\n .catch(() => undefined);\n throw error;\n }\n });\n const runtime = createRunnerServerRuntime(dependencies, runnerHost, host.context.directEvents, wakeAgent, execution);\n const bash = (wakeOnExit: boolean) =>\n createHeadlessBashTool(\n {\n run: async (request) => {\n await runtime.ensureSession(request.sessionId);\n const result = await dependencies.bashRunService.run({\n ...request,\n ...(signal ? { signal: request.signal ? AbortSignal.any([signal, request.signal]) : signal } : {}),\n });\n // Without this, nothing tells the agent the runner ended.\n if (wakeOnExit && result.kind === 'promoted') runtime.watchRunner(result.id);\n return result;\n },\n },\n (path, lines, bytes, tokens) => summarizeLog(path, lines, bytes, tokens, settings),\n settings,\n dependencies.environment,\n );\n const value = {\n tool: bash(true),\n // Remote MCP runners must not wake the local agent; the remote caller polls instead.\n remoteTool: bash(false),\n command: createHeadlessRunnersCommand(dependencies, {\n // Without the platform fs a missing folder still fails, only at spawn and less plainly.\n fs: readPoFs(context),\n onStart: (startedSessionId, failure) => runtime.reportStart(startedSessionId, failure),\n }),\n recoverCompaction: runtime.recoverCompaction,\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 // Package install is on the session-create path; bash awaits supervision itself.\n onStart: execution\n ? () => {\n void runtime\n .ensureSession(execution.sessionId)\n .catch((error: unknown) =>\n process.emitWarning(`Could not initialize headless runner supervision: ${String(error)}`),\n );\n }\n : undefined,\n onStop: runtime.dispose,\n };\n};\n\nexport type RunnerServerScope = Awaited<ReturnType<typeof createRunnerServerRoot>>['value'];\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createRunnerServerRoot } from './_lib/serverRoot';\n\nexport default defineRoot(createRunnerServerRoot);\n","import type { PoHubChannel, PoHubChannelSource } from '@agimon-ai/po-core/hubChannel';\n\nimport { RUNNER_RUNS_TYPE } from '../../constants/webRunners';\nimport { type RunnerRunView } from '../../types/webRunners';\n\nfunction runnerPayload(value: unknown): value is { runs: RunnerRunView[] } {\n return typeof value === 'object' && value !== null && Array.isArray((value as { runs?: unknown }).runs);\n}\n\n/**\n * The runners data channel consumes lifecycle-owned snapshots from the session\n * server. Durable registry files are read by the owning session only, never by\n * this hub channel.\n */\nexport function createRunnersChannel(): PoHubChannel {\n return {\n frameType: RUNNER_RUNS_TYPE,\n start(host) {\n const latest = new Map<string, RunnerRunView[]>();\n const subscriptions = new Map<string, () => void>();\n const source: PoHubChannelSource = {\n payloadFor(scope) {\n const runs = latest.get(scope.sessionId);\n return runs === undefined ? undefined : { runs };\n },\n sessionAdded(scope) {\n subscriptions.get(scope.sessionId)?.();\n latest.delete(scope.sessionId);\n const unsubscribe = host.directEvents.subscribe(\n RUNNER_RUNS_TYPE,\n scope.sessionId,\n (payload) => {\n if (!runnerPayload(payload)) return;\n latest.set(scope.sessionId, payload.runs);\n host.publish(scope.sessionId, payload);\n },\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n },\n sessionRemoved(sessionId) {\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n latest.delete(sessionId);\n },\n close() {\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n latest.clear();\n },\n };\n return source;\n },\n };\n}\n","import { defineChannel } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHubChannel } from '@agimon-ai/po-core/hubChannel';\n\nimport { createRunnersChannel } from '../../../services/runnersChannel';\nexport default defineChannel(function runnerRunsChannel(): PoHubChannel {\n return createRunnersChannel();\n});\n","export const LOG_DIR_NAME = 'logs';\nexport const STATE_EXTENSION = '.json';\nexport const RUNNING_STATE = 'running';\nexport const STATE_POLL_MS = 1000;\nexport const HEARTBEAT_MS = 15_000;\nexport const DEFAULT_LINES = 200;\nexport const MAX_LINES = 5000;\nexport const SAFE_SEGMENT = /^[\\w.@-]+$/;\nexport const SCREEN_POLL_MS = 100;\nexport const RAW_LOG_SUFFIX = '.raw';\nexport const MAX_INPUT_CHARS = 4096;\n/** Ceiling for one finite log/screen read, before base64 encoding. */\nexport const MAX_OUTPUT_BYTES = 64 * 1024;\nexport const NOT_ATTACHABLE = 'No attachable runner by that name in this session.';\n","export const RUNNER_API_BASE_PATH = 'runner';\nexport const RUN_ID_PARAM = 'runId';\nexport const RUNNER_SCREEN_EVENT = 'screen';\nexport const RUNNER_LOG_STREAM_EVENT = 'append';\nexport const RUNNER_LOG_PING_EVENT = 'ping';\n","import fs from 'node:fs';\n\nimport { POLL_MS } from '../../constants/logTail';\nimport type { ILogTail, LogTailHandle, LogTailOptions } from '../../types/logTail';\n\n/** How often the tail restats the file; the same cadence the CLI's --follow uses. */\n\n/**\n * Polling tail: read the delta between the last offset and the file's current\n * size, and hand back only complete lines.\n *\n * Same reliability posture as this package's runner watcher: the poll is the\n * mechanism, not an accelerator for a change notification. fs.watchFile only\n * fires when it observes a difference from a baseline it took itself, so a\n * write that lands before its first stat is a change it never reports; a\n * restat every tick has no such window.\n *\n * A write that lands mid-line is common, so the trailing fragment is held\n * until its newline arrives rather than emitted as a line that will change.\n */\nexport class LogTail implements ILogTail {\n follow(logPath: string, options: LogTailOptions): LogTailHandle {\n let offset = options.from;\n let pending = '';\n let closed = false;\n\n const drain = (size: number): void => {\n // A shrunken file was rotated or truncated; start over rather than\n // reading from an offset that now points into different content.\n if (size < offset) {\n offset = 0;\n pending = '';\n }\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(logPath, 'r');\n let bytesRead: number;\n try {\n bytesRead = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n offset = size;\n if (bytesRead <= 0) return;\n pending += buffer.subarray(0, bytesRead).toString('utf8');\n const lines = pending.split('\\n');\n pending = lines.pop() ?? '';\n if (lines.length > 0) options.onLines(lines, offset - Buffer.byteLength(pending, 'utf8'));\n };\n\n const tick = (): void => {\n if (closed) return;\n try {\n drain(fs.statSync(logPath).size);\n } catch (error) {\n // The runner has not opened its log yet; the next tick settles it.\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n options.onError(error instanceof Error ? error : new Error(String(error)));\n }\n };\n\n const timer = setInterval(tick, POLL_MS);\n // Following a log must never be the reason a process stays alive.\n timer.unref?.();\n return {\n close() {\n if (closed) return;\n closed = true;\n clearInterval(timer);\n },\n };\n }\n}\n","import type { LogSlice } from './logReader';\n\n/**\n * The runner log API, shared by this package's session-scoped routes and its\n * cockpit plugin. The two halves run in different processes, so the wire\n * vocabulary is declared here: the browser half may reach `src/types` and\n * nothing else on the server side.\n *\n * Where the routes live is not here. That is `src/types/apiRoutes`, which both\n * halves read, and the generated client turns into the URLs the page addresses.\n * The file a run writes to is still read from its metadata record by the server\n * alone.\n */\n\n/**\n * One chunk of an attached pane's output.\n *\n * Base64 because these are terminal bytes, escape sequences and all, and a\n * server-sent event is a line-oriented text frame that would mangle them.\n */\nexport interface RunnerScreenEvent {\n chunk: string;\n /** Byte offset just past this chunk, so a reconnect resumes rather than replays. */\n offset?: number;\n /** Set once the runner exits, so the page can stop watching. */\n ended?: boolean;\n}\n\n/** A finite byte slice for sealed HTTP followers. Decode chunks incrementally, not independently. */\nexport interface RunnerOutputResponse {\n /** Base64 bytes, preserving split UTF-8 characters and terminal escape sequences. */\n chunk: string;\n offset: number;\n /** The file was replaced or became shorter than the requested offset; discard the previous display/decoder state. */\n reset: boolean;\n /** Opaque raw file identity. Send back on screen/read to detect replacement, including regrowth. */\n generation?: string;\n exists: boolean;\n /** More bytes were present at read time; request the next slice before waiting. */\n hasMore: boolean;\n /** True only once the runner ended and all available bytes have been read. */\n ended: boolean;\n}\n\n/** The body of a keystroke POST. Text, not key codes: the pane wants bytes. */\nexport interface RunnerInputRequest {\n text: string;\n}\n\n/** The named SSE event carrying a RunnerScreenEvent payload. */\n\n/**\n * A log request as query parameters. `grep` is a literal substring, not a\n * regular expression, because that is what the reader matches; `lines` bounds\n * the answer to the last N lines of whatever survived the filter.\n */\nexport interface RunnerLogQueryParams {\n lines?: number;\n grep?: string;\n ignoreCase?: boolean;\n contextLines?: number;\n}\n\n/** The slice the log route answers with, plus which runner it came from. */\nexport interface RunnerLogResponse extends LogSlice {\n runId: string;\n /** True while the runner is still writing, so the page knows following is worth offering. */\n running: boolean;\n}\n\n/**\n * One server-sent event on the follow stream: the lines appended since the last\n * one. The stream never filters: a page that sets a query stops following and\n * reads a filtered slice instead, because grep with context is a whole-file\n * answer and cannot be assembled from a chunk at a time.\n */\nexport interface RunnerLogStreamEvent {\n /** Appended lines, in order; empty when the event only reports the runner ending. */\n lines: string[];\n /**\n * Byte offset just past the last line in this event.\n *\n * A follower that loses the stream resumes here. Without it the page could\n * only fall back to the offset its last slice reported, which every line\n * delivered since has already moved past, so a reconnect would replay them.\n */\n offset?: number;\n /** Set once the runner exits, so the page can stop following without polling the run list. */\n ended?: boolean;\n}\n\n/** The named SSE event carrying a RunnerLogStreamEvent payload. */\n\n/**\n * The named SSE event that carries nothing.\n *\n * A runner can be alive and silent for minutes, which on the wire is\n * indistinguishable from a connection that died. This is sent on a timer so\n * the socket keeps proving itself. Readers that only listen for `append`\n * ignore it, which is what makes it safe to add.\n */\n\n/** Query parameter names, shared so the page and the route cannot drift apart. */\nexport const RUNNER_LOG_PARAMS = {\n lines: 'lines',\n grep: 'grep',\n ignoreCase: 'ignoreCase',\n contextLines: 'contextLines',\n from: 'from',\n generation: 'generation',\n} as const;\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport { RUN_ID_PARAM } from '../constants/webRunnerLog';\nimport { RUNNER_LOG_PARAMS, type RunnerLogResponse, type RunnerOutputResponse } from './webRunnerLog';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path: the build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/runner/`, so the mount is\n * stated once, by the folder that creates it. This package stated it four times\n * before, once per URL builder, each of them re-spelling `/plugins/` and the\n * session prefix by hand.\n *\n * The Hono app registers these paths verbatim and the browser addresses them,\n * so a path cannot move on one side alone. `:runId` is Hono's own parameter\n * syntax for that reason; a caller supplies the value as `params`, and the\n * client encodes the segment.\n */\nconst routes = {\n log: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log`,\n query: [\n RUNNER_LOG_PARAMS.lines,\n RUNNER_LOG_PARAMS.grep,\n RUNNER_LOG_PARAMS.ignoreCase,\n RUNNER_LOG_PARAMS.contextLines,\n ],\n response: undefined as unknown as ApiResponseOf<RunnerLogResponse>,\n },\n /**\n * The live tail of that same log.\n *\n * `stream: true`, so the client exposes a URL and no call method: the\n * consumer is the browser’s own SSE reader, and a relayed call would\n * buffer a stream that never ends.\n */\n logStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** The unscrubbed pane bytes, for a terminal emulator rather than a reader. */\n screenStream: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/stream`,\n query: [RUNNER_LOG_PARAMS.from],\n stream: true,\n },\n /** Finite byte reads keep sealed HTTP exchanges bounded, unlike SSE. */\n logRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/log/read`,\n query: [RUNNER_LOG_PARAMS.from],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n screenRead: {\n method: 'GET',\n path: `/runners/:${RUN_ID_PARAM}/screen/read`,\n query: [RUNNER_LOG_PARAMS.from, RUNNER_LOG_PARAMS.generation],\n response: undefined as unknown as ApiResponseOf<RunnerOutputResponse>,\n },\n input: {\n method: 'POST',\n path: `/runners/:${RUN_ID_PARAM}/screen/input`,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { PoApi, PoApiContext, PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\n\nimport {\n DEFAULT_LINES,\n HEARTBEAT_MS,\n LOG_DIR_NAME,\n MAX_INPUT_CHARS,\n MAX_OUTPUT_BYTES,\n MAX_LINES,\n NOT_ATTACHABLE,\n RAW_LOG_SUFFIX,\n RUNNING_STATE,\n SAFE_SEGMENT,\n SCREEN_POLL_MS,\n STATE_EXTENSION,\n STATE_POLL_MS,\n} from '../../constants/runnerLogApi';\nimport {\n RUN_ID_PARAM,\n RUNNER_API_BASE_PATH,\n RUNNER_LOG_PING_EVENT,\n RUNNER_LOG_STREAM_EVENT,\n RUNNER_SCREEN_EVENT,\n} from '../../constants/webRunnerLog';\nimport { LogReader } from '../../services/logReader';\nimport { LogTail } from '../../services/logTail';\nimport { PtyBackendChain } from '../../services/ptyBackendChain';\nimport { RmuxBackend } from '../../services/rmuxBackend';\nimport { RunnerPaths, resolveRunnerStoreDirectory, runnerStateDirFor } from '../../services/runnerPaths';\nimport { TmuxBackend } from '../../services/tmuxBackend';\nimport { parseRunnerRecord } from '../../services/webRunnerRuns';\nimport routes from '../../types/apiRoutes';\nimport type { ILogReader, LogQuery } from '../../types/logReader';\nimport type { ILogTail } from '../../types/logTail';\nimport type { IRmuxBackend } from '../../types/rmuxBackend';\nimport type { RunnerRecord } from '../../types/runnerRegistry';\nimport {\n RUNNER_LOG_PARAMS,\n type RunnerLogResponse,\n type RunnerLogStreamEvent,\n type RunnerOutputResponse,\n type RunnerScreenEvent,\n} from '../../types/webRunnerLog';\n\n/** How often a follow re-reads the record to notice the runner exiting. */\n/**\n * How often a silent but living stream proves it is still there. Short enough\n * that an idle proxy does not reap the socket, long enough to cost nothing.\n */\n/** Kept in step with LogQuery's own default, so an unasked-for tail is the same size everywhere. */\n/** A page that asks for more than this gets this; the whole point of the route is a bounded read. */\n/** Path segments the client supplies are names, never paths. */\n/** Backends that expose a pane another process can read and write. */\nconst MULTIPLEXER_BACKENDS: ReadonlySet<string> = new Set(['rmux', 'tmux']);\n/** How often an attached pane's new bytes are picked up. */\n/** The unscrubbed copy the runner's sink writes beside the log, for interactive runs. */\n/** One keystroke batch; a paste is not a file transfer. */\n/** Said the same way for every reason, so the route never reports which check failed. */\n\nexport interface RunnerLogApiOptions {\n /** The session these routes answer for; the host owns exactly one. */\n sessionId: string;\n /** The store root; defaults to this environment's agent directory. */\n storeDir?: string;\n environment?: Readonly<Record<string, string | undefined>>;\n cwd?: string;\n logReader?: ILogReader;\n logTail?: ILogTail;\n /**\n * Reaches a live pane to read its screen and send it input.\n *\n * A session API is handed nothing but its session id, so it cannot touch the\n * in-process PTY handles the extension holds. This talks to the multiplexer\n * over its own socket instead, the same way the CLI's `input` verb does,\n * which is what makes an attached pane reachable across processes at all.\n */\n pane?: IRmuxBackend;\n}\n\n/** Rejects anything that could climb out of the session's own directory before a path is built. */\nfunction isSafeSegment(value: string): boolean {\n return value !== '' && value !== '.' && value !== '..' && SAFE_SEGMENT.test(value);\n}\n\n/** A log must stay inside its session even when a path component is a symlink. Missing files are normal. */\nfunction isSessionLog(file: string, logsDir: string): boolean {\n const resolved = path.resolve(file);\n if (resolved !== logsDir && !resolved.startsWith(`${logsDir}${path.sep}`)) return false;\n try {\n if (fs.lstatSync(file).isSymbolicLink()) return false;\n const realFile = fs.realpathSync(file);\n const realLogs = fs.realpathSync(logsDir);\n return realFile.startsWith(`${realLogs}${path.sep}`);\n } catch (error) {\n // The sink has not created its output yet; a later request validates the created path again.\n return isRecord(error) && error.code === 'ENOENT';\n }\n}\n\n/**\n * The log file a run wrote, or undefined when there is no such run or its\n * record points somewhere it has no business pointing.\n *\n * The record names its own log path, and that record is written by another\n * process, so the path is checked rather than trusted: it must sit inside this\n * session's own logs directory. Without that check a doctored record would\n * turn this route into an arbitrary file read.\n */\nfunction resolveLogFile(stateDir: string, logsDir: string, sessionId: string, runId: string): RunnerRecord | undefined {\n const statePath = path.join(stateDir, `${runId}${STATE_EXTENSION}`);\n let raw: string;\n try {\n raw = fs.readFileSync(statePath, 'utf8');\n } catch {\n return undefined; // No such run in this session; the route answers 404.\n }\n const record = parseRunnerRecord(raw);\n if (record === undefined || record.id !== runId || record.sessionId !== sessionId) return undefined;\n if (!isSessionLog(record.logPath, logsDir)) return undefined;\n return record;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction numberParam(value: string | undefined, fallback: number, max: number): number {\n const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed < 0) return fallback;\n return Math.min(parsed, max);\n}\n\n/** Missing output is normal before the sink opens it; other IO failures must not look like an empty read. */\nfunction readOutput(\n file: string,\n from: number,\n running: boolean,\n priorGeneration?: string,\n screen = false,\n): RunnerOutputResponse {\n let descriptor: number;\n try {\n descriptor = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);\n } catch (error) {\n if (!isRecord(error) || error.code !== 'ENOENT') throw error;\n return { chunk: '', offset: from, reset: false, exists: false, hasMore: false, ended: !running };\n }\n try {\n const stat = fs.fstatSync(descriptor, { bigint: true });\n const size = Number(stat.size);\n // Identity and bytes come from the same descriptor, never a separate marker read.\n const generation = screen ? `${stat.dev}:${stat.ino}:${stat.birthtimeNs}` : undefined;\n const reset = from > size || (priorGeneration !== undefined && generation !== priorGeneration);\n const start = reset ? 0 : from;\n const length = Math.min(size - start, MAX_OUTPUT_BYTES);\n const buffer = Buffer.alloc(length);\n const read = length === 0 ? 0 : fs.readSync(descriptor, buffer, 0, length, start);\n const offset = start + read;\n const hasMore = offset < size;\n return {\n ...(generation === undefined ? {} : { generation }),\n chunk: buffer.subarray(0, read).toString('base64'),\n offset,\n reset,\n exists: true,\n hasMore,\n ended: !running && !hasMore,\n };\n } finally {\n fs.closeSync(descriptor);\n }\n}\n\nfunction logQueryOf(url: URL): LogQuery {\n const grep = url.searchParams.get(RUNNER_LOG_PARAMS.grep) ?? undefined;\n return {\n lines: numberParam(url.searchParams.get(RUNNER_LOG_PARAMS.lines) ?? undefined, DEFAULT_LINES, MAX_LINES),\n ...(grep === undefined || grep === '' ? {} : { grep }),\n ignoreCase: url.searchParams.get(RUNNER_LOG_PARAMS.ignoreCase) === 'true',\n contextLines: numberParam(url.searchParams.get(RUNNER_LOG_PARAMS.contextLines) ?? undefined, 0, MAX_LINES),\n };\n}\n\n/**\n * This package's HTTP surface: one runner's log, as a bounded slice and as a\n * live tail.\n *\n * The routes are mounted inside one session's own server, so they name a runner\n * and nothing else; the session is the host, not a parameter. The reader only\n * ever returns the last N lines of what matched, so both routes stay bounded no\n * matter how large the file on disk is.\n *\n * Paths come from src/types/apiRoutes, the one table the browser's generated\n * client reads too, so this app and the cockpit cannot disagree about where a\n * route lives. They stay relative to the mount the host strips.\n */\nexport function createRunnerLogApi(options: RunnerLogApiOptions): Hono {\n const logReader = options.logReader ?? new LogReader();\n const logTail = options.logTail ?? new LogTail();\n const sessionId = options.sessionId;\n const paths = new RunnerPaths(options.cwd, options.environment);\n const storeDir = options.storeDir ?? resolveRunnerStoreDirectory(options.environment ?? process.env);\n const stateDir =\n options.storeDir === undefined ? paths.stateDirectory(sessionId) : runnerStateDirFor(storeDir, sessionId);\n const logsDir =\n options.storeDir === undefined ? paths.logDirectory(sessionId) : path.join(storeDir, sessionId, LOG_DIR_NAME);\n const app = new Hono();\n\n // Built on first use, not on mount: most sessions never attach to a pane,\n // and constructing this probes the filesystem for a multiplexer binary.\n let paneBackend: IRmuxBackend | undefined = options.pane;\n const pane = (): IRmuxBackend => {\n paneBackend ??= (() => {\n const paths = new RunnerPaths(options.cwd, options.environment);\n return new PtyBackendChain(new RmuxBackend(paths), new TmuxBackend(paths));\n })();\n return paneBackend;\n };\n\n /** The record behind a request, or undefined when this session has no such run. */\n const runnerOf = (runId: string): RunnerRecord | undefined =>\n isSafeSegment(runId) ? resolveLogFile(stateDir, logsDir, sessionId, runId) : undefined;\n\n /**\n * The owned pane whose recorded output a request may read.\n *\n * Same posture as the log routes: the record is written by another process,\n * so nothing on it is trusted until it is checked. The target must be one\n * this package owns, and the run must have been started\n * interactive. A non-interactive run has no terminal to type into, and a\n * subprocess-backed run has no pane at all.\n */\n const paneOf = (runId: string): { record: RunnerRecord; target: string } | undefined => {\n const record = runnerOf(runId);\n if (record === undefined || record.interactive !== true) return undefined;\n const target = record.backendTarget;\n if (target === undefined || !MULTIPLEXER_BACKENDS.has(record.backend) || !isSafeSegment(target)) return undefined;\n return { record, target };\n };\n const attachableOf = (runId: string): { record: RunnerRecord; target: string } | undefined => {\n const owned = paneOf(runId);\n return owned?.record.state === RUNNING_STATE ? owned : undefined;\n };\n\n for (const [route, screen] of [\n [routes.logRead, false],\n [routes.screenRead, true],\n ] as const) {\n app.get(route.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n // A completed pane is readable so a finite follower can drain its last bytes. Input still requires a live pane.\n const record = screen ? paneOf(runId)?.record : runnerOf(runId);\n if (record === undefined)\n return context.json({ error: screen ? NOT_ATTACHABLE : `No runner '${runId}' in this session.` }, 404);\n const value = context.req.query(RUNNER_LOG_PARAMS.from);\n const from = value === undefined ? 0 : Number(value);\n if ((value !== undefined && !/^\\d+$/.test(value)) || !Number.isSafeInteger(from) || from < 0) {\n return context.json({ error: 'Expected a non-negative safe integer byte offset.' }, 400);\n }\n const file = screen ? `${record.logPath}${RAW_LOG_SUFFIX}` : record.logPath;\n if (!isSessionLog(file, logsDir)) return context.json({ error: 'No readable output in this session.' }, 404);\n return context.json(\n readOutput(\n file,\n from,\n record.state === RUNNING_STATE,\n screen ? context.req.query(RUNNER_LOG_PARAMS.generation) : undefined,\n screen,\n ),\n );\n });\n }\n\n app.get(routes.log.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const record = runnerOf(runId);\n if (record === undefined) return context.json({ error: `No runner '${runId}' in this session.` }, 404);\n const slice = logReader.read(record.logPath, logQueryOf(new URL(context.req.url)));\n const body: RunnerLogResponse = { ...slice, runId, running: record.state === RUNNING_STATE };\n return context.json(body);\n });\n\n app.get(routes.logStream.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const record = runnerOf(runId);\n if (record === undefined) return context.json({ error: `No runner '${runId}' in this session.` }, 404);\n const from = numberParam(\n new URL(context.req.url).searchParams.get(RUNNER_LOG_PARAMS.from) ?? undefined,\n 0,\n Number.MAX_SAFE_INTEGER,\n );\n\n return streamSSE(context, async (stream) => {\n const send = (event: RunnerLogStreamEvent): Promise<void> =>\n stream.writeSSE({ event: RUNNER_LOG_STREAM_EVENT, data: JSON.stringify(event) });\n\n // The tail and the state poll both push onto one queue, so the stream\n // writes in the order things happened rather than interleaving two\n // writers onto the same socket.\n let pump: Promise<void> = Promise.resolve();\n const queue = (event: RunnerLogStreamEvent): void => {\n pump = pump.then(() => send(event)).catch(() => undefined);\n };\n\n let finish = (): void => undefined;\n const done = new Promise<void>((resolve) => {\n finish = resolve;\n });\n const handle = logTail.follow(record.logPath, {\n from,\n onLines: (lines, completeThrough) => queue({ lines, offset: completeThrough }),\n onError: () => finish(),\n });\n // A finished runner writes nothing more, so the page is told once and\n // the stream ends rather than holding a socket open forever.\n const poll = setInterval(() => {\n const current = runnerOf(runId);\n if (current !== undefined && current.state === RUNNING_STATE) return;\n queue({ lines: [], ended: true });\n finish();\n }, STATE_POLL_MS);\n // A runner can be alive and produce nothing for minutes. Without this the\n // page cannot tell that from a stream that quietly died.\n const heartbeat = setInterval(() => {\n pump = pump.then(() => stream.writeSSE({ event: RUNNER_LOG_PING_EVENT, data: '' })).catch(() => undefined);\n }, HEARTBEAT_MS);\n\n stream.onAbort(() => finish());\n await done;\n clearInterval(poll);\n clearInterval(heartbeat);\n handle.close();\n await pump;\n });\n });\n\n app.get(routes.screenStream.path, (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const attachable = attachableOf(runId);\n if (attachable === undefined) return context.json({ error: NOT_ATTACHABLE }, 404);\n // The record's log path is already proven to sit inside this session's own\n // logs directory, and the raw copy is that same path with a suffix, so it\n // cannot point anywhere new.\n const rawPath = `${attachable.record.logPath}${RAW_LOG_SUFFIX}`;\n const from = numberParam(\n new URL(context.req.url).searchParams.get(RUNNER_LOG_PARAMS.from) ?? undefined,\n 0,\n Number.MAX_SAFE_INTEGER,\n );\n\n return streamSSE(context, async (stream) => {\n let pump: Promise<void> = Promise.resolve();\n const queue = (event: RunnerScreenEvent): void => {\n pump = pump\n .then(() => stream.writeSSE({ event: RUNNER_SCREEN_EVENT, data: JSON.stringify(event) }))\n .catch(() => undefined);\n };\n\n let finish = (): void => undefined;\n const done = new Promise<void>((resolve) => {\n finish = resolve;\n });\n\n let offset = from;\n const drain = (): void => {\n let size: number;\n try {\n size = fs.statSync(rawPath).size;\n } catch {\n return; // The sink has not opened the file yet; the next tick settles it.\n }\n // The sink starts the file over when it passes its ceiling, so a\n // smaller file is a fresh one rather than a bad offset.\n if (size < offset) offset = 0;\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(rawPath, 'r');\n let read: number;\n try {\n read = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n if (read <= 0) return;\n offset += read;\n // Bytes, not lines: a terminal's escape sequences straddle newlines and\n // mean nothing once split on them.\n queue({ chunk: buffer.subarray(0, read).toString('base64'), offset });\n };\n\n const poll = setInterval(() => {\n const current = runnerOf(runId);\n drain();\n if (current === undefined || current.state !== RUNNING_STATE) {\n queue({ chunk: '', offset, ended: true });\n finish();\n }\n }, SCREEN_POLL_MS);\n\n stream.onAbort(() => finish());\n await done;\n clearInterval(poll);\n await pump;\n });\n });\n\n /**\n * Types into a live pane.\n *\n * This is the one route in the package that writes rather than reads, so it\n * is deliberately narrow: an owned target, still running, started\n * interactive, and a body that is nothing but text.\n */\n app.post(routes.input.path, async (context) => {\n const runId = context.req.param(RUN_ID_PARAM);\n const attachable = attachableOf(runId);\n if (attachable === undefined) return context.json({ error: NOT_ATTACHABLE }, 404);\n\n let body: unknown;\n try {\n body = await context.req.json();\n } catch {\n return context.json({ error: 'Expected a JSON body carrying text.' }, 400);\n }\n const text = isRecord(body) ? body.text : undefined;\n if (typeof text !== 'string' || text === '') return context.json({ error: 'Expected a non-empty text.' }, 400);\n if (text.length > MAX_INPUT_CHARS) return context.json({ error: 'That is more input than a pane takes.' }, 413);\n\n const delivered = await pane().input(attachable.target, text);\n if (!delivered) return context.json({ error: 'The runner did not take the input.' }, 502);\n return context.json({ delivered: true });\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built session entry. */\nexport const api: PoApi = {\n basePath: RUNNER_API_BASE_PATH,\n start(context: PoApiContext): PoApiHandler {\n // The host is one session's server, so its id is the whole scope; a hub\n // would have handed no session at all, and there is nothing to answer.\n const app = createRunnerLogApi({\n sessionId: context.sessionId ?? '',\n cwd: context.cwd,\n environment: context.environment,\n });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request: a follow's watch and its poll are both\n // torn down when its own stream ends or the socket aborts.\n close: () => undefined,\n };\n },\n};\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\n\nimport { api } from '../../../../../../services/runnerLogApi';\n\n/**\n * Mounts this package's HTTP API at `runner`, which the folder names.\n *\n * The folder is the declaration: the build reads the base path off it and hands\n * the browser a client bound to the same segment, so the mount is stated once\n * rather than in a constant and in four URL builders that were each free to\n * drift.\n */\nexport default defineRoute(api);\n","import { defineServerCommand, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\nexport default defineServerCommand(\n (context: WithRoot<PoServerPluginContext, RunnerServerScope>) => context.root.command,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\n\nexport default defineHook(({ root }: WithRoot<PoServerPluginContext, RunnerServerScope>) => ({\n event: 'session_compact' as const,\n handle: root.recoverCompaction,\n}));\n","import { defineResource } from '@agimon-ai/po-core/extensionFile';\nimport { PO_HELP_WHEN } from '@agimon-ai/po-core/help';\nimport { packageResourcePath, readPackageResource } from '@agimon-ai/po-core/serverPlugin';\n\nexport default defineResource({\n name: 'po-use-runner',\n description:\n 'Use Po Pi Runner to supervise shell commands, inspect durable logs, provide interactive input, and stop background runs.',\n when: PO_HELP_WHEN,\n kind: 'skill' as const,\n path: packageResourcePath(import.meta.url, 'src/prompts/po-use-runner/SKILL.md'),\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-use-runner/SKILL.md'),\n});\n","import { defineServerTool, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\n\nimport type { RunnerServerScope } from '../_lib/serverRoot';\nexport default defineServerTool((context: WithRoot<PoServerPluginContext, RunnerServerScope>) => context.root.tool);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport channelRunnerRuns from '../src/extensions/(backend)/channel/runnerRuns.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/runner/route.server';\nimport commandRunners from '../src/extensions/workspaces/sessions/(backend)/command/runners.server';\nimport hookSessionCompact from '../src/extensions/workspaces/sessions/(backend)/hook/session-compact.server';\nimport resourcePoUseRunner from '../src/extensions/workspaces/sessions/(backend)/resource/po-use-runner.server';\nimport toolBash from '../src/extensions/workspaces/sessions/(backend)/tool/bash.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-runner',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n global: (context) => ({\n get channels(): PoServerSessionPlugin['channels'] { return [() => via({ frameType: 'runner_runs' }, at(channelRunnerRuns, context))]; },\n }),\n workspace: (context) => ({\n get channels(): PoServerSessionPlugin['channels'] { return defined([context.hosted('global') ? undefined : () => via({ frameType: 'runner_runs' }, at(channelRunnerRuns, context))]); },\n }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get channels(): PoServerSessionPlugin['channels'] { return defined([context.hosted('global') ? undefined : () => via({ frameType: 'runner_runs' }, at(channelRunnerRuns, context))]); },\n get commands(): PoServerSessionPlugin['commands'] { return [via({ name: 'runners' }, at(commandRunners, contextSession))]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return [via({ event: 'session_compact' }, at(hookSessionCompact, contextSession))]; },\n get resources(): PoServerSessionPlugin['resources'] { return [at(resourcePoUseRunner, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return [via({ name: 'bash' }, at(toolBash, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/po-runner",
3
- "version": "0.0.0-alpha.28",
3
+ "version": "0.0.0-alpha.29",
4
4
  "description": "Supervised shell execution, background process control, and run logs for Pi coding agents.",
5
5
  "keywords": [
6
6
  "ai",
@@ -209,11 +209,11 @@
209
209
  },
210
210
  "dependencies": {
211
211
  "@agimon-ai/foundation-process-registry": "0.30.13",
212
- "@agimon-ai/po-core": "0.0.0-alpha.28",
213
- "@agimon-ai/po-telemetry": "0.0.0-alpha.28",
214
- "@agimon-ai/po-ui": "0.0.0-alpha.28",
215
- "@agimon-ai/po-web-components": "0.0.0-alpha.28",
216
- "@agimon-ai/po-web-security": "0.0.0-alpha.28",
212
+ "@agimon-ai/po-core": "0.0.0-alpha.29",
213
+ "@agimon-ai/po-telemetry": "0.0.0-alpha.29",
214
+ "@agimon-ai/po-ui": "0.0.0-alpha.29",
215
+ "@agimon-ai/po-web-components": "0.0.0-alpha.29",
216
+ "@agimon-ai/po-web-security": "0.0.0-alpha.29",
217
217
  "@deepseek-ai/cordis": "4.0.5",
218
218
  "@rmux/sdk": "0.6.5",
219
219
  "@xterm/headless": "6.0.0",
@@ -221,8 +221,8 @@
221
221
  "typebox": "1.3.37"
222
222
  },
223
223
  "devDependencies": {
224
- "@agimon-ai/po-build": "0.0.0-alpha.28",
225
- "@agimon-ai/po-native-components": "0.0.0-alpha.28",
224
+ "@agimon-ai/po-build": "0.0.0-alpha.29",
225
+ "@agimon-ai/po-native-components": "0.0.0-alpha.29",
226
226
  "@earendil-works/pi-coding-agent": "1.3.0",
227
227
  "@tanstack/react-store": "0.11.2",
228
228
  "@tanstack/store": "0.11.2",
@@ -238,7 +238,7 @@
238
238
  "vitest": "5.0.4"
239
239
  },
240
240
  "peerDependencies": {
241
- "@agimon-ai/po-native-components": "0.0.0-alpha.28",
241
+ "@agimon-ai/po-native-components": "0.0.0-alpha.29",
242
242
  "@earendil-works/pi-coding-agent": "1.3.0",
243
243
  "react-native": "0.86.3",
244
244
  "react-native-unistyles": "^3.5.3"
@@ -258,14 +258,14 @@
258
258
  }
259
259
  },
260
260
  "optionalDependencies": {
261
- "@agimon-ai/po-runner-rmux-darwin-arm64": "0.0.0-alpha.28",
262
- "@agimon-ai/po-runner-rmux-darwin-x64": "0.0.0-alpha.28",
263
- "@agimon-ai/po-runner-rmux-linux-arm64": "0.0.0-alpha.28",
264
- "@agimon-ai/po-runner-rmux-linux-x64": "0.0.0-alpha.28",
265
- "@agimon-ai/po-runner-rtk-darwin-arm64": "0.0.0-alpha.28",
266
- "@agimon-ai/po-runner-rtk-darwin-x64": "0.0.0-alpha.28",
267
- "@agimon-ai/po-runner-rtk-linux-arm64": "0.0.0-alpha.28",
268
- "@agimon-ai/po-runner-rtk-linux-x64": "0.0.0-alpha.28"
261
+ "@agimon-ai/po-runner-rmux-darwin-arm64": "0.0.0-alpha.29",
262
+ "@agimon-ai/po-runner-rmux-darwin-x64": "0.0.0-alpha.29",
263
+ "@agimon-ai/po-runner-rmux-linux-arm64": "0.0.0-alpha.29",
264
+ "@agimon-ai/po-runner-rmux-linux-x64": "0.0.0-alpha.29",
265
+ "@agimon-ai/po-runner-rtk-darwin-arm64": "0.0.0-alpha.29",
266
+ "@agimon-ai/po-runner-rtk-darwin-x64": "0.0.0-alpha.29",
267
+ "@agimon-ai/po-runner-rtk-linux-arm64": "0.0.0-alpha.29",
268
+ "@agimon-ai/po-runner-rtk-linux-x64": "0.0.0-alpha.29"
269
269
  },
270
270
  "engines": {
271
271
  "node": ">=24"