@schoolai/shipyard 3.11.0 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{auth-GGM253LQ.js → auth-AUY74PMB.js} +3 -3
- package/dist/capability-detector-worker.js +8 -8
- package/dist/{chunk-R3XQ6W7L.js → chunk-4SYLDZTY.js} +4 -4
- package/dist/{chunk-C6QOTETH.js → chunk-5LWD5W7O.js} +24 -10
- package/dist/chunk-5LWD5W7O.js.map +1 -0
- package/dist/{chunk-IJHF4OM4.js → chunk-5W5N5U2S.js} +2 -2
- package/dist/{chunk-L7ELOV3S.js → chunk-FVZ5BDZS.js} +4 -4
- package/dist/chunk-FVZ5BDZS.js.map +1 -0
- package/dist/{chunk-RW2OTTUA.js → chunk-KYLYGFMH.js} +4 -4
- package/dist/{chunk-QJP7JCIS.js → chunk-LRNGLC4V.js} +41 -3
- package/dist/chunk-LRNGLC4V.js.map +1 -0
- package/dist/{chunk-A2UK6TW2.js → chunk-LZSMNUAI.js} +18 -1
- package/dist/{chunk-A2UK6TW2.js.map → chunk-LZSMNUAI.js.map} +1 -1
- package/dist/{chunk-Z37T5W6S.js → chunk-P2HZDIN7.js} +12 -7
- package/dist/chunk-P2HZDIN7.js.map +1 -0
- package/dist/{chunk-ZRJTZLRF.js → chunk-QKJNVVQ3.js} +4 -4
- package/dist/{chunk-2EQOL57Z.js → chunk-TFRYQDDG.js} +2 -2
- package/dist/{chunk-YXPPZQBJ.js → chunk-VL5RUCRF.js} +164 -37
- package/dist/chunk-VL5RUCRF.js.map +1 -0
- package/dist/{chunk-3WEEGJJN.js → chunk-X5KCX6ZS.js} +2 -2
- package/dist/{chunk-GM6MH4CD.js → chunk-XIEOWUPV.js} +2 -2
- package/dist/{chunk-6LINHACK.js → chunk-Y5UWRARP.js} +47 -24
- package/dist/chunk-Y5UWRARP.js.map +1 -0
- package/dist/cursor-runner.js +88 -62
- package/dist/cursor-runner.js.map +1 -1
- package/dist/electron-utility.js +5 -5
- package/dist/{git-repo-QNGPCJLI.js → git-repo-CTZJS3ER.js} +6 -4
- package/dist/index.js +8 -8
- package/dist/{logger-2F3CBS3V.js → logger-AN7EUK2B.js} +7 -5
- package/dist/{login-U256OVOJ.js → login-YB34LF4L.js} +6 -6
- package/dist/{logout-HY3MPOY5.js → logout-GUXVSWLZ.js} +5 -5
- package/dist/{mcp-servers-ICHOWXZB.js → mcp-servers-OAPQNDA7.js} +4 -4
- package/dist/{roi-YM5OOWHG.js → roi-NXJHL5X2.js} +3 -3
- package/dist/{serve-D5GKV2RU.js → serve-P3U2C5YH.js} +1175 -739
- package/dist/{serve-D5GKV2RU.js.map → serve-P3U2C5YH.js.map} +1 -1
- package/dist/{skills-W2Y6TWHA.js → skills-2UBVHFQ5.js} +2 -2
- package/dist/{start-JY26XC5R.js → start-Y34X3WVF.js} +10 -10
- package/package.json +1 -1
- package/dist/chunk-6LINHACK.js.map +0 -1
- package/dist/chunk-C6QOTETH.js.map +0 -1
- package/dist/chunk-L7ELOV3S.js.map +0 -1
- package/dist/chunk-QJP7JCIS.js.map +0 -1
- package/dist/chunk-YXPPZQBJ.js.map +0 -1
- package/dist/chunk-Z37T5W6S.js.map +0 -1
- /package/dist/{auth-GGM253LQ.js.map → auth-AUY74PMB.js.map} +0 -0
- /package/dist/{chunk-R3XQ6W7L.js.map → chunk-4SYLDZTY.js.map} +0 -0
- /package/dist/{chunk-IJHF4OM4.js.map → chunk-5W5N5U2S.js.map} +0 -0
- /package/dist/{chunk-RW2OTTUA.js.map → chunk-KYLYGFMH.js.map} +0 -0
- /package/dist/{chunk-ZRJTZLRF.js.map → chunk-QKJNVVQ3.js.map} +0 -0
- /package/dist/{chunk-2EQOL57Z.js.map → chunk-TFRYQDDG.js.map} +0 -0
- /package/dist/{chunk-3WEEGJJN.js.map → chunk-X5KCX6ZS.js.map} +0 -0
- /package/dist/{chunk-GM6MH4CD.js.map → chunk-XIEOWUPV.js.map} +0 -0
- /package/dist/{git-repo-QNGPCJLI.js.map → git-repo-CTZJS3ER.js.map} +0 -0
- /package/dist/{logger-2F3CBS3V.js.map → logger-AN7EUK2B.js.map} +0 -0
- /package/dist/{login-U256OVOJ.js.map → login-YB34LF4L.js.map} +0 -0
- /package/dist/{logout-HY3MPOY5.js.map → logout-GUXVSWLZ.js.map} +0 -0
- /package/dist/{mcp-servers-ICHOWXZB.js.map → mcp-servers-OAPQNDA7.js.map} +0 -0
- /package/dist/{roi-YM5OOWHG.js.map → roi-NXJHL5X2.js.map} +0 -0
- /package/dist/{skills-W2Y6TWHA.js.map → skills-2UBVHFQ5.js.map} +0 -0
- /package/dist/{start-JY26XC5R.js.map → start-Y34X3WVF.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getLogFilePath
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LRNGLC4V.js";
|
|
5
5
|
|
|
6
6
|
// src/shared/commands/output.ts
|
|
7
7
|
import { appendFileSync } from "fs";
|
|
@@ -40,4 +40,4 @@ export {
|
|
|
40
40
|
print,
|
|
41
41
|
printError
|
|
42
42
|
};
|
|
43
|
-
//# sourceMappingURL=chunk-
|
|
43
|
+
//# sourceMappingURL=chunk-5W5N5U2S.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getDaemonVersion
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QKJNVVQ3.js";
|
|
5
5
|
import {
|
|
6
6
|
WorkerCommandCodec,
|
|
7
7
|
WorkerReplyCodec
|
|
8
8
|
} from "./chunk-NACJENDW.js";
|
|
9
9
|
import {
|
|
10
10
|
buildNodeSpawnEnv
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-XIEOWUPV.js";
|
|
12
12
|
import {
|
|
13
13
|
createServiceSupervisor
|
|
14
14
|
} from "./chunk-ZFKJAYAN.js";
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import {
|
|
19
19
|
resolveNodeExecPath,
|
|
20
20
|
validateEnv
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-P2HZDIN7.js";
|
|
22
22
|
|
|
23
23
|
// src/services/metrics/metrics-collector.ts
|
|
24
24
|
var NOOP_METRICS = {
|
|
@@ -1094,4 +1094,4 @@ export {
|
|
|
1094
1094
|
shutdownFileWatcherGuard,
|
|
1095
1095
|
guardedSubscribe
|
|
1096
1096
|
};
|
|
1097
|
-
//# sourceMappingURL=chunk-
|
|
1097
|
+
//# sourceMappingURL=chunk-FVZ5BDZS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/metrics/metrics-collector.ts","../src/shared/file-watcher-guard.ts","../src/services/watcher-worker/worker-supervisor.ts"],"sourcesContent":["import type { MetricsIngestRequest } from '@shipyard/session';\nimport { getDaemonVersion } from '../../shared/daemon-version.js';\n\n/**\n * Known event types emitted by the daemon. Documented in one place so the\n * grep target is unambiguous when wiring a new emitter or chasing a metric.\n *\n * - `daemon_catastrophic_restart` — boot detected a prior abnormal exit via\n * the heartbeat file (native crash, SIGABRT, SIGKILL). Payload:\n * `{ priorPid, priorHeartbeatAt, ageMs }` always, plus breadcrumb-derived\n * diagnostics when the prior run left a readable breadcrumb whose pid matches:\n * `lastPhase` (the boot/runtime phase it was last in), `lastError` (a\n * captured early-boot exception message, if any), `breadcrumbAgeMs` (age of\n * the last breadcrumb write). Without these the event was unrootcauseable —\n * they turn \"it died\" into \"it died in phase X with error Y\".\n * - `daemon_early_boot_crashed` — an exception escaped the daemon early-boot\n * path before the daemon stabilized (caught by the guard in `serve()` before\n * the entry-point `.catch()` exits the process). Payload: `{ phase, error,\n * stack? }`. The same error is also written to the breadcrumb so the NEXT\n * boot's `daemon_catastrophic_restart` carries it as `lastError`.\n * - `file_watcher_circuit_open` — file-watcher-guard tripped its breaker for\n * a path after repeated subscribe failures.\n * - `file_watcher_evicted` — guard evicted a `lazy` watcher to make room.\n * - `file_watcher_essential_evicted` — guard evicted an `essential` watcher\n * (should not happen in practice; warning telemetry).\n * - `file_watcher_subscribe_failed` — single subscribe attempt failed.\n * - `file_watcher_active_count` — periodic gauge of active watchers.\n * - `file_watcher_budget_exceeded` — guard rejected a subscribe because the\n * FSEvents budget would be overrun.\n * - `watcher_worker_started` — worker-supervisor forked the initial watcher\n * subprocess.\n * - `watcher_worker_died` — watcher subprocess exited abnormally\n * (SIGABRT/SIGSEGV/SIGBUS or non-zero code).\n * - `watcher_worker_respawned` — supervisor successfully re-forked the\n * subprocess and replayed its subscription map.\n * - `watcher_worker_circuit_open` — too many subprocess deaths in the\n * circuit window; supervisor opens cooldown before next probe.\n * - `watcher_worker_circuit_closed` — half-open probe succeeded (or a\n * normal subscribe_success closed an open cooldown); cooldown is reset.\n * - `daemon_supervisor_respawned` — outer CLI supervisor re-forked the\n * daemon child after an abnormal exit.\n * - `daemon_supervisor_circuit_open` — outer CLI supervisor gave up after\n * crash-loop and is exiting non-zero.\n * - `bootstrap_phase` — per-step timing+outcome emitted by the auto-instrumentation\n * primitive in `shared/bootstrap-phase.ts`. Payload: `{ phase, durationMs, outcome,\n * error? }`. Fires once per wrapped startup step (#3290).\n * - `startup_complete` — aggregate emitted at the end of daemon boot with\n * `{ totalMs, phaseCount, phases[] }`. Pairs with the same-named structured\n * log line for easy post-mortem.\n * - `run_stall_timeout` — Cursor runner silence watchdog fired.\n * - `request_task_state_replay_failed` — peer-requested task-state replay failed.\n * - `loro_recovery_failed` — CRDT recovery failed after a recoverable Loro panic.\n * - `mcp_server_connect_failed` — coordinator moved an MCP server into failed\n * because connect/probe/timeout failed.\n * - `mcp_server_needs_auth` — coordinator moved an MCP server into needs-auth\n * after an auth-class failure.\n * - `mcp_reconnect_failed` — subprocess-side MCP reconnect failed with a\n * transient/unclassified error.\n * - `mcp_reconnect_terminal` — subprocess-side reconnect failed with a\n * terminal reason that needs user action.\n * - `mcp_reconnect_terminal_unsupported` — reconnect hit a calm terminal\n * unsupported-server classification.\n * - `mcp_reauth_failed` — an MCP OAuth re-authentication flow failed, so the\n * user's recovery path for a needs-auth server is itself broken.\n * - `mcp_adopted_set_failed` — coordinator failed to push the resolved MCP set\n * into a newly adopted subprocess.\n * - `mcp_push_failed` — coordinator failed to repush the resolved MCP set into\n * a running subprocess.\n * - `harness_mcp_registration_failed` — an agent init did not report the\n * Shipyard harness MCP server as connected, so Shipyard tools may be absent.\n * - `codex_auth_divergence_detected` — Codex subprocess auth signal disagreed with disk.\n * - `cursor_auth_divergence_detected` — Cursor subprocess auth signal disagreed with Cursor.me().\n * - `event_loop_stall` — discrete event-loop stall above watchdog threshold.\n * - `machine_sleep_artifact` — watchdog lag too large to be actionable as a JS stall.\n * - `squirrel_install_stuck_recovered` — Electron main cleared a wedged Squirrel.Mac\n * staged install on launch so electron-updater can re-download.\n *\n * The wire format treats `eventType` as an opaque string — this list exists\n * to give type-aware editors something to autocomplete and to give code\n * review a single grep target. New event types are added here.\n */\nexport type DaemonMetricsEvent =\n | 'daemon_catastrophic_restart'\n | 'daemon_early_boot_crashed'\n | 'file_watcher_circuit_open'\n | 'file_watcher_evicted'\n | 'file_watcher_essential_evicted'\n | 'file_watcher_subscribe_failed'\n | 'file_watcher_active_count'\n | 'file_watcher_budget_exceeded'\n | 'watcher_worker_started'\n | 'watcher_worker_died'\n | 'watcher_worker_respawned'\n | 'watcher_worker_circuit_open'\n | 'watcher_worker_circuit_closed'\n | 'daemon_supervisor_respawned'\n | 'daemon_supervisor_circuit_open'\n | 'bootstrap_phase'\n | 'startup_complete'\n | 'run_stall_timeout'\n | 'request_task_state_replay_failed'\n | 'loro_recovery_failed'\n | 'mcp_server_connect_failed'\n | 'mcp_server_needs_auth'\n | 'mcp_reconnect_failed'\n | 'mcp_reconnect_terminal'\n | 'mcp_reconnect_terminal_unsupported'\n | 'mcp_reauth_failed'\n | 'mcp_adopted_set_failed'\n | 'mcp_push_failed'\n | 'harness_mcp_registration_failed'\n | 'codex_auth_divergence_detected'\n | 'cursor_auth_divergence_detected'\n | 'event_loop_stall'\n | 'machine_sleep_artifact'\n | 'squirrel_install_stuck_recovered';\n\nexport interface MetricsCapture {\n capture(eventType: DaemonMetricsEvent | string, properties?: Record<string, unknown>): void;\n dispose(): void;\n}\n\nexport const NOOP_METRICS: MetricsCapture = {\n capture() {},\n dispose() {},\n};\n\n/**\n * Sink for telemetry-delivery failures. Wired to the daemon logger at WARN so a\n * broken pipeline is always visible regardless of LOG_LEVEL. Without this, `flush()`\n * swallowed every non-2xx/error (`fetch().catch(() => {})`) — which is why a\n * fleet-wide ingest outage went unnoticed for 9 days.\n */\nexport type MetricsLogger = (entry: { event: string; [key: string]: unknown }) => void;\n\nconst NOOP_LOG: MetricsLogger = () => {};\n\n/** Min spacing between failure logs so a persistent outage doesn't itself become log spam. */\nconst FAILURE_LOG_THROTTLE_MS = 600_000;\n\ninterface MetricsCollectorOpts {\n flushIntervalMs?: number;\n maxBatchSize?: number;\n log?: MetricsLogger;\n}\n\ninterface BufferedEvent {\n eventType: string;\n taskId?: string;\n payload: Record<string, unknown>;\n clientTimestamp: number;\n}\n\nexport class MetricsCollector {\n readonly #workerUrl: string;\n /**\n * String OR a getter resolved at flush time. The daemon holds a static\n * boot-time token; a getter lets a long-running daemon pick up a token\n * refreshed in config.json (re-login) without restarting, so its telemetry\n * stops 401-ing on the stale token.\n */\n readonly #authToken: string | (() => string);\n readonly #maxBatchSize: number;\n readonly #log: MetricsLogger;\n /**\n * Resolved once at construction — the version is static for a process, and\n * getDaemonVersion() can throw in a misconfigured packaged build, so we never\n * want to call it per-event. Falls back to 'unknown' rather than failing\n * telemetry capture if resolution throws.\n */\n readonly #daemonVersion: string;\n #buffer: BufferedEvent[] = [];\n #timer: ReturnType<typeof setInterval> | null = null;\n #disposed = false;\n #lastFailureLogMs = 0;\n #suppressedFailures = 0;\n\n constructor(workerUrl: string, authToken: string | (() => string), opts?: MetricsCollectorOpts) {\n this.#workerUrl = workerUrl.replace(/\\/$/, '');\n this.#authToken = authToken;\n this.#maxBatchSize = opts?.maxBatchSize ?? 50;\n this.#log = opts?.log ?? NOOP_LOG;\n this.#daemonVersion = resolveDaemonVersionSafely();\n\n const intervalMs = opts?.flushIntervalMs ?? 30_000;\n this.#timer = setInterval(() => {\n this.flush();\n }, intervalMs);\n this.#timer.unref();\n }\n\n capture(eventType: string, properties?: Record<string, unknown>): void {\n if (this.#disposed) return;\n\n const taskId = typeof properties?.taskId === 'string' ? properties.taskId : undefined;\n\n this.#buffer.push({\n eventType,\n taskId,\n /**\n * Every event carries daemonVersion so fleet telemetry can be sliced by\n * version — without it, triage can't distinguish 'fix not deployed' from\n * 'fix ineffective'. Spread last so a stray caller field can't override\n * the authoritative value.\n */\n payload: { ...(properties ?? {}), daemonVersion: this.#daemonVersion },\n clientTimestamp: Date.now(),\n });\n\n if (this.#buffer.length >= this.#maxBatchSize) {\n this.flush();\n }\n }\n\n flush(): void {\n if (this.#buffer.length === 0) return;\n\n const events = this.#buffer;\n this.#buffer = [];\n\n const body: MetricsIngestRequest = { events };\n\n const token = typeof this.#authToken === 'function' ? this.#authToken() : this.#authToken;\n fetch(`${this.#workerUrl}/ingest`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(body),\n })\n .then((res) => {\n if (res.ok) {\n this.#suppressedFailures = 0;\n return;\n }\n this.#reportFailure({ status: res.status, droppedEvents: events.length });\n })\n .catch((err: unknown) => {\n this.#reportFailure({\n error: err instanceof Error ? err.message : String(err),\n droppedEvents: events.length,\n });\n });\n }\n\n /**\n * Surface a delivery failure at most once per throttle window, carrying the\n * count of failures suppressed since the last log so a persistent outage is\n * visible without flooding the log.\n */\n #reportFailure(detail: Record<string, unknown>): void {\n const now = Date.now();\n if (now - this.#lastFailureLogMs < FAILURE_LOG_THROTTLE_MS) {\n this.#suppressedFailures += 1;\n return;\n }\n this.#log({\n event: 'telemetry_ingest_failed',\n suppressedSinceLastLog: this.#suppressedFailures,\n ...detail,\n });\n this.#lastFailureLogMs = now;\n this.#suppressedFailures = 0;\n }\n\n dispose(): void {\n this.#disposed = true;\n if (this.#timer) {\n clearInterval(this.#timer);\n this.#timer = null;\n }\n this.flush();\n }\n}\n\nfunction resolveDaemonVersionSafely(): string {\n try {\n return getDaemonVersion();\n } catch {\n return 'unknown';\n }\n}\n\nexport function createMetricsCollector(\n workerUrl: string | undefined,\n authToken: string | (() => string) | undefined,\n telemetryEnabled: boolean,\n log: MetricsLogger\n): MetricsCapture {\n if (!telemetryEnabled || !workerUrl || !authToken) return NOOP_METRICS;\n return new MetricsCollector(workerUrl, authToken, { log });\n}\n","import { fork as childProcessFork } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type {\n AsyncSubscription,\n Event as ParcelEvent,\n Options as ParcelOptions,\n} from '@parcel/watcher';\nimport {\n createWatcherWorkerSupervisor,\n type SupervisorMetrics,\n type WatcherWorkerEventLike,\n type WatcherWorkerSupervisor,\n} from '../services/watcher-worker/worker-supervisor.js';\nimport { assertNever } from './assert-never.js';\nimport { validateEnv } from './env.js';\nimport { buildNodeSpawnEnv, resolveNodeExecPath } from './spawn-as-node.js';\n\n/**\n * Re-entry safety (AGENTS.md Invariant #11): synthetic 'evicted' dispatch is\n * deferred via queueMicrotask so we never re-enter @parcel/watcher's own\n * dispatch from inside this module.\n */\n\nexport type GuardTier = 'essential' | 'lazy';\nexport type GuardReason = 'initial' | 'escalation' | 'rescan';\n\n/**\n * Wider event shape than @parcel/watcher's. The guard injects synthetic\n * 'evicted' events into a watcher's callback when LRU pressure forces eviction;\n * consumers can react by re-establishing a subscription or doing a readdir\n * catch-up on the next read.\n */\nexport type GuardedEvent = ParcelEvent | { type: 'evicted'; path: string };\n\nexport type GuardedSubscribeCallback = (err: Error | null, events: GuardedEvent[]) => unknown;\n\nexport type GuardedSubscribeOptions = ParcelOptions;\n\nexport type FileWatcherGuardLogger = (entry: { event: string; [key: string]: unknown }) => void;\n\nexport interface FileWatcherGuardDeps {\n /**\n * Underlying watcher implementation. In production this routes through the\n * watcher worker subprocess via `WatcherWorkerSupervisor.subscribe`; the\n * supervisor's synthetic 'evicted' event (fired across worker respawns) is\n * forwarded to the consumer alongside the guard's own LRU-eviction synthetic\n * events. Tests inject a direct fake.\n */\n subscribe: (\n path: string,\n fn: (err: Error | null, events: GuardedEvent[]) => unknown,\n opts?: ParcelOptions\n ) => Promise<AsyncSubscription>;\n log: FileWatcherGuardLogger;\n /** Injected for deterministic testing. */\n now: () => number;\n /** Injected so tests can drive without real timers. */\n setTimeout: (fn: () => void, ms: number) => unknown;\n /** Injected so tests can cancel scheduled timers (e.g. supervisor shutdown). */\n clearTimeout: (timer: unknown) => void;\n}\n\nconst DEFAULT_MAX_ACTIVE_WATCHERS = 450;\nexport const MAX_ACTIVE_WATCHERS =\n validateEnv().SHIPYARD_FILE_WATCHER_MAX ?? DEFAULT_MAX_ACTIVE_WATCHERS;\n\nexport const STARTING_BACKOFF_MS_INITIAL = 250;\nexport const STARTING_BACKOFF_MS_ESCALATION = 1_000;\nexport const MAX_BACKOFF_MS = 30_000;\nexport const BACKOFF_RESET_AFTER_MS = 5 * 60_000;\n\nexport const CIRCUIT_FAILURES = 5;\nexport const CIRCUIT_WINDOW_MS = 60_000;\nexport const CIRCUIT_OPEN_MS = 5 * 60_000;\nexport const CIRCUIT_OPEN_MAX_MS = 30 * 60_000;\n\n/** Delay before the guard attempts to re-subscribe an evicted essential watcher. */\nexport const ESSENTIAL_RESUBSCRIBE_DELAY_MS = 1_000;\n\nexport interface AttemptState {\n /** Monotonic count of consecutive failures since last clean window. */\n failureCount: number;\n /** Current backoff value (next subscribe attempt waits this long). */\n backoffMs: number;\n /** Last failure timestamp, used to compute clean-window decay. */\n lastFailureAt: number;\n /** Set when circuit is open. */\n circuitOpenedAt: number | null;\n /** Cooldown duration for the currently-open circuit (doubles on re-open). */\n circuitCooldownMs: number;\n /** Most recent failure timestamps used to detect rate (5 within window). */\n recentFailures: number[];\n /** When circuit went half-open (one probe permitted). */\n halfOpenAt: number | null;\n}\n\nexport function makeInitialAttemptState(): AttemptState {\n return {\n failureCount: 0,\n backoffMs: 0,\n lastFailureAt: 0,\n circuitOpenedAt: null,\n circuitCooldownMs: CIRCUIT_OPEN_MS,\n recentFailures: [],\n halfOpenAt: null,\n };\n}\n\nfunction isCleanAttemptState(state: AttemptState): boolean {\n return (\n state.circuitOpenedAt === null &&\n state.failureCount === 0 &&\n state.backoffMs === 0 &&\n state.recentFailures.length === 0 &&\n state.halfOpenAt === null\n );\n}\n\nexport type DecisionEvent =\n | { kind: 'request_subscribe'; reason: GuardReason; activeCount: number }\n | { kind: 'subscribe_success' }\n | { kind: 'subscribe_failure' };\n\nexport type DecisionAction =\n | { kind: 'subscribe' }\n | { kind: 'evict_and_subscribe' }\n | { kind: 'wait'; ms: number }\n | { kind: 'reject_stub'; reason: 'circuit_open' }\n | { kind: 'noop' };\n\nexport interface DecisionResult {\n state: AttemptState;\n action: DecisionAction;\n /** Side-effect signals the shell should emit (logs). */\n signals: Array<\n { kind: 'circuit_opened' } | { kind: 'circuit_closed' } | { kind: 'circuit_probe' }\n >;\n}\n\n/**\n * Pure state-machine step. No I/O, deterministic — drives all retry / circuit /\n * backoff behavior. The shell interprets DecisionAction.\n */\nexport function decideAction(\n state: AttemptState,\n event: DecisionEvent,\n now: number\n): DecisionResult {\n switch (event.kind) {\n case 'request_subscribe':\n return decideRequest(state, event, now);\n case 'subscribe_success':\n return decideSuccess(state, now);\n case 'subscribe_failure':\n return decideFailure(state, now);\n default:\n return assertNever(event);\n }\n}\n\nfunction decideRequest(\n state: AttemptState,\n event: { kind: 'request_subscribe'; reason: GuardReason; activeCount: number },\n now: number\n): DecisionResult {\n /** Decay backoff after a long clean window. */\n const decayed = decayBackoff(state, now);\n\n if (decayed.circuitOpenedAt !== null) {\n const cooldownEnd = decayed.circuitOpenedAt + decayed.circuitCooldownMs;\n if (now < cooldownEnd) {\n return {\n state: decayed,\n action: { kind: 'reject_stub', reason: 'circuit_open' },\n signals: [],\n };\n }\n /** Half-open: allow ONE probe through; mark it. */\n return {\n state: { ...decayed, halfOpenAt: now },\n action:\n event.activeCount >= MAX_ACTIVE_WATCHERS\n ? { kind: 'evict_and_subscribe' }\n : { kind: 'subscribe' },\n signals: [{ kind: 'circuit_probe' }],\n };\n }\n\n /**\n * If we have an outstanding backoff window and we're inside it, tell the\n * shell to wait. The shell schedules a retry via setTimeout.\n */\n if (decayed.backoffMs > 0 && now < decayed.lastFailureAt + decayed.backoffMs) {\n const remaining = decayed.lastFailureAt + decayed.backoffMs - now;\n return { state: decayed, action: { kind: 'wait', ms: remaining }, signals: [] };\n }\n\n /**\n * No prior failure but reason='escalation' — start with a higher floor since\n * escalation is by definition retry pressure.\n */\n let nextState = decayed;\n if (decayed.backoffMs === 0 && event.reason === 'escalation') {\n nextState = { ...decayed, backoffMs: STARTING_BACKOFF_MS_ESCALATION };\n }\n\n if (event.activeCount >= MAX_ACTIVE_WATCHERS) {\n return { state: nextState, action: { kind: 'evict_and_subscribe' }, signals: [] };\n }\n return { state: nextState, action: { kind: 'subscribe' }, signals: [] };\n}\n\nfunction decideSuccess(state: AttemptState, _now: number): DecisionResult {\n const wasHalfOpen = state.halfOpenAt !== null;\n const next: AttemptState = {\n failureCount: 0,\n backoffMs: 0,\n lastFailureAt: 0,\n circuitOpenedAt: null,\n circuitCooldownMs: CIRCUIT_OPEN_MS,\n recentFailures: [],\n halfOpenAt: null,\n };\n return {\n state: next,\n action: { kind: 'noop' },\n signals: wasHalfOpen ? [{ kind: 'circuit_closed' }] : [],\n };\n}\n\nfunction decideFailure(state: AttemptState, now: number): DecisionResult {\n const recent = [...state.recentFailures.filter((t) => now - t < CIRCUIT_WINDOW_MS), now];\n const baseBackoff =\n state.backoffMs === 0\n ? STARTING_BACKOFF_MS_INITIAL\n : Math.min(state.backoffMs * 2, MAX_BACKOFF_MS);\n\n /** Already half-open and probe failed: re-open with doubled cooldown. */\n if (state.halfOpenAt !== null) {\n const nextCooldown = Math.min(state.circuitCooldownMs * 2, CIRCUIT_OPEN_MAX_MS);\n return {\n state: {\n ...state,\n failureCount: state.failureCount + 1,\n backoffMs: baseBackoff,\n lastFailureAt: now,\n recentFailures: recent,\n circuitOpenedAt: now,\n circuitCooldownMs: nextCooldown,\n halfOpenAt: null,\n },\n action: { kind: 'noop' },\n signals: [{ kind: 'circuit_opened' }],\n };\n }\n\n /** Newly opening the circuit. */\n if (recent.length >= CIRCUIT_FAILURES && state.circuitOpenedAt === null) {\n return {\n state: {\n ...state,\n failureCount: state.failureCount + 1,\n backoffMs: baseBackoff,\n lastFailureAt: now,\n recentFailures: recent,\n circuitOpenedAt: now,\n circuitCooldownMs: CIRCUIT_OPEN_MS,\n halfOpenAt: null,\n },\n action: { kind: 'noop' },\n signals: [{ kind: 'circuit_opened' }],\n };\n }\n\n return {\n state: {\n ...state,\n failureCount: state.failureCount + 1,\n backoffMs: baseBackoff,\n lastFailureAt: now,\n recentFailures: recent,\n },\n action: { kind: 'noop' },\n signals: [],\n };\n}\n\nfunction decayBackoff(state: AttemptState, now: number): AttemptState {\n if (state.lastFailureAt === 0 || state.backoffMs === 0) return state;\n const sinceLast = now - state.lastFailureAt;\n if (sinceLast >= BACKOFF_RESET_AFTER_MS) {\n return { ...state, backoffMs: 0, failureCount: 0, recentFailures: [] };\n }\n /** Halve every 60s of clean window — mirrors the documented decay. */\n const halvings = Math.floor(sinceLast / 60_000);\n if (halvings === 0) return state;\n const next = Math.max(STARTING_BACKOFF_MS_INITIAL, state.backoffMs >> halvings);\n return { ...state, backoffMs: next };\n}\n\ninterface ActiveEntry {\n path: string;\n tier: GuardTier;\n lastUsedAt: number;\n unsubscribe: () => Promise<void>;\n /** Original consumer callback so we can fire synthetic 'evicted' events. */\n consumerCallback: GuardedSubscribeCallback;\n}\n\n/**\n * A reservation reserves a slot in the budget while a `subscribe` call is\n * in flight. Without this, N concurrent subscribes when active.size == MAX-1\n * all see \"subscribe\" with no eviction and overrun the budget on resolve.\n */\ninterface Reservation {\n path: string;\n tier: GuardTier;\n reservedAt: number;\n}\n\ninterface GuardModuleState {\n active: Set<ActiveEntry>;\n reservations: Set<Reservation>;\n perPath: Map<string, AttemptState>;\n /** Cache of circuit-open log emission so we log only once per opening. */\n loggedOpenAt: Map<string, number>;\n /** Live deps; overridden in tests via _resetGuardForTesting / configureFileWatcherGuard. */\n deps: FileWatcherGuardDeps;\n /**\n * Lazily-instantiated worker supervisor — Commit 2 of the watcher-worker\n * subprocess rollout. Created on first `guardedSubscribe` call so test\n * envs that override `deps.subscribe` never instantiate it.\n */\n supervisor: WatcherWorkerSupervisor | null;\n supervisorMetrics: SupervisorMetrics | null;\n}\n\nlet moduleState: GuardModuleState = makeModuleState();\n\nfunction makeModuleState(): GuardModuleState {\n return {\n active: new Set<ActiveEntry>(),\n reservations: new Set<Reservation>(),\n perPath: new Map<string, AttemptState>(),\n loggedOpenAt: new Map<string, number>(),\n deps: defaultDeps(),\n supervisor: null,\n supervisorMetrics: null,\n };\n}\n\n/**\n * Vitest sets `process.env.VITEST === 'true'`; under that we fall back to\n * the direct `@parcel/watcher` import so existing consumer tests that mock\n * `@parcel/watcher` keep working without each test having to mock the\n * worker-supervisor module. Production code paths set neither, so the\n * supervisor routing is always live in `shipyard start`.\n *\n * WHY THIS FALLBACK STILL EXISTS (Commit 4 audit):\n * - `apps/daemon/src/services/file-watcher.test.ts` mocks `@parcel/watcher`\n * directly via `vi.mock('@parcel/watcher', ...)` and exercises the guard\n * indirectly through `FileWatcherPool`. Migrating it (and any future\n * consumer test that adopts the same pattern) to mock the worker supervisor\n * is a separate refactor — its scope is \"rewrite the FileWatcherPool test\n * harness\" which is independent of the watcher-worker subprocess rollout.\n * - `tests/integration-coverage.test.ts` keeps `file-watcher-guard.ts` in\n * the Invariant #17 allowlist for as long as this fallback exists. Once\n * `file-watcher.test.ts` switches to mocking the supervisor module, this\n * constant + `legacyDirectSubscribe` can both be removed and the allowlist\n * tightened to only `worker.ts` and its tests.\n */\nconst IN_VITEST = process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';\n\nfunction defaultDeps(): FileWatcherGuardDeps {\n return {\n /**\n * Production path: route every subscribe through the watcher worker\n * supervisor (which runs `@parcel/watcher` in a subprocess). The\n * supervisor delivers `WatcherWorkerEventLike[]` whose 'evicted' variant\n * is fired during worker respawns; we widen it to `GuardedEvent[]`\n * (which already accommodates 'evicted' via the LRU eviction path).\n */\n subscribe: (path, fn, opts) => {\n if (IN_VITEST) return legacyDirectSubscribe(path, fn, opts);\n return supervisorSubscribe(path, fn, opts);\n },\n log: () => {},\n now: () => Date.now(),\n setTimeout: (fn, ms) => setTimeout(fn, ms),\n clearTimeout: (timer) => {\n /**\n * `timer` is typed `unknown` in `FileWatcherGuardDeps` so test impls\n * can use numeric handles. The Node runtime accepts both numeric IDs\n * and `Timeout` objects via the same call.\n */\n if (timer === null || timer === undefined) return;\n clearTimeout(timer as never);\n },\n };\n}\n\nconst supervisorSubscribe: FileWatcherGuardDeps['subscribe'] = async (path, fn, opts) => {\n const supervisor = ensureSupervisor();\n const handle = await supervisor.subscribe(\n path,\n (err, events) => {\n fn(err, events.map(toGuardedEvent));\n },\n toSupervisorOpts(opts)\n );\n return { unsubscribe: () => handle.unsubscribe() };\n};\n\nfunction toSupervisorOpts(opts?: ParcelOptions): {\n ignore?: string[];\n backend?: 'default' | 'brute-force' | 'watchman';\n} {\n const out: { ignore?: string[]; backend?: 'default' | 'brute-force' | 'watchman' } = {};\n if (!opts) return out;\n if (opts.ignore) out.ignore = opts.ignore;\n if (opts.backend === 'brute-force' || opts.backend === 'watchman') {\n out.backend = opts.backend;\n }\n return out;\n}\n\nfunction toGuardedEvent(e: WatcherWorkerEventLike): GuardedEvent {\n switch (e.type) {\n case 'evicted':\n return { type: 'evicted', path: e.path };\n case 'create':\n case 'update':\n case 'delete':\n return { type: e.type, path: e.path };\n default:\n return assertNever(e.type);\n }\n}\n\nfunction ensureSupervisor(): WatcherWorkerSupervisor {\n if (moduleState.supervisor) return moduleState.supervisor;\n /**\n * `workerPath` resolves to the bundled worker entry shipped alongside the\n * daemon. tsup is configured (in `apps/daemon/tsup.config.ts`) with two\n * entries that emit at the top level of `dist/`: `src/index.ts` →\n * `dist/index.js` and `src/services/watcher-worker/worker.ts` →\n * `dist/worker.js`. tsup uses the basename of each entry for the output\n * filename and does NOT preserve the source directory structure. At\n * runtime `import.meta.url` is the daemon bundle (the only place that\n * imports this module in production) so `./worker.js` resolves to its\n * sibling. Meta-test \"Daemon Fork Targets Exist\" enforces this.\n *\n * In dev mode (`tsx src/index.ts`), `import.meta.url` is the source file\n * (`src/shared/file-watcher-guard.ts`), not `dist/index.js`. `./worker.js`\n * relative to `src/shared/` would point to a non-existent file. Detect the\n * dev path by checking whether the module URL contains `/src/` and resolve\n * to the actual worker source (`src/services/watcher-worker/worker.ts`)\n * instead — tsx handles the `.js` → `.ts` extension rewrite automatically.\n */\n const selfUrl = import.meta.url;\n const isDevMode = selfUrl.endsWith('.ts');\n const workerPath = isDevMode\n ? new URL('../services/watcher-worker/worker.ts', selfUrl).pathname\n : new URL('./worker.js', selfUrl).pathname;\n moduleState.supervisor = createWatcherWorkerSupervisor({\n fork: nodeFork,\n workerPath,\n log: (entry) => moduleState.deps.log(entry),\n metrics: moduleState.supervisorMetrics ?? undefined,\n now: () => moduleState.deps.now(),\n setTimeout: (fn, ms) => moduleState.deps.setTimeout(fn, ms),\n clearTimeout: (timer) => moduleState.deps.clearTimeout(timer),\n });\n return moduleState.supervisor;\n}\n\nconst nodeFork: Parameters<typeof createWatcherWorkerSupervisor>[0]['fork'] = (\n modulePath,\n args,\n options\n) =>\n /**\n * Fork the watcher worker against the resolved Node binary + Electron-node\n * env overlay — NOT the default `process.execPath`. In the packaged Electron\n * utility process the default resolves to a helper executable that may be\n * absent from the bundle (spawn ENOENT) and, even when present, needs\n * `ELECTRON_RUN_AS_NODE=1` to run a JS file instead of booting an Electron\n * app. Same root cause as the cursor-runner crash loop — this path is lazy\n * (first worktree subscription) so it broke file watching rather than boot.\n */\n childProcessFork(modulePath, args, {\n ...options,\n execPath: resolveNodeExecPath(),\n env: buildNodeSpawnEnv(process.env),\n });\n\n/** Total occupancy = active + outstanding reservations. */\nfunction occupancy(): number {\n return moduleState.active.size + moduleState.reservations.size;\n}\n\n/**\n * Production wiring: replace only the `log` callback (and not `now`/`setTimeout`/\n * `subscribe`) so structured events reach Pino + the metrics collector.\n * Idempotent: callers may invoke this multiple times; later calls win. Optionally\n * accepts a `metrics` capture so the supervisor's restart-class telemetry is\n * routed through the same collector that already receives the guard's events.\n */\nexport function configureFileWatcherGuard(deps: {\n log: FileWatcherGuardLogger;\n metrics?: SupervisorMetrics;\n}): void {\n moduleState.deps = { ...moduleState.deps, log: deps.log };\n if (deps.metrics) {\n moduleState.supervisorMetrics = deps.metrics;\n }\n}\n\n/**\n * Test-only: replace deps and reset internal state. NOT exported as part of\n * the production surface — guarded by underscore convention.\n *\n * Resets to a `subscribe` impl that dynamically imports `@parcel/watcher`\n * (the pre-Commit-2 default), so existing `vi.mock('@parcel/watcher')`\n * patterns in consumer tests continue to work without each test needing to\n * mock the worker supervisor module. Production `defaultDeps()` routes\n * through the supervisor; tests opt into that path explicitly.\n */\nexport function _resetGuardForTesting(deps?: Partial<FileWatcherGuardDeps>): void {\n moduleState = makeModuleState();\n moduleState.deps = {\n ...moduleState.deps,\n subscribe: legacyDirectSubscribe,\n };\n if (deps) moduleState.deps = { ...moduleState.deps, ...deps };\n}\n\n/**\n * Test-only fallback that calls `@parcel/watcher.subscribe` directly. Kept\n * isolated from `defaultDeps()` (which goes through the worker supervisor)\n * so we never accidentally ship the direct-import path into production.\n *\n * The guard's `subscribe` deps callback type accepts `GuardedEvent[]` (a\n * superset of `ParcelEvent[]`), so we adapt the direction by widening each\n * ParcelEvent to a GuardedEvent before forwarding — no type assertion\n * needed because every ParcelEvent is structurally a GuardedEvent.\n */\nconst legacyDirectSubscribe: FileWatcherGuardDeps['subscribe'] = async (path, fn, opts) => {\n const mod = await import('@parcel/watcher');\n return mod.subscribe(\n path,\n (err, events) => {\n const widened: GuardedEvent[] = events.map((e) => ({ type: e.type, path: e.path }));\n fn(err, widened);\n },\n opts\n );\n};\n\n/**\n * Graceful shutdown for the lazily-instantiated supervisor. No-op if the\n * supervisor was never created (test envs that override `deps.subscribe`).\n */\nexport async function shutdownFileWatcherGuard(): Promise<void> {\n const supervisor = moduleState.supervisor;\n if (!supervisor) return;\n moduleState.supervisor = null;\n await supervisor.shutdown();\n}\n\nexport function _getActiveCountForTesting(): number {\n return moduleState.active.size;\n}\n\nexport function _getPerPathSizeForTesting(): number {\n return moduleState.perPath.size;\n}\n\nexport function _getReservationCountForTesting(): number {\n return moduleState.reservations.size;\n}\n\n/**\n * Single chokepoint for every file watcher in the daemon. See AGENTS.md\n * Invariant #17 for the contract and rationale.\n */\nexport async function guardedSubscribe(\n path: string,\n callback: GuardedSubscribeCallback,\n opts: GuardedSubscribeOptions,\n tier: GuardTier,\n reason: GuardReason\n): Promise<AsyncSubscription> {\n const { deps } = moduleState;\n const now = deps.now();\n const priorState = moduleState.perPath.get(path) ?? makeInitialAttemptState();\n const decision = decideAction(\n priorState,\n { kind: 'request_subscribe', reason, activeCount: occupancy() },\n now\n );\n moduleState.perPath.set(path, decision.state);\n emitSignals(path, decision.signals);\n\n switch (decision.action.kind) {\n case 'reject_stub':\n return makeStubSubscription();\n case 'wait': {\n const waitMs = decision.action.ms;\n await new Promise<void>((resolve) => deps.setTimeout(() => resolve(), waitMs));\n return guardedSubscribe(path, callback, opts, tier, 'escalation');\n }\n case 'evict_and_subscribe':\n evictOne(path);\n return performSubscribe(path, callback, opts, tier);\n case 'subscribe':\n return performSubscribe(path, callback, opts, tier);\n case 'noop':\n /** decideRequest never returns 'noop'; satisfy exhaustiveness. */\n throw new Error('guardedSubscribe: unexpected noop on request');\n default:\n return assertNever(decision.action);\n }\n}\n\nasync function performSubscribe(\n path: string,\n consumerCallback: GuardedSubscribeCallback,\n opts: GuardedSubscribeOptions,\n tier: GuardTier\n): Promise<AsyncSubscription> {\n const { deps } = moduleState;\n let entry: ActiveEntry | null = null;\n\n /**\n * Reserve the slot synchronously (TOCTOU fix). Without this, N concurrent\n * subscribes at activeCount = MAX-1 all read \"below budget\" and skip the\n * eviction path; on resolve they all add entries and we silently overrun\n * the FSEvents stream limit.\n */\n const reservation: Reservation = { path, tier, reservedAt: deps.now() };\n moduleState.reservations.add(reservation);\n\n const wrapped = (err: Error | null, events: GuardedEvent[]) => {\n if (entry) entry.lastUsedAt = deps.now();\n consumerCallback(err, events);\n };\n\n try {\n const sub = await deps.subscribe(path, wrapped, opts);\n moduleState.reservations.delete(reservation);\n const now = deps.now();\n /** Mark success in the per-path state (closes any half-open circuit). */\n const priorState = moduleState.perPath.get(path) ?? makeInitialAttemptState();\n const successDecision = decideAction(priorState, { kind: 'subscribe_success' }, now);\n if (isCleanAttemptState(successDecision.state)) {\n moduleState.perPath.delete(path);\n } else {\n moduleState.perPath.set(path, successDecision.state);\n }\n emitSignals(path, successDecision.signals);\n\n entry = {\n path,\n tier,\n lastUsedAt: now,\n unsubscribe: () => sub.unsubscribe(),\n consumerCallback,\n };\n moduleState.active.add(entry);\n deps.log({ event: 'file_watcher_active_count', count: moduleState.active.size });\n\n /** Wrap unsubscribe so guard accounting stays consistent on consumer-driven teardown. */\n return {\n unsubscribe: async () => {\n if (entry && moduleState.active.has(entry)) {\n moduleState.active.delete(entry);\n }\n /**\n * Per-path cleanup (F6): drop the AttemptState entry when the watcher\n * unsubscribes cleanly and no failures have accumulated. Long-running\n * daemon with task churn would otherwise grow this map without bound.\n */\n const current = moduleState.perPath.get(path);\n if (current && isCleanAttemptState(current)) {\n moduleState.perPath.delete(path);\n }\n await sub.unsubscribe();\n },\n };\n } catch (err) {\n moduleState.reservations.delete(reservation);\n const now = deps.now();\n const priorState = moduleState.perPath.get(path) ?? makeInitialAttemptState();\n const failureDecision = decideAction(priorState, { kind: 'subscribe_failure' }, now);\n moduleState.perPath.set(path, failureDecision.state);\n const parentExists = pathExistsForLog(dirname(path));\n deps.log({\n event: 'file_watcher_subscribe_failed',\n path,\n pathExists: pathExistsForLog(path),\n parentExists,\n err: err instanceof Error ? err.message : String(err),\n /**\n * The expected-ENOENT fallback path (file doesn't exist yet but\n * parent does) is a normal poll cycle, not a failure. Downgrade\n * the log level so log triage doesn't flag 40+ of these per task.\n */\n ...(parentExists ? { level: 'debug' as const } : {}),\n });\n emitSignals(path, failureDecision.signals);\n\n /** If the failure tipped us into circuit-open, return a stub. Otherwise rethrow so caller can handle. */\n if (failureDecision.state.circuitOpenedAt !== null) {\n return makeStubSubscription();\n }\n throw err;\n }\n}\n\nfunction pathExistsForLog(path: string): boolean {\n try {\n return existsSync(path);\n } catch {\n return false;\n }\n}\n\n/**\n * Pick the oldest entry in the given tier (or any tier if `tier` is null).\n * Pure: depends only on `entries`. Returns null if no candidate matches.\n */\nfunction pickOldest(entries: Iterable<ActiveEntry>, tier: 'lazy' | null): ActiveEntry | null {\n let target: ActiveEntry | null = null;\n for (const entry of entries) {\n if (tier !== null && entry.tier !== tier) continue;\n if (target === null || entry.lastUsedAt < target.lastUsedAt) target = entry;\n }\n return target;\n}\n\nfunction selectEvictionVictim(\n entries: Iterable<ActiveEntry>\n): { victim: ActiveEntry; evictingEssential: boolean } | null {\n const lazy = pickOldest(entries, 'lazy');\n if (lazy !== null) return { victim: lazy, evictingEssential: false };\n const any = pickOldest(entries, null);\n if (any !== null) return { victim: any, evictingEssential: true };\n return null;\n}\n\nfunction evictOne(incomingPath: string): void {\n const { deps } = moduleState;\n if (moduleState.active.size === 0) {\n throw new Error('file-watcher-guard: cannot evict — active set is empty (counter is broken)');\n }\n\n const selection = selectEvictionVictim(moduleState.active);\n if (selection === null) {\n throw new Error('file-watcher-guard: eviction selection failed');\n }\n const { victim, evictingEssential } = selection;\n\n moduleState.active.delete(victim);\n deps.log({\n event: evictingEssential ? 'file_watcher_essential_evicted' : 'file_watcher_evicted',\n path: victim.path,\n tier: victim.tier,\n incomingPath,\n });\n\n /**\n * Re-entry safety (Invariant #11): defer the synthetic dispatch via\n * queueMicrotask so a callback that re-enters the guard cannot do so from\n * inside the dispatching frame.\n */\n queueMicrotask(() => {\n victim.consumerCallback(null, [{ type: 'evicted', path: victim.path }]);\n });\n /** Fire-and-forget unsubscribe; we don't block the new subscribe on it. */\n victim.unsubscribe().catch((err) => {\n deps.log({\n event: 'file_watcher_unsubscribe_failed_during_eviction',\n path: victim.path,\n err: err instanceof Error ? err.message : String(err),\n });\n });\n\n /**\n * Self-healing essentials (F4): consumers of essential watchers (capability\n * watcher, plan-file-bridge, published-artifact-store) don't currently\n * re-subscribe on `evicted` — they treat the synthetic event as fatal. The\n * guard schedules a retry on their behalf so an essential watcher that lost\n * its slot to a budget squeeze comes back online once the new arrival\n * settles. Bounded by the circuit breaker — repeated failures will trip it.\n */\n if (evictingEssential) {\n scheduleEssentialResubscribe(victim);\n }\n}\n\nfunction scheduleEssentialResubscribe(victim: ActiveEntry): void {\n const { deps } = moduleState;\n deps.log({\n event: 'file_watcher_essential_re_subscribe_scheduled',\n path: victim.path,\n delayMs: ESSENTIAL_RESUBSCRIBE_DELAY_MS,\n });\n deps.setTimeout(() => {\n void retryEssentialSubscribe(victim);\n }, ESSENTIAL_RESUBSCRIBE_DELAY_MS);\n}\n\nasync function retryEssentialSubscribe(victim: ActiveEntry): Promise<void> {\n const { deps } = moduleState;\n try {\n await guardedSubscribe(victim.path, victim.consumerCallback, {}, 'essential', 'rescan');\n deps.log({ event: 'file_watcher_essential_re_subscribed', path: victim.path });\n } catch (err) {\n deps.log({\n event: 'file_watcher_essential_re_subscribe_failed',\n path: victim.path,\n err: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\nfunction emitSignals(path: string, signals: DecisionResult['signals']): void {\n const { deps } = moduleState;\n for (const sig of signals) {\n switch (sig.kind) {\n case 'circuit_opened':\n deps.log({ event: 'file_watcher_circuit_open', path });\n moduleState.loggedOpenAt.set(path, deps.now());\n break;\n case 'circuit_closed':\n deps.log({ event: 'file_watcher_circuit_closed', path });\n moduleState.loggedOpenAt.delete(path);\n break;\n case 'circuit_probe':\n /** Probe attempts are attempt-level — no log to avoid spam. */\n break;\n default:\n assertNever(sig);\n }\n }\n}\n\nfunction makeStubSubscription(): AsyncSubscription {\n return { unsubscribe: async () => {} };\n}\n","import type { ChildProcess } from 'node:child_process';\nimport { randomUUID } from 'node:crypto';\nimport {\n createServiceSupervisor,\n type ServiceSupervisor,\n type StdioChildProcessLike,\n} from '@shipyard/local-runtime';\nimport { assertNever } from '../../shared/assert-never.js';\nimport {\n type AttemptState,\n decideAction,\n makeInitialAttemptState,\n} from '../../shared/file-watcher-guard.js';\nimport {\n type WorkerCommand,\n WorkerCommandCodec,\n type WorkerParcelEvent,\n type WorkerReply,\n WorkerReplyCodec,\n} from './worker-protocol.js';\n\n/**\n * Parent-side supervisor for the watcher worker subprocess. Owns the\n * Map<id, SubscriptionEntry> source of truth and replays it onto a fresh\n * worker after a crash, firing a synthetic 'evicted' event so consumers\n * can readdir-catch-up across the gap.\n *\n * Process lifecycle (spawn, peer attach, generation tokens, exit dispatch)\n * is delegated to `createServiceSupervisor` from @shipyard/local-runtime.\n * This module retains the watcher-specific concerns:\n * - subscription map + replay-on-respawn\n * - the file-watcher-guard-based half-open circuit (separate from the\n * primitive's simpler deaths-per-window circuit, which is opted out of)\n * - pendingRespawnGeneration accounting tied to subscribe-ack delivery\n * - synthetic 'evicted' eviction across the death gap\n * - SIGTERM → SIGKILL shutdown escalation\n *\n * The supervisor's restart-circuit (5 deaths in 60s -> 5min cooldown) is\n * SEPARATE from `file-watcher-guard.ts`'s per-path subscribe-failure\n * circuit. Both apply: a single bad path won't crash the worker, but a\n * worker that keeps SIGABRTing will eventually trip this layer.\n */\n\nexport interface WatcherWorkerEventLike {\n type: 'create' | 'update' | 'delete' | 'evicted';\n path: string;\n}\n\nexport type WatcherWorkerCallback = (\n err: Error | null,\n events: WatcherWorkerEventLike[]\n) => unknown;\n\nexport interface WatcherWorkerSubscribeOptions {\n ignore?: string[];\n backend?: 'default' | 'brute-force' | 'watchman';\n}\n\nexport interface WatcherWorkerSubscriptionHandle {\n unsubscribe(): Promise<void>;\n}\n\nexport interface WatcherWorkerSupervisor {\n /** Spawn the worker if not yet running. Idempotent. */\n start(): Promise<void>;\n subscribe(\n path: string,\n callback: WatcherWorkerCallback,\n opts: WatcherWorkerSubscribeOptions\n ): Promise<WatcherWorkerSubscriptionHandle>;\n /** Graceful shutdown. Sends shutdown command, escalates SIGTERM, then SIGKILL. */\n shutdown(): Promise<void>;\n}\n\nexport interface SupervisorMetrics {\n capture(eventType: string, properties: Record<string, unknown>): void;\n}\n\nexport type SupervisorLogger = (entry: { event: string; [key: string]: unknown }) => void;\n\nexport interface WatcherWorkerSupervisorDeps {\n readonly fork: (\n modulePath: string,\n args: string[],\n options: { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] }\n ) => ChildProcess;\n readonly workerPath: string;\n readonly log: SupervisorLogger;\n readonly metrics?: SupervisorMetrics;\n readonly now: () => number;\n readonly setTimeout: (fn: () => void, ms: number) => unknown;\n readonly clearTimeout: (timer: unknown) => void;\n}\n\n/** Empirically: SIGSEGV -> 139, SIGBUS -> 138, SIGABRT -> 134 on POSIX. */\nconst ABNORMAL_SIGNALS = new Set<NodeJS.Signals>(['SIGABRT', 'SIGSEGV', 'SIGBUS', 'SIGKILL']);\n\nconst SHUTDOWN_GRACEFUL_MS = 2_000;\nconst SHUTDOWN_SIGTERM_MS = 500;\n\ninterface SubscriptionEntry {\n id: string;\n path: string;\n opts: WatcherWorkerSubscribeOptions;\n callback: WatcherWorkerCallback;\n status: 'pending' | 'subscribed' | 'unsubscribing';\n resolveSubscribed?: (handle: WatcherWorkerSubscriptionHandle) => void;\n rejectSubscribed?: (err: Error) => void;\n resolveUnsubscribed?: () => void;\n}\n\nexport function createWatcherWorkerSupervisor(\n deps: WatcherWorkerSupervisorDeps\n): WatcherWorkerSupervisor {\n const subscriptions = new Map<string, SubscriptionEntry>();\n let restartState: AttemptState = makeInitialAttemptState();\n let liveGeneration = 0;\n let lastSpawnedAt = 0;\n let lastPid: number | null = null;\n let shutdownInitiated = false;\n let restartTimer: unknown = null;\n /**\n * Tracks the generation of a worker that was forked as a respawn and is\n * still awaiting its first `subscribed` ack. The ack from THAT generation\n * fires decideAction({kind:'subscribe_success'}) to close the cooldown\n * circuit. A boolean would mis-attribute: if worker B (respawn) dies before\n * acking and we respawn worker C, B's stale window would reset on C's first\n * ack — but C's ack should only count for C's own respawn (which it does,\n * because we re-set this on each respawn). More importantly, when B dies\n * before acking we MUST clear the pending generation so a future ack from\n * C is judged on its own respawn fork, not on the abandoned B window.\n */\n let pendingRespawnGeneration: number | null = null;\n let nextReplayStartedAt: number | null = null;\n\n function log(entry: { event: string; [key: string]: unknown }): void {\n deps.log(entry);\n }\n\n function captureMetric(eventType: string, properties: Record<string, unknown>): void {\n deps.metrics?.capture(eventType, properties);\n }\n\n /**\n * Adapt deps.fork to the primitive's ForkLike shape. ChildProcess\n * structurally satisfies StdioChildProcessLike; we just match the\n * argument signature (readonly args -> string[]).\n */\n const adaptedFork = (\n modulePath: string,\n args: readonly string[],\n options: { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] }\n ): StdioChildProcessLike => {\n return deps.fork(modulePath, Array.from(args), options);\n };\n\n const supervisor: ServiceSupervisor<WorkerCommand> = createServiceSupervisor<\n WorkerCommand,\n WorkerReply\n >({\n fork: adaptedFork,\n workerPath: deps.workerPath,\n commandCodec: WorkerCommandCodec,\n replyCodec: WorkerReplyCodec,\n onReply: (reply, fromGeneration) => dispatchReply(reply, fromGeneration),\n onChildSpawned: ({ generation, pid }) => {\n liveGeneration = generation;\n lastPid = pid;\n lastSpawnedAt = deps.now();\n log({ event: 'watcher_worker_started', pid });\n captureMetric('watcher_worker_started', { pid });\n },\n onChildExit: ({ code, signal, generation, uptimeMs }) => {\n handleChildExit(generation, code, signal, uptimeMs);\n },\n onDecodeFailed: ({ line }) => log({ event: 'watcher_worker_decode_failed', line }),\n onStderrLine: (line) => {\n if (!line.trim()) return;\n log({ event: 'watcher_worker_stderr', line });\n },\n onMissingStdout: () => log({ event: 'watcher_worker_no_stdout' }),\n onOverflow: ({ stream, bytes, maxBytes }) =>\n log({ event: 'watcher_worker_stdio_overflow', stream, bytes, maxBytes }),\n onPeerError: (err) => log({ event: 'watcher_worker_error', err: err.message }),\n log,\n now: deps.now,\n /** Watcher uses its own file-watcher-guard circuit; opt out of the primitive's. */\n });\n\n function sendCommand(cmd: WorkerCommand): boolean {\n const sent = supervisor.send(cmd);\n if (!sent && supervisor.getCurrentProcess() !== null) {\n log({\n event: 'watcher_worker_stdin_write_failed',\n err: 'stdio peer send returned false',\n });\n }\n return sent;\n }\n\n function dispatchReply(reply: WorkerReply, fromGeneration: number): void {\n if (fromGeneration !== liveGeneration) {\n log({\n event: 'watcher_worker_stale_reply_dropped',\n replyType: reply.type,\n id: reply.id,\n generation: fromGeneration,\n liveGeneration,\n });\n return;\n }\n switch (reply.type) {\n case 'subscribed':\n handleSubscribedReply(reply.id, fromGeneration);\n return;\n case 'subscribe_failed':\n handleSubscribeFailedReply(reply.id, reply.error);\n return;\n case 'unsubscribed':\n handleUnsubscribedReply(reply.id);\n return;\n case 'events':\n handleEventsReply(reply.id, reply.events);\n return;\n default:\n assertNever(reply);\n }\n }\n\n function handleSubscribedReply(id: string, fromGeneration: number): void {\n const entry = subscriptions.get(id);\n if (!entry) return;\n /**\n * Respawn-ack accounting (F4): the ack that closes the cooldown window\n * must come from the SAME generation we marked pending when respawning.\n * Pipe drains can deliver replies from a dead generation after a\n * successor is live, and only the original generation should consume\n * its own pending-ack window.\n */\n if (pendingRespawnGeneration !== null && fromGeneration === pendingRespawnGeneration) {\n pendingRespawnGeneration = null;\n const successDecision = decideAction(restartState, { kind: 'subscribe_success' }, deps.now());\n restartState = successDecision.state;\n for (const sig of successDecision.signals) {\n if (sig.kind === 'circuit_closed') {\n log({ event: 'watcher_worker_circuit_closed' });\n }\n }\n }\n if (entry.status === 'pending' && entry.resolveSubscribed) {\n entry.status = 'subscribed';\n entry.resolveSubscribed(makeHandle(entry));\n entry.resolveSubscribed = undefined;\n entry.rejectSubscribed = undefined;\n return;\n }\n entry.status = 'subscribed';\n }\n\n function handleSubscribeFailedReply(id: string, error: string): void {\n const entry = subscriptions.get(id);\n if (!entry) return;\n subscriptions.delete(id);\n entry.rejectSubscribed?.(new Error(error));\n }\n\n function handleUnsubscribedReply(id: string): void {\n const entry = subscriptions.get(id);\n if (!entry) return;\n subscriptions.delete(id);\n entry.resolveUnsubscribed?.();\n }\n\n function handleEventsReply(id: string, events: WorkerParcelEvent[]): void {\n const entry = subscriptions.get(id);\n if (!entry || entry.status === 'unsubscribing') return;\n entry.callback(null, toCallbackEvents(events));\n }\n\n function handleChildExit(\n generation: number,\n code: number | null,\n signal: NodeJS.Signals | null,\n uptimeMs: number\n ): void {\n /**\n * The primitive only fires onChildExit for the currently-live generation,\n * so this always matches `liveGeneration`. After this returns, the\n * primitive has set its internal live to null.\n */\n if (pendingRespawnGeneration === generation) {\n pendingRespawnGeneration = null;\n }\n\n const replayCount = subscriptions.size;\n\n if (shutdownInitiated) {\n log({ event: 'watcher_worker_exited_during_shutdown', code, signal, uptimeMs });\n return;\n }\n\n const abnormal = isAbnormalExit(code, signal) || replayCount > 0;\n if (!abnormal) {\n log({ event: 'watcher_worker_exited_clean', code, signal, uptimeMs });\n return;\n }\n\n log({ event: 'watcher_worker_died', code, signal, replayCount, uptimeMs });\n captureMetric('watcher_worker_died', {\n code,\n signal,\n replayCount,\n uptimeMs,\n });\n\n recordRestartFailure(uptimeMs);\n\n /** Fire synthetic 'evicted' so consumers know there was a gap. */\n fireSyntheticEvictionToAll();\n\n /** Schedule a respawn unless the circuit is open. */\n scheduleRespawn();\n }\n\n /**\n * Reuse the guard's pure restart core: a 'subscribe_failure' event drives\n * the same backoff/circuit logic per-process. activeCount=0 because the\n * supervisor has its own slot, not a shared FSEvents budget.\n */\n function recordRestartFailure(uptimeMs: number): void {\n const failureDecision = decideAction(restartState, { kind: 'subscribe_failure' }, deps.now());\n restartState = failureDecision.state;\n for (const sig of failureDecision.signals) {\n if (sig.kind === 'circuit_opened') {\n log({ event: 'watcher_worker_circuit_open' });\n captureMetric('watcher_worker_circuit_open', {\n deathCount: restartState.failureCount,\n windowMs: uptimeMs,\n });\n } else if (sig.kind === 'circuit_closed') {\n log({ event: 'watcher_worker_circuit_closed' });\n }\n }\n }\n\n function dispatchSyntheticEviction(entry: SubscriptionEntry): void {\n if (!subscriptions.has(entry.id)) return;\n try {\n entry.callback(null, [{ type: 'evicted', path: entry.path }]);\n } catch (err) {\n log({\n event: 'watcher_worker_synthetic_dispatch_failed',\n id: entry.id,\n err: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n function fireSyntheticEvictionToAll(): void {\n /**\n * Re-entry safety (Invariant #11): defer dispatch to a fresh microtask\n * so a callback that re-enters supervisor.subscribe() cannot do so from\n * inside the exit-handler frame.\n */\n const snapshot = Array.from(subscriptions.values());\n queueMicrotask(() => {\n for (const entry of snapshot) {\n dispatchSyntheticEviction(entry);\n }\n });\n }\n\n function scheduleRespawn(): void {\n if (shutdownInitiated) return;\n /**\n * Idempotency: pipe-failure and on-exit can both fire scheduleRespawn\n * for the same death. Without this guard both timers fire and the\n * second one runs forkAndReplay while a worker is already alive.\n */\n if (restartTimer !== null) return;\n\n /**\n * Always route through `decideAction({kind:'request_subscribe'})` so the\n * pure core handles half-open transition: when the cooldown expires,\n * `decideRequest` sets `halfOpenAt = now` and returns 'subscribe'.\n */\n const requestDecision = decideAction(\n restartState,\n { kind: 'request_subscribe', reason: 'rescan', activeCount: 0 },\n deps.now()\n );\n restartState = requestDecision.state;\n\n switch (requestDecision.action.kind) {\n case 'reject_stub': {\n const openedAt = restartState.circuitOpenedAt;\n if (openedAt === null) {\n attemptRespawn();\n return;\n }\n const cooldownEnd = openedAt + restartState.circuitCooldownMs;\n const waitMs = Math.max(0, cooldownEnd - deps.now());\n restartTimer = deps.setTimeout(() => {\n restartTimer = null;\n scheduleRespawn();\n }, waitMs);\n return;\n }\n case 'wait': {\n restartTimer = deps.setTimeout(() => {\n restartTimer = null;\n attemptRespawn();\n }, requestDecision.action.ms);\n return;\n }\n case 'subscribe':\n case 'evict_and_subscribe':\n attemptRespawn();\n return;\n case 'noop':\n attemptRespawn();\n return;\n default:\n assertNever(requestDecision.action);\n }\n }\n\n function attemptRespawn(): void {\n if (shutdownInitiated) return;\n if (restartState.circuitOpenedAt !== null) {\n log({ event: 'watcher_worker_probe_attempt' });\n }\n try {\n forkAndReplay();\n } catch (err) {\n log({\n event: 'watcher_worker_fork_failed',\n err: err instanceof Error ? err.message : String(err),\n });\n const failureDecision = decideAction(restartState, { kind: 'subscribe_failure' }, deps.now());\n restartState = failureDecision.state;\n scheduleRespawn();\n }\n }\n\n /**\n * Replay outcome: either we sent every entry (`completed`) or `sendCommand`\n * returned false partway through (`pipe_failed`). The latter means the\n * fresh worker's pipe is already dead; we abandon this generation and\n * trigger another respawn so the next fresh worker gets a clean replay.\n */\n type ReplayResult =\n | { kind: 'completed'; replayedCount: number }\n | { kind: 'pipe_failed'; replayedCount: number };\n\n function forkAndReplay(): void {\n const isRespawn = liveGeneration >= 1;\n nextReplayStartedAt = deps.now();\n /**\n * Whether the spawn we're about to perform is a respawn that should\n * close the circuit on its first ack. We snapshot here BEFORE start/\n * restart fires onChildSpawned (which bumps liveGeneration).\n */\n const shouldMarkPendingRespawn = isRespawn && subscriptions.size > 0;\n\n if (liveGeneration === 0) {\n supervisor.start();\n } else {\n supervisor.restart();\n }\n\n if (shouldMarkPendingRespawn) {\n pendingRespawnGeneration = liveGeneration;\n }\n\n const replayResult = replaySubscriptions();\n\n if (replayResult.kind === 'pipe_failed') {\n handleReplayPipeFailure(replayResult.replayedCount);\n return;\n }\n if (replayResult.replayedCount > 0) {\n log({ event: 'watcher_worker_replayed', replayCount: replayResult.replayedCount });\n }\n if (isRespawn) {\n recordRespawnTelemetry(\n replayResult.replayedCount,\n deps.now() - (nextReplayStartedAt ?? deps.now())\n );\n }\n }\n\n function replaySubscriptions(): ReplayResult {\n let replayedCount = 0;\n for (const entry of subscriptions.values()) {\n /**\n * Reset to 'pending' so the new {type:'subscribed'} reply hits the\n * resolution path. In-flight subscribers from before the death keep\n * their resolve/reject closures and resolve when the new worker\n * acknowledges. New subscribers added after replay get appended.\n */\n entry.status = 'pending';\n const ok = sendCommand({\n cmd: 'subscribe',\n id: entry.id,\n path: entry.path,\n opts: entry.opts,\n });\n if (!ok) {\n return { kind: 'pipe_failed', replayedCount };\n }\n replayedCount++;\n }\n return { kind: 'completed', replayedCount };\n }\n\n function handleReplayPipeFailure(replayedCount: number): void {\n log({\n event: 'watcher_worker_replay_pipe_failed',\n pid: lastPid,\n replayedCount,\n pendingCount: subscriptions.size - replayedCount,\n });\n /**\n * Count this as a failure for the restart-circuit. Without it, a\n * pathological environment where every fresh worker's stdin pipe is\n * dead-on-arrival would respawn forever — `failureCount` never\n * increments because the on-exit failure path requires an exit signal.\n */\n recordRestartFailure(deps.now() - lastSpawnedAt);\n /**\n * Pipe death usually means the worker has already exited or its stdin\n * was closed underneath us. Kill the underlying process so onExit fires\n * and the standard respawn cycle takes over.\n */\n const proc = supervisor.getCurrentProcess();\n if (proc !== null) {\n try {\n proc.kill('SIGKILL');\n } catch (err) {\n log({\n event: 'watcher_worker_kill_failed',\n signal: 'SIGKILL',\n err: err instanceof Error ? err.message : String(err),\n });\n }\n }\n scheduleRespawn();\n }\n\n function recordRespawnTelemetry(replayCount: number, replayDurationMs: number): void {\n log({\n event: 'watcher_worker_respawned',\n pid: lastPid,\n replayCount,\n replayDurationMs,\n });\n captureMetric('watcher_worker_respawned', {\n newPid: lastPid,\n replayCount,\n replayDurationMs,\n });\n }\n\n function makeHandle(entry: SubscriptionEntry): WatcherWorkerSubscriptionHandle {\n return {\n unsubscribe: () => unsubscribeEntry(entry),\n };\n }\n\n function unsubscribeEntry(entry: SubscriptionEntry): Promise<void> {\n if (!subscriptions.has(entry.id)) return Promise.resolve();\n entry.status = 'unsubscribing';\n if (supervisor.getCurrentProcess() === null) {\n /** No worker -> just drop the entry; the next worker won't replay it. */\n subscriptions.delete(entry.id);\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n entry.resolveUnsubscribed = resolve;\n const sent = sendCommand({ cmd: 'unsubscribe', id: entry.id });\n if (!sent) {\n /** Pipe gone — drop the entry so we don't leak. */\n subscriptions.delete(entry.id);\n resolve();\n }\n });\n }\n\n function makeStubHandle(): WatcherWorkerSubscriptionHandle {\n return { unsubscribe: async () => {} };\n }\n\n async function start(): Promise<void> {\n if (supervisor.getCurrentProcess() !== null) return;\n if (shutdownInitiated) {\n throw new Error('WatcherWorkerSupervisor: cannot start after shutdown');\n }\n forkAndReplay();\n }\n\n async function subscribe(\n path: string,\n callback: WatcherWorkerCallback,\n opts: WatcherWorkerSubscribeOptions\n ): Promise<WatcherWorkerSubscriptionHandle> {\n if (shutdownInitiated) {\n throw new Error('WatcherWorkerSupervisor: cannot subscribe after shutdown');\n }\n /**\n * Honor the supervisor's own circuit. Stub subscriptions still occupy a\n * slot in the map only briefly — we don't add to subscriptions on the\n * stub path, so a future probe doesn't try to replay them.\n */\n if (restartState.circuitOpenedAt !== null) {\n const cooldownEnd = restartState.circuitOpenedAt + restartState.circuitCooldownMs;\n if (deps.now() < cooldownEnd) {\n log({ event: 'watcher_worker_subscribe_stubbed', path });\n return makeStubHandle();\n }\n }\n\n const id = randomUUID();\n const entry: SubscriptionEntry = {\n id,\n path,\n opts,\n callback,\n status: 'pending',\n };\n const promise = new Promise<WatcherWorkerSubscriptionHandle>((resolve, reject) => {\n entry.resolveSubscribed = resolve;\n entry.rejectSubscribed = reject;\n });\n subscriptions.set(id, entry);\n\n if (supervisor.getCurrentProcess() === null) {\n await start();\n } else {\n sendCommand({ cmd: 'subscribe', id, path, opts });\n }\n return promise;\n }\n\n async function shutdown(): Promise<void> {\n if (shutdownInitiated) return;\n shutdownInitiated = true;\n if (restartTimer !== null) {\n deps.clearTimeout(restartTimer);\n restartTimer = null;\n }\n\n /**\n * Reject in-flight subscribes AND resolve pending unsubscribes (F5).\n * A consumer awaiting `handle.unsubscribe()` at shutdown-time would\n * otherwise hang forever.\n */\n for (const entry of subscriptions.values()) {\n entry.rejectSubscribed?.(new Error('WatcherWorkerSupervisor: shutdown'));\n entry.rejectSubscribed = undefined;\n entry.resolveSubscribed = undefined;\n entry.resolveUnsubscribed?.();\n entry.resolveUnsubscribed = undefined;\n }\n subscriptions.clear();\n\n const proc = supervisor.getCurrentProcess();\n supervisor.dispose();\n if (proc === null) return;\n\n sendCommand({ cmd: 'shutdown' });\n\n await escalateShutdown(proc);\n }\n\n function killWithSignal(proc: StdioChildProcessLike, signal: 'SIGTERM' | 'SIGKILL'): void {\n log({\n event:\n signal === 'SIGTERM'\n ? 'watcher_worker_shutdown_sigterm'\n : 'watcher_worker_shutdown_sigkill',\n });\n try {\n proc.kill(signal);\n } catch (err) {\n log({\n event: 'watcher_worker_kill_failed',\n signal,\n err: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n function escalateShutdown(proc: StdioChildProcessLike): Promise<void> {\n return new Promise<void>((resolve) => {\n const ctx = { done: false, exited: false };\n const finish = () => {\n if (ctx.done) return;\n ctx.done = true;\n resolve();\n };\n\n proc.once('exit', () => {\n ctx.exited = true;\n finish();\n });\n\n deps.setTimeout(() => onSigtermDeadline(proc, ctx, finish), SHUTDOWN_GRACEFUL_MS);\n });\n }\n\n function onSigtermDeadline(\n proc: StdioChildProcessLike,\n ctx: { done: boolean; exited: boolean },\n finish: () => void\n ): void {\n if (ctx.done || ctx.exited) return;\n killWithSignal(proc, 'SIGTERM');\n deps.setTimeout(() => onSigkillDeadline(proc, ctx, finish), SHUTDOWN_SIGTERM_MS);\n }\n\n function onSigkillDeadline(\n proc: StdioChildProcessLike,\n ctx: { done: boolean; exited: boolean },\n finish: () => void\n ): void {\n if (ctx.done || ctx.exited) return;\n killWithSignal(proc, 'SIGKILL');\n finish();\n }\n\n return { start, subscribe, shutdown };\n}\n\nfunction isAbnormalExit(code: number | null, signal: NodeJS.Signals | null): boolean {\n if (signal !== null && ABNORMAL_SIGNALS.has(signal)) return true;\n if (code !== null && code !== 0) return true;\n return false;\n}\n\nfunction toCallbackEvents(events: WorkerParcelEvent[]): WatcherWorkerEventLike[] {\n return events.map((e) => ({ type: e.type, path: e.path }));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
} from "./chunk-RR6V6SNM.js";
|
|
6
6
|
import {
|
|
7
7
|
logger
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LRNGLC4V.js";
|
|
9
9
|
import {
|
|
10
10
|
isClaudeCodeKeychainReadEnabled
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-P2HZDIN7.js";
|
|
12
12
|
import {
|
|
13
13
|
external_exports
|
|
14
14
|
} from "./chunk-CNR7O5YH.js";
|
|
@@ -1391,7 +1391,7 @@ async function detectMCPServers(environments, tokenStore, log, lastKnown, prefer
|
|
|
1391
1391
|
lastKnown
|
|
1392
1392
|
);
|
|
1393
1393
|
} catch (err) {
|
|
1394
|
-
const { logger: logger2 } = await import("./logger-
|
|
1394
|
+
const { logger: logger2 } = await import("./logger-AN7EUK2B.js");
|
|
1395
1395
|
if (lastKnown && lastKnown.length > 0) {
|
|
1396
1396
|
logger2.warn(
|
|
1397
1397
|
{ err, lastKnownCount: lastKnown.length },
|
|
@@ -1589,4 +1589,4 @@ export {
|
|
|
1589
1589
|
detectCodexPluginMcps,
|
|
1590
1590
|
detectMCPServers
|
|
1591
1591
|
};
|
|
1592
|
-
//# sourceMappingURL=chunk-
|
|
1592
|
+
//# sourceMappingURL=chunk-KYLYGFMH.js.map
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getShipyardHome,
|
|
4
4
|
validateEnv
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-P2HZDIN7.js";
|
|
6
6
|
|
|
7
7
|
// src/shared/logger.ts
|
|
8
8
|
import { mkdirSync } from "fs";
|
|
9
|
+
import { readdir, stat, unlink } from "fs/promises";
|
|
9
10
|
import { join } from "path";
|
|
10
11
|
import pino from "pino";
|
|
11
12
|
function shouldAttachFileTransport(env) {
|
|
@@ -17,6 +18,33 @@ function shouldAttachFileTransport(env) {
|
|
|
17
18
|
}
|
|
18
19
|
return true;
|
|
19
20
|
}
|
|
21
|
+
var LOG_RETENTION_DAYS = 7;
|
|
22
|
+
function shouldDeleteLogFile(fileName, mtimeMs, nowMs) {
|
|
23
|
+
if (!/^daemon\.log\.\d+$/.test(fileName)) return false;
|
|
24
|
+
return mtimeMs < nowMs - LOG_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
25
|
+
}
|
|
26
|
+
async function cleanOldLogs(dir) {
|
|
27
|
+
try {
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
const files = await readdir(dir);
|
|
30
|
+
const rotated = files.filter((file) => /^daemon\.log\.\d+$/.test(file));
|
|
31
|
+
const indexOf = (file) => Number(file.split(".").at(-1) ?? "0");
|
|
32
|
+
const maxIndex = rotated.reduce((max, file) => Math.max(max, indexOf(file)), 0);
|
|
33
|
+
await Promise.allSettled(
|
|
34
|
+
rotated.filter((file) => indexOf(file) < maxIndex).map(async (file) => {
|
|
35
|
+
try {
|
|
36
|
+
const filePath = join(dir, file);
|
|
37
|
+
const stats = await stat(filePath);
|
|
38
|
+
if (shouldDeleteLogFile(file, stats.mtimeMs, now)) {
|
|
39
|
+
await unlink(filePath);
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
20
48
|
var attachFileTransport = shouldAttachFileTransport({
|
|
21
49
|
VITEST: process.env.VITEST,
|
|
22
50
|
NODE_ENV: process.env.NODE_ENV
|
|
@@ -24,6 +52,7 @@ var attachFileTransport = shouldAttachFileTransport({
|
|
|
24
52
|
var logDir = join(getShipyardHome(), "logs");
|
|
25
53
|
if (attachFileTransport) {
|
|
26
54
|
mkdirSync(logDir, { recursive: true, mode: 448 });
|
|
55
|
+
void cleanOldLogs(logDir);
|
|
27
56
|
}
|
|
28
57
|
var logFilePath = join(logDir, "daemon.log");
|
|
29
58
|
function getLogFilePath() {
|
|
@@ -39,7 +68,15 @@ var transport = pino.transport({
|
|
|
39
68
|
target: "pino-roll",
|
|
40
69
|
options: {
|
|
41
70
|
file: logFilePath,
|
|
42
|
-
|
|
71
|
+
/*
|
|
72
|
+
* Size-based rotation (not frequency-based): pino-roll's daily frequency
|
|
73
|
+
* uses birthtime filtering in detectLastNumber, which causes every daemon
|
|
74
|
+
* restart to re-open daemon.log.1 (old files predate today's midnight).
|
|
75
|
+
* Without a frequency, detectLastNumber has no time filter and correctly
|
|
76
|
+
* identifies the highest-numbered existing file to continue writing there.
|
|
77
|
+
*/
|
|
78
|
+
size: "100m",
|
|
79
|
+
// NOTE: per-session cap only — cross-restart retention is owned by cleanOldLogs above
|
|
43
80
|
limit: { count: 7 },
|
|
44
81
|
mkdir: true
|
|
45
82
|
},
|
|
@@ -68,9 +105,10 @@ function flushLogger(timeoutMs = 2e3) {
|
|
|
68
105
|
|
|
69
106
|
export {
|
|
70
107
|
shouldAttachFileTransport,
|
|
108
|
+
shouldDeleteLogFile,
|
|
71
109
|
getLogFilePath,
|
|
72
110
|
logger,
|
|
73
111
|
createChildLogger,
|
|
74
112
|
flushLogger
|
|
75
113
|
};
|
|
76
|
-
//# sourceMappingURL=chunk-
|
|
114
|
+
//# sourceMappingURL=chunk-LRNGLC4V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/logger.ts"],"sourcesContent":["import { mkdirSync } from 'node:fs';\nimport { readdir, stat, unlink } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport pino from 'pino';\nimport { getShipyardHome, validateEnv } from './env.js';\n\n/** Whether the rolling file transport should attach (suppressed under vitest / NODE_ENV=test). */\nexport function shouldAttachFileTransport(env: { VITEST?: string; NODE_ENV?: string }): boolean {\n if (env.VITEST !== undefined) {\n return false;\n }\n if (env.NODE_ENV === 'test') {\n return false;\n }\n return true;\n}\n\nconst LOG_RETENTION_DAYS = 7;\n\n/**\n * Pure decision: should a rotated daemon log file be deleted?\n * fileName must be a basename (e.g. \"daemon.log.1\"), not a full path.\n * Exported for testing — no I/O.\n */\nexport function shouldDeleteLogFile(fileName: string, mtimeMs: number, nowMs: number): boolean {\n if (!/^daemon\\.log\\.\\d+$/.test(fileName)) return false;\n return mtimeMs < nowMs - LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000;\n}\n\nasync function cleanOldLogs(dir: string): Promise<void> {\n try {\n const now = Date.now();\n const files = await readdir(dir);\n const rotated = files.filter((file) => /^daemon\\.log\\.\\d+$/.test(file));\n /*\n * Skip the highest-indexed file: pino-roll's transport worker selects it\n * as the continuation target and may have an open fd on it before this\n * sweep completes. Files with lower indices are safe to prune.\n */\n const indexOf = (file: string): number => Number(file.split('.').at(-1) ?? '0');\n const maxIndex = rotated.reduce((max, file) => Math.max(max, indexOf(file)), 0);\n await Promise.allSettled(\n rotated\n .filter((file) => indexOf(file) < maxIndex)\n .map(async (file) => {\n try {\n const filePath = join(dir, file);\n const stats = await stat(filePath);\n if (shouldDeleteLogFile(file, stats.mtimeMs, now)) {\n await unlink(filePath);\n }\n } catch {\n // NOTE: ENOENT/EACCES expected — file may be concurrently deleted or temporarily unreadable\n }\n })\n );\n } catch {\n // NOTE: readdir failure is swallowed so log cleanup never blocks daemon startup\n }\n}\n\nconst attachFileTransport = shouldAttachFileTransport({\n VITEST: process.env.VITEST,\n NODE_ENV: process.env.NODE_ENV,\n});\n\nconst logDir = join(getShipyardHome(), 'logs');\nif (attachFileTransport) {\n mkdirSync(logDir, { recursive: true, mode: 0o700 });\n void cleanOldLogs(logDir);\n}\n\nconst logFilePath = join(logDir, 'daemon.log');\n\n/** Exposed for output.ts to append CLI output to the same log file. */\nexport function getLogFilePath(): string {\n return logFilePath;\n}\n\nconst isDev = process.env.NODE_ENV !== 'production';\nconst level = validateEnv().LOG_LEVEL;\n\nconst transport = pino.transport({\n targets: [\n ...(isDev\n ? [{ target: 'pino-pretty', options: { colorize: true }, level }]\n : [{ target: 'pino/file', options: { destination: 1 }, level }]),\n ...(attachFileTransport\n ? [\n {\n target: 'pino-roll',\n options: {\n file: logFilePath,\n /*\n * Size-based rotation (not frequency-based): pino-roll's daily frequency\n * uses birthtime filtering in detectLastNumber, which causes every daemon\n * restart to re-open daemon.log.1 (old files predate today's midnight).\n * Without a frequency, detectLastNumber has no time filter and correctly\n * identifies the highest-numbered existing file to continue writing there.\n */\n size: '100m',\n // NOTE: per-session cap only — cross-restart retention is owned by cleanOldLogs above\n limit: { count: 7 },\n mkdir: true,\n },\n level,\n },\n ]\n : []),\n ],\n});\n\n/**\n * Pino's error serializer only triggers on the `err` key (not `error`).\n * Always use `{ err: myError }` when logging Error objects.\n * See: https://github.com/pinojs/pino/issues/895\n */\nexport const logger = pino({ level }, transport);\n\nexport function createChildLogger(context: { taskId?: string; sessionId?: string; mode?: string }) {\n return logger.child(context);\n}\n\n/**\n * Flush pino's async transport buffer and wait for it to drain.\n * Must be called before process.exit() to avoid losing buffered log entries.\n * Times out after the given ms to avoid hanging on a stuck transport.\n */\nexport function flushLogger(timeoutMs = 2_000): Promise<void> {\n return new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, timeoutMs);\n logger.flush((err) => {\n clearTimeout(timer);\n if (err) {\n /** Best-effort: if flush fails, we still exit */\n process.stderr.write(`Logger flush error: ${err.message}\\n`);\n }\n resolve();\n });\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -441,6 +441,23 @@ var RunnerToDaemonSchema = external_exports.discriminatedUnion("kind", [
|
|
|
441
441
|
generation: external_exports.number().int().nonnegative(),
|
|
442
442
|
sessionId: external_exports.string()
|
|
443
443
|
}),
|
|
444
|
+
/**
|
|
445
|
+
* Runner→daemon liveness edge for in-flight tool execution. Emitted only
|
|
446
|
+
* on the 0→1 transition (first tool call started, phase `'started'`) and
|
|
447
|
+
* the 1→0 transition (last tool call completed, phase `'settled'`). Drives
|
|
448
|
+
* the daemon's `toolExecutionStallTimeoutMs` window: when a tool is
|
|
449
|
+
* in-flight the stall watchdog uses the extended per-tool budget so a
|
|
450
|
+
* legitimate long-running tool (e.g. `git commit` with a full `pnpm check`
|
|
451
|
+
* pre-commit hook, 3–10 min) is not false-killed. On settle the watchdog
|
|
452
|
+
* reverts to the shorter mid-run window. Stale-generation filtering applies.
|
|
453
|
+
*
|
|
454
|
+
* Daemon↔forked-child IPC only — NOT the browser wire. No PROTOCOL_VERSION bump.
|
|
455
|
+
*/
|
|
456
|
+
external_exports.object({
|
|
457
|
+
kind: external_exports.literal("tool_execution"),
|
|
458
|
+
generation: external_exports.number().int().nonnegative(),
|
|
459
|
+
phase: external_exports.enum(["started", "settled"])
|
|
460
|
+
}),
|
|
444
461
|
/**
|
|
445
462
|
* Warm-pool reinit succeeded (C9). The runner created a fresh Agent for the
|
|
446
463
|
* claimed task on the warm executor. `sessionId` is the new `agent.agentId`
|
|
@@ -486,4 +503,4 @@ export {
|
|
|
486
503
|
parseDaemonToRunner,
|
|
487
504
|
parseRunnerToDaemon
|
|
488
505
|
};
|
|
489
|
-
//# sourceMappingURL=chunk-
|
|
506
|
+
//# sourceMappingURL=chunk-LZSMNUAI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/session/cursor-runner-protocol.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * IPC protocol between the daemon (parent) and the `cursor-runner` forked\n * child subprocess.\n *\n * Wire format: structured-clone over the Node IPC channel\n * (`child.send(msg)` / `process.on('message', msg => ...)`). The schemas\n * below are Zod-parsed on receive so a corrupt or version-skewed peer\n * cannot drive the recipient into an undefined state.\n *\n * Stdout/stderr of the runner are inherited from the daemon (see S2b's\n * `child_process.fork({ stdio: 'inherit', silent: false })`) — those are\n * NOT carrying IPC traffic, they are for human-readable logs and native\n * panic stack traces.\n *\n * `generation` is a per-turn monotonic counter minted by the daemon-side\n * adapter. Every per-turn message in either direction is stamped with the\n * value live at the time of mint; the daemon drops inbound events whose\n * generation does not match the current run (closes Invariant #16 — the\n * transport discriminator must be present at every layer). The runner\n * does NOT drop on its own — it echoes whatever generation the daemon\n * stamped, so the daemon can correlate.\n *\n * Lifecycle events that are not per-turn (`init`, `init_ok`,\n * `init_error`, `log`, `close`) carry no generation.\n *\n * MCP server config shape: `Record<string, unknown>` here is intentional.\n * We keep the wire schema decoupled from `@cursor/sdk`'s `McpServerConfig`\n * to avoid coupling the daemon and runner to the same exact SDK version\n * at the type level. The runner re-validates against the SDK shape at\n * the call site.\n */\n\nconst McpServersSchema = z.record(z.string(), z.unknown());\nconst CursorResolvedSkillBodySchema = z.object({\n name: z.string(),\n namespace: z.string().optional(),\n body: z.string(),\n});\n\n/**\n * DaemonToRunner — outbound from the daemon-side `CursorAgentSubprocess`\n * adapter to the forked runner child.\n */\nexport const DaemonToRunnerSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('init'),\n taskId: z.string(),\n cwd: z.string(),\n apiKey: z.string(),\n modelId: z.string(),\n mcpServers: McpServersSchema,\n harnessUrl: z.string(),\n harnessToken: z.string(),\n /**\n * Stable per-runner identity for the runner-scoped harness path (C8/C9\n * warm pool). The runner puts this in the `x-shipyard-runner-id` header\n * (instead of the per-task `x-shipyard-task-id`) so the harness MCP-server\n * config — and therefore the SDK's process-scoped executor cache key — is\n * byte-identical across every task the runner serves. Optional/additive:\n * a pre-pool daemon omits it, and the runner falls back to the legacy\n * `x-shipyard-task-id` header keyed on `taskId`.\n */\n harnessRunnerId: z.string().optional(),\n /**\n * When true, the runner is pool-managed: on `close` it disposes the Agent\n * but does NOT `process.exit(0)` — it resets to a quiescent state and acks\n * `pooled_idle` so the daemon can keep the warm process (and its\n * `localExecutorCache`) alive for the next matching task. Optional/additive\n * — absent means \"throwaway runner, exit on close\" (legacy behavior).\n */\n pooled: z.boolean().optional(),\n /**\n * Absolute filesystem path where skill files for this task are\n * written (see plan v2 E#V7). The runner does not enumerate the\n * directory — it just forwards the path to the SDK so any future\n * skills-loading code has the location. May be empty string if\n * skills are disabled for this run.\n */\n skillsDir: z.string(),\n sessionId: z.string(),\n generation: z.number().int().nonnegative(),\n /**\n * Cross-runtime structured handoff (Wave 7.5). When set, the daemon\n * already wrote a fabricated `ConversationStateStructure` Merkle tree\n * to the SDK's SQLite stores during the pre-fork exclusive write\n * window. The runner MUST call `Agent.resume(injectedAgentId, ...)`\n * instead of `Agent.create(...)` so the SDK reads back the prior\n * conversation as context. Same-id `Agent.create()` would\n * PRIMARY-KEY-conflict on the agents row.\n *\n * Daemon↔forked-child IPC, NOT WebRTC — no PROTOCOL_VERSION bump.\n */\n injectedAgentId: z.string().optional(),\n /**\n * Persisted Cursor agentId from a prior `init_ok`. When set, the runner\n * calls `Agent.resume(resumeAgentId, ...)` instead of `Agent.create()` —\n * SDK reads its SQLite checkpoint store keyed by agentId and brings up\n * the prior conversation without rebuilding from scratch. Cold-build\n * cost (Statsig bootstrap + workspace runtime + MCP startup) is still\n * paid; the win is preserving conversation context across daemon\n * restarts and not depending on the JSONL replay path for that.\n *\n * Distinct from `injectedAgentId`: that one is for cross-runtime\n * structured handoff where the daemon fabricates a fake conversation;\n * this one is just rejoining a previously-real conversation. Resume\n * failures (agentId not in the SDK's local store, e.g. user wiped\n * `~/.cursor/`) fall back to `Agent.create` with the same options so\n * the spawn succeeds — just without prior context.\n */\n resumeAgentId: z.string().optional(),\n /**\n * Initial Fast Mode flag from `composerSettings.fastMode`. When true\n * AND `fastModeParam` is non-null, the runner attaches the param to\n * every `agent.send()` via `SendOptions.model.params`. When false\n * (or `fastModeParam` is null), the runner sends the plain\n * `agent.send(message)` shape. Optional/additive — pre-Fast-Mode\n * daemons omit the field and the runner treats it as false.\n */\n fastMode: z.boolean().optional(),\n /**\n * Resolved Fast Mode binding for `modelId`, sourced from the\n * daemon's `cursor-models-cache`. `null` means \"the cache has no\n * Fast-Mode binding for this model right now\" (unknown id, refresh\n * failed, or the model declares no Fast-Mode-shaped parameter).\n * The runner treats both `null` and `undefined` as \"no binding,\n * never attach params\" — gating Fast Mode the same way regardless\n * of why it's missing.\n */\n fastModeParam: z.object({ id: z.string(), value: z.string() }).nullable().optional(),\n /**\n * Named subagent catalog rendered for the Cursor runtime. When present,\n * the runner passes this to `Agent.create({agents,...})` and\n * `Agent.resume({agents,...})` so Cursor's built-in `task` tool can\n * spawn named subagents from Shipyard's catalog. Optional/additive —\n * pre-catalog daemons omit the field; the runner treats absence as an\n * empty catalog (no named subagents declared to the SDK).\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No\n * PROTOCOL_VERSION bump.\n */\n agents: z\n .record(\n z.string(),\n z.object({\n description: z.string(),\n prompt: z.string(),\n model: z.union([z.object({ id: z.string() }), z.literal('inherit')]).optional(),\n mcpServers: z.array(z.string()).optional(),\n })\n )\n .optional(),\n /**\n * Native Cursor SDK conversation mode for this agent. When `'plan'`, the\n * SDK tells composer-2.5 to call its built-in `createPlan` tool instead of\n * writing files. This is the SOFT signal complementing the hard-enforcement\n * layer in `.cursor/hooks.json` deny-writes. Optional/additive — pre-1.0.17\n * runners omit it and the SDK defaults to `'agent'` mode.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No\n * PROTOCOL_VERSION bump required.\n */\n mode: z.enum(['agent', 'plan']).optional(),\n }),\n z.object({\n kind: z.literal('push_message'),\n generation: z.number().int().nonnegative(),\n /**\n * Content blocks the daemon wants to send as a user turn. Shape is\n * Shipyard's internal `ContentBlock[]` — the runner translates to\n * Cursor's `SDKUserMessage` at the boundary (see cursor-content-builder.ts).\n *\n * Image attachments ride as `resource` blocks with `BlobResourceContents`\n * (mime + base64 blob). The runner extracts these into `SDKImage[]` and\n * attaches them to the `SDKUserMessage` it sends to `Agent.send()`.\n */\n content: z.array(z.unknown()),\n /**\n * SKILL.md bodies resolved by the daemon from the trusted capabilities\n * skill index for this turn. The runner must use these bodies instead of\n * any path carried on browser-authored `skill_invocation` content blocks.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No\n * PROTOCOL_VERSION bump required.\n */\n skillBodies: z.array(CursorResolvedSkillBodySchema).optional(),\n /**\n * Cursor SDK local recovery flag. Only the daemon-side AgentBusyError\n * retry path sets this after confirming the errored generation is still\n * current; stale-generation retries are dropped before reaching the\n * runner, so this must never expire a legitimately live successor run.\n */\n forceLocalRun: z.boolean().optional(),\n }),\n z.object({\n kind: z.literal('interrupt'),\n generation: z.number().int().nonnegative(),\n }),\n z.object({\n kind: z.literal('set_model'),\n generation: z.number().int().nonnegative(),\n modelId: z.string(),\n }),\n z.object({\n kind: z.literal('set_mcp_servers'),\n generation: z.number().int().nonnegative(),\n servers: McpServersSchema,\n }),\n z.object({\n kind: z.literal('set_harness_task_id'),\n generation: z.number().int().nonnegative(),\n taskId: z.string(),\n token: z.string(),\n }),\n /**\n * Warm-pool reinit (C9). A pooled runner whose prior task closed is being\n * claimed for a NEW task. The runner cancels any active run, disposes the\n * current Agent, updates its state to the new task, and creates a fresh\n * Agent (`Agent.resume(resumeAgentId)` if present, else `Agent.create`) —\n * reusing the warm process-scoped executor cache (cache HIT on the next\n * send, because the harness MCP entry uses the STABLE `harnessRunnerId`\n * header so `mcpServers` is byte-identical to the prior task's).\n *\n * On success → `reinit_ok { sessionId }`; on ANY throw → `reinit_error`\n * and the runner does NOT exit (the daemon kills + respawns fresh). This\n * is the fail-closed contract: a broken reinit becomes a cold spawn, never\n * a stale/cross-task state leak.\n *\n * Carries the full per-task spawn surface (everything the cache key and the\n * Agent depend on). `mcpServers` here MUST NOT include the harness entry —\n * the runner re-adds it from `harnessRunnerId`/`harnessToken` so the entry\n * is byte-identical across init→reinit.\n */\n z.object({\n kind: z.literal('reinit'),\n taskId: z.string(),\n modelId: z.string(),\n mcpServers: McpServersSchema,\n harnessRunnerId: z.string(),\n harnessToken: z.string(),\n skillsDir: z.string(),\n generation: z.number().int().nonnegative(),\n resumeAgentId: z.string().optional(),\n fastMode: z.boolean().optional(),\n fastModeParam: z.object({ id: z.string(), value: z.string() }).nullable().optional(),\n agents: z\n .record(\n z.string(),\n z.object({\n description: z.string(),\n prompt: z.string(),\n model: z.union([z.object({ id: z.string() }), z.literal('inherit')]).optional(),\n mcpServers: z.array(z.string()).optional(),\n })\n )\n .optional(),\n /**\n * Native Cursor SDK conversation mode for the task this warm runner is\n * being claimed for. Threaded on `reinit` (not just `init`) because the\n * Agent is recreated on adoption — without this, a warm-pool-adopted\n * plan-mode task would create its Agent in the SDK's default `'agent'`\n * mode and silently lose the soft plan signal. See the `init` variant's\n * `mode` doc. Optional/additive — same backwards-compat posture.\n */\n mode: z.enum(['agent', 'plan']).optional(),\n }),\n /**\n * Flip the runner's pooled flag mid-life (C9). The daemon sends this just\n * before `close` when it intends to keep the warm process: the runner's\n * `close` handler then disposes the Agent and acks `pooled_idle` instead of\n * `process.exit(0)`. Sending `pooled: false` reverts to throwaway behavior.\n */\n z.object({\n kind: z.literal('set_pooled'),\n generation: z.number().int().nonnegative(),\n pooled: z.boolean(),\n }),\n /**\n * Daemon→runner notification that the permission mode changed. The runner\n * logs this for diagnostic observability; it takes no action (mode-file\n * is the authoritative source the hook script reads on each tool call).\n */\n z.object({\n kind: z.literal('set_permission_mode'),\n generation: z.number().int().nonnegative(),\n mode: z.enum(['default', 'accept-edits', 'plan', 'bypass', 'auto']),\n }),\n /**\n * Daemon→runner notification that Fast Mode was toggled mid-session.\n * Carries the current generation (not bumped — this is per-state, not\n * per-turn) so the runner can correlate the update. Effective on the\n * NEXT `push_message`: the runner records the flag and rebuilds\n * `SendOptions` on each `agent.send()`.\n */\n z.object({\n kind: z.literal('set_fast_mode'),\n generation: z.number().int().nonnegative(),\n fastMode: z.boolean(),\n }),\n z.object({ kind: z.literal('close') }),\n]);\nexport type DaemonToRunner = z.infer<typeof DaemonToRunnerSchema>;\n\n/**\n * The agents map as it travels the wire — the `init`/`reinit` `agents` field.\n * Narrower than `@cursor/sdk`'s `AgentDefinition` (this `mcpServers` is\n * `string[]`); the runner reconstructs the full `AgentDefinition` on receipt.\n * Daemon-side code that builds agents for the wire should use THIS type so the\n * IPC payload is type-correct by construction (no boundary cast).\n */\nexport type DaemonToRunnerAgents = NonNullable<Extract<DaemonToRunner, { kind: 'init' }>['agents']>;\n\n/**\n * RunnerToDaemon — inbound to the daemon-side adapter from the runner.\n *\n * `sdk_message.payload` is typed as `unknown` on the wire and narrowed\n * to `SDKMessage` (from `@cursor/sdk`) by the daemon-side translator\n * (`cursor-event-translator.ts`, S2b). Keeping it `unknown` at the\n * protocol boundary insulates the daemon from new SDK message variants\n * — the translator decides what to drop versus translate.\n */\nexport const RunnerToDaemonSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('init_ok'),\n sessionId: z.string(),\n tools: z.array(z.string()),\n model: z.string(),\n mcpStatus: z.array(z.object({ name: z.string(), status: z.string() })),\n }),\n z.object({\n kind: z.literal('init_error'),\n error: z.string(),\n /** SDK error class name (e.g. AuthenticationError) for daemon-side classification. */\n errorName: z.string().optional(),\n /** Stable SDK/backend error code, when available. */\n errorCode: z.string().optional(),\n statusCode: z.number().int().optional(),\n /**\n * `true` for transient failures (network, rate limit) where the\n * daemon should consider a retry; `false` for configuration errors\n * (bad API key, unknown model) where retry will not help.\n */\n retryable: z.boolean(),\n }),\n z.object({\n kind: z.literal('sdk_message'),\n generation: z.number().int().nonnegative(),\n payload: z.unknown(),\n }),\n z.object({\n kind: z.literal('run_complete'),\n generation: z.number().int().nonnegative(),\n /**\n * Cursor's `RunResult` — kept as `unknown` on the wire (see\n * `sdk_message.payload` rationale). The daemon-side adapter\n * narrows this against `RunResult` from `@cursor/sdk` at the\n * call site.\n */\n result: z.unknown(),\n /**\n * Real token usage from the `turn-ended` interaction update emitted on\n * `SendOptions.onDelta`. Optional — absent when the SDK does not surface\n * usage for this run (e.g. local composer, older SDK versions). When\n * present the daemon uses real counts instead of estimated ones.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No PROTOCOL_VERSION bump.\n */\n usage: z\n .object({\n inputTokens: z.number().int().nonnegative(),\n outputTokens: z.number().int().nonnegative(),\n cacheReadTokens: z.number().int().nonnegative(),\n cacheWriteTokens: z.number().int().nonnegative(),\n })\n .optional(),\n /**\n * `Run.createdAt` from `@cursor/sdk` — authoritative SDK-side run-start\n * epoch ms (run.d.ts:42). Forwarded so the daemon's per-run cost / latency\n * accounting uses the SDK clock instead of `Date.now()` at the IPC\n * boundary (which drifts by IPC + cold-build latency). Optional —\n * older SDK builds may not emit it.\n */\n createdAt: z.number().nonnegative().optional(),\n /**\n * Total tokens reported via `token-delta` InteractionUpdate during the\n * run. Complements `usage.outputTokens` — `token-delta` carries no\n * input/cache breakdown, so use this only as a fallback when `usage` is\n * absent. Optional, omitted when zero.\n */\n tokenDeltaTotal: z.number().int().nonnegative().optional(),\n }),\n z.object({\n kind: z.literal('run_error'),\n generation: z.number().int().nonnegative(),\n error: z.string(),\n /** SDK error class name (e.g. RateLimitError) for daemon-side classification. */\n errorName: z.string().optional(),\n errorCode: z.string().optional(),\n statusCode: z.number().int().optional(),\n isRetryable: z.boolean(),\n }),\n z.object({\n kind: z.literal('log'),\n level: z.enum(['info', 'warn', 'error']),\n message: z.string(),\n data: z.unknown().optional(),\n }),\n /**\n * Runner→daemon notification that the agent proposed a plan via the\n * `createPlan` hook interception path. The daemon routes this to\n * `plan-handler.ts` which writes `~/.shipyard/plans/{taskId}.md` and\n * broadcasts `plan_detected`.\n *\n * Note: In v1 this message is reserved for future hook-socket capture.\n * The primary plan capture path is the `createPlan` tool_use interception\n * in `cursor-event-translator.ts` (turn-complete fallback). The protocol\n * variant is defined now so the wire schema is stable when hook-socket\n * plan capture ships.\n */\n z.object({\n kind: z.literal('plan_proposed'),\n generation: z.number().int().nonnegative(),\n /** Markdown plan content emitted by the agent via createPlan. */\n plan: z.string(),\n }),\n /**\n * Runner→daemon notification that Cursor's server-managed conversation\n * summarization (its native \"compaction\") started / completed (B2). These\n * arrive on the `SendOptions.onDelta` interaction-update stream as\n * `SummaryStartedUpdate` / `SummaryCompletedUpdate`, NOT on the public\n * `Run.stream()` `SDKMessage` union (verified against @cursor/sdk@1.0.13 —\n * `run.d.ts` exposes only `stream()`/`conversation()`/`wait()`, and the\n * bundle routes `summary-*` updates exclusively through `onDelta`). The\n * daemon translates these into normalized `compaction_started` /\n * `compaction_completed` SubprocessEvents so the Compaction SM resets the\n * (estimated) context fill-bar after Cursor's native summarization.\n *\n * Cursor's `summary-completed` event carries no token counts, but the runner\n * has already captured the pre-compaction usage from the preceding\n * `turn-ended` interaction update (stored in `latestUsage.ref`). The runner\n * forwards that as the optional `preTokens` field so the daemon can emit a\n * real `compaction_completed.preTokens` instead of falling back to the\n * estimated `planPostCompactTurnStats` baseline.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No PROTOCOL_VERSION bump.\n */\n z.object({\n kind: z.literal('summary_started'),\n generation: z.number().int().nonnegative(),\n }),\n z.object({\n kind: z.literal('summary_completed'),\n generation: z.number().int().nonnegative(),\n /** Total input-equivalent tokens at the moment summarization completed (inputTokens + cacheRead + cacheWrite). Absent when no turn-ended usage was captured before summary-started. */\n preTokens: z.number().int().nonnegative().optional(),\n }),\n /**\n * Runner→daemon heartbeat emitted on every `SendOptions.onStep` callback\n * (each `ConversationStep` boundary from the SDK). The daemon resets its\n * per-run stall timer on receipt so an active but slow run does not trip\n * the watchdog. Stale-generation filtering applies (same as sdk_message).\n */\n z.object({\n kind: z.literal('step_progress'),\n generation: z.number().int().nonnegative(),\n }),\n /**\n * Runner→daemon liveness heartbeat emitted (throttled in the runner) on any\n * `SendOptions.onDelta` interaction-update — `text-delta`, `thinking-delta`,\n * `shell-output-delta`, `step-started`, `tool-call-started`, etc. Unlike\n * `step_progress` (which fires only at completed `ConversationStep`\n * boundaries via `onStep`), this fires DURING a step while output streams.\n * The daemon resets the stall watchdog WITHOUT retiring the cold-start\n * window, so a long-but-active step (a 2-min shell/test run, a slow MCP\n * tool, a big read) no longer trips the mid-run watchdog while it is plainly\n * still working. Stale-generation filtering applies (same as sdk_message).\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No PROTOCOL_VERSION bump.\n */\n z.object({\n kind: z.literal('stream_activity'),\n generation: z.number().int().nonnegative(),\n }),\n /**\n * Runner→daemon notification that the SDK persisted the user turn to its\n * local store (`user-message-appended` interaction update on `onDelta`).\n * Useful as a write-confirmed ack for cross-runtime handoff durability\n * checks — the daemon knows the user message landed in the SDK's SQLite\n * checkpoint store and is recoverable via `Agent.resume`. Daemon-side\n * handler logs only for now.\n *\n * Stale-generation filtering applies.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No\n * PROTOCOL_VERSION bump.\n */\n z.object({\n kind: z.literal('user_message_persisted'),\n generation: z.number().int().nonnegative(),\n sessionId: z.string(),\n }),\n /**\n * Warm-pool reinit succeeded (C9). The runner created a fresh Agent for the\n * claimed task on the warm executor. `sessionId` is the new `agent.agentId`\n * — the daemon persists it for the next resume. No generation: reinit is a\n * session-lifecycle event, not a per-turn one.\n */\n z.object({\n kind: z.literal('reinit_ok'),\n sessionId: z.string(),\n }),\n /**\n * Warm-pool reinit failed (C9). The runner could not bring up the new\n * Agent. `retryable` is informational only — the daemon's fail-closed\n * contract is to KILL this runner and spawn a fresh cold one regardless,\n * so a broken warm process can never serve the task. The runner does NOT\n * exit on its own (it has no clean state); the daemon owns the kill.\n */\n z.object({\n kind: z.literal('reinit_error'),\n error: z.string(),\n retryable: z.boolean(),\n }),\n /**\n * Pooled-close ack (C9). A pool-managed runner received `close`, disposed\n * its per-task Agent, and reset to a quiescent state WITHOUT exiting. The\n * daemon keeps the warm process (and its executor cache) for the next\n * claim. No generation — this is a session-lifecycle event.\n */\n z.object({\n kind: z.literal('pooled_idle'),\n }),\n]);\nexport type RunnerToDaemon = z.infer<typeof RunnerToDaemonSchema>;\n\n/**\n * Parse an inbound IPC message. Returns the parsed message or `null`\n * if the payload does not match the expected schema (matching the\n * watcher-worker pattern: log and continue, never throw out of the\n * IPC handler).\n */\nexport function parseDaemonToRunner(raw: unknown): DaemonToRunner | null {\n const result = DaemonToRunnerSchema.safeParse(raw);\n return result.success ? result.data : null;\n}\n\nexport function parseRunnerToDaemon(raw: unknown): RunnerToDaemon | null {\n const result = RunnerToDaemonSchema.safeParse(raw);\n return result.success ? result.data : null;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/services/session/cursor-runner-protocol.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * IPC protocol between the daemon (parent) and the `cursor-runner` forked\n * child subprocess.\n *\n * Wire format: structured-clone over the Node IPC channel\n * (`child.send(msg)` / `process.on('message', msg => ...)`). The schemas\n * below are Zod-parsed on receive so a corrupt or version-skewed peer\n * cannot drive the recipient into an undefined state.\n *\n * Stdout/stderr of the runner are inherited from the daemon (see S2b's\n * `child_process.fork({ stdio: 'inherit', silent: false })`) — those are\n * NOT carrying IPC traffic, they are for human-readable logs and native\n * panic stack traces.\n *\n * `generation` is a per-turn monotonic counter minted by the daemon-side\n * adapter. Every per-turn message in either direction is stamped with the\n * value live at the time of mint; the daemon drops inbound events whose\n * generation does not match the current run (closes Invariant #16 — the\n * transport discriminator must be present at every layer). The runner\n * does NOT drop on its own — it echoes whatever generation the daemon\n * stamped, so the daemon can correlate.\n *\n * Lifecycle events that are not per-turn (`init`, `init_ok`,\n * `init_error`, `log`, `close`) carry no generation.\n *\n * MCP server config shape: `Record<string, unknown>` here is intentional.\n * We keep the wire schema decoupled from `@cursor/sdk`'s `McpServerConfig`\n * to avoid coupling the daemon and runner to the same exact SDK version\n * at the type level. The runner re-validates against the SDK shape at\n * the call site.\n */\n\nconst McpServersSchema = z.record(z.string(), z.unknown());\nconst CursorResolvedSkillBodySchema = z.object({\n name: z.string(),\n namespace: z.string().optional(),\n body: z.string(),\n});\n\n/**\n * DaemonToRunner — outbound from the daemon-side `CursorAgentSubprocess`\n * adapter to the forked runner child.\n */\nexport const DaemonToRunnerSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('init'),\n taskId: z.string(),\n cwd: z.string(),\n apiKey: z.string(),\n modelId: z.string(),\n mcpServers: McpServersSchema,\n harnessUrl: z.string(),\n harnessToken: z.string(),\n /**\n * Stable per-runner identity for the runner-scoped harness path (C8/C9\n * warm pool). The runner puts this in the `x-shipyard-runner-id` header\n * (instead of the per-task `x-shipyard-task-id`) so the harness MCP-server\n * config — and therefore the SDK's process-scoped executor cache key — is\n * byte-identical across every task the runner serves. Optional/additive:\n * a pre-pool daemon omits it, and the runner falls back to the legacy\n * `x-shipyard-task-id` header keyed on `taskId`.\n */\n harnessRunnerId: z.string().optional(),\n /**\n * When true, the runner is pool-managed: on `close` it disposes the Agent\n * but does NOT `process.exit(0)` — it resets to a quiescent state and acks\n * `pooled_idle` so the daemon can keep the warm process (and its\n * `localExecutorCache`) alive for the next matching task. Optional/additive\n * — absent means \"throwaway runner, exit on close\" (legacy behavior).\n */\n pooled: z.boolean().optional(),\n /**\n * Absolute filesystem path where skill files for this task are\n * written (see plan v2 E#V7). The runner does not enumerate the\n * directory — it just forwards the path to the SDK so any future\n * skills-loading code has the location. May be empty string if\n * skills are disabled for this run.\n */\n skillsDir: z.string(),\n sessionId: z.string(),\n generation: z.number().int().nonnegative(),\n /**\n * Cross-runtime structured handoff (Wave 7.5). When set, the daemon\n * already wrote a fabricated `ConversationStateStructure` Merkle tree\n * to the SDK's SQLite stores during the pre-fork exclusive write\n * window. The runner MUST call `Agent.resume(injectedAgentId, ...)`\n * instead of `Agent.create(...)` so the SDK reads back the prior\n * conversation as context. Same-id `Agent.create()` would\n * PRIMARY-KEY-conflict on the agents row.\n *\n * Daemon↔forked-child IPC, NOT WebRTC — no PROTOCOL_VERSION bump.\n */\n injectedAgentId: z.string().optional(),\n /**\n * Persisted Cursor agentId from a prior `init_ok`. When set, the runner\n * calls `Agent.resume(resumeAgentId, ...)` instead of `Agent.create()` —\n * SDK reads its SQLite checkpoint store keyed by agentId and brings up\n * the prior conversation without rebuilding from scratch. Cold-build\n * cost (Statsig bootstrap + workspace runtime + MCP startup) is still\n * paid; the win is preserving conversation context across daemon\n * restarts and not depending on the JSONL replay path for that.\n *\n * Distinct from `injectedAgentId`: that one is for cross-runtime\n * structured handoff where the daemon fabricates a fake conversation;\n * this one is just rejoining a previously-real conversation. Resume\n * failures (agentId not in the SDK's local store, e.g. user wiped\n * `~/.cursor/`) fall back to `Agent.create` with the same options so\n * the spawn succeeds — just without prior context.\n */\n resumeAgentId: z.string().optional(),\n /**\n * Initial Fast Mode flag from `composerSettings.fastMode`. When true\n * AND `fastModeParam` is non-null, the runner attaches the param to\n * every `agent.send()` via `SendOptions.model.params`. When false\n * (or `fastModeParam` is null), the runner sends the plain\n * `agent.send(message)` shape. Optional/additive — pre-Fast-Mode\n * daemons omit the field and the runner treats it as false.\n */\n fastMode: z.boolean().optional(),\n /**\n * Resolved Fast Mode binding for `modelId`, sourced from the\n * daemon's `cursor-models-cache`. `null` means \"the cache has no\n * Fast-Mode binding for this model right now\" (unknown id, refresh\n * failed, or the model declares no Fast-Mode-shaped parameter).\n * The runner treats both `null` and `undefined` as \"no binding,\n * never attach params\" — gating Fast Mode the same way regardless\n * of why it's missing.\n */\n fastModeParam: z.object({ id: z.string(), value: z.string() }).nullable().optional(),\n /**\n * Named subagent catalog rendered for the Cursor runtime. When present,\n * the runner passes this to `Agent.create({agents,...})` and\n * `Agent.resume({agents,...})` so Cursor's built-in `task` tool can\n * spawn named subagents from Shipyard's catalog. Optional/additive —\n * pre-catalog daemons omit the field; the runner treats absence as an\n * empty catalog (no named subagents declared to the SDK).\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No\n * PROTOCOL_VERSION bump.\n */\n agents: z\n .record(\n z.string(),\n z.object({\n description: z.string(),\n prompt: z.string(),\n model: z.union([z.object({ id: z.string() }), z.literal('inherit')]).optional(),\n mcpServers: z.array(z.string()).optional(),\n })\n )\n .optional(),\n /**\n * Native Cursor SDK conversation mode for this agent. When `'plan'`, the\n * SDK tells composer-2.5 to call its built-in `createPlan` tool instead of\n * writing files. This is the SOFT signal complementing the hard-enforcement\n * layer in `.cursor/hooks.json` deny-writes. Optional/additive — pre-1.0.17\n * runners omit it and the SDK defaults to `'agent'` mode.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No\n * PROTOCOL_VERSION bump required.\n */\n mode: z.enum(['agent', 'plan']).optional(),\n }),\n z.object({\n kind: z.literal('push_message'),\n generation: z.number().int().nonnegative(),\n /**\n * Content blocks the daemon wants to send as a user turn. Shape is\n * Shipyard's internal `ContentBlock[]` — the runner translates to\n * Cursor's `SDKUserMessage` at the boundary (see cursor-content-builder.ts).\n *\n * Image attachments ride as `resource` blocks with `BlobResourceContents`\n * (mime + base64 blob). The runner extracts these into `SDKImage[]` and\n * attaches them to the `SDKUserMessage` it sends to `Agent.send()`.\n */\n content: z.array(z.unknown()),\n /**\n * SKILL.md bodies resolved by the daemon from the trusted capabilities\n * skill index for this turn. The runner must use these bodies instead of\n * any path carried on browser-authored `skill_invocation` content blocks.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No\n * PROTOCOL_VERSION bump required.\n */\n skillBodies: z.array(CursorResolvedSkillBodySchema).optional(),\n /**\n * Cursor SDK local recovery flag. Only the daemon-side AgentBusyError\n * retry path sets this after confirming the errored generation is still\n * current; stale-generation retries are dropped before reaching the\n * runner, so this must never expire a legitimately live successor run.\n */\n forceLocalRun: z.boolean().optional(),\n }),\n z.object({\n kind: z.literal('interrupt'),\n generation: z.number().int().nonnegative(),\n }),\n z.object({\n kind: z.literal('set_model'),\n generation: z.number().int().nonnegative(),\n modelId: z.string(),\n }),\n z.object({\n kind: z.literal('set_mcp_servers'),\n generation: z.number().int().nonnegative(),\n servers: McpServersSchema,\n }),\n z.object({\n kind: z.literal('set_harness_task_id'),\n generation: z.number().int().nonnegative(),\n taskId: z.string(),\n token: z.string(),\n }),\n /**\n * Warm-pool reinit (C9). A pooled runner whose prior task closed is being\n * claimed for a NEW task. The runner cancels any active run, disposes the\n * current Agent, updates its state to the new task, and creates a fresh\n * Agent (`Agent.resume(resumeAgentId)` if present, else `Agent.create`) —\n * reusing the warm process-scoped executor cache (cache HIT on the next\n * send, because the harness MCP entry uses the STABLE `harnessRunnerId`\n * header so `mcpServers` is byte-identical to the prior task's).\n *\n * On success → `reinit_ok { sessionId }`; on ANY throw → `reinit_error`\n * and the runner does NOT exit (the daemon kills + respawns fresh). This\n * is the fail-closed contract: a broken reinit becomes a cold spawn, never\n * a stale/cross-task state leak.\n *\n * Carries the full per-task spawn surface (everything the cache key and the\n * Agent depend on). `mcpServers` here MUST NOT include the harness entry —\n * the runner re-adds it from `harnessRunnerId`/`harnessToken` so the entry\n * is byte-identical across init→reinit.\n */\n z.object({\n kind: z.literal('reinit'),\n taskId: z.string(),\n modelId: z.string(),\n mcpServers: McpServersSchema,\n harnessRunnerId: z.string(),\n harnessToken: z.string(),\n skillsDir: z.string(),\n generation: z.number().int().nonnegative(),\n resumeAgentId: z.string().optional(),\n fastMode: z.boolean().optional(),\n fastModeParam: z.object({ id: z.string(), value: z.string() }).nullable().optional(),\n agents: z\n .record(\n z.string(),\n z.object({\n description: z.string(),\n prompt: z.string(),\n model: z.union([z.object({ id: z.string() }), z.literal('inherit')]).optional(),\n mcpServers: z.array(z.string()).optional(),\n })\n )\n .optional(),\n /**\n * Native Cursor SDK conversation mode for the task this warm runner is\n * being claimed for. Threaded on `reinit` (not just `init`) because the\n * Agent is recreated on adoption — without this, a warm-pool-adopted\n * plan-mode task would create its Agent in the SDK's default `'agent'`\n * mode and silently lose the soft plan signal. See the `init` variant's\n * `mode` doc. Optional/additive — same backwards-compat posture.\n */\n mode: z.enum(['agent', 'plan']).optional(),\n }),\n /**\n * Flip the runner's pooled flag mid-life (C9). The daemon sends this just\n * before `close` when it intends to keep the warm process: the runner's\n * `close` handler then disposes the Agent and acks `pooled_idle` instead of\n * `process.exit(0)`. Sending `pooled: false` reverts to throwaway behavior.\n */\n z.object({\n kind: z.literal('set_pooled'),\n generation: z.number().int().nonnegative(),\n pooled: z.boolean(),\n }),\n /**\n * Daemon→runner notification that the permission mode changed. The runner\n * logs this for diagnostic observability; it takes no action (mode-file\n * is the authoritative source the hook script reads on each tool call).\n */\n z.object({\n kind: z.literal('set_permission_mode'),\n generation: z.number().int().nonnegative(),\n mode: z.enum(['default', 'accept-edits', 'plan', 'bypass', 'auto']),\n }),\n /**\n * Daemon→runner notification that Fast Mode was toggled mid-session.\n * Carries the current generation (not bumped — this is per-state, not\n * per-turn) so the runner can correlate the update. Effective on the\n * NEXT `push_message`: the runner records the flag and rebuilds\n * `SendOptions` on each `agent.send()`.\n */\n z.object({\n kind: z.literal('set_fast_mode'),\n generation: z.number().int().nonnegative(),\n fastMode: z.boolean(),\n }),\n z.object({ kind: z.literal('close') }),\n]);\nexport type DaemonToRunner = z.infer<typeof DaemonToRunnerSchema>;\n\n/**\n * The agents map as it travels the wire — the `init`/`reinit` `agents` field.\n * Narrower than `@cursor/sdk`'s `AgentDefinition` (this `mcpServers` is\n * `string[]`); the runner reconstructs the full `AgentDefinition` on receipt.\n * Daemon-side code that builds agents for the wire should use THIS type so the\n * IPC payload is type-correct by construction (no boundary cast).\n */\nexport type DaemonToRunnerAgents = NonNullable<Extract<DaemonToRunner, { kind: 'init' }>['agents']>;\n\n/**\n * RunnerToDaemon — inbound to the daemon-side adapter from the runner.\n *\n * `sdk_message.payload` is typed as `unknown` on the wire and narrowed\n * to `SDKMessage` (from `@cursor/sdk`) by the daemon-side translator\n * (`cursor-event-translator.ts`, S2b). Keeping it `unknown` at the\n * protocol boundary insulates the daemon from new SDK message variants\n * — the translator decides what to drop versus translate.\n */\nexport const RunnerToDaemonSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('init_ok'),\n sessionId: z.string(),\n tools: z.array(z.string()),\n model: z.string(),\n mcpStatus: z.array(z.object({ name: z.string(), status: z.string() })),\n }),\n z.object({\n kind: z.literal('init_error'),\n error: z.string(),\n /** SDK error class name (e.g. AuthenticationError) for daemon-side classification. */\n errorName: z.string().optional(),\n /** Stable SDK/backend error code, when available. */\n errorCode: z.string().optional(),\n statusCode: z.number().int().optional(),\n /**\n * `true` for transient failures (network, rate limit) where the\n * daemon should consider a retry; `false` for configuration errors\n * (bad API key, unknown model) where retry will not help.\n */\n retryable: z.boolean(),\n }),\n z.object({\n kind: z.literal('sdk_message'),\n generation: z.number().int().nonnegative(),\n payload: z.unknown(),\n }),\n z.object({\n kind: z.literal('run_complete'),\n generation: z.number().int().nonnegative(),\n /**\n * Cursor's `RunResult` — kept as `unknown` on the wire (see\n * `sdk_message.payload` rationale). The daemon-side adapter\n * narrows this against `RunResult` from `@cursor/sdk` at the\n * call site.\n */\n result: z.unknown(),\n /**\n * Real token usage from the `turn-ended` interaction update emitted on\n * `SendOptions.onDelta`. Optional — absent when the SDK does not surface\n * usage for this run (e.g. local composer, older SDK versions). When\n * present the daemon uses real counts instead of estimated ones.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No PROTOCOL_VERSION bump.\n */\n usage: z\n .object({\n inputTokens: z.number().int().nonnegative(),\n outputTokens: z.number().int().nonnegative(),\n cacheReadTokens: z.number().int().nonnegative(),\n cacheWriteTokens: z.number().int().nonnegative(),\n })\n .optional(),\n /**\n * `Run.createdAt` from `@cursor/sdk` — authoritative SDK-side run-start\n * epoch ms (run.d.ts:42). Forwarded so the daemon's per-run cost / latency\n * accounting uses the SDK clock instead of `Date.now()` at the IPC\n * boundary (which drifts by IPC + cold-build latency). Optional —\n * older SDK builds may not emit it.\n */\n createdAt: z.number().nonnegative().optional(),\n /**\n * Total tokens reported via `token-delta` InteractionUpdate during the\n * run. Complements `usage.outputTokens` — `token-delta` carries no\n * input/cache breakdown, so use this only as a fallback when `usage` is\n * absent. Optional, omitted when zero.\n */\n tokenDeltaTotal: z.number().int().nonnegative().optional(),\n }),\n z.object({\n kind: z.literal('run_error'),\n generation: z.number().int().nonnegative(),\n error: z.string(),\n /** SDK error class name (e.g. RateLimitError) for daemon-side classification. */\n errorName: z.string().optional(),\n errorCode: z.string().optional(),\n statusCode: z.number().int().optional(),\n isRetryable: z.boolean(),\n }),\n z.object({\n kind: z.literal('log'),\n level: z.enum(['info', 'warn', 'error']),\n message: z.string(),\n data: z.unknown().optional(),\n }),\n /**\n * Runner→daemon notification that the agent proposed a plan via the\n * `createPlan` hook interception path. The daemon routes this to\n * `plan-handler.ts` which writes `~/.shipyard/plans/{taskId}.md` and\n * broadcasts `plan_detected`.\n *\n * Note: In v1 this message is reserved for future hook-socket capture.\n * The primary plan capture path is the `createPlan` tool_use interception\n * in `cursor-event-translator.ts` (turn-complete fallback). The protocol\n * variant is defined now so the wire schema is stable when hook-socket\n * plan capture ships.\n */\n z.object({\n kind: z.literal('plan_proposed'),\n generation: z.number().int().nonnegative(),\n /** Markdown plan content emitted by the agent via createPlan. */\n plan: z.string(),\n }),\n /**\n * Runner→daemon notification that Cursor's server-managed conversation\n * summarization (its native \"compaction\") started / completed (B2). These\n * arrive on the `SendOptions.onDelta` interaction-update stream as\n * `SummaryStartedUpdate` / `SummaryCompletedUpdate`, NOT on the public\n * `Run.stream()` `SDKMessage` union (verified against @cursor/sdk@1.0.13 —\n * `run.d.ts` exposes only `stream()`/`conversation()`/`wait()`, and the\n * bundle routes `summary-*` updates exclusively through `onDelta`). The\n * daemon translates these into normalized `compaction_started` /\n * `compaction_completed` SubprocessEvents so the Compaction SM resets the\n * (estimated) context fill-bar after Cursor's native summarization.\n *\n * Cursor's `summary-completed` event carries no token counts, but the runner\n * has already captured the pre-compaction usage from the preceding\n * `turn-ended` interaction update (stored in `latestUsage.ref`). The runner\n * forwards that as the optional `preTokens` field so the daemon can emit a\n * real `compaction_completed.preTokens` instead of falling back to the\n * estimated `planPostCompactTurnStats` baseline.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No PROTOCOL_VERSION bump.\n */\n z.object({\n kind: z.literal('summary_started'),\n generation: z.number().int().nonnegative(),\n }),\n z.object({\n kind: z.literal('summary_completed'),\n generation: z.number().int().nonnegative(),\n /** Total input-equivalent tokens at the moment summarization completed (inputTokens + cacheRead + cacheWrite). Absent when no turn-ended usage was captured before summary-started. */\n preTokens: z.number().int().nonnegative().optional(),\n }),\n /**\n * Runner→daemon heartbeat emitted on every `SendOptions.onStep` callback\n * (each `ConversationStep` boundary from the SDK). The daemon resets its\n * per-run stall timer on receipt so an active but slow run does not trip\n * the watchdog. Stale-generation filtering applies (same as sdk_message).\n */\n z.object({\n kind: z.literal('step_progress'),\n generation: z.number().int().nonnegative(),\n }),\n /**\n * Runner→daemon liveness heartbeat emitted (throttled in the runner) on any\n * `SendOptions.onDelta` interaction-update — `text-delta`, `thinking-delta`,\n * `shell-output-delta`, `step-started`, `tool-call-started`, etc. Unlike\n * `step_progress` (which fires only at completed `ConversationStep`\n * boundaries via `onStep`), this fires DURING a step while output streams.\n * The daemon resets the stall watchdog WITHOUT retiring the cold-start\n * window, so a long-but-active step (a 2-min shell/test run, a slow MCP\n * tool, a big read) no longer trips the mid-run watchdog while it is plainly\n * still working. Stale-generation filtering applies (same as sdk_message).\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No PROTOCOL_VERSION bump.\n */\n z.object({\n kind: z.literal('stream_activity'),\n generation: z.number().int().nonnegative(),\n }),\n /**\n * Runner→daemon notification that the SDK persisted the user turn to its\n * local store (`user-message-appended` interaction update on `onDelta`).\n * Useful as a write-confirmed ack for cross-runtime handoff durability\n * checks — the daemon knows the user message landed in the SDK's SQLite\n * checkpoint store and is recoverable via `Agent.resume`. Daemon-side\n * handler logs only for now.\n *\n * Stale-generation filtering applies.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No\n * PROTOCOL_VERSION bump.\n */\n z.object({\n kind: z.literal('user_message_persisted'),\n generation: z.number().int().nonnegative(),\n sessionId: z.string(),\n }),\n /**\n * Runner→daemon liveness edge for in-flight tool execution. Emitted only\n * on the 0→1 transition (first tool call started, phase `'started'`) and\n * the 1→0 transition (last tool call completed, phase `'settled'`). Drives\n * the daemon's `toolExecutionStallTimeoutMs` window: when a tool is\n * in-flight the stall watchdog uses the extended per-tool budget so a\n * legitimate long-running tool (e.g. `git commit` with a full `pnpm check`\n * pre-commit hook, 3–10 min) is not false-killed. On settle the watchdog\n * reverts to the shorter mid-run window. Stale-generation filtering applies.\n *\n * Daemon↔forked-child IPC only — NOT the browser wire. No PROTOCOL_VERSION bump.\n */\n z.object({\n kind: z.literal('tool_execution'),\n generation: z.number().int().nonnegative(),\n phase: z.enum(['started', 'settled']),\n }),\n /**\n * Warm-pool reinit succeeded (C9). The runner created a fresh Agent for the\n * claimed task on the warm executor. `sessionId` is the new `agent.agentId`\n * — the daemon persists it for the next resume. No generation: reinit is a\n * session-lifecycle event, not a per-turn one.\n */\n z.object({\n kind: z.literal('reinit_ok'),\n sessionId: z.string(),\n }),\n /**\n * Warm-pool reinit failed (C9). The runner could not bring up the new\n * Agent. `retryable` is informational only — the daemon's fail-closed\n * contract is to KILL this runner and spawn a fresh cold one regardless,\n * so a broken warm process can never serve the task. The runner does NOT\n * exit on its own (it has no clean state); the daemon owns the kill.\n */\n z.object({\n kind: z.literal('reinit_error'),\n error: z.string(),\n retryable: z.boolean(),\n }),\n /**\n * Pooled-close ack (C9). A pool-managed runner received `close`, disposed\n * its per-task Agent, and reset to a quiescent state WITHOUT exiting. The\n * daemon keeps the warm process (and its executor cache) for the next\n * claim. No generation — this is a session-lifecycle event.\n */\n z.object({\n kind: z.literal('pooled_idle'),\n }),\n]);\nexport type RunnerToDaemon = z.infer<typeof RunnerToDaemonSchema>;\n\n/**\n * Parse an inbound IPC message. Returns the parsed message or `null`\n * if the payload does not match the expected schema (matching the\n * watcher-worker pattern: log and continue, never throw out of the\n * IPC handler).\n */\nexport function parseDaemonToRunner(raw: unknown): DaemonToRunner | null {\n const result = DaemonToRunnerSchema.safeParse(raw);\n return result.success ? result.data : null;\n}\n\nexport function parseRunnerToDaemon(raw: unknown): RunnerToDaemon | null {\n const result = RunnerToDaemonSchema.safeParse(raw);\n return result.success ? result.data : null;\n}\n"],"mappings":"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","names":[]}
|