@stigmer/runner 3.12.4 → 3.12.6

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 (180) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/call-agent.js +19 -6
  3. package/dist/activities/call-agent.js.map +1 -1
  4. package/dist/activities/execute-cursor/cursor-mcp-config.d.ts +45 -0
  5. package/dist/activities/execute-cursor/cursor-mcp-config.js +76 -0
  6. package/dist/activities/execute-cursor/cursor-mcp-config.js.map +1 -0
  7. package/dist/activities/execute-cursor/error-classifier.d.ts +9 -0
  8. package/dist/activities/execute-cursor/error-classifier.js +30 -1
  9. package/dist/activities/execute-cursor/error-classifier.js.map +1 -1
  10. package/dist/activities/execute-cursor/index.d.ts +10 -1
  11. package/dist/activities/execute-cursor/index.js +62 -36
  12. package/dist/activities/execute-cursor/index.js.map +1 -1
  13. package/dist/activities/execute-cursor/prompt-builder.d.ts +13 -2
  14. package/dist/activities/execute-cursor/prompt-builder.js +11 -0
  15. package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
  16. package/dist/activities/execute-cursor/service-tier.d.ts +38 -29
  17. package/dist/activities/execute-cursor/service-tier.js +92 -63
  18. package/dist/activities/execute-cursor/service-tier.js.map +1 -1
  19. package/dist/activities/execute-cursor/session-lifecycle.d.ts +1 -1
  20. package/dist/activities/execute-cursor/skill-resolver.d.ts +2 -21
  21. package/dist/activities/execute-cursor/skill-resolver.js +5 -108
  22. package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
  23. package/dist/activities/execute-cursor/usage-accumulator.d.ts +16 -2
  24. package/dist/activities/execute-cursor/usage-accumulator.js +12 -2
  25. package/dist/activities/execute-cursor/usage-accumulator.js.map +1 -1
  26. package/dist/activities/execute-deep-agent/approval-file-change.d.ts +9 -0
  27. package/dist/activities/execute-deep-agent/approval-file-change.js +2 -1
  28. package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -1
  29. package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +18 -1
  30. package/dist/activities/execute-deep-agent/cas-capture-backend.js +20 -3
  31. package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -1
  32. package/dist/activities/execute-deep-agent/index.js +57 -5
  33. package/dist/activities/execute-deep-agent/index.js.map +1 -1
  34. package/dist/activities/execute-deep-agent/prompt-builder.d.ts +9 -0
  35. package/dist/activities/execute-deep-agent/prompt-builder.js +10 -0
  36. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  37. package/dist/activities/execute-deep-agent/setup.js +17 -16
  38. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  39. package/dist/activities/execute-deep-agent/subagent-transformer.js +17 -11
  40. package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
  41. package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +3 -3
  42. package/dist/index.d.ts +1 -1
  43. package/dist/index.js +1 -1
  44. package/dist/middleware/path-normalization.d.ts +49 -49
  45. package/dist/middleware/path-normalization.js +73 -84
  46. package/dist/middleware/path-normalization.js.map +1 -1
  47. package/dist/middleware/types.d.ts +3 -3
  48. package/dist/runner-manager.d.ts +1 -1
  49. package/dist/runner-manager.js +22 -15
  50. package/dist/runner-manager.js.map +1 -1
  51. package/dist/runner.js +11 -0
  52. package/dist/runner.js.map +1 -1
  53. package/dist/shared/args-preview.d.ts +8 -0
  54. package/dist/shared/args-preview.js +14 -3
  55. package/dist/shared/args-preview.js.map +1 -1
  56. package/dist/shared/artifact-storage.d.ts +11 -1
  57. package/dist/shared/artifact-storage.js +10 -1
  58. package/dist/shared/artifact-storage.js.map +1 -1
  59. package/dist/shared/caller-identity.d.ts +10 -7
  60. package/dist/shared/caller-identity.js +10 -7
  61. package/dist/shared/caller-identity.js.map +1 -1
  62. package/dist/shared/declared-preferences.d.ts +47 -0
  63. package/dist/shared/declared-preferences.js +64 -0
  64. package/dist/shared/declared-preferences.js.map +1 -0
  65. package/dist/shared/mcp-enabled-tools.d.ts +2 -3
  66. package/dist/shared/mcp-enabled-tools.js +2 -3
  67. package/dist/shared/mcp-enabled-tools.js.map +1 -1
  68. package/dist/shared/mcp-resolver.d.ts +20 -15
  69. package/dist/shared/mcp-resolver.js +11 -12
  70. package/dist/shared/mcp-resolver.js.map +1 -1
  71. package/dist/shared/placeholder-resolver.d.ts +9 -2
  72. package/dist/shared/placeholder-resolver.js +9 -2
  73. package/dist/shared/placeholder-resolver.js.map +1 -1
  74. package/dist/shared/plan-mode-permissions.d.ts +26 -46
  75. package/dist/shared/plan-mode-permissions.js +27 -55
  76. package/dist/shared/plan-mode-permissions.js.map +1 -1
  77. package/dist/shared/skill-mount.d.ts +89 -0
  78. package/dist/shared/skill-mount.js +142 -0
  79. package/dist/shared/skill-mount.js.map +1 -0
  80. package/dist/shared/skill-writer.d.ts +28 -26
  81. package/dist/shared/skill-writer.js +79 -102
  82. package/dist/shared/skill-writer.js.map +1 -1
  83. package/dist/shared/thinking-mode.d.ts +35 -0
  84. package/dist/shared/thinking-mode.js +43 -0
  85. package/dist/shared/thinking-mode.js.map +1 -0
  86. package/dist/shared/worker-shutdown.d.ts +67 -0
  87. package/dist/shared/worker-shutdown.js +79 -0
  88. package/dist/shared/worker-shutdown.js.map +1 -0
  89. package/dist/shared/workspace/types.d.ts +3 -2
  90. package/dist/workflow-engine/loader.js +82 -6
  91. package/dist/workflow-engine/loader.js.map +1 -1
  92. package/dist/workflow-engine/tasks/human-input.d.ts +2 -1
  93. package/dist/workflow-engine/tasks/human-input.js +31 -3
  94. package/dist/workflow-engine/tasks/human-input.js.map +1 -1
  95. package/dist/workflow-engine/types.d.ts +17 -3
  96. package/dist/workflow-engine/types.js.map +1 -1
  97. package/dist/workflows/human-input-orchestrator.d.ts +4 -0
  98. package/dist/workflows/human-input-orchestrator.js +13 -0
  99. package/dist/workflows/human-input-orchestrator.js.map +1 -1
  100. package/package.json +15 -11
  101. package/src/activities/call-agent.ts +20 -6
  102. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +50 -0
  103. package/src/activities/execute-cursor/__tests__/cursor-mcp-config.test.ts +111 -0
  104. package/src/activities/execute-cursor/__tests__/error-classifier-billing.test.ts +67 -0
  105. package/src/activities/execute-cursor/__tests__/service-tier.test.ts +67 -7
  106. package/src/activities/execute-cursor/__tests__/skill-resolver.test.ts +3 -94
  107. package/src/activities/execute-cursor/__tests__/usage-accumulator.test.ts +33 -1
  108. package/src/activities/execute-cursor/cursor-mcp-config.ts +107 -0
  109. package/src/activities/execute-cursor/error-classifier.ts +42 -1
  110. package/src/activities/execute-cursor/index.ts +78 -41
  111. package/src/activities/execute-cursor/prompt-builder.ts +29 -2
  112. package/src/activities/execute-cursor/service-tier.ts +94 -63
  113. package/src/activities/execute-cursor/session-lifecycle.ts +1 -1
  114. package/src/activities/execute-cursor/skill-resolver.ts +10 -137
  115. package/src/activities/execute-cursor/usage-accumulator.ts +11 -1
  116. package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +15 -0
  117. package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +75 -0
  118. package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +1 -0
  119. package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +1 -0
  120. package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +1 -0
  121. package/src/activities/execute-deep-agent/__tests__/index.test.ts +1 -0
  122. package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +42 -29
  123. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +52 -0
  124. package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +1 -0
  125. package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +11 -8
  126. package/src/activities/execute-deep-agent/approval-file-change.ts +11 -1
  127. package/src/activities/execute-deep-agent/cas-capture-backend.ts +20 -3
  128. package/src/activities/execute-deep-agent/index.ts +57 -5
  129. package/src/activities/execute-deep-agent/prompt-builder.ts +22 -0
  130. package/src/activities/execute-deep-agent/setup.ts +21 -19
  131. package/src/activities/execute-deep-agent/subagent-transformer.ts +19 -12
  132. package/src/activities/execute-deep-agent/subagent-wiring.ts +3 -3
  133. package/src/index.ts +1 -1
  134. package/src/middleware/__tests__/path-normalization.test.ts +32 -32
  135. package/src/middleware/path-normalization.ts +78 -90
  136. package/src/middleware/types.ts +3 -3
  137. package/src/runner-manager.ts +26 -16
  138. package/src/runner.ts +14 -0
  139. package/src/shared/__tests__/artifact-storage.test.ts +28 -0
  140. package/src/shared/__tests__/bedrock-seam.test.ts +5 -5
  141. package/src/shared/__tests__/declared-preferences.test.ts +109 -0
  142. package/src/shared/__tests__/foundry-seam.test.ts +5 -5
  143. package/src/shared/__tests__/plan-mode-permissions.test.ts +15 -44
  144. package/src/shared/__tests__/skill-mount.test.ts +238 -0
  145. package/src/shared/__tests__/skill-writer.test.ts +151 -128
  146. package/src/shared/__tests__/vertex-adapter.test.ts +30 -3
  147. package/src/shared/__tests__/vertex-seam.test.ts +5 -5
  148. package/src/shared/__tests__/worker-shutdown.test.ts +101 -0
  149. package/src/shared/args-preview.ts +17 -3
  150. package/src/shared/artifact-storage.ts +20 -1
  151. package/src/shared/caller-identity.ts +10 -7
  152. package/src/shared/declared-preferences.ts +84 -0
  153. package/src/shared/mcp-enabled-tools.ts +2 -3
  154. package/src/shared/mcp-resolver.ts +20 -20
  155. package/src/shared/placeholder-resolver.ts +9 -2
  156. package/src/shared/plan-mode-permissions.ts +27 -58
  157. package/src/shared/skill-mount.ts +179 -0
  158. package/src/shared/skill-writer.ts +96 -130
  159. package/src/shared/thinking-mode.ts +52 -0
  160. package/src/shared/worker-shutdown.ts +99 -0
  161. package/src/shared/workspace/types.ts +3 -2
  162. package/src/workflow-engine/__tests__/loader.test.ts +95 -0
  163. package/src/workflow-engine/__tests__/tasks/human-input.test.ts +156 -0
  164. package/src/workflow-engine/loader.ts +97 -6
  165. package/src/workflow-engine/tasks/human-input.ts +41 -6
  166. package/src/workflow-engine/types.ts +18 -3
  167. package/src/workflows/human-input-orchestrator.ts +20 -2
  168. package/dist/activities/execute-cursor/connect-backfill.d.ts +0 -19
  169. package/dist/activities/execute-cursor/connect-backfill.js +0 -27
  170. package/dist/activities/execute-cursor/connect-backfill.js.map +0 -1
  171. package/dist/activities/execute-cursor/mcp-resolver.d.ts +0 -120
  172. package/dist/activities/execute-cursor/mcp-resolver.js +0 -194
  173. package/dist/activities/execute-cursor/mcp-resolver.js.map +0 -1
  174. package/dist/activities/execute-cursor/placeholder-resolver.d.ts +0 -34
  175. package/dist/activities/execute-cursor/placeholder-resolver.js +0 -82
  176. package/dist/activities/execute-cursor/placeholder-resolver.js.map +0 -1
  177. package/src/activities/execute-cursor/__tests__/mcp-resolver.test.ts +0 -125
  178. package/src/activities/execute-cursor/connect-backfill.ts +0 -52
  179. package/src/activities/execute-cursor/mcp-resolver.ts +0 -324
  180. package/src/activities/execute-cursor/placeholder-resolver.ts +0 -109
@@ -56,6 +56,7 @@ import {
56
56
  resolveCallerIdentity,
57
57
  } from "../../shared/caller-identity.js";
58
58
  import { readSessionContext } from "../../shared/session-context.js";
59
+ import { readDeclaredPreferences } from "../../shared/declared-preferences.js";
59
60
  import { withholdSecretContentFromMessages } from "../../shared/tool-row.js";
60
61
  import { StallTimeoutError, formatStallFailure } from "../../shared/stall-watchdog.js";
61
62
  import { resolveUsableArtifactStorage, loadArtifactStorageConfig, type ArtifactStorage } from "../../shared/artifact-storage.js";
@@ -71,7 +72,8 @@ import { DeltaEnricher } from "./delta-enricher.js";
71
72
  import { TodoTracker } from "./todo-tracker.js";
72
73
  import { StreamingUpdateScheduler, loadStreamingConfig } from "../../shared/streaming-scheduler.js";
73
74
  import { createCursorEventRecorder } from "./cursor-event-recorder.js";
74
- import { resolveMcpServers, toCursorMcpConfig, validateMcpServerEnv } from "./mcp-resolver.js";
75
+ import { resolveMcpServers } from "../../shared/mcp-resolver.js";
76
+ import { toCursorMcpConfig, validateMcpServerEnv } from "./cursor-mcp-config.js";
75
77
  import { resolveMcpTransportPosture } from "../../shared/mcp-transport-guard.js";
76
78
  import {
77
79
  discoverChannelMessaging,
@@ -85,7 +87,7 @@ import { injectSynthesizedAttachment } from "../../shared/synthesized-attachment
85
87
  import { mergeApprovalPolicies } from "./approval-policy.js";
86
88
  import { deriveActiveLeases, isUnattendedApprovalMode } from "../../shared/approval-policy.js";
87
89
  import { enabledToolsBySlug } from "../../shared/mcp-enabled-tools.js";
88
- import { backfillMcpServersIfNeeded } from "./connect-backfill.js";
90
+ import { backfillMcpServersIfNeeded } from "../../shared/connect-backfill.js";
89
91
  import { resolveExecutionEnv } from "./env-resolver.js";
90
92
  import { resolveBlueprint } from "./blueprint-resolver.js";
91
93
  import { buildCursorSubAgentDefinitions } from "./subagent-config.js";
@@ -136,6 +138,7 @@ import { setInterceptorExecutionId, runWithExecutionContext } from "./fetch-inte
136
138
  import { closeProxySessions } from "./http2-interceptor.js";
137
139
  import { resolveModelId, ensureLoaded as ensurePricingLoaded } from "./model-pricing.js";
138
140
  import { resolveEffectiveServiceTier } from "../../shared/service-tier.js";
141
+ import { resolveEffectiveThinkingMode } from "../../shared/thinking-mode.js";
139
142
  import { resolveServiceTierParams } from "./service-tier.js";
140
143
  import { UsageAccumulator } from "./usage-accumulator.js";
141
144
  import { StreamingUsageSummarySchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/usage_pb";
@@ -147,7 +150,7 @@ import type { ClassifiedError } from "./error-classifier.js";
147
150
  import { createAgent, createCloudAgent } from "./session-lifecycle.js";
148
151
  import { setMaxListeners } from "node:events";
149
152
  import { startHeartbeat } from "../../shared/heartbeat.js";
150
- import { getShutdownSignalForQueue } from "../../runner-manager.js";
153
+ import { classifyTurnInterruption, getShutdownSignalForQueue } from "../../shared/worker-shutdown.js";
151
154
 
152
155
  /**
153
156
  * Creates the activity functions bound to the runner config.
@@ -627,16 +630,19 @@ async function executeCursorInner(
627
630
  ),
628
631
  sessionId,
629
632
  );
630
- let mcpResolution = await resolveMcpServers(
633
+ // The resolved-server list mutates through backfill and attachment
634
+ // injection below; the Cursor SDK config is projected from it exactly
635
+ // once, after the last mutation (see toCursorMcpConfig).
636
+ let resolvedMcpServers = (await resolveMcpServers(
631
637
  client, blueprint.mergedMcpServerUsages, mcpEnvVars, transportPosture,
632
- );
638
+ )).resolvedServers;
633
639
  setupTiming.mark("resolve_mcp_servers");
634
640
 
635
641
  // Phase 4a: Connect backfill for undiscovered MCP servers
636
642
  heartbeatPhase = "resolving_mcp_servers";
637
643
  const sessionOrg = session.metadata?.org ?? "";
638
- mcpResolution = await backfillMcpServersIfNeeded(
639
- client, mcpResolution, blueprint.mergedMcpServerUsages, mcpEnvVars, sessionOrg,
644
+ resolvedMcpServers = await backfillMcpServersIfNeeded(
645
+ client, resolvedMcpServers, blueprint.mergedMcpServerUsages, mcpEnvVars, sessionOrg,
640
646
  transportPosture, heartbeat, secretKeys,
641
647
  );
642
648
  setupTiming.mark("backfill_mcp");
@@ -684,13 +690,9 @@ async function executeCursorInner(
684
690
  backendEndpoint: config.stigmerBackendEndpoint,
685
691
  });
686
692
  if (attachment) {
687
- const resolvedServers = injectSynthesizedAttachment(
688
- mcpResolution.resolvedServers, attachment, "channel messaging",
693
+ resolvedMcpServers = injectSynthesizedAttachment(
694
+ resolvedMcpServers, attachment, "channel messaging",
689
695
  );
690
- mcpResolution = {
691
- resolvedServers,
692
- cursorConfig: toCursorMcpConfig(resolvedServers),
693
- };
694
696
  }
695
697
  }
696
698
 
@@ -710,15 +712,13 @@ async function executeCursorInner(
710
712
  },
711
713
  );
712
714
  if (conversationAttachment) {
713
- const resolvedServers = injectSynthesizedAttachment(
714
- mcpResolution.resolvedServers, conversationAttachment, "conversation participation",
715
+ resolvedMcpServers = injectSynthesizedAttachment(
716
+ resolvedMcpServers, conversationAttachment, "conversation participation",
715
717
  );
716
- mcpResolution = {
717
- resolvedServers,
718
- cursorConfig: toCursorMcpConfig(resolvedServers),
719
- };
720
718
  }
721
- const mcpConfig = mcpResolution.cursorConfig;
719
+ // The one projection point: every mutation above is now visible in the
720
+ // Cursor SDK config by construction (no per-mutation rebuild to forget).
721
+ const mcpConfig = toCursorMcpConfig(resolvedMcpServers);
722
722
 
723
723
  // Phase 4b: Merge approval policies from all layers.
724
724
  //
@@ -734,16 +734,15 @@ async function executeCursorInner(
734
734
  // see ResolvedMcpServer.toolApprovalOverrides (issue #349) — so there
735
735
  // is no separate override input to pass here.
736
736
  const mergedPolicies = mergeApprovalPolicies(
737
- mcpResolution.resolvedServers,
737
+ resolvedMcpServers,
738
738
  leases,
739
739
  );
740
740
  heartbeat();
741
741
 
742
742
  // Phase 4c: Validate MCP server env health (diagnostic, non-blocking)
743
743
  const mcpWarnings = validateMcpServerEnv(
744
- mcpResolution.resolvedServers,
744
+ resolvedMcpServers,
745
745
  blueprint.mergedMcpServerUsages,
746
- envVars,
747
746
  );
748
747
  if (mcpWarnings.length > 0) {
749
748
  console.warn(
@@ -910,7 +909,7 @@ async function executeCursorInner(
910
909
  // servers' allow-lists, enforced by the hook's "disabled" arm ahead of
911
910
  // every approval bypass. The Cursor SDK config cannot hide a server's
912
911
  // tools, so this deny-at-call is the harness's enforcement.
913
- enabledToolsBySlug(mcpResolution.resolvedServers),
912
+ enabledToolsBySlug(resolvedMcpServers),
914
913
  );
915
914
  const hitlGate = await installHitlGate({
916
915
  workspaceRoot: primaryWorkspaceDir,
@@ -962,9 +961,10 @@ async function executeCursorInner(
962
961
  await ensurePricingLoaded();
963
962
  setupTiming.mark("load_pricing");
964
963
 
965
- // Phase 6: Validate model selection and resolve the service tier.
966
- // UNSPECIFIED → STANDARD resolves here and nowhere else (#357): every
967
- // upstream layer preserves the caller's raw enum value.
964
+ // Phase 6: Validate model selection and resolve the variant attributes.
965
+ // UNSPECIFIED → STANDARD (#357) and UNSPECIFIED DISABLED (#772)
966
+ // resolve here and nowhere else: every upstream layer preserves the
967
+ // caller's raw enum values.
968
968
  const requestedModel = spec.executionConfig?.modelName || "default";
969
969
  const validatedModel = resolveModelId(requestedModel);
970
970
  if (validatedModel !== requestedModel) {
@@ -973,6 +973,7 @@ async function executeCursorInner(
973
973
  );
974
974
  }
975
975
  const requestedServiceTier = resolveEffectiveServiceTier(spec.executionConfig?.serviceTier);
976
+ const requestedThinkingMode = resolveEffectiveThinkingMode(spec.executionConfig?.thinkingMode);
976
977
 
977
978
  heartbeat();
978
979
 
@@ -1007,13 +1008,15 @@ async function executeCursorInner(
1007
1008
  );
1008
1009
  }
1009
1010
 
1010
- // Translate the tier into the explicit variant params sent with every
1011
- // create/resume. Never a bare { id }: the catalog's default variant is
1012
- // account-influenced and picks the price (#357).
1011
+ // Translate the tier + thinking mode into the explicit variant params
1012
+ // sent with every create/resume. Never a bare { id }: the catalog's
1013
+ // default variant is account-influenced and picks the served variant
1014
+ // (#357 fast pricing, #772 thinking).
1013
1015
  const modelParams = await resolveServiceTierParams({
1014
1016
  apiKey: effectiveApiKey,
1015
1017
  modelId: validatedModel,
1016
1018
  tier: requestedServiceTier,
1019
+ thinking: requestedThinkingMode,
1017
1020
  executionId,
1018
1021
  });
1019
1022
 
@@ -1175,6 +1178,7 @@ async function executeCursorInner(
1175
1178
  contextBridge: readContextBridge(blueprint.sessionSpec.metadata),
1176
1179
  senderIdentity: readSenderIdentity(blueprint.sessionSpec.metadata),
1177
1180
  sessionContext: readSessionContext(blueprint.sessionSpec.metadata),
1181
+ declaredPreferences: readDeclaredPreferences(spec.declaredPreferences),
1178
1182
  conversationCatchup: readConversationCatchup(spec.conversationCatchup),
1179
1183
  // The turn's recorded transcript, seeded from the persisted execution
1180
1184
  // on a reinvocation (Phase 3). Consumed only by the HITL-recovery
@@ -1229,6 +1233,7 @@ async function executeCursorInner(
1229
1233
  validatedModel,
1230
1234
  requestedServiceTier,
1231
1235
  modelParams,
1236
+ requestedThinkingMode,
1232
1237
  );
1233
1238
 
1234
1239
  // Phase 10c: Start OTel turn span. Coarse-grained — spans the whole turn
@@ -1401,13 +1406,21 @@ async function executeCursorInner(
1401
1406
  // classifies a shutdown from the shutdown signal directly (in
1402
1407
  // resolvePreBoundaryTerminal). The heartbeat timer may set `cancelled` before
1403
1408
  // the AbortSignal microtask propagates; the direct signal check catches that.
1404
- const isShutdown = periodicHeartbeat.workerShutdown || (shutdownSignal?.aborted ?? false);
1405
- if (isShutdown) {
1409
+ // The decision table (including #776's grace-window guard: an aborted
1410
+ // shutdown signal with NO interruption evidence stays "none") lives in
1411
+ // classifyTurnInterruption — shared/worker-shutdown.ts.
1412
+ const interruption = classifyTurnInterruption({
1413
+ heartbeatCancelled: periodicHeartbeat.cancelled,
1414
+ heartbeatWorkerShutdown: periodicHeartbeat.workerShutdown,
1415
+ cancellationSignalAborted: Context.current().cancellationSignal.aborted,
1416
+ shutdownSignalAborted: shutdownSignal?.aborted ?? false,
1417
+ });
1418
+ if (interruption === "worker-shutdown") {
1406
1419
  turnState.pauseDetected = false;
1407
- } else if (periodicHeartbeat.cancelled) {
1420
+ } else if (interruption === "pause") {
1408
1421
  turnState.pauseDetected = true;
1409
1422
  }
1410
- workerShutdownDetected = isShutdown;
1423
+ workerShutdownDetected = interruption === "worker-shutdown";
1411
1424
 
1412
1425
  // Post-stream finalize, shared by the primary turn and both recovery retries:
1413
1426
  // finalize the transcript + streaming flags, mark any in-flight sub-agent
@@ -1505,10 +1518,16 @@ async function executeCursorInner(
1505
1518
  return { kind: "return" };
1506
1519
  }
1507
1520
 
1508
- // Worker shutdown: the runner-manager aborted the shutdown signal. NOT a
1521
+ // Worker shutdown: the runner/manager aborted the shutdown signal. NOT a
1509
1522
  // user pause. Checked via the shutdown signal directly so a retry (whose
1510
- // periodic heartbeat is already stopped) still classifies it correctly.
1511
- if (workerShutdownDetected || (shutdownSignal?.aborted ?? false)) {
1523
+ // periodic heartbeat is already stopped) still classifies it correctly
1524
+ // but only alongside a delivered cancellation: a retry that completed
1525
+ // normally inside the drain grace window must stay a completion (#776's
1526
+ // grace-window guard, mirroring the primary's `interrupted` gate).
1527
+ if (
1528
+ workerShutdownDetected ||
1529
+ ((shutdownSignal?.aborted ?? false) && Context.current().cancellationSignal.aborted)
1530
+ ) {
1512
1531
  status.phase = ExecutionPhase.EXECUTION_FAILED;
1513
1532
  status.error = "Execution interrupted: runner worker was shut down. Retry or resume.";
1514
1533
  status.completedAt = utcTimestamp();
@@ -1806,6 +1825,7 @@ async function executeCursorInner(
1806
1825
  fallbackContext: { model: validatedModel, mode: agentMode, agentId: resolution.agentId },
1807
1826
  durationMs: (result as unknown as Record<string, unknown>).durationMs as number | undefined,
1808
1827
  messageCount: status.messages.length,
1828
+ proxyMode: !!config.proxyEndpoint,
1809
1829
  });
1810
1830
 
1811
1831
  console.error(
@@ -1865,6 +1885,7 @@ async function executeCursorInner(
1865
1885
  contextBridge: readContextBridge(blueprint.sessionSpec.metadata),
1866
1886
  senderIdentity: readSenderIdentity(blueprint.sessionSpec.metadata),
1867
1887
  sessionContext: readSessionContext(blueprint.sessionSpec.metadata),
1888
+ declaredPreferences: readDeclaredPreferences(spec.declaredPreferences),
1868
1889
  conversationCatchup: readConversationCatchup(spec.conversationCatchup),
1869
1890
  // Composed fresh (not reused from Phase 10): the failed primary
1870
1891
  // stream may have appended partial work onto status.messages,
@@ -1944,6 +1965,7 @@ async function executeCursorInner(
1944
1965
  conversationErrorText: retryConversationErrorText,
1945
1966
  isResumedHandle: false,
1946
1967
  fallbackContext: { model: validatedModel, mode: agentMode, agentId: freshAgent.agentId },
1968
+ proxyMode: !!config.proxyEndpoint,
1947
1969
  });
1948
1970
 
1949
1971
  status.phase = ExecutionPhase.EXECUTION_FAILED;
@@ -2150,9 +2172,12 @@ async function executeCursorInner(
2150
2172
  periodicHeartbeat?.stop();
2151
2173
 
2152
2174
  if (err instanceof CancelledFailure) {
2153
- // workerShutdownDetected means the runner-manager signaled shutdown
2154
- // before the worker drained. This is infrastructure failure, not pause.
2155
- if (workerShutdownDetected) {
2175
+ // Worker shutdown is infrastructure failure, not pause. The direct
2176
+ // signal check covers a CancelledFailure thrown BEFORE the post-stream
2177
+ // classification ran (workerShutdownDetected still false); no extra
2178
+ // interruption-evidence gate is needed here — the caught
2179
+ // CancelledFailure IS the evidence (#776).
2180
+ if (workerShutdownDetected || (shutdownSignal?.aborted ?? false)) {
2156
2181
  console.log(`ExecuteCursor cancelled (worker shutdown) for execution ${executionId}`);
2157
2182
  status.phase = ExecutionPhase.EXECUTION_FAILED;
2158
2183
  status.error = "Execution interrupted: runner worker was shut down. Retry or resume.";
@@ -2242,6 +2267,7 @@ async function executeCursorInner(
2242
2267
  capturedRejection: getCapturedRejection(executionId),
2243
2268
  isResumedHandle: false,
2244
2269
  fallbackContext: errorContext,
2270
+ proxyMode: !!config.proxyEndpoint,
2245
2271
  });
2246
2272
  clearCapturedRejection(executionId);
2247
2273
  status.phase = ExecutionPhase.EXECUTION_FAILED;
@@ -2451,10 +2477,19 @@ export interface BuildPromptInput {
2451
2477
  * turn.
2452
2478
  */
2453
2479
  sessionContext?: string;
2480
+ /**
2481
+ * Platform-declared standing preferences from the execution spec's
2482
+ * `declared_preferences` (stigmer/stigmer#293). Like the bridge, only
2483
+ * the enhanced-prompt path consumes it — deliberately frozen per Cursor
2484
+ * session (DD-002 D3): the first turn delivers it into the agent's own
2485
+ * conversation store, and repeating it on resumed turns would bloat the
2486
+ * store with identical content.
2487
+ */
2488
+ declaredPreferences?: import("../../shared/declared-preferences.js").DeclaredPreferencesContent;
2454
2489
  /**
2455
2490
  * Conversation catchup from the execution spec's `conversation_catchup`
2456
2491
  * (cloud DD-006): what happened on the channel conversation that the
2457
- * agent has not seen. PER-TURN, so unlike the three standing values
2492
+ * agent has not seen. PER-TURN, so unlike the standing values
2458
2493
  * above it rides BOTH prompt paths — the enhanced prompt and a resumed
2459
2494
  * turn's prefix (the `interaction_mode` shape). Handback lands
2460
2495
  * mid-session on a resumed agent: the resumed path is the one that
@@ -2589,6 +2624,7 @@ export function buildPrompt(input: BuildPromptInput): string {
2589
2624
  contextBridge: input.contextBridge,
2590
2625
  senderIdentity: input.senderIdentity,
2591
2626
  sessionContext: input.sessionContext,
2627
+ declaredPreferences: input.declaredPreferences,
2592
2628
  conversationCatchup,
2593
2629
  },
2594
2630
  {
@@ -2655,6 +2691,7 @@ export function buildPrompt(input: BuildPromptInput): string {
2655
2691
  contextBridge: input.contextBridge,
2656
2692
  senderIdentity: input.senderIdentity,
2657
2693
  sessionContext: input.sessionContext,
2694
+ declaredPreferences: input.declaredPreferences,
2658
2695
  conversationCatchup,
2659
2696
  });
2660
2697
  }
@@ -31,6 +31,10 @@ import {
31
31
  type SenderIdentity,
32
32
  } from "../../shared/sender-identity.js";
33
33
  import { formatSessionContextText } from "../../shared/session-context.js";
34
+ import {
35
+ formatDeclaredPreferencesText,
36
+ type DeclaredPreferencesContent,
37
+ } from "../../shared/declared-preferences.js";
34
38
  import {
35
39
  visionDisclosureLines,
36
40
  type NotViewableEntry,
@@ -147,11 +151,20 @@ export interface EnhancedPromptOptions {
147
151
  * store — the context is constant for the session's lifetime.
148
152
  */
149
153
  sessionContext?: string;
154
+ /**
155
+ * Platform-declared standing preferences (stigmer/stigmer#293): the org's
156
+ * and user's standing context, server-snapshotted onto the execution
157
+ * spec's `declared_preferences` at create. Like the bridge, it lands in
158
+ * the first message and persists in the cursor agent's own conversation
159
+ * store — deliberately frozen per Cursor session (DD-002 D3): repeating
160
+ * it every resumed turn would bloat the store with identical content.
161
+ */
162
+ declaredPreferences?: DeclaredPreferencesContent;
150
163
  /**
151
164
  * Conversation catchup (cloud DD-006): what happened on the channel
152
165
  * conversation that the agent has not seen, read from the execution
153
- * spec's `conversation_catchup`. PER-TURN, unlike the three standing
154
- * siblings above: it rides BOTH prompt paths — this enhanced prompt and
166
+ * spec's `conversation_catchup`. PER-TURN, unlike the standing siblings
167
+ * above: it rides BOTH prompt paths — this enhanced prompt and
155
168
  * a resumed turn's prefix (the `interaction_mode` shape) — because
156
169
  * handback lands mid-session on a resumed agent, the exact case the
157
170
  * metadata lane cannot reach.
@@ -232,6 +245,14 @@ export function buildEnhancedPrompt(options: EnhancedPromptOptions): string {
232
245
  sections.push(formatSenderIdentitySection(options.senderIdentity));
233
246
  }
234
247
 
248
+ // Platform-declared standing facts precede embedder-supplied context
249
+ // (DD-002 D3): both are standing background, but the declared preferences
250
+ // are platform-authored while session context is the embedder's overlay —
251
+ // the more specific overlay reads later and naturally refines.
252
+ if (options.declaredPreferences) {
253
+ sections.push(formatDeclaredPreferencesSection(options.declaredPreferences));
254
+ }
255
+
235
256
  // Standing facts about the user (session context) come before the
236
257
  // carried conversation (bridge): the bridge may refer back to them.
237
258
  if (options.sessionContext) {
@@ -445,6 +466,12 @@ export function formatSenderIdentitySection(identity: SenderIdentity): string {
445
466
  return `<conversation_sender>\n${formatSenderIdentityText(identity)}\n</conversation_sender>`;
446
467
  }
447
468
 
469
+ export function formatDeclaredPreferencesSection(
470
+ preferences: DeclaredPreferencesContent,
471
+ ): string {
472
+ return `<declared_preferences>\n${formatDeclaredPreferencesText(preferences)}\n</declared_preferences>`;
473
+ }
474
+
448
475
  export function formatSessionContextSection(context: string): string {
449
476
  return `<session_context>\n${formatSessionContextText(context)}\n</session_context>`;
450
477
  }
@@ -1,18 +1,23 @@
1
1
  /**
2
- * Service-tier → Cursor variant-parameter translation (stigmer/stigmer#357).
2
+ * Variant-attribute → Cursor variant-parameter translation
3
+ * (stigmer/stigmer#357 service tier, #772 thinking mode).
3
4
  *
4
5
  * The platform contract: an execution's model selection is ALWAYS explicit.
5
- * A bare `{ id }` lets the Cursor catalog's default variant decide the price
6
- * (observed 2026-08-06: composer-2.5 defaults to fast=true at ~4x base
7
- * rates, claude-haiku-4-5 to thinking=true), and that default follows an
8
- * out-of-band account setting. This module pins every price-bearing variant
9
- * parameter the model declares, so the billed variant is a deterministic
10
- * function of ExecutionConfig.service_tier:
6
+ * A bare `{ id }` lets the Cursor catalog's default variant decide the
7
+ * variant (observed 2026-08-06: composer-2.5 defaults to fast=true at ~4x
8
+ * base rates, claude-haiku-4-5 to thinking=true), and that default follows
9
+ * an out-of-band account setting. This module pins every user-selectable
10
+ * variant parameter the model declares, so the served variant is a
11
+ * deterministic function of the execution config:
11
12
  *
12
- * - STANDARD: every price-bearing boolean pinned to its base value
13
- * (fast=false, thinking=false where the parameter exists).
14
- * - FAST: fast=true, thinking still pinned false.
15
- * - Price-neutral parameters (e.g. effort) are deliberately NOT pinned —
13
+ * - fast: pinned from ExecutionConfig.service_tier (FAST true).
14
+ * Price-bearing — the fast variant bills at pricingVariants.fast rates.
15
+ * - thinking: pinned from ExecutionConfig.thinking_mode (ENABLED → true).
16
+ * Per-token price-neutral (ledger-verified 2026-08-15: thinking wire ids
17
+ * bill exactly base rates; thinking+fast bills exactly the fast rate) —
18
+ * pinned anyway because the served variant must never follow the account
19
+ * default, and ENABLED turns consume more output (reasoning) tokens.
20
+ * - Parameters that are neither (e.g. effort) are deliberately NOT pinned —
16
21
  * they follow the catalog default and do not change the bill.
17
22
  *
18
23
  * Parameter bundles come from Cursor.models.list() (worker-cached): the
@@ -20,17 +25,19 @@
20
25
  * rides the same proxy fetch-interceptor as every other SDK call, so it
21
26
  * works identically in proxy and direct modes.
22
27
  *
23
- * The harness-neutral halves — the tier enum semantics, and the single
24
- * UNSPECIFIED→STANDARD resolution point — live in
25
- * `shared/service-tier.ts` since #361 extended tiers to the native
26
- * harness; this module keeps only the Cursor-catalog translation.
28
+ * The harness-neutral halves — the enum semantics and the single
29
+ * UNSPECIFIED→default resolution points — live in `shared/service-tier.ts`
30
+ * (since #361 extended tiers to the native harness) and
31
+ * `shared/thinking-mode.ts`; this module keeps only the Cursor-catalog
32
+ * translation.
27
33
  */
28
34
 
29
35
  import { Cursor } from "@cursor/sdk";
30
36
  import type { ModelListItem, ModelParameterValue } from "@cursor/sdk";
31
- import { ServiceTier } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
37
+ import { ServiceTier, ThinkingMode } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
32
38
 
33
39
  import { serviceTierLabel, type EffectiveServiceTier } from "../../shared/service-tier.js";
40
+ import { thinkingModeLabel, type EffectiveThinkingMode } from "../../shared/thinking-mode.js";
34
41
 
35
42
  /**
36
43
  * Catalog ids that mean "Cursor picks the model" (Auto). Auto's single
@@ -42,9 +49,11 @@ import { serviceTierLabel, type EffectiveServiceTier } from "../../shared/servic
42
49
  const AUTO_MODEL_IDS = new Set(["default", "auto"]);
43
50
 
44
51
  /**
45
- * Variant parameter ids that change the per-token price. Pinning exactly
46
- * these keeps the bill deterministic while leaving latency/effort knobs on
47
- * their catalog defaults. Sourced from the Cursor catalog survey
52
+ * The user-selectable variant parameter ids. Pinning exactly these keeps
53
+ * the served variant deterministic while leaving effort knobs on their
54
+ * catalog defaults. `fast` changes the per-token price; `thinking` is
55
+ * price-neutral but changes token consumption and must never follow the
56
+ * account default. Sourced from the Cursor catalog survey
48
57
  * (stigmer-cloud _projects/2026-08/20260806.04.model-service-tier).
49
58
  */
50
59
  const FAST_PARAM_ID = "fast";
@@ -108,46 +117,55 @@ export interface ResolveServiceTierParamsOptions {
108
117
  /** Validated model id the execution runs on (may be "default" for Auto). */
109
118
  readonly modelId: string;
110
119
  readonly tier: EffectiveServiceTier;
120
+ readonly thinking: EffectiveThinkingMode;
111
121
  /** For log correlation only. */
112
122
  readonly executionId: string;
113
123
  }
114
124
 
115
125
  /**
116
- * Translate the effective tier into the explicit variant parameters to send
117
- * with every Agent.create/resume for this execution.
126
+ * Translate the effective tier + thinking mode into the explicit variant
127
+ * parameters to send with every Agent.create/resume for this execution.
118
128
  *
119
- * Fail-closed posture: FAST with no pinnable fast dimension is an error,
120
- * never a silent downgrade — create-time validation makes this unreachable
121
- * unless the registry and the provider catalog have drifted, and that drift
122
- * must be heard about, not absorbed.
129
+ * Fail-closed posture: an ACTIVE selection (FAST tier, ENABLED thinking)
130
+ * with no pinnable dimension is an error, never a silent downgrade —
131
+ * create-time validation makes this unreachable unless the registry and the
132
+ * provider catalog have drifted, and that drift must be heard about, not
133
+ * absorbed.
123
134
  *
124
- * STANDARD degrades to empty params on catalog failures rather than failing
125
- * the execution — but be clear about what that costs: an unpinned selection
126
- * falls to the catalog default variant, which for several models IS the
127
- * fast/thinking variant at multiples of base rates (the incident this module
128
- * exists to prevent). Failing every standard execution whenever the catalog
129
- * endpoint blips would be the worse trade; the WARN below plus billing's
130
- * requested-vs-billed mismatch alarm (which catches exactly this window)
131
- * are the compensating controls.
135
+ * The base selection (STANDARD + DISABLED) degrades to empty params on
136
+ * catalog failures rather than failing the execution — but be clear about
137
+ * what that costs: an unpinned selection falls to the catalog default
138
+ * variant, which for several models IS the fast/thinking variant (the
139
+ * incident this module exists to prevent). Failing every base execution
140
+ * whenever the catalog endpoint blips would be the worse trade; the WARN
141
+ * below plus billing's requested-vs-billed mismatch alarms (which catch
142
+ * exactly this window) are the compensating controls.
132
143
  */
133
144
  export async function resolveServiceTierParams(
134
145
  options: ResolveServiceTierParamsOptions,
135
146
  ): Promise<ModelParameterValue[]> {
136
- const { apiKey, modelId, tier, executionId } = options;
147
+ const { apiKey, modelId, tier, thinking, executionId } = options;
137
148
  const tierName = serviceTierLabel(tier);
149
+ const thinkingName = thinkingModeLabel(thinking);
150
+ // Selections that actively deviate from the base variant must fail loudly
151
+ // when they cannot be pinned; the base selection may degrade with a WARN.
152
+ const active: string[] = [];
153
+ if (tier === ServiceTier.FAST) active.push("service_tier=fast");
154
+ if (thinking === ThinkingMode.ENABLED) active.push("thinking=enabled");
138
155
 
139
156
  if (AUTO_MODEL_IDS.has(modelId)) {
140
- if (tier === ServiceTier.FAST) {
157
+ if (active.length > 0) {
141
158
  throw new Error(
142
- `service_tier=fast requires a pinned model — Auto ("${modelId}") has no ` +
143
- `tier dimension. Execution ${executionId} should have been refused at ` +
144
- `create time; the model registry and provider catalog may have drifted.`,
159
+ `${active.join(" + ")} requires a pinned model — Auto ("${modelId}") has ` +
160
+ `no variant dimensions. Execution ${executionId} should have been ` +
161
+ `refused at create time; the model registry and provider catalog may ` +
162
+ `have drifted.`,
145
163
  );
146
164
  }
147
165
  console.log(
148
- `ServiceTier: execution=${executionId} model=${modelId} tier=${tierName} ` +
149
- `Auto has no variant parameters; Cursor picks the model and variant ` +
150
- `(documented v1 limitation).`,
166
+ `VariantParams: execution=${executionId} model=${modelId} tier=${tierName} ` +
167
+ `thinking=${thinkingName} — Auto has no variant parameters; Cursor picks ` +
168
+ `the model and variant (documented v1 limitation).`,
151
169
  );
152
170
  return [];
153
171
  }
@@ -156,37 +174,38 @@ export async function resolveServiceTierParams(
156
174
  try {
157
175
  models = await listCatalogModels(apiKey);
158
176
  } catch (err) {
159
- if (tier === ServiceTier.FAST) {
177
+ if (active.length > 0) {
160
178
  throw new Error(
161
- `service_tier=fast for execution ${executionId} needs the Cursor model ` +
162
- `catalog to resolve variant params for "${modelId}", and the catalog ` +
163
- `fetch failed: ${err instanceof Error ? err.message : String(err)}`,
179
+ `${active.join(" + ")} for execution ${executionId} needs the Cursor ` +
180
+ `model catalog to resolve variant params for "${modelId}", and the ` +
181
+ `catalog fetch failed: ${err instanceof Error ? err.message : String(err)}`,
164
182
  );
165
183
  }
166
184
  console.warn(
167
- `ServiceTier UNPINNED: execution=${executionId} model=${modelId} tier=${tierName} ` +
168
- `catalog fetch failed (${err instanceof Error ? err.message : err}); ` +
169
- `sending no variant params, so the catalog DEFAULT variant decides the ` +
170
- `price for this execution (fast/thinking on several models the ` +
171
- `expensive direction). Billing's requested-vs-billed mismatch alarm ` +
172
- `covers this window.`,
185
+ `VariantParams UNPINNED: execution=${executionId} model=${modelId} tier=${tierName} ` +
186
+ `thinking=${thinkingName} — catalog fetch failed ` +
187
+ `(${err instanceof Error ? err.message : err}); sending no variant params, ` +
188
+ `so the catalog DEFAULT variant decides the served variant for this ` +
189
+ `execution (fast/thinking on several models — the expensive direction). ` +
190
+ `Billing's requested-vs-billed mismatch alarms cover this window.`,
173
191
  );
174
192
  return [];
175
193
  }
176
194
 
177
195
  const model = findCatalogModel(models, modelId);
178
196
  if (!model) {
179
- if (tier === ServiceTier.FAST) {
197
+ if (active.length > 0) {
180
198
  throw new Error(
181
- `service_tier=fast requested for "${modelId}" (execution ${executionId}) ` +
182
- `but the Cursor catalog does not list that model — cannot pin a fast ` +
183
- `variant. The model registry and provider catalog have drifted.`,
199
+ `${active.join(" + ")} requested for "${modelId}" (execution ${executionId}) ` +
200
+ `but the Cursor catalog does not list that model — cannot pin its ` +
201
+ `variant parameters. The model registry and provider catalog have drifted.`,
184
202
  );
185
203
  }
186
204
  console.warn(
187
- `ServiceTier UNPINNED: execution=${executionId} model=${modelId} tier=${tierName} ` +
188
- `model not in the Cursor catalog; sending no variant params, so the ` +
189
- `catalog DEFAULT variant decides the price for this execution.`,
205
+ `VariantParams UNPINNED: execution=${executionId} model=${modelId} tier=${tierName} ` +
206
+ `thinking=${thinkingName} — model not in the Cursor catalog; sending no ` +
207
+ `variant params, so the catalog DEFAULT variant decides the served ` +
208
+ `variant for this execution.`,
190
209
  );
191
210
  return [];
192
211
  }
@@ -196,10 +215,13 @@ export async function resolveServiceTierParams(
196
215
  if (def.id === FAST_PARAM_ID) {
197
216
  params.push({ id: FAST_PARAM_ID, value: tier === ServiceTier.FAST ? "true" : "false" });
198
217
  } else if (def.id === THINKING_PARAM_ID) {
199
- params.push({ id: THINKING_PARAM_ID, value: "false" });
218
+ params.push({
219
+ id: THINKING_PARAM_ID,
220
+ value: thinking === ThinkingMode.ENABLED ? "true" : "false",
221
+ });
200
222
  }
201
- // Any other parameter (e.g. effort) is price-neutral: left to the
202
- // catalog default variant on purpose.
223
+ // Any other parameter (e.g. effort) is price-neutral and not
224
+ // user-selectable: left to the catalog default variant on purpose.
203
225
  }
204
226
 
205
227
  if (tier === ServiceTier.FAST && !params.some((p) => p.id === FAST_PARAM_ID)) {
@@ -211,10 +233,19 @@ export async function resolveServiceTierParams(
211
233
  );
212
234
  }
213
235
 
236
+ if (thinking === ThinkingMode.ENABLED && !params.some((p) => p.id === THINKING_PARAM_ID)) {
237
+ throw new Error(
238
+ `thinking=enabled requested for "${modelId}" (execution ${executionId}) ` +
239
+ `but the Cursor catalog declares no "thinking" parameter for it. The ` +
240
+ `model registry claims a thinking capability the provider no longer ` +
241
+ `offers — refusing rather than silently serving the base variant.`,
242
+ );
243
+ }
244
+
214
245
  params.sort((a, b) => a.id.localeCompare(b.id));
215
246
  console.log(
216
- `ServiceTier: execution=${executionId} model=${modelId} tier=${tierName} ` +
217
- `params=${JSON.stringify(params)}`,
247
+ `VariantParams: execution=${executionId} model=${modelId} tier=${tierName} ` +
248
+ `thinking=${thinkingName} params=${JSON.stringify(params)}`,
218
249
  );
219
250
  return params;
220
251
  }
@@ -47,7 +47,7 @@ import type {
47
47
  ModelParameterValue,
48
48
  } from "@cursor/sdk";
49
49
  import { withTimeout, TimeoutError } from "../../shared/with-timeout.js";
50
- import type { CursorMcpServerConfig } from "./mcp-resolver.js";
50
+ import type { CursorMcpServerConfig } from "./cursor-mcp-config.js";
51
51
 
52
52
  // ---------------------------------------------------------------------------
53
53
  // Constants