@adhdev/daemon-core 0.9.82-rc.508 → 0.9.82-rc.509

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.
@@ -7,6 +7,18 @@
7
7
  */
8
8
  import type { LocalMeshEntry, LocalMeshNodeEntry, RepoMeshPolicy, RepoMeshNodePolicy, RepoMeshNodeCapabilities, RepoMeshCoordinatorConfig, RepoMeshHostMetadata, RepoMeshDaemonRole } from '../repo-mesh-types.js';
9
9
  import type { MagiKindPanelMap, MagiSlot, DifficultyBrainMap } from '@adhdev/mesh-shared';
10
+ /**
11
+ * Migrate a node policy's legacy `providerRoles` cap onto `slots[].maxParallel`,
12
+ * in place, then delete the `providerRoles` field. Defensive against malformed
13
+ * entries. Returns true when the policy was mutated.
14
+ *
15
+ * Behavior-preserving: the resulting slots carry the same per-(node, provider)
16
+ * cap the queue previously enforced from providerRoles. When the node had no
17
+ * explicit slots, slots are derived from the legacy providerPriority (folding the
18
+ * caps in via deriveSlotsFromLegacy-equivalent logic); when it did, each cap is
19
+ * merged into the first matching-provider slot lacking a maxParallel.
20
+ */
21
+ export declare function migrateProviderRolesToSlots(policy: unknown): boolean;
10
22
  export declare function normalizeCapabilityTags(value: unknown): string[] | undefined;
11
23
  /**
12
24
  * Normalize a Git remote URL into a stable identity string.
package/dist/index.js CHANGED
@@ -122,20 +122,20 @@ function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy) {
122
122
  }
123
123
  return true;
124
124
  }
125
- function resolveProviderMaxParallel(nodePolicy, providerType) {
125
+ function resolveProviderMaxParallel(slots, providerType) {
126
126
  const wanted = typeof providerType === "string" ? providerType.trim().toLowerCase() : "";
127
127
  if (!wanted) return void 0;
128
- const roles = nodePolicy?.providerRoles;
129
- if (!Array.isArray(roles)) return void 0;
130
- for (const entry of roles) {
131
- if (!entry || typeof entry !== "object") continue;
132
- const type = typeof entry.providerType === "string" ? entry.providerType.trim().toLowerCase() : "";
128
+ if (!Array.isArray(slots)) return void 0;
129
+ let total;
130
+ for (const slot of slots) {
131
+ if (!slot || typeof slot !== "object") continue;
132
+ const type = typeof slot.provider === "string" ? slot.provider.trim().toLowerCase() : "";
133
133
  if (!type || type !== wanted) continue;
134
- const raw = Number(entry.maxParallel);
135
- if (!Number.isFinite(raw) || raw < 0) return void 0;
136
- return Math.floor(raw);
134
+ const raw = Number(slot.maxParallel);
135
+ if (!Number.isFinite(raw) || raw < 0) continue;
136
+ total = (total ?? 0) + Math.floor(raw);
137
137
  }
138
- return void 0;
138
+ return total;
139
139
  }
140
140
  var MESH_SCHEDULING_STRATEGIES, DEFAULT_MESH_SCHEDULING_STRATEGY, MESH_CONVERGE_REFINE_TAG, MESH_CONVERGE_FAST_FORWARD_TAG, DEFAULT_MESH_POLICY, SESSION_CLEANUP_MODES, SPAWNED_SESSION_VISIBILITY_MODES, DIRTY_WORKSPACE_BEHAVIORS, MESH_MAX_PARALLEL_TASKS_MIN, MESH_MAX_PARALLEL_TASKS_MAX, DEFAULT_MESH_READONLY_MULTIPLIER;
141
141
  var init_repo_mesh_types = __esm({
@@ -417,10 +417,10 @@ function readInjected(value) {
417
417
  }
418
418
  function getDaemonBuildInfo() {
419
419
  if (cached) return cached;
420
- const commit = readInjected(true ? "3c72ac19ec9253071d614cd2410dc9f8d555eb26" : void 0) ?? "unknown";
421
- const commitShort = readInjected(true ? "3c72ac19" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
422
- const version = readInjected(true ? "0.9.82-rc.508" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
423
- const builtAt = readInjected(true ? "2026-07-12T23:18:59.856Z" : void 0);
420
+ const commit = readInjected(true ? "8ebe4d4bcdc658e11253a51a28237d392aa4d978" : void 0) ?? "unknown";
421
+ const commitShort = readInjected(true ? "8ebe4d4b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
422
+ const version = readInjected(true ? "0.9.82-rc.509" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
423
+ const builtAt = readInjected(true ? "2026-07-13T02:51:57.282Z" : void 0);
424
424
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
425
425
  return cached;
426
426
  }
@@ -2848,12 +2848,6 @@ function normalizeNodeCapabilitySlots(raw) {
2848
2848
  function deriveSlotsFromLegacy(input) {
2849
2849
  const priority = Array.isArray(input.providerPriority) ? input.providerPriority.filter((p) => typeof p === "string" && !!p.trim()).map((p) => p.trim()) : [];
2850
2850
  if (priority.length === 0) return [];
2851
- const roleCap = /* @__PURE__ */ new Map();
2852
- for (const role of input.providerRoles || []) {
2853
- if (role && typeof role.providerType === "string" && Number.isFinite(role.maxParallel)) {
2854
- roleCap.set(role.providerType.trim(), Math.floor(Number(role.maxParallel)));
2855
- }
2856
- }
2857
2851
  const brains = input.difficultyBrains || {};
2858
2852
  const byProvider = /* @__PURE__ */ new Map();
2859
2853
  const shared = [];
@@ -2875,13 +2869,11 @@ function deriveSlotsFromLegacy(input) {
2875
2869
  const difficulty = applied.map((a) => a.difficulty);
2876
2870
  const model = applied.find((a) => a.model)?.model;
2877
2871
  const thinkingLevel = applied.find((a) => a.thinkingLevel)?.thinkingLevel;
2878
- const maxParallel = roleCap.get(provider);
2879
2872
  return {
2880
2873
  provider,
2881
2874
  ...model ? { model } : {},
2882
2875
  ...thinkingLevel ? { thinkingLevel } : {},
2883
- ...difficulty.length ? { difficulty } : {},
2884
- ...maxParallel !== void 0 ? { maxParallel } : {}
2876
+ ...difficulty.length ? { difficulty } : {}
2885
2877
  };
2886
2878
  });
2887
2879
  }
@@ -3049,6 +3041,7 @@ __export(mesh_config_exports, {
3049
3041
  listMagiKindPanels: () => listMagiKindPanels,
3050
3042
  listMeshes: () => listMeshes,
3051
3043
  markMeshHostPairingJoined: () => markMeshHostPairingJoined,
3044
+ migrateProviderRolesToSlots: () => migrateProviderRolesToSlots,
3052
3045
  normalizeCapabilityTags: () => normalizeCapabilityTags,
3053
3046
  normalizeMagiSlots: () => normalizeMagiSlots,
3054
3047
  normalizeRepoIdentity: () => normalizeRepoIdentity,
@@ -3087,23 +3080,51 @@ function migrateLoadedMeshConfig(config) {
3087
3080
  for (const mesh of config.meshes) {
3088
3081
  if (!mesh || !Array.isArray(mesh.nodes)) continue;
3089
3082
  for (const node of mesh.nodes) {
3090
- if (stripDeadRoleFromProviderRoles(node?.policy)) changed = true;
3083
+ if (migrateProviderRolesToSlots(node?.policy)) changed = true;
3091
3084
  }
3092
3085
  }
3093
3086
  return changed;
3094
3087
  }
3095
- function stripDeadRoleFromProviderRoles(policy) {
3096
- if (!policy || typeof policy !== "object") return false;
3097
- const roles = policy.providerRoles;
3098
- if (!Array.isArray(roles)) return false;
3099
- let changed = false;
3100
- for (const entry of roles) {
3101
- if (entry && typeof entry === "object" && !Array.isArray(entry) && Object.prototype.hasOwnProperty.call(entry, "role")) {
3102
- delete entry.role;
3103
- changed = true;
3088
+ function migrateProviderRolesToSlots(policy) {
3089
+ if (!policy || typeof policy !== "object" || Array.isArray(policy)) return false;
3090
+ const p = policy;
3091
+ const rawRoles = p.providerRoles;
3092
+ if (!Array.isArray(rawRoles)) return false;
3093
+ const roleCap = /* @__PURE__ */ new Map();
3094
+ for (const entry of rawRoles) {
3095
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
3096
+ const rec = entry;
3097
+ const provider = typeof rec.providerType === "string" ? rec.providerType.trim() : "";
3098
+ if (!provider) continue;
3099
+ const cap = Number(rec.maxParallel);
3100
+ if (!Number.isFinite(cap) || cap < 0) continue;
3101
+ roleCap.set(provider.toLowerCase(), { provider, cap: Math.floor(cap) });
3102
+ }
3103
+ const explicitSlots = Array.isArray(p.slots) ? normalizeNodeCapabilitySlots(p.slots) : [];
3104
+ if (explicitSlots.length) {
3105
+ for (const { provider, cap } of roleCap.values()) {
3106
+ const target = explicitSlots.find((s2) => s2.provider.trim().toLowerCase() === provider.toLowerCase() && s2.maxParallel === void 0);
3107
+ if (target) target.maxParallel = cap;
3108
+ }
3109
+ p.slots = explicitSlots;
3110
+ } else if (roleCap.size) {
3111
+ let difficultyBrains;
3112
+ try {
3113
+ difficultyBrains = getDifficultyBrains();
3114
+ } catch {
3115
+ difficultyBrains = void 0;
3116
+ }
3117
+ const priority = Array.isArray(p.providerPriority) ? p.providerPriority.map((t) => typeof t === "string" ? t.trim() : "").filter(Boolean) : [];
3118
+ const derived = deriveSlotsFromLegacy({ providerPriority: priority, difficultyBrains });
3119
+ const slots = derived.length ? derived : [...roleCap.values()].map((r) => ({ provider: r.provider }));
3120
+ for (const slot of slots) {
3121
+ const match = roleCap.get(slot.provider.trim().toLowerCase());
3122
+ if (match && slot.maxParallel === void 0) slot.maxParallel = match.cap;
3104
3123
  }
3124
+ p.slots = slots;
3105
3125
  }
3106
- return changed;
3126
+ delete p.providerRoles;
3127
+ return true;
3107
3128
  }
3108
3129
  function normalizeCapabilityTags(value) {
3109
3130
  if (!Array.isArray(value)) return void 0;
@@ -3561,6 +3582,39 @@ var init_mesh_config = __esm({
3561
3582
  }
3562
3583
  });
3563
3584
 
3585
+ // src/mesh/mesh-node-slots.ts
3586
+ function normalizeProviderPriority(policy) {
3587
+ const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
3588
+ if (!Array.isArray(raw)) return [];
3589
+ const seen = /* @__PURE__ */ new Set();
3590
+ return raw.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean).filter((type) => {
3591
+ if (seen.has(type)) return false;
3592
+ seen.add(type);
3593
+ return true;
3594
+ });
3595
+ }
3596
+ function resolveNodeCapabilitySlots(node) {
3597
+ const explicit = normalizeNodeCapabilitySlots(node?.policy?.slots);
3598
+ if (explicit.length) return explicit;
3599
+ let difficultyBrains;
3600
+ try {
3601
+ difficultyBrains = getDifficultyBrains();
3602
+ } catch {
3603
+ difficultyBrains = void 0;
3604
+ }
3605
+ return deriveSlotsFromLegacy({
3606
+ providerPriority: normalizeProviderPriority(node?.policy),
3607
+ difficultyBrains
3608
+ });
3609
+ }
3610
+ var init_mesh_node_slots = __esm({
3611
+ "src/mesh/mesh-node-slots.ts"() {
3612
+ "use strict";
3613
+ init_dist();
3614
+ init_mesh_config();
3615
+ }
3616
+ });
3617
+
3564
3618
  // src/mesh/coordinator-prompt.ts
3565
3619
  var coordinator_prompt_exports = {};
3566
3620
  __export(coordinator_prompt_exports, {
@@ -3744,13 +3798,20 @@ function buildNodeConfigSection(mesh) {
3744
3798
  const explicitMachineLabel = typeof n.machineLabel === "string" ? n.machineLabel : "";
3745
3799
  const explicitLabel = explicitMachineLabel ? ` label: **${explicitMachineLabel}** |` : "";
3746
3800
  const providerPriority = n.policy?.providerPriority?.length ? ` | providers: ${n.policy.providerPriority.join(", ")}` : "";
3747
- const providerRoles = Array.isArray(n.policy?.providerRoles) ? n.policy.providerRoles.map((r) => {
3748
- const type = typeof r?.providerType === "string" ? r.providerType.trim() : "";
3749
- if (!type) return "";
3750
- const cap = Number.isFinite(Number(r?.maxParallel)) ? ` (max ${Math.floor(Number(r.maxParallel))})` : "";
3751
- return `${type}${cap}`;
3752
- }).filter(Boolean) : [];
3753
- const providerRolesSuffix = providerRoles.length ? ` | caps: ${providerRoles.join(", ")}` : "";
3801
+ const nodeSlots = resolveNodeCapabilitySlots(n);
3802
+ const seenCapProvider = /* @__PURE__ */ new Set();
3803
+ const providerCaps = [];
3804
+ for (const slot of nodeSlots) {
3805
+ const type = typeof slot?.provider === "string" ? slot.provider.trim() : "";
3806
+ if (!type) continue;
3807
+ const key2 = type.toLowerCase();
3808
+ if (seenCapProvider.has(key2)) continue;
3809
+ seenCapProvider.add(key2);
3810
+ const cap = resolveProviderMaxParallel(nodeSlots, type);
3811
+ if (cap === void 0) continue;
3812
+ providerCaps.push(`${type} (max ${cap})`);
3813
+ }
3814
+ const providerRolesSuffix = providerCaps.length ? ` | caps: ${providerCaps.join(", ")}` : "";
3754
3815
  lines.push(`- ${explicitLabel} nodeId: \`${n.id}\` | workspace: \`${n.workspace}\`${n.daemonId ? ` | daemon: \`${n.daemonId}\`` : ""}${providerPriority}${providerRolesSuffix}${suffix}`);
3755
3816
  const routingTags = [];
3756
3817
  const custom = Array.isArray(n.capabilities) ? n.capabilities : [];
@@ -3962,6 +4023,7 @@ var init_coordinator_prompt = __esm({
3962
4023
  path8 = __toESM(require("path"));
3963
4024
  init_repo_mesh_types();
3964
4025
  init_mesh_config();
4026
+ init_mesh_node_slots();
3965
4027
  init_dist();
3966
4028
  PROMPT_SOFT_CAP_BYTES = 60 * 1024;
3967
4029
  OPERATING_NOTES_PROMPT_CAP = 20;
@@ -7163,8 +7225,8 @@ var init_mesh_runtime_store = __esm({
7163
7225
  /**
7164
7226
  * Count active (status='assigned') tasks on a (node, provider) combination,
7165
7227
  * matched by the assignedProviderType stamped on the payload at claim time.
7166
- * Drives the per-(node, provider) maxParallel cap (RepoMeshNodePolicy
7167
- * providerRoles). The active-assignment set for a single node is tiny, so
7228
+ * Drives the per-(node, provider) maxParallel cap (summed across a provider's
7229
+ * slots[].maxParallel). The active-assignment set for a single node is tiny, so
7168
7230
  * parsing payloads here is cheap and avoids a schema migration. Pre-cap legacy
7169
7231
  * rows (no provider stamp) and other providers on the same node do not consume
7170
7232
  * this provider's budget, so the cap is fully backward compatible.
@@ -12914,15 +12976,22 @@ function buildMeshSchedulingRuntime(mesh, queue) {
12914
12976
  if (globalWriteCapReached) capReasons.push("global_max_parallel_tasks_reached");
12915
12977
  if ((writeAssignedByNode.get(nodeId) ?? 0) > 0) capReasons.push("node_has_active_assignment");
12916
12978
  let providerRoles;
12917
- const declaredRoles = Array.isArray(policy?.providerRoles) ? policy.providerRoles : [];
12918
- if (declaredRoles.length) {
12979
+ const slots = resolveNodeCapabilitySlots(rawNode);
12980
+ const cappedProviders = [];
12981
+ const seenProvider = /* @__PURE__ */ new Set();
12982
+ for (const slot of slots) {
12983
+ const providerType = typeof slot?.provider === "string" ? slot.provider.trim() : "";
12984
+ if (!providerType) continue;
12985
+ const key2 = providerType.toLowerCase();
12986
+ if (seenProvider.has(key2)) continue;
12987
+ seenProvider.add(key2);
12988
+ if (resolveProviderMaxParallel(slots, providerType) !== void 0) cappedProviders.push(providerType);
12989
+ }
12990
+ if (cappedProviders.length) {
12919
12991
  const byProvider = providerCountByNode.get(nodeId);
12920
12992
  providerRoles = [];
12921
- for (const role of declaredRoles) {
12922
- if (!role || typeof role !== "object") continue;
12923
- const providerType = typeof role.providerType === "string" ? role.providerType.trim() : "";
12924
- if (!providerType) continue;
12925
- const maxParallel = resolveProviderMaxParallel(policy, providerType);
12993
+ for (const providerType of cappedProviders) {
12994
+ const maxParallel = resolveProviderMaxParallel(slots, providerType);
12926
12995
  const activeAssigned = byProvider?.get(providerType) ?? 0;
12927
12996
  const capReached = maxParallel !== void 0 && activeAssigned >= maxParallel;
12928
12997
  providerRoles.push({
@@ -12963,6 +13032,7 @@ var init_mesh_scheduling_runtime = __esm({
12963
13032
  "use strict";
12964
13033
  init_repo_mesh_types();
12965
13034
  init_dist();
13035
+ init_mesh_node_slots();
12966
13036
  init_mesh_work_queue();
12967
13037
  }
12968
13038
  });
@@ -15777,7 +15847,7 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
15777
15847
  return false;
15778
15848
  }
15779
15849
  const capabilityTags = buildMeshNodeCapabilityTags(node, providerType);
15780
- const providerMaxParallel = resolveProviderMaxParallel(node?.policy, providerType);
15850
+ const providerMaxParallel = resolveProviderMaxParallel(resolveNodeCapabilitySlots(node), providerType);
15781
15851
  const nodeIsWorktree = node?.isLocalWorktree === true;
15782
15852
  const task = claimNextTask(meshId, nodeId, sessionId, capabilityTags, {
15783
15853
  providerType,
@@ -16035,7 +16105,7 @@ function sweepExpiredCooldowns() {
16035
16105
  if (now >= until) autoLaunchCooldownUntil.delete(key2);
16036
16106
  }
16037
16107
  }
16038
- function normalizeProviderPriority(policy) {
16108
+ function normalizeProviderPriority2(policy) {
16039
16109
  const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
16040
16110
  if (!Array.isArray(raw)) return [];
16041
16111
  const seen = /* @__PURE__ */ new Set();
@@ -16149,21 +16219,6 @@ function buildSchedulingPool(localCandidates, remoteCandidates) {
16149
16219
  }));
16150
16220
  return { pool, uniqueNodes };
16151
16221
  }
16152
- function resolveNodeCapabilitySlots(node) {
16153
- const explicit = normalizeNodeCapabilitySlots(node?.policy?.slots);
16154
- if (explicit.length) return explicit;
16155
- let difficultyBrains;
16156
- try {
16157
- difficultyBrains = getDifficultyBrains();
16158
- } catch {
16159
- difficultyBrains = void 0;
16160
- }
16161
- return deriveSlotsFromLegacy({
16162
- providerPriority: normalizeProviderPriority(node?.policy),
16163
- providerRoles: Array.isArray(node?.policy?.providerRoles) ? node.policy.providerRoles : void 0,
16164
- difficultyBrains
16165
- });
16166
- }
16167
16222
  function scoreSlotForTask(slot, task) {
16168
16223
  let score = 1;
16169
16224
  const diff = isMeshTaskDifficulty(task.difficulty) ? task.difficulty : void 0;
@@ -16290,6 +16345,7 @@ function nodeHasLiveSessionPendingClaim(components, meshId, nodeId) {
16290
16345
  const state = inst.getState();
16291
16346
  const settings = state.settings || {};
16292
16347
  if (readNonEmptyString2(settings.meshNodeFor) !== meshId) return false;
16348
+ if (readNonEmptyString2(settings.meshCoordinatorFor) === meshId) return false;
16293
16349
  const instNodeId = readNonEmptyString2(settings.meshNodeId) || readNonEmptyString2(settings.nodeId);
16294
16350
  if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
16295
16351
  const status = readNonEmptyString2(state.status).toLowerCase();
@@ -16494,7 +16550,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
16494
16550
  if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
16495
16551
  if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
16496
16552
  if (task.requiredTags?.length) {
16497
- const priorities = normalizeProviderPriority(node?.policy);
16553
+ const priorities = normalizeProviderPriority2(node?.policy);
16498
16554
  const providerCandidates = priorities.length ? priorities : [void 0];
16499
16555
  return providerCandidates.some(
16500
16556
  (p) => nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, p))
@@ -16581,7 +16637,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
16581
16637
  if (rawEffectiveModel && effectiveModel === void 0) {
16582
16638
  LOG.info("MeshQueue", `CODEX-400 GUARD: dropped incompatible launch model '${rawEffectiveModel}' for non-Anthropic provider '${resolved.providerType}' on node ${nodeId} (task ${task.id}); provider will use its own default model`);
16583
16639
  }
16584
- const providerCap = resolveProviderMaxParallel(node?.policy, resolved.providerType);
16640
+ const providerCap = resolveProviderMaxParallel(resolveNodeCapabilitySlots(node), resolved.providerType);
16585
16641
  if (providerCap !== void 0 && activeProviderAssignedCount(meshId, nodeId, resolved.providerType) >= providerCap) {
16586
16642
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_provider_parallel_reached", nodeId, providerType: resolved.providerType });
16587
16643
  continue;
@@ -16905,6 +16961,7 @@ var init_mesh_queue_assignment = __esm({
16905
16961
  init_mesh_warmup_deadline();
16906
16962
  init_repo_mesh_types();
16907
16963
  init_dist();
16964
+ init_mesh_node_slots();
16908
16965
  init_mesh_events_stale();
16909
16966
  init_mesh_events_utils();
16910
16967
  init_mesh_node_identity();
@@ -45761,6 +45818,7 @@ var STATUS_HYDRATION_TAIL_LIMIT = 200;
45761
45818
  var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
45762
45819
  var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
45763
45820
  var NATIVE_HISTORY_MESH_IDLE_SETTLE_MS = 4e3;
45821
+ var PTY_PARSED_FINAL_ASSISTANT_QUIET_DWELL_MS = 1200;
45764
45822
  var BACKGROUND_TASK_HOLD_MAX_MS = 5 * 6e4;
45765
45823
  var USER_INPUT_ACK_DEDUP_WINDOW_MS = 6e4;
45766
45824
  var STARTUP_GRACE_IDLE_COLLAPSE_WINDOW_MS = 12e3;
@@ -46003,6 +46061,39 @@ var CliProviderInstance = class _CliProviderInstance {
46003
46061
  * the nudge was NOT deferred) and leaked to the coordinator.
46004
46062
  */
46005
46063
  static AUTO_APPROVE_MASK_STALL_MS = 10500;
46064
+ /**
46065
+ * AUTOAPPROVE-FLAP-INBOX-MISSING: sticky-approval overlay window. Same time-tick
46066
+ * hold idea as the FALSE-IDLE completion gate — an approval signal that was
46067
+ * DOMINANT within this recent window is re-presented across a momentary busy blip
46068
+ * instead of collapsing.
46069
+ *
46070
+ * RCA (live 2026-07-13): a claude-cli worker sitting at a Bash approval modal
46071
+ * ("Do you want to proceed? ❯1.Yes") flaps waiting_approval↔busy on a ~2-3s period.
46072
+ * The spec `approval→busy` transition fires whenever the footer/modal approval
46073
+ * markers momentarily drop out of their parsed sections while the PRIOR command's
46074
+ * residual spinner text ("✳ Checking vendor drift…") still matches the busy regex.
46075
+ * On the busy frame the adapter reports status='generating', activeModal=null. That
46076
+ * corrupts THREE consumers at once: (1) mesh_active_work samples 'generating' →
46077
+ * collectPendingApprovals never sees 'awaiting_approval' → mesh_list_pending_approvals
46078
+ * count:0 (inbox miss); (2) the auto-approve settle gate is torn down each busy phase
46079
+ * so the 600ms settle never accrues → auto-approve never fires; (3) a mesh_approve
46080
+ * landing on a busy frame hits "Not in approval state". The existing FLAP machinery
46081
+ * (AUTO_APPROVE_FLAP_CONTINUITY_MS) only keeps the settle gate warm while status is
46082
+ * STILL waiting_approval (buttons scrolled out) — it does nothing once the FSM fully
46083
+ * commits to 'busy', and it never stabilises the status the inbox samples.
46084
+ *
46085
+ * Fix: when the raw adapter status flaps to generating/busy/idle but a
46086
+ * waiting_approval WITH a concrete modal was observed within this window, overlay
46087
+ * the cached modal and report status='waiting_approval'. This stabilized status
46088
+ * feeds getState (→ inbox), detectStatusTransition (→ event emission), and
46089
+ * maybeAutoApproveStatus (→ settle gate) uniformly, so the approval both registers
46090
+ * in the inbox and settles for auto-approve across the flap. Bounded (a genuine
46091
+ * resume that never returns to approval unmasks after this window) and scoped at the
46092
+ * call site to autonomous mesh sessions. 4000ms bridges the observed ~2-3s flap with
46093
+ * margin while staying well under AUTO_APPROVE_MASK_STALL_MS (a truly stalled/absent
46094
+ * approval still surfaces).
46095
+ */
46096
+ static APPROVAL_STICKY_FLAP_MS = 4e3;
46006
46097
  /**
46007
46098
  * FALSE-IDLE (inter-approval quiet valley): grace window after an auto-approve
46008
46099
  * (or mesh_approve) RESOLVES a modal during which a subsequent generating→idle
@@ -46106,6 +46197,14 @@ var CliProviderInstance = class _CliProviderInstance {
46106
46197
  // signature) while a genuinely closed modal — buttons empty continuously past
46107
46198
  // the continuity window — is still recognised and resets the gate.
46108
46199
  autoApproveLastModalSeenAt = 0;
46200
+ // AUTOAPPROVE-FLAP-INBOX-MISSING sticky-approval overlay (see APPROVAL_STICKY_FLAP_MS).
46201
+ // The wall-clock of the last frame where the RAW adapter reported waiting_approval with
46202
+ // a CONCRETE modal (buttons present), the cached modal to re-present across a busy blip,
46203
+ // and the approvalEntrySeq at that frame (so a stabilized frame carries the right seq to
46204
+ // the emission-dedup fingerprint). All zero/null when no recent concrete approval.
46205
+ approvalStickyLastConcreteAt = 0;
46206
+ approvalStickyModal = null;
46207
+ approvalStickyEntrySeq = 0;
46109
46208
  // STATUS-MISMATCH: wall-clock when the CURRENT auto-approve episode (waiting_approval
46110
46209
  // + shouldAutoApprove) first began wanting to mask. Unlike pendingAutoApprovalSince it
46111
46210
  // is NOT reset when the modal signature changes (a still-streaming/flapping prompt) and
@@ -46247,7 +46346,7 @@ var CliProviderInstance = class _CliProviderInstance {
46247
46346
  }
46248
46347
  }
46249
46348
  getState() {
46250
- const adapterStatus = this.adapter.getStatus();
46349
+ const adapterStatus = this.stabilizeFlappingApprovalStatus(this.adapter.getStatus());
46251
46350
  if (Object.prototype.hasOwnProperty.call(adapterStatus, "activeInteractivePrompt")) {
46252
46351
  this.activeInteractivePrompt = adapterStatus.activeInteractivePrompt ?? null;
46253
46352
  }
@@ -47111,6 +47210,9 @@ var CliProviderInstance = class _CliProviderInstance {
47111
47210
  return null;
47112
47211
  }
47113
47212
  if (this.type === "antigravity-cli") {
47213
+ if (allowMissingAssistantTimeout) {
47214
+ return { reason: "missing_final_assistant", terminal: false, holdForTranscript: true };
47215
+ }
47114
47216
  return null;
47115
47217
  }
47116
47218
  if (allowMissingAssistantTimeout && pending.previousStatus === "waiting_approval") {
@@ -47142,6 +47244,19 @@ var CliProviderInstance = class _CliProviderInstance {
47142
47244
  }
47143
47245
  } catch {
47144
47246
  }
47247
+ if (allowMissingAssistantTimeout && !adapterOwnsMessagesElsewhere && finalAssistantEvidence.source === "parsed") {
47248
+ try {
47249
+ const outStatus = this.adapter.getStatus({ allowParse: false });
47250
+ const lastOutputAt = typeof outStatus?.lastOutputAt === "number" && Number.isFinite(outStatus.lastOutputAt) ? outStatus.lastOutputAt : void 0;
47251
+ if (typeof lastOutputAt === "number") {
47252
+ const quietMs = Date.now() - lastOutputAt;
47253
+ if (quietMs < PTY_PARSED_FINAL_ASSISTANT_QUIET_DWELL_MS) {
47254
+ return { reason: "parsed_final_assistant_quiet_dwell", terminal: false };
47255
+ }
47256
+ }
47257
+ } catch {
47258
+ }
47259
+ }
47145
47260
  return null;
47146
47261
  }
47147
47262
  // (FALSEIDLE-a) Positive, structural proof that the latest approval entry was resolved
@@ -47563,6 +47678,53 @@ var CliProviderInstance = class _CliProviderInstance {
47563
47678
  ...opts.completionDiagnostic !== void 0 ? { completionDiagnostic: opts.completionDiagnostic } : {}
47564
47679
  });
47565
47680
  }
47681
+ /**
47682
+ * AUTOAPPROVE-FLAP-INBOX-MISSING sticky-approval overlay. Returns the adapterStatus a
47683
+ * flap-prone claude-cli approval SHOULD present this frame — either the raw status
47684
+ * unchanged, or, when the raw status has momentarily flapped OFF a recently-dominant
47685
+ * concrete approval, a synthetic `waiting_approval` re-presenting the cached modal.
47686
+ *
47687
+ * Records the concrete approval whenever the raw status is waiting_approval WITH
47688
+ * buttons. On a subsequent non-approval frame (the spec `approval→busy` flap), if that
47689
+ * concrete approval was seen within APPROVAL_STICKY_FLAP_MS AND the engine has NOT
47690
+ * resolved a modal since (lastApprovalResolvedAt not advanced past the sticky start),
47691
+ * overlay the cached modal + waiting_approval so the inbox / auto-approve / mesh_approve
47692
+ * all see the stable approval. A genuine resolution (auto-approve or mesh_approve fires
47693
+ * resolveModal → lastApprovalResolvedAt advances) clears the sticky immediately, so a
47694
+ * legitimate post-approval resume is NEVER masked as a lingering approval. Bounded by the
47695
+ * window, and scoped to autonomous mesh sessions (a foreground/attended or non-mesh
47696
+ * session, where a human answers the prompt, is returned untouched).
47697
+ */
47698
+ stabilizeFlappingApprovalStatus(adapterStatus, now = Date.now()) {
47699
+ if (!this.isAutonomousMeshSession() || !this.shouldAutoApprove()) return adapterStatus;
47700
+ const rawStatus = adapterStatus?.status;
47701
+ const resolvedAt = typeof this.adapter?.lastApprovalResolvedAt === "number" ? this.adapter.lastApprovalResolvedAt : 0;
47702
+ if (rawStatus === "waiting_approval") {
47703
+ if (hasNonEmptyCliModalButtons(adapterStatus?.activeModal)) {
47704
+ this.approvalStickyLastConcreteAt = now;
47705
+ this.approvalStickyModal = adapterStatus.activeModal;
47706
+ this.approvalStickyEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : this.approvalStickyEntrySeq;
47707
+ }
47708
+ return adapterStatus;
47709
+ }
47710
+ if (this.approvalStickyLastConcreteAt > 0 && this.approvalStickyModal) {
47711
+ const withinWindow = now - this.approvalStickyLastConcreteAt < _CliProviderInstance.APPROVAL_STICKY_FLAP_MS;
47712
+ const resolvedSinceAnchor = resolvedAt >= this.approvalStickyLastConcreteAt;
47713
+ if (withinWindow && !resolvedSinceAnchor) {
47714
+ return {
47715
+ ...adapterStatus,
47716
+ status: "waiting_approval",
47717
+ activeModal: this.approvalStickyModal,
47718
+ ...this.approvalStickyEntrySeq ? { approvalEntrySeq: this.approvalStickyEntrySeq } : {},
47719
+ approvalStickyOverlay: true
47720
+ };
47721
+ }
47722
+ this.approvalStickyLastConcreteAt = 0;
47723
+ this.approvalStickyModal = null;
47724
+ this.approvalStickyEntrySeq = 0;
47725
+ }
47726
+ return adapterStatus;
47727
+ }
47566
47728
  maybeAutoApproveStatus(adapterStatus, now = Date.now()) {
47567
47729
  if (adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove() && this.manualAttendance.isAttended(now)) {
47568
47730
  this.lastAutoApprovalSignature = "";
@@ -47792,7 +47954,7 @@ ${buttons.join("\n")}`;
47792
47954
  }
47793
47955
  detectStatusTransition() {
47794
47956
  const now = Date.now();
47795
- const adapterStatus = this.adapter.getStatus({ allowParse: false });
47957
+ const adapterStatus = this.stabilizeFlappingApprovalStatus(this.adapter.getStatus({ allowParse: false }), now);
47796
47958
  const adapterProviderSessionId = normalizeProviderSessionId(
47797
47959
  this.provider,
47798
47960
  typeof adapterStatus?.providerSessionId === "string" ? adapterStatus.providerSessionId : ""
@@ -56081,7 +56243,7 @@ var meshCrudHandlers = {
56081
56243
  const ownerFailure = await ctx.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node addition");
56082
56244
  if (ownerFailure) return ownerFailure;
56083
56245
  try {
56084
- const { addNode: addNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
56246
+ const { addNode: addNode2, migrateProviderRolesToSlots: migrateProviderRolesToSlots2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
56085
56247
  const providerPriority = Array.isArray(args?.providerPriority) ? args.providerPriority.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean) : [];
56086
56248
  const readOnly = args?.readOnly === true;
56087
56249
  const providerRoles = normalizeProviderRoles(args?.providerRoles);
@@ -56090,6 +56252,7 @@ var meshCrudHandlers = {
56090
56252
  ...providerPriority.length ? { providerPriority } : {},
56091
56253
  ...providerRoles.length ? { providerRoles } : {}
56092
56254
  };
56255
+ if (providerRoles.length) migrateProviderRolesToSlots2(policy);
56093
56256
  const role = normalizeMeshDaemonRole(args?.role);
56094
56257
  const daemonId = typeof args?.daemonId === "string" && args.daemonId.trim() ? args.daemonId.trim() : void 0;
56095
56258
  const machineId = typeof args?.machineId === "string" && args.machineId.trim() ? args.machineId.trim() : void 0;
@@ -56118,7 +56281,7 @@ var meshCrudHandlers = {
56118
56281
  const ownerFailure = await ctx.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node update");
56119
56282
  if (ownerFailure) return ownerFailure;
56120
56283
  try {
56121
- const { updateNode: updateNode2, normalizeCapabilityTags: normalizeCapabilityTags2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
56284
+ const { updateNode: updateNode2, normalizeCapabilityTags: normalizeCapabilityTags2, migrateProviderRolesToSlots: migrateProviderRolesToSlots2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
56122
56285
  const policy = args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy) ? { ...args.policy } : {};
56123
56286
  if (Array.isArray(args?.providerPriority)) {
56124
56287
  const providerPriority = args.providerPriority.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean);
@@ -56131,12 +56294,10 @@ var meshCrudHandlers = {
56131
56294
  }
56132
56295
  if (Array.isArray(args?.providerRoles)) {
56133
56296
  const providerRoles = normalizeProviderRoles(args.providerRoles);
56134
- if (providerRoles.length) {
56135
- policy.providerRoles = providerRoles;
56136
- } else {
56137
- delete policy.providerRoles;
56138
- }
56297
+ if (providerRoles.length) policy.providerRoles = providerRoles;
56298
+ else delete policy.providerRoles;
56139
56299
  }
56300
+ migrateProviderRolesToSlots2(policy);
56140
56301
  const patch = { policy };
56141
56302
  if (typeof args?.systemPrompt === "string") {
56142
56303
  const trimmed = args.systemPrompt.trim();
@@ -56408,8 +56569,11 @@ var meshCrudHandlers = {
56408
56569
  console.log(`[mesh] clone_mesh_node base sync: ${result.baseSync.action} (startRef=${result.baseSync.startRef})`);
56409
56570
  }
56410
56571
  let node;
56572
+ const { migrateProviderRolesToSlots: migrateProviderRolesToSlots2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
56411
56573
  if (meshRecord.inline) {
56412
56574
  const { randomUUID: randomUUID15 } = await import("crypto");
56575
+ const clonedPolicy = { ...sourceNode.policy || {} };
56576
+ migrateProviderRolesToSlots2(clonedPolicy);
56413
56577
  node = {
56414
56578
  id: `node_${randomUUID15().replace(/-/g, "")}`,
56415
56579
  workspace: result.worktreePath,
@@ -56417,7 +56581,7 @@ var meshCrudHandlers = {
56417
56581
  daemonId: sourceNode.daemonId,
56418
56582
  machineId: sourceNode.machineId ?? sourceNode.machine_id,
56419
56583
  userOverrides: { ...sourceNode.userOverrides || {} },
56420
- policy: { ...sourceNode.policy || {} },
56584
+ policy: clonedPolicy,
56421
56585
  isLocalWorktree: true,
56422
56586
  worktreeBranch: result.branch,
56423
56587
  clonedFromNodeId: sourceNodeId
@@ -56425,6 +56589,8 @@ var meshCrudHandlers = {
56425
56589
  ctx.updateInlineMeshNode(meshId, mesh, node);
56426
56590
  } else {
56427
56591
  const { addNode: addNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
56592
+ const clonedPolicy = { ...sourceNode.policy || {} };
56593
+ migrateProviderRolesToSlots2(clonedPolicy);
56428
56594
  node = addNode2(meshId, {
56429
56595
  workspace: result.worktreePath,
56430
56596
  repoRoot: result.worktreePath,
@@ -56434,7 +56600,7 @@ var meshCrudHandlers = {
56434
56600
  isLocalWorktree: true,
56435
56601
  worktreeBranch: result.branch,
56436
56602
  clonedFromNodeId: sourceNodeId,
56437
- policy: { ...sourceNode.policy || {} }
56603
+ policy: clonedPolicy
56438
56604
  });
56439
56605
  if (!node) return { success: false, error: "Failed to register worktree node" };
56440
56606
  const inlineForReconcile = ctx.getCachedInlineMesh(meshId);