@schoolai/shipyard 3.23.0 → 3.24.0-rc.20260813.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 (36) hide show
  1. package/dist/capability-detector-worker.js +4 -4
  2. package/dist/{chunk-QI5ERXFK.js → chunk-BGJ6QJNS.js} +27 -2
  3. package/dist/chunk-BGJ6QJNS.js.map +1 -0
  4. package/dist/{chunk-Y4RRW6MA.js → chunk-BLGHJ3OJ.js} +2 -2
  5. package/dist/{chunk-OCYT65KO.js → chunk-BN54GQR3.js} +139 -20
  6. package/dist/chunk-BN54GQR3.js.map +1 -0
  7. package/dist/{chunk-W3EMVBWP.js → chunk-DDC5OQOC.js} +3 -3
  8. package/dist/{chunk-7E6VXTEO.js → chunk-JZTUQVHQ.js} +2 -2
  9. package/dist/{chunk-CKQL7TWQ.js → chunk-NG355H2L.js} +39 -14
  10. package/dist/chunk-NG355H2L.js.map +1 -0
  11. package/dist/{chunk-UESTOND6.js → chunk-RL25HGER.js} +3 -3
  12. package/dist/chunk-RL25HGER.js.map +1 -0
  13. package/dist/{chunk-HQTCVJRK.js → chunk-TU3MSY2I.js} +2 -2
  14. package/dist/{chunk-LRBG3X5U.js → chunk-UK3IMOSP.js} +2 -2
  15. package/dist/cursor-runner.js +24 -17
  16. package/dist/cursor-runner.js.map +1 -1
  17. package/dist/electron-utility.js +2 -2
  18. package/dist/index.js +4 -4
  19. package/dist/{login-WL5WEEGJ.js → login-AX7SM2SJ.js} +3 -3
  20. package/dist/{plan-backfill-EF6KR6BE.js → plan-backfill-XYOUK2DY.js} +4 -4
  21. package/dist/{serve-J3MG3WHK.js → serve-QHLWAVVY.js} +554 -231
  22. package/dist/{serve-J3MG3WHK.js.map → serve-QHLWAVVY.js.map} +1 -1
  23. package/dist/{start-TCI35UML.js → start-JJCGZ63I.js} +6 -6
  24. package/package.json +1 -1
  25. package/dist/chunk-CKQL7TWQ.js.map +0 -1
  26. package/dist/chunk-OCYT65KO.js.map +0 -1
  27. package/dist/chunk-QI5ERXFK.js.map +0 -1
  28. package/dist/chunk-UESTOND6.js.map +0 -1
  29. /package/dist/{chunk-Y4RRW6MA.js.map → chunk-BLGHJ3OJ.js.map} +0 -0
  30. /package/dist/{chunk-W3EMVBWP.js.map → chunk-DDC5OQOC.js.map} +0 -0
  31. /package/dist/{chunk-7E6VXTEO.js.map → chunk-JZTUQVHQ.js.map} +0 -0
  32. /package/dist/{chunk-HQTCVJRK.js.map → chunk-TU3MSY2I.js.map} +0 -0
  33. /package/dist/{chunk-LRBG3X5U.js.map → chunk-UK3IMOSP.js.map} +0 -0
  34. /package/dist/{login-WL5WEEGJ.js.map → login-AX7SM2SJ.js.map} +0 -0
  35. /package/dist/{plan-backfill-EF6KR6BE.js.map → plan-backfill-XYOUK2DY.js.map} +0 -0
  36. /package/dist/{start-TCI35UML.js.map → start-JJCGZ63I.js.map} +0 -0
@@ -12,7 +12,7 @@ import {
12
12
  guardedSubscribe,
13
13
  makeInitialAttemptState,
14
14
  shutdownFileWatcherGuard
15
- } from "./chunk-7E6VXTEO.js";
15
+ } from "./chunk-JZTUQVHQ.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-LRBG3X5U.js";
42
+ } from "./chunk-UK3IMOSP.js";
43
43
  import {
44
44
  loadAuthToken
45
45
  } from "./chunk-2UE3TNY7.js";
46
46
  import {
47
47
  getDaemonVersion
48
- } from "./chunk-W3EMVBWP.js";
48
+ } from "./chunk-DDC5OQOC.js";
49
49
  import {
50
50
  collectPriorCrashDetail,
51
51
  getRecentWasmPanicMessages
@@ -56,6 +56,7 @@ import {
56
56
  CHARS_PER_TOKEN,
57
57
  CODEX_BUNDLED_VERSION,
58
58
  CURSOR_MODEL_CATALOG,
59
+ DAEMON_PR_COLD_DISCOVERY_INTERVAL_MS,
59
60
  DAEMON_PR_POLL_INTERVAL_MS,
60
61
  PROFILE_AUTH_ADAPTERS,
61
62
  REDACTED_THINKING_PLACEHOLDER,
@@ -87,6 +88,7 @@ import {
87
88
  cursorImportConversation,
88
89
  cursorPermissionModeMap,
89
90
  decideCursorAuthRefresh,
91
+ decidePrColdDiscoveryPlan,
90
92
  decidePrPollPlan,
91
93
  deduplicatePRs,
92
94
  detectAgentProviders,
@@ -135,6 +137,7 @@ import {
135
137
  notificationsSince,
136
138
  pickLastKnownCapabilities,
137
139
  profileRegistry,
140
+ reconcileCursorSupportsFastMode,
138
141
  recordGithubReadFailure,
139
142
  refreshCodexAuthSlice,
140
143
  refreshCommands,
@@ -154,6 +157,7 @@ import {
154
157
  searchPrs,
155
158
  setBootstrapPhaseBreadcrumbSink,
156
159
  setBootstrapPhaseMetricsSink,
160
+ setCursorFastModeSupportLookup,
157
161
  skipForMainChannel,
158
162
  snapshotProfileAuth,
159
163
  supportsAdaptiveThinking,
@@ -174,7 +178,7 @@ import {
174
178
  unresolveReviewThread,
175
179
  withLabelAsync,
176
180
  writeCursorApiKeyToVault
177
- } from "./chunk-OCYT65KO.js";
181
+ } from "./chunk-BN54GQR3.js";
178
182
  import {
179
183
  buildNodeSpawnEnv,
180
184
  nodeSpawnEnvOverlay,
@@ -229,14 +233,15 @@ import {
229
233
  createOneShotPool
230
234
  } from "./chunk-ZFKJAYAN.js";
231
235
  import {
232
- parseRunnerToDaemon
233
- } from "./chunk-CKQL7TWQ.js";
236
+ parseRunnerToDaemon,
237
+ shouldUseCursorFastModePricing
238
+ } from "./chunk-NG355H2L.js";
234
239
  import {
235
240
  XML_TAGS,
236
241
  createAssetResolver,
237
242
  toSdkContent,
238
243
  toSdkPermissionMode
239
- } from "./chunk-HQTCVJRK.js";
244
+ } from "./chunk-TU3MSY2I.js";
240
245
  import {
241
246
  AGENT_IDS,
242
247
  AGENT_SYSTEM_CLAUDE_CODE,
@@ -441,7 +446,7 @@ import {
441
446
  unsafeAssertSourceId,
442
447
  vizFileExtension,
443
448
  withPreferredRuntimeAuthMethod
444
- } from "./chunk-QI5ERXFK.js";
449
+ } from "./chunk-BGJ6QJNS.js";
445
450
  import {
446
451
  AuthGitHubCallbackRequestSchema,
447
452
  AuthGitHubCallbackResponseSchema,
@@ -489,7 +494,7 @@ import {
489
494
  VaultKeyPutResponseSchema,
490
495
  classifyClaudeCodeCompatibility,
491
496
  classifyCodexCompatibility
492
- } from "./chunk-UESTOND6.js";
497
+ } from "./chunk-RL25HGER.js";
493
498
  import {
494
499
  external_exports as external_exports2
495
500
  } from "./chunk-EHQITHQX.js";
@@ -6016,7 +6021,7 @@ function nanoid(size = 21) {
6016
6021
  }
6017
6022
 
6018
6023
  // src/services/bootstrap/signaling.ts
6019
- var DAEMON_NPM_VERSION = true ? "3.23.0" : "unknown";
6024
+ var DAEMON_NPM_VERSION = true ? "3.24.0" : "unknown";
6020
6025
  function createDaemonSignaling(config) {
6021
6026
  const agentId = config.agentId ?? nanoid();
6022
6027
  function send(msg) {
@@ -7350,6 +7355,9 @@ function computeTransportEnabled(server, live) {
7350
7355
  return server.enabled;
7351
7356
  }
7352
7357
  function resolveSnapshotAwareLive(s2, sessionEntry, workspaceLive, inSnapshot) {
7358
+ if (workspaceLive?.status === "needs-auth") {
7359
+ return { live: workspaceLive, transportEnabled: true };
7360
+ }
7353
7361
  if (sessionEntry) {
7354
7362
  return { live: sessionEntry, transportEnabled: sessionEntry.status !== "disabled" };
7355
7363
  }
@@ -7362,6 +7370,22 @@ function resolveSnapshotAwareLive(s2, sessionEntry, workspaceLive, inSnapshot) {
7362
7370
  transportEnabled: computeTransportEnabled(s2, workspaceLive)
7363
7371
  };
7364
7372
  }
7373
+ function collectMcpSessionCoordinatorDivergences(sessionServers, liveStatuses) {
7374
+ const out = [];
7375
+ for (const [name, session] of Object.entries(sessionServers)) {
7376
+ if (!session) continue;
7377
+ const coordinator = liveStatuses[name];
7378
+ if (!coordinator) continue;
7379
+ if (session.status === "connected" && coordinator.status === "needs-auth") {
7380
+ out.push({
7381
+ name,
7382
+ sessionStatus: session.status,
7383
+ coordinatorStatus: coordinator.status
7384
+ });
7385
+ }
7386
+ }
7387
+ return out;
7388
+ }
7365
7389
  function buildWorkspaceRow(s2, live, transportEnabled, userEnabled) {
7366
7390
  const accountIssue = s2.source === "claudeai" && s2.authStatus === "unauthenticated" && s2.url === void 0 ? {
7367
7391
  errorType: "agent_signin_required",
@@ -12063,32 +12087,69 @@ function createPublishTools(ctx) {
12063
12087
  true
12064
12088
  );
12065
12089
  }
12066
- try {
12067
- await deletePublish({
12068
- sessionServerUrl: ctx.sessionServerUrl,
12069
- authToken: token,
12070
- publishId
12071
- });
12072
- const store = ctx.publishedArtifactStore;
12073
- if (store) {
12074
- const taskId = resolveHarnessTaskId(ctx.taskId, ctx.getTaskIdForThread);
12075
- const allBindings = store.listByTask(taskId);
12076
- const match = allBindings.find((b2) => b2.publishId === publishId);
12077
- if (match) {
12078
- await store.deleteBinding(taskId, match.bindKind, match.bindKey);
12079
- }
12080
- }
12090
+ const taskId = resolveHarnessTaskId(ctx.taskId, ctx.getTaskIdForThread);
12091
+ const outcome = await runUnpublish({
12092
+ publishId,
12093
+ taskId,
12094
+ sessionServerUrl: ctx.sessionServerUrl,
12095
+ authToken: token,
12096
+ publishedArtifactStore: ctx.publishedArtifactStore
12097
+ });
12098
+ if (outcome.ok) {
12081
12099
  return textResult(JSON.stringify({ ok: true }));
12082
- } catch (err3) {
12083
- return textResult(
12084
- `Failed to unpublish: ${err3 instanceof Error ? err3.message : String(err3)}`,
12085
- true
12086
- );
12087
12100
  }
12101
+ return textResult(outcome.error, true);
12088
12102
  }
12089
12103
  }
12090
12104
  ];
12091
12105
  }
12106
+ async function runUnpublish(params) {
12107
+ try {
12108
+ await deletePublish({
12109
+ sessionServerUrl: params.sessionServerUrl,
12110
+ authToken: params.authToken,
12111
+ publishId: params.publishId
12112
+ });
12113
+ } catch (err3) {
12114
+ return {
12115
+ ok: false,
12116
+ error: `Failed to unpublish: ${err3 instanceof Error ? err3.message : String(err3)}`
12117
+ };
12118
+ }
12119
+ const store = params.publishedArtifactStore;
12120
+ if (store) {
12121
+ const match = store.listByTask(params.taskId).find((b2) => b2.publishId === params.publishId);
12122
+ if (match) {
12123
+ await store.deleteBinding(params.taskId, match.bindKind, match.bindKey);
12124
+ }
12125
+ }
12126
+ return { ok: true };
12127
+ }
12128
+ async function handleUnpublishRequest(params) {
12129
+ const { correlationId, sendControl } = params;
12130
+ const token = params.getAuthToken();
12131
+ if (!token) {
12132
+ sendControl({
12133
+ type: "unpublish_result",
12134
+ correlationId,
12135
+ ok: false,
12136
+ error: "Not authenticated. Please log in before unpublishing."
12137
+ });
12138
+ return;
12139
+ }
12140
+ const outcome = await runUnpublish({
12141
+ publishId: params.publishId,
12142
+ taskId: params.taskId,
12143
+ sessionServerUrl: params.sessionServerUrl,
12144
+ authToken: token,
12145
+ publishedArtifactStore: params.publishedArtifactStore
12146
+ });
12147
+ if (outcome.ok) {
12148
+ sendControl({ type: "unpublish_result", correlationId, ok: true });
12149
+ } else {
12150
+ sendControl({ type: "unpublish_result", correlationId, ok: false, error: outcome.error });
12151
+ }
12152
+ }
12092
12153
  async function handlePublishRequest(params) {
12093
12154
  const { correlationId, target, ttl, title, sendControl } = params;
12094
12155
  function emitProgress(phase, detail) {
@@ -14692,6 +14753,7 @@ function filterOutboundForCollab(msg, collabTaskId, getTaskSync) {
14692
14753
  */
14693
14754
  case "publish_progress":
14694
14755
  case "publish_result":
14756
+ case "unpublish_result":
14695
14757
  return null;
14696
14758
  /**
14697
14759
  * task_index_version is a heartbeat staleness probe response carrying the
@@ -15461,6 +15523,61 @@ async function routeRequestSkills(args) {
15461
15523
  args.sendControl(args.buildSnapshot());
15462
15524
  }
15463
15525
 
15526
+ // src/services/channels/control-channel/unpublish-request-wiring.ts
15527
+ function wireOnRequestUnpublish(deps, logAdapter) {
15528
+ return ({ correlationId, taskId, publishId, sendControl }) => {
15529
+ logAdapter({
15530
+ event: "unpublish_request_received",
15531
+ correlationId,
15532
+ taskId,
15533
+ publishId
15534
+ });
15535
+ const wrappedSendControl = (msg) => {
15536
+ if (msg.type === "unpublish_result") {
15537
+ logAdapter({
15538
+ event: "unpublish_result",
15539
+ correlationId,
15540
+ ok: msg.ok,
15541
+ error: msg.ok ? void 0 : msg.error
15542
+ });
15543
+ }
15544
+ sendControl(msg);
15545
+ };
15546
+ const sessionServerUrl = deps.sessionServerUrl;
15547
+ const getAuthToken = deps.getAuthToken;
15548
+ if (!sessionServerUrl || !getAuthToken) {
15549
+ wrappedSendControl({
15550
+ type: "unpublish_result",
15551
+ correlationId,
15552
+ ok: false,
15553
+ error: "Unpublish not available: session server not configured."
15554
+ });
15555
+ return;
15556
+ }
15557
+ handleUnpublishRequest({
15558
+ correlationId,
15559
+ taskId,
15560
+ publishId,
15561
+ sessionServerUrl,
15562
+ getAuthToken,
15563
+ publishedArtifactStore: deps.publishedArtifactStore ?? null,
15564
+ sendControl: wrappedSendControl
15565
+ }).catch((err3) => {
15566
+ logAdapter({
15567
+ event: "unpublish_request_unhandled_error",
15568
+ correlationId,
15569
+ error: err3 instanceof Error ? err3.message : String(err3)
15570
+ });
15571
+ wrappedSendControl({
15572
+ type: "unpublish_result",
15573
+ correlationId,
15574
+ ok: false,
15575
+ error: `Unexpected error: ${err3 instanceof Error ? err3.message : String(err3)}`
15576
+ });
15577
+ });
15578
+ };
15579
+ }
15580
+
15464
15581
  // src/services/channels/control-channel-handler.ts
15465
15582
  function handleControlChannel(send, callbacks, log) {
15466
15583
  function sendControl(msg) {
@@ -15957,6 +16074,14 @@ function routeMessage(msg, callbacks, sendControl, log) {
15957
16074
  sendControl
15958
16075
  });
15959
16076
  break;
16077
+ case "request_unpublish":
16078
+ callbacks.onRequestUnpublish({
16079
+ correlationId: msg.correlationId,
16080
+ taskId: msg.taskId,
16081
+ publishId: msg.publishId,
16082
+ sendControl
16083
+ });
16084
+ break;
15960
16085
  case "start_from_linear":
15961
16086
  callbacks.onStartFromLinear(msg);
15962
16087
  break;
@@ -21436,7 +21561,11 @@ function matchCollabQueueCorrelationIds(messages, queueEntries) {
21436
21561
  }
21437
21562
 
21438
21563
  // src/services/session/claude-tool-guard.ts
21439
- var SHIPYARD_DENIED_CLAUDE_TOOLS = ["Artifact"];
21564
+ var SHIPYARD_DENIED_CLAUDE_TOOLS = [
21565
+ "Artifact",
21566
+ "EnterWorktree",
21567
+ "ExitWorktree"
21568
+ ];
21440
21569
  function detectDeniedToolsPresent(initTools, denied = SHIPYARD_DENIED_CLAUDE_TOOLS) {
21441
21570
  const present = new Set(initTools);
21442
21571
  return denied.filter((name) => present.has(name));
@@ -27032,6 +27161,14 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
27032
27161
  });
27033
27162
  });
27034
27163
  },
27164
+ onRequestUnpublish: wireOnRequestUnpublish(
27165
+ {
27166
+ sessionServerUrl: deps?.sessionServerUrl,
27167
+ getAuthToken: deps?.getAuthToken,
27168
+ publishedArtifactStore: deps?.publishedArtifactStore
27169
+ },
27170
+ logAdapter
27171
+ ),
27035
27172
  onStartFromLinear: (payload) => linearStartHandler.handleStartFromLinear(payload),
27036
27173
  /**
27037
27174
  * Browser-initiated flush for a specific resource URI (PROTOCOL_VERSION 48).
@@ -33580,6 +33717,7 @@ function buildCollabRoomManager(deps) {
33580
33717
  import { randomUUID as randomUUID25 } from "crypto";
33581
33718
  import { mkdir as mkdir36 } from "fs/promises";
33582
33719
  import { join as join79 } from "path";
33720
+ import { getHeapStatistics } from "v8";
33583
33721
 
33584
33722
  // src/shared/capabilities/cursor-boot-auth.ts
33585
33723
  async function mergeCursorAuthFromVault(caps, detect, log) {
@@ -34291,6 +34429,24 @@ function mergeWithDefaultComposerSettings(caller, defaults) {
34291
34429
  return normalizeComposerSettings(merged);
34292
34430
  }
34293
34431
 
34432
+ // src/services/task/session-activity.ts
34433
+ function isActiveUserWork(state) {
34434
+ switch (state) {
34435
+ case "spawning":
34436
+ case "running":
34437
+ case "stopping":
34438
+ return true;
34439
+ case "warm_idle":
34440
+ case "resumable_idle":
34441
+ case "cold_idle":
34442
+ return false;
34443
+ default: {
34444
+ const _exhaustive = state;
34445
+ throw new Error(`Unhandled session state: ${JSON.stringify(_exhaustive)}`);
34446
+ }
34447
+ }
34448
+ }
34449
+
34294
34450
  // src/services/bootstrap/rehydrate.ts
34295
34451
  var ACTIVE_STATUSES = {
34296
34452
  open: true,
@@ -34313,9 +34469,11 @@ var ACTIVE_STATUSES = {
34313
34469
  */
34314
34470
  canceled: false
34315
34471
  };
34316
- function planStaleSweep(tasks, isRunning) {
34472
+ function planStaleSweep(tasks, getSessionState) {
34317
34473
  return Object.values(tasks).map((task) => {
34318
- if (ACTIVE_STATUSES[task.status] && !isRunning(task.taskId)) {
34474
+ const state = getSessionState(task.taskId);
34475
+ const isLive = state !== null && isActiveUserWork(state);
34476
+ if (ACTIVE_STATUSES[task.status] && !isLive) {
34319
34477
  return { kind: "mark_input_required", taskId: task.taskId, previousStatus: task.status };
34320
34478
  }
34321
34479
  return { kind: "keep", taskId: task.taskId };
@@ -34342,6 +34500,15 @@ function classifyPersistedSessions(sessions, knownTaskIds) {
34342
34500
  }
34343
34501
  var EAGER_RESTORE_STATUSES = {
34344
34502
  open: true,
34503
+ /**
34504
+ * Since the `planStaleSweep` fix for issue #5224, an eagerly-restored
34505
+ * `in_progress` task lands in `resumable_idle` and is immediately demoted
34506
+ * to `input_required` by the sweep three lines later in the boot sequence
34507
+ * — the eventual status is identical to `input_required` either way.
34508
+ * Eager restore is still worth it here purely as a warm-cache optimization:
34509
+ * the resumable SDK session is materialized immediately rather than on the
34510
+ * user's next message, so replying doesn't pay the lazy-restore cost.
34511
+ */
34345
34512
  in_progress: true,
34346
34513
  /**
34347
34514
  * Excluded for boot-cost reasons, not correctness: `planStaleSweep`
@@ -34696,7 +34863,7 @@ async function migrateLegacyPreviewData(canvasRepo, previewStateStore, taskState
34696
34863
  }
34697
34864
  async function sweepStaleTasks(taskStateStore, taskManager, log) {
34698
34865
  const tasks = await taskStateStore.listTasks();
34699
- const actions = planStaleSweep(tasks, (id) => taskManager.isRunning(id));
34866
+ const actions = planStaleSweep(tasks, (id) => taskManager.getTaskState(id)?.sessionState ?? null);
34700
34867
  const noBroadcast = { broadcast: false };
34701
34868
  for (const action of actions) {
34702
34869
  if (action.kind === "mark_input_required") {
@@ -37464,7 +37631,7 @@ function createDaemonPrPollingService(deps) {
37464
37631
  syncing = true;
37465
37632
  return true;
37466
37633
  }
37467
- async function loadPlanTasks() {
37634
+ async function loadPlanTasks2() {
37468
37635
  const records = await deps.taskStateStore.listTasks();
37469
37636
  return Object.values(records).map(
37470
37637
  (record) => toPrPollPlanTask(record, deps.getPrState(record.taskId))
@@ -37488,7 +37655,7 @@ function createDaemonPrPollingService(deps) {
37488
37655
  }
37489
37656
  }
37490
37657
  async function runSyncCore() {
37491
- const tasks = await loadPlanTasks();
37658
+ const tasks = await loadPlanTasks2();
37492
37659
  if (disposed) return;
37493
37660
  const tasksById = new Map(tasks.map((task) => [task.taskId, task]));
37494
37661
  stopIneligibleSubscriptions(tasksById);
@@ -37549,70 +37716,8 @@ function createDaemonPrPollingService(deps) {
37549
37716
  return { start, requestSync, dispose };
37550
37717
  }
37551
37718
 
37552
- // src/services/pr-first-discovery.ts
37553
- function applyPrFirstDiscovery(payload, attributedTaskIds, effects) {
37554
- const { taskId } = payload;
37555
- const pr = payload.currentBranchPR;
37556
- if (!pr || attributedTaskIds.has(taskId)) return;
37557
- attributedTaskIds.add(taskId);
37558
- effects.forceRefresh(taskId);
37559
- effects.attribute(taskId, pr);
37560
- }
37561
-
37562
- // src/services/pr-poll-subscriptions.ts
37563
- function createPRPollSubscriptionCoordinator(poller) {
37564
- const subscriptions = /* @__PURE__ */ new Map();
37565
- function startPolling(taskId, cwd, source = "browser") {
37566
- const existing = subscriptions.get(taskId);
37567
- if (existing) {
37568
- const wasEmpty = existing.sources.size === 0;
37569
- const cwdChanged = existing.cwd !== cwd;
37570
- existing.cwd = cwd;
37571
- existing.sources.add(source);
37572
- if (wasEmpty) {
37573
- poller.startPolling(taskId, cwd);
37574
- } else if (cwdChanged) {
37575
- void poller.updateCwd(taskId, cwd);
37576
- } else if (source === "browser") {
37577
- poller.refreshNow(taskId);
37578
- }
37579
- return;
37580
- }
37581
- subscriptions.set(taskId, { cwd, sources: /* @__PURE__ */ new Set([source]) });
37582
- poller.startPolling(taskId, cwd);
37583
- }
37584
- function stopPolling(taskId, source = "browser") {
37585
- const entry = subscriptions.get(taskId);
37586
- if (!entry) return;
37587
- entry.sources.delete(source);
37588
- if (entry.sources.size > 0) return;
37589
- subscriptions.delete(taskId);
37590
- poller.stopPolling(taskId);
37591
- }
37592
- function getCwd(taskId) {
37593
- return subscriptions.get(taskId)?.cwd ?? poller.getCwd(taskId);
37594
- }
37595
- async function updateCwd(taskId, newCwd) {
37596
- const entry = subscriptions.get(taskId);
37597
- if (!entry) return;
37598
- if (entry.cwd === newCwd) return;
37599
- entry.cwd = newCwd;
37600
- await poller.updateCwd(taskId, newCwd);
37601
- }
37602
- function dispose() {
37603
- subscriptions.clear();
37604
- poller.dispose();
37605
- }
37606
- return {
37607
- startPolling,
37608
- stopPolling,
37609
- forceRefresh: poller.forceRefresh,
37610
- refreshNow: poller.refreshNow,
37611
- getCwd,
37612
- updateCwd,
37613
- dispose
37614
- };
37615
- }
37719
+ // src/services/pr-cold-discovery.ts
37720
+ import { existsSync as existsSync7 } from "fs";
37616
37721
 
37617
37722
  // src/services/pr-stack/build-stack-graph.ts
37618
37723
  var DEFAULT_MAX_DEPTH = 20;
@@ -37938,47 +38043,47 @@ async function fetchCIDataWithRemote(pr, cwd, remote) {
37938
38043
  );
37939
38044
  return { requiredChecks };
37940
38045
  }
38046
+ async function fetchFastPRState(cwd, log) {
38047
+ const backoff = getGithubReadBackoffState();
38048
+ if (backoff.backoffUntil !== null) {
38049
+ log.warn(
38050
+ { backoffUntil: backoff.backoffUntil, reason: backoff.reason },
38051
+ "PR polling skipped during GitHub read backoff"
38052
+ );
38053
+ return null;
38054
+ }
38055
+ const available = await isGhAvailable();
38056
+ if (!available) {
38057
+ return {
38058
+ prAvailable: false,
38059
+ currentBranchPR: null,
38060
+ assignedReviews: [],
38061
+ userPRs: [],
38062
+ allPRs: [],
38063
+ remote: ""
38064
+ };
38065
+ }
38066
+ const [prResults, remote] = await Promise.all([
38067
+ Promise.allSettled([getPRForCurrentBranch(cwd), getAssignedReviews(cwd), getUserPRs(cwd)]),
38068
+ gitExecSafe(cwd, ["remote", "get-url", "origin"]).then((v2) => v2 ?? "")
38069
+ ]);
38070
+ const [currentPR, assigned, user] = prResults;
38071
+ const { allPRs, currentBranchPR: rawPR } = deduplicatePRs(currentPR, assigned, user);
38072
+ const currentBranchPR = rawPR ? await enrichClosedPR(rawPR, cwd) : null;
38073
+ return {
38074
+ prAvailable: true,
38075
+ currentBranchPR,
38076
+ assignedReviews: assigned.status === "fulfilled" ? assigned.value : [],
38077
+ userPRs: user.status === "fulfilled" ? user.value : [],
38078
+ allPRs,
38079
+ remote
38080
+ };
38081
+ }
37941
38082
  function createPRPoller(callbacks, log, resolveTopLevel = getGitTopLevel) {
37942
38083
  const repos = /* @__PURE__ */ new Map();
37943
38084
  const taskToRepo = /* @__PURE__ */ new Map();
37944
38085
  let disposed = false;
37945
38086
  let activePollCount = 0;
37946
- async function fetchFastPRState(cwd) {
37947
- const backoff = getGithubReadBackoffState();
37948
- if (backoff.backoffUntil !== null) {
37949
- log.warn(
37950
- { backoffUntil: backoff.backoffUntil, reason: backoff.reason },
37951
- "PR polling skipped during GitHub read backoff"
37952
- );
37953
- return null;
37954
- }
37955
- const available = await isGhAvailable();
37956
- if (!available) {
37957
- return {
37958
- prAvailable: false,
37959
- currentBranchPR: null,
37960
- assignedReviews: [],
37961
- userPRs: [],
37962
- allPRs: [],
37963
- remote: ""
37964
- };
37965
- }
37966
- const [prResults, remote] = await Promise.all([
37967
- Promise.allSettled([getPRForCurrentBranch(cwd), getAssignedReviews(cwd), getUserPRs(cwd)]),
37968
- gitExecSafe(cwd, ["remote", "get-url", "origin"]).then((v2) => v2 ?? "")
37969
- ]);
37970
- const [currentPR, assigned, user] = prResults;
37971
- const { allPRs, currentBranchPR: rawPR } = deduplicatePRs(currentPR, assigned, user);
37972
- const currentBranchPR = rawPR ? await enrichClosedPR(rawPR, cwd) : null;
37973
- return {
37974
- prAvailable: true,
37975
- currentBranchPR,
37976
- assignedReviews: assigned.status === "fulfilled" ? assigned.value : [],
37977
- userPRs: user.status === "fulfilled" ? user.value : [],
37978
- allPRs,
37979
- remote
37980
- };
37981
- }
37982
38087
  async function fetchEnrichment(fast, cwd) {
37983
38088
  const ciData = await fetchCIDataWithRemote(fast.currentBranchPR, cwd, fast.remote).catch(
37984
38089
  () => ({
@@ -38115,7 +38220,7 @@ function createPRPoller(callbacks, log, resolveTopLevel = getGitTopLevel) {
38115
38220
  const currentBranch = await gitExecSafe(entry.cwd, ["rev-parse", "--abbrev-ref", "HEAD"]) ?? "";
38116
38221
  if (disposed) return;
38117
38222
  handleBranchChange(topLevel, entry, currentBranch);
38118
- const fast = await fetchFastPRState(entry.cwd);
38223
+ const fast = await fetchFastPRState(entry.cwd, log);
38119
38224
  if (!fast || disposed) return;
38120
38225
  entry.lastPolledAt = Date.now();
38121
38226
  pushPhase1IfChanged(topLevel, entry, fast);
@@ -38311,6 +38416,190 @@ function createPRPoller(callbacks, log, resolveTopLevel = getGitTopLevel) {
38311
38416
  return { startPolling, stopPolling, forceRefresh, refreshNow, getCwd, updateCwd, dispose };
38312
38417
  }
38313
38418
 
38419
+ // src/services/pr-cold-discovery.ts
38420
+ async function loadPlanTasks(taskStateStore, getPrState) {
38421
+ const records = await taskStateStore.listTasks();
38422
+ return Object.values(records).map(
38423
+ (record) => toPrPollPlanTask(record, getPrState(record.taskId))
38424
+ );
38425
+ }
38426
+ function createPrColdDiscoveryService(deps) {
38427
+ const intervalMs = deps.intervalMs ?? DAEMON_PR_COLD_DISCOVERY_INTERVAL_MS;
38428
+ const now = deps.now ?? Date.now;
38429
+ const checkPR = deps.checkPR ?? fetchFastPRState;
38430
+ const lastCheckedAt = /* @__PURE__ */ new Map();
38431
+ let timer = null;
38432
+ let disposed = false;
38433
+ let syncing = false;
38434
+ let syncQueued = false;
38435
+ let unsubscribeTaskStore = null;
38436
+ async function checkTask(task) {
38437
+ if (!task.cwd) return;
38438
+ if (!existsSync7(task.cwd)) {
38439
+ deps.log.debug({ taskId: task.taskId, cwd: task.cwd }, "Cold discovery skipped, cwd missing");
38440
+ return;
38441
+ }
38442
+ try {
38443
+ const fast = await checkPR(task.cwd, deps.log);
38444
+ if (fast?.currentBranchPR) {
38445
+ deps.log.debug(
38446
+ { taskId: task.taskId, prUrl: fast.currentBranchPR.url },
38447
+ "Cold discovery found a PR, handing off to daemon polling"
38448
+ );
38449
+ deps.subscriptions.startPolling(task.taskId, task.cwd, "daemon");
38450
+ }
38451
+ } catch (err3) {
38452
+ recordGithubReadFailure(err3);
38453
+ deps.log.warn({ err: err3, taskId: task.taskId }, "Cold discovery check failed");
38454
+ }
38455
+ }
38456
+ function tryEnterSync() {
38457
+ if (disposed) return false;
38458
+ if (syncing) {
38459
+ syncQueued = true;
38460
+ return false;
38461
+ }
38462
+ syncing = true;
38463
+ return true;
38464
+ }
38465
+ async function runSyncCore() {
38466
+ const tasks = await loadPlanTasks(deps.taskStateStore, deps.getPrState);
38467
+ if (disposed) return;
38468
+ const tasksById = new Map(tasks.map((task) => [task.taskId, task]));
38469
+ const currentTime = now();
38470
+ const toCheck = decidePrColdDiscoveryPlan(
38471
+ tasks,
38472
+ currentTime,
38473
+ lastCheckedAt,
38474
+ deps.getRateLimitState(),
38475
+ {
38476
+ intervalMs
38477
+ }
38478
+ );
38479
+ for (const taskId of toCheck) {
38480
+ lastCheckedAt.set(taskId, currentTime);
38481
+ }
38482
+ if (toCheck.length > 0) {
38483
+ deps.log.debug({ taskIds: toCheck }, "Cold PR discovery candidates selected");
38484
+ }
38485
+ await Promise.all(
38486
+ toCheck.map((taskId) => {
38487
+ const task = tasksById.get(taskId);
38488
+ return task ? checkTask(task) : Promise.resolve();
38489
+ })
38490
+ );
38491
+ }
38492
+ function finishSync() {
38493
+ syncing = false;
38494
+ if (!syncQueued || disposed) return;
38495
+ syncQueued = false;
38496
+ void sync2();
38497
+ }
38498
+ async function sync2() {
38499
+ if (!tryEnterSync()) return;
38500
+ try {
38501
+ await runSyncCore();
38502
+ } catch (err3) {
38503
+ deps.log.warn(
38504
+ { error: err3 instanceof Error ? err3.message : String(err3) },
38505
+ "Cold PR discovery sync failed"
38506
+ );
38507
+ } finally {
38508
+ finishSync();
38509
+ }
38510
+ }
38511
+ function requestSync() {
38512
+ void sync2();
38513
+ }
38514
+ function start() {
38515
+ if (timer !== null || disposed) return;
38516
+ unsubscribeTaskStore = deps.taskStateStore.subscribe((event) => {
38517
+ if (event.kind === "delete") {
38518
+ lastCheckedAt.delete(event.id);
38519
+ return;
38520
+ }
38521
+ requestSync();
38522
+ });
38523
+ requestSync();
38524
+ timer = setInterval(requestSync, intervalMs);
38525
+ timer.unref();
38526
+ }
38527
+ function dispose() {
38528
+ disposed = true;
38529
+ if (timer) clearInterval(timer);
38530
+ timer = null;
38531
+ unsubscribeTaskStore?.();
38532
+ unsubscribeTaskStore = null;
38533
+ lastCheckedAt.clear();
38534
+ }
38535
+ return { start, dispose };
38536
+ }
38537
+
38538
+ // src/services/pr-first-discovery.ts
38539
+ function applyPrFirstDiscovery(payload, attributedTaskIds, effects) {
38540
+ const { taskId } = payload;
38541
+ const pr = payload.currentBranchPR;
38542
+ if (!pr || attributedTaskIds.has(taskId)) return;
38543
+ attributedTaskIds.add(taskId);
38544
+ effects.forceRefresh(taskId);
38545
+ effects.attribute(taskId, pr);
38546
+ }
38547
+
38548
+ // src/services/pr-poll-subscriptions.ts
38549
+ function createPRPollSubscriptionCoordinator(poller) {
38550
+ const subscriptions = /* @__PURE__ */ new Map();
38551
+ function startPolling(taskId, cwd, source = "browser") {
38552
+ const existing = subscriptions.get(taskId);
38553
+ if (existing) {
38554
+ const wasEmpty = existing.sources.size === 0;
38555
+ const cwdChanged = existing.cwd !== cwd;
38556
+ existing.cwd = cwd;
38557
+ existing.sources.add(source);
38558
+ if (wasEmpty) {
38559
+ poller.startPolling(taskId, cwd);
38560
+ } else if (cwdChanged) {
38561
+ void poller.updateCwd(taskId, cwd);
38562
+ } else if (source === "browser") {
38563
+ poller.refreshNow(taskId);
38564
+ }
38565
+ return;
38566
+ }
38567
+ subscriptions.set(taskId, { cwd, sources: /* @__PURE__ */ new Set([source]) });
38568
+ poller.startPolling(taskId, cwd);
38569
+ }
38570
+ function stopPolling(taskId, source = "browser") {
38571
+ const entry = subscriptions.get(taskId);
38572
+ if (!entry) return;
38573
+ entry.sources.delete(source);
38574
+ if (entry.sources.size > 0) return;
38575
+ subscriptions.delete(taskId);
38576
+ poller.stopPolling(taskId);
38577
+ }
38578
+ function getCwd(taskId) {
38579
+ return subscriptions.get(taskId)?.cwd ?? poller.getCwd(taskId);
38580
+ }
38581
+ async function updateCwd(taskId, newCwd) {
38582
+ const entry = subscriptions.get(taskId);
38583
+ if (!entry) return;
38584
+ if (entry.cwd === newCwd) return;
38585
+ entry.cwd = newCwd;
38586
+ await poller.updateCwd(taskId, newCwd);
38587
+ }
38588
+ function dispose() {
38589
+ subscriptions.clear();
38590
+ poller.dispose();
38591
+ }
38592
+ return {
38593
+ startPolling,
38594
+ stopPolling,
38595
+ forceRefresh: poller.forceRefresh,
38596
+ refreshNow: poller.refreshNow,
38597
+ getCwd,
38598
+ updateCwd,
38599
+ dispose
38600
+ };
38601
+ }
38602
+
38314
38603
  // src/services/channels/control-channel/pr-ingest-dedup.ts
38315
38604
  var INGEST_DEDUPE_WINDOW_MS = 15e3;
38316
38605
  function createPrIngestDedup() {
@@ -38499,6 +38788,14 @@ function buildPRAttributionSubsystem(daemon, options) {
38499
38788
  log: prPollerLog
38500
38789
  });
38501
38790
  daemonPrPolling.start();
38791
+ const prColdDiscovery = createPrColdDiscoveryService({
38792
+ taskStateStore: daemon.taskStateStore,
38793
+ subscriptions: prPoller,
38794
+ getPrState,
38795
+ getRateLimitState: getGithubReadBackoffState,
38796
+ log: prPollerLog
38797
+ });
38798
+ prColdDiscovery.start();
38502
38799
  const unsubscribeCwdChanged = daemon.taskManager.addOnCwdChanged((taskId, newCwd) => {
38503
38800
  void prPoller.updateCwd(taskId, newCwd);
38504
38801
  });
@@ -38575,6 +38872,7 @@ function buildPRAttributionSubsystem(daemon, options) {
38575
38872
  seedReadyPromise,
38576
38873
  dispose: () => {
38577
38874
  daemonPrPolling?.dispose();
38875
+ prColdDiscovery.dispose();
38578
38876
  unsubscribeCwdChanged();
38579
38877
  prPoller.dispose();
38580
38878
  }
@@ -39897,6 +40195,11 @@ var EXTENSION_KIND = {
39897
40195
  ".jpeg": "screenshot",
39898
40196
  ".gif": "screenshot",
39899
40197
  ".webp": "screenshot",
40198
+ /**
40199
+ * #5231 — asset download already maps .svg → image/svg+xml; registration must
40200
+ * match so the viewer takes the visual path instead of monospace `other`.
40201
+ */
40202
+ ".svg": "screenshot",
39900
40203
  ".mp4": "video",
39901
40204
  ".webm": "video",
39902
40205
  ".mov": "video",
@@ -51381,7 +51684,7 @@ import { extname as extname3 } from "path";
51381
51684
  import { pathToFileURL as pathToFileURL3 } from "url";
51382
51685
 
51383
51686
  // src/services/lsp/language-server-registry.ts
51384
- import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
51687
+ import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
51385
51688
  import { createRequire as createRequire3 } from "module";
51386
51689
  import { dirname as dirname22, isAbsolute as isAbsolute4, join as join53 } from "path";
51387
51690
  function decideTypescriptServer(d) {
@@ -51507,7 +51810,7 @@ function createRegistryDeps(requireFrom) {
51507
51810
  resolveBin: (pkg, binName) => resolveBinViaPackageJson(pkg, binName, req),
51508
51811
  resolveTsgoBinary: () => resolveTsgoBinaryPath(req),
51509
51812
  nodeExecPath: resolveNodeExecPath(),
51510
- fileExists: (path5) => existsSync7(path5),
51813
+ fileExists: (path5) => existsSync8(path5),
51511
51814
  readJsonFile: (path5) => {
51512
51815
  try {
51513
51816
  return parse2(readFileSync8(path5, "utf-8"));
@@ -51531,7 +51834,7 @@ function resolveTsgoBinaryPath(req) {
51531
51834
  }
51532
51835
  const exeDir = join53(dirname22(pkgJsonPath), "lib");
51533
51836
  const exe = process.platform === "win32" ? join53(exeDir, "tsgo.exe") : join53(exeDir, "tsgo");
51534
- if (!existsSync7(exe)) {
51837
+ if (!existsSync8(exe)) {
51535
51838
  throw new Error(`tsgo native binary not found: ${exe}`);
51536
51839
  }
51537
51840
  return exe;
@@ -70010,7 +70313,7 @@ function applyCursorUsage(result, usage, ctx) {
70010
70313
  };
70011
70314
  const catalog = CURSOR_MODEL_CATALOG.find((m2) => m2.id === ctx.modelId);
70012
70315
  if (catalog !== void 0) {
70013
- const fastPricing = ctx.fastMode && ctx.fastModeParam !== null ? catalog.fastModePricing : void 0;
70316
+ const fastPricing = shouldUseCursorFastModePricing(ctx.fastMode, ctx.fastModeParam) ? catalog.fastModePricing : void 0;
70014
70317
  const costIn = fastPricing?.costInputUsd ?? catalog.costInputUsd;
70015
70318
  const costOut = fastPricing?.costOutputUsd ?? catalog.costOutputUsd;
70016
70319
  if (costIn !== void 0 && costOut !== void 0) {
@@ -70314,18 +70617,16 @@ var CursorAgentSubprocess = class {
70314
70617
  #injectedAgentId = null;
70315
70618
  /**
70316
70619
  * Live Fast Mode flag. Mirrored in the runner via `init` (initial value)
70317
- * and `set_fast_mode` IPC (subsequent toggles). `pushMessage` does not
70318
- * read this directly — it's the runner that wraps `agent.send()` with
70319
- * `SendOptions.model.params` when both `#fastMode === true` AND
70320
- * `#fastModeParam !== null`.
70620
+ * and `set_fast_mode` IPC (subsequent toggles). The runner attaches
70621
+ * `SendOptions.model.params` with value `"true"`/`"false"` whenever
70622
+ * `#fastModeParam` is non-null; `#fastMode` selects which value.
70321
70623
  */
70322
70624
  #fastMode = false;
70323
70625
  /**
70324
- * Resolved Fast Mode binding for this task's `modelId`. Frozen at spawn
70325
- * time — the daemon never re-discovers per-task because Cursor model
70326
- * Fast-Mode bindings are stable for the lifetime of a session. A future
70327
- * `setModel` call could in principle invalidate this; today the runner
70328
- * just keeps using the binding it was given.
70626
+ * Resolved Fast Mode ON-binding for this task's `modelId`. Frozen at
70627
+ * spawn time — the daemon never re-discovers per-task because Cursor
70628
+ * Fast Mode bindings are stable for the lifetime of a session. Null
70629
+ * means the model exposes no Fast Mode knob (omit `model.params`).
70329
70630
  */
70330
70631
  #fastModeParam = null;
70331
70632
  /**
@@ -71440,12 +71741,7 @@ var CursorAgentSubprocess = class {
71440
71741
  status: normalizeCursorMcpStatus(s2.status)
71441
71742
  }));
71442
71743
  if (this.#mcpStatus.length === 0) {
71443
- const fallback = [HARNESS_SERVER_NAME, ...this.#mcpServerSet].map(
71444
- (name) => ({
71445
- name,
71446
- status: "connected"
71447
- })
71448
- );
71744
+ const fallback = [{ name: HARNESS_SERVER_NAME, status: "connected" }];
71449
71745
  this.#mcpStatus = fallback;
71450
71746
  logger.info({
71451
71747
  event: "cursor_subprocess_mcp_status_seeded_from_spawn",
@@ -71759,49 +72055,41 @@ function createCursorMeCache(deps) {
71759
72055
  import { createHash as createHash8 } from "crypto";
71760
72056
 
71761
72057
  // src/services/session/profiles/cursor-fast-mode-resolver.ts
71762
- var FAST_MODE_ID_PREFIXES = ["fast", "speed", "tier", "pricing"];
71763
- var FAST_MODE_VALUE_PREFERENCE = ["fast", "priority", "high"];
71764
- function idHasFastModePrefix(rawId) {
71765
- const lower = rawId.toLowerCase();
71766
- for (const prefix of FAST_MODE_ID_PREFIXES) {
71767
- if (lower.startsWith(prefix)) return true;
71768
- }
71769
- return false;
71770
- }
71771
- function pickFastModeValue(values) {
71772
- const lowerToOriginal = /* @__PURE__ */ new Map();
72058
+ function findTrueFalseValues(values) {
72059
+ let trueValue;
72060
+ let falseValue;
71773
72061
  for (const entry of values) {
71774
- lowerToOriginal.set(entry.value.toLowerCase(), entry.value);
71775
- }
71776
- for (const preferred of FAST_MODE_VALUE_PREFERENCE) {
71777
- const match = lowerToOriginal.get(preferred);
71778
- if (match !== void 0) return match;
72062
+ const lower = entry.value.toLowerCase();
72063
+ if (lower === "true") trueValue = entry.value;
72064
+ if (lower === "false") falseValue = entry.value;
71779
72065
  }
71780
- return null;
72066
+ if (trueValue === void 0 || falseValue === void 0) return null;
72067
+ return { trueValue, falseValue };
71781
72068
  }
71782
72069
  function resolveCursorFastModeParam(model) {
71783
72070
  const warnings = [];
71784
72071
  const parameters = model.parameters ?? [];
71785
- const matching = parameters.filter(
71786
- (param) => idHasFastModePrefix(param.id) && pickFastModeValue(param.values) !== null
71787
- );
71788
- if (matching.length === 0) {
72072
+ const fastParams = parameters.filter((param) => param.id.toLowerCase() === "fast");
72073
+ if (fastParams.length === 0) {
71789
72074
  return { param: null, warnings };
71790
72075
  }
71791
- if (matching.length > 1) {
72076
+ if (fastParams.length > 1) {
71792
72077
  warnings.push(
71793
- `cursor model ${model.id} exposes ${matching.length} fast-mode-shaped parameters; picking first (${matching[0]?.id ?? "unknown"})`
72078
+ `cursor model ${model.id} exposes ${fastParams.length} parameters named "fast"; picking first`
71794
72079
  );
71795
72080
  }
71796
- const chosen = matching[0];
72081
+ const chosen = fastParams[0];
71797
72082
  if (chosen === void 0) {
71798
72083
  return { param: null, warnings };
71799
72084
  }
71800
- const value = pickFastModeValue(chosen.values);
71801
- if (value === null) {
72085
+ const pair = findTrueFalseValues(chosen.values);
72086
+ if (pair === null) {
72087
+ warnings.push(
72088
+ `cursor model ${model.id} has parameter id="fast" but values are not {true,false}; refusing to bind (values=[${chosen.values.map((v2) => v2.value).join(",")}])`
72089
+ );
71802
72090
  return { param: null, warnings };
71803
72091
  }
71804
- return { param: { id: chosen.id, value }, warnings };
72092
+ return { param: { id: chosen.id, value: pair.trueValue }, warnings };
71805
72093
  }
71806
72094
 
71807
72095
  // src/services/session/profiles/cursor-models-cache.ts
@@ -71860,6 +72148,7 @@ function createCursorModelsCache(deps) {
71860
72148
  function recordModel(model) {
71861
72149
  const { param, warnings } = resolveCursorFastModeParam(model);
71862
72150
  cache2.set(model.id, { fastModeParam: param });
72151
+ const catalogClaimsFast = CURSOR_MODEL_CATALOG.find((entry) => entry.id === model.id)?.supportsFastMode === true;
71863
72152
  if (param !== null) {
71864
72153
  deps.log("info", {
71865
72154
  event: "cursor_fast_mode_resolved",
@@ -71869,7 +72158,16 @@ function createCursorModelsCache(deps) {
71869
72158
  });
71870
72159
  } else {
71871
72160
  const reason = (model.parameters?.length ?? 0) === 0 ? "no_parameters" : "no_matching_param";
71872
- deps.log("info", { event: "cursor_fast_mode_unresolved", modelId: model.id, reason });
72161
+ if (catalogClaimsFast) {
72162
+ deps.log("warn", {
72163
+ event: "cursor_fast_mode_catalog_drift",
72164
+ modelId: model.id,
72165
+ reason,
72166
+ catalogSupportsFastMode: true
72167
+ });
72168
+ } else {
72169
+ deps.log("info", { event: "cursor_fast_mode_unresolved", modelId: model.id, reason });
72170
+ }
71873
72171
  }
71874
72172
  for (const warning of warnings) {
71875
72173
  deps.log("warn", {
@@ -71925,10 +72223,40 @@ function createCursorModelsCache(deps) {
71925
72223
  lastKeyHash = keyHash;
71926
72224
  lastRefreshAt = now();
71927
72225
  ready = true;
72226
+ deps.onRefreshComplete?.();
71928
72227
  }
71929
72228
  return { getFastModeParam, isReady, refresh };
71930
72229
  }
71931
72230
 
72231
+ // src/services/session/profiles/cursor-models-cache-wiring.ts
72232
+ function createWiredCursorModelsCache(deps) {
72233
+ let modelsApplier = null;
72234
+ const cache2 = createCursorModelsCache({
72235
+ listModels: deps.listModels,
72236
+ log: deps.log,
72237
+ onRefreshComplete: () => {
72238
+ const current = deps.getCurrentModels();
72239
+ if (current === void 0 || modelsApplier === null) return;
72240
+ if (!cache2.isReady()) return;
72241
+ const next = reconcileCursorSupportsFastMode(current, {
72242
+ isReady: () => true,
72243
+ supportsFastMode: (modelId) => cache2.getFastModeParam(modelId) !== null
72244
+ });
72245
+ void modelsApplier(next);
72246
+ }
72247
+ });
72248
+ setCursorFastModeSupportLookup({
72249
+ isReady: () => cache2.isReady(),
72250
+ supportsFastMode: (modelId) => cache2.getFastModeParam(modelId) !== null
72251
+ });
72252
+ return {
72253
+ cache: cache2,
72254
+ setModelsApplier: (apply) => {
72255
+ modelsApplier = apply;
72256
+ }
72257
+ };
72258
+ }
72259
+
71932
72260
  // src/services/session/profiles/cursor-vault-key-cache.ts
71933
72261
  function createCursorVaultKeyCache() {
71934
72262
  let current = null;
@@ -74335,6 +74663,12 @@ function logPerf(entry) {
74335
74663
  }
74336
74664
  }
74337
74665
  var DEFAULT_READ_CACHE_BUDGET_BYTES = 384 * 1024 * 1024;
74666
+ var READ_CACHE_HEAP_FRACTION = 0.05;
74667
+ var READ_CACHE_BUDGET_CAP_BYTES = 2048 * 1024 * 1024;
74668
+ function computeReadCacheBudgetBytes(heapSizeLimitBytes) {
74669
+ const scaled = Math.round(heapSizeLimitBytes * READ_CACHE_HEAP_FRACTION);
74670
+ return Math.min(READ_CACHE_BUDGET_CAP_BYTES, Math.max(DEFAULT_READ_CACHE_BUDGET_BYTES, scaled));
74671
+ }
74338
74672
  function buildJsonlConversationStore(dataDir, opts = {}) {
74339
74673
  const channelsDir = join67(dataDir, "channels");
74340
74674
  const seqCounters = /* @__PURE__ */ new Map();
@@ -75982,7 +76316,7 @@ var LoroDocSweeper = class {
75982
76316
  };
75983
76317
 
75984
76318
  // src/services/task/manager/task-manager.ts
75985
- import { existsSync as existsSync12 } from "fs";
76319
+ import { existsSync as existsSync13 } from "fs";
75986
76320
  import { homedir as homedir13 } from "os";
75987
76321
  import { dirname as dirname37 } from "path";
75988
76322
 
@@ -81691,7 +82025,7 @@ var Thread = class {
81691
82025
 
81692
82026
  // src/services/plan/plan-handler.ts
81693
82027
  import { randomUUID as randomUUID21 } from "crypto";
81694
- import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
82028
+ import { existsSync as existsSync11, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
81695
82029
  import { mkdir as mkdir33, readFile as readFile45, writeFile as writeFile26 } from "fs/promises";
81696
82030
  import { homedir as homedir10 } from "os";
81697
82031
  import { dirname as dirname34, join as join73 } from "path";
@@ -81803,7 +82137,7 @@ function decideWatchResolved(trigger, snapshot) {
81803
82137
 
81804
82138
  // src/services/plan/plan-file-bridge.ts
81805
82139
  import { createHash as createHash12, randomUUID as randomUUID20 } from "crypto";
81806
- import { existsSync as existsSync8 } from "fs";
82140
+ import { existsSync as existsSync9 } from "fs";
81807
82141
  import { readFile as readFile44, rename as rename18, writeFile as writeFile25 } from "fs/promises";
81808
82142
  import { basename as basename13, dirname as dirname33 } from "path";
81809
82143
  var FILE_READ_MAX_RETRIES = 3;
@@ -81998,7 +82332,7 @@ var PlanFileBridge = class _PlanFileBridge {
81998
82332
  */
81999
82333
  async #startDirectoryWatcher(filePath) {
82000
82334
  const dir = dirname33(filePath);
82001
- if (!existsSync8(dir)) return;
82335
+ if (!existsSync9(dir)) return;
82002
82336
  const targetFilename = basename13(filePath);
82003
82337
  let sub;
82004
82338
  try {
@@ -82231,7 +82565,7 @@ var PlanFileBridge = class _PlanFileBridge {
82231
82565
  }
82232
82566
  async #doWriteBackToFile() {
82233
82567
  if (this.#disposed || !this.#filePath) return;
82234
- if (!existsSync8(this.#filePath)) return;
82568
+ if (!existsSync9(this.#filePath)) return;
82235
82569
  const content = this.#config.planRepo.getContent(this.#config.taskId);
82236
82570
  const hash = contentHash(content);
82237
82571
  if (hash === this.#lastWrittenHash) return;
@@ -82462,7 +82796,7 @@ function greedyLCS(a, b2) {
82462
82796
  }
82463
82797
 
82464
82798
  // src/services/plan/plan-file-watcher.ts
82465
- import { existsSync as existsSync9 } from "fs";
82799
+ import { existsSync as existsSync10 } from "fs";
82466
82800
  import { homedir as homedir9 } from "os";
82467
82801
  import { join as join72 } from "path";
82468
82802
  var DEFAULT_PLANS_DIR = join72(homedir9(), ".claude", "plans");
@@ -82525,7 +82859,7 @@ function createPlanFileWatcher(deps) {
82525
82859
  deps.onTimeout();
82526
82860
  }, PLAN_WATCH_TIMEOUT_MS);
82527
82861
  const plansDir = deps.plansDir ?? DEFAULT_PLANS_DIR;
82528
- if (!existsSync9(plansDir)) {
82862
+ if (!existsSync10(plansDir)) {
82529
82863
  deps.log({
82530
82864
  event: "plan_watch_skip_missing_dir",
82531
82865
  taskId: deps.taskId,
@@ -82988,7 +83322,7 @@ var PlanHandler = class {
82988
83322
  }
82989
83323
  async #rehydratePlanFileBridge(filePath, toolUseId) {
82990
83324
  if (this.#deps.isDisposed()) return;
82991
- if (!existsSync10(filePath)) {
83325
+ if (!existsSync11(filePath)) {
82992
83326
  this.#deps.log({
82993
83327
  event: "plan_bridge_rehydrate_file_missing",
82994
83328
  taskId: this.#deps.taskId,
@@ -83687,7 +84021,7 @@ var PlanHandler = class {
83687
84021
  return newest?.path ?? null;
83688
84022
  }
83689
84023
  const plansDir = join73(homedir10(), ".claude", "plans");
83690
- if (!existsSync10(plansDir)) return null;
84024
+ if (!existsSync11(plansDir)) return null;
83691
84025
  try {
83692
84026
  const files = readdirSync5(plansDir).filter((f2) => f2.endsWith(".md")).map((f2) => ({
83693
84027
  path: join73(plansDir, f2),
@@ -89478,7 +89812,7 @@ async function resolveTaskListPrepend(ctx) {
89478
89812
  }
89479
89813
 
89480
89814
  // src/services/task/cc-task-file-store.ts
89481
- import { existsSync as existsSync11 } from "fs";
89815
+ import { existsSync as existsSync12 } from "fs";
89482
89816
  import { readdir as readdir21, readFile as readFile47 } from "fs/promises";
89483
89817
  import { homedir as homedir11 } from "os";
89484
89818
  import { basename as basename14, dirname as dirname36, join as join75 } from "path";
@@ -89607,7 +89941,7 @@ function createCCTaskFileWatcher(listId, log) {
89607
89941
  }
89608
89942
  }
89609
89943
  async function startDirWatcher(reason = "initial") {
89610
- if (!existsSync11(dir)) return false;
89944
+ if (!existsSync12(dir)) return false;
89611
89945
  try {
89612
89946
  const sub = await guardedSubscribe(dir, onDirEvent, { ignore: ["**/*/"] }, "lazy", reason);
89613
89947
  if (watchState.stopped) {
@@ -94249,12 +94583,22 @@ function pushTaskMcpStateSnapshot(ctx, taskId) {
94249
94583
  for (const s2 of ctx.sessionMcpsByTask.get(taskId) ?? []) {
94250
94584
  sessionServers[s2.name] = { status: normalizeMcpStatus(s2.status) };
94251
94585
  }
94586
+ const liveStatuses = liveStatusesFromStatusEntries(ctx.getMcpCoordinator()?.getStatuses() ?? []);
94587
+ for (const divergence of collectMcpSessionCoordinatorDivergences(sessionServers, liveStatuses)) {
94588
+ logger.info({
94589
+ event: "mcp_session_coordinator_status_divergence",
94590
+ taskId,
94591
+ serverName: divergence.name,
94592
+ sessionStatus: divergence.sessionStatus,
94593
+ coordinatorStatus: divergence.coordinatorStatus
94594
+ });
94595
+ }
94252
94596
  const message = {
94253
94597
  type: "mcp_state_snapshot",
94254
94598
  taskId,
94255
94599
  servers: resolveMcpStateSnapshot({
94256
94600
  workspaceServers: workspaceServersFromCapabilities(ctx.getCapabilities()),
94257
- liveStatuses: liveStatusesFromStatusEntries(ctx.getMcpCoordinator()?.getStatuses() ?? []),
94601
+ liveStatuses,
94258
94602
  sessionServers,
94259
94603
  perTaskDisabled: settings.disabledMcpServers ?? null,
94260
94604
  perTaskSnapshot: settings.mcpServerSnapshot ?? null
@@ -96547,16 +96891,6 @@ var TaskManager = class {
96547
96891
  getOrchestrator(taskId) {
96548
96892
  return this.#tasks.get(taskId)?.orchestrator ?? null;
96549
96893
  }
96550
- /**
96551
- * True if the task has an orchestrator with a live or resumable session.
96552
- * cold_idle orchestrators are NOT considered running — the SDK session is
96553
- * expired and the task needs user input to restart.
96554
- */
96555
- isRunning(taskId) {
96556
- const task = this.#tasks.get(taskId);
96557
- if (!task) return false;
96558
- return task.orchestrator.state !== "cold_idle";
96559
- }
96560
96894
  get size() {
96561
96895
  return this.#tasks.size;
96562
96896
  }
@@ -96951,7 +97285,7 @@ var TaskManager = class {
96951
97285
  function pathExistsForLog2(path5) {
96952
97286
  if (!path5) return false;
96953
97287
  try {
96954
- return existsSync12(path5);
97288
+ return existsSync13(path5);
96955
97289
  } catch {
96956
97290
  return false;
96957
97291
  }
@@ -97594,7 +97928,9 @@ async function createDaemon(deps) {
97594
97928
  const compactor = startLoroCompactor(storageAdapter);
97595
97929
  const taskStateStore = buildTaskStateStore(deps.dataDir);
97596
97930
  const planRepo = new PlanRepository(repo, PLAN_EPOCH, createPlanContentBridge(), storageAdapter);
97597
- const jsonlStore = buildJsonlConversationStore(deps.dataDir);
97931
+ const jsonlStore = buildJsonlConversationStore(deps.dataDir, {
97932
+ readCacheBudgetBytes: computeReadCacheBudgetBytes(getHeapStatistics().heap_size_limit)
97933
+ });
97598
97934
  const store = buildObservableConversationStore(jsonlStore);
97599
97935
  const sessionPersistence = buildSessionPersistence(deps.dataDir);
97600
97936
  const collabQueuePersistence = buildCollabQueuePersistence(deps.dataDir);
@@ -97654,14 +97990,15 @@ async function createDaemon(deps) {
97654
97990
  const CURSOR_VAULT_WRITE_TIMEOUT_MS = 5e3;
97655
97991
  const cursorStoredByRef = { current: "" };
97656
97992
  const cursorVaultKeyCache = createCursorVaultKeyCache();
97657
- const cursorModelsCache = createCursorModelsCache({
97993
+ const { cache: cursorModelsCache, setModelsApplier: setCursorFastModeModelsApplierFn } = createWiredCursorModelsCache({
97658
97994
  listModels: async (options) => {
97659
97995
  const { Cursor } = await import("@cursor/sdk");
97660
97996
  return Cursor.models.list(options);
97661
97997
  },
97662
97998
  log: (level, entry) => {
97663
97999
  logger[level](entry);
97664
- }
98000
+ },
98001
+ getCurrentModels: () => capabilitiesRef.current?.models
97665
98002
  });
97666
98003
  cursorVaultKeyCache.subscribe((key) => {
97667
98004
  void cursorModelsCache.refresh(key);
@@ -99792,6 +100129,7 @@ async function createDaemon(deps) {
99792
100129
  clearCursorVaultCache: () => {
99793
100130
  cursorVaultKeyCache.set(null);
99794
100131
  },
100132
+ setCursorFastModeModelsApplier: setCursorFastModeModelsApplierFn,
99795
100133
  notifyCodexAccountId: (accountId) => {
99796
100134
  codexEngineRef.current?.notifyAccountId(accountId);
99797
100135
  },
@@ -99921,7 +100259,7 @@ async function createDaemon(deps) {
99921
100259
  }
99922
100260
 
99923
100261
  // src/services/serve-helpers.ts
99924
- import { existsSync as existsSync13 } from "fs";
100262
+ import { existsSync as existsSync14 } from "fs";
99925
100263
 
99926
100264
  // src/services/bootstrap/self-update.ts
99927
100265
  import { execFile as execFile11, spawn as spawn13 } from "child_process";
@@ -100589,7 +100927,7 @@ function resolveClaudeCodePath(log) {
100589
100927
  log({ event: "claude_code_path_resolved", source: "sdk_default" });
100590
100928
  return void 0;
100591
100929
  }
100592
- if (!existsSync13(envPath)) {
100930
+ if (!existsSync14(envPath)) {
100593
100931
  log({ event: "claude_code_path_skipped", source: "env", path: envPath, reason: "missing" });
100594
100932
  log({ event: "claude_code_path_resolved", source: "sdk_default" });
100595
100933
  return void 0;
@@ -101978,24 +102316,6 @@ function buildPluginConfigStore(pluginsDir) {
101978
102316
  };
101979
102317
  }
101980
102318
 
101981
- // src/services/task/session-activity.ts
101982
- function isActiveUserWork(state) {
101983
- switch (state) {
101984
- case "spawning":
101985
- case "running":
101986
- case "stopping":
101987
- return true;
101988
- case "warm_idle":
101989
- case "resumable_idle":
101990
- case "cold_idle":
101991
- return false;
101992
- default: {
101993
- const _exhaustive = state;
101994
- throw new Error(`Unhandled session state: ${JSON.stringify(_exhaustive)}`);
101995
- }
101996
- }
101997
- }
101998
-
101999
102319
  // src/services/task/electron-idle-rpc.ts
102000
102320
  function registerElectronIdleRpc(taskManager, parentPort2) {
102001
102321
  parentPort2.on("message", (event) => {
@@ -102613,6 +102933,9 @@ async function runServeBody(options, captureRefs) {
102613
102933
  log: logAdapter
102614
102934
  });
102615
102935
  codexSkillsApplyScopedPatchRef = applyScopedPatch;
102936
+ daemon.setCursorFastModeModelsApplier(async (models) => {
102937
+ await applyScopedPatch({ models });
102938
+ });
102616
102939
  void (async () => {
102617
102940
  try {
102618
102941
  const seed = await loadCodexSkillsCache(codexSkillsCachePath);
@@ -103067,4 +103390,4 @@ export {
103067
103390
  decideWorkspaceScope,
103068
103391
  serve
103069
103392
  };
103070
- //# sourceMappingURL=serve-J3MG3WHK.js.map
103393
+ //# sourceMappingURL=serve-QHLWAVVY.js.map