chatroom-cli 1.102.2 → 1.103.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -27788,6 +27788,110 @@ function createHarnessActivityEmitter() {
27788
27788
  }
27789
27789
  var init_harness_activity_emitter = () => {};
27790
27790
 
27791
+ // ../../services/backend/src/domain/entities/harness/model-variant.ts
27792
+ function parseParamList(encoded, paramsPart) {
27793
+ const params = {};
27794
+ for (const param of paramsPart.split(",")) {
27795
+ if (!PARAM_RE.test(param)) {
27796
+ throw new ModelVariantParseError(`malformed variant param "${param}" in "${encoded}" (expected key=value)`);
27797
+ }
27798
+ const eq = param.indexOf("=");
27799
+ const key = param.slice(0, eq);
27800
+ if (Object.prototype.hasOwnProperty.call(params, key)) {
27801
+ throw new ModelVariantParseError(`duplicate variant param "${key}" in "${encoded}"`);
27802
+ }
27803
+ params[key] = param.slice(eq + 1);
27804
+ }
27805
+ return params;
27806
+ }
27807
+ function decodeModelVariant(encoded) {
27808
+ if (encoded.length === 0) {
27809
+ throw new ModelVariantParseError("empty model variant");
27810
+ }
27811
+ const match17 = MODEL_VARIANT_RE.exec(encoded);
27812
+ if (!match17) {
27813
+ throw new ModelVariantParseError(`malformed model variant: "${encoded}"`);
27814
+ }
27815
+ const [, model, paramsPart] = match17;
27816
+ if (paramsPart === undefined) {
27817
+ return { model, params: {} };
27818
+ }
27819
+ return { model, params: parseParamList(encoded, paramsPart) };
27820
+ }
27821
+ function encodeModelVariant(model, params = {}) {
27822
+ const entries2 = Object.entries(params);
27823
+ if (entries2.length === 0)
27824
+ return model;
27825
+ const paramsPart = entries2.map(([key, value]) => `${key}=${value}`).join(",");
27826
+ return `${model}[${paramsPart}]`;
27827
+ }
27828
+ function expandModelVariantCatalog(baseModelIds, combinations) {
27829
+ return baseModelIds.flatMap((id3) => combinations.map((combo) => encodeModelVariant(id3, combo)));
27830
+ }
27831
+ function describeCombination(combo) {
27832
+ const entries2 = Object.entries(combo);
27833
+ return entries2.length === 0 ? "no params" : entries2.map(([k, v2]) => `${k}=${v2}`).join(",");
27834
+ }
27835
+ function validateModelVariantParams(variant, schema) {
27836
+ const supported = schema.find((combo) => Object.keys(combo).length === Object.keys(variant.params).length && Object.entries(combo).every(([key, value]) => variant.params[key] === value));
27837
+ if (!supported) {
27838
+ throw new ModelVariantValidationError(`unsupported variant params for model "${variant.model}": ` + `${describeCombination(variant.params)}; ` + `allowed: ${schema.map(describeCombination).join(" | ")}`);
27839
+ }
27840
+ return variant;
27841
+ }
27842
+ var ModelVariantParseError, ModelVariantValidationError, MODEL_VARIANT_RE, PARAM_RE;
27843
+ var init_model_variant = __esm(() => {
27844
+ ModelVariantParseError = class ModelVariantParseError extends Error {
27845
+ code = "MODEL_VARIANT_PARSE";
27846
+ constructor(message) {
27847
+ super(message);
27848
+ this.name = "ModelVariantParseError";
27849
+ }
27850
+ };
27851
+ ModelVariantValidationError = class ModelVariantValidationError extends Error {
27852
+ code = "MODEL_VARIANT_VALIDATION";
27853
+ constructor(message) {
27854
+ super(message);
27855
+ this.name = "ModelVariantValidationError";
27856
+ }
27857
+ };
27858
+ MODEL_VARIANT_RE = /^([^\[\]]+?)(?:\[([^\]]*)\])?$/;
27859
+ PARAM_RE = /^[A-Za-z0-9_-]+=[^,\[\]\=]+$/;
27860
+ });
27861
+
27862
+ // src/daemon/infrastructure/local/harness/services/opencode-sdk/pure.ts
27863
+ function parseModelId(model) {
27864
+ if (!model)
27865
+ return;
27866
+ const slashIdx = model.indexOf("/");
27867
+ if (slashIdx === -1)
27868
+ return;
27869
+ const providerID = model.substring(0, slashIdx);
27870
+ const modelID = model.substring(slashIdx + 1);
27871
+ if (!providerID || !modelID)
27872
+ return;
27873
+ return { providerID, modelID };
27874
+ }
27875
+ function parseOpencodeSpawnModel(model) {
27876
+ if (!model.includes("["))
27877
+ return { model };
27878
+ const decoded = decodeModelVariant(model);
27879
+ const variant = decoded.params.variant;
27880
+ let resolvedModel = decoded.model;
27881
+ if (!resolvedModel.includes("/")) {
27882
+ const slashIdx = model.indexOf("/");
27883
+ const bracketIdx = model.indexOf("[");
27884
+ if (slashIdx !== -1 && slashIdx < bracketIdx) {
27885
+ resolvedModel = `${model.slice(0, slashIdx)}/${resolvedModel}`;
27886
+ }
27887
+ }
27888
+ return { model: resolvedModel, ...variant ? { variant } : {} };
27889
+ }
27890
+ var isInfoLine = (line) => line.trimStart().startsWith("INFO ");
27891
+ var init_pure = __esm(() => {
27892
+ init_model_variant();
27893
+ });
27894
+
27791
27895
  // src/daemon/infrastructure/local/harness/services/session-log-callbacks.ts
27792
27896
  function createSessionLogCallbacks() {
27793
27897
  let logCb;
@@ -27805,6 +27909,7 @@ var OPENCODE_COMMAND = "opencode", OpenCodeAgentService;
27805
27909
  var init_opencode_agent_service = __esm(() => {
27806
27910
  init_harness_activity_emitter();
27807
27911
  init_base_cli_agent_service();
27912
+ init_pure();
27808
27913
  OpenCodeAgentService = class OpenCodeAgentService extends BaseCLIAgentService {
27809
27914
  id = "opencode";
27810
27915
  displayName = "OpenCode";
@@ -27826,9 +27931,13 @@ var init_opencode_agent_service = __esm(() => {
27826
27931
  `).map((line) => line.trim()).filter((line) => line.length > 0);
27827
27932
  }
27828
27933
  async spawn(options) {
27934
+ const parsed = options.model ? parseOpencodeSpawnModel(options.model) : undefined;
27829
27935
  const args2 = ["run"];
27830
- if (options.model) {
27831
- args2.push("--model", options.model);
27936
+ if (parsed?.model) {
27937
+ args2.push("--model", parsed.model);
27938
+ }
27939
+ if (parsed?.variant) {
27940
+ args2.push("--variant", parsed.variant);
27832
27941
  }
27833
27942
  const fullPrompt = options.systemPrompt ? `${options.systemPrompt}
27834
27943
 
@@ -28025,6 +28134,36 @@ class PiRpcReader {
28025
28134
  }
28026
28135
  var init_pi_rpc_reader = () => {};
28027
28136
 
28137
+ // src/daemon/infrastructure/local/harness/services/pi/pure.ts
28138
+ function parsePiSpawnModel(model) {
28139
+ if (!model.includes("["))
28140
+ return { model };
28141
+ const decoded = decodeModelVariant(model);
28142
+ const thinking = decoded.params.thinking;
28143
+ let resolvedModel = decoded.model;
28144
+ if (!resolvedModel.includes("/")) {
28145
+ const slashIdx = model.indexOf("/");
28146
+ const bracketIdx = model.indexOf("[");
28147
+ if (slashIdx !== -1 && slashIdx < bracketIdx) {
28148
+ resolvedModel = `${model.slice(0, slashIdx)}/${resolvedModel}`;
28149
+ }
28150
+ }
28151
+ return { model: resolvedModel, ...thinking ? { thinking } : {} };
28152
+ }
28153
+ function resolvePiThinkingLevel(thinking) {
28154
+ if (!thinking)
28155
+ return;
28156
+ if (PI_THINKING_LEVELS.includes(thinking)) {
28157
+ return thinking;
28158
+ }
28159
+ return;
28160
+ }
28161
+ var PI_THINKING_LEVELS;
28162
+ var init_pure2 = __esm(() => {
28163
+ init_model_variant();
28164
+ PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
28165
+ });
28166
+
28028
28167
  // src/daemon/infrastructure/local/harness/services/agent-log-format.ts
28029
28168
  function buildAgentLogPrefix(agent, context5) {
28030
28169
  const roleTag = context5.role ?? "unknown";
@@ -28128,9 +28267,10 @@ function isExplicitToolError(result) {
28128
28267
  function getPiSessionDir(workingDir) {
28129
28268
  return join6(workingDir, ".chatroom", "pi-sessions");
28130
28269
  }
28131
- var PI_COMMAND = "pi", GET_STATE_TIMEOUT_MS = 5000, SPAWN_READY_DELAY_MS2 = 500, PiAgentService;
28270
+ var PI_COMMAND = "pi", GET_STATE_TIMEOUT_MS = 5000, PiAgentService;
28132
28271
  var init_pi_agent_service = __esm(() => {
28133
28272
  init_pi_rpc_reader();
28273
+ init_pure2();
28134
28274
  init_spawn_env();
28135
28275
  init_harness_activity_emitter();
28136
28276
  init_base_cli_agent_service();
@@ -28170,13 +28310,15 @@ var init_pi_agent_service = __esm(() => {
28170
28310
  }
28171
28311
  async spawn(options) {
28172
28312
  const { prompt, systemPrompt, model, context: context5, workingDir } = options;
28313
+ const parsed = model ? parsePiSpawnModel(model) : undefined;
28173
28314
  const childProcess = this.spawnPiRpcProcess({
28174
28315
  workingDir,
28175
28316
  systemPrompt,
28176
- model,
28317
+ model: parsed?.model,
28318
+ thinking: parsed?.thinking,
28177
28319
  resolvedConvexUrl: options.resolvedConvexUrl
28178
28320
  });
28179
- await this.waitForSpawnReady(childProcess);
28321
+ await this.assertChildProcessStarted(childProcess);
28180
28322
  if (!childProcess.stdout) {
28181
28323
  throw new Error("Pi RPC process has no stdout");
28182
28324
  }
@@ -28198,6 +28340,9 @@ var init_pi_agent_service = __esm(() => {
28198
28340
  if (args2.model) {
28199
28341
  rpcArgs.push("--model", args2.model);
28200
28342
  }
28343
+ if (args2.thinking) {
28344
+ rpcArgs.push("--thinking", args2.thinking);
28345
+ }
28201
28346
  if (args2.systemPrompt) {
28202
28347
  rpcArgs.push("--system-prompt", args2.systemPrompt);
28203
28348
  }
@@ -28210,15 +28355,6 @@ var init_pi_agent_service = __esm(() => {
28210
28355
  });
28211
28356
  return childProcess;
28212
28357
  }
28213
- async waitForSpawnReady(childProcess) {
28214
- await new Promise((resolve2) => setTimeout(resolve2, SPAWN_READY_DELAY_MS2));
28215
- if (childProcess.killed || childProcess.exitCode !== null) {
28216
- throw new Error(`Agent process exited immediately (exit code: ${childProcess.exitCode})`);
28217
- }
28218
- if (!childProcess.pid) {
28219
- throw new Error("Agent process started but has no PID");
28220
- }
28221
- }
28222
28358
  writePrompt(child, prompt) {
28223
28359
  return new Promise((resolve2, reject) => {
28224
28360
  if (!child.stdin) {
@@ -28512,13 +28648,27 @@ function cursorLegacySlugToVariant(flatSlug) {
28512
28648
  }
28513
28649
  const effort = base.match(/-(low|medium|high|xhigh|max)$/)?.[1];
28514
28650
  if (effort) {
28515
- params.effort = effort === "max" ? "xhigh" : effort;
28651
+ params.effort = effort === "max" && base.includes("opus") ? "xhigh" : effort;
28516
28652
  base = base.slice(0, -(effort.length + 1));
28517
28653
  }
28518
28654
  if (Object.keys(params).length === 0)
28519
28655
  return;
28520
28656
  return { base, params };
28521
28657
  }
28658
+ function normalizeCursorParamsForCliSlug(params) {
28659
+ const out = {};
28660
+ const effort = params.effort ?? (params.reasoning && params.reasoning !== "none" ? params.reasoning : undefined);
28661
+ if (effort && effort !== "none") {
28662
+ out.effort = effort;
28663
+ }
28664
+ if (params.fast === "enabled" || params.fast === "true") {
28665
+ out.fast = "enabled";
28666
+ }
28667
+ if (params.thinking === "enabled" || params.thinking === "true") {
28668
+ out.thinking = "enabled";
28669
+ }
28670
+ return out;
28671
+ }
28522
28672
  function cursorVariantToCliSlug(base, params) {
28523
28673
  const suffix = [];
28524
28674
  if (params.effort && params.effort !== "none")
@@ -28530,77 +28680,6 @@ function cursorVariantToCliSlug(base, params) {
28530
28680
  return suffix.length ? `${base}-${suffix.join("-")}` : base;
28531
28681
  }
28532
28682
 
28533
- // ../../services/backend/src/domain/entities/harness/model-variant.ts
28534
- function parseParamList(encoded, paramsPart) {
28535
- const params = {};
28536
- for (const param of paramsPart.split(",")) {
28537
- if (!PARAM_RE.test(param)) {
28538
- throw new ModelVariantParseError(`malformed variant param "${param}" in "${encoded}" (expected key=value)`);
28539
- }
28540
- const eq = param.indexOf("=");
28541
- const key = param.slice(0, eq);
28542
- if (Object.prototype.hasOwnProperty.call(params, key)) {
28543
- throw new ModelVariantParseError(`duplicate variant param "${key}" in "${encoded}"`);
28544
- }
28545
- params[key] = param.slice(eq + 1);
28546
- }
28547
- return params;
28548
- }
28549
- function decodeModelVariant(encoded) {
28550
- if (encoded.length === 0) {
28551
- throw new ModelVariantParseError("empty model variant");
28552
- }
28553
- const match17 = MODEL_VARIANT_RE.exec(encoded);
28554
- if (!match17) {
28555
- throw new ModelVariantParseError(`malformed model variant: "${encoded}"`);
28556
- }
28557
- const [, model, paramsPart] = match17;
28558
- if (paramsPart === undefined) {
28559
- return { model, params: {} };
28560
- }
28561
- return { model, params: parseParamList(encoded, paramsPart) };
28562
- }
28563
- function encodeModelVariant(model, params = {}) {
28564
- const entries2 = Object.entries(params);
28565
- if (entries2.length === 0)
28566
- return model;
28567
- const paramsPart = entries2.map(([key, value]) => `${key}=${value}`).join(",");
28568
- return `${model}[${paramsPart}]`;
28569
- }
28570
- function expandModelVariantCatalog(baseModelIds, combinations) {
28571
- return baseModelIds.flatMap((id3) => combinations.map((combo) => encodeModelVariant(id3, combo)));
28572
- }
28573
- function describeCombination(combo) {
28574
- const entries2 = Object.entries(combo);
28575
- return entries2.length === 0 ? "no params" : entries2.map(([k, v2]) => `${k}=${v2}`).join(",");
28576
- }
28577
- function validateModelVariantParams(variant, schema) {
28578
- const supported = schema.find((combo) => Object.keys(combo).length === Object.keys(variant.params).length && Object.entries(combo).every(([key, value]) => variant.params[key] === value));
28579
- if (!supported) {
28580
- throw new ModelVariantValidationError(`unsupported variant params for model "${variant.model}": ` + `${describeCombination(variant.params)}; ` + `allowed: ${schema.map(describeCombination).join(" | ")}`);
28581
- }
28582
- return variant;
28583
- }
28584
- var ModelVariantParseError, ModelVariantValidationError, MODEL_VARIANT_RE, PARAM_RE;
28585
- var init_model_variant = __esm(() => {
28586
- ModelVariantParseError = class ModelVariantParseError extends Error {
28587
- code = "MODEL_VARIANT_PARSE";
28588
- constructor(message) {
28589
- super(message);
28590
- this.name = "ModelVariantParseError";
28591
- }
28592
- };
28593
- ModelVariantValidationError = class ModelVariantValidationError extends Error {
28594
- code = "MODEL_VARIANT_VALIDATION";
28595
- constructor(message) {
28596
- super(message);
28597
- this.name = "ModelVariantValidationError";
28598
- }
28599
- };
28600
- MODEL_VARIANT_RE = /^([^\[\]]+?)(?:\[([^\]]*)\])?$/;
28601
- PARAM_RE = /^[A-Za-z0-9_-]+=[^,\[\]\=]+$/;
28602
- });
28603
-
28604
28683
  // ../../services/backend/src/domain/entities/harness/model-provider.ts
28605
28684
  function prefixModelWithProvider(provider, modelId) {
28606
28685
  const bracket = modelId.indexOf("[");
@@ -28809,7 +28888,7 @@ function decodeCursorVariant(encoded) {
28809
28888
  try {
28810
28889
  const decoded = decodeModelVariant(encoded);
28811
28890
  return {
28812
- cliSlug: cursorVariantToCliSlug(decoded.model, decoded.params),
28891
+ cliSlug: cursorVariantToCliSlug(decoded.model, normalizeCursorParamsForCliSlug(decoded.params)),
28813
28892
  params: decoded.params
28814
28893
  };
28815
28894
  } catch {
@@ -28838,9 +28917,7 @@ function decodedVariantToModelSelection(decoded) {
28838
28917
  ...params.length > 0 ? { params } : {}
28839
28918
  };
28840
28919
  }
28841
- function resolveCursorSdkSpawnModelSelection(model, defaultModel = "composer-2.5") {
28842
- if (!model)
28843
- return { id: defaultModel };
28920
+ function resolveCursorSdkSpawnModelSelection(model) {
28844
28921
  const bare = resolveCursorSdkModel(model);
28845
28922
  if (bare.includes("[")) {
28846
28923
  const decoded = decodeModelVariant(bare);
@@ -29949,6 +30026,18 @@ var init_claude_sdk_stream_adapter = __esm(() => {
29949
30026
  };
29950
30027
  });
29951
30028
 
30029
+ // src/daemon/infrastructure/local/harness/services/require-harness-model.ts
30030
+ function requireHarnessModel(model, context5) {
30031
+ const trimmed = model?.trim();
30032
+ if (!trimmed) {
30033
+ throw new Error(`Harness model is required (${context5})`);
30034
+ }
30035
+ return trimmed;
30036
+ }
30037
+ function resolveHarnessResumeModel(spawnModel, storedModel, context5) {
30038
+ return requireHarnessModel(spawnModel ?? storedModel, context5);
30039
+ }
30040
+
29952
30041
  // src/daemon/infrastructure/local/harness/services/wire-native-stream-adapter.ts
29953
30042
  function wireNativeStreamAdapter(args2) {
29954
30043
  args2.adapter.setAssistantTextCapture((text) => {
@@ -29985,7 +30074,7 @@ async function withTimeout(p, ms, label) {
29985
30074
  // src/daemon/infrastructure/local/harness/services/claude-sdk/claude-sdk-agent-service.ts
29986
30075
  import { randomUUID } from "node:crypto";
29987
30076
  function decodeClaudeSdkModel(model) {
29988
- const variant = decodeClaudeVariant(model);
30077
+ const variant = decodeClaudeVariant(requireHarnessModel(model, "claude-sdk"));
29989
30078
  return { model: variant?.model, effort: variant?.effort };
29990
30079
  }
29991
30080
  async function loadSdk() {
@@ -30408,7 +30497,8 @@ var init_claude_sdk_agent_service = __esm(() => {
30408
30497
  }
30409
30498
  }
30410
30499
  async spawn(options) {
30411
- const { model, effort } = decodeClaudeSdkModel(options.model);
30500
+ const resolvedModel = requireHarnessModel(options.model, "claude-sdk spawn");
30501
+ const { model, effort } = decodeClaudeSdkModel(resolvedModel);
30412
30502
  const deferInitialTurn = options.deferInitialTurn ?? false;
30413
30503
  const keeper = this.spawnKeeper(options.workingDir);
30414
30504
  const pid = keeper.pid;
@@ -30442,7 +30532,8 @@ var init_claude_sdk_agent_service = __esm(() => {
30442
30532
  const pid = keeper.pid;
30443
30533
  await loadSdk();
30444
30534
  const executablePath = await resolvePathToClaudeCodeExecutable();
30445
- const { model, effort } = decodeClaudeSdkModel(options.model ?? stored.model);
30535
+ const resolvedModel = resolveHarnessResumeModel(options.model, stored.model, "claude-sdk resumeFromDaemonMemory");
30536
+ const { model, effort } = decodeClaudeSdkModel(resolvedModel);
30446
30537
  return this.startRunningSession({
30447
30538
  pid,
30448
30539
  keeper,
@@ -30478,7 +30569,8 @@ var init_codex_sdk_model_variants = __esm(() => {
30478
30569
  { reasoning: "low" },
30479
30570
  { reasoning: "medium" },
30480
30571
  { reasoning: "high" },
30481
- { reasoning: "xhigh" }
30572
+ { reasoning: "xhigh" },
30573
+ { reasoning: "max" }
30482
30574
  ];
30483
30575
  });
30484
30576
 
@@ -31511,7 +31603,8 @@ ${deferredResume}` : deferredResume;
31511
31603
  }
31512
31604
  }
31513
31605
  async spawn(options) {
31514
- const variant = decodeCodexVariant(options.model);
31606
+ const model = requireHarnessModel(options.model, "codex-sdk spawn");
31607
+ const variant = decodeCodexVariant(model);
31515
31608
  const deferInitialTurn = options.deferInitialTurn ?? false;
31516
31609
  const keeper = this.spawnKeeper(options.workingDir);
31517
31610
  const pid = keeper.pid;
@@ -31542,7 +31635,7 @@ ${options.prompt}`;
31542
31635
  thread,
31543
31636
  context: context5,
31544
31637
  workingDir: options.workingDir,
31545
- model: options.model,
31638
+ model,
31546
31639
  initialPrompt: fullPrompt,
31547
31640
  deferInitialTurn,
31548
31641
  storedSystemPrompt: options.systemPrompt
@@ -31552,7 +31645,8 @@ ${options.prompt}`;
31552
31645
  try {
31553
31646
  const keeper = this.spawnKeeper(stored.workingDir);
31554
31647
  const pid = keeper.pid;
31555
- const variant = decodeCodexVariant(options.model ?? stored.model);
31648
+ const model = resolveHarnessResumeModel(options.model, stored.model, "codex-sdk resumeFromDaemonMemory");
31649
+ const variant = decodeCodexVariant(model);
31556
31650
  const { Codex } = await loadSdk2();
31557
31651
  const codexPath = resolveCodexExecutablePath();
31558
31652
  const codex = new Codex({
@@ -31567,7 +31661,7 @@ ${options.prompt}`;
31567
31661
  thread,
31568
31662
  context: options.context,
31569
31663
  workingDir: stored.workingDir,
31570
- model: options.model ?? stored.model,
31664
+ model,
31571
31665
  initialPrompt: options.prompt,
31572
31666
  storedSystemPrompt: options.systemPrompt,
31573
31667
  resumedThreadId: stored.harnessSessionId
@@ -32225,7 +32319,8 @@ var init_cursor_sdk_agent_service = __esm(() => {
32225
32319
  const context5 = options.context;
32226
32320
  const logPrefix = buildAgentLogPrefix("cursor-sdk", context5);
32227
32321
  const agentName = stored.agentName;
32228
- const modelSelection = resolveCursorSdkSpawnModelSelection(options.model ?? stored.model);
32322
+ const model = resolveHarnessResumeModel(options.model, stored.model, "cursor-sdk resumeFromDaemonMemory");
32323
+ const modelSelection = resolveCursorSdkSpawnModelSelection(model);
32229
32324
  const systemPrompt = options.systemPrompt ? `${NO_SUBAGENT_DIRECTIVE2}
32230
32325
 
32231
32326
  ${options.systemPrompt}` : NO_SUBAGENT_DIRECTIVE2;
@@ -32252,7 +32347,7 @@ ${options.prompt}`;
32252
32347
  agent,
32253
32348
  context: context5,
32254
32349
  agentName,
32255
- model: options.model ?? stored.model,
32350
+ model,
32256
32351
  workingDir: stored.workingDir,
32257
32352
  initialPrompt: fullPrompt,
32258
32353
  forceFirstTurn: true
@@ -32512,7 +32607,8 @@ ${deferredResume}` : deferredResume;
32512
32607
  const context5 = options.context;
32513
32608
  const logPrefix = buildAgentLogPrefix("cursor-sdk", context5);
32514
32609
  const agentName = buildAgentName2(context5);
32515
- const modelSelection = resolveCursorSdkSpawnModelSelection(options.model);
32610
+ const model = requireHarnessModel(options.model, "cursor-sdk spawn");
32611
+ const modelSelection = resolveCursorSdkSpawnModelSelection(model);
32516
32612
  const systemPrompt = options.systemPrompt ? `${NO_SUBAGENT_DIRECTIVE2}
32517
32613
 
32518
32614
  ${options.systemPrompt}` : NO_SUBAGENT_DIRECTIVE2;
@@ -32540,7 +32636,7 @@ ${options.prompt}`;
32540
32636
  agent,
32541
32637
  context: context5,
32542
32638
  agentName,
32543
- model: options.model,
32639
+ model,
32544
32640
  workingDir: options.workingDir,
32545
32641
  initialPrompt: fullPrompt,
32546
32642
  forceFirstTurn: !deferInitialTurn,
@@ -34513,21 +34609,6 @@ var init_parse_listening_url = __esm(() => {
34513
34609
  LISTENING_URL_RE = /opencode server listening on (https?:\/\/127\.0\.0\.1:\d+(?:\/[^\s]*)?)/;
34514
34610
  });
34515
34611
 
34516
- // src/daemon/infrastructure/local/harness/services/opencode-sdk/pure.ts
34517
- function parseModelId(model) {
34518
- if (!model)
34519
- return;
34520
- const slashIdx = model.indexOf("/");
34521
- if (slashIdx === -1)
34522
- return;
34523
- const providerID = model.substring(0, slashIdx);
34524
- const modelID = model.substring(slashIdx + 1);
34525
- if (!providerID || !modelID)
34526
- return;
34527
- return { providerID, modelID };
34528
- }
34529
- var isInfoLine = (line) => line.trimStart().startsWith("INFO ");
34530
-
34531
34612
  // src/daemon/infrastructure/local/harness/services/opencode-sdk/select-agent.ts
34532
34613
  function selectAgent(agents) {
34533
34614
  const primaries = agents.filter((a) => a.mode !== "subagent");
@@ -35018,12 +35099,15 @@ var init_binary_agent_service = __esm(() => {
35018
35099
 
35019
35100
  // src/daemon/infrastructure/local/harness/services/opencode-sdk/opencode-sdk-agent-service.ts
35020
35101
  function buildDisabledToolsPromptBody(args2) {
35021
- const modelParts = args2.model ? parseModelId(args2.model) : undefined;
35102
+ const model = requireHarnessModel(args2.model, "opencode-sdk prompt");
35103
+ const parsed = parseOpencodeSpawnModel(model);
35104
+ const modelParts = parseModelId(parsed.model);
35022
35105
  return {
35023
35106
  agent: args2.agentName,
35024
35107
  ...args2.composedSystem ? { system: args2.composedSystem } : {},
35025
35108
  parts: [{ type: "text", text: args2.prompt }],
35026
35109
  ...modelParts ? { model: modelParts } : {},
35110
+ ...parsed.variant ? { variant: parsed.variant } : {},
35027
35111
  tools: {
35028
35112
  task: false,
35029
35113
  question: false,
@@ -35035,6 +35119,7 @@ var SERVE_STARTUP_TIMEOUT_MS = 1e4, SESSION_CREATE_TIMEOUT_MS = 30000, PROMPT_AS
35035
35119
  var init_opencode_sdk_agent_service = __esm(() => {
35036
35120
  init_dist();
35037
35121
  init_parse_listening_url();
35122
+ init_pure();
35038
35123
  init_session_event_forwarder();
35039
35124
  init_session_metadata_store();
35040
35125
  init_spawn_env();
@@ -35299,6 +35384,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
35299
35384
  this.sessionStore.remove(args2.sessionId);
35300
35385
  }
35301
35386
  async startFreshSessionOnServe(args2) {
35387
+ const model = requireHarnessModel(args2.model, "opencode-sdk resume fallback");
35302
35388
  const existingForwarder = this.forwarders.get(args2.pid);
35303
35389
  existingForwarder?.stop();
35304
35390
  this.forwarders.delete(args2.pid);
@@ -35354,14 +35440,14 @@ var init_opencode_sdk_agent_service = __esm(() => {
35354
35440
  await this.promptSessionAsync(client4, newSessionId2, buildDisabledToolsPromptBody({
35355
35441
  agentName: args2.agentName,
35356
35442
  prompt: args2.prompt,
35357
- model: args2.model
35443
+ model
35358
35444
  }));
35359
35445
  }
35360
35446
  async resumeFromDaemonMemory(options, session2) {
35361
35447
  const { prompt, systemPrompt, model, context: context5 } = options;
35362
35448
  const sessionId = session2.harnessSessionId;
35363
35449
  const agentName = session2.agentName;
35364
- const modelForSession = model ?? session2.model;
35450
+ const modelForSession = resolveHarnessResumeModel(model, session2.model, "opencode-sdk resumeFromDaemonMemory");
35365
35451
  const workingDir = session2.workingDir;
35366
35452
  const { childProcess, pid, baseUrl, client: client4 } = await this.startServeAndClient(workingDir, options.resolvedConvexUrl);
35367
35453
  let forwarder;
@@ -35408,7 +35494,8 @@ var init_opencode_sdk_agent_service = __esm(() => {
35408
35494
  });
35409
35495
  }
35410
35496
  async spawn(options) {
35411
- const { prompt, systemPrompt, model, context: context5 } = options;
35497
+ const { prompt, systemPrompt, context: context5 } = options;
35498
+ const model = requireHarnessModel(options.model, "opencode-sdk spawn");
35412
35499
  const deferInitialTurn = options.deferInitialTurn ?? false;
35413
35500
  const { childProcess, pid, baseUrl, client: client4 } = await this.startServeAndClient(options.workingDir, options.resolvedConvexUrl);
35414
35501
  let sessionId;
@@ -35488,6 +35575,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
35488
35575
  const activityEmitter = this.activityEmittersByPid.get(pid);
35489
35576
  activityEmitter?.beginTurn();
35490
35577
  const client4 = createOpencodeClient({ baseUrl: meta.baseUrl });
35578
+ const model = requireHarnessModel(meta.model, "opencode-sdk resumeTurn");
35491
35579
  const deferredSystem = meta.deferredSystemPrompt;
35492
35580
  const context5 = {
35493
35581
  machineId: meta.machineId,
@@ -35499,7 +35587,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
35499
35587
  agentName: meta.agentName,
35500
35588
  prompt,
35501
35589
  composedSystem: deferredSystem,
35502
- model: meta.model
35590
+ model
35503
35591
  }));
35504
35592
  if (deferredSystem) {
35505
35593
  this.sessionStore.upsert({ ...meta, deferredSystemPrompt: undefined });
@@ -35511,7 +35599,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
35511
35599
  baseUrl: meta.baseUrl,
35512
35600
  context: context5,
35513
35601
  agentName: meta.agentName,
35514
- model: meta.model,
35602
+ model,
35515
35603
  prompt,
35516
35604
  oldSessionId: meta.sessionId
35517
35605
  });
@@ -35775,17 +35863,13 @@ function waitForResumeOrAbort4(session2) {
35775
35863
  })
35776
35864
  ]);
35777
35865
  }
35778
- function resolveModel(modelRegistry, model) {
35779
- if (model) {
35780
- const slash = model.indexOf("/");
35781
- if (slash === -1) {
35782
- return modelRegistry.getAll().find((entry) => entry.id === model);
35783
- }
35784
- const provider = model.slice(0, slash);
35785
- const modelId = model.slice(slash + 1);
35786
- return modelRegistry.find(provider, modelId);
35866
+ function resolveRequiredModel(modelRegistry, model) {
35867
+ const slash = model.indexOf("/");
35868
+ const resolved = slash === -1 ? modelRegistry.getAll().find((entry) => entry.id === model) : modelRegistry.find(model.slice(0, slash), model.slice(slash + 1));
35869
+ if (!resolved) {
35870
+ throw new Error(`Pi model not found: ${model}`);
35787
35871
  }
35788
- return modelRegistry.getAvailable()[0];
35872
+ return resolved;
35789
35873
  }
35790
35874
  function writeSpawnError4(logPrefix, err, emitLogLine) {
35791
35875
  const line = formatAgentLogLine(logPrefix, "spawn-error", formatPiSdkLoadError(err));
@@ -35803,6 +35887,7 @@ var init_pi_sdk_agent_service = __esm(() => {
35803
35887
  init_base_cli_agent_service();
35804
35888
  init_detection_result();
35805
35889
  init_pi_agent_service();
35890
+ init_pure2();
35806
35891
  PiSdkAgentService = class PiSdkAgentService extends BaseCLIAgentService {
35807
35892
  id = "pi-sdk";
35808
35893
  displayName = "Pi (SDK)";
@@ -35848,6 +35933,78 @@ var init_pi_sdk_agent_service = __esm(() => {
35848
35933
  return [];
35849
35934
  }
35850
35935
  }
35936
+ getHarnessReconnectContext(pid) {
35937
+ const session2 = this.sessions.get(pid);
35938
+ if (!session2) {
35939
+ return;
35940
+ }
35941
+ return {
35942
+ agentName: "builder",
35943
+ model: session2.model
35944
+ };
35945
+ }
35946
+ async resumeFromDaemonMemory(options, stored) {
35947
+ const model = resolveHarnessResumeModel(options.model, stored.model, "pi-sdk resumeFromDaemonMemory");
35948
+ const keeper = this.spawnKeeper(stored.workingDir);
35949
+ const pid = keeper.pid;
35950
+ try {
35951
+ const session2 = await this.openResumedSession({
35952
+ workingDir: stored.workingDir,
35953
+ systemPrompt: options.systemPrompt,
35954
+ harnessSessionId: stored.harnessSessionId
35955
+ });
35956
+ const fullPrompt = `${options.systemPrompt}
35957
+
35958
+ ${options.prompt}`;
35959
+ return this.startRunningSession({
35960
+ pid,
35961
+ keeper,
35962
+ session: session2,
35963
+ context: options.context,
35964
+ workingDir: stored.workingDir,
35965
+ model,
35966
+ initialPrompt: fullPrompt,
35967
+ deferInitialTurn: false,
35968
+ storedSystemPrompt: options.systemPrompt
35969
+ });
35970
+ } catch (err) {
35971
+ writeSpawnError4(buildAgentLogPrefix("pi-sdk", options.context), err);
35972
+ keeper.kill();
35973
+ this.deleteProcess(pid);
35974
+ return this.spawn({ ...options, model });
35975
+ }
35976
+ }
35977
+ async openResumedSession(args2) {
35978
+ const {
35979
+ AuthStorage,
35980
+ createAgentSession,
35981
+ DefaultResourceLoader,
35982
+ getAgentDir,
35983
+ ModelRegistry,
35984
+ SessionManager
35985
+ } = await loadSdk4();
35986
+ const authStorage = AuthStorage.create();
35987
+ const modelRegistry = ModelRegistry.create(authStorage);
35988
+ const sessions = await SessionManager.list(args2.workingDir, getPiSessionDir(args2.workingDir));
35989
+ const match17 = sessions.find((s) => s.id === args2.harnessSessionId);
35990
+ if (!match17) {
35991
+ throw new Error(`Pi session ${args2.harnessSessionId} not found`);
35992
+ }
35993
+ const resourceLoader = new DefaultResourceLoader({
35994
+ cwd: args2.workingDir,
35995
+ agentDir: getAgentDir(),
35996
+ systemPromptOverride: () => args2.systemPrompt
35997
+ });
35998
+ await resourceLoader.reload();
35999
+ const { session: session2 } = await withTimeout(createAgentSession({
36000
+ cwd: args2.workingDir,
36001
+ sessionManager: SessionManager.open(match17.path, getPiSessionDir(args2.workingDir)),
36002
+ authStorage,
36003
+ modelRegistry,
36004
+ resourceLoader
36005
+ }), SESSION_CREATE_TIMEOUT_MS2, "createAgentSession");
36006
+ return session2;
36007
+ }
35851
36008
  async resumeTurn(pid, prompt) {
35852
36009
  const session2 = this.sessions.get(pid);
35853
36010
  if (!session2) {
@@ -35907,10 +36064,10 @@ var init_pi_sdk_agent_service = __esm(() => {
35907
36064
  } = await loadSdk4();
35908
36065
  const authStorage = AuthStorage.create();
35909
36066
  const modelRegistry = ModelRegistry.create(authStorage);
35910
- const resolvedModel = resolveModel(modelRegistry, args2.model);
35911
- if (!resolvedModel) {
35912
- throw new Error("No Pi model available — configure provider credentials in ~/.pi/agent/auth.json");
35913
- }
36067
+ const model = requireHarnessModel(args2.model, "pi-sdk spawn");
36068
+ const parsed = parsePiSpawnModel(model);
36069
+ const resolvedModel = resolveRequiredModel(modelRegistry, parsed.model ?? model);
36070
+ const thinkingLevel = resolvePiThinkingLevel(parsed?.thinking);
35914
36071
  const agentDir = getAgentDir();
35915
36072
  const resourceLoader = new DefaultResourceLoader({
35916
36073
  cwd: args2.workingDir,
@@ -35921,6 +36078,7 @@ var init_pi_sdk_agent_service = __esm(() => {
35921
36078
  const { session: session2 } = await withTimeout(createAgentSession({
35922
36079
  cwd: args2.workingDir,
35923
36080
  model: resolvedModel,
36081
+ ...thinkingLevel ? { thinkingLevel } : {},
35924
36082
  sessionManager: SessionManager.create(getPiSessionDir(args2.workingDir)),
35925
36083
  authStorage,
35926
36084
  modelRegistry,
@@ -35934,6 +36092,7 @@ var init_pi_sdk_agent_service = __esm(() => {
35934
36092
  keeper,
35935
36093
  session: session2,
35936
36094
  context: context5,
36095
+ model,
35937
36096
  initialPrompt,
35938
36097
  deferInitialTurn = false,
35939
36098
  storedSystemPrompt
@@ -35943,6 +36102,7 @@ var init_pi_sdk_agent_service = __esm(() => {
35943
36102
  const logPrefix = buildAgentLogPrefix("pi-sdk", context5);
35944
36103
  const sdkSession = {
35945
36104
  session: session2,
36105
+ model,
35946
36106
  keeper,
35947
36107
  aborted: false,
35948
36108
  storedSystemPrompt
@@ -36096,6 +36256,7 @@ ${deferredResume}` : deferredResume;
36096
36256
  }
36097
36257
  async spawn(options) {
36098
36258
  const deferInitialTurn = options.deferInitialTurn ?? false;
36259
+ const model = requireHarnessModel(options.model, "pi-sdk spawn");
36099
36260
  const keeper = this.spawnKeeper(options.workingDir);
36100
36261
  const pid = keeper.pid;
36101
36262
  const context5 = options.context;
@@ -36107,7 +36268,7 @@ ${options.prompt}`;
36107
36268
  session2 = await this.createSession({
36108
36269
  workingDir: options.workingDir,
36109
36270
  systemPrompt: options.systemPrompt,
36110
- model: options.model
36271
+ model
36111
36272
  });
36112
36273
  } catch (err) {
36113
36274
  keeper.kill();
@@ -36120,7 +36281,7 @@ ${options.prompt}`;
36120
36281
  session: session2,
36121
36282
  context: context5,
36122
36283
  workingDir: options.workingDir,
36123
- model: options.model,
36284
+ model,
36124
36285
  initialPrompt: fullPrompt,
36125
36286
  deferInitialTurn,
36126
36287
  storedSystemPrompt: options.systemPrompt
@@ -79996,6 +80157,40 @@ var init_builder_to_planner = __esm(() => {
79996
80157
  // ../../services/backend/src/domain/usecase/enhancer/enhancer-handoff-content.ts
79997
80158
  var ENHANCER_USER_MESSAGE_PLACEHOLDER = "{{USER_MESSAGE}}";
79998
80159
 
80160
+ // ../../services/backend/prompts/utils/data-query-design.ts
80161
+ function getDataQueryDesignTemplateBlock() {
80162
+ return `## Persistent state and query pattern design
80163
+
80164
+ **Goal:** Small updates must not cause large cache invalidations. High-frequency writes use projections to smaller tables.
80165
+
80166
+ ### 1. Sources of concern
80167
+ | Source | Write frequency | Read pattern | Risk |
80168
+ |--------|---------------|--------------|------|
80169
+ | \`<table/mutation>\` | \`<frequency>\` | \`<pattern>\` | \`<scan / hot partition>\` |
80170
+
80171
+ ### 2. Schema design
80172
+ **Hot path:** \`<table>\` — fields, projection from \`<source>\`
80173
+ **Cold path:** \`<table>\` — …
80174
+
80175
+ \`\`\`typescript
80176
+ // target schema shape
80177
+ \`\`\`
80178
+
80179
+ ### 3. Index design (within limits)
80180
+ | Table | Index | Serves query | Budget |
80181
+ |-------|-------|--------------|--------|
80182
+
80183
+ ### 4. Query design (within limits)
80184
+ | Query | Index | Rows scanned | Timeout | Invalidation scope |
80185
+ |-------|-------|--------------|---------|-------------------|
80186
+
80187
+ \`\`\`typescript
80188
+ // target query signature
80189
+ \`\`\`
80190
+
80191
+ <!-- Write exactly "Not Applicable." for the entire section if no persistence changes -->`;
80192
+ }
80193
+
79999
80194
  // ../../services/backend/prompts/utils/frontend-design-ux-checklist.ts
80000
80195
  function getFrontendDesignUxPlanningPrinciplesBlock() {
80001
80196
  return `<!-- Ground every flow in user history and repository patterns. Recommend one existing pattern when multiple exist; do not prescribe style choices the project has not adopted. -->`;
@@ -80073,36 +80268,7 @@ export function ComponentName({ ... }: Props) {
80073
80268
  </handoff-frontend-design>
80074
80269
 
80075
80270
  <handoff-data-design>
80076
- ## Persistent state and query pattern design
80077
-
80078
- **Goal:** Small updates must not cause large cache invalidations. High-frequency writes use projections to smaller tables.
80079
-
80080
- ### 1. Sources of concern
80081
- | Source | Write frequency | Read pattern | Risk |
80082
- |--------|---------------|--------------|------|
80083
- | \`<table/mutation>\` | \`<frequency>\` | \`<pattern>\` | \`<scan / hot partition>\` |
80084
-
80085
- ### 2. Schema design
80086
- **Hot path:** \`<table>\` — fields, projection from \`<source>\`
80087
- **Cold path:** \`<table>\` — …
80088
-
80089
- \`\`\`typescript
80090
- // target schema shape
80091
- \`\`\`
80092
-
80093
- ### 3. Index design (within limits)
80094
- | Table | Index | Serves query | Budget |
80095
- |-------|-------|--------------|--------|
80096
-
80097
- ### 4. Query design (within limits)
80098
- | Query | Index | Rows scanned | Timeout | Invalidation scope |
80099
- |-------|-------|--------------|---------|-------------------|
80100
-
80101
- \`\`\`typescript
80102
- // target query signature
80103
- \`\`\`
80104
-
80105
- <!-- Write exactly "Not Applicable." for the entire section if no persistence changes -->
80271
+ ${getDataQueryDesignTemplateBlock()}
80106
80272
  </handoff-data-design>
80107
80273
 
80108
80274
  <handoff-notes>
@@ -80731,14 +80897,22 @@ var init_backlog = __esm(() => {
80731
80897
  // ../../services/backend/src/domain/usecase/skills/modules/code-review/index.ts
80732
80898
  var init_code_review = () => {};
80733
80899
 
80900
+ // ../../services/backend/src/domain/usecase/skills/modules/data-design/index.ts
80901
+ var init_data_design = () => {};
80902
+
80734
80903
  // ../../services/backend/src/domain/usecase/skills/modules/defragmentation/index.ts
80735
80904
  var init_defragmentation = () => {};
80736
80905
 
80906
+ // ../../services/backend/src/domain/usecase/skills/modules/user-centricity/index.ts
80907
+ var init_user_centricity = () => {};
80908
+
80737
80909
  // ../../services/backend/src/domain/usecase/skills/registry.ts
80738
80910
  var init_registry4 = __esm(() => {
80739
80911
  init_backlog();
80740
80912
  init_code_review();
80913
+ init_data_design();
80741
80914
  init_defragmentation();
80915
+ init_user_centricity();
80742
80916
  });
80743
80917
 
80744
80918
  // ../../services/backend/prompts/sections/glossary.ts
@@ -80774,9 +80948,23 @@ var init_glossary = __esm(() => {
80774
80948
  linkedSkillId: "code-review"
80775
80949
  },
80776
80950
  {
80777
- term: "structural-decisions",
80778
- definition: "Meta-level architectural choices that persist in the codebase and influence consistency: " + "folder structure, file naming, interface definitions, and key abstraction names/locations " + "(e.g., Repository/Service layers).",
80951
+ term: "defragmentation",
80952
+ definition: "Workflow for large or multi-surface revisions: study all relevant surfaces, establish a golden path, migrate every caller, then delete legacy implementations.",
80779
80953
  linkedSkillId: "defragmentation"
80954
+ },
80955
+ {
80956
+ term: "user-centricity",
80957
+ definition: "Design guidance for UI changes: translate user intent into complete flows with explicit loading, empty, error, and success states, stable layout, project patterns, keyboard behavior, feedback, affordances, and destructive-action safeguards.",
80958
+ linkedSkillId: "user-centricity"
80959
+ },
80960
+ {
80961
+ term: "data-design",
80962
+ definition: "Persistence and query design guidance: make write/read risks, hot and cold paths, projections, indexes, scan limits, timeouts, and invalidation scope explicit.",
80963
+ linkedSkillId: "data-design"
80964
+ },
80965
+ {
80966
+ term: "structural-decisions",
80967
+ definition: "Meta-level architectural choices that persist in the codebase and influence consistency: " + "folder structure, file naming, interface definitions, and key abstraction names/locations " + "(e.g., Repository/Service layers)."
80780
80968
  }
80781
80969
  ];
80782
80970
  NATIVE_GLOSSARY_TERMS = [
@@ -81041,6 +81229,14 @@ var init_cursor_sdk_config = __esm(() => {
81041
81229
  outputActivity: true,
81042
81230
  processExited: true
81043
81231
  },
81232
+ crashRecovery: {
81233
+ onSessionFailure: {
81234
+ maxAttempts: 6,
81235
+ intervalMs: 1e4,
81236
+ resumeFirstAttempts: 3,
81237
+ recoveryReason: "platform.cursor_sdk_session_reopen"
81238
+ }
81239
+ },
81044
81240
  wireEvents: ["sdk.cursor.message", "sdk.cursor.run.completed", "wire.log.agent_end"]
81045
81241
  };
81046
81242
  });
@@ -102818,6 +103014,25 @@ function resolveResumableHarnessSessionId(snapshot) {
102818
103014
  return snapshot.resumableHarnessSessionId ?? snapshot.harnessSessionId;
102819
103015
  }
102820
103016
 
103017
+ // src/daemon/domain/entities/session-recovery-policy.ts
103018
+ function resolveSessionRecoveryPolicy(harness, classification2) {
103019
+ const caps = getHarnessCapabilities(harness);
103020
+ if (classification2.hadSessionFailure && caps.crashRecovery?.onSessionFailure) {
103021
+ return caps.crashRecovery.onSessionFailure;
103022
+ }
103023
+ return DEFAULT_CRASH_RECOVERY_POLICY;
103024
+ }
103025
+ var DEFAULT_CRASH_RECOVERY_POLICY;
103026
+ var init_session_recovery_policy = __esm(() => {
103027
+ init_types();
103028
+ DEFAULT_CRASH_RECOVERY_POLICY = {
103029
+ maxAttempts: 1,
103030
+ intervalMs: 0,
103031
+ resumeFirstAttempts: 0,
103032
+ recoveryReason: "platform.crash_recovery"
103033
+ };
103034
+ });
103035
+
102821
103036
  // src/daemon/domain/usecase/classify-resume-storm-reason.ts
102822
103037
  function classifyResumeStormReason(logLines) {
102823
103038
  const blob = logLines.join(`
@@ -102926,18 +103141,6 @@ function appendRecentLogLine(slot, line) {
102926
103141
  }
102927
103142
  var RECENT_LOG_LINE_CAP2 = 100;
102928
103143
 
102929
- // src/daemon/domain/usecase/cursor-sdk-session-reopen-retry.ts
102930
- var CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS = 20, CURSOR_SDK_SESSION_REOPEN_INTERVAL_MS = 5000, CURSOR_SDK_SESSION_REOPEN_REASON = "platform.cursor_sdk_session_reopen", CURSOR_SDK_SESSION_RESUME_FIRST_ATTEMPTS = 3;
102931
-
102932
- // src/daemon/domain/usecase/detect-cursor-sdk-run-error.ts
102933
- function isCursorSdkRunErrorInLogs(logLines) {
102934
- return logLines.some((line) => line.includes(" run-error]"));
102935
- }
102936
- function formatCursorSdkRunErrorMessage(logLines) {
102937
- const line = [...logLines].reverse().find((l) => l.includes(" run-error]"));
102938
- return line?.trim() ?? "Cursor SDK run failed";
102939
- }
102940
-
102941
103144
  // src/daemon/domain/usecase/handle-turn-completed.ts
102942
103145
  async function handleTurnCompleted(deps, input, slot) {
102943
103146
  if (await tryAbortResumeStorm(deps, input, slot)) {
@@ -103074,6 +103277,19 @@ var init_native_spawn_presence = __esm(() => {
103074
103277
  init_execution_kind();
103075
103278
  });
103076
103279
 
103280
+ // src/daemon/infrastructure/local/harness/session-monitors/no-op-session-monitor.ts
103281
+ var NO_FAILURE, noOpSessionMonitor;
103282
+ var init_no_op_session_monitor = __esm(() => {
103283
+ NO_FAILURE = {
103284
+ hadSessionFailure: false,
103285
+ failureKind: "none",
103286
+ recoverable: true
103287
+ };
103288
+ noOpSessionMonitor = {
103289
+ classifyExitFailure: () => NO_FAILURE
103290
+ };
103291
+ });
103292
+
103077
103293
  // src/daemon/infrastructure/agent-process-manager/agent-process-manager.ts
103078
103294
  function agentKey3(chatroomId, role) {
103079
103295
  return `${chatroomId}:${role.toLowerCase()}`;
@@ -103083,7 +103299,7 @@ class AgentProcessManager {
103083
103299
  deps;
103084
103300
  slots = new Map;
103085
103301
  lastHarnessSessions = new Map;
103086
- sessionReopenRetryInFlight = new Set;
103302
+ sessionRecoveryRetryInFlight = new Set;
103087
103303
  exitRetryQueue = [];
103088
103304
  exitRetryTimer = null;
103089
103305
  turnEndQueue = new TurnEndQueue;
@@ -103501,12 +103717,12 @@ class AgentProcessManager {
103501
103717
  });
103502
103718
  }));
103503
103719
  this.resetSlotAfterExit(slot);
103504
- await this.emitExitEvent(slot, opts, ctx);
103720
+ const exitLifecyclePromise = this.emitExitEvent(slot, opts, ctx);
103505
103721
  try {
103506
103722
  await this.deps.persistence.clearAgentPid(this.deps.machineId, opts.chatroomId, opts.role);
103507
103723
  } catch {}
103508
103724
  this.untrackAllServices(opts.pid);
103509
- lifecyclePromise.then(() => this.dispatchRestartAfterExit(opts, ctx, key)).catch(() => {});
103725
+ lifecyclePromise.then(() => this.dispatchRestartAfterExit(opts, ctx, key, exitLifecyclePromise)).catch(() => {});
103510
103726
  }
103511
103727
  captureExitContext(slot, opts, stopReason) {
103512
103728
  return {
@@ -103554,7 +103770,7 @@ class AgentProcessManager {
103554
103770
  slot.pendingOperation = undefined;
103555
103771
  slot.nativeTurnPhase = undefined;
103556
103772
  }
103557
- async emitExitEvent(slot, opts, ctx) {
103773
+ emitExitEvent(slot, opts, ctx) {
103558
103774
  const stopReason = ctx.stopReason;
103559
103775
  const exitArgs = {
103560
103776
  sessionId: this.deps.sessionId,
@@ -103568,14 +103784,14 @@ class AgentProcessManager {
103568
103784
  signal: opts.signal ?? undefined,
103569
103785
  agentHarness: ctx.harness
103570
103786
  };
103571
- this.recordAgentExit(opts.role, exitArgs, "Failed to record agent exit event");
103787
+ return this.recordAgentExit(opts.role, exitArgs, "Failed to record agent exit event");
103572
103788
  }
103573
103789
  untrackAllServices(pid) {
103574
103790
  for (const service3 of this.deps.agentServices.values()) {
103575
103791
  service3.untrack(pid);
103576
103792
  }
103577
103793
  }
103578
- dispatchRestartAfterExit(opts, ctx, _key) {
103794
+ dispatchRestartAfterExit(opts, ctx, _key, exitLifecyclePromise) {
103579
103795
  if (this.isStopRequested(opts.chatroomId, opts.role)) {
103580
103796
  return;
103581
103797
  }
@@ -103586,14 +103802,123 @@ class AgentProcessManager {
103586
103802
  }
103587
103803
  return;
103588
103804
  }
103589
- this.maybeRestartAgent(opts, ctx);
103805
+ this.maybeRestartAgent(opts, ctx, exitLifecyclePromise);
103590
103806
  }
103591
- hasCursorSdkRunErrorInContext(recentLogLines) {
103592
- if (!isCursorSdkRunErrorInLogs(recentLogLines)) {
103593
- return false;
103807
+ maybeRestartAgent(opts, ctx, exitLifecyclePromise) {
103808
+ const { harness, recentLogLines } = ctx;
103809
+ const logs = recentLogLines ?? [];
103810
+ if (!harness || !ctx.workingDir) {
103811
+ console.log(`[AgentProcessManager] ⚠️ Cannot restart — missing harness or workingDir ` + `(role: ${opts.role}, harness: ${harness ?? "none"}, workingDir: ${ctx.workingDir ?? "none"})`);
103812
+ return;
103594
103813
  }
103595
- console.log(`[AgentProcessManager] cursor-sdk run-error detected: ${formatCursorSdkRunErrorMessage(recentLogLines)}`);
103596
- return true;
103814
+ const monitor = this.deps.sessionMonitors.get(harness) ?? noOpSessionMonitor;
103815
+ const monitorCtx = {
103816
+ recentLogLines: logs,
103817
+ harness,
103818
+ wantResume: ctx.wantResume
103819
+ };
103820
+ const classification2 = monitor.classifyExitFailure(monitorCtx);
103821
+ this.retrySessionRecovery(opts, ctx, classification2, monitor, monitorCtx, exitLifecyclePromise);
103822
+ }
103823
+ async retrySessionRecovery(opts, ctx, classification2, monitor, monitorCtx, exitLifecyclePromise) {
103824
+ const harness = ctx.harness;
103825
+ const policy = resolveSessionRecoveryPolicy(harness, classification2);
103826
+ const key = agentKey3(opts.chatroomId, opts.role);
103827
+ const multiAttempt = policy.maxAttempts > 1;
103828
+ if (multiAttempt && this.sessionRecoveryRetryInFlight.has(key)) {
103829
+ return;
103830
+ }
103831
+ if (multiAttempt) {
103832
+ this.sessionRecoveryRetryInFlight.add(key);
103833
+ }
103834
+ try {
103835
+ await this.prepareSessionRecoveryBeforeRetry(opts, classification2, exitLifecyclePromise);
103836
+ const lastError = await this.runSessionRecoveryAttempts(opts, ctx, classification2, monitor, monitorCtx, policy, key, multiAttempt);
103837
+ if (lastError === null)
103838
+ return;
103839
+ if (multiAttempt) {
103840
+ const failureMessage = `${harness} session recovery failed after ${policy.maxAttempts} attempts: ${lastError}`;
103841
+ console.log(`[AgentProcessManager] ⛔ ${failureMessage}`);
103842
+ this.emitStartFailedEvent(opts.role, opts.chatroomId, failureMessage);
103843
+ } else {
103844
+ console.log(`[AgentProcessManager] ⚠️ Agent restart did not complete for ${opts.role}: ${lastError}`);
103845
+ }
103846
+ } catch (err) {
103847
+ const message = err instanceof Error ? err.message : String(err);
103848
+ console.log(` ⚠️ Session recovery retry loop failed: ${message}`);
103849
+ this.emitStartFailedEvent(opts.role, opts.chatroomId, message);
103850
+ } finally {
103851
+ if (multiAttempt) {
103852
+ this.sessionRecoveryRetryInFlight.delete(key);
103853
+ }
103854
+ }
103855
+ }
103856
+ async prepareSessionRecoveryBeforeRetry(opts, classification2, exitLifecyclePromise) {
103857
+ if (!classification2.requiresTaskReleaseBeforeRecovery)
103858
+ return;
103859
+ this.clearLastInFlightTask(opts.chatroomId, opts.role);
103860
+ if (exitLifecyclePromise) {
103861
+ await exitLifecyclePromise;
103862
+ }
103863
+ }
103864
+ async runSessionRecoveryAttempts(opts, ctx, classification2, monitor, monitorCtx, policy, key, multiAttempt) {
103865
+ const generation = this.slots.get(key)?.stopGeneration;
103866
+ let lastError = "unknown";
103867
+ for (let attempt = 1;attempt <= policy.maxAttempts; attempt++) {
103868
+ if (this.isStopRequested(opts.chatroomId, opts.role, generation))
103869
+ return null;
103870
+ if (classification2.hadSessionFailure && policy.resumeFirstAttempts > 0 && attempt === policy.resumeFirstAttempts + 1) {
103871
+ this.clearHarnessSessionAfterResumePhaseFailure(key, opts);
103872
+ }
103873
+ const wantResume = monitor.resolveWantResume?.(attempt, classification2, monitorCtx) ?? (classification2.hadSessionFailure ? false : ctx.wantResume ?? true);
103874
+ if (multiAttempt) {
103875
+ const stored = this.lastHarnessSessions.get(key);
103876
+ const storedSessionId = stored ? resolveResumableHarnessSessionId(stored) : undefined;
103877
+ await this.emitSessionReopenRetry(opts.chatroomId, opts.role, attempt, policy.maxAttempts, attempt > 1 ? lastError : undefined, storedSessionId);
103878
+ if (this.isStopRequested(opts.chatroomId, opts.role, generation))
103879
+ return null;
103880
+ }
103881
+ const attemptResult = await this.executeSessionRecoveryAttempt(opts, ctx, policy, wantResume, multiAttempt, generation);
103882
+ if (attemptResult.kind === "success" || attemptResult.kind === "stopped")
103883
+ return null;
103884
+ if (attemptResult.kind === "crash_loop")
103885
+ return null;
103886
+ lastError = attemptResult.lastError;
103887
+ if (attempt < policy.maxAttempts && policy.intervalMs > 0) {
103888
+ await this.deps.clock.delay(policy.intervalMs);
103889
+ if (this.isStopRequested(opts.chatroomId, opts.role, generation))
103890
+ return null;
103891
+ }
103892
+ }
103893
+ return lastError;
103894
+ }
103895
+ async executeSessionRecoveryAttempt(opts, ctx, policy, wantResume, multiAttempt, generation) {
103896
+ const harness = ctx.harness;
103897
+ const ensureOpts = {
103898
+ chatroomId: opts.chatroomId,
103899
+ role: opts.role,
103900
+ agentHarness: harness,
103901
+ model: ctx.model,
103902
+ workingDir: ctx.workingDir,
103903
+ reason: policy.recoveryReason,
103904
+ wantResume
103905
+ };
103906
+ const result = await this.ensureRunning(ensureOpts);
103907
+ if (result.success)
103908
+ return { kind: "success" };
103909
+ if (this.isStopRequested(opts.chatroomId, opts.role, generation)) {
103910
+ return { kind: "stopped" };
103911
+ }
103912
+ const error51 = result.error ?? "unknown";
103913
+ if (!multiAttempt && result.error === "backoff") {
103914
+ await this.retryCrashRecoveryAfterBackoff(opts, ctx, ensureOpts, result.retryAfterMs);
103915
+ return { kind: "success" };
103916
+ }
103917
+ if (result.error === "crash_loop") {
103918
+ this.handleCrashLoopLimitReached(opts, ctx.recentLogLines);
103919
+ return { kind: "crash_loop" };
103920
+ }
103921
+ return { kind: "failed", lastError: error51 };
103597
103922
  }
103598
103923
  maybeEmitProviderUnavailable(chatroomId, role, slot) {
103599
103924
  if (!slot || slot.harness !== "codex-sdk" || slot.providerUnavailableEmitted)
@@ -103625,48 +103950,6 @@ class AgentProcessManager {
103625
103950
  recoverable: providerUnavailableRecoverable(classification2.reason)
103626
103951
  }).catch(() => {});
103627
103952
  }
103628
- maybeRestartAgent(opts, ctx) {
103629
- const { harness, model, workingDir, recentLogLines } = ctx;
103630
- const logs = recentLogLines ?? [];
103631
- if (!harness || !workingDir) {
103632
- console.log(`[AgentProcessManager] ⚠️ Cannot restart — missing harness or workingDir ` + `(role: ${opts.role}, harness: ${harness ?? "none"}, workingDir: ${workingDir ?? "none"})`);
103633
- return;
103634
- }
103635
- const hadRunError = this.hasCursorSdkRunErrorInContext(logs);
103636
- if (harness === "cursor-sdk") {
103637
- this.retryCursorSdkSessionReopen(opts, ctx, hadRunError);
103638
- return;
103639
- }
103640
- this.attemptCrashRecoveryRestart(opts, ctx, {
103641
- chatroomId: opts.chatroomId,
103642
- role: opts.role,
103643
- agentHarness: harness,
103644
- model,
103645
- workingDir,
103646
- reason: "platform.crash_recovery",
103647
- wantResume: hadRunError ? false : ctx.wantResume ?? true
103648
- });
103649
- }
103650
- async attemptCrashRecoveryRestart(exitOpts, ctx, ensureOpts) {
103651
- try {
103652
- const result = await this.ensureRunning(ensureOpts);
103653
- if (result.success)
103654
- return;
103655
- if (result.error === "backoff") {
103656
- await this.retryCrashRecoveryAfterBackoff(exitOpts, ctx, ensureOpts, result.retryAfterMs);
103657
- return;
103658
- }
103659
- if (result.error === "crash_loop") {
103660
- this.handleCrashLoopLimitReached(exitOpts, ctx.recentLogLines);
103661
- return;
103662
- }
103663
- console.log(`[AgentProcessManager] ⚠️ Agent restart did not complete for ${exitOpts.role}: ${result.error ?? "unknown"}`);
103664
- } catch (err) {
103665
- const message = err instanceof Error ? err.message : String(err);
103666
- console.log(` ⚠️ Failed to restart agent: ${message}`);
103667
- this.emitStartFailedEvent(exitOpts.role, exitOpts.chatroomId, message);
103668
- }
103669
- }
103670
103953
  async retryCrashRecoveryAfterBackoff(exitOpts, ctx, ensureOpts, retryAfterMs) {
103671
103954
  if (retryAfterMs === undefined || retryAfterMs <= 0) {
103672
103955
  return;
@@ -103704,71 +103987,6 @@ class AgentProcessManager {
103704
103987
  });
103705
103988
  }
103706
103989
  }
103707
- resolveCursorSdkReopenWantResume(hadRunError, attempt, ctx) {
103708
- if (hadRunError && attempt <= CURSOR_SDK_SESSION_RESUME_FIRST_ATTEMPTS) {
103709
- return true;
103710
- }
103711
- if (hadRunError) {
103712
- return false;
103713
- }
103714
- return ctx.wantResume ?? true;
103715
- }
103716
- async retryCursorSdkSessionReopen(opts, ctx, hadRunError) {
103717
- const key = agentKey3(opts.chatroomId, opts.role);
103718
- if (this.sessionReopenRetryInFlight.has(key)) {
103719
- return;
103720
- }
103721
- this.sessionReopenRetryInFlight.add(key);
103722
- const generation = this.slots.get(key)?.stopGeneration;
103723
- const harness = ctx.harness;
103724
- const model = ctx.model;
103725
- const workingDir = ctx.workingDir;
103726
- let lastError = "unknown";
103727
- try {
103728
- for (let attempt = 1;attempt <= CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS; attempt++) {
103729
- if (this.isStopRequested(opts.chatroomId, opts.role, generation))
103730
- return;
103731
- if (hadRunError && attempt === CURSOR_SDK_SESSION_RESUME_FIRST_ATTEMPTS + 1) {
103732
- this.clearHarnessSessionAfterResumePhaseFailure(key, opts);
103733
- }
103734
- const wantResume = this.resolveCursorSdkReopenWantResume(hadRunError, attempt, ctx);
103735
- const stored = this.lastHarnessSessions.get(key);
103736
- const storedSessionId = stored ? resolveResumableHarnessSessionId(stored) : undefined;
103737
- await this.emitSessionReopenRetry(opts.chatroomId, opts.role, attempt, attempt > 1 ? lastError : undefined, storedSessionId);
103738
- if (this.isStopRequested(opts.chatroomId, opts.role, generation))
103739
- return;
103740
- const result = await this.ensureRunning({
103741
- chatroomId: opts.chatroomId,
103742
- role: opts.role,
103743
- agentHarness: harness,
103744
- model,
103745
- workingDir,
103746
- reason: CURSOR_SDK_SESSION_REOPEN_REASON,
103747
- wantResume
103748
- });
103749
- if (result.success) {
103750
- return;
103751
- }
103752
- if (this.isStopRequested(opts.chatroomId, opts.role, generation))
103753
- return;
103754
- lastError = result.error ?? "unknown";
103755
- if (attempt < CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS) {
103756
- await this.deps.clock.delay(CURSOR_SDK_SESSION_REOPEN_INTERVAL_MS);
103757
- if (this.isStopRequested(opts.chatroomId, opts.role, generation))
103758
- return;
103759
- }
103760
- }
103761
- const failureMessage = `cursor-sdk session reopen failed after ${CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS} attempts: ${lastError}`;
103762
- console.log(`[AgentProcessManager] ⛔ ${failureMessage}`);
103763
- this.emitStartFailedEvent(opts.role, opts.chatroomId, failureMessage);
103764
- } catch (err) {
103765
- const message = err instanceof Error ? err.message : String(err);
103766
- console.log(` ⚠️ cursor-sdk session reopen retry loop failed: ${message}`);
103767
- this.emitStartFailedEvent(opts.role, opts.chatroomId, message);
103768
- } finally {
103769
- this.sessionReopenRetryInFlight.delete(key);
103770
- }
103771
- }
103772
103990
  emitStartFailedEvent(role, chatroomId, error51) {
103773
103991
  logDaemonAuditEvent(this.deps.logEvent, {
103774
103992
  type: "agent.startFailed",
@@ -103978,7 +104196,11 @@ class AgentProcessManager {
103978
104196
  this.queueExitRetry({ role, args: exitArgs });
103979
104197
  });
103980
104198
  const emittedAt = this.deps.clock.now();
103981
- this.deps.lifecycleOutbox.enqueue(buildExitedLifecycleFact(exitArgs, emittedAt)).catch((err) => console.log(` ⚠️ Failed to enqueue agent exited lifecycle fact: ${err.message}`));
104199
+ return this.deps.lifecycleOutbox.enqueue(buildExitedLifecycleFact(exitArgs, emittedAt)).then(() => {
104200
+ return;
104201
+ }).catch((err) => {
104202
+ console.log(` ⚠️ Failed to enqueue agent exited lifecycle fact: ${err.message}`);
104203
+ });
103982
104204
  }
103983
104205
  async clearAgentPidQuietly(chatroomId, role) {
103984
104206
  try {
@@ -104156,7 +104378,7 @@ class AgentProcessManager {
104156
104378
  console.log(` ⚠️ Failed to emit sessionResumeFailed event: ${err.message}`);
104157
104379
  }
104158
104380
  }
104159
- async emitSessionReopenRetry(chatroomId, role, attempt, error51, harnessSessionId) {
104381
+ async emitSessionReopenRetry(chatroomId, role, attempt, maxAttempts, error51, harnessSessionId) {
104160
104382
  try {
104161
104383
  await logDaemonAuditEvent(this.deps.logEvent, {
104162
104384
  type: "agent.sessionReopenRetry",
@@ -104164,7 +104386,7 @@ class AgentProcessManager {
104164
104386
  role,
104165
104387
  machineId: this.deps.machineId,
104166
104388
  attempt,
104167
- maxAttempts: CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS,
104389
+ maxAttempts,
104168
104390
  ...error51 ? { error: error51 } : {},
104169
104391
  ...harnessSessionId ? { harnessSessionId } : {}
104170
104392
  });
@@ -104174,11 +104396,11 @@ class AgentProcessManager {
104174
104396
  chatroomId,
104175
104397
  role,
104176
104398
  attempt,
104177
- maxAttempts: CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS,
104399
+ maxAttempts,
104178
104400
  ...error51 ? { error: error51 } : {},
104179
104401
  ...harnessSessionId ? { harnessSessionId } : {}
104180
104402
  });
104181
- console.log(`[AgentProcessManager] ✅ Emitted agent.sessionReopenRetry for ${role} (attempt ${attempt}/${CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS})`);
104403
+ console.log(`[AgentProcessManager] ✅ Emitted agent.sessionReopenRetry for ${role} (attempt ${attempt}/${maxAttempts})`);
104182
104404
  } catch (err) {
104183
104405
  console.log(` ⚠️ Failed to emit sessionReopenRetry event: ${err.message}`);
104184
104406
  }
@@ -104757,6 +104979,7 @@ var init_agent_process_manager = __esm(() => {
104757
104979
  init_agent_lifecycle_runtime();
104758
104980
  init_agent_lifecycle_types();
104759
104981
  init_agent_stop();
104982
+ init_session_recovery_policy();
104760
104983
  init_spawn_policy();
104761
104984
  init_abort_resume_storm();
104762
104985
  init_classify_provider_error();
@@ -104768,6 +104991,99 @@ var init_agent_process_manager = __esm(() => {
104768
104991
  init_native_task_delivery_coordinator();
104769
104992
  init_native_turn_phase2();
104770
104993
  init_native_spawn_presence();
104994
+ init_no_op_session_monitor();
104995
+ });
104996
+
104997
+ // src/daemon/domain/usecase/cursor-sdk-session-reopen-retry.ts
104998
+ var CURSOR_SDK_SESSION_RESUME_FIRST_ATTEMPTS = 3;
104999
+
105000
+ // src/daemon/domain/usecase/detect-cursor-sdk-run-error.ts
105001
+ function isCursorSdkRunErrorInLogs(logLines) {
105002
+ return logLines.some((line) => line.includes(" run-error]"));
105003
+ }
105004
+ function formatCursorSdkRunErrorMessage(logLines) {
105005
+ const line = [...logLines].reverse().find((l) => l.includes(" run-error]"));
105006
+ return line?.trim() ?? "Cursor SDK run failed";
105007
+ }
105008
+ function isCursorSdkAuthErrorInLogs(logLines) {
105009
+ if (classifyResumeStormReason(logLines) !== "auth_error")
105010
+ return false;
105011
+ return logLines.some((line) => line.includes("[cursor-sdk:"));
105012
+ }
105013
+ function hasCursorSdkSessionReopenTrigger(logLines) {
105014
+ return isCursorSdkRunErrorInLogs(logLines) || isCursorSdkAuthErrorInLogs(logLines);
105015
+ }
105016
+ var init_detect_cursor_sdk_run_error = __esm(() => {
105017
+ init_classify_resume_storm_reason();
105018
+ });
105019
+
105020
+ // src/daemon/infrastructure/local/harness/session-monitors/cursor-sdk-session-monitor.ts
105021
+ var cursorSdkSessionMonitor;
105022
+ var init_cursor_sdk_session_monitor = __esm(() => {
105023
+ init_detect_cursor_sdk_run_error();
105024
+ cursorSdkSessionMonitor = {
105025
+ classifyExitFailure(ctx) {
105026
+ const logs = ctx.recentLogLines;
105027
+ if (!hasCursorSdkSessionReopenTrigger(logs)) {
105028
+ return { hadSessionFailure: false, failureKind: "none", recoverable: true };
105029
+ }
105030
+ const auth = isCursorSdkAuthErrorInLogs(logs);
105031
+ console.log(`[CursorSdkSessionMonitor] session failure detected: ${formatCursorSdkRunErrorMessage(logs)}`);
105032
+ return {
105033
+ hadSessionFailure: true,
105034
+ failureKind: auth ? "auth_error" : "run_error",
105035
+ recoverable: true,
105036
+ requiresTaskReleaseBeforeRecovery: auth
105037
+ };
105038
+ },
105039
+ resolveWantResume(attempt, classification2, ctx) {
105040
+ if (classification2.hadSessionFailure && attempt <= CURSOR_SDK_SESSION_RESUME_FIRST_ATTEMPTS) {
105041
+ return true;
105042
+ }
105043
+ if (classification2.hadSessionFailure) {
105044
+ return false;
105045
+ }
105046
+ return ctx.wantResume ?? true;
105047
+ }
105048
+ };
105049
+ });
105050
+
105051
+ // src/daemon/infrastructure/local/harness/session-monitors/session-monitor-registry.ts
105052
+ function registerSessionMonitor(harnessId, monitor) {
105053
+ registry3.set(harnessId, monitor);
105054
+ }
105055
+ function getSessionMonitor(harnessId) {
105056
+ return registry3.get(harnessId);
105057
+ }
105058
+ function getAllSessionMonitors() {
105059
+ return new Map(registry3);
105060
+ }
105061
+ var registry3;
105062
+ var init_session_monitor_registry = __esm(() => {
105063
+ registry3 = new Map;
105064
+ });
105065
+
105066
+ // src/daemon/infrastructure/local/harness/session-monitors/init-session-monitors.ts
105067
+ function initSessionMonitorRegistry() {
105068
+ if (initialized2)
105069
+ return;
105070
+ for (const harness of AGENT_HARNESSES) {
105071
+ registerSessionMonitor(harness, harness === "cursor-sdk" ? cursorSdkSessionMonitor : noOpSessionMonitor);
105072
+ }
105073
+ for (const service3 of getAllHarnesses()) {
105074
+ if (!getSessionMonitor(service3.id)) {
105075
+ registerSessionMonitor(service3.id, noOpSessionMonitor);
105076
+ }
105077
+ }
105078
+ initialized2 = true;
105079
+ }
105080
+ var initialized2 = false;
105081
+ var init_init_session_monitors = __esm(() => {
105082
+ init_agent2();
105083
+ init_cursor_sdk_session_monitor();
105084
+ init_no_op_session_monitor();
105085
+ init_session_monitor_registry();
105086
+ init_registry2();
104771
105087
  });
104772
105088
 
104773
105089
  // src/daemon/entry/init-daemon.ts
@@ -104831,6 +105147,7 @@ function assembleDaemonSessionInit(args2) {
104831
105147
  config: config4,
104832
105148
  convexUrl,
104833
105149
  agentServices,
105150
+ sessionMonitors,
104834
105151
  cachedModels,
104835
105152
  deps
104836
105153
  } = args2;
@@ -104847,6 +105164,7 @@ function assembleDaemonSessionInit(args2) {
104847
105164
  }),
104848
105165
  logSink: activeLogSink,
104849
105166
  agentServices,
105167
+ sessionMonitors,
104850
105168
  backend: deps.backend,
104851
105169
  sessionId: typedSessionId,
104852
105170
  machineId,
@@ -105028,11 +105346,13 @@ Run: ${formatAuthLoginCommand(convexUrl)}`);
105028
105346
  const config4 = yield* setupMachineEffect();
105029
105347
  const { machineId } = config4;
105030
105348
  initHarnessRegistry();
105349
+ initSessionMonitorRegistry();
105031
105350
  const agentServices = new Map(getAllHarnesses().map((s) => [s.id, s]));
105351
+ const sessionMonitors = getAllSessionMonitors();
105032
105352
  yield* registerMachineEffect(client4, typedSessionId, config4);
105033
105353
  const cachedModels = yield* fetchCachedMachineModelsEffect(client4, typedSessionId, machineId);
105034
105354
  yield* connectDaemonEffect(client4, typedSessionId, machineId);
105035
- return { typedSessionId, config: config4, machineId, agentServices, cachedModels };
105355
+ return { typedSessionId, config: config4, machineId, agentServices, sessionMonitors, cachedModels };
105036
105356
  }), activeLogSink, activeLogEvent, connectDaemonEffect = (client4, sessionId, machineId) => exports_Effect.tryPromise({
105037
105357
  try: () => client4.mutation(api.machines.updateDaemonStatus, {
105038
105358
  sessionId: asConvexSessionId(sessionId),
@@ -105120,6 +105440,8 @@ var init_init_daemon = __esm(() => {
105120
105440
  init_agent_process_manager();
105121
105441
  init_registry3();
105122
105442
  init_services2();
105443
+ init_init_session_monitors();
105444
+ init_session_monitor_registry();
105123
105445
  AUTH_WAIT_TIMEOUT_MS = 5 * 60 * 1000;
105124
105446
  initDaemonEffect = exports_Effect.gen(function* () {
105125
105447
  const lockAcquired = yield* exports_Effect.tryPromise({
@@ -105149,7 +105471,7 @@ var init_init_daemon = __esm(() => {
105149
105471
  try: () => getConvexClient(),
105150
105472
  catch: (e) => e
105151
105473
  });
105152
- const { typedSessionId, config: config4, machineId, agentServices, cachedModels } = yield* connectWithRetryEffect(client4, sessionId, convexUrl);
105474
+ const { typedSessionId, config: config4, machineId, agentServices, sessionMonitors, cachedModels } = yield* connectWithRetryEffect(client4, sessionId, convexUrl);
105153
105475
  const init2 = assembleDaemonSessionInit({
105154
105476
  client: client4,
105155
105477
  typedSessionId,
@@ -105157,6 +105479,7 @@ var init_init_daemon = __esm(() => {
105157
105479
  config: config4,
105158
105480
  convexUrl,
105159
105481
  agentServices,
105482
+ sessionMonitors,
105160
105483
  cachedModels,
105161
105484
  deps: createDefaultDeps22()
105162
105485
  });
@@ -105455,7 +105778,8 @@ async function resumeSession(deps, input) {
105455
105778
  const { harness, journalFactory, chunkExtractor, nowFn = Date.now } = deps;
105456
105779
  const { harnessSessionId, opencodeSessionId } = input;
105457
105780
  const session2 = await harness.resumeSession(opencodeSessionId, {
105458
- harnessSessionId
105781
+ harnessSessionId,
105782
+ ...input.model ? { model: input.model } : {}
105459
105783
  });
105460
105784
  const journal = journalFactory.create(harnessSessionId);
105461
105785
  const unsubscribeEvents = session2.onEvent((event) => {
@@ -105781,6 +106105,7 @@ class ClaudeSdkHarness {
105781
106105
  async newSession(config4) {
105782
106106
  if (this.closed)
105783
106107
  throw new Error("Harness is closed");
106108
+ const defaultModel = requireHarnessModel(config4.model, "claude-sdk newSession");
105784
106109
  const opencodeSessionId = randomUUID6();
105785
106110
  const session2 = new ClaudeSdkSession({
105786
106111
  cwd: this.cwd,
@@ -105788,25 +106113,27 @@ class ClaudeSdkHarness {
105788
106113
  query: this.query,
105789
106114
  opencodeSessionId,
105790
106115
  sessionTitle: config4.title ?? "",
105791
- defaultModel: config4.model ?? DEFAULT_MODEL,
106116
+ defaultModel,
105792
106117
  systemPrompt: config4.systemPrompt,
105793
106118
  onClose: (id3) => this.sessions.delete(id3)
105794
106119
  });
105795
106120
  this.sessions.set(opencodeSessionId, session2);
105796
106121
  return session2;
105797
106122
  }
105798
- async resumeSession(sessionId, _options) {
106123
+ async resumeSession(sessionId, options) {
105799
106124
  if (this.closed)
105800
106125
  throw new Error("Harness is closed");
105801
106126
  const existing = this.sessions.get(sessionId);
105802
106127
  if (existing)
105803
106128
  return existing;
106129
+ const defaultModel = requireHarnessModel(options?.model, "claude-sdk resumeSession");
105804
106130
  const session2 = new ClaudeSdkSession({
105805
106131
  cwd: this.cwd,
105806
106132
  executablePath: this.executablePath,
105807
106133
  query: this.query,
105808
106134
  opencodeSessionId: sessionId,
105809
106135
  sessionTitle: "",
106136
+ defaultModel,
105810
106137
  providerSessionId: sessionId,
105811
106138
  resumeOnFirstQuery: true,
105812
106139
  onClose: (id3) => this.sessions.delete(id3)
@@ -105829,7 +106156,7 @@ class ClaudeSdkHarness {
105829
106156
  this.sessions.clear();
105830
106157
  }
105831
106158
  }
105832
- var DEFAULT_MODEL = "anthropic/sonnet", _sdkCache5, _sdkLoadError5, _executablePathCache, startClaudeSdkHarness = async (config4) => {
106159
+ var _sdkCache5, _sdkLoadError5, _executablePathCache, startClaudeSdkHarness = async (config4) => {
105833
106160
  try {
105834
106161
  const sdk = await loadSdk5();
105835
106162
  const executablePath = await loadExecutablePath();
@@ -106082,51 +106409,51 @@ class CursorSdkHarness {
106082
106409
  }
106083
106410
  ];
106084
106411
  }
106085
- async newSession(config4) {
106086
- if (this.closed)
106087
- throw new Error("Harness is closed");
106412
+ requireApiKey() {
106088
106413
  const apiKey = process.env.CURSOR_API_KEY?.trim();
106089
106414
  if (!apiKey)
106090
106415
  throw new Error("CURSOR_API_KEY is not set");
106091
- const modelSelection = resolveCursorSdkSpawnModelSelection(config4.model ?? DEFAULT_MODEL2);
106092
- const { Agent } = await loadSdk6();
106093
- const agent = await withTimeout(Agent.create({
106094
- apiKey,
106095
- model: modelSelection,
106096
- local: { cwd: this.cwd, settingSources: [], enableAgentRetries: true }
106097
- }), AGENT_CREATE_TIMEOUT_MS2, "Agent.create");
106416
+ return apiKey;
106417
+ }
106418
+ registerAgentSession(agent, sessionTitle) {
106098
106419
  const session2 = new CursorSdkSession({
106099
106420
  agent,
106100
106421
  opencodeSessionId: agent.agentId,
106101
- sessionTitle: config4.title ?? "",
106422
+ sessionTitle,
106102
106423
  onClose: (id3) => this.sessions.delete(id3)
106103
106424
  });
106104
106425
  this.sessions.set(agent.agentId, session2);
106105
106426
  return session2;
106106
106427
  }
106107
- async resumeSession(sessionId, _options) {
106428
+ async newSession(config4) {
106429
+ if (this.closed)
106430
+ throw new Error("Harness is closed");
106431
+ const apiKey = this.requireApiKey();
106432
+ const model = requireHarnessModel(config4.model, "cursor-sdk newSession");
106433
+ const modelSelection = resolveCursorSdkSpawnModelSelection(model);
106434
+ const { Agent } = await loadSdk6();
106435
+ const agent = await withTimeout(Agent.create({
106436
+ apiKey,
106437
+ model: modelSelection,
106438
+ local: { cwd: this.cwd, settingSources: [], enableAgentRetries: true }
106439
+ }), AGENT_CREATE_TIMEOUT_MS2, "Agent.create");
106440
+ return this.registerAgentSession(agent, config4.title ?? "");
106441
+ }
106442
+ async resumeSession(sessionId, options) {
106108
106443
  if (this.closed)
106109
106444
  throw new Error("Harness is closed");
106110
106445
  const existing = this.sessions.get(sessionId);
106111
106446
  if (existing)
106112
106447
  return existing;
106113
- const apiKey = process.env.CURSOR_API_KEY?.trim();
106114
- if (!apiKey)
106115
- throw new Error("CURSOR_API_KEY is not set");
106448
+ const model = requireHarnessModel(options?.model, "cursor-sdk resumeSession");
106449
+ const apiKey = this.requireApiKey();
106116
106450
  const { Agent } = await loadSdk6();
106117
106451
  const agent = await withTimeout(Agent.resume(sessionId, {
106118
106452
  apiKey,
106119
- model: resolveCursorSdkSpawnModelSelection(DEFAULT_MODEL2),
106453
+ model: resolveCursorSdkSpawnModelSelection(model),
106120
106454
  local: { cwd: this.cwd, settingSources: [], enableAgentRetries: true }
106121
106455
  }), AGENT_CREATE_TIMEOUT_MS2, "Agent.resume");
106122
- const session2 = new CursorSdkSession({
106123
- agent,
106124
- opencodeSessionId: agent.agentId,
106125
- sessionTitle: "",
106126
- onClose: (id3) => this.sessions.delete(id3)
106127
- });
106128
- this.sessions.set(agent.agentId, session2);
106129
- return session2;
106456
+ return this.registerAgentSession(agent, "");
106130
106457
  }
106131
106458
  async fetchSessionTitle(_opencodeSessionId) {
106132
106459
  return;
@@ -106143,7 +106470,7 @@ class CursorSdkHarness {
106143
106470
  this.sessions.clear();
106144
106471
  }
106145
106472
  }
106146
- var DEFAULT_MODEL2 = "composer-2.5", AGENT_CREATE_TIMEOUT_MS2 = 60000, _sdkCache6, _sdkLoadError6, startCursorSdkHarness = async (config4) => {
106473
+ var AGENT_CREATE_TIMEOUT_MS2 = 60000, _sdkCache6, _sdkLoadError6, startCursorSdkHarness = async (config4) => {
106147
106474
  try {
106148
106475
  await loadSdk6();
106149
106476
  } catch (err) {
@@ -106470,6 +106797,7 @@ class OpencodeSdkHarness {
106470
106797
  async newSession(config4) {
106471
106798
  if (this.closed)
106472
106799
  throw new Error("Harness is closed");
106800
+ requireHarnessModel(config4.model, "opencode-sdk newSession");
106473
106801
  const created = await this.client.session.create({
106474
106802
  body: {
106475
106803
  ...config4.title ? { title: config4.title } : {}
@@ -106805,17 +107133,13 @@ async function loadSdk7() {
106805
107133
  throw err;
106806
107134
  }
106807
107135
  }
106808
- function resolveModel2(modelRegistry, model) {
106809
- if (model) {
106810
- const slash = model.indexOf("/");
106811
- if (slash === -1) {
106812
- return modelRegistry.getAll().find((entry) => entry.id === model);
106813
- }
106814
- const provider = model.slice(0, slash);
106815
- const modelId = model.slice(slash + 1);
106816
- return modelRegistry.find(provider, modelId);
107136
+ function resolveRequiredModel2(modelRegistry, model) {
107137
+ const slash = model.indexOf("/");
107138
+ const resolved = slash === -1 ? modelRegistry.getAll().find((entry) => entry.id === model) : modelRegistry.find(model.slice(0, slash), model.slice(slash + 1));
107139
+ if (!resolved) {
107140
+ throw new Error(`Pi model not found: ${model}`);
106817
107141
  }
106818
- return modelRegistry.getAvailable()[0];
107142
+ return resolved;
106819
107143
  }
106820
107144
 
106821
107145
  class PiSdkHarness {
@@ -106857,7 +107181,14 @@ class PiSdkHarness {
106857
107181
  async newSession(config4) {
106858
107182
  if (this.closed)
106859
107183
  throw new Error("Harness is closed");
106860
- const session2 = await this.createAgentSession(config4.systemPrompt ?? "", config4.model);
107184
+ const model = requireHarnessModel(config4.model, "pi-sdk newSession");
107185
+ const { SessionManager } = await loadSdk7();
107186
+ const session2 = await this.openAgentSession({
107187
+ systemPrompt: config4.systemPrompt ?? "",
107188
+ model,
107189
+ sessionManager: SessionManager.create(getPiSessionDir(this.cwd)),
107190
+ requireModel: true
107191
+ });
106861
107192
  const piSession = new PiSdkSession({
106862
107193
  session: session2,
106863
107194
  opencodeSessionId: session2.sessionId,
@@ -106867,36 +107198,24 @@ class PiSdkHarness {
106867
107198
  this.sessions.set(session2.sessionId, piSession);
106868
107199
  return piSession;
106869
107200
  }
106870
- async resumeSession(sessionId, _options) {
107201
+ async resumeSession(sessionId, options) {
106871
107202
  if (this.closed)
106872
107203
  throw new Error("Harness is closed");
106873
107204
  const existing = this.sessions.get(sessionId);
106874
107205
  if (existing)
106875
107206
  return existing;
106876
- const { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager } = await loadSdk7();
107207
+ const { SessionManager } = await loadSdk7();
106877
107208
  const sessions = await SessionManager.list(this.cwd, getPiSessionDir(this.cwd));
106878
107209
  const match17 = sessions.find((s) => s.id === sessionId);
106879
107210
  if (!match17) {
106880
107211
  throw new Error(`Session ${sessionId} not found on the harness`);
106881
107212
  }
106882
- const resourceLoader = new DefaultResourceLoader({
106883
- cwd: this.cwd,
106884
- agentDir: getAgentDir(),
106885
- systemPromptOverride: () => ""
106886
- });
106887
- await resourceLoader.reload();
106888
- const resolvedModel = resolveModel2(this.modelRegistry);
106889
- if (!resolvedModel) {
106890
- throw new Error("No Pi model available");
106891
- }
106892
- const { session: session2 } = await withTimeout(createAgentSession({
106893
- cwd: this.cwd,
106894
- model: resolvedModel,
107213
+ const session2 = await this.openAgentSession({
107214
+ systemPrompt: "",
107215
+ model: options?.model,
106895
107216
  sessionManager: SessionManager.open(match17.path, getPiSessionDir(this.cwd)),
106896
- authStorage: this.authStorage,
106897
- modelRegistry: this.modelRegistry,
106898
- resourceLoader
106899
- }), SESSION_CREATE_TIMEOUT_MS3, "createAgentSession");
107217
+ requireModel: false
107218
+ });
106900
107219
  const piSession = new PiSdkSession({
106901
107220
  session: session2,
106902
107221
  opencodeSessionId: session2.sessionId,
@@ -106920,30 +107239,34 @@ class PiSdkHarness {
106920
107239
  await Promise.all(closing);
106921
107240
  this.sessions.clear();
106922
107241
  }
106923
- async createAgentSession(systemPrompt, model) {
106924
- const { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager } = await loadSdk7();
106925
- const resolvedModel = resolveModel2(this.modelRegistry, model ?? DEFAULT_MODEL3);
106926
- if (!resolvedModel) {
107242
+ async openAgentSession(args2) {
107243
+ const { createAgentSession, DefaultResourceLoader, getAgentDir } = await loadSdk7();
107244
+ const parsed = args2.model ? parsePiSpawnModel(args2.model) : undefined;
107245
+ const modelSlug = parsed?.model ?? args2.model;
107246
+ const resolvedModel = args2.requireModel ? resolveRequiredModel2(this.modelRegistry, requireHarnessModel(modelSlug, "pi-sdk openAgentSession")) : modelSlug ? resolveRequiredModel2(this.modelRegistry, modelSlug) : undefined;
107247
+ const thinkingLevel = resolvePiThinkingLevel(parsed?.thinking);
107248
+ if (args2.requireModel && !resolvedModel) {
106927
107249
  throw new Error("No Pi model available — configure provider credentials in ~/.pi/agent/auth.json");
106928
107250
  }
106929
107251
  const resourceLoader = new DefaultResourceLoader({
106930
107252
  cwd: this.cwd,
106931
107253
  agentDir: getAgentDir(),
106932
- systemPromptOverride: () => systemPrompt
107254
+ systemPromptOverride: () => args2.systemPrompt
106933
107255
  });
106934
107256
  await resourceLoader.reload();
106935
107257
  const { session: session2 } = await withTimeout(createAgentSession({
106936
107258
  cwd: this.cwd,
106937
- model: resolvedModel,
106938
- sessionManager: SessionManager.create(getPiSessionDir(this.cwd)),
107259
+ sessionManager: args2.sessionManager,
106939
107260
  authStorage: this.authStorage,
106940
107261
  modelRegistry: this.modelRegistry,
106941
- resourceLoader
107262
+ resourceLoader,
107263
+ ...resolvedModel ? { model: resolvedModel } : {},
107264
+ ...thinkingLevel ? { thinkingLevel } : {}
106942
107265
  }), SESSION_CREATE_TIMEOUT_MS3, "createAgentSession");
106943
107266
  return session2;
106944
107267
  }
106945
107268
  }
106946
- var SESSION_CREATE_TIMEOUT_MS3 = 60000, DEFAULT_MODEL3 = "opencode/big-pickle", _sdkCache7, _sdkLoadError7, startPiSdkHarness = async (config4) => {
107269
+ var SESSION_CREATE_TIMEOUT_MS3 = 60000, _sdkCache7, _sdkLoadError7, startPiSdkHarness = async (config4) => {
106947
107270
  try {
106948
107271
  const { AuthStorage, ModelRegistry } = await loadSdk7();
106949
107272
  const authStorage = AuthStorage.create();
@@ -106959,6 +107282,7 @@ var SESSION_CREATE_TIMEOUT_MS3 = 60000, DEFAULT_MODEL3 = "opencode/big-pickle",
106959
107282
  var init_pi_harness = __esm(() => {
106960
107283
  init_pi_session();
106961
107284
  init_pi_agent_service();
107285
+ init_pure2();
106962
107286
  init_pi_sdk_package();
106963
107287
  });
106964
107288
 
@@ -107145,6 +107469,8 @@ async function resolveSessionHandle(daemonSession, deps, info) {
107145
107469
  const harness = await ensureHarnessAlive(daemonSession, deps, info);
107146
107470
  if (!harness)
107147
107471
  return null;
107472
+ const modelConfig = info.lastUsedConfig.model;
107473
+ const model = modelConfig ? requireHarnessModel(`${modelConfig.providerID}/${modelConfig.modelID}`, "agentic-query resumeSession") : requireHarnessModel(undefined, "agentic-query resumeSession");
107148
107474
  const resumed = await resumeSession({
107149
107475
  harness,
107150
107476
  journalFactory: deps.journalFactory,
@@ -107153,7 +107479,8 @@ async function resolveSessionHandle(daemonSession, deps, info) {
107153
107479
  harnessSessionId: rowId,
107154
107480
  opencodeSessionId: info.opencodeSessionId,
107155
107481
  workspaceId: info.workspaceId,
107156
- harnessName: info.harnessName
107482
+ harnessName: info.harnessName,
107483
+ model
107157
107484
  });
107158
107485
  deps.activeSessions.set(rowId, resumed);
107159
107486
  wireResumedIdle(resumed, deps, info);
@@ -107265,7 +107592,10 @@ async function openPendingHarnessSession(daemonSession, deps, input, options) {
107265
107592
  logPrefix
107266
107593
  });
107267
107594
  const modelConfig = lastUsedConfig.model;
107268
- const model = modelConfig ? `${modelConfig.providerID}/${modelConfig.modelID}` : undefined;
107595
+ if (!modelConfig) {
107596
+ throw new Error("Harness model is required (agentic-query open session)");
107597
+ }
107598
+ const model = requireHarnessModel(`${modelConfig.providerID}/${modelConfig.modelID}`, "agentic-query newSession");
107269
107599
  const liveSession = await harness.newSession({
107270
107600
  agent: lastUsedConfig.agent,
107271
107601
  model,
@@ -134762,4 +135092,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
134762
135092
  });
134763
135093
  program2.parse();
134764
135094
 
134765
- //# debugId=B34E804643624D4B64756E2164756E21
135095
+ //# debugId=48E3BD8E7ACDCBFA64756E2164756E21