@adhdev/daemon-standalone 0.9.82-rc.373 → 0.9.82-rc.374

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.
@@ -382,213 +382,57 @@ function annotateRapidReadChatAdvisory(payload, options) {
382
382
  }
383
383
 
384
384
  // src/tools/mesh-tools.ts
385
- var import_daemon_core = require("@adhdev/daemon-core");
386
- var SESSION_PROVIDER_METADATA_TTL_MS = 30 * 6e4;
387
- var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
388
- function getSessionMetadata(key) {
389
- const entry = meshSessionProviderMetadata.get(key);
390
- if (!entry) return void 0;
391
- if (entry.expiresAt <= Date.now()) {
392
- meshSessionProviderMetadata.delete(key);
393
- return void 0;
394
- }
395
- return entry;
396
- }
397
- var ACTIVE_WORK_POLLING_BACKOFF_MS = 6e4;
398
- function buildActiveWorkPollingGuidance(summary, now = Date.now()) {
399
- if (!summary || summary.generatingCount <= 0) return void 0;
400
- return {
401
- activeGeneratingWork: true,
402
- generatingCount: summary.generatingCount,
403
- doNotPollBefore: new Date(now + ACTIVE_WORK_POLLING_BACKOFF_MS).toISOString(),
404
- eventSurface: "pendingCoordinatorEvents",
405
- nextRecommendedAction: "Wait for pendingCoordinatorEvents/completion events or an explicit user status request. If no terminal evidence appears and the user asks for status, make one bounded status check, then wait again.",
406
- message: "Do not repeatedly poll mesh_status/mesh_view_queue/mesh_read_chat while delegated work is generating; terminal ledger or completion evidence will be surfaced through pendingCoordinatorEvents when available."
407
- };
408
- }
385
+ var import_daemon_core2 = require("@adhdev/daemon-core");
386
+
387
+ // src/tools/mesh-tool-shared.ts
409
388
  function readString(value) {
410
389
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
411
390
  }
412
- function summarizeTaskMessage(message) {
413
- const taskSummary = message.replace(/\s+/g, " ").trim();
414
- const taskTitle = taskSummary.length > 96 ? `${taskSummary.slice(0, 93)}...` : taskSummary;
415
- return { taskTitle: taskTitle || "(untitled task)", taskSummary };
416
- }
417
- function buildDirectTaskPayload(message, via, opts) {
418
- const descriptor = summarizeTaskMessage(message);
419
- return {
420
- source: "direct",
421
- via,
422
- taskId: opts.taskId,
423
- message,
424
- taskTitle: descriptor.taskTitle,
425
- taskSummary: descriptor.taskSummary,
426
- ...opts.taskMode ? { taskMode: opts.taskMode } : {},
427
- ...opts.providerType ? { providerType: opts.providerType } : {},
428
- ...opts.targetSessionId ? { targetSessionId: opts.targetSessionId } : {},
429
- ...opts.dispatchedToIdleSession !== void 0 ? { dispatchedToIdleSession: opts.dispatchedToIdleSession } : {}
430
- };
431
- }
432
- function findNode(mesh, nodeId) {
433
- const node = mesh.nodes.find((n) => (0, import_daemon_core.meshNodeIdMatches)(n, nodeId));
434
- if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
435
- return node;
436
- }
437
- var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
438
- var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
439
- var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
440
- var ACTIVE_QUEUE_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned"]);
441
- var HISTORICAL_QUEUE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
442
- async function refreshMeshFromDaemon(ctx) {
443
- try {
444
- const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
445
- if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
446
- const refreshedNodes = result.mesh.nodes.filter((n) => n?.id).map((n) => n);
447
- ctx.mesh.nodes.splice(0, ctx.mesh.nodes.length, ...refreshedNodes);
448
- ctx.mesh.updatedAt = result.mesh.updatedAt ?? ctx.mesh.updatedAt;
449
- } catch {
450
- }
391
+ function readNumeric(value, fallback = 0) {
392
+ const parsed = Number(value);
393
+ return Number.isFinite(parsed) ? parsed : fallback;
451
394
  }
452
- async function syncCoordinatorDaemonMeshCache(ctx) {
453
- if (!(ctx.transport instanceof IpcTransport)) return;
454
- try {
455
- await ctx.transport.command("get_mesh", {
456
- meshId: ctx.mesh.id,
457
- inlineMesh: ctx.mesh
458
- });
459
- } catch {
395
+ var LARGE_LEDGER_FIELD_KEYS = /* @__PURE__ */ new Set(["plan", "validationPlan", "suggestedConfig", "payload"]);
396
+ var LARGE_LEDGER_OBJECT_THRESHOLD = 800;
397
+ var LARGE_LEDGER_NESTED_BYTES_THRESHOLD = 2e3;
398
+ function summarizeLargeLedgerField(key, value) {
399
+ if (typeof value === "string") {
400
+ return value.length > 500 ? value.slice(0, 500) + "\u2026" : value;
460
401
  }
461
- }
462
- async function findNodeWithRefresh(ctx, nodeId) {
463
- const hit = ctx.mesh.nodes.find((n) => (0, import_daemon_core.meshNodeIdMatches)(n, nodeId));
464
- if (hit && !hit.isLocalWorktree) return hit;
465
- await refreshMeshFromDaemon(ctx);
466
- const refreshed = ctx.mesh.nodes.find((n) => (0, import_daemon_core.meshNodeIdMatches)(n, nodeId));
467
- if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
468
- return refreshed;
469
- }
470
- async function findOptionalNodeWithRefresh(ctx, nodeId) {
471
- const hit = ctx.mesh.nodes.find((n) => (0, import_daemon_core.meshNodeIdMatches)(n, nodeId));
472
- if (hit && !hit.isLocalWorktree) return hit;
473
- await refreshMeshFromDaemon(ctx);
474
- return ctx.mesh.nodes.find((n) => (0, import_daemon_core.meshNodeIdMatches)(n, nodeId)) ?? null;
475
- }
476
- function hasRecentDuplicateDispatch(ctx, args) {
477
- const now = Date.now();
478
- const normalizedMessage = args.message.trim();
479
- for (const task of (0, import_daemon_core.getQueue)(ctx.mesh.id)) {
480
- const timestamp = new Date(task.updatedAt || task.createdAt).getTime();
481
- if (!Number.isFinite(timestamp) || now - timestamp > DUPLICATE_DISPATCH_WINDOW_MS) continue;
482
- if (task.targetNodeId && task.targetNodeId !== args.node_id) continue;
483
- if (task.assignedNodeId && task.assignedNodeId !== args.node_id) continue;
484
- if (args.session_id && task.targetSessionId !== args.session_id && task.assignedSessionId !== args.session_id) continue;
485
- if (task.message?.trim() === normalizedMessage) {
486
- return { duplicate: true, entry: task, source: "queue" };
402
+ if (Array.isArray(value)) {
403
+ const serialized = JSON.stringify(value);
404
+ if (serialized && serialized.length > LARGE_LEDGER_OBJECT_THRESHOLD) {
405
+ return `[${key} summarized: ${value.length} items \u2014 use verbose=true or mesh_reconcile_ledger]`;
487
406
  }
407
+ return value;
488
408
  }
489
- const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
490
- for (let i = entries.length - 1; i >= 0; i -= 1) {
491
- const entry = entries[i];
492
- const timestamp = new Date(entry.timestamp).getTime();
493
- if (Number.isFinite(timestamp) && now - timestamp > DUPLICATE_DISPATCH_WINDOW_MS) break;
494
- if (entry.kind !== "task_dispatched") continue;
495
- if (entry.nodeId !== args.node_id) continue;
496
- if (args.session_id && entry.sessionId !== args.session_id) continue;
497
- if (typeof entry.payload?.message !== "string") continue;
498
- if (entry.payload.message.trim() === normalizedMessage) {
499
- return { duplicate: true, entry, source: "ledger" };
409
+ if (value && typeof value === "object") {
410
+ const serialized = JSON.stringify(value);
411
+ if (serialized && serialized.length > LARGE_LEDGER_OBJECT_THRESHOLD) {
412
+ return `[${key} summarized: ${Object.keys(value).length} keys \u2014 use verbose=true or mesh_reconcile_ledger]`;
500
413
  }
414
+ return value;
501
415
  }
502
- return { duplicate: false };
416
+ return value;
503
417
  }
504
- function buildMissingNodeReadChatRecovery(ctx, args) {
505
- const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 300 });
506
- const relatedEntries = entries.filter((entry) => entry.nodeId === args.node_id || entry.sessionId === args.session_id);
507
- const completedEntries = relatedEntries.filter((entry) => entry.kind === "task_completed");
508
- const lastDispatch = [...relatedEntries].reverse().find((entry) => entry.kind === "task_dispatched");
509
- const lastTerminal = [...relatedEntries].reverse().find((entry) => entry.kind === "task_completed" || entry.kind === "task_failed" || entry.kind === "task_stalled");
510
- const lastRemoved = [...relatedEntries].reverse().find((entry) => entry.kind === "node_removed");
511
- const lastLaunch = [...relatedEntries].reverse().find((entry) => entry.kind === "session_launched");
512
- const providerSessionId = args.provider_session_id || readString(lastTerminal?.payload?.providerSessionId) || readString(lastLaunch?.payload?.providerSessionId) || readString(lastDispatch?.payload?.providerSessionId);
513
- const finalSummary = readString(lastTerminal?.payload?.finalSummary) || readString(lastTerminal?.payload?.compactSummary) || readString(lastTerminal?.payload?.summary);
514
- const ledger = {
515
- taskCompletedFound: completedEntries.length > 0,
516
- nodeRemovedFound: !!lastRemoved,
517
- providerType: lastTerminal?.providerType || lastLaunch?.providerType || lastDispatch?.providerType,
518
- providerSessionId,
519
- nodeRemovedAt: lastRemoved?.timestamp,
520
- sessionCleanupMode: readString(lastRemoved?.payload?.sessionCleanupMode),
521
- readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
522
- };
523
- if (finalSummary) {
524
- if (args.compact === true) {
525
- return {
526
- ...compactChatPayload({
527
- success: true,
528
- status: "idle",
529
- providerSessionId,
530
- summary: finalSummary,
531
- messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
532
- }, {
533
- nodeId: args.node_id,
534
- sessionId: args.session_id,
535
- limit: args.tail ?? 10
536
- }),
537
- recoveredFromLedger: true,
538
- ledger
539
- };
540
- }
541
- return {
542
- success: true,
543
- compact: false,
544
- recoveredFromLedger: true,
545
- nodeId: args.node_id,
546
- sessionId: args.session_id,
547
- summary: finalSummary,
548
- ledger,
549
- messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
550
- };
418
+ function elideLargeNestedValue(key, value) {
419
+ if (value === null || value === void 0) return value;
420
+ if (typeof value === "string") {
421
+ return value.length > 1e3 ? value.slice(0, 1e3) + "\u2026" : value;
551
422
  }
423
+ if (typeof value !== "object") return value;
424
+ const serialized = JSON.stringify(value);
425
+ const bytes = serialized ? serialized.length : 0;
426
+ if (bytes <= LARGE_LEDGER_NESTED_BYTES_THRESHOLD) return value;
552
427
  return {
553
- success: false,
554
- recoverable: true,
555
- code: "mesh_removed_node_transcript_unavailable",
556
- error: `Node '${args.node_id}' is not a current member of mesh '${ctx.mesh.name}'.`,
557
- nodeId: args.node_id,
558
- sessionId: args.session_id,
559
- providerSessionId,
560
- reason: "node_not_in_current_mesh_snapshot",
561
- ledger,
562
- completedSessionSeenInLedger: ledger.taskCompletedFound,
563
- lastDispatch: lastDispatch ? {
564
- timestamp: lastDispatch.timestamp,
565
- sessionId: lastDispatch.sessionId,
566
- providerType: lastDispatch.providerType,
567
- taskId: typeof lastDispatch.payload?.taskId === "string" ? lastDispatch.payload.taskId : void 0,
568
- messagePreview: typeof lastDispatch.payload?.message === "string" ? lastDispatch.payload.message.slice(0, 500) : void 0
569
- } : null,
570
- lastTerminalEvent: lastTerminal ? {
571
- kind: lastTerminal.kind,
572
- timestamp: lastTerminal.timestamp,
573
- sessionId: lastTerminal.sessionId,
574
- providerType: lastTerminal.providerType,
575
- taskId: typeof lastTerminal.payload?.taskId === "string" ? lastTerminal.payload.taskId : void 0,
576
- payload: lastTerminal.payload
577
- } : null,
578
- nextSteps: [
579
- providerSessionId ? `Retry mesh_read_chat with provider_session_id='${providerSessionId}' on a current live node for the same daemon if one exists.` : "If the node UI shows a provider transcript id, retry mesh_read_chat/mesh_read_debug with provider_session_id.",
580
- "Use mesh_read_debug with the provider_session_id or daemon-side debug bundle locator if available.",
581
- "Check mesh_task_history for task_completed and node_removed entries before redispatching; do not resend solely because transcript recovery failed.",
582
- "If this node was removed with stop_and_delete, the runtime transcript may be gone; rely on the ledger summary/locator or ask the operator for the saved UI output."
583
- ],
584
- recoveryHints: [
585
- "The worktree/node may have been removed or the mesh snapshot may be stale after task completion.",
586
- "If you have a provider_session_id, retry mesh_read_chat with that value while targeting a live node for the same daemon if available.",
587
- "Use mesh_read_debug with provider_session_id, or inspect the daemon/session-host history locator if the transcript has already been archived.",
588
- "Avoid redispatching the same task solely because read_chat could not recover the transcript; check task_history and git status first."
589
- ]
428
+ _elided: true,
429
+ _kind: key,
430
+ _bytes: bytes,
431
+ _hint: "full evidence via mesh_reconcile_ledger"
590
432
  };
591
433
  }
434
+
435
+ // src/tools/mesh-session-helpers.ts
592
436
  function readSessionRecordId(session) {
593
437
  return readString(session?.id) || readString(session?.sessionId) || readString(session?.session_id) || readString(session?.runtimeSessionId) || readString(session?.runtime_session_id) || readString(session?.instanceId) || readString(session?.instance_id);
594
438
  }
@@ -656,6 +500,36 @@ function collectNodeSessionIds(node) {
656
500
  sessionRecords.forEach((session) => addSessionRecord(sessions, session));
657
501
  return sessions;
658
502
  }
503
+ function unwrapCommandPayload(value) {
504
+ let current = value;
505
+ const seen = /* @__PURE__ */ new Set();
506
+ for (let depth = 0; depth < 8; depth += 1) {
507
+ if (!current || typeof current !== "object" || seen.has(current)) break;
508
+ seen.add(current);
509
+ const nested = current.result ?? current.payload;
510
+ if (!nested || typeof nested !== "object") break;
511
+ current = nested;
512
+ }
513
+ return current;
514
+ }
515
+ function isTerminalSessionRecord(session) {
516
+ const status = typeof session?.status === "string" ? session.status.toLowerCase() : "";
517
+ const lifecycle = typeof session?.lifecycle === "string" ? session.lifecycle.toLowerCase() : "";
518
+ const state = typeof session?.state === "string" ? session.state.toLowerCase() : "";
519
+ return [status, lifecycle, state].some((value) => ["stopped", "failed", "terminated", "exited", "closed"].includes(value));
520
+ }
521
+ function isIdleSessionRecord(session) {
522
+ if (isTerminalSessionRecord(session)) return false;
523
+ const status = typeof session?.status === "string" ? session.status.toLowerCase() : "";
524
+ const chatStatus = typeof session?.activeChat?.status === "string" ? session.activeChat.status.toLowerCase() : "";
525
+ return status === "idle" || chatStatus === "waiting_input";
526
+ }
527
+
528
+ // src/tools/mesh-queue-helpers.ts
529
+ var STALE_ASSIGNED_QUEUE_MS = 30 * 6e4;
530
+ var OLD_HISTORICAL_QUEUE_RECORD_MS = 7 * 24 * 60 * 6e4;
531
+ var ACTIVE_QUEUE_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned"]);
532
+ var HISTORICAL_QUEUE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
659
533
  function buildQueueLivenessIndex(mesh) {
660
534
  const nodeIds = /* @__PURE__ */ new Set();
661
535
  const nodeSessionIds = /* @__PURE__ */ new Map();
@@ -885,403 +759,105 @@ function annotateQueueStaleness(queue, mesh) {
885
759
  };
886
760
  });
887
761
  }
888
- function unwrapCommandPayload(value) {
889
- let current = value;
890
- const seen = /* @__PURE__ */ new Set();
891
- for (let depth = 0; depth < 8; depth += 1) {
892
- if (!current || typeof current !== "object" || seen.has(current)) break;
893
- seen.add(current);
894
- const nested = current.result ?? current.payload;
895
- if (!nested || typeof nested !== "object") break;
896
- current = nested;
897
- }
898
- return current;
899
- }
900
- function isDirectDispatchLedgerEntry(entry) {
901
- if (entry?.kind !== "task_dispatched") return false;
902
- const payload = entry.payload || {};
903
- const via = readString(payload.via);
904
- return payload.source === "direct" || via === "p2p_direct" || via === "local_direct" || via === "mesh_send_task";
905
- }
906
- function readMessageTimestampIso(message) {
907
- for (const value of [message?.timestamp, message?.createdAt, message?.created_at, message?.updatedAt, message?.time]) {
908
- if (typeof value === "number" && Number.isFinite(value)) {
909
- const ms = value > 1e10 ? value : value * 1e3;
910
- return new Date(ms).toISOString();
911
- }
912
- if (typeof value === "string" && value.trim()) {
913
- const ms = new Date(value.trim()).getTime();
914
- if (Number.isFinite(ms)) return new Date(ms).toISOString();
915
- }
762
+
763
+ // src/tools/mesh-compact.ts
764
+ function buildCompactGitSnapshot(status) {
765
+ if (!status || typeof status !== "object" || Array.isArray(status)) return void 0;
766
+ const slim = {};
767
+ const carry = [
768
+ "isGitRepo",
769
+ "branch",
770
+ "headCommit",
771
+ "upstream",
772
+ "upstreamStatus",
773
+ "ahead",
774
+ "behind",
775
+ "dirty",
776
+ "detached",
777
+ "submodules"
778
+ ];
779
+ for (const key of carry) {
780
+ if (status[key] !== void 0) slim[key] = status[key];
916
781
  }
917
- return void 0;
782
+ return slim;
918
783
  }
919
- function readFinalAssistantTranscriptEvidence(payload) {
920
- const rawMessages = Array.isArray(payload?.messages) ? payload.messages : [];
921
- const finalAssistant = [...rawMessages].reverse().filter(isCoordinatorVisibleMessage).find((message) => {
922
- const role = String(message?.role ?? "").toLowerCase();
923
- return (role === "assistant" || role === "agent") && messageContent(message).trim();
924
- });
925
- const finalSummary = messageContent(finalAssistant).trim() || (typeof payload?.summary === "string" && payload.summary.trim() ? payload.summary.trim() : void 0);
784
+ function summarizeCompactSubmodules(submodules) {
785
+ if (!Array.isArray(submodules) || submodules.length === 0) return void 0;
786
+ const outOfSync = submodules.filter((s) => s?.outOfSync).map((s) => s?.path).filter(Boolean);
926
787
  return {
927
- finalSummary,
928
- transcriptMessageAt: finalAssistant ? readMessageTimestampIso(finalAssistant) : void 0
788
+ count: submodules.length,
789
+ ...outOfSync.length > 0 ? { outOfSyncPaths: outOfSync } : {}
929
790
  };
930
791
  }
931
- function findNodeSession(nodes, nodeId, sessionId) {
932
- if (!nodeId || !sessionId) return {};
933
- const node = nodes.find((candidate) => (0, import_daemon_core.meshNodeIdMatches)(candidate, nodeId));
934
- if (!node) return {};
935
- const sessions = Array.isArray(node.sessions) ? node.sessions : [];
936
- const session = sessions.find((candidate) => readSessionRecordId(candidate) === sessionId);
937
- return { node, session };
938
- }
939
- function buildDirectDispatchReconciliationCandidates(directDispatches, ledgerEntries) {
940
- const candidates = [];
941
- const seenTaskIds = /* @__PURE__ */ new Set();
942
- for (const dispatch of directDispatches || []) {
943
- const taskId = readString(dispatch?.taskId);
944
- if (!taskId || seenTaskIds.has(taskId)) continue;
945
- seenTaskIds.add(taskId);
946
- candidates.push(dispatch);
947
- }
948
- for (const entry of ledgerEntries || []) {
949
- if (!isDirectDispatchLedgerEntry(entry)) continue;
950
- const taskId = readString(entry.payload?.taskId);
951
- if (!taskId || seenTaskIds.has(taskId)) continue;
952
- seenTaskIds.add(taskId);
953
- candidates.push({
954
- taskId,
955
- nodeId: entry.nodeId,
956
- sessionId: entry.sessionId,
957
- providerType: entry.providerType || readString(entry.payload?.providerType),
958
- message: readString(entry.payload?.message),
959
- dispatchedAt: entry.timestamp,
960
- via: readString(entry.payload?.via)
961
- });
962
- }
963
- return candidates;
964
- }
965
- async function reconcileDirectDispatchesFromTranscriptEvidence(ctx, liveNodes, directDispatches, ledgerEntries) {
966
- let attempted = 0;
967
- let reconciled = 0;
968
- let skipped = 0;
969
- const candidates = buildDirectDispatchReconciliationCandidates(directDispatches, ledgerEntries);
970
- for (const dispatch of candidates) {
971
- const taskId = readString(dispatch?.taskId);
972
- const nodeId = readString(dispatch?.nodeId);
973
- const sessionId = readString(dispatch?.sessionId);
974
- if (!taskId || !nodeId || !sessionId) {
975
- skipped += 1;
976
- continue;
977
- }
978
- const { session } = findNodeSession(liveNodes, nodeId, sessionId);
979
- if (!session || !isIdleSessionRecord(session)) {
980
- skipped += 1;
981
- continue;
982
- }
983
- const node = await findOptionalNodeWithRefresh(ctx, nodeId).catch(() => null);
984
- if (!node) {
985
- skipped += 1;
986
- continue;
987
- }
988
- const providerType = readString(dispatch?.providerType) || resolveSessionProviderType(session);
989
- const providerSessionId = readString(session?.providerSessionId) || readString(session?.activeChat?.providerSessionId) || readString(session?.settings?.providerSessionId) || resolveMeshSessionProviderMetadata(ctx, nodeId, sessionId)?.providerSessionId;
990
- attempted += 1;
991
- try {
992
- const readResult = await commandForNode(ctx, node, "read_chat", {
993
- sessionId,
994
- targetSessionId: sessionId,
995
- workspace: node.workspace,
996
- ...providerType ? { agentType: providerType, providerType } : {},
997
- ...providerSessionId ? { providerSessionId } : {},
998
- tailLimit: 10
999
- });
1000
- const payload = unwrapCommandPayload(readResult);
1001
- if (payload?.success === false) continue;
1002
- const evidence = readFinalAssistantTranscriptEvidence(payload);
1003
- if (!evidence.finalSummary) continue;
1004
- const result = (0, import_daemon_core.reconcileDirectDispatchCompletionFromTranscript)({
1005
- meshId: ctx.mesh.id,
1006
- nodeId,
1007
- sessionId,
1008
- providerType,
1009
- providerSessionId: readString(payload?.providerSessionId) || providerSessionId,
1010
- taskId,
1011
- finalSummary: evidence.finalSummary,
1012
- transcriptMessageAt: evidence.transcriptMessageAt,
1013
- targetCoordinatorDaemonId: ctx.localDaemonId,
1014
- source: "mcp_mesh_status_transcript_reconciliation"
1015
- });
1016
- if (result.reconciled) reconciled += 1;
1017
- } catch {
1018
- skipped += 1;
792
+ var MESH_COMPACT_PRESERVED_MARKER_FIELDS = ["dataFreshness"];
793
+ function compactMeshStatusNode(entry) {
794
+ if (!entry || typeof entry !== "object") return entry;
795
+ const next = { ...entry };
796
+ if (next.git !== void 0) {
797
+ const slimGit = buildCompactGitSnapshot(next.git);
798
+ if (slimGit) {
799
+ if (slimGit.submodules !== void 0) {
800
+ const subSummary = summarizeCompactSubmodules(slimGit.submodules);
801
+ if (subSummary) slimGit.submodules = subSummary;
802
+ else delete slimGit.submodules;
803
+ }
804
+ next.git = slimGit;
1019
805
  }
1020
806
  }
1021
- return { attempted, reconciled, skipped };
1022
- }
1023
- async function triggerMeshQueueAndReport(ctx) {
1024
- try {
1025
- const raw = await ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id });
1026
- const payload = unwrapCommandPayload(raw);
1027
- const trigger = payload?.trigger && typeof payload.trigger === "object" ? payload.trigger : payload;
1028
- return trigger && typeof trigger === "object" ? trigger : { success: true };
1029
- } catch (e) {
1030
- return {
1031
- success: false,
1032
- error: e?.message || String(e)
807
+ if (next.machine && typeof next.machine === "object") {
808
+ const m = next.machine;
809
+ next.machine = {
810
+ daemonId: m.daemonId,
811
+ machineId: m.machineId,
812
+ hostname: m.hostname,
813
+ displayName: m.displayName,
814
+ sameMachine: m.sameMachine,
815
+ locality: m.locality
1033
816
  };
1034
817
  }
1035
- }
1036
- function buildQueueTriggerGuidance(queueTrigger) {
1037
- if (!queueTrigger || queueTrigger.claimed === true) return void 0;
1038
- if (queueTrigger.success === false) {
1039
- return {
1040
- queueClaimed: false,
1041
- queueDispatchState: "trigger_failed",
1042
- nextAction: "Do not assume the queued task is running. Check mesh_view_queue and daemon connectivity before redispatching."
1043
- };
818
+ if (typeof next.submoduleWarning === "string") {
819
+ next.submodulesOutOfSync = true;
820
+ delete next.submoduleWarning;
1044
821
  }
1045
- if (queueTrigger.autoLaunchPending === true) {
1046
- return {
1047
- queueClaimed: false,
1048
- queueDispatchState: "pending_waiting_for_autolaunch",
1049
- nextAction: "A worker session was just auto-launched for this task and is booting; it will claim the task shortly. Wait for it to claim \u2014 do NOT launch another session. Use mesh_view_queue to confirm the assignment lands."
822
+ if (next.staleDaemonBuild && typeof next.staleDaemonBuild === "object") {
823
+ const b = next.staleDaemonBuild;
824
+ next.staleDaemonBuild = {
825
+ scope: b.scope,
826
+ isDaemonAffecting: b.isDaemonAffecting !== false,
827
+ seeStaleDaemonBuilds: true
1050
828
  };
1051
829
  }
1052
- if (queueTrigger.noIdleMeshSessionAvailable === true) {
1053
- return {
1054
- queueClaimed: false,
1055
- queueDispatchState: "pending_no_idle_mesh_session",
1056
- nextAction: "The task is queued but not running. Launch a managed worker with mesh_launch_session, or wait for a delegated session to become ready and trigger the queue again."
1057
- };
830
+ delete next.capabilityTagsByProvider;
831
+ const elideSkip = /* @__PURE__ */ new Set(["git", "machine", "branchConvergence", "staleDaemonBuild", "sessions", ...MESH_COMPACT_PRESERVED_MARKER_FIELDS]);
832
+ for (const k of Object.keys(next)) {
833
+ if (elideSkip.has(k)) continue;
834
+ next[k] = elideLargeNestedValue(k, next[k]);
1058
835
  }
1059
- return {
1060
- queueClaimed: false,
1061
- queueDispatchState: "pending_or_waiting_for_ready",
1062
- nextAction: "The task is queued but this trigger did not claim it. Use mesh_view_queue for the current active-work source of truth before retrying."
1063
- };
836
+ return next;
1064
837
  }
1065
- function isTerminalSessionRecord(session) {
1066
- const status = typeof session?.status === "string" ? session.status.toLowerCase() : "";
1067
- const lifecycle = typeof session?.lifecycle === "string" ? session.lifecycle.toLowerCase() : "";
1068
- const state = typeof session?.state === "string" ? session.state.toLowerCase() : "";
1069
- return [status, lifecycle, state].some((value) => ["stopped", "failed", "terminated", "exited", "closed"].includes(value));
838
+ function compactNodeSeverity(entry) {
839
+ if (!entry || typeof entry !== "object") return 0;
840
+ if (entry.error || entry.health && entry.health !== "online" && entry.health !== "dirty") return 5;
841
+ if (entry.launchReady === false) return 4;
842
+ if (entry.isDirty === true || entry.health === "dirty") return 3;
843
+ if (entry.branchConvergence?.needsConvergence === true) return 2;
844
+ if (entry.staleDaemonBuild || entry.submodulesOutOfSync || entry.recoveryHints) return 1;
845
+ return 0;
1070
846
  }
1071
- function isIdleSessionRecord(session) {
1072
- if (isTerminalSessionRecord(session)) return false;
1073
- const status = typeof session?.status === "string" ? session.status.toLowerCase() : "";
1074
- const chatStatus = typeof session?.activeChat?.status === "string" ? session.activeChat.status.toLowerCase() : "";
1075
- return status === "idle" || chatStatus === "waiting_input";
1076
- }
1077
- function isMeshOwnedDelegateSession(session, meshId, nodeId) {
1078
- const settings = session?.settings;
1079
- const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
1080
- const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
1081
- if (sessionMeshId) {
1082
- if (sessionMeshId !== meshId) return false;
1083
- return !sessionNodeId || sessionNodeId === nodeId;
1084
- }
1085
- const coordinatorOwned = settings?.launchedByCoordinator === true || Boolean(readString(settings?.meshCoordinatorDaemonId));
1086
- if (!coordinatorOwned) return false;
1087
- const lastNodeId = readString(settings?.meshLastNodeId);
1088
- if (lastNodeId) return lastNodeId === nodeId;
1089
- return true;
1090
- }
1091
- function hasRemoteRelayMetadata(session) {
1092
- return Boolean(
1093
- readString(session?.settings?.meshCoordinatorDaemonId) || readString(session?.meta?.meshCoordinatorDaemonId) || readString(session?.metadata?.meshCoordinatorDaemonId) || readString(session?.meshCoordinatorDaemonId)
1094
- );
1095
- }
1096
- function classifyRemoteDelegateRelaySafety(session, meshId, nodeId, coordinatorDaemonId) {
1097
- if (!isMeshOwnedDelegateSession(session, meshId, nodeId)) return "unsafe_alias";
1098
- if (hasRemoteRelayMetadata(session)) return "safe";
1099
- return coordinatorDaemonId ? "self_heal" : "missing_anchor";
1100
- }
1101
- function chooseDispatchableSession(sessions, providerType, meshId, nodeId, coordinatorDaemonId) {
1102
- const live = sessions.filter((session) => !isTerminalSessionRecord(session));
1103
- const matchingProvider = (session) => !providerType || session?.providerType === providerType || session?.cliType === providerType;
1104
- const meshSessions = live.filter((session) => {
1105
- const safety = classifyRemoteDelegateRelaySafety(session, meshId, nodeId, coordinatorDaemonId);
1106
- return safety === "safe" || safety === "self_heal";
1107
- });
1108
- return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || void 0;
1109
- }
1110
- function buildRelayUnsafeRemoteSessionFailure(ctx, node, sessionId, providerType) {
1111
- return {
1112
- success: false,
1113
- recoverable: true,
1114
- code: "mesh_delegate_session_missing_relay_metadata",
1115
- reason: "mesh_delegate_session_missing_relay_metadata",
1116
- transport: "mesh_transport",
1117
- retryRecommended: true,
1118
- meshId: ctx.mesh.id,
1119
- nodeId: node.id,
1120
- daemonId: node.daemonId,
1121
- workspace: node.workspace,
1122
- sessionId,
1123
- unsafeTranscriptAlias: true,
1124
- ...providerType ? { resolvedProviderType: providerType } : {},
1125
- error: `Remote session '${sessionId}' is not relay-safe for mesh '${ctx.mesh.id}': missing meshNodeFor/meshCoordinatorDaemonId metadata, so completion events would not reach the coordinator ledger. This session may be the coordinator itself or an unrelated session (unsafe_transcript_alias risk).`,
1126
- nextAction: `Launch a fresh relay-safe session with mesh_launch_session(node_id: '${node.id}'${providerType ? `, type: '${providerType}'` : ""}) or dispatch without session_id so Repo Mesh can choose a valid delegate session.`,
1127
- noFallbackReason: "Blindly reusing a remote session without mesh relay metadata would silently drop task_completed / generating_completed events."
1128
- };
1129
- }
1130
- function buildMissingCoordinatorDaemonIdFailure(ctx, node, providerType) {
1131
- return {
1132
- success: false,
1133
- recoverable: true,
1134
- code: "mesh_coordinator_daemon_unknown",
1135
- reason: "mesh_coordinator_daemon_unknown",
1136
- transport: "mesh_transport",
1137
- retryRecommended: true,
1138
- meshId: ctx.mesh.id,
1139
- nodeId: node.id,
1140
- daemonId: node.daemonId,
1141
- workspace: node.workspace,
1142
- ...providerType ? { resolvedProviderType: providerType } : {},
1143
- error: `Cannot launch a remote mesh delegate for node '${node.id}': coordinator daemon identity is unavailable, so the worker would be unable to relay completion events back to the coordinator.`,
1144
- nextAction: "Retry after the coordinator daemon identity is available (for example from an attached daemon-backed MCP session) so meshCoordinatorDaemonId can be stamped on the worker session.",
1145
- noFallbackReason: "Launching without meshCoordinatorDaemonId would create a worker session that can finish work but cannot emit task_completed / generating_completed back to the coordinator."
1146
- };
1147
- }
1148
- function findNestedPayload(value, predicate) {
1149
- const seen = /* @__PURE__ */ new Set();
1150
- const stack = [{ payload: value, depth: 0 }];
1151
- while (stack.length) {
1152
- const { payload, depth } = stack.pop();
1153
- if (predicate(payload)) return payload;
1154
- if (!payload || typeof payload !== "object" || seen.has(payload) || depth >= 8) continue;
1155
- seen.add(payload);
1156
- for (const key of ["payload", "result"]) {
1157
- if (key in payload) stack.push({ payload: payload[key], depth: depth + 1 });
1158
- }
1159
- }
1160
- return value;
1161
- }
1162
- function extractCloneNodePayload(value) {
1163
- return findNestedPayload(value, (payload) => Boolean(payload?.node?.id));
1164
- }
1165
- function extractGitStatus(value) {
1166
- const payload = unwrapCommandPayload(value);
1167
- return payload?.status ?? value?.status ?? payload;
1168
- }
1169
- function extractGitDiff(value) {
1170
- const payload = unwrapCommandPayload(value);
1171
- return payload?.diffSummary ?? payload?.diff ?? value?.diffSummary ?? value?.diff ?? payload;
1172
- }
1173
- function extractSubmodules(value, ignorePaths) {
1174
- const payload = unwrapCommandPayload(value);
1175
- const subs = payload?.status?.submodules ?? payload?.submodules ?? value?.status?.submodules ?? value?.submodules;
1176
- if (!Array.isArray(subs)) return void 0;
1177
- if (ignorePaths.length === 0) return subs;
1178
- const ignoreSet = new Set(ignorePaths);
1179
- return subs.filter((s) => s?.path && !ignoreSet.has(s.path));
1180
- }
1181
- function assignFullGitSnapshot(entry, status) {
1182
- if (!status || typeof status !== "object" || Array.isArray(status)) return;
1183
- entry.git = status;
1184
- }
1185
- function buildCompactGitSnapshot(status) {
1186
- if (!status || typeof status !== "object" || Array.isArray(status)) return void 0;
1187
- const slim = {};
1188
- const carry = [
1189
- "isGitRepo",
1190
- "branch",
1191
- "headCommit",
1192
- "upstream",
1193
- "upstreamStatus",
1194
- "ahead",
1195
- "behind",
1196
- "dirty",
1197
- "detached",
1198
- "submodules"
1199
- ];
1200
- for (const key of carry) {
1201
- if (status[key] !== void 0) slim[key] = status[key];
1202
- }
1203
- return slim;
1204
- }
1205
- function summarizeCompactSubmodules(submodules) {
1206
- if (!Array.isArray(submodules) || submodules.length === 0) return void 0;
1207
- const outOfSync = submodules.filter((s) => s?.outOfSync).map((s) => s?.path).filter(Boolean);
1208
- return {
1209
- count: submodules.length,
1210
- ...outOfSync.length > 0 ? { outOfSyncPaths: outOfSync } : {}
1211
- };
1212
- }
1213
- var MESH_COMPACT_PRESERVED_MARKER_FIELDS = ["dataFreshness"];
1214
- function compactMeshStatusNode(entry) {
1215
- if (!entry || typeof entry !== "object") return entry;
1216
- const next = { ...entry };
1217
- if (next.git !== void 0) {
1218
- const slimGit = buildCompactGitSnapshot(next.git);
1219
- if (slimGit) {
1220
- if (slimGit.submodules !== void 0) {
1221
- const subSummary = summarizeCompactSubmodules(slimGit.submodules);
1222
- if (subSummary) slimGit.submodules = subSummary;
1223
- else delete slimGit.submodules;
1224
- }
1225
- next.git = slimGit;
1226
- }
1227
- }
1228
- if (next.machine && typeof next.machine === "object") {
1229
- const m = next.machine;
1230
- next.machine = {
1231
- daemonId: m.daemonId,
1232
- machineId: m.machineId,
1233
- hostname: m.hostname,
1234
- displayName: m.displayName,
1235
- sameMachine: m.sameMachine,
1236
- locality: m.locality
1237
- };
1238
- }
1239
- if (typeof next.submoduleWarning === "string") {
1240
- next.submodulesOutOfSync = true;
1241
- delete next.submoduleWarning;
1242
- }
1243
- if (next.staleDaemonBuild && typeof next.staleDaemonBuild === "object") {
1244
- const b = next.staleDaemonBuild;
1245
- next.staleDaemonBuild = {
1246
- scope: b.scope,
1247
- isDaemonAffecting: b.isDaemonAffecting !== false,
1248
- seeStaleDaemonBuilds: true
1249
- };
1250
- }
1251
- delete next.capabilityTagsByProvider;
1252
- const elideSkip = /* @__PURE__ */ new Set(["git", "machine", "branchConvergence", "staleDaemonBuild", "sessions", ...MESH_COMPACT_PRESERVED_MARKER_FIELDS]);
1253
- for (const k of Object.keys(next)) {
1254
- if (elideSkip.has(k)) continue;
1255
- next[k] = elideLargeNestedValue(k, next[k]);
1256
- }
1257
- return next;
1258
- }
1259
- var COMPACT_DETAILED_NODES_BYTE_BUDGET = 9e3;
1260
- var COMPACT_NODES_TOTAL_BYTE_BUDGET = 13e3;
1261
- var COMPACT_MISSIONS_BYTE_BUDGET = 6e3;
1262
- function compactNodeSeverity(entry) {
1263
- if (!entry || typeof entry !== "object") return 0;
1264
- if (entry.error || entry.health && entry.health !== "online" && entry.health !== "dirty") return 5;
1265
- if (entry.launchReady === false) return 4;
1266
- if (entry.isDirty === true || entry.health === "dirty") return 3;
1267
- if (entry.branchConvergence?.needsConvergence === true) return 2;
1268
- if (entry.staleDaemonBuild || entry.submodulesOutOfSync || entry.recoveryHints) return 1;
1269
- return 0;
1270
- }
1271
- function isNoteworthyCompactNode(entry) {
1272
- if (!entry || typeof entry !== "object") return true;
1273
- if (entry.health && entry.health !== "online") return true;
1274
- if (entry.isDirty === true) return true;
1275
- if (entry.error) return true;
1276
- if (entry.launchReady === false) return true;
1277
- if (entry.staleDaemonBuild) return true;
1278
- if (entry.submoduleWarning || entry.submodulesOutOfSync) return true;
1279
- if (entry.recoveryHints) return true;
1280
- if (Array.isArray(entry.nextStepHints) && entry.nextStepHints.length > 0) return true;
1281
- if (entry.branchConvergence?.needsConvergence === true) return true;
1282
- const sessionCount = Array.isArray(entry.sessions) ? entry.sessions.length : entry.sessionSummary?.total ?? 0;
1283
- if (sessionCount > 0) return true;
1284
- return false;
847
+ function isNoteworthyCompactNode(entry) {
848
+ if (!entry || typeof entry !== "object") return true;
849
+ if (entry.health && entry.health !== "online") return true;
850
+ if (entry.isDirty === true) return true;
851
+ if (entry.error) return true;
852
+ if (entry.launchReady === false) return true;
853
+ if (entry.staleDaemonBuild) return true;
854
+ if (entry.submoduleWarning || entry.submodulesOutOfSync) return true;
855
+ if (entry.recoveryHints) return true;
856
+ if (Array.isArray(entry.nextStepHints) && entry.nextStepHints.length > 0) return true;
857
+ if (entry.branchConvergence?.needsConvergence === true) return true;
858
+ const sessionCount = Array.isArray(entry.sessions) ? entry.sessions.length : entry.sessionSummary?.total ?? 0;
859
+ if (sessionCount > 0) return true;
860
+ return false;
1285
861
  }
1286
862
  function minimalCompactNode(entry) {
1287
863
  if (!entry || typeof entry !== "object") return entry;
@@ -1335,284 +911,26 @@ function summarizeNodeSessions(sessions) {
1335
911
  }
1336
912
  return summary;
1337
913
  }
1338
- function extractLaunchPayload(value) {
1339
- return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
1340
- }
1341
- function classifyMeshLaunchFailure(error) {
1342
- const message = error instanceof Error ? error.message : String(error || "launch failed");
1343
- const lower = message.toLowerCase();
1344
- const p2pClassification = (0, import_daemon_core.classifyP2pRelayFailure)(error, { command: "launch_cli" });
1345
- if (p2pClassification.recoverable) {
1346
- return p2pClassification;
914
+
915
+ // src/tools/mesh-node-identity.ts
916
+ var import_daemon_core = require("@adhdev/daemon-core");
917
+ function resolveCoordinatorNode(ctx) {
918
+ const preferredNodeId = typeof ctx.mesh.coordinator?.preferredNodeId === "string" ? ctx.mesh.coordinator.preferredNodeId.trim() : "";
919
+ if (preferredNodeId) {
920
+ const preferred = ctx.mesh.nodes.find((n) => n.id === preferredNodeId && typeof n.daemonId === "string" && n.daemonId.trim());
921
+ if (preferred) return preferred;
1347
922
  }
1348
- if (lower.includes("cannot connect to daemon ipc") || lower.includes("daemon ipc command")) {
1349
- return {
1350
- code: "local_ipc_unavailable",
1351
- reason: "local_daemon_ipc_unavailable",
1352
- transport: "local_ipc",
1353
- recoverable: true,
1354
- retryRecommended: true,
1355
- nextAction: "Check the local daemon IPC connection, then retry mesh_launch_session once after the daemon is reachable."
1356
- };
923
+ if (ctx.localMachineId) {
924
+ const byMachine = ctx.mesh.nodes.find((n) => readNodeMachineId(n) === ctx.localMachineId);
925
+ if (byMachine) return byMachine;
1357
926
  }
1358
- if (lower.includes("timed out") || lower.includes("timeout")) {
1359
- return {
1360
- code: "mesh_transport_timeout",
1361
- reason: "mesh_transport_timeout",
1362
- transport: "mesh_transport",
1363
- recoverable: true,
1364
- retryRecommended: true,
1365
- nextAction: "Check mesh transport health, then do one bounded retry before requeueing or relaunching the task."
1366
- };
927
+ if (ctx.localDaemonId) {
928
+ return ctx.mesh.nodes.find((n) => readNodeDaemonId(n) === ctx.localDaemonId);
1367
929
  }
1368
- return {
1369
- code: "mesh_launch_failed",
1370
- reason: "provider_launch_failed",
1371
- transport: "mesh_transport",
1372
- recoverable: false,
1373
- retryRecommended: false,
1374
- nextAction: "Inspect the provider launch error and fix the underlying provider/configuration issue before retrying."
1375
- };
930
+ return void 0;
1376
931
  }
1377
- function buildWorktreeCleanupHint(node) {
1378
- if (!node.isLocalWorktree) return void 0;
1379
- return {
1380
- tool: "mesh_remove_node",
1381
- args: { node_id: node.id, session_cleanup_mode: "preserve" },
1382
- hint: `If the worktree is no longer needed, remove the orphan worktree node with mesh_remove_node(node_id: "${node.id}").`
1383
- };
1384
- }
1385
- function buildRecoverableLaunchFailure(ctx, node, providerType, error) {
1386
- const message = error instanceof Error ? error.message : String(error || "launch failed");
1387
- const classified = classifyMeshLaunchFailure(error);
1388
- const cleanup = buildWorktreeCleanupHint(node);
1389
- return {
1390
- success: false,
1391
- recoverable: classified.recoverable,
1392
- code: classified.code,
1393
- reason: classified.reason,
1394
- transport: classified.transport,
1395
- retryRecommended: classified.retryRecommended,
1396
- nextAction: classified.nextAction,
1397
- ...classified.noFallbackReason ? { noFallbackReason: classified.noFallbackReason } : {},
1398
- error: message,
1399
- meshId: ctx.mesh.id,
1400
- nodeId: node.id,
1401
- daemonId: node.daemonId,
1402
- workspace: node.workspace,
1403
- isLocalWorktree: node.isLocalWorktree === true,
1404
- worktreeBranch: node.worktreeBranch,
1405
- clonedFromNodeId: node.clonedFromNodeId,
1406
- ...providerType ? { resolvedProviderType: providerType } : {},
1407
- retryHint: `Retry mesh_launch_session(node_id: "${node.id}"${providerType ? `, type: "${providerType}"` : ""}) after daemon mesh transport/P2P is healthy.`,
1408
- ...cleanup ? { cleanup } : {},
1409
- nextStepHints: [
1410
- `Retry mesh_launch_session(node_id: "${node.id}"${providerType ? `, type: "${providerType}"` : ""}) after checking daemon/P2P health.`,
1411
- ...cleanup ? [`Cleanup orphan worktree node with mesh_remove_node(node_id: "${node.id}") if retry is not desired.`] : [],
1412
- "Run mesh_status to see the degraded reason and recovery hints before redispatching work."
1413
- ]
1414
- };
1415
- }
1416
- function recordRecoverableLaunchFailure(ctx, node, providerType, error) {
1417
- const failure = buildRecoverableLaunchFailure(ctx, node, providerType, error);
1418
- try {
1419
- (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
1420
- kind: "recovery_attempted",
1421
- nodeId: node.id,
1422
- providerType,
1423
- payload: {
1424
- event: "session_launch_failed",
1425
- ...failure
1426
- }
1427
- });
1428
- } catch {
1429
- }
1430
- return failure;
1431
- }
1432
- function getLatestActiveLaunchFailure(meshId, nodeId) {
1433
- const entries = (0, import_daemon_core.readLedgerEntries)(meshId, { tail: 200 });
1434
- for (let i = entries.length - 1; i >= 0; i -= 1) {
1435
- const entry = entries[i];
1436
- if (entry.nodeId !== nodeId) continue;
1437
- if (entry.kind === "session_launched" || entry.kind === "node_removed") return null;
1438
- if (entry.kind === "recovery_attempted" && entry.payload?.event === "session_launch_failed") {
1439
- return { timestamp: entry.timestamp, ...entry.payload };
1440
- }
1441
- }
1442
- return null;
1443
- }
1444
- function buildCoordinatorP2pRelayFailure(error, context) {
1445
- const payload = (0, import_daemon_core.buildP2pRelayFailurePayload)(error, {
1446
- command: context.command,
1447
- targetDaemonId: context.targetDaemonId
1448
- });
1449
- return {
1450
- ...payload,
1451
- ...context.nodeId ? { nodeId: context.nodeId } : {},
1452
- ...context.sessionId ? { sessionId: context.sessionId } : {},
1453
- retryHint: payload.retryRecommended ? payload.nextAction : "Do not retry as a P2P transport recovery; inspect the command/provider error first."
1454
- };
1455
- }
1456
- async function ipcDispatchToRemoteAgent(ctx, node, args) {
1457
- const transport = ctx.transport;
1458
- const daemonId = node.daemonId;
1459
- const dispatchCoordinatorDaemonId = readString(args.meshContext?.coordinatorDaemonId) || "";
1460
- let sessionId = args.session_id?.trim() || "";
1461
- const providerPriorityList = Array.isArray(node.policy?.providerPriority) ? node.policy.providerPriority : [];
1462
- let resolvedProviderType = args.providerType?.trim() || providerPriorityList[0] || "";
1463
- if (sessionId && args.verifiedSession) {
1464
- const explicitSession = args.verifiedSession;
1465
- const relaySafety = classifyRemoteDelegateRelaySafety(explicitSession, ctx.mesh.id, node.id, dispatchCoordinatorDaemonId);
1466
- if (relaySafety === "unsafe_alias") {
1467
- return buildRelayUnsafeRemoteSessionFailure(
1468
- ctx,
1469
- node,
1470
- sessionId,
1471
- resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
1472
- );
1473
- }
1474
- if (relaySafety === "missing_anchor") {
1475
- return buildMissingCoordinatorDaemonIdFailure(
1476
- ctx,
1477
- node,
1478
- resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
1479
- );
1480
- }
1481
- if (!resolvedProviderType) {
1482
- resolvedProviderType = resolveSessionProviderType(explicitSession);
1483
- }
1484
- } else if (!sessionId || args.session_id) {
1485
- try {
1486
- const relayResult = await transport.meshCommand(daemonId, "get_status_metadata", {});
1487
- const sessions = extractStatusMetadataSessions(relayResult);
1488
- if (sessionId) {
1489
- const explicitSession = sessions.find((session) => readSessionRecordId(session) === sessionId);
1490
- if (!explicitSession) {
1491
- return {
1492
- success: false,
1493
- recoverable: true,
1494
- code: "mesh_target_session_not_found",
1495
- reason: "mesh_target_session_not_found",
1496
- transport: "mesh_transport",
1497
- retryRecommended: true,
1498
- meshId: ctx.mesh.id,
1499
- nodeId: node.id,
1500
- daemonId,
1501
- workspace: node.workspace,
1502
- sessionId,
1503
- ...resolvedProviderType ? { resolvedProviderType } : {},
1504
- error: `Remote session '${sessionId}' is not present in the live status for node '${node.id}'.`,
1505
- nextAction: `Launch a fresh session with mesh_launch_session(node_id: '${node.id}'${resolvedProviderType ? `, type: '${resolvedProviderType}'` : ""}) or retry without session_id so Repo Mesh can target a live delegate session.`
1506
- };
1507
- }
1508
- const relaySafety = classifyRemoteDelegateRelaySafety(explicitSession, ctx.mesh.id, node.id, dispatchCoordinatorDaemonId);
1509
- if (relaySafety === "unsafe_alias") {
1510
- return buildRelayUnsafeRemoteSessionFailure(
1511
- ctx,
1512
- node,
1513
- sessionId,
1514
- resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
1515
- );
1516
- }
1517
- if (relaySafety === "missing_anchor") {
1518
- return buildMissingCoordinatorDaemonIdFailure(
1519
- ctx,
1520
- node,
1521
- resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
1522
- );
1523
- }
1524
- if (!resolvedProviderType) {
1525
- resolvedProviderType = resolveSessionProviderType(explicitSession);
1526
- }
1527
- } else {
1528
- const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id, dispatchCoordinatorDaemonId);
1529
- if (targetSession?.id || targetSession?.sessionId) {
1530
- sessionId = targetSession.id || targetSession.sessionId;
1531
- if (!resolvedProviderType) {
1532
- resolvedProviderType = resolveSessionProviderType(targetSession);
1533
- }
1534
- }
1535
- }
1536
- } catch (e) {
1537
- if (sessionId) {
1538
- return {
1539
- ...buildCoordinatorP2pRelayFailure(e, {
1540
- command: "get_status_metadata",
1541
- targetDaemonId: daemonId,
1542
- nodeId: node.id,
1543
- sessionId
1544
- }),
1545
- success: false,
1546
- error: `Cannot verify remote session '${sessionId}' before dispatch: ${e?.message || String(e)}`
1547
- };
1548
- }
1549
- }
1550
- }
1551
- if (!resolvedProviderType) {
1552
- return { success: false, error: `Cannot dispatch to remote node '${node.id}': providerType unknown. Set providerPriority on the node policy or call mesh_launch_session first.` };
1553
- }
1554
- try {
1555
- const dispatchResult = await transport.meshCommand(daemonId, "agent_command", {
1556
- ...sessionId ? { targetSessionId: sessionId } : {},
1557
- agentType: resolvedProviderType,
1558
- cliType: resolvedProviderType,
1559
- action: "send_chat",
1560
- message: args.message,
1561
- // WTCLAIM (B): carry the node workspace so a sessionless dispatch can be
1562
- // scoped to THIS node's session on the worker (findAdapter dir match /
1563
- // findMeshNodeAdapter). Without it, a worker hosting both a base node and a
1564
- // cloned worktree node (same daemonId) would fall through to a provider-only
1565
- // fuzzy match and could land worktree work on the base session.
1566
- ...node.workspace ? { dir: node.workspace } : {},
1567
- ...args.meshContext ? { meshContext: args.meshContext } : {}
1568
- });
1569
- const dispatchPayload = unwrapCommandPayload(dispatchResult);
1570
- if (dispatchPayload?.success === false || dispatchResult?.success === false) {
1571
- const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
1572
- const errorMessage = dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task";
1573
- return {
1574
- ...buildCoordinatorP2pRelayFailure(source?.error || errorMessage, {
1575
- command: "agent_command",
1576
- targetDaemonId: daemonId,
1577
- nodeId: node.id,
1578
- sessionId
1579
- }),
1580
- ...source && typeof source === "object" ? source : {},
1581
- success: false,
1582
- error: `P2P dispatch failed: ${errorMessage}`
1583
- };
1584
- }
1585
- return { success: true, dispatched: true, sessionId: sessionId || "", providerType: resolvedProviderType };
1586
- } catch (e) {
1587
- const errorMessage = e?.message || String(e);
1588
- return {
1589
- ...buildCoordinatorP2pRelayFailure(e, {
1590
- command: "agent_command",
1591
- targetDaemonId: daemonId,
1592
- nodeId: node.id,
1593
- sessionId
1594
- }),
1595
- error: `P2P dispatch failed: ${errorMessage}`
1596
- };
1597
- }
1598
- }
1599
- function resolveCoordinatorNode(ctx) {
1600
- const preferredNodeId = typeof ctx.mesh.coordinator?.preferredNodeId === "string" ? ctx.mesh.coordinator.preferredNodeId.trim() : "";
1601
- if (preferredNodeId) {
1602
- const preferred = ctx.mesh.nodes.find((n) => n.id === preferredNodeId && typeof n.daemonId === "string" && n.daemonId.trim());
1603
- if (preferred) return preferred;
1604
- }
1605
- if (ctx.localMachineId) {
1606
- const byMachine = ctx.mesh.nodes.find((n) => readNodeMachineId(n) === ctx.localMachineId);
1607
- if (byMachine) return byMachine;
1608
- }
1609
- if (ctx.localDaemonId) {
1610
- return ctx.mesh.nodes.find((n) => readNodeDaemonId(n) === ctx.localDaemonId);
1611
- }
1612
- return void 0;
1613
- }
1614
- function resolveCoordinatorDaemonId(ctx) {
1615
- return readString(resolveCoordinatorNode(ctx)?.daemonId) || readString(ctx.localDaemonId) || readString(ctx.localMachineId);
932
+ function resolveCoordinatorDaemonId(ctx) {
933
+ return readString(resolveCoordinatorNode(ctx)?.daemonId) || readString(ctx.localDaemonId) || readString(ctx.localMachineId);
1616
934
  }
1617
935
  function readNodeMachineId(node) {
1618
936
  return readString(node.machineId) || readString(node.machine_id) || readString(node.machine?.id) || readString(node.machine?.machineId) || readString(node.lastProbe?.machineId) || readString(node.last_probe?.machine_id) || readString(node.lastProbe?.machine?.id) || readString(node.lastProbe?.machine?.machineId) || readString(node.last_probe?.machine?.id) || readString(node.last_probe?.machine?.machine_id);
@@ -1760,809 +1078,271 @@ function resolvePreferredWorktreeNodeId(ctx) {
1760
1078
  function isLocalControlPlaneNode(ctx, node) {
1761
1079
  return !!getLocalControlPlaneMatchReason(ctx, node);
1762
1080
  }
1763
- function meshSessionCacheKey(nodeId, runtimeSessionId) {
1764
- return `${nodeId}:${runtimeSessionId}`;
1765
- }
1766
- function rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, metadata) {
1767
- const keyNodeId = readString(nodeId);
1768
- const keySessionId = readString(runtimeSessionId);
1769
- if (!keyNodeId || !keySessionId) return;
1770
- const providerType = readString(metadata.providerType);
1771
- const providerSessionId = readString(metadata.providerSessionId);
1772
- if (!providerType && !providerSessionId) return;
1773
- const existing = getSessionMetadata(meshSessionCacheKey(keyNodeId, keySessionId)) || { providerType: "" };
1774
- meshSessionProviderMetadata.set(meshSessionCacheKey(keyNodeId, keySessionId), {
1775
- providerType: providerType || existing.providerType,
1776
- providerSessionId: providerSessionId || existing.providerSessionId,
1777
- expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
1778
- });
1779
- }
1780
- function rememberMeshSessionProviderMetadataFromEvent(event) {
1781
- const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : event && typeof event === "object" ? event : {};
1782
- const nodeId = readString(event?.nodeId) || readString(metadataEvent.nodeId) || readString(metadataEvent.meshNodeId);
1783
- const sessionId = readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId) || readString(event?.sessionId);
1784
- rememberMeshSessionProviderMetadata(nodeId, sessionId, {
1785
- providerType: readString(metadataEvent.providerType) || readString(event?.providerType) || "",
1786
- providerSessionId: readString(metadataEvent.providerSessionId) || readString(event?.providerSessionId)
1787
- });
1788
- }
1789
- function resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId) {
1790
- const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 50 });
1791
- for (let i = entries.length - 1; i >= 0; i -= 1) {
1792
- const entry = entries[i];
1793
- const payload = entry.payload && typeof entry.payload === "object" && !Array.isArray(entry.payload) ? entry.payload : {};
1794
- const entryNodeId = readString(entry.nodeId) || readString(payload.nodeId) || readString(payload.meshNodeId);
1795
- if (entryNodeId && entryNodeId !== nodeId) continue;
1796
- const entrySessionId = readString(entry.sessionId) || readString(payload.targetSessionId) || readString(payload.sessionId) || readString(payload.instanceId);
1797
- if (entrySessionId !== runtimeSessionId) continue;
1798
- const providerType = readString(entry.providerType) || readString(payload.providerType);
1799
- const completionDiagnostic = payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? payload.completionDiagnostic : {};
1800
- const metadataEvent = payload.metadataEvent && typeof payload.metadataEvent === "object" && !Array.isArray(payload.metadataEvent) ? payload.metadataEvent : {};
1801
- const providerSessionId = readString(payload.providerSessionId) || readString(completionDiagnostic.providerSessionId) || readString(metadataEvent.providerSessionId);
1802
- if (providerType || providerSessionId) {
1803
- return { providerType: providerType || "", providerSessionId };
1804
- }
1805
- }
1806
- return void 0;
1807
- }
1808
- function resolveMeshSessionProviderMetadata(ctx, nodeId, runtimeSessionId) {
1809
- const cached = getSessionMetadata(meshSessionCacheKey(nodeId, runtimeSessionId));
1810
- if (cached?.providerType || cached?.providerSessionId) return cached;
1811
- const fromLedger = resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId);
1812
- if (fromLedger) rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, fromLedger);
1813
- return fromLedger;
1814
- }
1815
- function countUncommittedChanges(status) {
1816
- if (typeof status?.uncommittedChanges === "number") return status.uncommittedChanges;
1817
- const keys = ["staged", "modified", "untracked", "deleted", "renamed"];
1818
- const counted = keys.reduce((sum, key) => sum + (Number.isFinite(Number(status?.[key])) ? Number(status[key]) : 0), 0);
1819
- const conflicts = Array.isArray(status?.conflictFiles) ? status.conflictFiles.length : status?.hasConflicts ? 1 : 0;
1820
- return counted + conflicts;
1821
- }
1822
- function isGitStatusDirty(status) {
1823
- if (typeof status?.isDirty === "boolean") return status.isDirty;
1824
- if (typeof status?.dirty === "boolean") return status.dirty;
1825
- if (Array.isArray(status?.submodules) && status.submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return true;
1826
- return countUncommittedChanges(status) > 0;
1827
- }
1828
- var LARGE_LEDGER_FIELD_KEYS = /* @__PURE__ */ new Set(["plan", "validationPlan", "suggestedConfig", "payload"]);
1829
- var LARGE_LEDGER_OBJECT_THRESHOLD = 800;
1830
- var LARGE_LEDGER_NESTED_BYTES_THRESHOLD = 2e3;
1831
- function summarizeLargeLedgerField(key, value) {
1832
- if (typeof value === "string") {
1833
- return value.length > 500 ? value.slice(0, 500) + "\u2026" : value;
1834
- }
1835
- if (Array.isArray(value)) {
1836
- const serialized = JSON.stringify(value);
1837
- if (serialized && serialized.length > LARGE_LEDGER_OBJECT_THRESHOLD) {
1838
- return `[${key} summarized: ${value.length} items \u2014 use verbose=true or mesh_reconcile_ledger]`;
1081
+
1082
+ // src/tools/mesh-tool-schemas.ts
1083
+ var MESH_STATUS_TOOL = {
1084
+ name: "mesh_status",
1085
+ description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures. Also reports the running daemon build per daemonId under top-level daemonBuilds ({commit, commitShort, version}); when a live daemon was built from a commit BEHIND its workspace HEAD it adds staleDaemonBuilds[] + staleDaemonBuildWarning \u2014 meaning a just-merged refinery/mesh-tool fix is NOT yet live on that daemon (awaiting deploy/restart; a local dist rebuild does not update a cloud daemon). Do not repeatedly call this to wait for generating delegated work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.",
1086
+ inputSchema: {
1087
+ type: "object",
1088
+ properties: {
1089
+ _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." },
1090
+ includeStaleDirectWorkDetails: { type: "boolean", description: "Opt in to the full staleDirectWork array. Defaults false; normal status returns compact staleDirectWorkSummary only." },
1091
+ includeSessions: { type: "boolean", description: "Opt in to per-node live session arrays. Default false: compact mode returns a per-node sessionSummary (counts) and de-duplicated full session lists under top-level daemonSessions keyed by daemonId (sessions are not repeated for every node that shares a daemon). Set true to also include the full session array on each node." },
1092
+ compact: { type: "boolean", description: "Slim payload for LLM callers. Default true. Folds per-node session arrays to sessionSummary and de-duplicates daemon-shared sessions into daemonSessions. Set false (or verbose=true) for the full dashboard-grade payload." },
1093
+ verbose: { type: "boolean", description: "Force the full payload; overrides compact." }
1839
1094
  }
1840
- return value;
1841
1095
  }
1842
- if (value && typeof value === "object") {
1843
- const serialized = JSON.stringify(value);
1844
- if (serialized && serialized.length > LARGE_LEDGER_OBJECT_THRESHOLD) {
1845
- return `[${key} summarized: ${Object.keys(value).length} keys \u2014 use verbose=true or mesh_reconcile_ledger]`;
1096
+ };
1097
+ var MESH_LIST_NODES_TOOL = {
1098
+ name: "mesh_list_nodes",
1099
+ description: "List all nodes in the mesh with their capabilities, platform, and workspace paths.",
1100
+ inputSchema: {
1101
+ type: "object",
1102
+ properties: {
1103
+ _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." }
1846
1104
  }
1847
- return value;
1848
- }
1849
- return value;
1850
- }
1851
- function elideLargeNestedValue(key, value) {
1852
- if (value === null || value === void 0) return value;
1853
- if (typeof value === "string") {
1854
- return value.length > 1e3 ? value.slice(0, 1e3) + "\u2026" : value;
1855
1105
  }
1856
- if (typeof value !== "object") return value;
1857
- const serialized = JSON.stringify(value);
1858
- const bytes = serialized ? serialized.length : 0;
1859
- if (bytes <= LARGE_LEDGER_NESTED_BYTES_THRESHOLD) return value;
1860
- return {
1861
- _elided: true,
1862
- _kind: key,
1863
- _bytes: bytes,
1864
- _hint: "full evidence via mesh_reconcile_ledger"
1865
- };
1866
- }
1867
- function slimLedgerPayload(payload) {
1868
- const slim = {};
1869
- for (const [k, v] of Object.entries(payload)) {
1870
- if (k === "message" || k === "taskSummary") {
1871
- slim[k] = typeof v === "string" && v.length > 200 ? v.slice(0, 200) + "\u2026" : v;
1872
- } else if (k === "evidence" || k === "workerResult" || k === "gitStatus" || k === "validationResults") {
1873
- } else if (k === "finalSummary") {
1874
- slim[k] = typeof v === "string" && v.length > 300 ? v.slice(0, 300) + "\u2026" : v;
1875
- } else if (LARGE_LEDGER_FIELD_KEYS.has(k)) {
1876
- slim[k] = summarizeLargeLedgerField(k, v);
1877
- } else {
1878
- slim[k] = elideLargeNestedValue(k, v);
1879
- }
1106
+ };
1107
+ var MESH_ENQUEUE_TASK_TOOL = {
1108
+ name: "mesh_enqueue_task",
1109
+ description: "Add a new task to the mesh work queue. Idle nodes will automatically pull and execute tasks from this queue. Use this instead of mesh_send_task when you do not need to target a specific node.",
1110
+ inputSchema: {
1111
+ type: "object",
1112
+ properties: {
1113
+ message: { type: "string", description: "The task instruction for the agent." },
1114
+ task_mode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "Optional task-mode contract. live_debug_readonly rejects obvious write/commit/push/deploy/destructive instructions before dispatch." },
1115
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." },
1116
+ requiredTags: { type: "array", items: { type: "string" }, description: "Optional capability tags that every eligible node must have, e.g. os=darwin, provider=codex-cli, gpu." },
1117
+ required_tags: { type: "array", items: { type: "string" }, description: "Snake_case alias for requiredTags." },
1118
+ target_node_id: { type: "string", description: "Optional: only this node may claim the task. Use to route a queued task to a specific (e.g. freshly cloned) worktree node instead of letting the first idle base node claim it. Takes priority over prefer_worktree." },
1119
+ targetNodeId: { type: "string", description: "CamelCase alias for target_node_id." },
1120
+ prefer_worktree: { type: "boolean", description: "Optional: when true, route this task to the most recently cloned idle worktree node (avoids the main/base workspace preemptively claiming an isolated task). No-op if no worktree node exists; resolves to a target_node_id when one does." },
1121
+ preferWorktree: { type: "boolean", description: "CamelCase alias for prefer_worktree." },
1122
+ depends_on: { type: "array", items: { type: "string" }, description: "Task ids that must complete before this task becomes claimable. Cycles are rejected at enqueue." },
1123
+ dependsOn: { type: "array", items: { type: "string" }, description: "CamelCase alias for depends_on." },
1124
+ mission_id: { type: "string", description: "Mission this task belongs to (mesh_mission record id)." },
1125
+ missionId: { type: "string", description: "CamelCase alias for mission_id." }
1126
+ },
1127
+ required: ["message"]
1880
1128
  }
1881
- return slim;
1882
- }
1883
- function readRelatedRepos(node) {
1884
- const raw = Array.isArray(node.relatedRepos) ? node.relatedRepos : Array.isArray(node.policy?.relatedRepos) ? node.policy.relatedRepos : [];
1885
- return raw.map((entry) => ({
1886
- label: typeof entry?.label === "string" ? entry.label.trim() : "",
1887
- workspace: typeof entry?.workspace === "string" ? entry.workspace.trim() : ""
1888
- })).filter((entry) => Boolean(entry.label && entry.workspace));
1889
- }
1890
- function summarizeRelatedRepoStatus(repo, status) {
1891
- const dirty = isGitStatusDirty(status);
1892
- return {
1893
- label: repo.label,
1894
- workspace: repo.workspace,
1895
- isGitRepo: status?.isGitRepo === true,
1896
- branch: status?.branch ?? null,
1897
- upstream: status?.upstream ?? null,
1898
- upstreamStatus: typeof status?.upstreamStatus === "string" ? status.upstreamStatus : status?.upstream ? "unchecked" : "no_upstream",
1899
- upstreamFetchedAt: Number.isFinite(Number(status?.upstreamFetchedAt)) ? Number(status.upstreamFetchedAt) : null,
1900
- upstreamFetchError: typeof status?.upstreamFetchError === "string" ? status.upstreamFetchError : null,
1901
- ahead: Number.isFinite(Number(status?.ahead)) ? Number(status.ahead) : 0,
1902
- behind: Number.isFinite(Number(status?.behind)) ? Number(status.behind) : 0,
1903
- dirty,
1904
- uncommittedChanges: countUncommittedChanges(status),
1905
- head: status?.headCommit ?? null,
1906
- lastCommitSummary: status?.headMessage ?? null,
1907
- ...status?.reason ? { reason: status.reason } : {},
1908
- ...status?.error ? { error: status.error } : {}
1909
- };
1910
- }
1911
- async function collectRelatedRepoStatuses(ctx, node) {
1912
- const relatedRepos = readRelatedRepos(node);
1913
- if (!relatedRepos.length) return [];
1914
- const results = [];
1915
- for (const repo of relatedRepos) {
1916
- try {
1917
- const statusResult = await commandForNode(ctx, node, "git_status", { workspace: repo.workspace, refreshUpstream: true });
1918
- const status = extractGitStatus(statusResult);
1919
- results.push(summarizeRelatedRepoStatus(repo, status));
1920
- } catch (e) {
1921
- results.push({
1922
- label: repo.label,
1923
- workspace: repo.workspace,
1924
- error: e?.message || "related repo status failed"
1925
- });
1129
+ };
1130
+ var MESH_VIEW_QUEUE_TOOL = {
1131
+ name: "mesh_view_queue",
1132
+ description: "View the mesh work queue with source-of-truth active counts separated from historical completed/failed/cancelled records. Do not repeatedly call this to wait for generating assigned work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.",
1133
+ inputSchema: {
1134
+ type: "object",
1135
+ properties: {
1136
+ status: {
1137
+ type: "array",
1138
+ items: { type: "string" },
1139
+ description: "Explicit row filter by task status: pending, assigned, completed, failed, cancelled. Source-of-truth counts remain unfiltered; visible* counts describe returned rows."
1140
+ },
1141
+ view: {
1142
+ type: "string",
1143
+ enum: ["all", "active", "historical"],
1144
+ description: "Optional row view. active returns pending/assigned rows, historical returns completed/failed/cancelled rows, all returns every persisted queue row. Defaults to all for compatibility."
1145
+ },
1146
+ compact: { type: "boolean", description: "Slim payload for LLM callers. Default true. Drops large historical (completed/failed/cancelled) queue row arrays, the full staleDirectWork orphan array (kept as staleDirectWorkSummary counts), and per-row maintenance cleanupCandidates in favor of counts; pending/assigned active rows are retained. Set false (or verbose=true) for the full dashboard-grade payload." },
1147
+ verbose: { type: "boolean", description: "Force the full payload; overrides compact." }
1926
1148
  }
1927
1149
  }
1928
- return results;
1929
- }
1930
- function readProviderPriority(policy) {
1931
- const raw = policy?.providerPriority;
1932
- return Array.isArray(raw) ? raw.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean) : [];
1933
- }
1934
- function buildNodeCapabilityExposure(node) {
1935
- const providers = readProviderPriority(node.policy);
1936
- const capabilityTags = (0, import_daemon_core.buildMeshNodeCapabilityTags)(node);
1937
- const exposure = { capabilityTags };
1938
- if (providers.length) {
1939
- const byProvider = {};
1940
- for (const provider of providers) {
1941
- byProvider[provider] = (0, import_daemon_core.buildMeshNodeCapabilityTags)(node, provider);
1942
- }
1943
- exposure.capabilityTagsByProvider = byProvider;
1150
+ };
1151
+ var MESH_QUEUE_CANCEL_TOOL = {
1152
+ name: "mesh_queue_cancel",
1153
+ description: "Cancel a pending/assigned/completed/failed mesh queue task without deleting audit history. Use this to retire stale queue items that target dead sessions.",
1154
+ inputSchema: {
1155
+ type: "object",
1156
+ properties: {
1157
+ task_id: { type: "string", description: "Queue task ID to cancel." },
1158
+ reason: { type: "string", description: "Optional operator-visible reason for cancellation." }
1159
+ },
1160
+ required: ["task_id"]
1944
1161
  }
1945
- const capabilities = Array.isArray(node.capabilities) ? node.capabilities.filter((tag) => typeof tag === "string" && !!tag.trim()) : [];
1946
- if (capabilities.length) exposure.capabilities = capabilities;
1947
- return exposure;
1948
- }
1949
- function readSpawnedSessionVisibility(policy) {
1950
- return policy?.spawnedSessionVisibility === "hidden" ? "hidden" : "visible";
1951
- }
1952
- function missingProviderPriorityMessage(nodeId) {
1953
- return `Node '${nodeId}' has no providerPriority policy; pass type explicitly or configure node.policy.providerPriority`;
1954
- }
1955
- function getNodeLaunchReadiness(node) {
1956
- const bootstrap = node.worktreeBootstrap;
1957
- if (node.isLocalWorktree && bootstrap?.status === "failed" && bootstrap?.required !== false) {
1958
- return {
1959
- providerPriority: readProviderPriority(node.policy),
1960
- launchReady: false,
1961
- launchBlockedReason: "worktree_bootstrap_failed",
1962
- launchBlockedMessage: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : "Required worktree bootstrap failed; resolve it before launching an agent into this node.",
1963
- worktreeBootstrap: bootstrap
1964
- };
1162
+ };
1163
+ var MESH_QUEUE_REQUEUE_TOOL = {
1164
+ name: "mesh_queue_requeue",
1165
+ description: "Return a mesh queue task to pending for retry. By default clears stale assigned owner and target session so another live session can claim it. When the task has exceeded its retry cap it is auto-failed instead; use force=true to override.",
1166
+ inputSchema: {
1167
+ type: "object",
1168
+ properties: {
1169
+ task_id: { type: "string", description: "Queue task ID to requeue." },
1170
+ reason: { type: "string", description: "Optional operator-visible reason for requeueing." },
1171
+ target_node_id: { type: "string", description: "Optional replacement target node ID." },
1172
+ target_session_id: { type: "string", description: "Optional replacement target runtime session ID." },
1173
+ clear_target_node: { type: "boolean", description: "When true, remove any existing target node constraint." },
1174
+ keep_target_session: { type: "boolean", description: "When true, preserve an existing target session if target_session_id is not provided. Defaults false to avoid stale session targets." },
1175
+ force: { type: "boolean", description: "When true, bypass the retry cap and requeue even if maxRetries has been exceeded. Use only for explicit operator recovery." }
1176
+ },
1177
+ required: ["task_id"]
1965
1178
  }
1966
- const providerPriority = readProviderPriority(node.policy);
1967
- if (providerPriority.length) {
1968
- return {
1969
- providerPriority,
1970
- launchReady: true
1971
- };
1972
- }
1973
- return {
1974
- providerPriority,
1975
- launchReady: false,
1976
- launchBlockedReason: "missing_provider_priority",
1977
- launchBlockedMessage: missingProviderPriorityMessage(node.id)
1978
- };
1979
- }
1980
- function getWorktreeBootstrapLaunchBlock(node, meshPolicy) {
1981
- if (!node.isLocalWorktree) return void 0;
1982
- const bootstrap = node.worktreeBootstrap;
1983
- const requireReady = !!(meshPolicy && typeof meshPolicy === "object" && meshPolicy.requireBootstrapBeforeLaunch === true);
1984
- if (requireReady && bootstrap?.status !== "ready") {
1985
- return {
1986
- success: false,
1987
- code: "bootstrap_not_ready",
1988
- error: `Node '${node.id}' bootstrap state is '${bootstrap?.status ?? "unknown"}' and mesh policy requireBootstrapBeforeLaunch is enabled.`,
1989
- nodeId: node.id,
1990
- worktreeBootstrap: bootstrap ?? null,
1991
- recoveryHint: "Run the worktree bootstrap (clone runOnClone or a refine with bootstrap inherit) until the node reports ready, or disable requireBootstrapBeforeLaunch."
1992
- };
1179
+ };
1180
+ var MESH_SEND_TASK_TOOL = {
1181
+ name: "mesh_send_task",
1182
+ description: "Legacy push-based task assignment. Enqueues a task specifically targeted at a given node. The node will pull it immediately if idle.",
1183
+ inputSchema: {
1184
+ type: "object",
1185
+ properties: {
1186
+ node_id: { type: "string", description: "Target node ID (from mesh_list_nodes)." },
1187
+ session_id: { type: "string", description: "Agent session ID on the target node." },
1188
+ message: { type: "string", description: "Natural-language task to send to the agent." },
1189
+ task_mode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "Optional task-mode contract. live_debug_readonly rejects obvious write/commit/push/deploy/destructive instructions before local or remote direct dispatch." },
1190
+ taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." },
1191
+ mission_id: { type: "string", description: "Mission this task belongs to (mesh_mission record id). When set, the directly dispatched task is attributed to the mission task aggregates exactly like mesh_enqueue_task, including terminal completion. Omit for an unattributed direct dispatch." },
1192
+ missionId: { type: "string", description: "CamelCase alias for mission_id." }
1193
+ },
1194
+ required: ["node_id", "session_id", "message"]
1993
1195
  }
1994
- if (bootstrap?.status !== "failed" || bootstrap?.required === false) return void 0;
1995
- return {
1996
- success: false,
1997
- code: "worktree_bootstrap_failed",
1998
- error: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : `Node '${node.id}' has a failed required worktree bootstrap.`,
1999
- nodeId: node.id,
2000
- worktreeBootstrap: bootstrap,
2001
- recoveryHint: "Fix the configured worktree bootstrap command or remove/recreate the worktree node before launching an agent."
2002
- };
2003
- }
2004
- async function collectLiveStatusSessions(ctx, node) {
2005
- try {
2006
- const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2007
- return extractStatusMetadataSessions(statusResult);
2008
- } catch {
2009
- return [];
1196
+ };
1197
+ var MESH_READ_CHAT_TOOL = {
1198
+ name: "mesh_read_chat",
1199
+ description: "Read recent chat messages from a delegated agent session on a mesh node. Use compact=true for coordinator context-efficient review: it filters tool/internal/debug chatter and returns the final user-visible summary plus recent key messages. If the runtime session has completed, provider_session_id can explicitly target provider transcript history.",
1200
+ inputSchema: {
1201
+ type: "object",
1202
+ properties: {
1203
+ node_id: { type: "string", description: "Target node ID." },
1204
+ session_id: { type: "string", description: "Agent session ID to read from." },
1205
+ provider_session_id: { type: "string", description: "Optional provider transcript/session ID for completed sessions." },
1206
+ tail: { type: "number", description: "Number of recent messages to return (default: 10)." },
1207
+ compact: { type: "boolean", description: "When true, return a compact coordinator summary instead of the full transcript: tool/internal/control/debug messages are excluded and only recent user-visible key messages plus the final assistant summary are included." }
1208
+ },
1209
+ required: ["node_id", "session_id"]
2010
1210
  }
2011
- }
2012
- async function collectLiveStatusProbe(ctx, node) {
2013
- try {
2014
- const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2015
- return {
2016
- sessions: extractStatusMetadataSessions(statusResult),
2017
- daemonBuild: extractDaemonBuildInfo(statusResult)
2018
- };
2019
- } catch {
2020
- return { sessions: [] };
1211
+ };
1212
+ var MESH_READ_DEBUG_TOOL = {
1213
+ name: "mesh_read_debug",
1214
+ description: "Collect a daemon-side chat/parser debug bundle for a delegated agent session on a mesh node without opening the browser UI. Defaults to daemon_file delivery and returns a saved bundle locator.",
1215
+ inputSchema: {
1216
+ type: "object",
1217
+ properties: {
1218
+ node_id: { type: "string", description: "Target node ID." },
1219
+ session_id: { type: "string", description: "Agent session ID to debug." },
1220
+ provider_session_id: { type: "string", description: "Optional provider transcript/session ID for completed session history." },
1221
+ tail: { type: "number", description: "Number of recent read_chat messages to embed (default: 40)." },
1222
+ delivery: { type: "string", enum: ["daemon_file", "inline"], description: "daemon_file saves the full sanitized bundle on the daemon; inline returns it directly. Default: daemon_file." }
1223
+ },
1224
+ required: ["node_id", "session_id"]
2021
1225
  }
2022
- }
2023
- function extractDaemonBuildInfo(value) {
2024
- const payload = unwrapCommandPayload(value);
2025
- const build = payload?.daemonBuild && typeof payload.daemonBuild === "object" ? payload.daemonBuild : value?.daemonBuild && typeof value.daemonBuild === "object" ? value.daemonBuild : void 0;
2026
- if (!build) return void 0;
2027
- const commit = readString(build.commit);
2028
- if (!commit) return void 0;
2029
- return {
2030
- commit,
2031
- commitShort: readString(build.commitShort) || commit.slice(0, 7),
2032
- version: readString(build.version) || "unknown",
2033
- ...readString(build.builtAt) ? { builtAt: readString(build.builtAt) } : {}
2034
- };
2035
- }
2036
- async function collectMeshViewQueueNodesWithLiveSessions(ctx) {
2037
- const nodes = await Promise.all(ctx.mesh.nodes.map(async (node) => {
2038
- const liveSessions = await collectLiveStatusSessions(ctx, node);
2039
- return liveSessions.length > 0 ? { ...node, sessions: liveSessions } : node;
2040
- }));
2041
- return nodes;
2042
- }
2043
- function readNumeric(value, fallback = 0) {
2044
- const parsed = Number(value);
2045
- return Number.isFinite(parsed) ? parsed : fallback;
2046
- }
2047
- function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
2048
- const defaultBranch = readString(mesh.defaultBranch) ?? "main";
2049
- const branch = readString(status?.branch) ?? readString(node.worktreeBranch) ?? null;
2050
- const ahead = readNumeric(status?.ahead);
2051
- const behind = readNumeric(status?.behind);
2052
- const upstream = readString(status?.upstream) ?? null;
2053
- const upstreamStatus = readString(status?.upstreamStatus) ?? (upstream ? "unchecked" : "no_upstream");
2054
- const hasConflicts = status?.hasConflicts === true || Array.isArray(status?.conflictFiles) && status.conflictFiles.length > 0;
2055
- const base = {
2056
- defaultBranch,
2057
- branch,
2058
- upstream,
2059
- upstreamStatus,
2060
- ahead,
2061
- behind,
2062
- isWorktree: node.isLocalWorktree === true,
2063
- isDefaultBranch: branch === defaultBranch
2064
- };
2065
- if (status?.isGitRepo !== true) {
2066
- return {
2067
- ...base,
2068
- status: "blocked_review",
2069
- needsConvergence: true,
2070
- reason: "git_status_unavailable",
2071
- nextStep: `Resolve git status for node '${node.id}' before marking the task complete.`
2072
- };
1226
+ };
1227
+ var MESH_LAUNCH_SESSION_TOOL = {
1228
+ name: "mesh_launch_session",
1229
+ description: "Launch a new agent session on a mesh node. Returns the session ID for subsequent send_task/read_chat calls. If the user names a provider, preserve it exactly: Hermes = hermes-cli, Claude Code/Claude = claude-cli, Codex = codex-cli, Gemini = gemini-cli. If type is omitted, resolve strictly from the node policy providerPriority and provider detection; fail closed when no configured provider is usable. Do not default to claude-cli.",
1230
+ inputSchema: {
1231
+ type: "object",
1232
+ properties: {
1233
+ node_id: { type: "string", description: "Target node ID." },
1234
+ type: { type: "string", description: "Optional provider type to launch. Use hermes-cli for Hermes, claude-cli for Claude Code, codex-cli for Codex, gemini-cli for Gemini. When omitted, node.policy.providerPriority is probed in order." }
1235
+ },
1236
+ required: ["node_id"]
2073
1237
  }
2074
- if (!branch) {
2075
- return {
2076
- ...base,
2077
- status: "blocked_review",
2078
- needsConvergence: true,
2079
- reason: "branch_unknown",
2080
- nextStep: `Inspect node '${node.id}' git branch before deciding whether it is merged to ${defaultBranch}.`
2081
- };
1238
+ };
1239
+ var MESH_GIT_STATUS_TOOL = {
1240
+ name: "mesh_git_status",
1241
+ description: "Get git status for a mesh node workspace \u2014 branch, dirty state, changed files.",
1242
+ inputSchema: {
1243
+ type: "object",
1244
+ properties: {
1245
+ node_id: { type: "string", description: "Target node ID." }
1246
+ },
1247
+ required: ["node_id"]
2082
1248
  }
2083
- if (hasConflicts || dirty || uncommittedChanges > 0) {
2084
- return {
2085
- ...base,
2086
- status: "not_mergeable",
2087
- needsConvergence: true,
2088
- reason: hasConflicts ? "conflicts_present" : "dirty_workspace",
2089
- nextStep: `Commit, checkpoint, or resolve node '${node.id}' before any main convergence step.`
2090
- };
1249
+ };
1250
+ var MESH_READ_NODE_LOGS_TOOL = {
1251
+ name: "mesh_read_node_logs",
1252
+ description: "Fetch a recent daemon LOG tail directly from a (possibly remote) mesh node over P2P \u2014 no session launch, no PowerShell/shell grep on the remote machine. Use this to debug a node's daemon: read its error/warn lines, grep for a pattern, or read since a timestamp. The reply is byte-bounded (\u2264128KB, default 64KB; truncated:true when the file was larger, newest lines kept) and secrets (API keys, machine secrets, bearer tokens, JWTs, TURN credentials) are redacted before transmission. This reads the DAEMON log, not an agent session transcript \u2014 for a session transcript use mesh_read_chat / mesh_read_debug.",
1253
+ inputSchema: {
1254
+ type: "object",
1255
+ properties: {
1256
+ node_id: { type: "string", description: "Target node ID (the daemon owning it serves its own log)." },
1257
+ grep: { type: "string", description: "Optional regex (case-insensitive) \u2014 only matching log lines are returned. Invalid regex falls back to a literal substring match." },
1258
+ since_ms: { type: "number", description: "Optional epoch-ms floor \u2014 only log lines at/after this time are returned (lines without a parseable timestamp are kept)." },
1259
+ tail_bytes: { type: "number", description: "Max bytes of log tail to read (default 65536, capped at 131072). Larger files are truncated to the newest tail_bytes." },
1260
+ date: { type: "string", description: "Optional YYYY-MM-DD log date (defaults to today). Falls back to the size-rotation backup when the active file is absent." }
1261
+ },
1262
+ required: ["node_id"]
2091
1263
  }
2092
- if (branch === defaultBranch) {
2093
- if (upstream && upstreamStatus !== "fresh") {
2094
- return {
2095
- ...base,
2096
- status: "blocked_review",
2097
- needsConvergence: true,
2098
- reason: "default_branch_upstream_unverified",
2099
- nextStep: `Refresh ${defaultBranch}'s upstream refs or resolve the fetch failure before declaring convergence complete for node '${node.id}'.`
2100
- };
2101
- }
2102
- if (ahead > 0 || behind > 0) {
2103
- return {
2104
- ...base,
2105
- status: "blocked_review",
2106
- needsConvergence: true,
2107
- reason: "default_branch_not_even_with_upstream",
2108
- nextStep: `Bring ${defaultBranch} even with its upstream before declaring convergence complete.`
2109
- };
2110
- }
2111
- return {
2112
- ...base,
2113
- status: "merged_to_main",
2114
- needsConvergence: false,
2115
- reason: "clean_default_branch",
2116
- nextStep: null
2117
- };
1264
+ };
1265
+ var MESH_FAST_FORWARD_NODE_TOOL = {
1266
+ name: "mesh_fast_forward_node",
1267
+ description: 'Safely dry-run or execute an obvious direct fast-forward for a mesh node without launching an agent session. mode="merge" (default) absorbs upstream commits into the local branch via git merge --ff-only (ahead=0, behind>0). mode="push" publishes local commits to origin via a strict ff-only push (HEAD must be a descendant of origin/<branch>). Defaults to dry-run; execution requires execute=true. Never force-pushes, rebases, resets, cleans, or checks out arbitrary revisions. When the merge path finds the branch ahead with nothing to merge, it returns code "ahead_needs_push" pointing at mode="push".',
1268
+ inputSchema: {
1269
+ type: "object",
1270
+ properties: {
1271
+ node_id: { type: "string", description: "Target node ID." },
1272
+ mode: { type: "string", enum: ["merge", "push"], description: "merge (default): git merge --ff-only to absorb upstream. push: strict ff-only push of local commits to origin/<branch>; refuses any non-fast-forward." },
1273
+ branch: { type: "string", description: "Optional guard: require the node's current branch to match this branch before planning/executing." },
1274
+ execute: { type: "boolean", description: "When true, apply the fast-forward/push if all safety gates pass. Defaults false/dry-run." },
1275
+ dry_run: { type: "boolean", description: "Preview only. Defaults true unless execute=true; dry_run=true overrides execute." },
1276
+ update_submodules: { type: "boolean", description: 'mode="merge" only: when true, if the root fast-forward changes gitlinks, run only git submodule update --init --recursive and verify submodules clean.' },
1277
+ push_submodules: { type: "boolean", description: 'mode="push" only: also ff-only push submodule HEADs to their origin main. Gated by mesh policy allowAutoPublishSubmoduleMainCommits \u2014 skipped unless that policy is enabled. Defaults false (root push only).' }
1278
+ },
1279
+ required: ["node_id"]
2118
1280
  }
2119
- if (node.isLocalWorktree) {
2120
- return {
2121
- ...base,
2122
- status: "cleanup_candidate",
2123
- needsConvergence: true,
2124
- reason: "clean_non_default_worktree_branch",
2125
- nextStep: `Run mesh_refine_node(node_id: "${node.id}") or explicitly classify this worktree as blocked_review/not_mergeable before ending the task.`
2126
- };
2127
- }
2128
- if (upstream && upstreamStatus !== "fresh") {
2129
- return {
2130
- ...base,
2131
- status: "blocked_review",
2132
- needsConvergence: true,
2133
- reason: "feature_branch_upstream_unverified",
2134
- nextStep: `Refresh branch '${branch}' upstream refs or resolve the fetch failure before deciding whether it is ready to merge into ${defaultBranch}.`
2135
- };
2136
- }
2137
- if (!upstream || ahead > 0 || behind > 0) {
2138
- return {
2139
- ...base,
2140
- status: "blocked_review",
2141
- needsConvergence: true,
2142
- reason: !upstream ? "feature_branch_missing_upstream" : "feature_branch_not_even_with_upstream",
2143
- nextStep: `Push or reconcile branch '${branch}', then merge it into ${defaultBranch} or mark it not_mergeable with a reason.`
2144
- };
2145
- }
2146
- return {
2147
- ...base,
2148
- status: "pushed_feature_branch_needs_merge",
2149
- needsConvergence: true,
2150
- reason: "clean_non_default_branch",
2151
- nextStep: `Review and merge branch '${branch}' into ${defaultBranch}; do not report the task as fully complete while it remains off main.`
2152
- };
2153
- }
2154
- var COMPACT_MAX_CONVERGENCE_FOLLOWUPS = 12;
2155
- function summarizeBranchConvergence(nodes, compact = false) {
2156
- const allFollowUps = nodes.filter((node) => node?.branchConvergence?.needsConvergence === true).map((node) => ({
2157
- nodeId: node.nodeId,
2158
- // workspace is a long absolute path redundant with nodeId — drop it in
2159
- // compact mode to keep this summary bounded.
2160
- ...compact ? {} : { workspace: node.workspace },
2161
- branch: node.branchConvergence.branch,
2162
- status: node.branchConvergence.status,
2163
- reason: node.branchConvergence.reason,
2164
- // The per-node nextStep is long prose that repeats node ids/branch names.
2165
- // In compact mode drop it (the status+reason carry the actionable signal;
2166
- // verbose still surfaces the full nextStep) so this summary stays bounded
2167
- // as node count grows.
2168
- ...compact ? {} : { nextStep: node.branchConvergence.nextStep }
2169
- }));
2170
- const byStatus = {};
2171
- for (const f of allFollowUps) {
2172
- const s = typeof f.status === "string" ? f.status : "unknown";
2173
- byStatus[s] = (byStatus[s] ?? 0) + 1;
2174
- }
2175
- const followUps = compact ? allFollowUps.slice(0, COMPACT_MAX_CONVERGENCE_FOLLOWUPS) : allFollowUps;
2176
- const omitted = allFollowUps.length - followUps.length;
2177
- return {
2178
- needsFollowUp: allFollowUps.length > 0,
2179
- unresolvedCount: allFollowUps.length,
2180
- byStatus,
2181
- requiredFinalStates: ["merged_to_main", "pushed_feature_branch_needs_merge", "blocked_review", "cleanup_candidate", "not_mergeable"],
2182
- followUps,
2183
- ...omitted > 0 ? { followUpsOmitted: omitted, followUpsHint: "Per-node followUp rows are capped in compact mode; counts above are complete. Use verbose=true for the full list." } : {}
2184
- };
2185
- }
2186
- async function commandForNode(ctx, node, command, args = {}) {
2187
- const isLocalNode = isLocalControlPlaneNode(ctx, node);
2188
- if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
2189
- return ctx.transport.meshCommand(node.daemonId, command, args);
2190
- }
2191
- return ctx.transport.command(command, args);
2192
- }
2193
- function normalizePendingMeshCoordinatorEvents(value) {
2194
- const payload = unwrapCommandPayload(value);
2195
- const events = Array.isArray(payload?.events) ? payload.events : Array.isArray(value?.events) ? value.events : [];
2196
- return events.filter((event) => event && typeof event === "object");
2197
- }
2198
- function buildMeshForwardPayloadFromPendingEvent(event) {
2199
- const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : {};
2200
- return {
2201
- event: readString(event?.event),
2202
- meshId: readString(event?.meshId),
2203
- nodeId: readString(event?.nodeId) || readString(metadataEvent.meshNodeId),
2204
- workspace: readString(event?.workspace) || readString(metadataEvent.workspace),
2205
- targetSessionId: readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId),
2206
- providerType: readString(metadataEvent.providerType),
2207
- providerSessionId: readString(metadataEvent.providerSessionId),
2208
- finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
2209
- jobId: readString(metadataEvent.jobId),
2210
- interactionId: readString(metadataEvent.interactionId),
2211
- status: readString(metadataEvent.status),
2212
- targetDaemonId: readString(metadataEvent.targetDaemonId),
2213
- startedAt: readString(metadataEvent.startedAt),
2214
- completedAt: readString(metadataEvent.completedAt),
2215
- retryOfJobId: readString(metadataEvent.retryOfJobId),
2216
- ...metadataEvent.result && typeof metadataEvent.result === "object" && !Array.isArray(metadataEvent.result) ? { result: metadataEvent.result } : {},
2217
- ...metadataEvent.intentional === true ? { intentional: true } : {},
2218
- ...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
2219
- ...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
2220
- ...readString(metadataEvent.reason) ? { reason: readString(metadataEvent.reason) } : {},
2221
- ...readString(metadataEvent.stopReason) ? { stopReason: readString(metadataEvent.stopReason) } : {},
2222
- ...readString(metadataEvent.cleanupReason) ? { cleanupReason: readString(metadataEvent.cleanupReason) } : {},
2223
- ...readString(metadataEvent.source) ? { source: readString(metadataEvent.source) } : {}
2224
- };
2225
- }
2226
- async function drainCoordinatorPendingEvents(ctx, opts) {
2227
- const requestedNodeIds = opts?.nodeIds?.length ? new Set(opts.nodeIds) : null;
2228
- const matchesCurrentMesh = (event) => readString(event?.meshId) === ctx.mesh.id;
2229
- if (ctx.transport instanceof IpcTransport) {
2230
- const surfacedEvents = [];
2231
- const coordinatorDaemonId = readString(ctx.localDaemonId);
2232
- const pendingEventArgs = {
2233
- meshId: ctx.mesh.id,
2234
- ...coordinatorDaemonId ? { coordinatorDaemonId } : {}
2235
- };
2236
- try {
2237
- const localEvents = normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", pendingEventArgs)).filter(matchesCurrentMesh);
2238
- for (const event of localEvents) {
2239
- const payload = buildMeshForwardPayloadFromPendingEvent(event);
2240
- if (!payload.event || !payload.meshId) continue;
2241
- let injected = false;
2242
- try {
2243
- await ctx.transport.command("mesh_forward_event", payload);
2244
- injected = true;
2245
- } catch {
2246
- }
2247
- rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
2248
- if (!injected) surfacedEvents.push(event);
2249
- }
2250
- } catch {
2251
- }
2252
- for (const node of ctx.mesh.nodes) {
2253
- if (!node.daemonId || isLocalControlPlaneNode(ctx, node)) continue;
2254
- if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
2255
- try {
2256
- const remoteEvents = normalizePendingMeshCoordinatorEvents(
2257
- await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", pendingEventArgs)
2258
- ).filter(matchesCurrentMesh);
2259
- if (remoteEvents.length === 0) continue;
2260
- for (const event of remoteEvents) {
2261
- const payload = buildMeshForwardPayloadFromPendingEvent(event);
2262
- if (!payload.event || !payload.meshId) continue;
2263
- await ctx.transport.command("mesh_forward_event", payload);
2264
- rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
2265
- }
2266
- } catch {
2267
- }
2268
- }
2269
- try {
2270
- const localEvents = normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", pendingEventArgs)).filter(matchesCurrentMesh);
2271
- for (const event of localEvents) {
2272
- const payload = buildMeshForwardPayloadFromPendingEvent(event);
2273
- if (!payload.event || !payload.meshId) continue;
2274
- let injected = false;
2275
- try {
2276
- await ctx.transport.command("mesh_forward_event", payload);
2277
- injected = true;
2278
- } catch {
2279
- }
2280
- rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
2281
- if (!injected) surfacedEvents.push(event);
2282
- }
2283
- } catch {
2284
- }
2285
- return surfacedEvents;
2286
- }
2287
- const events = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId).filter(matchesCurrentMesh);
2288
- events.forEach(rememberMeshSessionProviderMetadataFromEvent);
2289
- return events;
2290
- }
2291
- function isP2pTransportUnavailableError(error) {
2292
- return (0, import_daemon_core.isP2pRelayTransportFailure)(error);
2293
- }
2294
- function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode, force) {
2295
- return {
2296
- meshId: ctx.mesh.id,
2297
- nodeId,
2298
- ...sessionCleanupMode ? { sessionCleanupMode } : {},
2299
- ...force === true ? { force: true } : {},
2300
- inlineMesh: ctx.mesh
2301
- };
2302
- }
2303
- var MESH_STATUS_TOOL = {
2304
- name: "mesh_status",
2305
- description: "Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures. Also reports the running daemon build per daemonId under top-level daemonBuilds ({commit, commitShort, version}); when a live daemon was built from a commit BEHIND its workspace HEAD it adds staleDaemonBuilds[] + staleDaemonBuildWarning \u2014 meaning a just-merged refinery/mesh-tool fix is NOT yet live on that daemon (awaiting deploy/restart; a local dist rebuild does not update a cloud daemon). Do not repeatedly call this to wait for generating delegated work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.",
2306
- inputSchema: {
2307
- type: "object",
2308
- properties: {
2309
- _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." },
2310
- includeStaleDirectWorkDetails: { type: "boolean", description: "Opt in to the full staleDirectWork array. Defaults false; normal status returns compact staleDirectWorkSummary only." },
2311
- includeSessions: { type: "boolean", description: "Opt in to per-node live session arrays. Default false: compact mode returns a per-node sessionSummary (counts) and de-duplicated full session lists under top-level daemonSessions keyed by daemonId (sessions are not repeated for every node that shares a daemon). Set true to also include the full session array on each node." },
2312
- compact: { type: "boolean", description: "Slim payload for LLM callers. Default true. Folds per-node session arrays to sessionSummary and de-duplicates daemon-shared sessions into daemonSessions. Set false (or verbose=true) for the full dashboard-grade payload." },
2313
- verbose: { type: "boolean", description: "Force the full payload; overrides compact." }
2314
- }
1281
+ };
1282
+ var MESH_RESTART_DAEMON_TOOL = {
1283
+ name: "mesh_restart_daemon",
1284
+ description: `Update a mesh node's daemon to the latest published version on its release channel and restart it \u2014 the same path as the dashboard "preview update" button, exposed as a mesh command so a coordinator can roll a worker daemon onto a freshly deployed version without a manual restart round-trip. No agent session is launched. Idle-gated: a node whose daemon has an active session (generating / waiting_approval / starting) is refused with code "blocking_sessions" so an in-flight turn is never interrupted. If the node is already on the latest version it is a no-op (no restart), matching the dashboard button (returns alreadyLatest:true). Targets a single node \u2014 call other (idle) nodes first; restarting the coordinator's OWN daemon is naturally refused while its calling turn is active. Passing channel switches the daemon's release channel (and server URL) before restarting; omit it to keep the daemon on its configured channel.`,
1285
+ inputSchema: {
1286
+ type: "object",
1287
+ properties: {
1288
+ node_id: { type: "string", description: "Target node ID \u2014 the daemon that owns this node is updated and restarted." },
1289
+ channel: { type: "string", enum: ["stable", "preview"], description: "Optional release channel to update from. Defaults to the daemon's configured updateChannel. Setting it also repoints the daemon's server URL to that channel." }
1290
+ },
1291
+ required: ["node_id"]
2315
1292
  }
2316
1293
  };
2317
- var MESH_LIST_NODES_TOOL = {
2318
- name: "mesh_list_nodes",
2319
- description: "List all nodes in the mesh with their capabilities, platform, and workspace paths.",
1294
+ var MESH_CHECKPOINT_TOOL = {
1295
+ name: "mesh_checkpoint",
1296
+ description: "Create a git checkpoint (commit) on a mesh node workspace.",
2320
1297
  inputSchema: {
2321
1298
  type: "object",
2322
1299
  properties: {
2323
- _gemini_compat: { type: "string", description: "Dummy property for Gemini compatibility. Ignore this." }
2324
- }
1300
+ node_id: { type: "string", description: "Target node ID." },
1301
+ message: { type: "string", description: "Checkpoint commit message." }
1302
+ },
1303
+ required: ["node_id", "message"]
2325
1304
  }
2326
1305
  };
2327
- var MESH_ENQUEUE_TASK_TOOL = {
2328
- name: "mesh_enqueue_task",
2329
- description: "Add a new task to the mesh work queue. Idle nodes will automatically pull and execute tasks from this queue. Use this instead of mesh_send_task when you do not need to target a specific node.",
1306
+ var MESH_MISSION_UPSERT_TOOL = {
1307
+ name: "mesh_mission_upsert",
1308
+ description: "Create or update a persistent mission record so the plan survives coordinator restarts. Create a mission before enqueueing a multi-task batch, attach tasks via mesh_enqueue_task mission_id, and update status to completed/abandoned when the outcome is decided. Progress is derived from task statuses \u2014 there is no separate progress field.",
2330
1309
  inputSchema: {
2331
1310
  type: "object",
2332
1311
  properties: {
2333
- message: { type: "string", description: "The task instruction for the agent." },
2334
- task_mode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "Optional task-mode contract. live_debug_readonly rejects obvious write/commit/push/deploy/destructive instructions before dispatch." },
2335
- taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." },
2336
- requiredTags: { type: "array", items: { type: "string" }, description: "Optional capability tags that every eligible node must have, e.g. os=darwin, provider=codex-cli, gpu." },
2337
- required_tags: { type: "array", items: { type: "string" }, description: "Snake_case alias for requiredTags." },
2338
- target_node_id: { type: "string", description: "Optional: only this node may claim the task. Use to route a queued task to a specific (e.g. freshly cloned) worktree node instead of letting the first idle base node claim it. Takes priority over prefer_worktree." },
2339
- targetNodeId: { type: "string", description: "CamelCase alias for target_node_id." },
2340
- prefer_worktree: { type: "boolean", description: "Optional: when true, route this task to the most recently cloned idle worktree node (avoids the main/base workspace preemptively claiming an isolated task). No-op if no worktree node exists; resolves to a target_node_id when one does." },
2341
- preferWorktree: { type: "boolean", description: "CamelCase alias for prefer_worktree." },
2342
- depends_on: { type: "array", items: { type: "string" }, description: "Task ids that must complete before this task becomes claimable. Cycles are rejected at enqueue." },
2343
- dependsOn: { type: "array", items: { type: "string" }, description: "CamelCase alias for depends_on." },
2344
- mission_id: { type: "string", description: "Mission this task belongs to (mesh_mission record id)." },
2345
- missionId: { type: "string", description: "CamelCase alias for mission_id." }
1312
+ mission_id: { type: "string", description: "Mission id to update. Omit to create a new mission." },
1313
+ title: { type: "string", description: "Short mission title." },
1314
+ goal: { type: "string", description: "Free-text mission goal/definition of done." },
1315
+ status: { type: "string", enum: ["active", "paused", "completed", "abandoned"], description: "Mission lifecycle status. Defaults to active on create." }
2346
1316
  },
2347
- required: ["message"]
1317
+ required: ["title"]
2348
1318
  }
2349
1319
  };
2350
- var MESH_VIEW_QUEUE_TOOL = {
2351
- name: "mesh_view_queue",
2352
- description: "View the mesh work queue with source-of-truth active counts separated from historical completed/failed/cancelled records. Do not repeatedly call this to wait for generating assigned work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.",
1320
+ var MESH_MISSION_LIST_TOOL = {
1321
+ name: "mesh_mission_list",
1322
+ description: 'List missions with their goal, status, and live task progress (total/pending/assigned/completed/failed). Unlike mesh_status (which surfaces live + recent missions), this returns every mission regardless of status by default, so paused/abandoned/completed missions are never hidden. Filter with `status` to scope (e.g. ["paused"] to find paused missions). Compact (default) elides the full goal to a capped preview; pass verbose=true for full goal text. Read-only.',
2353
1323
  inputSchema: {
2354
1324
  type: "object",
2355
1325
  properties: {
2356
1326
  status: {
2357
1327
  type: "array",
2358
- items: { type: "string" },
2359
- description: "Explicit row filter by task status: pending, assigned, completed, failed, cancelled. Source-of-truth counts remain unfiltered; visible* counts describe returned rows."
2360
- },
2361
- view: {
2362
- type: "string",
2363
- enum: ["all", "active", "historical"],
2364
- description: "Optional row view. active returns pending/assigned rows, historical returns completed/failed/cancelled rows, all returns every persisted queue row. Defaults to all for compatibility."
1328
+ items: { type: "string", enum: ["active", "paused", "completed", "abandoned"] },
1329
+ description: "Optional status filter. Omit to return missions of every status."
2365
1330
  },
2366
- compact: { type: "boolean", description: "Slim payload for LLM callers. Default true. Drops large historical (completed/failed/cancelled) queue row arrays, the full staleDirectWork orphan array (kept as staleDirectWorkSummary counts), and per-row maintenance cleanupCandidates in favor of counts; pending/assigned active rows are retained. Set false (or verbose=true) for the full dashboard-grade payload." },
2367
- verbose: { type: "boolean", description: "Force the full payload; overrides compact." }
1331
+ verbose: { type: "boolean", description: "Return full goal text instead of a capped preview. Defaults to false (compact)." }
2368
1332
  }
2369
1333
  }
2370
1334
  };
2371
- var MESH_QUEUE_CANCEL_TOOL = {
2372
- name: "mesh_queue_cancel",
2373
- description: "Cancel a pending/assigned/completed/failed mesh queue task without deleting audit history. Use this to retire stale queue items that target dead sessions.",
1335
+ var MESH_APPROVE_TOOL = {
1336
+ name: "mesh_approve",
1337
+ description: "Approve or reject a pending action on a delegated agent session.",
2374
1338
  inputSchema: {
2375
1339
  type: "object",
2376
1340
  properties: {
2377
- task_id: { type: "string", description: "Queue task ID to cancel." },
2378
- reason: { type: "string", description: "Optional operator-visible reason for cancellation." }
1341
+ node_id: { type: "string", description: "Target node ID." },
1342
+ session_id: { type: "string", description: "Agent session ID with pending approval." },
1343
+ action: { type: "string", enum: ["approve", "reject"], description: "Action to take." }
2379
1344
  },
2380
- required: ["task_id"]
2381
- }
2382
- };
2383
- var MESH_QUEUE_REQUEUE_TOOL = {
2384
- name: "mesh_queue_requeue",
2385
- description: "Return a mesh queue task to pending for retry. By default clears stale assigned owner and target session so another live session can claim it. When the task has exceeded its retry cap it is auto-failed instead; use force=true to override.",
2386
- inputSchema: {
2387
- type: "object",
2388
- properties: {
2389
- task_id: { type: "string", description: "Queue task ID to requeue." },
2390
- reason: { type: "string", description: "Optional operator-visible reason for requeueing." },
2391
- target_node_id: { type: "string", description: "Optional replacement target node ID." },
2392
- target_session_id: { type: "string", description: "Optional replacement target runtime session ID." },
2393
- clear_target_node: { type: "boolean", description: "When true, remove any existing target node constraint." },
2394
- keep_target_session: { type: "boolean", description: "When true, preserve an existing target session if target_session_id is not provided. Defaults false to avoid stale session targets." },
2395
- force: { type: "boolean", description: "When true, bypass the retry cap and requeue even if maxRetries has been exceeded. Use only for explicit operator recovery." }
2396
- },
2397
- required: ["task_id"]
2398
- }
2399
- };
2400
- var MESH_SEND_TASK_TOOL = {
2401
- name: "mesh_send_task",
2402
- description: "Legacy push-based task assignment. Enqueues a task specifically targeted at a given node. The node will pull it immediately if idle.",
2403
- inputSchema: {
2404
- type: "object",
2405
- properties: {
2406
- node_id: { type: "string", description: "Target node ID (from mesh_list_nodes)." },
2407
- session_id: { type: "string", description: "Agent session ID on the target node." },
2408
- message: { type: "string", description: "Natural-language task to send to the agent." },
2409
- task_mode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "Optional task-mode contract. live_debug_readonly rejects obvious write/commit/push/deploy/destructive instructions before local or remote direct dispatch." },
2410
- taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." },
2411
- mission_id: { type: "string", description: "Mission this task belongs to (mesh_mission record id). When set, the directly dispatched task is attributed to the mission task aggregates exactly like mesh_enqueue_task, including terminal completion. Omit for an unattributed direct dispatch." },
2412
- missionId: { type: "string", description: "CamelCase alias for mission_id." }
2413
- },
2414
- required: ["node_id", "session_id", "message"]
2415
- }
2416
- };
2417
- var MESH_READ_CHAT_TOOL = {
2418
- name: "mesh_read_chat",
2419
- description: "Read recent chat messages from a delegated agent session on a mesh node. Use compact=true for coordinator context-efficient review: it filters tool/internal/debug chatter and returns the final user-visible summary plus recent key messages. If the runtime session has completed, provider_session_id can explicitly target provider transcript history.",
2420
- inputSchema: {
2421
- type: "object",
2422
- properties: {
2423
- node_id: { type: "string", description: "Target node ID." },
2424
- session_id: { type: "string", description: "Agent session ID to read from." },
2425
- provider_session_id: { type: "string", description: "Optional provider transcript/session ID for completed sessions." },
2426
- tail: { type: "number", description: "Number of recent messages to return (default: 10)." },
2427
- compact: { type: "boolean", description: "When true, return a compact coordinator summary instead of the full transcript: tool/internal/control/debug messages are excluded and only recent user-visible key messages plus the final assistant summary are included." }
2428
- },
2429
- required: ["node_id", "session_id"]
2430
- }
2431
- };
2432
- var MESH_READ_DEBUG_TOOL = {
2433
- name: "mesh_read_debug",
2434
- description: "Collect a daemon-side chat/parser debug bundle for a delegated agent session on a mesh node without opening the browser UI. Defaults to daemon_file delivery and returns a saved bundle locator.",
2435
- inputSchema: {
2436
- type: "object",
2437
- properties: {
2438
- node_id: { type: "string", description: "Target node ID." },
2439
- session_id: { type: "string", description: "Agent session ID to debug." },
2440
- provider_session_id: { type: "string", description: "Optional provider transcript/session ID for completed session history." },
2441
- tail: { type: "number", description: "Number of recent read_chat messages to embed (default: 40)." },
2442
- delivery: { type: "string", enum: ["daemon_file", "inline"], description: "daemon_file saves the full sanitized bundle on the daemon; inline returns it directly. Default: daemon_file." }
2443
- },
2444
- required: ["node_id", "session_id"]
2445
- }
2446
- };
2447
- var MESH_LAUNCH_SESSION_TOOL = {
2448
- name: "mesh_launch_session",
2449
- description: "Launch a new agent session on a mesh node. Returns the session ID for subsequent send_task/read_chat calls. If the user names a provider, preserve it exactly: Hermes = hermes-cli, Claude Code/Claude = claude-cli, Codex = codex-cli, Gemini = gemini-cli. If type is omitted, resolve strictly from the node policy providerPriority and provider detection; fail closed when no configured provider is usable. Do not default to claude-cli.",
2450
- inputSchema: {
2451
- type: "object",
2452
- properties: {
2453
- node_id: { type: "string", description: "Target node ID." },
2454
- type: { type: "string", description: "Optional provider type to launch. Use hermes-cli for Hermes, claude-cli for Claude Code, codex-cli for Codex, gemini-cli for Gemini. When omitted, node.policy.providerPriority is probed in order." }
2455
- },
2456
- required: ["node_id"]
2457
- }
2458
- };
2459
- var MESH_GIT_STATUS_TOOL = {
2460
- name: "mesh_git_status",
2461
- description: "Get git status for a mesh node workspace \u2014 branch, dirty state, changed files.",
2462
- inputSchema: {
2463
- type: "object",
2464
- properties: {
2465
- node_id: { type: "string", description: "Target node ID." }
2466
- },
2467
- required: ["node_id"]
2468
- }
2469
- };
2470
- var MESH_READ_NODE_LOGS_TOOL = {
2471
- name: "mesh_read_node_logs",
2472
- description: "Fetch a recent daemon LOG tail directly from a (possibly remote) mesh node over P2P \u2014 no session launch, no PowerShell/shell grep on the remote machine. Use this to debug a node's daemon: read its error/warn lines, grep for a pattern, or read since a timestamp. The reply is byte-bounded (\u2264128KB, default 64KB; truncated:true when the file was larger, newest lines kept) and secrets (API keys, machine secrets, bearer tokens, JWTs, TURN credentials) are redacted before transmission. This reads the DAEMON log, not an agent session transcript \u2014 for a session transcript use mesh_read_chat / mesh_read_debug.",
2473
- inputSchema: {
2474
- type: "object",
2475
- properties: {
2476
- node_id: { type: "string", description: "Target node ID (the daemon owning it serves its own log)." },
2477
- grep: { type: "string", description: "Optional regex (case-insensitive) \u2014 only matching log lines are returned. Invalid regex falls back to a literal substring match." },
2478
- since_ms: { type: "number", description: "Optional epoch-ms floor \u2014 only log lines at/after this time are returned (lines without a parseable timestamp are kept)." },
2479
- tail_bytes: { type: "number", description: "Max bytes of log tail to read (default 65536, capped at 131072). Larger files are truncated to the newest tail_bytes." },
2480
- date: { type: "string", description: "Optional YYYY-MM-DD log date (defaults to today). Falls back to the size-rotation backup when the active file is absent." }
2481
- },
2482
- required: ["node_id"]
2483
- }
2484
- };
2485
- var MESH_FAST_FORWARD_NODE_TOOL = {
2486
- name: "mesh_fast_forward_node",
2487
- description: 'Safely dry-run or execute an obvious direct fast-forward for a mesh node without launching an agent session. mode="merge" (default) absorbs upstream commits into the local branch via git merge --ff-only (ahead=0, behind>0). mode="push" publishes local commits to origin via a strict ff-only push (HEAD must be a descendant of origin/<branch>). Defaults to dry-run; execution requires execute=true. Never force-pushes, rebases, resets, cleans, or checks out arbitrary revisions. When the merge path finds the branch ahead with nothing to merge, it returns code "ahead_needs_push" pointing at mode="push".',
2488
- inputSchema: {
2489
- type: "object",
2490
- properties: {
2491
- node_id: { type: "string", description: "Target node ID." },
2492
- mode: { type: "string", enum: ["merge", "push"], description: "merge (default): git merge --ff-only to absorb upstream. push: strict ff-only push of local commits to origin/<branch>; refuses any non-fast-forward." },
2493
- branch: { type: "string", description: "Optional guard: require the node's current branch to match this branch before planning/executing." },
2494
- execute: { type: "boolean", description: "When true, apply the fast-forward/push if all safety gates pass. Defaults false/dry-run." },
2495
- dry_run: { type: "boolean", description: "Preview only. Defaults true unless execute=true; dry_run=true overrides execute." },
2496
- update_submodules: { type: "boolean", description: 'mode="merge" only: when true, if the root fast-forward changes gitlinks, run only git submodule update --init --recursive and verify submodules clean.' },
2497
- push_submodules: { type: "boolean", description: 'mode="push" only: also ff-only push submodule HEADs to their origin main. Gated by mesh policy allowAutoPublishSubmoduleMainCommits \u2014 skipped unless that policy is enabled. Defaults false (root push only).' }
2498
- },
2499
- required: ["node_id"]
2500
- }
2501
- };
2502
- var MESH_RESTART_DAEMON_TOOL = {
2503
- name: "mesh_restart_daemon",
2504
- description: `Update a mesh node's daemon to the latest published version on its release channel and restart it \u2014 the same path as the dashboard "preview update" button, exposed as a mesh command so a coordinator can roll a worker daemon onto a freshly deployed version without a manual restart round-trip. No agent session is launched. Idle-gated: a node whose daemon has an active session (generating / waiting_approval / starting) is refused with code "blocking_sessions" so an in-flight turn is never interrupted. If the node is already on the latest version it is a no-op (no restart), matching the dashboard button (returns alreadyLatest:true). Targets a single node \u2014 call other (idle) nodes first; restarting the coordinator's OWN daemon is naturally refused while its calling turn is active. Passing channel switches the daemon's release channel (and server URL) before restarting; omit it to keep the daemon on its configured channel.`,
2505
- inputSchema: {
2506
- type: "object",
2507
- properties: {
2508
- node_id: { type: "string", description: "Target node ID \u2014 the daemon that owns this node is updated and restarted." },
2509
- channel: { type: "string", enum: ["stable", "preview"], description: "Optional release channel to update from. Defaults to the daemon's configured updateChannel. Setting it also repoints the daemon's server URL to that channel." }
2510
- },
2511
- required: ["node_id"]
2512
- }
2513
- };
2514
- var MESH_CHECKPOINT_TOOL = {
2515
- name: "mesh_checkpoint",
2516
- description: "Create a git checkpoint (commit) on a mesh node workspace.",
2517
- inputSchema: {
2518
- type: "object",
2519
- properties: {
2520
- node_id: { type: "string", description: "Target node ID." },
2521
- message: { type: "string", description: "Checkpoint commit message." }
2522
- },
2523
- required: ["node_id", "message"]
2524
- }
2525
- };
2526
- var MESH_MISSION_UPSERT_TOOL = {
2527
- name: "mesh_mission_upsert",
2528
- description: "Create or update a persistent mission record so the plan survives coordinator restarts. Create a mission before enqueueing a multi-task batch, attach tasks via mesh_enqueue_task mission_id, and update status to completed/abandoned when the outcome is decided. Progress is derived from task statuses \u2014 there is no separate progress field.",
2529
- inputSchema: {
2530
- type: "object",
2531
- properties: {
2532
- mission_id: { type: "string", description: "Mission id to update. Omit to create a new mission." },
2533
- title: { type: "string", description: "Short mission title." },
2534
- goal: { type: "string", description: "Free-text mission goal/definition of done." },
2535
- status: { type: "string", enum: ["active", "paused", "completed", "abandoned"], description: "Mission lifecycle status. Defaults to active on create." }
2536
- },
2537
- required: ["title"]
2538
- }
2539
- };
2540
- var MESH_MISSION_LIST_TOOL = {
2541
- name: "mesh_mission_list",
2542
- description: 'List missions with their goal, status, and live task progress (total/pending/assigned/completed/failed). Unlike mesh_status (which surfaces live + recent missions), this returns every mission regardless of status by default, so paused/abandoned/completed missions are never hidden. Filter with `status` to scope (e.g. ["paused"] to find paused missions). Compact (default) elides the full goal to a capped preview; pass verbose=true for full goal text. Read-only.',
2543
- inputSchema: {
2544
- type: "object",
2545
- properties: {
2546
- status: {
2547
- type: "array",
2548
- items: { type: "string", enum: ["active", "paused", "completed", "abandoned"] },
2549
- description: "Optional status filter. Omit to return missions of every status."
2550
- },
2551
- verbose: { type: "boolean", description: "Return full goal text instead of a capped preview. Defaults to false (compact)." }
2552
- }
2553
- }
2554
- };
2555
- var MESH_APPROVE_TOOL = {
2556
- name: "mesh_approve",
2557
- description: "Approve or reject a pending action on a delegated agent session.",
2558
- inputSchema: {
2559
- type: "object",
2560
- properties: {
2561
- node_id: { type: "string", description: "Target node ID." },
2562
- session_id: { type: "string", description: "Agent session ID with pending approval." },
2563
- action: { type: "string", enum: ["approve", "reject"], description: "Action to take." }
2564
- },
2565
- required: ["node_id", "session_id", "action"]
1345
+ required: ["node_id", "session_id", "action"]
2566
1346
  }
2567
1347
  };
2568
1348
  var MESH_CLONE_NODE_TOOL = {
@@ -2809,12 +1589,1247 @@ var ALL_MESH_TOOLS = [
2809
1589
  MESH_MISSION_LIST_TOOL,
2810
1590
  MESH_REVIEW_INBOX_TOOL
2811
1591
  ];
1592
+
1593
+ // src/tools/mesh-tools.ts
1594
+ var SESSION_PROVIDER_METADATA_TTL_MS = 30 * 6e4;
1595
+ var meshSessionProviderMetadata = /* @__PURE__ */ new Map();
1596
+ function getSessionMetadata(key) {
1597
+ const entry = meshSessionProviderMetadata.get(key);
1598
+ if (!entry) return void 0;
1599
+ if (entry.expiresAt <= Date.now()) {
1600
+ meshSessionProviderMetadata.delete(key);
1601
+ return void 0;
1602
+ }
1603
+ return entry;
1604
+ }
1605
+ var ACTIVE_WORK_POLLING_BACKOFF_MS = 6e4;
1606
+ function buildActiveWorkPollingGuidance(summary, now = Date.now()) {
1607
+ if (!summary || summary.generatingCount <= 0) return void 0;
1608
+ return {
1609
+ activeGeneratingWork: true,
1610
+ generatingCount: summary.generatingCount,
1611
+ doNotPollBefore: new Date(now + ACTIVE_WORK_POLLING_BACKOFF_MS).toISOString(),
1612
+ eventSurface: "pendingCoordinatorEvents",
1613
+ nextRecommendedAction: "Wait for pendingCoordinatorEvents/completion events or an explicit user status request. If no terminal evidence appears and the user asks for status, make one bounded status check, then wait again.",
1614
+ message: "Do not repeatedly poll mesh_status/mesh_view_queue/mesh_read_chat while delegated work is generating; terminal ledger or completion evidence will be surfaced through pendingCoordinatorEvents when available."
1615
+ };
1616
+ }
1617
+ function summarizeTaskMessage(message) {
1618
+ const taskSummary = message.replace(/\s+/g, " ").trim();
1619
+ const taskTitle = taskSummary.length > 96 ? `${taskSummary.slice(0, 93)}...` : taskSummary;
1620
+ return { taskTitle: taskTitle || "(untitled task)", taskSummary };
1621
+ }
1622
+ function buildDirectTaskPayload(message, via, opts) {
1623
+ const descriptor = summarizeTaskMessage(message);
1624
+ return {
1625
+ source: "direct",
1626
+ via,
1627
+ taskId: opts.taskId,
1628
+ message,
1629
+ taskTitle: descriptor.taskTitle,
1630
+ taskSummary: descriptor.taskSummary,
1631
+ ...opts.taskMode ? { taskMode: opts.taskMode } : {},
1632
+ ...opts.providerType ? { providerType: opts.providerType } : {},
1633
+ ...opts.targetSessionId ? { targetSessionId: opts.targetSessionId } : {},
1634
+ ...opts.dispatchedToIdleSession !== void 0 ? { dispatchedToIdleSession: opts.dispatchedToIdleSession } : {}
1635
+ };
1636
+ }
1637
+ function findNode(mesh, nodeId) {
1638
+ const node = mesh.nodes.find((n) => (0, import_daemon_core2.meshNodeIdMatches)(n, nodeId));
1639
+ if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
1640
+ return node;
1641
+ }
1642
+ var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
1643
+ async function refreshMeshFromDaemon(ctx) {
1644
+ try {
1645
+ const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
1646
+ if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
1647
+ const refreshedNodes = result.mesh.nodes.filter((n) => n?.id).map((n) => n);
1648
+ ctx.mesh.nodes.splice(0, ctx.mesh.nodes.length, ...refreshedNodes);
1649
+ ctx.mesh.updatedAt = result.mesh.updatedAt ?? ctx.mesh.updatedAt;
1650
+ } catch {
1651
+ }
1652
+ }
1653
+ async function syncCoordinatorDaemonMeshCache(ctx) {
1654
+ if (!(ctx.transport instanceof IpcTransport)) return;
1655
+ try {
1656
+ await ctx.transport.command("get_mesh", {
1657
+ meshId: ctx.mesh.id,
1658
+ inlineMesh: ctx.mesh
1659
+ });
1660
+ } catch {
1661
+ }
1662
+ }
1663
+ async function findNodeWithRefresh(ctx, nodeId) {
1664
+ const hit = ctx.mesh.nodes.find((n) => (0, import_daemon_core2.meshNodeIdMatches)(n, nodeId));
1665
+ if (hit && !hit.isLocalWorktree) return hit;
1666
+ await refreshMeshFromDaemon(ctx);
1667
+ const refreshed = ctx.mesh.nodes.find((n) => (0, import_daemon_core2.meshNodeIdMatches)(n, nodeId));
1668
+ if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
1669
+ return refreshed;
1670
+ }
1671
+ async function findOptionalNodeWithRefresh(ctx, nodeId) {
1672
+ const hit = ctx.mesh.nodes.find((n) => (0, import_daemon_core2.meshNodeIdMatches)(n, nodeId));
1673
+ if (hit && !hit.isLocalWorktree) return hit;
1674
+ await refreshMeshFromDaemon(ctx);
1675
+ return ctx.mesh.nodes.find((n) => (0, import_daemon_core2.meshNodeIdMatches)(n, nodeId)) ?? null;
1676
+ }
1677
+ function hasRecentDuplicateDispatch(ctx, args) {
1678
+ const now = Date.now();
1679
+ const normalizedMessage = args.message.trim();
1680
+ for (const task of (0, import_daemon_core2.getQueue)(ctx.mesh.id)) {
1681
+ const timestamp = new Date(task.updatedAt || task.createdAt).getTime();
1682
+ if (!Number.isFinite(timestamp) || now - timestamp > DUPLICATE_DISPATCH_WINDOW_MS) continue;
1683
+ if (task.targetNodeId && task.targetNodeId !== args.node_id) continue;
1684
+ if (task.assignedNodeId && task.assignedNodeId !== args.node_id) continue;
1685
+ if (args.session_id && task.targetSessionId !== args.session_id && task.assignedSessionId !== args.session_id) continue;
1686
+ if (task.message?.trim() === normalizedMessage) {
1687
+ return { duplicate: true, entry: task, source: "queue" };
1688
+ }
1689
+ }
1690
+ const entries = (0, import_daemon_core2.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
1691
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
1692
+ const entry = entries[i];
1693
+ const timestamp = new Date(entry.timestamp).getTime();
1694
+ if (Number.isFinite(timestamp) && now - timestamp > DUPLICATE_DISPATCH_WINDOW_MS) break;
1695
+ if (entry.kind !== "task_dispatched") continue;
1696
+ if (entry.nodeId !== args.node_id) continue;
1697
+ if (args.session_id && entry.sessionId !== args.session_id) continue;
1698
+ if (typeof entry.payload?.message !== "string") continue;
1699
+ if (entry.payload.message.trim() === normalizedMessage) {
1700
+ return { duplicate: true, entry, source: "ledger" };
1701
+ }
1702
+ }
1703
+ return { duplicate: false };
1704
+ }
1705
+ function buildMissingNodeReadChatRecovery(ctx, args) {
1706
+ const entries = (0, import_daemon_core2.readLedgerEntries)(ctx.mesh.id, { tail: 300 });
1707
+ const relatedEntries = entries.filter((entry) => entry.nodeId === args.node_id || entry.sessionId === args.session_id);
1708
+ const completedEntries = relatedEntries.filter((entry) => entry.kind === "task_completed");
1709
+ const lastDispatch = [...relatedEntries].reverse().find((entry) => entry.kind === "task_dispatched");
1710
+ const lastTerminal = [...relatedEntries].reverse().find((entry) => entry.kind === "task_completed" || entry.kind === "task_failed" || entry.kind === "task_stalled");
1711
+ const lastRemoved = [...relatedEntries].reverse().find((entry) => entry.kind === "node_removed");
1712
+ const lastLaunch = [...relatedEntries].reverse().find((entry) => entry.kind === "session_launched");
1713
+ const providerSessionId = args.provider_session_id || readString(lastTerminal?.payload?.providerSessionId) || readString(lastLaunch?.payload?.providerSessionId) || readString(lastDispatch?.payload?.providerSessionId);
1714
+ const finalSummary = readString(lastTerminal?.payload?.finalSummary) || readString(lastTerminal?.payload?.compactSummary) || readString(lastTerminal?.payload?.summary);
1715
+ const ledger = {
1716
+ taskCompletedFound: completedEntries.length > 0,
1717
+ nodeRemovedFound: !!lastRemoved,
1718
+ providerType: lastTerminal?.providerType || lastLaunch?.providerType || lastDispatch?.providerType,
1719
+ providerSessionId,
1720
+ nodeRemovedAt: lastRemoved?.timestamp,
1721
+ sessionCleanupMode: readString(lastRemoved?.payload?.sessionCleanupMode),
1722
+ readDebugLocator: readString(lastTerminal?.payload?.readDebugLocator) || readString(lastTerminal?.payload?.debugBundlePath)
1723
+ };
1724
+ if (finalSummary) {
1725
+ if (args.compact === true) {
1726
+ return {
1727
+ ...compactChatPayload({
1728
+ success: true,
1729
+ status: "idle",
1730
+ providerSessionId,
1731
+ summary: finalSummary,
1732
+ messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
1733
+ }, {
1734
+ nodeId: args.node_id,
1735
+ sessionId: args.session_id,
1736
+ limit: args.tail ?? 10
1737
+ }),
1738
+ recoveredFromLedger: true,
1739
+ ledger
1740
+ };
1741
+ }
1742
+ return {
1743
+ success: true,
1744
+ compact: false,
1745
+ recoveredFromLedger: true,
1746
+ nodeId: args.node_id,
1747
+ sessionId: args.session_id,
1748
+ summary: finalSummary,
1749
+ ledger,
1750
+ messages: [{ role: "assistant", content: finalSummary, isHistorical: true }]
1751
+ };
1752
+ }
1753
+ return {
1754
+ success: false,
1755
+ recoverable: true,
1756
+ code: "mesh_removed_node_transcript_unavailable",
1757
+ error: `Node '${args.node_id}' is not a current member of mesh '${ctx.mesh.name}'.`,
1758
+ nodeId: args.node_id,
1759
+ sessionId: args.session_id,
1760
+ providerSessionId,
1761
+ reason: "node_not_in_current_mesh_snapshot",
1762
+ ledger,
1763
+ completedSessionSeenInLedger: ledger.taskCompletedFound,
1764
+ lastDispatch: lastDispatch ? {
1765
+ timestamp: lastDispatch.timestamp,
1766
+ sessionId: lastDispatch.sessionId,
1767
+ providerType: lastDispatch.providerType,
1768
+ taskId: typeof lastDispatch.payload?.taskId === "string" ? lastDispatch.payload.taskId : void 0,
1769
+ messagePreview: typeof lastDispatch.payload?.message === "string" ? lastDispatch.payload.message.slice(0, 500) : void 0
1770
+ } : null,
1771
+ lastTerminalEvent: lastTerminal ? {
1772
+ kind: lastTerminal.kind,
1773
+ timestamp: lastTerminal.timestamp,
1774
+ sessionId: lastTerminal.sessionId,
1775
+ providerType: lastTerminal.providerType,
1776
+ taskId: typeof lastTerminal.payload?.taskId === "string" ? lastTerminal.payload.taskId : void 0,
1777
+ payload: lastTerminal.payload
1778
+ } : null,
1779
+ nextSteps: [
1780
+ providerSessionId ? `Retry mesh_read_chat with provider_session_id='${providerSessionId}' on a current live node for the same daemon if one exists.` : "If the node UI shows a provider transcript id, retry mesh_read_chat/mesh_read_debug with provider_session_id.",
1781
+ "Use mesh_read_debug with the provider_session_id or daemon-side debug bundle locator if available.",
1782
+ "Check mesh_task_history for task_completed and node_removed entries before redispatching; do not resend solely because transcript recovery failed.",
1783
+ "If this node was removed with stop_and_delete, the runtime transcript may be gone; rely on the ledger summary/locator or ask the operator for the saved UI output."
1784
+ ],
1785
+ recoveryHints: [
1786
+ "The worktree/node may have been removed or the mesh snapshot may be stale after task completion.",
1787
+ "If you have a provider_session_id, retry mesh_read_chat with that value while targeting a live node for the same daemon if available.",
1788
+ "Use mesh_read_debug with provider_session_id, or inspect the daemon/session-host history locator if the transcript has already been archived.",
1789
+ "Avoid redispatching the same task solely because read_chat could not recover the transcript; check task_history and git status first."
1790
+ ]
1791
+ };
1792
+ }
1793
+ function isDirectDispatchLedgerEntry(entry) {
1794
+ if (entry?.kind !== "task_dispatched") return false;
1795
+ const payload = entry.payload || {};
1796
+ const via = readString(payload.via);
1797
+ return payload.source === "direct" || via === "p2p_direct" || via === "local_direct" || via === "mesh_send_task";
1798
+ }
1799
+ function readMessageTimestampIso(message) {
1800
+ for (const value of [message?.timestamp, message?.createdAt, message?.created_at, message?.updatedAt, message?.time]) {
1801
+ if (typeof value === "number" && Number.isFinite(value)) {
1802
+ const ms = value > 1e10 ? value : value * 1e3;
1803
+ return new Date(ms).toISOString();
1804
+ }
1805
+ if (typeof value === "string" && value.trim()) {
1806
+ const ms = new Date(value.trim()).getTime();
1807
+ if (Number.isFinite(ms)) return new Date(ms).toISOString();
1808
+ }
1809
+ }
1810
+ return void 0;
1811
+ }
1812
+ function readFinalAssistantTranscriptEvidence(payload) {
1813
+ const rawMessages = Array.isArray(payload?.messages) ? payload.messages : [];
1814
+ const finalAssistant = [...rawMessages].reverse().filter(isCoordinatorVisibleMessage).find((message) => {
1815
+ const role = String(message?.role ?? "").toLowerCase();
1816
+ return (role === "assistant" || role === "agent") && messageContent(message).trim();
1817
+ });
1818
+ const finalSummary = messageContent(finalAssistant).trim() || (typeof payload?.summary === "string" && payload.summary.trim() ? payload.summary.trim() : void 0);
1819
+ return {
1820
+ finalSummary,
1821
+ transcriptMessageAt: finalAssistant ? readMessageTimestampIso(finalAssistant) : void 0
1822
+ };
1823
+ }
1824
+ function findNodeSession(nodes, nodeId, sessionId) {
1825
+ if (!nodeId || !sessionId) return {};
1826
+ const node = nodes.find((candidate) => (0, import_daemon_core2.meshNodeIdMatches)(candidate, nodeId));
1827
+ if (!node) return {};
1828
+ const sessions = Array.isArray(node.sessions) ? node.sessions : [];
1829
+ const session = sessions.find((candidate) => readSessionRecordId(candidate) === sessionId);
1830
+ return { node, session };
1831
+ }
1832
+ function buildDirectDispatchReconciliationCandidates(directDispatches, ledgerEntries) {
1833
+ const candidates = [];
1834
+ const seenTaskIds = /* @__PURE__ */ new Set();
1835
+ for (const dispatch of directDispatches || []) {
1836
+ const taskId = readString(dispatch?.taskId);
1837
+ if (!taskId || seenTaskIds.has(taskId)) continue;
1838
+ seenTaskIds.add(taskId);
1839
+ candidates.push(dispatch);
1840
+ }
1841
+ for (const entry of ledgerEntries || []) {
1842
+ if (!isDirectDispatchLedgerEntry(entry)) continue;
1843
+ const taskId = readString(entry.payload?.taskId);
1844
+ if (!taskId || seenTaskIds.has(taskId)) continue;
1845
+ seenTaskIds.add(taskId);
1846
+ candidates.push({
1847
+ taskId,
1848
+ nodeId: entry.nodeId,
1849
+ sessionId: entry.sessionId,
1850
+ providerType: entry.providerType || readString(entry.payload?.providerType),
1851
+ message: readString(entry.payload?.message),
1852
+ dispatchedAt: entry.timestamp,
1853
+ via: readString(entry.payload?.via)
1854
+ });
1855
+ }
1856
+ return candidates;
1857
+ }
1858
+ async function reconcileDirectDispatchesFromTranscriptEvidence(ctx, liveNodes, directDispatches, ledgerEntries) {
1859
+ let attempted = 0;
1860
+ let reconciled = 0;
1861
+ let skipped = 0;
1862
+ const candidates = buildDirectDispatchReconciliationCandidates(directDispatches, ledgerEntries);
1863
+ for (const dispatch of candidates) {
1864
+ const taskId = readString(dispatch?.taskId);
1865
+ const nodeId = readString(dispatch?.nodeId);
1866
+ const sessionId = readString(dispatch?.sessionId);
1867
+ if (!taskId || !nodeId || !sessionId) {
1868
+ skipped += 1;
1869
+ continue;
1870
+ }
1871
+ const { session } = findNodeSession(liveNodes, nodeId, sessionId);
1872
+ if (!session || !isIdleSessionRecord(session)) {
1873
+ skipped += 1;
1874
+ continue;
1875
+ }
1876
+ const node = await findOptionalNodeWithRefresh(ctx, nodeId).catch(() => null);
1877
+ if (!node) {
1878
+ skipped += 1;
1879
+ continue;
1880
+ }
1881
+ const providerType = readString(dispatch?.providerType) || resolveSessionProviderType(session);
1882
+ const providerSessionId = readString(session?.providerSessionId) || readString(session?.activeChat?.providerSessionId) || readString(session?.settings?.providerSessionId) || resolveMeshSessionProviderMetadata(ctx, nodeId, sessionId)?.providerSessionId;
1883
+ attempted += 1;
1884
+ try {
1885
+ const readResult = await commandForNode(ctx, node, "read_chat", {
1886
+ sessionId,
1887
+ targetSessionId: sessionId,
1888
+ workspace: node.workspace,
1889
+ ...providerType ? { agentType: providerType, providerType } : {},
1890
+ ...providerSessionId ? { providerSessionId } : {},
1891
+ tailLimit: 10
1892
+ });
1893
+ const payload = unwrapCommandPayload(readResult);
1894
+ if (payload?.success === false) continue;
1895
+ const evidence = readFinalAssistantTranscriptEvidence(payload);
1896
+ if (!evidence.finalSummary) continue;
1897
+ const result = (0, import_daemon_core2.reconcileDirectDispatchCompletionFromTranscript)({
1898
+ meshId: ctx.mesh.id,
1899
+ nodeId,
1900
+ sessionId,
1901
+ providerType,
1902
+ providerSessionId: readString(payload?.providerSessionId) || providerSessionId,
1903
+ taskId,
1904
+ finalSummary: evidence.finalSummary,
1905
+ transcriptMessageAt: evidence.transcriptMessageAt,
1906
+ targetCoordinatorDaemonId: ctx.localDaemonId,
1907
+ source: "mcp_mesh_status_transcript_reconciliation"
1908
+ });
1909
+ if (result.reconciled) reconciled += 1;
1910
+ } catch {
1911
+ skipped += 1;
1912
+ }
1913
+ }
1914
+ return { attempted, reconciled, skipped };
1915
+ }
1916
+ async function triggerMeshQueueAndReport(ctx) {
1917
+ try {
1918
+ const raw = await ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id });
1919
+ const payload = unwrapCommandPayload(raw);
1920
+ const trigger = payload?.trigger && typeof payload.trigger === "object" ? payload.trigger : payload;
1921
+ return trigger && typeof trigger === "object" ? trigger : { success: true };
1922
+ } catch (e) {
1923
+ return {
1924
+ success: false,
1925
+ error: e?.message || String(e)
1926
+ };
1927
+ }
1928
+ }
1929
+ function buildQueueTriggerGuidance(queueTrigger) {
1930
+ if (!queueTrigger || queueTrigger.claimed === true) return void 0;
1931
+ if (queueTrigger.success === false) {
1932
+ return {
1933
+ queueClaimed: false,
1934
+ queueDispatchState: "trigger_failed",
1935
+ nextAction: "Do not assume the queued task is running. Check mesh_view_queue and daemon connectivity before redispatching."
1936
+ };
1937
+ }
1938
+ if (queueTrigger.autoLaunchPending === true) {
1939
+ return {
1940
+ queueClaimed: false,
1941
+ queueDispatchState: "pending_waiting_for_autolaunch",
1942
+ nextAction: "A worker session was just auto-launched for this task and is booting; it will claim the task shortly. Wait for it to claim \u2014 do NOT launch another session. Use mesh_view_queue to confirm the assignment lands."
1943
+ };
1944
+ }
1945
+ if (queueTrigger.noIdleMeshSessionAvailable === true) {
1946
+ return {
1947
+ queueClaimed: false,
1948
+ queueDispatchState: "pending_no_idle_mesh_session",
1949
+ nextAction: "The task is queued but not running. Launch a managed worker with mesh_launch_session, or wait for a delegated session to become ready and trigger the queue again."
1950
+ };
1951
+ }
1952
+ return {
1953
+ queueClaimed: false,
1954
+ queueDispatchState: "pending_or_waiting_for_ready",
1955
+ nextAction: "The task is queued but this trigger did not claim it. Use mesh_view_queue for the current active-work source of truth before retrying."
1956
+ };
1957
+ }
1958
+ function isMeshOwnedDelegateSession(session, meshId, nodeId) {
1959
+ const settings = session?.settings;
1960
+ const sessionMeshId = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
1961
+ const sessionNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
1962
+ if (sessionMeshId) {
1963
+ if (sessionMeshId !== meshId) return false;
1964
+ return !sessionNodeId || sessionNodeId === nodeId;
1965
+ }
1966
+ const coordinatorOwned = settings?.launchedByCoordinator === true || Boolean(readString(settings?.meshCoordinatorDaemonId));
1967
+ if (!coordinatorOwned) return false;
1968
+ const lastNodeId = readString(settings?.meshLastNodeId);
1969
+ if (lastNodeId) return lastNodeId === nodeId;
1970
+ return true;
1971
+ }
1972
+ function hasRemoteRelayMetadata(session) {
1973
+ return Boolean(
1974
+ readString(session?.settings?.meshCoordinatorDaemonId) || readString(session?.meta?.meshCoordinatorDaemonId) || readString(session?.metadata?.meshCoordinatorDaemonId) || readString(session?.meshCoordinatorDaemonId)
1975
+ );
1976
+ }
1977
+ function classifyRemoteDelegateRelaySafety(session, meshId, nodeId, coordinatorDaemonId) {
1978
+ if (!isMeshOwnedDelegateSession(session, meshId, nodeId)) return "unsafe_alias";
1979
+ if (hasRemoteRelayMetadata(session)) return "safe";
1980
+ return coordinatorDaemonId ? "self_heal" : "missing_anchor";
1981
+ }
1982
+ function chooseDispatchableSession(sessions, providerType, meshId, nodeId, coordinatorDaemonId) {
1983
+ const live = sessions.filter((session) => !isTerminalSessionRecord(session));
1984
+ const matchingProvider = (session) => !providerType || session?.providerType === providerType || session?.cliType === providerType;
1985
+ const meshSessions = live.filter((session) => {
1986
+ const safety = classifyRemoteDelegateRelaySafety(session, meshId, nodeId, coordinatorDaemonId);
1987
+ return safety === "safe" || safety === "self_heal";
1988
+ });
1989
+ return meshSessions.find((session) => isIdleSessionRecord(session) && matchingProvider(session)) || void 0;
1990
+ }
1991
+ function buildRelayUnsafeRemoteSessionFailure(ctx, node, sessionId, providerType) {
1992
+ return {
1993
+ success: false,
1994
+ recoverable: true,
1995
+ code: "mesh_delegate_session_missing_relay_metadata",
1996
+ reason: "mesh_delegate_session_missing_relay_metadata",
1997
+ transport: "mesh_transport",
1998
+ retryRecommended: true,
1999
+ meshId: ctx.mesh.id,
2000
+ nodeId: node.id,
2001
+ daemonId: node.daemonId,
2002
+ workspace: node.workspace,
2003
+ sessionId,
2004
+ unsafeTranscriptAlias: true,
2005
+ ...providerType ? { resolvedProviderType: providerType } : {},
2006
+ error: `Remote session '${sessionId}' is not relay-safe for mesh '${ctx.mesh.id}': missing meshNodeFor/meshCoordinatorDaemonId metadata, so completion events would not reach the coordinator ledger. This session may be the coordinator itself or an unrelated session (unsafe_transcript_alias risk).`,
2007
+ nextAction: `Launch a fresh relay-safe session with mesh_launch_session(node_id: '${node.id}'${providerType ? `, type: '${providerType}'` : ""}) or dispatch without session_id so Repo Mesh can choose a valid delegate session.`,
2008
+ noFallbackReason: "Blindly reusing a remote session without mesh relay metadata would silently drop task_completed / generating_completed events."
2009
+ };
2010
+ }
2011
+ function buildMissingCoordinatorDaemonIdFailure(ctx, node, providerType) {
2012
+ return {
2013
+ success: false,
2014
+ recoverable: true,
2015
+ code: "mesh_coordinator_daemon_unknown",
2016
+ reason: "mesh_coordinator_daemon_unknown",
2017
+ transport: "mesh_transport",
2018
+ retryRecommended: true,
2019
+ meshId: ctx.mesh.id,
2020
+ nodeId: node.id,
2021
+ daemonId: node.daemonId,
2022
+ workspace: node.workspace,
2023
+ ...providerType ? { resolvedProviderType: providerType } : {},
2024
+ error: `Cannot launch a remote mesh delegate for node '${node.id}': coordinator daemon identity is unavailable, so the worker would be unable to relay completion events back to the coordinator.`,
2025
+ nextAction: "Retry after the coordinator daemon identity is available (for example from an attached daemon-backed MCP session) so meshCoordinatorDaemonId can be stamped on the worker session.",
2026
+ noFallbackReason: "Launching without meshCoordinatorDaemonId would create a worker session that can finish work but cannot emit task_completed / generating_completed back to the coordinator."
2027
+ };
2028
+ }
2029
+ function findNestedPayload(value, predicate) {
2030
+ const seen = /* @__PURE__ */ new Set();
2031
+ const stack = [{ payload: value, depth: 0 }];
2032
+ while (stack.length) {
2033
+ const { payload, depth } = stack.pop();
2034
+ if (predicate(payload)) return payload;
2035
+ if (!payload || typeof payload !== "object" || seen.has(payload) || depth >= 8) continue;
2036
+ seen.add(payload);
2037
+ for (const key of ["payload", "result"]) {
2038
+ if (key in payload) stack.push({ payload: payload[key], depth: depth + 1 });
2039
+ }
2040
+ }
2041
+ return value;
2042
+ }
2043
+ function extractCloneNodePayload(value) {
2044
+ return findNestedPayload(value, (payload) => Boolean(payload?.node?.id));
2045
+ }
2046
+ function extractGitStatus(value) {
2047
+ const payload = unwrapCommandPayload(value);
2048
+ return payload?.status ?? value?.status ?? payload;
2049
+ }
2050
+ function extractGitDiff(value) {
2051
+ const payload = unwrapCommandPayload(value);
2052
+ return payload?.diffSummary ?? payload?.diff ?? value?.diffSummary ?? value?.diff ?? payload;
2053
+ }
2054
+ function extractSubmodules(value, ignorePaths) {
2055
+ const payload = unwrapCommandPayload(value);
2056
+ const subs = payload?.status?.submodules ?? payload?.submodules ?? value?.status?.submodules ?? value?.submodules;
2057
+ if (!Array.isArray(subs)) return void 0;
2058
+ if (ignorePaths.length === 0) return subs;
2059
+ const ignoreSet = new Set(ignorePaths);
2060
+ return subs.filter((s) => s?.path && !ignoreSet.has(s.path));
2061
+ }
2062
+ function assignFullGitSnapshot(entry, status) {
2063
+ if (!status || typeof status !== "object" || Array.isArray(status)) return;
2064
+ entry.git = status;
2065
+ }
2066
+ var COMPACT_DETAILED_NODES_BYTE_BUDGET = 9e3;
2067
+ var COMPACT_NODES_TOTAL_BYTE_BUDGET = 13e3;
2068
+ var COMPACT_MISSIONS_BYTE_BUDGET = 6e3;
2069
+ function extractLaunchPayload(value) {
2070
+ return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
2071
+ }
2072
+ function classifyMeshLaunchFailure(error) {
2073
+ const message = error instanceof Error ? error.message : String(error || "launch failed");
2074
+ const lower = message.toLowerCase();
2075
+ const p2pClassification = (0, import_daemon_core2.classifyP2pRelayFailure)(error, { command: "launch_cli" });
2076
+ if (p2pClassification.recoverable) {
2077
+ return p2pClassification;
2078
+ }
2079
+ if (lower.includes("cannot connect to daemon ipc") || lower.includes("daemon ipc command")) {
2080
+ return {
2081
+ code: "local_ipc_unavailable",
2082
+ reason: "local_daemon_ipc_unavailable",
2083
+ transport: "local_ipc",
2084
+ recoverable: true,
2085
+ retryRecommended: true,
2086
+ nextAction: "Check the local daemon IPC connection, then retry mesh_launch_session once after the daemon is reachable."
2087
+ };
2088
+ }
2089
+ if (lower.includes("timed out") || lower.includes("timeout")) {
2090
+ return {
2091
+ code: "mesh_transport_timeout",
2092
+ reason: "mesh_transport_timeout",
2093
+ transport: "mesh_transport",
2094
+ recoverable: true,
2095
+ retryRecommended: true,
2096
+ nextAction: "Check mesh transport health, then do one bounded retry before requeueing or relaunching the task."
2097
+ };
2098
+ }
2099
+ return {
2100
+ code: "mesh_launch_failed",
2101
+ reason: "provider_launch_failed",
2102
+ transport: "mesh_transport",
2103
+ recoverable: false,
2104
+ retryRecommended: false,
2105
+ nextAction: "Inspect the provider launch error and fix the underlying provider/configuration issue before retrying."
2106
+ };
2107
+ }
2108
+ function buildWorktreeCleanupHint(node) {
2109
+ if (!node.isLocalWorktree) return void 0;
2110
+ return {
2111
+ tool: "mesh_remove_node",
2112
+ args: { node_id: node.id, session_cleanup_mode: "preserve" },
2113
+ hint: `If the worktree is no longer needed, remove the orphan worktree node with mesh_remove_node(node_id: "${node.id}").`
2114
+ };
2115
+ }
2116
+ function buildRecoverableLaunchFailure(ctx, node, providerType, error) {
2117
+ const message = error instanceof Error ? error.message : String(error || "launch failed");
2118
+ const classified = classifyMeshLaunchFailure(error);
2119
+ const cleanup = buildWorktreeCleanupHint(node);
2120
+ return {
2121
+ success: false,
2122
+ recoverable: classified.recoverable,
2123
+ code: classified.code,
2124
+ reason: classified.reason,
2125
+ transport: classified.transport,
2126
+ retryRecommended: classified.retryRecommended,
2127
+ nextAction: classified.nextAction,
2128
+ ...classified.noFallbackReason ? { noFallbackReason: classified.noFallbackReason } : {},
2129
+ error: message,
2130
+ meshId: ctx.mesh.id,
2131
+ nodeId: node.id,
2132
+ daemonId: node.daemonId,
2133
+ workspace: node.workspace,
2134
+ isLocalWorktree: node.isLocalWorktree === true,
2135
+ worktreeBranch: node.worktreeBranch,
2136
+ clonedFromNodeId: node.clonedFromNodeId,
2137
+ ...providerType ? { resolvedProviderType: providerType } : {},
2138
+ retryHint: `Retry mesh_launch_session(node_id: "${node.id}"${providerType ? `, type: "${providerType}"` : ""}) after daemon mesh transport/P2P is healthy.`,
2139
+ ...cleanup ? { cleanup } : {},
2140
+ nextStepHints: [
2141
+ `Retry mesh_launch_session(node_id: "${node.id}"${providerType ? `, type: "${providerType}"` : ""}) after checking daemon/P2P health.`,
2142
+ ...cleanup ? [`Cleanup orphan worktree node with mesh_remove_node(node_id: "${node.id}") if retry is not desired.`] : [],
2143
+ "Run mesh_status to see the degraded reason and recovery hints before redispatching work."
2144
+ ]
2145
+ };
2146
+ }
2147
+ function recordRecoverableLaunchFailure(ctx, node, providerType, error) {
2148
+ const failure = buildRecoverableLaunchFailure(ctx, node, providerType, error);
2149
+ try {
2150
+ (0, import_daemon_core2.appendLedgerEntry)(ctx.mesh.id, {
2151
+ kind: "recovery_attempted",
2152
+ nodeId: node.id,
2153
+ providerType,
2154
+ payload: {
2155
+ event: "session_launch_failed",
2156
+ ...failure
2157
+ }
2158
+ });
2159
+ } catch {
2160
+ }
2161
+ return failure;
2162
+ }
2163
+ function getLatestActiveLaunchFailure(meshId, nodeId) {
2164
+ const entries = (0, import_daemon_core2.readLedgerEntries)(meshId, { tail: 200 });
2165
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
2166
+ const entry = entries[i];
2167
+ if (entry.nodeId !== nodeId) continue;
2168
+ if (entry.kind === "session_launched" || entry.kind === "node_removed") return null;
2169
+ if (entry.kind === "recovery_attempted" && entry.payload?.event === "session_launch_failed") {
2170
+ return { timestamp: entry.timestamp, ...entry.payload };
2171
+ }
2172
+ }
2173
+ return null;
2174
+ }
2175
+ function buildCoordinatorP2pRelayFailure(error, context) {
2176
+ const payload = (0, import_daemon_core2.buildP2pRelayFailurePayload)(error, {
2177
+ command: context.command,
2178
+ targetDaemonId: context.targetDaemonId
2179
+ });
2180
+ return {
2181
+ ...payload,
2182
+ ...context.nodeId ? { nodeId: context.nodeId } : {},
2183
+ ...context.sessionId ? { sessionId: context.sessionId } : {},
2184
+ retryHint: payload.retryRecommended ? payload.nextAction : "Do not retry as a P2P transport recovery; inspect the command/provider error first."
2185
+ };
2186
+ }
2187
+ async function ipcDispatchToRemoteAgent(ctx, node, args) {
2188
+ const transport = ctx.transport;
2189
+ const daemonId = node.daemonId;
2190
+ const dispatchCoordinatorDaemonId = readString(args.meshContext?.coordinatorDaemonId) || "";
2191
+ let sessionId = args.session_id?.trim() || "";
2192
+ const providerPriorityList = Array.isArray(node.policy?.providerPriority) ? node.policy.providerPriority : [];
2193
+ let resolvedProviderType = args.providerType?.trim() || providerPriorityList[0] || "";
2194
+ if (sessionId && args.verifiedSession) {
2195
+ const explicitSession = args.verifiedSession;
2196
+ const relaySafety = classifyRemoteDelegateRelaySafety(explicitSession, ctx.mesh.id, node.id, dispatchCoordinatorDaemonId);
2197
+ if (relaySafety === "unsafe_alias") {
2198
+ return buildRelayUnsafeRemoteSessionFailure(
2199
+ ctx,
2200
+ node,
2201
+ sessionId,
2202
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
2203
+ );
2204
+ }
2205
+ if (relaySafety === "missing_anchor") {
2206
+ return buildMissingCoordinatorDaemonIdFailure(
2207
+ ctx,
2208
+ node,
2209
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
2210
+ );
2211
+ }
2212
+ if (!resolvedProviderType) {
2213
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
2214
+ }
2215
+ } else if (!sessionId || args.session_id) {
2216
+ try {
2217
+ const relayResult = await transport.meshCommand(daemonId, "get_status_metadata", {});
2218
+ const sessions = extractStatusMetadataSessions(relayResult);
2219
+ if (sessionId) {
2220
+ const explicitSession = sessions.find((session) => readSessionRecordId(session) === sessionId);
2221
+ if (!explicitSession) {
2222
+ return {
2223
+ success: false,
2224
+ recoverable: true,
2225
+ code: "mesh_target_session_not_found",
2226
+ reason: "mesh_target_session_not_found",
2227
+ transport: "mesh_transport",
2228
+ retryRecommended: true,
2229
+ meshId: ctx.mesh.id,
2230
+ nodeId: node.id,
2231
+ daemonId,
2232
+ workspace: node.workspace,
2233
+ sessionId,
2234
+ ...resolvedProviderType ? { resolvedProviderType } : {},
2235
+ error: `Remote session '${sessionId}' is not present in the live status for node '${node.id}'.`,
2236
+ nextAction: `Launch a fresh session with mesh_launch_session(node_id: '${node.id}'${resolvedProviderType ? `, type: '${resolvedProviderType}'` : ""}) or retry without session_id so Repo Mesh can target a live delegate session.`
2237
+ };
2238
+ }
2239
+ const relaySafety = classifyRemoteDelegateRelaySafety(explicitSession, ctx.mesh.id, node.id, dispatchCoordinatorDaemonId);
2240
+ if (relaySafety === "unsafe_alias") {
2241
+ return buildRelayUnsafeRemoteSessionFailure(
2242
+ ctx,
2243
+ node,
2244
+ sessionId,
2245
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
2246
+ );
2247
+ }
2248
+ if (relaySafety === "missing_anchor") {
2249
+ return buildMissingCoordinatorDaemonIdFailure(
2250
+ ctx,
2251
+ node,
2252
+ resolvedProviderType || resolveSessionProviderType(explicitSession) || void 0
2253
+ );
2254
+ }
2255
+ if (!resolvedProviderType) {
2256
+ resolvedProviderType = resolveSessionProviderType(explicitSession);
2257
+ }
2258
+ } else {
2259
+ const targetSession = chooseDispatchableSession(sessions, resolvedProviderType, ctx.mesh.id, node.id, dispatchCoordinatorDaemonId);
2260
+ if (targetSession?.id || targetSession?.sessionId) {
2261
+ sessionId = targetSession.id || targetSession.sessionId;
2262
+ if (!resolvedProviderType) {
2263
+ resolvedProviderType = resolveSessionProviderType(targetSession);
2264
+ }
2265
+ }
2266
+ }
2267
+ } catch (e) {
2268
+ if (sessionId) {
2269
+ return {
2270
+ ...buildCoordinatorP2pRelayFailure(e, {
2271
+ command: "get_status_metadata",
2272
+ targetDaemonId: daemonId,
2273
+ nodeId: node.id,
2274
+ sessionId
2275
+ }),
2276
+ success: false,
2277
+ error: `Cannot verify remote session '${sessionId}' before dispatch: ${e?.message || String(e)}`
2278
+ };
2279
+ }
2280
+ }
2281
+ }
2282
+ if (!resolvedProviderType) {
2283
+ return { success: false, error: `Cannot dispatch to remote node '${node.id}': providerType unknown. Set providerPriority on the node policy or call mesh_launch_session first.` };
2284
+ }
2285
+ try {
2286
+ const dispatchResult = await transport.meshCommand(daemonId, "agent_command", {
2287
+ ...sessionId ? { targetSessionId: sessionId } : {},
2288
+ agentType: resolvedProviderType,
2289
+ cliType: resolvedProviderType,
2290
+ action: "send_chat",
2291
+ message: args.message,
2292
+ // WTCLAIM (B): carry the node workspace so a sessionless dispatch can be
2293
+ // scoped to THIS node's session on the worker (findAdapter dir match /
2294
+ // findMeshNodeAdapter). Without it, a worker hosting both a base node and a
2295
+ // cloned worktree node (same daemonId) would fall through to a provider-only
2296
+ // fuzzy match and could land worktree work on the base session.
2297
+ ...node.workspace ? { dir: node.workspace } : {},
2298
+ ...args.meshContext ? { meshContext: args.meshContext } : {}
2299
+ });
2300
+ const dispatchPayload = unwrapCommandPayload(dispatchResult);
2301
+ if (dispatchPayload?.success === false || dispatchResult?.success === false) {
2302
+ const source = dispatchPayload?.success === false ? dispatchPayload : dispatchResult;
2303
+ const errorMessage = dispatchPayload?.error || dispatchResult?.error || "agent_command rejected the task";
2304
+ return {
2305
+ ...buildCoordinatorP2pRelayFailure(source?.error || errorMessage, {
2306
+ command: "agent_command",
2307
+ targetDaemonId: daemonId,
2308
+ nodeId: node.id,
2309
+ sessionId
2310
+ }),
2311
+ ...source && typeof source === "object" ? source : {},
2312
+ success: false,
2313
+ error: `P2P dispatch failed: ${errorMessage}`
2314
+ };
2315
+ }
2316
+ return { success: true, dispatched: true, sessionId: sessionId || "", providerType: resolvedProviderType };
2317
+ } catch (e) {
2318
+ const errorMessage = e?.message || String(e);
2319
+ return {
2320
+ ...buildCoordinatorP2pRelayFailure(e, {
2321
+ command: "agent_command",
2322
+ targetDaemonId: daemonId,
2323
+ nodeId: node.id,
2324
+ sessionId
2325
+ }),
2326
+ error: `P2P dispatch failed: ${errorMessage}`
2327
+ };
2328
+ }
2329
+ }
2330
+ function meshSessionCacheKey(nodeId, runtimeSessionId) {
2331
+ return `${nodeId}:${runtimeSessionId}`;
2332
+ }
2333
+ function rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, metadata) {
2334
+ const keyNodeId = readString(nodeId);
2335
+ const keySessionId = readString(runtimeSessionId);
2336
+ if (!keyNodeId || !keySessionId) return;
2337
+ const providerType = readString(metadata.providerType);
2338
+ const providerSessionId = readString(metadata.providerSessionId);
2339
+ if (!providerType && !providerSessionId) return;
2340
+ const existing = getSessionMetadata(meshSessionCacheKey(keyNodeId, keySessionId)) || { providerType: "" };
2341
+ meshSessionProviderMetadata.set(meshSessionCacheKey(keyNodeId, keySessionId), {
2342
+ providerType: providerType || existing.providerType,
2343
+ providerSessionId: providerSessionId || existing.providerSessionId,
2344
+ expiresAt: Date.now() + SESSION_PROVIDER_METADATA_TTL_MS
2345
+ });
2346
+ }
2347
+ function rememberMeshSessionProviderMetadataFromEvent(event) {
2348
+ const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : event && typeof event === "object" ? event : {};
2349
+ const nodeId = readString(event?.nodeId) || readString(metadataEvent.nodeId) || readString(metadataEvent.meshNodeId);
2350
+ const sessionId = readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId) || readString(event?.sessionId);
2351
+ rememberMeshSessionProviderMetadata(nodeId, sessionId, {
2352
+ providerType: readString(metadataEvent.providerType) || readString(event?.providerType) || "",
2353
+ providerSessionId: readString(metadataEvent.providerSessionId) || readString(event?.providerSessionId)
2354
+ });
2355
+ }
2356
+ function resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId) {
2357
+ const entries = (0, import_daemon_core2.readLedgerEntries)(ctx.mesh.id, { tail: 50 });
2358
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
2359
+ const entry = entries[i];
2360
+ const payload = entry.payload && typeof entry.payload === "object" && !Array.isArray(entry.payload) ? entry.payload : {};
2361
+ const entryNodeId = readString(entry.nodeId) || readString(payload.nodeId) || readString(payload.meshNodeId);
2362
+ if (entryNodeId && entryNodeId !== nodeId) continue;
2363
+ const entrySessionId = readString(entry.sessionId) || readString(payload.targetSessionId) || readString(payload.sessionId) || readString(payload.instanceId);
2364
+ if (entrySessionId !== runtimeSessionId) continue;
2365
+ const providerType = readString(entry.providerType) || readString(payload.providerType);
2366
+ const completionDiagnostic = payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? payload.completionDiagnostic : {};
2367
+ const metadataEvent = payload.metadataEvent && typeof payload.metadataEvent === "object" && !Array.isArray(payload.metadataEvent) ? payload.metadataEvent : {};
2368
+ const providerSessionId = readString(payload.providerSessionId) || readString(completionDiagnostic.providerSessionId) || readString(metadataEvent.providerSessionId);
2369
+ if (providerType || providerSessionId) {
2370
+ return { providerType: providerType || "", providerSessionId };
2371
+ }
2372
+ }
2373
+ return void 0;
2374
+ }
2375
+ function resolveMeshSessionProviderMetadata(ctx, nodeId, runtimeSessionId) {
2376
+ const cached = getSessionMetadata(meshSessionCacheKey(nodeId, runtimeSessionId));
2377
+ if (cached?.providerType || cached?.providerSessionId) return cached;
2378
+ const fromLedger = resolveMeshSessionProviderMetadataFromLedger(ctx, nodeId, runtimeSessionId);
2379
+ if (fromLedger) rememberMeshSessionProviderMetadata(nodeId, runtimeSessionId, fromLedger);
2380
+ return fromLedger;
2381
+ }
2382
+ function countUncommittedChanges(status) {
2383
+ if (typeof status?.uncommittedChanges === "number") return status.uncommittedChanges;
2384
+ const keys = ["staged", "modified", "untracked", "deleted", "renamed"];
2385
+ const counted = keys.reduce((sum, key) => sum + (Number.isFinite(Number(status?.[key])) ? Number(status[key]) : 0), 0);
2386
+ const conflicts = Array.isArray(status?.conflictFiles) ? status.conflictFiles.length : status?.hasConflicts ? 1 : 0;
2387
+ return counted + conflicts;
2388
+ }
2389
+ function isGitStatusDirty(status) {
2390
+ if (typeof status?.isDirty === "boolean") return status.isDirty;
2391
+ if (typeof status?.dirty === "boolean") return status.dirty;
2392
+ if (Array.isArray(status?.submodules) && status.submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return true;
2393
+ return countUncommittedChanges(status) > 0;
2394
+ }
2395
+ function slimLedgerPayload(payload) {
2396
+ const slim = {};
2397
+ for (const [k, v] of Object.entries(payload)) {
2398
+ if (k === "message" || k === "taskSummary") {
2399
+ slim[k] = typeof v === "string" && v.length > 200 ? v.slice(0, 200) + "\u2026" : v;
2400
+ } else if (k === "evidence" || k === "workerResult" || k === "gitStatus" || k === "validationResults") {
2401
+ } else if (k === "finalSummary") {
2402
+ slim[k] = typeof v === "string" && v.length > 300 ? v.slice(0, 300) + "\u2026" : v;
2403
+ } else if (LARGE_LEDGER_FIELD_KEYS.has(k)) {
2404
+ slim[k] = summarizeLargeLedgerField(k, v);
2405
+ } else {
2406
+ slim[k] = elideLargeNestedValue(k, v);
2407
+ }
2408
+ }
2409
+ return slim;
2410
+ }
2411
+ function readRelatedRepos(node) {
2412
+ const raw = Array.isArray(node.relatedRepos) ? node.relatedRepos : Array.isArray(node.policy?.relatedRepos) ? node.policy.relatedRepos : [];
2413
+ return raw.map((entry) => ({
2414
+ label: typeof entry?.label === "string" ? entry.label.trim() : "",
2415
+ workspace: typeof entry?.workspace === "string" ? entry.workspace.trim() : ""
2416
+ })).filter((entry) => Boolean(entry.label && entry.workspace));
2417
+ }
2418
+ function summarizeRelatedRepoStatus(repo, status) {
2419
+ const dirty = isGitStatusDirty(status);
2420
+ return {
2421
+ label: repo.label,
2422
+ workspace: repo.workspace,
2423
+ isGitRepo: status?.isGitRepo === true,
2424
+ branch: status?.branch ?? null,
2425
+ upstream: status?.upstream ?? null,
2426
+ upstreamStatus: typeof status?.upstreamStatus === "string" ? status.upstreamStatus : status?.upstream ? "unchecked" : "no_upstream",
2427
+ upstreamFetchedAt: Number.isFinite(Number(status?.upstreamFetchedAt)) ? Number(status.upstreamFetchedAt) : null,
2428
+ upstreamFetchError: typeof status?.upstreamFetchError === "string" ? status.upstreamFetchError : null,
2429
+ ahead: Number.isFinite(Number(status?.ahead)) ? Number(status.ahead) : 0,
2430
+ behind: Number.isFinite(Number(status?.behind)) ? Number(status.behind) : 0,
2431
+ dirty,
2432
+ uncommittedChanges: countUncommittedChanges(status),
2433
+ head: status?.headCommit ?? null,
2434
+ lastCommitSummary: status?.headMessage ?? null,
2435
+ ...status?.reason ? { reason: status.reason } : {},
2436
+ ...status?.error ? { error: status.error } : {}
2437
+ };
2438
+ }
2439
+ async function collectRelatedRepoStatuses(ctx, node) {
2440
+ const relatedRepos = readRelatedRepos(node);
2441
+ if (!relatedRepos.length) return [];
2442
+ const results = [];
2443
+ for (const repo of relatedRepos) {
2444
+ try {
2445
+ const statusResult = await commandForNode(ctx, node, "git_status", { workspace: repo.workspace, refreshUpstream: true });
2446
+ const status = extractGitStatus(statusResult);
2447
+ results.push(summarizeRelatedRepoStatus(repo, status));
2448
+ } catch (e) {
2449
+ results.push({
2450
+ label: repo.label,
2451
+ workspace: repo.workspace,
2452
+ error: e?.message || "related repo status failed"
2453
+ });
2454
+ }
2455
+ }
2456
+ return results;
2457
+ }
2458
+ function readProviderPriority(policy) {
2459
+ const raw = policy?.providerPriority;
2460
+ return Array.isArray(raw) ? raw.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean) : [];
2461
+ }
2462
+ function buildNodeCapabilityExposure(node) {
2463
+ const providers = readProviderPriority(node.policy);
2464
+ const capabilityTags = (0, import_daemon_core2.buildMeshNodeCapabilityTags)(node);
2465
+ const exposure = { capabilityTags };
2466
+ if (providers.length) {
2467
+ const byProvider = {};
2468
+ for (const provider of providers) {
2469
+ byProvider[provider] = (0, import_daemon_core2.buildMeshNodeCapabilityTags)(node, provider);
2470
+ }
2471
+ exposure.capabilityTagsByProvider = byProvider;
2472
+ }
2473
+ const capabilities = Array.isArray(node.capabilities) ? node.capabilities.filter((tag) => typeof tag === "string" && !!tag.trim()) : [];
2474
+ if (capabilities.length) exposure.capabilities = capabilities;
2475
+ return exposure;
2476
+ }
2477
+ function readSpawnedSessionVisibility(policy) {
2478
+ return policy?.spawnedSessionVisibility === "hidden" ? "hidden" : "visible";
2479
+ }
2480
+ function missingProviderPriorityMessage(nodeId) {
2481
+ return `Node '${nodeId}' has no providerPriority policy; pass type explicitly or configure node.policy.providerPriority`;
2482
+ }
2483
+ function getNodeLaunchReadiness(node) {
2484
+ const bootstrap = node.worktreeBootstrap;
2485
+ if (node.isLocalWorktree && bootstrap?.status === "failed" && bootstrap?.required !== false) {
2486
+ return {
2487
+ providerPriority: readProviderPriority(node.policy),
2488
+ launchReady: false,
2489
+ launchBlockedReason: "worktree_bootstrap_failed",
2490
+ launchBlockedMessage: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : "Required worktree bootstrap failed; resolve it before launching an agent into this node.",
2491
+ worktreeBootstrap: bootstrap
2492
+ };
2493
+ }
2494
+ const providerPriority = readProviderPriority(node.policy);
2495
+ if (providerPriority.length) {
2496
+ return {
2497
+ providerPriority,
2498
+ launchReady: true
2499
+ };
2500
+ }
2501
+ return {
2502
+ providerPriority,
2503
+ launchReady: false,
2504
+ launchBlockedReason: "missing_provider_priority",
2505
+ launchBlockedMessage: missingProviderPriorityMessage(node.id)
2506
+ };
2507
+ }
2508
+ function getWorktreeBootstrapLaunchBlock(node, meshPolicy) {
2509
+ if (!node.isLocalWorktree) return void 0;
2510
+ const bootstrap = node.worktreeBootstrap;
2511
+ const requireReady = !!(meshPolicy && typeof meshPolicy === "object" && meshPolicy.requireBootstrapBeforeLaunch === true);
2512
+ if (requireReady && bootstrap?.status !== "ready") {
2513
+ return {
2514
+ success: false,
2515
+ code: "bootstrap_not_ready",
2516
+ error: `Node '${node.id}' bootstrap state is '${bootstrap?.status ?? "unknown"}' and mesh policy requireBootstrapBeforeLaunch is enabled.`,
2517
+ nodeId: node.id,
2518
+ worktreeBootstrap: bootstrap ?? null,
2519
+ recoveryHint: "Run the worktree bootstrap (clone runOnClone or a refine with bootstrap inherit) until the node reports ready, or disable requireBootstrapBeforeLaunch."
2520
+ };
2521
+ }
2522
+ if (bootstrap?.status !== "failed" || bootstrap?.required === false) return void 0;
2523
+ return {
2524
+ success: false,
2525
+ code: "worktree_bootstrap_failed",
2526
+ error: typeof bootstrap.error === "string" && bootstrap.error.trim() ? bootstrap.error.trim() : `Node '${node.id}' has a failed required worktree bootstrap.`,
2527
+ nodeId: node.id,
2528
+ worktreeBootstrap: bootstrap,
2529
+ recoveryHint: "Fix the configured worktree bootstrap command or remove/recreate the worktree node before launching an agent."
2530
+ };
2531
+ }
2532
+ async function collectLiveStatusSessions(ctx, node) {
2533
+ try {
2534
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2535
+ return extractStatusMetadataSessions(statusResult);
2536
+ } catch {
2537
+ return [];
2538
+ }
2539
+ }
2540
+ async function collectLiveStatusProbe(ctx, node) {
2541
+ try {
2542
+ const statusResult = await commandForNode(ctx, node, "get_status_metadata", {});
2543
+ return {
2544
+ sessions: extractStatusMetadataSessions(statusResult),
2545
+ daemonBuild: extractDaemonBuildInfo(statusResult)
2546
+ };
2547
+ } catch {
2548
+ return { sessions: [] };
2549
+ }
2550
+ }
2551
+ function extractDaemonBuildInfo(value) {
2552
+ const payload = unwrapCommandPayload(value);
2553
+ const build = payload?.daemonBuild && typeof payload.daemonBuild === "object" ? payload.daemonBuild : value?.daemonBuild && typeof value.daemonBuild === "object" ? value.daemonBuild : void 0;
2554
+ if (!build) return void 0;
2555
+ const commit = readString(build.commit);
2556
+ if (!commit) return void 0;
2557
+ return {
2558
+ commit,
2559
+ commitShort: readString(build.commitShort) || commit.slice(0, 7),
2560
+ version: readString(build.version) || "unknown",
2561
+ ...readString(build.builtAt) ? { builtAt: readString(build.builtAt) } : {}
2562
+ };
2563
+ }
2564
+ async function collectMeshViewQueueNodesWithLiveSessions(ctx) {
2565
+ const nodes = await Promise.all(ctx.mesh.nodes.map(async (node) => {
2566
+ const liveSessions = await collectLiveStatusSessions(ctx, node);
2567
+ return liveSessions.length > 0 ? { ...node, sessions: liveSessions } : node;
2568
+ }));
2569
+ return nodes;
2570
+ }
2571
+ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
2572
+ const defaultBranch = readString(mesh.defaultBranch) ?? "main";
2573
+ const branch = readString(status?.branch) ?? readString(node.worktreeBranch) ?? null;
2574
+ const ahead = readNumeric(status?.ahead);
2575
+ const behind = readNumeric(status?.behind);
2576
+ const upstream = readString(status?.upstream) ?? null;
2577
+ const upstreamStatus = readString(status?.upstreamStatus) ?? (upstream ? "unchecked" : "no_upstream");
2578
+ const hasConflicts = status?.hasConflicts === true || Array.isArray(status?.conflictFiles) && status.conflictFiles.length > 0;
2579
+ const base = {
2580
+ defaultBranch,
2581
+ branch,
2582
+ upstream,
2583
+ upstreamStatus,
2584
+ ahead,
2585
+ behind,
2586
+ isWorktree: node.isLocalWorktree === true,
2587
+ isDefaultBranch: branch === defaultBranch
2588
+ };
2589
+ if (status?.isGitRepo !== true) {
2590
+ return {
2591
+ ...base,
2592
+ status: "blocked_review",
2593
+ needsConvergence: true,
2594
+ reason: "git_status_unavailable",
2595
+ nextStep: `Resolve git status for node '${node.id}' before marking the task complete.`
2596
+ };
2597
+ }
2598
+ if (!branch) {
2599
+ return {
2600
+ ...base,
2601
+ status: "blocked_review",
2602
+ needsConvergence: true,
2603
+ reason: "branch_unknown",
2604
+ nextStep: `Inspect node '${node.id}' git branch before deciding whether it is merged to ${defaultBranch}.`
2605
+ };
2606
+ }
2607
+ if (hasConflicts || dirty || uncommittedChanges > 0) {
2608
+ return {
2609
+ ...base,
2610
+ status: "not_mergeable",
2611
+ needsConvergence: true,
2612
+ reason: hasConflicts ? "conflicts_present" : "dirty_workspace",
2613
+ nextStep: `Commit, checkpoint, or resolve node '${node.id}' before any main convergence step.`
2614
+ };
2615
+ }
2616
+ if (branch === defaultBranch) {
2617
+ if (upstream && upstreamStatus !== "fresh") {
2618
+ return {
2619
+ ...base,
2620
+ status: "blocked_review",
2621
+ needsConvergence: true,
2622
+ reason: "default_branch_upstream_unverified",
2623
+ nextStep: `Refresh ${defaultBranch}'s upstream refs or resolve the fetch failure before declaring convergence complete for node '${node.id}'.`
2624
+ };
2625
+ }
2626
+ if (ahead > 0 || behind > 0) {
2627
+ return {
2628
+ ...base,
2629
+ status: "blocked_review",
2630
+ needsConvergence: true,
2631
+ reason: "default_branch_not_even_with_upstream",
2632
+ nextStep: `Bring ${defaultBranch} even with its upstream before declaring convergence complete.`
2633
+ };
2634
+ }
2635
+ return {
2636
+ ...base,
2637
+ status: "merged_to_main",
2638
+ needsConvergence: false,
2639
+ reason: "clean_default_branch",
2640
+ nextStep: null
2641
+ };
2642
+ }
2643
+ if (node.isLocalWorktree) {
2644
+ return {
2645
+ ...base,
2646
+ status: "cleanup_candidate",
2647
+ needsConvergence: true,
2648
+ reason: "clean_non_default_worktree_branch",
2649
+ nextStep: `Run mesh_refine_node(node_id: "${node.id}") or explicitly classify this worktree as blocked_review/not_mergeable before ending the task.`
2650
+ };
2651
+ }
2652
+ if (upstream && upstreamStatus !== "fresh") {
2653
+ return {
2654
+ ...base,
2655
+ status: "blocked_review",
2656
+ needsConvergence: true,
2657
+ reason: "feature_branch_upstream_unverified",
2658
+ nextStep: `Refresh branch '${branch}' upstream refs or resolve the fetch failure before deciding whether it is ready to merge into ${defaultBranch}.`
2659
+ };
2660
+ }
2661
+ if (!upstream || ahead > 0 || behind > 0) {
2662
+ return {
2663
+ ...base,
2664
+ status: "blocked_review",
2665
+ needsConvergence: true,
2666
+ reason: !upstream ? "feature_branch_missing_upstream" : "feature_branch_not_even_with_upstream",
2667
+ nextStep: `Push or reconcile branch '${branch}', then merge it into ${defaultBranch} or mark it not_mergeable with a reason.`
2668
+ };
2669
+ }
2670
+ return {
2671
+ ...base,
2672
+ status: "pushed_feature_branch_needs_merge",
2673
+ needsConvergence: true,
2674
+ reason: "clean_non_default_branch",
2675
+ nextStep: `Review and merge branch '${branch}' into ${defaultBranch}; do not report the task as fully complete while it remains off main.`
2676
+ };
2677
+ }
2678
+ var COMPACT_MAX_CONVERGENCE_FOLLOWUPS = 12;
2679
+ function summarizeBranchConvergence(nodes, compact = false) {
2680
+ const allFollowUps = nodes.filter((node) => node?.branchConvergence?.needsConvergence === true).map((node) => ({
2681
+ nodeId: node.nodeId,
2682
+ // workspace is a long absolute path redundant with nodeId — drop it in
2683
+ // compact mode to keep this summary bounded.
2684
+ ...compact ? {} : { workspace: node.workspace },
2685
+ branch: node.branchConvergence.branch,
2686
+ status: node.branchConvergence.status,
2687
+ reason: node.branchConvergence.reason,
2688
+ // The per-node nextStep is long prose that repeats node ids/branch names.
2689
+ // In compact mode drop it (the status+reason carry the actionable signal;
2690
+ // verbose still surfaces the full nextStep) so this summary stays bounded
2691
+ // as node count grows.
2692
+ ...compact ? {} : { nextStep: node.branchConvergence.nextStep }
2693
+ }));
2694
+ const byStatus = {};
2695
+ for (const f of allFollowUps) {
2696
+ const s = typeof f.status === "string" ? f.status : "unknown";
2697
+ byStatus[s] = (byStatus[s] ?? 0) + 1;
2698
+ }
2699
+ const followUps = compact ? allFollowUps.slice(0, COMPACT_MAX_CONVERGENCE_FOLLOWUPS) : allFollowUps;
2700
+ const omitted = allFollowUps.length - followUps.length;
2701
+ return {
2702
+ needsFollowUp: allFollowUps.length > 0,
2703
+ unresolvedCount: allFollowUps.length,
2704
+ byStatus,
2705
+ requiredFinalStates: ["merged_to_main", "pushed_feature_branch_needs_merge", "blocked_review", "cleanup_candidate", "not_mergeable"],
2706
+ followUps,
2707
+ ...omitted > 0 ? { followUpsOmitted: omitted, followUpsHint: "Per-node followUp rows are capped in compact mode; counts above are complete. Use verbose=true for the full list." } : {}
2708
+ };
2709
+ }
2710
+ async function commandForNode(ctx, node, command, args = {}) {
2711
+ const isLocalNode = isLocalControlPlaneNode(ctx, node);
2712
+ if (ctx.transport instanceof IpcTransport && node.daemonId && !isLocalNode) {
2713
+ return ctx.transport.meshCommand(node.daemonId, command, args);
2714
+ }
2715
+ return ctx.transport.command(command, args);
2716
+ }
2717
+ function normalizePendingMeshCoordinatorEvents(value) {
2718
+ const payload = unwrapCommandPayload(value);
2719
+ const events = Array.isArray(payload?.events) ? payload.events : Array.isArray(value?.events) ? value.events : [];
2720
+ return events.filter((event) => event && typeof event === "object");
2721
+ }
2722
+ function buildMeshForwardPayloadFromPendingEvent(event) {
2723
+ const metadataEvent = event?.metadataEvent && typeof event.metadataEvent === "object" ? event.metadataEvent : {};
2724
+ return {
2725
+ event: readString(event?.event),
2726
+ meshId: readString(event?.meshId),
2727
+ nodeId: readString(event?.nodeId) || readString(metadataEvent.meshNodeId),
2728
+ workspace: readString(event?.workspace) || readString(metadataEvent.workspace),
2729
+ targetSessionId: readString(metadataEvent.targetSessionId) || readString(metadataEvent.sessionId) || readString(metadataEvent.instanceId),
2730
+ providerType: readString(metadataEvent.providerType),
2731
+ providerSessionId: readString(metadataEvent.providerSessionId),
2732
+ finalSummary: readString(metadataEvent.finalSummary) || readString(metadataEvent.summary),
2733
+ jobId: readString(metadataEvent.jobId),
2734
+ interactionId: readString(metadataEvent.interactionId),
2735
+ status: readString(metadataEvent.status),
2736
+ targetDaemonId: readString(metadataEvent.targetDaemonId),
2737
+ startedAt: readString(metadataEvent.startedAt),
2738
+ completedAt: readString(metadataEvent.completedAt),
2739
+ retryOfJobId: readString(metadataEvent.retryOfJobId),
2740
+ ...metadataEvent.result && typeof metadataEvent.result === "object" && !Array.isArray(metadataEvent.result) ? { result: metadataEvent.result } : {},
2741
+ ...metadataEvent.intentional === true ? { intentional: true } : {},
2742
+ ...metadataEvent.intentionalStop === true ? { intentionalStop: true } : {},
2743
+ ...metadataEvent.operatorCleanup === true ? { operatorCleanup: true } : {},
2744
+ ...readString(metadataEvent.reason) ? { reason: readString(metadataEvent.reason) } : {},
2745
+ ...readString(metadataEvent.stopReason) ? { stopReason: readString(metadataEvent.stopReason) } : {},
2746
+ ...readString(metadataEvent.cleanupReason) ? { cleanupReason: readString(metadataEvent.cleanupReason) } : {},
2747
+ ...readString(metadataEvent.source) ? { source: readString(metadataEvent.source) } : {}
2748
+ };
2749
+ }
2750
+ async function drainCoordinatorPendingEvents(ctx, opts) {
2751
+ const requestedNodeIds = opts?.nodeIds?.length ? new Set(opts.nodeIds) : null;
2752
+ const matchesCurrentMesh = (event) => readString(event?.meshId) === ctx.mesh.id;
2753
+ if (ctx.transport instanceof IpcTransport) {
2754
+ const surfacedEvents = [];
2755
+ const coordinatorDaemonId = readString(ctx.localDaemonId);
2756
+ const pendingEventArgs = {
2757
+ meshId: ctx.mesh.id,
2758
+ ...coordinatorDaemonId ? { coordinatorDaemonId } : {}
2759
+ };
2760
+ try {
2761
+ const localEvents = normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", pendingEventArgs)).filter(matchesCurrentMesh);
2762
+ for (const event of localEvents) {
2763
+ const payload = buildMeshForwardPayloadFromPendingEvent(event);
2764
+ if (!payload.event || !payload.meshId) continue;
2765
+ let injected = false;
2766
+ try {
2767
+ await ctx.transport.command("mesh_forward_event", payload);
2768
+ injected = true;
2769
+ } catch {
2770
+ }
2771
+ rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
2772
+ if (!injected) surfacedEvents.push(event);
2773
+ }
2774
+ } catch {
2775
+ }
2776
+ for (const node of ctx.mesh.nodes) {
2777
+ if (!node.daemonId || isLocalControlPlaneNode(ctx, node)) continue;
2778
+ if (requestedNodeIds && !requestedNodeIds.has(node.id)) continue;
2779
+ try {
2780
+ const remoteEvents = normalizePendingMeshCoordinatorEvents(
2781
+ await ctx.transport.meshCommand(node.daemonId, "get_pending_mesh_events", pendingEventArgs)
2782
+ ).filter(matchesCurrentMesh);
2783
+ if (remoteEvents.length === 0) continue;
2784
+ for (const event of remoteEvents) {
2785
+ const payload = buildMeshForwardPayloadFromPendingEvent(event);
2786
+ if (!payload.event || !payload.meshId) continue;
2787
+ await ctx.transport.command("mesh_forward_event", payload);
2788
+ rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
2789
+ }
2790
+ } catch {
2791
+ }
2792
+ }
2793
+ try {
2794
+ const localEvents = normalizePendingMeshCoordinatorEvents(await ctx.transport.command("get_pending_mesh_events", pendingEventArgs)).filter(matchesCurrentMesh);
2795
+ for (const event of localEvents) {
2796
+ const payload = buildMeshForwardPayloadFromPendingEvent(event);
2797
+ if (!payload.event || !payload.meshId) continue;
2798
+ let injected = false;
2799
+ try {
2800
+ await ctx.transport.command("mesh_forward_event", payload);
2801
+ injected = true;
2802
+ } catch {
2803
+ }
2804
+ rememberMeshSessionProviderMetadataFromEvent({ ...event, metadataEvent: payload });
2805
+ if (!injected) surfacedEvents.push(event);
2806
+ }
2807
+ } catch {
2808
+ }
2809
+ return surfacedEvents;
2810
+ }
2811
+ const events = (0, import_daemon_core2.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId).filter(matchesCurrentMesh);
2812
+ events.forEach(rememberMeshSessionProviderMetadataFromEvent);
2813
+ return events;
2814
+ }
2815
+ function isP2pTransportUnavailableError(error) {
2816
+ return (0, import_daemon_core2.isP2pRelayTransportFailure)(error);
2817
+ }
2818
+ function buildRemoveNodeArgs(ctx, nodeId, sessionCleanupMode, force) {
2819
+ return {
2820
+ meshId: ctx.mesh.id,
2821
+ nodeId,
2822
+ ...sessionCleanupMode ? { sessionCleanupMode } : {},
2823
+ ...force === true ? { force: true } : {},
2824
+ inlineMesh: ctx.mesh
2825
+ };
2826
+ }
2812
2827
  async function meshStatus(ctx, args = {}) {
2813
- const rateResult = (0, import_daemon_core.recordMeshToolCall)({ meshId: ctx.mesh.id, tool: "mesh_status" });
2828
+ const rateResult = (0, import_daemon_core2.recordMeshToolCall)({ meshId: ctx.mesh.id, tool: "mesh_status" });
2814
2829
  const compact = args.verbose === true ? false : args.compact ?? true;
2815
2830
  await refreshMeshFromDaemon(ctx);
2816
2831
  const { mesh, transport } = ctx;
2817
- let ledgerSummary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
2832
+ let ledgerSummary = (0, import_daemon_core2.getLedgerSummary)(mesh.id);
2818
2833
  const results = await Promise.all(mesh.nodes.map(async (node) => {
2819
2834
  const entry = {
2820
2835
  nodeId: node.id,
@@ -2870,14 +2885,14 @@ async function meshStatus(ctx, args = {}) {
2870
2885
  noFallbackReason: failure.noFallbackReason
2871
2886
  });
2872
2887
  }
2873
- entry.dataFreshness = (0, import_daemon_core.buildMeshNodeProbeFreshness)({
2888
+ entry.dataFreshness = (0, import_daemon_core2.buildMeshNodeProbeFreshness)({
2874
2889
  git: entry.git,
2875
2890
  liveTruthProbed,
2876
2891
  isSelfNode: entry.machine?.sameMachine === true,
2877
2892
  daemonId: readNodeDaemonId(node),
2878
2893
  node
2879
2894
  });
2880
- const recoveryContext = (0, import_daemon_core.getSessionRecoveryContext)(mesh.id, { nodeId: node.id });
2895
+ const recoveryContext = (0, import_daemon_core2.getSessionRecoveryContext)(mesh.id, { nodeId: node.id });
2881
2896
  if (recoveryContext.consecutiveNodeFailures > 0) {
2882
2897
  entry.recoveryHints = {
2883
2898
  consecutiveFailures: recoveryContext.consecutiveNodeFailures,
@@ -2949,23 +2964,23 @@ async function meshStatus(ctx, args = {}) {
2949
2964
  }
2950
2965
  return entry;
2951
2966
  }));
2952
- let ledgerEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 200 });
2953
- let directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(mesh.id);
2967
+ let ledgerEntries = (0, import_daemon_core2.readLedgerEntries)(mesh.id, { tail: 200 });
2968
+ let directDispatches = (0, import_daemon_core2.getActiveDirectDispatches)(mesh.id);
2954
2969
  const directReconciliation = await reconcileDirectDispatchesFromTranscriptEvidence(ctx, results, directDispatches, ledgerEntries);
2955
2970
  if (directReconciliation.reconciled > 0) {
2956
- ledgerEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail: 200 });
2957
- directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(mesh.id);
2958
- ledgerSummary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
2971
+ ledgerEntries = (0, import_daemon_core2.readLedgerEntries)(mesh.id, { tail: 200 });
2972
+ directDispatches = (0, import_daemon_core2.getActiveDirectDispatches)(mesh.id);
2973
+ ledgerSummary = (0, import_daemon_core2.getLedgerSummary)(mesh.id);
2959
2974
  }
2960
- const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
2975
+ const activeWorkEvidence = (0, import_daemon_core2.buildMeshActiveWork)({
2961
2976
  meshId: mesh.id,
2962
- queue: (0, import_daemon_core.getQueue)(mesh.id),
2977
+ queue: (0, import_daemon_core2.getQueue)(mesh.id),
2963
2978
  ledgerEntries,
2964
2979
  directDispatches,
2965
2980
  nodes: results
2966
2981
  });
2967
2982
  const pollingGuidance = buildActiveWorkPollingGuidance(activeWorkEvidence.summary);
2968
- const staleDirectWorkSummary = (0, import_daemon_core.buildCompactStaleDirectWorkSummary)(activeWorkEvidence.staleDirectWork, {
2983
+ const staleDirectWorkSummary = (0, import_daemon_core2.buildCompactStaleDirectWorkSummary)(activeWorkEvidence.staleDirectWork, {
2969
2984
  note: activeWorkEvidence.staleDirectWorkNote,
2970
2985
  detailHint: "Full stale direct entries are omitted from mesh_status by default. Call mesh_status with includeStaleDirectWorkDetails=true or inspect mesh_task_history for ledger detail."
2971
2986
  });
@@ -3153,7 +3168,7 @@ async function meshStatus(ctx, args = {}) {
3153
3168
  }
3154
3169
  try {
3155
3170
  if (compact) {
3156
- const { live, historyFold } = (0, import_daemon_core.getMeshStatusMissionsCompact)(mesh.id);
3171
+ const { live, historyFold } = (0, import_daemon_core2.getMeshStatusMissionsCompact)(mesh.id);
3157
3172
  const ranked = [...live].sort((a, b) => String(b.tasks?.lastActivityAt ?? "").localeCompare(String(a.tasks?.lastActivityAt ?? "")));
3158
3173
  const kept = [];
3159
3174
  const overflow = [];
@@ -3180,11 +3195,11 @@ async function meshStatus(ctx, args = {}) {
3180
3195
  }
3181
3196
  if (historyFold) response.missionsHistory = historyFold;
3182
3197
  } else {
3183
- const missions = (0, import_daemon_core.getMeshStatusMissionSummaries)(mesh.id, { verbose: true });
3198
+ const missions = (0, import_daemon_core2.getMeshStatusMissionSummaries)(mesh.id, { verbose: true });
3184
3199
  if (missions.length > 0) {
3185
3200
  response.missions = missions.map((mission) => {
3186
3201
  try {
3187
- return { ...mission, stats: (0, import_daemon_core.computeMeshMissionStats)(mesh.id, mission.id) };
3202
+ return { ...mission, stats: (0, import_daemon_core2.computeMeshMissionStats)(mesh.id, mission.id) };
3188
3203
  } catch {
3189
3204
  return mission;
3190
3205
  }
@@ -3195,14 +3210,14 @@ async function meshStatus(ctx, args = {}) {
3195
3210
  }
3196
3211
  try {
3197
3212
  const pendingEvents = await drainCoordinatorPendingEvents(ctx);
3198
- const asyncRefineJobs = (0, import_daemon_core.buildMeshAsyncRefineJobs)({
3213
+ const asyncRefineJobs = (0, import_daemon_core2.buildMeshAsyncRefineJobs)({
3199
3214
  meshId: mesh.id,
3200
3215
  ledgerEntries,
3201
3216
  pendingEvents
3202
3217
  });
3203
3218
  if (asyncRefineJobs.length > 0) {
3204
3219
  if (compact) {
3205
- const summary = (0, import_daemon_core.summarizeMeshAsyncRefineJobs)(asyncRefineJobs);
3220
+ const summary = (0, import_daemon_core2.summarizeMeshAsyncRefineJobs)(asyncRefineJobs);
3206
3221
  if (summary.activeJobs.length > 0) response.asyncRefineJobs = summary.activeJobs;
3207
3222
  response.asyncRefineJobsSummary = {
3208
3223
  total: summary.total,
@@ -3228,17 +3243,17 @@ async function meshTaskHistory(ctx, args) {
3228
3243
  const compactCap = requestedTail > 50 ? 20 : 30;
3229
3244
  const tail = compact ? Math.min(requestedTail, compactCap) : Math.min(requestedTail, 200);
3230
3245
  const kind = typeof args.kind === "string" && args.kind.trim() ? [args.kind.trim()] : void 0;
3231
- const rawEntries = (0, import_daemon_core.readLedgerEntries)(mesh.id, { tail, kind });
3246
+ const rawEntries = (0, import_daemon_core2.readLedgerEntries)(mesh.id, { tail, kind });
3232
3247
  const entries = compact ? rawEntries.map((e) => ({
3233
3248
  ...e,
3234
3249
  payload: e.payload ? slimLedgerPayload(e.payload) : e.payload
3235
3250
  })) : rawEntries;
3236
- const summary = (0, import_daemon_core.getLedgerSummary)(mesh.id);
3251
+ const summary = (0, import_daemon_core2.getLedgerSummary)(mesh.id);
3237
3252
  let taskStats;
3238
3253
  try {
3239
3254
  const taskIds = [...new Set(rawEntries.map((e) => typeof e.payload?.taskId === "string" ? e.payload.taskId : "").filter(Boolean))];
3240
3255
  if (taskIds.length > 0) {
3241
- const stats = (0, import_daemon_core.computeMeshTaskStats)(mesh.id, { taskIds });
3256
+ const stats = (0, import_daemon_core2.computeMeshTaskStats)(mesh.id, { taskIds });
3242
3257
  if (stats.length > 0) taskStats = stats;
3243
3258
  }
3244
3259
  } catch {
@@ -3267,8 +3282,8 @@ async function meshReconcileLedger(ctx, args) {
3267
3282
  for (const node of nodes) {
3268
3283
  try {
3269
3284
  if (isLocalControlPlaneNode(ctx, node) || !node.daemonId) {
3270
- const slice2 = (0, import_daemon_core.readLedgerSliceFromStore)(ctx.mesh.id, queryArgs);
3271
- replicas.push((0, import_daemon_core.buildMeshLedgerReplicaEvidence)({
3285
+ const slice2 = (0, import_daemon_core2.readLedgerSliceFromStore)(ctx.mesh.id, queryArgs);
3286
+ replicas.push((0, import_daemon_core2.buildMeshLedgerReplicaEvidence)({
3272
3287
  nodeId: node.id,
3273
3288
  daemonId: node.daemonId,
3274
3289
  transport: "local",
@@ -3286,8 +3301,8 @@ async function meshReconcileLedger(ctx, args) {
3286
3301
  if (slice?.protocol !== "adhdev.mesh.ledger.slice.v1" || !Array.isArray(slice.entries)) {
3287
3302
  throw new Error("remote daemon returned an invalid ledger slice payload");
3288
3303
  }
3289
- const importResult = shouldImport ? (0, import_daemon_core.appendRemoteLedgerEntries)(ctx.mesh.id, slice.entries) : { accepted: 0, skippedDuplicate: 0, rejectedInvalid: 0, entries: [] };
3290
- replicas.push((0, import_daemon_core.buildMeshLedgerReplicaEvidence)({
3304
+ const importResult = shouldImport ? (0, import_daemon_core2.appendRemoteLedgerEntries)(ctx.mesh.id, slice.entries) : { accepted: 0, skippedDuplicate: 0, rejectedInvalid: 0, entries: [] };
3305
+ replicas.push((0, import_daemon_core2.buildMeshLedgerReplicaEvidence)({
3291
3306
  nodeId: node.id,
3292
3307
  daemonId: node.daemonId,
3293
3308
  transport: "p2p_datachannel",
@@ -3295,7 +3310,7 @@ async function meshReconcileLedger(ctx, args) {
3295
3310
  importResult
3296
3311
  }));
3297
3312
  if (shouldImport && importResult.accepted > 0) {
3298
- (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
3313
+ (0, import_daemon_core2.appendLedgerEntry)(ctx.mesh.id, {
3299
3314
  kind: "ledger_replicated",
3300
3315
  nodeId: node.id,
3301
3316
  payload: {
@@ -3309,7 +3324,7 @@ async function meshReconcileLedger(ctx, args) {
3309
3324
  });
3310
3325
  }
3311
3326
  } catch (e) {
3312
- replicas.push((0, import_daemon_core.buildMeshLedgerReplicaEvidence)({
3327
+ replicas.push((0, import_daemon_core2.buildMeshLedgerReplicaEvidence)({
3313
3328
  nodeId: node.id,
3314
3329
  daemonId: node.daemonId,
3315
3330
  transport: node.daemonId ? "p2p_datachannel" : "local",
@@ -3318,8 +3333,8 @@ async function meshReconcileLedger(ctx, args) {
3318
3333
  }));
3319
3334
  }
3320
3335
  }
3321
- const evidence = (0, import_daemon_core.buildMeshLedgerReconciliationEvidence)(ctx.mesh.id, replicas);
3322
- (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
3336
+ const evidence = (0, import_daemon_core2.buildMeshLedgerReconciliationEvidence)(ctx.mesh.id, replicas);
3337
+ (0, import_daemon_core2.appendLedgerEntry)(ctx.mesh.id, {
3323
3338
  kind: "ledger_reconciled",
3324
3339
  payload: {
3325
3340
  protocol: evidence.protocol,
@@ -3335,11 +3350,11 @@ async function meshPruneStaleDirect(ctx, args = {}) {
3335
3350
  const execute = args.execute === true && args.dry_run !== true;
3336
3351
  const includeTerminal = args.include_terminal === true;
3337
3352
  const liveNodes = await collectMeshViewQueueNodesWithLiveSessions(ctx);
3338
- const ledgerEntries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 500 });
3339
- const directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
3340
- const result = (0, import_daemon_core.pruneStaleDirectDispatches)({
3353
+ const ledgerEntries = (0, import_daemon_core2.readLedgerEntries)(ctx.mesh.id, { tail: 500 });
3354
+ const directDispatches = (0, import_daemon_core2.getActiveDirectDispatches)(ctx.mesh.id);
3355
+ const result = (0, import_daemon_core2.pruneStaleDirectDispatches)({
3341
3356
  meshId: ctx.mesh.id,
3342
- queue: (0, import_daemon_core.getQueue)(ctx.mesh.id),
3357
+ queue: (0, import_daemon_core2.getQueue)(ctx.mesh.id),
3343
3358
  ledgerEntries,
3344
3359
  directDispatches,
3345
3360
  nodes: liveNodes,
@@ -3402,7 +3417,7 @@ async function meshListNodes(ctx) {
3402
3417
  }
3403
3418
  async function meshMissionUpsert(ctx, args) {
3404
3419
  try {
3405
- const mission = (0, import_daemon_core.upsertMeshMission)(ctx.mesh.id, {
3420
+ const mission = (0, import_daemon_core2.upsertMeshMission)(ctx.mesh.id, {
3406
3421
  id: readString(args.mission_id) || readString(args.missionId) || void 0,
3407
3422
  title: args.title,
3408
3423
  goal: typeof args.goal === "string" ? args.goal : void 0,
@@ -3422,21 +3437,21 @@ async function meshMissionUpsert(ctx, args) {
3422
3437
  async function meshMissionList(ctx, args = {}) {
3423
3438
  try {
3424
3439
  const rawStatuses = Array.isArray(args.status) ? args.status : typeof args.status === "string" && args.status.trim() ? [args.status] : [];
3425
- const invalid = rawStatuses.filter((s) => !import_daemon_core.MESH_MISSION_STATUSES.includes(s));
3440
+ const invalid = rawStatuses.filter((s) => !import_daemon_core2.MESH_MISSION_STATUSES.includes(s));
3426
3441
  if (invalid.length > 0) {
3427
3442
  return JSON.stringify({
3428
3443
  success: false,
3429
3444
  code: "invalid_mission_status",
3430
- error: `invalid status filter: ${invalid.join(", ")} (valid: ${import_daemon_core.MESH_MISSION_STATUSES.join(", ")})`
3445
+ error: `invalid status filter: ${invalid.join(", ")} (valid: ${import_daemon_core2.MESH_MISSION_STATUSES.join(", ")})`
3431
3446
  });
3432
3447
  }
3433
3448
  const statuses = rawStatuses.length > 0 ? rawStatuses : void 0;
3434
- const missions = (0, import_daemon_core.listMeshMissionSummaries)(ctx.mesh.id, {
3449
+ const missions = (0, import_daemon_core2.listMeshMissionSummaries)(ctx.mesh.id, {
3435
3450
  statuses,
3436
3451
  verbose: args.verbose === true
3437
3452
  }).map((mission) => {
3438
3453
  try {
3439
- return { ...mission, stats: (0, import_daemon_core.computeMeshMissionStats)(ctx.mesh.id, mission.id) };
3454
+ return { ...mission, stats: (0, import_daemon_core2.computeMeshMissionStats)(ctx.mesh.id, mission.id) };
3440
3455
  } catch {
3441
3456
  return mission;
3442
3457
  }
@@ -3453,14 +3468,14 @@ async function meshMissionList(ctx, args = {}) {
3453
3468
  }
3454
3469
  async function meshEnqueueTask(ctx, args) {
3455
3470
  const taskMode = readString(args.task_mode) || readString(args.taskMode);
3456
- const requiredTags = (0, import_daemon_core.normalizeMeshCapabilityTags)(Array.isArray(args.requiredTags) ? args.requiredTags : args.required_tags);
3471
+ const requiredTags = (0, import_daemon_core2.normalizeMeshCapabilityTags)(Array.isArray(args.requiredTags) ? args.requiredTags : args.required_tags);
3457
3472
  const dependsOn = Array.isArray(args.dependsOn) ? args.dependsOn : Array.isArray(args.depends_on) ? args.depends_on : void 0;
3458
3473
  const missionId = readString(args.missionId) || readString(args.mission_id) || void 0;
3459
3474
  const explicitTarget = readString(args.targetNodeId) || readString(args.target_node_id) || void 0;
3460
3475
  const preferWorktree = args.preferWorktree === true || args.prefer_worktree === true;
3461
3476
  const targetNodeId = explicitTarget || (preferWorktree ? resolvePreferredWorktreeNodeId(ctx) : void 0);
3462
3477
  try {
3463
- const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, { taskMode, requiredTags, dependsOn, missionId, targetNodeId, ...ctx.coordinatorSessionId ? { sourceCoordinatorSessionId: ctx.coordinatorSessionId } : {} });
3478
+ const task = (0, import_daemon_core2.enqueueTask)(ctx.mesh.id, args.message, { taskMode, requiredTags, dependsOn, missionId, targetNodeId, ...ctx.coordinatorSessionId ? { sourceCoordinatorSessionId: ctx.coordinatorSessionId } : {} });
3464
3479
  if (!(ctx.transport instanceof IpcTransport)) {
3465
3480
  const queueTrigger = await triggerMeshQueueAndReport(ctx);
3466
3481
  return JSON.stringify({
@@ -3483,14 +3498,14 @@ async function meshEnqueueTask(ctx, args) {
3483
3498
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
3484
3499
  if (isLocalNode || !node.daemonId) continue;
3485
3500
  if (targetNodeId && node.id !== targetNodeId) continue;
3486
- if (!(0, import_daemon_core.nodeSatisfiesRequiredTags)(requiredTags, (0, import_daemon_core.buildMeshNodeCapabilityTags)(node))) continue;
3501
+ if (!(0, import_daemon_core2.nodeSatisfiesRequiredTags)(requiredTags, (0, import_daemon_core2.buildMeshNodeCapabilityTags)(node))) continue;
3487
3502
  dispatchPromises.push(
3488
3503
  ipcDispatchToRemoteAgent(ctx, node, { message: args.message }).then((result) => {
3489
3504
  if (result.success) {
3490
3505
  try {
3491
3506
  const providerType = result.providerType;
3492
3507
  const descriptor = summarizeTaskMessage(args.message);
3493
- (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
3508
+ (0, import_daemon_core2.appendLedgerEntry)(ctx.mesh.id, {
3494
3509
  kind: "task_dispatched",
3495
3510
  nodeId: node.id,
3496
3511
  sessionId: result.sessionId,
@@ -3512,7 +3527,7 @@ async function meshEnqueueTask(ctx, args) {
3512
3527
  }
3513
3528
  }).catch((err) => {
3514
3529
  try {
3515
- (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
3530
+ (0, import_daemon_core2.appendLedgerEntry)(ctx.mesh.id, {
3516
3531
  kind: "p2p_dispatch_failed",
3517
3532
  nodeId: node.id,
3518
3533
  payload: {
@@ -3555,17 +3570,17 @@ async function meshEnqueueTask(ctx, args) {
3555
3570
  }
3556
3571
  }
3557
3572
  async function meshViewQueue(ctx, args) {
3558
- const rateResult = (0, import_daemon_core.recordMeshToolCall)({ meshId: ctx.mesh.id, tool: "mesh_view_queue" });
3573
+ const rateResult = (0, import_daemon_core2.recordMeshToolCall)({ meshId: ctx.mesh.id, tool: "mesh_view_queue" });
3559
3574
  const compact = args.verbose === true ? false : args.compact ?? true;
3560
3575
  try {
3561
3576
  await refreshMeshFromDaemon(ctx);
3562
3577
  const statusFilter = sanitizeQueueStatusFilter(args.status);
3563
3578
  const view = normalizeQueueViewMode(args.view);
3564
- const rawQueue = (0, import_daemon_core.getQueue)(ctx.mesh.id);
3579
+ const rawQueue = (0, import_daemon_core2.getQueue)(ctx.mesh.id);
3565
3580
  const statusById = new Map(rawQueue.map((task) => [task.id, task.status]));
3566
3581
  const withDependencies = rawQueue.map((task) => {
3567
3582
  if (!Array.isArray(task.dependsOn) || task.dependsOn.length === 0) return task;
3568
- const depState = (0, import_daemon_core.describeTaskDependencyState)(task, statusById);
3583
+ const depState = (0, import_daemon_core2.describeTaskDependencyState)(task, statusById);
3569
3584
  return { ...task, ...depState };
3570
3585
  });
3571
3586
  const fullQueue = prioritizeActiveQueueRows(annotateQueueStaleness(withDependencies, ctx.mesh));
@@ -3574,16 +3589,16 @@ async function meshViewQueue(ctx, args) {
3574
3589
  const visibleSummary = buildQueueStatusSummary(queue);
3575
3590
  const maintenance = buildQueueMaintenanceReport(fullQueue);
3576
3591
  const liveNodes = await collectMeshViewQueueNodesWithLiveSessions(ctx);
3577
- let ledgerEntries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
3578
- let directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
3592
+ let ledgerEntries = (0, import_daemon_core2.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
3593
+ let directDispatches = (0, import_daemon_core2.getActiveDirectDispatches)(ctx.mesh.id);
3579
3594
  const directReconciliation = await reconcileDirectDispatchesFromTranscriptEvidence(ctx, liveNodes, directDispatches, ledgerEntries);
3580
3595
  if (directReconciliation.reconciled > 0) {
3581
- ledgerEntries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
3582
- directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
3596
+ ledgerEntries = (0, import_daemon_core2.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
3597
+ directDispatches = (0, import_daemon_core2.getActiveDirectDispatches)(ctx.mesh.id);
3583
3598
  }
3584
- (0, import_daemon_core.markStaleDirectDispatches)(ctx.mesh.id);
3585
- directDispatches = (0, import_daemon_core.getActiveDirectDispatches)(ctx.mesh.id);
3586
- const activeWorkEvidence = (0, import_daemon_core.buildMeshActiveWork)({
3599
+ (0, import_daemon_core2.markStaleDirectDispatches)(ctx.mesh.id);
3600
+ directDispatches = (0, import_daemon_core2.getActiveDirectDispatches)(ctx.mesh.id);
3601
+ const activeWorkEvidence = (0, import_daemon_core2.buildMeshActiveWork)({
3587
3602
  meshId: ctx.mesh.id,
3588
3603
  queue: fullQueue,
3589
3604
  ledgerEntries,
@@ -3608,7 +3623,7 @@ async function meshViewQueue(ctx, args) {
3608
3623
  const wantActiveQueueArray = view === "active" || statusFilter?.some((status) => ACTIVE_QUEUE_STATUSES.has(status));
3609
3624
  const wantHistoricalQueueArray = !compact && (view === "historical" || requestedHistoricalRows);
3610
3625
  const activeWorkResult = compact ? compactActiveWorkRecords(activeWorkEvidence.activeWork) : { records: activeWorkEvidence.activeWork, omitted: 0 };
3611
- const staleDirectWorkSummary = (0, import_daemon_core.buildCompactStaleDirectWorkSummary)(activeWorkEvidence.staleDirectWork, {
3626
+ const staleDirectWorkSummary = (0, import_daemon_core2.buildCompactStaleDirectWorkSummary)(activeWorkEvidence.staleDirectWork, {
3612
3627
  note: activeWorkEvidence.staleDirectWorkNote,
3613
3628
  detailHint: "Full stale direct entries are omitted from mesh_view_queue in compact mode. Call mesh_view_queue with verbose=true, or inspect mesh_task_history for ledger detail."
3614
3629
  });
@@ -3683,7 +3698,7 @@ async function meshQueueCancel(ctx, args) {
3683
3698
  try {
3684
3699
  const taskId = (args.task_id || args.taskId || "").trim();
3685
3700
  if (!taskId) return JSON.stringify({ success: false, error: "task_id required" });
3686
- const task = (0, import_daemon_core.cancelTask)(ctx.mesh.id, taskId, { reason: args.reason });
3701
+ const task = (0, import_daemon_core2.cancelTask)(ctx.mesh.id, taskId, { reason: args.reason });
3687
3702
  if (!task) return JSON.stringify({ success: false, error: `Queue task '${taskId}' not found` });
3688
3703
  ctx.transport.command("trigger_mesh_queue", { meshId: ctx.mesh.id }).catch(() => {
3689
3704
  });
@@ -3699,7 +3714,7 @@ async function meshQueueRequeue(ctx, args) {
3699
3714
  const targetNodeId = (args.target_node_id || args.targetNodeId || "").trim() || void 0;
3700
3715
  const targetSessionId = (args.target_session_id || args.targetSessionId || "").trim() || void 0;
3701
3716
  const keepTargetSession = args.keep_target_session === true || args.keepTargetSession === true;
3702
- const task = (0, import_daemon_core.requeueTask)(ctx.mesh.id, taskId, {
3717
+ const task = (0, import_daemon_core2.requeueTask)(ctx.mesh.id, taskId, {
3703
3718
  reason: args.reason,
3704
3719
  targetNodeId,
3705
3720
  targetSessionId,
@@ -3731,7 +3746,7 @@ async function meshQueueRequeue(ctx, args) {
3731
3746
  async function meshSendTask(ctx, args) {
3732
3747
  const requestedTaskMode = readString(args.task_mode) || readString(args.taskMode);
3733
3748
  const missionId = readString(args.missionId) || readString(args.mission_id) || void 0;
3734
- const modeValidation = (0, import_daemon_core.validateMeshTaskModeRequest)(requestedTaskMode, args.message);
3749
+ const modeValidation = (0, import_daemon_core2.validateMeshTaskModeRequest)(requestedTaskMode, args.message);
3735
3750
  if (!modeValidation.valid) {
3736
3751
  return JSON.stringify({
3737
3752
  success: false,
@@ -3843,7 +3858,7 @@ async function meshSendTask(ctx, args) {
3843
3858
  const dispatchedAt = (/* @__PURE__ */ new Date()).toISOString();
3844
3859
  try {
3845
3860
  const providerType = result2.providerType || cached?.providerType;
3846
- (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
3861
+ (0, import_daemon_core2.appendLedgerEntry)(ctx.mesh.id, {
3847
3862
  kind: "task_dispatched",
3848
3863
  nodeId: args.node_id,
3849
3864
  sessionId: dispatchedSessionId,
@@ -3855,7 +3870,7 @@ async function meshSendTask(ctx, args) {
3855
3870
  targetSessionId: dispatchedSessionId
3856
3871
  })
3857
3872
  });
3858
- (0, import_daemon_core.insertDirectDispatch)(ctx.mesh.id, {
3873
+ (0, import_daemon_core2.insertDirectDispatch)(ctx.mesh.id, {
3859
3874
  taskId,
3860
3875
  nodeId: args.node_id,
3861
3876
  sessionId: dispatchedSessionId,
@@ -3866,7 +3881,7 @@ async function meshSendTask(ctx, args) {
3866
3881
  dispatchedAt
3867
3882
  });
3868
3883
  if (missionId) {
3869
- (0, import_daemon_core.recordDirectDispatchTask)(ctx.mesh.id, args.message, {
3884
+ (0, import_daemon_core2.recordDirectDispatchTask)(ctx.mesh.id, args.message, {
3870
3885
  id: taskId,
3871
3886
  missionId,
3872
3887
  assignedNodeId: args.node_id,
@@ -4025,7 +4040,7 @@ async function meshSendTask(ctx, args) {
4025
4040
  });
4026
4041
  }
4027
4042
  try {
4028
- (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
4043
+ (0, import_daemon_core2.appendLedgerEntry)(ctx.mesh.id, {
4029
4044
  kind: "task_dispatched",
4030
4045
  nodeId: args.node_id,
4031
4046
  sessionId: args.session_id,
@@ -4040,7 +4055,7 @@ async function meshSendTask(ctx, args) {
4040
4055
  });
4041
4056
  } catch {
4042
4057
  }
4043
- (0, import_daemon_core.insertDirectDispatch)(ctx.mesh.id, {
4058
+ (0, import_daemon_core2.insertDirectDispatch)(ctx.mesh.id, {
4044
4059
  taskId,
4045
4060
  nodeId: args.node_id,
4046
4061
  sessionId: args.session_id,
@@ -4053,7 +4068,7 @@ async function meshSendTask(ctx, args) {
4053
4068
  });
4054
4069
  if (missionId) {
4055
4070
  try {
4056
- (0, import_daemon_core.recordDirectDispatchTask)(ctx.mesh.id, args.message, {
4071
+ (0, import_daemon_core2.recordDirectDispatchTask)(ctx.mesh.id, args.message, {
4057
4072
  id: taskId,
4058
4073
  missionId,
4059
4074
  assignedNodeId: args.node_id,
@@ -4098,14 +4113,14 @@ async function meshSendTask(ctx, args) {
4098
4113
  } : {}
4099
4114
  });
4100
4115
  }
4101
- const task = (0, import_daemon_core.enqueueTask)(ctx.mesh.id, args.message, {
4116
+ const task = (0, import_daemon_core2.enqueueTask)(ctx.mesh.id, args.message, {
4102
4117
  targetNodeId: args.node_id,
4103
4118
  targetSessionId: args.session_id,
4104
4119
  taskMode,
4105
4120
  ...missionId ? { missionId } : {}
4106
4121
  });
4107
4122
  const queueTrigger = await triggerMeshQueueAndReport(ctx);
4108
- const pendingEvents = (0, import_daemon_core.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId);
4123
+ const pendingEvents = (0, import_daemon_core2.drainPendingMeshCoordinatorEvents)(ctx.mesh.id, ctx.localDaemonId);
4109
4124
  const result = {
4110
4125
  success: true,
4111
4126
  source: "queue",
@@ -4138,7 +4153,7 @@ function classifyReadChatTransportCause(error) {
4138
4153
  return "saturated";
4139
4154
  }
4140
4155
  function resolveCachedMeshSessionPreviewFromLedger(ctx, nodeId, sessionId) {
4141
- const entries = (0, import_daemon_core.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
4156
+ const entries = (0, import_daemon_core2.readLedgerEntries)(ctx.mesh.id, { tail: 200 });
4142
4157
  for (let i = entries.length - 1; i >= 0; i -= 1) {
4143
4158
  const entry = entries[i];
4144
4159
  const payload = entry.payload && typeof entry.payload === "object" && !Array.isArray(entry.payload) ? entry.payload : {};
@@ -4147,7 +4162,7 @@ function resolveCachedMeshSessionPreviewFromLedger(ctx, nodeId, sessionId) {
4147
4162
  const entrySessionId = readString(entry.sessionId) || readString(payload.targetSessionId) || readString(payload.sessionId) || readString(payload.instanceId);
4148
4163
  if (entrySessionId !== sessionId) continue;
4149
4164
  const metadataEvent = payload.metadataEvent && typeof payload.metadataEvent === "object" && !Array.isArray(payload.metadataEvent) ? payload.metadataEvent : payload;
4150
- const preview = (0, import_daemon_core.resolveMeshSurfacedSessionPreview)(metadataEvent);
4165
+ const preview = (0, import_daemon_core2.resolveMeshSurfacedSessionPreview)(metadataEvent);
4151
4166
  if (preview) {
4152
4167
  return { ...preview, ledgerKind: entry.kind, timestamp: entry.timestamp };
4153
4168
  }
@@ -4155,7 +4170,7 @@ function resolveCachedMeshSessionPreviewFromLedger(ctx, nodeId, sessionId) {
4155
4170
  return void 0;
4156
4171
  }
4157
4172
  function buildMeshReadChatCacheFallback(ctx, args, node, error) {
4158
- const classification = (0, import_daemon_core.classifyP2pRelayFailure)(error, { command: "read_chat", targetDaemonId: node.daemonId });
4173
+ const classification = (0, import_daemon_core2.classifyP2pRelayFailure)(error, { command: "read_chat", targetDaemonId: node.daemonId });
4159
4174
  const cause = classifyReadChatTransportCause(error);
4160
4175
  const errorMessage = error instanceof Error ? error.message : String(error ?? "");
4161
4176
  const causeNote = cause === "not_connected" ? "the worker daemon is not currently connected over P2P (no live channel)" : "the worker daemon is connected but saturated \u2014 it acknowledged the request but did not return the transcript within the deadline";
@@ -4225,7 +4240,7 @@ async function meshReadChat(ctx, args) {
4225
4240
  tailLimit: args.tail ?? 10
4226
4241
  });
4227
4242
  } catch (e) {
4228
- if (isLocalNode || !(0, import_daemon_core.isP2pRelayTransportFailure)(e)) throw e;
4243
+ if (isLocalNode || !(0, import_daemon_core2.isP2pRelayTransportFailure)(e)) throw e;
4229
4244
  return buildMeshReadChatCacheFallback(ctx, args, node, e);
4230
4245
  }
4231
4246
  const payload = annotateRapidReadChatAdvisory(unwrapCommandPayload(result), {
@@ -4293,7 +4308,7 @@ async function meshLaunchSession(ctx, args) {
4293
4308
  const coordinatorNode = resolveCoordinatorNode(ctx);
4294
4309
  const coordinatorDaemonId = resolveCoordinatorDaemonId(ctx);
4295
4310
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
4296
- const delegatedWorkerAutoApprove = (0, import_daemon_core.resolveDelegatedWorkerAutoApprove)(ctx.mesh.policy, node.policy);
4311
+ const delegatedWorkerAutoApprove = (0, import_daemon_core2.resolveDelegatedWorkerAutoApprove)(ctx.mesh.policy, node.policy);
4297
4312
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
4298
4313
  if (node.daemonId && !isLocalNode && !coordinatorDaemonId) {
4299
4314
  return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
@@ -4340,7 +4355,7 @@ async function meshLaunchSession(ctx, args) {
4340
4355
  });
4341
4356
  }
4342
4357
  try {
4343
- (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
4358
+ (0, import_daemon_core2.appendLedgerEntry)(ctx.mesh.id, {
4344
4359
  kind: "session_launched",
4345
4360
  nodeId: args.node_id,
4346
4361
  sessionId: runtimeSessionId || void 0,
@@ -4493,7 +4508,7 @@ async function meshCheckpoint(ctx, args) {
4493
4508
  includeUntracked: true
4494
4509
  });
4495
4510
  try {
4496
- (0, import_daemon_core.appendLedgerEntry)(ctx.mesh.id, {
4511
+ (0, import_daemon_core2.appendLedgerEntry)(ctx.mesh.id, {
4497
4512
  kind: "checkpoint_created",
4498
4513
  nodeId: args.node_id,
4499
4514
  payload: {