adhdev 0.9.82-rc.7 → 0.9.82-rc.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -784,8 +784,14 @@ async function getGitRepoStatus(workspace, options = {}) {
784
784
  const includeSubmodules = options.includeSubmodules !== false;
785
785
  try {
786
786
  const repo = await resolveGitRepository(workspace, options);
787
- const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
788
- const parsed = parsePorcelainV2Status(statusOutput.stdout);
787
+ let parsed = await readPorcelainStatus(repo, options);
788
+ let upstreamProbe = getInitialUpstreamProbe(parsed);
789
+ if (options.refreshUpstream) {
790
+ upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
791
+ if (upstreamProbe.upstreamStatus === "fresh") {
792
+ parsed = await readPorcelainStatus(repo, options);
793
+ }
794
+ }
789
795
  const head = await readHead(repo, options);
790
796
  const stashCount = await readStashCount(repo, options);
791
797
  let submodules;
@@ -800,6 +806,9 @@ async function getGitRepoStatus(workspace, options = {}) {
800
806
  headCommit: head.commit,
801
807
  headMessage: head.message,
802
808
  upstream: parsed.upstream,
809
+ upstreamStatus: parsed.upstream ? upstreamProbe.upstreamStatus : "no_upstream",
810
+ upstreamFetchedAt: upstreamProbe.upstreamFetchedAt,
811
+ upstreamFetchError: upstreamProbe.upstreamFetchError,
803
812
  ahead: parsed.ahead,
804
813
  behind: parsed.behind,
805
814
  staged: parsed.staged,
@@ -824,6 +833,60 @@ async function getGitRepoStatus(workspace, options = {}) {
824
833
  );
825
834
  }
826
835
  }
836
+ async function readPorcelainStatus(repo, options) {
837
+ const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
838
+ return parsePorcelainV2Status(statusOutput.stdout);
839
+ }
840
+ function getInitialUpstreamProbe(parsed) {
841
+ return {
842
+ upstreamStatus: parsed.upstream ? "unchecked" : "no_upstream"
843
+ };
844
+ }
845
+ async function refreshTrackedUpstream(repo, parsed, options) {
846
+ if (!parsed.upstream || !parsed.branch) {
847
+ return { upstreamStatus: "no_upstream" };
848
+ }
849
+ const remoteName = await readBranchRemote(repo, parsed.branch, options) ?? inferRemoteName(parsed.upstream);
850
+ if (!remoteName) {
851
+ return {
852
+ upstreamStatus: "stale",
853
+ upstreamFetchError: `Unable to resolve remote for upstream '${parsed.upstream}'`
854
+ };
855
+ }
856
+ try {
857
+ await runGit(repo, ["fetch", "--quiet", "--prune", "--no-tags", remoteName], options);
858
+ return {
859
+ upstreamStatus: "fresh",
860
+ upstreamFetchedAt: Date.now()
861
+ };
862
+ } catch (error48) {
863
+ return {
864
+ upstreamStatus: "stale",
865
+ upstreamFetchError: formatGitError(error48)
866
+ };
867
+ }
868
+ }
869
+ async function readBranchRemote(repo, branch, options) {
870
+ try {
871
+ const result = await runGit(repo, ["config", "--get", `branch.${branch}.remote`], options);
872
+ return result.stdout.trim() || null;
873
+ } catch {
874
+ return null;
875
+ }
876
+ }
877
+ function inferRemoteName(upstream) {
878
+ const [remoteName] = upstream.split("/");
879
+ return remoteName?.trim() || null;
880
+ }
881
+ function formatGitError(error48) {
882
+ if (error48 instanceof GitCommandError) {
883
+ return error48.stderr || error48.message;
884
+ }
885
+ if (error48 instanceof Error) {
886
+ return error48.message;
887
+ }
888
+ return String(error48);
889
+ }
827
890
  function parsePorcelainV2Status(output) {
828
891
  const parsed = {
829
892
  branch: null,
@@ -918,6 +981,7 @@ function emptyStatus(workspace, lastCheckedAt, error48) {
918
981
  headCommit: null,
919
982
  headMessage: null,
920
983
  upstream: null,
984
+ upstreamStatus: "unavailable",
921
985
  ahead: 0,
922
986
  behind: 0,
923
987
  staged: 0,
@@ -1210,6 +1274,9 @@ function createGitCompactSummary(status, diffSummary) {
1210
1274
  isGitRepo: status.isGitRepo,
1211
1275
  repoRoot: status.repoRoot,
1212
1276
  branch: status.branch,
1277
+ upstreamStatus: status.upstreamStatus,
1278
+ upstreamFetchedAt: status.upstreamFetchedAt,
1279
+ upstreamFetchError: status.upstreamFetchError,
1213
1280
  dirty: status.staged > 0 || status.modified > 0 || status.untracked > 0 || status.deleted > 0 || status.renamed > 0 || conflictCount > 0 || changedFiles > 0,
1214
1281
  changedFiles,
1215
1282
  ahead: status.ahead,
@@ -1537,7 +1604,7 @@ function serviceNotImplemented(command) {
1537
1604
  }
1538
1605
  function createDefaultGitCommandServices() {
1539
1606
  return {
1540
- getStatus: ({ workspace }) => getGitRepoStatus(workspace),
1607
+ getStatus: ({ workspace, refreshUpstream }) => getGitRepoStatus(workspace, { refreshUpstream }),
1541
1608
  getDiffSummary: ({ workspace }) => getGitDiffSummary(workspace),
1542
1609
  getDiffFile: ({ workspace, path: filePath }) => getGitFileDiff(workspace, filePath),
1543
1610
  createSnapshot: ({ workspace, reason, sessionId, turnId }) => defaultSnapshotStore.create({
@@ -1622,7 +1689,7 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
1622
1689
  switch (command) {
1623
1690
  case "git_status": {
1624
1691
  if (!services.getStatus) return serviceNotImplemented(command);
1625
- const status = await runService(() => services.getStatus({ workspace }));
1692
+ const status = await runService(() => services.getStatus({ workspace, refreshUpstream: optionalBoolean(args?.refreshUpstream) }));
1626
1693
  return "success" in status ? status : { success: true, status };
1627
1694
  }
1628
1695
  case "git_diff_summary": {
@@ -46524,17 +46591,87 @@ function readCachedInlineMeshActiveSessions(node) {
46524
46591
  const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
46525
46592
  return sessionId ? [sessionId] : [];
46526
46593
  }
46594
+ function readCachedInlineMeshActiveSessionDetails(node) {
46595
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46596
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
46597
+ const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
46598
+ const sessionId = readStringValue(
46599
+ fallbackSession.id,
46600
+ fallbackSession.sessionId,
46601
+ fallbackSession.session_id,
46602
+ node?.activeSessionId,
46603
+ node?.active_session_id,
46604
+ node?.sessionId,
46605
+ node?.session_id
46606
+ );
46607
+ if (!sessionId) return [];
46608
+ return [{
46609
+ sessionId,
46610
+ providerType: readStringValue(
46611
+ fallbackSession.providerType,
46612
+ fallbackSession.provider_type,
46613
+ fallbackSession.cliType,
46614
+ fallbackSession.cli_type,
46615
+ fallbackSession.provider,
46616
+ node?.providerType,
46617
+ node?.provider_type
46618
+ ),
46619
+ state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
46620
+ lifecycle: readStringValue(fallbackSession.lifecycle),
46621
+ title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
46622
+ workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
46623
+ lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
46624
+ recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
46625
+ isCached: true
46626
+ }];
46627
+ }
46628
+ function readLiveMeshSessionState(record2) {
46629
+ return readStringValue(
46630
+ record2?.meta?.sessionStatus,
46631
+ record2?.meta?.status,
46632
+ record2?.meta?.providerStatus,
46633
+ record2?.status,
46634
+ record2?.state,
46635
+ record2?.lifecycle
46636
+ );
46637
+ }
46638
+ function toIsoTimestamp(value) {
46639
+ if (typeof value === "number" && Number.isFinite(value)) return new Date(value).toISOString();
46640
+ const stringValue = readStringValue(value);
46641
+ return stringValue || null;
46642
+ }
46643
+ function summarizeMeshSessionRecord(record2) {
46644
+ return {
46645
+ sessionId: readStringValue(record2?.sessionId) || "unknown",
46646
+ providerType: readStringValue(record2?.providerType),
46647
+ state: readLiveMeshSessionState(record2),
46648
+ lifecycle: readStringValue(record2?.lifecycle),
46649
+ surfaceKind: getSessionHostSurfaceKind(record2),
46650
+ recoveryState: readStringValue(record2?.meta?.runtimeRecoveryState) ?? null,
46651
+ workspace: readStringValue(record2?.workspace) ?? null,
46652
+ title: readStringValue(record2?.displayName, record2?.workspaceLabel) ?? null,
46653
+ lastActivityAt: toIsoTimestamp(record2?.updatedAt ?? record2?.lastActivityAt ?? record2?.last_activity_at),
46654
+ isCached: false
46655
+ };
46656
+ }
46527
46657
  function applyCachedInlineMeshNodeStatus(status, node) {
46528
46658
  const cachedStatus = readObjectRecord(node?.cachedStatus);
46529
46659
  const git = buildCachedInlineMeshGitStatus(node);
46530
46660
  const error48 = readStringValue(cachedStatus.error, node?.error);
46531
46661
  const health = readStringValue(cachedStatus.health, node?.health);
46532
46662
  const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
46663
+ const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
46664
+ const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
46533
46665
  const activeSessions = readCachedInlineMeshActiveSessions(node);
46534
- if (!git && !error48 && !health && !machineStatus && activeSessions.length === 0) return false;
46666
+ const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
46667
+ if (!git && !error48 && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
46535
46668
  if (git) status.git = git;
46536
46669
  if (error48) status.error = error48;
46670
+ if (machineStatus) status.machineStatus = machineStatus;
46671
+ if (lastSeenAt) status.lastSeenAt = lastSeenAt;
46672
+ if (updatedAt) status.updatedAt = updatedAt;
46537
46673
  if (activeSessions.length > 0) status.activeSessions = activeSessions;
46674
+ if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
46538
46675
  if (health) {
46539
46676
  status.health = health;
46540
46677
  return true;
@@ -46543,7 +46680,7 @@ function applyCachedInlineMeshNodeStatus(status, node) {
46543
46680
  status.health = deriveMeshNodeHealthFromGit(git);
46544
46681
  return true;
46545
46682
  }
46546
- return activeSessions.length > 0 || !!machineStatus;
46683
+ return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
46547
46684
  }
46548
46685
  async function resolveProviderTypeFromPriority(args) {
46549
46686
  if (!args.providerPriority.length) {
@@ -48822,33 +48959,84 @@ ${block}`);
48822
48959
  const ledgerSummary = getLedgerSummary2(meshId);
48823
48960
  const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
48824
48961
  const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
48962
+ const localMachineId = loadConfig().machineId || "";
48963
+ const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes) ? readStringValue(mesh.nodes[0]?.id, mesh.nodes[0]?.nodeId) : void 0;
48964
+ const refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
48825
48965
  const nodeStatuses = [];
48826
- for (const node of mesh.nodes || []) {
48966
+ for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
48967
+ const nodeId = String(node.id || node.nodeId || "");
48968
+ const daemonId = readStringValue(node.daemonId);
48969
+ const providerPriority = readProviderPriorityFromPolicy(node.policy);
48970
+ const isSelfNode = Boolean(
48971
+ nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId
48972
+ ) || Boolean(
48973
+ daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId)
48974
+ ) || Boolean(meshRecord?.inline && nodeIndex === 0);
48827
48975
  const status = {
48828
- nodeId: node.id || node.nodeId,
48976
+ nodeId,
48829
48977
  machineLabel: node.machineLabel || node.id || node.nodeId,
48830
48978
  workspace: node.workspace,
48831
48979
  repoRoot: node.repoRoot,
48832
48980
  isLocalWorktree: node.isLocalWorktree,
48833
48981
  worktreeBranch: node.worktreeBranch,
48834
- daemonId: node.daemonId,
48982
+ daemonId,
48835
48983
  machineId: node.machineId,
48984
+ machineStatus: node.machineStatus,
48836
48985
  health: "unknown",
48837
48986
  providers: node.providers || [],
48838
- activeSessions: []
48987
+ providerPriority,
48988
+ activeSessions: [],
48989
+ activeSessionDetails: [],
48990
+ launchReady: false
48839
48991
  };
48840
- const nodeId = String(node.id || node.nodeId || "");
48841
- const matchedLiveSessions = liveMeshSessions.filter((record2) => this.sessionMatchesMeshNode(record2, node, nodeId)).map((record2) => typeof record2?.sessionId === "string" ? record2.sessionId : "").filter(Boolean);
48842
- if (matchedLiveSessions.length > 0) {
48843
- status.activeSessions = matchedLiveSessions;
48992
+ if (isSelfNode) {
48993
+ status.connection = {
48994
+ perspective: "selected_coordinator",
48995
+ source: "mesh_peer_status",
48996
+ state: "self",
48997
+ transport: "local",
48998
+ reported: true,
48999
+ reason: "Selected coordinator daemon",
49000
+ lastStateChangeAt: refreshedAt
49001
+ };
49002
+ } else if (daemonId) {
49003
+ const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
49004
+ status.connection = connection ?? {
49005
+ perspective: "selected_coordinator",
49006
+ source: "not_reported",
49007
+ state: "unknown",
49008
+ transport: "unknown",
49009
+ reported: false,
49010
+ reason: "No live mesh peer telemetry reported by the selected coordinator yet."
49011
+ };
49012
+ } else {
49013
+ status.connection = {
49014
+ perspective: "selected_coordinator",
49015
+ source: "not_reported",
49016
+ state: "unknown",
49017
+ transport: "unknown",
49018
+ reported: false,
49019
+ reason: "Node has no daemon id, so mesh transport cannot be reported from the selected coordinator."
49020
+ };
49021
+ }
49022
+ const matchedLiveSessionRecords = liveMeshSessions.filter((record2) => this.sessionMatchesMeshNode(record2, node, nodeId));
49023
+ if (matchedLiveSessionRecords.length > 0) {
49024
+ const sessionIds = matchedLiveSessionRecords.map((record2) => typeof record2?.sessionId === "string" ? record2.sessionId : "").filter(Boolean);
49025
+ const providerTypes = matchedLiveSessionRecords.map((record2) => readStringValue(record2?.providerType)).filter(Boolean);
49026
+ status.activeSessions = sessionIds;
49027
+ status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
49028
+ if (providerTypes.length > 0) {
49029
+ status.providers = Array.from(/* @__PURE__ */ new Set([...Array.isArray(status.providers) ? status.providers : [], ...providerTypes]));
49030
+ }
48844
49031
  }
48845
49032
  if (node.workspace && typeof node.workspace === "string") {
48846
49033
  if (!fs10.existsSync(node.workspace) && applyCachedInlineMeshNodeStatus(status, node)) {
49034
+ status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || isSelfNode);
48847
49035
  nodeStatuses.push(status);
48848
49036
  continue;
48849
49037
  }
48850
49038
  try {
48851
- const gitStatus = await getGitRepoStatus(node.workspace, { timeoutMs: 1e4 });
49039
+ const gitStatus = await getGitRepoStatus(node.workspace, { timeoutMs: 1e4, refreshUpstream: true });
48852
49040
  status.git = gitStatus;
48853
49041
  if (gitStatus.isGitRepo) {
48854
49042
  status.health = deriveMeshNodeHealthFromGit(gitStatus);
@@ -48864,6 +49052,7 @@ ${block}`);
48864
49052
  } else {
48865
49053
  applyCachedInlineMeshNodeStatus(status, node);
48866
49054
  }
49055
+ status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || isSelfNode);
48867
49056
  nodeStatuses.push(status);
48868
49057
  }
48869
49058
  return {
@@ -48872,6 +49061,7 @@ ${block}`);
48872
49061
  meshName: mesh.name,
48873
49062
  repoIdentity: mesh.repoIdentity,
48874
49063
  defaultBranch: mesh.defaultBranch,
49064
+ refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
48875
49065
  nodes: nodeStatuses,
48876
49066
  queue: { tasks: queue, summary: queueSummary },
48877
49067
  ledger: { entries: ledgerEntries, summary: ledgerSummary }
@@ -56950,6 +57140,7 @@ async function initDaemonComponents(config2) {
56950
57140
  sessionHostControl: config2.sessionHostControl,
56951
57141
  statusInstanceId: config2.statusInstanceId,
56952
57142
  statusVersion: config2.statusVersion,
57143
+ getMeshPeerConnectionStatus: config2.getMeshPeerConnectionStatus,
56953
57144
  getCdpLogFn: config2.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
56954
57145
  });
56955
57146
  poller = new AgentStreamPoller({
@@ -97227,11 +97418,30 @@ var init_daemon_mesh_manager = __esm({
97227
97418
  nodeDatachannel = null;
97228
97419
  peers = /* @__PURE__ */ new Map();
97229
97420
  // Map<targetDaemonId, PeerEntry>
97421
+ peerSnapshots = /* @__PURE__ */ new Map();
97230
97422
  pendingRequests = /* @__PURE__ */ new Map();
97231
97423
  commandCallback;
97232
97424
  p2pFailure(message, command, targetDaemonId) {
97233
97425
  return new P2pRelayFailureError(message, { command, targetDaemonId });
97234
97426
  }
97427
+ updatePeerSnapshot(targetDaemonId, state, patch = {}) {
97428
+ const previous = this.peerSnapshots.get(targetDaemonId);
97429
+ const now = (/* @__PURE__ */ new Date()).toISOString();
97430
+ this.peerSnapshots.set(targetDaemonId, {
97431
+ perspective: "selected_coordinator",
97432
+ source: "mesh_peer_status",
97433
+ reported: true,
97434
+ state,
97435
+ transport: patch.transport ?? previous?.transport ?? "unknown",
97436
+ reason: patch.reason ?? previous?.reason,
97437
+ lastStateChangeAt: now,
97438
+ lastConnectedAt: patch.lastConnectedAt ?? previous?.lastConnectedAt,
97439
+ lastCommandAt: patch.lastCommandAt ?? previous?.lastCommandAt
97440
+ });
97441
+ }
97442
+ getPeerConnectionStatus(targetDaemonId) {
97443
+ return this.peerSnapshots.get(targetDaemonId) ?? null;
97444
+ }
97235
97445
  invalidatePeer(targetDaemonId, reason, options = {}) {
97236
97446
  const peer = this.peers.get(targetDaemonId);
97237
97447
  if (peer?.commandQueue) {
@@ -97246,6 +97456,11 @@ var init_daemon_mesh_manager = __esm({
97246
97456
  pending.reject(this.p2pFailure(reason, pending.command, targetDaemonId));
97247
97457
  }
97248
97458
  }
97459
+ const snapshotState = peer?.state === "closed" ? "closed" : peer?.state === "disconnected" ? "disconnected" : "failed";
97460
+ this.updatePeerSnapshot(targetDaemonId, snapshotState, {
97461
+ reason,
97462
+ transport: peer?.isRelay === true ? "relay" : peer?.isRelay === false ? "direct" : "unknown"
97463
+ });
97249
97464
  if (options.closeResources !== false && peer) {
97250
97465
  try {
97251
97466
  peer.dataChannel?.close?.();
@@ -97365,6 +97580,20 @@ var init_daemon_mesh_manager = __esm({
97365
97580
  if (!peer) {
97366
97581
  throw this.p2pFailure("Failed to initiate P2P connection entry", command, targetDaemonId);
97367
97582
  }
97583
+ const lastCommandAt = (/* @__PURE__ */ new Date()).toISOString();
97584
+ if (peer.state === "connected") {
97585
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
97586
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown",
97587
+ lastConnectedAt: this.peerSnapshots.get(targetDaemonId)?.lastConnectedAt,
97588
+ lastCommandAt
97589
+ });
97590
+ } else {
97591
+ this.updatePeerSnapshot(targetDaemonId, "connecting", {
97592
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown",
97593
+ reason: "Waiting for mesh DataChannel to open.",
97594
+ lastCommandAt
97595
+ });
97596
+ }
97368
97597
  return new Promise((resolve23, reject) => {
97369
97598
  const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
97370
97599
  const timer = setTimeout(() => {
@@ -97508,6 +97737,9 @@ var init_daemon_mesh_manager = __esm({
97508
97737
  remoteDescriptionSet: false
97509
97738
  };
97510
97739
  this.peers.set(targetDaemonId, entry);
97740
+ this.updatePeerSnapshot(targetDaemonId, "connecting", {
97741
+ reason: isInitiator ? "P2P mesh connection initiated by the selected coordinator." : "Waiting for the remote daemon to finish the mesh DataChannel handshake."
97742
+ });
97511
97743
  pc.onLocalDescription((sdp, type2) => {
97512
97744
  this.serverConn.sendMeshCommand(targetDaemonId, type2 === "offer" ? "mesh_p2p_offer" : "mesh_p2p_answer", { sdp, type: type2 });
97513
97745
  });
@@ -97518,7 +97750,26 @@ var init_daemon_mesh_manager = __esm({
97518
97750
  LOG.info("Mesh", `[Mesh] P2P state with ${targetDaemonId.slice(0, 12)}: ${state}`);
97519
97751
  if (state === "connected") {
97520
97752
  entry.state = "connected";
97753
+ let transport = "unknown";
97754
+ try {
97755
+ const pair = pc.getSelectedCandidatePair?.();
97756
+ if (pair) {
97757
+ const localType = pair.local?.type || "unknown";
97758
+ const remoteType = pair.remote?.type || "unknown";
97759
+ entry.isRelay = localType === "relay" || remoteType === "relay";
97760
+ transport = entry.isRelay ? "relay" : "direct";
97761
+ LOG.info("Mesh", `[Mesh] Candidate pair with ${targetDaemonId.slice(0, 12)}: local=${localType} remote=${remoteType} \u2192 ${transport}`);
97762
+ }
97763
+ } catch {
97764
+ transport = entry.isRelay === true ? "relay" : entry.isRelay === false ? "direct" : "unknown";
97765
+ }
97766
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
97767
+ transport,
97768
+ reason: transport === "relay" ? "Connected over TURN relay." : transport === "direct" ? "Connected directly peer-to-peer." : "Connected, but selected candidate pair details are unavailable.",
97769
+ lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString()
97770
+ });
97521
97771
  } else if (state === "failed" || state === "closed" || state === "disconnected") {
97772
+ entry.state = state;
97522
97773
  this.invalidatePeer(targetDaemonId, `P2P state changed to ${state}`, { rejectPending: true, closeResources: false });
97523
97774
  }
97524
97775
  });
@@ -97536,6 +97787,11 @@ var init_daemon_mesh_manager = __esm({
97536
97787
  dc.onOpen(() => {
97537
97788
  LOG.info("Mesh", `[Mesh] DataChannel OPEN with ${targetDaemonId.slice(0, 12)}`);
97538
97789
  entry.state = "connected";
97790
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
97791
+ transport: entry.isRelay === true ? "relay" : entry.isRelay === false ? "direct" : "unknown",
97792
+ reason: entry.isRelay === true ? "Connected over TURN relay." : entry.isRelay === false ? "Connected directly peer-to-peer." : "DataChannel open; transport details not reported yet.",
97793
+ lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString()
97794
+ });
97539
97795
  if (entry.commandQueue) {
97540
97796
  const queue = entry.commandQueue;
97541
97797
  entry.commandQueue = [];
@@ -97831,7 +98087,7 @@ var init_adhdev_daemon = __esm({
97831
98087
  init_version();
97832
98088
  init_src();
97833
98089
  init_runtime_defaults();
97834
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.7" });
98090
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.9" });
97835
98091
  AdhdevDaemon = class _AdhdevDaemon {
97836
98092
  localHttpServer = null;
97837
98093
  localWss = null;
@@ -98355,6 +98611,7 @@ ${err?.stack || ""}`);
98355
98611
  if (!this.meshManager) throw new Error("Mesh manager not initialized");
98356
98612
  return this.meshManager.sendCommand(daemonId, command, args);
98357
98613
  },
98614
+ getMeshPeerConnectionStatus: (daemonId) => this.meshManager?.getPeerConnectionStatus(daemonId) ?? null,
98358
98615
  onStatusChange: () => {
98359
98616
  this.invalidateHotChatSnapshotCache();
98360
98617
  this.statusReporter?.onStatusChange();