@adhdev/daemon-core 0.9.82-rc.77 → 0.9.82-rc.78

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.mjs CHANGED
@@ -1128,25 +1128,42 @@ ${userInstruction}`);
1128
1128
  return sections.join("\n\n");
1129
1129
  }
1130
1130
  function buildNodeStatusSection(nodes) {
1131
- const lines = ["## Current Node Status", ""];
1131
+ const lines = [
1132
+ "## Current Node Status",
1133
+ "",
1134
+ "Node labels are display context, not aliases. Use exact `nodeId` values in mesh tool calls; do not invent shorthand names such as M1/M2 unless they are explicitly configured labels.",
1135
+ ""
1136
+ ];
1132
1137
  for (const n of nodes) {
1133
1138
  const healthIcon = n.health === "online" ? "\u{1F7E2}" : n.health === "dirty" ? "\u{1F7E1}" : n.health === "offline" ? "\u26AB" : "\u{1F534}";
1134
1139
  const sessions = n.activeSessions.length > 0 ? `sessions: ${n.activeSessions.join(", ")}` : "no active sessions";
1135
1140
  const branch = n.git?.branch ? `branch: \`${n.git.branch}\`` : "";
1136
- lines.push(`- ${healthIcon} **${n.machineLabel}** (${n.nodeId})`);
1137
- lines.push(` workspace: \`${n.workspace}\` | ${branch} | ${sessions}`);
1141
+ const context = [
1142
+ n.daemonId ? `daemon: \`${n.daemonId}\`` : "",
1143
+ n.providers?.length ? `providers: ${n.providers.join(", ")}` : ""
1144
+ ].filter(Boolean).join(" | ");
1145
+ lines.push(`- ${healthIcon} **${n.machineLabel}** (nodeId: \`${n.nodeId}\`)`);
1146
+ lines.push(` workspace: \`${n.workspace}\`${context ? ` | ${context}` : ""} | ${branch} | ${sessions}`);
1138
1147
  if (n.error) lines.push(` \u26A0\uFE0F ${n.error}`);
1139
1148
  }
1140
1149
  return lines.join("\n");
1141
1150
  }
1142
1151
  function buildNodeConfigSection(mesh) {
1143
- const lines = ["## Configured Nodes", ""];
1152
+ const lines = [
1153
+ "## Configured Nodes",
1154
+ "",
1155
+ "Node labels are display context, not aliases. Use exact `nodeId` values in mesh tool calls; do not invent shorthand names such as M1/M2 unless they are explicitly configured labels.",
1156
+ ""
1157
+ ];
1144
1158
  for (const n of mesh.nodes) {
1145
1159
  const labels = [];
1146
1160
  if (n.isLocalWorktree) labels.push("worktree");
1147
1161
  if (n.policy?.readOnly) labels.push("read-only");
1148
1162
  const suffix = labels.length ? ` [${labels.join(", ")}]` : "";
1149
- lines.push(`- **${n.workspace}** (${n.id})${suffix}`);
1163
+ const explicitMachineLabel = typeof n.machineLabel === "string" ? n.machineLabel : "";
1164
+ const explicitLabel = explicitMachineLabel ? ` label: **${explicitMachineLabel}** |` : "";
1165
+ const providerPriority = n.policy?.providerPriority?.length ? ` | providers: ${n.policy.providerPriority.join(", ")}` : "";
1166
+ lines.push(`- ${explicitLabel} nodeId: \`${n.id}\` | workspace: \`${n.workspace}\`${n.daemonId ? ` | daemon: \`${n.daemonId}\`` : ""}${providerPriority}${suffix}`);
1150
1167
  }
1151
1168
  lines.push("", "_Use `mesh_status` to probe live health before delegating work._");
1152
1169
  return lines.join("\n");
@@ -1187,7 +1204,7 @@ function buildRulesSection(coordinatorCliType) {
1187
1204
  - **Clean up worktree nodes.** After a worktree task completes and its changes are merged or checkpointed, call \`mesh_remove_node\` to free resources.
1188
1205
  - **Do not strand completed branches.** A checkpointed or clean feature/worktree branch is not done by itself. Merge/refine it to the mesh default branch, fast-forward obvious clean behind-only branches with \`mesh_fast_forward_node\`, or explicitly report one of \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\` with the next action.
1189
1206
  - **Keep Refinery validation project-configurable.** \`mesh_refine_node\` must execute validation from repo mesh/refine config (for example \`.adhdev/refine.{json,yaml,yml}\`, \`.adhdev/repo-mesh-refine.*\`, or \`repo-mesh.refine.*\`). Heuristics are suggestions/scaffolding only, not the execution path.
1190
- - **Treat submodule reachability as publish-needed.** A \`submodule_reachability_failed\` refine result means the root gitlink points at a submodule commit that is not reachable from the configured submodule remote. Do not retry validation blindly or start code review first. Classify it as \`blocked_review\`, request user approval to push/publish the submodule commit, then rerun \`mesh_refine_node\`.
1207
+ - **Treat submodule main reachability as publish-needed.** A \`submodule_reachability_failed\` refine result means the root gitlink points at a submodule commit that is not reachable from the configured submodule remote main branch. Do not treat feature-branch reachability as complete, retry validation blindly, or start code review first. Classify it as \`blocked_review\`, request user approval to push/publish the submodule commit to submodule main, then rerun \`mesh_refine_node\`.
1191
1208
  - **Name worktree branches meaningfully.** Use descriptive names like \`feat/auth-refactor\` or \`fix/build-123\`.${coordinatorNote}`;
1192
1209
  }
1193
1210
  var TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION;
@@ -1234,7 +1251,7 @@ Before doing any coordinator work, confirm that the actual callable tool list in
1234
1251
  4. **Monitor** \u2014 Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Use \`mesh_view_queue\` to see the status of all pending, assigned, completed, and failed tasks. Do not call \`mesh_read_chat\` again within a few seconds for the same generating session. Use at most one compact \`mesh_read_chat\` check after a completion/approval signal. Handle approvals via \`mesh_approve\`.
1235
1252
  5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
1236
1253
  6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
1237
- 7. **Converge branches** \u2014 Before marking any task complete, classify every touched node/branch into exactly one final state: \`merged_to_main\`, \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\`. Use \`mesh_status\` branchConvergenceSummary. For obvious clean branch catch-up (ahead 0, behind > 0, upstream fresh, no dirty/stash/submodule issues), use \`mesh_fast_forward_node\` dry-run first and execute only when explicitly safe/approved; this avoids consuming an agent session. Use \`mesh_refine_node\` for clean worktree branches when safe. Before/refine merging root commits that contain submodule gitlink changes, require each submodule commit to be reachable from its configured remote. If \`mesh_refine_node\` returns \`submodule_reachability_failed\` or publish-required evidence, keep the public convergence bucket as \`blocked_review\`, ask the user for explicit approval to push/publish the unreachable submodule commit(s), then rerun \`mesh_refine_node\`; do not merge the root branch until the submodule commit(s) are reachable. A task that remains on a non-main branch is not fully complete unless the final report names the follow-up state and next step.
1254
+ 7. **Converge branches** \u2014 Before marking any task complete, classify every touched node/branch into exactly one final state: \`merged_to_main\`, \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\`. Use \`mesh_status\` branchConvergenceSummary. For obvious clean branch catch-up (ahead 0, behind > 0, upstream fresh, no dirty/stash/submodule issues), use \`mesh_fast_forward_node\` dry-run first and execute only when explicitly safe/approved; this avoids consuming an agent session. Use \`mesh_refine_node\` for clean worktree branches when safe. Before/refine merging root commits that contain submodule gitlink changes, require each submodule commit to be reachable from the configured submodule remote main branch, not merely present on a feature ref or local checkout. If \`mesh_refine_node\` returns \`submodule_reachability_failed\` or publish-required evidence, keep the public convergence bucket as \`blocked_review\`, ask the user for explicit approval to push/publish the unreachable submodule commit(s) to submodule main, then rerun \`mesh_refine_node\`; do not merge the root branch until the submodule commit(s) are reachable from submodule origin/main. A task that remains on a non-main branch is not fully complete unless the final report names the follow-up state and next step.
1238
1255
  8. **Clean up** \u2014 Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
1239
1256
  9. **Report** \u2014 Summarize what was done, what changed, any issues, and the branch convergence state.
1240
1257
 
@@ -3061,8 +3078,20 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
3061
3078
  const result = readRecord(args.metadataEvent.result);
3062
3079
  const code = readNonEmptyString2(result?.code);
3063
3080
  const error = readNonEmptyString2(result?.error);
3064
- const details = [jobId ? `job_id=${jobId}` : "", code ? `code=${code}` : ""].filter(Boolean).join("; ");
3065
- return `[System] Refinery async job for ${args.nodeLabel} failed${details ? ` (${details})` : ""}${error ? `: ${error}` : "."} Review the terminal refine event/ledger before retrying.`;
3081
+ const convergenceStatus = readNonEmptyString2(result?.convergenceStatus);
3082
+ const blockedReason = readNonEmptyString2(result?.blockedReason);
3083
+ const nextStep = readNonEmptyString2(result?.nextStep) || readNonEmptyString2(readRecord(result?.finalBranchConvergenceState)?.nextStep);
3084
+ const details = [
3085
+ jobId ? `job_id=${jobId}` : "",
3086
+ code ? `code=${code}` : "",
3087
+ convergenceStatus ? `convergence=${convergenceStatus}` : "",
3088
+ blockedReason ? `reason=${blockedReason}` : ""
3089
+ ].filter(Boolean).join("; ");
3090
+ const parts = [
3091
+ `[System] Refinery async job for ${args.nodeLabel} failed${details ? ` (${details})` : ""}${error ? `: ${error}` : "."}`,
3092
+ nextStep ? `Next step: ${nextStep}` : "Review the terminal refine event/ledger before retrying."
3093
+ ];
3094
+ return parts.join("\n");
3066
3095
  }
3067
3096
  return "";
3068
3097
  }
@@ -3184,7 +3213,8 @@ function injectMeshSystemMessage(components, args) {
3184
3213
  remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
3185
3214
  }
3186
3215
  if (sessionId) {
3187
- updateSessionTaskStatus(args.meshId, sessionId, "failed");
3216
+ const failedTask = updateSessionTaskStatus(args.meshId, sessionId, "failed");
3217
+ completedTaskForLedger = failedTask ? { id: failedTask.id } : null;
3188
3218
  }
3189
3219
  }
3190
3220
  const ledgerKind = EVENT_TO_LEDGER_KIND[args.event];
@@ -9020,9 +9050,11 @@ function elapsedSince(value, now) {
9020
9050
  return Number.isFinite(started) ? Math.max(0, now - started) : 0;
9021
9051
  }
9022
9052
  function sessionStatusFromNodes(nodes, nodeId, sessionId) {
9023
- if (!nodeId || !sessionId || !Array.isArray(nodes)) return void 0;
9053
+ if (!Array.isArray(nodes)) return {};
9054
+ if (!nodeId) return { staleReason: "direct task has no node id" };
9024
9055
  const node = nodes.find((item) => readString2(item?.id) === nodeId || readString2(item?.nodeId) === nodeId || readString2(item?.node_id) === nodeId);
9025
- if (!node) return void 0;
9056
+ if (!node) return { staleReason: "direct task node is no longer in the live mesh" };
9057
+ if (!sessionId) return {};
9026
9058
  const candidates = [];
9027
9059
  for (const value of [node.sessions, node.activeSessions, node.active_sessions, node.lastProbe?.sessions, node.last_probe?.sessions, node.lastProbe?.status?.sessions, node.last_probe?.status?.sessions]) {
9028
9060
  if (Array.isArray(value)) candidates.push(...value);
@@ -9031,16 +9063,18 @@ function sessionStatusFromNodes(nodes, nodeId, sessionId) {
9031
9063
  if (value && typeof value === "object") candidates.push(value);
9032
9064
  }
9033
9065
  const session = candidates.find((item) => {
9066
+ if (typeof item === "string") return item === sessionId;
9034
9067
  const id = readString2(item?.id) || readString2(item?.sessionId) || readString2(item?.session_id) || readString2(item?.runtimeSessionId) || readString2(item?.instanceId);
9035
9068
  return id === sessionId;
9036
9069
  });
9037
- if (!session) return void 0;
9070
+ if (!session) return { staleReason: "direct task session is not present in live session records" };
9071
+ if (typeof session === "string") return {};
9038
9072
  const raw = `${readString2(session.status) || ""} ${readString2(session.lifecycle) || ""} ${readString2(session.state) || ""} ${readString2(session.activeChat?.status) || ""}`.toLowerCase();
9039
- if (raw.includes("approval")) return "awaiting_approval";
9040
- if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return "generating";
9041
- if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return "failed";
9042
- if (raw.includes("idle") || raw.includes("waiting_input") || raw.includes("ready")) return "idle";
9043
- return void 0;
9073
+ if (raw.includes("approval")) return { status: "awaiting_approval" };
9074
+ if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
9075
+ if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
9076
+ if (raw.includes("idle") || raw.includes("waiting_input") || raw.includes("ready")) return { status: "idle" };
9077
+ return {};
9044
9078
  }
9045
9079
  function isDirectDispatch(entry) {
9046
9080
  if (entry.kind !== "task_dispatched") return false;
@@ -9087,15 +9121,17 @@ function buildMeshActiveWorkSummary(activeWork) {
9087
9121
  failedCount: statusCounts.failed,
9088
9122
  idleCount: statusCounts.idle,
9089
9123
  sourceCounts,
9090
- statusCounts
9124
+ statusCounts,
9125
+ staleDirectCount: activeWork.filter((item) => item.source === "direct" && item.staleReason).length
9091
9126
  };
9092
9127
  }
9093
9128
  function buildMeshActiveWork(opts) {
9094
9129
  const now = opts.now ?? Date.now();
9095
9130
  const records = [];
9131
+ const staleDirectWork = [];
9096
9132
  for (const task of opts.queue || []) {
9097
9133
  if (task.status !== "pending" && task.status !== "assigned") continue;
9098
- const { title, summary } = summarizeMessage(task.message || "");
9134
+ const { title, summary: summary2 } = summarizeMessage(task.message || "");
9099
9135
  records.push({
9100
9136
  taskId: task.id,
9101
9137
  source: "queue",
@@ -9103,7 +9139,7 @@ function buildMeshActiveWork(opts) {
9103
9139
  nodeId: task.assignedNodeId || task.targetNodeId,
9104
9140
  sessionId: task.assignedSessionId || task.targetSessionId,
9105
9141
  taskTitle: title,
9106
- taskSummary: summary,
9142
+ taskSummary: summary2,
9107
9143
  message: task.message,
9108
9144
  taskMode: task.taskMode,
9109
9145
  createdAt: task.createdAt,
@@ -9118,13 +9154,13 @@ function buildMeshActiveWork(opts) {
9118
9154
  const taskId = directDispatchTaskId(dispatch);
9119
9155
  const terminal = terminals.filter((entry) => new Date(entry.timestamp).getTime() >= new Date(dispatch.timestamp).getTime()).find((entry) => terminalMatchesDispatch(entry, dispatch, taskId));
9120
9156
  const terminalStatus = terminal ? statusFromTerminal(terminal) : void 0;
9121
- const liveStatus = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
9122
- const status = terminalStatus || liveStatus || "assigned";
9157
+ const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
9158
+ const status = terminalStatus || live.status || "assigned";
9123
9159
  const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
9124
9160
  if (terminalRow && opts.includeTerminalDirect !== true) continue;
9125
9161
  const message = readString2(dispatch.payload?.message) || readString2(dispatch.payload?.summary) || "";
9126
- const { title, summary } = summarizeMessage(message);
9127
- records.push({
9162
+ const { title, summary: summary2 } = summarizeMessage(message);
9163
+ const record = {
9128
9164
  taskId,
9129
9165
  source: "direct",
9130
9166
  status,
@@ -9132,7 +9168,7 @@ function buildMeshActiveWork(opts) {
9132
9168
  sessionId: dispatch.sessionId,
9133
9169
  providerType: dispatch.providerType || readString2(dispatch.payload?.providerType),
9134
9170
  taskTitle: readString2(dispatch.payload?.taskTitle) || title,
9135
- taskSummary: readString2(dispatch.payload?.taskSummary) || summary,
9171
+ taskSummary: readString2(dispatch.payload?.taskSummary) || summary2,
9136
9172
  message,
9137
9173
  taskMode: readString2(dispatch.payload?.taskMode),
9138
9174
  createdAt: dispatch.timestamp,
@@ -9141,11 +9177,20 @@ function buildMeshActiveWork(opts) {
9141
9177
  elapsedMs: elapsedSince(dispatch.timestamp, now),
9142
9178
  terminal: terminalRow,
9143
9179
  terminalKind: terminal?.kind,
9144
- terminalAt: terminal?.timestamp
9145
- });
9180
+ terminalAt: terminal?.timestamp,
9181
+ staleReason: live.staleReason
9182
+ };
9183
+ if (live.staleReason && !terminalRow) {
9184
+ staleDirectWork.push(record);
9185
+ continue;
9186
+ }
9187
+ records.push(record);
9146
9188
  }
9147
9189
  records.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
9148
- return { activeWork: records, summary: buildMeshActiveWorkSummary(records) };
9190
+ staleDirectWork.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
9191
+ const summary = buildMeshActiveWorkSummary(records);
9192
+ summary.staleDirectCount = staleDirectWork.length;
9193
+ return { activeWork: records, staleDirectWork, summary };
9149
9194
  }
9150
9195
 
9151
9196
  // src/index.ts
@@ -19432,23 +19477,28 @@ var CliProviderInstance = class {
19432
19477
  if (hasNonEmptyCliModalButtons(parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
19433
19478
  return !this.hasAdapterPendingResponse();
19434
19479
  }
19435
- getCompletedFinalizationBlockReason(latestVisibleStatus) {
19436
- if (latestVisibleStatus !== "idle") return `status:${latestVisibleStatus}`;
19480
+ getCompletedFinalizationBlock(latestVisibleStatus) {
19481
+ if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
19437
19482
  const adapterAny = this.adapter;
19438
- if (adapterAny?.isWaitingForResponse === true) return "adapter_waiting_for_response";
19439
- if (adapterAny?.currentTurnScope) return "adapter_turn_scope_active";
19483
+ if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
19484
+ if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
19485
+ if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
19440
19486
  const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
19441
- if (typeof partial === "string" && partial.trim()) return "partial_response_pending";
19487
+ if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
19442
19488
  let parsed;
19443
19489
  try {
19444
19490
  parsed = this.adapter.getScriptParsedStatus();
19445
19491
  } catch (error) {
19446
- return `parse_error:${error?.message || String(error)}`;
19492
+ return { reason: `parse_error:${error?.message || String(error)}` };
19447
19493
  }
19448
19494
  const parsedStatus = typeof parsed?.status === "string" ? parsed.status : "unknown";
19449
- if (parsedStatus !== "idle") return `parsed_status:${parsedStatus}`;
19450
- if (parsed?.activeModal || parsed?.modal) return "parsed_modal_active";
19451
- if (!this.completionHasFinalAssistantMessage(parsed?.messages)) return "missing_final_assistant";
19495
+ if (parsedStatus !== "idle") {
19496
+ const adapterStatus = this.adapter.getStatus({ allowParse: false });
19497
+ if (this.shouldSuppressStaleParsedBusyStatus(parsed, adapterStatus)) return null;
19498
+ return { reason: `parsed_status:${parsedStatus}`, terminal: isCliGeneratingLikeStatus(parsedStatus) };
19499
+ }
19500
+ if (parsed?.activeModal || parsed?.modal) return { reason: "parsed_modal_active", terminal: true };
19501
+ if (!this.completionHasFinalAssistantMessage(parsed?.messages)) return { reason: "missing_final_assistant" };
19452
19502
  return null;
19453
19503
  }
19454
19504
  scheduleCompletedDebounceFlush(delayMs) {
@@ -19470,10 +19520,11 @@ var CliProviderInstance = class {
19470
19520
  this.completedDebounceTimer = null;
19471
19521
  return;
19472
19522
  }
19473
- const blockReason = this.getCompletedFinalizationBlockReason(latestVisibleStatus);
19474
- if (blockReason) {
19523
+ const block2 = this.getCompletedFinalizationBlock(latestVisibleStatus);
19524
+ if (block2) {
19525
+ const blockReason = block2.reason;
19475
19526
  const waitedMs = Date.now() - pending.firstObservedAt;
19476
- if (waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
19527
+ if (block2.terminal || waitedMs < COMPLETED_FINALIZATION_MAX_WAIT_MS) {
19477
19528
  if (pending.loggedBlockReason !== blockReason) {
19478
19529
  LOG.info("CLI", `[${this.type}] waiting to emit completed until transcript finalizes (${blockReason})`);
19479
19530
  pending.loggedBlockReason = blockReason;
@@ -21342,6 +21393,47 @@ var BUSY_AGENT_STATUSES = /* @__PURE__ */ new Set(["generating", "running", "str
21342
21393
  function normalizeAgentStatus(value) {
21343
21394
  return typeof value === "string" ? value.trim().toLowerCase() : "";
21344
21395
  }
21396
+ function hasNonEmptyModalButtons2(activeModal) {
21397
+ const buttons = activeModal?.buttons;
21398
+ return Array.isArray(buttons) && buttons.some((button) => String(button || "").trim().length > 0);
21399
+ }
21400
+ function hasAdapterPendingResponse(adapter) {
21401
+ if (adapter?.isWaitingForResponse === true) return true;
21402
+ if (adapter?.currentTurnScope) return true;
21403
+ try {
21404
+ if (typeof adapter?.isProcessing === "function" && adapter.isProcessing()) return true;
21405
+ } catch {
21406
+ }
21407
+ try {
21408
+ const partial = typeof adapter?.getPartialResponse === "function" ? adapter.getPartialResponse() : "";
21409
+ if (typeof partial === "string" && partial.trim()) return true;
21410
+ } catch {
21411
+ }
21412
+ return false;
21413
+ }
21414
+ function shouldSuppressStaleParsedBusyStatus(adapterStatus, parsedStatus, adapter) {
21415
+ const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
21416
+ if (!BUSY_AGENT_STATUSES.has(parsedRawStatus)) return false;
21417
+ if (adapterStatus !== "idle") return false;
21418
+ if (hasNonEmptyModalButtons2(parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
21419
+ return !hasAdapterPendingResponse(adapter);
21420
+ }
21421
+ function getEffectiveAgentSendStatus(adapter) {
21422
+ const adapterStatus = normalizeAgentStatus(adapter?.getStatus?.({ allowParse: false })?.status ?? adapter?.getStatus?.()?.status);
21423
+ if (adapterStatus && adapterStatus !== "idle") return adapterStatus;
21424
+ if (adapterStatus !== "idle") return adapterStatus;
21425
+ if (typeof adapter?.getScriptParsedStatus !== "function") return adapterStatus;
21426
+ try {
21427
+ const parsedStatus = adapter.getScriptParsedStatus();
21428
+ const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
21429
+ if (BUSY_AGENT_STATUSES.has(parsedRawStatus) && !shouldSuppressStaleParsedBusyStatus(adapterStatus, parsedStatus, adapter)) {
21430
+ return parsedRawStatus;
21431
+ }
21432
+ } catch {
21433
+ return adapterStatus;
21434
+ }
21435
+ return adapterStatus;
21436
+ }
21345
21437
  var chalkModule = chalk;
21346
21438
  var chalkApi = typeof chalkModule.yellow === "function" ? chalkModule : chalkModule.default || null;
21347
21439
  function colorize(color, text) {
@@ -22116,7 +22208,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
22116
22208
  if (!found) throw new Error(`CLI agent not running: ${agentType}`);
22117
22209
  const { adapter, key } = found;
22118
22210
  if (action === "send_chat") {
22119
- const currentStatus = normalizeAgentStatus(adapter.getStatus?.()?.status);
22211
+ const currentStatus = getEffectiveAgentSendStatus(adapter);
22120
22212
  if (BUSY_AGENT_STATUSES.has(currentStatus)) {
22121
22213
  return {
22122
22214
  success: false,
@@ -25462,7 +25554,7 @@ async function maybeRunDaemonUpgradeHelperFromEnv() {
25462
25554
  // src/commands/router.ts
25463
25555
  init_mesh_work_queue();
25464
25556
  import { homedir as homedir19, tmpdir as tmpdir5 } from "os";
25465
- import { join as pathJoin, resolve as pathResolve } from "path";
25557
+ import { basename as pathBasename, join as pathJoin, resolve as pathResolve } from "path";
25466
25558
  import * as fs11 from "fs";
25467
25559
  var CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
25468
25560
  var CHANNEL_SERVER_URL = {
@@ -25605,6 +25697,17 @@ function readGitSubmodules(value, parentRepoRoot) {
25605
25697
  }).filter((entry) => entry !== null);
25606
25698
  return submodules.length > 0 ? submodules : void 0;
25607
25699
  }
25700
+ function buildMeshNodeDisplayLabel(node, nodeId, providerPriority) {
25701
+ const explicit = readStringValue(node.machineLabel, node.machine_label, node.machineNickname, node.machine_nickname, node.alias);
25702
+ if (explicit) return explicit;
25703
+ const workspace = readStringValue(node.workspace, node.repoRoot, node.repo_root);
25704
+ const workspaceName = workspace ? pathBasename(workspace) : void 0;
25705
+ const host = readStringValue(node.hostname, node.host, node.daemonId, node.daemon_id, node.machineId, node.machine_id);
25706
+ const provider = providerPriority[0] || (Array.isArray(node.providers) ? readStringValue(...node.providers) : void 0);
25707
+ const parts = [workspaceName, host, provider].filter(Boolean);
25708
+ if (parts.length > 0) return parts.join(" \xB7 ");
25709
+ return nodeId || "unidentified mesh node";
25710
+ }
25608
25711
  function normalizeInlineMeshGitStatus(status, node, options) {
25609
25712
  const isGitRepo = readBooleanValue(status.isGitRepo);
25610
25713
  if (!Object.keys(status).length || isGitRepo === void 0) return void 0;
@@ -26275,7 +26378,7 @@ function recordMeshRefineStage(stages, stage, status, startedAt, details) {
26275
26378
  }
26276
26379
  function buildSubmodulePublishRequiredNextStep(entries) {
26277
26380
  const refs = entries.map((entry) => `${entry.path}@${entry.commit}`).join(", ");
26278
- return `Ask the user for explicit approval to push/publish the unreachable submodule commit(s) (${refs}) to their configured submodule remote(s), then rerun mesh_refine_node. Do not merge the root branch until every submodule gitlink commit is reachable from its configured remote.`;
26381
+ return `Ask the user for explicit approval to push/publish the unreachable submodule commit(s) (${refs}) to the configured submodule remote main branch, then rerun mesh_refine_node. Do not merge the root branch until every submodule gitlink commit is reachable from submodule origin/main.`;
26279
26382
  }
26280
26383
  async function computeGitPatchId(cwd, fromRef, toRef) {
26281
26384
  const { execFileSync: execFileSync3 } = await import("child_process");
@@ -26362,12 +26465,13 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree) {
26362
26465
  });
26363
26466
  return String(stdout || "");
26364
26467
  };
26365
- const verifyRemoteCommitReachable = async (remoteUrl, commit) => {
26468
+ const verifyRemoteMainContainsCommit = async (remoteUrl, commit, branch = "main") => {
26366
26469
  const probeDir = fs11.mkdtempSync(pathJoin(tmpdir5(), "adhdev-submodule-reachability-"));
26367
26470
  try {
26368
26471
  await runGit2(probeDir, ["init", "-q"]);
26369
- await runGit2(probeDir, ["-c", "protocol.file.allow=always", "fetch", "--depth=1", remoteUrl, commit]);
26472
+ await runGit2(probeDir, ["-c", "protocol.file.allow=always", "fetch", "--depth=1", remoteUrl, `refs/heads/${branch}:refs/remotes/origin/${branch}`]);
26370
26473
  await runGit2(probeDir, ["cat-file", "-e", `${commit}^{commit}`]);
26474
+ await runGit2(probeDir, ["merge-base", "--is-ancestor", commit, `refs/remotes/origin/${branch}`]);
26371
26475
  } finally {
26372
26476
  fs11.rmSync(probeDir, { recursive: true, force: true });
26373
26477
  }
@@ -26411,15 +26515,18 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree) {
26411
26515
  entries.push(entry);
26412
26516
  continue;
26413
26517
  }
26414
- await verifyRemoteCommitReachable(remoteUrl, gitlink.commit);
26518
+ entry.remoteMainBranch = "main";
26519
+ await verifyRemoteMainContainsCommit(remoteUrl, gitlink.commit, "main");
26415
26520
  entry.fetchedFromOrigin = true;
26416
26521
  entry.remoteReachable = true;
26522
+ entry.remoteMainReachable = true;
26417
26523
  entry.reachable = true;
26418
26524
  } catch (e) {
26419
26525
  entry.remoteReachable = false;
26526
+ entry.remoteMainReachable = false;
26420
26527
  entry.publishRequired = true;
26421
26528
  const details = truncateValidationOutput(e?.stderr || e?.message || String(e));
26422
- entry.error = `Submodule remote reachability check failed for origin: ${details}`;
26529
+ entry.error = `Submodule remote main reachability check failed for origin/main: ${details}`;
26423
26530
  }
26424
26531
  } catch (e) {
26425
26532
  entry.error = truncateValidationOutput(e?.message || String(e));
@@ -27567,6 +27674,8 @@ var DaemonCommandRouter = class {
27567
27674
  remote: entry.remote,
27568
27675
  remoteUrl: entry.remoteUrl,
27569
27676
  remoteReachable: entry.remoteReachable,
27677
+ remoteMainBranch: entry.remoteMainBranch,
27678
+ remoteMainReachable: entry.remoteMainReachable,
27570
27679
  error: entry.error
27571
27680
  })),
27572
27681
  error: submoduleReachability.error
@@ -27579,13 +27688,13 @@ var DaemonCommandRouter = class {
27579
27688
  convergenceStatus: "blocked_review",
27580
27689
  publishRequired: true,
27581
27690
  blockedReason: "submodule_publish_required",
27582
- error: "Refinery submodule reachability preflight failed because one or more submodule gitlink commits are not reachable from their configured remote; merge/refine cleanup was not attempted.",
27691
+ error: "Refinery submodule reachability preflight failed because one or more submodule gitlink commits are not reachable from their configured remote main branch; merge/refine cleanup was not attempted.",
27583
27692
  nextStep,
27584
27693
  nextSteps: [
27585
27694
  "Ask the user for explicit approval before pushing or publishing any submodule commit.",
27586
- "Push/publish each unreachable submodule commit to the configured submodule remote shown in the evidence.",
27695
+ "Push/publish each unreachable submodule commit to the configured submodule remote main branch shown in the evidence.",
27587
27696
  "Rerun mesh_refine_node after remote reachability is confirmed.",
27588
- "Do not merge the root branch until every submodule gitlink commit is reachable from its configured remote."
27697
+ "Do not merge the root branch until every submodule gitlink commit is reachable from submodule origin/main."
27589
27698
  ],
27590
27699
  unreachableSubmoduleCommits: submoduleReachability.unreachable.map((entry) => ({
27591
27700
  path: entry.path,
@@ -27593,6 +27702,8 @@ var DaemonCommandRouter = class {
27593
27702
  remote: entry.remote,
27594
27703
  remoteUrl: entry.remoteUrl,
27595
27704
  remoteReachable: entry.remoteReachable,
27705
+ remoteMainBranch: entry.remoteMainBranch,
27706
+ remoteMainReachable: entry.remoteMainReachable,
27596
27707
  error: entry.error
27597
27708
  })),
27598
27709
  branch,
@@ -29467,7 +29578,8 @@ ${block2}`);
29467
29578
  ) || Boolean(meshRecord?.inline && nodeIndex === 0);
29468
29579
  const status = {
29469
29580
  nodeId,
29470
- machineLabel: node.machineLabel || node.id || node.nodeId,
29581
+ machineLabel: buildMeshNodeDisplayLabel(node, nodeId, providerPriority),
29582
+ labelSource: readStringValue(node.machineLabel, node.machine_label, node.machineNickname, node.machine_nickname, node.alias) ? "explicit_metadata" : "workspace_host_provider_context",
29471
29583
  workspace: node.workspace,
29472
29584
  repoRoot: node.repoRoot,
29473
29585
  isLocalWorktree: node.isLocalWorktree,