@spencer-kit/coder-studio 0.3.10 → 0.3.11

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.
@@ -996,7 +996,14 @@ var init_image = __esm({
996
996
 
997
997
  // packages/server/src/fs/file-io.ts
998
998
  import { createHash } from "crypto";
999
- import { readFile as fsReadFile, writeFile as fsWriteFile, mkdir, rm, stat } from "fs/promises";
999
+ import {
1000
+ readFile as fsReadFile,
1001
+ rename as fsRename,
1002
+ writeFile as fsWriteFile,
1003
+ mkdir,
1004
+ rm,
1005
+ stat
1006
+ } from "fs/promises";
1000
1007
  import { dirname as dirname2, isAbsolute, relative, resolve as resolve2 } from "path";
1001
1008
  async function statSafe(path10) {
1002
1009
  try {
@@ -1030,6 +1037,27 @@ async function deleteEntry(rootPath, relPath) {
1030
1037
  }
1031
1038
  await rm(abs, { recursive: true });
1032
1039
  }
1040
+ async function renameEntry(rootPath, fromPath, toPath) {
1041
+ const fromAbs = resolveSafe(rootPath, fromPath);
1042
+ const toAbs = resolveSafe(rootPath, toPath);
1043
+ const source = await statSafe(fromAbs);
1044
+ const target = await statSafe(toAbs);
1045
+ const fromParent = dirname2(fromAbs);
1046
+ const toParent = dirname2(toAbs);
1047
+ if (!source) {
1048
+ throw { code: "not_found", message: "Source not found" };
1049
+ }
1050
+ if (fromParent !== toParent) {
1051
+ throw {
1052
+ code: "rename_across_directories_not_supported",
1053
+ message: "Rename must stay within the current directory"
1054
+ };
1055
+ }
1056
+ if (target) {
1057
+ throw { code: "already_exists", message: "Target already exists" };
1058
+ }
1059
+ await fsRename(fromAbs, toAbs);
1060
+ }
1033
1061
  function resolveSafe(root, relPath) {
1034
1062
  const absRoot = resolve2(root);
1035
1063
  const abs = resolve2(absRoot, relPath);
@@ -2010,12 +2038,12 @@ var init_auto_fetch = __esm({
2010
2038
  }
2011
2039
  acquireWorkspaceOperation(workspaceId) {
2012
2040
  const state = this.getOrCreateState(workspaceId);
2013
- return new Promise((resolve4) => {
2041
+ return new Promise((resolve5) => {
2014
2042
  const grant = () => {
2015
2043
  state.inFlight = true;
2016
2044
  state.nextFetchAt = void 0;
2017
2045
  let released = false;
2018
- resolve4(() => {
2046
+ resolve5(() => {
2019
2047
  if (released) {
2020
2048
  return;
2021
2049
  }
@@ -2043,7 +2071,7 @@ var init_auto_fetch = __esm({
2043
2071
  // packages/server/src/provider-runtime/command-runner.ts
2044
2072
  import { spawn } from "node:child_process";
2045
2073
  async function runCommandAsString(file, args, options) {
2046
- return new Promise((resolve4, reject) => {
2074
+ return new Promise((resolve5, reject) => {
2047
2075
  const child = spawn(file, args, {
2048
2076
  shell: shouldUseShellForCommand(file, process.platform),
2049
2077
  windowsHide: options?.windowsHide ?? true
@@ -2068,7 +2096,7 @@ async function runCommandAsString(file, args, options) {
2068
2096
  const stdout = Buffer.concat(stdoutChunks).toString("utf8");
2069
2097
  const stderr = Buffer.concat(stderrChunks).toString("utf8");
2070
2098
  if (code === 0) {
2071
- resolve4({ stdout, stderr });
2099
+ resolve5({ stdout, stderr });
2072
2100
  return;
2073
2101
  }
2074
2102
  reject(
@@ -2677,6 +2705,13 @@ ${details.stdout}`.toLowerCase();
2677
2705
  }
2678
2706
  });
2679
2707
 
2708
+ // packages/core/src/domain/diagnostics.ts
2709
+ var init_diagnostics = __esm({
2710
+ "packages/core/src/domain/diagnostics.ts"() {
2711
+ "use strict";
2712
+ }
2713
+ });
2714
+
2680
2715
  // packages/core/src/domain/events.ts
2681
2716
  var init_events = __esm({
2682
2717
  "packages/core/src/domain/events.ts"() {
@@ -2927,6 +2962,7 @@ var init_idle_heuristics2 = __esm({
2927
2962
  var init_src3 = __esm({
2928
2963
  "packages/core/src/index.ts"() {
2929
2964
  "use strict";
2965
+ init_diagnostics();
2930
2966
  init_events();
2931
2967
  init_mcp();
2932
2968
  init_provider_install();
@@ -5010,13 +5046,13 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
5010
5046
  return;
5011
5047
  }
5012
5048
  const arch = deps.arch ?? process.arch;
5013
- const resolve4 = deps.resolve ?? ((id) => require2.resolve(id));
5049
+ const resolve5 = deps.resolve ?? ((id) => require2.resolve(id));
5014
5050
  const fileExists = deps.existsSync ?? existsSync4;
5015
- const stat7 = deps.statSync ?? statSync;
5051
+ const stat8 = deps.statSync ?? statSync;
5016
5052
  const chmod = deps.chmodSync ?? chmodSync2;
5017
5053
  let packageJsonPath;
5018
5054
  try {
5019
- packageJsonPath = resolve4(NODE_PTY_PKG);
5055
+ packageJsonPath = resolve5(NODE_PTY_PKG);
5020
5056
  } catch {
5021
5057
  return;
5022
5058
  }
@@ -5030,7 +5066,7 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
5030
5066
  if (!fileExists(helperPath)) {
5031
5067
  return;
5032
5068
  }
5033
- const currentMode = stat7(helperPath).mode;
5069
+ const currentMode = stat8(helperPath).mode;
5034
5070
  const executableMode = currentMode | 73;
5035
5071
  if (executableMode === currentMode) {
5036
5072
  return;
@@ -5081,7 +5117,7 @@ async function escalateKillWithPolling(pid, signal, options) {
5081
5117
  const startTime = Date.now();
5082
5118
  const deadline = startTime + timeoutMs;
5083
5119
  while (Date.now() < deadline) {
5084
- await new Promise((resolve4) => setTimeout(resolve4, pollIntervalMs));
5120
+ await new Promise((resolve5) => setTimeout(resolve5, pollIntervalMs));
5085
5121
  if (!isProcessAlive(pid)) {
5086
5122
  return true;
5087
5123
  }
@@ -5233,38 +5269,107 @@ var init_settings = __esm({
5233
5269
 
5234
5270
  // packages/server/src/supervisor/evaluator.ts
5235
5271
  import { spawn as spawn2 } from "node:child_process";
5236
- function buildPrompt(context) {
5272
+ function buildPrompt(context, mode) {
5273
+ if (mode === "decompose") {
5274
+ return [
5275
+ "You are an autonomous supervisor for a target-scoped software task.",
5276
+ "Your first job is to decompose the target into a supervision structure before evaluation begins.",
5277
+ "",
5278
+ "Return JSON only.",
5279
+ "No prose before or after the JSON.",
5280
+ "",
5281
+ "Decomposition policy:",
5282
+ "- Do not ask the user any questions.",
5283
+ "- Do not ask for clarification, confirmation, or approval.",
5284
+ "- Do not propose options for the user to choose from.",
5285
+ "- If information is incomplete, make the most conservative reasonable assumptions and decide the decomposition yourself.",
5286
+ "- Your job is to return the best useful decomposition now, not to begin a discussion or planning workflow.",
5287
+ "- Keep the user-visible target as the top-level supervision owner.",
5288
+ '- Choose "stage" by default.',
5289
+ '- Choose "subtarget" only when the work clearly breaks into independently deliverable and independently verifiable workstreams.',
5290
+ "- If the distinction is unclear, choose stage.",
5291
+ "- Produce 1 to 7 decomposition items.",
5292
+ "- Each item must be concrete, milestone-sized, and useful for subsequent evaluation.",
5293
+ "- Do not leave the structure empty.",
5294
+ "",
5295
+ "Item requirements:",
5296
+ '- Each item must include "id", "kind", "title", "objective", "deliverable", "acceptanceCriteria", and "status".',
5297
+ '- "kind" must match the selected decompositionMode: all "stage" or all "subtarget".',
5298
+ '- "acceptanceCriteria" must be a non-empty string array.',
5299
+ '- Use statuses "pending", "in_progress", or "done".',
5300
+ "- Usually mark the first active item as in_progress and the rest as pending.",
5301
+ "",
5302
+ "Output schema:",
5303
+ "{",
5304
+ ' "mode": "decompose",',
5305
+ ' "decompositionMode": "stage" | "subtarget",',
5306
+ ' "items": [',
5307
+ ' { "id": string, "kind": "stage" | "subtarget", "title": string, "objective": string, "deliverable": string, "acceptanceCriteria": string[], "status": "pending" | "in_progress" | "done" }',
5308
+ " ],",
5309
+ ' "activeItemId": optional string,',
5310
+ ' "progressSummary": optional brief summary',
5311
+ "}",
5312
+ "",
5313
+ "Current objective:",
5314
+ context.objective,
5315
+ "",
5316
+ "Current target memory:",
5317
+ JSON.stringify(context.targetMemory, null, 2),
5318
+ "",
5319
+ "Latest user input:",
5320
+ context.latestUserInput?.trim() || "(none)",
5321
+ "",
5322
+ "Current terminal snapshot:",
5323
+ context.terminalExcerpt || "(no output yet)"
5324
+ ].join("\n");
5325
+ }
5237
5326
  const lines = [
5238
5327
  "You are an autonomous supervisor for a target-scoped software task.",
5239
5328
  "Your job is to keep the agent moving toward the objective until the objective is complete.",
5240
5329
  "",
5241
5330
  "Return JSON only.",
5331
+ "No prose before or after the JSON.",
5242
5332
  "",
5243
5333
  "Decision policy:",
5244
- '- Prefer "continue" whenever there is a reasonable next action.',
5334
+ '- Prefer "continue" over "stop" whenever the objective is not yet verified complete and there is a concrete next action.',
5335
+ '- "continue" may mean continuing the current item, verifying the current item, unblocking the current item, or advancing to the next item only after the current item is verified done.',
5245
5336
  "- Do not ask the user to decide, clarify, or choose among implementation options.",
5337
+ "- Do not tell the agent to ask the user to decide, clarify, or choose among implementation options unless continuing would likely be unsafe or clearly unsupported.",
5338
+ "- If the agent asks a question or presents multiple options, choose the most conservative reasonable option yourself and direct the next action.",
5339
+ "- If multiple reasonable paths exist, pick one and move forward unless doing so would be unsafe or clearly unsupported.",
5246
5340
  "- When information is incomplete, choose a conservative next action based on the objective, target memory, latest user input, and terminal snapshot.",
5341
+ "- Do not treat the agent's claims, summaries, or self-reports as sufficient evidence of completion.",
5247
5342
  "- Stop only when the objective is complete, or when continuing would likely push the agent in an unsafe or clearly unsupported direction.",
5248
5343
  "",
5249
5344
  "Stage decision policy:",
5250
5345
  "- Use the target memory as the current supervision state.",
5251
- "- Base your decision on the objective, current plan, activeStepId, progressSummary, lastGuidance, stalledCount, latest user input, and terminal snapshot.",
5252
- "- Identify which plan step is currently active.",
5253
- "- Decide whether the active step is done, still in progress, blocked, or obsolete.",
5254
- "- If the active step is done, advance to the next useful step.",
5255
- "- If the active step is still in progress, give guidance that moves it forward.",
5346
+ "- Base your decision on the objective, current decompositionMode, items, activeItemId, progressSummary, lastGuidance, stalledCount, latest user input, and terminal snapshot.",
5347
+ "- Identify which decomposition item is currently active.",
5348
+ "- Keep the current active item unless there is evidence that it is done, blocked, or obsolete.",
5349
+ "- Decide whether the active item is done, still in progress, blocked, or obsolete based on observable evidence.",
5350
+ '- Treat statements like "done", "fixed", "implemented", or "should pass" as unverified unless supported by observable evidence.',
5351
+ '- Mark an item as "done" only when there is observable evidence that its deliverable or acceptanceCriteria were satisfied.',
5352
+ "- Prefer evidence from terminal output, test results, build results, explicit verification output, or other observable artifacts in the terminal snapshot.",
5353
+ "- If evidence is missing or ambiguous, keep the item in_progress and direct the agent to gather or produce the missing verification evidence.",
5354
+ "- If the current item appears nearly complete but is not yet verified, keep the same active item and direct targeted verification.",
5355
+ "- Advance to the next item only after the current item's deliverable or acceptanceCriteria are supported by observable evidence.",
5356
+ '- When advancing to the next item, mark the previous item as "done" and set activeItemId to the next item explicitly.',
5357
+ "- If the active item is blocked, give guidance that is most likely to unblock it.",
5358
+ "- If the active item is obsolete, explain the reason briefly and move to the next useful item.",
5256
5359
  "- If the agent appears stuck or repeated the same action, give a different concrete next action.",
5257
- "- If the plan is obsolete, update only the affected steps unless a full replacement is necessary.",
5360
+ "- Do not rewrite the decomposition structure during normal evaluation cycles.",
5258
5361
  "",
5259
5362
  "Allowed statuses:",
5260
- '- "continue": more work is needed; include "reason" and "guidance".',
5363
+ '- "continue": supervision should continue; include "reason" and "guidance".',
5261
5364
  '- "stop": supervision should stop; include "stopReason" and "reason".',
5262
5365
  "",
5263
5366
  "Allowed stop reasons:",
5264
5367
  '- "objective_complete"',
5265
5368
  '- "supervisor_uncertain"',
5266
5369
  "",
5267
- 'Use "objective_complete" only when the objective has been satisfied.',
5370
+ 'Use "objective_complete" only when there is evidence that the objective and relevant acceptanceCriteria have been satisfied.',
5371
+ "- Do not stop only because the agent says the work is complete or because code changes exist without verification evidence.",
5372
+ "- If completion looks plausible but remains unverified, continue and require targeted verification.",
5268
5373
  'Use "supervisor_uncertain" only as a last resort when no useful next action can be inferred and additional guidance would likely be misleading.',
5269
5374
  "",
5270
5375
  'Guidance requirements for "continue":',
@@ -5272,30 +5377,32 @@ function buildPrompt(context) {
5272
5377
  "- Focus on the highest-value step toward completing the objective.",
5273
5378
  "- Be specific enough for the supervised agent to act without asking the user.",
5274
5379
  "- Avoid generic reminders, encouragement, or restating the objective.",
5275
- "- If verification is needed, tell the agent exactly what to verify next.",
5380
+ "- If verification is needed, tell the agent exactly what command, file, behavior, or artifact to verify next.",
5276
5381
  "- If implementation is needed, point to the likely area, behavior, or file/module based on available evidence.",
5382
+ "- If the agent asked a question, answer it directly in the guidance and continue with a concrete next action.",
5277
5383
  "",
5278
- "Planning policy:",
5279
- "- If planGenerated is false, include a plan with 3 to 7 milestone-sized steps.",
5280
- "- If planGenerated is true, update progress incrementally.",
5281
- "- Do not rewrite the full plan unless the existing plan is clearly wrong or obsolete.",
5282
- "- Use stepUpdates to mark completed or active steps when the terminal snapshot shows progress.",
5283
- "- Keep activeStepId aligned with the next useful step.",
5384
+ "Evaluation policy:",
5385
+ "- Update progress incrementally against the existing decomposition.",
5386
+ "- Use itemUpdates to reflect evidence-backed status changes only.",
5387
+ "- Keep activeItemId on the current item by default.",
5388
+ "- Change activeItemId only when there is a clear reason to switch items.",
5389
+ "- If evidence is missing or ambiguous, prefer verification over further implementation.",
5284
5390
  "",
5285
5391
  "Output schema:",
5286
5392
  "For continue:",
5287
5393
  "{",
5394
+ ' "mode": "evaluate",',
5288
5395
  ' "status": "continue",',
5289
5396
  ' "reason": "brief explanation of why more work is needed",',
5290
5397
  ' "guidance": "specific next action for the supervised agent",',
5291
- ' "plan": optional array of plan steps,',
5292
- ' "activeStepId": optional step id,',
5398
+ ' "activeItemId": optional item id,',
5293
5399
  ' "progressSummary": optional brief progress summary,',
5294
- ' "stepUpdates": optional array of { "id": string, "status": "pending" | "in_progress" | "done" }',
5400
+ ' "itemUpdates": optional array of { "id": string, "status": "pending" | "in_progress" | "done" }',
5295
5401
  "}",
5296
5402
  "",
5297
5403
  "For stop:",
5298
5404
  "{",
5405
+ ' "mode": "evaluate",',
5299
5406
  ' "status": "stop",',
5300
5407
  ' "stopReason": "objective_complete" | "supervisor_uncertain",',
5301
5408
  ' "reason": "brief explanation"',
@@ -5319,7 +5426,7 @@ async function runCommand(command, timeoutMs, options = {}) {
5319
5426
  if (options.signal?.aborted) {
5320
5427
  throw createSupervisorEvalAbortedError();
5321
5428
  }
5322
- return await new Promise((resolve4, reject) => {
5429
+ return await new Promise((resolve5, reject) => {
5323
5430
  const child = spawn2(command.argv[0], command.argv.slice(1), {
5324
5431
  cwd: command.cwd,
5325
5432
  detached: process.platform !== "win32",
@@ -5349,7 +5456,7 @@ async function runCommand(command, timeoutMs, options = {}) {
5349
5456
  }
5350
5457
  settled = true;
5351
5458
  cleanup();
5352
- resolve4(value);
5459
+ resolve5(value);
5353
5460
  };
5354
5461
  const terminate = (error) => {
5355
5462
  if (terminationError) {
@@ -5380,7 +5487,10 @@ async function runCommand(command, timeoutMs, options = {}) {
5380
5487
  settleReject(terminationError);
5381
5488
  return;
5382
5489
  }
5383
- settleReject(error);
5490
+ settleReject({
5491
+ code: "supervisor_eval_failed",
5492
+ message: error instanceof Error ? error.message : "Evaluator process failed to start"
5493
+ });
5384
5494
  });
5385
5495
  child.on("exit", (code) => {
5386
5496
  if (terminationError) {
@@ -5558,7 +5668,7 @@ function extractSupervisorPayload(output, providerId) {
5558
5668
  }
5559
5669
  throw new Error("Supervisor did not return a recognizable message");
5560
5670
  }
5561
- function parseSupervisorEvaluationResult(payloadText, guidanceMaxChars) {
5671
+ function parseSupervisorEvaluationResult(payloadText, guidanceMaxChars, requestedMode) {
5562
5672
  let parsed;
5563
5673
  try {
5564
5674
  parsed = JSON.parse(stripCodeFence(payloadText));
@@ -5571,9 +5681,49 @@ function parseSupervisorEvaluationResult(payloadText, guidanceMaxChars) {
5571
5681
  throw new Error("Supervisor returned invalid evaluation payload");
5572
5682
  }
5573
5683
  const record = parsed;
5684
+ const payloadMode = record.mode;
5685
+ if (requestedMode === "decompose") {
5686
+ if (payloadMode !== "decompose") {
5687
+ throw new Error("Supervisor returned invalid decompose payload");
5688
+ }
5689
+ const decompositionMode = record.decompositionMode;
5690
+ if (decompositionMode !== "stage" && decompositionMode !== "subtarget") {
5691
+ throw new Error("Supervisor decompose result is missing a valid decompositionMode");
5692
+ }
5693
+ const items = Array.isArray(record.items) ? record.items.flatMap((value) => {
5694
+ if (!value || typeof value !== "object") {
5695
+ return [];
5696
+ }
5697
+ const item = value;
5698
+ if (typeof item.id !== "string" || item.kind !== "stage" && item.kind !== "subtarget" || item.kind !== decompositionMode || typeof item.title !== "string" || typeof item.objective !== "string" || typeof item.deliverable !== "string" || !Array.isArray(item.acceptanceCriteria) || item.acceptanceCriteria.length === 0 || item.acceptanceCriteria.some((entry) => typeof entry !== "string") || item.status !== "pending" && item.status !== "in_progress" && item.status !== "done") {
5699
+ return [];
5700
+ }
5701
+ return [
5702
+ {
5703
+ id: item.id,
5704
+ kind: item.kind,
5705
+ title: item.title,
5706
+ objective: item.objective,
5707
+ deliverable: item.deliverable,
5708
+ acceptanceCriteria: item.acceptanceCriteria,
5709
+ status: item.status
5710
+ }
5711
+ ];
5712
+ }) : [];
5713
+ if (items.length === 0) {
5714
+ throw new Error("Supervisor decompose result must include at least one valid item");
5715
+ }
5716
+ return {
5717
+ mode: "decompose",
5718
+ decompositionMode,
5719
+ items,
5720
+ activeItemId: typeof record.activeItemId === "string" && record.activeItemId.trim() ? record.activeItemId : void 0,
5721
+ progressSummary: typeof record.progressSummary === "string" && record.progressSummary.trim() ? record.progressSummary.trim() : void 0
5722
+ };
5723
+ }
5574
5724
  const status = record.status;
5575
5725
  const reason = record.reason;
5576
- if (status !== "continue" && status !== "stop" || typeof reason !== "string" || !reason.trim()) {
5726
+ if (payloadMode !== void 0 && payloadMode !== "evaluate" || status !== "continue" && status !== "stop" || typeof reason !== "string" || !reason.trim()) {
5577
5727
  throw new Error("Supervisor returned invalid evaluation payload");
5578
5728
  }
5579
5729
  if (status === "stop") {
@@ -5582,23 +5732,14 @@ function parseSupervisorEvaluationResult(payloadText, guidanceMaxChars) {
5582
5732
  throw new Error("Supervisor stop result is missing a valid stopReason");
5583
5733
  }
5584
5734
  return {
5735
+ mode: "evaluate",
5585
5736
  status,
5586
5737
  stopReason,
5587
5738
  reason: reason.trim()
5588
5739
  };
5589
5740
  }
5590
5741
  const guidance = typeof record.guidance === "string" && record.guidance.trim() ? record.guidance.trim().slice(0, guidanceMaxChars) : void 0;
5591
- const plan = Array.isArray(record.plan) ? record.plan.flatMap((value) => {
5592
- if (!value || typeof value !== "object") {
5593
- return [];
5594
- }
5595
- const step = value;
5596
- if (typeof step.id !== "string" || typeof step.title !== "string" || step.status !== "pending" && step.status !== "in_progress" && step.status !== "done") {
5597
- return [];
5598
- }
5599
- return [{ id: step.id, title: step.title, status: step.status }];
5600
- }) : void 0;
5601
- const stepUpdates = Array.isArray(record.stepUpdates) ? record.stepUpdates.flatMap((value) => {
5742
+ const itemUpdates = Array.isArray(record.itemUpdates) ? record.itemUpdates.flatMap((value) => {
5602
5743
  if (!value || typeof value !== "object") {
5603
5744
  return [];
5604
5745
  }
@@ -5609,13 +5750,13 @@ function parseSupervisorEvaluationResult(payloadText, guidanceMaxChars) {
5609
5750
  return [{ id: update.id, status: update.status }];
5610
5751
  }) : void 0;
5611
5752
  return {
5753
+ mode: "evaluate",
5612
5754
  status,
5613
5755
  reason: reason.trim(),
5614
5756
  guidance,
5615
- plan,
5616
- activeStepId: typeof record.activeStepId === "string" && record.activeStepId.trim() ? record.activeStepId : void 0,
5757
+ activeItemId: typeof record.activeItemId === "string" && record.activeItemId.trim() ? record.activeItemId : void 0,
5617
5758
  progressSummary: typeof record.progressSummary === "string" && record.progressSummary.trim() ? record.progressSummary.trim() : void 0,
5618
- stepUpdates
5759
+ itemUpdates
5619
5760
  };
5620
5761
  }
5621
5762
  var NOOP_LOGGER2, SupervisorEvaluator;
@@ -5667,7 +5808,8 @@ var init_evaluator = __esm({
5667
5808
  provider,
5668
5809
  this.deps.providerConfigRepo.get(provider.id)
5669
5810
  );
5670
- const prompt = buildPrompt(context);
5811
+ const mode = options.mode ?? "evaluate";
5812
+ const prompt = buildPrompt(context, mode);
5671
5813
  const command = provider.buildSupervisorEvalCommand(config, {
5672
5814
  prompt,
5673
5815
  sessionId: supervisor.sessionId,
@@ -5701,7 +5843,7 @@ var init_evaluator = __esm({
5701
5843
  );
5702
5844
  throw error;
5703
5845
  }
5704
- return parseSupervisorEvaluationResult(payloadText, this.config.guidanceMaxChars);
5846
+ return parseSupervisorEvaluationResult(payloadText, this.config.guidanceMaxChars, mode);
5705
5847
  }
5706
5848
  };
5707
5849
  }
@@ -5872,12 +6014,12 @@ var init_scheduler = __esm({
5872
6014
 
5873
6015
  // packages/server/src/supervisor/manager.ts
5874
6016
  function createDeferredCompletion() {
5875
- let resolve4 = () => {
6017
+ let resolve5 = () => {
5876
6018
  };
5877
6019
  const promise = new Promise((innerResolve) => {
5878
- resolve4 = innerResolve;
6020
+ resolve5 = innerResolve;
5879
6021
  });
5880
- return { promise, resolve: resolve4 };
6022
+ return { promise, resolve: resolve5 };
5881
6023
  }
5882
6024
  function generateSupervisorId() {
5883
6025
  return `sup_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
@@ -5903,6 +6045,12 @@ function messageOf(error, fallback) {
5903
6045
  function logFailure(logger, error, context, message) {
5904
6046
  logger.error({ ...context, err: error }, message);
5905
6047
  }
6048
+ function isDecomposeResult(result) {
6049
+ return result.mode === "decompose";
6050
+ }
6051
+ function isEvaluateStopResult(result) {
6052
+ return "status" in result && result.status === "stop";
6053
+ }
5906
6054
  function isSupervisorEvalAborted(error) {
5907
6055
  return !!error && typeof error === "object" && error.code === "supervisor_eval_aborted";
5908
6056
  }
@@ -6406,7 +6554,12 @@ var init_manager2 = __esm({
6406
6554
  turnId: context.lastTurnId,
6407
6555
  createdAt: Date.now()
6408
6556
  });
6409
- this.broadcastCycle(evaluatingSupervisor, activeCycle, "created");
6557
+ this.broadcastCycle(evaluatingSupervisor, activeCycle, "created", {
6558
+ phase: "waiting_evaluator",
6559
+ currentAttemptIndex: 0,
6560
+ attemptCount: 1,
6561
+ maxAttempts: 1 + retrySettings.retryMaxCount
6562
+ });
6410
6563
  return {
6411
6564
  cycle: activeCycle,
6412
6565
  supervisor: hydratedSupervisor,
@@ -6604,6 +6757,8 @@ var init_manager2 = __esm({
6604
6757
  }
6605
6758
  async executeCycleWithRetry(started, signal) {
6606
6759
  const supervisor = started.supervisor;
6760
+ let context = started.context;
6761
+ let currentMemory = context.targetMemory;
6607
6762
  for (let attemptIndex = 0; ; attemptIndex += 1) {
6608
6763
  const attempt = this.deps.cycleAttemptRepo.create({
6609
6764
  id: generateAttemptId(),
@@ -6612,23 +6767,79 @@ var init_manager2 = __esm({
6612
6767
  status: "evaluating",
6613
6768
  startedAt: Date.now()
6614
6769
  });
6770
+ this.broadcastCycle(started.supervisor, started.cycle, "updated", {
6771
+ phase: "waiting_evaluator",
6772
+ currentAttemptIndex: attemptIndex,
6773
+ attemptCount: attemptIndex + 1,
6774
+ maxAttempts: 1 + started.retry.retryMaxCount
6775
+ });
6615
6776
  try {
6616
- const evaluation = await this.evaluator.evaluate(supervisor, started.context, { signal });
6777
+ if (!currentMemory.decompositionGenerated || currentMemory.items.length === 0) {
6778
+ const decomposition = await this.evaluator.evaluate(supervisor, context, {
6779
+ signal,
6780
+ mode: "decompose"
6781
+ });
6782
+ if (!isDecomposeResult(decomposition)) {
6783
+ throw new Error("Supervisor decompose pass did not return a decomposition result");
6784
+ }
6785
+ currentMemory = {
6786
+ ...currentMemory,
6787
+ decompositionGenerated: true,
6788
+ decompositionMode: decomposition.decompositionMode,
6789
+ items: decomposition.items,
6790
+ activeItemId: decomposition.activeItemId,
6791
+ progressSummary: decomposition.progressSummary ?? currentMemory.progressSummary,
6792
+ updatedAt: Date.now()
6793
+ };
6794
+ const workspace = this.requireWorkspace(context.workspaceId);
6795
+ await this.deps.targetStore.saveTargetMemory(
6796
+ workspace.path,
6797
+ started.targetId,
6798
+ currentMemory
6799
+ );
6800
+ const currentSupervisor2 = this.supervisors.get(supervisor.id) ?? this.requireSupervisor(supervisor.id);
6801
+ if (currentSupervisor2.targetId === started.targetId) {
6802
+ const refreshed = this.attachCycles({
6803
+ ...currentSupervisor2,
6804
+ currentTargetMemory: currentMemory
6805
+ });
6806
+ this.storeSnapshot(refreshed);
6807
+ }
6808
+ context = {
6809
+ ...context,
6810
+ targetMemory: currentMemory
6811
+ };
6812
+ }
6813
+ const evaluation = await this.evaluator.evaluate(supervisor, context, {
6814
+ signal,
6815
+ mode: "evaluate"
6816
+ });
6617
6817
  this.deps.cycleAttemptRepo.update(attempt.id, {
6618
6818
  status: "completed",
6619
6819
  completedAt: Date.now(),
6620
6820
  providerModel: supervisor.evaluatorModel ?? null
6621
6821
  });
6622
- if (evaluation.status === "stop") {
6822
+ if (isDecomposeResult(evaluation)) {
6823
+ throw new Error("Supervisor evaluate pass returned a decompose result");
6824
+ }
6825
+ const nextTargetMemory = this.applyEvaluationToTargetMemory(
6826
+ currentMemory,
6827
+ evaluation,
6828
+ void 0,
6829
+ Date.now()
6830
+ );
6831
+ if (isEvaluateStopResult(evaluation)) {
6623
6832
  return {
6624
6833
  evaluation,
6625
- injected: false
6834
+ injected: false,
6835
+ targetMemory: nextTargetMemory
6626
6836
  };
6627
6837
  }
6628
6838
  if (!evaluation.guidance?.trim()) {
6629
6839
  return {
6630
6840
  evaluation,
6631
- injected: false
6841
+ injected: false,
6842
+ targetMemory: nextTargetMemory
6632
6843
  };
6633
6844
  }
6634
6845
  if (signal?.aborted || this.pendingPauses.has(supervisor.id)) {
@@ -6638,7 +6849,8 @@ var init_manager2 = __esm({
6638
6849
  if (currentSupervisor.targetId !== started.targetId) {
6639
6850
  return {
6640
6851
  evaluation,
6641
- injected: false
6852
+ injected: false,
6853
+ targetMemory: nextTargetMemory
6642
6854
  };
6643
6855
  }
6644
6856
  const injectingSupervisor = this.attachCycles(
@@ -6651,6 +6863,12 @@ var init_manager2 = __esm({
6651
6863
  );
6652
6864
  this.storeSnapshot(injectingSupervisor);
6653
6865
  this.broadcastState(injectingSupervisor, "state_changed");
6866
+ this.broadcastCycle(injectingSupervisor, started.cycle, "updated", {
6867
+ phase: "injecting",
6868
+ currentAttemptIndex: attemptIndex,
6869
+ attemptCount: attemptIndex + 1,
6870
+ maxAttempts: 1 + started.retry.retryMaxCount
6871
+ });
6654
6872
  const recentCycles = this.deps.cycleRepo.listRecentForSupervisor(supervisor.id, this.config.guidanceDedupeWindow + 1).filter((cycle) => cycle.id !== started.cycle.id);
6655
6873
  const injection = await this.injector.inject(
6656
6874
  injectingSupervisor,
@@ -6663,7 +6881,13 @@ var init_manager2 = __esm({
6663
6881
  return {
6664
6882
  evaluation,
6665
6883
  injected: injection.injected,
6666
- injectedText: injection.injected ? injection.text : void 0
6884
+ injectedText: injection.injected ? injection.text : void 0,
6885
+ targetMemory: this.applyEvaluationToTargetMemory(
6886
+ currentMemory,
6887
+ evaluation,
6888
+ injection.injected ? injection.text : void 0,
6889
+ Date.now()
6890
+ )
6667
6891
  };
6668
6892
  } catch (error) {
6669
6893
  if (isSupervisorEvalAborted(error)) {
@@ -6684,6 +6908,15 @@ var init_manager2 = __esm({
6684
6908
  if (!this.shouldRetryAttempt(error, attemptIndex, started.retry)) {
6685
6909
  throw error;
6686
6910
  }
6911
+ const nextRetryAt = Date.now() + started.retry.retryDelayMs;
6912
+ this.broadcastCycle(supervisor, started.cycle, "updated", {
6913
+ phase: "retry_wait",
6914
+ currentAttemptIndex: attemptIndex,
6915
+ attemptCount: attemptIndex + 1,
6916
+ maxAttempts: 1 + started.retry.retryMaxCount,
6917
+ lastAttemptError: reason,
6918
+ nextRetryAt
6919
+ });
6687
6920
  await this.sleep(started.retry.retryDelayMs, signal);
6688
6921
  const evaluatingSupervisor = this.attachCycles(
6689
6922
  this.withCurrentTargetState(
@@ -6701,9 +6934,9 @@ var init_manager2 = __esm({
6701
6934
  async finalizeSuccessfulCycle(activeCycle, context, result, targetId) {
6702
6935
  const workspace = this.requireWorkspace(context.workspaceId);
6703
6936
  const currentSupervisor = this.supervisors.get(activeCycle.supervisorId) ?? this.requireSupervisor(activeCycle.supervisorId);
6704
- const targetMemory = targetId === currentSupervisor.targetId && currentSupervisor.currentTargetMemory ? currentSupervisor.currentTargetMemory : await this.deps.targetStore.loadTargetMemory(workspace.path, targetId);
6937
+ const evaluation = result.evaluation;
6705
6938
  const finalStatus = result.injected ? "injected" : "completed";
6706
- const cycleReason = result.evaluation.status === "stop" ? result.evaluation.reason : result.injected ? result.injectedText : result.evaluation.guidance ? `Skipped duplicate: ${result.evaluation.guidance}` : void 0;
6939
+ const cycleReason = isEvaluateStopResult(evaluation) ? evaluation.reason : result.injected ? result.injectedText : evaluation.guidance ? `Skipped duplicate: ${evaluation.guidance}` : void 0;
6707
6940
  const finishedCycle = this.deps.cycleRepo.update(activeCycle.id, {
6708
6941
  status: finalStatus,
6709
6942
  result: cycleReason ?? null,
@@ -6711,24 +6944,22 @@ var init_manager2 = __esm({
6711
6944
  errorReason: null,
6712
6945
  completedAt: Date.now()
6713
6946
  });
6714
- const nextTargetMemory = this.applyEvaluationToTargetMemory(
6715
- targetMemory,
6716
- result.evaluation,
6717
- result.injectedText,
6718
- finishedCycle.completedAt ?? Date.now()
6719
- );
6947
+ const nextTargetMemory = {
6948
+ ...result.targetMemory,
6949
+ updatedAt: finishedCycle.completedAt ?? Date.now()
6950
+ };
6720
6951
  await this.deps.targetStore.saveTargetMemory(workspace.path, targetId, nextTargetMemory);
6721
- const cycleRecord = result.evaluation.status === "stop" ? {
6952
+ const cycleRecord = isEvaluateStopResult(evaluation) ? {
6722
6953
  cycleId: activeCycle.id,
6723
6954
  targetId,
6724
6955
  startedAt: activeCycle.createdAt,
6725
6956
  completedAt: finishedCycle.completedAt ?? Date.now(),
6726
6957
  result: "stop",
6727
- stopReason: result.evaluation.stopReason,
6728
- reason: result.evaluation.reason,
6729
- progressSummary: result.evaluation.progressSummary ?? nextTargetMemory.progressSummary,
6730
- activeStepId: result.evaluation.activeStepId ?? nextTargetMemory.activeStepId,
6731
- stepUpdates: result.evaluation.stepUpdates,
6958
+ stopReason: evaluation.stopReason,
6959
+ reason: evaluation.reason,
6960
+ progressSummary: nextTargetMemory.progressSummary,
6961
+ decompositionMode: nextTargetMemory.decompositionMode,
6962
+ activeItemId: nextTargetMemory.activeItemId,
6732
6963
  injected: false,
6733
6964
  attemptCount: this.deps.cycleAttemptRepo.listForCycle(activeCycle.id).length
6734
6965
  } : {
@@ -6737,18 +6968,19 @@ var init_manager2 = __esm({
6737
6968
  startedAt: activeCycle.createdAt,
6738
6969
  completedAt: finishedCycle.completedAt ?? Date.now(),
6739
6970
  result: "continue",
6740
- reason: result.evaluation.reason,
6741
- guidance: result.injected ? result.injectedText : result.evaluation.guidance,
6971
+ reason: evaluation.reason,
6972
+ guidance: result.injected ? result.injectedText : evaluation.guidance,
6742
6973
  progressSummary: nextTargetMemory.progressSummary,
6743
- activeStepId: nextTargetMemory.activeStepId,
6744
- stepUpdates: result.evaluation.stepUpdates,
6974
+ decompositionMode: nextTargetMemory.decompositionMode,
6975
+ activeItemId: nextTargetMemory.activeItemId,
6976
+ itemUpdates: evaluation.itemUpdates,
6745
6977
  injected: result.injected,
6746
6978
  attemptCount: this.deps.cycleAttemptRepo.listForCycle(activeCycle.id).length
6747
6979
  };
6748
6980
  await this.deps.targetStore.appendTargetCycleRecord(workspace.path, targetId, cycleRecord);
6749
- if (result.evaluation.status === "stop") {
6981
+ if (isEvaluateStopResult(evaluation)) {
6750
6982
  await this.updateTargetMetaStatus(workspace.path, targetId, {
6751
- status: result.evaluation.stopReason === "objective_complete" ? "completed" : "cancelled",
6983
+ status: evaluation.stopReason === "objective_complete" ? "completed" : "cancelled",
6752
6984
  completedAt: finishedCycle.completedAt ?? Date.now()
6753
6985
  });
6754
6986
  }
@@ -6765,9 +6997,9 @@ var init_manager2 = __esm({
6765
6997
  const finishedSupervisor = this.attachCycles(
6766
6998
  this.withCurrentTargetState(
6767
6999
  this.deps.supervisorRepo.update(activeCycle.supervisorId, {
6768
- state: result.evaluation.status === "stop" ? "stopped" : "idle",
7000
+ state: isEvaluateStopResult(result.evaluation) ? "stopped" : "idle",
6769
7001
  completedSupervisionCount: (this.supervisors.get(activeCycle.supervisorId)?.completedSupervisionCount ?? 0) + 1,
6770
- stopReason: result.evaluation.status === "stop" ? result.evaluation.stopReason : null,
7002
+ stopReason: isEvaluateStopResult(evaluation) ? evaluation.stopReason : null,
6771
7003
  lastCycleAt: finishedCycle.completedAt,
6772
7004
  lastEvaluatedTurnId: context.lastTurnId ?? void 0,
6773
7005
  errorReason: null,
@@ -6921,23 +7153,46 @@ var init_manager2 = __esm({
6921
7153
  };
6922
7154
  }
6923
7155
  applyEvaluationToTargetMemory(memory, evaluation, injectedText, updatedAt) {
6924
- let plan = memory.plan;
6925
- if (evaluation.plan && evaluation.plan.length > 0) {
6926
- plan = evaluation.plan;
6927
- } else if (evaluation.stepUpdates?.length) {
6928
- const updates = new Map(evaluation.stepUpdates.map((step) => [step.id, step.status]));
6929
- plan = memory.plan.map(
6930
- (step) => updates.has(step.id) ? { ...step, status: updates.get(step.id) } : step
7156
+ if (isDecomposeResult(evaluation)) {
7157
+ return {
7158
+ ...memory,
7159
+ decompositionGenerated: true,
7160
+ decompositionMode: evaluation.decompositionMode,
7161
+ items: evaluation.items,
7162
+ activeItemId: evaluation.activeItemId,
7163
+ progressSummary: evaluation.progressSummary ?? memory.progressSummary,
7164
+ updatedAt
7165
+ };
7166
+ }
7167
+ if (isEvaluateStopResult(evaluation)) {
7168
+ return {
7169
+ ...memory,
7170
+ decompositionGenerated: memory.decompositionGenerated,
7171
+ decompositionMode: memory.decompositionMode,
7172
+ items: memory.items,
7173
+ activeItemId: memory.activeItemId,
7174
+ progressSummary: memory.progressSummary,
7175
+ lastGuidance: memory.lastGuidance,
7176
+ stalledCount: 0,
7177
+ updatedAt
7178
+ };
7179
+ }
7180
+ let items = memory.items;
7181
+ if (evaluation.itemUpdates?.length) {
7182
+ const updates = new Map(evaluation.itemUpdates.map((item) => [item.id, item.status]));
7183
+ items = memory.items.map(
7184
+ (item) => updates.has(item.id) ? { ...item, status: updates.get(item.id) } : item
6931
7185
  );
6932
7186
  }
6933
7187
  const progressSummary = evaluation.progressSummary ?? memory.progressSummary;
6934
- const lastGuidance = evaluation.status === "continue" ? injectedText ?? evaluation.guidance ?? memory.lastGuidance : memory.lastGuidance;
6935
- const stalledCount = evaluation.status === "continue" && !evaluation.progressSummary && !evaluation.stepUpdates?.length ? memory.stalledCount + 1 : 0;
7188
+ const lastGuidance = injectedText ?? evaluation.guidance ?? memory.lastGuidance;
7189
+ const stalledCount = !evaluation.progressSummary && !evaluation.itemUpdates?.length ? memory.stalledCount + 1 : 0;
6936
7190
  return {
6937
7191
  ...memory,
6938
- planGenerated: memory.planGenerated || Boolean(evaluation.plan?.length),
6939
- plan,
6940
- activeStepId: evaluation.activeStepId ?? memory.activeStepId,
7192
+ decompositionGenerated: memory.decompositionGenerated,
7193
+ decompositionMode: memory.decompositionMode,
7194
+ items,
7195
+ activeItemId: evaluation.activeItemId ?? memory.activeItemId,
6941
7196
  progressSummary,
6942
7197
  lastGuidance,
6943
7198
  stalledCount,
@@ -7060,10 +7315,10 @@ var init_manager2 = __esm({
7060
7315
  { supervisor, event }
7061
7316
  );
7062
7317
  }
7063
- broadcastCycle(supervisor, cycle, event) {
7318
+ broadcastCycle(supervisor, cycle, event, runtime) {
7064
7319
  this.deps.broadcaster.broadcast(
7065
7320
  Topics.supervisorCycle(supervisor.workspaceId, supervisor.sessionId),
7066
- { cycle, event }
7321
+ { cycle: runtime ? { ...cycle, runtime } : cycle, event }
7067
7322
  );
7068
7323
  }
7069
7324
  shouldRetryAttempt(error, attemptIndex, retry) {
@@ -7089,10 +7344,10 @@ var init_manager2 = __esm({
7089
7344
  if (signal?.aborted) {
7090
7345
  throw { code: "supervisor_eval_aborted", message: "Supervisor evaluator aborted" };
7091
7346
  }
7092
- await new Promise((resolve4, reject) => {
7347
+ await new Promise((resolve5, reject) => {
7093
7348
  const timer = setTimeout(() => {
7094
7349
  signal?.removeEventListener("abort", onAbort);
7095
- resolve4();
7350
+ resolve5();
7096
7351
  }, delayMs);
7097
7352
  timer.unref?.();
7098
7353
  const onAbort = () => {
@@ -7160,6 +7415,119 @@ function errorMessage(error, fallback) {
7160
7415
  }
7161
7416
  return fallback;
7162
7417
  }
7418
+ function isRecord(value) {
7419
+ return Boolean(value) && typeof value === "object";
7420
+ }
7421
+ function readNonEmptyString(value) {
7422
+ if (typeof value !== "string") {
7423
+ return void 0;
7424
+ }
7425
+ const next = value.trim();
7426
+ return next ? next : void 0;
7427
+ }
7428
+ function readStatus(value) {
7429
+ return value === "in_progress" || value === "done" || value === "pending" ? value : "pending";
7430
+ }
7431
+ function readDecompositionMode(value) {
7432
+ return value === "stage" || value === "subtarget" ? value : void 0;
7433
+ }
7434
+ function readNonNegativeInteger(value, fallback) {
7435
+ if (!Number.isSafeInteger(value) || typeof value !== "number" || value < 0) {
7436
+ return fallback;
7437
+ }
7438
+ return value;
7439
+ }
7440
+ function readTimestamp(value, fallback) {
7441
+ if (!Number.isSafeInteger(value) || typeof value !== "number") {
7442
+ return fallback;
7443
+ }
7444
+ return value;
7445
+ }
7446
+ function fallbackAcceptanceCriteria(title) {
7447
+ return [`${title} is complete`];
7448
+ }
7449
+ function normalizeItem(value, fallbackKind) {
7450
+ if (!isRecord(value)) {
7451
+ return null;
7452
+ }
7453
+ const id = readNonEmptyString(value.id);
7454
+ const title = readNonEmptyString(value.title);
7455
+ if (!id || !title) {
7456
+ return null;
7457
+ }
7458
+ const kind = readDecompositionMode(value.kind) ?? fallbackKind ?? "stage";
7459
+ const objective = readNonEmptyString(value.objective) ?? title;
7460
+ const deliverable = readNonEmptyString(value.deliverable) ?? `${title} completed`;
7461
+ const acceptanceCriteria = Array.isArray(value.acceptanceCriteria) ? value.acceptanceCriteria.flatMap((entry) => {
7462
+ const next = readNonEmptyString(entry);
7463
+ return next ? [next] : [];
7464
+ }) : [];
7465
+ return {
7466
+ id,
7467
+ kind,
7468
+ title,
7469
+ objective,
7470
+ deliverable,
7471
+ acceptanceCriteria: acceptanceCriteria.length > 0 ? acceptanceCriteria : fallbackAcceptanceCriteria(title),
7472
+ status: readStatus(value.status)
7473
+ };
7474
+ }
7475
+ function normalizeLegacyPlanItems(plan) {
7476
+ if (!Array.isArray(plan)) {
7477
+ return [];
7478
+ }
7479
+ return plan.flatMap((value) => {
7480
+ const item = normalizeItem(
7481
+ isRecord(value) ? {
7482
+ id: value.id,
7483
+ kind: "stage",
7484
+ title: value.title,
7485
+ objective: value.title,
7486
+ deliverable: `${readNonEmptyString(value.title) ?? "Legacy step"} completed`,
7487
+ acceptanceCriteria: fallbackAcceptanceCriteria(
7488
+ readNonEmptyString(value.title) ?? "Legacy step"
7489
+ ),
7490
+ status: value.status
7491
+ } : value,
7492
+ "stage"
7493
+ );
7494
+ return item ? [item] : [];
7495
+ });
7496
+ }
7497
+ function resolveActiveItemId(items, candidate) {
7498
+ const next = readNonEmptyString(candidate);
7499
+ if (next && items.some((item) => item.id === next)) {
7500
+ return next;
7501
+ }
7502
+ return items.find((item) => item.status === "in_progress")?.id ?? items.find((item) => item.status === "pending")?.id ?? items[0]?.id;
7503
+ }
7504
+ function normalizeTargetMemory(raw, targetId) {
7505
+ if (!isRecord(raw)) {
7506
+ return buildTargetMemory(targetId, 0);
7507
+ }
7508
+ const updatedAt = readTimestamp(raw.updatedAt, 0);
7509
+ const declaredMode = readDecompositionMode(raw.decompositionMode);
7510
+ let items = Array.isArray(raw.items) ? raw.items.flatMap((value) => {
7511
+ const item = normalizeItem(value, declaredMode);
7512
+ return item ? [item] : [];
7513
+ }) : [];
7514
+ let decompositionMode = declaredMode ?? items[0]?.kind;
7515
+ if (items.length === 0) {
7516
+ items = normalizeLegacyPlanItems(raw.plan);
7517
+ decompositionMode = items.length > 0 ? "stage" : void 0;
7518
+ }
7519
+ return {
7520
+ targetId: readNonEmptyString(raw.targetId) ?? targetId,
7521
+ decompositionGenerated: items.length > 0,
7522
+ decompositionMode,
7523
+ items,
7524
+ activeItemId: resolveActiveItemId(items, raw.activeItemId ?? raw.activeStepId),
7525
+ progressSummary: readNonEmptyString(raw.progressSummary),
7526
+ lastGuidance: readNonEmptyString(raw.lastGuidance),
7527
+ stalledCount: readNonNegativeInteger(raw.stalledCount, 0),
7528
+ updatedAt
7529
+ };
7530
+ }
7163
7531
  async function writeJsonIfMissing(path10, value) {
7164
7532
  try {
7165
7533
  await writeFile3(path10, JSON.stringify(value, null, 2) + "\n", {
@@ -7188,8 +7556,12 @@ function buildTargetMeta(input) {
7188
7556
  function buildTargetMemory(targetId, createdAt) {
7189
7557
  return {
7190
7558
  targetId,
7191
- planGenerated: false,
7192
- plan: [],
7559
+ decompositionGenerated: false,
7560
+ decompositionMode: void 0,
7561
+ items: [],
7562
+ activeItemId: void 0,
7563
+ progressSummary: void 0,
7564
+ lastGuidance: void 0,
7193
7565
  stalledCount: 0,
7194
7566
  updatedAt: createdAt
7195
7567
  };
@@ -7278,8 +7650,9 @@ async function readTargetMeta(workspacePath, targetId) {
7278
7650
  );
7279
7651
  }
7280
7652
  async function loadTargetMemory(workspacePath, targetId) {
7281
- return JSON.parse(
7282
- await readFile2(memoryPath(workspacePath, targetId), "utf-8")
7653
+ return normalizeTargetMemory(
7654
+ JSON.parse(await readFile2(memoryPath(workspacePath, targetId), "utf-8")),
7655
+ targetId
7283
7656
  );
7284
7657
  }
7285
7658
  async function saveTargetMemory(workspacePath, targetId, memory) {
@@ -7616,8 +7989,8 @@ var init_terminal_snapshot_buffer = __esm({
7616
7989
  if (this.pendingWriteCount === 0) {
7617
7990
  return Promise.resolve();
7618
7991
  }
7619
- return new Promise((resolve4) => {
7620
- this.drainResolvers.push(resolve4);
7992
+ return new Promise((resolve5) => {
7993
+ this.drainResolvers.push(resolve5);
7621
7994
  });
7622
7995
  }
7623
7996
  resolveDrainIfIdle() {
@@ -7626,8 +7999,8 @@ var init_terminal_snapshot_buffer = __esm({
7626
7999
  }
7627
8000
  const resolvers = this.drainResolvers;
7628
8001
  this.drainResolvers = [];
7629
- for (const resolve4 of resolvers) {
7630
- resolve4();
8002
+ for (const resolve5 of resolvers) {
8003
+ resolve5();
7631
8004
  }
7632
8005
  }
7633
8006
  requireTerminal() {
@@ -7946,10 +8319,10 @@ var init_manager3 = __esm({
7946
8319
  }
7947
8320
  return existing.promise;
7948
8321
  }
7949
- let resolve4 = () => {
8322
+ let resolve5 = () => {
7950
8323
  };
7951
8324
  const promise = new Promise((innerResolve) => {
7952
- resolve4 = innerResolve;
8325
+ resolve5 = innerResolve;
7953
8326
  });
7954
8327
  let markKillCompleted = () => {
7955
8328
  };
@@ -7962,7 +8335,7 @@ var init_manager3 = __esm({
7962
8335
  markKillCompleted,
7963
8336
  finalized: false,
7964
8337
  promise,
7965
- resolve: resolve4
8338
+ resolve: resolve5
7966
8339
  });
7967
8340
  void terminal.pty.kill(signal).finally(() => {
7968
8341
  const waiter = this.explicitCloseWaiters.get(terminalId);
@@ -8235,11 +8608,11 @@ var init_watcher = __esm({
8235
8608
  this.pendingReason = "fs_change";
8236
8609
  }
8237
8610
  const elapsed = now - this.firstDirtyTime;
8238
- const delay = Math.min(this.DEBOUNCE_MS, Math.max(0, this.MAX_WAIT_MS - elapsed));
8611
+ const delay2 = Math.min(this.DEBOUNCE_MS, Math.max(0, this.MAX_WAIT_MS - elapsed));
8239
8612
  if (this.dirtyTimer) {
8240
8613
  clearTimeout(this.dirtyTimer);
8241
8614
  }
8242
- this.dirtyTimer = setTimeout(() => this.flushDirty(), delay);
8615
+ this.dirtyTimer = setTimeout(() => this.flushDirty(), delay2);
8243
8616
  }
8244
8617
  flushDirty() {
8245
8618
  this.broadcaster?.broadcast(Topics.workspaceFsDirty(this.workspaceId), {
@@ -8837,7 +9210,8 @@ var init_fencing = __esm({
8837
9210
  });
8838
9211
 
8839
9212
  // packages/server/src/commands/terminal.ts
8840
- import { basename } from "node:path";
9213
+ import { stat as stat6 } from "node:fs/promises";
9214
+ import { basename, isAbsolute as isAbsolute3 } from "node:path";
8841
9215
  import { z as z5 } from "zod";
8842
9216
  function decodeTerminalInput(args) {
8843
9217
  if ("bytes" in args) {
@@ -8908,6 +9282,7 @@ var init_terminal = __esm({
8908
9282
  "packages/server/src/commands/terminal.ts"() {
8909
9283
  "use strict";
8910
9284
  init_src3();
9285
+ init_file_io();
8911
9286
  init_dispatch();
8912
9287
  TerminalInputActivitySchema = z5.enum(TERMINAL_INPUT_ACTIVITIES).optional();
8913
9288
  TerminalInputSchema = z5.union([
@@ -8942,20 +9317,44 @@ var init_terminal = __esm({
8942
9317
  z5.object({
8943
9318
  workspaceId: z5.string(),
8944
9319
  cols: z5.number().int().positive().optional(),
8945
- rows: z5.number().int().positive().optional()
9320
+ rows: z5.number().int().positive().optional(),
9321
+ cwdPath: z5.string().optional()
8946
9322
  }),
8947
9323
  async (args, ctx) => {
8948
9324
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
8949
9325
  if (!workspace) {
8950
9326
  throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
8951
9327
  }
9328
+ let cwd = workspace.path;
9329
+ if (args.cwdPath && args.cwdPath !== ".") {
9330
+ if (isAbsolute3(args.cwdPath)) {
9331
+ throw { code: "invalid_cwd_path", message: "cwdPath must be workspace-relative" };
9332
+ }
9333
+ let resolvedCwd;
9334
+ try {
9335
+ resolvedCwd = resolveSafe(workspace.path, args.cwdPath);
9336
+ } catch (error) {
9337
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "path_escape") {
9338
+ throw { code: "invalid_cwd_path", message: "cwdPath must be workspace-relative" };
9339
+ }
9340
+ throw error;
9341
+ }
9342
+ const cwdStats = await stat6(resolvedCwd).catch(() => null);
9343
+ if (!cwdStats) {
9344
+ throw { code: "cwd_not_found", message: `Directory not found: ${args.cwdPath}` };
9345
+ }
9346
+ if (!cwdStats.isDirectory()) {
9347
+ throw { code: "cwd_not_directory", message: `Not a directory: ${args.cwdPath}` };
9348
+ }
9349
+ cwd = resolvedCwd;
9350
+ }
8952
9351
  const shell = resolveShellCommand();
8953
9352
  const terminal = ctx.terminalMgr.create({
8954
9353
  workspaceId: args.workspaceId,
8955
9354
  kind: "shell",
8956
9355
  argv: shell.argv,
8957
9356
  title: shell.title,
8958
- cwd: workspace.path,
9357
+ cwd,
8959
9358
  cols: args.cols ?? 120,
8960
9359
  rows: args.rows ?? 30
8961
9360
  });
@@ -9641,7 +10040,7 @@ var init_hub = __esm({
9641
10040
  }
9642
10041
  }
9643
10042
  awaitBinaryPayload(clientId) {
9644
- return new Promise((resolve4, reject) => {
10043
+ return new Promise((resolve5, reject) => {
9645
10044
  const timer = setTimeout(() => {
9646
10045
  const waiters = this.pendingBinaryWaiters.get(clientId);
9647
10046
  if (!waiters) return;
@@ -9653,7 +10052,7 @@ var init_hub = __esm({
9653
10052
  }
9654
10053
  reject(new Error("Timeout waiting for terminal input binary payload"));
9655
10054
  }, BINARY_PAYLOAD_TIMEOUT_MS);
9656
- const waiter = { resolve: resolve4, reject, timer };
10055
+ const waiter = { resolve: resolve5, reject, timer };
9657
10056
  const queue = this.pendingBinaryWaiters.get(clientId);
9658
10057
  if (queue) {
9659
10058
  queue.push(waiter);
@@ -9931,10 +10330,34 @@ var init_hub = __esm({
9931
10330
  });
9932
10331
 
9933
10332
  // packages/server/src/commands/workspace.ts
9934
- import { readdir as readdir2 } from "node:fs/promises";
10333
+ import { readdir as readdir2, realpath as realpath2 } from "node:fs/promises";
9935
10334
  import { homedir as homedir2 } from "node:os";
9936
- import { join as join6 } from "node:path";
10335
+ import { isAbsolute as isAbsolute4, join as join6, resolve as resolve3 } from "node:path";
9937
10336
  import { z as z6 } from "zod";
10337
+ function resolveBrowsePath(path10) {
10338
+ const home = homedir2();
10339
+ if (!path10 || path10 === "~") {
10340
+ return home;
10341
+ }
10342
+ if (path10.startsWith("~/")) {
10343
+ return join6(home, path10.slice(2));
10344
+ }
10345
+ return isAbsolute4(path10) ? path10 : resolve3(home, path10);
10346
+ }
10347
+ async function buildRootPaths(currentPath) {
10348
+ const roots = /* @__PURE__ */ new Set(["/"]);
10349
+ const home = homedir2();
10350
+ roots.add(home);
10351
+ try {
10352
+ roots.add(await realpath2(home));
10353
+ } catch {
10354
+ }
10355
+ const currentSegments = currentPath.split("/").filter(Boolean);
10356
+ if (currentSegments.length > 0) {
10357
+ roots.add(`/${currentSegments[0]}`);
10358
+ }
10359
+ return Array.from(roots);
10360
+ }
9938
10361
  var init_workspace = __esm({
9939
10362
  "packages/server/src/commands/workspace.ts"() {
9940
10363
  "use strict";
@@ -9948,7 +10371,7 @@ var init_workspace = __esm({
9948
10371
  path: z6.string().optional()
9949
10372
  }),
9950
10373
  async (args) => {
9951
- const basePath = args.path || homedir2();
10374
+ const basePath = resolveBrowsePath(args.path);
9952
10375
  const entries = await readdir2(basePath, { withFileTypes: true });
9953
10376
  const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
9954
10377
  name: entry.name,
@@ -9957,7 +10380,8 @@ var init_workspace = __esm({
9957
10380
  return {
9958
10381
  currentPath: basePath,
9959
10382
  parentPath: basePath !== "/" ? join6(basePath, "..") : null,
9960
- directories
10383
+ directories,
10384
+ rootPaths: await buildRootPaths(basePath)
9961
10385
  };
9962
10386
  }
9963
10387
  );
@@ -10254,16 +10678,105 @@ var init_runtime_status = __esm({
10254
10678
  }
10255
10679
  });
10256
10680
 
10681
+ // packages/server/src/workspace/pane-layout.ts
10682
+ function applyPaneDisposition(layout, sessionId, disposition) {
10683
+ if (!layout) {
10684
+ return layout;
10685
+ }
10686
+ return disposition === "remove" ? removePaneBySessionId(layout, sessionId) : closePaneBySessionId(layout, sessionId);
10687
+ }
10688
+ function closePaneBySessionId(node, sessionId) {
10689
+ return replaceSessionWithDraft(node, sessionId);
10690
+ }
10691
+ function replaceSessionWithDraft(node, sessionId) {
10692
+ if (node.type === "leaf") {
10693
+ if (node.sessionId === sessionId) {
10694
+ return {
10695
+ id: node.id,
10696
+ type: "leaf"
10697
+ };
10698
+ }
10699
+ return node;
10700
+ }
10701
+ const children = node.children ?? [];
10702
+ let changed = false;
10703
+ const nextChildren = children.map((child) => {
10704
+ const nextChild = replaceSessionWithDraft(child, sessionId);
10705
+ if (nextChild !== child) {
10706
+ changed = true;
10707
+ }
10708
+ return nextChild;
10709
+ });
10710
+ if (!changed) {
10711
+ return node;
10712
+ }
10713
+ return {
10714
+ ...node,
10715
+ children: nextChildren
10716
+ };
10717
+ }
10718
+ function removePaneBySessionId(node, sessionId) {
10719
+ return removeSessionPane(node, sessionId) ?? { id: node.id, type: "leaf" };
10720
+ }
10721
+ function removeSessionPane(node, sessionId) {
10722
+ if (node.type === "leaf") {
10723
+ if (node.sessionId === sessionId) {
10724
+ return null;
10725
+ }
10726
+ return node;
10727
+ }
10728
+ const children = node.children ?? [];
10729
+ let changed = false;
10730
+ const nextChildren = [];
10731
+ for (const child of children) {
10732
+ const nextChild = removeSessionPane(child, sessionId);
10733
+ if (nextChild !== child) {
10734
+ changed = true;
10735
+ }
10736
+ if (nextChild !== null) {
10737
+ nextChildren.push(nextChild);
10738
+ }
10739
+ }
10740
+ if (!changed) {
10741
+ return node;
10742
+ }
10743
+ if (nextChildren.length === 1) {
10744
+ return nextChildren[0];
10745
+ }
10746
+ if (nextChildren.length === 0) {
10747
+ return null;
10748
+ }
10749
+ return {
10750
+ ...node,
10751
+ children: nextChildren
10752
+ };
10753
+ }
10754
+ var init_pane_layout = __esm({
10755
+ "packages/server/src/workspace/pane-layout.ts"() {
10756
+ "use strict";
10757
+ }
10758
+ });
10759
+
10257
10760
  // packages/server/src/commands/session.ts
10258
10761
  import { z as z10 } from "zod";
10762
+ function delay(ms) {
10763
+ return new Promise((resolve5) => {
10764
+ setTimeout(resolve5, ms);
10765
+ });
10766
+ }
10259
10767
  function getProviderFromRegistry(providerId, registry) {
10260
10768
  return registry.find((provider) => provider.id === providerId);
10261
10769
  }
10770
+ var SESSION_CLOSE_POLL_INTERVAL_MS, SESSION_CLOSE_TIMEOUT_MS;
10262
10771
  var init_session = __esm({
10263
10772
  "packages/server/src/commands/session.ts"() {
10264
10773
  "use strict";
10265
10774
  init_runtime_status();
10775
+ init_database();
10776
+ init_pane_layout();
10266
10777
  init_dispatch();
10778
+ SESSION_CLOSE_POLL_INTERVAL_MS = 100;
10779
+ SESSION_CLOSE_TIMEOUT_MS = 5e3;
10267
10780
  registerCommand(
10268
10781
  "session.list",
10269
10782
  z10.object({
@@ -10335,11 +10848,75 @@ var init_session = __esm({
10335
10848
  ctx.sessionMgr.delete(args.sessionId);
10336
10849
  }
10337
10850
  );
10851
+ registerCommand(
10852
+ "session.close",
10853
+ z10.object({
10854
+ sessionId: z10.string(),
10855
+ paneDisposition: z10.enum(["draft", "remove"]).default("draft")
10856
+ }),
10857
+ async (args, ctx) => {
10858
+ let session = ctx.sessionMgr.get(args.sessionId);
10859
+ if (!session) {
10860
+ throw { code: "session_not_found", message: `Session not found: ${args.sessionId}` };
10861
+ }
10862
+ if (session.state !== "ended") {
10863
+ try {
10864
+ await ctx.sessionMgr.stop(args.sessionId);
10865
+ } catch (error) {
10866
+ const candidate = error;
10867
+ throw {
10868
+ code: "session_close_failed",
10869
+ message: candidate.message ?? `Failed to stop session: ${args.sessionId}`
10870
+ };
10871
+ }
10872
+ const deadline = Date.now() + SESSION_CLOSE_TIMEOUT_MS;
10873
+ while (Date.now() < deadline) {
10874
+ session = ctx.sessionMgr.get(args.sessionId);
10875
+ if (!session) {
10876
+ return;
10877
+ }
10878
+ if (session.state === "ended") {
10879
+ break;
10880
+ }
10881
+ await delay(SESSION_CLOSE_POLL_INTERVAL_MS);
10882
+ }
10883
+ session = ctx.sessionMgr.get(args.sessionId);
10884
+ if (!session) {
10885
+ return;
10886
+ }
10887
+ if (session.state !== "ended") {
10888
+ throw {
10889
+ code: "session_close_timeout",
10890
+ message: `Timed out waiting for session to end before closing: ${args.sessionId}`
10891
+ };
10892
+ }
10893
+ }
10894
+ const workspace = ctx.workspaceMgr.get(session.workspaceId);
10895
+ if (!workspace) {
10896
+ throw {
10897
+ code: "workspace_not_found",
10898
+ message: `Workspace not found: ${session.workspaceId}`
10899
+ };
10900
+ }
10901
+ const nextUiState = {
10902
+ ...workspace.uiState,
10903
+ paneLayout: applyPaneDisposition(
10904
+ workspace.uiState.paneLayout,
10905
+ args.sessionId,
10906
+ args.paneDisposition
10907
+ )
10908
+ };
10909
+ withTransaction(ctx.db, () => {
10910
+ ctx.workspaceMgr.updateUiState(session.workspaceId, nextUiState);
10911
+ ctx.sessionMgr.delete(args.sessionId);
10912
+ });
10913
+ }
10914
+ );
10338
10915
  }
10339
10916
  });
10340
10917
 
10341
10918
  // packages/server/src/fs/tree.ts
10342
- import { readdir as readdir3, stat as stat6 } from "fs/promises";
10919
+ import { readdir as readdir3, stat as stat7 } from "fs/promises";
10343
10920
  import { join as join7, relative as relative4 } from "path";
10344
10921
  async function readTree(rootPath, subdir) {
10345
10922
  const targetPath = subdir ? join7(rootPath, subdir) : rootPath;
@@ -10361,7 +10938,7 @@ async function readTree(rootPath, subdir) {
10361
10938
  // Not loaded yet - client will request on expand
10362
10939
  });
10363
10940
  } else if (entry.isFile()) {
10364
- const stats = await stat6(fullPath);
10941
+ const stats = await stat7(fullPath);
10365
10942
  nodes.push({
10366
10943
  name: entry.name,
10367
10944
  path: relPath,
@@ -10430,7 +11007,7 @@ async function searchFiles(rootPath, query, limit = 10) {
10430
11007
  }
10431
11008
  return a.path.toLowerCase().localeCompare(b.path.toLowerCase());
10432
11009
  }).slice(0, limit)) {
10433
- const stats = await stat6(match.fullPath);
11010
+ const stats = await stat7(match.fullPath);
10434
11011
  files.push({
10435
11012
  name: match.name,
10436
11013
  path: match.path,
@@ -10597,6 +11174,27 @@ var init_file = __esm({
10597
11174
  return { ok: true };
10598
11175
  }
10599
11176
  );
11177
+ registerCommand(
11178
+ "file.rename",
11179
+ z11.object({
11180
+ workspaceId: z11.string(),
11181
+ fromPath: z11.string(),
11182
+ toPath: z11.string()
11183
+ }),
11184
+ async (args, ctx) => {
11185
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
11186
+ if (!workspace) {
11187
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
11188
+ }
11189
+ await renameEntry(workspace.path, args.fromPath, args.toPath);
11190
+ ctx.eventBus.emit({
11191
+ type: "fs.dirty",
11192
+ workspaceId: args.workspaceId,
11193
+ reason: "fs_change"
11194
+ });
11195
+ return { ok: true };
11196
+ }
11197
+ );
10600
11198
  registerCommand(
10601
11199
  "file.write",
10602
11200
  z11.object({
@@ -10778,7 +11376,7 @@ import { mkdir as mkdir4, mkdtemp as mkdtemp2, rm as rm5, writeFile as writeFile
10778
11376
  import os2 from "os";
10779
11377
  import path6 from "path";
10780
11378
  async function runGit(cwd, args, options = {}) {
10781
- return new Promise((resolve4, reject) => {
11379
+ return new Promise((resolve5, reject) => {
10782
11380
  const gitArgs = [
10783
11381
  ...options.config?.flatMap(([key, value]) => ["-c", `${key}=${value}`]) ?? [],
10784
11382
  ...args
@@ -10805,7 +11403,7 @@ async function runGit(cwd, args, options = {}) {
10805
11403
  if (err) {
10806
11404
  reject(new GitError(err.message, stderr));
10807
11405
  } else {
10808
- resolve4({ stdout, stderr });
11406
+ resolve5({ stdout, stderr });
10809
11407
  }
10810
11408
  }
10811
11409
  );
@@ -12149,20 +12747,373 @@ var init_settings2 = __esm({
12149
12747
  }
12150
12748
  });
12151
12749
 
12152
- // packages/server/src/commands/provider.ts
12750
+ // packages/server/src/commands/diagnostics.ts
12153
12751
  import { z as z14 } from "zod";
12752
+ function isLoopbackHost(host) {
12753
+ return host === void 0 || host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0";
12754
+ }
12755
+ async function resolveWorkspacePathCheck(workspacePath) {
12756
+ if (!workspacePath) {
12757
+ return {
12758
+ canContinue: false,
12759
+ checks: [
12760
+ {
12761
+ id: "workspace-selection",
12762
+ code: "workspace_selection_missing",
12763
+ status: "needs_attention"
12764
+ }
12765
+ ]
12766
+ };
12767
+ }
12768
+ const validation = await validatePath(workspacePath);
12769
+ if (validation.valid) {
12770
+ return {
12771
+ canContinue: true,
12772
+ checks: [
12773
+ {
12774
+ id: "workspace-path",
12775
+ code: "workspace_path_ready",
12776
+ status: "ready",
12777
+ workspacePath
12778
+ }
12779
+ ]
12780
+ };
12781
+ }
12782
+ return {
12783
+ canContinue: false,
12784
+ checks: [
12785
+ {
12786
+ id: "workspace-path",
12787
+ code: validation.error === "Path does not exist" ? "workspace_path_not_found" : "workspace_path_unreadable",
12788
+ status: "needs_attention",
12789
+ workspacePath
12790
+ }
12791
+ ]
12792
+ };
12793
+ }
12794
+ function buildProviderCheck(providerStatus, providerId) {
12795
+ if (providerStatus.available) {
12796
+ return {
12797
+ canContinue: true,
12798
+ checks: [
12799
+ {
12800
+ id: `provider:${providerId}`,
12801
+ code: "provider_runtime_ready",
12802
+ status: "ready",
12803
+ providerId,
12804
+ autoInstallSupported: providerStatus.autoInstallSupported,
12805
+ installReadiness: providerStatus.installReadiness
12806
+ }
12807
+ ]
12808
+ };
12809
+ }
12810
+ if (providerStatus.missingPrerequisites.length > 0) {
12811
+ return {
12812
+ canContinue: false,
12813
+ checks: [
12814
+ {
12815
+ id: `provider:${providerId}`,
12816
+ code: "provider_prerequisite_missing",
12817
+ status: "needs_attention",
12818
+ providerId,
12819
+ autoInstallSupported: providerStatus.autoInstallSupported,
12820
+ installReadiness: providerStatus.installReadiness,
12821
+ missingCommands: providerStatus.missingCommands,
12822
+ missingPrerequisites: providerStatus.missingPrerequisites,
12823
+ manualGuideKeys: providerStatus.manualGuideKeys,
12824
+ docUrl: providerStatus.docUrls.provider
12825
+ }
12826
+ ]
12827
+ };
12828
+ }
12829
+ return {
12830
+ canContinue: false,
12831
+ checks: [
12832
+ {
12833
+ id: `provider:${providerId}`,
12834
+ code: "provider_cli_missing",
12835
+ status: "needs_attention",
12836
+ providerId,
12837
+ autoInstallSupported: providerStatus.autoInstallSupported,
12838
+ installReadiness: providerStatus.installReadiness,
12839
+ missingCommands: providerStatus.missingCommands,
12840
+ missingPrerequisites: providerStatus.missingPrerequisites,
12841
+ manualGuideKeys: providerStatus.manualGuideKeys,
12842
+ docUrl: providerStatus.docUrls.provider
12843
+ }
12844
+ ]
12845
+ };
12846
+ }
12847
+ async function buildWorkspaceSelectionChecks(args, ctx) {
12848
+ if (args.workspacePath) {
12849
+ const workspaceResult = await resolveWorkspacePathCheck(args.workspacePath);
12850
+ return {
12851
+ canContinue: workspaceResult.canContinue,
12852
+ checks: workspaceResult.checks,
12853
+ workspacePath: args.workspacePath
12854
+ };
12855
+ }
12856
+ const workspace = args.workspaceId ? ctx.workspaceMgr.get(args.workspaceId) : void 0;
12857
+ if (!args.workspaceId) {
12858
+ return {
12859
+ canContinue: true,
12860
+ checks: []
12861
+ };
12862
+ }
12863
+ if (!workspace) {
12864
+ return {
12865
+ canContinue: false,
12866
+ checks: [
12867
+ {
12868
+ id: "session-workspace",
12869
+ code: "session_workspace_missing",
12870
+ status: "needs_attention",
12871
+ workspaceId: args.workspaceId
12872
+ }
12873
+ ]
12874
+ };
12875
+ }
12876
+ const pathCheck = await resolveWorkspacePathCheck(workspace.path);
12877
+ return {
12878
+ canContinue: pathCheck.canContinue,
12879
+ checks: [
12880
+ {
12881
+ id: "session-workspace",
12882
+ code: "session_workspace_ready",
12883
+ status: "ready",
12884
+ workspaceId: workspace.id,
12885
+ workspacePath: workspace.path
12886
+ },
12887
+ ...pathCheck.checks.map(
12888
+ (check) => check.id === "workspace-path" ? {
12889
+ ...check,
12890
+ id: `workspace-path:${workspace.id}`
12891
+ } : check
12892
+ )
12893
+ ],
12894
+ workspacePath: workspace.path
12895
+ };
12896
+ }
12897
+ async function buildAllProviderChecks(ctx, preferredProviderId) {
12898
+ const checks = [];
12899
+ let canContinueForPreferredProvider = preferredProviderId ? false : true;
12900
+ const runtimeStatus = await buildProviderRuntimeStatus(
12901
+ ctx.providerRegistry,
12902
+ ctx.providerRuntimeDeps
12903
+ );
12904
+ for (const provider of ctx.providerRegistry) {
12905
+ const providerStatus = runtimeStatus.providers[provider.id];
12906
+ if (!providerStatus) {
12907
+ checks.push({
12908
+ id: `provider:${provider.id}`,
12909
+ code: "provider_unknown",
12910
+ status: "needs_attention",
12911
+ providerId: provider.id
12912
+ });
12913
+ if (provider.id === preferredProviderId) {
12914
+ canContinueForPreferredProvider = false;
12915
+ }
12916
+ continue;
12917
+ }
12918
+ const providerCheck = buildProviderCheck(providerStatus, provider.id);
12919
+ checks.push(...providerCheck.checks);
12920
+ if (provider.id === preferredProviderId) {
12921
+ canContinueForPreferredProvider = providerCheck.canContinue;
12922
+ }
12923
+ }
12924
+ if (preferredProviderId && !ctx.providerRegistry.find((provider) => provider.id === preferredProviderId)) {
12925
+ checks.unshift({
12926
+ id: "session-provider",
12927
+ code: "provider_unknown",
12928
+ status: "needs_attention",
12929
+ providerId: preferredProviderId
12930
+ });
12931
+ canContinueForPreferredProvider = false;
12932
+ }
12933
+ return { checks, canContinueForPreferredProvider };
12934
+ }
12935
+ function buildServerAuthCheck(ctx) {
12936
+ return {
12937
+ id: "server-auth",
12938
+ code: ctx.config?.auth.enabled ? "server_auth_ready" : "server_auth_not_required",
12939
+ status: "ready"
12940
+ };
12941
+ }
12942
+ function buildMobileHostCheck(ctx) {
12943
+ if (isLoopbackHost(ctx.config?.host)) {
12944
+ return {
12945
+ canContinue: false,
12946
+ check: {
12947
+ id: "mobile-host",
12948
+ code: "mobile_host_local_only",
12949
+ status: "needs_attention"
12950
+ }
12951
+ };
12952
+ }
12953
+ return {
12954
+ canContinue: true,
12955
+ check: {
12956
+ id: "mobile-host",
12957
+ code: "mobile_host_ready",
12958
+ status: "ready"
12959
+ }
12960
+ };
12961
+ }
12962
+ async function buildSessionStartDiagnostics(args, ctx) {
12963
+ const workspaceSelection = await buildWorkspaceSelectionChecks(args, ctx);
12964
+ const providerChecks = await buildAllProviderChecks(ctx, args.providerId);
12965
+ const mobileHost = buildMobileHostCheck(ctx);
12966
+ const checks = [
12967
+ ...workspaceSelection.checks,
12968
+ ...providerChecks.checks,
12969
+ buildServerAuthCheck(ctx),
12970
+ mobileHost.check
12971
+ ];
12972
+ const canContinue = workspaceSelection.canContinue && providerChecks.canContinueForPreferredProvider;
12973
+ return {
12974
+ context: "session_start",
12975
+ canContinue,
12976
+ checks,
12977
+ metadata: {
12978
+ providerId: args.providerId,
12979
+ workspaceId: args.workspaceId,
12980
+ workspacePath: workspaceSelection.workspacePath,
12981
+ authEnabled: ctx.config?.auth.enabled ?? false,
12982
+ host: ctx.config?.host
12983
+ }
12984
+ };
12985
+ }
12986
+ async function buildManualDiagnostics(args, ctx) {
12987
+ const workspaceSelection = await buildWorkspaceSelectionChecks(args, ctx);
12988
+ const providerChecks = await buildAllProviderChecks(ctx, args.providerId);
12989
+ const mobileHost = buildMobileHostCheck(ctx);
12990
+ const checks = [
12991
+ ...workspaceSelection.checks,
12992
+ ...providerChecks.checks,
12993
+ buildServerAuthCheck(ctx),
12994
+ mobileHost.check
12995
+ ];
12996
+ return {
12997
+ context: "manual_check",
12998
+ canContinue: checks.every((check) => check.status !== "needs_attention"),
12999
+ checks,
13000
+ metadata: {
13001
+ authEnabled: ctx.config?.auth.enabled ?? false,
13002
+ host: ctx.config?.host,
13003
+ providerId: args.providerId,
13004
+ workspaceId: args.workspaceId,
13005
+ workspacePath: workspaceSelection.workspacePath ?? args.workspacePath
13006
+ }
13007
+ };
13008
+ }
13009
+ async function buildMobileDiagnostics(args, ctx) {
13010
+ const host = ctx.config?.host;
13011
+ const authEnabled = ctx.config?.auth.enabled ?? false;
13012
+ const workspaceSelection = await buildWorkspaceSelectionChecks(args, ctx);
13013
+ const providerChecks = await buildAllProviderChecks(ctx, args.providerId);
13014
+ const mobileHost = buildMobileHostCheck(ctx);
13015
+ const checks = [
13016
+ ...workspaceSelection.checks,
13017
+ ...providerChecks.checks,
13018
+ buildServerAuthCheck(ctx),
13019
+ mobileHost.check
13020
+ ];
13021
+ let canContinue = mobileHost.canContinue;
13022
+ if (!authEnabled) {
13023
+ canContinue = false;
13024
+ checks.push({
13025
+ id: "mobile-auth",
13026
+ code: "mobile_auth_disabled",
13027
+ status: "needs_attention"
13028
+ });
13029
+ } else {
13030
+ checks.push({
13031
+ id: "mobile-auth",
13032
+ code: "server_auth_ready",
13033
+ status: "ready"
13034
+ });
13035
+ }
13036
+ return {
13037
+ context: "mobile_continue",
13038
+ canContinue,
13039
+ checks,
13040
+ metadata: {
13041
+ authEnabled,
13042
+ host,
13043
+ workspaceId: args.workspaceId,
13044
+ workspacePath: workspaceSelection.workspacePath,
13045
+ providerId: args.providerId
13046
+ }
13047
+ };
13048
+ }
13049
+ async function buildDiagnostics(args, ctx) {
13050
+ switch (args.context) {
13051
+ case "workspace_open": {
13052
+ const workspaceSelection = await buildWorkspaceSelectionChecks(args, ctx);
13053
+ const providerChecks = await buildAllProviderChecks(ctx, args.providerId);
13054
+ const mobileHost = buildMobileHostCheck(ctx);
13055
+ return {
13056
+ context: "workspace_open",
13057
+ canContinue: workspaceSelection.canContinue,
13058
+ checks: [
13059
+ ...workspaceSelection.checks,
13060
+ ...providerChecks.checks,
13061
+ buildServerAuthCheck(ctx),
13062
+ mobileHost.check
13063
+ ],
13064
+ metadata: {
13065
+ workspacePath: workspaceSelection.workspacePath ?? args.workspacePath,
13066
+ authEnabled: ctx.config?.auth.enabled ?? false,
13067
+ host: ctx.config?.host,
13068
+ providerId: args.providerId,
13069
+ workspaceId: args.workspaceId
13070
+ }
13071
+ };
13072
+ }
13073
+ case "session_start":
13074
+ return buildSessionStartDiagnostics(args, ctx);
13075
+ case "mobile_continue":
13076
+ return buildMobileDiagnostics(args, ctx);
13077
+ case "manual_check":
13078
+ return buildManualDiagnostics(args, ctx);
13079
+ }
13080
+ }
13081
+ var DiagnosticsRequestSchema;
13082
+ var init_diagnostics2 = __esm({
13083
+ "packages/server/src/commands/diagnostics.ts"() {
13084
+ "use strict";
13085
+ init_runtime_status();
13086
+ init_validator();
13087
+ init_dispatch();
13088
+ DiagnosticsRequestSchema = z14.object({
13089
+ context: z14.enum(["workspace_open", "session_start", "mobile_continue", "manual_check"]),
13090
+ workspaceId: z14.string().optional(),
13091
+ workspacePath: z14.string().optional(),
13092
+ providerId: z14.string().optional()
13093
+ });
13094
+ registerCommand("diagnostics.get", DiagnosticsRequestSchema, async (args, ctx) => {
13095
+ return buildDiagnostics(args, ctx);
13096
+ });
13097
+ registerCommand("diagnostics.recheck", DiagnosticsRequestSchema, async (args, ctx) => {
13098
+ return buildDiagnostics(args, ctx);
13099
+ });
13100
+ }
13101
+ });
13102
+
13103
+ // packages/server/src/commands/provider.ts
13104
+ import { z as z15 } from "zod";
12154
13105
  var init_provider = __esm({
12155
13106
  "packages/server/src/commands/provider.ts"() {
12156
13107
  "use strict";
12157
13108
  init_runtime_status();
12158
13109
  init_dispatch();
12159
- registerCommand("provider.runtimeStatus", z14.object({}), async (_args, ctx) => {
13110
+ registerCommand("provider.runtimeStatus", z15.object({}), async (_args, ctx) => {
12160
13111
  return buildProviderRuntimeStatus(ctx.providerRegistry, ctx.providerRuntimeDeps);
12161
13112
  });
12162
13113
  registerCommand(
12163
13114
  "provider.install.start",
12164
- z14.object({
12165
- providerId: z14.string()
13115
+ z15.object({
13116
+ providerId: z15.string()
12166
13117
  }),
12167
13118
  async (args, ctx) => {
12168
13119
  if (!ctx.providerInstallMgr) {
@@ -12176,8 +13127,8 @@ var init_provider = __esm({
12176
13127
  );
12177
13128
  registerCommand(
12178
13129
  "provider.install.get",
12179
- z14.object({
12180
- jobId: z14.string()
13130
+ z15.object({
13131
+ jobId: z15.string()
12181
13132
  }),
12182
13133
  async (args, ctx) => {
12183
13134
  if (!ctx.providerInstallMgr) {
@@ -12200,35 +13151,35 @@ var init_provider = __esm({
12200
13151
  });
12201
13152
 
12202
13153
  // packages/server/src/commands/supervisor.ts
12203
- import { z as z15 } from "zod";
13154
+ import { z as z16 } from "zod";
12204
13155
  var supervisorObjectiveSchema, createSupervisorSchema, updateSupervisorSchema, sessionIdSchema, supervisorIdSchema;
12205
13156
  var init_supervisor2 = __esm({
12206
13157
  "packages/server/src/commands/supervisor.ts"() {
12207
13158
  "use strict";
12208
13159
  init_dispatch();
12209
- supervisorObjectiveSchema = z15.string().trim().min(1).max(4e3);
12210
- createSupervisorSchema = z15.object({
12211
- sessionId: z15.string(),
12212
- workspaceId: z15.string(),
13160
+ supervisorObjectiveSchema = z16.string().trim().min(1).max(4e3);
13161
+ createSupervisorSchema = z16.object({
13162
+ sessionId: z16.string(),
13163
+ workspaceId: z16.string(),
12213
13164
  objective: supervisorObjectiveSchema,
12214
- evaluatorProviderId: z15.string(),
12215
- evaluatorModel: z15.string().trim().min(1).max(200).optional(),
12216
- maxSupervisionCount: z15.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
12217
- scheduledAt: z15.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional()
13165
+ evaluatorProviderId: z16.string(),
13166
+ evaluatorModel: z16.string().trim().min(1).max(200).optional(),
13167
+ maxSupervisionCount: z16.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
13168
+ scheduledAt: z16.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional()
12218
13169
  }).strict();
12219
- updateSupervisorSchema = z15.object({
12220
- id: z15.string(),
13170
+ updateSupervisorSchema = z16.object({
13171
+ id: z16.string(),
12221
13172
  objective: supervisorObjectiveSchema.optional(),
12222
- evaluatorProviderId: z15.string().optional(),
12223
- evaluatorModel: z15.string().trim().min(1).max(200).nullable().optional(),
12224
- maxSupervisionCount: z15.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
12225
- scheduledAt: z15.number().int().min(0).max(Number.MAX_SAFE_INTEGER).nullable().optional()
13173
+ evaluatorProviderId: z16.string().optional(),
13174
+ evaluatorModel: z16.string().trim().min(1).max(200).nullable().optional(),
13175
+ maxSupervisionCount: z16.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),
13176
+ scheduledAt: z16.number().int().min(0).max(Number.MAX_SAFE_INTEGER).nullable().optional()
12226
13177
  }).strict().refine(
12227
13178
  (input) => input.objective !== void 0 || input.evaluatorProviderId !== void 0 || input.evaluatorModel !== void 0 || input.maxSupervisionCount !== void 0 || input.scheduledAt !== void 0,
12228
13179
  "at least one supervisor field is required"
12229
13180
  );
12230
- sessionIdSchema = z15.object({ sessionId: z15.string() });
12231
- supervisorIdSchema = z15.object({ id: z15.string() });
13181
+ sessionIdSchema = z16.object({ sessionId: z16.string() });
13182
+ supervisorIdSchema = z16.object({ id: z16.string() });
12232
13183
  registerCommand("supervisor.create", createSupervisorSchema, async (args, ctx) => {
12233
13184
  return {
12234
13185
  supervisor: await ctx.supervisorMgr.create({
@@ -12412,7 +13363,7 @@ var init_worktree = __esm({
12412
13363
 
12413
13364
  // packages/server/src/commands/worktree.ts
12414
13365
  import path9 from "node:path";
12415
- import { z as z16 } from "zod";
13366
+ import { z as z17 } from "zod";
12416
13367
  async function findRelatedWorkspaceIds(ctx, workspacePath) {
12417
13368
  const targetCommonDir = await getGitCommonDirPath(workspacePath);
12418
13369
  const relatedWorkspaceIds = await Promise.all(
@@ -12445,7 +13396,7 @@ var init_worktree2 = __esm({
12445
13396
  init_worktree();
12446
13397
  init_dispatch();
12447
13398
  init_git_events();
12448
- registerCommand("worktree.list", z16.object({ workspaceId: z16.string() }), async (args, ctx) => {
13399
+ registerCommand("worktree.list", z17.object({ workspaceId: z17.string() }), async (args, ctx) => {
12449
13400
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
12450
13401
  if (!workspace) {
12451
13402
  throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
@@ -12454,7 +13405,7 @@ var init_worktree2 = __esm({
12454
13405
  });
12455
13406
  registerCommand(
12456
13407
  "worktree.status",
12457
- z16.object({ workspaceId: z16.string(), worktreePath: z16.string() }),
13408
+ z17.object({ workspaceId: z17.string(), worktreePath: z17.string() }),
12458
13409
  async (args, ctx) => {
12459
13410
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
12460
13411
  if (!workspace) {
@@ -12466,10 +13417,10 @@ var init_worktree2 = __esm({
12466
13417
  );
12467
13418
  registerCommand(
12468
13419
  "worktree.diff",
12469
- z16.object({
12470
- workspaceId: z16.string(),
12471
- worktreePath: z16.string(),
12472
- staged: z16.boolean().optional().default(false)
13420
+ z17.object({
13421
+ workspaceId: z17.string(),
13422
+ worktreePath: z17.string(),
13423
+ staged: z17.boolean().optional().default(false)
12473
13424
  }),
12474
13425
  async (args, ctx) => {
12475
13426
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -12482,7 +13433,7 @@ var init_worktree2 = __esm({
12482
13433
  );
12483
13434
  registerCommand(
12484
13435
  "worktree.tree",
12485
- z16.object({ workspaceId: z16.string(), worktreePath: z16.string() }),
13436
+ z17.object({ workspaceId: z17.string(), worktreePath: z17.string() }),
12486
13437
  async (args, ctx) => {
12487
13438
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
12488
13439
  if (!workspace) {
@@ -12494,10 +13445,10 @@ var init_worktree2 = __esm({
12494
13445
  );
12495
13446
  registerCommand(
12496
13447
  "worktree.create",
12497
- z16.object({
12498
- workspaceId: z16.string(),
12499
- branch: z16.string(),
12500
- path: z16.string()
13448
+ z17.object({
13449
+ workspaceId: z17.string(),
13450
+ branch: z17.string(),
13451
+ path: z17.string()
12501
13452
  }),
12502
13453
  async (args, ctx) => {
12503
13454
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -12512,10 +13463,10 @@ var init_worktree2 = __esm({
12512
13463
  );
12513
13464
  registerCommand(
12514
13465
  "worktree.remove",
12515
- z16.object({
12516
- workspaceId: z16.string(),
12517
- worktreePath: z16.string(),
12518
- force: z16.boolean().optional().default(false)
13466
+ z17.object({
13467
+ workspaceId: z17.string(),
13468
+ worktreePath: z17.string(),
13469
+ force: z17.boolean().optional().default(false)
12519
13470
  }),
12520
13471
  async (args, ctx) => {
12521
13472
  const workspace = ctx.workspaceMgr.get(args.workspaceId);
@@ -12539,7 +13490,7 @@ var init_worktree2 = __esm({
12539
13490
  });
12540
13491
 
12541
13492
  // packages/server/src/commands/fencing.ts
12542
- import { z as z17 } from "zod";
13493
+ import { z as z18 } from "zod";
12543
13494
  function createMockFencingRequest() {
12544
13495
  return {
12545
13496
  ip: "127.0.0.1",
@@ -12552,9 +13503,9 @@ var init_fencing2 = __esm({
12552
13503
  init_dispatch();
12553
13504
  registerCommand(
12554
13505
  "fencing.request",
12555
- z17.object({
12556
- workspaceId: z17.string(),
12557
- tabId: z17.string()
13506
+ z18.object({
13507
+ workspaceId: z18.string(),
13508
+ tabId: z18.string()
12558
13509
  }),
12559
13510
  async (args, ctx, clientId) => {
12560
13511
  return ctx.fencingMgr.requestControl(
@@ -12567,7 +13518,7 @@ var init_fencing2 = __esm({
12567
13518
  );
12568
13519
  registerCommand(
12569
13520
  "fencing.heartbeat",
12570
- z17.object({ workspaceId: z17.string() }),
13521
+ z18.object({ workspaceId: z18.string() }),
12571
13522
  async (args, ctx, clientId) => {
12572
13523
  const success = ctx.fencingMgr.heartbeat(args.workspaceId, clientId);
12573
13524
  return { success };
@@ -12575,13 +13526,13 @@ var init_fencing2 = __esm({
12575
13526
  );
12576
13527
  registerCommand(
12577
13528
  "fencing.release",
12578
- z17.object({ workspaceId: z17.string() }),
13529
+ z18.object({ workspaceId: z18.string() }),
12579
13530
  async (args, ctx, clientId) => {
12580
13531
  ctx.fencingMgr.release(args.workspaceId, clientId);
12581
13532
  return {};
12582
13533
  }
12583
13534
  );
12584
- registerCommand("fencing.status", z17.object({ workspaceId: z17.string() }), async (args, ctx) => {
13535
+ registerCommand("fencing.status", z18.object({ workspaceId: z18.string() }), async (args, ctx) => {
12585
13536
  const controller = ctx.fencingMgr.getController(args.workspaceId);
12586
13537
  const isUnresponsive = ctx.fencingMgr.isControllerUnresponsive(args.workspaceId);
12587
13538
  return {
@@ -12592,9 +13543,9 @@ var init_fencing2 = __esm({
12592
13543
  });
12593
13544
  registerCommand(
12594
13545
  "fencing.takeover",
12595
- z17.object({
12596
- workspaceId: z17.string(),
12597
- tabId: z17.string()
13546
+ z18.object({
13547
+ workspaceId: z18.string(),
13548
+ tabId: z18.string()
12598
13549
  }),
12599
13550
  async (args, ctx, clientId) => {
12600
13551
  return ctx.fencingMgr.forceTakeover(
@@ -12621,6 +13572,7 @@ var init_commands = __esm({
12621
13572
  init_file();
12622
13573
  init_git2();
12623
13574
  init_settings2();
13575
+ init_diagnostics2();
12624
13576
  init_provider();
12625
13577
  init_supervisor2();
12626
13578
  init_worktree2();
@@ -12764,7 +13716,8 @@ async function createServer(configOverrides) {
12764
13716
  autoFetch,
12765
13717
  providerRuntimeDeps,
12766
13718
  providerInstallMgr,
12767
- activationMgr
13719
+ activationMgr,
13720
+ config
12768
13721
  };
12769
13722
  wsHub.setCommandContext(commandContext);
12770
13723
  await app.listen({
@@ -13269,11 +14222,11 @@ function readCliConfig() {
13269
14222
 
13270
14223
  // packages/cli/src/embed.ts
13271
14224
  import { existsSync as existsSync8 } from "fs";
13272
- import { dirname as dirname6, resolve as resolve3 } from "path";
14225
+ import { dirname as dirname6, resolve as resolve4 } from "path";
13273
14226
  import { fileURLToPath } from "url";
13274
14227
  var __filename = fileURLToPath(import.meta.url);
13275
14228
  var __dirname = dirname6(__filename);
13276
- var WEB_ASSETS_DIR = resolve3(__dirname, "../web");
14229
+ var WEB_ASSETS_DIR = resolve4(__dirname, "../web");
13277
14230
  function getStaticAssetsDir() {
13278
14231
  return WEB_ASSETS_DIR;
13279
14232
  }