@schoolai/shipyard 3.14.0 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/capability-detector-worker.js +5 -5
  2. package/dist/{chunk-OX3UY44R.js → chunk-5PBWS7BB.js} +31 -42
  3. package/dist/chunk-5PBWS7BB.js.map +1 -0
  4. package/dist/{chunk-RCEAMZVG.js → chunk-6SK6FBYC.js} +2 -2
  5. package/dist/{chunk-M3WBYTB3.js → chunk-AXHG3QQA.js} +16 -1
  6. package/dist/chunk-AXHG3QQA.js.map +1 -0
  7. package/dist/{chunk-T3OTZ66B.js → chunk-B7WZUKYX.js} +219 -86
  8. package/dist/chunk-B7WZUKYX.js.map +1 -0
  9. package/dist/{chunk-BUAEJNUG.js → chunk-HPDRJ4VZ.js} +3 -3
  10. package/dist/{chunk-BUAEJNUG.js.map → chunk-HPDRJ4VZ.js.map} +1 -1
  11. package/dist/{chunk-WGS7ZW6N.js → chunk-L5FQEZ6Y.js} +3 -3
  12. package/dist/{chunk-KUPHN3ZN.js → chunk-RHHRJR3L.js} +2 -2
  13. package/dist/{chunk-3KE2VDKA.js → chunk-VKY7UXTP.js} +25 -3
  14. package/dist/chunk-VKY7UXTP.js.map +1 -0
  15. package/dist/{chunk-IWBDVGD2.js → chunk-VTALUCDB.js} +2 -2
  16. package/dist/cursor-runner.js +3 -3
  17. package/dist/electron-utility.js +2 -2
  18. package/dist/index.js +4 -4
  19. package/dist/{login-HRR3T4SZ.js → login-EYGYOEXL.js} +3 -3
  20. package/dist/{mcp-servers-GUA5WOHO.js → mcp-servers-DSOX243C.js} +8 -2
  21. package/dist/{plan-backfill-QNJUWOYP.js → plan-backfill-SMJEKTMM.js} +4 -4
  22. package/dist/{serve-25I4ML7R.js → serve-DAP6V7SY.js} +339 -147
  23. package/dist/{serve-25I4ML7R.js.map → serve-DAP6V7SY.js.map} +1 -1
  24. package/dist/{start-O2DXLW23.js → start-AX2ZK3AY.js} +6 -6
  25. package/package.json +1 -1
  26. package/dist/chunk-3KE2VDKA.js.map +0 -1
  27. package/dist/chunk-M3WBYTB3.js.map +0 -1
  28. package/dist/chunk-OX3UY44R.js.map +0 -1
  29. package/dist/chunk-T3OTZ66B.js.map +0 -1
  30. /package/dist/{chunk-RCEAMZVG.js.map → chunk-6SK6FBYC.js.map} +0 -0
  31. /package/dist/{chunk-WGS7ZW6N.js.map → chunk-L5FQEZ6Y.js.map} +0 -0
  32. /package/dist/{chunk-KUPHN3ZN.js.map → chunk-RHHRJR3L.js.map} +0 -0
  33. /package/dist/{chunk-IWBDVGD2.js.map → chunk-VTALUCDB.js.map} +0 -0
  34. /package/dist/{login-HRR3T4SZ.js.map → login-EYGYOEXL.js.map} +0 -0
  35. /package/dist/{mcp-servers-GUA5WOHO.js.map → mcp-servers-DSOX243C.js.map} +0 -0
  36. /package/dist/{plan-backfill-QNJUWOYP.js.map → plan-backfill-SMJEKTMM.js.map} +0 -0
  37. /package/dist/{start-O2DXLW23.js.map → start-AX2ZK3AY.js.map} +0 -0
@@ -12,7 +12,7 @@ import {
12
12
  guardedSubscribe,
13
13
  makeInitialAttemptState,
14
14
  shutdownFileWatcherGuard
15
- } from "./chunk-3KE2VDKA.js";
15
+ } from "./chunk-VKY7UXTP.js";
16
16
  import {
17
17
  TRAILER_SCHEMA_VERSION,
18
18
  appendTrailerToMessage,
@@ -39,13 +39,13 @@ import {
39
39
  startPlanBackfillForDaemon,
40
40
  sync,
41
41
  validatePeerId
42
- } from "./chunk-BUAEJNUG.js";
42
+ } from "./chunk-HPDRJ4VZ.js";
43
43
  import {
44
44
  loadAuthToken
45
45
  } from "./chunk-TFWDJCUJ.js";
46
46
  import {
47
47
  getDaemonVersion
48
- } from "./chunk-WGS7ZW6N.js";
48
+ } from "./chunk-L5FQEZ6Y.js";
49
49
  import {
50
50
  collectPriorCrashDetail,
51
51
  getRecentWasmPanicMessages
@@ -54,6 +54,7 @@ import "./chunk-UNGAIUEX.js";
54
54
  import "./chunk-WW4UGZBY.js";
55
55
  import {
56
56
  CHARS_PER_TOKEN,
57
+ CODEX_BUNDLED_VERSION,
57
58
  CURSOR_MODEL_CATALOG,
58
59
  PROFILE_AUTH_ADAPTERS,
59
60
  REDACTED_THINKING_PLACEHOLDER,
@@ -167,7 +168,7 @@ import {
167
168
  translateWarningNotification,
168
169
  unresolveReviewThread,
169
170
  writeCursorApiKeyToVault
170
- } from "./chunk-OX3UY44R.js";
171
+ } from "./chunk-5PBWS7BB.js";
171
172
  import {
172
173
  buildNodeSpawnEnv,
173
174
  nodeSpawnEnvOverlay,
@@ -202,8 +203,10 @@ import {
202
203
  redactArgs,
203
204
  redactEnv,
204
205
  resolveEnabledMcpServers,
205
- resolveStdioEnv
206
- } from "./chunk-T3OTZ66B.js";
206
+ resolveStdioEnv,
207
+ setTelemetrySink,
208
+ telemeter
209
+ } from "./chunk-B7WZUKYX.js";
207
210
  import {
208
211
  atomicWriteFile,
209
212
  findProjectRoot,
@@ -226,7 +229,7 @@ import {
226
229
  createAssetResolver,
227
230
  toSdkContent,
228
231
  toSdkPermissionMode
229
- } from "./chunk-RCEAMZVG.js";
232
+ } from "./chunk-6SK6FBYC.js";
230
233
  import {
231
234
  AGENT_IDS,
232
235
  AGENT_SYSTEM_CLAUDE_CODE,
@@ -408,7 +411,7 @@ import {
408
411
  unsafeAssertSourceId,
409
412
  vizFileExtension,
410
413
  withPreferredRuntimeAuthMethod
411
- } from "./chunk-IWBDVGD2.js";
414
+ } from "./chunk-VTALUCDB.js";
412
415
  import {
413
416
  AuthGitHubCallbackRequestSchema,
414
417
  AuthGitHubCallbackResponseSchema,
@@ -448,13 +451,15 @@ import {
448
451
  PublishCreateResponseSchema,
449
452
  ROUTES,
450
453
  TESTED_CLAUDE_CODE_VERSION,
454
+ TESTED_CODEX_VERSION,
451
455
  TurnHealthResponseSchema,
452
456
  ValidationErrorResponseSchema,
453
457
  VaultKeyGetResponseSchema,
454
458
  VaultKeyPutRequestSchema,
455
459
  VaultKeyPutResponseSchema,
456
- classifyClaudeCodeCompatibility
457
- } from "./chunk-M3WBYTB3.js";
460
+ classifyClaudeCodeCompatibility,
461
+ classifyCodexCompatibility
462
+ } from "./chunk-AXHG3QQA.js";
458
463
  import {
459
464
  external_exports as external_exports2
460
465
  } from "./chunk-EHQITHQX.js";
@@ -5764,7 +5769,7 @@ function nanoid(size = 21) {
5764
5769
  }
5765
5770
 
5766
5771
  // src/services/bootstrap/signaling.ts
5767
- var DAEMON_NPM_VERSION = true ? "3.14.0" : "unknown";
5772
+ var DAEMON_NPM_VERSION = true ? "3.15.0" : "unknown";
5768
5773
  function createDaemonSignaling(config) {
5769
5774
  const agentId = config.agentId ?? nanoid();
5770
5775
  function send(msg) {
@@ -7123,6 +7128,12 @@ function resolveSnapshotAwareLive(s2, sessionEntry, workspaceLive, inSnapshot) {
7123
7128
  };
7124
7129
  }
7125
7130
  function buildWorkspaceRow(s2, live, transportEnabled, userEnabled) {
7131
+ const accountIssue = s2.source === "claudeai" && s2.authStatus === "unauthenticated" && s2.url === void 0 ? {
7132
+ errorType: "agent_signin_required",
7133
+ remediation: {
7134
+ message: "Re-authenticate with Claude to enable claude.ai connectors."
7135
+ }
7136
+ } : null;
7126
7137
  return {
7127
7138
  name: s2.name,
7128
7139
  canonicalId: canonicalMcpServerKey(s2),
@@ -7139,6 +7150,8 @@ function buildWorkspaceRow(s2, live, transportEnabled, userEnabled) {
7139
7150
  ...live?.toolCount !== void 0 ? { toolCount: live.toolCount } : {},
7140
7151
  ...live?.errorType !== void 0 ? { errorType: live.errorType } : {},
7141
7152
  ...live?.remediation !== void 0 ? { remediation: live.remediation } : {},
7153
+ ...accountIssue?.errorType !== void 0 ? { errorType: accountIssue.errorType } : {},
7154
+ ...accountIssue?.remediation !== void 0 ? { remediation: accountIssue.remediation } : {},
7142
7155
  ...s2.sources !== void 0 ? { sources: s2.sources } : {}
7143
7156
  };
7144
7157
  }
@@ -8017,7 +8030,7 @@ function searchViaRipgrep(query3, jsonlPaths, signal) {
8017
8030
  };
8018
8031
  rl.on("line", (line) => {
8019
8032
  const parsed = parseRgJsonLine(line);
8020
- if (!parsed || parsed.type !== "match") return;
8033
+ if (parsed?.type !== "match") return;
8021
8034
  if (!isRgMatchData(parsed.data)) return;
8022
8035
  const data = parsed.data;
8023
8036
  const sessionId = extractSessionIdFromPath(data.path.text);
@@ -8792,7 +8805,7 @@ function parseRipgrepOutput(stdout, maxResults) {
8792
8805
  for (const raw of stdout.split("\n")) {
8793
8806
  if (raw.length === 0) continue;
8794
8807
  const parsed = parseRgJsonLine2(raw);
8795
- if (!parsed || parsed.type !== "match") continue;
8808
+ if (parsed?.type !== "match") continue;
8796
8809
  if (!isRgMatchData2(parsed.data)) continue;
8797
8810
  const [firstSub] = parsed.data.submatches;
8798
8811
  if (!firstSub) continue;
@@ -13393,7 +13406,7 @@ var WorktreeMachineRunner = class {
13393
13406
  }
13394
13407
  #runValidate(_input, key) {
13395
13408
  const entry = this.#entries.get(key);
13396
- if (!entry || entry.state.phase !== "validating") return;
13409
+ if (entry?.state.phase !== "validating") return;
13397
13410
  const result = this.#deps.io.validateInputs(entry.state.input);
13398
13411
  if (!result.ok) {
13399
13412
  this.dispatch({ type: "validation_failed", error: result.error, reason: result.reason }, key);
@@ -13450,7 +13463,7 @@ var WorktreeMachineRunner = class {
13450
13463
  }
13451
13464
  #clearSetupTimeout(key) {
13452
13465
  const entry = this.#entries.get(key);
13453
- if (!entry || !entry.setupTimer) return;
13466
+ if (!entry?.setupTimer) return;
13454
13467
  this.#clearTimeoutFn(entry.setupTimer);
13455
13468
  entry.setupTimer = null;
13456
13469
  }
@@ -13775,81 +13788,6 @@ function flattenControlChannel(v2) {
13775
13788
  };
13776
13789
  }
13777
13790
 
13778
- // src/services/metrics/telemeter.ts
13779
- var TELEMETERED_EVENT_NAMES = [
13780
- "run_stall_timeout",
13781
- "request_task_state_replay_failed",
13782
- "loro_recovery_failed",
13783
- "mcp_server_connect_failed",
13784
- "mcp_server_needs_auth",
13785
- "mcp_reconnect_failed",
13786
- "mcp_reconnect_terminal",
13787
- "mcp_reconnect_terminal_unsupported",
13788
- "mcp_reauth_failed",
13789
- "mcp_adopted_set_failed",
13790
- "mcp_push_failed",
13791
- "harness_mcp_registration_failed",
13792
- "codex_auth_divergence_detected",
13793
- "cursor_auth_divergence_detected",
13794
- /**
13795
- * A Codex remote-compact cycle failed with a 401 "Missing bearer" error —
13796
- * the app-server lost its in-memory token during refresh-token rotation.
13797
- * Emitted once per compact attempt (not per retry) so fleet dashboards can
13798
- * surface the storm rate without log amplification.
13799
- */
13800
- "codex_compact_auth_failure",
13801
- "event_loop_stall",
13802
- "machine_sleep_artifact",
13803
- /**
13804
- * Per-process memory from the Electron main process (`app.getAppMetrics()`).
13805
- * The only source of true per-process RSS for the renderer/GPU/utility — JS
13806
- * heap (browser_metrics_sample) is capped ~4GB and cannot reveal a 50GB
13807
- * process. Forwarded to D1 by the daemon so runaway RAM is localizable
13808
- * fleet-wide to the offending Electron process.
13809
- */
13810
- "electron_app_metrics",
13811
- /** Electron main cleared a wedged Squirrel.Mac staged install on launch. */
13812
- "squirrel_install_stuck_recovered",
13813
- /**
13814
- * An ALO control-channel entry exhausted its maxDeliver retry budget
13815
- * without receiving an ack. Payload: { streamId, attempts, channel: 'control' }.
13816
- * Emitted via the `onDeadLetter` callback in control-channel-wiring.ts.
13817
- * Complements the `alo_resend_exhausted` ERROR log event emitted by the
13818
- * shell itself; this telemetry path routes it to D1 for fleet-wide
13819
- * observability so silent resend exhaustion is detectable in aggregate.
13820
- */
13821
- "alo_resend_exhausted",
13822
- /**
13823
- * An ALO control-channel shell reaped itself as an orphan — no truncating
13824
- * ack landed for `orphanThresholdMs` (5 min) while its retry buffer was
13825
- * non-empty, so the WHOLE buffer of unacked control messages was discarded.
13826
- * This is the bulk-loss sibling of `alo_resend_exhausted` (one entry): a
13827
- * reap can drop dozens of messages at once. Payload:
13828
- * { streamId, discardedCount, msSinceLastAck, attempts, channel: 'control' }.
13829
- * Emitted via the `onOrphanReaped` callback in control-channel-wiring.ts;
13830
- * the shell also emits a matching ERROR log. Routed to D1 so silent
13831
- * reconnect-time loss is detectable fleet-wide.
13832
- */
13833
- "alo_shell_orphan_reaped",
13834
- /**
13835
- * A `task_index_snapshot` envelope serialized past the control-channel
13836
- * high-water even after byte-bounded pagination — i.e. a single slim entry
13837
- * alone exceeds the cap (a page is guaranteed >=1 entry for forward
13838
- * progress). The control channel may drop it whole (`channel_send_dropped`).
13839
- * Payload: { bytes, taskCount, limitBytes }. Surfaces the pathological
13840
- * oversized-record case the byte budget cannot otherwise bound.
13841
- */
13842
- "task_index_snapshot_oversized"
13843
- ];
13844
- var TELEMETERED_EVENTS = new Set(TELEMETERED_EVENT_NAMES);
13845
- var sink = null;
13846
- function setTelemetrySink(fn) {
13847
- sink = fn;
13848
- }
13849
- function telemeter(event, payload) {
13850
- sink?.(event, payload);
13851
- }
13852
-
13853
13791
  // src/services/channels/auth/agent-auth-verifier.ts
13854
13792
  var AUTH_CONVERGENCE_RUNTIMES = /* @__PURE__ */ new Set(["codex", "cursor", "claude-code"]);
13855
13793
  function isAuthConvergenceRuntime(runtimeId) {
@@ -17095,7 +17033,7 @@ async function refreshPluginCapabilities(daemon, tokenStore) {
17095
17033
  const userSettings = await daemon.userSettingsStore.getSettings();
17096
17034
  const [updatedMarketplace, updatedMcp, updatedSkills] = await Promise.all([
17097
17035
  detectMarketplacePlugins(daemon.capabilities.marketplacePlugins),
17098
- import("./mcp-servers-GUA5WOHO.js").then(
17036
+ import("./mcp-servers-DSOX243C.js").then(
17099
17037
  (m2) => m2.detectMCPServers(
17100
17038
  daemon.capabilities.environments,
17101
17039
  tokenStore,
@@ -17144,6 +17082,17 @@ function runPluginOp(pluginName, marketplace, action, ctx) {
17144
17082
  return;
17145
17083
  }
17146
17084
  logAdapter({ event: `plugin_cc_${action}_requested`, pluginName, marketplace });
17085
+ const claudeBinary = resolveClaudeBinaryPath(logAdapter);
17086
+ if (!claudeBinary) {
17087
+ handler.sendControl({
17088
+ type: action === "install" ? "plugin_cc_install_status" : "plugin_cc_uninstall_status",
17089
+ pluginName,
17090
+ marketplace,
17091
+ status: "error",
17092
+ error: "Claude Code binary not found. Reinstall Shipyard or set CLAUDE_CODE_PATH."
17093
+ });
17094
+ return;
17095
+ }
17147
17096
  inFlightPluginOps.add(fullId);
17148
17097
  if (action === "install") {
17149
17098
  handler.sendControl({
@@ -17160,7 +17109,7 @@ function runPluginOp(pluginName, marketplace, action, ctx) {
17160
17109
  status: "uninstalling"
17161
17110
  });
17162
17111
  }
17163
- execFile8("claude", ["plugin", action, fullId], { timeout: 6e4 }).then(async () => {
17112
+ execFile8(claudeBinary, ["plugin", action, fullId], { timeout: 6e4 }).then(async () => {
17164
17113
  logAdapter({ event: `plugin_cc_${action}_completed`, pluginName, marketplace });
17165
17114
  if (action === "install") {
17166
17115
  handler.sendControl({
@@ -19233,6 +19182,10 @@ function handleLoginExit(exitCode, output, ctx) {
19233
19182
  output,
19234
19183
  reason: classification.kind
19235
19184
  });
19185
+ telemeter("anthropic_login_failed", {
19186
+ exitCode,
19187
+ method: ctx.method ?? "default"
19188
+ });
19236
19189
  ctx.sendStatus({
19237
19190
  type: "anthropic_login_status",
19238
19191
  requestId: ctx.requestId,
@@ -30199,6 +30152,14 @@ function handleMessageChannel(opts) {
30199
30152
  };
30200
30153
  }
30201
30154
 
30155
+ // src/services/collab/connection-lifecycle.ts
30156
+ function decideStaleSignal(entry, incoming) {
30157
+ if (!entry) return { action: "drop" };
30158
+ if (entry.generationId === incoming.generationId) return { action: "apply" };
30159
+ if (entry.lastState === "new") return { action: "reoffer", generationId: entry.generationId };
30160
+ return { action: "drop" };
30161
+ }
30162
+
30202
30163
  // src/services/peer-manager.ts
30203
30164
  function routeDataChannel(label) {
30204
30165
  if (label === LORO_SYNC_LABEL) {
@@ -30404,12 +30365,6 @@ function installControlGuard(dc, label, machineId, log, logAdapter, dcBufferedSa
30404
30365
  var HANDSHAKE_TIMEOUT_MS = 3e4;
30405
30366
  function noopLogAdapter(_entry) {
30406
30367
  }
30407
- function decideStaleSignal(entry, incoming) {
30408
- if (!entry) return { action: "drop" };
30409
- if (entry.generationId === incoming.generationId) return { action: "apply" };
30410
- if (entry.lastState === "new") return { action: "reoffer", generationId: entry.generationId };
30411
- return { action: "drop" };
30412
- }
30413
30368
  var DISCONNECT_WATCHDOG_MS = 5e3;
30414
30369
  var REOFFER_THROTTLE_MS = 1e3;
30415
30370
  function createPeerManager(config) {
@@ -32799,7 +32754,7 @@ function filterEntriesToPorts(entries, cwdByPid, workspaceRoot) {
32799
32754
  if (seenPorts.has(entry.port)) continue;
32800
32755
  if (IGNORED_COMMANDS.has(entry.command.toLowerCase())) continue;
32801
32756
  const cwd = cwdByPid.get(entry.pid);
32802
- if (!cwd || !cwd.startsWith(workspaceRoot)) continue;
32757
+ if (!cwd?.startsWith(workspaceRoot)) continue;
32803
32758
  const framework = detectFramework2(entry.command);
32804
32759
  const isDevRuntime = /^(node|python|ruby|bun|deno)$/i.test(entry.command);
32805
32760
  if (!framework && !isDevRuntime) continue;
@@ -35321,6 +35276,15 @@ function makeBinarySender(dc, guarded) {
35321
35276
  }
35322
35277
  };
35323
35278
  }
35279
+ function sendLoadedPluginPanelsToCollabPeer(dc, loadedPluginsRef) {
35280
+ if (loadedPluginsRef.current.length === 0) return;
35281
+ if (dc.readyState !== "open") return;
35282
+ const msg = {
35283
+ type: "plugin_panels_updated",
35284
+ plugins: loadedPluginsRef.current
35285
+ };
35286
+ dc.send(JSON.stringify(msg));
35287
+ }
35324
35288
  function buildCollabRoomManager(deps) {
35325
35289
  const {
35326
35290
  peerRoleRegistry,
@@ -35435,12 +35399,7 @@ function buildCollabRoomManager(deps) {
35435
35399
  housekeepingBarrier,
35436
35400
  skillResolutionService: daemon.skillResolution
35437
35401
  });
35438
- if (loadedPluginsRef.current.length > 0) {
35439
- daemon.taskManager.broadcastControl({
35440
- type: "plugin_panels_updated",
35441
- plugins: loadedPluginsRef.current
35442
- });
35443
- }
35402
+ sendLoadedPluginPanelsToCollabPeer(narrow(rawChannel), loadedPluginsRef);
35444
35403
  },
35445
35404
  onTaskMessageChannel: (machineId, rawChannel, taskId) => {
35446
35405
  if (!isTaskMessageChannelAllowed(taskId, collabTaskId)) {
@@ -36888,7 +36847,7 @@ function buildOutgoingMessage(report) {
36888
36847
  ...report.previousVersion ? { previousVersion: report.previousVersion } : {}
36889
36848
  };
36890
36849
  }
36891
- function wireUpdateStatusForwarding(report, sink2, onMessage, log) {
36850
+ function wireUpdateStatusForwarding(report, sink, onMessage, log) {
36892
36851
  if (!report) return;
36893
36852
  let sent = false;
36894
36853
  let unsub = null;
@@ -36896,7 +36855,7 @@ function wireUpdateStatusForwarding(report, sink2, onMessage, log) {
36896
36855
  if (sent) return;
36897
36856
  if (msg.type !== "authenticated") return;
36898
36857
  try {
36899
- sink2.send(buildOutgoingMessage(report));
36858
+ sink.send(buildOutgoingMessage(report));
36900
36859
  } catch (err3) {
36901
36860
  log?.warn({ err: err3 }, "update-status forward failed; will retry on next authenticated");
36902
36861
  return;
@@ -53350,7 +53309,7 @@ var McpCoordinator = class {
53350
53309
  if (snap.state !== "disabled") continue;
53351
53310
  if (this.#disabledNames.has(snap.name)) continue;
53352
53311
  const resolved = this.#resolvedByName.get(snap.name);
53353
- if (!resolved || resolved.transport !== "stdio") continue;
53312
+ if (resolved?.transport !== "stdio") continue;
53354
53313
  this.#deps.log({ event: "mcp_stdio_adoption_recovery", serverName: snap.name });
53355
53314
  this.#registry.dispatch(snap.name, { type: "enable", config: resolved });
53356
53315
  }
@@ -55223,7 +55182,7 @@ async function handleProxyHttpRequest(opts) {
55223
55182
  return;
55224
55183
  }
55225
55184
  const authHeader = opts.req.headers.authorization;
55226
- if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) {
55185
+ if (!authHeader?.toLowerCase().startsWith("bearer ")) {
55227
55186
  await respondProxyError(
55228
55187
  opts.req,
55229
55188
  opts.res,
@@ -55822,7 +55781,7 @@ async function buildMcpCoordinator(input) {
55822
55781
  resolveServerUrl: (name) => {
55823
55782
  const caps = input.getCapabilities();
55824
55783
  const server = caps?.mcpServers?.find((s2) => s2.name === name);
55825
- if (!server || !server.url) return null;
55784
+ if (!server?.url) return null;
55826
55785
  const out = { url: server.url };
55827
55786
  if (server.oauth?.callbackPort !== void 0 && server.oauth.callbackPort > 0) {
55828
55787
  out.callbackPort = server.oauth.callbackPort;
@@ -55942,6 +55901,13 @@ var DEFAULT_THRESHOLD_MS = 250;
55942
55901
  var DEFAULT_INTERVAL_MS4 = 100;
55943
55902
  var MACHINE_SLEEP_ARTIFACT_LAG_MS = 6e4;
55944
55903
  var ATTRIBUTION_EPSILON_MS = 100;
55904
+ var lastMachineSleepArtifactAtMs = 0;
55905
+ function markMachineSleepArtifact(atMs) {
55906
+ lastMachineSleepArtifactAtMs = atMs;
55907
+ }
55908
+ function didMachineSleepArtifactOccurSince(sinceMs) {
55909
+ return lastMachineSleepArtifactAtMs > sinceMs;
55910
+ }
55945
55911
  var EventLoopWatchdog = class {
55946
55912
  #log;
55947
55913
  #thresholdMs;
@@ -56005,6 +55971,7 @@ var EventLoopWatchdog = class {
56005
55971
  ...cause.attributedLabel !== void 0 && { attributedLabel: cause.attributedLabel }
56006
55972
  };
56007
55973
  if (lag > MACHINE_SLEEP_ARTIFACT_LAG_MS) {
55974
+ markMachineSleepArtifact(now);
56008
55975
  this.#log({ event: "machine_sleep_artifact", ...payload });
56009
55976
  telemeter("machine_sleep_artifact", payload);
56010
55977
  return;
@@ -56072,12 +56039,14 @@ var HealthMetrics = class {
56072
56039
  };
56073
56040
  hist.reset();
56074
56041
  const nowMs = Date.now();
56042
+ const windowStartMs = this.#lastCpuSampleAtMs;
56075
56043
  const cpuDelta = process.cpuUsage(this.#lastCpuUsage ?? void 0);
56076
56044
  const cpu = {
56077
56045
  userMs: cpuDelta.user / 1e3,
56078
56046
  systemMs: cpuDelta.system / 1e3,
56079
- windowMs: this.#lastCpuSampleAtMs > 0 ? nowMs - this.#lastCpuSampleAtMs : 0
56047
+ windowMs: windowStartMs > 0 ? nowMs - windowStartMs : 0
56080
56048
  };
56049
+ const postSleepArtifact = windowStartMs > 0 && (didMachineSleepArtifactOccurSince(windowStartMs) || cpu.windowMs - this.#intervalMs > MACHINE_SLEEP_ARTIFACT_LAG_MS);
56081
56050
  this.#lastCpuUsage = process.cpuUsage();
56082
56051
  this.#lastCpuSampleAtMs = nowMs;
56083
56052
  return {
@@ -56098,7 +56067,8 @@ var HealthMetrics = class {
56098
56067
  canvas: this.#canvasDocsCache.cachedDocCount
56099
56068
  },
56100
56069
  eventLoop,
56101
- cpu
56070
+ cpu,
56071
+ postSleepArtifact
56102
56072
  };
56103
56073
  }
56104
56074
  #emitSnapshot() {
@@ -56124,6 +56094,7 @@ var HealthMetrics = class {
56124
56094
  cpuUserMs: snap.cpu.userMs,
56125
56095
  cpuSystemMs: snap.cpu.systemMs,
56126
56096
  cpuWindowMs: snap.cpu.windowMs,
56097
+ ...snap.postSleepArtifact && { postSleepArtifact: true },
56127
56098
  windowEpoch
56128
56099
  });
56129
56100
  this.#metricsCollector.capture("health_snapshot", {
@@ -56137,7 +56108,8 @@ var HealthMetrics = class {
56137
56108
  loroCanvasDocCount: snap.loroDocs.canvas,
56138
56109
  eventLoopP99Ms: snap.eventLoop.p99,
56139
56110
  cpuUserMs: snap.cpu.userMs,
56140
- cpuSystemMs: snap.cpu.systemMs
56111
+ cpuSystemMs: snap.cpu.systemMs,
56112
+ ...snap.postSleepArtifact && { postSleepArtifact: true }
56141
56113
  });
56142
56114
  }
56143
56115
  };
@@ -60090,7 +60062,7 @@ var ScheduleEvaluator = class {
60090
60062
  }
60091
60063
  async #fireSchedule(schedule) {
60092
60064
  const freshSchedule = await this.#deps.loadSchedule(schedule.id);
60093
- if (!freshSchedule || !freshSchedule.enabled) {
60065
+ if (!freshSchedule?.enabled) {
60094
60066
  this.#deps.log({
60095
60067
  event: "schedule_fire_skipped",
60096
60068
  scheduleId: schedule.id,
@@ -61801,8 +61773,8 @@ var CodexAppServerClient = class {
61801
61773
  *
61802
61774
  * Calling again replaces the previous sink. Pass `null` to deregister.
61803
61775
  */
61804
- registerAccountSink(sink2) {
61805
- this.#accountSink = sink2;
61776
+ registerAccountSink(sink) {
61777
+ this.#accountSink = sink;
61806
61778
  }
61807
61779
  /**
61808
61780
  * Register the MCP server set to be sent on the next `thread/start` call.
@@ -62350,16 +62322,16 @@ var CodexAppServerClient = class {
62350
62322
  if (threadId === null) return false;
62351
62323
  if (!this.#compactThreads.has(threadId)) return false;
62352
62324
  if (method === CodexNotifications.error) {
62353
- const sink3 = this.#threadScopedSinks(params).onError;
62354
- if (!sink3) return false;
62325
+ const sink2 = this.#threadScopedSinks(params).onError;
62326
+ if (!sink2) return false;
62355
62327
  this.#compactThreads.delete(threadId);
62356
- sink3(params);
62328
+ sink2(params);
62357
62329
  return true;
62358
62330
  }
62359
62331
  if (!isContextCompactionItemNotification(method, params)) return false;
62360
- const sink2 = this.#threadScopedSinks(params).onUnregisteredTurnEvent;
62361
- if (!sink2) return false;
62362
- sink2({ method, params });
62332
+ const sink = this.#threadScopedSinks(params).onUnregisteredTurnEvent;
62333
+ if (!sink) return false;
62334
+ sink({ method, params });
62363
62335
  if (method === CodexNotifications.itemCompleted) {
62364
62336
  this.#compactThreads.delete(threadId);
62365
62337
  }
@@ -65398,6 +65370,35 @@ function isCodexResumeThreadNotFound(err3) {
65398
65370
  }
65399
65371
 
65400
65372
  // src/services/session/codex-subprocess-facade.ts
65373
+ function detectedCodexVersionForLog(input) {
65374
+ const { detectedCodexVersion, compatibility } = input;
65375
+ switch (compatibility.status) {
65376
+ case "compatible":
65377
+ return detectedCodexVersion ?? null;
65378
+ case "untested":
65379
+ return compatibility.detected;
65380
+ case "unknown":
65381
+ return detectedCodexVersion ?? null;
65382
+ default: {
65383
+ const _exhaustive = compatibility;
65384
+ return _exhaustive;
65385
+ }
65386
+ }
65387
+ }
65388
+ function formatCodexSpawnFailureMessage(input) {
65389
+ const { detail, compatibility } = input;
65390
+ switch (compatibility.status) {
65391
+ case "compatible":
65392
+ case "unknown":
65393
+ return `Codex spawn failed: ${detail}`;
65394
+ case "untested":
65395
+ return `Codex spawn failed: codex version mismatch; update Shipyard bundled Codex (detected ${compatibility.detected}, tested ${compatibility.tested}). Original error: ${detail}`;
65396
+ default: {
65397
+ const _exhaustive = compatibility;
65398
+ return _exhaustive;
65399
+ }
65400
+ }
65401
+ }
65401
65402
  function createCodexSubprocessFacade(options, log) {
65402
65403
  const pending = [];
65403
65404
  let inner = null;
@@ -65483,9 +65484,28 @@ function createCodexSubprocessFacade(options, log) {
65483
65484
  drainOnto(subprocess);
65484
65485
  } catch (err3) {
65485
65486
  const msg = err3 instanceof Error ? err3.message : String(err3);
65486
- log({ event: "codex_spawn_failed", detail: msg });
65487
+ const detectedCodexVersion = options.spawnArgs.codexVersion;
65488
+ const compatibility = classifyCodexCompatibility(detectedCodexVersion);
65489
+ const detectedForLog = detectedCodexVersionForLog({ detectedCodexVersion, compatibility });
65490
+ log({
65491
+ event: "codex_spawn_failed",
65492
+ detail: msg,
65493
+ codexVersionCompatibility: compatibility.status,
65494
+ detectedCodexVersion: detectedForLog,
65495
+ testedCodexVersion: TESTED_CODEX_VERSION,
65496
+ hint: compatibility.status === "untested" ? "codex version mismatch; update Shipyard bundled Codex" : null
65497
+ });
65498
+ telemeter("codex_spawn_failed", {
65499
+ errorName: err3 instanceof Error ? err3.name : "NonErrorThrown",
65500
+ codexVersionCompatibility: compatibility.status,
65501
+ detectedCodexVersion: detectedForLog,
65502
+ testedCodexVersion: TESTED_CODEX_VERSION
65503
+ });
65487
65504
  spawnFailed = true;
65488
- options.onEvent({ type: "sdk_error", error: `Codex spawn failed: ${msg}` });
65505
+ options.onEvent({
65506
+ type: "sdk_error",
65507
+ error: formatCodexSpawnFailureMessage({ detail: msg, compatibility })
65508
+ });
65489
65509
  options.onEvent({ type: "subprocess_died", exitCode: 1 });
65490
65510
  }
65491
65511
  })();
@@ -66037,7 +66057,7 @@ async function createHarnessHttpServer(opts = {}) {
66037
66057
  return;
66038
66058
  }
66039
66059
  const authHeader = req.headers.authorization;
66040
- if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) {
66060
+ if (!authHeader?.toLowerCase().startsWith("bearer ")) {
66041
66061
  await rejectAuth(req, res, 401, JSON_RPC_ERROR_UNAUTHORIZED, "unauthorized");
66042
66062
  return;
66043
66063
  }
@@ -66208,6 +66228,7 @@ function buildCodexSpawnArgs(args) {
66208
66228
  });
66209
66229
  return {
66210
66230
  binary: spawn15.binary,
66231
+ ...args.codexVersion !== void 0 ? { codexVersion: args.codexVersion } : {},
66211
66232
  argv: spawn15.argv,
66212
66233
  ...spawn15.env !== void 0 ? { env: spawn15.env } : {},
66213
66234
  stripEnv: args.stripEnv
@@ -66380,13 +66401,18 @@ function createCodexFacadeSubsystem(d) {
66380
66401
  unregisterCodexFacade
66381
66402
  });
66382
66403
  const codexFacadesByTaskId = /* @__PURE__ */ new Map();
66404
+ async function resolveRequiredCodexBinary() {
66405
+ const binary = await codexProfile.spawn.binaryResolver();
66406
+ if (binary === null) throw new Error("Codex binary not resolved");
66407
+ return binary;
66408
+ }
66383
66409
  const codexEngineRef = { current: null };
66384
66410
  function getOrCreateCodexEngine() {
66385
66411
  const existing = codexEngineRef.current;
66386
66412
  if (existing !== null && !existing.isDisposed) return existing;
66387
66413
  const engine = new CodexEngineSingleton({
66388
66414
  resolveSpawnConfig: async () => {
66389
- const binaryPath = await codexProfile.spawn.binaryResolver() ?? "codex";
66415
+ const binaryPath = await resolveRequiredCodexBinary();
66390
66416
  const spawn15 = rewriteElectronNodeScriptSpawn({
66391
66417
  binary: binaryPath,
66392
66418
  argv: ["app-server"]
@@ -66457,7 +66483,7 @@ function createCodexFacadeSubsystem(d) {
66457
66483
  state.pending.length = 0;
66458
66484
  }
66459
66485
  async function initCodexInner(state, input) {
66460
- const binary = await codexProfile.spawn.binaryResolver() ?? "codex";
66486
+ const binary = await resolveRequiredCodexBinary();
66461
66487
  const spawnCtx = {
66462
66488
  taskId: input.args.taskId,
66463
66489
  cwd: input.args.cwd,
@@ -66469,7 +66495,13 @@ function createCodexFacadeSubsystem(d) {
66469
66495
  const argv = codexProfile.spawn.args(spawnCtx);
66470
66496
  const envInject = codexProfile.spawn.envInject(spawnCtx);
66471
66497
  const stripEnv = codexProfile.spawn.envStrip;
66472
- const spawnArgs = buildCodexSpawnArgs({ binary, argv, env: envInject, stripEnv });
66498
+ const spawnArgs = buildCodexSpawnArgs({
66499
+ binary,
66500
+ codexVersion: CODEX_BUNDLED_VERSION,
66501
+ argv,
66502
+ env: envInject,
66503
+ stripEnv
66504
+ });
66473
66505
  const harnessReg = await input.harnessHttp.registerTask(
66474
66506
  buildHarnessRegisterCtx({
66475
66507
  taskId: input.args.taskId,
@@ -78858,10 +78890,10 @@ var ThreadStreamDeltaForwarder = class {
78858
78890
  #sinks = /* @__PURE__ */ new Set();
78859
78891
  #inFlight = [];
78860
78892
  #disposed = false;
78861
- addSink(sink2) {
78862
- this.#sinks.add(sink2);
78893
+ addSink(sink) {
78894
+ this.#sinks.add(sink);
78863
78895
  return () => {
78864
- this.#sinks.delete(sink2);
78896
+ this.#sinks.delete(sink);
78865
78897
  };
78866
78898
  }
78867
78899
  /** Shape a stream_delta event, buffer it for replay, and fan it out to sinks. */
@@ -78909,9 +78941,9 @@ var ThreadStreamDeltaForwarder = class {
78909
78941
  this.#inFlight = [];
78910
78942
  }
78911
78943
  #fanOut(msg) {
78912
- for (const sink2 of this.#sinks) {
78944
+ for (const sink of this.#sinks) {
78913
78945
  try {
78914
- sink2(msg);
78946
+ sink(msg);
78915
78947
  } catch {
78916
78948
  }
78917
78949
  }
@@ -79439,8 +79471,8 @@ var Thread = class {
79439
79471
  denyAllPendingPermissions(reason) {
79440
79472
  this.#permissionHandler.denyAllPending(reason);
79441
79473
  }
79442
- addStreamDeltaSink(sink2) {
79443
- return this.#streamDelta.addSink(sink2);
79474
+ addStreamDeltaSink(sink) {
79475
+ return this.#streamDelta.addSink(sink);
79444
79476
  }
79445
79477
  getInFlightStreamDeltas() {
79446
79478
  return this.#streamDelta.getInFlight();
@@ -81184,6 +81216,132 @@ ${diff}
81184
81216
  }
81185
81217
  }
81186
81218
 
81219
+ // src/services/plan-backfill/plan-storage-recovery.ts
81220
+ var PLAN_STORAGE_RECOVERY_MAX_ATTEMPTS = 4;
81221
+ var BASE_BACKOFF_MS = 750;
81222
+ var MAX_BACKOFF_MS = 6e3;
81223
+ function planStorageRecoveryBackoffMs(attempt) {
81224
+ return Math.min(BASE_BACKOFF_MS * 2 ** Math.max(0, attempt - 1), MAX_BACKOFF_MS);
81225
+ }
81226
+ function decidePlanStorageRecovery(state) {
81227
+ switch (state.outcome) {
81228
+ case "has-content":
81229
+ case "rebuilt":
81230
+ return { kind: "recovered" };
81231
+ case "no-source":
81232
+ return { kind: "exhausted" };
81233
+ case "storage-error":
81234
+ case "failed":
81235
+ return state.attempts >= state.maxAttempts ? { kind: "give_up" } : { kind: "retry" };
81236
+ default: {
81237
+ const _exhaustive = state.outcome;
81238
+ void _exhaustive;
81239
+ return { kind: "give_up" };
81240
+ }
81241
+ }
81242
+ }
81243
+
81244
+ // src/services/plan/plan-storage-recovery-controller.ts
81245
+ function defaultScheduleStorageRecovery(fn, delayMs) {
81246
+ const timer = setTimeout(fn, delayMs);
81247
+ return () => clearTimeout(timer);
81248
+ }
81249
+ var PlanStorageRecoveryController = class {
81250
+ #deps;
81251
+ /**
81252
+ * One-shot guard: true while a cycle is scheduled or running, so repeated
81253
+ * empty replays (reconnects, several viewers requesting state) cannot start
81254
+ * overlapping evict-and-reload loops against the live doc.
81255
+ */
81256
+ #inFlight = false;
81257
+ /** Cancel handle for the pending attempt's deferred timer; null when none armed. */
81258
+ #cancel = null;
81259
+ constructor(deps) {
81260
+ this.#deps = deps;
81261
+ }
81262
+ /** Begin a recovery cycle for `detection`. No-op if disposed or already running. */
81263
+ schedule(detection) {
81264
+ if (this.#deps.isDisposed() || this.#inFlight) return;
81265
+ this.#inFlight = true;
81266
+ this.#deps.log({ event: "plan_storage_recovery_scheduled", taskId: this.#deps.taskId });
81267
+ this.#scheduleAttempt(detection, 1);
81268
+ }
81269
+ /** Cancel any pending attempt and clear the one-shot guard (dispose/rewind). */
81270
+ cancel() {
81271
+ this.#cancel?.();
81272
+ this.#cancel = null;
81273
+ this.#inFlight = false;
81274
+ }
81275
+ #scheduleAttempt(detection, attempt) {
81276
+ const fire = () => {
81277
+ this.#cancel = null;
81278
+ if (this.#deps.isDisposed()) {
81279
+ this.#inFlight = false;
81280
+ return;
81281
+ }
81282
+ this.#deps.enqueueAsync(() => this.#runAttempt(detection, attempt));
81283
+ };
81284
+ const schedule = this.#deps.scheduleStorageRecovery ?? defaultScheduleStorageRecovery;
81285
+ this.#cancel = schedule(fire, planStorageRecoveryBackoffMs(attempt));
81286
+ }
81287
+ async #runAttempt(detection, attempt) {
81288
+ if (this.#deps.isDisposed() || !this.#inFlight) return;
81289
+ const evicted = await this.#deps.planRepo.invalidate(this.#deps.taskId);
81290
+ if (!evicted) {
81291
+ this.#deps.log({
81292
+ event: "plan_storage_recovery_evict_blocked",
81293
+ taskId: this.#deps.taskId,
81294
+ attempt
81295
+ });
81296
+ }
81297
+ if (this.#deps.isDisposed() || !this.#inFlight) return;
81298
+ const outcome = await ensurePlanDocRebuilt({
81299
+ taskId: this.#deps.taskId,
81300
+ planRepo: this.#deps.planRepo,
81301
+ taskRecord: { lastPlanDetection: { filePath: detection.filePath } },
81302
+ dataDir: this.#deps.dataDir,
81303
+ shipyardHome: getShipyardHome(),
81304
+ log: this.#deps.log
81305
+ });
81306
+ if (this.#deps.isDisposed() || !this.#inFlight) return;
81307
+ const action = decidePlanStorageRecovery({
81308
+ outcome: outcome.kind,
81309
+ attempts: attempt,
81310
+ maxAttempts: PLAN_STORAGE_RECOVERY_MAX_ATTEMPTS
81311
+ });
81312
+ switch (action.kind) {
81313
+ case "recovered":
81314
+ this.#inFlight = false;
81315
+ this.#deps.log({
81316
+ event: "plan_storage_recovery_succeeded",
81317
+ taskId: this.#deps.taskId,
81318
+ attempt
81319
+ });
81320
+ this.#deps.reEmitPlanDetected(detection);
81321
+ return;
81322
+ case "retry":
81323
+ this.#scheduleAttempt(detection, attempt + 1);
81324
+ return;
81325
+ case "exhausted":
81326
+ case "give_up":
81327
+ this.#inFlight = false;
81328
+ this.#deps.log({
81329
+ event: "plan_storage_recovery_gave_up",
81330
+ taskId: this.#deps.taskId,
81331
+ attempt,
81332
+ reason: action.kind
81333
+ });
81334
+ return;
81335
+ default: {
81336
+ const _exhaustive = action;
81337
+ void _exhaustive;
81338
+ this.#inFlight = false;
81339
+ return;
81340
+ }
81341
+ }
81342
+ }
81343
+ };
81344
+
81187
81345
  // src/services/plan/plan-handler.ts
81188
81346
  var PLAN_PUBLISHED_DENY_MESSAGE = `Your plan has been published to the Shipyard canvas for collaborative review. Reviewers can comment, edit, and approve your plan in real-time. You will receive feedback as <${XML_TAGS.COMMENT}> children of the <plan> resource and plan edits as <${XML_TAGS.PLAN_UPDATE}> diffs. Wait for <${XML_TAGS.PLAN_REVIEW} decision="approve"> before calling ExitPlanMode again.`;
81189
81347
  var PLAN_STILL_UNDER_REVIEW_MESSAGE = `Your plan is still under review. Wait for <${XML_TAGS.PLAN_REVIEW} decision="approve"> before calling ExitPlanMode again.`;
@@ -81250,8 +81408,23 @@ var PlanHandler = class {
81250
81408
  * single chained promise here suffices.
81251
81409
  */
81252
81410
  #codexPlanQueue = Promise.resolve();
81411
+ /**
81412
+ * #4770 storage-error recovery loop (evict + reload + re-emit). Owns its own
81413
+ * one-shot/timer state; cancelled on dispose/rewind. See the controller.
81414
+ */
81415
+ #storageRecovery;
81253
81416
  constructor(deps) {
81254
81417
  this.#deps = deps;
81418
+ this.#storageRecovery = new PlanStorageRecoveryController({
81419
+ taskId: deps.taskId,
81420
+ planRepo: deps.planRepo,
81421
+ dataDir: deps.dataDir,
81422
+ isDisposed: deps.isDisposed,
81423
+ enqueueAsync: deps.enqueueAsync,
81424
+ log: deps.log,
81425
+ reEmitPlanDetected: (detection) => this.#reEmitPlanDetected(detection),
81426
+ scheduleStorageRecovery: deps.scheduleStorageRecovery
81427
+ });
81255
81428
  if (deps.initialPlanDetection) {
81256
81429
  this.#lastPlanDetection = deps.initialPlanDetection;
81257
81430
  this.#lastPersistedPlanDetection = deps.initialPlanDetection;
@@ -81666,7 +81839,24 @@ var PlanHandler = class {
81666
81839
  shipyardHome: getShipyardHome(),
81667
81840
  log: this.#deps.log
81668
81841
  });
81669
- if (outcome.kind !== "rebuilt" || this.#deps.isDisposed()) return;
81842
+ if (this.#deps.isDisposed()) return;
81843
+ if (outcome.kind === "storage-error") {
81844
+ this.#storageRecovery.schedule(detection);
81845
+ return;
81846
+ }
81847
+ if (outcome.kind !== "rebuilt") return;
81848
+ this.#reEmitPlanDetected(detection);
81849
+ }
81850
+ /**
81851
+ * Re-broadcast `plan_detected` carrying the plan doc's CURRENT markdown via
81852
+ * the durable path (current sender, else the persist queue) rather than a
81853
+ * captured per-peer `send`, so the populated state reaches every browser —
81854
+ * owner and collab viewers (`plan_detected` is collab-whitelisted) — even if
81855
+ * the original requester reconnected meanwhile. No-op when the doc is still
81856
+ * empty, so a failed recovery never emits a misleading empty re-detection.
81857
+ */
81858
+ #reEmitPlanDetected(detection) {
81859
+ if (this.#deps.isDisposed()) return;
81670
81860
  const markdown = this.#deps.planRepo.getContent(this.#deps.taskId);
81671
81861
  if (markdown.trim() === "") return;
81672
81862
  const msg = this.#buildPlanDetectedReplay(detection, markdown);
@@ -81715,6 +81905,7 @@ var PlanHandler = class {
81715
81905
  this.#createdPlanFiles.clear();
81716
81906
  this.#proactiveToolUseId = null;
81717
81907
  this.#detectionInFlight = false;
81908
+ this.#storageRecovery.cancel();
81718
81909
  void this.#planFileBridge?.dispose();
81719
81910
  this.#planFileBridge = null;
81720
81911
  }
@@ -81722,6 +81913,7 @@ var PlanHandler = class {
81722
81913
  this.#approvalReceived = false;
81723
81914
  this.#planPublished = false;
81724
81915
  this.#stopPlanWatch();
81916
+ this.#storageRecovery.cancel();
81725
81917
  void this.#planFileBridge?.dispose();
81726
81918
  this.#planFileBridge = null;
81727
81919
  this.#processedPlanToolUseIds.clear();
@@ -85866,7 +86058,7 @@ function maybeEmitCursorWindowOccupancy(ctx, event) {
85866
86058
  handleAutoCompactionSnapshot(ctx, synthetic, resolved.windowTokens, resolved.source === "held");
85867
86059
  }
85868
86060
  function priorOccupancyFromTurnStats(prior) {
85869
- if (!prior || prior.type !== "turn_stats") return null;
86061
+ if (prior?.type !== "turn_stats") return null;
85870
86062
  const used = prior.liveContextTokens ?? 0;
85871
86063
  const priorWindow = prior.contextWindow ?? 0;
85872
86064
  if (used <= 0 || priorWindow <= 0) return null;
@@ -86782,7 +86974,7 @@ var RoiTracker = class {
86782
86974
  function syntheticResourceUri(msg) {
86783
86975
  if (!msg.isSynthetic || msg.content.length !== 1) return null;
86784
86976
  const block = msg.content[0];
86785
- if (!block || block.type !== "resource") return null;
86977
+ if (block?.type !== "resource") return null;
86786
86978
  if (!("uri" in block.resource)) return null;
86787
86979
  return block.resource.uri;
86788
86980
  }
@@ -86790,7 +86982,7 @@ function seedPushedEverFromHistory(msgs, pushManager) {
86790
86982
  const seeded = /* @__PURE__ */ new Set();
86791
86983
  for (const msg of msgs) {
86792
86984
  const uri = syntheticResourceUri(msg);
86793
- if (!uri || !uri.startsWith("shipyard://")) continue;
86985
+ if (!uri?.startsWith("shipyard://")) continue;
86794
86986
  if (seeded.has(uri)) continue;
86795
86987
  seeded.add(uri);
86796
86988
  pushManager.seedSessionPushed(uri);
@@ -88183,7 +88375,7 @@ var StructuredTaskTracker = class {
88183
88375
  const overlay = this.#currentOverlay ?? DEFAULT_TASK_OVERLAY;
88184
88376
  const merged = applyOverlayToMap(this.#structuredTasks, overlay);
88185
88377
  const item = merged.get(itemId);
88186
- if (!item || item.status !== "in_progress") return;
88378
+ if (item?.status !== "in_progress") return;
88187
88379
  const caps = this.#resolveEffectiveCaps(itemId, item);
88188
88380
  this.#maybeFireItemActivated(itemId, caps);
88189
88381
  }
@@ -91104,8 +91296,8 @@ var Task = class {
91104
91296
  thread.clearControlChannelSend();
91105
91297
  }
91106
91298
  }
91107
- addStreamDeltaSink(sink2) {
91108
- return this.#getPrimary().addStreamDeltaSink(sink2);
91299
+ addStreamDeltaSink(sink) {
91300
+ return this.#getPrimary().addStreamDeltaSink(sink);
91109
91301
  }
91110
91302
  getInFlightStreamDeltas() {
91111
91303
  return this.#getPrimary().getInFlightStreamDeltas();
@@ -94970,7 +95162,7 @@ function extractTaskSnapshotDigest(ctx) {
94970
95162
  return JSON.stringify(parts);
94971
95163
  }
94972
95164
  function summarizePrState(payload) {
94973
- if (!payload || !payload.currentBranchPR) {
95165
+ if (!payload?.currentBranchPR) {
94974
95166
  return { prState: null, ci: null, reviewers: null, mergeable: null };
94975
95167
  }
94976
95168
  const pr = payload.currentBranchPR;
@@ -95029,7 +95221,7 @@ function findPendingAskQuestion(notifications) {
95029
95221
  function findLastAssistantPreview(messages) {
95030
95222
  for (let i = messages.length - 1; i >= 0; i--) {
95031
95223
  const m2 = messages[i];
95032
- if (!m2 || m2.senderKind !== "agent") continue;
95224
+ if (m2?.senderKind !== "agent") continue;
95033
95225
  const text = collectAgentText(m2);
95034
95226
  if (!text) continue;
95035
95227
  return previewSlice(text);
@@ -97708,8 +97900,8 @@ async function createDaemon(deps) {
97708
97900
  throw err3;
97709
97901
  }
97710
97902
  },
97711
- wireUpdateStatusForwarding: (sink2, onMessage) => {
97712
- wireUpdateStatusForwarding(pendingUpdateReport, sink2, onMessage);
97903
+ wireUpdateStatusForwarding: (sink, onMessage) => {
97904
+ wireUpdateStatusForwarding(pendingUpdateReport, sink, onMessage);
97713
97905
  pendingUpdateReport = null;
97714
97906
  },
97715
97907
  findVizBySlug: (slug) => {
@@ -100994,4 +101186,4 @@ export {
100994
101186
  decideWorkspaceScope,
100995
101187
  serve
100996
101188
  };
100997
- //# sourceMappingURL=serve-25I4ML7R.js.map
101189
+ //# sourceMappingURL=serve-DAP6V7SY.js.map