@adhdev/daemon-standalone 1.0.56-rc.6 → 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 ? "d143fc7369d10b184b4cad79ce3a4240bc811faf" : void 0) ?? "unknown";
36624
- const commitShort = readInjected(true ? "d143fc73" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36625
- const version2 = readInjected(true ? "1.0.56-rc.6" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36626
- const builtAt = readInjected(true ? "2026-08-19T09:46:30.077Z" : 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,
@@ -59520,6 +59744,7 @@ ${rendered}`, "utf-8");
59520
59744
  void opts.registerNode;
59521
59745
  return {
59522
59746
  nowMs: () => Date.now(),
59747
+ resolveBaseRevision: (req) => defaultResolveBaseRevision(req),
59523
59748
  createWorktree: (req) => defaultCreateWorktree(req),
59524
59749
  findOwnedWorktree: (req) => defaultFindOwnedWorktree(req),
59525
59750
  inspectWorktree: (req) => defaultInspectWorktree(req),
@@ -59565,6 +59790,40 @@ ${rendered}`, "utf-8");
59565
59790
  env: gitChildEnv()
59566
59791
  });
59567
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
+ }
59568
59827
  async function defaultCreateWorktree(req) {
59569
59828
  const existing = await defaultFindOwnedWorktree({
59570
59829
  meshId: req.meshId,
@@ -59910,8 +60169,26 @@ ${rendered}`, "utf-8");
59910
60169
  }
59911
60170
  return compensateClaimedIntent({ ...intent, ownerTag }, ports, generation);
59912
60171
  }
59913
- if (intent.sagaState === "declared" && !intent.baseRevision) {
59914
- 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
+ }
59915
60192
  }
59916
60193
  store.transaction(() => {
59917
60194
  store.graphStore().patchWorkspaceIntent(graphId, workspaceRef, {
@@ -59919,7 +60196,7 @@ ${rendered}`, "utf-8");
59919
60196
  ownerTag
59920
60197
  }, nowIso, { leaseGeneration: generation });
59921
60198
  });
59922
- return prepareClaimedIntent({ ...intent, sagaState: "preparing", ownerTag }, ports, generation, faults);
60199
+ return prepareClaimedIntent({ ...intent, baseRevision, sagaState: "preparing", ownerTag }, ports, generation, faults);
59923
60200
  }
59924
60201
  async function prepareClaimedIntent(intent, ports, generation, faults) {
59925
60202
  const store = MeshRuntimeStore.getInstance();
@@ -65073,36 +65350,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65073
65350
  init_chat_history();
65074
65351
  }
65075
65352
  });
65076
- function s(v) {
65077
- return typeof v === "string" && v.trim() ? v.trim() : "";
65078
- }
65079
- function meshEventTraceKey(ctx) {
65080
- const segs = [`task=${s(ctx.taskId) || "-"}`];
65081
- const eventId = s(ctx.eventId);
65082
- if (eventId) segs.push(`evt=${eventId}`);
65083
- segs.push(`sess=${s(ctx.sessionId) || "-"}`);
65084
- const nodeId = s(ctx.nodeId);
65085
- if (nodeId) segs.push(`node=${nodeId}`);
65086
- const meshId = s(ctx.meshId);
65087
- if (meshId) segs.push(`mesh=${meshId}`);
65088
- const event = s(ctx.event);
65089
- if (event) segs.push(`event=${event}`);
65090
- return segs.join(" ");
65091
- }
65092
- function traceMeshEventStage(stage, ctx, detail) {
65093
- LOG2.info(CAT, `[stage:${stage}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
65094
- }
65095
- function traceMeshEventDrop(reason, ctx, detail) {
65096
- LOG2.warn(CAT, `[drop:${reason}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
65097
- }
65098
- var CAT;
65099
- var init_mesh_event_trace = __esm2({
65100
- "src/mesh/mesh-event-trace.ts"() {
65101
- "use strict";
65102
- init_logger();
65103
- CAT = "EvtTrace";
65104
- }
65105
- });
65106
65353
  function isAnthropicProvider(providerType) {
65107
65354
  const p = typeof providerType === "string" ? providerType.trim().toLowerCase() : "";
65108
65355
  return p.length > 0 && ANTHROPIC_PROVIDER_TYPES.has(p);
@@ -65577,6 +65824,43 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65577
65824
  const anchorMs = Date.parse(task.requeuedAt || task.createdAt || "");
65578
65825
  return Number.isFinite(anchorMs) ? nowMs - anchorMs : null;
65579
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
+ }
65580
65864
  function resolveDeadTargetVerdict(components, meshId, mesh, task) {
65581
65865
  const NOT_DEAD = { dead: false, nodeDead: false, reason: "" };
65582
65866
  const targetSessionId = readNonEmptyString(task.targetSessionId);
@@ -65652,6 +65936,21 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65652
65936
  summary: "the node's workspace is dirty, so auto-launch is blocked to avoid clobbering uncommitted changes",
65653
65937
  nextAction: "Clean or commit the node's working tree (or fast-forward it); the task will then auto-assign."
65654
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
+ }
65655
65954
  if (reason === "target_session_pin_expired") {
65656
65955
  if (evidence === "consumed") return {
65657
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",
@@ -65698,11 +65997,11 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65698
65997
  const targetCoordinatorDaemonId = readNonEmptyString(loadConfig2().machineId);
65699
65998
  const targetCoordinatorSessionId = readNonEmptyString(task?.sourceCoordinatorSessionId);
65700
65999
  const nodeLabel = readNonEmptyString(nodeId) || readNonEmptyString(task?.targetNodeId);
65701
- 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;
65702
66001
  const { summary, nextAction } = actionableSkipGuidance(reason, evidence);
65703
66002
  const providerAvailabilityResult = reason.startsWith("provider") || reason === "missing_provider_priority";
65704
66003
  const reachabilityResult = reason.startsWith("remote_auto_launch");
65705
- 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.";
65706
66005
  const coordinatorMessage = `[System] A queued mesh task${nodeLabel ? ` for node ${nodeLabel}` : ""} is not being dispatched because ${summary}. ${nextAction} ${closing}`;
65707
66006
  try {
65708
66007
  queuePendingMeshCoordinatorEvent({
@@ -65731,6 +66030,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65731
66030
  var lastActionableSkipNotified;
65732
66031
  var DEAD_TARGET_GRACE_MS;
65733
66032
  var TARGET_SESSION_PIN_TTL_MS;
66033
+ var targetPinGeneratingCreditMs;
65734
66034
  var init_mesh_skip_notify = __esm2({
65735
66035
  "src/mesh/mesh-skip-notify.ts"() {
65736
66036
  "use strict";
@@ -65746,6 +66046,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65746
66046
  init_slot_model_enforcement();
65747
66047
  init_mesh_queue_assignment();
65748
66048
  init_mesh_queue_observability();
66049
+ init_mesh_task_parking();
65749
66050
  ACTIONABLE_SKIP_REASON_PREFIXES = [
65750
66051
  "target_node_id_unmatched",
65751
66052
  "no_node_satisfies_required_tags",
@@ -65768,6 +66069,14 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65768
66069
  // premise the delta was meant to correct. The coordinator must know its
65769
66070
  // addressed message lost its address.
65770
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,
65771
66080
  // SLOT MODEL GUARD (absent): no slot on the node declares the task's model.
65772
66081
  // Permanent — no amount of waiting produces a slot, so the coordinator must
65773
66082
  // re-drive (adjust difficulty, target another node, ask the owner). Its
@@ -65786,6 +66095,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
65786
66095
  lastActionableSkipNotified = /* @__PURE__ */ new Map();
65787
66096
  DEAD_TARGET_GRACE_MS = 6e4;
65788
66097
  TARGET_SESSION_PIN_TTL_MS = 15 * 6e4;
66098
+ targetPinGeneratingCreditMs = /* @__PURE__ */ new Map();
65789
66099
  }
65790
66100
  });
65791
66101
  function nodesForMesh(meshId) {
@@ -66194,6 +66504,173 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66194
66504
  INTRA_NODE_LOSERS_MAX = 2;
66195
66505
  }
66196
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
+ });
66197
66674
  function classifiedDifficultiesForSlot(slot) {
66198
66675
  const highest = slot.difficulty?.reduce((rank, value) => {
66199
66676
  const candidate = CLASSIFIED_DIFFICULTIES.indexOf(value);
@@ -66298,7 +66775,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66298
66775
  CLASSIFIED_DIFFICULTIES = ["easy", "medium", "difficult"];
66299
66776
  }
66300
66777
  });
66301
- function localCoordinatorDaemonId() {
66778
+ function localCoordinatorDaemonId2() {
66302
66779
  return canonicalDaemonId(readNonEmptyString(loadConfig2().machineId));
66303
66780
  }
66304
66781
  function loadRepoConfigForNode(node) {
@@ -66407,7 +66884,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66407
66884
  const routingDecision = {
66408
66885
  source: routing?.source ?? "queue",
66409
66886
  selectedNodeId: ctx.nodeId,
66410
- ...localCoordinatorDaemonId() ? { daemonId: localCoordinatorDaemonId() } : {},
66887
+ ...localCoordinatorDaemonId2() ? { daemonId: localCoordinatorDaemonId2() } : {},
66411
66888
  transport: ctx.transport,
66412
66889
  // D: resolved execution profile — prefer the caller's resolved values, fall back
66413
66890
  // to what the claimed task row carries (queue/idle drains carry it on the task).
@@ -66727,7 +67204,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66727
67204
  nodeId,
66728
67205
  sessionId,
66729
67206
  providerType,
66730
- coordinatorDaemonId: localCoordinatorDaemonId(),
67207
+ coordinatorDaemonId: localCoordinatorDaemonId2(),
66731
67208
  coordinatorSessionId: readNonEmptyString(task.sourceCoordinatorSessionId) || void 0
66732
67209
  });
66733
67210
  dispatchAttemptId = attempt.attemptId;
@@ -66771,7 +67248,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66771
67248
  if (remoteDaemonId && components.dispatchMeshCommand) {
66772
67249
  const isLocalNode = isLocalAutoLaunchNode(node) || components.cliManager.adapters.has(sessionId);
66773
67250
  if (!isLocalNode) {
66774
- const localDaemonIdForDispatch = localCoordinatorDaemonId();
67251
+ const localDaemonIdForDispatch = localCoordinatorDaemonId2();
66775
67252
  const sourceCoordinatorSessionId = readNonEmptyString(task.sourceCoordinatorSessionId) || void 0;
66776
67253
  const dispatchMeshCommand = components.dispatchMeshCommand;
66777
67254
  deliverTaskToSession(
@@ -66821,7 +67298,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66821
67298
  try {
66822
67299
  const inst = components.instanceManager.getInstance(sessionId);
66823
67300
  if (inst && typeof inst.updateSettings === "function") {
66824
- const localDaemonId = localCoordinatorDaemonId();
67301
+ const localDaemonId = localCoordinatorDaemonId2();
66825
67302
  const localSourceCoordinatorSessionId = readNonEmptyString(task.sourceCoordinatorSessionId);
66826
67303
  inst.updateSettings({
66827
67304
  meshNodeFor: meshId,
@@ -66865,7 +67342,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66865
67342
  ...typeof task.dispatchNonce === "number" ? { dispatchNonce: task.dispatchNonce } : {},
66866
67343
  // TURN-LEDGER (Stage 5): the opaque attempt identity (see remote branch above).
66867
67344
  ...dispatchAttemptId ? { attemptId: dispatchAttemptId } : {},
66868
- ...localCoordinatorDaemonId() ? { coordinatorDaemonId: localCoordinatorDaemonId() } : {},
67345
+ ...localCoordinatorDaemonId2() ? { coordinatorDaemonId: localCoordinatorDaemonId2() } : {},
66869
67346
  ...readNonEmptyString(task.sourceCoordinatorSessionId) ? { coordinatorSessionId: readNonEmptyString(task.sourceCoordinatorSessionId) } : {},
66870
67347
  ...silentIdlePushOnDispatch ? { silentIdlePush: true } : {}
66871
67348
  }
@@ -66878,38 +67355,12 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66878
67355
  task,
66879
67356
  transport: "local",
66880
67357
  ...readNonEmptyString(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString(task.sourceCoordinatorSessionId) } : {},
66881
- ...localCoordinatorDaemonId() ? { sourceCoordinatorDaemonId: localCoordinatorDaemonId() } : {},
67358
+ ...localCoordinatorDaemonId2() ? { sourceCoordinatorDaemonId: localCoordinatorDaemonId2() } : {},
66882
67359
  ...routingDecision ? { routingDecision } : {}
66883
67360
  }
66884
67361
  );
66885
67362
  return true;
66886
67363
  }
66887
- function awaitClaimWindowMs(cycles) {
66888
- return AUTO_LAUNCH_AWAIT_CLAIM_MS * Math.pow(2, Math.min(cycles, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES));
66889
- }
66890
- function remoteSessionAppearsLive(meshId, sessionId) {
66891
- if (!sessionId) return false;
66892
- try {
66893
- return MeshRuntimeStore.getInstance().getRemoteIdleSessions(meshId).some((s2) => sessionIdsEquivalent(s2.sessionId, sessionId));
66894
- } catch {
66895
- return false;
66896
- }
66897
- }
66898
- function inWindowAutoLaunchSessionIdsForNode(meshId, nodeId) {
66899
- const nowMs = Date.now();
66900
- const out = [];
66901
- for (const task of getQueue(meshId, { status: ["pending"] })) {
66902
- const al = task.autoLaunch;
66903
- const sid = al ? readNonEmptyString(al.sessionId) : "";
66904
- if (!al || al.status !== "started" && al.status !== "completed" || !sid) continue;
66905
- if (!daemonIdsEquivalent(al.nodeId, nodeId)) continue;
66906
- const launchedAtMs = Date.parse(al.updatedAt);
66907
- const inBaseWindow = Number.isFinite(launchedAtMs) && nowMs - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS;
66908
- const inBackoff = autoLaunchAwaitClaimBackoff.has(`${meshId}::${task.id}`);
66909
- if (inBaseWindow || inBackoff) out.push(sid);
66910
- }
66911
- return out;
66912
- }
66913
67364
  function sweepExpiredCooldowns() {
66914
67365
  const now = Date.now();
66915
67366
  for (const [key2, until] of autoLaunchCooldownUntil) {
@@ -66929,7 +67380,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66929
67380
  function isTerminalSessionStatus(status) {
66930
67381
  return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
66931
67382
  }
66932
- function isIdleSessionState2(state) {
67383
+ function isIdleSessionState3(state) {
66933
67384
  const status = readNonEmptyString(state?.status).toLowerCase();
66934
67385
  if (isTerminalSessionStatus(status)) return false;
66935
67386
  return status === "idle" || state?.activeChat?.status === "waiting_input";
@@ -66949,7 +67400,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66949
67400
  const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
66950
67401
  if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
66951
67402
  const sessionId = readNonEmptyString(state.instanceId);
66952
- if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState2(state)) return false;
67403
+ if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState3(state)) return false;
66953
67404
  return sessionStateLooksActive(state);
66954
67405
  });
66955
67406
  }
@@ -66980,7 +67431,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66980
67431
  const daemonId = readMeshNodeDaemonId(node ?? {});
66981
67432
  if (!daemonId) return { mode: "skip", reason: "remote_auto_launch_unsupported" };
66982
67433
  if (!components.dispatchMeshCommand) return { mode: "skip", reason: "remote_auto_launch_unsupported" };
66983
- const coordinatorDaemonId = localCoordinatorDaemonId();
67434
+ const coordinatorDaemonId = localCoordinatorDaemonId2();
66984
67435
  if (!coordinatorDaemonId) return { mode: "skip", reason: "remote_auto_launch_no_coordinator_daemon_id" };
66985
67436
  return { mode: "remote", daemonId, coordinatorDaemonId };
66986
67437
  }
@@ -67054,8 +67505,8 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67054
67505
  const reason = args.reason || args.error;
67055
67506
  const difficultyFloorSkip = args.status === "skipped" && isDifficultyFloorWaitReason(reason);
67056
67507
  if (difficultyFloorSkip) {
67057
- handleDifficultyFloorSkip({ meshId, taskId, reason, nodeId: args.nodeId, coordinatorDaemonId: localCoordinatorDaemonId() });
67058
- } else {
67508
+ handleDifficultyFloorSkip({ meshId, taskId, reason, nodeId: args.nodeId, coordinatorDaemonId: localCoordinatorDaemonId2() });
67509
+ } else if (!autoLaunchWriteWouldClobberWinner(meshId, taskId, args, AUTO_LAUNCH_AWAIT_CLAIM_MS)) {
67059
67510
  recordTaskAutoLaunch(meshId, taskId, {
67060
67511
  status: args.status,
67061
67512
  reason,
@@ -67207,7 +67658,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67207
67658
  const nowMs = Date.now();
67208
67659
  const state = autoLaunchAwaitClaimBackoff.get(backoffKey) || { cycles: 0, nextAttemptAtMs: 0 };
67209
67660
  if (state.nextAttemptAtMs && nowMs < state.nextAttemptAtMs) return "backoff";
67210
- const atCap = state.cycles >= AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES;
67661
+ const atCap = state.cycles >= AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2;
67211
67662
  const live = remoteSessionAppearsLive(meshId, sessionId);
67212
67663
  if ((live || atCap) && nodeId && providerType) {
67213
67664
  try {
@@ -67233,7 +67684,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67233
67684
  return "respawn";
67234
67685
  }
67235
67686
  }
67236
- 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);
67237
67688
  autoLaunchAwaitClaimBackoff.set(backoffKey, { cycles, nextAttemptAtMs: nowMs + awaitClaimWindowMs(cycles) });
67238
67689
  recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "awaiting_launched_session_claim_backoff", nodeId, sessionId });
67239
67690
  return "backoff";
@@ -67254,333 +67705,339 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67254
67705
  const maxParallelTasks = resolveMaxParallelTasks(mesh?.policy?.maxParallelTasks);
67255
67706
  const maxReadonlyParallelTasks = resolveMaxReadonlyParallelTasks(maxParallelTasks);
67256
67707
  for (const task of pending) {
67257
- if (!taskDependenciesSatisfied(task, statusById)) {
67258
- 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" });
67259
67711
  continue;
67260
67712
  }
67261
- if (!meshTaskNotBeforeReady(task)) {
67262
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "not_before_delayed" });
67263
- continue;
67264
- }
67265
- const isReadonly = isTaskReadonly(task);
67266
- if (isReadonly) {
67267
- if (activeReadonlyAssignedCount(meshId) >= maxReadonlyParallelTasks) {
67268
- 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" });
67269
67717
  continue;
67270
67718
  }
67271
- } else if (activeWriteAssignedCount(meshId) >= maxParallelTasks) {
67272
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_parallel_tasks_reached" });
67273
- continue;
67274
- }
67275
- if (task.targetSessionId) {
67276
- const deadTarget = resolveDeadTargetVerdict(components, meshId, mesh, task);
67277
- if (deadTarget.dead) {
67278
- const requeued = requeueTask(meshId, task.id, {
67279
- reason: deadTarget.reason,
67280
- clearTargetSession: true,
67281
- // Keep the node pin if only the SESSION died on a still-live node; clear it
67282
- // when the NODE itself is absent (nothing to pin to).
67283
- clearTargetNode: deadTarget.nodeDead
67284
- });
67285
- if (requeued) {
67286
- noteTargetPinCleared(deadTarget.reason);
67287
- 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}).`);
67288
- }
67289
- 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" });
67290
67721
  continue;
67291
67722
  }
67292
- const pinAgeMs = targetPinAgeMs(task);
67293
- if (pinAgeMs !== null && pinAgeMs >= TARGET_SESSION_PIN_TTL_MS) {
67294
- const expired = expireTaskTargetPin(meshId, task.id, { reason: "target_session_pin_expired_unclaimed" });
67295
- if (expired) {
67296
- noteTargetPinCleared("target_session_pin_expired_unclaimed");
67297
- traceMeshEventDrop("target_session_pin_expired", {
67298
- taskId: task.id,
67299
- sessionId: readNonEmptyString(task.targetSessionId),
67300
- nodeId: readNonEmptyString(task.targetNodeId),
67301
- meshId,
67302
- event: "agent:ready"
67303
- }, `unclaimed ${Math.round(pinAgeMs / 1e3)}s \u2265 ttl ${Math.round(TARGET_SESSION_PIN_TTL_MS / 1e3)}s \u2192 pin cleared, claimable`);
67304
- 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;
67305
67728
  }
67306
- 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" });
67307
67731
  continue;
67308
67732
  }
67309
- markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
67310
- continue;
67311
- }
67312
- if (task.autoLaunch?.status === "completed" && task.autoLaunch.sessionId) {
67313
- const launchedAtMs = Date.parse(task.autoLaunch.updatedAt);
67314
- const alSessionId = readNonEmptyString(task.autoLaunch.sessionId);
67315
- const alNodeId = readNonEmptyString(task.autoLaunch.nodeId);
67316
- const alProvider = readNonEmptyString(task.autoLaunch.providerType);
67317
- if (Number.isFinite(launchedAtMs) && Date.now() - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS) {
67318
- recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "awaiting_launched_session_claim", nodeId: alNodeId, sessionId: alSessionId });
67319
- continue;
67320
- }
67321
- if (Number.isFinite(launchedAtMs) && alSessionId && alNodeId) {
67322
- const outcome = driveExpiredAwaitClaim(components, meshId, task, { sessionId: alSessionId, nodeId: alNodeId, providerType: alProvider });
67323
- if (outcome === "claimed" || outcome === "fallback") return true;
67324
- if (outcome === "backoff") continue;
67325
- }
67326
- }
67327
- const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
67328
- if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
67329
- if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
67330
- if (task.requiredTags?.length) {
67331
- const slotProviders = resolveNodeCapabilitySlots(node, meshId).map((s2) => s2.provider).filter(Boolean);
67332
- const priorities = slotProviders.length ? slotProviders : normalizeProviderPriority2(node?.policy);
67333
- const providerCandidates = priorities.length ? priorities : [void 0];
67334
- return providerCandidates.some(
67335
- (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
67336
67739
  );
67337
- }
67338
- return true;
67339
- }) : [];
67340
- if (!candidateNodes.length) {
67341
- const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
67342
- const convergenceOntoWorktree = task.taskMode === "convergence" && Array.isArray(mesh?.nodes) && (() => {
67343
- const matched = mesh.nodes.filter((n) => !task.targetNodeId || meshNodeIdMatches(n, task.targetNodeId));
67344
- return matched.length > 0 && matched.every((n) => n?.isLocalWorktree === true);
67345
- })();
67346
- const targetTransientlyUnresolved = targetPinUnmatched && isTargetNodeTransientlyUnresolved(mesh, task);
67347
- markAutoLaunch(meshId, task.id, {
67348
- status: "skipped",
67349
- reason: convergenceOntoWorktree ? "mesh_convergence_target_is_worktree" : targetTransientlyUnresolved ? TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON : targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
67350
- nodeId: task.targetNodeId
67351
- });
67352
- continue;
67353
- }
67354
- const strategy = resolveSchedulingStrategy(mesh);
67355
- const orderedCandidateNodes = strategy === "first_eligible" ? candidateNodes : orderEligibleNodes(
67356
- meshId,
67357
- strategy,
67358
- candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
67359
- // Auto-launch drains one task at a time, so the task IS in scope here —
67360
- // pass it through for the 'fitness' strategy's task→slot ranking. The
67361
- // mesh's quotaRouting thresholds ride along so the fitness score can
67362
- // include the quota-headroom spread bonus (fail-open when unset).
67363
- { bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags }, quotaRouting: mesh?.policy?.quotaRouting ?? null, quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader) }
67364
- ).map((c) => c.node);
67365
- const skippedCandidates = [];
67366
- const SKIPPED_CANDIDATES_MAX = 5;
67367
- const markSkip = (nodeIdForSkip, reason, extra) => {
67368
- markAutoLaunch(meshId, task.id, { status: "skipped", reason, nodeId: nodeIdForSkip, ...extra || {} });
67369
- if (nodeIdForSkip && skippedCandidates.length < SKIPPED_CANDIDATES_MAX) {
67370
- skippedCandidates.push({ nodeId: nodeIdForSkip, reason });
67371
- }
67372
- };
67373
- for (const node of orderedCandidateNodes) {
67374
- const nodeId = readMeshNodeId(node);
67375
- if (!nodeId) continue;
67376
- const launchKey = `${meshId}:${nodeId}`;
67377
- const now = Date.now();
67378
- const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
67379
- if (cooldownUntil > 0 && now >= cooldownUntil) autoLaunchCooldownUntil.delete(launchKey);
67380
- if (autoLaunchInProgress.has(launchKey)) {
67381
- markSkip(nodeId, "auto_launch_in_progress");
67382
- continue;
67383
- }
67384
- if (now < cooldownUntil) {
67385
- markSkip(nodeId, "auto_launch_cooldown");
67386
- continue;
67387
- }
67388
- if (isDirtyNode(node)) {
67389
- markSkip(nodeId, "dirty_workspace");
67390
- continue;
67391
- }
67392
- if (!isLaunchableNode(node)) {
67393
- markSkip(nodeId, "node_health_not_launchable");
67394
- continue;
67395
- }
67396
- if (!isMeshNodeFreshEnoughToLaunch(node, freshnessGate)) {
67397
- markSkip(nodeId, "node_stale_behind_upstream");
67398
67740
  continue;
67399
67741
  }
67400
- const launchTarget = resolveAutoLaunchTarget(components, node);
67401
- if (launchTarget.mode === "skip") {
67402
- markSkip(nodeId, launchTarget.reason || "auto_launch_unavailable");
67403
- autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67404
- sweepExpiredCooldowns();
67405
- continue;
67406
- }
67407
- if (nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node)) {
67408
- markSkip(nodeId, "node_has_live_session_pending_claim");
67409
- continue;
67410
- }
67411
- if (!isTaskReadonly(task) && nodeHasActiveAssignment(meshId, nodeId)) {
67412
- 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" });
67413
67764
  continue;
67414
67765
  }
67415
- const maxConcurrentSessions = Number(node?.policy?.maxConcurrentSessions);
67416
- if (Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0 && liveSessionCountForNode(components, meshId, nodeId) >= maxConcurrentSessions) {
67417
- 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
+ });
67418
67806
  continue;
67419
67807
  }
67420
- autoLaunchInProgress.add(launchKey);
67421
- try {
67422
- 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);
67423
- if (!resolved.providerType) {
67424
- 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");
67425
67836
  continue;
67426
67837
  }
67427
- const slotCoversDifficulty = slotCoversTaskDifficulty(resolved.slot, task.difficulty);
67428
- const requestedModel = resolveLaunchAxis(task.model, task.modelSource, resolved.model, slotCoversDifficulty);
67429
- const effectiveThinkingLevel = resolveLaunchAxis(task.thinkingLevel, task.thinkingLevelSource, resolved.thinkingLevel, slotCoversDifficulty);
67430
- const slotDecision = decideSlotForModel({
67431
- requestedModel,
67432
- slots: resolveNodeCapabilitySlots(node, meshId).map((slot) => ({
67433
- slot,
67434
- available: slotHasCapacity(meshId, nodeId, node, slot, mesh?.nodes, isReadonly)
67435
- }))
67436
- });
67437
- if (slotDecision.outcome === "wait") {
67438
- 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`);
67439
- markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
67838
+ if (now < cooldownUntil) {
67839
+ markSkip(nodeId, "auto_launch_cooldown");
67440
67840
  continue;
67441
67841
  }
67442
- if (slotDecision.outcome === "notify") {
67443
- 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`);
67444
- markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
67842
+ if (isDirtyNode(node)) {
67843
+ markSkip(nodeId, "dirty_workspace");
67445
67844
  continue;
67446
67845
  }
67447
- const rawEffectiveModel = slotDecision.model;
67448
- const winningModel = resolved.slot?.model?.trim() || void 0;
67449
- const finalizedModel = slotDecision.slot.model?.trim() || void 0;
67450
- const slotWasDemoted = resolved.slot?.provider !== slotDecision.slot.provider || winningModel !== finalizedModel;
67451
- const demotionReason = slotWasDemoted ? slotHasCapacity(meshId, nodeId, node, resolved.slot, mesh?.nodes, isReadonly) ? "slot_reselected_during_launch" : "winning_slot_capacity_exhausted" : void 0;
67452
- const effectiveModel = isModelCompatibleWithProvider(rawEffectiveModel, resolved.providerType) ? rawEffectiveModel : void 0;
67453
- if (rawEffectiveModel && effectiveModel === void 0) {
67454
- 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`);
67455
- }
67456
- const providerCap = effectiveSlotCap(
67457
- resolveProviderMaxParallel(resolveNodeCapabilitySlots(node, meshId), resolved.providerType),
67458
- isReadonly
67459
- );
67460
- if (providerCap !== void 0 && activeProviderAssignedCount(
67461
- meshId,
67462
- nodeId,
67463
- resolved.providerType,
67464
- resolveDaemonSiblingNodeIds(nodeId, mesh?.nodes)
67465
- ) >= providerCap) {
67466
- markSkip(nodeId, "max_provider_parallel_reached", { providerType: resolved.providerType });
67846
+ if (!isLaunchableNode(node)) {
67847
+ markSkip(nodeId, "node_health_not_launchable");
67467
67848
  continue;
67468
67849
  }
67469
- const launchSettings = {
67470
- // Worker launch envelope: role + mesh context so worker can route completion events.
67471
- role: "worker",
67472
- meshNodeFor: meshId,
67473
- meshNodeId: nodeId,
67474
- spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
67475
- // Coordinator-dispatched worker: auto-approve unless mesh/node policy
67476
- // opts out (default true). Lands in settingsOverride and beats the
67477
- // global per-provider-type boolean/mode through explicit opposite-key clearing.
67478
- ...delegatedWorkerAutoApproveSettingsForNode(
67479
- mesh,
67480
- node,
67481
- components.providerLoader?.getMeta(resolved.providerType),
67482
- resolved.providerType
67483
- ),
67484
- launchedByCoordinator: true,
67485
- autoLaunchedForQueueTaskId: task.id
67486
- };
67487
- if (launchTarget.mode === "remote") {
67488
- const remoteSettings = {
67489
- ...launchSettings,
67490
- meshCoordinatorDaemonId: launchTarget.coordinatorDaemonId,
67491
- 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
67492
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
+ }
67493
67982
  markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67494
- let launchResult2;
67495
- try {
67496
- launchResult2 = await components.dispatchMeshCommand(launchTarget.daemonId, "launch_cli", withStatusProbeMarker({
67497
- cliType: resolved.providerType,
67498
- dir: node.workspace,
67499
- settings: remoteSettings,
67500
- // MAGI-KIND-PANEL model axis: forward the task's model override so the
67501
- // remote worker session launches with it (initialModel). Best-effort.
67502
- // Slot-aware: task override wins, else the matched slot's model.
67503
- ...effectiveModel ? { initialModel: effectiveModel } : {},
67504
- // BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
67505
- ...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
67506
- }));
67507
- } catch (e) {
67508
- 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 });
67509
67996
  autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67510
67997
  sweepExpiredCooldowns();
67511
67998
  return false;
67512
67999
  }
67513
- const payload = launchResult2 && typeof launchResult2 === "object" && "payload" in launchResult2 && launchResult2.payload && typeof launchResult2.payload === "object" ? launchResult2.payload : launchResult2;
67514
- if (!payload?.success) {
67515
- const reason = readNonEmptyString(payload?.error) || "remote_launch_cli_failed";
67516
- 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 });
67517
68003
  autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67518
68004
  sweepExpiredCooldowns();
67519
68005
  return false;
67520
68006
  }
67521
- const remoteSessionId = readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.id) || readNonEmptyString(payload.runtimeSessionId);
67522
- markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId: remoteSessionId || void 0, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67523
- logAutoLaunchQuotaFallbackSuccess(resolved, task.id, nodeId, remoteSessionId || void 0);
67524
- autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67525
- 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);
67526
68030
  return true;
67527
- }
67528
- markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67529
- const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
67530
- cliType: resolved.providerType,
67531
- dir: node.workspace,
67532
- settings: launchSettings,
67533
- // MAGI-KIND-PANEL model axis: local launch forwards the effective model
67534
- // (task override, else matched slot) as initialModel (CLI → modelLaunchArgs; ACP → setConfigOption).
67535
- ...effectiveModel ? { initialModel: effectiveModel } : {},
67536
- // BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
67537
- ...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
67538
- });
67539
- if (!launchResult?.success) {
67540
- const reason = launchResult?.error || "launch_cli_failed";
67541
- markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
67542
- autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67543
- sweepExpiredCooldowns();
67544
- return false;
67545
- }
67546
- const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
67547
- if (!sessionId) {
67548
- 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 });
67549
68033
  autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67550
- sweepExpiredCooldowns();
67551
68034
  return false;
68035
+ } finally {
68036
+ autoLaunchInProgress.delete(launchKey);
67552
68037
  }
67553
- markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
67554
- logAutoLaunchQuotaFallbackSuccess(resolved, task.id, nodeId, sessionId);
67555
- await waitForLocalSessionReady(components, sessionId);
67556
- const requiredTags = Array.isArray(task.requiredTags) ? task.requiredTags.filter((t) => !!t) : [];
67557
- const routingDecision = buildAutoLaunchRoutingDecision({
67558
- node,
67559
- meshId,
67560
- task: { difficulty: task.difficulty, requiredTags: task.requiredTags },
67561
- resolved,
67562
- quotaRouting: mesh?.policy?.quotaRouting ?? null,
67563
- quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader),
67564
- skippedCandidates,
67565
- requiredTagsResult: {
67566
- required: requiredTags,
67567
- satisfied: !requiredTags.length || nodeSatisfiesRequiredTags(requiredTags, buildMeshNodeCapabilityTags(node, resolved.providerType)),
67568
- missing: requiredTags.filter((t) => !buildMeshNodeCapabilityTags(node, resolved.providerType).includes(t))
67569
- },
67570
- effectiveModel,
67571
- effectiveThinkingLevel,
67572
- executedSlot: slotDecision.slot,
67573
- demotionReason
67574
- });
67575
- tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType, routingDecision);
67576
- return true;
67577
- } catch (e) {
67578
- markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
67579
- autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
67580
- return false;
67581
- } finally {
67582
- autoLaunchInProgress.delete(launchKey);
67583
68038
  }
68039
+ } finally {
68040
+ autoLaunchTaskInProgress.delete(taskLaunchKey);
67584
68041
  }
67585
68042
  }
67586
68043
  return false;
@@ -67627,7 +68084,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67627
68084
  if (instMeshId !== meshId) continue;
67628
68085
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
67629
68086
  if (!nodeId) continue;
67630
- if (!isIdleSessionState2(state)) {
68087
+ if (!isIdleSessionState3(state)) {
67631
68088
  const status = readNonEmptyString(state.status).toLowerCase();
67632
68089
  skippedSessions.push({
67633
68090
  nodeId,
@@ -67700,6 +68157,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67700
68157
  }
67701
68158
  }
67702
68159
  autoLaunchStarted = await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
68160
+ sweepAutoLaunchOrphanSessions(components, meshId);
67703
68161
  const afterQueue = getQueue(meshId);
67704
68162
  const pendingAfter = afterQueue.filter((task) => task.status === "pending").length;
67705
68163
  const assignedAfter = afterQueue.filter((task) => task.status === "assigned").length;
@@ -67758,12 +68216,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67758
68216
  var LOCAL_LAUNCH_READY_TIMEOUT_MS;
67759
68217
  var LOCAL_LAUNCH_READY_POLL_MS;
67760
68218
  var autoLaunchInProgress;
68219
+ var autoLaunchTaskInProgress;
67761
68220
  var autoLaunchCooldownUntil;
67762
68221
  var AUTO_LAUNCH_COOLDOWN_MS;
67763
- var AUTO_LAUNCH_AWAIT_CLAIM_MS;
67764
- var AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES;
67765
- var AUTO_LAUNCH_REMOTE_IDLE_TTL_MS;
67766
- var autoLaunchAwaitClaimBackoff;
68222
+ var AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2;
67767
68223
  var init_mesh_queue_assignment = __esm2({
67768
68224
  "src/mesh/mesh-queue-assignment.ts"() {
67769
68225
  "use strict";
@@ -67796,14 +68252,17 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67796
68252
  init_mesh_duplicate_dispatch();
67797
68253
  init_mesh_auto_fast_forward();
67798
68254
  init_mesh_skip_notify();
68255
+ init_mesh_task_parking();
67799
68256
  init_mesh_scheduling_fitness();
67800
68257
  init_mesh_queue_observability();
67801
68258
  init_mesh_routing_decision();
68259
+ init_mesh_autolaunch_integrity();
67802
68260
  init_mesh_difficulty_floor();
67803
68261
  init_mesh_difficulty_floor();
67804
68262
  init_mesh_auto_fast_forward();
67805
68263
  init_mesh_skip_notify();
67806
68264
  init_mesh_scheduling_fitness();
68265
+ init_mesh_autolaunch_integrity();
67807
68266
  BOOTSTRAP_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["complete", "failed"]);
67808
68267
  DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
67809
68268
  DISPATCH_CONNECT_TIMEOUT_MS = MESH_CONNECT_TIMEOUT_MS2;
@@ -67811,12 +68270,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67811
68270
  LOCAL_LAUNCH_READY_TIMEOUT_MS = 15e3;
67812
68271
  LOCAL_LAUNCH_READY_POLL_MS = 100;
67813
68272
  autoLaunchInProgress = /* @__PURE__ */ new Set();
68273
+ autoLaunchTaskInProgress = /* @__PURE__ */ new Set();
67814
68274
  autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
67815
68275
  AUTO_LAUNCH_COOLDOWN_MS = 5e3;
67816
- AUTO_LAUNCH_AWAIT_CLAIM_MS = 9e4;
67817
- AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES = 2;
67818
- AUTO_LAUNCH_REMOTE_IDLE_TTL_MS = 5 * 60 * 1e3;
67819
- autoLaunchAwaitClaimBackoff = /* @__PURE__ */ new Map();
68276
+ AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2 = 2;
67820
68277
  }
67821
68278
  });
67822
68279
  function readSettings(state) {
@@ -70550,6 +71007,11 @@ ${cleanBody}`;
70550
71007
  if (isFalseIdleCompletion(metadataEvent)) return false;
70551
71008
  return !!readWorkerResultMetadata(metadataEvent) || !!readNonEmptyString(metadataEvent.finalSummary);
70552
71009
  }
71010
+ function resolveGraphEnvelopeWorkerResult(metadataEvent) {
71011
+ const explicit = readWorkerResultMetadata(metadataEvent);
71012
+ if (explicit) return explicit;
71013
+ return extractJsonObjectFromSummary(readNonEmptyString(metadataEvent.finalSummary) || void 0);
71014
+ }
70553
71015
  function supersedesTruncatedTerminalSummary(args) {
70554
71016
  if (!args.terminalTaskId || !args.eventTaskId || args.terminalTaskId !== args.eventTaskId) return false;
70555
71017
  if (!isGenuineCompletionEvidence(args.metadataEvent)) return false;
@@ -71187,7 +71649,7 @@ ${cleanBody}`;
71187
71649
  // as the terminal flip (design :317-327 step 2).
71188
71650
  envelope: {
71189
71651
  finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0,
71190
- workerResult: readWorkerResultMetadata(args.metadataEvent),
71652
+ workerResult: resolveGraphEnvelopeWorkerResult(args.metadataEvent),
71191
71653
  nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
71192
71654
  providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0
71193
71655
  }
@@ -82466,6 +82928,7 @@ ${e?.stderr || ""}`;
82466
82928
  CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES: () => CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES,
82467
82929
  CHAT_MESSAGE_VISIBILITIES: () => CHAT_MESSAGE_VISIBILITIES,
82468
82930
  COMPACT_STATUS_GOAL_PREVIEW_MAX: () => COMPACT_STATUS_GOAL_PREVIEW_MAX,
82931
+ CTRL_C: () => CTRL_C,
82469
82932
  CdpDomHandlers: () => CdpDomHandlers,
82470
82933
  CliProviderInstance: () => CliProviderInstance,
82471
82934
  DAEMON_WS_PATH: () => DAEMON_WS_PATH2,
@@ -82475,6 +82938,7 @@ ${e?.stderr || ""}`;
82475
82938
  DEFAULT_CHAT_TAIL_MISSING_SESSION_POLICY: () => DEFAULT_CHAT_TAIL_MISSING_SESSION_POLICY,
82476
82939
  DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS: () => DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS2,
82477
82940
  DEFAULT_DAEMON_PORT: () => DEFAULT_DAEMON_PORT2,
82941
+ DEFAULT_DELIVERY_MODE: () => DEFAULT_DELIVERY_MODE,
82478
82942
  DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS: () => DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
82479
82943
  DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS2,
82480
82944
  DEFAULT_MESH_POLICY: () => DEFAULT_MESH_POLICY,
@@ -82500,6 +82964,7 @@ ${e?.stderr || ""}`;
82500
82964
  DaemonStatusReporter: () => DaemonStatusReporter,
82501
82965
  DevServer: () => DevServer,
82502
82966
  DuplicateMeshDispatchError: () => DuplicateMeshDispatchError,
82967
+ ESC: () => ESC,
82503
82968
  FsmDriver: () => FsmDriver,
82504
82969
  GOAL_PREVIEW_MAX: () => GOAL_PREVIEW_MAX,
82505
82970
  GitCommandError: () => GitCommandError,
@@ -82554,6 +83019,10 @@ ${e?.stderr || ""}`;
82554
83019
  OPERATING_NOTE_KIND: () => OPERATING_NOTE_KIND,
82555
83020
  OPERATING_NOTE_TOMBSTONE_KIND: () => OPERATING_NOTE_TOMBSTONE_KIND,
82556
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,
82557
83026
  PROVIDER_CHANNEL_ENV_VAR: () => PROVIDER_CHANNEL_ENV_VAR,
82558
83027
  PRUNABLE_ORPHAN_STALE_REASONS: () => PRUNABLE_ORPHAN_STALE_REASONS,
82559
83028
  ProviderChannelError: () => ProviderChannelError,
@@ -82567,6 +83036,7 @@ ${e?.stderr || ""}`;
82567
83036
  STALE_MAGI_WINDOW_MS: () => STALE_MAGI_WINDOW_MS,
82568
83037
  STALE_TERMINAL_REFINE_WINDOW_MS: () => STALE_TERMINAL_REFINE_WINDOW_MS,
82569
83038
  STANDALONE_CDP_SCAN_INTERVAL_MS: () => STANDALONE_CDP_SCAN_INTERVAL_MS2,
83039
+ STOP_CONTROL_ID: () => STOP_CONTROL_ID,
82570
83040
  SYM: () => SYM,
82571
83041
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory2,
82572
83042
  StatuslineInstallError: () => StatuslineInstallError2,
@@ -82617,6 +83087,7 @@ ${e?.stderr || ""}`;
82617
83087
  buildMissionPromptSection: () => buildMissionPromptSection,
82618
83088
  buildOrphanedPinNotice: () => buildOrphanedPinNotice,
82619
83089
  buildP2pRelayFailurePayload: () => buildP2pRelayFailurePayload,
83090
+ buildParkedTaskNotice: () => buildParkedTaskNotice,
82620
83091
  buildPinnedGlobalInstallCommand: () => buildPinnedGlobalInstallCommand,
82621
83092
  buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
82622
83093
  buildSessionEntries: () => buildSessionEntries,
@@ -82696,6 +83167,7 @@ ${e?.stderr || ""}`;
82696
83167
  evaluateProviderQuotaGate: () => evaluateProviderQuotaGate,
82697
83168
  execNpmCommandSync: () => execNpmCommandSync,
82698
83169
  expandDaemonIdForms: () => expandDaemonIdForms,
83170
+ failRetentionExpiredParkedTask: () => failRetentionExpiredParkedTask,
82699
83171
  fastForwardMeshNode: () => fastForwardMeshNode,
82700
83172
  fetchAntigravityQuota: () => fetchAntigravityQuota2,
82701
83173
  fetchClaudeQuota: () => fetchClaudeQuota2,
@@ -82748,6 +83220,7 @@ ${e?.stderr || ""}`;
82748
83220
  getMeshStatusMissionSummaries: () => getMeshStatusMissionSummaries,
82749
83221
  getMeshStatusMissionsCompact: () => getMeshStatusMissionsCompact,
82750
83222
  getNpmExecOptions: () => getNpmExecOptions,
83223
+ getParkedTasks: () => getParkedTasks,
82751
83224
  getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
82752
83225
  getProcessInstanceContext: () => getProcessInstanceContext,
82753
83226
  getQueue: () => getQueue,
@@ -82847,6 +83320,7 @@ ${e?.stderr || ""}`;
82847
83320
  normalizeChatMessageKind: () => normalizeChatMessageKind,
82848
83321
  normalizeChatMessages: () => normalizeChatMessages,
82849
83322
  normalizeChatTailActiveModal: () => normalizeChatTailActiveModal,
83323
+ normalizeDeliveryMode: () => normalizeDeliveryMode,
82850
83324
  normalizeGitOutput: () => normalizeGitOutput,
82851
83325
  normalizeGitWorkspaceSubscriptionParams: () => normalizeGitWorkspaceSubscriptionParams2,
82852
83326
  normalizeInputEnvelope: () => normalizeInputEnvelope,
@@ -82867,7 +83341,11 @@ ${e?.stderr || ""}`;
82867
83341
  normalizeRepoMeshDeclarativeConfig: () => normalizeRepoMeshDeclarativeConfig,
82868
83342
  normalizeSessionModalFields: () => normalizeSessionModalFields,
82869
83343
  notifyCoordinatorOfOrphanedPins: () => notifyCoordinatorOfOrphanedPins,
83344
+ notifyCoordinatorOfParkedTaskDropped: () => notifyCoordinatorOfParkedTaskDropped,
82870
83345
  otherTrackConfigDir: () => otherTrackConfigDir,
83346
+ parkTaskTargetPin: () => parkTaskTargetPin,
83347
+ parkedAgeMs: () => parkedAgeMs,
83348
+ parkedTaskRetentionExpired: () => parkedTaskRetentionExpired,
82871
83349
  parseOnDependencyFailurePolicy: () => parseOnDependencyFailurePolicy,
82872
83350
  parsePorcelainV2Status: () => parsePorcelainV2Status,
82873
83351
  parseProviderSourceConfigUpdate: () => parseProviderSourceConfigUpdate,
@@ -82951,6 +83429,7 @@ ${e?.stderr || ""}`;
82951
83429
  resolveGitRepository: () => resolveGitRepository,
82952
83430
  resolveInstanceContext: () => resolveInstanceContext,
82953
83431
  resolveInstanceDir: () => resolveInstanceDir,
83432
+ resolveInterruptCapability: () => resolveInterruptCapability,
82954
83433
  resolveMagiSessionCleanupMode: () => resolveMagiSessionCleanupMode,
82955
83434
  resolveMaxParallelTasks: () => resolveMaxParallelTasks,
82956
83435
  resolveMeshHostStatus: () => resolveMeshHostStatus,
@@ -83008,6 +83487,7 @@ ${e?.stderr || ""}`;
83008
83487
  supportsUnicodeSymbols: () => supportsUnicodeSymbols,
83009
83488
  sweepMeshGraphGateTimeouts: () => sweepMeshGraphGateTimeouts,
83010
83489
  taskDependenciesSatisfied: () => taskDependenciesSatisfied,
83490
+ taskIsParked: () => taskIsParked,
83011
83491
  tombstoneOperatingNote: () => tombstoneOperatingNote,
83012
83492
  totalTokens: () => totalTokens,
83013
83493
  triggerMeshQueue: () => triggerMeshQueue,
@@ -85505,6 +85985,8 @@ ${e?.stderr || ""}`;
85505
85985
  };
85506
85986
  }
85507
85987
  init_mesh_work_queue();
85988
+ init_mesh_work_queue();
85989
+ init_mesh_task_parking();
85508
85990
  init_mesh_graph_derived_failure();
85509
85991
  init_mesh_graph_workspace_saga();
85510
85992
  init_mesh_graph_workspace_safety();
@@ -86104,6 +86586,12 @@ ${e?.stderr || ""}`;
86104
86586
  workspaceRef: ws.workspaceRef,
86105
86587
  detail: `Workspace '${ws.workspaceRef}' needs manual compensation: ${ws.lastError ?? "compensation refused"}.`
86106
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
+ });
86107
86595
  }
86108
86596
  }
86109
86597
  return {
@@ -86270,6 +86758,47 @@ The pin is NOT cleared automatically: a pin often encodes required context conti
86270
86758
  init_contracts();
86271
86759
  init_mesh_events_utils();
86272
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
+ }
86273
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.";
86274
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.";
86275
86804
  var NON_P2P_NEXT_ACTION = "Inspect the provider/command error and fix the underlying logic or configuration before retrying.";
@@ -102077,6 +102606,52 @@ ${marker}`,
102077
102606
  hash: hash2
102078
102607
  };
102079
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
+ }
102080
102655
  /**
102081
102656
  * MESH-SEND-KEYS (feature 3: key injection). Inject a STRUCTURED key
102082
102657
  * sequence into the spec-driven PTY for mesh_send_keys. Mirrors
@@ -110692,6 +111267,39 @@ Run 'adhdev doctor' for detailed diagnostics.`
110692
111267
  } else if (action === "stop") {
110693
111268
  await this.stopSession(key2);
110694
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
+ };
110695
111303
  }
110696
111304
  throw new Error(`Unknown action: ${action}`);
110697
111305
  }
@@ -110854,7 +111462,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
110854
111462
  const { shouldDeferDispatchForBootstrap: shouldDeferDispatchForBootstrap2 } = await Promise.resolve().then(() => (init_worktree_bootstrap_config(), worktree_bootstrap_config_exports));
110855
111463
  if (shouldDeferDispatchForBootstrap2(nodeObj)) {
110856
111464
  const dispatchSessionState = dispatchSessionId ? ctx.deps.instanceManager?.getInstance?.(dispatchSessionId)?.getState?.() : void 0;
110857
- const sessionConfirmedReady = !!dispatchSessionState && isIdleSessionState2(dispatchSessionState);
111465
+ const sessionConfirmedReady = !!dispatchSessionState && isIdleSessionState3(dispatchSessionState);
110858
111466
  if (!sessionConfirmedReady) {
110859
111467
  return {
110860
111468
  success: false,
@@ -118475,7 +119083,12 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
118475
119083
  // CANON-IDENTITY: an in-flight (actively-generating) task is refused by
118476
119084
  // default to avoid a duplicate second dispatch; an explicit operator
118477
119085
  // force overrides that guard (and the retry cap).
118478
- 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
118479
119092
  });
118480
119093
  if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
118481
119094
  if (task.status === "assigned" && args?.force !== true) {