@adhdev/daemon-core 1.0.18-rc.10 → 1.0.18-rc.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.
Files changed (33) hide show
  1. package/dist/index.js +690 -35
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +690 -35
  4. package/dist/index.mjs.map +1 -1
  5. package/dist/mesh/mesh-active-work.d.ts +1 -1
  6. package/dist/mesh/mesh-ledger.d.ts +1 -1
  7. package/dist/mesh/mesh-node-identity.d.ts +23 -0
  8. package/dist/mesh/mesh-refine-gates.d.ts +89 -0
  9. package/dist/mesh/mesh-work-queue.d.ts +24 -1
  10. package/dist/providers/cli-provider-instance-types.d.ts +2 -0
  11. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +14 -0
  12. package/dist/providers/types/interactive-prompt.d.ts +18 -0
  13. package/package.json +3 -3
  14. package/src/commands/high-family/mesh-events.ts +13 -2
  15. package/src/commands/med-family/mesh-queue.ts +74 -1
  16. package/src/commands/router-refine.ts +71 -4
  17. package/src/commands/router.ts +8 -0
  18. package/src/mesh/coordinator-prompt.ts +2 -1
  19. package/src/mesh/mesh-active-work.ts +11 -1
  20. package/src/mesh/mesh-event-classify.ts +19 -0
  21. package/src/mesh/mesh-event-forwarding.ts +46 -4
  22. package/src/mesh/mesh-events-utils.ts +42 -0
  23. package/src/mesh/mesh-ledger.ts +5 -0
  24. package/src/mesh/mesh-node-identity.ts +49 -0
  25. package/src/mesh/mesh-queue-assignment.ts +18 -1
  26. package/src/mesh/mesh-reconcile-loop.ts +6 -1
  27. package/src/mesh/mesh-refine-gates.ts +300 -0
  28. package/src/mesh/mesh-work-queue.ts +85 -2
  29. package/src/providers/cli-provider-instance-types.ts +28 -0
  30. package/src/providers/cli-provider-instance.ts +90 -15
  31. package/src/providers/sdk/v1/builders/cli/detect-status.ts +23 -0
  32. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +20 -0
  33. package/src/providers/types/interactive-prompt.ts +77 -0
package/dist/index.js CHANGED
@@ -442,10 +442,10 @@ function readInjected(value) {
442
442
  }
443
443
  function getDaemonBuildInfo() {
444
444
  if (cached) return cached;
445
- const commit = readInjected(true ? "36bb91235b40db3d3fb1587850f0a213d040bcef" : void 0) ?? "unknown";
446
- const commitShort = readInjected(true ? "36bb9123" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
447
- const version = readInjected(true ? "1.0.18-rc.10" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
448
- const builtAt = readInjected(true ? "2026-07-22T07:21:15.085Z" : void 0);
445
+ const commit = readInjected(true ? "99f9c20bcf4ceec1e7ab2bfe6c8f641ca88da360" : void 0) ?? "unknown";
446
+ const commitShort = readInjected(true ? "99f9c20b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
447
+ const version = readInjected(true ? "1.0.18-rc.11" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
448
+ const builtAt = readInjected(true ? "2026-07-22T10:18:44.739Z" : void 0);
449
449
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
450
450
  return cached;
451
451
  }
@@ -2635,6 +2635,51 @@ var init_hash = __esm({
2635
2635
  });
2636
2636
 
2637
2637
  // ../mesh-shared/dist/index.mjs
2638
+ var dist_exports = {};
2639
+ __export(dist_exports, {
2640
+ CANONICAL_MESH_TOOL_COUNT: () => CANONICAL_MESH_TOOL_COUNT,
2641
+ CANONICAL_MESH_TOOL_NAMES: () => CANONICAL_MESH_TOOL_NAMES,
2642
+ DEFAULT_DIFFICULTY_BRAINS: () => DEFAULT_DIFFICULTY_BRAINS,
2643
+ MAGI_RAW_ANSWER_CAP: () => MAGI_RAW_ANSWER_CAP,
2644
+ MESH_TASK_DIFFICULTIES: () => MESH_TASK_DIFFICULTIES,
2645
+ STATUS_PROBE_ARG_KEY: () => STATUS_PROBE_ARG_KEY,
2646
+ argsCarryStatusProbeMarker: () => argsCarryStatusProbeMarker,
2647
+ canonicalDaemonId: () => canonicalDaemonId,
2648
+ daemonIdsEquivalent: () => daemonIdsEquivalent,
2649
+ deriveSlotsFromLegacy: () => deriveSlotsFromLegacy,
2650
+ expandDaemonIdForms: () => expandDaemonIdForms,
2651
+ hasGitStatusEvidence: () => hasGitStatusEvidence,
2652
+ interpolateArgs: () => interpolateArgs,
2653
+ interpolateString: () => interpolateString,
2654
+ isMeshTaskDifficulty: () => isMeshTaskDifficulty,
2655
+ joinRepoPath: () => joinRepoPath,
2656
+ machineCoreFromDaemonId: () => machineCoreFromDaemonId,
2657
+ meshNodeIdMatches: () => meshNodeIdMatches,
2658
+ meshWorkspacesEquivalent: () => meshWorkspacesEquivalent,
2659
+ normalizeBrainSlot: () => normalizeBrainSlot,
2660
+ normalizeDifficultyBrainMap: () => normalizeDifficultyBrainMap,
2661
+ normalizeGitStatus: () => normalizeGitStatus,
2662
+ normalizeMeshNodeId: () => normalizeMeshNodeId,
2663
+ normalizeMeshSessionRecord: () => normalizeMeshSessionRecord,
2664
+ normalizeMeshWorkspaceForCompare: () => normalizeMeshWorkspaceForCompare,
2665
+ normalizeNodeCapabilitySlot: () => normalizeNodeCapabilitySlot,
2666
+ normalizeNodeCapabilitySlots: () => normalizeNodeCapabilitySlots,
2667
+ normalizeThinkingLevel: () => normalizeThinkingLevel,
2668
+ parseJsonRecord: () => parseJsonRecord,
2669
+ pickBestTransitGitStatus: () => pickBestTransitGitStatus,
2670
+ readBoolean: () => readBoolean,
2671
+ readGitSubmodules: () => readGitSubmodules,
2672
+ readNumber: () => readNumber,
2673
+ readRecord: () => readRecord,
2674
+ readString: () => readString2,
2675
+ readStringArray: () => readStringArray2,
2676
+ scoreGitStatusCandidate: () => scoreGitStatusCandidate,
2677
+ scoreGitUpstreamFreshness: () => scoreGitUpstreamFreshness,
2678
+ sessionIdsEquivalent: () => sessionIdsEquivalent,
2679
+ stripStatusProbeMarker: () => stripStatusProbeMarker,
2680
+ summarizeGitShape: () => summarizeGitShape,
2681
+ withStatusProbeMarker: () => withStatusProbeMarker
2682
+ });
2638
2683
  function readRecord(value) {
2639
2684
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
2640
2685
  }
@@ -2658,6 +2703,21 @@ function readBoolean(...values) {
2658
2703
  }
2659
2704
  return void 0;
2660
2705
  }
2706
+ function readStringArray2(value) {
2707
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
2708
+ }
2709
+ function parseJsonRecord(value) {
2710
+ if (!value) return {};
2711
+ if (typeof value === "object") return readRecord(value);
2712
+ if (typeof value !== "string") return {};
2713
+ const trimmed = value.trim();
2714
+ if (!trimmed) return {};
2715
+ try {
2716
+ return readRecord(JSON.parse(trimmed));
2717
+ } catch {
2718
+ return {};
2719
+ }
2720
+ }
2661
2721
  function joinRepoPath(root, relativePath) {
2662
2722
  const normalizedRoot = typeof root === "string" ? root.trim().replace(/[\\/]+$/, "") : "";
2663
2723
  const normalizedPath = typeof relativePath === "string" ? relativePath.trim() : "";
@@ -2798,12 +2858,48 @@ function pickBestTransitGitStatus(node, options) {
2798
2858
  }
2799
2859
  return best?.git;
2800
2860
  }
2861
+ function deriveSyntheticSessionId(record) {
2862
+ const parts = [
2863
+ readString2(record.workspace),
2864
+ readString2(record.providerType, record.provider),
2865
+ readString2(record.role),
2866
+ readString2(record.state, record.status),
2867
+ readString2(record.title),
2868
+ readString2(record.createdAt, record.created_at),
2869
+ readString2(record.startedAt, record.started_at)
2870
+ ].filter((part) => Boolean(part));
2871
+ if (parts.length === 0) return void 0;
2872
+ return `synthetic:${parts.join("|")}`;
2873
+ }
2801
2874
  function sessionIdsEquivalent(a, b) {
2802
2875
  const idA = readString2(a);
2803
2876
  const idB = readString2(b);
2804
2877
  if (!idA || !idB) return false;
2805
2878
  return idA === idB;
2806
2879
  }
2880
+ function normalizeMeshSessionRecord(entry) {
2881
+ const record = readRecord(entry);
2882
+ const sessionId = readString2(record.sessionId, record.session_id, record.id) ?? deriveSyntheticSessionId(record);
2883
+ if (!sessionId) return null;
2884
+ return {
2885
+ sessionId,
2886
+ ...readString2(record.providerType, record.provider) ? { providerType: readString2(record.providerType, record.provider) } : {},
2887
+ ...readString2(record.state, record.status) ? { state: readString2(record.state, record.status) } : {},
2888
+ ...readString2(record.chatStatus, record.chat_status) ? { chatStatus: readString2(record.chatStatus, record.chat_status) } : {},
2889
+ ...readString2(record.lifecycle) ? { lifecycle: readString2(record.lifecycle) } : {},
2890
+ ...readString2(record.surfaceKind, record.surface_kind) ? { surfaceKind: readString2(record.surfaceKind, record.surface_kind) } : {},
2891
+ ...readString2(record.recoveryState, record.recovery_state) ? { recoveryState: readString2(record.recoveryState, record.recovery_state) } : {},
2892
+ ...readString2(record.workspace) ? { workspace: readString2(record.workspace) } : {},
2893
+ ...readString2(record.title) ? { title: readString2(record.title) } : {},
2894
+ ...readString2(record.role) ? { role: readString2(record.role) } : {},
2895
+ ...readBoolean(record.isSelfCoordinator, record.is_self_coordinator) !== void 0 ? { isSelfCoordinator: readBoolean(record.isSelfCoordinator, record.is_self_coordinator) } : {},
2896
+ ...readString2(record.statusNote, record.status_note) ? { statusNote: readString2(record.statusNote, record.status_note) } : {},
2897
+ ...readString2(record.createdAt, record.created_at) ? { createdAt: readString2(record.createdAt, record.created_at) } : {},
2898
+ ...readString2(record.startedAt, record.started_at) ? { startedAt: readString2(record.startedAt, record.started_at) } : {},
2899
+ ...readString2(record.lastActivityAt, record.last_activity_at) ? { lastActivityAt: readString2(record.lastActivityAt, record.last_activity_at) } : {},
2900
+ ...readBoolean(record.isCached, record.is_cached) !== void 0 ? { isCached: readBoolean(record.isCached, record.is_cached) } : {}
2901
+ };
2902
+ }
2807
2903
  function normalizeMeshNodeId(node) {
2808
2904
  const record = node && typeof node === "object" ? node : {};
2809
2905
  return readString2(record.id, record.nodeId, record.node_id);
@@ -2986,9 +3082,30 @@ function deriveSlotsFromLegacy(input) {
2986
3082
  };
2987
3083
  });
2988
3084
  }
3085
+ function interpolateArgs(args, context) {
3086
+ const result = {};
3087
+ for (const [k, v] of Object.entries(args)) {
3088
+ result[k] = typeof v === "string" ? interpolateString(v, context) : v;
3089
+ }
3090
+ return result;
3091
+ }
3092
+ function interpolateString(template, ctx) {
3093
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key2) => {
3094
+ const val = ctx[key2];
3095
+ return val !== void 0 ? String(val) : `{{${key2}}}`;
3096
+ });
3097
+ }
2989
3098
  function withStatusProbeMarker(args = {}) {
2990
3099
  return { ...args, [STATUS_PROBE_ARG_KEY]: true };
2991
3100
  }
3101
+ function argsCarryStatusProbeMarker(args) {
3102
+ return !!args && typeof args === "object" && args[STATUS_PROBE_ARG_KEY] === true;
3103
+ }
3104
+ function stripStatusProbeMarker(args) {
3105
+ if (!argsCarryStatusProbeMarker(args)) return args;
3106
+ const { [STATUS_PROBE_ARG_KEY]: _drop, ...rest } = args;
3107
+ return rest;
3108
+ }
2992
3109
  var DAEMON_ID_PREFIXES, MAGI_RAW_ANSWER_CAP, MESH_TASK_DIFFICULTIES, DEFAULT_DIFFICULTY_BRAINS, CANONICAL_MESH_TOOL_NAMES, CANONICAL_MESH_TOOL_COUNT, STATUS_PROBE_ARG_KEY;
2993
3110
  var init_dist = __esm({
2994
3111
  "../mesh-shared/dist/index.mjs"() {
@@ -3020,6 +3137,7 @@ var init_dist = __esm({
3020
3137
  "mesh_restart_daemon",
3021
3138
  "mesh_checkpoint",
3022
3139
  "mesh_approve",
3140
+ "mesh_answer_question",
3023
3141
  "mesh_list_pending_approvals",
3024
3142
  "mesh_clone_node",
3025
3143
  "mesh_remove_node",
@@ -4458,6 +4576,42 @@ ${surfaced}`;
4458
4576
  if (args.event === "agent:waiting_approval") {
4459
4577
  return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
4460
4578
  }
4579
+ if (args.event === "agent:waiting_choice") {
4580
+ const prompt = args.metadataEvent.interactivePrompt;
4581
+ const promptId = readNonEmptyString(args.metadataEvent.promptId) || prompt && readNonEmptyString(prompt.promptId);
4582
+ const lines = [
4583
+ `[System] ${args.nodeLabel} is asking a question and is waiting for your answer${metadata}.`
4584
+ ];
4585
+ const questions = Array.isArray(prompt?.questions) ? prompt.questions : [];
4586
+ if (questions.length > 0) {
4587
+ for (const q of questions) {
4588
+ const header = readNonEmptyString(q.header);
4589
+ const question = readNonEmptyString(q.question);
4590
+ const multiSelect = q.multiSelect === true;
4591
+ if (question) {
4592
+ lines.push(`
4593
+ **${header ? `${header}: ` : ""}${question}**${multiSelect ? " (select one or more)" : ""}`);
4594
+ }
4595
+ const options = Array.isArray(q.options) ? q.options : [];
4596
+ options.forEach((opt, i) => {
4597
+ const record = opt && typeof opt === "object" ? opt : {};
4598
+ const label = readNonEmptyString(record.label);
4599
+ if (!label) return;
4600
+ const description = readNonEmptyString(record.description);
4601
+ lines.push(` ${i + 1}. ${label}${description ? ` \u2014 ${description}` : ""}`);
4602
+ });
4603
+ }
4604
+ } else {
4605
+ const modalMessage = readNonEmptyString(args.metadataEvent.modalMessage);
4606
+ if (modalMessage) lines.push(`
4607
+ ${modalMessage}`);
4608
+ }
4609
+ lines.push(
4610
+ `
4611
+ Answer with mesh_answer_question(node_id, session_id${promptId ? `, promptId: "${promptId}"` : ""}, answers). Do NOT use mesh_approve \u2014 that only resolves yes/no consent modals, not a question. Use mesh_read_chat once if you need the full context first.`
4612
+ );
4613
+ return lines.join("\n");
4614
+ }
4461
4615
  if (args.event === "agent:stopped") {
4462
4616
  const rc = args.recoveryContext;
4463
4617
  if (rc && rc.consecutiveNodeFailures > 0) {
@@ -5959,6 +6113,7 @@ __export(mesh_work_queue_exports, {
5959
6113
  requeueTask: () => requeueTask,
5960
6114
  resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
5961
6115
  resolveNotBefore: () => resolveNotBefore,
6116
+ takeCancelledTaskAssignment: () => takeCancelledTaskAssignment,
5962
6117
  taskDependenciesSatisfied: () => taskDependenciesSatisfied,
5963
6118
  updateDirectDispatchStatus: () => updateDirectDispatchStatus,
5964
6119
  updateSessionTaskStatus: () => updateSessionTaskStatus,
@@ -6518,6 +6673,10 @@ function updateTaskStatus(meshId, taskId, status, opts) {
6518
6673
  const result = withQueueLock(meshId, () => {
6519
6674
  const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
6520
6675
  if (!entry) return null;
6676
+ if (!opts?.force && TERMINAL_TASK_STATUSES.has(entry.status) && !TERMINAL_TASK_STATUSES.has(status)) {
6677
+ LOG.debug("MeshQueue", `Refusing updateTaskStatus(${taskId} \u2192 ${status}) on mesh ${meshId}: row is terminal (${entry.status}). A late writer (e.g. dispatch-failure requeue) must not resurrect a cancelled/completed/failed task. Pass force to override.`);
6678
+ return { entry, cascaded: [] };
6679
+ }
6521
6680
  entry.status = status;
6522
6681
  MeshRuntimeStore.getInstance().updateQueueEntry(entry);
6523
6682
  if (status !== "assigned") endTaskDispatchInFlight(meshId, taskId);
@@ -6543,17 +6702,34 @@ function cancelTask(meshId, taskId, opts) {
6543
6702
  const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
6544
6703
  if (!entry) return null;
6545
6704
  const now = (/* @__PURE__ */ new Date()).toISOString();
6705
+ const priorAssignment = entry.assignedSessionId ? {
6706
+ sessionId: entry.assignedSessionId,
6707
+ nodeId: entry.assignedNodeId,
6708
+ providerType: entry.assignedProviderType
6709
+ } : void 0;
6546
6710
  entry.status = "cancelled";
6547
6711
  entry.cancelledAt = now;
6548
6712
  if (opts?.reason) entry.cancelReason = opts.reason;
6713
+ delete entry.assignedNodeId;
6714
+ delete entry.assignedSessionId;
6715
+ delete entry.assignedProviderType;
6716
+ delete entry.dispatchTimestamp;
6717
+ entry.dispatchNonce = (entry.dispatchNonce || 0) + 1;
6549
6718
  MeshRuntimeStore.getInstance().updateQueueEntry(entry);
6550
6719
  endTaskDispatchInFlight(meshId, taskId);
6551
6720
  const cascaded = propagateDependencyFailure(meshId, taskId);
6552
- return { entry, cascaded };
6721
+ return { entry, cascaded, priorAssignment };
6553
6722
  });
6554
6723
  if (result) scheduleMissionCloseCandidateCheck(meshId, [result.entry, ...result.cascaded]);
6724
+ if (result?.priorAssignment) lastCancelledTaskAssignment.set(`${meshId}::${taskId}`, result.priorAssignment);
6555
6725
  return result ? result.entry : null;
6556
6726
  }
6727
+ function takeCancelledTaskAssignment(meshId, taskId) {
6728
+ const key2 = `${meshId}::${taskId}`;
6729
+ const value = lastCancelledTaskAssignment.get(key2);
6730
+ if (value) lastCancelledTaskAssignment.delete(key2);
6731
+ return value;
6732
+ }
6557
6733
  function requeueTask(meshId, taskId, opts) {
6558
6734
  requireMeshHostQueueOwner(opts);
6559
6735
  const result = withQueueLock(meshId, () => {
@@ -6768,7 +6944,7 @@ function recordMeshToolCall(opts) {
6768
6944
  return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
6769
6945
  }
6770
6946
  }
6771
- var import_crypto7, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, MESH_TASK_PRIORITIES, NOT_BEFORE_RELATIVE_THRESHOLD_MS, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, MAX_STRANDED_RECLAIMS, REDRIVE_RECLAIM_REASONS, REDRIVE_SUPERSEDE_WINDOW_MS;
6947
+ var import_crypto7, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, MESH_TASK_PRIORITIES, NOT_BEFORE_RELATIVE_THRESHOLD_MS, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, TERMINAL_TASK_STATUSES, lastCancelledTaskAssignment, MAX_STRANDED_RECLAIMS, REDRIVE_RECLAIM_REASONS, REDRIVE_SUPERSEDE_WINDOW_MS;
6772
6948
  var init_mesh_work_queue = __esm({
6773
6949
  "src/mesh/mesh-work-queue.ts"() {
6774
6950
  "use strict";
@@ -6836,6 +7012,8 @@ var init_mesh_work_queue = __esm({
6836
7012
  ]);
6837
7013
  GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
6838
7014
  DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
7015
+ TERMINAL_TASK_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
7016
+ lastCancelledTaskAssignment = /* @__PURE__ */ new Map();
6839
7017
  MAX_STRANDED_RECLAIMS = 3;
6840
7018
  REDRIVE_RECLAIM_REASONS = /* @__PURE__ */ new Set([
6841
7019
  "delivered_no_turn_deadline",
@@ -8932,7 +9110,7 @@ function compactLedger(meshId) {
8932
9110
  function readNonEmptyString2(value) {
8933
9111
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
8934
9112
  }
8935
- function readStringArray2(value) {
9113
+ function readStringArray3(value) {
8936
9114
  if (!Array.isArray(value)) return [];
8937
9115
  return value.map((item) => readNonEmptyString2(item)).filter(Boolean);
8938
9116
  }
@@ -8992,11 +9170,11 @@ function normalizeMeshWorkerResult(input, source = "explicit_metadata") {
8992
9170
  return {
8993
9171
  status,
8994
9172
  ...readNonEmptyString2(raw.classification) ? { classification: readNonEmptyString2(raw.classification) } : {},
8995
- changedFiles: readStringArray2(raw.changedFiles),
9173
+ changedFiles: readStringArray3(raw.changedFiles),
8996
9174
  validationResults: normalizeValidationResults(raw.validationResults),
8997
9175
  ...gitStatus ? { gitStatus } : {},
8998
9176
  processArtifacts: normalizeProcessArtifacts(raw.processArtifacts),
8999
- errors: readStringArray2(raw.errors),
9177
+ errors: readStringArray3(raw.errors),
9000
9178
  ...readNonEmptyString2(raw.nextAction) ? { nextAction: readNonEmptyString2(raw.nextAction) } : {},
9001
9179
  requiresUserAction: raw.requiresUserAction === true,
9002
9180
  source
@@ -10145,7 +10323,8 @@ var init_coordinator_prompt = __esm({
10145
10323
  | \`mesh_fast_forward_node\` | Safely dry-run or explicitly execute an obvious clean fast-forward without launching an agent session |
10146
10324
  | \`mesh_restart_daemon\` | Update a node's daemon to the latest published version on its channel and restart it (the dashboard "preview update" path, as a mesh command) |
10147
10325
  | \`mesh_checkpoint\` | Create a git checkpoint on a node |
10148
- | \`mesh_approve\` | Approve/reject a pending agent action |
10326
+ | \`mesh_approve\` | Approve/reject a pending agent action (a yes/no tool-consent modal) |
10327
+ | \`mesh_answer_question\` | Answer a delegated session's multi-choice QUESTION (AskUserQuestion / status awaiting_choice). NOT an approval \u2014 it offers labelled options (possibly multi-select or freeform). Pass the promptId from the agent:waiting_choice event + one answer per question (select by option label or 1-based index). Use this, never mesh_approve, for a question |
10149
10328
  | \`mesh_list_pending_approvals\` | List every session across the mesh awaiting an approval decision (the approval inbox) \u2014 read-only; enumerate all blocked sessions at once, then drive a mesh_approve for each |
10150
10329
  | \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
10151
10330
  | \`mesh_refine_node\` | Validate and merge a completed worktree node back into its base branch |
@@ -10383,7 +10562,7 @@ function readString5(value) {
10383
10562
  function readRecord4(value) {
10384
10563
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
10385
10564
  }
10386
- function readStringArray3(value, max) {
10565
+ function readStringArray4(value, max) {
10387
10566
  if (!Array.isArray(value)) return { values: [], truncated: false };
10388
10567
  const values = value.filter((item) => typeof item === "string" && item.trim().length > 0);
10389
10568
  return { values: values.slice(0, max), truncated: values.length > max };
@@ -10413,14 +10592,14 @@ function isMergeCandidate(convergence) {
10413
10592
  function readWorkerArtifact(value) {
10414
10593
  const worker = readRecord4(value);
10415
10594
  if (!worker) return null;
10416
- const changed = readStringArray3(worker.changedFiles, MAX_CHANGED_FILES);
10595
+ const changed = readStringArray4(worker.changedFiles, MAX_CHANGED_FILES);
10417
10596
  return {
10418
10597
  status: readString5(worker.status) ?? "unknown",
10419
10598
  ...readString5(worker.classification) ? { classification: readString5(worker.classification) } : {},
10420
10599
  changedFiles: changed.values,
10421
10600
  ...changed.truncated ? { changedFilesTruncated: true } : {},
10422
10601
  validationResults: Array.isArray(worker.validationResults) ? worker.validationResults.map((item) => readRecord4(item)).filter((item) => item !== null) : [],
10423
- errors: readStringArray3(worker.errors, 20).values,
10602
+ errors: readStringArray4(worker.errors, 20).values,
10424
10603
  requiresUserAction: worker.requiresUserAction === true
10425
10604
  };
10426
10605
  }
@@ -13549,6 +13728,61 @@ var init_runtime_defaults = __esm({
13549
13728
  });
13550
13729
 
13551
13730
  // src/mesh/mesh-node-identity.ts
13731
+ var mesh_node_identity_exports = {};
13732
+ __export(mesh_node_identity_exports, {
13733
+ MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS: () => MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS,
13734
+ MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS: () => MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS,
13735
+ MESH_DIRECT_PROBE_REUSE_MS: () => MESH_DIRECT_PROBE_REUSE_MS,
13736
+ MESH_DIRECT_PROBE_TIMEOUT_MS: () => MESH_DIRECT_PROBE_TIMEOUT_MS,
13737
+ MESH_NODE_LIVE_TRUTH_MARKER: () => MESH_NODE_LIVE_TRUTH_MARKER,
13738
+ MeshGitProbeCache: () => MeshGitProbeCache,
13739
+ applyCachedInlineMeshNodeStatus: () => applyCachedInlineMeshNodeStatus,
13740
+ applyInlineMeshBranchConvergence: () => applyInlineMeshBranchConvergence,
13741
+ awaitWithWarmupDeadline: () => awaitWithWarmupDeadline,
13742
+ buildHistoricalMeshSessions: () => buildHistoricalMeshSessions,
13743
+ buildInlineMeshTransitGitStatus: () => buildInlineMeshTransitGitStatus,
13744
+ buildLivePeerGitConnection: () => buildLivePeerGitConnection,
13745
+ buildMeshNodeDataFreshness: () => buildMeshNodeDataFreshness,
13746
+ buildMeshNodeDisplayLabel: () => buildMeshNodeDisplayLabel,
13747
+ buildMeshNodeMachineIdentity: () => buildMeshNodeMachineIdentity,
13748
+ buildMeshNodeProbeFreshness: () => buildMeshNodeProbeFreshness,
13749
+ collectLiveMeshSessionRecords: () => collectLiveMeshSessionRecords,
13750
+ collectMeshNodeHostedSessionIds: () => collectMeshNodeHostedSessionIds,
13751
+ deriveMeshNodeHealthFromGit: () => deriveMeshNodeHealthFromGit,
13752
+ finalizeMeshNodeStatus: () => finalizeMeshNodeStatus,
13753
+ foldMeshNodeIdentityToCanonical: () => foldMeshNodeIdentityToCanonical,
13754
+ hydrateInlineMeshDirectTruth: () => hydrateInlineMeshDirectTruth,
13755
+ inlineMeshCarriesTransientNodeTruth: () => inlineMeshCarriesTransientNodeTruth,
13756
+ isDeadLocalWorktreeNode: () => isDeadLocalWorktreeNode,
13757
+ isMeshNodeFreshEnoughToLaunch: () => isMeshNodeFreshEnoughToLaunch,
13758
+ isMeshNodeHealthLaunchable: () => isMeshNodeHealthLaunchable,
13759
+ logRepoMeshStatusDebug: () => logRepoMeshStatusDebug,
13760
+ normalizeInlineMeshNodeIdentity: () => normalizeInlineMeshNodeIdentity,
13761
+ normalizeProviderRoles: () => normalizeProviderRoles,
13762
+ persistNodeReporterPlatform: () => persistNodeReporterPlatform,
13763
+ probeRemoteMeshGitStatusWithRetry: () => probeRemoteMeshGitStatusWithRetry,
13764
+ readBooleanValue: () => readBooleanValue,
13765
+ readCachedInlineMeshActiveSessionDetails: () => readCachedInlineMeshActiveSessionDetails,
13766
+ readCachedInlineMeshActiveSessions: () => readCachedInlineMeshActiveSessions,
13767
+ readInlineMeshNodeId: () => readInlineMeshNodeId,
13768
+ readLiveMeshNodeWorkspace: () => readLiveMeshNodeWorkspace,
13769
+ readMeshNodeDaemonId: () => readMeshNodeDaemonId,
13770
+ readMeshNodeHostname: () => readMeshNodeHostname,
13771
+ readMeshNodeMachineId: () => readMeshNodeMachineId,
13772
+ readObjectRecord: () => readObjectRecord,
13773
+ readProviderPriorityFromPolicy: () => readProviderPriorityFromPolicy,
13774
+ readStringValue: () => readStringValue,
13775
+ reconcileInlineMeshCache: () => reconcileInlineMeshCache,
13776
+ recordInlineMeshDirectGitTruth: () => recordInlineMeshDirectGitTruth,
13777
+ resolveEffectiveMeshNodeHealth: () => resolveEffectiveMeshNodeHealth,
13778
+ resolveMeshNodeAttribution: () => resolveMeshNodeAttribution,
13779
+ resolveProviderTypeFromPriority: () => resolveProviderTypeFromPriority,
13780
+ sanitizeInlineMesh: () => sanitizeInlineMesh,
13781
+ shouldRefreshStalePendingAggregate: () => shouldRefreshStalePendingAggregate,
13782
+ summarizeInlineMeshBranchConvergence: () => summarizeInlineMeshBranchConvergence,
13783
+ summarizeMeshSessionRecord: () => summarizeMeshSessionRecord,
13784
+ summarizeRepoMeshStatusDebug: () => summarizeRepoMeshStatusDebug
13785
+ });
13552
13786
  function readProviderPriorityFromPolicy(policy) {
13553
13787
  const record = policy && typeof policy === "object" && !Array.isArray(policy) ? policy : {};
13554
13788
  const raw = record.providerPriority;
@@ -14112,6 +14346,23 @@ function isMeshNodeHealthLaunchable(node) {
14112
14346
  const health = resolveEffectiveMeshNodeHealth(node);
14113
14347
  return health === "online" || health === "unknown";
14114
14348
  }
14349
+ function resolveEffectiveNodeGit(node) {
14350
+ const git = readObjectRecord(node?.git);
14351
+ if (Object.keys(git).length > 0) return git;
14352
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
14353
+ return readObjectRecord(cachedStatus.git);
14354
+ }
14355
+ function isMeshNodeFreshEnoughToLaunch(node, opts) {
14356
+ const git = resolveEffectiveNodeGit(node);
14357
+ if (Object.keys(git).length === 0) return true;
14358
+ if (readBooleanValue(git.isGitRepo) === false) return true;
14359
+ const submoduleDrift = getGitSubmoduleDriftState(git);
14360
+ if (submoduleDrift.outOfSync) return false;
14361
+ const behind = readNumberValue(git.behind);
14362
+ if (behind === void 0) return true;
14363
+ const maxBehind = Number.isFinite(opts?.maxBehind) && opts?.maxBehind >= 0 ? Math.floor(opts.maxBehind) : 0;
14364
+ return behind <= maxBehind;
14365
+ }
14115
14366
  function readMeshNodeLabel(status, node) {
14116
14367
  return readStringValue(status.nodeId, normalizeMeshNodeId(node)) ?? "unknown";
14117
14368
  }
@@ -15008,6 +15259,7 @@ function sessionStatusFromNodes(nodes, nodeId, sessionId) {
15008
15259
  if (!session) return { staleReason: "direct task session is not present in live session records" };
15009
15260
  if (typeof session === "string") return {};
15010
15261
  const raw = `${readString6(session.status) || ""} ${readString6(session.lifecycle) || ""} ${readString6(session.state) || ""} ${readString6(session.activeChat?.status) || ""}`.toLowerCase();
15262
+ if (raw.includes("waiting_choice") || raw.includes("choice")) return { status: "awaiting_choice" };
15011
15263
  if (raw.includes("approval")) return { status: "awaiting_approval" };
15012
15264
  if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
15013
15265
  if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
@@ -15033,6 +15285,7 @@ function terminalMatchesDispatch(terminal, dispatch, taskId) {
15033
15285
  }
15034
15286
  function statusFromTerminal(entry) {
15035
15287
  if (entry.kind === "task_approval_needed") return "awaiting_approval";
15288
+ if (entry.kind === "task_question_pending") return "awaiting_choice";
15036
15289
  if (entry.kind === "task_completed") return "idle";
15037
15290
  return "failed";
15038
15291
  }
@@ -15111,7 +15364,8 @@ function buildMeshActiveWorkSummary(activeWork) {
15111
15364
  generating: 0,
15112
15365
  idle: 0,
15113
15366
  failed: 0,
15114
- awaiting_approval: 0
15367
+ awaiting_approval: 0,
15368
+ awaiting_choice: 0
15115
15369
  };
15116
15370
  const sourceCounts = { queue: 0, direct: 0 };
15117
15371
  for (const item of activeWork) {
@@ -15146,7 +15400,7 @@ function buildMeshActiveWork(opts) {
15146
15400
  const queueNodeId = task.assignedNodeId || task.targetNodeId;
15147
15401
  const queueSessionId = task.assignedSessionId || task.targetSessionId;
15148
15402
  const queueLive = task.status === "assigned" ? sessionStatusFromNodes(opts.nodes, queueNodeId ?? void 0, queueSessionId ?? void 0) : {};
15149
- const queueStatus = queueLive.status === "awaiting_approval" || queueLive.status === "generating" ? queueLive.status : task.status;
15403
+ const queueStatus = queueLive.status === "awaiting_approval" || queueLive.status === "awaiting_choice" || queueLive.status === "generating" ? queueLive.status : task.status;
15150
15404
  records.push({
15151
15405
  taskId: task.id,
15152
15406
  source: "queue",
@@ -16142,6 +16396,11 @@ var init_mesh_event_classify = __esm({
16142
16396
  "agent:generating_started",
16143
16397
  "agent:generating_completed",
16144
16398
  "agent:waiting_approval",
16399
+ // A worker parked on an AskUserQuestion multi-choice prompt. DISTINCT from
16400
+ // agent:waiting_approval (a yes/no tool-consent modal): a question is answered
16401
+ // with mesh_answer_question, never mesh_approve (mission f1d25e11). Carries the
16402
+ // full InteractivePrompt payload so the coordinator can render + answer.
16403
+ "agent:waiting_choice",
16145
16404
  "agent:stopped",
16146
16405
  "agent:ready",
16147
16406
  "monitor:no_progress",
@@ -16159,6 +16418,7 @@ var init_mesh_event_classify = __esm({
16159
16418
  EVENT_TO_LEDGER_KIND = {
16160
16419
  "agent:generating_completed": "task_completed",
16161
16420
  "agent:waiting_approval": "task_approval_needed",
16421
+ "agent:waiting_choice": "task_question_pending",
16162
16422
  "agent:stopped": "task_failed",
16163
16423
  "monitor:no_progress": "task_stalled"
16164
16424
  };
@@ -16166,13 +16426,26 @@ var init_mesh_event_classify = __esm({
16166
16426
  "agent:generating_completed",
16167
16427
  "agent:stopped",
16168
16428
  "agent:waiting_approval",
16429
+ // A worker's question (waiting_choice) is real-time and the coordinator may be
16430
+ // generating when it arrives; force-inject it like approval so the busy coordinator
16431
+ // learns it must answer (mesh_answer_question) rather than the nudge sitting in the
16432
+ // adapter's outbound queue until the coordinator idles on its own (mission f1d25e11).
16433
+ "agent:waiting_choice",
16169
16434
  "refine:completed",
16170
16435
  "refine:failed",
16171
16436
  "worktree_bootstrap_complete",
16172
16437
  "worktree_bootstrap_failed"
16173
16438
  ]);
16174
16439
  MESH_APPROVAL_EVENTS = /* @__PURE__ */ new Set([
16175
- "agent:waiting_approval"
16440
+ "agent:waiting_approval",
16441
+ // agent:waiting_choice (a worker's AskUserQuestion) shares the approval class's
16442
+ // real-time-nudge / level-backed contract: its authoritative state is recorded at
16443
+ // LEVEL the moment it is processed (task_question_pending → mesh_status
16444
+ // awaiting_choice), so the pending event is only a NUDGE and can be delivered to a
16445
+ // busy coordinator's inbox (and dropped) without data loss — the level state
16446
+ // re-derives it. It is therefore exempt from the idle-edge hold completions require,
16447
+ // exactly like an approval nudge (mission f1d25e11).
16448
+ "agent:waiting_choice"
16176
16449
  ]);
16177
16450
  }
16178
16451
  });
@@ -17161,6 +17434,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
17161
17434
  }
17162
17435
  }
17163
17436
  if (!pending.length) return false;
17437
+ const freshnessGate = { maxBehind: resolveAutoFastForwardPolicy(mesh).maxBehind };
17164
17438
  const maxParallelTasks = resolveMaxParallelTasks(mesh?.policy?.maxParallelTasks);
17165
17439
  const maxReadonlyParallelTasks = resolveMaxReadonlyParallelTasks(maxParallelTasks);
17166
17440
  for (const task of pending) {
@@ -17275,6 +17549,10 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
17275
17549
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_not_launch_ready", nodeId });
17276
17550
  continue;
17277
17551
  }
17552
+ if (!isMeshNodeFreshEnoughToLaunch(node, freshnessGate)) {
17553
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_stale_behind_upstream", nodeId });
17554
+ continue;
17555
+ }
17278
17556
  const launchTarget = resolveAutoLaunchTarget(components, node);
17279
17557
  if (launchTarget.mode === "skip") {
17280
17558
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: launchTarget.reason || "auto_launch_unavailable", nodeId });
@@ -20976,10 +21254,19 @@ function injectMeshSystemMessage(components, args) {
20976
21254
  }
20977
21255
  function markSessionTerminal(sessionId, outcome, occurredAtMs, opts) {
20978
21256
  const eventTaskId = readNonEmptyString(args.metadataEvent.taskId) || void 0;
20979
- const task = updateSessionTaskStatus(args.meshId, sessionId, outcome, {
21257
+ const completionDiagnostic = args.metadataEvent.completionDiagnostic && typeof args.metadataEvent.completionDiagnostic === "object" ? args.metadataEvent.completionDiagnostic : void 0;
21258
+ const emittedAfterFinalizationTimeout = completionDiagnostic?.emittedAfterFinalizationTimeout === true;
21259
+ const weakCompleted = outcome === "completed" && isWeakCompletionEvidence(args.metadataEvent) && !emittedAfterFinalizationTimeout;
21260
+ const task = weakCompleted ? updateSessionTaskStatus(args.meshId, sessionId, "assigned", {
21261
+ occurredAt: occurredAtMs != null ? new Date(occurredAtMs).toISOString() : void 0,
21262
+ taskId: eventTaskId
21263
+ }) : updateSessionTaskStatus(args.meshId, sessionId, outcome, {
20980
21264
  occurredAt: occurredAtMs != null ? new Date(occurredAtMs).toISOString() : void 0,
20981
21265
  taskId: eventTaskId
20982
21266
  });
21267
+ if (weakCompleted && task) {
21268
+ LOG.info("MeshQueue", `Weak completion (${readNonEmptyString(args.metadataEvent.evidenceLevel) || "missing_final_assistant"}) kept queue task ${task.id} tentative (session ${sessionId}); reconcile owns the genuine terminal`);
21269
+ }
20983
21270
  const leaveDirectDispatchActive = !task && opts?.tentativeIfDirect === true || !eventTaskId && !sessionHasActiveAssignment(args.meshId, sessionId);
20984
21271
  if (!leaveDirectDispatchActive) {
20985
21272
  updateDirectDispatchStatus(args.meshId, sessionId, outcome, eventTaskId);
@@ -21436,6 +21723,14 @@ function buildRelayMetadataEvent(payload) {
21436
21723
  retryOfJobId: readNonEmptyString(payload.retryOfJobId),
21437
21724
  ...relayModalMessage ? { modalMessage: relayModalMessage } : {},
21438
21725
  ...relayModalButtons && relayModalButtons.length > 0 ? { modalButtons: relayModalButtons } : {},
21726
+ // agent:waiting_choice (mission f1d25e11): carry the FULL structured question
21727
+ // payload across the machine boundary so a REMOTE worker's AskUserQuestion reaches
21728
+ // the coordinator with every question + option intact — the coordinator renders
21729
+ // these and answers with mesh_answer_question. The local in-process forward path
21730
+ // preserves the whole event for free; this mirrors the fields for the remote relay.
21731
+ ...payload.interactivePrompt && typeof payload.interactivePrompt === "object" && !Array.isArray(payload.interactivePrompt) ? { interactivePrompt: payload.interactivePrompt } : {},
21732
+ ...readNonEmptyString(payload.promptId) ? { promptId: readNonEmptyString(payload.promptId) } : {},
21733
+ ...payload.multiSelect === true ? { multiSelect: true } : {},
21439
21734
  ...payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? { result: payload.result } : {},
21440
21735
  ...payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {},
21441
21736
  ...payload.workerResult && typeof payload.workerResult === "object" && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {},
@@ -22855,7 +23150,8 @@ function drainAndDeliverApprovalNudges(meshId, drainDaemonIds, localDaemonId, me
22855
23150
  function assignedRowLiveStatusIsAwaitingApproval(mesh, nodeId, sessionId) {
22856
23151
  if (!nodeId || !sessionId) return false;
22857
23152
  try {
22858
- return sessionStatusFromNodes(mesh.nodes, nodeId, sessionId).status === "awaiting_approval";
23153
+ const liveStatus = sessionStatusFromNodes(mesh.nodes, nodeId, sessionId).status;
23154
+ return liveStatus === "awaiting_approval" || liveStatus === "awaiting_choice";
22859
23155
  } catch {
22860
23156
  return false;
22861
23157
  }
@@ -25336,8 +25632,15 @@ function buttonBlockApprovalCue(spec, text) {
25336
25632
  if (pickApprovalButton(labels).index < 0) return false;
25337
25633
  return hasNegativeApprovalOption(labels);
25338
25634
  }
25635
+ function isAskUserQuestionPickerSignature(text) {
25636
+ if (!text) return false;
25637
+ const hasSelectFooter = /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
25638
+ if (!hasSelectFooter) return false;
25639
+ return /Type something\.?|Chat about this/i.test(text);
25640
+ }
25339
25641
  function modalMatches(spec, input) {
25340
25642
  const text = input.screenText ?? "";
25643
+ if (isAskUserQuestionPickerSignature(text)) return false;
25341
25644
  const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
25342
25645
  if (question.test(text)) return true;
25343
25646
  for (const variant of spec.questionVariants ?? []) {
@@ -25479,6 +25782,12 @@ function compile3(re, flags) {
25479
25782
  throw new Error(`Invalid regex /${re}/${flags ?? ""}: ${e.message}`);
25480
25783
  }
25481
25784
  }
25785
+ function isAskUserQuestionPickerSignature2(text) {
25786
+ if (!text) return false;
25787
+ const hasSelectFooter = /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
25788
+ if (!hasSelectFooter) return false;
25789
+ return /Type something\.?|Chat about this/i.test(text);
25790
+ }
25482
25791
  function findQuestionLineIndex(spec, lines) {
25483
25792
  const primary = compile3(spec.questionPattern, spec.questionFlags ?? "i");
25484
25793
  const buttonFlags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
@@ -25603,6 +25912,7 @@ function buildParseApprovalFromTui(spec, visibleRegion) {
25603
25912
  return function parseApproval(input) {
25604
25913
  const rawText = input.screenText ?? input.buffer ?? "";
25605
25914
  if (!rawText) return null;
25915
+ if (isAskUserQuestionPickerSignature2(rawText)) return null;
25606
25916
  const text = visibleRegion ? applyVisibleRegion(visibleRegion, rawText) : rawText;
25607
25917
  const lines = text.split("\n");
25608
25918
  const question = findQuestionLineIndex(spec, lines);
@@ -30411,6 +30721,57 @@ function normalizeInteractivePromptResponse(raw) {
30411
30721
  }
30412
30722
  return { promptId, answers };
30413
30723
  }
30724
+ function resolveInteractivePromptResponse(prompt, raw) {
30725
+ if (!raw || typeof raw !== "object") throw new Error("Interactive prompt response must be an object");
30726
+ const record = raw;
30727
+ const promptId = readString(record.promptId);
30728
+ if (!promptId) throw new Error("promptId must be a non-empty string");
30729
+ if (promptId !== prompt.promptId) throw new Error("Interactive prompt response does not match active prompt");
30730
+ if (record.answers && typeof record.answers === "object" && !Array.isArray(record.answers)) {
30731
+ return normalizeInteractivePromptResponse(record);
30732
+ }
30733
+ if (!Array.isArray(record.answers)) throw new Error("answers must be an array or a questionId-keyed object");
30734
+ const resolveOneLabel = (question, sel) => {
30735
+ if (typeof sel === "number" && Number.isFinite(sel)) {
30736
+ const idx = Math.trunc(sel) - 1;
30737
+ const option = question.options[idx];
30738
+ if (!option) throw new Error(`Option index ${sel} out of range for ${question.questionId}`);
30739
+ return option.label;
30740
+ }
30741
+ const label = readString(sel);
30742
+ if (!label) throw new Error(`Empty selection for ${question.questionId}`);
30743
+ const exact = question.options.find((o) => o.label === label);
30744
+ if (exact) return exact.label;
30745
+ const asIndex = Number(label);
30746
+ if (Number.isInteger(asIndex)) {
30747
+ const option = question.options[asIndex - 1];
30748
+ if (option) return option.label;
30749
+ }
30750
+ throw new Error(`Unknown option for ${question.questionId}: ${label}`);
30751
+ };
30752
+ const answers = {};
30753
+ const entries = record.answers;
30754
+ entries.forEach((entryRaw, index) => {
30755
+ if (!entryRaw || typeof entryRaw !== "object" || Array.isArray(entryRaw)) return;
30756
+ const entry = entryRaw;
30757
+ const questionId = readString(entry.questionId);
30758
+ const question = (questionId ? prompt.questions.find((q) => q.questionId === questionId) : void 0) ?? prompt.questions[index];
30759
+ if (!question) throw new Error(`No matching question for answer entry ${index}`);
30760
+ const freeformText = readString(entry.freeform) ?? readString(entry.freeformText);
30761
+ const selectedLabels = [];
30762
+ const select = entry.select;
30763
+ if (Array.isArray(select)) {
30764
+ for (const sel of select) selectedLabels.push(resolveOneLabel(question, sel));
30765
+ } else if (select !== void 0 && select !== null) {
30766
+ selectedLabels.push(resolveOneLabel(question, select));
30767
+ }
30768
+ answers[question.questionId] = {
30769
+ selectedLabels,
30770
+ ...freeformText ? { freeformText } : {}
30771
+ };
30772
+ });
30773
+ return { promptId, answers };
30774
+ }
30414
30775
  function buildClaudeInteractiveToolResult(response) {
30415
30776
  return JSON.stringify({
30416
30777
  type: "user",
@@ -48803,6 +49164,7 @@ async function waitForCliAdapterReady(adapter, options) {
48803
49164
  var STATUS_HYDRATION_TAIL_LIMIT = 200;
48804
49165
  var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
48805
49166
  var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
49167
+ var CANON_C_MISSING_ASSISTANT_MIN_ELAPSED_MS = 2e4;
48806
49168
  var NATIVE_HISTORY_MESH_IDLE_SETTLE_MS = 4e3;
48807
49169
  var PTY_PARSED_FINAL_ASSISTANT_QUIET_DWELL_MS = 1200;
48808
49170
  var ANTIGRAVITY_HOLD_QUIET_DWELL_MS = 3e3;
@@ -49223,10 +49585,12 @@ var CliProviderInstance = class _CliProviderInstance {
49223
49585
  // `waiting_choice` overlay in getState(); the raw adapter status stays idle/generating,
49224
49586
  // so detectStatusTransition()'s status-keyed arms never fire and no push-worthy
49225
49587
  // agent:* event is emitted — the owner misses the "ACTION REQUIRED" prompt when the
49226
- // app is backgrounded. We emit one agent:waiting_approval on ENTRY into the prompt
49227
- // state (reusing the existing server push path, no cloud change) and dedupe on this
49228
- // key so repeated status ticks with the same prompt do not re-fire. '' means no
49229
- // prompt is currently active (cleared when the prompt is answered/gone).
49588
+ // app is backgrounded. We emit one agent:waiting_choice on ENTRY into the prompt
49589
+ // state (its own coordinator event, NOT the approval channel a multi-choice
49590
+ // question is answered with mesh_answer_question, never mesh_approve) carrying the
49591
+ // FULL InteractivePrompt payload, and dedupe on this key so repeated status ticks
49592
+ // with the same prompt do not re-fire. '' means no prompt is currently active
49593
+ // (cleared when the prompt is answered/gone).
49230
49594
  lastInteractivePromptEventKey = "";
49231
49595
  autoApproveBusy = false;
49232
49596
  autoApproveBusyTimer = null;
@@ -49871,7 +50235,7 @@ var CliProviderInstance = class _CliProviderInstance {
49871
50235
  }
49872
50236
  } else if (event === "interactive_prompt_response" && data) {
49873
50237
  try {
49874
- const response = normalizeInteractivePromptResponse(data);
50238
+ const response = this.activeInteractivePrompt && this.activeInteractivePrompt.promptId === data?.promptId && Array.isArray(data?.answers) ? resolveInteractivePromptResponse(this.activeInteractivePrompt, data) : normalizeInteractivePromptResponse(data);
49875
50239
  if (this.activeInteractivePrompt?.promptId === response.promptId) {
49876
50240
  this.activeInteractivePrompt = null;
49877
50241
  }
@@ -50410,7 +50774,12 @@ var CliProviderInstance = class _CliProviderInstance {
50410
50774
  return {
50411
50775
  reason: "missing_final_assistant",
50412
50776
  terminal: this.provider.requiresFinalAssistantBeforeIdle === true,
50413
- allowTimeout: allowMissingAssistantTimeout
50777
+ allowTimeout: allowMissingAssistantTimeout,
50778
+ // PTY-parsed provider (codex-cli): no external transcript trails the idle
50779
+ // transition, so the CANON-C decoupled emit must observe the min-elapsed floor
50780
+ // rather than fire at the first-poll waitedMs. (claude-cli's external-native
50781
+ // branch above is exempt — its transcript legitimately lands moments later.)
50782
+ noExternalTranscriptSource: true
50414
50783
  };
50415
50784
  }
50416
50785
  }
@@ -50908,6 +51277,24 @@ var CliProviderInstance = class _CliProviderInstance {
50908
51277
  this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
50909
51278
  return;
50910
51279
  }
51280
+ if (isTranscriptEvidenceGate && blockReason === "missing_final_assistant" && block2.noExternalTranscriptSource === true && waitedMs < CANON_C_MISSING_ASSISTANT_MIN_ELAPSED_MS) {
51281
+ if (pending.loggedBlockReason !== "canon_c_min_elapsed_floor") {
51282
+ LOG.info("CLI", `[${this.type}] holding CANON-C decoupled emit until min-elapsed floor (waitedMs=${waitedMs} floor=${CANON_C_MISSING_ASSISTANT_MIN_ELAPSED_MS}); no final assistant yet (${blockReason})`);
51283
+ if (this.isMeshWorkerSession()) {
51284
+ traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `canon_c_min_elapsed_floor waited=${waitedMs}ms`);
51285
+ }
51286
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("hold", {
51287
+ blockReason,
51288
+ latestVisibleStatus,
51289
+ terminal: block2.terminal === true,
51290
+ canonCMinElapsedFloor: true,
51291
+ waitedMs
51292
+ });
51293
+ pending.loggedBlockReason = "canon_c_min_elapsed_floor";
51294
+ }
51295
+ this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
51296
+ return;
51297
+ }
50911
51298
  if (this.type === "antigravity-cli" && block2.holdForTranscript === true && waitedMs < ANTIGRAVITY_HOLD_HARD_CAP_MS && this.antigravityHoldPtyStillActive()) {
50912
51299
  if (pending.loggedBlockReason !== "antigravity_hold_pty_active") {
50913
51300
  LOG.info("CLI", `[${this.type}] 30s cap reached but PTY still generating; holding antigravity completion past cap (waitedMs=${waitedMs} hardCap=${ANTIGRAVITY_HOLD_HARD_CAP_MS}) (${blockReason})`);
@@ -51607,9 +51994,16 @@ ${buttons.join("\n")}`;
51607
51994
  const modalMessage = firstQuestion ? firstQuestion.header ? `${firstQuestion.header}: ${firstQuestion.question}` : firstQuestion.question : void 0;
51608
51995
  const modalButtons = firstQuestion?.options?.map((option) => option.label);
51609
51996
  this.pushEvent({
51610
- event: "agent:waiting_approval",
51997
+ event: "agent:waiting_choice",
51611
51998
  chatTitle,
51612
51999
  timestamp: now,
52000
+ // Structured, authoritative payload — the coordinator renders these and
52001
+ // answers via mesh_answer_question. Carried whole (all questions, all
52002
+ // options) so multi-question / multi-select prompts round-trip fully.
52003
+ interactivePrompt,
52004
+ promptId: interactivePrompt.promptId,
52005
+ multiSelect: firstQuestion?.multiSelect === true,
52006
+ // Push-notification-friendly projection (server push path reads these).
51613
52007
  modalMessage,
51614
52008
  modalButtons
51615
52009
  });
@@ -60521,6 +60915,50 @@ var meshHostPairingHandlers = {
60521
60915
  };
60522
60916
 
60523
60917
  // src/commands/med-family/mesh-queue.ts
60918
+ init_logger();
60919
+ async function stopCancelledTaskWorker(ctx, meshId, prior) {
60920
+ const { sessionId, nodeId, providerType } = prior;
60921
+ const stopArgs = {
60922
+ targetSessionId: sessionId,
60923
+ ...providerType ? { cliType: providerType } : {},
60924
+ mode: "hard",
60925
+ reason: "mesh_task_cancelled"
60926
+ };
60927
+ try {
60928
+ const cliManager = ctx.deps.cliManager;
60929
+ const isLocal = cliManager?.adapters?.has?.(sessionId) === true;
60930
+ if (isLocal) {
60931
+ if (!stopArgs.cliType) {
60932
+ const localType = cliManager?.adapters?.get?.(sessionId)?.cliType;
60933
+ if (localType) stopArgs.cliType = localType;
60934
+ }
60935
+ await Promise.resolve(cliManager?.handleCliCommand?.("stop_cli", stopArgs)).catch((e) => LOG.warn("MeshQueue", `Local stop of cancelled worker ${sessionId} failed: ${e?.message || e}`));
60936
+ return;
60937
+ }
60938
+ const dispatch = ctx.deps.dispatchMeshCommand;
60939
+ let daemonId;
60940
+ if (nodeId) {
60941
+ try {
60942
+ const [{ getMesh: getMesh2 }, { meshNodeIdMatches: meshNodeIdMatches2 }, { readMeshNodeDaemonId: readMeshNodeDaemonId2 }] = await Promise.all([
60943
+ Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports)),
60944
+ Promise.resolve().then(() => (init_dist(), dist_exports)),
60945
+ Promise.resolve().then(() => (init_mesh_node_identity(), mesh_node_identity_exports))
60946
+ ]);
60947
+ const mesh = getMesh2(meshId);
60948
+ const node = mesh?.nodes?.find((n) => meshNodeIdMatches2(n, nodeId));
60949
+ daemonId = node ? readMeshNodeDaemonId2(node) || void 0 : void 0;
60950
+ } catch {
60951
+ }
60952
+ }
60953
+ if (daemonId && dispatch) {
60954
+ await Promise.resolve(dispatch(daemonId, "stop_cli", stopArgs)).catch((e) => LOG.warn("MeshQueue", `Remote stop of cancelled worker ${sessionId} on daemon ${daemonId} failed: ${e?.message || e}`));
60955
+ } else {
60956
+ LOG.warn("MeshQueue", `Cannot stop cancelled worker ${sessionId}: no local adapter and no resolvable remote daemon id (node ${nodeId ?? "?"}). Row is already terminal; if the worker completes it strand-fails harmlessly.`);
60957
+ }
60958
+ } catch (e) {
60959
+ LOG.warn("MeshQueue", `stopCancelledTaskWorker error for ${sessionId}: ${e?.message || e}`);
60960
+ }
60961
+ }
60524
60962
  var meshQueueHandlers = {
60525
60963
  get_mesh_queue: async (_ctx, args) => {
60526
60964
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
@@ -60554,10 +60992,14 @@ var meshQueueHandlers = {
60554
60992
  const ownerFailure = await ctx.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue cancellation");
60555
60993
  if (ownerFailure) return ownerFailure;
60556
60994
  try {
60557
- const { cancelTask: cancelTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
60995
+ const { cancelTask: cancelTask2, takeCancelledTaskAssignment: takeCancelledTaskAssignment2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
60558
60996
  const reason = typeof args?.reason === "string" ? args.reason : void 0;
60559
60997
  const task = cancelTask2(meshId, taskId, { reason });
60560
60998
  if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
60999
+ const prior = takeCancelledTaskAssignment2(meshId, taskId);
61000
+ if (prior?.sessionId) {
61001
+ void stopCancelledTaskWorker(ctx, meshId, prior);
61002
+ }
60561
61003
  return { success: true, task };
60562
61004
  } catch (e) {
60563
61005
  return { success: false, error: e.message };
@@ -61036,10 +61478,12 @@ var meshEventsHandlers = {
61036
61478
  interactive_prompt_response: async (ctx, args) => {
61037
61479
  const sessionId = typeof args?.targetSessionId === "string" && args.targetSessionId.trim() ? args.targetSessionId.trim() : typeof args?.sessionId === "string" && args.sessionId.trim() ? args.sessionId.trim() : "";
61038
61480
  if (!sessionId) return { success: false, error: "targetSessionId required" };
61039
- const response = normalizeInteractivePromptResponse(args?.response ?? args);
61481
+ const rawResponse = args?.response ?? args;
61040
61482
  const instance = ctx.deps.instanceManager.getInstance(sessionId);
61041
61483
  if (!instance) return { success: false, error: `No running instance for session ${sessionId}` };
61042
- ctx.deps.instanceManager.sendEvent(sessionId, "interactive_prompt_response", response);
61484
+ const isFriendlyArrayForm = rawResponse && typeof rawResponse === "object" && Array.isArray(rawResponse.answers);
61485
+ const payload = isFriendlyArrayForm ? rawResponse : normalizeInteractivePromptResponse(rawResponse);
61486
+ ctx.deps.instanceManager.sendEvent(sessionId, "interactive_prompt_response", payload);
61043
61487
  return { success: true };
61044
61488
  }
61045
61489
  };
@@ -63104,6 +63548,171 @@ function collectFastForwardGitlinkPaths(repoRoot, baseHead, branchHead) {
63104
63548
  return isSubmoduleFastForward((0, import_path15.resolve)(repoRoot, path46), baseCommit, branchCommit);
63105
63549
  });
63106
63550
  }
63551
+ function isSubmoduleDivergedSibling(submoduleRepoPath, baseCommit, branchCommit) {
63552
+ if (!baseCommit || !branchCommit || baseCommit === branchCommit) return false;
63553
+ try {
63554
+ if (!fs34.existsSync(submoduleRepoPath)) return false;
63555
+ (0, import_node_child_process6.execFileSync)(GIT2, ["cat-file", "-e", `${baseCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
63556
+ (0, import_node_child_process6.execFileSync)(GIT2, ["cat-file", "-e", `${branchCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
63557
+ } catch {
63558
+ return false;
63559
+ }
63560
+ try {
63561
+ (0, import_node_child_process6.execFileSync)(GIT2, ["merge-base", "--is-ancestor", baseCommit, branchCommit], { cwd: submoduleRepoPath, stdio: "ignore" });
63562
+ return false;
63563
+ } catch {
63564
+ }
63565
+ try {
63566
+ (0, import_node_child_process6.execFileSync)(GIT2, ["merge-base", "--is-ancestor", branchCommit, baseCommit], { cwd: submoduleRepoPath, stdio: "ignore" });
63567
+ return false;
63568
+ } catch {
63569
+ }
63570
+ try {
63571
+ const mb = (0, import_node_child_process6.execFileSync)(GIT2, ["merge-base", baseCommit, branchCommit], { cwd: submoduleRepoPath, encoding: "utf8" }).trim();
63572
+ return !!mb;
63573
+ } catch {
63574
+ return false;
63575
+ }
63576
+ }
63577
+ function ensureSubmoduleCommitLocal(submoduleRepoPath, baseSubmoduleRepoPath, commit) {
63578
+ if (!commit) return;
63579
+ try {
63580
+ (0, import_node_child_process6.execFileSync)(GIT2, ["cat-file", "-e", `${commit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
63581
+ return;
63582
+ } catch {
63583
+ }
63584
+ try {
63585
+ if (!fs34.existsSync(submoduleRepoPath) || !fs34.existsSync(baseSubmoduleRepoPath)) return;
63586
+ (0, import_node_child_process6.execFileSync)(GIT2, ["-c", "protocol.file.allow=always", "fetch", "-q", baseSubmoduleRepoPath, "+refs/heads/*:refs/adhdev-refine-base/*"], {
63587
+ cwd: submoduleRepoPath,
63588
+ stdio: ["ignore", "ignore", "pipe"]
63589
+ });
63590
+ } catch {
63591
+ }
63592
+ }
63593
+ function convergeDivergedSubmoduleGitlinks(worktreeRoot, baseRepoRoot, baseHead, branchHead) {
63594
+ const changed = readChangedGitlinkPaths(worktreeRoot, baseHead, branchHead);
63595
+ if (changed.length === 0) {
63596
+ return { converged: false, reason: "no_changed_gitlinks", resolutions: [], gitlinks: [] };
63597
+ }
63598
+ const gitlinks = [];
63599
+ const resolutions = [];
63600
+ let sawDiverged = false;
63601
+ for (const path46 of changed) {
63602
+ const baseCommit = readTreeObject(baseRepoRoot, baseHead, path46);
63603
+ const branchCommit = readTreeObject(worktreeRoot, branchHead, path46);
63604
+ const submoduleRepoPath = (0, import_path15.resolve)(worktreeRoot, path46);
63605
+ if (baseCommit) {
63606
+ ensureSubmoduleCommitLocal(submoduleRepoPath, (0, import_path15.resolve)(baseRepoRoot, path46), baseCommit);
63607
+ }
63608
+ if (!baseCommit || !branchCommit || !isSubmoduleDivergedSibling(submoduleRepoPath, baseCommit, branchCommit)) {
63609
+ gitlinks.push({ path: path46, baseCommit, branchCommit, action: "skipped_not_diverged" });
63610
+ continue;
63611
+ }
63612
+ sawDiverged = true;
63613
+ let rebasedCommit;
63614
+ try {
63615
+ (0, import_node_child_process6.execFileSync)(GIT2, ["checkout", "-q", "--detach", branchCommit], { cwd: submoduleRepoPath, stdio: ["ignore", "ignore", "pipe"] });
63616
+ (0, import_node_child_process6.execFileSync)(GIT2, ["rebase", baseCommit], { cwd: submoduleRepoPath, stdio: ["ignore", "pipe", "pipe"] });
63617
+ rebasedCommit = (0, import_node_child_process6.execFileSync)(GIT2, ["rev-parse", "HEAD"], { cwd: submoduleRepoPath, encoding: "utf8" }).trim();
63618
+ } catch {
63619
+ try {
63620
+ (0, import_node_child_process6.execFileSync)(GIT2, ["rebase", "--abort"], { cwd: submoduleRepoPath, stdio: "ignore" });
63621
+ } catch {
63622
+ }
63623
+ try {
63624
+ (0, import_node_child_process6.execFileSync)(GIT2, ["checkout", "-q", "--detach", branchCommit], { cwd: submoduleRepoPath, stdio: "ignore" });
63625
+ } catch {
63626
+ }
63627
+ gitlinks.push({ path: path46, baseCommit, branchCommit, action: "rebase_conflict" });
63628
+ return { converged: false, reason: "rebase_conflict", resolutions: [], gitlinks };
63629
+ }
63630
+ gitlinks.push({ path: path46, baseCommit, branchCommit, rebasedCommit, action: "rebased" });
63631
+ resolutions.push({ path: path46, baseCommit, branchCommit, rebasedCommit });
63632
+ }
63633
+ if (!sawDiverged) {
63634
+ return { converged: false, reason: "not_diverged", resolutions: [], gitlinks };
63635
+ }
63636
+ return { converged: resolutions.length > 0, resolutions, gitlinks };
63637
+ }
63638
+ function rootRebaseResolvingGitlinks(worktreeRoot, baseHead, resolutions) {
63639
+ const resolveByPath = new Map(resolutions.map((r) => [r.path, r.rebasedCommit]));
63640
+ const runRebase = (args) => {
63641
+ try {
63642
+ (0, import_node_child_process6.execFileSync)(GIT2, args, {
63643
+ cwd: worktreeRoot,
63644
+ stdio: ["ignore", "pipe", "pipe"],
63645
+ // A rebase editor prompt would hang; keep it non-interactive.
63646
+ env: { ...process.env, GIT_EDITOR: "true", GIT_SEQUENCE_EDITOR: "true" }
63647
+ });
63648
+ return { ok: true };
63649
+ } catch {
63650
+ return { ok: false };
63651
+ }
63652
+ };
63653
+ const unmergedPaths = () => {
63654
+ try {
63655
+ return (0, import_node_child_process6.execFileSync)(GIT2, ["diff", "--name-only", "--diff-filter=U"], { cwd: worktreeRoot, encoding: "utf8" }).split("\n").map((s2) => s2.trim()).filter(Boolean);
63656
+ } catch {
63657
+ return [];
63658
+ }
63659
+ };
63660
+ const abort = (reason, conflictPaths) => {
63661
+ try {
63662
+ (0, import_node_child_process6.execFileSync)(GIT2, ["rebase", "--abort"], { cwd: worktreeRoot, stdio: "ignore" });
63663
+ } catch {
63664
+ }
63665
+ return { ok: false, reason, conflictPaths };
63666
+ };
63667
+ let progress = runRebase(["rebase", baseHead]);
63668
+ let guard = 0;
63669
+ while (!progress.ok) {
63670
+ if (guard++ > 100) return abort("rebase_error");
63671
+ const conflicts = unmergedPaths();
63672
+ if (conflicts.length === 0) {
63673
+ return abort("rebase_error");
63674
+ }
63675
+ const unresolvable = conflicts.filter((p) => !resolveByPath.has(p));
63676
+ if (unresolvable.length > 0) {
63677
+ const unresolvableGitlink = unresolvable.some((p) => {
63678
+ try {
63679
+ const staged = (0, import_node_child_process6.execFileSync)(GIT2, ["ls-files", "--stage", "--", p], { cwd: worktreeRoot, encoding: "utf8" });
63680
+ return /^160000\s/m.test(staged);
63681
+ } catch {
63682
+ return false;
63683
+ }
63684
+ });
63685
+ const allGitlink = unresolvable.every((p) => {
63686
+ try {
63687
+ const staged = (0, import_node_child_process6.execFileSync)(GIT2, ["ls-files", "--stage", "--", p], { cwd: worktreeRoot, encoding: "utf8" });
63688
+ return /^160000\s/m.test(staged);
63689
+ } catch {
63690
+ return false;
63691
+ }
63692
+ });
63693
+ return abort(allGitlink && unresolvableGitlink ? "unexpected_gitlink" : "non_gitlink_conflict", conflicts);
63694
+ }
63695
+ for (const p of conflicts) {
63696
+ const commit = resolveByPath.get(p);
63697
+ try {
63698
+ (0, import_node_child_process6.execFileSync)(GIT2, ["checkout", "-q", "--detach", commit], { cwd: (0, import_path15.resolve)(worktreeRoot, p), stdio: "ignore" });
63699
+ } catch {
63700
+ }
63701
+ try {
63702
+ (0, import_node_child_process6.execFileSync)(GIT2, ["add", p], { cwd: worktreeRoot, stdio: "ignore" });
63703
+ } catch {
63704
+ return abort("rebase_error", conflicts);
63705
+ }
63706
+ }
63707
+ progress = runRebase(["rebase", "--continue"]);
63708
+ }
63709
+ let branchHead;
63710
+ try {
63711
+ branchHead = (0, import_node_child_process6.execFileSync)(GIT2, ["rev-parse", "HEAD"], { cwd: worktreeRoot, encoding: "utf8" }).trim();
63712
+ } catch {
63713
+ }
63714
+ return { ok: true, branchHead };
63715
+ }
63107
63716
  function evaluateGitlinkTrivialFastForward(repoRoot, baseHead, branchHead) {
63108
63717
  const changedGitlinks = readChangedGitlinkPaths(repoRoot, baseHead, branchHead).map((path46) => {
63109
63718
  const baseCommit = readTreeObject(repoRoot, baseHead, path46);
@@ -64135,6 +64744,7 @@ async function computeBranchBaseDivergence(execFileAsync4, cwd, baseHead, branch
64135
64744
  async function refineSyncBaseStage(self, ctx) {
64136
64745
  const { repoRoot, baseHead, node, branch, baseBranch, refineStages, execFileAsync: execFileAsync4 } = ctx;
64137
64746
  let branchHead = ctx.branchHead;
64747
+ let gitlinkResolutions = [];
64138
64748
  const syncStarted = Date.now();
64139
64749
  const divergence = await computeBranchBaseDivergence(execFileAsync4, node.workspace, baseHead, branchHead);
64140
64750
  if (divergence.behind === 0) {
@@ -64150,24 +64760,61 @@ async function refineSyncBaseStage(self, ctx) {
64150
64760
  const preRebasePe = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
64151
64761
  const alreadyMerged = !preRebasePe.actualPatchId && !!preRebasePe.expectedPatchId;
64152
64762
  const submoduleConflict = preRebasePe.actionableHint?.kind === "submodule_conflict";
64153
- if (alreadyMerged || submoduleConflict) {
64763
+ if (alreadyMerged) {
64154
64764
  recordMeshRefineStage(refineStages, "sync_base", "passed", syncStarted, {
64155
64765
  ahead: divergence.ahead,
64156
64766
  behind: divergence.behind,
64157
64767
  rebased: false,
64158
- reason: alreadyMerged ? "already_merged_via_other_path_skip_rebase" : "submodule_conflict_defer_to_patch_equivalence"
64768
+ reason: "already_merged_via_other_path_skip_rebase"
64159
64769
  });
64160
64770
  return { kind: "continue", ctx };
64161
64771
  }
64772
+ if (submoduleConflict) {
64773
+ const converge = convergeDivergedSubmoduleGitlinks(node.workspace, repoRoot, baseHead, branchHead);
64774
+ if (converge.converged) {
64775
+ gitlinkResolutions = converge.resolutions;
64776
+ recordMeshRefineStage(refineStages, "submodule_gitlink_converge", "passed", syncStarted, {
64777
+ reason: "submodule_diverged_auto_rebased",
64778
+ gitlinks: converge.gitlinks
64779
+ });
64780
+ LOG.info("Mesh", `[Refinery] Auto-converged diverged submodule gitlink(s) onto base for node ${node.id}: ` + converge.resolutions.map((r) => `${r.path}\u2192${r.rebasedCommit.slice(0, 12)}`).join(", "));
64781
+ } else {
64782
+ recordMeshRefineStage(refineStages, "submodule_gitlink_converge", "skipped", syncStarted, {
64783
+ reason: "submodule_conflict_defer_to_patch_equivalence",
64784
+ convergeReason: converge.reason,
64785
+ gitlinks: converge.gitlinks
64786
+ });
64787
+ recordMeshRefineStage(refineStages, "sync_base", "passed", syncStarted, {
64788
+ ahead: divergence.ahead,
64789
+ behind: divergence.behind,
64790
+ rebased: false,
64791
+ reason: "submodule_conflict_defer_to_patch_equivalence"
64792
+ });
64793
+ return { kind: "continue", ctx };
64794
+ }
64795
+ }
64162
64796
  } catch {
64163
64797
  }
64164
64798
  const rebaseStarted = Date.now();
64165
64799
  try {
64166
- (0, import_node_child_process7.execFileSync)("git", ["rebase", baseHead], { cwd: node.workspace, stdio: ["ignore", "pipe", "pipe"] });
64800
+ if (gitlinkResolutions.length > 0) {
64801
+ const gitlinkRebase = rootRebaseResolvingGitlinks(node.workspace, baseHead, gitlinkResolutions);
64802
+ if (!gitlinkRebase.ok) {
64803
+ const err = new Error(`gitlink-aware rebase aborted: ${gitlinkRebase.reason || "unknown"}`);
64804
+ err.gitlinkRebaseReason = gitlinkRebase.reason;
64805
+ err.gitlinkRebaseConflicts = gitlinkRebase.conflictPaths;
64806
+ err.alreadyAborted = true;
64807
+ throw err;
64808
+ }
64809
+ } else {
64810
+ (0, import_node_child_process7.execFileSync)("git", ["rebase", baseHead], { cwd: node.workspace, stdio: ["ignore", "pipe", "pipe"] });
64811
+ }
64167
64812
  } catch (rebaseErr) {
64168
- try {
64169
- (0, import_node_child_process7.execFileSync)("git", ["rebase", "--abort"], { cwd: node.workspace, stdio: "ignore" });
64170
- } catch {
64813
+ if (!rebaseErr?.alreadyAborted) {
64814
+ try {
64815
+ (0, import_node_child_process7.execFileSync)("git", ["rebase", "--abort"], { cwd: node.workspace, stdio: "ignore" });
64816
+ } catch {
64817
+ }
64171
64818
  }
64172
64819
  let submoduleHintPatchEquivalence;
64173
64820
  try {
@@ -66486,7 +67133,15 @@ var MESH_FORWARDABLE_SESSION_COMMANDS = /* @__PURE__ */ new Set([
66486
67133
  // returns 'Session not found' (same class as mission 6938892f). Unlike read_terminal this
66487
67134
  // MUTATES the worker PTY, so forwarding to the real owner (not a wrong local session) is
66488
67135
  // doubly important. The daemon re-enforces the destructive-key confirm gate after the forward.
66489
- "send_keys"
67136
+ "send_keys",
67137
+ // interactive_prompt_response (mesh_answer_question, mission f1d25e11): the coordinator
67138
+ // answers a REMOTE worker's AskUserQuestion (waiting_choice). The answer must reach the
67139
+ // OWNING worker session's live instance — its activeInteractivePrompt (the authoritative
67140
+ // prompt the labels/indexes resolve against) and its adapter.setInteractivePromptResponse
67141
+ // live only there. Without forwarding, the coordinator's local high-family handler returns
67142
+ // 'No running instance for session …' and the question is never answered — the exact
67143
+ // remote-worker forwarding gap of mission 6938892f, now closed for questions too.
67144
+ "interactive_prompt_response"
66490
67145
  ]);
66491
67146
  function normalizeCommandSource(source) {
66492
67147
  switch (source) {