@adhdev/daemon-standalone 1.0.56-rc.5 → 1.0.56-rc.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -36620,10 +36620,10 @@ var require_dist3 = __commonJS({
36620
36620
  }
36621
36621
  function getDaemonBuildInfo() {
36622
36622
  if (cached2) return cached2;
36623
- const commit = readInjected(true ? "d41089a0d560d75e670a90d3c4e9955bf955bbbc" : void 0) ?? "unknown";
36624
- const commitShort = readInjected(true ? "d41089a0" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36625
- const version2 = readInjected(true ? "1.0.56-rc.5" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36626
- const builtAt = readInjected(true ? "2026-08-19T05:42:19.473Z" : void 0);
36623
+ const commit = readInjected(true ? "13ea4fe8748ca3d97f0e4f9cc10a4c5969855fff" : void 0) ?? "unknown";
36624
+ const commitShort = readInjected(true ? "13ea4fe8" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36625
+ const version2 = readInjected(true ? "1.0.56-rc.7" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36626
+ const builtAt = readInjected(true ? "2026-08-19T14:43:40.002Z" : void 0);
36627
36627
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
36628
36628
  return cached2;
36629
36629
  }
@@ -44261,6 +44261,13 @@ Next step: ${nextStep}`;
44261
44261
  import_meta = {};
44262
44262
  }
44263
44263
  });
44264
+ function normalizeDeliveryMode(raw) {
44265
+ if (raw === void 0 || raw === null || raw === "") return { mode: DEFAULT_DELIVERY_MODE };
44266
+ const v = String(raw).trim().toLowerCase();
44267
+ if (v === "when_idle" || v === "whenidle") return { mode: "when_idle" };
44268
+ if (v === "interrupt") return { mode: "interrupt" };
44269
+ return { mode: DEFAULT_DELIVERY_MODE, unrecognized: String(raw) };
44270
+ }
44264
44271
  function resolveDeliveryDecision(sessionStatus, opts) {
44265
44272
  const status = (sessionStatus || "").trim().toLowerCase();
44266
44273
  if (!status) {
@@ -44292,6 +44299,20 @@ Next step: ${nextStep}`;
44292
44299
  message: "Session is waiting for approval \u2014 approval message delivered immediately."
44293
44300
  };
44294
44301
  }
44302
+ if (opts?.deliveryMode === "interrupt") {
44303
+ if (!opts.interruptSupported) {
44304
+ return {
44305
+ decision: "rejected",
44306
+ reason: "interrupt_unsupported_for_provider",
44307
+ message: opts.interruptUnsupportedMessage ?? `Session is ${status} and delivery mode 'interrupt' was requested, but this provider cannot interrupt a running turn. Refusing rather than silently queueing: queueing would let the current turn finish on the old instructions. Re-dispatch with delivery mode 'when_idle' if delivery-after-completion is acceptable.`
44308
+ };
44309
+ }
44310
+ return {
44311
+ decision: "interrupt",
44312
+ reason: `session_${status}_interrupt_requested`,
44313
+ message: `Session is ${status}. Aborting the in-flight turn via the provider's stop control, then delivering the task once it settles to idle. The work the session had not yet finished is discarded.`
44314
+ };
44315
+ }
44295
44316
  return {
44296
44317
  decision: "queued",
44297
44318
  reason: `session_${status}_busy`,
@@ -44380,6 +44401,7 @@ Next step: ${nextStep}`;
44380
44401
  }
44381
44402
  }
44382
44403
  var import_crypto4;
44404
+ var DEFAULT_DELIVERY_MODE;
44383
44405
  var IMMEDIATE_DELIVERY_STATUSES;
44384
44406
  var BUSY_DELIVERY_STATUSES;
44385
44407
  var TERMINAL_DELIVERY_STATUSES;
@@ -44388,6 +44410,7 @@ Next step: ${nextStep}`;
44388
44410
  "use strict";
44389
44411
  import_crypto4 = require("crypto");
44390
44412
  init_mesh_runtime_store();
44413
+ DEFAULT_DELIVERY_MODE = "when_idle";
44391
44414
  IMMEDIATE_DELIVERY_STATUSES = /* @__PURE__ */ new Set([
44392
44415
  "idle",
44393
44416
  "waiting_input",
@@ -45688,6 +45711,25 @@ Next step: ${nextStep}`;
45688
45711
  while (end > 0 && (buf[end] & 192) === 128) end -= 1;
45689
45712
  return `${buf.subarray(0, end).toString("utf8")}${marker}`;
45690
45713
  }
45714
+ function describeAvailablePointers(upstream) {
45715
+ if (!upstream) return " (no output is recorded for that ref yet)";
45716
+ const envelope = upstream.envelope;
45717
+ if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
45718
+ return " (that ref has an output, but its envelope is not an object)";
45719
+ }
45720
+ const pointers = [];
45721
+ for (const key2 of Object.keys(envelope)) {
45722
+ pointers.push(`/${key2}`);
45723
+ if (key2 !== "worker_result") continue;
45724
+ const workerResult = envelope.worker_result;
45725
+ if (!workerResult || typeof workerResult !== "object" || Array.isArray(workerResult)) continue;
45726
+ for (const sub of Object.keys(workerResult)) {
45727
+ pointers.push(`/worker_result/${sub}`);
45728
+ }
45729
+ }
45730
+ if (pointers.length === 0) return " (that ref's envelope is empty)";
45731
+ return `. Available pointers on ref '${upstream.ref}': ${pointers.join(", ")}`;
45732
+ }
45691
45733
  function resolveInputBindings(bindings, outputsByRef) {
45692
45734
  const resolved = [];
45693
45735
  let totalBytes = 0;
@@ -45698,7 +45740,7 @@ Next step: ${nextStep}`;
45698
45740
  if (binding.required) {
45699
45741
  throw new MeshMaterializationError(
45700
45742
  "required_input_missing",
45701
- `required binding '${binding.as}' found nothing at '${binding.select}' of ref '${binding.from}'`,
45743
+ `required binding '${binding.as}' found nothing at '${binding.select}' of ref '${binding.from}'` + describeAvailablePointers(upstream),
45702
45744
  binding.as
45703
45745
  );
45704
45746
  }
@@ -47122,108 +47164,6 @@ ${blocks.join("\n\n")}`;
47122
47164
  GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
47123
47165
  }
47124
47166
  });
47125
- function readPayloadTaskId(entry) {
47126
- const value = entry.payload?.taskId;
47127
- return typeof value === "string" && value.trim() ? value.trim() : "";
47128
- }
47129
- function parseTime(value) {
47130
- if (!value) return null;
47131
- const parsed = new Date(value).getTime();
47132
- return Number.isFinite(parsed) ? parsed : null;
47133
- }
47134
- function computeMeshTaskStats(meshId, opts) {
47135
- const queue = getQueue(meshId);
47136
- const queueById = new Map(queue.map((task) => [task.id, task]));
47137
- let targetIds;
47138
- if (opts?.taskIds?.length) {
47139
- targetIds = [...new Set(opts.taskIds)];
47140
- } else if (opts?.missionId) {
47141
- targetIds = queue.filter((task) => task.missionId === opts.missionId).map((task) => task.id);
47142
- } else {
47143
- targetIds = queue.map((task) => task.id);
47144
- }
47145
- if (targetIds.length === 0) return [];
47146
- const targetSet = new Set(targetIds);
47147
- const entries = readLedgerEntries(meshId, { tail: opts?.tail ?? 1e3 });
47148
- const dispatches = /* @__PURE__ */ new Map();
47149
- const terminals = /* @__PURE__ */ new Map();
47150
- for (const entry of entries) {
47151
- const taskId = readPayloadTaskId(entry);
47152
- if (!taskId || !targetSet.has(taskId)) continue;
47153
- if (entry.kind === "task_dispatched") {
47154
- const existing = dispatches.get(taskId);
47155
- if (existing) existing.count += 1;
47156
- else dispatches.set(taskId, { first: entry.timestamp, count: 1 });
47157
- } else if (entry.kind === "task_completed" || entry.kind === "task_failed") {
47158
- terminals.set(taskId, { at: entry.timestamp, kind: entry.kind });
47159
- }
47160
- }
47161
- return targetIds.map((taskId) => {
47162
- const queueEntry = queueById.get(taskId);
47163
- const dispatch = dispatches.get(taskId);
47164
- const terminal = terminals.get(taskId);
47165
- const status = queueEntry?.status ?? (terminal ? terminal.kind === "task_completed" ? "completed" : "failed" : "unknown");
47166
- const isTerminalStatus = status === "completed" || status === "failed" || status === "cancelled";
47167
- const dispatchTime = parseTime(dispatch?.first ?? queueEntry?.dispatchTimestamp);
47168
- const terminalTime = parseTime(terminal?.at);
47169
- const stats = {
47170
- taskId,
47171
- status,
47172
- dispatchedAt: dispatch?.first ?? queueEntry?.dispatchTimestamp ?? null,
47173
- terminalAt: terminal?.at ?? null,
47174
- terminalKind: terminal?.kind ?? null,
47175
- durationMs: null,
47176
- dispatchCount: dispatch?.count ?? 0,
47177
- requeueCount: queueEntry?.requeueCount ?? 0
47178
- };
47179
- if (dispatchTime !== null && terminalTime !== null && terminalTime >= dispatchTime) {
47180
- stats.durationMs = terminalTime - dispatchTime;
47181
- } else if (isTerminalStatus) {
47182
- stats.incompleteEvidence = true;
47183
- }
47184
- return stats;
47185
- });
47186
- }
47187
- function computeMeshMissionStats(meshId, missionId) {
47188
- const tasks = computeMeshTaskStats(meshId, { missionId });
47189
- const stats = {
47190
- missionId,
47191
- taskCount: tasks.length,
47192
- completed: 0,
47193
- failed: 0,
47194
- totalDurationMs: 0,
47195
- wallClockMs: null,
47196
- retries: 0,
47197
- incompleteTaskIds: []
47198
- };
47199
- let firstDispatch = null;
47200
- let lastTerminal = null;
47201
- for (const task of tasks) {
47202
- if (task.status === "completed") stats.completed += 1;
47203
- else if (task.status === "failed") stats.failed += 1;
47204
- stats.retries += task.requeueCount;
47205
- if (task.incompleteEvidence) {
47206
- stats.incompleteTaskIds.push(task.taskId);
47207
- continue;
47208
- }
47209
- if (task.durationMs !== null) stats.totalDurationMs += task.durationMs;
47210
- const dispatchTime = parseTime(task.dispatchedAt);
47211
- const terminalTime = parseTime(task.terminalAt);
47212
- if (dispatchTime !== null && (firstDispatch === null || dispatchTime < firstDispatch)) firstDispatch = dispatchTime;
47213
- if (terminalTime !== null && (lastTerminal === null || terminalTime > lastTerminal)) lastTerminal = terminalTime;
47214
- }
47215
- if (firstDispatch !== null && lastTerminal !== null && lastTerminal >= firstDispatch) {
47216
- stats.wallClockMs = lastTerminal - firstDispatch;
47217
- }
47218
- return stats;
47219
- }
47220
- var init_mesh_task_stats = __esm2({
47221
- "src/mesh/mesh-task-stats.ts"() {
47222
- "use strict";
47223
- init_mesh_ledger();
47224
- init_mesh_work_queue();
47225
- }
47226
- });
47227
47167
  function truncateString(value, maxChars) {
47228
47168
  if (value.length <= maxChars) return value;
47229
47169
  if (maxChars <= 12) return value.slice(0, Math.max(0, maxChars));
@@ -47943,6 +47883,7 @@ ${blocks.join("\n\n")}`;
47943
47883
  const taskId = readNonEmptyString(metadata.taskId) || readNonEmptyString(readRecord2(metadata.payload)?.taskId);
47944
47884
  const jobId = readRefineJobId2(event);
47945
47885
  const timestamp2 = metadata.timestamp !== void 0 && metadata.timestamp !== null ? String(metadata.timestamp) : "";
47886
+ const alertReason = COORDINATOR_ALERT_EVENTS_WITH_REASON_FINGERPRINT.has(event.event) ? readNonEmptyString(metadata.reason) : void 0;
47946
47887
  return [
47947
47888
  event.meshId,
47948
47889
  event.event,
@@ -47951,7 +47892,8 @@ ${blocks.join("\n\n")}`;
47951
47892
  providerSessionId || "",
47952
47893
  taskId || "",
47953
47894
  jobId || "",
47954
- timestamp2 || ""
47895
+ timestamp2 || "",
47896
+ ...alertReason ? [alertReason] : []
47955
47897
  ].join("::");
47956
47898
  }
47957
47899
  function hasPendingCoordinatorEventDuplicate(event) {
@@ -48400,6 +48342,7 @@ ${blocks.join("\n\n")}`;
48400
48342
  var warnedV2Violations;
48401
48343
  var loggedSqlitePendingDrainFailure;
48402
48344
  var TERMINAL_COMPLETION_EVENTS;
48345
+ var COORDINATOR_ALERT_EVENTS_WITH_REASON_FINGERPRINT;
48403
48346
  var PENDING_EVENTS_DRAINED_RETENTION_MS;
48404
48347
  var PENDING_EVENTS_UNDRAINED_RETENTION_MS;
48405
48348
  var PENDING_RETENTION_EXPIRED_HOLD_REASON;
@@ -48450,6 +48393,9 @@ ${blocks.join("\n\n")}`;
48450
48393
  warnedV2Violations = /* @__PURE__ */ new Set();
48451
48394
  loggedSqlitePendingDrainFailure = false;
48452
48395
  TERMINAL_COMPLETION_EVENTS = /* @__PURE__ */ new Set(["agent:generating_completed", "agent:stopped"]);
48396
+ COORDINATOR_ALERT_EVENTS_WITH_REASON_FINGERPRINT = /* @__PURE__ */ new Set([
48397
+ "mesh:dispatch_blocked"
48398
+ ]);
48453
48399
  PENDING_EVENTS_DRAINED_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
48454
48400
  PENDING_EVENTS_UNDRAINED_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
48455
48401
  PENDING_RETENTION_EXPIRED_HOLD_REASON = "pending_retention_expired";
@@ -48470,6 +48416,245 @@ ${blocks.join("\n\n")}`;
48470
48416
  };
48471
48417
  }
48472
48418
  });
48419
+ function s(v) {
48420
+ return typeof v === "string" && v.trim() ? v.trim() : "";
48421
+ }
48422
+ function meshEventTraceKey(ctx) {
48423
+ const segs = [`task=${s(ctx.taskId) || "-"}`];
48424
+ const eventId = s(ctx.eventId);
48425
+ if (eventId) segs.push(`evt=${eventId}`);
48426
+ segs.push(`sess=${s(ctx.sessionId) || "-"}`);
48427
+ const nodeId = s(ctx.nodeId);
48428
+ if (nodeId) segs.push(`node=${nodeId}`);
48429
+ const meshId = s(ctx.meshId);
48430
+ if (meshId) segs.push(`mesh=${meshId}`);
48431
+ const event = s(ctx.event);
48432
+ if (event) segs.push(`event=${event}`);
48433
+ return segs.join(" ");
48434
+ }
48435
+ function traceMeshEventStage(stage, ctx, detail) {
48436
+ LOG2.info(CAT, `[stage:${stage}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
48437
+ }
48438
+ function traceMeshEventDrop(reason, ctx, detail) {
48439
+ LOG2.warn(CAT, `[drop:${reason}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
48440
+ }
48441
+ var CAT;
48442
+ var init_mesh_event_trace = __esm2({
48443
+ "src/mesh/mesh-event-trace.ts"() {
48444
+ "use strict";
48445
+ init_logger();
48446
+ CAT = "EvtTrace";
48447
+ }
48448
+ });
48449
+ function taskIsParked(task) {
48450
+ return !!readNonEmptyString(task?.parked?.reason);
48451
+ }
48452
+ function buildParkingRecord(task, reason, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
48453
+ return {
48454
+ reason,
48455
+ parkedAt: nowIso,
48456
+ // Mirror the ORIGINAL address. `targetSessionId` also stays on the row
48457
+ // (that is what keeps the row unclaimable), but a later re-target
48458
+ // overwrites it — this copy is what still answers "who was this delta
48459
+ // written for?" after the coordinator has moved it.
48460
+ ...readNonEmptyString(task.targetSessionId) ? { targetSessionId: task.targetSessionId } : {},
48461
+ ...readNonEmptyString(task.targetNodeId) ? { targetNodeId: task.targetNodeId } : {}
48462
+ };
48463
+ }
48464
+ function parkedAgeMs(task, nowMs = Date.now()) {
48465
+ const parkedAt = readNonEmptyString(task.parked?.parkedAt);
48466
+ if (!parkedAt) return null;
48467
+ const parsed = Date.parse(parkedAt);
48468
+ return Number.isFinite(parsed) ? nowMs - parsed : null;
48469
+ }
48470
+ function parkedTaskRetentionExpired(task, nowMs = Date.now(), retentionMs = PARKED_TASK_RETENTION_MS) {
48471
+ if (!taskIsParked(task)) return false;
48472
+ const age = parkedAgeMs(task, nowMs);
48473
+ return age !== null && age >= retentionMs;
48474
+ }
48475
+ function buildParkedTaskNotice(taskId, targetSessionId, reason) {
48476
+ const addressee = readNonEmptyString(targetSessionId);
48477
+ return `The delta was addressed to session '${addressee || "(unknown)"}' and that pin went stale (${reason || PARK_REASON_PIN_EXPIRED}), so the task is now PARKED \u2014 held for you, deliberately NOT re-homed onto another session. A message written for one session's context becomes a context-free instruction anywhere else, so the daemon will not choose a new addressee for you. The task is claimable by nobody until you act on it, and it is dropped (as a terminal failure, with another notification) if left parked for ${Math.round(PARKED_TASK_RETENTION_MS / 36e5)}h. Four ways out \u2014 inspect it with mesh_view_queue (parked rows are listed under parkedTasks with their original addressee), then either re-target it with mesh_queue_requeue(task_id='${taskId}', target_session_id='<live session>') \u2014 or clear_target_session to let any compatible session take it; REWRITE it first with mesh_queue_requeue(task_id='${taskId}', message='<updated instruction>') if the situation moved on while it waited (the common case: the worker already did the part your delta was about); or cancel it with mesh_queue_cancel(task_id='${taskId}') if it is no longer wanted. Any requeue unparks the task.`;
48478
+ }
48479
+ function notifyCoordinatorOfParkedTaskDropped(meshId, task) {
48480
+ const taskId = task.id;
48481
+ const addressee = readNonEmptyString(task.parked?.targetSessionId);
48482
+ const hours = Math.round(PARKED_TASK_RETENTION_MS / 36e5);
48483
+ const coordinatorMessage = `[System] A PARKED mesh task was dropped after ${hours}h with no coordinator decision.
48484
+ Task ${taskId} was parked because its delta was addressed to session '${addressee || "(unknown)"}' and that pin went stale. It was held \u2014 claimable by nobody \u2014 waiting for you to re-target, rewrite, or cancel it. That never happened, so it is now marked FAILED (${PARK_RETENTION_EXPIRED_REASON}) and any dependent tasks have been unblocked.
48485
+ The instruction it carried was never delivered to anyone. If it still matters, re-enqueue it (mesh_enqueue_task) against a live session; the failed row remains in the queue as the audit record. To avoid this next time, check parkedTasks in mesh_view_queue \u2014 parked rows are surfaced there from the moment they park.`;
48486
+ const nodeLabel = readNonEmptyString(task.targetNodeId) || readNonEmptyString(task.parked?.targetNodeId) || meshId;
48487
+ try {
48488
+ const targetCoordinatorDaemonId = readNonEmptyString(loadConfig2().machineId);
48489
+ const targetCoordinatorSessionId = readNonEmptyString(task.sourceCoordinatorSessionId);
48490
+ queuePendingMeshCoordinatorEvent({
48491
+ event: "mesh:dispatch_blocked",
48492
+ meshId,
48493
+ nodeLabel,
48494
+ metadataEvent: {
48495
+ source: "mesh_parked_task_retention_expired",
48496
+ taskId,
48497
+ reason: PARK_RETENTION_EXPIRED_REASON,
48498
+ ...addressee ? { parkedTargetSessionId: addressee } : {},
48499
+ coordinatorMessage
48500
+ },
48501
+ coordinatorMessage,
48502
+ queuedAt: Date.now(),
48503
+ ...targetCoordinatorDaemonId ? { targetCoordinatorDaemonId } : {},
48504
+ ...targetCoordinatorSessionId ? { targetCoordinatorSessionId } : {}
48505
+ });
48506
+ } catch (e) {
48507
+ LOG2.warn("MeshQueue", `Failed to surface parked-task retention drop for task ${taskId} (mesh ${meshId}): ${e?.message || e}`);
48508
+ }
48509
+ }
48510
+ function parkExpiredTargetPin(meshId, task, verdict, ttlMs, park) {
48511
+ if (!verdict.expired) return false;
48512
+ const parked = park(meshId, task.id, PARK_REASON_PIN_EXPIRED);
48513
+ if (parked) {
48514
+ noteTargetPinCleared(PARK_REASON_PIN_EXPIRED);
48515
+ traceMeshEventDrop("target_session_pin_expired", {
48516
+ taskId: task.id,
48517
+ sessionId: readNonEmptyString(task.targetSessionId),
48518
+ nodeId: readNonEmptyString(task.targetNodeId),
48519
+ meshId,
48520
+ event: "agent:ready"
48521
+ }, `unproductive ${Math.round((verdict.ageMs ?? 0) / 1e3)}s \u2265 ttl ${Math.round(ttlMs / 1e3)}s \u2192 task PARKED (held for the coordinator, NOT re-homed)`);
48522
+ }
48523
+ return true;
48524
+ }
48525
+ function settleParkedQueueTask(meshId, task, markSkip, sweep) {
48526
+ const swept = sweep(meshId, task.id);
48527
+ if (swept) {
48528
+ notifyCoordinatorOfParkedTaskDropped(meshId, task);
48529
+ return "swept";
48530
+ }
48531
+ markSkip(PARKED_SKIP_REASON);
48532
+ return "held";
48533
+ }
48534
+ function logTaskParked(meshId, taskId, reason, targetSessionId) {
48535
+ LOG2.warn("MeshQueue", `PIN-PARKING: task ${taskId} (mesh ${meshId}) PARKED (${reason})${targetSessionId ? ` \u2014 original addressee session ${targetSessionId}` : ""}; it is claimable by nobody until the coordinator re-targets, edits, or cancels it.`);
48536
+ }
48537
+ var PARKED_TASK_RETENTION_MS;
48538
+ var PARK_REASON_PIN_EXPIRED;
48539
+ var PARKED_SKIP_REASON;
48540
+ var PARK_RETENTION_EXPIRED_REASON;
48541
+ var init_mesh_task_parking = __esm2({
48542
+ "src/mesh/mesh-task-parking.ts"() {
48543
+ "use strict";
48544
+ init_logger();
48545
+ init_mesh_events_utils();
48546
+ init_mesh_events_pending();
48547
+ init_mesh_turn_ledger();
48548
+ init_mesh_event_trace();
48549
+ init_config();
48550
+ PARKED_TASK_RETENTION_MS = 24 * 60 * 6e4;
48551
+ PARK_REASON_PIN_EXPIRED = "target_session_pin_expired_parked";
48552
+ PARKED_SKIP_REASON = "target_session_pin_parked";
48553
+ PARK_RETENTION_EXPIRED_REASON = "parked_task_retention_expired";
48554
+ }
48555
+ });
48556
+ function readPayloadTaskId(entry) {
48557
+ const value = entry.payload?.taskId;
48558
+ return typeof value === "string" && value.trim() ? value.trim() : "";
48559
+ }
48560
+ function parseTime(value) {
48561
+ if (!value) return null;
48562
+ const parsed = new Date(value).getTime();
48563
+ return Number.isFinite(parsed) ? parsed : null;
48564
+ }
48565
+ function computeMeshTaskStats(meshId, opts) {
48566
+ const queue = getQueue(meshId);
48567
+ const queueById = new Map(queue.map((task) => [task.id, task]));
48568
+ let targetIds;
48569
+ if (opts?.taskIds?.length) {
48570
+ targetIds = [...new Set(opts.taskIds)];
48571
+ } else if (opts?.missionId) {
48572
+ targetIds = queue.filter((task) => task.missionId === opts.missionId).map((task) => task.id);
48573
+ } else {
48574
+ targetIds = queue.map((task) => task.id);
48575
+ }
48576
+ if (targetIds.length === 0) return [];
48577
+ const targetSet = new Set(targetIds);
48578
+ const entries = readLedgerEntries(meshId, { tail: opts?.tail ?? 1e3 });
48579
+ const dispatches = /* @__PURE__ */ new Map();
48580
+ const terminals = /* @__PURE__ */ new Map();
48581
+ for (const entry of entries) {
48582
+ const taskId = readPayloadTaskId(entry);
48583
+ if (!taskId || !targetSet.has(taskId)) continue;
48584
+ if (entry.kind === "task_dispatched") {
48585
+ const existing = dispatches.get(taskId);
48586
+ if (existing) existing.count += 1;
48587
+ else dispatches.set(taskId, { first: entry.timestamp, count: 1 });
48588
+ } else if (entry.kind === "task_completed" || entry.kind === "task_failed") {
48589
+ terminals.set(taskId, { at: entry.timestamp, kind: entry.kind });
48590
+ }
48591
+ }
48592
+ return targetIds.map((taskId) => {
48593
+ const queueEntry = queueById.get(taskId);
48594
+ const dispatch = dispatches.get(taskId);
48595
+ const terminal = terminals.get(taskId);
48596
+ const status = queueEntry?.status ?? (terminal ? terminal.kind === "task_completed" ? "completed" : "failed" : "unknown");
48597
+ const isTerminalStatus = status === "completed" || status === "failed" || status === "cancelled";
48598
+ const dispatchTime = parseTime(dispatch?.first ?? queueEntry?.dispatchTimestamp);
48599
+ const terminalTime = parseTime(terminal?.at);
48600
+ const stats = {
48601
+ taskId,
48602
+ status,
48603
+ dispatchedAt: dispatch?.first ?? queueEntry?.dispatchTimestamp ?? null,
48604
+ terminalAt: terminal?.at ?? null,
48605
+ terminalKind: terminal?.kind ?? null,
48606
+ durationMs: null,
48607
+ dispatchCount: dispatch?.count ?? 0,
48608
+ requeueCount: queueEntry?.requeueCount ?? 0
48609
+ };
48610
+ if (dispatchTime !== null && terminalTime !== null && terminalTime >= dispatchTime) {
48611
+ stats.durationMs = terminalTime - dispatchTime;
48612
+ } else if (isTerminalStatus) {
48613
+ stats.incompleteEvidence = true;
48614
+ }
48615
+ return stats;
48616
+ });
48617
+ }
48618
+ function computeMeshMissionStats(meshId, missionId) {
48619
+ const tasks = computeMeshTaskStats(meshId, { missionId });
48620
+ const stats = {
48621
+ missionId,
48622
+ taskCount: tasks.length,
48623
+ completed: 0,
48624
+ failed: 0,
48625
+ totalDurationMs: 0,
48626
+ wallClockMs: null,
48627
+ retries: 0,
48628
+ incompleteTaskIds: []
48629
+ };
48630
+ let firstDispatch = null;
48631
+ let lastTerminal = null;
48632
+ for (const task of tasks) {
48633
+ if (task.status === "completed") stats.completed += 1;
48634
+ else if (task.status === "failed") stats.failed += 1;
48635
+ stats.retries += task.requeueCount;
48636
+ if (task.incompleteEvidence) {
48637
+ stats.incompleteTaskIds.push(task.taskId);
48638
+ continue;
48639
+ }
48640
+ if (task.durationMs !== null) stats.totalDurationMs += task.durationMs;
48641
+ const dispatchTime = parseTime(task.dispatchedAt);
48642
+ const terminalTime = parseTime(task.terminalAt);
48643
+ if (dispatchTime !== null && (firstDispatch === null || dispatchTime < firstDispatch)) firstDispatch = dispatchTime;
48644
+ if (terminalTime !== null && (lastTerminal === null || terminalTime > lastTerminal)) lastTerminal = terminalTime;
48645
+ }
48646
+ if (firstDispatch !== null && lastTerminal !== null && lastTerminal >= firstDispatch) {
48647
+ stats.wallClockMs = lastTerminal - firstDispatch;
48648
+ }
48649
+ return stats;
48650
+ }
48651
+ var init_mesh_task_stats = __esm2({
48652
+ "src/mesh/mesh-task-stats.ts"() {
48653
+ "use strict";
48654
+ init_mesh_ledger();
48655
+ init_mesh_work_queue();
48656
+ }
48657
+ });
48473
48658
  var mesh_missions_exports = {};
48474
48659
  __export2(mesh_missions_exports, {
48475
48660
  COMPACT_STATUS_GOAL_PREVIEW_MAX: () => COMPACT_STATUS_GOAL_PREVIEW_MAX,
@@ -48855,11 +49040,12 @@ ${blocks.join("\n\n")}`;
48855
49040
  describeTaskDependencyState: () => describeTaskDependencyState,
48856
49041
  enqueueTask: () => enqueueTask,
48857
49042
  enqueueTaskGraph: () => enqueueTaskGraph,
48858
- expireTaskTargetPin: () => expireTaskTargetPin,
49043
+ failRetentionExpiredParkedTask: () => failRetentionExpiredParkedTask,
48859
49044
  formatMeshTaskModeViolations: () => formatMeshTaskModeViolations,
48860
49045
  getActiveDirectDispatches: () => getActiveDirectDispatches,
48861
49046
  getMeshQueueRevision: () => getMeshQueueRevision,
48862
49047
  getMeshQueueStats: () => getMeshQueueStats,
49048
+ getParkedTasks: () => getParkedTasks,
48863
49049
  getQueue: () => getQueue,
48864
49050
  hasPendingDependents: () => hasPendingDependents,
48865
49051
  insertDirectDispatch: () => insertDirectDispatch,
@@ -48871,6 +49057,7 @@ ${blocks.join("\n\n")}`;
48871
49057
  normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
48872
49058
  normalizeMeshTaskMode: () => normalizeMeshTaskMode,
48873
49059
  normalizeMeshTaskPriority: () => normalizeMeshTaskPriority,
49060
+ parkTaskTargetPin: () => parkTaskTargetPin,
48874
49061
  reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
48875
49062
  recordDirectDispatchTask: () => recordDirectDispatchTask,
48876
49063
  recordMeshToolCall: () => recordMeshToolCall,
@@ -49352,6 +49539,7 @@ ${blocks.join("\n\n")}`;
49352
49539
  entry.status = "cancelled";
49353
49540
  entry.cancelledAt = now;
49354
49541
  if (opts?.reason) entry.cancelReason = opts.reason;
49542
+ delete entry.parked;
49355
49543
  delete entry.assignedNodeId;
49356
49544
  delete entry.assignedSessionId;
49357
49545
  delete entry.assignedProviderType;
@@ -49384,6 +49572,12 @@ ${blocks.join("\n\n")}`;
49384
49572
  if (value) lastCancelledTaskAssignment.delete(key2);
49385
49573
  return value;
49386
49574
  }
49575
+ function applyRequeueMessageEdit(entry, message) {
49576
+ if (typeof message !== "string") return;
49577
+ const next = message.trim();
49578
+ if (!next || next === entry.message) return;
49579
+ entry.message = next;
49580
+ }
49387
49581
  function requeueTask(meshId, taskId, opts) {
49388
49582
  requireMeshHostQueueOwner(opts);
49389
49583
  const result = withQueueLock(meshId, () => {
@@ -49418,6 +49612,8 @@ ${blocks.join("\n\n")}`;
49418
49612
  entry.requeuedAt = (/* @__PURE__ */ new Date()).toISOString();
49419
49613
  entry.dispatchFailureCount = dispatchFailures;
49420
49614
  if (opts?.reason) entry.requeueReason = opts.reason;
49615
+ applyRequeueMessageEdit(entry, opts?.message);
49616
+ delete entry.parked;
49421
49617
  const backoffMs = DISPATCH_RETRY_BACKOFF_BASE_MS * Math.pow(2, dispatchFailures - 1);
49422
49618
  entry.notBefore = resolveNotBefore(Math.min(backoffMs, DISPATCH_RETRY_BACKOFF_MAX_MS));
49423
49619
  MeshRuntimeStore.getInstance().updateQueueEntry(entry);
@@ -49446,6 +49642,8 @@ ${blocks.join("\n\n")}`;
49446
49642
  entry.requeuedAt = (/* @__PURE__ */ new Date()).toISOString();
49447
49643
  entry.requeueCount = currentCount + 1;
49448
49644
  if (opts?.reason) entry.requeueReason = opts.reason;
49645
+ applyRequeueMessageEdit(entry, opts?.message);
49646
+ delete entry.parked;
49449
49647
  const notBefore = resolveNotBefore(opts?.notBefore);
49450
49648
  if (notBefore) entry.notBefore = notBefore;
49451
49649
  else delete entry.notBefore;
@@ -49455,24 +49653,46 @@ ${blocks.join("\n\n")}`;
49455
49653
  if (result?.missionAffected) scheduleMissionCloseCandidateCheck(meshId, [result.entry, ...result.cascaded]);
49456
49654
  return result ? result.entry : null;
49457
49655
  }
49458
- function expireTaskTargetPin(meshId, taskId, opts) {
49656
+ function parkTaskTargetPin(meshId, taskId, opts) {
49459
49657
  requireMeshHostQueueOwner(opts);
49460
49658
  return withQueueLock(meshId, () => {
49461
49659
  const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
49462
49660
  if (!entry) return null;
49463
49661
  if (entry.status !== "pending") return null;
49464
49662
  if (!entry.targetSessionId && !entry.targetNodeId) return null;
49465
- const clearedSession = entry.targetSessionId;
49466
- const clearedNode = opts?.clearTargetNode ? entry.targetNodeId : void 0;
49467
- delete entry.targetSessionId;
49468
- if (opts?.clearTargetNode) delete entry.targetNodeId;
49469
- entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
49470
- if (opts?.reason) entry.requeueReason = opts.reason;
49663
+ if (taskIsParked(entry)) return null;
49664
+ const reason = opts?.reason || PARK_REASON_PIN_EXPIRED;
49665
+ const now = (/* @__PURE__ */ new Date()).toISOString();
49666
+ entry.parked = buildParkingRecord(entry, reason, now);
49667
+ entry.updatedAt = now;
49668
+ entry.requeueReason = reason;
49471
49669
  MeshRuntimeStore.getInstance().updateQueueEntry(entry);
49472
- LOG2.warn("MeshQueue", `Expired stale target pin on task ${taskId} (mesh ${meshId}): cleared${clearedSession ? ` targetSessionId=${clearedSession}` : ""}${clearedNode ? ` targetNodeId=${clearedNode}` : ""} (${opts?.reason ?? "target_pin_expired"}) \u2014 the task is now claimable by any compatible session`);
49670
+ logTaskParked(meshId, taskId, reason, entry.targetSessionId);
49473
49671
  return entry;
49474
49672
  });
49475
49673
  }
49674
+ function failRetentionExpiredParkedTask(meshId, taskId, opts) {
49675
+ requireMeshHostQueueOwner(opts);
49676
+ const result = withQueueLock(meshId, () => {
49677
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
49678
+ if (!entry) return null;
49679
+ if (entry.status !== "pending" || !taskIsParked(entry)) return null;
49680
+ if (!parkedTaskRetentionExpired(entry, Date.now(), opts?.retentionMs)) return null;
49681
+ const hours = Math.round((opts?.retentionMs ?? PARKED_TASK_RETENTION_MS) / 36e5);
49682
+ entry.status = "failed";
49683
+ entry.cancelReason = `${PARK_RETENTION_EXPIRED_REASON}: parked for over ${hours}h (addressed to session ${entry.parked?.targetSessionId || "unknown"}) with no coordinator decision`;
49684
+ entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
49685
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
49686
+ const cascaded = propagateDependencyFailure(meshId, taskId);
49687
+ LOG2.warn("MeshQueue", `PIN-PARKING retention: task ${taskId} (mesh ${meshId}) stayed parked past ${hours}h with no coordinator decision; failed it (dependents unblocked). This is reported to the coordinator, never a silent drop.`);
49688
+ return { entry, cascaded, missionAffected: true };
49689
+ });
49690
+ if (result?.missionAffected) scheduleMissionCloseCandidateCheck(meshId, [result.entry, ...result.cascaded]);
49691
+ return result ? result.entry : null;
49692
+ }
49693
+ function getParkedTasks(meshId) {
49694
+ return getQueue(meshId, { status: ["pending"] }).filter(taskIsParked);
49695
+ }
49476
49696
  function reclaimStrandedAssignedTask(meshId, taskId, opts) {
49477
49697
  requireMeshHostQueueOwner(opts);
49478
49698
  const result = withQueueLock(meshId, () => {
@@ -49700,6 +49920,7 @@ ${blocks.join("\n\n")}`;
49700
49920
  init_mesh_graph_derived_failure();
49701
49921
  init_dist();
49702
49922
  init_mesh_task_mode_guardrail();
49923
+ init_mesh_task_parking();
49703
49924
  init_mesh_task_mode_guardrail();
49704
49925
  ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
49705
49926
  HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
@@ -52571,6 +52792,7 @@ ${blocks.join("\n\n")}`;
52571
52792
  init_config();
52572
52793
  init_mesh_ledger();
52573
52794
  init_mesh_work_queue();
52795
+ init_mesh_task_parking();
52574
52796
  init_mesh_graph_schema();
52575
52797
  init_mesh_graph_store();
52576
52798
  init_slot_model_enforcement();
@@ -53539,7 +53761,8 @@ ${blocks.join("\n\n")}`;
53539
53761
  };
53540
53762
  const allowedTaskDifficulties = opts?.allowedTaskDifficulties;
53541
53763
  const difficultyAllows = (candidate) => !allowedTaskDifficulties || candidate.difficulty === "freeform" || !candidate.difficulty || allowedTaskDifficulties.includes(candidate.difficulty);
53542
- const entry = candidates.find((candidate) => nodeSatisfiesRequiredTags(candidate.requiredTags, capabilityTags) && dependenciesSatisfied(candidate) && notBeforeReady(candidate) && convergenceAllows(candidate) && targetMatches(candidate) && difficultyAllows(candidate) && parallelCapsAllow(candidate) && nodeConflictAllows(candidate));
53764
+ const notParked = (candidate) => !taskIsParked(candidate);
53765
+ const entry = candidates.find((candidate) => nodeSatisfiesRequiredTags(candidate.requiredTags, capabilityTags) && dependenciesSatisfied(candidate) && notBeforeReady(candidate) && notParked(candidate) && convergenceAllows(candidate) && targetMatches(candidate) && difficultyAllows(candidate) && parallelCapsAllow(candidate) && nodeConflictAllows(candidate));
53543
53766
  if (!entry) return null;
53544
53767
  const now = (/* @__PURE__ */ new Date()).toISOString();
53545
53768
  entry.status = "assigned";
@@ -55255,6 +55478,7 @@ ${blocks.join("\n\n")}`;
55255
55478
  compactLedger: () => compactLedger,
55256
55479
  enforceAllLedgerRotationCaps: () => enforceAllLedgerRotationCaps,
55257
55480
  enforceLedgerRotationCap: () => enforceLedgerRotationCap,
55481
+ extractJsonObjectFromSummary: () => extractJsonObjectFromSummary,
55258
55482
  getLedgerDir: () => getLedgerDir2,
55259
55483
  getLedgerSummary: () => getLedgerSummary,
55260
55484
  getSessionRecoveryContext: () => getSessionRecoveryContext,
@@ -56873,11 +57097,12 @@ ${rules.join("\n")}`;
56873
57097
  return `## Rules
56874
57098
 
56875
57099
  - **Route, don't implement.** Delegate all code reading, analysis, and execution to node agents. Never read source files or run commands in the coordinator \u2014 keep context lean. See also: **Never use local sub-agents** below.${destructiveGitRule}
56876
- - **Never use local sub-agents.** Do NOT spawn your runtime's own sub-agents (e.g. Claude Code's Task/Explore/Agent tools, or any equivalent in-process agent-spawning tool) to read code, investigate, run RCA, or implement. Such sub-agents execute on the coordinator's machine, outside the mesh \u2014 they escape mesh parallelism, the ledger/audit trail, node capability profiles, and worktree isolation, and leave no \`mesh_task_history\` record. ALL code reading, analysis, RCA, and implementation must be delegated through \`mesh_enqueue_batch\` for a currently known multi-step graph, \`mesh_enqueue_task\` for one ready task, or \`mesh_send_task\` for a same-session continuation (use \`task_mode: "live_debug_readonly"\` for read-only investigation), or cross-verified via \`mesh_magi_review\` for read-only fan-out. The coordinator's own actions are limited to \`mesh_*\` tool orchestration and synthesizing results.
56877
- - **Front-load immutable task instructions.** Include everything the agent needs (files, problem, expected fix) in \`mesh_enqueue_batch\` / \`mesh_enqueue_task\` / \`mesh_send_task\`. Put predecessor-produced data in explicit \`inputs_from\` bindings and coordinator decisions in gates; never copy untrusted worker output into a new instruction by hand when a binding can preserve provenance. Append a structured result request at the end: ask the worker to conclude with a JSON block containing \`status\`, \`changedFiles\`, \`gitStatus\`, \`validationResults\`, \`errors\`, \`nextAction\`. The daemon parses this automatically; you can read it from \`mesh_task_history\`.
57100
+ - **Never use local sub-agents.** Do NOT spawn your runtime's own sub-agents (e.g. Claude Code's Task/Explore/Agent tools, or any equivalent in-process agent-spawning tool) to read code, investigate, run RCA, or implement. Such sub-agents execute on the coordinator's machine, outside the mesh \u2014 they escape mesh parallelism, the ledger/audit trail, node capability profiles, and worktree isolation, and leave no \`mesh_task_history\` record. ALL code reading, analysis, RCA, and implementation must be delegated through \`mesh_enqueue_batch\` (the default \u2014 see Workflow 3.a), falling back to \`mesh_enqueue_task\` only for a terminal single step, to \`mesh_send_task\` for a same-session continuation (use \`task_mode: "live_debug_readonly"\` for read-only investigation), or cross-verified via \`mesh_magi_review\` for read-only fan-out. The coordinator's own actions are limited to \`mesh_*\` tool orchestration and synthesizing results.
57101
+ - **Front-load immutable task instructions.** Include everything the agent needs (files, problem, expected fix) in whichever dispatch surface Workflow 3.a selects (\`mesh_enqueue_batch\` by default; \`mesh_enqueue_task\` / \`mesh_send_task\` in their narrower cases). Put predecessor-produced data in explicit \`inputs_from\` bindings and coordinator decisions in gates; never copy untrusted worker output into a new instruction by hand when a binding can preserve provenance. Append a structured result request at the end: ask the worker to conclude with a JSON block containing \`status\`, \`changedFiles\`, \`gitStatus\`, \`validationResults\`, \`errors\`, \`nextAction\`. The daemon parses this automatically; you can read it from \`mesh_task_history\`.
56878
57102
  - **Reuse idle sessions.** For follow-up, retry, commit/push, or cleanup on the same issue, send only the delta to the existing idle session. Start a fresh session only when: (a) branch/worktree isolation is required, (b) the existing session had a dispatch failure or provider mismatch, (c) the transcript/runtime is contaminated or interrupted, (d) the user explicitly asks for a different provider/session, or (e) **the delta is a genuinely NEW subject rather than a continuation** \u2014 a new topic appended to an existing session can be dropped or re-run as the previous task, so give it its own task even when a session sits idle. Continuation of the same issue in an already-idle session is allowed and preferred \u2014 this rule blocks concurrent unrelated work interleaved into a live (still-generating) session, not sequential same-issue follow-ups. The test is subject continuity, not timing: carrying an investigation forward into its own fix is the SAME subject and belongs in that session (Workflow 3f), while an unrelated bug is a new subject even if the same session just went idle.
56879
57103
  - **Nodes are separate machines with separate checkouts \u2014 not interchangeable execution slots.** Each node is a different physical computer with its own clone of the repo. Work done on another node must be committed, pushed, and pulled back before this machine sees it, and since RELEASE/DEPLOY runs on the coordinator's own machine, sending a code change elsewhere buys a round trip out and another one back. So **default to this coordinator's own machine for code changes** \u2014 its local node (base or a worktree cloned from it). Routing to a DIFFERENT machine is the exception and needs a reason, of which there are exactly two: (a) **platform-specific verification** that cannot be done here \u2014 win32 PATH/registry, a clean install/uninstall on that OS, that machine's package-manager state; or (b) **parallelizing read-only investigation** across machines. "That node is idle" is not a reason. If you catch yourself dispatching a fix to another machine without (a) or (b), route it here instead.
56880
57104
  - **Don't split investigation from the fix.** When a task will plainly end in a code change, dispatch it as \`code_change\` from the start \u2014 the in-session handoff and split criteria live in Workflow 3f. Split only when the fix genuinely belongs on another machine for reason (a) above; redoing an investigator's context in a fresh session (worse, on another machine) is pure loss.
57105
+ - **Batch is the default enqueue surface.** Apply Workflow 3.a: before every enqueue, ask whether you will read this task's result and then dispatch more work. If yes, that successor is declarable \u2014 submit the whole plan as one \`mesh_enqueue_batch\` with \`inputs_from\` / \`run_if\` / \`gated_by\`, rather than enqueueing a step, waiting, and enqueueing the next by hand. \`mesh_enqueue_task\` is the fallback for a genuinely terminal single step, and it takes an \`orchestration_decision\` stating which \`single_reason\` applies.
56881
57106
  - **Base nodes are reserved for environment-specific testing.** Apply Workflow 3.b0: only work that verifies a machine's physical environment runs on a base node (pinned with \`required_tags\`/\`target_node_id\`); every ordinary \`code_change\` gets its own cloned worktree. Node availability is not branch isolation.
56882
57107
  - **Worktree affinity.** Apply Workflow 3.b1: route a branch's follow-ups back to its own worktree node (\`required_tags: ["worktree=<branch>"]\` or \`target_node_id\`, taken from the \`mesh_clone_node\` result or a live \`mesh_status\`); only \`convergence\` (merge/push) runs base-side.
56883
57108
  - **Classify task difficulty honestly.** Judge each task's real difficulty (\`easy\`/\`medium\`/\`difficult\`/\`freeform\`) per the Task difficulty section above \u2014 it is a routing hint, and the matched slot's own model/thinking is what launches. Never bend difficulty to chase a model; retune slots instead (\`mesh_node_slots_set\`).
@@ -56943,7 +57168,7 @@ When you compose the task message you dispatch to a node, include this requireme
56943
57168
  | \`mesh_view_queue\` | Queue status \u2014 pending/assigned/completed/failed/cancelled |
56944
57169
  | \`mesh_graph_view\` | Inspect orchestration graphs \u2014 node states, gates awaiting you, workspace sagas, why something is blocked |
56945
57170
  | \`mesh_graph_gate_claim\` | Take the lease on a gate awaiting a coordinator; returns the fencing token + generation a release needs |
56946
- | \`mesh_graph_gate_release\` | Pass a gate you hold \u2014 the ONLY way through one (no timeout ever passes a gate) |
57171
+ | \`mesh_graph_gate_release\` | Pass a gate you hold \u2014 the ONLY way through one (no timeout ever passes a gate). **A gate and its dependents are one unit**: a gate earns its keep only when some task names it in \`gated_by\`, because releasing it is what dispatches that task. A gate nothing depends on opens nothing and is pure claim/release overhead \u2014 declare the follower in the same batch, or skip the gate |
56947
57172
  | \`mesh_queue_cancel\` | Cancel a queue task (audit history kept) |
56948
57173
  | \`mesh_queue_requeue\` | Return a task to pending for retry |
56949
57174
  | \`mesh_send_task\` | Push a task straight to a specific node/session |
@@ -57008,13 +57233,22 @@ Before doing any coordinator work, confirm that the actual callable tool list in
57008
57233
  WORKFLOW_SECTION = `## Orchestration Workflow
57009
57234
 
57010
57235
  1. **Assess** \u2014 Call \`mesh_status\` to see which nodes are healthy and available. Check \`mesh_task_history\` to understand what has already been done in this mesh \u2014 previous delegations, completions, and failures.
57011
- 2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign. **For multi-task work, create a mission first**: call \`mesh_mission_upsert\` with a title and goal, then attach every enqueued task with \`mission_id\`. Express "B after A" ordering with \`depends_on\` instead of waiting and polling \u2014 the system claims dependents automatically when their dependencies complete. When the mission's outcome is decided, update its status (\`completed\`/\`abandoned\`) via \`mesh_mission_upsert\`. If the prompt already shows an **Active Mission**, continue it from its current task state \u2014 do not re-enqueue tasks that already exist.
57236
+ 2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign. **For multi-task work, create a mission first**: call \`mesh_mission_upsert\` with a title and goal, then submit the plan as one \`mesh_enqueue_batch\` carrying that \`mission_id\` (a top-level \`mission_id\` applies to every entry). Express "B after A" ordering with \`depends_on\` instead of waiting and polling \u2014 the system claims dependents automatically when their dependencies complete. When the mission's outcome is decided, update its status (\`completed\`/\`abandoned\`) via \`mesh_mission_upsert\`. If the prompt already shows an **Active Mission**, continue it from its current task state \u2014 do not re-enqueue tasks that already exist.
57012
57237
  3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
57013
- a. **Batch-first rule:** submit one \`mesh_enqueue_batch\` whenever the currently known plan contains two or more graph steps. A graph may include worker tasks, selected predecessor outputs via \`inputs_from\`, declarative \`run_if\` branches, delayed \`workspace_ref\` preparation, and coordinator gates for approval, Refinery, CI waiting, publish, or deployment. Submit the whole materializable plan once; do not wait and enqueue each known step separately.
57238
+ a. **Batch-first rule.** Before enqueueing anything, answer ONE question: *when this task reports back, will I read its result and then dispatch more work \u2014 or does the result go to the user with nothing behind it?*
57239
+ - **You will act on the result \u2192 \`mesh_enqueue_batch\`.** If you already intend to read the output and dispatch a next step, that next step is a step you can declare. Declare it: bind the evidence with \`inputs_from\`, branch on it with \`run_if\`, or put your own decision behind a coordinator gate and point the follower at it with \`gated_by\`. "I need to see the result first" is HOW a graph edge is expressed, not a reason to skip the graph \u2014 that is exactly what \`inputs_from\` and gates are for. An investigation you plan to act on is, by this test, a declarable two-step plan.
57240
+ - **The result goes to the user and nothing follows \u2192 \`mesh_enqueue_task\`.** One task, no successor you intend to dispatch yourself. Same-subject continuation of an already-running session is \`mesh_send_task\`, not a new enqueue either way.
57241
+ - Submit the whole materializable plan in ONE batch. A batch may mix worker tasks, \`inputs_from\` bindings, \`run_if\` branches, delayed \`workspace_ref\` preparation, and coordinator gates for approval, Refinery, CI waiting, publish, or deployment. Do not enqueue one known step, wait for it, and then enqueue the next known step by hand.
57242
+ - **A mesh with idle nodes or a short plan does not remove this requirement.** "It is only two steps" and "I will just enqueue the next one when this finishes" are the two ways the graph gets skipped; neither is a reason. Two declarable steps is precisely the case the batch surface exists for.
57243
+ - **"The only thing I am sure of right now is this one task" is not evidence of a single.** That feeling is about your confidence, not about how many steps are settled; two steps are routinely settled at once while it still feels that way. Count the steps you would actually dispatch. If that count is two or more, it is a batch no matter how the plan feels.
57244
+ - **"I cannot choose the next step until I see this result" states that the next step EXISTS.** What the result decides is *which branch you take*, not *whether* there is a follow-up \u2014 and a branch that turns on a predecessor's output is what \`run_if\` selects, what \`inputs_from\` feeds, and what a coordinator gate holds until you decide. Needing to look at evidence before choosing is the ordinary case for a graph edge, not an exception to it.
57245
+ - **A gate or a batch is not overhead when something follows it.** The extra call buys the dispatch of everything behind it, so it costs one call and saves a whole round-trip through you. The only genuinely wasteful gate is one nothing depends on \u2014 a gate no task names in \`gated_by\` opens nothing when released, which the release response tells you about; that case is answered by declaring the follower, not by abandoning the batch.
57014
57246
 
57015
- Use \`mesh_enqueue_task\` only when exactly one new worker task is currently known and there is no known downstream step to declare, or when continuing an already-running session via \`mesh_send_task\` is the correct same-subject handoff. If later work is genuinely unknown, enqueue the one ready task and start a new batch when the next frontier becomes known.
57247
+ When you do use \`mesh_enqueue_task\`, pass \`orchestration_decision\` recording why a single was right (\`single_reason\`: \`only_one_step_known\` / \`future_step_not_specifiable\` / \`same_session_continuation\` / \`legacy_client\` / \`operator_override\`). Omitting it is recorded as \`decision_missing\`.
57016
57248
 
57017
- Do not invent speculative downstream instructions merely to form a batch. Use an \`inputs_from\` binding when the instruction is stable but needs predecessor evidence; use a coordinator gate when a human/coordinator decision or external side effect must happen first; use \`run_if\` for a structured result predicate. If none can state the future step faithfully, single-task enqueue is correct.
57249
+ Do not invent speculative downstream instructions merely to form a batch \u2014 a step you would not actually dispatch does not belong in the graph. But check the three declaration surfaces before concluding a step is unstatable: \`inputs_from\` when the instruction is stable and only needs predecessor evidence, a coordinator gate when a human/coordinator decision or external side effect must come first, \`run_if\` when the branch turns on a structured result predicate. Only when none of the three can state the future step faithfully is single-task enqueue correct.
57250
+
57251
+ **The line between the two failures.** The rebuttals above are aimed at declining to declare a step you already intend to dispatch; this guard is aimed at fabricating one you do not. They never both apply to the same step: the test is whether the step exists in your plan, not whether its details are settled. A step you intend to run but whose exact branch depends on evidence is KNOWN and belongs in the batch \u2014 that dependency is what the three surfaces express. A step you would have to make up to fill the batch is UNKNOWN and belongs nowhere. When you genuinely cannot tell which one you are looking at, enqueue the single and record \`future_step_not_specifiable\`; that is the honest answer and it is measured.
57018
57252
 
57019
57253
  Either way, idle node agents automatically pull tasks from the queue and begin working. A batch inserts atomically (a mid-batch error such as a cycle or bad difficulty rolls the whole set back, so no half-registered chain), and each task's \`depends_on\` may name sibling tasks by their batch-local \`ref\` label, forward references included.
57020
57254
  b. **Node Preparation**: Reuse an existing idle session on the correct node/provider before launching a new chat/session. Call \`mesh_launch_session\` only when no suitable session exists, when the user explicitly asks for a fresh provider/session, or when branch/worktree isolation requires it. **A node is not limited to one live session for read-only work** \u2014 \`readonly\`/\`live_debug_readonly\` tasks are exempt from the one-active-per-node invariant, so the SAME node can auto-launch multiple concurrent read-only sessions with no worktree needed. Cloning a worktree costs roughly 10 seconds, so it is cheap enough to create one whenever write work needs a free node; use it for branch isolation, for parallel write tasks (one active write per node), or when a node's read-only queue is deep enough that a second node would clearly finish faster \u2014 call \`mesh_clone_node\` to create the worktree node first.
@@ -59510,6 +59744,7 @@ ${rendered}`, "utf-8");
59510
59744
  void opts.registerNode;
59511
59745
  return {
59512
59746
  nowMs: () => Date.now(),
59747
+ resolveBaseRevision: (req) => defaultResolveBaseRevision(req),
59513
59748
  createWorktree: (req) => defaultCreateWorktree(req),
59514
59749
  findOwnedWorktree: (req) => defaultFindOwnedWorktree(req),
59515
59750
  inspectWorktree: (req) => defaultInspectWorktree(req),
@@ -59555,6 +59790,40 @@ ${rendered}`, "utf-8");
59555
59790
  env: gitChildEnv()
59556
59791
  });
59557
59792
  }
59793
+ async function defaultResolveBaseRevision(req) {
59794
+ let repoRoot;
59795
+ try {
59796
+ ({ repoRoot } = resolveSourceRepo(req.meshId, req.sourceNodeId));
59797
+ } catch {
59798
+ return void 0;
59799
+ }
59800
+ try {
59801
+ const { stdout } = await execFileAsync4("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
59802
+ cwd: repoRoot,
59803
+ encoding: "utf8",
59804
+ timeout: GIT_TIMEOUT_MS3,
59805
+ maxBuffer: GIT_MAX_BUFFER2,
59806
+ windowsHide: true,
59807
+ env: gitChildEnv()
59808
+ });
59809
+ const branch = stdout.trim();
59810
+ if (branch && branch !== "HEAD") return branch;
59811
+ } catch {
59812
+ }
59813
+ try {
59814
+ const { stdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], {
59815
+ cwd: repoRoot,
59816
+ encoding: "utf8",
59817
+ timeout: GIT_TIMEOUT_MS3,
59818
+ maxBuffer: GIT_MAX_BUFFER2,
59819
+ windowsHide: true,
59820
+ env: gitChildEnv()
59821
+ });
59822
+ return stdout.trim() || void 0;
59823
+ } catch {
59824
+ return void 0;
59825
+ }
59826
+ }
59558
59827
  async function defaultCreateWorktree(req) {
59559
59828
  const existing = await defaultFindOwnedWorktree({
59560
59829
  meshId: req.meshId,
@@ -59900,8 +60169,26 @@ ${rendered}`, "utf-8");
59900
60169
  }
59901
60170
  return compensateClaimedIntent({ ...intent, ownerTag }, ports, generation);
59902
60171
  }
59903
- if (intent.sagaState === "declared" && !intent.baseRevision) {
59904
- return { graphId, workspaceRef, sagaState: "declared", action: "skipped_no_base" };
60172
+ let baseRevision = intent.baseRevision;
60173
+ if (intent.sagaState === "declared" && !baseRevision) {
60174
+ const derived = await ports.resolveBaseRevision({
60175
+ meshId: intent.meshId,
60176
+ graphId,
60177
+ workspaceRef,
60178
+ sourceNodeId: intent.sourceNodeId
60179
+ }).catch(() => void 0);
60180
+ if (derived) {
60181
+ store.transaction(() => {
60182
+ store.graphStore().patchWorkspaceIntent(graphId, workspaceRef, {
60183
+ baseRevision: derived
60184
+ }, nowIso, { leaseGeneration: generation });
60185
+ });
60186
+ baseRevision = derived;
60187
+ LOG2.info("MeshGraphWorkspace", `derived base revision '${derived}' for ${graphId}/${workspaceRef} from source node ${intent.sourceNodeId ?? "base"}`);
60188
+ } else {
60189
+ LOG2.debug("MeshGraphWorkspace", `${graphId}/${workspaceRef} stays declared: no base_revision and none derivable from source node ${intent.sourceNodeId ?? "(none declared)"}`);
60190
+ return { graphId, workspaceRef, sagaState: "declared", action: "skipped_no_base" };
60191
+ }
59905
60192
  }
59906
60193
  store.transaction(() => {
59907
60194
  store.graphStore().patchWorkspaceIntent(graphId, workspaceRef, {
@@ -59909,7 +60196,7 @@ ${rendered}`, "utf-8");
59909
60196
  ownerTag
59910
60197
  }, nowIso, { leaseGeneration: generation });
59911
60198
  });
59912
- return prepareClaimedIntent({ ...intent, sagaState: "preparing", ownerTag }, ports, generation, faults);
60199
+ return prepareClaimedIntent({ ...intent, baseRevision, sagaState: "preparing", ownerTag }, ports, generation, faults);
59913
60200
  }
59914
60201
  async function prepareClaimedIntent(intent, ports, generation, faults) {
59915
60202
  const store = MeshRuntimeStore.getInstance();
@@ -60537,9 +60824,11 @@ ${rendered}`, "utf-8");
60537
60824
  const materializedNodeIds = advanceReleasedGateDownstream(store, gate, node, nowIso);
60538
60825
  const freshNodes = graphStore.listNodes(gate.graphId);
60539
60826
  const graph = graphStore.getGraph(gate.graphId);
60827
+ let graphCompleted = false;
60540
60828
  if (graph && freshNodes.every(isTerminalEquivalent)) {
60541
60829
  graphStore.updateGraphStatus(gate.graphId, "completed", nowIso, true);
60542
60830
  insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_completed", { graphId: gate.graphId }, nowIso);
60831
+ graphCompleted = true;
60543
60832
  } else if (graph?.status === "waiting_gate") {
60544
60833
  const stillWaiting = graphStore.listGatesByGraph(gate.graphId).some((g) => g.gateId !== gate.gateId && (g.state === "awaiting_coordinator" || g.state === "claimed"));
60545
60834
  if (!stillWaiting) graphStore.updateGraphStatus(gate.graphId, "active", nowIso);
@@ -60563,7 +60852,16 @@ ${rendered}`, "utf-8");
60563
60852
  gateId: gate.gateId
60564
60853
  }, nowIso);
60565
60854
  }
60566
- return { released: true, duplicate: false, gate: graphStore.getGate(gate.gateId), materializedNodeIds };
60855
+ return {
60856
+ released: true,
60857
+ duplicate: false,
60858
+ gate: graphStore.getGate(gate.gateId),
60859
+ materializedNodeIds,
60860
+ // Read from the edge set this release already walked — a gate with an empty
60861
+ // directDownstream is terminal, and terminal gates materialize nothing by design.
60862
+ downstreamNodeCount: directDownstream.size,
60863
+ graphCompleted
60864
+ };
60567
60865
  });
60568
60866
  try {
60569
60867
  drainMeshGraphOutbox(input.meshId);
@@ -60824,6 +61122,18 @@ ${rendered}`, "utf-8");
60824
61122
  ...p.replayed ? { replayed: true } : {}
60825
61123
  });
60826
61124
  }
61125
+ function recordSingleEnqueueDecision(meshId, p) {
61126
+ safeAppend(meshId, "single_enqueue_decision", {
61127
+ taskId: p.taskId,
61128
+ enqueueSurface: "single",
61129
+ ...p.missionId ? { missionId: p.missionId } : {},
61130
+ ...p.coordinatorSessionId ? { coordinatorSessionId: p.coordinatorSessionId } : {},
61131
+ orchestrationDecision: p.decision,
61132
+ ...p.decisionMissing ? { decisionMissing: true } : {},
61133
+ ...p.declaredEligibleSingle ? { declaredEligibleSingle: true } : {},
61134
+ ...p.batchCapabilityAvailable ? { batchCapabilityAvailable: p.batchCapabilityAvailable } : {}
61135
+ }, p.taskId);
61136
+ }
60827
61137
  function recordGraphEnqueueValidationFailed(meshId, p) {
60828
61138
  safeAppend(meshId, "graph_enqueue_validation_failed", {
60829
61139
  code: p.code,
@@ -60881,6 +61191,7 @@ ${rendered}`, "utf-8");
60881
61191
  }
60882
61192
  var MESH_VALID_SINGLE_REASONS;
60883
61193
  var MESH_SUPERSEDED_SINGLE_REASONS;
61194
+ var MESH_DECLARED_ELIGIBLE_SINGLE_HINT;
60884
61195
  var init_mesh_graph_provenance = __esm2({
60885
61196
  "src/mesh/mesh-graph-provenance.ts"() {
60886
61197
  "use strict";
@@ -60898,6 +61209,7 @@ ${rendered}`, "utf-8");
60898
61209
  "workspace_unresolved",
60899
61210
  "coordinator_action_between"
60900
61211
  ];
61212
+ MESH_DECLARED_ELIGIBLE_SINGLE_HINT = "You declared known_graph_steps >= 2 but used the single-task surface. Submit the known steps as one mesh_enqueue_batch \u2014 its input bindings, conditions and coordinator gates declare the ones that need predecessor evidence, a condition, or a coordinator action \u2014 instead of enqueueing them one at a time.";
60901
61213
  }
60902
61214
  });
60903
61215
  function turnProjectionActiveWorkStatus(meshId, taskId) {
@@ -61748,8 +62060,28 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61748
62060
  }
61749
62061
  };
61750
62062
  }
61751
- function quotaFactsContextForLiveRouting(mesh, isLocalNode) {
61752
- return { nodes: mesh?.nodes, liveLocalQuota: liveLocalQuotaForRouting(isLocalNode) };
62063
+ function liveLocalProviderEnablementForRouting(loader2, isLocalNode) {
62064
+ if (!loader2) return null;
62065
+ const verdicts = /* @__PURE__ */ new Map();
62066
+ return {
62067
+ isMachineProviderEnabled: (providerType) => loader2.isMachineProviderEnabled(providerType),
62068
+ isMachineQuotaEnabled: (providerType) => loader2.isMachineQuotaEnabled ? loader2.isMachineQuotaEnabled(providerType) : true,
62069
+ isLocalNode(node) {
62070
+ let verdict = verdicts.get(node);
62071
+ if (verdict === void 0) {
62072
+ verdict = !!node && isLocalNode(node) === true;
62073
+ verdicts.set(node, verdict);
62074
+ }
62075
+ return verdict;
62076
+ }
62077
+ };
62078
+ }
62079
+ function quotaFactsContextForLiveRouting(mesh, isLocalNode, providerLoader) {
62080
+ return {
62081
+ nodes: mesh?.nodes,
62082
+ liveLocalQuota: liveLocalQuotaForRouting(isLocalNode),
62083
+ providerEnablement: liveLocalProviderEnablementForRouting(providerLoader, isLocalNode)
62084
+ };
61753
62085
  }
61754
62086
  function directQuotaEntryFor(node, providerType) {
61755
62087
  const facts = node?.nodeFacts;
@@ -61806,6 +62138,28 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61806
62138
  const ageText = Number.isFinite(ageMs2) ? `${Math.round(ageMs2 / 6e4)}m` : "unparseable";
61807
62139
  LOG2.info("MeshQuota", `QUOTA GATE: provider '${providerType}' on node ${nodeId} fails open \u2014 snapshot age ${ageText} exceeds the ${Math.round(staleAfterMs / 6e4)}m freshness threshold`);
61808
62140
  }
62141
+ function classifyAbsentQuotaReason(node, providerType, context) {
62142
+ const enablement = context?.providerEnablement;
62143
+ if (!enablement) return "unclassified_remote";
62144
+ const sourceNode = cloneSourceNodeFor(node, context);
62145
+ if (!enablement.isLocalNode(node) && !(sourceNode !== void 0 && enablement.isLocalNode(sourceNode))) {
62146
+ return "unclassified_remote";
62147
+ }
62148
+ if (!enablement.isMachineProviderEnabled(providerType)) return "provider_disabled";
62149
+ if (!enablement.isMachineQuotaEnabled(providerType)) return "probe_disabled";
62150
+ return "not_measured";
62151
+ }
62152
+ function logAbsentQuotaFailOpen(node, providerType, policy, now, context) {
62153
+ const staleAfterMs = resolveQuotaRoutingPolicy(policy).staleAfterMs;
62154
+ const nodeId = typeof node?.nodeId === "string" && node.nodeId ? node.nodeId : typeof node?.id === "string" && node.id ? node.id : "unknown";
62155
+ const key2 = `${nodeId} ${providerType}`;
62156
+ const last = absentFailOpenLoggedAt.get(key2);
62157
+ if (last !== void 0 && now - last < staleAfterMs) return;
62158
+ absentFailOpenLoggedAt.set(key2, now);
62159
+ const reason = classifyAbsentQuotaReason(node, providerType, context);
62160
+ const reasonText = reason === "probe_disabled" ? "quota probing is disabled for this provider on this machine (quotaEnabled: false \u2014 an intended opt-out, not a fault)" : reason === "provider_disabled" ? "this provider is not enabled on this machine" : reason === "not_measured" ? "no snapshot has been measured yet" : "this daemon cannot read that node's machineProviders config (remote node, or its own daemon has not injected one) \u2014 cannot tell not-yet-measured from an opt-out; check the owning node's config directly";
62161
+ LOG2.info("MeshQuota", `QUOTA GATE: provider '${providerType}' on node ${nodeId} fails open \u2014 no quota entry (${reason}: ${reasonText})`);
62162
+ }
61809
62163
  function isSessionResetImminent(session, facts, quota, imminentMs, now) {
61810
62164
  const resetsAt = Number(session?.resetsAt);
61811
62165
  if (!Number.isFinite(resetsAt) || resetsAt <= 0) return false;
@@ -61827,7 +62181,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61827
62181
  }
61828
62182
  function evaluateProviderQuotaGate(node, providerType, policy, now = Date.now(), context) {
61829
62183
  const entry = quotaEntryFor(node, providerType, context, now);
61830
- if (!entry) return null;
62184
+ if (!entry) {
62185
+ logAbsentQuotaFailOpen(node, providerType, policy, now, context);
62186
+ return null;
62187
+ }
61831
62188
  const { facts, quota } = entry;
61832
62189
  let sessionTrustworthy = true;
61833
62190
  let weeklyTrustworthy = true;
@@ -61989,6 +62346,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61989
62346
  var PROVIDER_QUOTA_EXHAUSTED_SKIP_REASON;
61990
62347
  var ALL_PROVIDERS_QUOTA_GATED_SKIP_REASON;
61991
62348
  var staleFailOpenLoggedAt;
62349
+ var absentFailOpenLoggedAt;
61992
62350
  var DEFAULT_WEEKLY_WINDOW_MINUTES;
61993
62351
  var DEFAULT_SESSION_WINDOW_MINUTES;
61994
62352
  var lastQuotaRankingByNode;
@@ -62004,6 +62362,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
62004
62362
  PROVIDER_QUOTA_EXHAUSTED_SKIP_REASON = "provider_quota_exhausted";
62005
62363
  ALL_PROVIDERS_QUOTA_GATED_SKIP_REASON = "all_providers_quota_gated";
62006
62364
  staleFailOpenLoggedAt = /* @__PURE__ */ new Map();
62365
+ absentFailOpenLoggedAt = /* @__PURE__ */ new Map();
62007
62366
  DEFAULT_WEEKLY_WINDOW_MINUTES = 7 * 24 * 60;
62008
62367
  DEFAULT_SESSION_WINDOW_MINUTES = 5 * 60;
62009
62368
  lastQuotaRankingByNode = /* @__PURE__ */ new Map();
@@ -64991,36 +65350,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
64991
65350
  init_chat_history();
64992
65351
  }
64993
65352
  });
64994
- function s(v) {
64995
- return typeof v === "string" && v.trim() ? v.trim() : "";
64996
- }
64997
- function meshEventTraceKey(ctx) {
64998
- const segs = [`task=${s(ctx.taskId) || "-"}`];
64999
- const eventId = s(ctx.eventId);
65000
- if (eventId) segs.push(`evt=${eventId}`);
65001
- segs.push(`sess=${s(ctx.sessionId) || "-"}`);
65002
- const nodeId = s(ctx.nodeId);
65003
- if (nodeId) segs.push(`node=${nodeId}`);
65004
- const meshId = s(ctx.meshId);
65005
- if (meshId) segs.push(`mesh=${meshId}`);
65006
- const event = s(ctx.event);
65007
- if (event) segs.push(`event=${event}`);
65008
- return segs.join(" ");
65009
- }
65010
- function traceMeshEventStage(stage, ctx, detail) {
65011
- LOG2.info(CAT, `[stage:${stage}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
65012
- }
65013
- function traceMeshEventDrop(reason, ctx, detail) {
65014
- LOG2.warn(CAT, `[drop:${reason}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
65015
- }
65016
- var CAT;
65017
- var init_mesh_event_trace = __esm2({
65018
- "src/mesh/mesh-event-trace.ts"() {
65019
- "use strict";
65020
- init_logger();
65021
- CAT = "EvtTrace";
65022
- }
65023
- });
65024
65353
  function isAnthropicProvider(providerType) {
65025
65354
  const p = typeof providerType === "string" ? providerType.trim().toLowerCase() : "";
65026
65355
  return p.length > 0 && ANTHROPIC_PROVIDER_TYPES.has(p);
@@ -65495,6 +65824,43 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65495
65824
  const anchorMs = Date.parse(task.requeuedAt || task.createdAt || "");
65496
65825
  return Number.isFinite(anchorMs) ? nowMs - anchorMs : null;
65497
65826
  }
65827
+ function resolveTargetPinTtlVerdict(components, task, nowMs = Date.now()) {
65828
+ const wallAgeMs = targetPinAgeMs(task, nowMs);
65829
+ if (wallAgeMs === null) return { expired: false, ageMs: null, suspended: false };
65830
+ const targetSessionId = readNonEmptyString(task.targetSessionId);
65831
+ const key2 = `${task.meshId}::${task.id}`;
65832
+ const prior = targetPinGeneratingCreditMs.get(key2);
65833
+ const generating = !!targetSessionId && resolveSessionBusyVerdict(components, targetSessionId) === "GENERATING";
65834
+ let creditMs = prior?.creditMs ?? 0;
65835
+ if (generating && prior) {
65836
+ creditMs += Math.max(0, nowMs - prior.lastSeenMs);
65837
+ }
65838
+ targetPinGeneratingCreditMs.set(key2, { creditMs, lastSeenMs: nowMs });
65839
+ const unproductiveAgeMs = Math.max(0, wallAgeMs - creditMs);
65840
+ return {
65841
+ // A tick on which the addressee is OBSERVABLY GENERATING never expires the pin,
65842
+ // independently of the accumulated credit.
65843
+ //
65844
+ // The credit ledger alone is not sufficient here, and the difference is the
65845
+ // whole live defect. Credit only accrues from the SECOND observation onward
65846
+ // (the first has no prior interval to bank), and it is in-memory — so a pin
65847
+ // that crossed the wall-clock TTL while the daemon was not watching, or before
65848
+ // a restart, would arrive at its very first post-restart observation with zero
65849
+ // credit and expire on the spot, while the session it is addressed to is
65850
+ // visibly mid-turn. That is exactly the observed failure (`unclaimed 902s`
65851
+ // logged on an `agent:ready`, i.e. at the end of real work) reproduced by a
65852
+ // different route.
65853
+ //
65854
+ // Gating on the live verdict makes "is the addressee working right now?" the
65855
+ // decisive question and leaves the credit ledger as what it should be: an
65856
+ // optimisation that stops intermittent work from silently burning the budget.
65857
+ // It cannot make a pin immortal — the verdict is re-evaluated every tick from
65858
+ // live state and only a LOCAL, observably-generating session can produce it.
65859
+ expired: !generating && unproductiveAgeMs >= TARGET_SESSION_PIN_TTL_MS,
65860
+ ageMs: unproductiveAgeMs,
65861
+ suspended: generating
65862
+ };
65863
+ }
65498
65864
  function resolveDeadTargetVerdict(components, meshId, mesh, task) {
65499
65865
  const NOT_DEAD = { dead: false, nodeDead: false, reason: "" };
65500
65866
  const targetSessionId = readNonEmptyString(task.targetSessionId);
@@ -65570,6 +65936,21 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65570
65936
  summary: "the node's workspace is dirty, so auto-launch is blocked to avoid clobbering uncommitted changes",
65571
65937
  nextAction: "Clean or commit the node's working tree (or fast-forward it); the task will then auto-assign."
65572
65938
  };
65939
+ if (reason === PARKED_SKIP_REASON) {
65940
+ const base = "it was pinned to a specific session, that pin went stale, and the task is now PARKED \u2014 deliberately held for you rather than re-homed onto another session, because a delta written for one session's context becomes a context-free instruction anywhere else";
65941
+ if (evidence === "consumed") return {
65942
+ summary: `${base}. The delivery record shows the addressed session DID receive and start acting on this message, so the parked row is a bookkeeping remnant, not a lost delta`,
65943
+ nextAction: "Do NOT re-send it. Confirm with mesh_read_chat / mesh_read_terminal that the work is under way, then clear the park with mesh_queue_cancel (or requeue it only if you genuinely want it run again)."
65944
+ };
65945
+ if (evidence === "delivered") return {
65946
+ summary: `${base}. The message WAS handed to that session's transport but no turn start was echoed, so whether it acted on it is unconfirmed`,
65947
+ nextAction: "Check the session (mesh_read_chat / mesh_read_terminal) before acting. If it is already handling it, cancel the parked row; if not, mesh_queue_requeue with target_session_id=<live session> \u2014 and pass message=<rewritten instruction> if the situation moved on while it waited."
65948
+ };
65949
+ return {
65950
+ summary: `${base}. No delivery to that session was ever recorded, so the message did not reach it and the worker is still acting on its previous instructions`,
65951
+ nextAction: "Re-target it with mesh_queue_requeue(target_session_id=<live session>), or drop the pin with clear_target_session to let any compatible session take it. Re-read the work produced meanwhile and pass message=<rewritten instruction> if the delta is now partly stale; mesh_queue_cancel if it is moot."
65952
+ };
65953
+ }
65573
65954
  if (reason === "target_session_pin_expired") {
65574
65955
  if (evidence === "consumed") return {
65575
65956
  summary: "it was pinned to a specific session and the pin TTL expired before the queue row was claimed \u2014 but the delivery record shows this session DID receive and start acting on the message (a turn was started for it), so the queue row lagging is a bookkeeping gap, not a lost delta",
@@ -65616,11 +65997,11 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65616
65997
  const targetCoordinatorDaemonId = readNonEmptyString(loadConfig2().machineId);
65617
65998
  const targetCoordinatorSessionId = readNonEmptyString(task?.sourceCoordinatorSessionId);
65618
65999
  const nodeLabel = readNonEmptyString(nodeId) || readNonEmptyString(task?.targetNodeId);
65619
- const evidence = reason === "target_session_pin_expired" ? resolveTaskDeliveryEvidence(meshId, taskId) : void 0;
66000
+ const evidence = reason === "target_session_pin_expired" || reason === PARKED_SKIP_REASON ? resolveTaskDeliveryEvidence(meshId, taskId) : void 0;
65620
66001
  const { summary, nextAction } = actionableSkipGuidance(reason, evidence);
65621
66002
  const providerAvailabilityResult = reason.startsWith("provider") || reason === "missing_provider_priority";
65622
66003
  const reachabilityResult = reason.startsWith("remote_auto_launch");
65623
- const closing = reason === "target_session_pin_expired" ? "The stale pin has already been cleared, so the task is now claimable by any compatible session \u2014 the action above is about the session it was originally addressed to." : providerAvailabilityResult ? "This result needs action if it persists: a later provider-status refresh or an already-starting usable session can clear it, but a genuinely missing, disabled, or misconfigured provider will keep the task pending until you fix that configuration." : reachabilityResult ? "This result needs action if it persists: the node reconnecting (or re-registering its daemon id) clears it on its own, but a node that stays unreachable will keep the task pending until you bring it back or re-target the task." : "This is an actionable blocker \u2014 it will NOT clear on its own; the task stays pending until you resolve it.";
66004
+ const closing = reason === PARKED_SKIP_REASON ? `This task is claimable by NOBODY until you act on it \u2014 no session will pick it up and no timer will re-home it. It is held for ${Math.round(PARKED_TASK_RETENTION_MS / 36e5)}h and then failed (with another notification), so it is never silently discarded. Parked rows are listed under parkedTasks in mesh_view_queue, and any mesh_queue_requeue unparks it \u2014 including one that only rewrites its message.` : reason === "target_session_pin_expired" ? "The stale pin has already been cleared, so the task is now claimable by any compatible session \u2014 the action above is about the session it was originally addressed to." : providerAvailabilityResult ? "This result needs action if it persists: a later provider-status refresh or an already-starting usable session can clear it, but a genuinely missing, disabled, or misconfigured provider will keep the task pending until you fix that configuration." : reachabilityResult ? "This result needs action if it persists: the node reconnecting (or re-registering its daemon id) clears it on its own, but a node that stays unreachable will keep the task pending until you bring it back or re-target the task." : "This is an actionable blocker \u2014 it will NOT clear on its own; the task stays pending until you resolve it.";
65624
66005
  const coordinatorMessage = `[System] A queued mesh task${nodeLabel ? ` for node ${nodeLabel}` : ""} is not being dispatched because ${summary}. ${nextAction} ${closing}`;
65625
66006
  try {
65626
66007
  queuePendingMeshCoordinatorEvent({
@@ -65649,6 +66030,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65649
66030
  var lastActionableSkipNotified;
65650
66031
  var DEAD_TARGET_GRACE_MS;
65651
66032
  var TARGET_SESSION_PIN_TTL_MS;
66033
+ var targetPinGeneratingCreditMs;
65652
66034
  var init_mesh_skip_notify = __esm2({
65653
66035
  "src/mesh/mesh-skip-notify.ts"() {
65654
66036
  "use strict";
@@ -65664,6 +66046,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65664
66046
  init_slot_model_enforcement();
65665
66047
  init_mesh_queue_assignment();
65666
66048
  init_mesh_queue_observability();
66049
+ init_mesh_task_parking();
65667
66050
  ACTIONABLE_SKIP_REASON_PREFIXES = [
65668
66051
  "target_node_id_unmatched",
65669
66052
  "no_node_satisfies_required_tags",
@@ -65686,6 +66069,14 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65686
66069
  // premise the delta was meant to correct. The coordinator must know its
65687
66070
  // addressed message lost its address.
65688
66071
  "target_session_pin_expired",
66072
+ // PIN-PARKING: the reason above's successor. The pin no longer CLEARS on expiry
66073
+ // (that silently re-homed a context-bound delta onto an arbitrary session); the
66074
+ // task PARKS instead — held, still addressed, claimable by nobody. That state is
66075
+ // by construction actionable and terminal-until-touched: nothing in the daemon
66076
+ // will ever move a parked task, so if the coordinator is not told, the work is
66077
+ // lost exactly as surely as if it had been dropped. Both reasons stay listed —
66078
+ // the old one so a version-skewed daemon's rows still page.
66079
+ PARKED_SKIP_REASON,
65689
66080
  // SLOT MODEL GUARD (absent): no slot on the node declares the task's model.
65690
66081
  // Permanent — no amount of waiting produces a slot, so the coordinator must
65691
66082
  // re-drive (adjust difficulty, target another node, ask the owner). Its
@@ -65704,6 +66095,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65704
66095
  lastActionableSkipNotified = /* @__PURE__ */ new Map();
65705
66096
  DEAD_TARGET_GRACE_MS = 6e4;
65706
66097
  TARGET_SESSION_PIN_TTL_MS = 15 * 6e4;
66098
+ targetPinGeneratingCreditMs = /* @__PURE__ */ new Map();
65707
66099
  }
65708
66100
  });
65709
66101
  function nodesForMesh(meshId) {
@@ -66112,6 +66504,173 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66112
66504
  INTRA_NODE_LOSERS_MAX = 2;
66113
66505
  }
66114
66506
  });
66507
+ function localCoordinatorDaemonId() {
66508
+ return canonicalDaemonId(readNonEmptyString(loadConfig2().machineId));
66509
+ }
66510
+ function isIdleSessionState2(state) {
66511
+ const status = readNonEmptyString(state?.status).toLowerCase();
66512
+ if (TERMINAL_SESSION_STATUSES.includes(status)) return false;
66513
+ return status === "idle" || state?.activeChat?.status === "waiting_input";
66514
+ }
66515
+ function sweepAutoLaunchOrphanSessions(components, meshId) {
66516
+ let sessions;
66517
+ try {
66518
+ sessions = components.instanceManager?.getByCategory?.("cli") || [];
66519
+ } catch {
66520
+ return;
66521
+ }
66522
+ if (!sessions.length) return;
66523
+ let queue;
66524
+ try {
66525
+ queue = getQueue(meshId);
66526
+ } catch {
66527
+ return;
66528
+ }
66529
+ const byId = new Map(queue.map((t) => [t.id, t]));
66530
+ for (const inst of sessions) {
66531
+ let state;
66532
+ try {
66533
+ state = inst.getState();
66534
+ } catch {
66535
+ continue;
66536
+ }
66537
+ const settings = state?.settings || {};
66538
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) continue;
66539
+ const originTaskId = readNonEmptyString(settings.autoLaunchedForQueueTaskId);
66540
+ if (!originTaskId) continue;
66541
+ const sessionId = readNonEmptyString(state?.instanceId);
66542
+ if (!sessionId) continue;
66543
+ if (!isIdleSessionState2(state)) continue;
66544
+ if (sessionHasActiveAssignment(meshId, sessionId)) continue;
66545
+ const originTask = byId.get(originTaskId);
66546
+ const assignedElsewhere = !!originTask && originTask.status === "assigned" && !!readNonEmptyString(originTask.assignedSessionId) && !sessionIdsEquivalent(readNonEmptyString(originTask.assignedSessionId), sessionId);
66547
+ const originGone = !originTask || originTask.status !== "pending" && originTask.status !== "assigned";
66548
+ const dedupKey = `${meshId}::${sessionId}`;
66549
+ if (!assignedElsewhere && !originGone) {
66550
+ autoLaunchOrphanFirstSeenAtMs.delete(dedupKey);
66551
+ continue;
66552
+ }
66553
+ const nowMs = Date.now();
66554
+ const firstSeenAtMs = autoLaunchOrphanFirstSeenAtMs.get(dedupKey);
66555
+ if (firstSeenAtMs === void 0) {
66556
+ autoLaunchOrphanFirstSeenAtMs.set(dedupKey, nowMs);
66557
+ if (autoLaunchOrphanFirstSeenAtMs.size > AUTO_LAUNCH_LEDGER_DEDUP_MAX) {
66558
+ const oldest = autoLaunchOrphanFirstSeenAtMs.keys().next().value;
66559
+ if (oldest !== void 0) autoLaunchOrphanFirstSeenAtMs.delete(oldest);
66560
+ }
66561
+ continue;
66562
+ }
66563
+ if (nowMs - firstSeenAtMs < AUTO_LAUNCH_ORPHAN_GRACE_MS) continue;
66564
+ if (autoLaunchOrphanNotified.has(dedupKey)) continue;
66565
+ autoLaunchOrphanNotified.add(dedupKey);
66566
+ if (autoLaunchOrphanNotified.size > AUTO_LAUNCH_LEDGER_DEDUP_MAX) {
66567
+ const oldest = autoLaunchOrphanNotified.values().next().value;
66568
+ if (oldest !== void 0) autoLaunchOrphanNotified.delete(oldest);
66569
+ }
66570
+ const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
66571
+ const detail = assignedElsewhere ? `task ${originTaskId} is assigned to a different session (${readNonEmptyString(originTask.assignedSessionId)})` : `task ${originTaskId} is no longer active (${originTask ? originTask.status : "absent from the queue"})`;
66572
+ recordAutoLaunchEvent(meshId, {
66573
+ phase: "skipped",
66574
+ taskId: originTaskId,
66575
+ reason: "auto_launch_orphan_session_detected",
66576
+ nodeId,
66577
+ sessionId
66578
+ });
66579
+ LOG2.warn("MeshQueue", `AUTOLAUNCH-ORPHAN-SWEEP: session ${sessionId}${nodeId ? ` on node ${nodeId}` : ""} (mesh ${meshId}) was auto-launched for task ${originTaskId} but ${detail}; it is idle with no work and will not self-recover.`);
66580
+ try {
66581
+ queuePendingMeshCoordinatorEvent({
66582
+ event: "mesh:dispatch_blocked",
66583
+ meshId,
66584
+ nodeLabel: nodeId || meshId,
66585
+ ...nodeId ? { nodeId } : {},
66586
+ metadataEvent: {
66587
+ source: "mesh_auto_launch_orphan_session",
66588
+ taskId: originTaskId,
66589
+ sessionId,
66590
+ reason: "auto_launch_orphan_session_detected"
66591
+ },
66592
+ coordinatorMessage: `[System] Mesh session ${sessionId}${nodeId ? ` on node ${nodeId}` : ""} was auto-launched for task ${originTaskId}, but ${detail}. The session is idle with no work assigned and nothing reclaims it on its own. Reuse it for other queued work or stop it.`,
66593
+ queuedAt: Date.now(),
66594
+ ...readNonEmptyString(originTask?.sourceCoordinatorSessionId) ? { targetCoordinatorSessionId: readNonEmptyString(originTask.sourceCoordinatorSessionId) } : {},
66595
+ ...localCoordinatorDaemonId() ? { targetCoordinatorDaemonId: localCoordinatorDaemonId() } : {}
66596
+ });
66597
+ } catch (e) {
66598
+ LOG2.warn("MeshQueue", `AUTOLAUNCH-ORPHAN-SWEEP: failed to queue the coordinator notification for session ${sessionId}: ${e?.message || e}`);
66599
+ }
66600
+ }
66601
+ }
66602
+ function autoLaunchWriteWouldClobberWinner(meshId, taskId, args, awaitClaimWindowMs2) {
66603
+ if (args.status === "completed" && readNonEmptyString(args.sessionId)) return false;
66604
+ let existing;
66605
+ try {
66606
+ existing = getQueue(meshId).find((t) => t.id === taskId)?.autoLaunch;
66607
+ } catch {
66608
+ return false;
66609
+ }
66610
+ const heldSessionId = existing ? readNonEmptyString(existing.sessionId) : "";
66611
+ if (!existing || existing.status !== "completed" || !heldSessionId) return false;
66612
+ const heldAtMs = Date.parse(existing.updatedAt);
66613
+ if (!Number.isFinite(heldAtMs) || Date.now() - heldAtMs >= awaitClaimWindowMs2) return false;
66614
+ if (sessionIdsEquivalent(readNonEmptyString(args.sessionId), heldSessionId)) return false;
66615
+ LOG2.info("MeshQueue", `AUTOLAUNCH-WINNER-CLOBBER: suppressed a '${args.status}' autoLaunch write for task ${taskId} (mesh ${meshId}) that would have overwritten the in-window launch record for session ${heldSessionId}; the field keeps pointing at the actually-launched session.`);
66616
+ return true;
66617
+ }
66618
+ function awaitClaimWindowMs(cycles) {
66619
+ return AUTO_LAUNCH_AWAIT_CLAIM_MS * Math.pow(2, Math.min(cycles, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES));
66620
+ }
66621
+ function remoteSessionAppearsLive(meshId, sessionId) {
66622
+ if (!sessionId) return false;
66623
+ try {
66624
+ return MeshRuntimeStore.getInstance().getRemoteIdleSessions(meshId).some((s2) => sessionIdsEquivalent(s2.sessionId, sessionId));
66625
+ } catch {
66626
+ return false;
66627
+ }
66628
+ }
66629
+ function inWindowAutoLaunchSessionIdsForNode(meshId, nodeId) {
66630
+ const nowMs = Date.now();
66631
+ const out = [];
66632
+ for (const task of getQueue(meshId, { status: ["pending"] })) {
66633
+ const al = task.autoLaunch;
66634
+ const sid = al ? readNonEmptyString(al.sessionId) : "";
66635
+ if (!al || al.status !== "started" && al.status !== "completed" || !sid) continue;
66636
+ if (!daemonIdsEquivalent(al.nodeId, nodeId)) continue;
66637
+ const launchedAtMs = Date.parse(al.updatedAt);
66638
+ const inBaseWindow = Number.isFinite(launchedAtMs) && nowMs - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS;
66639
+ const inBackoff = autoLaunchAwaitClaimBackoff.has(`${meshId}::${task.id}`);
66640
+ if (inBaseWindow || inBackoff) out.push(sid);
66641
+ }
66642
+ return out;
66643
+ }
66644
+ var AUTO_LAUNCH_AWAIT_CLAIM_MS;
66645
+ var AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES;
66646
+ var AUTO_LAUNCH_REMOTE_IDLE_TTL_MS;
66647
+ var TERMINAL_SESSION_STATUSES;
66648
+ var autoLaunchOrphanNotified;
66649
+ var autoLaunchOrphanFirstSeenAtMs;
66650
+ var AUTO_LAUNCH_ORPHAN_GRACE_MS;
66651
+ var autoLaunchAwaitClaimBackoff;
66652
+ var init_mesh_autolaunch_integrity = __esm2({
66653
+ "src/mesh/mesh-autolaunch-integrity.ts"() {
66654
+ "use strict";
66655
+ init_config();
66656
+ init_logger();
66657
+ init_dist();
66658
+ init_mesh_work_queue();
66659
+ init_mesh_events_utils();
66660
+ init_mesh_events_pending();
66661
+ init_mesh_queue_observability();
66662
+ init_mesh_scheduling_fitness();
66663
+ init_mesh_runtime_store();
66664
+ AUTO_LAUNCH_AWAIT_CLAIM_MS = 9e4;
66665
+ AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES = 2;
66666
+ AUTO_LAUNCH_REMOTE_IDLE_TTL_MS = 5 * 60 * 1e3;
66667
+ TERMINAL_SESSION_STATUSES = ["stopped", "failed", "terminated", "exited", "closed"];
66668
+ autoLaunchOrphanNotified = /* @__PURE__ */ new Set();
66669
+ autoLaunchOrphanFirstSeenAtMs = /* @__PURE__ */ new Map();
66670
+ AUTO_LAUNCH_ORPHAN_GRACE_MS = 3e4;
66671
+ autoLaunchAwaitClaimBackoff = /* @__PURE__ */ new Map();
66672
+ }
66673
+ });
66115
66674
  function classifiedDifficultiesForSlot(slot) {
66116
66675
  const highest = slot.difficulty?.reduce((rank, value) => {
66117
66676
  const candidate = CLASSIFIED_DIFFICULTIES.indexOf(value);
@@ -66216,7 +66775,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66216
66775
  CLASSIFIED_DIFFICULTIES = ["easy", "medium", "difficult"];
66217
66776
  }
66218
66777
  });
66219
- function localCoordinatorDaemonId() {
66778
+ function localCoordinatorDaemonId2() {
66220
66779
  return canonicalDaemonId(readNonEmptyString(loadConfig2().machineId));
66221
66780
  }
66222
66781
  function loadRepoConfigForNode(node) {
@@ -66325,7 +66884,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66325
66884
  const routingDecision = {
66326
66885
  source: routing?.source ?? "queue",
66327
66886
  selectedNodeId: ctx.nodeId,
66328
- ...localCoordinatorDaemonId() ? { daemonId: localCoordinatorDaemonId() } : {},
66887
+ ...localCoordinatorDaemonId2() ? { daemonId: localCoordinatorDaemonId2() } : {},
66329
66888
  transport: ctx.transport,
66330
66889
  // D: resolved execution profile — prefer the caller's resolved values, fall back
66331
66890
  // to what the claimed task row carries (queue/idle drains carry it on the task).
@@ -66540,7 +67099,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66540
67099
  LOG2.info("MeshQueue", `Deferring queue claim for node ${nodeId} (${sessionId}): an auto fast-forward is mutating its workspace \u2014 task left pending, claim re-fires next tick`);
66541
67100
  return false;
66542
67101
  }
66543
- const quotaClaimBlock = evaluateProviderQuotaGate(node, providerType, mesh?.policy?.quotaRouting ?? null, Date.now(), quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode));
67102
+ const quotaClaimBlock = evaluateProviderQuotaGate(node, providerType, mesh?.policy?.quotaRouting ?? null, Date.now(), quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader));
66544
67103
  if (quotaClaimTrace) quotaClaimTrace.evaluated += 1;
66545
67104
  if (quotaClaimBlock) {
66546
67105
  const observation = { nodeId, sessionId, providerType, block: quotaClaimBlock };
@@ -66645,7 +67204,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66645
67204
  nodeId,
66646
67205
  sessionId,
66647
67206
  providerType,
66648
- coordinatorDaemonId: localCoordinatorDaemonId(),
67207
+ coordinatorDaemonId: localCoordinatorDaemonId2(),
66649
67208
  coordinatorSessionId: readNonEmptyString(task.sourceCoordinatorSessionId) || void 0
66650
67209
  });
66651
67210
  dispatchAttemptId = attempt.attemptId;
@@ -66689,7 +67248,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66689
67248
  if (remoteDaemonId && components.dispatchMeshCommand) {
66690
67249
  const isLocalNode = isLocalAutoLaunchNode(node) || components.cliManager.adapters.has(sessionId);
66691
67250
  if (!isLocalNode) {
66692
- const localDaemonIdForDispatch = localCoordinatorDaemonId();
67251
+ const localDaemonIdForDispatch = localCoordinatorDaemonId2();
66693
67252
  const sourceCoordinatorSessionId = readNonEmptyString(task.sourceCoordinatorSessionId) || void 0;
66694
67253
  const dispatchMeshCommand = components.dispatchMeshCommand;
66695
67254
  deliverTaskToSession(
@@ -66739,7 +67298,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66739
67298
  try {
66740
67299
  const inst = components.instanceManager.getInstance(sessionId);
66741
67300
  if (inst && typeof inst.updateSettings === "function") {
66742
- const localDaemonId = localCoordinatorDaemonId();
67301
+ const localDaemonId = localCoordinatorDaemonId2();
66743
67302
  const localSourceCoordinatorSessionId = readNonEmptyString(task.sourceCoordinatorSessionId);
66744
67303
  inst.updateSettings({
66745
67304
  meshNodeFor: meshId,
@@ -66783,7 +67342,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66783
67342
  ...typeof task.dispatchNonce === "number" ? { dispatchNonce: task.dispatchNonce } : {},
66784
67343
  // TURN-LEDGER (Stage 5): the opaque attempt identity (see remote branch above).
66785
67344
  ...dispatchAttemptId ? { attemptId: dispatchAttemptId } : {},
66786
- ...localCoordinatorDaemonId() ? { coordinatorDaemonId: localCoordinatorDaemonId() } : {},
67345
+ ...localCoordinatorDaemonId2() ? { coordinatorDaemonId: localCoordinatorDaemonId2() } : {},
66787
67346
  ...readNonEmptyString(task.sourceCoordinatorSessionId) ? { coordinatorSessionId: readNonEmptyString(task.sourceCoordinatorSessionId) } : {},
66788
67347
  ...silentIdlePushOnDispatch ? { silentIdlePush: true } : {}
66789
67348
  }
@@ -66796,38 +67355,12 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66796
67355
  task,
66797
67356
  transport: "local",
66798
67357
  ...readNonEmptyString(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString(task.sourceCoordinatorSessionId) } : {},
66799
- ...localCoordinatorDaemonId() ? { sourceCoordinatorDaemonId: localCoordinatorDaemonId() } : {},
67358
+ ...localCoordinatorDaemonId2() ? { sourceCoordinatorDaemonId: localCoordinatorDaemonId2() } : {},
66800
67359
  ...routingDecision ? { routingDecision } : {}
66801
67360
  }
66802
67361
  );
66803
67362
  return true;
66804
67363
  }
66805
- function awaitClaimWindowMs(cycles) {
66806
- return AUTO_LAUNCH_AWAIT_CLAIM_MS * Math.pow(2, Math.min(cycles, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES));
66807
- }
66808
- function remoteSessionAppearsLive(meshId, sessionId) {
66809
- if (!sessionId) return false;
66810
- try {
66811
- return MeshRuntimeStore.getInstance().getRemoteIdleSessions(meshId).some((s2) => sessionIdsEquivalent(s2.sessionId, sessionId));
66812
- } catch {
66813
- return false;
66814
- }
66815
- }
66816
- function inWindowAutoLaunchSessionIdsForNode(meshId, nodeId) {
66817
- const nowMs = Date.now();
66818
- const out = [];
66819
- for (const task of getQueue(meshId, { status: ["pending"] })) {
66820
- const al = task.autoLaunch;
66821
- const sid = al ? readNonEmptyString(al.sessionId) : "";
66822
- if (!al || al.status !== "started" && al.status !== "completed" || !sid) continue;
66823
- if (!daemonIdsEquivalent(al.nodeId, nodeId)) continue;
66824
- const launchedAtMs = Date.parse(al.updatedAt);
66825
- const inBaseWindow = Number.isFinite(launchedAtMs) && nowMs - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS;
66826
- const inBackoff = autoLaunchAwaitClaimBackoff.has(`${meshId}::${task.id}`);
66827
- if (inBaseWindow || inBackoff) out.push(sid);
66828
- }
66829
- return out;
66830
- }
66831
67364
  function sweepExpiredCooldowns() {
66832
67365
  const now = Date.now();
66833
67366
  for (const [key2, until] of autoLaunchCooldownUntil) {
@@ -66847,7 +67380,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66847
67380
  function isTerminalSessionStatus(status) {
66848
67381
  return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
66849
67382
  }
66850
- function isIdleSessionState2(state) {
67383
+ function isIdleSessionState3(state) {
66851
67384
  const status = readNonEmptyString(state?.status).toLowerCase();
66852
67385
  if (isTerminalSessionStatus(status)) return false;
66853
67386
  return status === "idle" || state?.activeChat?.status === "waiting_input";
@@ -66867,7 +67400,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66867
67400
  const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
66868
67401
  if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
66869
67402
  const sessionId = readNonEmptyString(state.instanceId);
66870
- if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState2(state)) return false;
67403
+ if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState3(state)) return false;
66871
67404
  return sessionStateLooksActive(state);
66872
67405
  });
66873
67406
  }
@@ -66898,7 +67431,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66898
67431
  const daemonId = readMeshNodeDaemonId(node ?? {});
66899
67432
  if (!daemonId) return { mode: "skip", reason: "remote_auto_launch_unsupported" };
66900
67433
  if (!components.dispatchMeshCommand) return { mode: "skip", reason: "remote_auto_launch_unsupported" };
66901
- const coordinatorDaemonId = localCoordinatorDaemonId();
67434
+ const coordinatorDaemonId = localCoordinatorDaemonId2();
66902
67435
  if (!coordinatorDaemonId) return { mode: "skip", reason: "remote_auto_launch_no_coordinator_daemon_id" };
66903
67436
  return { mode: "remote", daemonId, coordinatorDaemonId };
66904
67437
  }
@@ -66972,8 +67505,8 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66972
67505
  const reason = args.reason || args.error;
66973
67506
  const difficultyFloorSkip = args.status === "skipped" && isDifficultyFloorWaitReason(reason);
66974
67507
  if (difficultyFloorSkip) {
66975
- handleDifficultyFloorSkip({ meshId, taskId, reason, nodeId: args.nodeId, coordinatorDaemonId: localCoordinatorDaemonId() });
66976
- } else {
67508
+ handleDifficultyFloorSkip({ meshId, taskId, reason, nodeId: args.nodeId, coordinatorDaemonId: localCoordinatorDaemonId2() });
67509
+ } else if (!autoLaunchWriteWouldClobberWinner(meshId, taskId, args, AUTO_LAUNCH_AWAIT_CLAIM_MS)) {
66977
67510
  recordTaskAutoLaunch(meshId, taskId, {
66978
67511
  status: args.status,
66979
67512
  reason,
@@ -67125,7 +67658,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67125
67658
  const nowMs = Date.now();
67126
67659
  const state = autoLaunchAwaitClaimBackoff.get(backoffKey) || { cycles: 0, nextAttemptAtMs: 0 };
67127
67660
  if (state.nextAttemptAtMs && nowMs < state.nextAttemptAtMs) return "backoff";
67128
- const atCap = state.cycles >= AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES;
67661
+ const atCap = state.cycles >= AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2;
67129
67662
  const live = remoteSessionAppearsLive(meshId, sessionId);
67130
67663
  if ((live || atCap) && nodeId && providerType) {
67131
67664
  try {
@@ -67151,7 +67684,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67151
67684
  return "respawn";
67152
67685
  }
67153
67686
  }
67154
- const cycles = Math.min(state.cycles + 1, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES);
67687
+ const cycles = Math.min(state.cycles + 1, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2);
67155
67688
  autoLaunchAwaitClaimBackoff.set(backoffKey, { cycles, nextAttemptAtMs: nowMs + awaitClaimWindowMs(cycles) });
67156
67689
  recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "awaiting_launched_session_claim_backoff", nodeId, sessionId });
67157
67690
  return "backoff";
@@ -67172,333 +67705,339 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67172
67705
  const maxParallelTasks = resolveMaxParallelTasks(mesh?.policy?.maxParallelTasks);
67173
67706
  const maxReadonlyParallelTasks = resolveMaxReadonlyParallelTasks(maxParallelTasks);
67174
67707
  for (const task of pending) {
67175
- if (!taskDependenciesSatisfied(task, statusById)) {
67176
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "dependencies_unsatisfied" });
67708
+ const taskLaunchKey = `${meshId}::${task.id}`;
67709
+ if (autoLaunchTaskInProgress.has(taskLaunchKey)) {
67710
+ recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "auto_launch_task_in_progress" });
67177
67711
  continue;
67178
67712
  }
67179
- if (!meshTaskNotBeforeReady(task)) {
67180
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "not_before_delayed" });
67181
- continue;
67182
- }
67183
- const isReadonly = isTaskReadonly(task);
67184
- if (isReadonly) {
67185
- if (activeReadonlyAssignedCount(meshId) >= maxReadonlyParallelTasks) {
67186
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_readonly_parallel_tasks_reached" });
67713
+ autoLaunchTaskInProgress.add(taskLaunchKey);
67714
+ try {
67715
+ if (!taskDependenciesSatisfied(task, statusById)) {
67716
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "dependencies_unsatisfied" });
67187
67717
  continue;
67188
67718
  }
67189
- } else if (activeWriteAssignedCount(meshId) >= maxParallelTasks) {
67190
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_parallel_tasks_reached" });
67191
- continue;
67192
- }
67193
- if (task.targetSessionId) {
67194
- const deadTarget = resolveDeadTargetVerdict(components, meshId, mesh, task);
67195
- if (deadTarget.dead) {
67196
- const requeued = requeueTask(meshId, task.id, {
67197
- reason: deadTarget.reason,
67198
- clearTargetSession: true,
67199
- // Keep the node pin if only the SESSION died on a still-live node; clear it
67200
- // when the NODE itself is absent (nothing to pin to).
67201
- clearTargetNode: deadTarget.nodeDead
67202
- });
67203
- if (requeued) {
67204
- noteTargetPinCleared(deadTarget.reason);
67205
- LOG2.warn("MeshQueue", `DEAD-TARGET-SELFHEAL: task ${task.id} (mesh ${meshId}) was pinned to a dead target (${deadTarget.reason}); requeued${deadTarget.nodeDead ? " and unpinned node" : ""} (requeueCount=${requeued.requeueCount ?? "?"}, status=${requeued.status}).`);
67206
- }
67207
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_dead_requeued" });
67719
+ if (!meshTaskNotBeforeReady(task)) {
67720
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "not_before_delayed" });
67208
67721
  continue;
67209
67722
  }
67210
- const pinAgeMs = targetPinAgeMs(task);
67211
- if (pinAgeMs !== null && pinAgeMs >= TARGET_SESSION_PIN_TTL_MS) {
67212
- const expired = expireTaskTargetPin(meshId, task.id, { reason: "target_session_pin_expired_unclaimed" });
67213
- if (expired) {
67214
- noteTargetPinCleared("target_session_pin_expired_unclaimed");
67215
- traceMeshEventDrop("target_session_pin_expired", {
67216
- taskId: task.id,
67217
- sessionId: readNonEmptyString(task.targetSessionId),
67218
- nodeId: readNonEmptyString(task.targetNodeId),
67219
- meshId,
67220
- event: "agent:ready"
67221
- }, `unclaimed ${Math.round(pinAgeMs / 1e3)}s \u2265 ttl ${Math.round(TARGET_SESSION_PIN_TTL_MS / 1e3)}s \u2192 pin cleared, claimable`);
67222
- LOG2.warn("MeshQueue", `TARGET-PIN-TTL: task ${task.id} (mesh ${meshId}) stayed pinned-but-unclaimed for ${Math.round(pinAgeMs / 1e3)}s (ttl ${Math.round(TARGET_SESSION_PIN_TTL_MS / 1e3)}s); expired the stale target pin so a compatible session can claim it.`);
67723
+ const isReadonly = isTaskReadonly(task);
67724
+ if (isReadonly) {
67725
+ if (activeReadonlyAssignedCount(meshId) >= maxReadonlyParallelTasks) {
67726
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_readonly_parallel_tasks_reached" });
67727
+ continue;
67223
67728
  }
67224
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_pin_expired" });
67729
+ } else if (activeWriteAssignedCount(meshId) >= maxParallelTasks) {
67730
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_parallel_tasks_reached" });
67225
67731
  continue;
67226
67732
  }
67227
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
67228
- continue;
67229
- }
67230
- if (task.autoLaunch?.status === "completed" && task.autoLaunch.sessionId) {
67231
- const launchedAtMs = Date.parse(task.autoLaunch.updatedAt);
67232
- const alSessionId = readNonEmptyString(task.autoLaunch.sessionId);
67233
- const alNodeId = readNonEmptyString(task.autoLaunch.nodeId);
67234
- const alProvider = readNonEmptyString(task.autoLaunch.providerType);
67235
- if (Number.isFinite(launchedAtMs) && Date.now() - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS) {
67236
- recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "awaiting_launched_session_claim", nodeId: alNodeId, sessionId: alSessionId });
67237
- continue;
67238
- }
67239
- if (Number.isFinite(launchedAtMs) && alSessionId && alNodeId) {
67240
- const outcome = driveExpiredAwaitClaim(components, meshId, task, { sessionId: alSessionId, nodeId: alNodeId, providerType: alProvider });
67241
- if (outcome === "claimed" || outcome === "fallback") return true;
67242
- if (outcome === "backoff") continue;
67243
- }
67244
- }
67245
- const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
67246
- if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
67247
- if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
67248
- if (task.requiredTags?.length) {
67249
- const slotProviders = resolveNodeCapabilitySlots(node, meshId).map((s2) => s2.provider).filter(Boolean);
67250
- const priorities = slotProviders.length ? slotProviders : normalizeProviderPriority2(node?.policy);
67251
- const providerCandidates = priorities.length ? priorities : [void 0];
67252
- return providerCandidates.some(
67253
- (p) => nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, p))
67733
+ if (taskIsParked(task)) {
67734
+ settleParkedQueueTask(
67735
+ meshId,
67736
+ task,
67737
+ (reason) => markAutoLaunch(meshId, task.id, { status: "skipped", reason }),
67738
+ failRetentionExpiredParkedTask
67254
67739
  );
67255
- }
67256
- return true;
67257
- }) : [];
67258
- if (!candidateNodes.length) {
67259
- const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
67260
- const convergenceOntoWorktree = task.taskMode === "convergence" && Array.isArray(mesh?.nodes) && (() => {
67261
- const matched = mesh.nodes.filter((n) => !task.targetNodeId || meshNodeIdMatches(n, task.targetNodeId));
67262
- return matched.length > 0 && matched.every((n) => n?.isLocalWorktree === true);
67263
- })();
67264
- const targetTransientlyUnresolved = targetPinUnmatched && isTargetNodeTransientlyUnresolved(mesh, task);
67265
- markAutoLaunch(meshId, task.id, {
67266
- status: "skipped",
67267
- reason: convergenceOntoWorktree ? "mesh_convergence_target_is_worktree" : targetTransientlyUnresolved ? TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON : targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
67268
- nodeId: task.targetNodeId
67269
- });
67270
- continue;
67271
- }
67272
- const strategy = resolveSchedulingStrategy(mesh);
67273
- const orderedCandidateNodes = strategy === "first_eligible" ? candidateNodes : orderEligibleNodes(
67274
- meshId,
67275
- strategy,
67276
- candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
67277
- // Auto-launch drains one task at a time, so the task IS in scope here —
67278
- // pass it through for the 'fitness' strategy's task→slot ranking. The
67279
- // mesh's quotaRouting thresholds ride along so the fitness score can
67280
- // include the quota-headroom spread bonus (fail-open when unset).
67281
- { bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags }, quotaRouting: mesh?.policy?.quotaRouting ?? null, quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode) }
67282
- ).map((c) => c.node);
67283
- const skippedCandidates = [];
67284
- const SKIPPED_CANDIDATES_MAX = 5;
67285
- const markSkip = (nodeIdForSkip, reason, extra) => {
67286
- markAutoLaunch(meshId, task.id, { status: "skipped", reason, nodeId: nodeIdForSkip, ...extra || {} });
67287
- if (nodeIdForSkip && skippedCandidates.length < SKIPPED_CANDIDATES_MAX) {
67288
- skippedCandidates.push({ nodeId: nodeIdForSkip, reason });
67289
- }
67290
- };
67291
- for (const node of orderedCandidateNodes) {
67292
- const nodeId = readMeshNodeId(node);
67293
- if (!nodeId) continue;
67294
- const launchKey = `${meshId}:${nodeId}`;
67295
- const now = Date.now();
67296
- const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
67297
- if (cooldownUntil > 0 && now >= cooldownUntil) autoLaunchCooldownUntil.delete(launchKey);
67298
- if (autoLaunchInProgress.has(launchKey)) {
67299
- markSkip(nodeId, "auto_launch_in_progress");
67300
- continue;
67301
- }
67302
- if (now < cooldownUntil) {
67303
- markSkip(nodeId, "auto_launch_cooldown");
67304
- continue;
67305
- }
67306
- if (isDirtyNode(node)) {
67307
- markSkip(nodeId, "dirty_workspace");
67308
- continue;
67309
- }
67310
- if (!isLaunchableNode(node)) {
67311
- markSkip(nodeId, "node_health_not_launchable");
67312
- continue;
67313
- }
67314
- if (!isMeshNodeFreshEnoughToLaunch(node, freshnessGate)) {
67315
- markSkip(nodeId, "node_stale_behind_upstream");
67316
- continue;
67317
- }
67318
- const launchTarget = resolveAutoLaunchTarget(components, node);
67319
- if (launchTarget.mode === "skip") {
67320
- markSkip(nodeId, launchTarget.reason || "auto_launch_unavailable");
67321
- autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67322
- sweepExpiredCooldowns();
67323
67740
  continue;
67324
67741
  }
67325
- if (nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node)) {
67326
- markSkip(nodeId, "node_has_live_session_pending_claim");
67327
- continue;
67328
- }
67329
- if (!isTaskReadonly(task) && nodeHasActiveAssignment(meshId, nodeId)) {
67330
- markSkip(nodeId, "node_has_active_assignment");
67742
+ if (task.targetSessionId) {
67743
+ const deadTarget = resolveDeadTargetVerdict(components, meshId, mesh, task);
67744
+ if (deadTarget.dead) {
67745
+ const requeued = requeueTask(meshId, task.id, {
67746
+ reason: deadTarget.reason,
67747
+ clearTargetSession: true,
67748
+ // Keep the node pin if only the SESSION died on a still-live node; clear it
67749
+ // when the NODE itself is absent (nothing to pin to).
67750
+ clearTargetNode: deadTarget.nodeDead
67751
+ });
67752
+ if (requeued) {
67753
+ noteTargetPinCleared(deadTarget.reason);
67754
+ LOG2.warn("MeshQueue", `DEAD-TARGET-SELFHEAL: task ${task.id} (mesh ${meshId}) was pinned to a dead target (${deadTarget.reason}); requeued${deadTarget.nodeDead ? " and unpinned node" : ""} (requeueCount=${requeued.requeueCount ?? "?"}, status=${requeued.status}).`);
67755
+ }
67756
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_dead_requeued" });
67757
+ continue;
67758
+ }
67759
+ if (parkExpiredTargetPin(meshId, task, resolveTargetPinTtlVerdict(components, task), TARGET_SESSION_PIN_TTL_MS, (m, t, r) => parkTaskTargetPin(m, t, { reason: r }))) {
67760
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: PARKED_SKIP_REASON });
67761
+ continue;
67762
+ }
67763
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
67331
67764
  continue;
67332
67765
  }
67333
- const maxConcurrentSessions = Number(node?.policy?.maxConcurrentSessions);
67334
- if (Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0 && liveSessionCountForNode(components, meshId, nodeId) >= maxConcurrentSessions) {
67335
- markSkip(nodeId, "max_concurrent_sessions_reached");
67766
+ if (task.autoLaunch?.status === "completed" && task.autoLaunch.sessionId) {
67767
+ const launchedAtMs = Date.parse(task.autoLaunch.updatedAt);
67768
+ const alSessionId = readNonEmptyString(task.autoLaunch.sessionId);
67769
+ const alNodeId = readNonEmptyString(task.autoLaunch.nodeId);
67770
+ const alProvider = readNonEmptyString(task.autoLaunch.providerType);
67771
+ if (Number.isFinite(launchedAtMs) && Date.now() - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS) {
67772
+ recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "awaiting_launched_session_claim", nodeId: alNodeId, sessionId: alSessionId });
67773
+ continue;
67774
+ }
67775
+ if (Number.isFinite(launchedAtMs) && alSessionId && alNodeId) {
67776
+ const outcome = driveExpiredAwaitClaim(components, meshId, task, { sessionId: alSessionId, nodeId: alNodeId, providerType: alProvider });
67777
+ if (outcome === "claimed" || outcome === "fallback") return true;
67778
+ if (outcome === "backoff") continue;
67779
+ }
67780
+ }
67781
+ const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
67782
+ if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
67783
+ if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
67784
+ if (task.requiredTags?.length) {
67785
+ const slotProviders = resolveNodeCapabilitySlots(node, meshId).map((s2) => s2.provider).filter(Boolean);
67786
+ const priorities = slotProviders.length ? slotProviders : normalizeProviderPriority2(node?.policy);
67787
+ const providerCandidates = priorities.length ? priorities : [void 0];
67788
+ return providerCandidates.some(
67789
+ (p) => nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, p))
67790
+ );
67791
+ }
67792
+ return true;
67793
+ }) : [];
67794
+ if (!candidateNodes.length) {
67795
+ const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
67796
+ const convergenceOntoWorktree = task.taskMode === "convergence" && Array.isArray(mesh?.nodes) && (() => {
67797
+ const matched = mesh.nodes.filter((n) => !task.targetNodeId || meshNodeIdMatches(n, task.targetNodeId));
67798
+ return matched.length > 0 && matched.every((n) => n?.isLocalWorktree === true);
67799
+ })();
67800
+ const targetTransientlyUnresolved = targetPinUnmatched && isTargetNodeTransientlyUnresolved(mesh, task);
67801
+ markAutoLaunch(meshId, task.id, {
67802
+ status: "skipped",
67803
+ reason: convergenceOntoWorktree ? "mesh_convergence_target_is_worktree" : targetTransientlyUnresolved ? TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON : targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
67804
+ nodeId: task.targetNodeId
67805
+ });
67336
67806
  continue;
67337
67807
  }
67338
- autoLaunchInProgress.add(launchKey);
67339
- try {
67340
- const resolved = await resolveUsableProvider(components, nodeId, node, meshId, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags }, mesh?.policy?.quotaRouting ?? null, quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode), task.id);
67341
- if (!resolved.providerType) {
67342
- markSkip(nodeId, resolved.reason || "provider_unusable");
67808
+ const strategy = resolveSchedulingStrategy(mesh);
67809
+ const orderedCandidateNodes = strategy === "first_eligible" ? candidateNodes : orderEligibleNodes(
67810
+ meshId,
67811
+ strategy,
67812
+ candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
67813
+ // Auto-launch drains one task at a time, so the task IS in scope here —
67814
+ // pass it through for the 'fitness' strategy's task→slot ranking. The
67815
+ // mesh's quotaRouting thresholds ride along so the fitness score can
67816
+ // include the quota-headroom spread bonus (fail-open when unset).
67817
+ { bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags }, quotaRouting: mesh?.policy?.quotaRouting ?? null, quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader) }
67818
+ ).map((c) => c.node);
67819
+ const skippedCandidates = [];
67820
+ const SKIPPED_CANDIDATES_MAX = 5;
67821
+ const markSkip = (nodeIdForSkip, reason, extra) => {
67822
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason, nodeId: nodeIdForSkip, ...extra || {} });
67823
+ if (nodeIdForSkip && skippedCandidates.length < SKIPPED_CANDIDATES_MAX) {
67824
+ skippedCandidates.push({ nodeId: nodeIdForSkip, reason });
67825
+ }
67826
+ };
67827
+ for (const node of orderedCandidateNodes) {
67828
+ const nodeId = readMeshNodeId(node);
67829
+ if (!nodeId) continue;
67830
+ const launchKey = `${meshId}:${nodeId}`;
67831
+ const now = Date.now();
67832
+ const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
67833
+ if (cooldownUntil > 0 && now >= cooldownUntil) autoLaunchCooldownUntil.delete(launchKey);
67834
+ if (autoLaunchInProgress.has(launchKey)) {
67835
+ markSkip(nodeId, "auto_launch_in_progress");
67343
67836
  continue;
67344
67837
  }
67345
- const slotCoversDifficulty = slotCoversTaskDifficulty(resolved.slot, task.difficulty);
67346
- const requestedModel = resolveLaunchAxis(task.model, task.modelSource, resolved.model, slotCoversDifficulty);
67347
- const effectiveThinkingLevel = resolveLaunchAxis(task.thinkingLevel, task.thinkingLevelSource, resolved.thinkingLevel, slotCoversDifficulty);
67348
- const slotDecision = decideSlotForModel({
67349
- requestedModel,
67350
- slots: resolveNodeCapabilitySlots(node, meshId).map((slot) => ({
67351
- slot,
67352
- available: slotHasCapacity(meshId, nodeId, node, slot, mesh?.nodes, isReadonly)
67353
- }))
67354
- });
67355
- if (slotDecision.outcome === "wait") {
67356
- LOG2.info("MeshQueue", `SLOT MODEL GUARD: model '${requestedModel}' is declared on node ${nodeId} but every matching slot is at its maxParallel cap (task ${task.id}); leaving the task queued until a slot goes idle`);
67357
- markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
67838
+ if (now < cooldownUntil) {
67839
+ markSkip(nodeId, "auto_launch_cooldown");
67358
67840
  continue;
67359
67841
  }
67360
- if (slotDecision.outcome === "notify") {
67361
- LOG2.warn("MeshQueue", `SLOT MODEL GUARD: no slot on node ${nodeId} declares model '${requestedModel}' (declared: ${slotDecision.declaredModels.join(", ") || "none"}) for task ${task.id}; not launching \u2014 surfacing to the coordinator to re-drive`);
67362
- markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
67842
+ if (isDirtyNode(node)) {
67843
+ markSkip(nodeId, "dirty_workspace");
67363
67844
  continue;
67364
67845
  }
67365
- const rawEffectiveModel = slotDecision.model;
67366
- const winningModel = resolved.slot?.model?.trim() || void 0;
67367
- const finalizedModel = slotDecision.slot.model?.trim() || void 0;
67368
- const slotWasDemoted = resolved.slot?.provider !== slotDecision.slot.provider || winningModel !== finalizedModel;
67369
- const demotionReason = slotWasDemoted ? slotHasCapacity(meshId, nodeId, node, resolved.slot, mesh?.nodes, isReadonly) ? "slot_reselected_during_launch" : "winning_slot_capacity_exhausted" : void 0;
67370
- const effectiveModel = isModelCompatibleWithProvider(rawEffectiveModel, resolved.providerType) ? rawEffectiveModel : void 0;
67371
- if (rawEffectiveModel && effectiveModel === void 0) {
67372
- LOG2.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`);
67373
- }
67374
- const providerCap = effectiveSlotCap(
67375
- resolveProviderMaxParallel(resolveNodeCapabilitySlots(node, meshId), resolved.providerType),
67376
- isReadonly
67377
- );
67378
- if (providerCap !== void 0 && activeProviderAssignedCount(
67379
- meshId,
67380
- nodeId,
67381
- resolved.providerType,
67382
- resolveDaemonSiblingNodeIds(nodeId, mesh?.nodes)
67383
- ) >= providerCap) {
67384
- markSkip(nodeId, "max_provider_parallel_reached", { providerType: resolved.providerType });
67846
+ if (!isLaunchableNode(node)) {
67847
+ markSkip(nodeId, "node_health_not_launchable");
67385
67848
  continue;
67386
67849
  }
67387
- const launchSettings = {
67388
- // Worker launch envelope: role + mesh context so worker can route completion events.
67389
- role: "worker",
67390
- meshNodeFor: meshId,
67391
- meshNodeId: nodeId,
67392
- spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
67393
- // Coordinator-dispatched worker: auto-approve unless mesh/node policy
67394
- // opts out (default true). Lands in settingsOverride and beats the
67395
- // global per-provider-type boolean/mode through explicit opposite-key clearing.
67396
- ...delegatedWorkerAutoApproveSettingsForNode(
67397
- mesh,
67398
- node,
67399
- components.providerLoader?.getMeta(resolved.providerType),
67400
- resolved.providerType
67401
- ),
67402
- launchedByCoordinator: true,
67403
- autoLaunchedForQueueTaskId: task.id
67404
- };
67405
- if (launchTarget.mode === "remote") {
67406
- const remoteSettings = {
67407
- ...launchSettings,
67408
- meshCoordinatorDaemonId: launchTarget.coordinatorDaemonId,
67409
- meshCoordinatorNodeId: nodeId
67850
+ if (!isMeshNodeFreshEnoughToLaunch(node, freshnessGate)) {
67851
+ markSkip(nodeId, "node_stale_behind_upstream");
67852
+ continue;
67853
+ }
67854
+ const launchTarget = resolveAutoLaunchTarget(components, node);
67855
+ if (launchTarget.mode === "skip") {
67856
+ markSkip(nodeId, launchTarget.reason || "auto_launch_unavailable");
67857
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67858
+ sweepExpiredCooldowns();
67859
+ continue;
67860
+ }
67861
+ if (nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node)) {
67862
+ markSkip(nodeId, "node_has_live_session_pending_claim");
67863
+ continue;
67864
+ }
67865
+ if (!isTaskReadonly(task) && nodeHasActiveAssignment(meshId, nodeId)) {
67866
+ markSkip(nodeId, "node_has_active_assignment");
67867
+ continue;
67868
+ }
67869
+ const maxConcurrentSessions = Number(node?.policy?.maxConcurrentSessions);
67870
+ if (Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0 && liveSessionCountForNode(components, meshId, nodeId) >= maxConcurrentSessions) {
67871
+ markSkip(nodeId, "max_concurrent_sessions_reached");
67872
+ continue;
67873
+ }
67874
+ autoLaunchInProgress.add(launchKey);
67875
+ try {
67876
+ const resolved = await resolveUsableProvider(components, nodeId, node, meshId, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags }, mesh?.policy?.quotaRouting ?? null, quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader), task.id);
67877
+ if (!resolved.providerType) {
67878
+ markSkip(nodeId, resolved.reason || "provider_unusable");
67879
+ continue;
67880
+ }
67881
+ const slotCoversDifficulty = slotCoversTaskDifficulty(resolved.slot, task.difficulty);
67882
+ const requestedModel = resolveLaunchAxis(task.model, task.modelSource, resolved.model, slotCoversDifficulty);
67883
+ const effectiveThinkingLevel = resolveLaunchAxis(task.thinkingLevel, task.thinkingLevelSource, resolved.thinkingLevel, slotCoversDifficulty);
67884
+ const slotDecision = decideSlotForModel({
67885
+ requestedModel,
67886
+ slots: resolveNodeCapabilitySlots(node, meshId).map((slot) => ({
67887
+ slot,
67888
+ available: slotHasCapacity(meshId, nodeId, node, slot, mesh?.nodes, isReadonly)
67889
+ }))
67890
+ });
67891
+ if (slotDecision.outcome === "wait") {
67892
+ LOG2.info("MeshQueue", `SLOT MODEL GUARD: model '${requestedModel}' is declared on node ${nodeId} but every matching slot is at its maxParallel cap (task ${task.id}); leaving the task queued until a slot goes idle`);
67893
+ markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
67894
+ continue;
67895
+ }
67896
+ if (slotDecision.outcome === "notify") {
67897
+ LOG2.warn("MeshQueue", `SLOT MODEL GUARD: no slot on node ${nodeId} declares model '${requestedModel}' (declared: ${slotDecision.declaredModels.join(", ") || "none"}) for task ${task.id}; not launching \u2014 surfacing to the coordinator to re-drive`);
67898
+ markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
67899
+ continue;
67900
+ }
67901
+ const rawEffectiveModel = slotDecision.model;
67902
+ const winningModel = resolved.slot?.model?.trim() || void 0;
67903
+ const finalizedModel = slotDecision.slot.model?.trim() || void 0;
67904
+ const slotWasDemoted = resolved.slot?.provider !== slotDecision.slot.provider || winningModel !== finalizedModel;
67905
+ const demotionReason = slotWasDemoted ? slotHasCapacity(meshId, nodeId, node, resolved.slot, mesh?.nodes, isReadonly) ? "slot_reselected_during_launch" : "winning_slot_capacity_exhausted" : void 0;
67906
+ const effectiveModel = isModelCompatibleWithProvider(rawEffectiveModel, resolved.providerType) ? rawEffectiveModel : void 0;
67907
+ if (rawEffectiveModel && effectiveModel === void 0) {
67908
+ LOG2.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`);
67909
+ }
67910
+ const providerCap = effectiveSlotCap(
67911
+ resolveProviderMaxParallel(resolveNodeCapabilitySlots(node, meshId), resolved.providerType),
67912
+ isReadonly
67913
+ );
67914
+ if (providerCap !== void 0 && activeProviderAssignedCount(
67915
+ meshId,
67916
+ nodeId,
67917
+ resolved.providerType,
67918
+ resolveDaemonSiblingNodeIds(nodeId, mesh?.nodes)
67919
+ ) >= providerCap) {
67920
+ markSkip(nodeId, "max_provider_parallel_reached", { providerType: resolved.providerType });
67921
+ continue;
67922
+ }
67923
+ const launchSettings = {
67924
+ // Worker launch envelope: role + mesh context so worker can route completion events.
67925
+ role: "worker",
67926
+ meshNodeFor: meshId,
67927
+ meshNodeId: nodeId,
67928
+ spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
67929
+ // Coordinator-dispatched worker: auto-approve unless mesh/node policy
67930
+ // opts out (default true). Lands in settingsOverride and beats the
67931
+ // global per-provider-type boolean/mode through explicit opposite-key clearing.
67932
+ ...delegatedWorkerAutoApproveSettingsForNode(
67933
+ mesh,
67934
+ node,
67935
+ components.providerLoader?.getMeta(resolved.providerType),
67936
+ resolved.providerType
67937
+ ),
67938
+ launchedByCoordinator: true,
67939
+ autoLaunchedForQueueTaskId: task.id
67410
67940
  };
67941
+ if (launchTarget.mode === "remote") {
67942
+ const remoteSettings = {
67943
+ ...launchSettings,
67944
+ meshCoordinatorDaemonId: launchTarget.coordinatorDaemonId,
67945
+ meshCoordinatorNodeId: nodeId
67946
+ };
67947
+ markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67948
+ let launchResult2;
67949
+ try {
67950
+ launchResult2 = await components.dispatchMeshCommand(launchTarget.daemonId, "launch_cli", withStatusProbeMarker({
67951
+ cliType: resolved.providerType,
67952
+ dir: node.workspace,
67953
+ settings: remoteSettings,
67954
+ // MAGI-KIND-PANEL model axis: forward the task's model override so the
67955
+ // remote worker session launches with it (initialModel). Best-effort.
67956
+ // Slot-aware: task override wins, else the matched slot's model.
67957
+ ...effectiveModel ? { initialModel: effectiveModel } : {},
67958
+ // BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
67959
+ ...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
67960
+ }));
67961
+ } catch (e) {
67962
+ markAutoLaunch(meshId, task.id, { status: "failed", reason: `remote_launch_dispatch_failed: ${e?.message || String(e)}`, nodeId, providerType: resolved.providerType });
67963
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67964
+ sweepExpiredCooldowns();
67965
+ return false;
67966
+ }
67967
+ const payload = launchResult2 && typeof launchResult2 === "object" && "payload" in launchResult2 && launchResult2.payload && typeof launchResult2.payload === "object" ? launchResult2.payload : launchResult2;
67968
+ if (!payload?.success) {
67969
+ const reason = readNonEmptyString(payload?.error) || "remote_launch_cli_failed";
67970
+ markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
67971
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67972
+ sweepExpiredCooldowns();
67973
+ return false;
67974
+ }
67975
+ const remoteSessionId = readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.id) || readNonEmptyString(payload.runtimeSessionId);
67976
+ markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId: remoteSessionId || void 0, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67977
+ logAutoLaunchQuotaFallbackSuccess(resolved, task.id, nodeId, remoteSessionId || void 0);
67978
+ autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67979
+ sweepExpiredCooldowns();
67980
+ return true;
67981
+ }
67411
67982
  markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67412
- let launchResult2;
67413
- try {
67414
- launchResult2 = await components.dispatchMeshCommand(launchTarget.daemonId, "launch_cli", withStatusProbeMarker({
67415
- cliType: resolved.providerType,
67416
- dir: node.workspace,
67417
- settings: remoteSettings,
67418
- // MAGI-KIND-PANEL model axis: forward the task's model override so the
67419
- // remote worker session launches with it (initialModel). Best-effort.
67420
- // Slot-aware: task override wins, else the matched slot's model.
67421
- ...effectiveModel ? { initialModel: effectiveModel } : {},
67422
- // BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
67423
- ...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
67424
- }));
67425
- } catch (e) {
67426
- markAutoLaunch(meshId, task.id, { status: "failed", reason: `remote_launch_dispatch_failed: ${e?.message || String(e)}`, nodeId, providerType: resolved.providerType });
67983
+ const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
67984
+ cliType: resolved.providerType,
67985
+ dir: node.workspace,
67986
+ settings: launchSettings,
67987
+ // MAGI-KIND-PANEL model axis: local launch forwards the effective model
67988
+ // (task override, else matched slot) as initialModel (CLI → modelLaunchArgs; ACP → setConfigOption).
67989
+ ...effectiveModel ? { initialModel: effectiveModel } : {},
67990
+ // BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
67991
+ ...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
67992
+ });
67993
+ if (!launchResult?.success) {
67994
+ const reason = launchResult?.error || "launch_cli_failed";
67995
+ markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
67427
67996
  autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67428
67997
  sweepExpiredCooldowns();
67429
67998
  return false;
67430
67999
  }
67431
- const payload = launchResult2 && typeof launchResult2 === "object" && "payload" in launchResult2 && launchResult2.payload && typeof launchResult2.payload === "object" ? launchResult2.payload : launchResult2;
67432
- if (!payload?.success) {
67433
- const reason = readNonEmptyString(payload?.error) || "remote_launch_cli_failed";
67434
- markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
68000
+ const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
68001
+ if (!sessionId) {
68002
+ markAutoLaunch(meshId, task.id, { status: "failed", reason: "launch_missing_session_id", nodeId, providerType: resolved.providerType });
67435
68003
  autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67436
68004
  sweepExpiredCooldowns();
67437
68005
  return false;
67438
68006
  }
67439
- const remoteSessionId = readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.id) || readNonEmptyString(payload.runtimeSessionId);
67440
- markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId: remoteSessionId || void 0, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67441
- logAutoLaunchQuotaFallbackSuccess(resolved, task.id, nodeId, remoteSessionId || void 0);
67442
- autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67443
- sweepExpiredCooldowns();
68007
+ markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
68008
+ logAutoLaunchQuotaFallbackSuccess(resolved, task.id, nodeId, sessionId);
68009
+ await waitForLocalSessionReady(components, sessionId);
68010
+ const requiredTags = Array.isArray(task.requiredTags) ? task.requiredTags.filter((t) => !!t) : [];
68011
+ const routingDecision = buildAutoLaunchRoutingDecision({
68012
+ node,
68013
+ meshId,
68014
+ task: { difficulty: task.difficulty, requiredTags: task.requiredTags },
68015
+ resolved,
68016
+ quotaRouting: mesh?.policy?.quotaRouting ?? null,
68017
+ quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader),
68018
+ skippedCandidates,
68019
+ requiredTagsResult: {
68020
+ required: requiredTags,
68021
+ satisfied: !requiredTags.length || nodeSatisfiesRequiredTags(requiredTags, buildMeshNodeCapabilityTags(node, resolved.providerType)),
68022
+ missing: requiredTags.filter((t) => !buildMeshNodeCapabilityTags(node, resolved.providerType).includes(t))
68023
+ },
68024
+ effectiveModel,
68025
+ effectiveThinkingLevel,
68026
+ executedSlot: slotDecision.slot,
68027
+ demotionReason
68028
+ });
68029
+ tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType, routingDecision);
67444
68030
  return true;
67445
- }
67446
- markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67447
- const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
67448
- cliType: resolved.providerType,
67449
- dir: node.workspace,
67450
- settings: launchSettings,
67451
- // MAGI-KIND-PANEL model axis: local launch forwards the effective model
67452
- // (task override, else matched slot) as initialModel (CLI → modelLaunchArgs; ACP → setConfigOption).
67453
- ...effectiveModel ? { initialModel: effectiveModel } : {},
67454
- // BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
67455
- ...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
67456
- });
67457
- if (!launchResult?.success) {
67458
- const reason = launchResult?.error || "launch_cli_failed";
67459
- markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
67460
- autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67461
- sweepExpiredCooldowns();
67462
- return false;
67463
- }
67464
- const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
67465
- if (!sessionId) {
67466
- markAutoLaunch(meshId, task.id, { status: "failed", reason: "launch_missing_session_id", nodeId, providerType: resolved.providerType });
68031
+ } catch (e) {
68032
+ markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
67467
68033
  autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67468
- sweepExpiredCooldowns();
67469
68034
  return false;
68035
+ } finally {
68036
+ autoLaunchInProgress.delete(launchKey);
67470
68037
  }
67471
- markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67472
- logAutoLaunchQuotaFallbackSuccess(resolved, task.id, nodeId, sessionId);
67473
- await waitForLocalSessionReady(components, sessionId);
67474
- const requiredTags = Array.isArray(task.requiredTags) ? task.requiredTags.filter((t) => !!t) : [];
67475
- const routingDecision = buildAutoLaunchRoutingDecision({
67476
- node,
67477
- meshId,
67478
- task: { difficulty: task.difficulty, requiredTags: task.requiredTags },
67479
- resolved,
67480
- quotaRouting: mesh?.policy?.quotaRouting ?? null,
67481
- quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode),
67482
- skippedCandidates,
67483
- requiredTagsResult: {
67484
- required: requiredTags,
67485
- satisfied: !requiredTags.length || nodeSatisfiesRequiredTags(requiredTags, buildMeshNodeCapabilityTags(node, resolved.providerType)),
67486
- missing: requiredTags.filter((t) => !buildMeshNodeCapabilityTags(node, resolved.providerType).includes(t))
67487
- },
67488
- effectiveModel,
67489
- effectiveThinkingLevel,
67490
- executedSlot: slotDecision.slot,
67491
- demotionReason
67492
- });
67493
- tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType, routingDecision);
67494
- return true;
67495
- } catch (e) {
67496
- markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
67497
- autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67498
- return false;
67499
- } finally {
67500
- autoLaunchInProgress.delete(launchKey);
67501
68038
  }
68039
+ } finally {
68040
+ autoLaunchTaskInProgress.delete(taskLaunchKey);
67502
68041
  }
67503
68042
  }
67504
68043
  return false;
@@ -67545,7 +68084,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67545
68084
  if (instMeshId !== meshId) continue;
67546
68085
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
67547
68086
  if (!nodeId) continue;
67548
- if (!isIdleSessionState2(state)) {
68087
+ if (!isIdleSessionState3(state)) {
67549
68088
  const status = readNonEmptyString(state.status).toLowerCase();
67550
68089
  skippedSessions.push({
67551
68090
  nodeId,
@@ -67618,6 +68157,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67618
68157
  }
67619
68158
  }
67620
68159
  autoLaunchStarted = await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
68160
+ sweepAutoLaunchOrphanSessions(components, meshId);
67621
68161
  const afterQueue = getQueue(meshId);
67622
68162
  const pendingAfter = afterQueue.filter((task) => task.status === "pending").length;
67623
68163
  const assignedAfter = afterQueue.filter((task) => task.status === "assigned").length;
@@ -67676,12 +68216,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67676
68216
  var LOCAL_LAUNCH_READY_TIMEOUT_MS;
67677
68217
  var LOCAL_LAUNCH_READY_POLL_MS;
67678
68218
  var autoLaunchInProgress;
68219
+ var autoLaunchTaskInProgress;
67679
68220
  var autoLaunchCooldownUntil;
67680
68221
  var AUTO_LAUNCH_COOLDOWN_MS;
67681
- var AUTO_LAUNCH_AWAIT_CLAIM_MS;
67682
- var AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES;
67683
- var AUTO_LAUNCH_REMOTE_IDLE_TTL_MS;
67684
- var autoLaunchAwaitClaimBackoff;
68222
+ var AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2;
67685
68223
  var init_mesh_queue_assignment = __esm2({
67686
68224
  "src/mesh/mesh-queue-assignment.ts"() {
67687
68225
  "use strict";
@@ -67714,14 +68252,17 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67714
68252
  init_mesh_duplicate_dispatch();
67715
68253
  init_mesh_auto_fast_forward();
67716
68254
  init_mesh_skip_notify();
68255
+ init_mesh_task_parking();
67717
68256
  init_mesh_scheduling_fitness();
67718
68257
  init_mesh_queue_observability();
67719
68258
  init_mesh_routing_decision();
68259
+ init_mesh_autolaunch_integrity();
67720
68260
  init_mesh_difficulty_floor();
67721
68261
  init_mesh_difficulty_floor();
67722
68262
  init_mesh_auto_fast_forward();
67723
68263
  init_mesh_skip_notify();
67724
68264
  init_mesh_scheduling_fitness();
68265
+ init_mesh_autolaunch_integrity();
67725
68266
  BOOTSTRAP_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["complete", "failed"]);
67726
68267
  DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
67727
68268
  DISPATCH_CONNECT_TIMEOUT_MS = MESH_CONNECT_TIMEOUT_MS2;
@@ -67729,12 +68270,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67729
68270
  LOCAL_LAUNCH_READY_TIMEOUT_MS = 15e3;
67730
68271
  LOCAL_LAUNCH_READY_POLL_MS = 100;
67731
68272
  autoLaunchInProgress = /* @__PURE__ */ new Set();
68273
+ autoLaunchTaskInProgress = /* @__PURE__ */ new Set();
67732
68274
  autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
67733
68275
  AUTO_LAUNCH_COOLDOWN_MS = 5e3;
67734
- AUTO_LAUNCH_AWAIT_CLAIM_MS = 9e4;
67735
- AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES = 2;
67736
- AUTO_LAUNCH_REMOTE_IDLE_TTL_MS = 5 * 60 * 1e3;
67737
- autoLaunchAwaitClaimBackoff = /* @__PURE__ */ new Map();
68276
+ AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2 = 2;
67738
68277
  }
67739
68278
  });
67740
68279
  function readSettings(state) {
@@ -70468,6 +71007,11 @@ ${cleanBody}`;
70468
71007
  if (isFalseIdleCompletion(metadataEvent)) return false;
70469
71008
  return !!readWorkerResultMetadata(metadataEvent) || !!readNonEmptyString(metadataEvent.finalSummary);
70470
71009
  }
71010
+ function resolveGraphEnvelopeWorkerResult(metadataEvent) {
71011
+ const explicit = readWorkerResultMetadata(metadataEvent);
71012
+ if (explicit) return explicit;
71013
+ return extractJsonObjectFromSummary(readNonEmptyString(metadataEvent.finalSummary) || void 0);
71014
+ }
70471
71015
  function supersedesTruncatedTerminalSummary(args) {
70472
71016
  if (!args.terminalTaskId || !args.eventTaskId || args.terminalTaskId !== args.eventTaskId) return false;
70473
71017
  if (!isGenuineCompletionEvidence(args.metadataEvent)) return false;
@@ -71105,7 +71649,7 @@ ${cleanBody}`;
71105
71649
  // as the terminal flip (design :317-327 step 2).
71106
71650
  envelope: {
71107
71651
  finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0,
71108
- workerResult: readWorkerResultMetadata(args.metadataEvent),
71652
+ workerResult: resolveGraphEnvelopeWorkerResult(args.metadataEvent),
71109
71653
  nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
71110
71654
  providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0
71111
71655
  }
@@ -71531,7 +72075,7 @@ ${cleanBody}`;
71531
72075
  // daemon's live quota cache, not the git_status-stamped
71532
72076
  // nodeFacts copy (mesh-quota-routing). Remote nodes keep
71533
72077
  // reading their reported nodeFacts.
71534
- quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode)
72078
+ quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader)
71535
72079
  ) : { action: "block" };
71536
72080
  const relaunchProviderType = relaunch.action === "block" ? null : relaunch.providerType;
71537
72081
  if (node) {
@@ -82384,6 +82928,7 @@ ${e?.stderr || ""}`;
82384
82928
  CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES: () => CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES,
82385
82929
  CHAT_MESSAGE_VISIBILITIES: () => CHAT_MESSAGE_VISIBILITIES,
82386
82930
  COMPACT_STATUS_GOAL_PREVIEW_MAX: () => COMPACT_STATUS_GOAL_PREVIEW_MAX,
82931
+ CTRL_C: () => CTRL_C,
82387
82932
  CdpDomHandlers: () => CdpDomHandlers,
82388
82933
  CliProviderInstance: () => CliProviderInstance,
82389
82934
  DAEMON_WS_PATH: () => DAEMON_WS_PATH2,
@@ -82393,6 +82938,7 @@ ${e?.stderr || ""}`;
82393
82938
  DEFAULT_CHAT_TAIL_MISSING_SESSION_POLICY: () => DEFAULT_CHAT_TAIL_MISSING_SESSION_POLICY,
82394
82939
  DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS: () => DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS2,
82395
82940
  DEFAULT_DAEMON_PORT: () => DEFAULT_DAEMON_PORT2,
82941
+ DEFAULT_DELIVERY_MODE: () => DEFAULT_DELIVERY_MODE,
82396
82942
  DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS: () => DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
82397
82943
  DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS2,
82398
82944
  DEFAULT_MESH_POLICY: () => DEFAULT_MESH_POLICY,
@@ -82418,6 +82964,7 @@ ${e?.stderr || ""}`;
82418
82964
  DaemonStatusReporter: () => DaemonStatusReporter,
82419
82965
  DevServer: () => DevServer,
82420
82966
  DuplicateMeshDispatchError: () => DuplicateMeshDispatchError,
82967
+ ESC: () => ESC,
82421
82968
  FsmDriver: () => FsmDriver,
82422
82969
  GOAL_PREVIEW_MAX: () => GOAL_PREVIEW_MAX,
82423
82970
  GitCommandError: () => GitCommandError,
@@ -82437,6 +82984,7 @@ ${e?.stderr || ""}`;
82437
82984
  MAX_SIZE_ROTATION_GENERATIONS: () => MAX_SIZE_ROTATION_GENERATIONS,
82438
82985
  MESH_CONVERGE_FAST_FORWARD_TAG: () => MESH_CONVERGE_FAST_FORWARD_TAG,
82439
82986
  MESH_CONVERGE_REFINE_TAG: () => MESH_CONVERGE_REFINE_TAG,
82987
+ MESH_DECLARED_ELIGIBLE_SINGLE_HINT: () => MESH_DECLARED_ELIGIBLE_SINGLE_HINT,
82440
82988
  MESH_GATE_DEFAULT_LEASE_SECONDS: () => MESH_GATE_DEFAULT_LEASE_SECONDS,
82441
82989
  MESH_GATE_NAMED_OUTCOMES: () => MESH_GATE_NAMED_OUTCOMES,
82442
82990
  MESH_GATE_RELEASE_PATCH_KEYS: () => MESH_GATE_RELEASE_PATCH_KEYS,
@@ -82471,6 +83019,10 @@ ${e?.stderr || ""}`;
82471
83019
  OPERATING_NOTE_KIND: () => OPERATING_NOTE_KIND,
82472
83020
  OPERATING_NOTE_TOMBSTONE_KIND: () => OPERATING_NOTE_TOMBSTONE_KIND,
82473
83021
  P2pRelayFailureError: () => P2pRelayFailureError,
83022
+ PARKED_SKIP_REASON: () => PARKED_SKIP_REASON,
83023
+ PARKED_TASK_RETENTION_MS: () => PARKED_TASK_RETENTION_MS,
83024
+ PARK_REASON_PIN_EXPIRED: () => PARK_REASON_PIN_EXPIRED,
83025
+ PARK_RETENTION_EXPIRED_REASON: () => PARK_RETENTION_EXPIRED_REASON,
82474
83026
  PROVIDER_CHANNEL_ENV_VAR: () => PROVIDER_CHANNEL_ENV_VAR,
82475
83027
  PRUNABLE_ORPHAN_STALE_REASONS: () => PRUNABLE_ORPHAN_STALE_REASONS,
82476
83028
  ProviderChannelError: () => ProviderChannelError,
@@ -82484,6 +83036,7 @@ ${e?.stderr || ""}`;
82484
83036
  STALE_MAGI_WINDOW_MS: () => STALE_MAGI_WINDOW_MS,
82485
83037
  STALE_TERMINAL_REFINE_WINDOW_MS: () => STALE_TERMINAL_REFINE_WINDOW_MS,
82486
83038
  STANDALONE_CDP_SCAN_INTERVAL_MS: () => STANDALONE_CDP_SCAN_INTERVAL_MS2,
83039
+ STOP_CONTROL_ID: () => STOP_CONTROL_ID,
82487
83040
  SYM: () => SYM,
82488
83041
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory2,
82489
83042
  StatuslineInstallError: () => StatuslineInstallError2,
@@ -82534,6 +83087,7 @@ ${e?.stderr || ""}`;
82534
83087
  buildMissionPromptSection: () => buildMissionPromptSection,
82535
83088
  buildOrphanedPinNotice: () => buildOrphanedPinNotice,
82536
83089
  buildP2pRelayFailurePayload: () => buildP2pRelayFailurePayload,
83090
+ buildParkedTaskNotice: () => buildParkedTaskNotice,
82537
83091
  buildPinnedGlobalInstallCommand: () => buildPinnedGlobalInstallCommand,
82538
83092
  buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
82539
83093
  buildSessionEntries: () => buildSessionEntries,
@@ -82613,6 +83167,7 @@ ${e?.stderr || ""}`;
82613
83167
  evaluateProviderQuotaGate: () => evaluateProviderQuotaGate,
82614
83168
  execNpmCommandSync: () => execNpmCommandSync,
82615
83169
  expandDaemonIdForms: () => expandDaemonIdForms,
83170
+ failRetentionExpiredParkedTask: () => failRetentionExpiredParkedTask,
82616
83171
  fastForwardMeshNode: () => fastForwardMeshNode,
82617
83172
  fetchAntigravityQuota: () => fetchAntigravityQuota2,
82618
83173
  fetchClaudeQuota: () => fetchClaudeQuota2,
@@ -82665,6 +83220,7 @@ ${e?.stderr || ""}`;
82665
83220
  getMeshStatusMissionSummaries: () => getMeshStatusMissionSummaries,
82666
83221
  getMeshStatusMissionsCompact: () => getMeshStatusMissionsCompact,
82667
83222
  getNpmExecOptions: () => getNpmExecOptions,
83223
+ getParkedTasks: () => getParkedTasks,
82668
83224
  getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
82669
83225
  getProcessInstanceContext: () => getProcessInstanceContext,
82670
83226
  getQueue: () => getQueue,
@@ -82764,6 +83320,7 @@ ${e?.stderr || ""}`;
82764
83320
  normalizeChatMessageKind: () => normalizeChatMessageKind,
82765
83321
  normalizeChatMessages: () => normalizeChatMessages,
82766
83322
  normalizeChatTailActiveModal: () => normalizeChatTailActiveModal,
83323
+ normalizeDeliveryMode: () => normalizeDeliveryMode,
82767
83324
  normalizeGitOutput: () => normalizeGitOutput,
82768
83325
  normalizeGitWorkspaceSubscriptionParams: () => normalizeGitWorkspaceSubscriptionParams2,
82769
83326
  normalizeInputEnvelope: () => normalizeInputEnvelope,
@@ -82784,7 +83341,11 @@ ${e?.stderr || ""}`;
82784
83341
  normalizeRepoMeshDeclarativeConfig: () => normalizeRepoMeshDeclarativeConfig,
82785
83342
  normalizeSessionModalFields: () => normalizeSessionModalFields,
82786
83343
  notifyCoordinatorOfOrphanedPins: () => notifyCoordinatorOfOrphanedPins,
83344
+ notifyCoordinatorOfParkedTaskDropped: () => notifyCoordinatorOfParkedTaskDropped,
82787
83345
  otherTrackConfigDir: () => otherTrackConfigDir,
83346
+ parkTaskTargetPin: () => parkTaskTargetPin,
83347
+ parkedAgeMs: () => parkedAgeMs,
83348
+ parkedTaskRetentionExpired: () => parkedTaskRetentionExpired,
82788
83349
  parseOnDependencyFailurePolicy: () => parseOnDependencyFailurePolicy,
82789
83350
  parsePorcelainV2Status: () => parsePorcelainV2Status,
82790
83351
  parseProviderSourceConfigUpdate: () => parseProviderSourceConfigUpdate,
@@ -82835,6 +83396,7 @@ ${e?.stderr || ""}`;
82835
83396
  recordMeshToolCall: () => recordMeshToolCall,
82836
83397
  recordMissingSessionAttempt: () => recordMissingSessionAttempt2,
82837
83398
  recordSessionUsage: () => recordSessionUsage,
83399
+ recordSingleEnqueueDecision: () => recordSingleEnqueueDecision,
82838
83400
  recoverExpiredWorkspaceSagas: () => recoverExpiredWorkspaceSagas,
82839
83401
  registerExtensionProviders: () => registerExtensionProviders,
82840
83402
  registerMeshCoordinator: () => registerMeshCoordinator,
@@ -82867,6 +83429,7 @@ ${e?.stderr || ""}`;
82867
83429
  resolveGitRepository: () => resolveGitRepository,
82868
83430
  resolveInstanceContext: () => resolveInstanceContext,
82869
83431
  resolveInstanceDir: () => resolveInstanceDir,
83432
+ resolveInterruptCapability: () => resolveInterruptCapability,
82870
83433
  resolveMagiSessionCleanupMode: () => resolveMagiSessionCleanupMode,
82871
83434
  resolveMaxParallelTasks: () => resolveMaxParallelTasks,
82872
83435
  resolveMeshHostStatus: () => resolveMeshHostStatus,
@@ -82924,6 +83487,7 @@ ${e?.stderr || ""}`;
82924
83487
  supportsUnicodeSymbols: () => supportsUnicodeSymbols,
82925
83488
  sweepMeshGraphGateTimeouts: () => sweepMeshGraphGateTimeouts,
82926
83489
  taskDependenciesSatisfied: () => taskDependenciesSatisfied,
83490
+ taskIsParked: () => taskIsParked,
82927
83491
  tombstoneOperatingNote: () => tombstoneOperatingNote,
82928
83492
  totalTokens: () => totalTokens,
82929
83493
  triggerMeshQueue: () => triggerMeshQueue,
@@ -85421,6 +85985,8 @@ ${e?.stderr || ""}`;
85421
85985
  };
85422
85986
  }
85423
85987
  init_mesh_work_queue();
85988
+ init_mesh_work_queue();
85989
+ init_mesh_task_parking();
85424
85990
  init_mesh_graph_derived_failure();
85425
85991
  init_mesh_graph_workspace_saga();
85426
85992
  init_mesh_graph_workspace_safety();
@@ -86020,6 +86586,12 @@ ${e?.stderr || ""}`;
86020
86586
  workspaceRef: ws.workspaceRef,
86021
86587
  detail: `Workspace '${ws.workspaceRef}' needs manual compensation: ${ws.lastError ?? "compensation refused"}.`
86022
86588
  });
86589
+ } else if (ws.sagaState === "declared" && !ws.baseRevision) {
86590
+ actions.push({
86591
+ kind: "workspace_declared_no_base",
86592
+ workspaceRef: ws.workspaceRef,
86593
+ detail: `Workspace '${ws.workspaceRef}' is stuck in 'declared': no base_revision was given and none could be derived from ${ws.sourceNodeId ? `source node '${ws.sourceNodeId}' (its repo is unreachable on this daemon, or git could not resolve its HEAD)` : "a source node, because the declaration named none"}. The workspace will never clone and every task bound to it stays blocked. Re-declare the workspace with an explicit base_revision (e.g. 'main')${ws.sourceNodeId ? "" : " and a source_node_id"}.`
86594
+ });
86023
86595
  }
86024
86596
  }
86025
86597
  return {
@@ -86186,6 +86758,47 @@ The pin is NOT cleared automatically: a pin often encodes required context conti
86186
86758
  init_contracts();
86187
86759
  init_mesh_events_utils();
86188
86760
  init_mesh_delivery_policy();
86761
+ var CTRL_C = "";
86762
+ var ESC = "\x1B";
86763
+ var STOP_CONTROL_ID = "stop";
86764
+ var LIVE_VERIFIED_PROVIDERS = /* @__PURE__ */ new Set(["claude-cli"]);
86765
+ function nameForKeys(keys) {
86766
+ if (keys === CTRL_C) return "Ctrl-C";
86767
+ if (keys === ESC) return "ESC";
86768
+ return "custom";
86769
+ }
86770
+ function resolveInterruptCapability(providerType, controlBar) {
86771
+ const stop = (controlBar ?? []).find((c) => c.id === STOP_CONTROL_ID);
86772
+ if (!stop) {
86773
+ return {
86774
+ supported: false,
86775
+ reason: "no_stop_control",
86776
+ message: `Provider '${providerType}' declares no '${STOP_CONTROL_ID}' control, so a running turn cannot be interrupted. Dispatch with delivery mode when_idle and the task will be delivered once the session finishes on its own.`
86777
+ };
86778
+ }
86779
+ if (stop.action?.type !== "send_keys") {
86780
+ return {
86781
+ supported: false,
86782
+ reason: "stop_control_not_send_keys",
86783
+ message: `Provider '${providerType}' declares a '${STOP_CONTROL_ID}' control of type '${stop.action?.type}', not a key write, so it cannot be used to abort a turn. Dispatch with delivery mode when_idle instead.`
86784
+ };
86785
+ }
86786
+ const keys = stop.action.keys;
86787
+ if (typeof keys !== "string" || keys.length === 0) {
86788
+ return {
86789
+ supported: false,
86790
+ reason: "stop_keys_empty",
86791
+ message: `Provider '${providerType}' declares a '${STOP_CONTROL_ID}' control with an EMPTY key sequence, so pressing it would write nothing to the terminal while still reporting success. Treating this as unsupported rather than silently doing nothing. Dispatch with delivery mode when_idle, or fix the provider spec to declare a real stop key.`
86792
+ };
86793
+ }
86794
+ return {
86795
+ supported: true,
86796
+ keys,
86797
+ keyName: nameForKeys(keys),
86798
+ confidence: LIVE_VERIFIED_PROVIDERS.has(providerType) ? "proven" : "declared",
86799
+ ...stop.visible_when_state ? { visibleWhenState: stop.visible_when_state } : {}
86800
+ };
86801
+ }
86189
86802
  var NO_FALLBACK_REASON = "This mesh operation needs a live peer-to-peer connection to the node, which is not open right now. This is often transient \u2014 the peer may be connecting, slow via TURN relay, or briefly offline. Wait a moment and retry, or re-establish the node's connection. Mesh commands use P2P only by design \u2014 there is no cloud/WS relay fallback.";
86190
86803
  var P2P_NEXT_ACTION = "The peer connection is recoverable. Wait a moment for the connection to establish (especially over TURN relay), then retry. If the node remains unreachable, check that the target daemon is running and online, then re-establish its connection.";
86191
86804
  var NON_P2P_NEXT_ACTION = "Inspect the provider/command error and fix the underlying logic or configuration before retrying.";
@@ -101993,6 +102606,52 @@ ${marker}`,
101993
102606
  hash: hash2
101994
102607
  };
101995
102608
  }
102609
+ /**
102610
+ * Report whether this provider can have its in-flight turn interrupted,
102611
+ * resolved from the spec THIS session actually loaded (never a hardcoded
102612
+ * per-provider table — the stop key varies by spec version; hermes-cli
102613
+ * ships Ctrl-C in specs/0.14.json and an EMPTY key in specs/4.0.json).
102614
+ */
102615
+ getInterruptCapability() {
102616
+ return resolveInterruptCapability(this.cliType, this.spec.control_bar);
102617
+ }
102618
+ /**
102619
+ * Abort the turn currently in flight by writing the provider's own stop
102620
+ * key to the PTY. Used by delivery mode 'interrupt': the caller then waits
102621
+ * for the FSM to report idle and lets the ordinary queued-send drain
102622
+ * deliver the new prompt as a genuine new turn.
102623
+ *
102624
+ * ★ Deliberately NOT routed through invokeScript('stop'). That path calls
102625
+ * FsmDriver.handleClickControl, which silently returns when the control's
102626
+ * `visible_when_state` does not include the current state, and calls
102627
+ * send_keys("") for a provider whose stop key is empty — while
102628
+ * invokeScript unconditionally returns `{ ok: true, effects:[sent_keys] }`
102629
+ * either way. Reporting a successful interrupt that wrote nothing is
102630
+ * exactly the failure this feature exists to remove, so capability is
102631
+ * validated HERE, before any write, and the outcome is reported honestly.
102632
+ */
102633
+ async interruptTurn() {
102634
+ if (!this.spawned || this.exited) {
102635
+ return { ok: false, reason: "not_running", message: `${this.cliName} is not running.` };
102636
+ }
102637
+ const cap = this.getInterruptCapability();
102638
+ if (!cap.supported) {
102639
+ LOG2.warn("SpecAdapter", `[${this.cliType}] interrupt refused: ${cap.reason}`);
102640
+ return { ok: false, reason: cap.reason, message: cap.message };
102641
+ }
102642
+ const status = this.latestState?.status;
102643
+ if (status !== "generating") {
102644
+ return {
102645
+ ok: false,
102646
+ reason: "not_busy",
102647
+ message: `Session is '${status ?? "unknown"}', not generating \u2014 nothing to interrupt.`
102648
+ };
102649
+ }
102650
+ this.driver.dispatch({ kind: "pty_write", data: cap.keys });
102651
+ const bytes = Buffer.byteLength(cap.keys, "utf8");
102652
+ LOG2.info("SpecAdapter", `[${this.cliType}] turn interrupted via ${cap.keyName} (bytes=${bytes}, confidence=${cap.confidence})`);
102653
+ return { ok: true, keyName: cap.keyName, bytes, confidence: cap.confidence };
102654
+ }
101996
102655
  /**
101997
102656
  * MESH-SEND-KEYS (feature 3: key injection). Inject a STRUCTURED key
101998
102657
  * sequence into the spec-driven PTY for mesh_send_keys. Mirrors
@@ -110608,6 +111267,39 @@ Run 'adhdev doctor' for detailed diagnostics.`
110608
111267
  } else if (action === "stop") {
110609
111268
  await this.stopSession(key2);
110610
111269
  return { success: true, stopped: true };
111270
+ } else if (action === "interrupt_capability") {
111271
+ const probe = adapter;
111272
+ if (typeof probe.getInterruptCapability !== "function") {
111273
+ return {
111274
+ success: true,
111275
+ supported: false,
111276
+ reason: "interrupt_not_implemented",
111277
+ message: `Provider '${agentType}' runs on an adapter with no interrupt support.`
111278
+ };
111279
+ }
111280
+ const cap = probe.getInterruptCapability();
111281
+ return { success: true, ...cap };
111282
+ } else if (action === "interrupt_turn") {
111283
+ const interruptible = adapter;
111284
+ if (typeof interruptible.interruptTurn !== "function") {
111285
+ return {
111286
+ success: false,
111287
+ interrupted: false,
111288
+ reason: "interrupt_not_implemented",
111289
+ error: `Provider '${agentType}' runs on an adapter that cannot interrupt a turn.`
111290
+ };
111291
+ }
111292
+ const outcome = await interruptible.interruptTurn();
111293
+ if (!outcome.ok) {
111294
+ return { success: false, interrupted: false, reason: outcome.reason, error: outcome.message };
111295
+ }
111296
+ return {
111297
+ success: true,
111298
+ interrupted: true,
111299
+ keyName: outcome.keyName,
111300
+ bytes: outcome.bytes,
111301
+ confidence: outcome.confidence
111302
+ };
110611
111303
  }
110612
111304
  throw new Error(`Unknown action: ${action}`);
110613
111305
  }
@@ -110770,7 +111462,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
110770
111462
  const { shouldDeferDispatchForBootstrap: shouldDeferDispatchForBootstrap2 } = await Promise.resolve().then(() => (init_worktree_bootstrap_config(), worktree_bootstrap_config_exports));
110771
111463
  if (shouldDeferDispatchForBootstrap2(nodeObj)) {
110772
111464
  const dispatchSessionState = dispatchSessionId ? ctx.deps.instanceManager?.getInstance?.(dispatchSessionId)?.getState?.() : void 0;
110773
- const sessionConfirmedReady = !!dispatchSessionState && isIdleSessionState2(dispatchSessionState);
111465
+ const sessionConfirmedReady = !!dispatchSessionState && isIdleSessionState3(dispatchSessionState);
110774
111466
  if (!sessionConfirmedReady) {
110775
111467
  return {
110776
111468
  success: false,
@@ -118391,7 +119083,12 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
118391
119083
  // CANON-IDENTITY: an in-flight (actively-generating) task is refused by
118392
119084
  // default to avoid a duplicate second dispatch; an explicit operator
118393
119085
  // force overrides that guard (and the retry cap).
118394
- force: args?.force === true
119086
+ force: args?.force === true,
119087
+ // PIN-PARKING (edit): optional instruction rewrite, so a coordinator in
119088
+ // IPC/cloud mode has the same exit from parking as the in-process path.
119089
+ // Without this the field would be silently dropped exactly where the
119090
+ // multi-coordinator topology needs it most.
119091
+ message: typeof args?.message === "string" ? args.message : void 0
118395
119092
  });
118396
119093
  if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
118397
119094
  if (task.status === "assigned" && args?.force !== true) {